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