Added support for setting specific founder public key in
[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       g_free_not_null(chanrec->topic);
481       chanrec->topic = *tmp == '\0' ? NULL : g_strdup(tmp);
482       signal_emit("channel topic changed", 1, chanrec);
483     }
484     
485     if (idtype == SILC_ID_CLIENT) {
486       client_entry = (SilcClientEntry)entry;
487       memset(buf, 0, sizeof(buf));
488       snprintf(buf, sizeof(buf) - 1, "%s@%s",
489                client_entry->username, client_entry->hostname);
490       signal_emit("message topic", 5, server, channel->channel_name,
491                   tmp, client_entry->nickname, buf);
492     } else if (idtype == SILC_ID_SERVER) {
493       server_entry = (SilcServerEntry)entry;
494       signal_emit("message topic", 5, server, channel->channel_name,
495                   tmp, server_entry->server_name, 
496                   server_entry->server_name);
497     } else if (idtype == SILC_ID_CHANNEL) {
498       channel = (SilcChannelEntry)entry;
499       signal_emit("message topic", 5, server, channel->channel_name,
500                   tmp, channel->channel_name, channel->channel_name);
501     }
502     break;
503
504   case SILC_NOTIFY_TYPE_NICK_CHANGE:
505     /*
506      * Changed nickname.
507      */
508
509     SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
510
511     client_entry = va_arg(va, SilcClientEntry);
512     client_entry2 = va_arg(va, SilcClientEntry);
513
514     if (!strcmp(client_entry->nickname, client_entry2->nickname))
515       break;
516     
517     memset(buf, 0, sizeof(buf));
518     snprintf(buf, sizeof(buf) - 1, "%s@%s",
519              client_entry2->username, client_entry2->hostname);
520     nicklist_rename_unique(SERVER(server),
521                            client_entry, client_entry->nickname,
522                            client_entry2, client_entry2->nickname);
523     signal_emit("message nick", 4, server, client_entry2->nickname, 
524                 client_entry->nickname, buf);
525     break;
526
527   case SILC_NOTIFY_TYPE_CMODE_CHANGE:
528     /*
529      * Changed channel mode.
530      */
531
532     SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
533
534     idtype = va_arg(va, int);
535     entry = va_arg(va, void *);
536     mode = va_arg(va, SilcUInt32);
537     (void)va_arg(va, char *);
538     (void)va_arg(va, char *);
539     channel = va_arg(va, SilcChannelEntry);
540
541     tmp = silc_client_chmode(mode,
542                              channel->channel_key ? 
543                              silc_cipher_get_name(channel->channel_key) : "",
544                              channel->hmac ? 
545                              silc_hmac_get_name(channel->hmac) : "");
546     
547     chanrec = silc_channel_find_entry(server, channel);
548     if (chanrec != NULL) {
549       g_free_not_null(chanrec->mode);
550       chanrec->mode = g_strdup(tmp == NULL ? "" : tmp);
551       signal_emit("channel mode changed", 1, chanrec);
552     }
553     
554     if (idtype == SILC_ID_CLIENT) {
555       client_entry = (SilcClientEntry)entry;
556       printformat_module("fe-common/silc", server, channel->channel_name,
557                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
558                          channel->channel_name, tmp ? tmp : "removed all",
559                          client_entry->nickname);
560     } else if (idtype == SILC_ID_SERVER) {
561       server_entry = (SilcServerEntry)entry;
562       printformat_module("fe-common/silc", server, channel->channel_name,
563                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
564                          channel->channel_name, tmp ? tmp : "removed all",
565                          server_entry->server_name);
566     } else if (idtype == SILC_ID_CHANNEL) {
567       channel2 = (SilcChannelEntry)entry;
568       printformat_module("fe-common/silc", server, channel->channel_name,
569                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
570                          channel->channel_name, tmp ? tmp : "removed all",
571                          channel2->channel_name);
572     }
573
574     silc_free(tmp);
575     break;
576
577   case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
578     /*
579      * Changed user's mode on channel.
580      */
581
582     SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
583
584     idtype = va_arg(va, int);
585     entry = va_arg(va, void *);
586     mode = va_arg(va, SilcUInt32);
587     client_entry2 = va_arg(va, SilcClientEntry);
588     channel = va_arg(va, SilcChannelEntry);
589
590     tmp = silc_client_chumode(mode);
591     chanrec = silc_channel_find_entry(server, channel);
592     if (chanrec != NULL) {
593       SILC_NICK_REC *nick;
594
595       if (client_entry2 == server->conn->local_entry)
596         chanrec->chanop = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
597       
598       nick = silc_nicklist_find(chanrec, client_entry2);
599       if (nick != NULL) {
600         nick->op = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
601         nick->founder = (mode & SILC_CHANNEL_UMODE_CHANFO) != 0;
602         signal_emit("nick mode changed", 2, chanrec, nick);
603       }
604     }
605
606     if (idtype == SILC_ID_CLIENT) {
607       client_entry = (SilcClientEntry)entry;
608       printformat_module("fe-common/silc", server, channel->channel_name,
609                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
610                          channel->channel_name, client_entry2->nickname, 
611                          tmp ? tmp : "removed all",
612                          client_entry->nickname);
613     } else if (idtype == SILC_ID_SERVER) {
614       server_entry = (SilcServerEntry)entry;
615       printformat_module("fe-common/silc", server, channel->channel_name,
616                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
617                          channel->channel_name, client_entry2->nickname, 
618                          tmp ? tmp : "removed all",
619                          server_entry->server_name);
620     } else if (idtype == SILC_ID_CHANNEL) {
621       channel2 = (SilcChannelEntry)entry;
622       printformat_module("fe-common/silc", server, channel->channel_name,
623                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
624                          channel->channel_name, client_entry2->nickname, 
625                          tmp ? tmp : "removed all",
626                          channel2->channel_name);
627     }
628
629     if (mode & SILC_CHANNEL_UMODE_CHANFO)
630       printformat_module("fe-common/silc", 
631                          server, channel->channel_name, MSGLEVEL_CRAP,
632                          SILCTXT_CHANNEL_FOUNDER,
633                          channel->channel_name, client_entry2->nickname);
634
635     if (mode & SILC_CHANNEL_UMODE_QUIET && conn->local_entry == client_entry2)
636       printformat_module("fe-common/silc", 
637                          server, channel->channel_name, MSGLEVEL_CRAP,
638                          SILCTXT_CHANNEL_QUIETED, channel->channel_name);
639
640     silc_free(tmp);
641     break;
642
643   case SILC_NOTIFY_TYPE_MOTD:
644     /*
645      * Received MOTD.
646      */
647
648     SILC_LOG_DEBUG(("Notify: MOTD"));
649
650     tmp = va_arg(va, char *);
651
652     if (!settings_get_bool("skip_motd"))
653       printtext_multiline(server, NULL, MSGLEVEL_CRAP, "%s", tmp);
654     break;
655
656   case SILC_NOTIFY_TYPE_KICKED:
657     /*
658      * Someone was kicked from channel.
659      */
660
661     SILC_LOG_DEBUG(("Notify: KICKED"));
662
663     client_entry = va_arg(va, SilcClientEntry);
664     tmp = va_arg(va, char *);
665     client_entry2 = va_arg(va, SilcClientEntry);
666     channel = va_arg(va, SilcChannelEntry);
667
668     chanrec = silc_channel_find_entry(server, channel);
669   
670     if (client_entry == conn->local_entry) {
671       printformat_module("fe-common/silc", server, channel->channel_name,
672                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED_YOU, 
673                          channel->channel_name, 
674                          client_entry ? client_entry2->nickname : "",
675                          tmp ? tmp : "");
676       if (chanrec) {
677         chanrec->kicked = TRUE;
678         channel_destroy((CHANNEL_REC *)chanrec);
679       }
680     } else {
681       printformat_module("fe-common/silc", server, channel->channel_name,
682                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED, 
683                          client_entry->nickname, channel->channel_name, 
684                          client_entry2 ? client_entry2->nickname : "", 
685                          tmp ? tmp : "");
686
687       if (chanrec) {
688         SILC_NICK_REC *nickrec = silc_nicklist_find(chanrec, client_entry);
689         if (nickrec != NULL)
690           nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
691       }
692     }
693     break;
694
695   case SILC_NOTIFY_TYPE_KILLED:
696     /*
697      * Someone was killed from the network.
698      */
699
700     SILC_LOG_DEBUG(("Notify: KILLED"));
701
702     client_entry = va_arg(va, SilcClientEntry);
703     tmp = va_arg(va, char *);
704     idtype = va_arg(va, int);
705     entry = va_arg(va, SilcClientEntry);
706   
707     if (client_entry == conn->local_entry) {
708       if (idtype == SILC_ID_CLIENT) {
709         client_entry2 = (SilcClientEntry)entry;
710         printformat_module("fe-common/silc", server, NULL,
711                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU, 
712                            client_entry2 ? client_entry2->nickname : "",
713                            tmp ? tmp : "");
714       } else if (idtype == SILC_ID_SERVER) {
715         server_entry = (SilcServerEntry)entry;
716         printformat_module("fe-common/silc", server, NULL,
717                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU, 
718                            server_entry->server_name, tmp ? tmp : "");
719       } else if (idtype == SILC_ID_CHANNEL) {
720         channel = (SilcChannelEntry)entry;
721         printformat_module("fe-common/silc", server, NULL,
722                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU, 
723                            channel->channel_name, tmp ? tmp : "");
724       }
725     } else {
726       list1 = nicklist_get_same_unique(SERVER(server), client_entry);
727       for (list_tmp = list1; list_tmp != NULL; list_tmp = 
728              list_tmp->next->next) {
729         CHANNEL_REC *channel = list_tmp->data;
730         NICK_REC *nickrec = list_tmp->next->data;
731         nicklist_remove(channel, nickrec);
732       }
733
734       if (idtype == SILC_ID_CLIENT) {
735         client_entry2 = (SilcClientEntry)entry;
736         printformat_module("fe-common/silc", server, NULL,
737                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED, 
738                            client_entry->nickname,
739                            client_entry2 ? client_entry2->nickname : "",
740                            tmp ? tmp : "");
741       } else if (idtype == SILC_ID_SERVER) {
742         server_entry = (SilcServerEntry)entry;
743         printformat_module("fe-common/silc", server, NULL,
744                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED, 
745                            client_entry->nickname,
746                            server_entry->server_name, tmp ? tmp : "");
747       } else if (idtype == SILC_ID_CHANNEL) {
748         channel = (SilcChannelEntry)entry;
749         printformat_module("fe-common/silc", server, NULL,
750                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED, 
751                            client_entry->nickname,
752                            channel->channel_name, tmp ? tmp : "");
753       }
754     }
755     break;
756
757   case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
758     break;
759
760   case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
761     {
762       /*
763        * Server has quit the network.
764        */
765       int i;
766       SilcClientEntry *clients;
767       SilcUInt32 clients_count;
768
769       SILC_LOG_DEBUG(("Notify: SIGNOFF"));
770       
771       (void)va_arg(va, void *);
772       clients = va_arg(va, SilcClientEntry *);
773       clients_count = va_arg(va, SilcUInt32);
774   
775       for (i = 0; i < clients_count; i++) {
776         memset(buf, 0, sizeof(buf));
777         if (clients[i]->username)
778           snprintf(buf, sizeof(buf) - 1, "%s@%s",
779                    clients[i]->username, clients[i]->hostname);
780         signal_emit("message quit", 4, server, clients[i]->nickname,
781                     clients[i]->username ? buf : "", 
782                     "server signoff");
783
784         silc_server_free_ftp(server, clients[i]);
785         
786         list1 = nicklist_get_same_unique(SERVER(server), clients[i]);
787         for (list_tmp = list1; list_tmp != NULL; list_tmp = 
788                list_tmp->next->next) {
789           CHANNEL_REC *channel = list_tmp->data;
790           NICK_REC *nickrec = list_tmp->next->data;
791           nicklist_remove(channel, nickrec);
792         }
793       }
794     }
795     break;
796
797   case SILC_NOTIFY_TYPE_ERROR:
798     {
799       SilcStatus error = va_arg(va, int);
800
801       silc_say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
802                 "%s", silc_get_status_message(error));
803     }
804     break;
805
806   case SILC_NOTIFY_TYPE_WATCH:
807     {
808       SilcNotifyType notify;
809
810       client_entry = va_arg(va, SilcClientEntry);
811       name = va_arg(va, char *);          /* Maybe NULL */
812       mode = va_arg(va, SilcUInt32);
813       notify = va_arg(va, int);
814
815       if (notify == SILC_NOTIFY_TYPE_NICK_CHANGE) {
816         if (name)
817           printformat_module("fe-common/silc", server, NULL,
818                              MSGLEVEL_CRAP, SILCTXT_WATCH_NICK_CHANGE,
819                              client_entry->nickname, name);
820         else
821           printformat_module("fe-common/silc", server, NULL,
822                              MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
823                              client_entry->nickname);
824       } else if (notify == SILC_NOTIFY_TYPE_UMODE_CHANGE) {
825         /* See if client was away and is now present */
826         if (!(mode & (SILC_UMODE_GONE | SILC_UMODE_INDISPOSED |
827                       SILC_UMODE_BUSY | SILC_UMODE_PAGE |
828                       SILC_UMODE_DETACHED)) &&
829             (client_entry->mode & SILC_UMODE_GONE ||
830              client_entry->mode & SILC_UMODE_INDISPOSED ||
831              client_entry->mode & SILC_UMODE_BUSY ||
832              client_entry->mode & SILC_UMODE_PAGE ||
833              client_entry->mode & SILC_UMODE_DETACHED)) {
834           printformat_module("fe-common/silc", server, NULL,
835                              MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
836                              client_entry->nickname);
837         }
838
839         if (mode) {
840           memset(buf, 0, sizeof(buf));
841           silc_get_umode_string(mode, buf, sizeof(buf) - 1);
842           printformat_module("fe-common/silc", server, NULL,
843                              MSGLEVEL_CRAP, SILCTXT_WATCH_UMODE_CHANGE,
844                              client_entry->nickname, buf);
845         }
846       } else if (notify == SILC_NOTIFY_TYPE_KILLED) {
847         printformat_module("fe-common/silc", server, NULL,
848                            MSGLEVEL_CRAP, SILCTXT_WATCH_KILLED,
849                            client_entry->nickname);
850       } else if (notify == SILC_NOTIFY_TYPE_SIGNOFF ||
851                  notify == SILC_NOTIFY_TYPE_SERVER_SIGNOFF) {
852         printformat_module("fe-common/silc", server, NULL,
853                            MSGLEVEL_CRAP, SILCTXT_WATCH_SIGNOFF,
854                            client_entry->nickname);
855       } else if (notify == SILC_NOTIFY_TYPE_NONE) {
856         /* Client logged in to the network */
857         printformat_module("fe-common/silc", server, NULL,
858                            MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
859                            client_entry->nickname);
860       }
861     }
862     break;
863
864   default:
865     /* Unknown notify */
866     printformat_module("fe-common/silc", server, NULL,
867                        MSGLEVEL_CRAP, SILCTXT_UNKNOWN_NOTIFY, type);
868     break;
869   }
870
871   va_end(va);
872 }
873
874 /* Called to indicate that connection was either successfully established
875    or connecting failed.  This is also the first time application receives
876    the SilcClientConnection object which it should save somewhere. */
877
878 void silc_connect(SilcClient client, SilcClientConnection conn,
879                   SilcClientConnectionStatus status)
880 {
881   SILC_SERVER_REC *server = conn->context;
882
883   if (!server || server->disconnected) {
884     silc_client_close_connection(client, conn);
885     return;
886   }
887
888   switch (status) {
889   case SILC_CLIENT_CONN_SUCCESS:
890     /* We have successfully connected to server */
891     server->connected = TRUE;
892     signal_emit("event connected", 1, server);
893     break;
894
895   case SILC_CLIENT_CONN_SUCCESS_RESUME:
896     /* We have successfully resumed old detached session */
897     server->connected = TRUE;
898     signal_emit("event connected", 1, server);
899
900     /* If we resumed old session check whether we need to update 
901        our nickname */
902     if (strcmp(server->nick, conn->local_entry->nickname)) {
903       char *old;
904       old = g_strdup(server->nick);
905       server_change_nick(SERVER(server), conn->local_entry->nickname);
906       nicklist_rename_unique(SERVER(server), 
907                              conn->local_entry, server->nick,
908                              conn->local_entry, conn->local_entry->nickname);
909       signal_emit("message own_nick", 4, server, server->nick, old, "");
910       g_free(old);
911     }
912     break;
913
914   default:
915     server->connection_lost = TRUE;
916     if (server->conn)
917       server->conn->context = NULL;
918     server_disconnect(SERVER(server));
919     break;
920   }
921 }
922
923 /* Called to indicate that connection was disconnected to the server. */
924
925 void silc_disconnect(SilcClient client, SilcClientConnection conn,
926                      SilcStatus status, const char *message)
927 {
928   SILC_SERVER_REC *server = conn->context;
929
930   SILC_LOG_DEBUG(("Start"));
931
932   if (!server || server->connection_lost)
933     return;
934
935   if (server->conn && server->conn->local_entry) {
936     nicklist_rename_unique(SERVER(server),
937                            server->conn->local_entry, server->nick,
938                            server->conn->local_entry, 
939                            silc_client->username);
940     silc_change_nick(server, silc_client->username);
941   }
942
943   if (message)
944     silc_say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
945              "Server closed connection: %s (%d) %s",
946              silc_get_status_message(status), status,
947              message ? message : "");
948
949   server->conn->context = NULL;
950   server->conn = NULL;
951   server->connection_lost = TRUE;
952   server_disconnect(SERVER(server));
953 }
954
955 /* Command handler. This function is called always in the command function.
956    If error occurs it will be called as well. `conn' is the associated
957    client connection. `cmd_context' is the command context that was
958    originally sent to the command. `success' is FALSE if error occured
959    during command. `command' is the command being processed. It must be
960    noted that this is not reply from server. This is merely called just
961    after application has called the command. Just to tell application
962    that the command really was processed. */
963
964 void silc_command(SilcClient client, SilcClientConnection conn, 
965                   SilcClientCommandContext cmd_context, bool success,
966                   SilcCommand command, SilcStatus status)
967 {
968   SILC_SERVER_REC *server = conn->context;
969
970   SILC_LOG_DEBUG(("Start"));
971
972   if (!success) {
973     silc_say_error("%s", silc_get_status_message(status));
974     return;
975   }
976
977   switch (command) {
978
979   case SILC_COMMAND_INVITE:
980     if (cmd_context->argc > 2)
981       printformat_module("fe-common/silc", server, NULL,
982                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING,
983                          cmd_context->argv[2], 
984                          (cmd_context->argv[1][0] == '*' ?
985                           (char *)conn->current_channel->channel_name :
986                           (char *)cmd_context->argv[1]));
987     break;
988
989   case SILC_COMMAND_DETACH:
990     server->no_reconnect = TRUE;
991     break;
992
993   default:
994     break;
995   }
996 }
997
998 /* Client info resolving callback when JOIN command reply is received.
999    This will cache all users on the channel. */
1000
1001 static void silc_client_join_get_users(SilcClient client,
1002                                        SilcClientConnection conn,
1003                                        SilcClientEntry *clients,
1004                                        SilcUInt32 clients_count,
1005                                        void *context)
1006 {
1007   SilcChannelEntry channel = (SilcChannelEntry)context;
1008   SilcHashTableList htl;
1009   SilcChannelUser chu;
1010   SILC_SERVER_REC *server = conn->context;
1011   SILC_CHANNEL_REC *chanrec;
1012   SilcClientEntry founder = NULL;
1013   NICK_REC *ownnick;
1014
1015   SILC_LOG_DEBUG(("Start, channel %s, %d users", channel->channel_name,
1016                   silc_hash_table_count(channel->user_list)));
1017
1018   if (!clients)
1019     return;
1020
1021   chanrec = silc_channel_find(server, channel->channel_name);
1022   if (chanrec == NULL)
1023     return;
1024
1025   silc_hash_table_list(channel->user_list, &htl);
1026   while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1027     if (!chu->client->nickname)
1028       continue;
1029     if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
1030       founder = chu->client;
1031     silc_nicklist_insert(chanrec, chu, FALSE);
1032   }
1033   silc_hash_table_list_reset(&htl);
1034
1035   ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
1036   nicklist_set_own(CHANNEL(chanrec), ownnick);
1037   signal_emit("channel joined", 1, chanrec);
1038   chanrec->entry = channel;
1039
1040   if (chanrec->topic)
1041     printformat_module("fe-common/silc", server, channel->channel_name,
1042                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
1043                        channel->channel_name, chanrec->topic);
1044
1045   if (founder) {
1046     if (founder == conn->local_entry)
1047       printformat_module("fe-common/silc", 
1048                          server, channel->channel_name, MSGLEVEL_CRAP,
1049                          SILCTXT_CHANNEL_FOUNDER_YOU,
1050                          channel->channel_name);
1051     else
1052       printformat_module("fe-common/silc", 
1053                          server, channel->channel_name, MSGLEVEL_CRAP,
1054                          SILCTXT_CHANNEL_FOUNDER,
1055                          channel->channel_name, founder->nickname);
1056   }
1057 }
1058
1059 typedef struct {
1060   SilcClient client;
1061   SilcClientConnection conn;
1062   void *entry;
1063   SilcIdType id_type;
1064   char *fingerprint;
1065 } *GetkeyContext;
1066
1067 void silc_getkey_cb(bool success, void *context)
1068 {
1069   GetkeyContext getkey = (GetkeyContext)context;
1070   char *entity = (getkey->id_type == SILC_ID_CLIENT ? "user" : "server");
1071   char *name = (getkey->id_type == SILC_ID_CLIENT ? 
1072                 ((SilcClientEntry)getkey->entry)->nickname :
1073                 ((SilcServerEntry)getkey->entry)->server_name);
1074
1075   if (success) {
1076     printformat_module("fe-common/silc", NULL, NULL,
1077                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_VERIFIED, entity, name);
1078   } else {
1079     printformat_module("fe-common/silc", NULL, NULL,
1080                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOTVERIFIED,
1081                        entity, name);
1082   }
1083
1084   silc_free(getkey->fingerprint);
1085   silc_free(getkey);
1086 }
1087
1088 /* Command reply handler. This function is called always in the command reply
1089    function. If error occurs it will be called as well. Normal scenario
1090    is that it will be called after the received command data has been parsed
1091    and processed. The function is used to pass the received command data to
1092    the application. 
1093
1094    `conn' is the associated client connection. `cmd_payload' is the command
1095    payload data received from server and it can be ignored. It is provided
1096    if the application would like to re-parse the received command data,
1097    however, it must be noted that the data is parsed already by the library
1098    thus the payload can be ignored. `success' is FALSE if error occured.
1099    In this case arguments are not sent to the application. `command' is the
1100    command reply being processed. The function has variable argument list
1101    and each command defines the number and type of arguments it passes to the
1102    application (on error they are not sent). */
1103
1104 void 
1105 silc_command_reply(SilcClient client, SilcClientConnection conn,
1106                    SilcCommandPayload cmd_payload, bool success,
1107                    SilcCommand command, SilcStatus status, ...)
1108
1109 {
1110   SILC_SERVER_REC *server = conn->context;
1111   SILC_CHANNEL_REC *chanrec;
1112   va_list vp;
1113
1114   va_start(vp, status);
1115
1116   SILC_LOG_DEBUG(("Start"));
1117
1118   switch(command) {
1119   case SILC_COMMAND_WHOIS:
1120     {
1121       char buf[1024], *nickname, *username, *realname, *nick;
1122       unsigned char *fingerprint;
1123       SilcUInt32 idle, mode;
1124       SilcBuffer channels, user_modes;
1125       SilcClientEntry client_entry;
1126       SilcDList attrs;
1127       
1128       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1129         /* Print the unknown nick for user */
1130         unsigned char *tmp =
1131           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1132                                      3, NULL);
1133         if (tmp)
1134           silc_say_error("%s: %s", tmp, 
1135                          silc_get_status_message(status));
1136         break;
1137       } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1138         /* Try to find the entry for the unknown client ID, since we
1139            might have, and print the nickname of it for user. */
1140         SilcUInt32 tmp_len;
1141         unsigned char *tmp =
1142           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1143                                      2, &tmp_len);
1144         if (tmp) {
1145           SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, 
1146                                                              NULL);
1147           if (client_id) {
1148             client_entry = silc_client_get_client_by_id(client, conn,
1149                                                         client_id);
1150             if (client_entry && client_entry->nickname)
1151               silc_say_error("%s: %s", client_entry->nickname,
1152                              silc_get_status_message(status));
1153             silc_free(client_id);
1154           }
1155         }
1156         break;
1157       } else if (!success) {
1158         silc_say_error("WHOIS: %s", silc_get_status_message(status));
1159         return;
1160       }
1161
1162       client_entry = va_arg(vp, SilcClientEntry);
1163       nickname = va_arg(vp, char *);
1164       username = va_arg(vp, char *);
1165       realname = va_arg(vp, char *);
1166       channels = va_arg(vp, SilcBuffer);
1167       mode = va_arg(vp, SilcUInt32);
1168       idle = va_arg(vp, SilcUInt32);
1169       fingerprint = va_arg(vp, unsigned char *);
1170       user_modes = va_arg(vp, SilcBuffer);
1171       attrs = va_arg(vp, SilcDList);
1172       
1173       silc_parse_userfqdn(nickname, &nick, NULL);
1174       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1175                          SILCTXT_WHOIS_USERINFO, nickname, 
1176                          client_entry->username, client_entry->hostname,
1177                          nick, client_entry->nickname);
1178       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1179                          SILCTXT_WHOIS_REALNAME, realname);
1180       silc_free(nick);
1181
1182       if (channels && user_modes) {
1183         SilcUInt32 *umodes;
1184         SilcDList list = silc_channel_payload_parse_list(channels->data,
1185                                                          channels->len);
1186         if (list && silc_get_mode_list(user_modes, silc_dlist_count(list),
1187                                        &umodes)) {
1188           SilcChannelPayload entry;
1189           int i = 0;
1190
1191           memset(buf, 0, sizeof(buf));
1192           silc_dlist_start(list);
1193           while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
1194             SilcUInt32 name_len;
1195             char *m = silc_client_chumode_char(umodes[i++]);
1196             char *name = silc_channel_get_name(entry, &name_len);
1197             
1198             if (m)
1199               strncat(buf, m, strlen(m));
1200             strncat(buf, name, name_len);
1201             strncat(buf, " ", 1);
1202             silc_free(m);
1203           }
1204
1205           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1206                              SILCTXT_WHOIS_CHANNELS, buf);
1207           silc_channel_payload_list_free(list);
1208           silc_free(umodes);
1209         }
1210       }
1211       
1212       if (mode) {
1213         memset(buf, 0, sizeof(buf));
1214         silc_get_umode_string(mode, buf, sizeof(buf - 1));
1215         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1216                            SILCTXT_WHOIS_MODES, buf);
1217       }
1218       
1219       if (idle && nickname) {
1220         memset(buf, 0, sizeof(buf));
1221         snprintf(buf, sizeof(buf) - 1, "%lu %s",
1222                  idle > 60 ? (idle / 60) : idle,
1223                  idle > 60 ? "minutes" : "seconds");
1224
1225         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1226                            SILCTXT_WHOIS_IDLE, buf);
1227       }
1228
1229       if (fingerprint) {
1230         fingerprint = silc_fingerprint(fingerprint, 20);
1231         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1232                            SILCTXT_WHOIS_FINGERPRINT, fingerprint);
1233         silc_free(fingerprint);
1234       }
1235
1236       if (attrs)
1237         silc_query_attributes_print(server, silc_client, conn, attrs,
1238                                     client_entry);
1239     }
1240     break;
1241     
1242   case SILC_COMMAND_IDENTIFY:
1243     {
1244       SilcClientEntry client_entry;
1245       
1246       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1247         /* Print the unknown nick for user */
1248         unsigned char *tmp =
1249           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1250                                      3, NULL);
1251         if (tmp)
1252           silc_say_error("%s: %s", tmp, 
1253                          silc_get_status_message(status));
1254         break;
1255       } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1256         /* Try to find the entry for the unknown client ID, since we
1257            might have, and print the nickname of it for user. */
1258         SilcUInt32 tmp_len;
1259         unsigned char *tmp =
1260           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1261                                      2, &tmp_len);
1262         if (tmp) {
1263           SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len,
1264                                                              NULL);
1265           if (client_id) {
1266             client_entry = silc_client_get_client_by_id(client, conn,
1267                                                         client_id);
1268             if (client_entry && client_entry->nickname)
1269               silc_say_error("%s: %s", client_entry->nickname,
1270                              silc_get_status_message(status));
1271             silc_free(client_id);
1272           }
1273         }
1274         break;
1275       }
1276
1277       break;
1278     }
1279
1280   case SILC_COMMAND_WHOWAS:
1281     {
1282       char *nickname, *username, *realname;
1283       
1284       if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
1285           status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1286         char *tmp;
1287         tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1288                                          3, NULL);
1289         if (tmp)
1290           silc_say_error("%s: %s", tmp, 
1291                          silc_get_status_message(status));
1292         break;
1293       } else if (!success) {
1294         silc_say_error("WHOWAS: %s", silc_get_status_message(status));
1295         return;
1296       }
1297       
1298       (void)va_arg(vp, SilcClientEntry);
1299       nickname = va_arg(vp, char *);
1300       username = va_arg(vp, char *);
1301       realname = va_arg(vp, char *);
1302       
1303       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1304                          SILCTXT_WHOWAS_USERINFO, nickname, username, 
1305                          realname ? realname : "");
1306     }
1307     break;
1308     
1309   case SILC_COMMAND_INVITE:
1310     {
1311       SilcChannelEntry channel;
1312       char *invite_list;
1313       SilcArgumentPayload args;
1314       int argc = 0;
1315       
1316       if (!success)
1317         return;
1318       
1319       channel = va_arg(vp, SilcChannelEntry);
1320       invite_list = va_arg(vp, char *);
1321
1322       args = silc_command_get_args(cmd_payload);
1323       if (args)
1324         argc = silc_argument_get_arg_num(args);
1325
1326       if (invite_list)
1327         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1328                            SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
1329                            invite_list);
1330       else if (argc == 3)
1331         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1332                            SILCTXT_CHANNEL_NO_INVITE_LIST, 
1333                            channel->channel_name);
1334     }
1335     break;
1336
1337   case SILC_COMMAND_JOIN: 
1338     {
1339       char *channel, *mode, *topic;
1340       SilcUInt32 modei;
1341       SilcChannelEntry channel_entry;
1342       SilcBuffer client_id_list;
1343       SilcUInt32 list_count;
1344
1345       if (!success)
1346         return;
1347
1348       channel = va_arg(vp, char *);
1349       channel_entry = va_arg(vp, SilcChannelEntry);
1350       modei = va_arg(vp, SilcUInt32);
1351       (void)va_arg(vp, SilcUInt32);
1352       (void)va_arg(vp, unsigned char *);
1353       (void)va_arg(vp, unsigned char *);
1354       (void)va_arg(vp, unsigned char *);
1355       topic = va_arg(vp, char *);
1356       (void)va_arg(vp, unsigned char *);
1357       list_count = va_arg(vp, SilcUInt32);
1358       client_id_list = va_arg(vp, SilcBuffer);
1359
1360       chanrec = silc_channel_find(server, channel);
1361       if (!chanrec)
1362         chanrec = silc_channel_create(server, channel, channel, TRUE);
1363
1364       if (topic) {
1365         g_free_not_null(chanrec->topic);
1366         chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1367         signal_emit("channel topic changed", 1, chanrec);
1368       }
1369
1370       mode = silc_client_chmode(modei, 
1371                                 channel_entry->channel_key ? 
1372                                 silc_cipher_get_name(channel_entry->
1373                                                      channel_key) : "",
1374                                 channel_entry->hmac ? 
1375                                 silc_hmac_get_name(channel_entry->hmac) : "");
1376       g_free_not_null(chanrec->mode);
1377       chanrec->mode = g_strdup(mode == NULL ? "" : mode);
1378       signal_emit("channel mode changed", 1, chanrec);
1379
1380       /* Resolve the client information */
1381       silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
1382                                       silc_client_join_get_users, 
1383                                       channel_entry);
1384
1385       break;
1386     }
1387
1388   case SILC_COMMAND_NICK: 
1389     {
1390       char *old;
1391       SilcClientEntry client_entry = va_arg(vp, SilcClientEntry);
1392       GSList *nicks;
1393       
1394       if (!success)
1395         return;
1396
1397       nicks = nicklist_get_same(SERVER(server), client_entry->nickname);
1398       if (nicks != NULL) {
1399         char buf[512];
1400         SilcClientEntry collider, old;
1401
1402         old = ((SILC_NICK_REC *)(nicks->next->data))->silc_user->client;
1403         collider = silc_client_get_client_by_id(client, conn,
1404                                                 old->id);
1405         
1406         memset(buf, 0, sizeof(buf));
1407         snprintf(buf, sizeof(buf) - 1, "%s@%s",
1408                  collider->username, collider->hostname);
1409         nicklist_rename_unique(SERVER(server),
1410                                old, old->nickname,
1411                                collider, collider->nickname);
1412         silc_print_nick_change(server, collider->nickname,
1413                                client_entry->nickname, buf);
1414         g_slist_free(nicks);
1415       }
1416
1417       old = g_strdup(server->nick);
1418       server_change_nick(SERVER(server), client_entry->nickname);
1419       nicklist_rename_unique(SERVER(server),
1420                              server->conn->local_entry, server->nick,
1421                              client_entry, client_entry->nickname);
1422       signal_emit("message own_nick", 4, server, server->nick, old, "");
1423       g_free(old);
1424       break;
1425     }
1426     
1427   case SILC_COMMAND_LIST:
1428     {
1429       char *topic, *name;
1430       int usercount;
1431       char users[20];
1432       char tmp[256], *cp, *dm = NULL;
1433       
1434       if (!success)
1435         return;
1436       
1437       (void)va_arg(vp, SilcChannelEntry);
1438       name = va_arg(vp, char *);
1439       topic = va_arg(vp, char *);
1440       usercount = va_arg(vp, int);
1441
1442       if (topic && !silc_term_utf8() &&
1443           silc_utf8_valid(topic, strlen(topic))) {
1444         memset(tmp, 0, sizeof(tmp));
1445         cp = tmp;
1446         if (strlen(topic) > sizeof(tmp) - 1) {
1447           dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
1448           cp = dm;
1449         }
1450
1451         silc_utf8_decode(topic, strlen(topic), SILC_STRING_LANGUAGE,
1452                          cp, strlen(topic));
1453         topic = cp;
1454       }
1455       
1456       if (status == SILC_STATUS_LIST_START ||
1457           status == SILC_STATUS_OK)
1458         printformat_module("fe-common/silc", server, NULL,
1459                            MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
1460
1461       if (!usercount)
1462         snprintf(users, sizeof(users) - 1, "N/A");
1463       else
1464         snprintf(users, sizeof(users) - 1, "%d", usercount);
1465       printformat_module("fe-common/silc", server, NULL,
1466                          MSGLEVEL_CRAP, SILCTXT_LIST,
1467                          name, users, topic ? topic : "");
1468       silc_free(dm);
1469     }
1470     break;
1471     
1472   case SILC_COMMAND_UMODE:
1473     {
1474       SilcUInt32 mode;
1475       char *reason;
1476       
1477       if (!success)
1478         return;
1479       
1480       mode = va_arg(vp, SilcUInt32);
1481       
1482       if (mode & SILC_UMODE_SERVER_OPERATOR &&
1483           !(server->umode & SILC_UMODE_SERVER_OPERATOR))
1484         printformat_module("fe-common/silc", server, NULL,
1485                            MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1486
1487       if (mode & SILC_UMODE_ROUTER_OPERATOR &&
1488           !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
1489         printformat_module("fe-common/silc", server, NULL,
1490                            MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1491
1492       if ((mode & SILC_UMODE_GONE) != (server->umode & SILC_UMODE_GONE)) {
1493         if (mode & SILC_UMODE_GONE) {      
1494           if ((server->away_reason != NULL) && (server->away_reason[0] != '\0'))
1495             reason = g_strdup(server->away_reason);
1496           else
1497             reason = g_strdup("away");
1498         } else
1499           reason = g_strdup("");
1500
1501         silc_set_away(reason, server);
1502
1503         g_free(reason);
1504       }
1505
1506       server->umode = mode;
1507       signal_emit("user mode changed", 2, server, NULL);
1508     }
1509     break;
1510     
1511   case SILC_COMMAND_OPER:
1512     if (!success)
1513       return;
1514
1515     server->umode |= SILC_UMODE_SERVER_OPERATOR;
1516     signal_emit("user mode changed", 2, server, NULL);
1517
1518     printformat_module("fe-common/silc", server, NULL,
1519                        MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1520     break;
1521     
1522   case SILC_COMMAND_SILCOPER:
1523     if (!success)
1524       return;
1525
1526     server->umode |= SILC_UMODE_ROUTER_OPERATOR;
1527     signal_emit("user mode changed", 2, server, NULL);
1528
1529     printformat_module("fe-common/silc", server, NULL,
1530                        MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1531     break;
1532     
1533   case SILC_COMMAND_USERS: 
1534     {
1535       SilcHashTableList htl;
1536       SilcChannelEntry channel;
1537       SilcChannelUser chu;
1538       
1539       if (!success)
1540         return;
1541       
1542       channel = va_arg(vp, SilcChannelEntry);
1543       
1544       printformat_module("fe-common/silc", server, channel->channel_name,
1545                          MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
1546                          channel->channel_name);
1547
1548       silc_hash_table_list(channel->user_list, &htl);
1549       while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1550         SilcClientEntry e = chu->client;
1551         char stat[5], *mode;
1552
1553         if (!e->nickname)
1554           continue;
1555         
1556         memset(stat, 0, sizeof(stat));
1557         mode = silc_client_chumode_char(chu->mode);
1558         if (e->mode & SILC_UMODE_GONE)
1559           strcat(stat, "G");
1560         else if (e->mode & SILC_UMODE_INDISPOSED)
1561           strcat(stat, "I");
1562         else if (e->mode & SILC_UMODE_BUSY)
1563           strcat(stat, "B");
1564         else if (e->mode & SILC_UMODE_PAGE)
1565           strcat(stat, "P");
1566         else if (e->mode & SILC_UMODE_HYPER)
1567           strcat(stat, "H");
1568         else if (e->mode & SILC_UMODE_ROBOT)
1569           strcat(stat, "R");
1570         else if (e->mode & SILC_UMODE_ANONYMOUS)
1571           strcat(stat, "?");
1572         else
1573           strcat(stat, "A");
1574         if (mode)
1575           strcat(stat, mode);
1576
1577         printformat_module("fe-common/silc", server, channel->channel_name,
1578                            MSGLEVEL_CRAP, SILCTXT_USERS,
1579                            e->nickname, stat, 
1580                            e->username ? e->username : "",
1581                            e->hostname ? e->hostname : "",
1582                            e->realname ? e->realname : "");
1583         if (mode)
1584           silc_free(mode);
1585       }
1586       silc_hash_table_list_reset(&htl);
1587     }
1588     break;
1589
1590   case SILC_COMMAND_BAN:
1591     {
1592       SilcChannelEntry channel;
1593       char *ban_list;
1594       
1595       if (!success)
1596         return;
1597       
1598       channel = va_arg(vp, SilcChannelEntry);
1599       ban_list = va_arg(vp, char *);
1600       
1601       if (ban_list)
1602         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1603                            SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
1604                            ban_list);
1605       else
1606         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1607                            SILCTXT_CHANNEL_NO_BAN_LIST, 
1608                            channel->channel_name);
1609     }
1610     break;
1611     
1612   case SILC_COMMAND_GETKEY:
1613     {
1614       SilcIdType id_type;
1615       void *entry;
1616       SilcPublicKey public_key;
1617       unsigned char *pk;
1618       SilcUInt32 pk_len;
1619       GetkeyContext getkey;
1620       char *name;
1621       
1622       if (!success)
1623         return;
1624       
1625       id_type = va_arg(vp, SilcUInt32);
1626       entry = va_arg(vp, void *);
1627       public_key = va_arg(vp, SilcPublicKey);
1628
1629       if (public_key) {
1630         pk = silc_pkcs_public_key_encode(public_key, &pk_len);
1631
1632         getkey = silc_calloc(1, sizeof(*getkey));
1633         getkey->entry = entry;
1634         getkey->id_type = id_type;
1635         getkey->client = client;
1636         getkey->conn = conn;
1637         getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1638
1639         name = (id_type == SILC_ID_CLIENT ? 
1640                 ((SilcClientEntry)entry)->nickname :
1641                 ((SilcServerEntry)entry)->server_name);
1642
1643         silc_verify_public_key_internal(client, conn, name,
1644                                         (id_type == SILC_ID_CLIENT ?
1645                                          SILC_SOCKET_TYPE_CLIENT :
1646                                          SILC_SOCKET_TYPE_SERVER),
1647                                         pk, pk_len, SILC_SKE_PK_TYPE_SILC,
1648                                         silc_getkey_cb, getkey);
1649         silc_free(pk);
1650       } else {
1651         printformat_module("fe-common/silc", server, NULL,
1652                            MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOKEY);
1653       }
1654     }
1655     break;
1656
1657   case SILC_COMMAND_INFO:
1658     {
1659       SilcServerEntry server_entry;
1660       char *server_name;
1661       char *server_info;
1662
1663       if (!success)
1664         return;
1665       
1666       server_entry = va_arg(vp, SilcServerEntry);
1667       server_name = va_arg(vp, char *);
1668       server_info = va_arg(vp, char *);
1669
1670       if (server_name && server_info )
1671         {
1672           printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
1673           printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
1674         }
1675     }
1676     break;
1677     
1678   case SILC_COMMAND_TOPIC:
1679     {
1680       SilcChannelEntry channel;
1681       char *topic;
1682       char tmp[256], *cp, *dm = NULL;
1683       
1684       if (!success)
1685         return;
1686       
1687       channel = va_arg(vp, SilcChannelEntry);
1688       topic = va_arg(vp, char *);
1689
1690       if (topic && !silc_term_utf8() &&
1691           silc_utf8_valid(topic, strlen(topic))) {
1692         memset(tmp, 0, sizeof(tmp));
1693         cp = tmp;
1694         if (strlen(topic) > sizeof(tmp) - 1) {
1695           dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
1696           cp = dm;
1697         }
1698
1699         silc_utf8_decode(topic, strlen(topic), SILC_STRING_LANGUAGE,
1700                          cp, strlen(topic));
1701         topic = cp;
1702       }
1703
1704       if (topic) {
1705         chanrec = silc_channel_find_entry(server, channel);
1706         if (chanrec) {
1707           g_free_not_null(chanrec->topic);
1708           chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1709           signal_emit("channel topic changed", 1, chanrec);
1710         }
1711         printformat_module("fe-common/silc", server, channel->channel_name,
1712                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
1713                            channel->channel_name, topic);
1714       } else {
1715         printformat_module("fe-common/silc", server, channel->channel_name,
1716                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
1717                            channel->channel_name);
1718       }
1719       silc_free(dm);
1720     }
1721     break;
1722
1723   case SILC_COMMAND_WATCH:
1724     break;
1725   
1726   case SILC_COMMAND_STATS:
1727     {
1728       SilcUInt32 starttime, uptime, my_clients, my_channels, my_server_ops,
1729                  my_router_ops, cell_clients, cell_channels, cell_servers,
1730                  clients, channels, servers, routers, server_ops, router_ops;
1731       SilcUInt32 buf_len;
1732       SilcBufferStruct buf;
1733       unsigned char *tmp_buf;
1734       char tmp[40];
1735       const char *tmptime;
1736       int days, hours, mins, secs;
1737
1738       if (!success)
1739         return;
1740
1741       tmp_buf = va_arg(vp, unsigned char *);
1742       buf_len = va_arg(vp, SilcUInt32);
1743
1744       if (!tmp_buf || !buf_len) {
1745         printtext(server, NULL, MSGLEVEL_CRAP, "No statistics available");
1746         return;
1747       }
1748
1749       /* Get statistics structure */
1750       silc_buffer_set(&buf, tmp_buf, buf_len);
1751       silc_buffer_unformat(&buf,
1752                            SILC_STR_UI_INT(&starttime),
1753                            SILC_STR_UI_INT(&uptime),
1754                            SILC_STR_UI_INT(&my_clients),
1755                            SILC_STR_UI_INT(&my_channels),
1756                            SILC_STR_UI_INT(&my_server_ops),
1757                            SILC_STR_UI_INT(&my_router_ops),
1758                            SILC_STR_UI_INT(&cell_clients),
1759                            SILC_STR_UI_INT(&cell_channels),
1760                            SILC_STR_UI_INT(&cell_servers),
1761                            SILC_STR_UI_INT(&clients),
1762                            SILC_STR_UI_INT(&channels),
1763                            SILC_STR_UI_INT(&servers),
1764                            SILC_STR_UI_INT(&routers),
1765                            SILC_STR_UI_INT(&server_ops),
1766                            SILC_STR_UI_INT(&router_ops),
1767                            SILC_STR_END);
1768
1769       tmptime = silc_get_time(starttime);
1770       printformat_module("fe-common/silc", server, NULL,
1771                          MSGLEVEL_CRAP, SILCTXT_STATS,
1772                          "Local server start time", tmptime);
1773
1774       days = uptime / (24 * 60 * 60);
1775       uptime -= days * (24 * 60 * 60);
1776       hours = uptime / (60 * 60);
1777       uptime -= hours * (60 * 60);
1778       mins = uptime / 60;
1779       uptime -= mins * 60;
1780       secs = uptime;
1781       snprintf(tmp, sizeof(tmp) - 1, "%d days %d hours %d mins %d secs",
1782                days, hours, mins, secs);
1783       printformat_module("fe-common/silc", server, NULL,
1784                          MSGLEVEL_CRAP, SILCTXT_STATS,
1785                          "Local server uptime", tmp);
1786
1787       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_clients);
1788       printformat_module("fe-common/silc", server, NULL,
1789                          MSGLEVEL_CRAP, SILCTXT_STATS,
1790                          "Local server clients", tmp);
1791
1792       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_channels);
1793       printformat_module("fe-common/silc", server, NULL,
1794                          MSGLEVEL_CRAP, SILCTXT_STATS,
1795                          "Local server channels", tmp);
1796
1797       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_server_ops);
1798       printformat_module("fe-common/silc", server, NULL,
1799                          MSGLEVEL_CRAP, SILCTXT_STATS,
1800                          "Local server operators", tmp);
1801
1802       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_router_ops);
1803       printformat_module("fe-common/silc", server, NULL,
1804                          MSGLEVEL_CRAP, SILCTXT_STATS,
1805                          "Local router operators", tmp);
1806
1807       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_clients);
1808       printformat_module("fe-common/silc", server, NULL,
1809                          MSGLEVEL_CRAP, SILCTXT_STATS,
1810                          "Local cell clients", tmp);
1811
1812       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_channels);
1813       printformat_module("fe-common/silc", server, NULL,
1814                          MSGLEVEL_CRAP, SILCTXT_STATS,
1815                          "Local cell channels", tmp);
1816
1817       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_servers);
1818       printformat_module("fe-common/silc", server, NULL,
1819                          MSGLEVEL_CRAP, SILCTXT_STATS,
1820                          "Local cell servers", tmp);
1821
1822       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)clients);
1823       printformat_module("fe-common/silc", server, NULL,
1824                          MSGLEVEL_CRAP, SILCTXT_STATS,
1825                          "Total clients", tmp);
1826
1827       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)channels);
1828       printformat_module("fe-common/silc", server, NULL,
1829                          MSGLEVEL_CRAP, SILCTXT_STATS,
1830                          "Total channels", tmp);
1831
1832       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)servers);
1833       printformat_module("fe-common/silc", server, NULL,
1834                          MSGLEVEL_CRAP, SILCTXT_STATS,
1835                          "Total servers", tmp);
1836
1837       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)routers);
1838       printformat_module("fe-common/silc", server, NULL,
1839                          MSGLEVEL_CRAP, SILCTXT_STATS,
1840                          "Total routers", tmp);
1841
1842       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)server_ops);
1843       printformat_module("fe-common/silc", server, NULL,
1844                          MSGLEVEL_CRAP, SILCTXT_STATS,
1845                            "Total server operators", tmp);
1846
1847       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)router_ops);
1848       printformat_module("fe-common/silc", server, NULL,
1849                          MSGLEVEL_CRAP, SILCTXT_STATS,
1850                          "Total router operators", tmp);
1851     }
1852     break;
1853
1854   }
1855
1856   va_end(vp);
1857 }
1858
1859 typedef struct {
1860   SilcClient client;
1861   SilcClientConnection conn;
1862   char *filename;
1863   char *entity;
1864   char *entity_name;
1865   unsigned char *pk;
1866   SilcUInt32 pk_len;
1867   SilcSKEPKType pk_type;
1868   SilcVerifyPublicKey completion;
1869   void *context;
1870 } *PublicKeyVerify;
1871
1872 static void verify_public_key_completion(const char *line, void *context)
1873 {
1874   PublicKeyVerify verify = (PublicKeyVerify)context;
1875
1876   if (line[0] == 'Y' || line[0] == 'y') {
1877     /* Call the completion */
1878     if (verify->completion)
1879       verify->completion(TRUE, verify->context);
1880
1881     /* Save the key for future checking */
1882     silc_pkcs_save_public_key_data(verify->filename, verify->pk, 
1883                                    verify->pk_len, SILC_PKCS_FILE_PEM);
1884   } else {
1885     /* Call the completion */
1886     if (verify->completion)
1887       verify->completion(FALSE, verify->context);
1888
1889     printformat_module("fe-common/silc", NULL, NULL,
1890                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, 
1891                        verify->entity_name ? verify->entity_name :
1892                        verify->entity);
1893   }
1894
1895   silc_free(verify->filename);
1896   silc_free(verify->entity);
1897   silc_free(verify->entity_name);
1898   silc_free(verify->pk);
1899   silc_free(verify);
1900 }
1901
1902 /* Internal routine to verify public key. If the `completion' is provided
1903    it will be called to indicate whether public was verified or not. For
1904    server/router public key this will check for filename that includes the
1905    remote host's IP address and remote host's hostname. */
1906
1907 static void 
1908 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
1909                                 const char *name, SilcSocketType conn_type, 
1910                                 unsigned char *pk, SilcUInt32 pk_len, 
1911                                 SilcSKEPKType pk_type,
1912                                 SilcVerifyPublicKey completion, void *context)
1913 {
1914   int i;
1915   char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
1916   char *fingerprint, *babbleprint, *format;
1917   struct passwd *pw;
1918   struct stat st;
1919   char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
1920                    conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
1921                   "server" : "client");
1922   PublicKeyVerify verify;
1923
1924   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
1925     printformat_module("fe-common/silc", NULL, NULL,
1926                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED, 
1927                        entity, pk_type);
1928     if (completion)
1929       completion(FALSE, context);
1930     return;
1931   }
1932
1933   pw = getpwuid(getuid());
1934   if (!pw) {
1935     if (completion)
1936       completion(FALSE, context);
1937     return;
1938   }
1939
1940   memset(filename, 0, sizeof(filename));
1941   memset(filename2, 0, sizeof(filename2));
1942   memset(file, 0, sizeof(file));
1943
1944   if (conn_type == SILC_SOCKET_TYPE_SERVER ||
1945       conn_type == SILC_SOCKET_TYPE_ROUTER) {
1946     if (!name) {
1947       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1948                conn->sock->ip, conn->sock->port);
1949       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
1950                get_irssi_dir(), entity, file);
1951       
1952       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1953                conn->sock->hostname, conn->sock->port);
1954       snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s", 
1955                get_irssi_dir(), entity, file);
1956       
1957       ipf = filename;
1958       hostf = filename2;
1959     } else {
1960       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1961                name, conn->sock->port);
1962       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
1963                get_irssi_dir(), entity, file);
1964       
1965       ipf = filename;
1966     }
1967   } else {
1968     /* Replace all whitespaces with `_'. */
1969     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1970     for (i = 0; i < strlen(fingerprint); i++)
1971       if (fingerprint[i] == ' ')
1972         fingerprint[i] = '_';
1973     
1974     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
1975     snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
1976              get_irssi_dir(), entity, file);
1977     silc_free(fingerprint);
1978
1979     ipf = filename;
1980   }
1981
1982   /* Take fingerprint of the public key */
1983   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1984   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
1985
1986   verify = silc_calloc(1, sizeof(*verify));
1987   verify->client = client;
1988   verify->conn = conn;
1989   verify->filename = strdup(ipf);
1990   verify->entity = strdup(entity);
1991   verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ?
1992                          (name ? strdup(name) : strdup(conn->sock->hostname))
1993                          : NULL);
1994   verify->pk = silc_memdup(pk, pk_len);
1995   verify->pk_len = pk_len;
1996   verify->pk_type = pk_type;
1997   verify->completion = completion;
1998   verify->context = context;
1999
2000   /* Check whether this key already exists */
2001   if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
2002     /* Key does not exist, ask user to verify the key and save it */
2003
2004     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2005                        SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
2006                        verify->entity_name : entity);
2007     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2008                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2009     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2010                        SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2011     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2012                              SILCTXT_PUBKEY_ACCEPT);
2013     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2014                             format, 0, verify);
2015     g_free(format);
2016     silc_free(fingerprint);
2017     return;
2018   } else {
2019     /* The key already exists, verify it. */
2020     SilcPublicKey public_key;
2021     unsigned char *encpk;
2022     SilcUInt32 encpk_len;
2023
2024     /* Load the key file, try for both IP filename and hostname filename */
2025     if (!silc_pkcs_load_public_key(ipf, &public_key, 
2026                                    SILC_PKCS_FILE_PEM) &&
2027         !silc_pkcs_load_public_key(ipf, &public_key, 
2028                                    SILC_PKCS_FILE_BIN) &&
2029         (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key, 
2030                                                SILC_PKCS_FILE_PEM) &&
2031                     !silc_pkcs_load_public_key(hostf, &public_key, 
2032                                                SILC_PKCS_FILE_BIN)))) {
2033       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2034                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
2035                          verify->entity_name : entity);
2036       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2037                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2038       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2039                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2040       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2041                          SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
2042       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2043                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2044       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2045                               format, 0, verify);
2046       g_free(format);
2047       silc_free(fingerprint);
2048       return;
2049     }
2050
2051     /* Encode the key data */
2052     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
2053     if (!encpk) {
2054       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2055                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
2056                          verify->entity_name : entity);
2057       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2058                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2059       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2060                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2061       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2062                          SILCTXT_PUBKEY_MALFORMED, entity);
2063       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2064                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2065       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2066                               format, 0, verify);
2067       g_free(format);
2068       silc_free(fingerprint);
2069       return;
2070     }
2071
2072     /* Compare the keys */
2073     if (memcmp(encpk, pk, encpk_len)) {
2074       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2075                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
2076                          verify->entity_name : entity);
2077       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2078                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2079       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2080                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2081       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2082                          SILCTXT_PUBKEY_NO_MATCH, entity);
2083       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2084                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
2085       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2086                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
2087
2088       /* Ask user to verify the key and save it */
2089       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2090                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2091       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2092                               format, 0, verify);
2093       g_free(format);
2094       silc_free(fingerprint);
2095       return;
2096     }
2097
2098     /* Local copy matched */
2099     if (completion)
2100       completion(TRUE, context);
2101     silc_free(fingerprint);
2102     silc_free(verify->filename);
2103     silc_free(verify->entity);
2104     silc_free(verify->entity_name);
2105     silc_free(verify->pk);
2106     silc_free(verify);
2107   }
2108 }
2109
2110 /* Verifies received public key. The `conn_type' indicates which entity
2111    (server, client etc.) has sent the public key. If user decides to trust
2112    the key may be saved as trusted public key for later use. The 
2113    `completion' must be called after the public key has been verified. */
2114
2115 void 
2116 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
2117                        SilcSocketType conn_type, unsigned char *pk, 
2118                        SilcUInt32 pk_len, SilcSKEPKType pk_type,
2119                        SilcVerifyPublicKey completion, void *context)
2120 {
2121   silc_verify_public_key_internal(client, conn, NULL, conn_type, pk,
2122                                   pk_len, pk_type,
2123                                   completion, context);
2124 }
2125
2126 /* Asks passphrase from user on the input line. */
2127
2128 typedef struct {
2129   SilcAskPassphrase completion;
2130   void *context;
2131 } *AskPassphrase;
2132
2133 void ask_passphrase_completion(const char *passphrase, void *context)
2134 {
2135   AskPassphrase p = (AskPassphrase)context;
2136   if (passphrase && passphrase[0] == '\0')
2137     passphrase = NULL;
2138   p->completion((unsigned char *)passphrase, 
2139                 passphrase ? strlen(passphrase) : 0, p->context);
2140   silc_free(p);
2141 }
2142
2143 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
2144                          SilcAskPassphrase completion, void *context)
2145 {
2146   AskPassphrase p = silc_calloc(1, sizeof(*p));
2147   p->completion = completion;
2148   p->context = context;
2149
2150   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
2151                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
2152 }
2153
2154 typedef struct {
2155   SilcGetAuthMeth completion;
2156   void *context;
2157 } *InternalGetAuthMethod;
2158
2159 /* Callback called when we've received the authentication method information
2160    from the server after we've requested it. This will get the authentication
2161    data from the user if needed. */
2162
2163 static void silc_get_auth_method_callback(SilcClient client,
2164                                           SilcClientConnection conn,
2165                                           SilcAuthMethod auth_meth,
2166                                           void *context)
2167 {
2168   InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
2169
2170   SILC_LOG_DEBUG(("Start"));
2171
2172   switch (auth_meth) {
2173   case SILC_AUTH_NONE:
2174     /* No authentication required. */
2175     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2176     break;
2177   case SILC_AUTH_PASSWORD:
2178     {
2179       /* Check whether we find the password for this server in our
2180          configuration.  If not, then don't provide so library will ask
2181          it from the user. */
2182       SERVER_SETUP_REC *setup = server_setup_find_port(conn->remote_host,
2183                                                        conn->remote_port);
2184       if (!setup || !setup->password) {
2185         (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2186         break;
2187       }
2188       
2189       (*internal->completion)(TRUE, auth_meth, setup->password,
2190                               strlen(setup->password), internal->context);
2191     }
2192     break;
2193   case SILC_AUTH_PUBLIC_KEY:
2194     /* Do not get the authentication data now, the library will generate
2195        it using our default key, if we do not provide it here. */
2196     /* XXX In the future when we support multiple local keys and multiple
2197        local certificates we will need to ask from user which one to use. */
2198     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2199     break;
2200   }
2201
2202   silc_free(internal);
2203 }
2204
2205 /* Find authentication method and authentication data by hostname and
2206    port. The hostname may be IP address as well. The found authentication
2207    method and authentication data is returned to `auth_meth', `auth_data'
2208    and `auth_data_len'. The function returns TRUE if authentication method
2209    is found and FALSE if not. `conn' may be NULL. */
2210
2211 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
2212                           char *hostname, SilcUInt16 port,
2213                           SilcGetAuthMeth completion, void *context)
2214 {
2215   InternalGetAuthMethod internal;
2216
2217   SILC_LOG_DEBUG(("Start"));
2218
2219   /* If we do not have this connection configured by the user in a
2220      configuration file then resolve the authentication method from the
2221      server for this session. */
2222   internal = silc_calloc(1, sizeof(*internal));
2223   internal->completion = completion;
2224   internal->context = context;
2225
2226   silc_client_request_authentication_method(client, conn, 
2227                                             silc_get_auth_method_callback,
2228                                             internal);
2229 }
2230
2231 /* Notifies application that failure packet was received.  This is called
2232    if there is some protocol active in the client.  The `protocol' is the
2233    protocol context.  The `failure' is opaque pointer to the failure
2234    indication.  Note, that the `failure' is protocol dependant and application
2235    must explicitly cast it to correct type.  Usually `failure' is 32 bit
2236    failure type (see protocol specs for all protocol failure types). */
2237
2238 void silc_failure(SilcClient client, SilcClientConnection conn, 
2239                   SilcProtocol protocol, void *failure)
2240 {
2241   SILC_LOG_DEBUG(("Start"));
2242
2243   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
2244     SilcSKEStatus status = (SilcSKEStatus)failure;
2245     
2246     if (status == SILC_SKE_STATUS_BAD_VERSION)
2247       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2248                          SILCTXT_KE_BAD_VERSION);
2249     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
2250       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2251                          SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
2252     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
2253       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2254                          SILCTXT_KE_UNKNOWN_GROUP);
2255     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
2256       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2257                          SILCTXT_KE_UNKNOWN_CIPHER);
2258     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
2259       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2260                          SILCTXT_KE_UNKNOWN_PKCS);
2261     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
2262       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2263                          SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
2264     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
2265       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2266                          SILCTXT_KE_UNKNOWN_HMAC);
2267     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
2268       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2269                          SILCTXT_KE_INCORRECT_SIGNATURE);
2270     if (status == SILC_SKE_STATUS_INVALID_COOKIE)
2271       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2272                          SILCTXT_KE_INVALID_COOKIE);
2273   }
2274
2275   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
2276     SilcUInt32 err = (SilcUInt32)failure;
2277
2278     if (err == SILC_AUTH_FAILED)
2279       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2280                          SILCTXT_AUTH_FAILED);
2281   }
2282 }
2283
2284 /* Asks whether the user would like to perform the key agreement protocol.
2285    This is called after we have received an key agreement packet or an
2286    reply to our key agreement packet. This returns TRUE if the user wants
2287    the library to perform the key agreement protocol and FALSE if it is not
2288    desired (application may start it later by calling the function
2289    silc_client_perform_key_agreement). */
2290
2291 bool silc_key_agreement(SilcClient client, SilcClientConnection conn,
2292                         SilcClientEntry client_entry, const char *hostname,
2293                         SilcUInt16 port, SilcKeyAgreementCallback *completion,
2294                         void **context)
2295 {
2296   char portstr[12];
2297
2298   SILC_LOG_DEBUG(("Start"));
2299
2300   /* We will just display the info on the screen and return FALSE and user
2301      will have to start the key agreement with a command. */
2302
2303   if (hostname) 
2304     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2305
2306   if (!hostname)
2307     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2308                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
2309   else
2310     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2311                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST, 
2312                        client_entry->nickname, hostname, portstr);
2313
2314   *completion = NULL;
2315   *context = NULL;
2316
2317   return FALSE;
2318 }
2319
2320 /* Notifies application that file transfer protocol session is being
2321    requested by the remote client indicated by the `client_entry' from
2322    the `hostname' and `port'. The `session_id' is the file transfer
2323    session and it can be used to either accept or reject the file
2324    transfer request, by calling the silc_client_file_receive or
2325    silc_client_file_close, respectively. */
2326
2327 void silc_ftp(SilcClient client, SilcClientConnection conn,
2328               SilcClientEntry client_entry, SilcUInt32 session_id,
2329               const char *hostname, SilcUInt16 port)
2330 {
2331   SILC_SERVER_REC *server;
2332   char portstr[12];
2333   FtpSession ftp = NULL;
2334
2335   SILC_LOG_DEBUG(("Start"));
2336
2337   server = conn->context;
2338
2339   silc_dlist_start(server->ftp_sessions);
2340   while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
2341     if (ftp->client_entry == client_entry &&
2342         ftp->session_id == session_id) {
2343       server->current_session = ftp;
2344       break;
2345     }
2346   }
2347   if (ftp == SILC_LIST_END) {
2348     ftp = silc_calloc(1, sizeof(*ftp));
2349     ftp->client_entry = client_entry;
2350     ftp->session_id = session_id;
2351     ftp->send = FALSE;
2352     ftp->conn = conn;
2353     silc_dlist_add(server->ftp_sessions, ftp);
2354     server->current_session = ftp;
2355   }
2356
2357   if (hostname) 
2358     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2359
2360   if (!hostname)
2361     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2362                        SILCTXT_FILE_REQUEST, client_entry->nickname);
2363   else
2364     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2365                        SILCTXT_FILE_REQUEST_HOST, 
2366                        client_entry->nickname, hostname, portstr);
2367 }
2368
2369 /* Delivers SILC session detachment data indicated by `detach_data' to the
2370    application.  If application has issued SILC_COMMAND_DETACH command
2371    the client session in the SILC network is not quit.  The client remains
2372    in the network but is detached.  The detachment data may be used later
2373    to resume the session in the SILC Network.  The appliation is
2374    responsible of saving the `detach_data', to for example in a file.
2375
2376    The detachment data can be given as argument to the functions
2377    silc_client_connect_to_server, or silc_client_add_connection when
2378    creating connection to remote server, inside SilcClientConnectionParams
2379    structure.  If it is provided the client library will attempt to resume
2380    the session in the network.  After the connection is created
2381    successfully, the application is responsible of setting the user
2382    interface for user into the same state it was before detaching (showing
2383    same channels, channel modes, etc).  It can do this by fetching the
2384    information (like joined channels) from the client library. */
2385
2386 void
2387 silc_detach(SilcClient client, SilcClientConnection conn,
2388             const unsigned char *detach_data, SilcUInt32 detach_data_len)
2389 {
2390   char file[256];
2391
2392   /* Save the detachment data to file. */
2393
2394   memset(file, 0, sizeof(file));
2395   snprintf(file, sizeof(file) - 1, "%s/session", get_irssi_dir());
2396   silc_file_writefile(file, detach_data, detach_data_len);
2397 }
2398
2399
2400 /* SILC client operations */
2401 SilcClientOperations ops = {
2402   silc_say,
2403   silc_channel_message,
2404   silc_private_message,
2405   silc_notify,
2406   silc_command,
2407   silc_command_reply,
2408   silc_connect,
2409   silc_disconnect,
2410   silc_get_auth_method,
2411   silc_verify_public_key,
2412   silc_ask_passphrase,
2413   silc_failure,
2414   silc_key_agreement,
2415   silc_ftp,
2416   silc_detach,
2417 };