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