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