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