Added support for auto-passphrase authentication from the config
[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_PUBKEY_VERIFIED, entity, name);
1048   } else {
1049     printformat_module("fe-common/silc", NULL, NULL,
1050                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOTVERIFIED,
1051                        entity, name);
1052   }
1053
1054   silc_free(getkey->fingerprint);
1055   silc_free(getkey);
1056 }
1057
1058 /* Command reply handler. This function is called always in the command reply
1059    function. If error occurs it will be called as well. Normal scenario
1060    is that it will be called after the received command data has been parsed
1061    and processed. The function is used to pass the received command data to
1062    the application. 
1063
1064    `conn' is the associated client connection. `cmd_payload' is the command
1065    payload data received from server and it can be ignored. It is provided
1066    if the application would like to re-parse the received command data,
1067    however, it must be noted that the data is parsed already by the library
1068    thus the payload can be ignored. `success' is FALSE if error occured.
1069    In this case arguments are not sent to the application. `command' is the
1070    command reply being processed. The function has variable argument list
1071    and each command defines the number and type of arguments it passes to the
1072    application (on error they are not sent). */
1073
1074 void 
1075 silc_command_reply(SilcClient client, SilcClientConnection conn,
1076                    SilcCommandPayload cmd_payload, bool success,
1077                    SilcCommand command, SilcStatus status, ...)
1078
1079 {
1080   SILC_SERVER_REC *server = conn->context;
1081   SILC_CHANNEL_REC *chanrec;
1082   va_list vp;
1083
1084   va_start(vp, status);
1085
1086   SILC_LOG_DEBUG(("Start"));
1087
1088   switch(command) {
1089   case SILC_COMMAND_WHOIS:
1090     {
1091       char buf[1024], *nickname, *username, *realname, *nick;
1092       unsigned char *fingerprint;
1093       SilcUInt32 idle, mode;
1094       SilcBuffer channels, user_modes;
1095       SilcClientEntry client_entry;
1096       SilcDList attrs;
1097       
1098       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1099         /* Print the unknown nick for user */
1100         unsigned char *tmp =
1101           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1102                                      3, NULL);
1103         if (tmp)
1104           silc_say_error("%s: %s", tmp, 
1105                          silc_get_status_message(status));
1106         break;
1107       } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1108         /* Try to find the entry for the unknown client ID, since we
1109            might have, and print the nickname of it for user. */
1110         SilcUInt32 tmp_len;
1111         unsigned char *tmp =
1112           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1113                                      2, &tmp_len);
1114         if (tmp) {
1115           SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, 
1116                                                              NULL);
1117           if (client_id) {
1118             client_entry = silc_client_get_client_by_id(client, conn,
1119                                                         client_id);
1120             if (client_entry && client_entry->nickname)
1121               silc_say_error("%s: %s", client_entry->nickname,
1122                              silc_get_status_message(status));
1123             silc_free(client_id);
1124           }
1125         }
1126         break;
1127       } else if (!success) {
1128         silc_say_error("WHOIS: %s", silc_get_status_message(status));
1129         return;
1130       }
1131
1132       client_entry = va_arg(vp, SilcClientEntry);
1133       nickname = va_arg(vp, char *);
1134       username = va_arg(vp, char *);
1135       realname = va_arg(vp, char *);
1136       channels = va_arg(vp, SilcBuffer);
1137       mode = va_arg(vp, SilcUInt32);
1138       idle = va_arg(vp, SilcUInt32);
1139       fingerprint = va_arg(vp, unsigned char *);
1140       user_modes = va_arg(vp, SilcBuffer);
1141       attrs = va_arg(vp, SilcDList);
1142       
1143       silc_parse_userfqdn(nickname, &nick, NULL);
1144       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1145                          SILCTXT_WHOIS_USERINFO, nickname, 
1146                          client_entry->username, client_entry->hostname,
1147                          nick, client_entry->nickname);
1148       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1149                          SILCTXT_WHOIS_REALNAME, realname);
1150       silc_free(nick);
1151
1152       if (channels && user_modes) {
1153         SilcUInt32 *umodes;
1154         SilcDList list = silc_channel_payload_parse_list(channels->data,
1155                                                          channels->len);
1156         if (list && silc_get_mode_list(user_modes, silc_dlist_count(list),
1157                                        &umodes)) {
1158           SilcChannelPayload entry;
1159           int i = 0;
1160
1161           memset(buf, 0, sizeof(buf));
1162           silc_dlist_start(list);
1163           while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
1164             SilcUInt32 name_len;
1165             char *m = silc_client_chumode_char(umodes[i++]);
1166             char *name = silc_channel_get_name(entry, &name_len);
1167             
1168             if (m)
1169               strncat(buf, m, strlen(m));
1170             strncat(buf, name, name_len);
1171             strncat(buf, " ", 1);
1172             silc_free(m);
1173           }
1174
1175           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1176                              SILCTXT_WHOIS_CHANNELS, buf);
1177           silc_channel_payload_list_free(list);
1178           silc_free(umodes);
1179         }
1180       }
1181       
1182       if (mode) {
1183         memset(buf, 0, sizeof(buf));
1184         silc_get_umode_string(mode, buf, sizeof(buf - 1));
1185         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1186                            SILCTXT_WHOIS_MODES, buf);
1187       }
1188       
1189       if (idle && nickname) {
1190         memset(buf, 0, sizeof(buf));
1191         snprintf(buf, sizeof(buf) - 1, "%lu %s",
1192                  idle > 60 ? (idle / 60) : idle,
1193                  idle > 60 ? "minutes" : "seconds");
1194
1195         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1196                            SILCTXT_WHOIS_IDLE, buf);
1197       }
1198
1199       if (fingerprint) {
1200         fingerprint = silc_fingerprint(fingerprint, 20);
1201         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1202                            SILCTXT_WHOIS_FINGERPRINT, fingerprint);
1203         silc_free(fingerprint);
1204       }
1205
1206       if (attrs)
1207         silc_query_attributes_print(server, silc_client, conn, attrs,
1208                                     client_entry);
1209     }
1210     break;
1211     
1212   case SILC_COMMAND_IDENTIFY:
1213     {
1214       SilcClientEntry client_entry;
1215       
1216       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1217         /* Print the unknown nick for user */
1218         unsigned char *tmp =
1219           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1220                                      3, NULL);
1221         if (tmp)
1222           silc_say_error("%s: %s", tmp, 
1223                          silc_get_status_message(status));
1224         break;
1225       } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1226         /* Try to find the entry for the unknown client ID, since we
1227            might have, and print the nickname of it for user. */
1228         SilcUInt32 tmp_len;
1229         unsigned char *tmp =
1230           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1231                                      2, &tmp_len);
1232         if (tmp) {
1233           SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len,
1234                                                              NULL);
1235           if (client_id) {
1236             client_entry = silc_client_get_client_by_id(client, conn,
1237                                                         client_id);
1238             if (client_entry && client_entry->nickname)
1239               silc_say_error("%s: %s", client_entry->nickname,
1240                              silc_get_status_message(status));
1241             silc_free(client_id);
1242           }
1243         }
1244         break;
1245       }
1246
1247       break;
1248     }
1249
1250   case SILC_COMMAND_WHOWAS:
1251     {
1252       char *nickname, *username, *realname;
1253       
1254       if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
1255           status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1256         char *tmp;
1257         tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1258                                          3, NULL);
1259         if (tmp)
1260           silc_say_error("%s: %s", tmp, 
1261                          silc_get_status_message(status));
1262         break;
1263       } else if (!success) {
1264         silc_say_error("WHOWAS: %s", silc_get_status_message(status));
1265         return;
1266       }
1267       
1268       (void)va_arg(vp, SilcClientEntry);
1269       nickname = va_arg(vp, char *);
1270       username = va_arg(vp, char *);
1271       realname = va_arg(vp, char *);
1272       
1273       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1274                          SILCTXT_WHOWAS_USERINFO, nickname, username, 
1275                          realname ? realname : "");
1276     }
1277     break;
1278     
1279   case SILC_COMMAND_INVITE:
1280     {
1281       SilcChannelEntry channel;
1282       char *invite_list;
1283       SilcArgumentPayload args;
1284       int argc = 0;
1285       
1286       if (!success)
1287         return;
1288       
1289       channel = va_arg(vp, SilcChannelEntry);
1290       invite_list = va_arg(vp, char *);
1291
1292       args = silc_command_get_args(cmd_payload);
1293       if (args)
1294         argc = silc_argument_get_arg_num(args);
1295
1296       if (invite_list)
1297         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1298                            SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
1299                            invite_list);
1300       else if (argc == 3)
1301         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1302                            SILCTXT_CHANNEL_NO_INVITE_LIST, 
1303                            channel->channel_name);
1304     }
1305     break;
1306
1307   case SILC_COMMAND_JOIN: 
1308     {
1309       char *channel, *mode, *topic;
1310       SilcUInt32 modei;
1311       SilcChannelEntry channel_entry;
1312       SilcBuffer client_id_list;
1313       SilcUInt32 list_count;
1314
1315       if (!success)
1316         return;
1317
1318       channel = va_arg(vp, char *);
1319       channel_entry = va_arg(vp, SilcChannelEntry);
1320       modei = va_arg(vp, SilcUInt32);
1321       (void)va_arg(vp, SilcUInt32);
1322       (void)va_arg(vp, unsigned char *);
1323       (void)va_arg(vp, unsigned char *);
1324       (void)va_arg(vp, unsigned char *);
1325       topic = va_arg(vp, char *);
1326       (void)va_arg(vp, unsigned char *);
1327       list_count = va_arg(vp, SilcUInt32);
1328       client_id_list = va_arg(vp, SilcBuffer);
1329
1330       chanrec = silc_channel_find(server, channel);
1331       if (!chanrec)
1332         chanrec = silc_channel_create(server, channel, TRUE);
1333
1334       if (topic) {
1335         g_free_not_null(chanrec->topic);
1336         chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1337         signal_emit("channel topic changed", 1, chanrec);
1338       }
1339
1340       mode = silc_client_chmode(modei, 
1341                                 channel_entry->channel_key ? 
1342                                 channel_entry->channel_key->cipher->name : "",
1343                                 channel_entry->hmac ? 
1344                                 silc_hmac_get_name(channel_entry->hmac) : "");
1345       g_free_not_null(chanrec->mode);
1346       chanrec->mode = g_strdup(mode == NULL ? "" : mode);
1347       signal_emit("channel mode changed", 1, chanrec);
1348
1349       /* Resolve the client information */
1350       silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
1351                                       silc_client_join_get_users, 
1352                                       channel_entry);
1353
1354       break;
1355     }
1356
1357   case SILC_COMMAND_NICK: 
1358     {
1359       char *old;
1360       SilcClientEntry client = va_arg(vp, SilcClientEntry);
1361       
1362       if (!success)
1363         return;
1364
1365       old = g_strdup(server->nick);
1366       server_change_nick(SERVER(server), client->nickname);
1367       nicklist_rename_unique(SERVER(server),
1368                              server->conn->local_entry, server->nick,
1369                              client, client->nickname);
1370       signal_emit("message own_nick", 4, server, server->nick, old, "");
1371       g_free(old);
1372       break;
1373     }
1374     
1375   case SILC_COMMAND_LIST:
1376     {
1377       char *topic, *name;
1378       int usercount;
1379       char users[20];
1380       
1381       if (!success)
1382         return;
1383       
1384       (void)va_arg(vp, SilcChannelEntry);
1385       name = va_arg(vp, char *);
1386       topic = va_arg(vp, char *);
1387       usercount = va_arg(vp, int);
1388       
1389       if (status == SILC_STATUS_LIST_START ||
1390           status == SILC_STATUS_OK)
1391         printformat_module("fe-common/silc", server, NULL,
1392                            MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
1393
1394       if (!usercount)
1395         snprintf(users, sizeof(users) - 1, "N/A");
1396       else
1397         snprintf(users, sizeof(users) - 1, "%d", usercount);
1398       printformat_module("fe-common/silc", server, NULL,
1399                          MSGLEVEL_CRAP, SILCTXT_LIST,
1400                          name, users, topic ? topic : "");
1401     }
1402     break;
1403     
1404   case SILC_COMMAND_UMODE:
1405     {
1406       SilcUInt32 mode;
1407       
1408       if (!success)
1409         return;
1410       
1411       mode = va_arg(vp, SilcUInt32);
1412       
1413       if (mode & SILC_UMODE_SERVER_OPERATOR &&
1414           !(server->umode & SILC_UMODE_SERVER_OPERATOR))
1415         printformat_module("fe-common/silc", server, NULL,
1416                            MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1417
1418       if (mode & SILC_UMODE_ROUTER_OPERATOR &&
1419           !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
1420         printformat_module("fe-common/silc", server, NULL,
1421                            MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1422
1423       server->umode = mode;
1424       signal_emit("user mode changed", 2, server, NULL);
1425     }
1426     break;
1427     
1428   case SILC_COMMAND_OPER:
1429     if (!success)
1430       return;
1431
1432     server->umode |= SILC_UMODE_SERVER_OPERATOR;
1433     signal_emit("user mode changed", 2, server, NULL);
1434
1435     printformat_module("fe-common/silc", server, NULL,
1436                        MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1437     break;
1438     
1439   case SILC_COMMAND_SILCOPER:
1440     if (!success)
1441       return;
1442
1443     server->umode |= SILC_UMODE_ROUTER_OPERATOR;
1444     signal_emit("user mode changed", 2, server, NULL);
1445
1446     printformat_module("fe-common/silc", server, NULL,
1447                        MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1448     break;
1449     
1450   case SILC_COMMAND_USERS: 
1451     {
1452       SilcHashTableList htl;
1453       SilcChannelEntry channel;
1454       SilcChannelUser chu;
1455       
1456       if (!success)
1457         return;
1458       
1459       channel = va_arg(vp, SilcChannelEntry);
1460       
1461       printformat_module("fe-common/silc", server, channel->channel_name,
1462                          MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
1463                          channel->channel_name);
1464
1465       silc_hash_table_list(channel->user_list, &htl);
1466       while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1467         SilcClientEntry e = chu->client;
1468         char stat[5], *mode;
1469
1470         if (!e->nickname)
1471           continue;
1472         
1473         memset(stat, 0, sizeof(stat));
1474         mode = silc_client_chumode_char(chu->mode);
1475         if (e->mode & SILC_UMODE_GONE)
1476           strcat(stat, "G");
1477         else if (e->mode & SILC_UMODE_INDISPOSED)
1478           strcat(stat, "I");
1479         else if (e->mode & SILC_UMODE_BUSY)
1480           strcat(stat, "B");
1481         else if (e->mode & SILC_UMODE_PAGE)
1482           strcat(stat, "P");
1483         else if (e->mode & SILC_UMODE_HYPER)
1484           strcat(stat, "H");
1485         else if (e->mode & SILC_UMODE_ROBOT)
1486           strcat(stat, "R");
1487         else if (e->mode & SILC_UMODE_ANONYMOUS)
1488           strcat(stat, "?");
1489         else
1490           strcat(stat, "A");
1491         if (mode)
1492           strcat(stat, mode);
1493
1494         printformat_module("fe-common/silc", server, channel->channel_name,
1495                            MSGLEVEL_CRAP, SILCTXT_USERS,
1496                            e->nickname, stat, 
1497                            e->username ? e->username : "",
1498                            e->hostname ? e->hostname : "",
1499                            e->realname ? e->realname : "");
1500         if (mode)
1501           silc_free(mode);
1502       }
1503       silc_hash_table_list_reset(&htl);
1504     }
1505     break;
1506
1507   case SILC_COMMAND_BAN:
1508     {
1509       SilcChannelEntry channel;
1510       char *ban_list;
1511       
1512       if (!success)
1513         return;
1514       
1515       channel = va_arg(vp, SilcChannelEntry);
1516       ban_list = va_arg(vp, char *);
1517       
1518       if (ban_list)
1519         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1520                            SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
1521                            ban_list);
1522       else
1523         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1524                            SILCTXT_CHANNEL_NO_BAN_LIST, 
1525                            channel->channel_name);
1526     }
1527     break;
1528     
1529   case SILC_COMMAND_GETKEY:
1530     {
1531       SilcIdType id_type;
1532       void *entry;
1533       SilcPublicKey public_key;
1534       unsigned char *pk;
1535       SilcUInt32 pk_len;
1536       GetkeyContext getkey;
1537       char *name;
1538       
1539       if (!success)
1540         return;
1541       
1542       id_type = va_arg(vp, SilcUInt32);
1543       entry = va_arg(vp, void *);
1544       public_key = va_arg(vp, SilcPublicKey);
1545
1546       if (public_key) {
1547         pk = silc_pkcs_public_key_encode(public_key, &pk_len);
1548
1549         getkey = silc_calloc(1, sizeof(*getkey));
1550         getkey->entry = entry;
1551         getkey->id_type = id_type;
1552         getkey->client = client;
1553         getkey->conn = conn;
1554         getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1555
1556         name = (id_type == SILC_ID_CLIENT ? 
1557                 ((SilcClientEntry)entry)->nickname :
1558                 ((SilcServerEntry)entry)->server_name);
1559
1560         silc_verify_public_key_internal(client, conn, name,
1561                                         (id_type == SILC_ID_CLIENT ?
1562                                          SILC_SOCKET_TYPE_CLIENT :
1563                                          SILC_SOCKET_TYPE_SERVER),
1564                                         pk, pk_len, SILC_SKE_PK_TYPE_SILC,
1565                                         silc_getkey_cb, getkey);
1566         silc_free(pk);
1567       } else {
1568         printformat_module("fe-common/silc", server, NULL,
1569                            MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOKEY);
1570       }
1571     }
1572     break;
1573
1574   case SILC_COMMAND_INFO:
1575     {
1576       SilcServerEntry server_entry;
1577       char *server_name;
1578       char *server_info;
1579
1580       if (!success)
1581         return;
1582       
1583       server_entry = va_arg(vp, SilcServerEntry);
1584       server_name = va_arg(vp, char *);
1585       server_info = va_arg(vp, char *);
1586
1587       if (server_name && server_info )
1588         {
1589           printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
1590           printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
1591         }
1592     }
1593     break;
1594     
1595   case SILC_COMMAND_TOPIC:
1596     {
1597       SilcChannelEntry channel;
1598       char *topic;
1599       
1600       if (!success)
1601         return;
1602       
1603       channel = va_arg(vp, SilcChannelEntry);
1604       topic = va_arg(vp, char *);
1605       
1606       if (topic) {
1607         chanrec = silc_channel_find_entry(server, channel);
1608         if (chanrec) {
1609           g_free_not_null(chanrec->topic);
1610           chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1611           signal_emit("channel topic changed", 1, chanrec);
1612         }
1613         printformat_module("fe-common/silc", server, channel->channel_name,
1614                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
1615                            channel->channel_name, topic);
1616       } else {
1617         printformat_module("fe-common/silc", server, channel->channel_name,
1618                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
1619                            channel->channel_name);
1620       }
1621     }
1622     break;
1623
1624   case SILC_COMMAND_WATCH:
1625     break;
1626   
1627   case SILC_COMMAND_STATS:
1628     {
1629       SilcUInt32 starttime, uptime, my_clients, my_channels, my_server_ops,
1630                  my_router_ops, cell_clients, cell_channels, cell_servers,
1631                  clients, channels, servers, routers, server_ops, router_ops;
1632       SilcUInt32 buf_len;
1633       SilcBufferStruct buf;
1634       unsigned char *tmp_buf;
1635       char tmp[40];
1636       const char *tmptime;
1637       int days, hours, mins, secs;
1638
1639       if (!success)
1640         return;
1641
1642       tmp_buf = va_arg(vp, unsigned char *);
1643       buf_len = va_arg(vp, SilcUInt32);
1644
1645       if (!tmp_buf || !buf_len) {
1646         printtext(server, NULL, MSGLEVEL_CRAP, "No statistics available");
1647         return;
1648       }
1649
1650       /* Get statistics structure */
1651       silc_buffer_set(&buf, tmp_buf, buf_len);
1652       silc_buffer_unformat(&buf,
1653                            SILC_STR_UI_INT(&starttime),
1654                            SILC_STR_UI_INT(&uptime),
1655                            SILC_STR_UI_INT(&my_clients),
1656                            SILC_STR_UI_INT(&my_channels),
1657                            SILC_STR_UI_INT(&my_server_ops),
1658                            SILC_STR_UI_INT(&my_router_ops),
1659                            SILC_STR_UI_INT(&cell_clients),
1660                            SILC_STR_UI_INT(&cell_channels),
1661                            SILC_STR_UI_INT(&cell_servers),
1662                            SILC_STR_UI_INT(&clients),
1663                            SILC_STR_UI_INT(&channels),
1664                            SILC_STR_UI_INT(&servers),
1665                            SILC_STR_UI_INT(&routers),
1666                            SILC_STR_UI_INT(&server_ops),
1667                            SILC_STR_UI_INT(&router_ops),
1668                            SILC_STR_END);
1669
1670       tmptime = silc_get_time(starttime);
1671       printformat_module("fe-common/silc", server, NULL,
1672                          MSGLEVEL_CRAP, SILCTXT_STATS,
1673                          "Local server start time", tmptime);
1674
1675       days = uptime / (24 * 60 * 60);
1676       uptime -= days * (24 * 60 * 60);
1677       hours = uptime / (60 * 60);
1678       uptime -= hours * (60 * 60);
1679       mins = uptime / 60;
1680       uptime -= mins * 60;
1681       secs = uptime;
1682       snprintf(tmp, sizeof(tmp) - 1, "%d days %d hours %d mins %d secs",
1683                days, hours, mins, secs);
1684       printformat_module("fe-common/silc", server, NULL,
1685                          MSGLEVEL_CRAP, SILCTXT_STATS,
1686                          "Local server uptime", tmp);
1687
1688       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_clients);
1689       printformat_module("fe-common/silc", server, NULL,
1690                          MSGLEVEL_CRAP, SILCTXT_STATS,
1691                          "Local server clients", tmp);
1692
1693       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_channels);
1694       printformat_module("fe-common/silc", server, NULL,
1695                          MSGLEVEL_CRAP, SILCTXT_STATS,
1696                          "Local server channels", tmp);
1697
1698       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_server_ops);
1699       printformat_module("fe-common/silc", server, NULL,
1700                          MSGLEVEL_CRAP, SILCTXT_STATS,
1701                          "Local server operators", tmp);
1702
1703       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_router_ops);
1704       printformat_module("fe-common/silc", server, NULL,
1705                          MSGLEVEL_CRAP, SILCTXT_STATS,
1706                          "Local router operators", tmp);
1707
1708       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_clients);
1709       printformat_module("fe-common/silc", server, NULL,
1710                          MSGLEVEL_CRAP, SILCTXT_STATS,
1711                          "Local cell clients", tmp);
1712
1713       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_channels);
1714       printformat_module("fe-common/silc", server, NULL,
1715                          MSGLEVEL_CRAP, SILCTXT_STATS,
1716                          "Local cell channels", tmp);
1717
1718       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_servers);
1719       printformat_module("fe-common/silc", server, NULL,
1720                          MSGLEVEL_CRAP, SILCTXT_STATS,
1721                          "Local cell servers", tmp);
1722
1723       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)clients);
1724       printformat_module("fe-common/silc", server, NULL,
1725                          MSGLEVEL_CRAP, SILCTXT_STATS,
1726                          "Total clients", tmp);
1727
1728       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)channels);
1729       printformat_module("fe-common/silc", server, NULL,
1730                          MSGLEVEL_CRAP, SILCTXT_STATS,
1731                          "Total channels", tmp);
1732
1733       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)servers);
1734       printformat_module("fe-common/silc", server, NULL,
1735                          MSGLEVEL_CRAP, SILCTXT_STATS,
1736                          "Total servers", tmp);
1737
1738       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)routers);
1739       printformat_module("fe-common/silc", server, NULL,
1740                          MSGLEVEL_CRAP, SILCTXT_STATS,
1741                          "Total routers", tmp);
1742
1743       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)server_ops);
1744       printformat_module("fe-common/silc", server, NULL,
1745                          MSGLEVEL_CRAP, SILCTXT_STATS,
1746                            "Total server operators", tmp);
1747
1748       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)router_ops);
1749       printformat_module("fe-common/silc", server, NULL,
1750                          MSGLEVEL_CRAP, SILCTXT_STATS,
1751                          "Total router operators", tmp);
1752     }
1753     break;
1754
1755   }
1756
1757   va_end(vp);
1758 }
1759
1760 typedef struct {
1761   SilcClient client;
1762   SilcClientConnection conn;
1763   char *filename;
1764   char *entity;
1765   char *entity_name;
1766   unsigned char *pk;
1767   SilcUInt32 pk_len;
1768   SilcSKEPKType pk_type;
1769   SilcVerifyPublicKey completion;
1770   void *context;
1771 } *PublicKeyVerify;
1772
1773 static void verify_public_key_completion(const char *line, void *context)
1774 {
1775   PublicKeyVerify verify = (PublicKeyVerify)context;
1776
1777   if (line[0] == 'Y' || line[0] == 'y') {
1778     /* Call the completion */
1779     if (verify->completion)
1780       verify->completion(TRUE, verify->context);
1781
1782     /* Save the key for future checking */
1783     silc_pkcs_save_public_key_data(verify->filename, verify->pk, 
1784                                    verify->pk_len, SILC_PKCS_FILE_PEM);
1785   } else {
1786     /* Call the completion */
1787     if (verify->completion)
1788       verify->completion(FALSE, verify->context);
1789
1790     printformat_module("fe-common/silc", NULL, NULL,
1791                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, 
1792                        verify->entity_name ? verify->entity_name :
1793                        verify->entity);
1794   }
1795
1796   silc_free(verify->filename);
1797   silc_free(verify->entity);
1798   silc_free(verify->entity_name);
1799   silc_free(verify->pk);
1800   silc_free(verify);
1801 }
1802
1803 /* Internal routine to verify public key. If the `completion' is provided
1804    it will be called to indicate whether public was verified or not. For
1805    server/router public key this will check for filename that includes the
1806    remote host's IP address and remote host's hostname. */
1807
1808 static void 
1809 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
1810                                 const char *name, SilcSocketType conn_type, 
1811                                 unsigned char *pk, SilcUInt32 pk_len, 
1812                                 SilcSKEPKType pk_type,
1813                                 SilcVerifyPublicKey completion, void *context)
1814 {
1815   int i;
1816   char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
1817   char *fingerprint, *babbleprint, *format;
1818   struct passwd *pw;
1819   struct stat st;
1820   char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
1821                    conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
1822                   "server" : "client");
1823   PublicKeyVerify verify;
1824
1825   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
1826     printformat_module("fe-common/silc", NULL, NULL,
1827                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED, 
1828                        entity, pk_type);
1829     if (completion)
1830       completion(FALSE, context);
1831     return;
1832   }
1833
1834   pw = getpwuid(getuid());
1835   if (!pw) {
1836     if (completion)
1837       completion(FALSE, context);
1838     return;
1839   }
1840
1841   memset(filename, 0, sizeof(filename));
1842   memset(filename2, 0, sizeof(filename2));
1843   memset(file, 0, sizeof(file));
1844
1845   if (conn_type == SILC_SOCKET_TYPE_SERVER ||
1846       conn_type == SILC_SOCKET_TYPE_ROUTER) {
1847     if (!name) {
1848       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1849                conn->sock->ip, conn->sock->port);
1850       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
1851                get_irssi_dir(), entity, file);
1852       
1853       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1854                conn->sock->hostname, conn->sock->port);
1855       snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s", 
1856                get_irssi_dir(), entity, file);
1857       
1858       ipf = filename;
1859       hostf = filename2;
1860     } else {
1861       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1862                name, conn->sock->port);
1863       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
1864                get_irssi_dir(), entity, file);
1865       
1866       ipf = filename;
1867     }
1868   } else {
1869     /* Replace all whitespaces with `_'. */
1870     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1871     for (i = 0; i < strlen(fingerprint); i++)
1872       if (fingerprint[i] == ' ')
1873         fingerprint[i] = '_';
1874     
1875     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
1876     snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
1877              get_irssi_dir(), entity, file);
1878     silc_free(fingerprint);
1879
1880     ipf = filename;
1881   }
1882
1883   /* Take fingerprint of the public key */
1884   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1885   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
1886
1887   verify = silc_calloc(1, sizeof(*verify));
1888   verify->client = client;
1889   verify->conn = conn;
1890   verify->filename = strdup(ipf);
1891   verify->entity = strdup(entity);
1892   verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ?
1893                          (name ? strdup(name) : strdup(conn->sock->hostname))
1894                          : NULL);
1895   verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
1896   memcpy(verify->pk, pk, pk_len);
1897   verify->pk_len = pk_len;
1898   verify->pk_type = pk_type;
1899   verify->completion = completion;
1900   verify->context = context;
1901
1902   /* Check whether this key already exists */
1903   if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
1904     /* Key does not exist, ask user to verify the key and save it */
1905
1906     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1907                        SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1908                        verify->entity_name : entity);
1909     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1910                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1911     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1912                        SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1913     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1914                              SILCTXT_PUBKEY_ACCEPT);
1915     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1916                             format, 0, verify);
1917     g_free(format);
1918     silc_free(fingerprint);
1919     return;
1920   } else {
1921     /* The key already exists, verify it. */
1922     SilcPublicKey public_key;
1923     unsigned char *encpk;
1924     SilcUInt32 encpk_len;
1925
1926     /* Load the key file, try for both IP filename and hostname filename */
1927     if (!silc_pkcs_load_public_key(ipf, &public_key, 
1928                                    SILC_PKCS_FILE_PEM) &&
1929         !silc_pkcs_load_public_key(ipf, &public_key, 
1930                                    SILC_PKCS_FILE_BIN) &&
1931         (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key, 
1932                                                SILC_PKCS_FILE_PEM) &&
1933                     !silc_pkcs_load_public_key(hostf, &public_key, 
1934                                                SILC_PKCS_FILE_BIN)))) {
1935       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1936                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1937                          verify->entity_name : entity);
1938       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1939                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1940       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1941                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1942       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1943                          SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
1944       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1945                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1946       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1947                               format, 0, verify);
1948       g_free(format);
1949       silc_free(fingerprint);
1950       return;
1951     }
1952
1953     /* Encode the key data */
1954     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
1955     if (!encpk) {
1956       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1957                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1958                          verify->entity_name : entity);
1959       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1960                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1961       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1962                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1963       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1964                          SILCTXT_PUBKEY_MALFORMED, entity);
1965       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1966                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1967       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1968                               format, 0, verify);
1969       g_free(format);
1970       silc_free(fingerprint);
1971       return;
1972     }
1973
1974     /* Compare the keys */
1975     if (memcmp(encpk, pk, encpk_len)) {
1976       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1977                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1978                          verify->entity_name : entity);
1979       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1980                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1981       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1982                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1983       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1984                          SILCTXT_PUBKEY_NO_MATCH, entity);
1985       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1986                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
1987       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1988                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
1989
1990       /* Ask user to verify the key and save it */
1991       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1992                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1993       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1994                               format, 0, verify);
1995       g_free(format);
1996       silc_free(fingerprint);
1997       return;
1998     }
1999
2000     /* Local copy matched */
2001     if (completion)
2002       completion(TRUE, context);
2003     silc_free(fingerprint);
2004   }
2005 }
2006
2007 /* Verifies received public key. The `conn_type' indicates which entity
2008    (server, client etc.) has sent the public key. If user decides to trust
2009    the key may be saved as trusted public key for later use. The 
2010    `completion' must be called after the public key has been verified. */
2011
2012 void 
2013 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
2014                        SilcSocketType conn_type, unsigned char *pk, 
2015                        SilcUInt32 pk_len, SilcSKEPKType pk_type,
2016                        SilcVerifyPublicKey completion, void *context)
2017 {
2018   silc_verify_public_key_internal(client, conn, NULL, conn_type, pk,
2019                                   pk_len, pk_type,
2020                                   completion, context);
2021 }
2022
2023 /* Asks passphrase from user on the input line. */
2024
2025 typedef struct {
2026   SilcAskPassphrase completion;
2027   void *context;
2028 } *AskPassphrase;
2029
2030 void ask_passphrase_completion(const char *passphrase, void *context)
2031 {
2032   AskPassphrase p = (AskPassphrase)context;
2033   if (passphrase && passphrase[0] == '\0')
2034     passphrase = NULL;
2035   p->completion((unsigned char *)passphrase, 
2036                 passphrase ? strlen(passphrase) : 0, p->context);
2037   silc_free(p);
2038 }
2039
2040 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
2041                          SilcAskPassphrase completion, void *context)
2042 {
2043   AskPassphrase p = silc_calloc(1, sizeof(*p));
2044   p->completion = completion;
2045   p->context = context;
2046
2047   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
2048                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
2049 }
2050
2051 typedef struct {
2052   SilcGetAuthMeth completion;
2053   void *context;
2054 } *InternalGetAuthMethod;
2055
2056 /* Callback called when we've received the authentication method information
2057    from the server after we've requested it. This will get the authentication
2058    data from the user if needed. */
2059
2060 static void silc_get_auth_method_callback(SilcClient client,
2061                                           SilcClientConnection conn,
2062                                           SilcAuthMethod auth_meth,
2063                                           void *context)
2064 {
2065   InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
2066
2067   SILC_LOG_DEBUG(("Start"));
2068
2069   switch (auth_meth) {
2070   case SILC_AUTH_NONE:
2071     /* No authentication required. */
2072     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2073     break;
2074   case SILC_AUTH_PASSWORD:
2075     {
2076       /* Check whether we find the password for this server in our
2077          configuration.  If not, then don't provide so library will ask
2078          it from the user. */
2079       SERVER_SETUP_REC *setup = server_setup_find_port(conn->remote_host,
2080                                                        conn->remote_port);
2081       if (!setup || !setup->password) {
2082         (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2083         break;
2084       }
2085       
2086       (*internal->completion)(TRUE, auth_meth, setup->password,
2087                               strlen(setup->password), internal->context);
2088     }
2089     break;
2090   case SILC_AUTH_PUBLIC_KEY:
2091     /* Do not get the authentication data now, the library will generate
2092        it using our default key, if we do not provide it here. */
2093     /* XXX In the future when we support multiple local keys and multiple
2094        local certificates we will need to ask from user which one to use. */
2095     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2096     break;
2097   }
2098
2099   silc_free(internal);
2100 }
2101
2102 /* Find authentication method and authentication data by hostname and
2103    port. The hostname may be IP address as well. The found authentication
2104    method and authentication data is returned to `auth_meth', `auth_data'
2105    and `auth_data_len'. The function returns TRUE if authentication method
2106    is found and FALSE if not. `conn' may be NULL. */
2107
2108 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
2109                           char *hostname, SilcUInt16 port,
2110                           SilcGetAuthMeth completion, void *context)
2111 {
2112   InternalGetAuthMethod internal;
2113
2114   SILC_LOG_DEBUG(("Start"));
2115
2116   /* If we do not have this connection configured by the user in a
2117      configuration file then resolve the authentication method from the
2118      server for this session. */
2119   internal = silc_calloc(1, sizeof(*internal));
2120   internal->completion = completion;
2121   internal->context = context;
2122
2123   silc_client_request_authentication_method(client, conn, 
2124                                             silc_get_auth_method_callback,
2125                                             internal);
2126 }
2127
2128 /* Notifies application that failure packet was received.  This is called
2129    if there is some protocol active in the client.  The `protocol' is the
2130    protocol context.  The `failure' is opaque pointer to the failure
2131    indication.  Note, that the `failure' is protocol dependant and application
2132    must explicitly cast it to correct type.  Usually `failure' is 32 bit
2133    failure type (see protocol specs for all protocol failure types). */
2134
2135 void silc_failure(SilcClient client, SilcClientConnection conn, 
2136                   SilcProtocol protocol, void *failure)
2137 {
2138   SILC_LOG_DEBUG(("Start"));
2139
2140   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
2141     SilcSKEStatus status = (SilcSKEStatus)failure;
2142     
2143     if (status == SILC_SKE_STATUS_BAD_VERSION)
2144       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2145                          SILCTXT_KE_BAD_VERSION);
2146     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
2147       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2148                          SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
2149     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
2150       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2151                          SILCTXT_KE_UNKNOWN_GROUP);
2152     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
2153       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2154                          SILCTXT_KE_UNKNOWN_CIPHER);
2155     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
2156       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2157                          SILCTXT_KE_UNKNOWN_PKCS);
2158     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
2159       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2160                          SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
2161     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
2162       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2163                          SILCTXT_KE_UNKNOWN_HMAC);
2164     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
2165       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2166                          SILCTXT_KE_INCORRECT_SIGNATURE);
2167     if (status == SILC_SKE_STATUS_INVALID_COOKIE)
2168       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2169                          SILCTXT_KE_INVALID_COOKIE);
2170   }
2171
2172   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
2173     SilcUInt32 err = (SilcUInt32)failure;
2174
2175     if (err == SILC_AUTH_FAILED)
2176       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2177                          SILCTXT_AUTH_FAILED);
2178   }
2179 }
2180
2181 /* Asks whether the user would like to perform the key agreement protocol.
2182    This is called after we have received an key agreement packet or an
2183    reply to our key agreement packet. This returns TRUE if the user wants
2184    the library to perform the key agreement protocol and FALSE if it is not
2185    desired (application may start it later by calling the function
2186    silc_client_perform_key_agreement). */
2187
2188 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
2189                        SilcClientEntry client_entry, const char *hostname,
2190                        SilcUInt16 port, SilcKeyAgreementCallback *completion,
2191                        void **context)
2192 {
2193   char portstr[12];
2194
2195   SILC_LOG_DEBUG(("Start"));
2196
2197   /* We will just display the info on the screen and return FALSE and user
2198      will have to start the key agreement with a command. */
2199
2200   if (hostname) 
2201     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2202
2203   if (!hostname)
2204     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2205                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
2206   else
2207     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2208                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST, 
2209                        client_entry->nickname, hostname, portstr);
2210
2211   *completion = NULL;
2212   *context = NULL;
2213
2214   return FALSE;
2215 }
2216
2217 /* Notifies application that file transfer protocol session is being
2218    requested by the remote client indicated by the `client_entry' from
2219    the `hostname' and `port'. The `session_id' is the file transfer
2220    session and it can be used to either accept or reject the file
2221    transfer request, by calling the silc_client_file_receive or
2222    silc_client_file_close, respectively. */
2223
2224 void silc_ftp(SilcClient client, SilcClientConnection conn,
2225               SilcClientEntry client_entry, SilcUInt32 session_id,
2226               const char *hostname, SilcUInt16 port)
2227 {
2228   SILC_SERVER_REC *server;
2229   char portstr[12];
2230   FtpSession ftp = silc_calloc(1, sizeof(*ftp));
2231
2232   SILC_LOG_DEBUG(("Start"));
2233
2234   server = conn->context;
2235
2236   ftp->client_entry = client_entry;
2237   ftp->session_id = session_id;
2238   ftp->send = FALSE;
2239   ftp->conn = conn;
2240   silc_dlist_add(server->ftp_sessions, ftp);
2241   server->current_session = ftp;
2242
2243   if (hostname) 
2244     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2245
2246   if (!hostname)
2247     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2248                        SILCTXT_FILE_REQUEST, client_entry->nickname);
2249   else
2250     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2251                        SILCTXT_FILE_REQUEST_HOST, 
2252                        client_entry->nickname, hostname, portstr);
2253 }
2254
2255 /* Delivers SILC session detachment data indicated by `detach_data' to the
2256    application.  If application has issued SILC_COMMAND_DETACH command
2257    the client session in the SILC network is not quit.  The client remains
2258    in the network but is detached.  The detachment data may be used later
2259    to resume the session in the SILC Network.  The appliation is
2260    responsible of saving the `detach_data', to for example in a file.
2261
2262    The detachment data can be given as argument to the functions
2263    silc_client_connect_to_server, or silc_client_add_connection when
2264    creating connection to remote server, inside SilcClientConnectionParams
2265    structure.  If it is provided the client library will attempt to resume
2266    the session in the network.  After the connection is created
2267    successfully, the application is responsible of setting the user
2268    interface for user into the same state it was before detaching (showing
2269    same channels, channel modes, etc).  It can do this by fetching the
2270    information (like joined channels) from the client library. */
2271
2272 void
2273 silc_detach(SilcClient client, SilcClientConnection conn,
2274             const unsigned char *detach_data, SilcUInt32 detach_data_len)
2275 {
2276   char file[256];
2277
2278   /* Save the detachment data to file. */
2279
2280   memset(file, 0, sizeof(file));
2281   snprintf(file, sizeof(file) - 1, "%s/session", get_irssi_dir());
2282   silc_file_writefile(file, detach_data, detach_data_len);
2283 }
2284
2285
2286 /* SILC client operations */
2287 SilcClientOperations ops = {
2288   silc_say,
2289   silc_channel_message,
2290   silc_private_message,
2291   silc_notify,
2292   silc_command,
2293   silc_command_reply,
2294   silc_connect,
2295   silc_disconnect,
2296   silc_get_auth_method,
2297   silc_verify_public_key,
2298   silc_ask_passphrase,
2299   silc_failure,
2300   silc_key_agreement,
2301   silc_ftp,
2302   silc_detach,
2303 };