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