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