verify_message_signature returned FAILED when the public key was not locally
[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 "ignore.h"
37 #include "fe-common/core/printtext.h"
38 #include "fe-common/core/fe-channels.h"
39 #include "fe-common/core/keyboard.h"
40 #include "fe-common/core/window-items.h"
41 #include "fe-common/silc/module-formats.h"
42
43 #include "core.h"
44
45 static void 
46 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
47                                 const char *name, SilcSocketType conn_type, 
48                                 unsigned char *pk, SilcUInt32 pk_len, 
49                                 SilcSKEPKType pk_type,
50                                 SilcVerifyPublicKey completion, void *context);
51
52 static void silc_get_umode_string(SilcUInt32 mode, char *buf, 
53                                   SilcUInt32 buf_size)
54 {
55   if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
56       (mode & SILC_UMODE_ROUTER_OPERATOR)) {
57     strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
58            "[server operator]" :
59            (mode & SILC_UMODE_ROUTER_OPERATOR) ?
60            "[SILC operator]" : "[unknown mode]");
61   }
62   if (mode & SILC_UMODE_GONE)
63     strcat(buf, " [away]");
64   if (mode & SILC_UMODE_INDISPOSED)
65     strcat(buf, " [indisposed]");
66   if (mode & SILC_UMODE_BUSY)
67     strcat(buf, " [busy]");
68   if (mode & SILC_UMODE_PAGE)
69     strcat(buf, " [page to reach]");
70   if (mode & SILC_UMODE_HYPER)
71     strcat(buf, " [hyper active]");
72   if (mode & SILC_UMODE_ROBOT)
73     strcat(buf, " [robot]");
74   if (mode & SILC_UMODE_ANONYMOUS)
75     strcat(buf, " [anonymous]");
76   if (mode & SILC_UMODE_BLOCK_PRIVMSG)
77     strcat(buf, " [blocks private messages]");
78   if (mode & SILC_UMODE_DETACHED)
79     strcat(buf, " [detached]");
80   if (mode & SILC_UMODE_REJECT_WATCHING)
81     strcat(buf, " [rejects watching]");
82   if (mode & SILC_UMODE_BLOCK_INVITE)
83     strcat(buf, " [blocks invites]");
84 }
85
86 /* print "nick appears as" message to every channel of a server */
87 static void 
88 silc_print_nick_change_channel(SILC_SERVER_REC *server, const char *channel,
89                               const char *newnick, const char *oldnick,
90                               const char *address)
91 {
92   if (ignore_check(SERVER(server), oldnick, address,
93                    channel, newnick, MSGLEVEL_NICKS))
94     return;
95   
96   printformat_module("fe-common/silc", server, channel, MSGLEVEL_NICKS,
97                      SILCTXT_CHANNEL_APPEARS,
98                      oldnick, newnick, channel, address);
99 }
100
101 static void
102 silc_print_nick_change(SILC_SERVER_REC *server, const char *newnick,
103                        const char *oldnick, const char *address)
104 {
105   GSList *tmp, *windows;
106
107   /* Print to each channel/query where the nick is.
108      Don't print more than once to the same window. */
109   windows = NULL;
110     
111   for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
112     CHANNEL_REC *channel = tmp->data;
113     WINDOW_REC *window = window_item_window((WI_ITEM_REC *) channel);
114
115     if (nicklist_find(channel, newnick) == NULL ||
116         g_slist_find(windows, window) != NULL)
117       continue;
118
119     windows = g_slist_append(windows, window);
120     silc_print_nick_change_channel(server, channel->visible_name,
121                                    newnick, oldnick, address);
122   }
123
124   g_slist_free(windows);
125 }
126
127 void silc_say(SilcClient client, SilcClientConnection conn,
128               SilcClientMessageType type, char *msg, ...)
129 {
130   SILC_SERVER_REC *server;
131   va_list va;
132   char *str;
133
134   server = conn == NULL ? NULL : conn->context;
135   
136   va_start(va, msg);
137   str = g_strdup_vprintf(msg, va);
138   printtext(server, NULL, MSGLEVEL_CRAP, "%s", str);
139   g_free(str);
140   va_end(va);
141 }
142
143 void silc_say_error(char *msg, ...)
144 {
145   va_list va;
146   char *str;
147
148   va_start(va, msg);
149   str = g_strdup_vprintf(msg, va);
150   printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str);
151
152   g_free(str);
153   va_end(va);
154 }
155
156 /* try to verify a message using locally stored public key data */
157 int verify_message_signature(SilcClientEntry sender,
158                              SilcMessageSignedPayload sig,
159                              SilcMessagePayload message)
160 {
161   SilcPublicKey pk;
162   char file[256], filename[256];
163   char *fingerprint, *fingerprint2;
164   unsigned char *pk_data;
165   SilcUInt32 pk_datalen;
166   struct stat st;
167   int ret = SILC_MSG_SIGNED_VERIFIED, i;
168
169   if (sig == NULL)
170     return SILC_MSG_SIGNED_UNKNOWN;
171
172   /* get public key from the signature payload and compare it with the
173      one stored in the client entry */
174   pk = silc_message_signed_get_public_key(sig, &pk_data, &pk_datalen);
175
176   if (pk != NULL) {
177     fingerprint = silc_hash_fingerprint(NULL, pk_data, pk_datalen);
178
179     if (sender->fingerprint) {
180       fingerprint2 = silc_fingerprint(sender->fingerprint,
181                                       sender->fingerprint_len);
182       if (strcmp(fingerprint, fingerprint2)) {
183         /* since the public key differs from the senders public key, the
184            verification _failed_ */
185         silc_pkcs_public_key_free(pk);
186         silc_free(fingerprint);
187         ret = SILC_MSG_SIGNED_UNKNOWN;
188       }
189       silc_free(fingerprint2);
190     }
191   } else if (sender->fingerprint)
192     fingerprint = silc_fingerprint(sender->fingerprint,
193                                    sender->fingerprint_len);
194   else
195     /* no idea, who or what signed that message ... */
196     return SILC_MSG_SIGNED_UNKNOWN;
197   
198   /* search our local client key cache */
199   for (i = 0; i < strlen(fingerprint); i++)
200     if (fingerprint[i] == ' ')
201       fingerprint[i] = '_';
202     
203   snprintf(file, sizeof(file) - 1, "clientkey_%s.pub", fingerprint);
204   snprintf(filename, sizeof(filename) - 1, "%s/clientkeys/%s", 
205            get_irssi_dir(), file);
206   silc_free(fingerprint);
207   
208   if (stat(filename, &st) < 0)
209     /* we don't have the public key cached ... use the one from the sig */
210     ret = SILC_MSG_SIGNED_UNKNOWN;
211   else {
212     SilcPublicKey cached_pk=NULL;
213
214     /* try to load the file */
215     if (!silc_pkcs_load_public_key(filename, &cached_pk, SILC_PKCS_FILE_PEM) &&
216         !silc_pkcs_load_public_key(filename, &cached_pk, 
217                                    SILC_PKCS_FILE_BIN)) {
218       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
219                          SILCTXT_PUBKEY_COULD_NOT_LOAD, "client");
220       if (pk == NULL)
221         return SILC_MSG_SIGNED_UNKNOWN;
222       else
223         ret = SILC_MSG_SIGNED_UNKNOWN;
224     }
225
226     if (cached_pk) {
227       if (pk)
228         silc_pkcs_public_key_free(pk);
229       pk = cached_pk; 
230     }
231   }
232
233   /* the public key is now in pk, our "level of trust" in ret */
234   if ((pk) && silc_message_signed_verify(sig, message, pk, 
235                                          silc_client->sha1hash)!= SILC_AUTH_OK)
236     ret = SILC_MSG_SIGNED_FAILED;
237
238   if (pk)
239     silc_pkcs_public_key_free(pk);
240
241   return ret;
242 }
243
244 /* Message for a channel. The `sender' is the nickname of the sender 
245    received in the packet. The `channel_name' is the name of the channel. */
246
247 void silc_channel_message(SilcClient client, SilcClientConnection conn,
248                           SilcClientEntry sender, SilcChannelEntry channel,
249                           SilcMessagePayload payload,
250                           SilcMessageFlags flags, const unsigned char *message,
251                           SilcUInt32 message_len)
252 {
253   SILC_SERVER_REC *server;
254   SILC_NICK_REC *nick;
255   SILC_CHANNEL_REC *chanrec;
256   int verified = 0;
257   
258   SILC_LOG_DEBUG(("Start"));
259
260   if (!message)
261     return;
262
263   server = conn == NULL ? NULL : conn->context;
264   chanrec = silc_channel_find_entry(server, channel);
265   if (!chanrec)
266     return;
267   
268   nick = silc_nicklist_find(chanrec, sender);
269   if (!nick) {
270     /* We didn't find client but it clearly exists, add it. */
271     SilcChannelUser chu = silc_client_on_channel(channel, sender);
272     if (chu)
273       nick = silc_nicklist_insert(chanrec, chu, FALSE);
274   }
275
276   /* If the messages is digitally signed, verify it, if possible. */
277   if (flags & SILC_MESSAGE_FLAG_SIGNED) {
278     if (!settings_get_bool("ignore_message_signatures")) {
279       SilcMessageSignedPayload sig = silc_message_get_signature(payload);
280       verified = verify_message_signature(sender, sig, payload);
281     } else {
282       flags &= ~SILC_MESSAGE_FLAG_SIGNED;
283     }
284   }
285   
286   if (flags & SILC_MESSAGE_FLAG_DATA) {
287     /* MIME object received, try to display it as well as we can */
288     char type[128], enc[128];
289     unsigned char *data;
290     SilcUInt32 data_len;
291
292     memset(type, 0, sizeof(type));
293     memset(enc, 0, sizeof(enc));
294     if (!silc_mime_parse(message, message_len, NULL, 0, type, sizeof(type) - 1,
295                          enc, sizeof(enc) - 1, &data, &data_len))
296       return;
297
298     /* Then figure out what we can display */
299     if (strstr(type, "text/") && !strstr(type, "text/t140") &&
300         !strstr(type, "text/vnd")) {
301       /* It is something textual, display it */
302       message = (const unsigned char *)data;
303     } else {
304       printformat_module("fe-common/silc", server, channel->channel_name,
305                          MSGLEVEL_CRAP, SILCTXT_MESSAGE_DATA,
306                          nick == NULL ? "[<unknown>]" : nick->nick, type);
307       message = NULL;
308     }
309   }
310
311   if (!message)
312     return;
313
314   /* FIXME: replace those printformat calls with signals and add signature
315             information to them (if present) */
316   if (flags & SILC_MESSAGE_FLAG_ACTION)
317     printformat_module("fe-common/silc", server, channel->channel_name,
318                        MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_ACTION, 
319                        nick == NULL ? "[<unknown>]" : nick->nick, message);
320   else if (flags & SILC_MESSAGE_FLAG_NOTICE)
321     printformat_module("fe-common/silc", server, channel->channel_name,
322                        MSGLEVEL_NOTICES, SILCTXT_CHANNEL_NOTICE, 
323                        nick == NULL ? "[<unknown>]" : nick->nick, message);
324   else {
325     if (flags & SILC_MESSAGE_FLAG_UTF8 && !silc_term_utf8()) {
326       char tmp[256], *cp, *dm = NULL;
327
328       memset(tmp, 0, sizeof(tmp));
329       cp = tmp;
330       if (message_len > sizeof(tmp) - 1) {
331         dm = silc_calloc(message_len + 1, sizeof(*dm));
332         cp = dm;
333       }
334
335       silc_utf8_decode(message, message_len, SILC_STRING_LANGUAGE,
336                        cp, message_len);
337       if (flags & SILC_MESSAGE_FLAG_SIGNED)
338         signal_emit("message signed_public", 6, server, cp,
339                     nick == NULL ? "[<unknown>]" : nick->nick,
340                     nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
341                     chanrec->name, verified);
342       else
343         signal_emit("message public", 6, server, cp,
344                     nick == NULL ? "[<unknown>]" : nick->nick,
345                     nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
346                     chanrec->name, nick);
347       silc_free(dm);
348       return;
349     }
350
351     if (flags & SILC_MESSAGE_FLAG_SIGNED)
352       signal_emit("message signed_public", 6, server, message,
353                   nick == NULL ? "[<unknown>]" : nick->nick,
354                   nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
355                   chanrec->name, verified);
356     else
357       signal_emit("message public", 6, server, message,
358                   nick == NULL ? "[<unknown>]" : nick->nick,
359                   nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
360                   chanrec->name, nick);
361   }
362 }
363
364 /* Private message to the client. The `sender' is the nickname of the
365    sender received in the packet. */
366
367 void silc_private_message(SilcClient client, SilcClientConnection conn,
368                           SilcClientEntry sender, SilcMessagePayload payload,
369                           SilcMessageFlags flags,
370                           const unsigned char *message,
371                           SilcUInt32 message_len)
372 {
373   SILC_SERVER_REC *server;
374   char userhost[256];
375   int verified = 0;
376   
377   SILC_LOG_DEBUG(("Start"));
378
379   server = conn == NULL ? NULL : conn->context;
380   memset(userhost, 0, sizeof(userhost));
381   if (sender->username)
382     snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
383              sender->username, sender->hostname);
384
385   /* If the messages is digitally signed, verify it, if possible. */
386   if (flags & SILC_MESSAGE_FLAG_SIGNED) {
387     if (!settings_get_bool("ignore_message_signatures")) {
388       SilcMessageSignedPayload sig = silc_message_get_signature(payload);
389       verified = verify_message_signature(sender, sig, payload);
390     } else {
391       flags &= ~SILC_MESSAGE_FLAG_SIGNED;
392     }
393   }
394   
395   if (flags & SILC_MESSAGE_FLAG_DATA) {
396     /* MIME object received, try to display it as well as we can */
397     char type[128], enc[128];
398     unsigned char *data;
399     SilcUInt32 data_len;
400
401     memset(type, 0, sizeof(type));
402     memset(enc, 0, sizeof(enc));
403     if (!silc_mime_parse(message, message_len, NULL, 0, type, sizeof(type) - 1,
404                          enc, sizeof(enc) - 1, &data, &data_len))
405       return;
406
407     /* Then figure out what we can display */
408     if (strstr(type, "text/") && !strstr(type, "text/t140") &&
409         !strstr(type, "text/vnd")) {
410       /* It is something textual, display it */
411       message = (const unsigned char *)data;
412     } else {
413       printformat_module("fe-common/silc", server, NULL,
414                          MSGLEVEL_CRAP, SILCTXT_MESSAGE_DATA,
415                          sender->nickname ? sender->nickname : "[<unknown>]",
416                          type);
417       message = NULL;
418     }
419   }
420
421   if (!message)
422     return;
423
424   if (flags & SILC_MESSAGE_FLAG_UTF8 && !silc_term_utf8()) {
425     char tmp[256], *cp, *dm = NULL;
426
427     memset(tmp, 0, sizeof(tmp));
428     cp = tmp;
429     if (message_len > sizeof(tmp) - 1) {
430       dm = silc_calloc(message_len + 1, sizeof(*dm));
431       cp = dm;
432     }
433
434     silc_utf8_decode(message, message_len, SILC_STRING_LANGUAGE,
435                      cp, message_len);
436     if (flags & SILC_MESSAGE_FLAG_SIGNED)
437       signal_emit("message signed_private", 5, server, cp,
438                   sender->nickname ? sender->nickname : "[<unknown>]",
439                   sender->username ? userhost : NULL, verified);
440     else
441       signal_emit("message private", 4, server, cp,
442                   sender->nickname ? sender->nickname : "[<unknown>]",
443                   sender->username ? userhost : NULL);
444     silc_free(dm);
445     return;
446   }
447
448   if (flags & SILC_MESSAGE_FLAG_SIGNED)
449     signal_emit("message signed_private", 5, server, message,
450                 sender->nickname ? sender->nickname : "[<unknown>]",
451                 sender->username ? userhost : NULL, verified);
452   else
453     signal_emit("message private", 4, server, message,
454                 sender->nickname ? sender->nickname : "[<unknown>]",
455                 sender->username ? userhost : NULL);
456 }
457
458 /* Notify message to the client. The notify arguments are sent in the
459    same order as servers sends them. The arguments are same as received
460    from the server except for ID's.  If ID is received application receives
461    the corresponding entry to the ID. For example, if Client ID is received
462    application receives SilcClientEntry.  Also, if the notify type is
463    for channel the channel entry is sent to application (even if server
464    does not send it). */
465
466 void silc_notify(SilcClient client, SilcClientConnection conn,
467                  SilcNotifyType type, ...)
468 {
469   va_list va;
470   SILC_SERVER_REC *server;
471   SILC_CHANNEL_REC *chanrec;
472   SILC_NICK_REC *nickrec;
473   SilcClientEntry client_entry, client_entry2;
474   SilcChannelEntry channel, channel2;
475   SilcServerEntry server_entry;
476   SilcIdType idtype;
477   void *entry;
478   SilcUInt32 mode;
479   char buf[512];
480   char *name, *tmp;
481   GSList *list1, *list_tmp;
482
483   SILC_LOG_DEBUG(("Start"));
484
485   va_start(va, type);
486
487   server = conn == NULL ? NULL : conn->context;
488   
489   switch(type) {
490   case SILC_NOTIFY_TYPE_NONE:
491     /* Some generic notice from server */
492     printtext(server, NULL, MSGLEVEL_CRAP, "%s", (char *)va_arg(va, char *));
493     break;
494
495   case SILC_NOTIFY_TYPE_INVITE:
496     /*
497      * Invited or modified invite list.
498      */
499
500     SILC_LOG_DEBUG(("Notify: INVITE"));
501
502     channel = va_arg(va, SilcChannelEntry);
503     name = va_arg(va, char *);
504     client_entry = va_arg(va, SilcClientEntry);
505
506     memset(buf, 0, sizeof(buf));
507     snprintf(buf, sizeof(buf) - 1, "%s@%s",
508              client_entry->username, client_entry->hostname);
509     signal_emit("message invite", 4, server, channel ? channel->channel_name :
510                 name, client_entry->nickname, buf);
511     break;
512
513   case SILC_NOTIFY_TYPE_JOIN:
514     /*
515      * Joined channel.
516      */
517  
518     SILC_LOG_DEBUG(("Notify: JOIN"));
519
520     client_entry = va_arg(va, SilcClientEntry);
521     channel = va_arg(va, SilcChannelEntry);
522
523     if (client_entry == server->conn->local_entry) {
524       /* You joined to channel */
525       chanrec = silc_channel_find(server, channel->channel_name);
526       if (chanrec != NULL && !chanrec->joined)
527         chanrec->entry = channel;
528     } else {
529       chanrec = silc_channel_find_entry(server, channel);
530       if (chanrec != NULL) {
531         SilcChannelUser chu = silc_client_on_channel(channel, client_entry);
532         if (chu)
533           nickrec = silc_nicklist_insert(chanrec, chu, TRUE);
534       }
535     }
536     
537     memset(buf, 0, sizeof(buf));
538     if (client_entry->username)
539     snprintf(buf, sizeof(buf) - 1, "%s@%s",
540              client_entry->username, client_entry->hostname);
541     signal_emit("message join", 4, server, channel->channel_name,
542                 client_entry->nickname,
543                 client_entry->username == NULL ? "" : buf);
544     break;
545
546   case SILC_NOTIFY_TYPE_LEAVE:
547     /*
548      * Left a channel.
549      */
550
551     SILC_LOG_DEBUG(("Notify: LEAVE"));
552
553     client_entry = va_arg(va, SilcClientEntry);
554     channel = va_arg(va, SilcChannelEntry);
555     
556     memset(buf, 0, sizeof(buf));
557     if (client_entry->username)
558       snprintf(buf, sizeof(buf) - 1, "%s@%s",
559                client_entry->username, client_entry->hostname);
560     signal_emit("message part", 5, server, channel->channel_name,
561                 client_entry->nickname,  client_entry->username ? 
562                 buf : "", client_entry->nickname);
563     
564     chanrec = silc_channel_find_entry(server, channel);
565     if (chanrec != NULL) {
566       nickrec = silc_nicklist_find(chanrec, client_entry);
567       if (nickrec != NULL)
568         nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
569     }
570     break;
571
572   case SILC_NOTIFY_TYPE_SIGNOFF:
573     /*
574      * Left the network.
575      */
576
577     SILC_LOG_DEBUG(("Notify: SIGNOFF"));
578
579     client_entry = va_arg(va, SilcClientEntry);
580     tmp = va_arg(va, char *);
581     
582     silc_server_free_ftp(server, client_entry);
583     
584     memset(buf, 0, sizeof(buf));
585     if (client_entry->username)
586       snprintf(buf, sizeof(buf) - 1, "%s@%s",
587                client_entry->username, client_entry->hostname);
588     signal_emit("message quit", 4, server, client_entry->nickname,
589                 client_entry->username ? buf : "", 
590                 tmp ? tmp : "");
591     
592     list1 = nicklist_get_same_unique(SERVER(server), client_entry);
593     for (list_tmp = list1; list_tmp != NULL; list_tmp = 
594            list_tmp->next->next) {
595       CHANNEL_REC *channel = list_tmp->data;
596       NICK_REC *nickrec = list_tmp->next->data;
597       
598       nicklist_remove(channel, nickrec);
599     }
600     break;
601
602   case SILC_NOTIFY_TYPE_TOPIC_SET:
603     /*
604      * Changed topic.
605      */
606
607     SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
608
609     idtype = va_arg(va, int);
610     entry = va_arg(va, void *);
611     tmp = va_arg(va, char *);
612     channel = va_arg(va, SilcChannelEntry);
613     
614     chanrec = silc_channel_find_entry(server, channel);
615     if (chanrec != NULL) {
616       char tmp2[256], *cp, *dm = NULL;
617
618       g_free_not_null(chanrec->topic);
619       if (tmp && !silc_term_utf8() && silc_utf8_valid(tmp, strlen(tmp))) {
620         memset(tmp2, 0, sizeof(tmp2));
621         cp = tmp2;
622         if (strlen(tmp) > sizeof(tmp2) - 1) {
623           dm = silc_calloc(strlen(tmp) + 1, sizeof(*dm));
624           cp = dm;
625         }
626
627         silc_utf8_decode(tmp, strlen(tmp), SILC_STRING_LANGUAGE,
628                          cp, strlen(tmp));
629         tmp = cp;
630       }
631
632       chanrec->topic = *tmp == '\0' ? NULL : g_strdup(tmp);
633       signal_emit("channel topic changed", 1, chanrec);
634
635       silc_free(dm);
636     }
637     
638     if (idtype == SILC_ID_CLIENT) {
639       client_entry = (SilcClientEntry)entry;
640       memset(buf, 0, sizeof(buf));
641       snprintf(buf, sizeof(buf) - 1, "%s@%s",
642                client_entry->username, client_entry->hostname);
643       signal_emit("message topic", 5, server, channel->channel_name,
644                   tmp, client_entry->nickname, buf);
645     } else if (idtype == SILC_ID_SERVER) {
646       server_entry = (SilcServerEntry)entry;
647       signal_emit("message topic", 5, server, channel->channel_name,
648                   tmp, server_entry->server_name, 
649                   server_entry->server_name);
650     } else if (idtype == SILC_ID_CHANNEL) {
651       channel = (SilcChannelEntry)entry;
652       signal_emit("message topic", 5, server, channel->channel_name,
653                   tmp, channel->channel_name, channel->channel_name);
654     }
655     break;
656
657   case SILC_NOTIFY_TYPE_NICK_CHANGE:
658     /*
659      * Changed nickname.
660      */
661
662     SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
663
664     client_entry = va_arg(va, SilcClientEntry);
665     client_entry2 = va_arg(va, SilcClientEntry);
666
667     if (!strcmp(client_entry->nickname, client_entry2->nickname))
668       break;
669     
670     memset(buf, 0, sizeof(buf));
671     snprintf(buf, sizeof(buf) - 1, "%s@%s",
672              client_entry2->username, client_entry2->hostname);
673     nicklist_rename_unique(SERVER(server),
674                            client_entry, client_entry->nickname,
675                            client_entry2, client_entry2->nickname);
676     signal_emit("message nick", 4, server, client_entry2->nickname, 
677                 client_entry->nickname, buf);
678     break;
679
680   case SILC_NOTIFY_TYPE_CMODE_CHANGE:
681     /*
682      * Changed channel mode.
683      */
684
685     SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
686
687     idtype = va_arg(va, int);
688     entry = va_arg(va, void *);
689     mode = va_arg(va, SilcUInt32);
690     (void)va_arg(va, char *);
691     (void)va_arg(va, char *);
692     channel = va_arg(va, SilcChannelEntry);
693
694     tmp = silc_client_chmode(mode,
695                              channel->channel_key ? 
696                              silc_cipher_get_name(channel->channel_key) : "",
697                              channel->hmac ? 
698                              silc_hmac_get_name(channel->hmac) : "");
699     
700     chanrec = silc_channel_find_entry(server, channel);
701     if (chanrec != NULL) {
702       g_free_not_null(chanrec->mode);
703       chanrec->mode = g_strdup(tmp == NULL ? "" : tmp);
704       signal_emit("channel mode changed", 1, chanrec);
705     }
706     
707     if (idtype == SILC_ID_CLIENT) {
708       client_entry = (SilcClientEntry)entry;
709       printformat_module("fe-common/silc", server, channel->channel_name,
710                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
711                          channel->channel_name, tmp ? tmp : "removed all",
712                          client_entry->nickname);
713     } else if (idtype == SILC_ID_SERVER) {
714       server_entry = (SilcServerEntry)entry;
715       printformat_module("fe-common/silc", server, channel->channel_name,
716                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
717                          channel->channel_name, tmp ? tmp : "removed all",
718                          server_entry->server_name);
719     } else if (idtype == SILC_ID_CHANNEL) {
720       channel2 = (SilcChannelEntry)entry;
721       printformat_module("fe-common/silc", server, channel->channel_name,
722                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
723                          channel->channel_name, tmp ? tmp : "removed all",
724                          channel2->channel_name);
725     }
726
727     silc_free(tmp);
728     break;
729
730   case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
731     /*
732      * Changed user's mode on channel.
733      */
734
735     SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
736
737     idtype = va_arg(va, int);
738     entry = va_arg(va, void *);
739     mode = va_arg(va, SilcUInt32);
740     client_entry2 = va_arg(va, SilcClientEntry);
741     channel = va_arg(va, SilcChannelEntry);
742
743     tmp = silc_client_chumode(mode);
744     chanrec = silc_channel_find_entry(server, channel);
745     if (chanrec != NULL) {
746       SILC_NICK_REC *nick;
747
748       if (client_entry2 == server->conn->local_entry)
749         chanrec->chanop = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
750       
751       nick = silc_nicklist_find(chanrec, client_entry2);
752       if (nick != NULL) {
753         nick->op = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
754         nick->founder = (mode & SILC_CHANNEL_UMODE_CHANFO) != 0;
755         signal_emit("nick mode changed", 2, chanrec, nick);
756       }
757     }
758
759     if (idtype == SILC_ID_CLIENT) {
760       client_entry = (SilcClientEntry)entry;
761       printformat_module("fe-common/silc", server, channel->channel_name,
762                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
763                          channel->channel_name, client_entry2->nickname, 
764                          tmp ? tmp : "removed all",
765                          client_entry->nickname);
766     } else if (idtype == SILC_ID_SERVER) {
767       server_entry = (SilcServerEntry)entry;
768       printformat_module("fe-common/silc", server, channel->channel_name,
769                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
770                          channel->channel_name, client_entry2->nickname, 
771                          tmp ? tmp : "removed all",
772                          server_entry->server_name);
773     } else if (idtype == SILC_ID_CHANNEL) {
774       channel2 = (SilcChannelEntry)entry;
775       printformat_module("fe-common/silc", server, channel->channel_name,
776                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
777                          channel->channel_name, client_entry2->nickname, 
778                          tmp ? tmp : "removed all",
779                          channel2->channel_name);
780     }
781
782     if (mode & SILC_CHANNEL_UMODE_CHANFO)
783       printformat_module("fe-common/silc", 
784                          server, channel->channel_name, MSGLEVEL_CRAP,
785                          SILCTXT_CHANNEL_FOUNDER,
786                          channel->channel_name, client_entry2->nickname);
787
788     if (mode & SILC_CHANNEL_UMODE_QUIET && conn->local_entry == client_entry2)
789       printformat_module("fe-common/silc", 
790                          server, channel->channel_name, MSGLEVEL_CRAP,
791                          SILCTXT_CHANNEL_QUIETED, channel->channel_name);
792
793     silc_free(tmp);
794     break;
795
796   case SILC_NOTIFY_TYPE_MOTD:
797     /*
798      * Received MOTD.
799      */
800
801     SILC_LOG_DEBUG(("Notify: MOTD"));
802
803     tmp = va_arg(va, char *);
804
805     if (!settings_get_bool("skip_motd"))
806       printtext_multiline(server, NULL, MSGLEVEL_CRAP, "%s", tmp);
807     break;
808
809   case SILC_NOTIFY_TYPE_KICKED:
810     /*
811      * Someone was kicked from channel.
812      */
813
814     SILC_LOG_DEBUG(("Notify: KICKED"));
815
816     client_entry = va_arg(va, SilcClientEntry);
817     tmp = va_arg(va, char *);
818     client_entry2 = va_arg(va, SilcClientEntry);
819     channel = va_arg(va, SilcChannelEntry);
820
821     chanrec = silc_channel_find_entry(server, channel);
822   
823     if (client_entry == conn->local_entry) {
824       printformat_module("fe-common/silc", server, channel->channel_name,
825                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED_YOU, 
826                          channel->channel_name, 
827                          client_entry ? client_entry2->nickname : "",
828                          tmp ? tmp : "");
829       if (chanrec) {
830         chanrec->kicked = TRUE;
831         channel_destroy((CHANNEL_REC *)chanrec);
832       }
833     } else {
834       printformat_module("fe-common/silc", server, channel->channel_name,
835                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED, 
836                          client_entry->nickname, channel->channel_name, 
837                          client_entry2 ? client_entry2->nickname : "", 
838                          tmp ? tmp : "");
839
840       if (chanrec) {
841         SILC_NICK_REC *nickrec = silc_nicklist_find(chanrec, client_entry);
842         if (nickrec != NULL)
843           nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
844       }
845     }
846     break;
847
848   case SILC_NOTIFY_TYPE_KILLED:
849     /*
850      * Someone was killed from the network.
851      */
852
853     SILC_LOG_DEBUG(("Notify: KILLED"));
854
855     client_entry = va_arg(va, SilcClientEntry);
856     tmp = va_arg(va, char *);
857     idtype = va_arg(va, int);
858     entry = va_arg(va, SilcClientEntry);
859   
860     if (client_entry == conn->local_entry) {
861       if (idtype == SILC_ID_CLIENT) {
862         client_entry2 = (SilcClientEntry)entry;
863         printformat_module("fe-common/silc", server, NULL,
864                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU, 
865                            client_entry2 ? client_entry2->nickname : "",
866                            tmp ? tmp : "");
867       } else if (idtype == SILC_ID_SERVER) {
868         server_entry = (SilcServerEntry)entry;
869         printformat_module("fe-common/silc", server, NULL,
870                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU, 
871                            server_entry->server_name, tmp ? tmp : "");
872       } else if (idtype == SILC_ID_CHANNEL) {
873         channel = (SilcChannelEntry)entry;
874         printformat_module("fe-common/silc", server, NULL,
875                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU, 
876                            channel->channel_name, tmp ? tmp : "");
877       }
878     } else {
879       list1 = nicklist_get_same_unique(SERVER(server), client_entry);
880       for (list_tmp = list1; list_tmp != NULL; list_tmp = 
881              list_tmp->next->next) {
882         CHANNEL_REC *channel = list_tmp->data;
883         NICK_REC *nickrec = list_tmp->next->data;
884         nicklist_remove(channel, nickrec);
885       }
886
887       if (idtype == SILC_ID_CLIENT) {
888         client_entry2 = (SilcClientEntry)entry;
889         printformat_module("fe-common/silc", server, NULL,
890                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED, 
891                            client_entry->nickname,
892                            client_entry2 ? client_entry2->nickname : "",
893                            tmp ? tmp : "");
894       } else if (idtype == SILC_ID_SERVER) {
895         server_entry = (SilcServerEntry)entry;
896         printformat_module("fe-common/silc", server, NULL,
897                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED, 
898                            client_entry->nickname,
899                            server_entry->server_name, tmp ? tmp : "");
900       } else if (idtype == SILC_ID_CHANNEL) {
901         channel = (SilcChannelEntry)entry;
902         printformat_module("fe-common/silc", server, NULL,
903                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED, 
904                            client_entry->nickname,
905                            channel->channel_name, tmp ? tmp : "");
906       }
907     }
908     break;
909
910   case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
911     break;
912
913   case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
914     {
915       /*
916        * Server has quit the network.
917        */
918       int i;
919       SilcClientEntry *clients;
920       SilcUInt32 clients_count;
921
922       SILC_LOG_DEBUG(("Notify: SIGNOFF"));
923       
924       (void)va_arg(va, void *);
925       clients = va_arg(va, SilcClientEntry *);
926       clients_count = va_arg(va, SilcUInt32);
927   
928       for (i = 0; i < clients_count; i++) {
929         memset(buf, 0, sizeof(buf));
930         if (clients[i]->username)
931           snprintf(buf, sizeof(buf) - 1, "%s@%s",
932                    clients[i]->username, clients[i]->hostname);
933         signal_emit("message quit", 4, server, clients[i]->nickname,
934                     clients[i]->username ? buf : "", 
935                     "server signoff");
936
937         silc_server_free_ftp(server, clients[i]);
938         
939         list1 = nicklist_get_same_unique(SERVER(server), clients[i]);
940         for (list_tmp = list1; list_tmp != NULL; list_tmp = 
941                list_tmp->next->next) {
942           CHANNEL_REC *channel = list_tmp->data;
943           NICK_REC *nickrec = list_tmp->next->data;
944           nicklist_remove(channel, nickrec);
945         }
946       }
947     }
948     break;
949
950   case SILC_NOTIFY_TYPE_ERROR:
951     {
952       SilcStatus error = va_arg(va, int);
953
954       silc_say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
955                 "%s", silc_get_status_message(error));
956     }
957     break;
958
959   case SILC_NOTIFY_TYPE_WATCH:
960     {
961       SilcNotifyType notify;
962
963       client_entry = va_arg(va, SilcClientEntry);
964       name = va_arg(va, char *);          /* Maybe NULL */
965       mode = va_arg(va, SilcUInt32);
966       notify = va_arg(va, int);
967
968       if (notify == SILC_NOTIFY_TYPE_NICK_CHANGE) {
969         if (name)
970           printformat_module("fe-common/silc", server, NULL,
971                              MSGLEVEL_CRAP, SILCTXT_WATCH_NICK_CHANGE,
972                              client_entry->nickname, name);
973         else
974           printformat_module("fe-common/silc", server, NULL,
975                              MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
976                              client_entry->nickname);
977       } else if (notify == SILC_NOTIFY_TYPE_UMODE_CHANGE) {
978         /* See if client was away and is now present */
979         if (!(mode & (SILC_UMODE_GONE | SILC_UMODE_INDISPOSED |
980                       SILC_UMODE_BUSY | SILC_UMODE_PAGE |
981                       SILC_UMODE_DETACHED)) &&
982             (client_entry->mode & SILC_UMODE_GONE ||
983              client_entry->mode & SILC_UMODE_INDISPOSED ||
984              client_entry->mode & SILC_UMODE_BUSY ||
985              client_entry->mode & SILC_UMODE_PAGE ||
986              client_entry->mode & SILC_UMODE_DETACHED)) {
987           printformat_module("fe-common/silc", server, NULL,
988                              MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
989                              client_entry->nickname);
990         }
991
992         if (mode) {
993           memset(buf, 0, sizeof(buf));
994           silc_get_umode_string(mode, buf, sizeof(buf) - 1);
995           printformat_module("fe-common/silc", server, NULL,
996                              MSGLEVEL_CRAP, SILCTXT_WATCH_UMODE_CHANGE,
997                              client_entry->nickname, buf);
998         }
999       } else if (notify == SILC_NOTIFY_TYPE_KILLED) {
1000         printformat_module("fe-common/silc", server, NULL,
1001                            MSGLEVEL_CRAP, SILCTXT_WATCH_KILLED,
1002                            client_entry->nickname);
1003       } else if (notify == SILC_NOTIFY_TYPE_SIGNOFF ||
1004                  notify == SILC_NOTIFY_TYPE_SERVER_SIGNOFF) {
1005         printformat_module("fe-common/silc", server, NULL,
1006                            MSGLEVEL_CRAP, SILCTXT_WATCH_SIGNOFF,
1007                            client_entry->nickname);
1008       } else if (notify == SILC_NOTIFY_TYPE_NONE) {
1009         /* Client logged in to the network */
1010         printformat_module("fe-common/silc", server, NULL,
1011                            MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
1012                            client_entry->nickname);
1013       }
1014     }
1015     break;
1016
1017   default:
1018     /* Unknown notify */
1019     printformat_module("fe-common/silc", server, NULL,
1020                        MSGLEVEL_CRAP, SILCTXT_UNKNOWN_NOTIFY, type);
1021     break;
1022   }
1023
1024   va_end(va);
1025 }
1026
1027 /* Called to indicate that connection was either successfully established
1028    or connecting failed.  This is also the first time application receives
1029    the SilcClientConnection object which it should save somewhere. */
1030
1031 void silc_connect(SilcClient client, SilcClientConnection conn,
1032                   SilcClientConnectionStatus status)
1033 {
1034   SILC_SERVER_REC *server = conn->context;
1035
1036   if (!server || server->disconnected) {
1037     silc_client_close_connection(client, conn);
1038     return;
1039   }
1040
1041   switch (status) {
1042   case SILC_CLIENT_CONN_SUCCESS:
1043     /* We have successfully connected to server */
1044     server->connected = TRUE;
1045     signal_emit("event connected", 1, server);
1046     break;
1047
1048   case SILC_CLIENT_CONN_SUCCESS_RESUME:
1049     /* We have successfully resumed old detached session */
1050     server->connected = TRUE;
1051     signal_emit("event connected", 1, server);
1052
1053     /* If we resumed old session check whether we need to update 
1054        our nickname */
1055     if (strcmp(server->nick, conn->local_entry->nickname)) {
1056       char *old;
1057       old = g_strdup(server->nick);
1058       server_change_nick(SERVER(server), conn->local_entry->nickname);
1059       nicklist_rename_unique(SERVER(server), 
1060                              conn->local_entry, server->nick,
1061                              conn->local_entry, conn->local_entry->nickname);
1062       signal_emit("message own_nick", 4, server, server->nick, old, "");
1063       g_free(old);
1064     }
1065     break;
1066
1067   default:
1068     server->connection_lost = TRUE;
1069     if (server->conn)
1070       server->conn->context = NULL;
1071     server_disconnect(SERVER(server));
1072     break;
1073   }
1074 }
1075
1076 /* Called to indicate that connection was disconnected to the server. */
1077
1078 void silc_disconnect(SilcClient client, SilcClientConnection conn,
1079                      SilcStatus status, const char *message)
1080 {
1081   SILC_SERVER_REC *server = conn->context;
1082
1083   SILC_LOG_DEBUG(("Start"));
1084
1085   if (!server || server->connection_lost)
1086     return;
1087
1088   if (server->conn && server->conn->local_entry) {
1089     nicklist_rename_unique(SERVER(server),
1090                            server->conn->local_entry, server->nick,
1091                            server->conn->local_entry, 
1092                            silc_client->username);
1093     silc_change_nick(server, silc_client->username);
1094   }
1095
1096   if (message)
1097     silc_say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
1098              "Server closed connection: %s (%d) %s",
1099              silc_get_status_message(status), status,
1100              message ? message : "");
1101
1102   server->conn->context = NULL;
1103   server->conn = NULL;
1104   server->connection_lost = TRUE;
1105   server_disconnect(SERVER(server));
1106 }
1107
1108 /* Command handler. This function is called always in the command function.
1109    If error occurs it will be called as well. `conn' is the associated
1110    client connection. `cmd_context' is the command context that was
1111    originally sent to the command. `success' is FALSE if error occured
1112    during command. `command' is the command being processed. It must be
1113    noted that this is not reply from server. This is merely called just
1114    after application has called the command. Just to tell application
1115    that the command really was processed. */
1116
1117 void silc_command(SilcClient client, SilcClientConnection conn, 
1118                   SilcClientCommandContext cmd_context, bool success,
1119                   SilcCommand command, SilcStatus status)
1120 {
1121   SILC_SERVER_REC *server = conn->context;
1122
1123   SILC_LOG_DEBUG(("Start"));
1124
1125   if (!success) {
1126     silc_say_error("%s", silc_get_status_message(status));
1127     return;
1128   }
1129
1130   switch (command) {
1131
1132   case SILC_COMMAND_INVITE:
1133     if (cmd_context->argc > 2)
1134       printformat_module("fe-common/silc", server, NULL,
1135                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING,
1136                          cmd_context->argv[2], 
1137                          (cmd_context->argv[1][0] == '*' ?
1138                           (char *)conn->current_channel->channel_name :
1139                           (char *)cmd_context->argv[1]));
1140     break;
1141
1142   case SILC_COMMAND_DETACH:
1143     server->no_reconnect = TRUE;
1144     break;
1145
1146   default:
1147     break;
1148   }
1149 }
1150
1151 /* Client info resolving callback when JOIN command reply is received.
1152    This will cache all users on the channel. */
1153
1154 static void silc_client_join_get_users(SilcClient client,
1155                                        SilcClientConnection conn,
1156                                        SilcClientEntry *clients,
1157                                        SilcUInt32 clients_count,
1158                                        void *context)
1159 {
1160   SilcChannelEntry channel = (SilcChannelEntry)context;
1161   SilcHashTableList htl;
1162   SilcChannelUser chu;
1163   SILC_SERVER_REC *server = conn->context;
1164   SILC_CHANNEL_REC *chanrec;
1165   SilcClientEntry founder = NULL;
1166   NICK_REC *ownnick;
1167
1168   SILC_LOG_DEBUG(("Start, channel %s, %d users", channel->channel_name,
1169                   silc_hash_table_count(channel->user_list)));
1170
1171   if (!clients)
1172     return;
1173
1174   chanrec = silc_channel_find(server, channel->channel_name);
1175   if (chanrec == NULL)
1176     return;
1177
1178   silc_hash_table_list(channel->user_list, &htl);
1179   while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1180     if (!chu->client->nickname)
1181       continue;
1182     if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
1183       founder = chu->client;
1184     silc_nicklist_insert(chanrec, chu, FALSE);
1185   }
1186   silc_hash_table_list_reset(&htl);
1187
1188   ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
1189   nicklist_set_own(CHANNEL(chanrec), ownnick);
1190   signal_emit("channel joined", 1, chanrec);
1191   chanrec->entry = channel;
1192
1193   if (chanrec->topic)
1194     printformat_module("fe-common/silc", server, channel->channel_name,
1195                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
1196                        channel->channel_name, chanrec->topic);
1197
1198   if (founder) {
1199     if (founder == conn->local_entry)
1200       printformat_module("fe-common/silc", 
1201                          server, channel->channel_name, MSGLEVEL_CRAP,
1202                          SILCTXT_CHANNEL_FOUNDER_YOU,
1203                          channel->channel_name);
1204     else
1205       printformat_module("fe-common/silc", 
1206                          server, channel->channel_name, MSGLEVEL_CRAP,
1207                          SILCTXT_CHANNEL_FOUNDER,
1208                          channel->channel_name, founder->nickname);
1209   }
1210 }
1211
1212 typedef struct {
1213   SilcClient client;
1214   SilcClientConnection conn;
1215   void *entry;
1216   SilcIdType id_type;
1217   char *fingerprint;
1218 } *GetkeyContext;
1219
1220 void silc_getkey_cb(bool success, void *context)
1221 {
1222   GetkeyContext getkey = (GetkeyContext)context;
1223   char *entity = (getkey->id_type == SILC_ID_CLIENT ? "user" : "server");
1224   char *name = (getkey->id_type == SILC_ID_CLIENT ? 
1225                 ((SilcClientEntry)getkey->entry)->nickname :
1226                 ((SilcServerEntry)getkey->entry)->server_name);
1227
1228   if (success) {
1229     printformat_module("fe-common/silc", NULL, NULL,
1230                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_VERIFIED, entity, name);
1231   } else {
1232     printformat_module("fe-common/silc", NULL, NULL,
1233                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOTVERIFIED,
1234                        entity, name);
1235   }
1236
1237   silc_free(getkey->fingerprint);
1238   silc_free(getkey);
1239 }
1240
1241 /* Parse an invite or ban list */
1242 void  silc_parse_inviteban_list(SilcClient client,
1243                                 SilcClientConnection conn,
1244                                 SILC_SERVER_REC *server, 
1245                                 SilcChannelEntry channel, 
1246                                 const char *list_type,
1247                                 SilcArgumentPayload list)
1248 {
1249   unsigned char *tmp;
1250   SilcUInt32 type, len;
1251   SILC_CHANNEL_REC *chanrec = silc_channel_find_entry(server, channel);
1252   int counter=0, resolving = FALSE;
1253
1254   if (!silc_argument_get_arg_num(list)) {
1255     printformat_module("fe-common/silc", server,
1256                        (chanrec ? chanrec->visible_name : NULL),
1257                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_NO_INVITEBAN_LIST,
1258                        channel->channel_name, list_type);
1259     return;
1260   }
1261   
1262   printformat_module("fe-common/silc", server,
1263                      (chanrec ? chanrec->visible_name : NULL),
1264                      MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_LIST,
1265                      channel->channel_name, list_type);
1266
1267   /* parse the list */
1268   tmp = silc_argument_get_first_arg(list, &type, &len);
1269   while (tmp) {
1270     switch (type) {
1271       case 1:
1272         {
1273           /* an invite string */
1274           char **list;
1275           int i=0;
1276                 
1277           if (tmp[len-1] == ',')
1278             tmp[len-1] = '\0';
1279           
1280           list = g_strsplit(tmp, ",", -1);
1281           while (list[i])
1282             printformat_module("fe-common/silc", server,
1283                                (chanrec ? chanrec->visible_name : NULL),
1284                                MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_STRING,
1285                                ++counter, channel->channel_name, list_type,
1286                                list[i++]);
1287           g_strfreev(list);
1288         }
1289         break;
1290
1291       case 2:
1292         {
1293           /* a public key */
1294           char *fingerprint, *babbleprint;
1295
1296           /* tmp is Public Key Payload, take public key from it. */
1297           fingerprint = silc_hash_fingerprint(NULL, tmp + 4, len - 4);
1298           babbleprint = silc_hash_babbleprint(NULL, tmp + 4, len - 4);
1299           
1300           printformat_module("fe-common/silc", server,
1301                              (chanrec ? chanrec->visible_name : NULL),
1302                              MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_PUBKEY,
1303                              ++counter, channel->channel_name, list_type,
1304                              fingerprint, babbleprint);
1305         }
1306         break;
1307         
1308       case 3:
1309         {
1310           /* a client ID */
1311           SilcClientID *client_id;
1312           SilcClientEntry client_entry;
1313           
1314           client_id = silc_id_payload_parse_id(tmp, len, NULL);
1315                           
1316           if (client_id == NULL) {
1317             silc_say_error("Invalid data in %s list encountered", list_type);
1318             break;
1319           }
1320
1321           client_entry = silc_client_get_client_by_id(client, conn, client_id);
1322
1323           if (client_entry) {
1324             printformat_module("fe-common/silc", server,
1325                                (chanrec ? chanrec->visible_name : NULL),
1326                                MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_STRING,
1327                                ++counter, channel->channel_name, list_type,
1328                                client_entry->nickname);
1329           } else {
1330             resolving = TRUE;
1331             silc_client_get_client_by_id_resolve(client, conn, client_id,
1332                                                  NULL, NULL, NULL);
1333           }
1334
1335           silc_free(client_id);
1336         }
1337         break;
1338         
1339       default:
1340         /* "trash" */
1341         silc_say_error("Unkown type in %s list: %u (len %u)",
1342                        list_type, type, len);
1343     }
1344     tmp = silc_argument_get_next_arg(list, &type, &len);
1345   }
1346
1347   if (resolving)
1348     printformat_module("fe-common/silc", server, 
1349                        (chanrec ? chanrec->visible_name : NULL),
1350                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_REGET,
1351                        list_type, channel->channel_name);
1352 }
1353
1354 /* Command reply handler. This function is called always in the command reply
1355    function. If error occurs it will be called as well. Normal scenario
1356    is that it will be called after the received command data has been parsed
1357    and processed. The function is used to pass the received command data to
1358    the application. 
1359
1360    `conn' is the associated client connection. `cmd_payload' is the command
1361    payload data received from server and it can be ignored. It is provided
1362    if the application would like to re-parse the received command data,
1363    however, it must be noted that the data is parsed already by the library
1364    thus the payload can be ignored. `success' is FALSE if error occured.
1365    In this case arguments are not sent to the application. `command' is the
1366    command reply being processed. The function has variable argument list
1367    and each command defines the number and type of arguments it passes to the
1368    application (on error they are not sent). */
1369
1370 void 
1371 silc_command_reply(SilcClient client, SilcClientConnection conn,
1372                    SilcCommandPayload cmd_payload, bool success,
1373                    SilcCommand command, SilcStatus status, ...)
1374
1375 {
1376   SILC_SERVER_REC *server = conn->context;
1377   SILC_CHANNEL_REC *chanrec;
1378   va_list vp;
1379
1380   va_start(vp, status);
1381
1382   SILC_LOG_DEBUG(("Start"));
1383
1384   switch(command) {
1385   case SILC_COMMAND_WHOIS:
1386     {
1387       char buf[1024], *nickname, *username, *realname, *nick;
1388       unsigned char *fingerprint;
1389       SilcUInt32 idle, mode;
1390       SilcBuffer channels, user_modes;
1391       SilcClientEntry client_entry;
1392       SilcDList attrs;
1393       
1394       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1395         /* Print the unknown nick for user */
1396         unsigned char *tmp =
1397           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1398                                      3, NULL);
1399         if (tmp)
1400           silc_say_error("%s: %s", tmp, 
1401                          silc_get_status_message(status));
1402         break;
1403       } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1404         /* Try to find the entry for the unknown client ID, since we
1405            might have, and print the nickname of it for user. */
1406         SilcUInt32 tmp_len;
1407         unsigned char *tmp =
1408           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1409                                      2, &tmp_len);
1410         if (tmp) {
1411           SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, 
1412                                                              NULL);
1413           if (client_id) {
1414             client_entry = silc_client_get_client_by_id(client, conn,
1415                                                         client_id);
1416             if (client_entry && client_entry->nickname)
1417               silc_say_error("%s: %s", client_entry->nickname,
1418                              silc_get_status_message(status));
1419             silc_free(client_id);
1420           }
1421         }
1422         break;
1423       } else if (!success) {
1424         silc_say_error("WHOIS: %s", silc_get_status_message(status));
1425         return;
1426       }
1427
1428       client_entry = va_arg(vp, SilcClientEntry);
1429       nickname = va_arg(vp, char *);
1430       username = va_arg(vp, char *);
1431       realname = va_arg(vp, char *);
1432       channels = va_arg(vp, SilcBuffer);
1433       mode = va_arg(vp, SilcUInt32);
1434       idle = va_arg(vp, SilcUInt32);
1435       fingerprint = va_arg(vp, unsigned char *);
1436       user_modes = va_arg(vp, SilcBuffer);
1437       attrs = va_arg(vp, SilcDList);
1438       
1439       silc_parse_userfqdn(nickname, &nick, NULL);
1440       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1441                          SILCTXT_WHOIS_USERINFO, nickname, 
1442                          client_entry->username, client_entry->hostname,
1443                          nick, client_entry->nickname);
1444       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1445                          SILCTXT_WHOIS_REALNAME, realname);
1446       silc_free(nick);
1447
1448       if (channels && user_modes) {
1449         SilcUInt32 *umodes;
1450         SilcDList list = silc_channel_payload_parse_list(channels->data,
1451                                                          channels->len);
1452         if (list && silc_get_mode_list(user_modes, silc_dlist_count(list),
1453                                        &umodes)) {
1454           SilcChannelPayload entry;
1455           int i = 0;
1456
1457           memset(buf, 0, sizeof(buf));
1458           silc_dlist_start(list);
1459           while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
1460             SilcUInt32 name_len;
1461             char *m = silc_client_chumode_char(umodes[i++]);
1462             char *name = silc_channel_get_name(entry, &name_len);
1463             
1464             if (m)
1465               strncat(buf, m, strlen(m));
1466             strncat(buf, name, name_len);
1467             strncat(buf, " ", 1);
1468             silc_free(m);
1469           }
1470
1471           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1472                              SILCTXT_WHOIS_CHANNELS, buf);
1473           silc_channel_payload_list_free(list);
1474           silc_free(umodes);
1475         }
1476       }
1477       
1478       if (mode) {
1479         memset(buf, 0, sizeof(buf));
1480         silc_get_umode_string(mode, buf, sizeof(buf - 1));
1481         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1482                            SILCTXT_WHOIS_MODES, buf);
1483       }
1484       
1485       if (idle && nickname) {
1486         memset(buf, 0, sizeof(buf));
1487         snprintf(buf, sizeof(buf) - 1, "%lu %s",
1488                  idle > 60 ? (idle / 60) : idle,
1489                  idle > 60 ? "minutes" : "seconds");
1490
1491         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1492                            SILCTXT_WHOIS_IDLE, buf);
1493       }
1494
1495       if (fingerprint) {
1496         fingerprint = silc_fingerprint(fingerprint, 20);
1497         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1498                            SILCTXT_WHOIS_FINGERPRINT, fingerprint);
1499         silc_free(fingerprint);
1500       }
1501
1502       if (attrs)
1503         silc_query_attributes_print(server, silc_client, conn, attrs,
1504                                     client_entry);
1505     }
1506     break;
1507     
1508   case SILC_COMMAND_IDENTIFY:
1509     {
1510       SilcClientEntry client_entry;
1511       
1512       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1513         /* Print the unknown nick for user */
1514         unsigned char *tmp =
1515           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1516                                      3, NULL);
1517         if (tmp)
1518           silc_say_error("%s: %s", tmp, 
1519                          silc_get_status_message(status));
1520         break;
1521       } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1522         /* Try to find the entry for the unknown client ID, since we
1523            might have, and print the nickname of it for user. */
1524         SilcUInt32 tmp_len;
1525         unsigned char *tmp =
1526           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1527                                      2, &tmp_len);
1528         if (tmp) {
1529           SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len,
1530                                                              NULL);
1531           if (client_id) {
1532             client_entry = silc_client_get_client_by_id(client, conn,
1533                                                         client_id);
1534             if (client_entry && client_entry->nickname)
1535               silc_say_error("%s: %s", client_entry->nickname,
1536                              silc_get_status_message(status));
1537             silc_free(client_id);
1538           }
1539         }
1540         break;
1541       }
1542
1543       break;
1544     }
1545
1546   case SILC_COMMAND_WHOWAS:
1547     {
1548       char *nickname, *username, *realname;
1549       
1550       if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
1551           status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1552         char *tmp;
1553         tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1554                                          3, NULL);
1555         if (tmp)
1556           silc_say_error("%s: %s", tmp, 
1557                          silc_get_status_message(status));
1558         break;
1559       } else if (!success) {
1560         silc_say_error("WHOWAS: %s", silc_get_status_message(status));
1561         return;
1562       }
1563       
1564       (void)va_arg(vp, SilcClientEntry);
1565       nickname = va_arg(vp, char *);
1566       username = va_arg(vp, char *);
1567       realname = va_arg(vp, char *);
1568       
1569       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1570                          SILCTXT_WHOWAS_USERINFO, nickname, username, 
1571                          realname ? realname : "");
1572     }
1573     break;
1574     
1575   case SILC_COMMAND_INVITE:
1576     {
1577       SilcChannelEntry channel;
1578       SilcBuffer payload;
1579       SilcArgumentPayload invite_list;
1580       SilcUInt32 argc;
1581       
1582       if (!success)
1583         return;
1584       
1585       channel = va_arg(vp, SilcChannelEntry);
1586       payload = va_arg(vp, SilcBuffer);
1587
1588       if (payload) {
1589         SILC_GET16_MSB(argc, payload->data);
1590         invite_list = silc_argument_payload_parse(payload->data + 2, 
1591                                                   payload->len - 2, argc);
1592         if (invite_list) {
1593           silc_parse_inviteban_list(client, conn, server, channel, 
1594                                     "invite", invite_list);
1595           silc_argument_payload_free(invite_list);
1596         }
1597       }
1598     }
1599     break;
1600
1601   case SILC_COMMAND_JOIN: 
1602     {
1603       char *channel, *mode, *topic;
1604       SilcUInt32 modei;
1605       SilcChannelEntry channel_entry;
1606       SilcBuffer client_id_list;
1607       SilcUInt32 list_count;
1608
1609       if (!success)
1610         return;
1611
1612       channel = va_arg(vp, char *);
1613       channel_entry = va_arg(vp, SilcChannelEntry);
1614       modei = va_arg(vp, SilcUInt32);
1615       (void)va_arg(vp, SilcUInt32);
1616       (void)va_arg(vp, unsigned char *);
1617       (void)va_arg(vp, unsigned char *);
1618       (void)va_arg(vp, unsigned char *);
1619       topic = va_arg(vp, char *);
1620       (void)va_arg(vp, unsigned char *);
1621       list_count = va_arg(vp, SilcUInt32);
1622       client_id_list = va_arg(vp, SilcBuffer);
1623
1624       chanrec = silc_channel_find(server, channel);
1625       if (!chanrec)
1626         chanrec = silc_channel_create(server, channel, channel, TRUE);
1627
1628       if (topic) {
1629         char tmp[256], *cp, *dm = NULL;
1630         g_free_not_null(chanrec->topic);
1631
1632         if (!silc_term_utf8() && silc_utf8_valid(topic, strlen(topic))) {
1633           memset(tmp, 0, sizeof(tmp));
1634           cp = tmp;
1635           if (strlen(topic) > sizeof(tmp) - 1) {
1636             dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
1637             cp = dm;
1638           }
1639
1640           silc_utf8_decode(topic, strlen(topic), SILC_STRING_LANGUAGE,
1641                            cp, strlen(topic));
1642           topic = cp;
1643         }
1644
1645         chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1646         signal_emit("channel topic changed", 1, chanrec);
1647
1648         silc_free(dm);
1649       }
1650
1651       mode = silc_client_chmode(modei, 
1652                                 channel_entry->channel_key ? 
1653                                 silc_cipher_get_name(channel_entry->
1654                                                      channel_key) : "",
1655                                 channel_entry->hmac ? 
1656                                 silc_hmac_get_name(channel_entry->hmac) : "");
1657       g_free_not_null(chanrec->mode);
1658       chanrec->mode = g_strdup(mode == NULL ? "" : mode);
1659       signal_emit("channel mode changed", 1, chanrec);
1660
1661       /* Resolve the client information */
1662       silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
1663                                       silc_client_join_get_users, 
1664                                       channel_entry);
1665
1666       break;
1667     }
1668
1669   case SILC_COMMAND_NICK: 
1670     {
1671       char *old;
1672       SilcClientEntry client_entry = va_arg(vp, SilcClientEntry);
1673       GSList *nicks;
1674       
1675       if (!success)
1676         return;
1677
1678       nicks = nicklist_get_same(SERVER(server), client_entry->nickname);
1679       if (nicks != NULL) {
1680         char buf[512];
1681         SilcClientEntry collider, old;
1682
1683         old = ((SILC_NICK_REC *)(nicks->next->data))->silc_user->client;
1684         collider = silc_client_get_client_by_id(client, conn,
1685                                                 old->id);
1686         
1687         memset(buf, 0, sizeof(buf));
1688         snprintf(buf, sizeof(buf) - 1, "%s@%s",
1689                  collider->username, collider->hostname);
1690         nicklist_rename_unique(SERVER(server),
1691                                old, old->nickname,
1692                                collider, collider->nickname);
1693         silc_print_nick_change(server, collider->nickname,
1694                                client_entry->nickname, buf);
1695         g_slist_free(nicks);
1696       }
1697
1698       old = g_strdup(server->nick);
1699       server_change_nick(SERVER(server), client_entry->nickname);
1700       nicklist_rename_unique(SERVER(server),
1701                              server->conn->local_entry, server->nick,
1702                              client_entry, client_entry->nickname);
1703       signal_emit("message own_nick", 4, server, server->nick, old, "");
1704       g_free(old);
1705       break;
1706     }
1707     
1708   case SILC_COMMAND_LIST:
1709     {
1710       char *topic, *name;
1711       int usercount;
1712       char users[20];
1713       char tmp[256], *cp, *dm = NULL;
1714       
1715       if (!success)
1716         return;
1717       
1718       (void)va_arg(vp, SilcChannelEntry);
1719       name = va_arg(vp, char *);
1720       topic = va_arg(vp, char *);
1721       usercount = va_arg(vp, int);
1722
1723       if (topic && !silc_term_utf8() &&
1724           silc_utf8_valid(topic, strlen(topic))) {
1725         memset(tmp, 0, sizeof(tmp));
1726         cp = tmp;
1727         if (strlen(topic) > sizeof(tmp) - 1) {
1728           dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
1729           cp = dm;
1730         }
1731
1732         silc_utf8_decode(topic, strlen(topic), SILC_STRING_LANGUAGE,
1733                          cp, strlen(topic));
1734         topic = cp;
1735       }
1736       
1737       if (status == SILC_STATUS_LIST_START ||
1738           status == SILC_STATUS_OK)
1739         printformat_module("fe-common/silc", server, NULL,
1740                            MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
1741
1742       if (!usercount)
1743         snprintf(users, sizeof(users) - 1, "N/A");
1744       else
1745         snprintf(users, sizeof(users) - 1, "%d", usercount);
1746       printformat_module("fe-common/silc", server, NULL,
1747                          MSGLEVEL_CRAP, SILCTXT_LIST,
1748                          name, users, topic ? topic : "");
1749       silc_free(dm);
1750     }
1751     break;
1752     
1753   case SILC_COMMAND_UMODE:
1754     {
1755       SilcUInt32 mode;
1756       char *reason;
1757       
1758       if (!success)
1759         return;
1760       
1761       mode = va_arg(vp, SilcUInt32);
1762       
1763       if (mode & SILC_UMODE_SERVER_OPERATOR &&
1764           !(server->umode & SILC_UMODE_SERVER_OPERATOR))
1765         printformat_module("fe-common/silc", server, NULL,
1766                            MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1767
1768       if (mode & SILC_UMODE_ROUTER_OPERATOR &&
1769           !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
1770         printformat_module("fe-common/silc", server, NULL,
1771                            MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1772
1773       if ((mode & SILC_UMODE_GONE) != (server->umode & SILC_UMODE_GONE)) {
1774         if (mode & SILC_UMODE_GONE) {      
1775           if ((server->away_reason != NULL) && (server->away_reason[0] != '\0'))
1776             reason = g_strdup(server->away_reason);
1777           else
1778             reason = g_strdup("away");
1779         } else
1780           reason = g_strdup("");
1781
1782         silc_set_away(reason, server);
1783
1784         g_free(reason);
1785       }
1786
1787       server->umode = mode;
1788       signal_emit("user mode changed", 2, server, NULL);
1789     }
1790     break;
1791     
1792   case SILC_COMMAND_OPER:
1793     if (!success)
1794       return;
1795
1796     server->umode |= SILC_UMODE_SERVER_OPERATOR;
1797     signal_emit("user mode changed", 2, server, NULL);
1798
1799     printformat_module("fe-common/silc", server, NULL,
1800                        MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1801     break;
1802     
1803   case SILC_COMMAND_SILCOPER:
1804     if (!success)
1805       return;
1806
1807     server->umode |= SILC_UMODE_ROUTER_OPERATOR;
1808     signal_emit("user mode changed", 2, server, NULL);
1809
1810     printformat_module("fe-common/silc", server, NULL,
1811                        MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1812     break;
1813     
1814   case SILC_COMMAND_USERS: 
1815     {
1816       SilcHashTableList htl;
1817       SilcChannelEntry channel;
1818       SilcChannelUser chu;
1819       
1820       if (!success)
1821         return;
1822       
1823       channel = va_arg(vp, SilcChannelEntry);
1824       
1825       printformat_module("fe-common/silc", server, channel->channel_name,
1826                          MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
1827                          channel->channel_name);
1828
1829       silc_hash_table_list(channel->user_list, &htl);
1830       while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1831         SilcClientEntry e = chu->client;
1832         char stat[5], *mode;
1833
1834         if (!e->nickname)
1835           continue;
1836         
1837         memset(stat, 0, sizeof(stat));
1838         mode = silc_client_chumode_char(chu->mode);
1839         if (e->mode & SILC_UMODE_GONE)
1840           strcat(stat, "G");
1841         else if (e->mode & SILC_UMODE_INDISPOSED)
1842           strcat(stat, "I");
1843         else if (e->mode & SILC_UMODE_BUSY)
1844           strcat(stat, "B");
1845         else if (e->mode & SILC_UMODE_PAGE)
1846           strcat(stat, "P");
1847         else if (e->mode & SILC_UMODE_HYPER)
1848           strcat(stat, "H");
1849         else if (e->mode & SILC_UMODE_ROBOT)
1850           strcat(stat, "R");
1851         else if (e->mode & SILC_UMODE_ANONYMOUS)
1852           strcat(stat, "?");
1853         else
1854           strcat(stat, "A");
1855         if (mode)
1856           strcat(stat, mode);
1857
1858         printformat_module("fe-common/silc", server, channel->channel_name,
1859                            MSGLEVEL_CRAP, SILCTXT_USERS,
1860                            e->nickname, stat, 
1861                            e->username ? e->username : "",
1862                            e->hostname ? e->hostname : "",
1863                            e->realname ? e->realname : "");
1864         if (mode)
1865           silc_free(mode);
1866       }
1867       silc_hash_table_list_reset(&htl);
1868     }
1869     break;
1870
1871   case SILC_COMMAND_BAN:
1872     {
1873       SilcChannelEntry channel;
1874       SilcBuffer payload;
1875       SilcArgumentPayload ban_list;
1876       SilcUInt32 argc;
1877       
1878       if (!success)
1879         return;
1880       
1881       channel = va_arg(vp, SilcChannelEntry);
1882       payload = va_arg(vp, SilcBuffer);
1883
1884       if (payload) {
1885         SILC_GET16_MSB(argc, payload->data);
1886         ban_list = silc_argument_payload_parse(payload->data + 2, 
1887                                                payload->len - 2, argc);
1888         if (ban_list) {
1889           silc_parse_inviteban_list(client, conn, server, channel, 
1890                                     "ban", ban_list);
1891           silc_argument_payload_free(ban_list);
1892         }
1893       }
1894     }
1895     break;
1896     
1897   case SILC_COMMAND_GETKEY:
1898     {
1899       SilcIdType id_type;
1900       void *entry;
1901       SilcPublicKey public_key;
1902       unsigned char *pk;
1903       SilcUInt32 pk_len;
1904       GetkeyContext getkey;
1905       char *name;
1906       
1907       if (!success)
1908         return;
1909       
1910       id_type = va_arg(vp, SilcUInt32);
1911       entry = va_arg(vp, void *);
1912       public_key = va_arg(vp, SilcPublicKey);
1913
1914       if (public_key) {
1915         pk = silc_pkcs_public_key_encode(public_key, &pk_len);
1916
1917         getkey = silc_calloc(1, sizeof(*getkey));
1918         getkey->entry = entry;
1919         getkey->id_type = id_type;
1920         getkey->client = client;
1921         getkey->conn = conn;
1922         getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1923
1924         name = (id_type == SILC_ID_CLIENT ? 
1925                 ((SilcClientEntry)entry)->nickname :
1926                 ((SilcServerEntry)entry)->server_name);
1927
1928         silc_verify_public_key_internal(client, conn, name,
1929                                         (id_type == SILC_ID_CLIENT ?
1930                                          SILC_SOCKET_TYPE_CLIENT :
1931                                          SILC_SOCKET_TYPE_SERVER),
1932                                         pk, pk_len, SILC_SKE_PK_TYPE_SILC,
1933                                         silc_getkey_cb, getkey);
1934         silc_free(pk);
1935       } else {
1936         printformat_module("fe-common/silc", server, NULL,
1937                            MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOKEY);
1938       }
1939     }
1940     break;
1941
1942   case SILC_COMMAND_INFO:
1943     {
1944       SilcServerEntry server_entry;
1945       char *server_name;
1946       char *server_info;
1947
1948       if (!success)
1949         return;
1950       
1951       server_entry = va_arg(vp, SilcServerEntry);
1952       server_name = va_arg(vp, char *);
1953       server_info = va_arg(vp, char *);
1954
1955       if (server_name && server_info )
1956         {
1957           printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
1958           printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
1959         }
1960     }
1961     break;
1962     
1963   case SILC_COMMAND_TOPIC:
1964     {
1965       SilcChannelEntry channel;
1966       char *topic;
1967       char tmp[256], *cp, *dm = NULL;
1968       
1969       if (!success)
1970         return;
1971       
1972       channel = va_arg(vp, SilcChannelEntry);
1973       topic = va_arg(vp, char *);
1974
1975       if (topic && !silc_term_utf8() &&
1976           silc_utf8_valid(topic, strlen(topic))) {
1977         memset(tmp, 0, sizeof(tmp));
1978         cp = tmp;
1979         if (strlen(topic) > sizeof(tmp) - 1) {
1980           dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
1981           cp = dm;
1982         }
1983
1984         silc_utf8_decode(topic, strlen(topic), SILC_STRING_LANGUAGE,
1985                          cp, strlen(topic));
1986         topic = cp;
1987       }
1988
1989       if (topic) {
1990         chanrec = silc_channel_find_entry(server, channel);
1991         if (chanrec) {
1992           g_free_not_null(chanrec->topic);
1993           chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1994           signal_emit("channel topic changed", 1, chanrec);
1995         }
1996         printformat_module("fe-common/silc", server, channel->channel_name,
1997                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
1998                            channel->channel_name, topic);
1999       } else {
2000         printformat_module("fe-common/silc", server, channel->channel_name,
2001                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
2002                            channel->channel_name);
2003       }
2004       silc_free(dm);
2005     }
2006     break;
2007
2008   case SILC_COMMAND_WATCH:
2009     break;
2010   
2011   case SILC_COMMAND_STATS:
2012     {
2013       SilcUInt32 starttime, uptime, my_clients, my_channels, my_server_ops,
2014                  my_router_ops, cell_clients, cell_channels, cell_servers,
2015                  clients, channels, servers, routers, server_ops, router_ops;
2016       SilcUInt32 buf_len;
2017       SilcBufferStruct buf;
2018       unsigned char *tmp_buf;
2019       char tmp[40];
2020       const char *tmptime;
2021       int days, hours, mins, secs;
2022
2023       if (!success)
2024         return;
2025
2026       tmp_buf = va_arg(vp, unsigned char *);
2027       buf_len = va_arg(vp, SilcUInt32);
2028
2029       if (!tmp_buf || !buf_len) {
2030         printtext(server, NULL, MSGLEVEL_CRAP, "No statistics available");
2031         return;
2032       }
2033
2034       /* Get statistics structure */
2035       silc_buffer_set(&buf, tmp_buf, buf_len);
2036       silc_buffer_unformat(&buf,
2037                            SILC_STR_UI_INT(&starttime),
2038                            SILC_STR_UI_INT(&uptime),
2039                            SILC_STR_UI_INT(&my_clients),
2040                            SILC_STR_UI_INT(&my_channels),
2041                            SILC_STR_UI_INT(&my_server_ops),
2042                            SILC_STR_UI_INT(&my_router_ops),
2043                            SILC_STR_UI_INT(&cell_clients),
2044                            SILC_STR_UI_INT(&cell_channels),
2045                            SILC_STR_UI_INT(&cell_servers),
2046                            SILC_STR_UI_INT(&clients),
2047                            SILC_STR_UI_INT(&channels),
2048                            SILC_STR_UI_INT(&servers),
2049                            SILC_STR_UI_INT(&routers),
2050                            SILC_STR_UI_INT(&server_ops),
2051                            SILC_STR_UI_INT(&router_ops),
2052                            SILC_STR_END);
2053
2054       tmptime = silc_get_time(starttime);
2055       printformat_module("fe-common/silc", server, NULL,
2056                          MSGLEVEL_CRAP, SILCTXT_STATS,
2057                          "Local server start time", tmptime);
2058
2059       days = uptime / (24 * 60 * 60);
2060       uptime -= days * (24 * 60 * 60);
2061       hours = uptime / (60 * 60);
2062       uptime -= hours * (60 * 60);
2063       mins = uptime / 60;
2064       uptime -= mins * 60;
2065       secs = uptime;
2066       snprintf(tmp, sizeof(tmp) - 1, "%d days %d hours %d mins %d secs",
2067                days, hours, mins, secs);
2068       printformat_module("fe-common/silc", server, NULL,
2069                          MSGLEVEL_CRAP, SILCTXT_STATS,
2070                          "Local server uptime", tmp);
2071
2072       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_clients);
2073       printformat_module("fe-common/silc", server, NULL,
2074                          MSGLEVEL_CRAP, SILCTXT_STATS,
2075                          "Local server clients", tmp);
2076
2077       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_channels);
2078       printformat_module("fe-common/silc", server, NULL,
2079                          MSGLEVEL_CRAP, SILCTXT_STATS,
2080                          "Local server channels", tmp);
2081
2082       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_server_ops);
2083       printformat_module("fe-common/silc", server, NULL,
2084                          MSGLEVEL_CRAP, SILCTXT_STATS,
2085                          "Local server operators", tmp);
2086
2087       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_router_ops);
2088       printformat_module("fe-common/silc", server, NULL,
2089                          MSGLEVEL_CRAP, SILCTXT_STATS,
2090                          "Local router operators", tmp);
2091
2092       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_clients);
2093       printformat_module("fe-common/silc", server, NULL,
2094                          MSGLEVEL_CRAP, SILCTXT_STATS,
2095                          "Local cell clients", tmp);
2096
2097       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_channels);
2098       printformat_module("fe-common/silc", server, NULL,
2099                          MSGLEVEL_CRAP, SILCTXT_STATS,
2100                          "Local cell channels", tmp);
2101
2102       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_servers);
2103       printformat_module("fe-common/silc", server, NULL,
2104                          MSGLEVEL_CRAP, SILCTXT_STATS,
2105                          "Local cell servers", tmp);
2106
2107       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)clients);
2108       printformat_module("fe-common/silc", server, NULL,
2109                          MSGLEVEL_CRAP, SILCTXT_STATS,
2110                          "Total clients", tmp);
2111
2112       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)channels);
2113       printformat_module("fe-common/silc", server, NULL,
2114                          MSGLEVEL_CRAP, SILCTXT_STATS,
2115                          "Total channels", tmp);
2116
2117       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)servers);
2118       printformat_module("fe-common/silc", server, NULL,
2119                          MSGLEVEL_CRAP, SILCTXT_STATS,
2120                          "Total servers", tmp);
2121
2122       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)routers);
2123       printformat_module("fe-common/silc", server, NULL,
2124                          MSGLEVEL_CRAP, SILCTXT_STATS,
2125                          "Total routers", tmp);
2126
2127       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)server_ops);
2128       printformat_module("fe-common/silc", server, NULL,
2129                          MSGLEVEL_CRAP, SILCTXT_STATS,
2130                            "Total server operators", tmp);
2131
2132       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)router_ops);
2133       printformat_module("fe-common/silc", server, NULL,
2134                          MSGLEVEL_CRAP, SILCTXT_STATS,
2135                          "Total router operators", tmp);
2136     }
2137     break;
2138
2139   }
2140
2141   va_end(vp);
2142 }
2143
2144 typedef struct {
2145   SilcClient client;
2146   SilcClientConnection conn;
2147   char *filename;
2148   char *entity;
2149   char *entity_name;
2150   unsigned char *pk;
2151   SilcUInt32 pk_len;
2152   SilcSKEPKType pk_type;
2153   SilcVerifyPublicKey completion;
2154   void *context;
2155 } *PublicKeyVerify;
2156
2157 static void verify_public_key_completion(const char *line, void *context)
2158 {
2159   PublicKeyVerify verify = (PublicKeyVerify)context;
2160
2161   if (line[0] == 'Y' || line[0] == 'y') {
2162     /* Call the completion */
2163     if (verify->completion)
2164       verify->completion(TRUE, verify->context);
2165
2166     /* Save the key for future checking */
2167     silc_pkcs_save_public_key_data(verify->filename, verify->pk, 
2168                                    verify->pk_len, SILC_PKCS_FILE_PEM);
2169   } else {
2170     /* Call the completion */
2171     if (verify->completion)
2172       verify->completion(FALSE, verify->context);
2173
2174     printformat_module("fe-common/silc", NULL, NULL,
2175                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, 
2176                        verify->entity_name ? verify->entity_name :
2177                        verify->entity);
2178   }
2179
2180   silc_free(verify->filename);
2181   silc_free(verify->entity);
2182   silc_free(verify->entity_name);
2183   silc_free(verify->pk);
2184   silc_free(verify);
2185 }
2186
2187 /* Internal routine to verify public key. If the `completion' is provided
2188    it will be called to indicate whether public was verified or not. For
2189    server/router public key this will check for filename that includes the
2190    remote host's IP address and remote host's hostname. */
2191
2192 static void 
2193 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
2194                                 const char *name, SilcSocketType conn_type, 
2195                                 unsigned char *pk, SilcUInt32 pk_len, 
2196                                 SilcSKEPKType pk_type,
2197                                 SilcVerifyPublicKey completion, void *context)
2198 {
2199   int i;
2200   char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
2201   char *fingerprint, *babbleprint, *format;
2202   struct passwd *pw;
2203   struct stat st;
2204   char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
2205                    conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
2206                   "server" : "client");
2207   PublicKeyVerify verify;
2208
2209   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
2210     printformat_module("fe-common/silc", NULL, NULL,
2211                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED, 
2212                        entity, pk_type);
2213     if (completion)
2214       completion(FALSE, context);
2215     return;
2216   }
2217
2218   pw = getpwuid(getuid());
2219   if (!pw) {
2220     if (completion)
2221       completion(FALSE, context);
2222     return;
2223   }
2224
2225   memset(filename, 0, sizeof(filename));
2226   memset(filename2, 0, sizeof(filename2));
2227   memset(file, 0, sizeof(file));
2228
2229   if (conn_type == SILC_SOCKET_TYPE_SERVER ||
2230       conn_type == SILC_SOCKET_TYPE_ROUTER) {
2231     if (!name) {
2232       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
2233                conn->sock->ip, conn->sock->port);
2234       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
2235                get_irssi_dir(), entity, file);
2236       
2237       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
2238                conn->sock->hostname, conn->sock->port);
2239       snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s", 
2240                get_irssi_dir(), entity, file);
2241       
2242       ipf = filename;
2243       hostf = filename2;
2244     } else {
2245       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
2246                name, conn->sock->port);
2247       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
2248                get_irssi_dir(), entity, file);
2249       
2250       ipf = filename;
2251     }
2252   } else {
2253     /* Replace all whitespaces with `_'. */
2254     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2255     for (i = 0; i < strlen(fingerprint); i++)
2256       if (fingerprint[i] == ' ')
2257         fingerprint[i] = '_';
2258     
2259     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
2260     snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
2261              get_irssi_dir(), entity, file);
2262     silc_free(fingerprint);
2263
2264     ipf = filename;
2265   }
2266
2267   /* Take fingerprint of the public key */
2268   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2269   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
2270
2271   verify = silc_calloc(1, sizeof(*verify));
2272   verify->client = client;
2273   verify->conn = conn;
2274   verify->filename = strdup(ipf);
2275   verify->entity = strdup(entity);
2276   verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ?
2277                          (name ? strdup(name) : strdup(conn->sock->hostname))
2278                          : NULL);
2279   verify->pk = silc_memdup(pk, pk_len);
2280   verify->pk_len = pk_len;
2281   verify->pk_type = pk_type;
2282   verify->completion = completion;
2283   verify->context = context;
2284
2285   /* Check whether this key already exists */
2286   if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
2287     /* Key does not exist, ask user to verify the key and save it */
2288
2289     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2290                        SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
2291                        verify->entity_name : entity);
2292     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2293                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2294     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2295                        SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2296     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2297                              SILCTXT_PUBKEY_ACCEPT);
2298     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2299                             format, 0, verify);
2300     g_free(format);
2301     silc_free(fingerprint);
2302     return;
2303   } else {
2304     /* The key already exists, verify it. */
2305     SilcPublicKey public_key;
2306     unsigned char *encpk;
2307     SilcUInt32 encpk_len;
2308
2309     /* Load the key file, try for both IP filename and hostname filename */
2310     if (!silc_pkcs_load_public_key(ipf, &public_key, 
2311                                    SILC_PKCS_FILE_PEM) &&
2312         !silc_pkcs_load_public_key(ipf, &public_key, 
2313                                    SILC_PKCS_FILE_BIN) &&
2314         (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key, 
2315                                                SILC_PKCS_FILE_PEM) &&
2316                     !silc_pkcs_load_public_key(hostf, &public_key, 
2317                                                SILC_PKCS_FILE_BIN)))) {
2318       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2319                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
2320                          verify->entity_name : entity);
2321       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2322                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2323       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2324                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2325       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2326                          SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
2327       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2328                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2329       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2330                               format, 0, verify);
2331       g_free(format);
2332       silc_free(fingerprint);
2333       return;
2334     }
2335
2336     /* Encode the key data */
2337     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
2338     if (!encpk) {
2339       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2340                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
2341                          verify->entity_name : entity);
2342       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2343                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2344       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2345                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2346       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2347                          SILCTXT_PUBKEY_MALFORMED, entity);
2348       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2349                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2350       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2351                               format, 0, verify);
2352       g_free(format);
2353       silc_free(fingerprint);
2354       return;
2355     }
2356
2357     /* Compare the keys */
2358     if (memcmp(encpk, pk, encpk_len)) {
2359       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2360                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
2361                          verify->entity_name : entity);
2362       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2363                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2364       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2365                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2366       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2367                          SILCTXT_PUBKEY_NO_MATCH, entity);
2368       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2369                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
2370       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2371                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
2372
2373       /* Ask user to verify the key and save it */
2374       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2375                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2376       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2377                               format, 0, verify);
2378       g_free(format);
2379       silc_free(fingerprint);
2380       return;
2381     }
2382
2383     /* Local copy matched */
2384     if (completion)
2385       completion(TRUE, context);
2386     silc_free(fingerprint);
2387     silc_free(verify->filename);
2388     silc_free(verify->entity);
2389     silc_free(verify->entity_name);
2390     silc_free(verify->pk);
2391     silc_free(verify);
2392   }
2393 }
2394
2395 /* Verifies received public key. The `conn_type' indicates which entity
2396    (server, client etc.) has sent the public key. If user decides to trust
2397    the key may be saved as trusted public key for later use. The 
2398    `completion' must be called after the public key has been verified. */
2399
2400 void 
2401 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
2402                        SilcSocketType conn_type, unsigned char *pk, 
2403                        SilcUInt32 pk_len, SilcSKEPKType pk_type,
2404                        SilcVerifyPublicKey completion, void *context)
2405 {
2406   silc_verify_public_key_internal(client, conn, NULL, conn_type, pk,
2407                                   pk_len, pk_type,
2408                                   completion, context);
2409 }
2410
2411 /* Asks passphrase from user on the input line. */
2412
2413 typedef struct {
2414   SilcAskPassphrase completion;
2415   void *context;
2416 } *AskPassphrase;
2417
2418 void ask_passphrase_completion(const char *passphrase, void *context)
2419 {
2420   AskPassphrase p = (AskPassphrase)context;
2421   if (passphrase && passphrase[0] == '\0')
2422     passphrase = NULL;
2423   p->completion((unsigned char *)passphrase, 
2424                 passphrase ? strlen(passphrase) : 0, p->context);
2425   silc_free(p);
2426 }
2427
2428 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
2429                          SilcAskPassphrase completion, void *context)
2430 {
2431   AskPassphrase p = silc_calloc(1, sizeof(*p));
2432   p->completion = completion;
2433   p->context = context;
2434
2435   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
2436                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
2437 }
2438
2439 typedef struct {
2440   SilcGetAuthMeth completion;
2441   void *context;
2442 } *InternalGetAuthMethod;
2443
2444 /* Callback called when we've received the authentication method information
2445    from the server after we've requested it. This will get the authentication
2446    data from the user if needed. */
2447
2448 static void silc_get_auth_method_callback(SilcClient client,
2449                                           SilcClientConnection conn,
2450                                           SilcAuthMethod auth_meth,
2451                                           void *context)
2452 {
2453   InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
2454
2455   SILC_LOG_DEBUG(("Start"));
2456
2457   switch (auth_meth) {
2458   case SILC_AUTH_NONE:
2459     /* No authentication required. */
2460     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2461     break;
2462   case SILC_AUTH_PASSWORD:
2463     {
2464       /* Check whether we find the password for this server in our
2465          configuration.  If not, then don't provide so library will ask
2466          it from the user. */
2467       SERVER_SETUP_REC *setup = server_setup_find_port(conn->remote_host,
2468                                                        conn->remote_port);
2469       if (!setup || !setup->password) {
2470         (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2471         break;
2472       }
2473       
2474       (*internal->completion)(TRUE, auth_meth, setup->password,
2475                               strlen(setup->password), internal->context);
2476     }
2477     break;
2478   case SILC_AUTH_PUBLIC_KEY:
2479     /* Do not get the authentication data now, the library will generate
2480        it using our default key, if we do not provide it here. */
2481     /* XXX In the future when we support multiple local keys and multiple
2482        local certificates we will need to ask from user which one to use. */
2483     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2484     break;
2485   }
2486
2487   silc_free(internal);
2488 }
2489
2490 /* Find authentication method and authentication data by hostname and
2491    port. The hostname may be IP address as well. The found authentication
2492    method and authentication data is returned to `auth_meth', `auth_data'
2493    and `auth_data_len'. The function returns TRUE if authentication method
2494    is found and FALSE if not. `conn' may be NULL. */
2495
2496 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
2497                           char *hostname, SilcUInt16 port,
2498                           SilcGetAuthMeth completion, void *context)
2499 {
2500   InternalGetAuthMethod internal;
2501
2502   SILC_LOG_DEBUG(("Start"));
2503
2504   /* If we do not have this connection configured by the user in a
2505      configuration file then resolve the authentication method from the
2506      server for this session. */
2507   internal = silc_calloc(1, sizeof(*internal));
2508   internal->completion = completion;
2509   internal->context = context;
2510
2511   silc_client_request_authentication_method(client, conn, 
2512                                             silc_get_auth_method_callback,
2513                                             internal);
2514 }
2515
2516 /* Notifies application that failure packet was received.  This is called
2517    if there is some protocol active in the client.  The `protocol' is the
2518    protocol context.  The `failure' is opaque pointer to the failure
2519    indication.  Note, that the `failure' is protocol dependant and application
2520    must explicitly cast it to correct type.  Usually `failure' is 32 bit
2521    failure type (see protocol specs for all protocol failure types). */
2522
2523 void silc_failure(SilcClient client, SilcClientConnection conn, 
2524                   SilcProtocol protocol, void *failure)
2525 {
2526   SILC_LOG_DEBUG(("Start"));
2527
2528   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
2529     SilcSKEStatus status = (SilcSKEStatus)failure;
2530     
2531     if (status == SILC_SKE_STATUS_BAD_VERSION)
2532       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2533                          SILCTXT_KE_BAD_VERSION);
2534     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
2535       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2536                          SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
2537     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
2538       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2539                          SILCTXT_KE_UNKNOWN_GROUP);
2540     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
2541       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2542                          SILCTXT_KE_UNKNOWN_CIPHER);
2543     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
2544       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2545                          SILCTXT_KE_UNKNOWN_PKCS);
2546     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
2547       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2548                          SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
2549     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
2550       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2551                          SILCTXT_KE_UNKNOWN_HMAC);
2552     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
2553       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2554                          SILCTXT_KE_INCORRECT_SIGNATURE);
2555     if (status == SILC_SKE_STATUS_INVALID_COOKIE)
2556       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2557                          SILCTXT_KE_INVALID_COOKIE);
2558   }
2559
2560   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
2561     SilcUInt32 err = (SilcUInt32)failure;
2562
2563     if (err == SILC_AUTH_FAILED)
2564       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2565                          SILCTXT_AUTH_FAILED);
2566   }
2567 }
2568
2569 /* Asks whether the user would like to perform the key agreement protocol.
2570    This is called after we have received an key agreement packet or an
2571    reply to our key agreement packet. This returns TRUE if the user wants
2572    the library to perform the key agreement protocol and FALSE if it is not
2573    desired (application may start it later by calling the function
2574    silc_client_perform_key_agreement). */
2575
2576 bool silc_key_agreement(SilcClient client, SilcClientConnection conn,
2577                         SilcClientEntry client_entry, const char *hostname,
2578                         SilcUInt16 port, SilcKeyAgreementCallback *completion,
2579                         void **context)
2580 {
2581   char portstr[12];
2582
2583   SILC_LOG_DEBUG(("Start"));
2584
2585   /* We will just display the info on the screen and return FALSE and user
2586      will have to start the key agreement with a command. */
2587
2588   if (hostname) 
2589     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2590
2591   if (!hostname)
2592     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2593                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
2594   else
2595     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2596                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST, 
2597                        client_entry->nickname, hostname, portstr);
2598
2599   *completion = NULL;
2600   *context = NULL;
2601
2602   return FALSE;
2603 }
2604
2605 /* Notifies application that file transfer protocol session is being
2606    requested by the remote client indicated by the `client_entry' from
2607    the `hostname' and `port'. The `session_id' is the file transfer
2608    session and it can be used to either accept or reject the file
2609    transfer request, by calling the silc_client_file_receive or
2610    silc_client_file_close, respectively. */
2611
2612 void silc_ftp(SilcClient client, SilcClientConnection conn,
2613               SilcClientEntry client_entry, SilcUInt32 session_id,
2614               const char *hostname, SilcUInt16 port)
2615 {
2616   SILC_SERVER_REC *server;
2617   char portstr[12];
2618   FtpSession ftp = NULL;
2619
2620   SILC_LOG_DEBUG(("Start"));
2621
2622   server = conn->context;
2623
2624   silc_dlist_start(server->ftp_sessions);
2625   while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
2626     if (ftp->client_entry == client_entry &&
2627         ftp->session_id == session_id) {
2628       server->current_session = ftp;
2629       break;
2630     }
2631   }
2632   if (ftp == SILC_LIST_END) {
2633     ftp = silc_calloc(1, sizeof(*ftp));
2634     ftp->client_entry = client_entry;
2635     ftp->session_id = session_id;
2636     ftp->send = FALSE;
2637     ftp->conn = conn;
2638     silc_dlist_add(server->ftp_sessions, ftp);
2639     server->current_session = ftp;
2640   }
2641
2642   if (hostname) 
2643     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2644
2645   if (!hostname)
2646     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2647                        SILCTXT_FILE_REQUEST, client_entry->nickname);
2648   else
2649     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2650                        SILCTXT_FILE_REQUEST_HOST, 
2651                        client_entry->nickname, hostname, portstr);
2652 }
2653
2654 /* Delivers SILC session detachment data indicated by `detach_data' to the
2655    application.  If application has issued SILC_COMMAND_DETACH command
2656    the client session in the SILC network is not quit.  The client remains
2657    in the network but is detached.  The detachment data may be used later
2658    to resume the session in the SILC Network.  The appliation is
2659    responsible of saving the `detach_data', to for example in a file.
2660
2661    The detachment data can be given as argument to the functions
2662    silc_client_connect_to_server, or silc_client_add_connection when
2663    creating connection to remote server, inside SilcClientConnectionParams
2664    structure.  If it is provided the client library will attempt to resume
2665    the session in the network.  After the connection is created
2666    successfully, the application is responsible of setting the user
2667    interface for user into the same state it was before detaching (showing
2668    same channels, channel modes, etc).  It can do this by fetching the
2669    information (like joined channels) from the client library. */
2670
2671 void
2672 silc_detach(SilcClient client, SilcClientConnection conn,
2673             const unsigned char *detach_data, SilcUInt32 detach_data_len)
2674 {
2675   char file[256];
2676
2677   /* Save the detachment data to file. */
2678
2679   memset(file, 0, sizeof(file));
2680   snprintf(file, sizeof(file) - 1, "%s/session", get_irssi_dir());
2681   silc_file_writefile(file, detach_data, detach_data_len);
2682 }
2683
2684
2685 /* SILC client operations */
2686 SilcClientOperations ops = {
2687   silc_say,
2688   silc_channel_message,
2689   silc_private_message,
2690   silc_notify,
2691   silc_command,
2692   silc_command_reply,
2693   silc_connect,
2694   silc_disconnect,
2695   silc_get_auth_method,
2696   silc_verify_public_key,
2697   silc_ask_passphrase,
2698   silc_failure,
2699   silc_key_agreement,
2700   silc_ftp,
2701   silc_detach,
2702 };