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