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