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