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