Merged c0ffee's MIME signal patch.
[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       }
1120       
1121       if (!success)
1122         return;
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       }
1251       
1252       if (!success)
1253         return;
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
1615   va_end(vp);
1616 }
1617
1618 typedef struct {
1619   SilcClient client;
1620   SilcClientConnection conn;
1621   char *filename;
1622   char *entity;
1623   char *entity_name;
1624   unsigned char *pk;
1625   SilcUInt32 pk_len;
1626   SilcSKEPKType pk_type;
1627   SilcVerifyPublicKey completion;
1628   void *context;
1629 } *PublicKeyVerify;
1630
1631 static void verify_public_key_completion(const char *line, void *context)
1632 {
1633   PublicKeyVerify verify = (PublicKeyVerify)context;
1634
1635   if (line[0] == 'Y' || line[0] == 'y') {
1636     /* Call the completion */
1637     if (verify->completion)
1638       verify->completion(TRUE, verify->context);
1639
1640     /* Save the key for future checking */
1641     silc_pkcs_save_public_key_data(verify->filename, verify->pk, 
1642                                    verify->pk_len, SILC_PKCS_FILE_PEM);
1643   } else {
1644     /* Call the completion */
1645     if (verify->completion)
1646       verify->completion(FALSE, verify->context);
1647
1648     printformat_module("fe-common/silc", NULL, NULL,
1649                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, 
1650                        verify->entity_name ? verify->entity_name :
1651                        verify->entity);
1652   }
1653
1654   silc_free(verify->filename);
1655   silc_free(verify->entity);
1656   silc_free(verify->entity_name);
1657   silc_free(verify->pk);
1658   silc_free(verify);
1659 }
1660
1661 /* Internal routine to verify public key. If the `completion' is provided
1662    it will be called to indicate whether public was verified or not. For
1663    server/router public key this will check for filename that includes the
1664    remote host's IP address and remote host's hostname. */
1665
1666 static void 
1667 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
1668                                 const char *name, SilcSocketType conn_type, 
1669                                 unsigned char *pk, SilcUInt32 pk_len, 
1670                                 SilcSKEPKType pk_type,
1671                                 SilcVerifyPublicKey completion, void *context)
1672 {
1673   int i;
1674   char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
1675   char *fingerprint, *babbleprint, *format;
1676   struct passwd *pw;
1677   struct stat st;
1678   char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
1679                    conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
1680                   "server" : "client");
1681   PublicKeyVerify verify;
1682
1683   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
1684     printformat_module("fe-common/silc", NULL, NULL,
1685                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED, 
1686                        entity, pk_type);
1687     if (completion)
1688       completion(FALSE, context);
1689     return;
1690   }
1691
1692   pw = getpwuid(getuid());
1693   if (!pw) {
1694     if (completion)
1695       completion(FALSE, context);
1696     return;
1697   }
1698
1699   memset(filename, 0, sizeof(filename));
1700   memset(filename2, 0, sizeof(filename2));
1701   memset(file, 0, sizeof(file));
1702
1703   if (conn_type == SILC_SOCKET_TYPE_SERVER ||
1704       conn_type == SILC_SOCKET_TYPE_ROUTER) {
1705     if (!name) {
1706       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1707                conn->sock->ip, conn->sock->port);
1708       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
1709                get_irssi_dir(), entity, file);
1710       
1711       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1712                conn->sock->hostname, conn->sock->port);
1713       snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s", 
1714                get_irssi_dir(), entity, file);
1715       
1716       ipf = filename;
1717       hostf = filename2;
1718     } else {
1719       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1720                name, conn->sock->port);
1721       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
1722                get_irssi_dir(), entity, file);
1723       
1724       ipf = filename;
1725     }
1726   } else {
1727     /* Replace all whitespaces with `_'. */
1728     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1729     for (i = 0; i < strlen(fingerprint); i++)
1730       if (fingerprint[i] == ' ')
1731         fingerprint[i] = '_';
1732     
1733     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
1734     snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
1735              get_irssi_dir(), entity, file);
1736     silc_free(fingerprint);
1737
1738     ipf = filename;
1739   }
1740
1741   /* Take fingerprint of the public key */
1742   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1743   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
1744
1745   verify = silc_calloc(1, sizeof(*verify));
1746   verify->client = client;
1747   verify->conn = conn;
1748   verify->filename = strdup(ipf);
1749   verify->entity = strdup(entity);
1750   verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ?
1751                          (name ? strdup(name) : strdup(conn->sock->hostname))
1752                          : NULL);
1753   verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
1754   memcpy(verify->pk, pk, pk_len);
1755   verify->pk_len = pk_len;
1756   verify->pk_type = pk_type;
1757   verify->completion = completion;
1758   verify->context = context;
1759
1760   /* Check whether this key already exists */
1761   if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
1762     /* Key does not exist, ask user to verify the key and save it */
1763
1764     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1765                        SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1766                        verify->entity_name : entity);
1767     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1768                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1769     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1770                        SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1771     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1772                              SILCTXT_PUBKEY_ACCEPT);
1773     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1774                             format, 0, verify);
1775     g_free(format);
1776     silc_free(fingerprint);
1777     return;
1778   } else {
1779     /* The key already exists, verify it. */
1780     SilcPublicKey public_key;
1781     unsigned char *encpk;
1782     SilcUInt32 encpk_len;
1783
1784     /* Load the key file, try for both IP filename and hostname filename */
1785     if (!silc_pkcs_load_public_key(ipf, &public_key, 
1786                                    SILC_PKCS_FILE_PEM) &&
1787         !silc_pkcs_load_public_key(ipf, &public_key, 
1788                                    SILC_PKCS_FILE_BIN) &&
1789         (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key, 
1790                                                SILC_PKCS_FILE_PEM) &&
1791                     !silc_pkcs_load_public_key(hostf, &public_key, 
1792                                                SILC_PKCS_FILE_BIN)))) {
1793       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1794                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1795                          verify->entity_name : entity);
1796       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1797                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1798       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1799                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1800       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1801                          SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
1802       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1803                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1804       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1805                               format, 0, verify);
1806       g_free(format);
1807       silc_free(fingerprint);
1808       return;
1809     }
1810
1811     /* Encode the key data */
1812     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
1813     if (!encpk) {
1814       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1815                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1816                          verify->entity_name : entity);
1817       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1818                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1819       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1820                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1821       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1822                          SILCTXT_PUBKEY_MALFORMED, entity);
1823       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1824                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1825       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1826                               format, 0, verify);
1827       g_free(format);
1828       silc_free(fingerprint);
1829       return;
1830     }
1831
1832     /* Compare the keys */
1833     if (memcmp(encpk, pk, encpk_len)) {
1834       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1835                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1836                          verify->entity_name : entity);
1837       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1838                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1839       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1840                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1841       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1842                          SILCTXT_PUBKEY_NO_MATCH, entity);
1843       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1844                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
1845       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1846                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
1847
1848       /* Ask user to verify the key and save it */
1849       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1850                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1851       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1852                               format, 0, verify);
1853       g_free(format);
1854       silc_free(fingerprint);
1855       return;
1856     }
1857
1858     /* Local copy matched */
1859     if (completion)
1860       completion(TRUE, context);
1861     silc_free(fingerprint);
1862   }
1863 }
1864
1865 /* Verifies received public key. The `conn_type' indicates which entity
1866    (server, client etc.) has sent the public key. If user decides to trust
1867    the key may be saved as trusted public key for later use. The 
1868    `completion' must be called after the public key has been verified. */
1869
1870 void 
1871 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
1872                        SilcSocketType conn_type, unsigned char *pk, 
1873                        SilcUInt32 pk_len, SilcSKEPKType pk_type,
1874                        SilcVerifyPublicKey completion, void *context)
1875 {
1876   silc_verify_public_key_internal(client, conn, NULL, conn_type, pk,
1877                                   pk_len, pk_type,
1878                                   completion, context);
1879 }
1880
1881 /* Asks passphrase from user on the input line. */
1882
1883 typedef struct {
1884   SilcAskPassphrase completion;
1885   void *context;
1886 } *AskPassphrase;
1887
1888 void ask_passphrase_completion(const char *passphrase, void *context)
1889 {
1890   AskPassphrase p = (AskPassphrase)context;
1891   if (passphrase && passphrase[0] == '\0')
1892     passphrase = NULL;
1893   p->completion((unsigned char *)passphrase, 
1894                 passphrase ? strlen(passphrase) : 0, p->context);
1895   silc_free(p);
1896 }
1897
1898 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1899                          SilcAskPassphrase completion, void *context)
1900 {
1901   AskPassphrase p = silc_calloc(1, sizeof(*p));
1902   p->completion = completion;
1903   p->context = context;
1904
1905   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
1906                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
1907 }
1908
1909 typedef struct {
1910   SilcGetAuthMeth completion;
1911   void *context;
1912 } *InternalGetAuthMethod;
1913
1914 /* Callback called when we've received the authentication method information
1915    from the server after we've requested it. This will get the authentication
1916    data from the user if needed. */
1917
1918 static void silc_get_auth_method_callback(SilcClient client,
1919                                           SilcClientConnection conn,
1920                                           SilcAuthMethod auth_meth,
1921                                           void *context)
1922 {
1923   InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
1924
1925   SILC_LOG_DEBUG(("Start"));
1926
1927   switch (auth_meth) {
1928   case SILC_AUTH_NONE:
1929     /* No authentication required. */
1930     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1931     break;
1932   case SILC_AUTH_PASSWORD:
1933     /* Do not ask the passphrase from user, the library will ask it if
1934        we do not provide it here. */
1935     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1936     break;
1937   case SILC_AUTH_PUBLIC_KEY:
1938     /* Do not get the authentication data now, the library will generate
1939        it using our default key, if we do not provide it here. */
1940     /* XXX In the future when we support multiple local keys and multiple
1941        local certificates we will need to ask from user which one to use. */
1942     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1943     break;
1944   }
1945
1946   silc_free(internal);
1947 }
1948
1949 /* Find authentication method and authentication data by hostname and
1950    port. The hostname may be IP address as well. The found authentication
1951    method and authentication data is returned to `auth_meth', `auth_data'
1952    and `auth_data_len'. The function returns TRUE if authentication method
1953    is found and FALSE if not. `conn' may be NULL. */
1954
1955 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1956                           char *hostname, SilcUInt16 port,
1957                           SilcGetAuthMeth completion, void *context)
1958 {
1959   InternalGetAuthMethod internal;
1960
1961   SILC_LOG_DEBUG(("Start"));
1962
1963   /* XXX must resolve from configuration whether this connection has
1964      any specific authentication data */
1965
1966   /* If we do not have this connection configured by the user in a
1967      configuration file then resolve the authentication method from the
1968      server for this session. */
1969   internal = silc_calloc(1, sizeof(*internal));
1970   internal->completion = completion;
1971   internal->context = context;
1972
1973   silc_client_request_authentication_method(client, conn, 
1974                                             silc_get_auth_method_callback,
1975                                             internal);
1976 }
1977
1978 /* Notifies application that failure packet was received.  This is called
1979    if there is some protocol active in the client.  The `protocol' is the
1980    protocol context.  The `failure' is opaque pointer to the failure
1981    indication.  Note, that the `failure' is protocol dependant and application
1982    must explicitly cast it to correct type.  Usually `failure' is 32 bit
1983    failure type (see protocol specs for all protocol failure types). */
1984
1985 void silc_failure(SilcClient client, SilcClientConnection conn, 
1986                   SilcProtocol protocol, void *failure)
1987 {
1988   SILC_LOG_DEBUG(("Start"));
1989
1990   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1991     SilcSKEStatus status = (SilcSKEStatus)failure;
1992     
1993     if (status == SILC_SKE_STATUS_BAD_VERSION)
1994       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1995                          SILCTXT_KE_BAD_VERSION);
1996     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1997       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1998                          SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1999     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
2000       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2001                          SILCTXT_KE_UNKNOWN_GROUP);
2002     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
2003       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2004                          SILCTXT_KE_UNKNOWN_CIPHER);
2005     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
2006       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2007                          SILCTXT_KE_UNKNOWN_PKCS);
2008     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
2009       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2010                          SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
2011     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
2012       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2013                          SILCTXT_KE_UNKNOWN_HMAC);
2014     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
2015       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2016                          SILCTXT_KE_INCORRECT_SIGNATURE);
2017     if (status == SILC_SKE_STATUS_INVALID_COOKIE)
2018       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2019                          SILCTXT_KE_INVALID_COOKIE);
2020   }
2021
2022   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
2023     SilcUInt32 err = (SilcUInt32)failure;
2024
2025     if (err == SILC_AUTH_FAILED)
2026       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2027                          SILCTXT_AUTH_FAILED);
2028   }
2029 }
2030
2031 /* Asks whether the user would like to perform the key agreement protocol.
2032    This is called after we have received an key agreement packet or an
2033    reply to our key agreement packet. This returns TRUE if the user wants
2034    the library to perform the key agreement protocol and FALSE if it is not
2035    desired (application may start it later by calling the function
2036    silc_client_perform_key_agreement). */
2037
2038 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
2039                        SilcClientEntry client_entry, const char *hostname,
2040                        SilcUInt16 port, SilcKeyAgreementCallback *completion,
2041                        void **context)
2042 {
2043   char portstr[12];
2044
2045   SILC_LOG_DEBUG(("Start"));
2046
2047   /* We will just display the info on the screen and return FALSE and user
2048      will have to start the key agreement with a command. */
2049
2050   if (hostname) 
2051     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2052
2053   if (!hostname)
2054     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2055                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
2056   else
2057     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2058                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST, 
2059                        client_entry->nickname, hostname, portstr);
2060
2061   *completion = NULL;
2062   *context = NULL;
2063
2064   return FALSE;
2065 }
2066
2067 /* Notifies application that file transfer protocol session is being
2068    requested by the remote client indicated by the `client_entry' from
2069    the `hostname' and `port'. The `session_id' is the file transfer
2070    session and it can be used to either accept or reject the file
2071    transfer request, by calling the silc_client_file_receive or
2072    silc_client_file_close, respectively. */
2073
2074 void silc_ftp(SilcClient client, SilcClientConnection conn,
2075               SilcClientEntry client_entry, SilcUInt32 session_id,
2076               const char *hostname, SilcUInt16 port)
2077 {
2078   SILC_SERVER_REC *server;
2079   char portstr[12];
2080   FtpSession ftp = silc_calloc(1, sizeof(*ftp));
2081
2082   SILC_LOG_DEBUG(("Start"));
2083
2084   server = conn->context;
2085
2086   ftp->client_entry = client_entry;
2087   ftp->session_id = session_id;
2088   ftp->send = FALSE;
2089   ftp->conn = conn;
2090   silc_dlist_add(server->ftp_sessions, ftp);
2091   server->current_session = ftp;
2092
2093   if (hostname) 
2094     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2095
2096   if (!hostname)
2097     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2098                        SILCTXT_FILE_REQUEST, client_entry->nickname);
2099   else
2100     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2101                        SILCTXT_FILE_REQUEST_HOST, 
2102                        client_entry->nickname, hostname, portstr);
2103 }
2104
2105 /* Delivers SILC session detachment data indicated by `detach_data' to the
2106    application.  If application has issued SILC_COMMAND_DETACH command
2107    the client session in the SILC network is not quit.  The client remains
2108    in the network but is detached.  The detachment data may be used later
2109    to resume the session in the SILC Network.  The appliation is
2110    responsible of saving the `detach_data', to for example in a file.
2111
2112    The detachment data can be given as argument to the functions
2113    silc_client_connect_to_server, or silc_client_add_connection when
2114    creating connection to remote server, inside SilcClientConnectionParams
2115    structure.  If it is provided the client library will attempt to resume
2116    the session in the network.  After the connection is created
2117    successfully, the application is responsible of setting the user
2118    interface for user into the same state it was before detaching (showing
2119    same channels, channel modes, etc).  It can do this by fetching the
2120    information (like joined channels) from the client library. */
2121
2122 void
2123 silc_detach(SilcClient client, SilcClientConnection conn,
2124             const unsigned char *detach_data, SilcUInt32 detach_data_len)
2125 {
2126   char file[256];
2127
2128   /* Save the detachment data to file. */
2129
2130   memset(file, 0, sizeof(file));
2131   snprintf(file, sizeof(file) - 1, "%s/session", get_irssi_dir());
2132   silc_file_writefile(file, detach_data, detach_data_len);
2133 }
2134
2135
2136 /* SILC client operations */
2137 SilcClientOperations ops = {
2138   silc_say,
2139   silc_channel_message,
2140   silc_private_message,
2141   silc_notify,
2142   silc_command,
2143   silc_command_reply,
2144   silc_connect,
2145   silc_disconnect,
2146   silc_get_auth_method,
2147   silc_verify_public_key,
2148   silc_ask_passphrase,
2149   silc_failure,
2150   silc_key_agreement,
2151   silc_ftp,
2152   silc_detach,
2153 };