Fix file transfer crash
[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 - 2014 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 && *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     /* Save the key for future checking */
2406     silc_pkcs_save_public_key(verify->filename, verify->public_key,
2407                               SILC_PKCS_FILE_BASE64);
2408
2409     /* Call the completion */
2410     if (verify->completion)
2411       verify->completion(TRUE, verify->context);
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   if (conn_type != SILC_CONN_CLIENT) {
2479     server = (SILC_SERVER_REC*)conn->context;
2480     SILC_VERIFY(server);
2481     if (!server) {
2482       if (completion)
2483         completion(FALSE, context);
2484       return;
2485     }
2486   }
2487
2488   if (silc_pkcs_get_type(public_key) != SILC_PKCS_SILC) {
2489     printformat_module("fe-common/silc", NULL, NULL,
2490                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
2491                        entity, silc_pkcs_get_type(public_key));
2492     if (completion)
2493       completion(FALSE, context);
2494     return;
2495   }
2496
2497   /* Encode public key */
2498   pk = silc_pkcs_public_key_encode(public_key, &pk_len);
2499   if (!pk) {
2500     if (completion)
2501       completion(FALSE, context);
2502     return;
2503   }
2504
2505   silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
2506
2507   pw = getpwuid(getuid());
2508   if (!pw) {
2509     if (completion)
2510       completion(FALSE, context);
2511     silc_free(pk);
2512     return;
2513   }
2514
2515   memset(filename, 0, sizeof(filename));
2516   memset(filename2, 0, sizeof(filename2));
2517   memset(file, 0, sizeof(file));
2518
2519   /* Get remote host information */
2520   silc_socket_stream_get_info(silc_packet_stream_get_stream(conn->stream),
2521                               NULL, &hostname, &ip, &port);
2522
2523   if (conn_type == SILC_CONN_SERVER ||
2524       conn_type == SILC_CONN_ROUTER) {
2525     if (!name) {
2526       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, ip, port);
2527       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2528                get_irssi_dir(), entity, file);
2529
2530       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2531                hostname, port);
2532       snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s",
2533                get_irssi_dir(), entity, file);
2534
2535       ipf = filename;
2536       hostf = filename2;
2537     } else {
2538       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2539                name, port);
2540       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2541                get_irssi_dir(), entity, file);
2542
2543       ipf = filename;
2544     }
2545   } else {
2546     /* Replace all whitespaces with `_'. */
2547     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2548     for (i = 0; i < strlen(fingerprint); i++)
2549       if (fingerprint[i] == ' ')
2550         fingerprint[i] = '_';
2551
2552     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
2553     snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2554              get_irssi_dir(), entity, file);
2555     silc_free(fingerprint);
2556
2557     ipf = filename;
2558   }
2559
2560   /* Take fingerprint of the public key */
2561   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2562   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
2563
2564   verify = silc_calloc(1, sizeof(*verify));
2565   verify->client = client;
2566   verify->conn = conn;
2567   verify->filename = strdup(ipf);
2568   verify->entity = strdup(entity);
2569   verify->entity_name = (conn_type != SILC_CONN_CLIENT ?
2570                          (name ? strdup(name) : strdup(hostname))
2571                          : NULL);
2572   verify->public_key = public_key;
2573   verify->completion = completion;
2574   verify->context = context;
2575
2576   /* Check whether this key already exists */
2577   if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
2578     /* Key does not exist, ask user to verify the key and save it */
2579
2580     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2581                        SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2582                        verify->entity_name : entity);
2583     if (conn_type == SILC_CONN_CLIENT && name &&
2584         silc_pubkey->identifier.realname)
2585       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2586                          SILCTXT_PUBKEY_RECEIVED_CLIENT, name,
2587                          silc_pubkey->identifier.realname,
2588                          silc_pubkey->identifier.email ?
2589                          silc_pubkey->identifier.email : "");
2590     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2591                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2592     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2593                        SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2594     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2595                              SILCTXT_PUBKEY_ACCEPT);
2596     silc_keyboard_entry_redirect(verify_public_key_completion,
2597                             format, 0, verify, &server->prompt_op);
2598     g_free(format);
2599     silc_free(fingerprint);
2600     silc_free(babbleprint);
2601     silc_free(pk);
2602     return;
2603   } else {
2604     /* The key already exists, verify it. */
2605     unsigned char *encpk;
2606     SilcUInt32 encpk_len;
2607
2608     /* Load the key file, try for both IP filename and hostname filename */
2609     if (!silc_pkcs_load_public_key(ipf, &local_pubkey) &&
2610         (!hostf || (!silc_pkcs_load_public_key(hostf, &local_pubkey)))) {
2611       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2612                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2613                          verify->entity_name : entity);
2614       if (conn_type == SILC_CONN_CLIENT && name &&
2615           silc_pubkey->identifier.realname)
2616         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2617                            SILCTXT_PUBKEY_RECEIVED_CLIENT, name,
2618                            silc_pubkey->identifier.realname,
2619                            silc_pubkey->identifier.email ?
2620                            silc_pubkey->identifier.email : "");
2621       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2622                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2623       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2624                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2625       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2626                          SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
2627       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2628                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2629       silc_keyboard_entry_redirect(verify_public_key_completion,
2630                               format, 0, verify, &server->prompt_op);
2631       g_free(format);
2632       silc_free(fingerprint);
2633       silc_free(babbleprint);
2634       silc_free(pk);
2635       return;
2636     }
2637
2638     /* Encode the key data */
2639     encpk = silc_pkcs_public_key_encode(local_pubkey, &encpk_len);
2640     if (!encpk) {
2641       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2642                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2643                          verify->entity_name : entity);
2644       if (conn_type == SILC_CONN_CLIENT && name &&
2645           silc_pubkey->identifier.realname)
2646         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2647                            SILCTXT_PUBKEY_RECEIVED_CLIENT, name,
2648                            silc_pubkey->identifier.realname,
2649                            silc_pubkey->identifier.email ?
2650                            silc_pubkey->identifier.email : "");
2651       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2652                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2653       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2654                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2655       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2656                          SILCTXT_PUBKEY_MALFORMED, entity);
2657       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2658                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2659       silc_keyboard_entry_redirect(verify_public_key_completion,
2660                               format, 0, verify, &server->prompt_op);
2661       g_free(format);
2662       silc_free(fingerprint);
2663       silc_free(babbleprint);
2664       silc_free(pk);
2665       return;
2666     }
2667     silc_pkcs_public_key_free(local_pubkey);
2668
2669     /* Compare the keys */
2670     if (memcmp(encpk, pk, encpk_len)) {
2671       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2672                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2673                          verify->entity_name : entity);
2674       if (conn_type == SILC_CONN_CLIENT && name &&
2675           silc_pubkey->identifier.realname)
2676         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2677                            SILCTXT_PUBKEY_RECEIVED_CLIENT, name,
2678                            silc_pubkey->identifier.realname,
2679                            silc_pubkey->identifier.email ?
2680                            silc_pubkey->identifier.email : "");
2681       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2682                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2683       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2684                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2685       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2686                          SILCTXT_PUBKEY_NO_MATCH, entity);
2687       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2688                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
2689       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2690                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
2691
2692       /* Ask user to verify the key and save it */
2693       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2694                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2695       silc_keyboard_entry_redirect(verify_public_key_completion,
2696                               format, 0, verify, &server->prompt_op);
2697       g_free(format);
2698       silc_free(fingerprint);
2699       silc_free(babbleprint);
2700       silc_free(encpk);
2701       silc_free(pk);
2702       return;
2703     }
2704
2705     /* Local copy matched */
2706     if (completion)
2707       completion(TRUE, context);
2708     silc_free(encpk);
2709     silc_free(fingerprint);
2710     silc_free(babbleprint);
2711     silc_free(verify->filename);
2712     silc_free(verify->entity);
2713     silc_free(verify->entity_name);
2714     silc_free(verify);
2715     silc_free(pk);
2716   }
2717 }
2718
2719 /* Verifies received public key. The `conn_type' indicates which entity
2720    (server, client etc.) has sent the public key. If user decides to trust
2721    the key may be saved as trusted public key for later use. The
2722    `completion' must be called after the public key has been verified. */
2723
2724 void
2725 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
2726                        SilcConnectionType conn_type,
2727                        SilcPublicKey public_key,
2728                        SilcVerifyPublicKey completion, void *context)
2729 {
2730   silc_verify_public_key_internal(client, conn, NULL, conn_type, public_key,
2731                                   completion, context);
2732 }
2733
2734 /* Asks passphrase from user on the input line. */
2735
2736 typedef struct {
2737   SilcAskPassphrase completion;
2738   SilcClientConnection conn;
2739   void *context;
2740 } *AskPassphrase;
2741
2742 void ask_passphrase_completion(const char *passphrase, void *context,
2743                                SilcKeyboardPromptStatus reason)
2744 {
2745   AskPassphrase p = (AskPassphrase)context;
2746   if (passphrase && passphrase[0] == '\0')
2747     passphrase = NULL;
2748   p->completion((unsigned char *)passphrase,
2749                 passphrase ? strlen(passphrase) : 0, p->context);
2750
2751   if (reason != KeyboardCompletionFailed) {
2752     SILC_SERVER_REC *server = (SILC_SERVER_REC *)(p->conn->context);
2753     server->prompt_op = NULL;
2754   }
2755
2756   silc_free(p);
2757 }
2758
2759 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
2760                          SilcAskPassphrase completion, void *context)
2761 {
2762   SILC_SERVER_REC *server = (SILC_SERVER_REC*)(conn->context);
2763   AskPassphrase p;
2764
2765   p = silc_calloc(1, sizeof(*p));
2766   if (!p) {
2767     if (completion)
2768       completion(NULL, 0, context);
2769     return;
2770   }
2771
2772   p->completion = completion;
2773   p->conn       = conn;
2774   p->context    = context;
2775
2776   silc_keyboard_entry_redirect(ask_passphrase_completion,
2777                                "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN,
2778                                p, &server->prompt_op);
2779 }
2780
2781 typedef struct {
2782   SilcGetAuthMeth completion;
2783   void *context;
2784 } *GetAuthMethod;
2785
2786 static void silc_get_auth_ask_passphrase(const unsigned char *passphrase,
2787                                          SilcUInt32 passphrase_len,
2788                                          void *context)
2789 {
2790   GetAuthMethod a = context;
2791   a->completion(passphrase ? SILC_AUTH_PASSWORD : SILC_AUTH_NONE,
2792                 passphrase, passphrase_len, a->context);
2793   silc_free(a);
2794 }
2795
2796 /* Find authentication data by hostname and port. The hostname may be IP
2797    address as well.*/
2798
2799 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
2800                           char *hostname, SilcUInt16 port,
2801                           SilcAuthMethod auth_meth,
2802                           SilcGetAuthMeth completion, void *context)
2803 {
2804   SERVER_SETUP_REC *setup;
2805
2806   SILC_LOG_DEBUG(("Start"));
2807
2808   if (auth_meth == SILC_AUTH_PUBLIC_KEY) {
2809     /* Returning NULL will cause library to use our private key configured
2810        for this connection */
2811     completion(SILC_AUTH_PUBLIC_KEY, NULL, 0, context);
2812     return;
2813   }
2814
2815   /* Check whether we find the password for this server in our
2816      configuration.  If it's set, always send it server. */
2817   setup = server_setup_find_port(hostname, port);
2818   if (setup && setup->password) {
2819     completion(SILC_AUTH_PASSWORD, setup->password, strlen(setup->password),
2820                context);
2821     return;
2822   }
2823
2824   /* Didn't find password.  If server wants it, ask it from user. */
2825   if (auth_meth == SILC_AUTH_PASSWORD) {
2826     GetAuthMethod a;
2827     a = silc_calloc(1, sizeof(*a));
2828     if (a) {
2829       a->completion = completion;
2830       a->context = context;
2831       silc_ask_passphrase(client, conn, silc_get_auth_ask_passphrase, a);
2832       return;
2833     }
2834   }
2835
2836   /* No authentication */
2837   completion(SILC_AUTH_NONE, NULL, 0, context);
2838 }
2839
2840 /* Asks whether the user would like to perform the key agreement protocol.
2841    This is called after we have received an key agreement packet or an
2842    reply to our key agreement packet. This returns TRUE if the user wants
2843    the library to perform the key agreement protocol and FALSE if it is not
2844    desired (application may start it later by calling the function
2845    silc_client_perform_key_agreement). */
2846
2847 void silc_key_agreement(SilcClient client, SilcClientConnection conn,
2848                         SilcClientEntry client_entry, const char *hostname,
2849                         SilcUInt16 protocol, SilcUInt16 port)
2850 {
2851   char portstr[12], protostr[5];
2852
2853   SILC_LOG_DEBUG(("Start"));
2854
2855   /* We will just display the info on the screen and return FALSE and user
2856      will have to start the key agreement with a command. */
2857
2858   if (hostname) {
2859     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2860     snprintf(protostr, sizeof(protostr) - 1, "%s", protocol == 1 ? "UDP" :
2861              "TCP");
2862   }
2863
2864   if (!hostname)
2865     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2866                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
2867   else
2868     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2869                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
2870                        client_entry->nickname, hostname, portstr, protostr);
2871 }
2872
2873 /* Notifies application that file transfer protocol session is being
2874    requested by the remote client indicated by the `client_entry' from
2875    the `hostname' and `port'. The `session_id' is the file transfer
2876    session and it can be used to either accept or reject the file
2877    transfer request, by calling the silc_client_file_receive or
2878    silc_client_file_close, respectively. */
2879
2880 void silc_ftp(SilcClient client, SilcClientConnection conn,
2881               SilcClientEntry client_entry, SilcUInt32 session_id,
2882               const char *hostname, SilcUInt16 port)
2883 {
2884   SILC_SERVER_REC *server;
2885   char portstr[12];
2886   FtpSession ftp = NULL;
2887
2888   SILC_LOG_DEBUG(("Start"));
2889
2890   server = conn->context;
2891
2892   silc_dlist_start(server->ftp_sessions);
2893   while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
2894     if (ftp->client_entry == client_entry &&
2895         ftp->session_id == session_id) {
2896       server->current_session = ftp;
2897       break;
2898     }
2899   }
2900   if (ftp == SILC_LIST_END) {
2901     ftp = silc_calloc(1, sizeof(*ftp));
2902     ftp->client_entry = client_entry;
2903     ftp->session_id = session_id;
2904     ftp->send = FALSE;
2905     ftp->conn = conn;
2906     silc_dlist_add(server->ftp_sessions, ftp);
2907     server->current_session = ftp;
2908   }
2909
2910   if (hostname)
2911     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2912
2913   if (!hostname)
2914     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2915                        SILCTXT_FILE_REQUEST, client_entry->nickname);
2916   else
2917     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2918                        SILCTXT_FILE_REQUEST_HOST,
2919                        client_entry->nickname, hostname, portstr);
2920 }
2921
2922 /* SILC client operations */
2923 SilcClientOperations ops = {
2924   silc_say,
2925   silc_channel_message,
2926   silc_private_message,
2927   silc_notify,
2928   silc_command,
2929   silc_command_reply,
2930   silc_get_auth_method,
2931   silc_verify_public_key,
2932   silc_ask_passphrase,
2933   silc_key_agreement,
2934   silc_ftp,
2935 };