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