Integer type name change.
[silc.git] / apps / irssi / src / silc / core / client_ops.c
1 /*
2
3   client_ops.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 2001 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20
21 #include "module.h"
22 #include "chat-protocols.h"
23 #include "args.h"
24
25 #include "chatnets.h"
26 #include "servers-setup.h"
27 #include "channels-setup.h"
28 #include "silc-servers.h"
29 #include "silc-channels.h"
30 #include "silc-queries.h"
31 #include "silc-nicklist.h"
32
33 #include "signals.h"
34 #include "levels.h"
35 #include "settings.h"
36 #include "fe-common/core/printtext.h"
37 #include "fe-common/core/fe-channels.h"
38 #include "fe-common/core/keyboard.h"
39 #include "fe-common/silc/module-formats.h"
40
41 static void 
42 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
43                                 const char *name, SilcSocketType conn_type, 
44                                 unsigned char *pk, SilcUInt32 pk_len, 
45                                 SilcSKEPKType pk_type,
46                                 SilcVerifyPublicKey completion, void *context);
47
48 void silc_say(SilcClient client, SilcClientConnection conn,
49               SilcClientMessageType type, char *msg, ...)
50 {
51   SILC_SERVER_REC *server;
52   va_list va;
53   char *str;
54
55   server = conn == NULL ? NULL : conn->context;
56   
57   va_start(va, msg);
58   str = g_strdup_vprintf(msg, va);
59   printtext(server, NULL, MSGLEVEL_CRAP, "%s", str);
60   g_free(str);
61   va_end(va);
62 }
63
64 void silc_say_error(char *msg, ...)
65 {
66   va_list va;
67   char *str;
68
69   va_start(va, msg);
70   str = g_strdup_vprintf(msg, va);
71   printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str);
72
73   g_free(str);
74   va_end(va);
75 }
76
77 /* Message for a channel. The `sender' is the nickname of the sender 
78    received in the packet. The `channel_name' is the name of the channel. */
79
80 void silc_channel_message(SilcClient client, SilcClientConnection conn,
81                           SilcClientEntry sender, SilcChannelEntry channel,
82                           SilcMessageFlags flags, char *msg)
83 {
84   SILC_SERVER_REC *server;
85   SILC_NICK_REC *nick;
86   SILC_CHANNEL_REC *chanrec;
87   
88   SILC_LOG_DEBUG(("Start"));
89
90   if (!msg)
91     return;
92
93   server = conn == NULL ? NULL : conn->context;
94   chanrec = silc_channel_find_entry(server, channel);
95   if (!chanrec)
96     return;
97   
98   nick = silc_nicklist_find(chanrec, sender);
99   if (!nick) {
100     /* We didn't find client but it clearly exists, add it. */
101     SilcChannelUser chu = silc_client_on_channel(channel, sender);
102     if (chu)
103       nick = silc_nicklist_insert(chanrec, chu, FALSE);
104   }
105
106   if (flags & SILC_MESSAGE_FLAG_ACTION)
107     printformat_module("fe-common/silc", server, channel->channel_name,
108                        MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_ACTION, 
109                        nick == NULL ? "[<unknown>]" : nick->nick, msg);
110   else if (flags & SILC_MESSAGE_FLAG_NOTICE)
111     printformat_module("fe-common/silc", server, channel->channel_name,
112                        MSGLEVEL_NOTICES, SILCTXT_CHANNEL_NOTICE, 
113                        nick == NULL ? "[<unknown>]" : nick->nick, msg);
114   else
115     signal_emit("message public", 6, server, msg,
116                 nick == NULL ? "[<unknown>]" : nick->nick,
117                 nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
118                 chanrec->name, nick);
119 }
120
121 /* Private message to the client. The `sender' is the nickname of the
122    sender received in the packet. */
123
124 void silc_private_message(SilcClient client, SilcClientConnection conn,
125                           SilcClientEntry sender, SilcMessageFlags flags,
126                           char *msg)
127 {
128   SILC_SERVER_REC *server;
129   char userhost[256];
130   
131   SILC_LOG_DEBUG(("Start"));
132
133   server = conn == NULL ? NULL : conn->context;
134   memset(userhost, 0, sizeof(userhost));
135   if (sender->username)
136     snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
137              sender->username, sender->hostname);
138   signal_emit("message private", 4, server, msg,
139               sender->nickname ? sender->nickname : "[<unknown>]",
140               sender->username ? userhost : NULL);
141 }
142
143 /* Notify message to the client. The notify arguments are sent in the
144    same order as servers sends them. The arguments are same as received
145    from the server except for ID's.  If ID is received application receives
146    the corresponding entry to the ID. For example, if Client ID is received
147    application receives SilcClientEntry.  Also, if the notify type is
148    for channel the channel entry is sent to application (even if server
149    does not send it). */
150
151 void silc_notify(SilcClient client, SilcClientConnection conn,
152                  SilcNotifyType type, ...)
153 {
154   va_list va;
155   SILC_SERVER_REC *server;
156   SILC_CHANNEL_REC *chanrec;
157   SILC_NICK_REC *nickrec;
158   SilcClientEntry client_entry, client_entry2;
159   SilcChannelEntry channel;
160   SilcServerEntry server_entry;
161   SilcIdType idtype;
162   void *entry;
163   SilcUInt32 mode;
164   char userhost[512];
165   char *name, *tmp;
166   GSList *list1, *list_tmp;
167
168   SILC_LOG_DEBUG(("Start"));
169
170   va_start(va, type);
171
172   server = conn == NULL ? NULL : conn->context;
173   
174   switch(type) {
175   case SILC_NOTIFY_TYPE_NONE:
176     /* Some generic notice from server */
177     printtext(server, NULL, MSGLEVEL_CRAP, "%s", (char *)va_arg(va, char *));
178     break;
179
180   case SILC_NOTIFY_TYPE_INVITE:
181     /*
182      * Invited or modified invite list.
183      */
184
185     SILC_LOG_DEBUG(("Notify: INVITE"));
186
187     channel = va_arg(va, SilcChannelEntry);
188     name = va_arg(va, char *);
189     client_entry = va_arg(va, SilcClientEntry);
190
191     memset(userhost, 0, sizeof(userhost));
192     snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
193              client_entry->username, client_entry->hostname);
194     signal_emit("message invite", 4, server, channel ? channel->channel_name :
195                 name, client_entry->nickname, userhost);
196     break;
197
198   case SILC_NOTIFY_TYPE_JOIN:
199     /*
200      * Joined channel.
201      */
202  
203     SILC_LOG_DEBUG(("Notify: JOIN"));
204
205     client_entry = va_arg(va, SilcClientEntry);
206     channel = va_arg(va, SilcChannelEntry);
207
208     if (client_entry == server->conn->local_entry) {
209       /* You joined to channel */
210       chanrec = silc_channel_find(server, channel->channel_name);
211       if (chanrec != NULL && !chanrec->joined)
212         chanrec->entry = channel;
213     } else {
214       chanrec = silc_channel_find_entry(server, channel);
215       if (chanrec != NULL) {
216         SilcChannelUser chu = silc_client_on_channel(channel, client_entry);
217         if (chu)
218           nickrec = silc_nicklist_insert(chanrec, chu, TRUE);
219       }
220     }
221     
222     memset(userhost, 0, sizeof(userhost));
223     if (client_entry->username)
224     snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
225              client_entry->username, client_entry->hostname);
226     signal_emit("message join", 4, server, channel->channel_name,
227                 client_entry->nickname,
228                 client_entry->username == NULL ? "" : userhost);
229     break;
230
231   case SILC_NOTIFY_TYPE_LEAVE:
232     /*
233      * Left a channel.
234      */
235
236     SILC_LOG_DEBUG(("Notify: LEAVE"));
237
238     client_entry = va_arg(va, SilcClientEntry);
239     channel = va_arg(va, SilcChannelEntry);
240     
241     memset(userhost, 0, sizeof(userhost));
242     if (client_entry->username)
243       snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
244                client_entry->username, client_entry->hostname);
245     signal_emit("message part", 5, server, channel->channel_name,
246                 client_entry->nickname,  client_entry->username ? 
247                 userhost : "", client_entry->nickname);
248     
249     chanrec = silc_channel_find_entry(server, channel);
250     if (chanrec != NULL) {
251       nickrec = silc_nicklist_find(chanrec, client_entry);
252       if (nickrec != NULL)
253         nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
254     }
255     break;
256
257   case SILC_NOTIFY_TYPE_SIGNOFF:
258     /*
259      * Left the network.
260      */
261
262     SILC_LOG_DEBUG(("Notify: SIGNOFF"));
263
264     client_entry = va_arg(va, SilcClientEntry);
265     tmp = va_arg(va, char *);
266     
267     silc_server_free_ftp(server, client_entry);
268     
269     memset(userhost, 0, sizeof(userhost));
270     if (client_entry->username)
271       snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
272                client_entry->username, client_entry->hostname);
273     signal_emit("message quit", 4, server, client_entry->nickname,
274                 client_entry->username ? userhost : "", 
275                 tmp ? tmp : "");
276     
277     list1 = nicklist_get_same_unique(SERVER(server), client_entry);
278     for (list_tmp = list1; list_tmp != NULL; list_tmp = 
279            list_tmp->next->next) {
280       CHANNEL_REC *channel = list_tmp->data;
281       NICK_REC *nickrec = list_tmp->next->data;
282       
283       nicklist_remove(channel, nickrec);
284     }
285     break;
286
287   case SILC_NOTIFY_TYPE_TOPIC_SET:
288     /*
289      * Changed topic.
290      */
291
292     SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
293
294     idtype = va_arg(va, int);
295     entry = va_arg(va, void *);
296     tmp = va_arg(va, char *);
297     channel = va_arg(va, SilcChannelEntry);
298     
299     chanrec = silc_channel_find_entry(server, channel);
300     if (chanrec != NULL) {
301       g_free_not_null(chanrec->topic);
302       chanrec->topic = *tmp == '\0' ? NULL : g_strdup(tmp);
303       signal_emit("channel topic changed", 1, chanrec);
304     }
305     
306     if (idtype == SILC_ID_CLIENT) {
307       client_entry = (SilcClientEntry)entry;
308       memset(userhost, 0, sizeof(userhost));
309       snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
310                client_entry->username, client_entry->hostname);
311       signal_emit("message topic", 5, server, channel->channel_name,
312                   tmp, client_entry->nickname, userhost);
313     } else if (idtype == SILC_ID_SERVER) {
314       server_entry = (SilcServerEntry)entry;
315       signal_emit("message topic", 5, server, channel->channel_name,
316                   tmp, server_entry->server_name, 
317                   server_entry->server_name);
318     } else {
319       channel = (SilcChannelEntry)entry;
320       signal_emit("message topic", 5, server, channel->channel_name,
321                   tmp, channel->channel_name, channel->channel_name);
322     }
323     break;
324
325   case SILC_NOTIFY_TYPE_NICK_CHANGE:
326     /*
327      * Changed nickname.
328      */
329
330     SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
331
332     client_entry = va_arg(va, SilcClientEntry);
333     client_entry2 = va_arg(va, SilcClientEntry);
334     
335     memset(userhost, 0, sizeof(userhost));
336     snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
337              client_entry2->username, client_entry2->hostname);
338     nicklist_rename_unique(SERVER(server),
339                            client_entry, client_entry->nickname,
340                            client_entry2, client_entry2->nickname);
341     signal_emit("message nick", 4, server, client_entry2->nickname, 
342                 client_entry->nickname, userhost);
343     break;
344
345   case SILC_NOTIFY_TYPE_CMODE_CHANGE:
346     /*
347      * Changed channel mode.
348      */
349
350     SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
351
352     idtype = va_arg(va, int);
353     entry = va_arg(va, void *);
354     mode = va_arg(va, SilcUInt32);
355     (void)va_arg(va, char *);
356     (void)va_arg(va, char *);
357     channel = va_arg(va, SilcChannelEntry);
358
359     tmp = silc_client_chmode(mode,
360                              channel->channel_key ? 
361                              channel->channel_key->cipher->name : "",
362                              channel->hmac ? 
363                              silc_hmac_get_name(channel->hmac) : "");
364     
365     chanrec = silc_channel_find_entry(server, channel);
366     if (chanrec != NULL) {
367       g_free_not_null(chanrec->mode);
368       chanrec->mode = g_strdup(tmp == NULL ? "" : tmp);
369       signal_emit("channel mode changed", 1, chanrec);
370     }
371     
372     if (idtype == SILC_ID_CLIENT) {
373       client_entry = (SilcClientEntry)entry;
374       printformat_module("fe-common/silc", server, channel->channel_name,
375                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
376                          channel->channel_name, tmp ? tmp : "removed all",
377                          client_entry->nickname);
378     } else if (idtype == SILC_ID_SERVER) {
379       server_entry = (SilcServerEntry)entry;
380       printformat_module("fe-common/silc", server, channel->channel_name,
381                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
382                          channel->channel_name, tmp ? tmp : "removed all",
383                          server_entry->server_name);
384     }
385
386     silc_free(tmp);
387     break;
388
389   case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
390     /*
391      * Changed user's mode on channel.
392      */
393
394     SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
395
396     client_entry = va_arg(va, SilcClientEntry);
397     mode = va_arg(va, SilcUInt32);
398     client_entry2 = va_arg(va, SilcClientEntry);
399     channel = va_arg(va, SilcChannelEntry);
400     
401     tmp = silc_client_chumode(mode);
402     chanrec = silc_channel_find_entry(server, channel);
403     if (chanrec != NULL) {
404       SILC_NICK_REC *nick;
405       
406       if (client_entry2 == server->conn->local_entry)
407         chanrec->chanop = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
408       
409       nick = silc_nicklist_find(chanrec, client_entry2);
410       if (nick != NULL) {
411         nick->op = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
412         nick->founder = (mode & SILC_CHANNEL_UMODE_CHANFO) != 0;
413         signal_emit("nick mode changed", 2, chanrec, nick);
414       }
415     }
416   
417     printformat_module("fe-common/silc", server, channel->channel_name,
418                        MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
419                        channel->channel_name, client_entry2->nickname, 
420                        tmp ? tmp : "removed all",
421                        client_entry->nickname);
422
423     if (mode & SILC_CHANNEL_UMODE_CHANFO)
424       printformat_module("fe-common/silc", 
425                          server, channel->channel_name, MSGLEVEL_CRAP,
426                          SILCTXT_CHANNEL_FOUNDER,
427                          channel->channel_name, client_entry2->nickname);
428     
429     silc_free(tmp);
430     break;
431
432   case SILC_NOTIFY_TYPE_MOTD:
433     /*
434      * Received MOTD.
435      */
436
437     SILC_LOG_DEBUG(("Notify: MOTD"));
438
439     tmp = va_arg(va, char *);
440
441     if (!settings_get_bool("skip_motd"))
442       printtext_multiline(server, NULL, MSGLEVEL_CRAP, "%s", tmp);
443     break;
444
445   case SILC_NOTIFY_TYPE_KICKED:
446     /*
447      * Someone was kicked from channel.
448      */
449
450     SILC_LOG_DEBUG(("Notify: KICKED"));
451
452     client_entry = va_arg(va, SilcClientEntry);
453     tmp = va_arg(va, char *);
454     client_entry2 = va_arg(va, SilcClientEntry);
455     channel = va_arg(va, SilcChannelEntry);
456
457     chanrec = silc_channel_find_entry(server, channel);
458   
459     if (client_entry == conn->local_entry) {
460       printformat_module("fe-common/silc", server, channel->channel_name,
461                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED_YOU, 
462                          channel->channel_name, client_entry2->nickname,
463                          tmp ? tmp : "");
464       if (chanrec) {
465         chanrec->kicked = TRUE;
466         channel_destroy((CHANNEL_REC *)chanrec);
467       }
468     } else {
469       printformat_module("fe-common/silc", server, channel->channel_name,
470                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED, 
471                          client_entry->nickname, channel->channel_name, 
472                          client_entry2->nickname, tmp ? tmp : "");
473
474       if (chanrec) {
475         SILC_NICK_REC *nickrec = silc_nicklist_find(chanrec, client_entry);
476         if (nickrec != NULL)
477           nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
478       }
479     }
480     break;
481
482   case SILC_NOTIFY_TYPE_KILLED:
483     /*
484      * Someone was killed from the network.
485      */
486
487     SILC_LOG_DEBUG(("Notify: KILLED"));
488
489     client_entry = va_arg(va, SilcClientEntry);
490     tmp = va_arg(va, char *);
491   
492     if (client_entry == conn->local_entry) {
493       printformat_module("fe-common/silc", server, NULL,
494                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU, 
495                          tmp ? tmp : "");
496     } else {
497       list1 = nicklist_get_same_unique(SERVER(server), client_entry);
498       for (list_tmp = list1; list_tmp != NULL; list_tmp = 
499              list_tmp->next->next) {
500         CHANNEL_REC *channel = list_tmp->data;
501         NICK_REC *nickrec = list_tmp->next->data;
502         nicklist_remove(channel, nickrec);
503       }
504
505       printformat_module("fe-common/silc", server, NULL,
506                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED, 
507                          client_entry->nickname,
508                          tmp ? tmp : "");
509     }
510     break;
511
512   case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
513     {
514       /*
515        * Server has quit the network.
516        */
517       int i;
518       SilcClientEntry *clients;
519       SilcUInt32 clients_count;
520
521       SILC_LOG_DEBUG(("Notify: SIGNOFF"));
522       
523       (void)va_arg(va, void *);
524       clients = va_arg(va, SilcClientEntry *);
525       clients_count = va_arg(va, SilcUInt32);
526   
527       for (i = 0; i < clients_count; i++) {
528         memset(userhost, 0, sizeof(userhost));
529         if (clients[i]->username)
530           snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
531                    clients[i]->username, clients[i]->hostname);
532         signal_emit("message quit", 4, server, clients[i]->nickname,
533                     clients[i]->username ? userhost : "", 
534                     "server signoff");
535
536         silc_server_free_ftp(server, clients[i]);
537         
538         list1 = nicklist_get_same_unique(SERVER(server), clients[i]);
539         for (list_tmp = list1; list_tmp != NULL; list_tmp = 
540                list_tmp->next->next) {
541           CHANNEL_REC *channel = list_tmp->data;
542           NICK_REC *nickrec = list_tmp->next->data;
543           nicklist_remove(channel, nickrec);
544         }
545       }
546     }
547     break;
548
549   default:
550     /* Unknown notify */
551     printformat_module("fe-common/silc", server, NULL,
552                        MSGLEVEL_CRAP, SILCTXT_UNKNOWN_NOTIFY, type);
553     break;
554   }
555
556   va_end(va);
557 }
558
559 /* Called to indicate that connection was either successfully established
560    or connecting failed.  This is also the first time application receives
561    the SilcClientConnection object which it should save somewhere. */
562
563 void silc_connect(SilcClient client, SilcClientConnection conn, int success)
564 {
565   SILC_SERVER_REC *server = conn->context;
566
567   if (!server && !success) {
568     silc_client_close_connection(client, NULL, conn);
569     return;
570   }
571
572   if (success) {
573     server->connected = TRUE;
574     signal_emit("event connected", 1, server);
575   } else {
576     server->connection_lost = TRUE;
577     if (server->conn)
578       server->conn->context = NULL;
579     server_disconnect(SERVER(server));
580   }
581 }
582
583 /* Called to indicate that connection was disconnected to the server. */
584
585 void silc_disconnect(SilcClient client, SilcClientConnection conn)
586 {
587   SILC_SERVER_REC *server = conn->context;
588
589   SILC_LOG_DEBUG(("Start"));
590
591   if (server->conn && server->conn->local_entry) {
592     nicklist_rename_unique(SERVER(server),
593                            server->conn->local_entry, server->nick,
594                            server->conn->local_entry, 
595                            silc_client->username);
596     silc_change_nick(server, silc_client->username);
597   }
598
599   server->conn->context = NULL;
600   server->conn = NULL;
601   server->connection_lost = TRUE;
602   server_disconnect(SERVER(server));
603 }
604
605 /* Command handler. This function is called always in the command function.
606    If error occurs it will be called as well. `conn' is the associated
607    client connection. `cmd_context' is the command context that was
608    originally sent to the command. `success' is FALSE if error occured
609    during command. `command' is the command being processed. It must be
610    noted that this is not reply from server. This is merely called just
611    after application has called the command. Just to tell application
612    that the command really was processed. */
613
614 void silc_command(SilcClient client, SilcClientConnection conn, 
615                   SilcClientCommandContext cmd_context, int success,
616                   SilcCommand command)
617 {
618   SILC_SERVER_REC *server = conn->context;
619
620   SILC_LOG_DEBUG(("Start"));
621
622   if (!success)
623     return;
624
625   switch(command) {
626   case SILC_COMMAND_INVITE:
627     printformat_module("fe-common/silc", server, NULL,
628                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING,
629                        cmd_context->argv[2], 
630                        (cmd_context->argv[1][0] == '*' ?
631                         (char *)conn->current_channel->channel_name :
632                         (char *)cmd_context->argv[1]));
633     break;
634   default:
635     break;
636   }
637 }
638
639 /* Client info resolving callback when JOIN command reply is received.
640    This will cache all users on the channel. */
641
642 static void silc_client_join_get_users(SilcClient client,
643                                        SilcClientConnection conn,
644                                        SilcClientEntry *clients,
645                                        SilcUInt32 clients_count,
646                                        void *context)
647 {
648   SilcChannelEntry channel = (SilcChannelEntry)context;
649   SilcHashTableList htl;
650   SilcChannelUser chu;
651   SILC_SERVER_REC *server = conn->context;
652   SILC_CHANNEL_REC *chanrec;
653   SilcClientEntry founder = NULL;
654   NICK_REC *ownnick;
655
656   if (!clients)
657     return;
658
659   chanrec = silc_channel_find(server, channel->channel_name);
660   if (chanrec == NULL)
661     return;
662
663   silc_hash_table_list(channel->user_list, &htl);
664   while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
665     if (!chu->client->nickname)
666       continue;
667     if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
668       founder = chu->client;
669     silc_nicklist_insert(chanrec, chu, FALSE);
670   }
671   silc_hash_table_list_reset(&htl);
672
673   ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
674   nicklist_set_own(CHANNEL(chanrec), ownnick);
675   signal_emit("channel joined", 1, chanrec);
676
677   if (chanrec->topic)
678     printformat_module("fe-common/silc", server, channel->channel_name,
679                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
680                        channel->channel_name, chanrec->topic);
681
682   fe_channels_nicklist(CHANNEL(chanrec), CHANNEL_NICKLIST_FLAG_ALL);
683
684   if (founder) {
685     if (founder == conn->local_entry)
686       printformat_module("fe-common/silc", 
687                          server, channel->channel_name, MSGLEVEL_CRAP,
688                          SILCTXT_CHANNEL_FOUNDER_YOU,
689                          channel->channel_name);
690     else
691       printformat_module("fe-common/silc", 
692                          server, channel->channel_name, MSGLEVEL_CRAP,
693                          SILCTXT_CHANNEL_FOUNDER,
694                          channel->channel_name, founder->nickname);
695   }
696 }
697
698 typedef struct {
699   SilcClient client;
700   SilcClientConnection conn;
701   void *entry;
702   SilcIdType id_type;
703   char *fingerprint;
704 } *GetkeyContext;
705
706 void silc_getkey_cb(bool success, void *context)
707 {
708   GetkeyContext getkey = (GetkeyContext)context;
709   char *entity = (getkey->id_type == SILC_ID_CLIENT ? "user" : "server");
710   char *name = (getkey->id_type == SILC_ID_CLIENT ? 
711                 ((SilcClientEntry)getkey->entry)->nickname :
712                 ((SilcServerEntry)getkey->entry)->server_name);
713
714   if (success) {
715     printformat_module("fe-common/silc", NULL, NULL,
716                        MSGLEVEL_CRAP, SILCTXT_GETKEY_VERIFIED, entity, name);
717   } else {
718     printformat_module("fe-common/silc", NULL, NULL,
719                        MSGLEVEL_CRAP, SILCTXT_GETKEY_DISCARD, entity, name);
720   }
721
722   silc_free(getkey->fingerprint);
723   silc_free(getkey);
724 }
725
726 /* Command reply handler. This function is called always in the command reply
727    function. If error occurs it will be called as well. Normal scenario
728    is that it will be called after the received command data has been parsed
729    and processed. The function is used to pass the received command data to
730    the application. 
731
732    `conn' is the associated client connection. `cmd_payload' is the command
733    payload data received from server and it can be ignored. It is provided
734    if the application would like to re-parse the received command data,
735    however, it must be noted that the data is parsed already by the library
736    thus the payload can be ignored. `success' is FALSE if error occured.
737    In this case arguments are not sent to the application. `command' is the
738    command reply being processed. The function has variable argument list
739    and each command defines the number and type of arguments it passes to the
740    application (on error they are not sent). */
741
742 void 
743 silc_command_reply(SilcClient client, SilcClientConnection conn,
744                    SilcCommandPayload cmd_payload, int success,
745                    SilcCommand command, SilcCommandStatus status, ...)
746
747 {
748   SILC_SERVER_REC *server = conn->context;
749   SILC_CHANNEL_REC *chanrec;
750   va_list vp;
751
752   va_start(vp, status);
753
754   SILC_LOG_DEBUG(("Start"));
755
756   switch(command) {
757   case SILC_COMMAND_WHOIS:
758     {
759       char buf[1024], *nickname, *username, *realname, *nick;
760       unsigned char *fingerprint;
761       SilcUInt32 idle, mode;
762       SilcBuffer channels;
763       SilcClientEntry client_entry;
764       
765       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
766         /* Print the unknown nick for user */
767         unsigned char *tmp =
768           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
769                                      3, NULL);
770         if (tmp)
771           silc_say_error("%s: %s", tmp, 
772                          silc_client_command_status_message(status));
773         break;
774       } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
775         /* Try to find the entry for the unknown client ID, since we
776            might have, and print the nickname of it for user. */
777         SilcUInt32 tmp_len;
778         unsigned char *tmp =
779           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
780                                      2, &tmp_len);
781         if (tmp) {
782           SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len);
783           if (client_id) {
784             client_entry = silc_client_get_client_by_id(client, conn,
785                                                         client_id);
786             if (client_entry && client_entry->nickname)
787               silc_say_error("%s: %s", client_entry->nickname,
788                              silc_client_command_status_message(status));
789             silc_free(client_id);
790           }
791         }
792         break;
793       }
794       
795       if (!success)
796         return;
797       
798       client_entry = va_arg(vp, SilcClientEntry);
799       nickname = va_arg(vp, char *);
800       username = va_arg(vp, char *);
801       realname = va_arg(vp, char *);
802       channels = va_arg(vp, SilcBuffer);
803       mode = va_arg(vp, SilcUInt32);
804       idle = va_arg(vp, SilcUInt32);
805       fingerprint = va_arg(vp, unsigned char *);
806       
807       silc_parse_userfqdn(nickname, &nick, NULL);
808       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
809                          SILCTXT_WHOIS_USERINFO, nickname, 
810                          client_entry->username, client_entry->hostname,
811                          nick, client_entry->nickname);
812       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
813                          SILCTXT_WHOIS_REALNAME, realname);
814       silc_free(nick);
815
816       if (channels) {
817         SilcDList list = silc_channel_payload_parse_list(channels->data,
818                                                          channels->len);
819         if (list) {
820           SilcChannelPayload entry;
821           memset(buf, 0, sizeof(buf));
822           silc_dlist_start(list);
823           while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
824             char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
825             SilcUInt32 name_len;
826             char *name = silc_channel_get_name(entry, &name_len);
827             
828             if (m)
829               strncat(buf, m, strlen(m));
830             strncat(buf, name, name_len);
831             strncat(buf, " ", 1);
832             silc_free(m);
833           }
834
835           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
836                              SILCTXT_WHOIS_CHANNELS, buf);
837           silc_channel_payload_list_free(list);
838         }
839       }
840       
841       if (mode) {
842         memset(buf, 0, sizeof(buf));
843
844         if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
845             (mode & SILC_UMODE_ROUTER_OPERATOR)) {
846           strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
847                  "Server Operator " :
848                  (mode & SILC_UMODE_ROUTER_OPERATOR) ?
849                  "SILC Operator " : "[Unknown mode] ");
850         }
851         if (mode & SILC_UMODE_GONE)
852           strcat(buf, "away");
853
854         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
855                            SILCTXT_WHOIS_MODES, buf);
856       }
857       
858       if (idle && nickname) {
859         memset(buf, 0, sizeof(buf));
860         snprintf(buf, sizeof(buf) - 1, "%lu %s",
861                  idle > 60 ? (idle / 60) : idle,
862                  idle > 60 ? "minutes" : "seconds");
863
864         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
865                            SILCTXT_WHOIS_IDLE, buf);
866       }
867
868       if (fingerprint) {
869         fingerprint = silc_fingerprint(fingerprint, 20);
870         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
871                            SILCTXT_WHOIS_FINGERPRINT, fingerprint);
872         silc_free(fingerprint);
873       }
874     }
875     break;
876     
877   case SILC_COMMAND_IDENTIFY:
878     {
879       SilcClientEntry client_entry;
880       
881       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
882         /* Print the unknown nick for user */
883         unsigned char *tmp =
884           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
885                                      3, NULL);
886         if (tmp)
887           silc_say_error("%s: %s", tmp, 
888                          silc_client_command_status_message(status));
889         break;
890       } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
891         /* Try to find the entry for the unknown client ID, since we
892            might have, and print the nickname of it for user. */
893         SilcUInt32 tmp_len;
894         unsigned char *tmp =
895           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
896                                      2, &tmp_len);
897         if (tmp) {
898           SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len);
899           if (client_id) {
900             client_entry = silc_client_get_client_by_id(client, conn,
901                                                         client_id);
902             if (client_entry && client_entry->nickname)
903               silc_say_error("%s: %s", client_entry->nickname,
904                              silc_client_command_status_message(status));
905             silc_free(client_id);
906           }
907         }
908         break;
909       }
910
911       break;
912     }
913
914   case SILC_COMMAND_WHOWAS:
915     {
916       char *nickname, *username, *realname;
917       
918       if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
919           status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
920         char *tmp;
921         tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
922                                          3, NULL);
923         if (tmp)
924           silc_say_error("%s: %s", tmp, 
925                          silc_client_command_status_message(status));
926         break;
927       }
928       
929       if (!success)
930         return;
931       
932       (void)va_arg(vp, SilcClientEntry);
933       nickname = va_arg(vp, char *);
934       username = va_arg(vp, char *);
935       realname = va_arg(vp, char *);
936       
937       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
938                          SILCTXT_WHOWAS_USERINFO, nickname, username, 
939                          realname ? realname : "");
940     }
941     break;
942     
943   case SILC_COMMAND_INVITE:
944     {
945       SilcChannelEntry channel;
946       char *invite_list;
947       SilcArgumentPayload args;
948       int argc = 0;
949       
950       if (!success)
951         return;
952       
953       channel = va_arg(vp, SilcChannelEntry);
954       invite_list = va_arg(vp, char *);
955
956       args = silc_command_get_args(cmd_payload);
957       if (args)
958         argc = silc_argument_get_arg_num(args);
959
960       if (invite_list)
961         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
962                            SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
963                            invite_list);
964       else if (argc == 3)
965         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
966                            SILCTXT_CHANNEL_NO_INVITE_LIST, 
967                            channel->channel_name);
968     }
969     break;
970
971   case SILC_COMMAND_JOIN: 
972     {
973       char *channel, *mode, *topic;
974       SilcUInt32 modei;
975       SilcChannelEntry channel_entry;
976       SilcBuffer client_id_list;
977       SilcUInt32 list_count;
978
979       if (!success)
980         return;
981
982       channel = va_arg(vp, char *);
983       channel_entry = va_arg(vp, SilcChannelEntry);
984       modei = va_arg(vp, SilcUInt32);
985       (void)va_arg(vp, SilcUInt32);
986       (void)va_arg(vp, unsigned char *);
987       (void)va_arg(vp, unsigned char *);
988       (void)va_arg(vp, unsigned char *);
989       topic = va_arg(vp, char *);
990       (void)va_arg(vp, unsigned char *);
991       list_count = va_arg(vp, SilcUInt32);
992       client_id_list = va_arg(vp, SilcBuffer);
993
994       chanrec = silc_channel_find(server, channel);
995       if (!chanrec)
996         chanrec = silc_channel_create(server, channel, TRUE);
997
998       if (topic) {
999         g_free_not_null(chanrec->topic);
1000         chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1001         signal_emit("channel topic changed", 1, chanrec);
1002       }
1003
1004       mode = silc_client_chmode(modei, 
1005                                 channel_entry->channel_key ? 
1006                                 channel_entry->channel_key->cipher->name : "",
1007                                 channel_entry->hmac ? 
1008                                 silc_hmac_get_name(channel_entry->hmac) : "");
1009       g_free_not_null(chanrec->mode);
1010       chanrec->mode = g_strdup(mode == NULL ? "" : mode);
1011       signal_emit("channel mode changed", 1, chanrec);
1012
1013       /* Resolve the client information */
1014       silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
1015                                       silc_client_join_get_users, 
1016                                       channel_entry);
1017
1018       break;
1019     }
1020
1021   case SILC_COMMAND_NICK: 
1022     {
1023       SilcClientEntry client = va_arg(vp, SilcClientEntry);
1024       char *old;
1025       
1026       if (!success)
1027         return;
1028
1029       old = g_strdup(server->nick);
1030       server_change_nick(SERVER(server), client->nickname);
1031       nicklist_rename_unique(SERVER(server),
1032                              server->conn->local_entry, server->nick,
1033                              client, client->nickname);
1034       signal_emit("message own_nick", 4, server, server->nick, old, "");
1035       g_free(old);
1036       break;
1037     }
1038     
1039   case SILC_COMMAND_LIST:
1040     {
1041       char *topic, *name;
1042       int usercount;
1043       char users[20];
1044       
1045       if (!success)
1046         return;
1047       
1048       (void)va_arg(vp, SilcChannelEntry);
1049       name = va_arg(vp, char *);
1050       topic = va_arg(vp, char *);
1051       usercount = va_arg(vp, int);
1052       
1053       if (status == SILC_STATUS_LIST_START ||
1054           status == SILC_STATUS_OK)
1055         printformat_module("fe-common/silc", server, NULL,
1056                            MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
1057
1058       if (!usercount)
1059         snprintf(users, sizeof(users) - 1, "N/A");
1060       else
1061         snprintf(users, sizeof(users) - 1, "%d", usercount);
1062       printformat_module("fe-common/silc", server, NULL,
1063                          MSGLEVEL_CRAP, SILCTXT_LIST,
1064                          name, users, topic ? topic : "");
1065     }
1066     break;
1067     
1068   case SILC_COMMAND_UMODE:
1069     {
1070       SilcUInt32 mode;
1071       
1072       if (!success)
1073         return;
1074       
1075       mode = va_arg(vp, SilcUInt32);
1076       
1077       if (mode & SILC_UMODE_SERVER_OPERATOR &&
1078           !(server->umode & SILC_UMODE_SERVER_OPERATOR))
1079         printformat_module("fe-common/silc", server, NULL,
1080                            MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1081
1082       if (mode & SILC_UMODE_ROUTER_OPERATOR &&
1083           !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
1084         printformat_module("fe-common/silc", server, NULL,
1085                            MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1086
1087       server->umode = mode;
1088     }
1089     break;
1090     
1091   case SILC_COMMAND_OPER:
1092     if (!success)
1093       return;
1094
1095     printformat_module("fe-common/silc", server, NULL,
1096                        MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1097     break;
1098     
1099   case SILC_COMMAND_SILCOPER:
1100     if (!success)
1101       return;
1102
1103     printformat_module("fe-common/silc", server, NULL,
1104                        MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1105     break;
1106     
1107   case SILC_COMMAND_USERS: 
1108     {
1109       SilcHashTableList htl;
1110       SilcChannelEntry channel;
1111       SilcChannelUser chu;
1112       
1113       if (!success)
1114         return;
1115       
1116       channel = va_arg(vp, SilcChannelEntry);
1117       
1118       printformat_module("fe-common/silc", server, channel->channel_name,
1119                          MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
1120                          channel->channel_name);
1121
1122       silc_hash_table_list(channel->user_list, &htl);
1123       while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1124         SilcClientEntry e = chu->client;
1125         char stat[5], *mode;
1126
1127         if (!e->nickname)
1128           continue;
1129         
1130         memset(stat, 0, sizeof(stat));
1131         mode = silc_client_chumode_char(chu->mode);
1132         if (e->mode & SILC_UMODE_GONE)
1133           strcat(stat, "G");
1134         else
1135           strcat(stat, "H");
1136         if (mode)
1137           strcat(stat, mode);
1138
1139         printformat_module("fe-common/silc", server, channel->channel_name,
1140                            MSGLEVEL_CRAP, SILCTXT_USERS,
1141                            e->nickname, stat, 
1142                            e->username ? e->username : "",
1143                            e->hostname ? e->hostname : "",
1144                            e->realname ? e->realname : "");
1145         if (mode)
1146           silc_free(mode);
1147       }
1148       silc_hash_table_list_reset(&htl);
1149     }
1150     break;
1151
1152   case SILC_COMMAND_BAN:
1153     {
1154       SilcChannelEntry channel;
1155       char *ban_list;
1156       
1157       if (!success)
1158         return;
1159       
1160       channel = va_arg(vp, SilcChannelEntry);
1161       ban_list = va_arg(vp, char *);
1162       
1163       if (ban_list)
1164         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1165                            SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
1166                            ban_list);
1167       else
1168         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1169                            SILCTXT_CHANNEL_NO_BAN_LIST, 
1170                            channel->channel_name);
1171     }
1172     break;
1173     
1174   case SILC_COMMAND_GETKEY:
1175     {
1176       SilcIdType id_type;
1177       void *entry;
1178       SilcPublicKey public_key;
1179       unsigned char *pk;
1180       SilcUInt32 pk_len;
1181       GetkeyContext getkey;
1182       char *name;
1183       
1184       if (!success)
1185         return;
1186       
1187       id_type = va_arg(vp, SilcUInt32);
1188       entry = va_arg(vp, void *);
1189       public_key = va_arg(vp, SilcPublicKey);
1190
1191       if (public_key) {
1192         pk = silc_pkcs_public_key_encode(public_key, &pk_len);
1193
1194         getkey = silc_calloc(1, sizeof(*getkey));
1195         getkey->entry = entry;
1196         getkey->id_type = id_type;
1197         getkey->client = client;
1198         getkey->conn = conn;
1199         getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1200
1201         name = (id_type == SILC_ID_CLIENT ? 
1202                 ((SilcClientEntry)entry)->nickname :
1203                 ((SilcServerEntry)entry)->server_name);
1204
1205         silc_verify_public_key_internal(client, conn, name,
1206                                         (id_type == SILC_ID_CLIENT ?
1207                                          SILC_SOCKET_TYPE_CLIENT :
1208                                          SILC_SOCKET_TYPE_SERVER),
1209                                         pk, pk_len, SILC_SKE_PK_TYPE_SILC,
1210                                         silc_getkey_cb, getkey);
1211         silc_free(pk);
1212       } else {
1213         printformat_module("fe-common/silc", server, NULL,
1214                            MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
1215       }
1216     }
1217     break;
1218
1219   case SILC_COMMAND_INFO:
1220     {
1221       SilcServerEntry server_entry;
1222       char *server_name;
1223       char *server_info;
1224
1225       if (!success)
1226         return;
1227       
1228       server_entry = va_arg(vp, SilcServerEntry);
1229       server_name = va_arg(vp, char *);
1230       server_info = va_arg(vp, char *);
1231
1232       if (server_name && server_info )
1233         {
1234           printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
1235           printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
1236         }
1237     }
1238     break;
1239     
1240   case SILC_COMMAND_TOPIC:
1241     {
1242       SilcChannelEntry channel;
1243       char *topic;
1244       
1245       if (!success)
1246         return;
1247       
1248       channel = va_arg(vp, SilcChannelEntry);
1249       topic = va_arg(vp, char *);
1250       
1251       if (topic) {
1252         chanrec = silc_channel_find_entry(server, channel);
1253         if (chanrec) {
1254           g_free_not_null(chanrec->topic);
1255           chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1256           signal_emit("channel topic changed", 1, chanrec);
1257         }
1258         printformat_module("fe-common/silc", server, channel->channel_name,
1259                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
1260                            channel->channel_name, topic);
1261       } else {
1262         printformat_module("fe-common/silc", server, channel->channel_name,
1263                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
1264                            channel->channel_name);
1265       }
1266     }
1267     break;
1268
1269   }
1270
1271   va_end(vp);
1272 }
1273
1274 typedef struct {
1275   SilcClient client;
1276   SilcClientConnection conn;
1277   char *filename;
1278   char *entity;
1279   char *entity_name;
1280   unsigned char *pk;
1281   SilcUInt32 pk_len;
1282   SilcSKEPKType pk_type;
1283   SilcVerifyPublicKey completion;
1284   void *context;
1285 } *PublicKeyVerify;
1286
1287 static void verify_public_key_completion(const char *line, void *context)
1288 {
1289   PublicKeyVerify verify = (PublicKeyVerify)context;
1290
1291   if (line[0] == 'Y' || line[0] == 'y') {
1292     /* Call the completion */
1293     if (verify->completion)
1294       verify->completion(TRUE, verify->context);
1295
1296     /* Save the key for future checking */
1297     silc_pkcs_save_public_key_data(verify->filename, verify->pk, 
1298                                    verify->pk_len, SILC_PKCS_FILE_PEM);
1299   } else {
1300     /* Call the completion */
1301     if (verify->completion)
1302       verify->completion(FALSE, verify->context);
1303
1304     printformat_module("fe-common/silc", NULL, NULL,
1305                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, 
1306                        verify->entity_name ? verify->entity_name :
1307                        verify->entity);
1308   }
1309
1310   silc_free(verify->filename);
1311   silc_free(verify->entity);
1312   silc_free(verify->entity_name);
1313   silc_free(verify->pk);
1314   silc_free(verify);
1315 }
1316
1317 /* Internal routine to verify public key. If the `completion' is provided
1318    it will be called to indicate whether public was verified or not. For
1319    server/router public key this will check for filename that includes the
1320    remote host's IP address and remote host's hostname. */
1321
1322 static void 
1323 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
1324                                 const char *name, SilcSocketType conn_type, 
1325                                 unsigned char *pk, SilcUInt32 pk_len, 
1326                                 SilcSKEPKType pk_type,
1327                                 SilcVerifyPublicKey completion, void *context)
1328 {
1329   int i;
1330   char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
1331   char *fingerprint, *babbleprint, *format;
1332   struct passwd *pw;
1333   struct stat st;
1334   char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
1335                    conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
1336                   "server" : "client");
1337   PublicKeyVerify verify;
1338
1339   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
1340     printformat_module("fe-common/silc", NULL, NULL,
1341                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED, 
1342                        entity, pk_type);
1343     if (completion)
1344       completion(FALSE, context);
1345     return;
1346   }
1347
1348   pw = getpwuid(getuid());
1349   if (!pw) {
1350     if (completion)
1351       completion(FALSE, context);
1352     return;
1353   }
1354
1355   memset(filename, 0, sizeof(filename));
1356   memset(filename2, 0, sizeof(filename2));
1357   memset(file, 0, sizeof(file));
1358
1359   if (conn_type == SILC_SOCKET_TYPE_SERVER ||
1360       conn_type == SILC_SOCKET_TYPE_ROUTER) {
1361     if (!name) {
1362       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1363                conn->sock->ip, conn->sock->port);
1364       snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
1365                pw->pw_dir, entity, file);
1366       
1367       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1368                conn->sock->hostname, conn->sock->port);
1369       snprintf(filename2, sizeof(filename2) - 1, "%s/.silc/%skeys/%s", 
1370                pw->pw_dir, entity, file);
1371       
1372       ipf = filename;
1373       hostf = filename2;
1374     } else {
1375       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1376                name, conn->sock->port);
1377       snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
1378                pw->pw_dir, entity, file);
1379       
1380       ipf = filename;
1381     }
1382   } else {
1383     /* Replace all whitespaces with `_'. */
1384     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1385     for (i = 0; i < strlen(fingerprint); i++)
1386       if (fingerprint[i] == ' ')
1387         fingerprint[i] = '_';
1388     
1389     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
1390     snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
1391              pw->pw_dir, entity, file);
1392     silc_free(fingerprint);
1393
1394     ipf = filename;
1395   }
1396
1397   /* Take fingerprint of the public key */
1398   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1399   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
1400
1401   verify = silc_calloc(1, sizeof(*verify));
1402   verify->client = client;
1403   verify->conn = conn;
1404   verify->filename = strdup(ipf);
1405   verify->entity = strdup(entity);
1406   verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ?
1407                          (name ? strdup(name) : strdup(conn->sock->hostname))
1408                          : NULL);
1409   verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
1410   memcpy(verify->pk, pk, pk_len);
1411   verify->pk_len = pk_len;
1412   verify->pk_type = pk_type;
1413   verify->completion = completion;
1414   verify->context = context;
1415
1416   /* Check whether this key already exists */
1417   if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
1418     /* Key does not exist, ask user to verify the key and save it */
1419
1420     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1421                        SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1422                        verify->entity_name : entity);
1423     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1424                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1425     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1426                        SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1427     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1428                              SILCTXT_PUBKEY_ACCEPT);
1429     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1430                             format, 0, verify);
1431     g_free(format);
1432     silc_free(fingerprint);
1433     return;
1434   } else {
1435     /* The key already exists, verify it. */
1436     SilcPublicKey public_key;
1437     unsigned char *encpk;
1438     SilcUInt32 encpk_len;
1439
1440     /* Load the key file, try for both IP filename and hostname filename */
1441     if (!silc_pkcs_load_public_key(ipf, &public_key, 
1442                                    SILC_PKCS_FILE_PEM) &&
1443         !silc_pkcs_load_public_key(ipf, &public_key, 
1444                                    SILC_PKCS_FILE_BIN) &&
1445         (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key, 
1446                                                SILC_PKCS_FILE_PEM) &&
1447                     !silc_pkcs_load_public_key(hostf, &public_key, 
1448                                                SILC_PKCS_FILE_BIN)))) {
1449       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1450                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1451                          verify->entity_name : entity);
1452       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1453                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1454       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1455                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1456       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1457                          SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
1458       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1459                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1460       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1461                               format, 0, verify);
1462       g_free(format);
1463       silc_free(fingerprint);
1464       return;
1465     }
1466
1467     /* Encode the key data */
1468     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
1469     if (!encpk) {
1470       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1471                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1472                          verify->entity_name : entity);
1473       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1474                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1475       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1476                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1477       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1478                          SILCTXT_PUBKEY_MALFORMED, entity);
1479       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1480                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1481       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1482                               format, 0, verify);
1483       g_free(format);
1484       silc_free(fingerprint);
1485       return;
1486     }
1487
1488     /* Compare the keys */
1489     if (memcmp(encpk, pk, encpk_len)) {
1490       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1491                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1492                          verify->entity_name : entity);
1493       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1494                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1495       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1496                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1497       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1498                          SILCTXT_PUBKEY_NO_MATCH, entity);
1499       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1500                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
1501       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1502                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
1503
1504       /* Ask user to verify the key and save it */
1505       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1506                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1507       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1508                               format, 0, verify);
1509       g_free(format);
1510       silc_free(fingerprint);
1511       return;
1512     }
1513
1514     /* Local copy matched */
1515     if (completion)
1516       completion(TRUE, context);
1517     silc_free(fingerprint);
1518   }
1519 }
1520
1521 /* Verifies received public key. The `conn_type' indicates which entity
1522    (server, client etc.) has sent the public key. If user decides to trust
1523    the key may be saved as trusted public key for later use. The 
1524    `completion' must be called after the public key has been verified. */
1525
1526 void 
1527 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
1528                        SilcSocketType conn_type, unsigned char *pk, 
1529                        SilcUInt32 pk_len, SilcSKEPKType pk_type,
1530                        SilcVerifyPublicKey completion, void *context)
1531 {
1532   silc_verify_public_key_internal(client, conn, NULL, conn_type, pk,
1533                                   pk_len, pk_type,
1534                                   completion, context);
1535 }
1536
1537 /* Asks passphrase from user on the input line. */
1538
1539 typedef struct {
1540   SilcAskPassphrase completion;
1541   void *context;
1542 } *AskPassphrase;
1543
1544 void ask_passphrase_completion(const char *passphrase, void *context)
1545 {
1546   AskPassphrase p = (AskPassphrase)context;
1547   p->completion((unsigned char *)passphrase, 
1548                 passphrase ? strlen(passphrase) : 0, p->context);
1549   silc_free(p);
1550 }
1551
1552 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1553                          SilcAskPassphrase completion, void *context)
1554 {
1555   AskPassphrase p = silc_calloc(1, sizeof(*p));
1556   p->completion = completion;
1557   p->context = context;
1558
1559   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
1560                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
1561 }
1562
1563 typedef struct {
1564   SilcGetAuthMeth completion;
1565   void *context;
1566 } *InternalGetAuthMethod;
1567
1568 /* Callback called when we've received the authentication method information
1569    from the server after we've requested it. This will get the authentication
1570    data from the user if needed. */
1571
1572 static void silc_get_auth_method_callback(SilcClient client,
1573                                           SilcClientConnection conn,
1574                                           SilcAuthMethod auth_meth,
1575                                           void *context)
1576 {
1577   InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
1578
1579   SILC_LOG_DEBUG(("Start"));
1580
1581   switch (auth_meth) {
1582   case SILC_AUTH_NONE:
1583     /* No authentication required. */
1584     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1585     break;
1586   case SILC_AUTH_PASSWORD:
1587     /* Do not ask the passphrase from user, the library will ask it if
1588        we do not provide it here. */
1589     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1590     break;
1591   case SILC_AUTH_PUBLIC_KEY:
1592     /* Do not get the authentication data now, the library will generate
1593        it using our default key, if we do not provide it here. */
1594     /* XXX In the future when we support multiple local keys and multiple
1595        local certificates we will need to ask from user which one to use. */
1596     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1597     break;
1598   }
1599
1600   silc_free(internal);
1601 }
1602
1603 /* Find authentication method and authentication data by hostname and
1604    port. The hostname may be IP address as well. The found authentication
1605    method and authentication data is returned to `auth_meth', `auth_data'
1606    and `auth_data_len'. The function returns TRUE if authentication method
1607    is found and FALSE if not. `conn' may be NULL. */
1608
1609 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1610                           char *hostname, SilcUInt16 port,
1611                           SilcGetAuthMeth completion, void *context)
1612 {
1613   InternalGetAuthMethod internal;
1614
1615   SILC_LOG_DEBUG(("Start"));
1616
1617   /* XXX must resolve from configuration whether this connection has
1618      any specific authentication data */
1619
1620   /* If we do not have this connection configured by the user in a
1621      configuration file then resolve the authentication method from the
1622      server for this session. */
1623   internal = silc_calloc(1, sizeof(*internal));
1624   internal->completion = completion;
1625   internal->context = context;
1626
1627   silc_client_request_authentication_method(client, conn, 
1628                                             silc_get_auth_method_callback,
1629                                             internal);
1630 }
1631
1632 /* Notifies application that failure packet was received.  This is called
1633    if there is some protocol active in the client.  The `protocol' is the
1634    protocol context.  The `failure' is opaque pointer to the failure
1635    indication.  Note, that the `failure' is protocol dependant and application
1636    must explicitly cast it to correct type.  Usually `failure' is 32 bit
1637    failure type (see protocol specs for all protocol failure types). */
1638
1639 void silc_failure(SilcClient client, SilcClientConnection conn, 
1640                   SilcProtocol protocol, void *failure)
1641 {
1642   SILC_LOG_DEBUG(("Start"));
1643
1644   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1645     SilcSKEStatus status = (SilcSKEStatus)failure;
1646     
1647     if (status == SILC_SKE_STATUS_BAD_VERSION)
1648       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1649                          SILCTXT_KE_BAD_VERSION);
1650     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1651       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1652                          SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1653     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1654       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1655                          SILCTXT_KE_UNKNOWN_GROUP);
1656     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1657       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1658                          SILCTXT_KE_UNKNOWN_CIPHER);
1659     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1660       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1661                          SILCTXT_KE_UNKNOWN_PKCS);
1662     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1663       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1664                          SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1665     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1666       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1667                          SILCTXT_KE_UNKNOWN_HMAC);
1668     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1669       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1670                          SILCTXT_KE_INCORRECT_SIGNATURE);
1671     if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1672       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1673                          SILCTXT_KE_INVALID_COOKIE);
1674   }
1675
1676   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1677     SilcUInt32 err = (SilcUInt32)failure;
1678
1679     if (err == SILC_AUTH_FAILED)
1680       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1681                          SILCTXT_AUTH_FAILED);
1682   }
1683 }
1684
1685 /* Asks whether the user would like to perform the key agreement protocol.
1686    This is called after we have received an key agreement packet or an
1687    reply to our key agreement packet. This returns TRUE if the user wants
1688    the library to perform the key agreement protocol and FALSE if it is not
1689    desired (application may start it later by calling the function
1690    silc_client_perform_key_agreement). */
1691
1692 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1693                        SilcClientEntry client_entry, const char *hostname,
1694                        SilcUInt16 port, SilcKeyAgreementCallback *completion,
1695                        void **context)
1696 {
1697   char portstr[12];
1698
1699   SILC_LOG_DEBUG(("Start"));
1700
1701   /* We will just display the info on the screen and return FALSE and user
1702      will have to start the key agreement with a command. */
1703
1704   if (hostname) 
1705     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1706
1707   if (!hostname)
1708     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1709                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1710   else
1711     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1712                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST, 
1713                        client_entry->nickname, hostname, portstr);
1714
1715   *completion = NULL;
1716   *context = NULL;
1717
1718   return FALSE;
1719 }
1720
1721 void silc_ftp(SilcClient client, SilcClientConnection conn,
1722               SilcClientEntry client_entry, SilcUInt32 session_id,
1723               const char *hostname, SilcUInt16 port)
1724 {
1725   SILC_SERVER_REC *server;
1726   char portstr[12];
1727   FtpSession ftp = silc_calloc(1, sizeof(*ftp));
1728
1729   SILC_LOG_DEBUG(("Start"));
1730
1731   server = conn->context;
1732
1733   ftp->client_entry = client_entry;
1734   ftp->session_id = session_id;
1735   ftp->send = FALSE;
1736   ftp->conn = conn;
1737   silc_dlist_add(server->ftp_sessions, ftp);
1738   server->current_session = ftp;
1739
1740   if (hostname) 
1741     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1742
1743   if (!hostname)
1744     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1745                        SILCTXT_FILE_REQUEST, client_entry->nickname);
1746   else
1747     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1748                        SILCTXT_FILE_REQUEST_HOST, 
1749                        client_entry->nickname, hostname, portstr);
1750 }
1751
1752 /* SILC client operations */
1753 SilcClientOperations ops = {
1754   silc_say,
1755   silc_channel_message,
1756   silc_private_message,
1757   silc_notify,
1758   silc_command,
1759   silc_command_reply,
1760   silc_connect,
1761   silc_disconnect,
1762   silc_get_auth_method,
1763   silc_verify_public_key,
1764   silc_ask_passphrase,
1765   silc_failure,
1766   silc_key_agreement,
1767   silc_ftp,
1768 };