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