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