Merge branch 'master' of git://valera-ext.nynaeve.net/silc into silc.1.1.branch
[silc.git] / apps / irssi / src / silc / core / client_ops.c
1 /*
2
3   client_ops.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2001 - 2008 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20
21 #include "module.h"
22 #include "chat-protocols.h"
23 #include "args.h"
24
25 #include "chatnets.h"
26 #include "servers-setup.h"
27 #include "channels-setup.h"
28 #include "silc-servers.h"
29 #include "silc-channels.h"
30 #include "silc-queries.h"
31 #include "silc-nicklist.h"
32 #include "silc-cmdqueue.h"
33 #include "clientutil.h"
34
35 #include "signals.h"
36 #include "levels.h"
37 #include "settings.h"
38 #include "ignore.h"
39 #include "special-vars.h"
40 #include "fe-common/core/printtext.h"
41 #include "fe-common/core/fe-channels.h"
42 #include "fe-common/core/keyboard.h"
43 #include "fe-common/core/window-items.h"
44 #include "fe-common/silc/module-formats.h"
45
46 #include "core.h"
47
48 static void
49 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
50                                 const char *name, SilcConnectionType conn_type,
51                                 SilcPublicKey public_key,
52                                 SilcVerifyPublicKey completion, void *context);
53
54 char *silc_get_session_filename(SILC_SERVER_REC *server)
55 {
56   char *file, *expanded;
57
58   expanded = parse_special_string(settings_get_str("session_filename"),
59                                 SERVER(server), NULL, "", NULL, 0);
60
61   file = silc_calloc(1, strlen(expanded) + 255);
62   snprintf(file, strlen(expanded) + 255, "%s/%s", get_irssi_dir(), expanded);
63   free(expanded);
64
65   return file;
66 }
67
68 static void silc_get_umode_string(SilcUInt32 mode, char *buf,
69                                   SilcUInt32 buf_size)
70 {
71   if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
72       (mode & SILC_UMODE_ROUTER_OPERATOR)) {
73     strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
74            "[server operator]" :
75            (mode & SILC_UMODE_ROUTER_OPERATOR) ?
76            "[SILC operator]" : "[unknown mode]");
77   }
78   if (mode & SILC_UMODE_GONE)
79     strcat(buf, " [away]");
80   if (mode & SILC_UMODE_INDISPOSED)
81     strcat(buf, " [indisposed]");
82   if (mode & SILC_UMODE_BUSY)
83     strcat(buf, " [busy]");
84   if (mode & SILC_UMODE_PAGE)
85     strcat(buf, " [page to reach]");
86   if (mode & SILC_UMODE_HYPER)
87     strcat(buf, " [hyper active]");
88   if (mode & SILC_UMODE_ROBOT)
89     strcat(buf, " [robot]");
90   if (mode & SILC_UMODE_ANONYMOUS)
91     strcat(buf, " [anonymous]");
92   if (mode & SILC_UMODE_BLOCK_PRIVMSG)
93     strcat(buf, " [blocks private messages]");
94   if (mode & SILC_UMODE_DETACHED)
95     strcat(buf, " [detached]");
96   if (mode & SILC_UMODE_REJECT_WATCHING)
97     strcat(buf, " [rejects watching]");
98   if (mode & SILC_UMODE_BLOCK_INVITE)
99     strcat(buf, " [blocks invites]");
100 }
101
102 /* converts an utf-8 string to current locale */
103 char * silc_convert_utf8_string(const char *str)
104 {
105   int message_len = (str != NULL ? strlen(str) : 0);
106   char *message = silc_calloc(message_len + 1, sizeof(*message));
107
108   g_return_val_if_fail(message != NULL, NULL);
109
110   if (str == NULL) {
111     *message = 0;
112     return message;
113   }
114
115   if (!silc_term_utf8() && silc_utf8_valid(str, message_len))
116     silc_utf8_decode(str, message_len, SILC_STRING_LOCALE,
117                      message, message_len);
118   else
119     strcpy(message, str);
120
121   return message;
122 }
123
124 /* print "nick appears as" message to every channel of a server */
125 static void
126 silc_print_nick_change_channel(SILC_SERVER_REC *server, const char *channel,
127                               const char *newnick, const char *oldnick,
128                               const char *address)
129 {
130   if (ignore_check(SERVER(server), oldnick, address,
131                    channel, newnick, MSGLEVEL_NICKS))
132     return;
133
134   printformat_module("fe-common/silc", server, channel, MSGLEVEL_NICKS,
135                      SILCTXT_CHANNEL_APPEARS,
136                      oldnick, newnick, channel, address);
137 }
138
139 static void
140 silc_print_nick_change(SILC_SERVER_REC *server, const char *newnick,
141                        const char *oldnick, const char *address)
142 {
143   GSList *tmp, *windows;
144
145   /* Print to each channel/query where the nick is.
146      Don't print more than once to the same window. */
147   windows = NULL;
148
149   for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
150     CHANNEL_REC *channel = tmp->data;
151     WINDOW_REC *window = window_item_window((WI_ITEM_REC *) channel);
152
153     if (nicklist_find(channel, newnick) == NULL ||
154         g_slist_find(windows, window) != NULL)
155       continue;
156
157     windows = g_slist_append(windows, window);
158     silc_print_nick_change_channel(server, channel->visible_name,
159                                    newnick, oldnick, address);
160   }
161
162   g_slist_free(windows);
163 }
164
165 static void silc_parse_channel_public_keys(SILC_SERVER_REC *server,
166                                            SilcChannelEntry channel_entry,
167                                            SilcDList channel_pubkeys)
168 {
169   SilcArgumentDecodedList e;
170   SilcPublicKey pubkey;
171   SilcSILCPublicKey silc_pubkey;
172   SilcUInt32 pk_len, type;
173   unsigned char *pk;
174   char *fingerprint, *babbleprint;
175   int c = 1;
176
177   printformat_module("fe-common/silc", server, NULL,
178                      MSGLEVEL_CRAP, SILCTXT_CHANNEL_PK_LIST,
179                      channel_entry->channel_name);
180
181   silc_dlist_start(channel_pubkeys);
182   while ((e = silc_dlist_get(channel_pubkeys))) {
183     pubkey = e->argument;
184     type = e->arg_type;
185
186     if (silc_pkcs_get_type(pubkey) != SILC_PKCS_SILC)
187       continue;
188
189     pk = silc_pkcs_public_key_encode(pubkey, &pk_len);
190     if (!pk)
191       continue;
192
193     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
194     babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
195     silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, pubkey);
196
197     printformat_module("fe-common/silc", server, NULL,
198                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_PK_LIST_ENTRY,
199                        c++, channel_entry->channel_name,
200                        type == 0x00 ? "Added" : "Removed",
201                        silc_pubkey->identifier.realname ?
202                        silc_pubkey->identifier.realname : "",
203                        fingerprint, babbleprint);
204
205     silc_free(fingerprint);
206     silc_free(babbleprint);
207     silc_free(pk);
208   }
209 }
210
211 void silc_say(SilcClient client, SilcClientConnection conn,
212               SilcClientMessageType type, char *msg, ...)
213 {
214   SILC_SERVER_REC *server;
215   va_list va;
216   char *str;
217
218   server = conn == NULL ? NULL : conn->context;
219
220   va_start(va, msg);
221   str = g_strdup_vprintf(msg, va);
222   printtext(server, NULL, MSGLEVEL_CRAP, "%s", str);
223   g_free(str);
224   va_end(va);
225 }
226
227 void silc_say_error(char *msg, ...)
228 {
229   va_list va;
230   char *str;
231
232   va_start(va, msg);
233   str = g_strdup_vprintf(msg, va);
234   printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str);
235
236   g_free(str);
237   va_end(va);
238 }
239
240 /* Try to verify a message using locally stored public key data */
241
242 int verify_message_signature(SilcClientEntry sender,
243                              SilcMessagePayload message)
244 {
245   SilcPublicKey pk;
246   char file[256], filename[256];
247   char *fingerprint, *fingerprint2;
248   const unsigned char *pk_data;
249   SilcUInt32 pk_datalen;
250   struct stat st;
251   int ret = SILC_MSG_SIGNED_VERIFIED, i;
252
253   /* get public key from the signature payload and compare it with the
254      one stored in the client entry */
255   pk = silc_message_signed_get_public_key(message, &pk_data, &pk_datalen);
256
257   if (pk != NULL) {
258     fingerprint = silc_hash_fingerprint(NULL, pk_data, pk_datalen);
259
260     if (sender->fingerprint[0]) {
261       fingerprint2 = silc_fingerprint(sender->fingerprint,
262                                       sizeof(sender->fingerprint));
263       if (strcmp(fingerprint, fingerprint2)) {
264         /* since the public key differs from the senders public key, the
265            verification _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   case SILC_ID_CLIENT:
1447     silc_client_unref_client(getkey->client, getkey->conn,
1448                              (SilcClientEntry)getkey->entry);
1449     break;
1450
1451   case SILC_ID_SERVER:
1452     silc_client_unref_server(getkey->client, getkey->conn,
1453                              (SilcServerEntry)getkey->entry);
1454     break;
1455   }
1456
1457   silc_free(getkey);
1458 }
1459
1460 /* Parse an invite or ban list */
1461 void silc_parse_inviteban_list(SilcClient client,
1462                                SilcClientConnection conn,
1463                                SILC_SERVER_REC *server,
1464                                SilcChannelEntry channel,
1465                                const char *list_type,
1466                                SilcArgumentPayload list)
1467 {
1468   unsigned char *tmp;
1469   SilcUInt32 type, len;
1470   SILC_CHANNEL_REC *chanrec = silc_channel_find_entry(server, channel);
1471   int counter=0, resolving = FALSE;
1472
1473   if (!silc_argument_get_arg_num(list)) {
1474     printformat_module("fe-common/silc", server,
1475                        (chanrec ? chanrec->visible_name : NULL),
1476                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_NO_INVITEBAN_LIST,
1477                        channel->channel_name, list_type);
1478     return;
1479   }
1480
1481   printformat_module("fe-common/silc", server,
1482                      (chanrec ? chanrec->visible_name : NULL),
1483                      MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_LIST,
1484                      channel->channel_name, list_type);
1485
1486   /* Parse the list */
1487   tmp = silc_argument_get_first_arg(list, &type, &len);
1488   while (tmp) {
1489     switch (type) {
1490       case 1:
1491         {
1492           /* An invite string */
1493           char **list;
1494           int i=0;
1495
1496           if (tmp[len-1] == ',')
1497             tmp[len-1] = '\0';
1498
1499           list = g_strsplit(tmp, ",", -1);
1500           while (list[i])
1501             printformat_module("fe-common/silc", server,
1502                                (chanrec ? chanrec->visible_name : NULL),
1503                                MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_STRING,
1504                                ++counter, channel->channel_name, list_type,
1505                                list[i++]);
1506           g_strfreev(list);
1507         }
1508         break;
1509
1510       case 2:
1511         {
1512           /* A public key */
1513           char *fingerprint, *babbleprint;
1514
1515           /* tmp is Public Key Payload, take public key from it. */
1516           fingerprint = silc_hash_fingerprint(NULL, tmp + 4, len - 4);
1517           babbleprint = silc_hash_babbleprint(NULL, tmp + 4, len - 4);
1518
1519           printformat_module("fe-common/silc", server,
1520                              (chanrec ? chanrec->visible_name : NULL),
1521                              MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_PUBKEY,
1522                              ++counter, channel->channel_name, list_type,
1523                              fingerprint, babbleprint);
1524         }
1525         break;
1526
1527       case 3:
1528         {
1529           /* A Client ID */
1530           SilcClientEntry client_entry;
1531           SilcID id;
1532
1533           if (!silc_id_payload_parse_id(tmp, len, &id)) {
1534             silc_say_error("Invalid data in %s list encountered", list_type);
1535             break;
1536           }
1537
1538           client_entry = silc_client_get_client_by_id(client, conn,
1539                                                       &id.u.client_id);
1540           if (client_entry) {
1541             printformat_module("fe-common/silc", server,
1542                                (chanrec ? chanrec->visible_name : NULL),
1543                                MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_STRING,
1544                                ++counter, channel->channel_name, list_type,
1545                                client_entry->nickname);
1546             silc_client_unref_client(client, conn, client_entry);
1547           } else {
1548             resolving = TRUE;
1549             silc_client_get_client_by_id_resolve(client, conn, &id.u.client_id,
1550                                                  NULL, NULL, NULL);
1551           }
1552         }
1553         break;
1554
1555       default:
1556         /* "trash" */
1557         silc_say_error("Unkown type in %s list: %u (len %u)",
1558                        list_type, type, len);
1559         break;
1560     }
1561     tmp = silc_argument_get_next_arg(list, &type, &len);
1562   }
1563
1564   if (resolving)
1565     printformat_module("fe-common/silc", server,
1566                        (chanrec ? chanrec->visible_name : NULL),
1567                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_REGET,
1568                        list_type, channel->channel_name);
1569 }
1570
1571 /* Command reply handler. This function is called always in the command reply
1572    function. If error occurs it will be called as well. Normal scenario
1573    is that it will be called after the received command data has been parsed
1574    and processed. The function is used to pass the received command data to
1575    the application.
1576
1577    `conn' is the associated client connection. `cmd_payload' is the command
1578    payload data received from server and it can be ignored. It is provided
1579    if the application would like to re-parse the received command data,
1580    however, it must be noted that the data is parsed already by the library
1581    thus the payload can be ignored. `success' is FALSE if error occured.
1582    In this case arguments are not sent to the application. `command' is the
1583    command reply being processed. The function has variable argument list
1584    and each command defines the number and type of arguments it passes to the
1585    application (on error they are not sent). */
1586
1587 void silc_command_reply(SilcClient client, SilcClientConnection conn,
1588                         SilcCommand command, SilcStatus status,
1589                         SilcStatus error, va_list vp)
1590 {
1591   SILC_SERVER_REC *server = conn->context;
1592   SILC_CHANNEL_REC *chanrec;
1593
1594   SILC_LOG_DEBUG(("Start"));
1595
1596   switch(command) {
1597   case SILC_COMMAND_WHOIS:
1598     {
1599       char buf[1024], *nickname, *username, *realname, *nick;
1600       unsigned char *fingerprint;
1601       SilcUInt32 idle, mode, *user_modes;
1602       SilcDList channels;
1603       SilcClientEntry client_entry;
1604       SilcDList attrs;
1605
1606       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1607         /* Print the unknown nick for user */
1608         char *tmp = va_arg(vp, char *);
1609         if (tmp)
1610           silc_say_error("%s: %s", tmp, silc_get_status_message(status));
1611         break;
1612       } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1613         /* Try to find the entry for the unknown client ID, since we
1614            might have, and print the nickname of it for user. */
1615         SilcClientID *id = va_arg(vp, SilcClientID *);
1616         if (id) {
1617           client_entry = silc_client_get_client_by_id(client, conn, id);
1618           if (client_entry && client_entry->nickname[0])
1619             silc_say_error("%s: %s", client_entry->nickname,
1620                            silc_get_status_message(status));
1621           silc_client_unref_client(client, conn, client_entry);
1622         }
1623         break;
1624       } else if (SILC_STATUS_IS_ERROR(status)) {
1625         silc_say_error("WHOIS: %s", silc_get_status_message(status));
1626         return;
1627       }
1628
1629       client_entry = va_arg(vp, SilcClientEntry);
1630       nickname = va_arg(vp, char *);
1631       username = va_arg(vp, char *);
1632       realname = va_arg(vp, char *);
1633       channels = va_arg(vp, SilcDList);
1634       mode = va_arg(vp, SilcUInt32);
1635       idle = va_arg(vp, SilcUInt32);
1636       fingerprint = va_arg(vp, unsigned char *);
1637       user_modes = va_arg(vp, SilcUInt32 *);
1638       attrs = va_arg(vp, SilcDList);
1639
1640       silc_client_nickname_parse(client, conn, client_entry->nickname, &nick);
1641       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1642                          SILCTXT_WHOIS_USERINFO, nickname,
1643                          client_entry->username, client_entry->hostname,
1644                          nick, client_entry->nickname);
1645       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1646                          SILCTXT_WHOIS_REALNAME, realname);
1647       silc_free(nick);
1648
1649       if (channels && user_modes) {
1650         SilcChannelPayload entry;
1651         int i = 0;
1652
1653         memset(buf, 0, sizeof(buf));
1654         silc_dlist_start(channels);
1655         while ((entry = silc_dlist_get(channels))) {
1656           SilcUInt32 name_len;
1657           char *m = silc_client_chumode_char(user_modes[i++]);
1658           char *name = silc_channel_get_name(entry, &name_len);
1659
1660           if (m)
1661             silc_strncat(buf, sizeof(buf) - 1, m, strlen(m));
1662           silc_strncat(buf, sizeof(buf) - 1, name, name_len);
1663           silc_strncat(buf, sizeof(buf) - 1, " ", 1);
1664           silc_free(m);
1665         }
1666
1667         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1668                            SILCTXT_WHOIS_CHANNELS, buf);
1669       }
1670
1671       if (mode) {
1672         memset(buf, 0, sizeof(buf));
1673         silc_get_umode_string(mode, buf, sizeof(buf - 1));
1674         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1675                            SILCTXT_WHOIS_MODES, buf);
1676       }
1677
1678       if (idle && nickname) {
1679         memset(buf, 0, sizeof(buf));
1680         snprintf(buf, sizeof(buf) - 1, "%lu %s",
1681                  idle > 60 ? (idle / 60) : idle,
1682                  idle > 60 ? "minutes" : "seconds");
1683
1684         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1685                            SILCTXT_WHOIS_IDLE, buf);
1686       }
1687
1688       if (fingerprint) {
1689         fingerprint = silc_fingerprint(fingerprint, 20);
1690         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1691                            SILCTXT_WHOIS_FINGERPRINT, fingerprint);
1692         silc_free(fingerprint);
1693       }
1694
1695       if (attrs)
1696         silc_query_attributes_print(server, silc_client, conn, attrs,
1697                                     client_entry);
1698     }
1699     break;
1700
1701   case SILC_COMMAND_WHOWAS:
1702     {
1703       char *nickname, *username, *realname;
1704
1705       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1706         char *tmp = va_arg(vp, char *);
1707         if (tmp)
1708           silc_say_error("%s: %s", tmp,
1709                          silc_get_status_message(status));
1710         break;
1711       } else if (SILC_STATUS_IS_ERROR(status)) {
1712         silc_say_error("WHOWAS: %s", silc_get_status_message(status));
1713         return;
1714       }
1715
1716       (void)va_arg(vp, SilcClientEntry);
1717       nickname = va_arg(vp, char *);
1718       username = va_arg(vp, char *);
1719       realname = va_arg(vp, char *);
1720
1721       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1722                          SILCTXT_WHOWAS_USERINFO, nickname, username,
1723                          realname ? realname : "");
1724     }
1725     break;
1726
1727   case SILC_COMMAND_INVITE:
1728     {
1729       SilcChannelEntry channel;
1730       SilcArgumentPayload invite_list;
1731
1732       if (SILC_STATUS_IS_ERROR(status))
1733         return;
1734
1735       channel = va_arg(vp, SilcChannelEntry);
1736       invite_list = va_arg(vp, SilcArgumentPayload);
1737
1738       if (invite_list)
1739         silc_parse_inviteban_list(client, conn, server, channel,
1740                                   "invite", invite_list);
1741     }
1742     break;
1743
1744   case SILC_COMMAND_JOIN:
1745     {
1746       char *channel, *mode, *topic, *cipher, *hmac;
1747       SilcUInt32 modei;
1748       SilcHashTableList *user_list;
1749       SilcChannelEntry channel_entry;
1750       SilcChannelUser chu;
1751       SilcClientEntry founder = NULL;
1752       NICK_REC *ownnick;
1753
1754       if (SILC_STATUS_IS_ERROR(status)) {
1755         if (status == SILC_STATUS_ERR_NO_SUCH_SERVER) {
1756           char *tmp = va_arg(vp, char *);
1757           if (tmp)
1758             silc_say_error("JOIN: %s: %s", tmp,
1759                            silc_get_status_message(status));
1760           return;
1761         }
1762         if (status == SILC_STATUS_ERR_NO_SUCH_CHANNEL) {
1763           char *tmp = va_arg(vp, char *);
1764           if (tmp)
1765             silc_say_error("JOIN: %s: %s", tmp,
1766                            silc_get_status_message(status));
1767           return;
1768         }
1769         silc_say_error("JOIN: %s", silc_get_status_message(status));
1770         return;
1771       }
1772
1773       channel = va_arg(vp, char *);
1774       channel_entry = va_arg(vp, SilcChannelEntry);
1775       modei = va_arg(vp, SilcUInt32);
1776       user_list = va_arg(vp, SilcHashTableList *);
1777       topic = va_arg(vp, char *);
1778       cipher = va_arg(vp, char *);
1779       hmac = va_arg(vp, char *);
1780
1781       chanrec = silc_channel_find(server, channel);
1782       if (!chanrec)
1783         chanrec = silc_channel_create(server, channel, channel, TRUE);
1784
1785       if (topic) {
1786         char tmp[256], *cp, *dm = NULL;
1787         g_free_not_null(chanrec->topic);
1788
1789         if (!silc_term_utf8() && silc_utf8_valid(topic, strlen(topic))) {
1790           memset(tmp, 0, sizeof(tmp));
1791           cp = tmp;
1792           if (strlen(topic) > sizeof(tmp) - 1) {
1793             dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
1794             cp = dm;
1795           }
1796
1797           silc_utf8_decode(topic, strlen(topic), SILC_STRING_LOCALE,
1798                            cp, strlen(topic));
1799           topic = cp;
1800         }
1801
1802         chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1803         signal_emit("channel topic changed", 1, chanrec);
1804
1805         silc_free(dm);
1806       }
1807
1808       mode = silc_client_chmode(modei, cipher ? cipher : "", hmac ? hmac : "");
1809       g_free_not_null(chanrec->mode);
1810       chanrec->mode = g_strdup(mode == NULL ? "" : mode);
1811       signal_emit("channel mode changed", 1, chanrec);
1812
1813       /* Get user list */
1814       while (silc_hash_table_get(user_list, NULL, (void *)&chu)) {
1815         if (!chu->client->nickname[0])
1816           continue;
1817         if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
1818           founder = chu->client;
1819         silc_nicklist_insert(chanrec, chu, FALSE);
1820       }
1821
1822       ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
1823       if (!ownnick)
1824         break;
1825       nicklist_set_own(CHANNEL(chanrec), ownnick);
1826       chanrec->entry = channel_entry;
1827       signal_emit("channel joined", 1, chanrec);
1828
1829       if (chanrec->topic)
1830         printformat_module("fe-common/silc", server,
1831                            channel_entry->channel_name,
1832                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
1833                            channel_entry->channel_name, chanrec->topic);
1834
1835       if (founder) {
1836         if (founder == conn->local_entry) {
1837           printformat_module("fe-common/silc",
1838                              server, channel_entry->channel_name,
1839                              MSGLEVEL_CRAP, SILCTXT_CHANNEL_FOUNDER_YOU,
1840                              channel_entry->channel_name);
1841           signal_emit("nick mode changed", 2, chanrec, ownnick);
1842         } else
1843           printformat_module("fe-common/silc",
1844                              server, channel_entry->channel_name,
1845                              MSGLEVEL_CRAP, SILCTXT_CHANNEL_FOUNDER,
1846                              channel_entry->channel_name, founder->nickname);
1847       }
1848
1849       break;
1850     }
1851
1852   case SILC_COMMAND_NICK:
1853     {
1854       char *old;
1855       SilcClientEntry client_entry = va_arg(vp, SilcClientEntry);
1856       GSList *nicks;
1857
1858       if (SILC_STATUS_IS_ERROR(status)) {
1859         silc_say_error("NICK: %s", silc_get_status_message(status));
1860         return;
1861       }
1862
1863       nicks = nicklist_get_same(SERVER(server), client_entry->nickname);
1864       if ((nicks != NULL) &&
1865           (strcmp(SERVER(server)->nick, client_entry->nickname))) {
1866         char buf[512];
1867         SilcClientEntry collider, old;
1868
1869         old = ((SILC_NICK_REC *)(nicks->next->data))->silc_user->client;
1870         collider = silc_client_get_client_by_id(client, conn, &old->id);
1871         if (collider != client_entry) {
1872           memset(buf, 0, sizeof(buf));
1873           snprintf(buf, sizeof(buf) - 1, "%s@%s",
1874                    collider->username, collider->hostname);
1875           nicklist_rename_unique(SERVER(server),
1876                                  old, old->nickname,
1877                                  collider, collider->nickname);
1878           silc_print_nick_change(server, collider->nickname,
1879                                  client_entry->nickname, buf);
1880         }
1881         silc_client_unref_client(client, conn, collider);
1882       }
1883
1884       if (nicks != NULL)
1885         g_slist_free(nicks);
1886
1887       old = g_strdup(server->nick);
1888       server_change_nick(SERVER(server), client_entry->nickname);
1889       nicklist_rename_unique(SERVER(server),
1890                              server->conn->local_entry, server->nick,
1891                              client_entry, client_entry->nickname);
1892       signal_emit("message own_nick", 4, server, server->nick, old, "");
1893       g_free(old);
1894
1895       /* when connecting to a server, the last thing we receive
1896          is a SILC_COMMAND_LIST reply. Since we enable queueing
1897          during the connection, we can now safely disable it again */
1898       silc_queue_disable(conn);
1899       break;
1900     }
1901
1902   case SILC_COMMAND_LIST:
1903     {
1904       char *topic, *name;
1905       int usercount;
1906       char users[20];
1907       char tmp[256], *cp, *dm = NULL;
1908
1909       if (SILC_STATUS_IS_ERROR(status))
1910         return;
1911
1912       (void)va_arg(vp, SilcChannelEntry);
1913       name = va_arg(vp, char *);
1914       topic = va_arg(vp, char *);
1915       usercount = va_arg(vp, int);
1916
1917       if (topic && !silc_term_utf8() &&
1918           silc_utf8_valid(topic, strlen(topic))) {
1919         memset(tmp, 0, sizeof(tmp));
1920         cp = tmp;
1921         if (strlen(topic) > sizeof(tmp) - 1) {
1922           dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
1923           cp = dm;
1924         }
1925
1926         silc_utf8_decode(topic, strlen(topic), SILC_STRING_LOCALE,
1927                          cp, strlen(topic));
1928         topic = cp;
1929       }
1930
1931       if (status == SILC_STATUS_LIST_START ||
1932           status == SILC_STATUS_OK)
1933         printformat_module("fe-common/silc", server, NULL,
1934                            MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
1935
1936       if (!usercount)
1937         snprintf(users, sizeof(users) - 1, "N/A");
1938       else
1939         snprintf(users, sizeof(users) - 1, "%d", usercount);
1940       printformat_module("fe-common/silc", server, NULL,
1941                          MSGLEVEL_CRAP, SILCTXT_LIST,
1942                          name, users, topic ? topic : "");
1943       silc_free(dm);
1944     }
1945     break;
1946
1947   case SILC_COMMAND_UMODE:
1948     {
1949       SilcUInt32 mode;
1950       char *reason;
1951
1952       if (SILC_STATUS_IS_ERROR(status))
1953         return;
1954
1955       mode = va_arg(vp, SilcUInt32);
1956
1957       if (mode & SILC_UMODE_SERVER_OPERATOR &&
1958           !(server->umode & SILC_UMODE_SERVER_OPERATOR))
1959         printformat_module("fe-common/silc", server, NULL,
1960                            MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1961
1962       if (mode & SILC_UMODE_ROUTER_OPERATOR &&
1963           !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
1964         printformat_module("fe-common/silc", server, NULL,
1965                            MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1966
1967       if ((mode & SILC_UMODE_GONE) != (server->umode & SILC_UMODE_GONE)) {
1968         if (mode & SILC_UMODE_GONE) {
1969           if ((server->away_reason != NULL) && (server->away_reason[0] != '\0'))
1970             reason = g_strdup(server->away_reason);
1971           else
1972             reason = g_strdup("away");
1973         } else
1974           reason = g_strdup("");
1975
1976         silc_set_away(reason, server);
1977
1978         g_free(reason);
1979       }
1980
1981       server->umode = mode;
1982       signal_emit("user mode changed", 2, server, NULL);
1983     }
1984     break;
1985
1986   case SILC_COMMAND_OPER:
1987     if (SILC_STATUS_IS_ERROR(status)) {
1988       silc_say_error("OPER: %s", silc_get_status_message(status));
1989       return;
1990     }
1991
1992     server->umode |= SILC_UMODE_SERVER_OPERATOR;
1993     signal_emit("user mode changed", 2, server, NULL);
1994
1995     printformat_module("fe-common/silc", server, NULL,
1996                        MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1997     break;
1998
1999   case SILC_COMMAND_SILCOPER:
2000     if (SILC_STATUS_IS_ERROR(status)) {
2001       silc_say_error("SILCOPER: %s", silc_get_status_message(status));
2002       return;
2003     }
2004
2005     server->umode |= SILC_UMODE_ROUTER_OPERATOR;
2006     signal_emit("user mode changed", 2, server, NULL);
2007
2008     printformat_module("fe-common/silc", server, NULL,
2009                        MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
2010     break;
2011
2012   case SILC_COMMAND_USERS:
2013     {
2014       SilcHashTableList htl;
2015       SilcChannelEntry channel;
2016       SilcChannelUser chu;
2017
2018       if (SILC_STATUS_IS_ERROR(status)) {
2019         silc_say_error("USERS: %s", silc_get_status_message(status));
2020         return;
2021       }
2022
2023       channel = va_arg(vp, SilcChannelEntry);
2024
2025       printformat_module("fe-common/silc", server, channel->channel_name,
2026                          MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
2027                          channel->channel_name);
2028
2029       silc_hash_table_list(channel->user_list, &htl);
2030       while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
2031         SilcClientEntry e = chu->client;
2032         char stat[5], *mode;
2033
2034         if (!e->nickname[0])
2035           continue;
2036
2037         memset(stat, 0, sizeof(stat));
2038         mode = silc_client_chumode_char(chu->mode);
2039         if (e->mode & SILC_UMODE_GONE)
2040           strcat(stat, "G");
2041         else if (e->mode & SILC_UMODE_INDISPOSED)
2042           strcat(stat, "I");
2043         else if (e->mode & SILC_UMODE_BUSY)
2044           strcat(stat, "B");
2045         else if (e->mode & SILC_UMODE_PAGE)
2046           strcat(stat, "P");
2047         else if (e->mode & SILC_UMODE_HYPER)
2048           strcat(stat, "H");
2049         else if (e->mode & SILC_UMODE_ROBOT)
2050           strcat(stat, "R");
2051         else if (e->mode & SILC_UMODE_ANONYMOUS)
2052           strcat(stat, "?");
2053         else
2054           strcat(stat, "A");
2055         if (mode)
2056           strcat(stat, mode);
2057
2058         printformat_module("fe-common/silc", server, channel->channel_name,
2059                            MSGLEVEL_CRAP, SILCTXT_USERS,
2060                            e->nickname, stat,
2061                            e->username[0] ? e->username : "",
2062                            e->hostname[0] ? e->hostname : "",
2063                            e->realname ? e->realname : "");
2064         if (mode)
2065           silc_free(mode);
2066       }
2067       silc_hash_table_list_reset(&htl);
2068     }
2069     break;
2070
2071   case SILC_COMMAND_BAN:
2072     {
2073       SilcChannelEntry channel;
2074       SilcArgumentPayload invite_list;
2075
2076       if (SILC_STATUS_IS_ERROR(status))
2077         return;
2078
2079       channel = va_arg(vp, SilcChannelEntry);
2080       invite_list = va_arg(vp, SilcArgumentPayload);
2081
2082       if (invite_list)
2083         silc_parse_inviteban_list(client, conn, server, channel,
2084                                   "ban", invite_list);
2085     }
2086     break;
2087
2088   case SILC_COMMAND_GETKEY:
2089     {
2090       SilcIdType id_type;
2091       void *entry;
2092       SilcPublicKey public_key;
2093       GetkeyContext getkey;
2094       char *name;
2095
2096       if (SILC_STATUS_IS_ERROR(status)) {
2097         silc_say_error("GETKEY: %s", silc_get_status_message(status));
2098         return;
2099       }
2100
2101       id_type = va_arg(vp, SilcUInt32);
2102       entry = va_arg(vp, void *);
2103       public_key = va_arg(vp, SilcPublicKey);
2104
2105       if (public_key) {
2106         getkey = silc_calloc(1, sizeof(*getkey));
2107         getkey->entry = entry;
2108         getkey->id_type = id_type;
2109         getkey->client = client;
2110         getkey->conn = conn;
2111
2112         name = (id_type == SILC_ID_CLIENT ?
2113                 ((SilcClientEntry)entry)->nickname :
2114                 ((SilcServerEntry)entry)->server_name);
2115
2116         switch (id_type) {
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   SilcBool 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   if (reason != KeyboardCompletionFailed) {
2430     /*
2431      * Null out the completion context in the server record as this operation
2432      * is done as far as we are concerned.  The underlying keyboard library
2433      * routine will take care of freeing the async context memory when the
2434      * actual callback is called by irssi in the abort case.  In the success
2435      * case, it will free the async context memory after we return from this
2436      * routine.
2437      */
2438     SILC_SERVER_REC *server = (SILC_SERVER_REC*)(verify->conn->context);
2439     server->prompt_op = NULL;
2440   }
2441
2442   silc_free(verify->filename);
2443   silc_free(verify->entity);
2444   silc_free(verify->entity_name);
2445   silc_free(verify);
2446 }
2447
2448 /* Internal routine to verify public key. If the `completion' is provided
2449    it will be called to indicate whether public was verified or not. For
2450    server/router public key this will check for filename that includes the
2451    remote host's IP address and remote host's hostname. */
2452
2453 static void
2454 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
2455                                 const char *name,
2456                                 SilcConnectionType conn_type,
2457                                 SilcPublicKey public_key,
2458                                 SilcVerifyPublicKey completion, void *context)
2459 {
2460   PublicKeyVerify verify;
2461   char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
2462   char *fingerprint, *babbleprint, *format;
2463   SilcPublicKey local_pubkey;
2464   SilcSILCPublicKey silc_pubkey;
2465   SilcUInt16 port;
2466   SILC_SERVER_REC *server;
2467   const char *hostname, *ip;
2468   unsigned char *pk;
2469   SilcUInt32 pk_len;
2470   struct passwd *pw;
2471   struct stat st;
2472   char *entity = ((conn_type == SILC_CONN_SERVER ||
2473                    conn_type == SILC_CONN_ROUTER) ?
2474                   "server" : "client");
2475   int i;
2476
2477   server = (SILC_SERVER_REC*)conn->context;
2478   SILC_VERIFY(server);
2479   if (!server) {
2480     if (completion)
2481       completion(FALSE, context);
2482     return;
2483   }
2484
2485   if (silc_pkcs_get_type(public_key) != SILC_PKCS_SILC) {
2486     printformat_module("fe-common/silc", NULL, NULL,
2487                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
2488                        entity, silc_pkcs_get_type(public_key));
2489     if (completion)
2490       completion(FALSE, context);
2491     return;
2492   }
2493
2494   /* Encode public key */
2495   pk = silc_pkcs_public_key_encode(public_key, &pk_len);
2496   if (!pk) {
2497     if (completion)
2498       completion(FALSE, context);
2499     return;
2500   }
2501
2502   silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
2503
2504   pw = getpwuid(getuid());
2505   if (!pw) {
2506     if (completion)
2507       completion(FALSE, context);
2508     silc_free(pk);
2509     return;
2510   }
2511
2512   memset(filename, 0, sizeof(filename));
2513   memset(filename2, 0, sizeof(filename2));
2514   memset(file, 0, sizeof(file));
2515
2516   /* Get remote host information */
2517   silc_socket_stream_get_info(silc_packet_stream_get_stream(conn->stream),
2518                               NULL, &hostname, &ip, &port);
2519
2520   if (conn_type == SILC_CONN_SERVER ||
2521       conn_type == SILC_CONN_ROUTER) {
2522     if (!name) {
2523       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, ip, port);
2524       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2525                get_irssi_dir(), entity, file);
2526
2527       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2528                hostname, port);
2529       snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s",
2530                get_irssi_dir(), entity, file);
2531
2532       ipf = filename;
2533       hostf = filename2;
2534     } else {
2535       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2536                name, port);
2537       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2538                get_irssi_dir(), entity, file);
2539
2540       ipf = filename;
2541     }
2542   } else {
2543     /* Replace all whitespaces with `_'. */
2544     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2545     for (i = 0; i < strlen(fingerprint); i++)
2546       if (fingerprint[i] == ' ')
2547         fingerprint[i] = '_';
2548
2549     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
2550     snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2551              get_irssi_dir(), entity, file);
2552     silc_free(fingerprint);
2553
2554     ipf = filename;
2555   }
2556
2557   /* Take fingerprint of the public key */
2558   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2559   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
2560
2561   verify = silc_calloc(1, sizeof(*verify));
2562   verify->client = client;
2563   verify->conn = conn;
2564   verify->filename = strdup(ipf);
2565   verify->entity = strdup(entity);
2566   verify->entity_name = (conn_type != SILC_CONN_CLIENT ?
2567                          (name ? strdup(name) : strdup(hostname))
2568                          : NULL);
2569   verify->public_key = public_key;
2570   verify->completion = completion;
2571   verify->context = context;
2572
2573   /* Check whether this key already exists */
2574   if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
2575     /* Key does not exist, ask user to verify the key and save it */
2576
2577     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2578                        SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2579                        verify->entity_name : entity);
2580     if (conn_type == SILC_CONN_CLIENT && name &&
2581         silc_pubkey->identifier.realname)
2582       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2583                          SILCTXT_PUBKEY_RECEIVED_CLIENT, name,
2584                          silc_pubkey->identifier.realname,
2585                          silc_pubkey->identifier.email ?
2586                          silc_pubkey->identifier.email : "");
2587     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2588                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2589     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2590                        SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2591     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2592                              SILCTXT_PUBKEY_ACCEPT);
2593     silc_keyboard_entry_redirect(verify_public_key_completion,
2594                             format, 0, verify, &server->prompt_op);
2595     g_free(format);
2596     silc_free(fingerprint);
2597     silc_free(babbleprint);
2598     silc_free(pk);
2599     return;
2600   } else {
2601     /* The key already exists, verify it. */
2602     unsigned char *encpk;
2603     SilcUInt32 encpk_len;
2604
2605     /* Load the key file, try for both IP filename and hostname filename */
2606     if (!silc_pkcs_load_public_key(ipf, &local_pubkey) &&
2607         (!hostf || (!silc_pkcs_load_public_key(hostf, &local_pubkey)))) {
2608       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2609                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2610                          verify->entity_name : entity);
2611       if (conn_type == SILC_CONN_CLIENT && name &&
2612           silc_pubkey->identifier.realname)
2613         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2614                            SILCTXT_PUBKEY_RECEIVED_CLIENT, name,
2615                            silc_pubkey->identifier.realname,
2616                            silc_pubkey->identifier.email ?
2617                            silc_pubkey->identifier.email : "");
2618       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2619                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2620       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2621                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2622       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2623                          SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
2624       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2625                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2626       silc_keyboard_entry_redirect(verify_public_key_completion,
2627                               format, 0, verify, &server->prompt_op);
2628       g_free(format);
2629       silc_free(fingerprint);
2630       silc_free(babbleprint);
2631       silc_free(pk);
2632       return;
2633     }
2634
2635     /* Encode the key data */
2636     encpk = silc_pkcs_public_key_encode(local_pubkey, &encpk_len);
2637     if (!encpk) {
2638       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2639                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2640                          verify->entity_name : entity);
2641       if (conn_type == SILC_CONN_CLIENT && name &&
2642           silc_pubkey->identifier.realname)
2643         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2644                            SILCTXT_PUBKEY_RECEIVED_CLIENT, name,
2645                            silc_pubkey->identifier.realname,
2646                            silc_pubkey->identifier.email ?
2647                            silc_pubkey->identifier.email : "");
2648       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2649                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2650       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2651                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2652       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2653                          SILCTXT_PUBKEY_MALFORMED, entity);
2654       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2655                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2656       silc_keyboard_entry_redirect(verify_public_key_completion,
2657                               format, 0, verify, &server->prompt_op);
2658       g_free(format);
2659       silc_free(fingerprint);
2660       silc_free(babbleprint);
2661       silc_free(pk);
2662       return;
2663     }
2664     silc_pkcs_public_key_free(local_pubkey);
2665
2666     /* Compare the keys */
2667     if (memcmp(encpk, pk, encpk_len)) {
2668       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2669                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2670                          verify->entity_name : entity);
2671       if (conn_type == SILC_CONN_CLIENT && name &&
2672           silc_pubkey->identifier.realname)
2673         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2674                            SILCTXT_PUBKEY_RECEIVED_CLIENT, name,
2675                            silc_pubkey->identifier.realname,
2676                            silc_pubkey->identifier.email ?
2677                            silc_pubkey->identifier.email : "");
2678       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2679                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2680       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2681                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2682       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2683                          SILCTXT_PUBKEY_NO_MATCH, entity);
2684       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2685                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
2686       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2687                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
2688
2689       /* Ask user to verify the key and save it */
2690       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2691                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2692       silc_keyboard_entry_redirect(verify_public_key_completion,
2693                               format, 0, verify, &server->prompt_op);
2694       g_free(format);
2695       silc_free(fingerprint);
2696       silc_free(babbleprint);
2697       silc_free(encpk);
2698       silc_free(pk);
2699       return;
2700     }
2701
2702     /* Local copy matched */
2703     if (completion)
2704       completion(TRUE, context);
2705     silc_free(encpk);
2706     silc_free(fingerprint);
2707     silc_free(babbleprint);
2708     silc_free(verify->filename);
2709     silc_free(verify->entity);
2710     silc_free(verify->entity_name);
2711     silc_free(verify);
2712     silc_free(pk);
2713   }
2714 }
2715
2716 /* Verifies received public key. The `conn_type' indicates which entity
2717    (server, client etc.) has sent the public key. If user decides to trust
2718    the key may be saved as trusted public key for later use. The
2719    `completion' must be called after the public key has been verified. */
2720
2721 void
2722 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
2723                        SilcConnectionType conn_type,
2724                        SilcPublicKey public_key,
2725                        SilcVerifyPublicKey completion, void *context)
2726 {
2727   silc_verify_public_key_internal(client, conn, NULL, conn_type, public_key,
2728                                   completion, context);
2729 }
2730
2731 /* Asks passphrase from user on the input line. */
2732
2733 typedef struct {
2734   SilcAskPassphrase completion;
2735   SilcClientConnection conn;
2736   void *context;
2737 } *AskPassphrase;
2738
2739 void ask_passphrase_completion(const char *passphrase, void *context,
2740                                SilcKeyboardPromptStatus reason)
2741 {
2742   AskPassphrase p = (AskPassphrase)context;
2743   if (passphrase && passphrase[0] == '\0')
2744     passphrase = NULL;
2745   p->completion((unsigned char *)passphrase,
2746                 passphrase ? strlen(passphrase) : 0, p->context);
2747
2748   if (reason != KeyboardCompletionFailed) {
2749     SILC_SERVER_REC *server = (SILC_SERVER_REC *)(p->conn->context);
2750     server->prompt_op = NULL;
2751   }
2752
2753   silc_free(p);
2754 }
2755
2756 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
2757                          SilcAskPassphrase completion, void *context)
2758 {
2759   SILC_SERVER_REC *server = (SILC_SERVER_REC*)(conn->context);
2760   AskPassphrase p;
2761
2762   p = silc_calloc(1, sizeof(*p));
2763   if (!p) {
2764     if (completion)
2765       completion(NULL, 0, context);
2766     return;
2767   }
2768
2769   p->completion = completion;
2770   p->conn       = conn;
2771   p->context    = context;
2772
2773   silc_keyboard_entry_redirect(ask_passphrase_completion,
2774                                "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN,
2775                                p, &server->prompt_op);
2776 }
2777
2778 typedef struct {
2779   SilcGetAuthMeth completion;
2780   void *context;
2781 } *GetAuthMethod;
2782
2783 static void silc_get_auth_ask_passphrase(const unsigned char *passphrase,
2784                                          SilcUInt32 passphrase_len,
2785                                          void *context)
2786 {
2787   GetAuthMethod a = context;
2788   a->completion(passphrase ? SILC_AUTH_PASSWORD : SILC_AUTH_NONE,
2789                 passphrase, passphrase_len, a->context);
2790   silc_free(a);
2791 }
2792
2793 /* Find authentication data by hostname and port. The hostname may be IP
2794    address as well.*/
2795
2796 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
2797                           char *hostname, SilcUInt16 port,
2798                           SilcAuthMethod auth_meth,
2799                           SilcGetAuthMeth completion, void *context)
2800 {
2801   SERVER_SETUP_REC *setup;
2802
2803   SILC_LOG_DEBUG(("Start"));
2804
2805   if (auth_meth == SILC_AUTH_PUBLIC_KEY) {
2806     /* Returning NULL will cause library to use our private key configured
2807        for this connection */
2808     completion(SILC_AUTH_PUBLIC_KEY, NULL, 0, context);
2809     return;
2810   }
2811
2812   /* Check whether we find the password for this server in our
2813      configuration.  If it's set, always send it server. */
2814   setup = server_setup_find_port(hostname, port);
2815   if (setup && setup->password) {
2816     completion(SILC_AUTH_PASSWORD, setup->password, strlen(setup->password),
2817                context);
2818     return;
2819   }
2820
2821   /* Didn't find password.  If server wants it, ask it from user. */
2822   if (auth_meth == SILC_AUTH_PASSWORD) {
2823     GetAuthMethod a;
2824     a = silc_calloc(1, sizeof(*a));
2825     if (a) {
2826       a->completion = completion;
2827       a->context = context;
2828       silc_ask_passphrase(client, conn, silc_get_auth_ask_passphrase, a);
2829       return;
2830     }
2831   }
2832
2833   /* No authentication */
2834   completion(SILC_AUTH_NONE, NULL, 0, context);
2835 }
2836
2837 /* Asks whether the user would like to perform the key agreement protocol.
2838    This is called after we have received an key agreement packet or an
2839    reply to our key agreement packet. This returns TRUE if the user wants
2840    the library to perform the key agreement protocol and FALSE if it is not
2841    desired (application may start it later by calling the function
2842    silc_client_perform_key_agreement). */
2843
2844 void silc_key_agreement(SilcClient client, SilcClientConnection conn,
2845                         SilcClientEntry client_entry, const char *hostname,
2846                         SilcUInt16 protocol, SilcUInt16 port)
2847 {
2848   char portstr[12], protostr[5];
2849
2850   SILC_LOG_DEBUG(("Start"));
2851
2852   /* We will just display the info on the screen and return FALSE and user
2853      will have to start the key agreement with a command. */
2854
2855   if (hostname) {
2856     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2857     snprintf(protostr, sizeof(protostr) - 1, "%s", protocol == 1 ? "UDP" :
2858              "TCP");
2859   }
2860
2861   if (!hostname)
2862     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2863                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
2864   else
2865     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2866                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
2867                        client_entry->nickname, hostname, portstr, protostr);
2868 }
2869
2870 /* Notifies application that file transfer protocol session is being
2871    requested by the remote client indicated by the `client_entry' from
2872    the `hostname' and `port'. The `session_id' is the file transfer
2873    session and it can be used to either accept or reject the file
2874    transfer request, by calling the silc_client_file_receive or
2875    silc_client_file_close, respectively. */
2876
2877 void silc_ftp(SilcClient client, SilcClientConnection conn,
2878               SilcClientEntry client_entry, SilcUInt32 session_id,
2879               const char *hostname, SilcUInt16 port)
2880 {
2881   SILC_SERVER_REC *server;
2882   char portstr[12];
2883   FtpSession ftp = NULL;
2884
2885   SILC_LOG_DEBUG(("Start"));
2886
2887   server = conn->context;
2888
2889   silc_dlist_start(server->ftp_sessions);
2890   while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
2891     if (ftp->client_entry == client_entry &&
2892         ftp->session_id == session_id) {
2893       server->current_session = ftp;
2894       break;
2895     }
2896   }
2897   if (ftp == SILC_LIST_END) {
2898     ftp = silc_calloc(1, sizeof(*ftp));
2899     ftp->client_entry = client_entry;
2900     ftp->session_id = session_id;
2901     ftp->send = FALSE;
2902     ftp->conn = conn;
2903     silc_dlist_add(server->ftp_sessions, ftp);
2904     server->current_session = ftp;
2905   }
2906
2907   if (hostname)
2908     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2909
2910   if (!hostname)
2911     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2912                        SILCTXT_FILE_REQUEST, client_entry->nickname);
2913   else
2914     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2915                        SILCTXT_FILE_REQUEST_HOST,
2916                        client_entry->nickname, hostname, portstr);
2917 }
2918
2919 /* SILC client operations */
2920 SilcClientOperations ops = {
2921   silc_say,
2922   silc_channel_message,
2923   silc_private_message,
2924   silc_notify,
2925   silc_command,
2926   silc_command_reply,
2927   silc_get_auth_method,
2928   silc_verify_public_key,
2929   silc_ask_passphrase,
2930   silc_key_agreement,
2931   silc_ftp,
2932 };