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