Thu Nov 14 09:44:54 CET 2002 Jochen Eisinger <c0ffee@penguin-breeder.org>
[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       char *reason;
1412       
1413       if (!success)
1414         return;
1415       
1416       mode = va_arg(vp, SilcUInt32);
1417       
1418       if (mode & SILC_UMODE_SERVER_OPERATOR &&
1419           !(server->umode & SILC_UMODE_SERVER_OPERATOR))
1420         printformat_module("fe-common/silc", server, NULL,
1421                            MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1422
1423       if (mode & SILC_UMODE_ROUTER_OPERATOR &&
1424           !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
1425         printformat_module("fe-common/silc", server, NULL,
1426                            MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1427
1428       if ((mode & SILC_UMODE_GONE) != (server->umode & SILC_UMODE_GONE)) {
1429         if (mode & SILC_UMODE_GONE) {      
1430           if ((server->away_reason != NULL) && (server->away_reason[0] != '\0'))
1431             reason = g_strdup(server->away_reason);
1432           else
1433             reason = g_strdup("away");
1434         } else
1435           reason = g_strdup("");
1436
1437         silc_set_away(reason, server);
1438
1439         g_free(reason);
1440       }
1441
1442       server->umode = mode;
1443       signal_emit("user mode changed", 2, server, NULL);
1444     }
1445     break;
1446     
1447   case SILC_COMMAND_OPER:
1448     if (!success)
1449       return;
1450
1451     server->umode |= SILC_UMODE_SERVER_OPERATOR;
1452     signal_emit("user mode changed", 2, server, NULL);
1453
1454     printformat_module("fe-common/silc", server, NULL,
1455                        MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1456     break;
1457     
1458   case SILC_COMMAND_SILCOPER:
1459     if (!success)
1460       return;
1461
1462     server->umode |= SILC_UMODE_ROUTER_OPERATOR;
1463     signal_emit("user mode changed", 2, server, NULL);
1464
1465     printformat_module("fe-common/silc", server, NULL,
1466                        MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1467     break;
1468     
1469   case SILC_COMMAND_USERS: 
1470     {
1471       SilcHashTableList htl;
1472       SilcChannelEntry channel;
1473       SilcChannelUser chu;
1474       
1475       if (!success)
1476         return;
1477       
1478       channel = va_arg(vp, SilcChannelEntry);
1479       
1480       printformat_module("fe-common/silc", server, channel->channel_name,
1481                          MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
1482                          channel->channel_name);
1483
1484       silc_hash_table_list(channel->user_list, &htl);
1485       while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1486         SilcClientEntry e = chu->client;
1487         char stat[5], *mode;
1488
1489         if (!e->nickname)
1490           continue;
1491         
1492         memset(stat, 0, sizeof(stat));
1493         mode = silc_client_chumode_char(chu->mode);
1494         if (e->mode & SILC_UMODE_GONE)
1495           strcat(stat, "G");
1496         else if (e->mode & SILC_UMODE_INDISPOSED)
1497           strcat(stat, "I");
1498         else if (e->mode & SILC_UMODE_BUSY)
1499           strcat(stat, "B");
1500         else if (e->mode & SILC_UMODE_PAGE)
1501           strcat(stat, "P");
1502         else if (e->mode & SILC_UMODE_HYPER)
1503           strcat(stat, "H");
1504         else if (e->mode & SILC_UMODE_ROBOT)
1505           strcat(stat, "R");
1506         else if (e->mode & SILC_UMODE_ANONYMOUS)
1507           strcat(stat, "?");
1508         else
1509           strcat(stat, "A");
1510         if (mode)
1511           strcat(stat, mode);
1512
1513         printformat_module("fe-common/silc", server, channel->channel_name,
1514                            MSGLEVEL_CRAP, SILCTXT_USERS,
1515                            e->nickname, stat, 
1516                            e->username ? e->username : "",
1517                            e->hostname ? e->hostname : "",
1518                            e->realname ? e->realname : "");
1519         if (mode)
1520           silc_free(mode);
1521       }
1522       silc_hash_table_list_reset(&htl);
1523     }
1524     break;
1525
1526   case SILC_COMMAND_BAN:
1527     {
1528       SilcChannelEntry channel;
1529       char *ban_list;
1530       
1531       if (!success)
1532         return;
1533       
1534       channel = va_arg(vp, SilcChannelEntry);
1535       ban_list = va_arg(vp, char *);
1536       
1537       if (ban_list)
1538         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1539                            SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
1540                            ban_list);
1541       else
1542         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1543                            SILCTXT_CHANNEL_NO_BAN_LIST, 
1544                            channel->channel_name);
1545     }
1546     break;
1547     
1548   case SILC_COMMAND_GETKEY:
1549     {
1550       SilcIdType id_type;
1551       void *entry;
1552       SilcPublicKey public_key;
1553       unsigned char *pk;
1554       SilcUInt32 pk_len;
1555       GetkeyContext getkey;
1556       char *name;
1557       
1558       if (!success)
1559         return;
1560       
1561       id_type = va_arg(vp, SilcUInt32);
1562       entry = va_arg(vp, void *);
1563       public_key = va_arg(vp, SilcPublicKey);
1564
1565       if (public_key) {
1566         pk = silc_pkcs_public_key_encode(public_key, &pk_len);
1567
1568         getkey = silc_calloc(1, sizeof(*getkey));
1569         getkey->entry = entry;
1570         getkey->id_type = id_type;
1571         getkey->client = client;
1572         getkey->conn = conn;
1573         getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1574
1575         name = (id_type == SILC_ID_CLIENT ? 
1576                 ((SilcClientEntry)entry)->nickname :
1577                 ((SilcServerEntry)entry)->server_name);
1578
1579         silc_verify_public_key_internal(client, conn, name,
1580                                         (id_type == SILC_ID_CLIENT ?
1581                                          SILC_SOCKET_TYPE_CLIENT :
1582                                          SILC_SOCKET_TYPE_SERVER),
1583                                         pk, pk_len, SILC_SKE_PK_TYPE_SILC,
1584                                         silc_getkey_cb, getkey);
1585         silc_free(pk);
1586       } else {
1587         printformat_module("fe-common/silc", server, NULL,
1588                            MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOKEY);
1589       }
1590     }
1591     break;
1592
1593   case SILC_COMMAND_INFO:
1594     {
1595       SilcServerEntry server_entry;
1596       char *server_name;
1597       char *server_info;
1598
1599       if (!success)
1600         return;
1601       
1602       server_entry = va_arg(vp, SilcServerEntry);
1603       server_name = va_arg(vp, char *);
1604       server_info = va_arg(vp, char *);
1605
1606       if (server_name && server_info )
1607         {
1608           printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
1609           printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
1610         }
1611     }
1612     break;
1613     
1614   case SILC_COMMAND_TOPIC:
1615     {
1616       SilcChannelEntry channel;
1617       char *topic;
1618       char tmp[256], *cp, *dm = NULL;
1619       
1620       if (!success)
1621         return;
1622       
1623       channel = va_arg(vp, SilcChannelEntry);
1624       topic = va_arg(vp, char *);
1625
1626       if (topic && !silc_term_utf8() &&
1627           silc_utf8_valid(topic, strlen(topic))) {
1628         memset(tmp, 0, sizeof(tmp));
1629         cp = tmp;
1630         if (strlen(topic) > sizeof(tmp) - 1) {
1631           dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
1632           cp = dm;
1633         }
1634
1635         silc_utf8_decode(topic, strlen(topic), SILC_STRING_LANGUAGE,
1636                          cp, strlen(topic));
1637         topic = cp;
1638       }
1639
1640       if (topic) {
1641         chanrec = silc_channel_find_entry(server, channel);
1642         if (chanrec) {
1643           g_free_not_null(chanrec->topic);
1644           chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1645           signal_emit("channel topic changed", 1, chanrec);
1646         }
1647         printformat_module("fe-common/silc", server, channel->channel_name,
1648                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
1649                            channel->channel_name, topic);
1650       } else {
1651         printformat_module("fe-common/silc", server, channel->channel_name,
1652                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
1653                            channel->channel_name);
1654       }
1655       silc_free(dm);
1656     }
1657     break;
1658
1659   case SILC_COMMAND_WATCH:
1660     break;
1661   
1662   case SILC_COMMAND_STATS:
1663     {
1664       SilcUInt32 starttime, uptime, my_clients, my_channels, my_server_ops,
1665                  my_router_ops, cell_clients, cell_channels, cell_servers,
1666                  clients, channels, servers, routers, server_ops, router_ops;
1667       SilcUInt32 buf_len;
1668       SilcBufferStruct buf;
1669       unsigned char *tmp_buf;
1670       char tmp[40];
1671       const char *tmptime;
1672       int days, hours, mins, secs;
1673
1674       if (!success)
1675         return;
1676
1677       tmp_buf = va_arg(vp, unsigned char *);
1678       buf_len = va_arg(vp, SilcUInt32);
1679
1680       if (!tmp_buf || !buf_len) {
1681         printtext(server, NULL, MSGLEVEL_CRAP, "No statistics available");
1682         return;
1683       }
1684
1685       /* Get statistics structure */
1686       silc_buffer_set(&buf, tmp_buf, buf_len);
1687       silc_buffer_unformat(&buf,
1688                            SILC_STR_UI_INT(&starttime),
1689                            SILC_STR_UI_INT(&uptime),
1690                            SILC_STR_UI_INT(&my_clients),
1691                            SILC_STR_UI_INT(&my_channels),
1692                            SILC_STR_UI_INT(&my_server_ops),
1693                            SILC_STR_UI_INT(&my_router_ops),
1694                            SILC_STR_UI_INT(&cell_clients),
1695                            SILC_STR_UI_INT(&cell_channels),
1696                            SILC_STR_UI_INT(&cell_servers),
1697                            SILC_STR_UI_INT(&clients),
1698                            SILC_STR_UI_INT(&channels),
1699                            SILC_STR_UI_INT(&servers),
1700                            SILC_STR_UI_INT(&routers),
1701                            SILC_STR_UI_INT(&server_ops),
1702                            SILC_STR_UI_INT(&router_ops),
1703                            SILC_STR_END);
1704
1705       tmptime = silc_get_time(starttime);
1706       printformat_module("fe-common/silc", server, NULL,
1707                          MSGLEVEL_CRAP, SILCTXT_STATS,
1708                          "Local server start time", tmptime);
1709
1710       days = uptime / (24 * 60 * 60);
1711       uptime -= days * (24 * 60 * 60);
1712       hours = uptime / (60 * 60);
1713       uptime -= hours * (60 * 60);
1714       mins = uptime / 60;
1715       uptime -= mins * 60;
1716       secs = uptime;
1717       snprintf(tmp, sizeof(tmp) - 1, "%d days %d hours %d mins %d secs",
1718                days, hours, mins, secs);
1719       printformat_module("fe-common/silc", server, NULL,
1720                          MSGLEVEL_CRAP, SILCTXT_STATS,
1721                          "Local server uptime", tmp);
1722
1723       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_clients);
1724       printformat_module("fe-common/silc", server, NULL,
1725                          MSGLEVEL_CRAP, SILCTXT_STATS,
1726                          "Local server clients", tmp);
1727
1728       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_channels);
1729       printformat_module("fe-common/silc", server, NULL,
1730                          MSGLEVEL_CRAP, SILCTXT_STATS,
1731                          "Local server channels", tmp);
1732
1733       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_server_ops);
1734       printformat_module("fe-common/silc", server, NULL,
1735                          MSGLEVEL_CRAP, SILCTXT_STATS,
1736                          "Local server operators", tmp);
1737
1738       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_router_ops);
1739       printformat_module("fe-common/silc", server, NULL,
1740                          MSGLEVEL_CRAP, SILCTXT_STATS,
1741                          "Local router operators", tmp);
1742
1743       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_clients);
1744       printformat_module("fe-common/silc", server, NULL,
1745                          MSGLEVEL_CRAP, SILCTXT_STATS,
1746                          "Local cell clients", tmp);
1747
1748       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_channels);
1749       printformat_module("fe-common/silc", server, NULL,
1750                          MSGLEVEL_CRAP, SILCTXT_STATS,
1751                          "Local cell channels", tmp);
1752
1753       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_servers);
1754       printformat_module("fe-common/silc", server, NULL,
1755                          MSGLEVEL_CRAP, SILCTXT_STATS,
1756                          "Local cell servers", tmp);
1757
1758       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)clients);
1759       printformat_module("fe-common/silc", server, NULL,
1760                          MSGLEVEL_CRAP, SILCTXT_STATS,
1761                          "Total clients", tmp);
1762
1763       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)channels);
1764       printformat_module("fe-common/silc", server, NULL,
1765                          MSGLEVEL_CRAP, SILCTXT_STATS,
1766                          "Total channels", tmp);
1767
1768       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)servers);
1769       printformat_module("fe-common/silc", server, NULL,
1770                          MSGLEVEL_CRAP, SILCTXT_STATS,
1771                          "Total servers", tmp);
1772
1773       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)routers);
1774       printformat_module("fe-common/silc", server, NULL,
1775                          MSGLEVEL_CRAP, SILCTXT_STATS,
1776                          "Total routers", tmp);
1777
1778       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)server_ops);
1779       printformat_module("fe-common/silc", server, NULL,
1780                          MSGLEVEL_CRAP, SILCTXT_STATS,
1781                            "Total server operators", tmp);
1782
1783       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)router_ops);
1784       printformat_module("fe-common/silc", server, NULL,
1785                          MSGLEVEL_CRAP, SILCTXT_STATS,
1786                          "Total router operators", tmp);
1787     }
1788     break;
1789
1790   }
1791
1792   va_end(vp);
1793 }
1794
1795 typedef struct {
1796   SilcClient client;
1797   SilcClientConnection conn;
1798   char *filename;
1799   char *entity;
1800   char *entity_name;
1801   unsigned char *pk;
1802   SilcUInt32 pk_len;
1803   SilcSKEPKType pk_type;
1804   SilcVerifyPublicKey completion;
1805   void *context;
1806 } *PublicKeyVerify;
1807
1808 static void verify_public_key_completion(const char *line, void *context)
1809 {
1810   PublicKeyVerify verify = (PublicKeyVerify)context;
1811
1812   if (line[0] == 'Y' || line[0] == 'y') {
1813     /* Call the completion */
1814     if (verify->completion)
1815       verify->completion(TRUE, verify->context);
1816
1817     /* Save the key for future checking */
1818     silc_pkcs_save_public_key_data(verify->filename, verify->pk, 
1819                                    verify->pk_len, SILC_PKCS_FILE_PEM);
1820   } else {
1821     /* Call the completion */
1822     if (verify->completion)
1823       verify->completion(FALSE, verify->context);
1824
1825     printformat_module("fe-common/silc", NULL, NULL,
1826                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, 
1827                        verify->entity_name ? verify->entity_name :
1828                        verify->entity);
1829   }
1830
1831   silc_free(verify->filename);
1832   silc_free(verify->entity);
1833   silc_free(verify->entity_name);
1834   silc_free(verify->pk);
1835   silc_free(verify);
1836 }
1837
1838 /* Internal routine to verify public key. If the `completion' is provided
1839    it will be called to indicate whether public was verified or not. For
1840    server/router public key this will check for filename that includes the
1841    remote host's IP address and remote host's hostname. */
1842
1843 static void 
1844 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
1845                                 const char *name, SilcSocketType conn_type, 
1846                                 unsigned char *pk, SilcUInt32 pk_len, 
1847                                 SilcSKEPKType pk_type,
1848                                 SilcVerifyPublicKey completion, void *context)
1849 {
1850   int i;
1851   char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
1852   char *fingerprint, *babbleprint, *format;
1853   struct passwd *pw;
1854   struct stat st;
1855   char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
1856                    conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
1857                   "server" : "client");
1858   PublicKeyVerify verify;
1859
1860   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
1861     printformat_module("fe-common/silc", NULL, NULL,
1862                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED, 
1863                        entity, pk_type);
1864     if (completion)
1865       completion(FALSE, context);
1866     return;
1867   }
1868
1869   pw = getpwuid(getuid());
1870   if (!pw) {
1871     if (completion)
1872       completion(FALSE, context);
1873     return;
1874   }
1875
1876   memset(filename, 0, sizeof(filename));
1877   memset(filename2, 0, sizeof(filename2));
1878   memset(file, 0, sizeof(file));
1879
1880   if (conn_type == SILC_SOCKET_TYPE_SERVER ||
1881       conn_type == SILC_SOCKET_TYPE_ROUTER) {
1882     if (!name) {
1883       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1884                conn->sock->ip, conn->sock->port);
1885       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
1886                get_irssi_dir(), entity, file);
1887       
1888       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1889                conn->sock->hostname, conn->sock->port);
1890       snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s", 
1891                get_irssi_dir(), entity, file);
1892       
1893       ipf = filename;
1894       hostf = filename2;
1895     } else {
1896       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1897                name, conn->sock->port);
1898       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
1899                get_irssi_dir(), entity, file);
1900       
1901       ipf = filename;
1902     }
1903   } else {
1904     /* Replace all whitespaces with `_'. */
1905     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1906     for (i = 0; i < strlen(fingerprint); i++)
1907       if (fingerprint[i] == ' ')
1908         fingerprint[i] = '_';
1909     
1910     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
1911     snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
1912              get_irssi_dir(), entity, file);
1913     silc_free(fingerprint);
1914
1915     ipf = filename;
1916   }
1917
1918   /* Take fingerprint of the public key */
1919   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1920   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
1921
1922   verify = silc_calloc(1, sizeof(*verify));
1923   verify->client = client;
1924   verify->conn = conn;
1925   verify->filename = strdup(ipf);
1926   verify->entity = strdup(entity);
1927   verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ?
1928                          (name ? strdup(name) : strdup(conn->sock->hostname))
1929                          : NULL);
1930   verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
1931   memcpy(verify->pk, pk, pk_len);
1932   verify->pk_len = pk_len;
1933   verify->pk_type = pk_type;
1934   verify->completion = completion;
1935   verify->context = context;
1936
1937   /* Check whether this key already exists */
1938   if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
1939     /* Key does not exist, ask user to verify the key and save it */
1940
1941     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1942                        SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1943                        verify->entity_name : entity);
1944     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1945                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1946     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1947                        SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1948     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1949                              SILCTXT_PUBKEY_ACCEPT);
1950     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1951                             format, 0, verify);
1952     g_free(format);
1953     silc_free(fingerprint);
1954     return;
1955   } else {
1956     /* The key already exists, verify it. */
1957     SilcPublicKey public_key;
1958     unsigned char *encpk;
1959     SilcUInt32 encpk_len;
1960
1961     /* Load the key file, try for both IP filename and hostname filename */
1962     if (!silc_pkcs_load_public_key(ipf, &public_key, 
1963                                    SILC_PKCS_FILE_PEM) &&
1964         !silc_pkcs_load_public_key(ipf, &public_key, 
1965                                    SILC_PKCS_FILE_BIN) &&
1966         (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key, 
1967                                                SILC_PKCS_FILE_PEM) &&
1968                     !silc_pkcs_load_public_key(hostf, &public_key, 
1969                                                SILC_PKCS_FILE_BIN)))) {
1970       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1971                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1972                          verify->entity_name : entity);
1973       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1974                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1975       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1976                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1977       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1978                          SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
1979       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1980                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1981       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1982                               format, 0, verify);
1983       g_free(format);
1984       silc_free(fingerprint);
1985       return;
1986     }
1987
1988     /* Encode the key data */
1989     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
1990     if (!encpk) {
1991       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1992                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1993                          verify->entity_name : entity);
1994       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1995                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1996       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1997                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1998       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1999                          SILCTXT_PUBKEY_MALFORMED, entity);
2000       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2001                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2002       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2003                               format, 0, verify);
2004       g_free(format);
2005       silc_free(fingerprint);
2006       return;
2007     }
2008
2009     /* Compare the keys */
2010     if (memcmp(encpk, pk, encpk_len)) {
2011       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2012                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
2013                          verify->entity_name : entity);
2014       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2015                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2016       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2017                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2018       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2019                          SILCTXT_PUBKEY_NO_MATCH, entity);
2020       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2021                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
2022       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2023                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
2024
2025       /* Ask user to verify the key and save it */
2026       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2027                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2028       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2029                               format, 0, verify);
2030       g_free(format);
2031       silc_free(fingerprint);
2032       return;
2033     }
2034
2035     /* Local copy matched */
2036     if (completion)
2037       completion(TRUE, context);
2038     silc_free(fingerprint);
2039   }
2040 }
2041
2042 /* Verifies received public key. The `conn_type' indicates which entity
2043    (server, client etc.) has sent the public key. If user decides to trust
2044    the key may be saved as trusted public key for later use. The 
2045    `completion' must be called after the public key has been verified. */
2046
2047 void 
2048 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
2049                        SilcSocketType conn_type, unsigned char *pk, 
2050                        SilcUInt32 pk_len, SilcSKEPKType pk_type,
2051                        SilcVerifyPublicKey completion, void *context)
2052 {
2053   silc_verify_public_key_internal(client, conn, NULL, conn_type, pk,
2054                                   pk_len, pk_type,
2055                                   completion, context);
2056 }
2057
2058 /* Asks passphrase from user on the input line. */
2059
2060 typedef struct {
2061   SilcAskPassphrase completion;
2062   void *context;
2063 } *AskPassphrase;
2064
2065 void ask_passphrase_completion(const char *passphrase, void *context)
2066 {
2067   AskPassphrase p = (AskPassphrase)context;
2068   if (passphrase && passphrase[0] == '\0')
2069     passphrase = NULL;
2070   p->completion((unsigned char *)passphrase, 
2071                 passphrase ? strlen(passphrase) : 0, p->context);
2072   silc_free(p);
2073 }
2074
2075 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
2076                          SilcAskPassphrase completion, void *context)
2077 {
2078   AskPassphrase p = silc_calloc(1, sizeof(*p));
2079   p->completion = completion;
2080   p->context = context;
2081
2082   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
2083                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
2084 }
2085
2086 typedef struct {
2087   SilcGetAuthMeth completion;
2088   void *context;
2089 } *InternalGetAuthMethod;
2090
2091 /* Callback called when we've received the authentication method information
2092    from the server after we've requested it. This will get the authentication
2093    data from the user if needed. */
2094
2095 static void silc_get_auth_method_callback(SilcClient client,
2096                                           SilcClientConnection conn,
2097                                           SilcAuthMethod auth_meth,
2098                                           void *context)
2099 {
2100   InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
2101
2102   SILC_LOG_DEBUG(("Start"));
2103
2104   switch (auth_meth) {
2105   case SILC_AUTH_NONE:
2106     /* No authentication required. */
2107     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2108     break;
2109   case SILC_AUTH_PASSWORD:
2110     {
2111       /* Check whether we find the password for this server in our
2112          configuration.  If not, then don't provide so library will ask
2113          it from the user. */
2114       SERVER_SETUP_REC *setup = server_setup_find_port(conn->remote_host,
2115                                                        conn->remote_port);
2116       if (!setup || !setup->password) {
2117         (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2118         break;
2119       }
2120       
2121       (*internal->completion)(TRUE, auth_meth, setup->password,
2122                               strlen(setup->password), internal->context);
2123     }
2124     break;
2125   case SILC_AUTH_PUBLIC_KEY:
2126     /* Do not get the authentication data now, the library will generate
2127        it using our default key, if we do not provide it here. */
2128     /* XXX In the future when we support multiple local keys and multiple
2129        local certificates we will need to ask from user which one to use. */
2130     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2131     break;
2132   }
2133
2134   silc_free(internal);
2135 }
2136
2137 /* Find authentication method and authentication data by hostname and
2138    port. The hostname may be IP address as well. The found authentication
2139    method and authentication data is returned to `auth_meth', `auth_data'
2140    and `auth_data_len'. The function returns TRUE if authentication method
2141    is found and FALSE if not. `conn' may be NULL. */
2142
2143 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
2144                           char *hostname, SilcUInt16 port,
2145                           SilcGetAuthMeth completion, void *context)
2146 {
2147   InternalGetAuthMethod internal;
2148
2149   SILC_LOG_DEBUG(("Start"));
2150
2151   /* If we do not have this connection configured by the user in a
2152      configuration file then resolve the authentication method from the
2153      server for this session. */
2154   internal = silc_calloc(1, sizeof(*internal));
2155   internal->completion = completion;
2156   internal->context = context;
2157
2158   silc_client_request_authentication_method(client, conn, 
2159                                             silc_get_auth_method_callback,
2160                                             internal);
2161 }
2162
2163 /* Notifies application that failure packet was received.  This is called
2164    if there is some protocol active in the client.  The `protocol' is the
2165    protocol context.  The `failure' is opaque pointer to the failure
2166    indication.  Note, that the `failure' is protocol dependant and application
2167    must explicitly cast it to correct type.  Usually `failure' is 32 bit
2168    failure type (see protocol specs for all protocol failure types). */
2169
2170 void silc_failure(SilcClient client, SilcClientConnection conn, 
2171                   SilcProtocol protocol, void *failure)
2172 {
2173   SILC_LOG_DEBUG(("Start"));
2174
2175   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
2176     SilcSKEStatus status = (SilcSKEStatus)failure;
2177     
2178     if (status == SILC_SKE_STATUS_BAD_VERSION)
2179       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2180                          SILCTXT_KE_BAD_VERSION);
2181     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
2182       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2183                          SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
2184     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
2185       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2186                          SILCTXT_KE_UNKNOWN_GROUP);
2187     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
2188       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2189                          SILCTXT_KE_UNKNOWN_CIPHER);
2190     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
2191       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2192                          SILCTXT_KE_UNKNOWN_PKCS);
2193     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
2194       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2195                          SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
2196     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
2197       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2198                          SILCTXT_KE_UNKNOWN_HMAC);
2199     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
2200       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2201                          SILCTXT_KE_INCORRECT_SIGNATURE);
2202     if (status == SILC_SKE_STATUS_INVALID_COOKIE)
2203       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2204                          SILCTXT_KE_INVALID_COOKIE);
2205   }
2206
2207   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
2208     SilcUInt32 err = (SilcUInt32)failure;
2209
2210     if (err == SILC_AUTH_FAILED)
2211       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2212                          SILCTXT_AUTH_FAILED);
2213   }
2214 }
2215
2216 /* Asks whether the user would like to perform the key agreement protocol.
2217    This is called after we have received an key agreement packet or an
2218    reply to our key agreement packet. This returns TRUE if the user wants
2219    the library to perform the key agreement protocol and FALSE if it is not
2220    desired (application may start it later by calling the function
2221    silc_client_perform_key_agreement). */
2222
2223 bool silc_key_agreement(SilcClient client, SilcClientConnection conn,
2224                         SilcClientEntry client_entry, const char *hostname,
2225                         SilcUInt16 port, SilcKeyAgreementCallback *completion,
2226                         void **context)
2227 {
2228   char portstr[12];
2229
2230   SILC_LOG_DEBUG(("Start"));
2231
2232   /* We will just display the info on the screen and return FALSE and user
2233      will have to start the key agreement with a command. */
2234
2235   if (hostname) 
2236     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2237
2238   if (!hostname)
2239     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2240                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
2241   else
2242     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2243                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST, 
2244                        client_entry->nickname, hostname, portstr);
2245
2246   *completion = NULL;
2247   *context = NULL;
2248
2249   return FALSE;
2250 }
2251
2252 /* Notifies application that file transfer protocol session is being
2253    requested by the remote client indicated by the `client_entry' from
2254    the `hostname' and `port'. The `session_id' is the file transfer
2255    session and it can be used to either accept or reject the file
2256    transfer request, by calling the silc_client_file_receive or
2257    silc_client_file_close, respectively. */
2258
2259 void silc_ftp(SilcClient client, SilcClientConnection conn,
2260               SilcClientEntry client_entry, SilcUInt32 session_id,
2261               const char *hostname, SilcUInt16 port)
2262 {
2263   SILC_SERVER_REC *server;
2264   char portstr[12];
2265   FtpSession ftp = NULL;
2266
2267   SILC_LOG_DEBUG(("Start"));
2268
2269   server = conn->context;
2270
2271   silc_dlist_start(server->ftp_sessions);
2272   while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
2273     if (ftp->client_entry == client_entry &&
2274         ftp->session_id == session_id) {
2275       server->current_session = ftp;
2276       break;
2277     }
2278   }
2279   if (ftp == SILC_LIST_END) {
2280     ftp = silc_calloc(1, sizeof(*ftp));
2281     ftp->client_entry = client_entry;
2282     ftp->session_id = session_id;
2283     ftp->send = FALSE;
2284     ftp->conn = conn;
2285     silc_dlist_add(server->ftp_sessions, ftp);
2286     server->current_session = ftp;
2287   }
2288
2289   if (hostname) 
2290     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2291
2292   if (!hostname)
2293     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2294                        SILCTXT_FILE_REQUEST, client_entry->nickname);
2295   else
2296     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2297                        SILCTXT_FILE_REQUEST_HOST, 
2298                        client_entry->nickname, hostname, portstr);
2299 }
2300
2301 /* Delivers SILC session detachment data indicated by `detach_data' to the
2302    application.  If application has issued SILC_COMMAND_DETACH command
2303    the client session in the SILC network is not quit.  The client remains
2304    in the network but is detached.  The detachment data may be used later
2305    to resume the session in the SILC Network.  The appliation is
2306    responsible of saving the `detach_data', to for example in a file.
2307
2308    The detachment data can be given as argument to the functions
2309    silc_client_connect_to_server, or silc_client_add_connection when
2310    creating connection to remote server, inside SilcClientConnectionParams
2311    structure.  If it is provided the client library will attempt to resume
2312    the session in the network.  After the connection is created
2313    successfully, the application is responsible of setting the user
2314    interface for user into the same state it was before detaching (showing
2315    same channels, channel modes, etc).  It can do this by fetching the
2316    information (like joined channels) from the client library. */
2317
2318 void
2319 silc_detach(SilcClient client, SilcClientConnection conn,
2320             const unsigned char *detach_data, SilcUInt32 detach_data_len)
2321 {
2322   char file[256];
2323
2324   /* Save the detachment data to file. */
2325
2326   memset(file, 0, sizeof(file));
2327   snprintf(file, sizeof(file) - 1, "%s/session", get_irssi_dir());
2328   silc_file_writefile(file, detach_data, detach_data_len);
2329 }
2330
2331
2332 /* SILC client operations */
2333 SilcClientOperations ops = {
2334   silc_say,
2335   silc_channel_message,
2336   silc_private_message,
2337   silc_notify,
2338   silc_command,
2339   silc_command_reply,
2340   silc_connect,
2341   silc_disconnect,
2342   silc_get_auth_method,
2343   silc_verify_public_key,
2344   silc_ask_passphrase,
2345   silc_failure,
2346   silc_key_agreement,
2347   silc_ftp,
2348   silc_detach,
2349 };