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