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