Merged silc_1_1_branch to trunk.
[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   silc_free(getkey);
1442 }
1443
1444 /* Parse an invite or ban list */
1445 void silc_parse_inviteban_list(SilcClient client,
1446                                SilcClientConnection conn,
1447                                SILC_SERVER_REC *server,
1448                                SilcChannelEntry channel,
1449                                const char *list_type,
1450                                SilcArgumentPayload list)
1451 {
1452   unsigned char *tmp;
1453   SilcUInt32 type, len;
1454   SILC_CHANNEL_REC *chanrec = silc_channel_find_entry(server, channel);
1455   int counter=0, resolving = FALSE;
1456
1457   if (!silc_argument_get_arg_num(list)) {
1458     printformat_module("fe-common/silc", server,
1459                        (chanrec ? chanrec->visible_name : NULL),
1460                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_NO_INVITEBAN_LIST,
1461                        channel->channel_name, list_type);
1462     return;
1463   }
1464
1465   printformat_module("fe-common/silc", server,
1466                      (chanrec ? chanrec->visible_name : NULL),
1467                      MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_LIST,
1468                      channel->channel_name, list_type);
1469
1470   /* Parse the list */
1471   tmp = silc_argument_get_first_arg(list, &type, &len);
1472   while (tmp) {
1473     switch (type) {
1474       case 1:
1475         {
1476           /* An invite string */
1477           char **list;
1478           int i=0;
1479
1480           if (tmp[len-1] == ',')
1481             tmp[len-1] = '\0';
1482
1483           list = g_strsplit(tmp, ",", -1);
1484           while (list[i])
1485             printformat_module("fe-common/silc", server,
1486                                (chanrec ? chanrec->visible_name : NULL),
1487                                MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_STRING,
1488                                ++counter, channel->channel_name, list_type,
1489                                list[i++]);
1490           g_strfreev(list);
1491         }
1492         break;
1493
1494       case 2:
1495         {
1496           /* A public key */
1497           char *fingerprint, *babbleprint;
1498
1499           /* tmp is Public Key Payload, take public key from it. */
1500           fingerprint = silc_hash_fingerprint(NULL, tmp + 4, len - 4);
1501           babbleprint = silc_hash_babbleprint(NULL, tmp + 4, len - 4);
1502
1503           printformat_module("fe-common/silc", server,
1504                              (chanrec ? chanrec->visible_name : NULL),
1505                              MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_PUBKEY,
1506                              ++counter, channel->channel_name, list_type,
1507                              fingerprint, babbleprint);
1508         }
1509         break;
1510
1511       case 3:
1512         {
1513           /* A Client ID */
1514           SilcClientEntry client_entry;
1515           SilcID id;
1516
1517           if (!silc_id_payload_parse_id(tmp, len, &id)) {
1518             silc_say_error("Invalid data in %s list encountered", list_type);
1519             break;
1520           }
1521
1522           client_entry = silc_client_get_client_by_id(client, conn,
1523                                                       &id.u.client_id);
1524           if (client_entry) {
1525             printformat_module("fe-common/silc", server,
1526                                (chanrec ? chanrec->visible_name : NULL),
1527                                MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_STRING,
1528                                ++counter, channel->channel_name, list_type,
1529                                client_entry->nickname);
1530             silc_client_unref_client(client, conn, client_entry);
1531           } else {
1532             resolving = TRUE;
1533             silc_client_get_client_by_id_resolve(client, conn, &id.u.client_id,
1534                                                  NULL, NULL, NULL);
1535           }
1536         }
1537         break;
1538
1539       default:
1540         /* "trash" */
1541         silc_say_error("Unkown type in %s list: %u (len %u)",
1542                        list_type, type, len);
1543         break;
1544     }
1545     tmp = silc_argument_get_next_arg(list, &type, &len);
1546   }
1547
1548   if (resolving)
1549     printformat_module("fe-common/silc", server,
1550                        (chanrec ? chanrec->visible_name : NULL),
1551                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_REGET,
1552                        list_type, channel->channel_name);
1553 }
1554
1555 /* Command reply handler. This function is called always in the command reply
1556    function. If error occurs it will be called as well. Normal scenario
1557    is that it will be called after the received command data has been parsed
1558    and processed. The function is used to pass the received command data to
1559    the application.
1560
1561    `conn' is the associated client connection. `cmd_payload' is the command
1562    payload data received from server and it can be ignored. It is provided
1563    if the application would like to re-parse the received command data,
1564    however, it must be noted that the data is parsed already by the library
1565    thus the payload can be ignored. `success' is FALSE if error occured.
1566    In this case arguments are not sent to the application. `command' is the
1567    command reply being processed. The function has variable argument list
1568    and each command defines the number and type of arguments it passes to the
1569    application (on error they are not sent). */
1570
1571 void silc_command_reply(SilcClient client, SilcClientConnection conn,
1572                         SilcCommand command, SilcStatus status,
1573                         SilcStatus error, va_list vp)
1574 {
1575   SILC_SERVER_REC *server = conn->context;
1576   SILC_CHANNEL_REC *chanrec;
1577
1578   SILC_LOG_DEBUG(("Start"));
1579
1580   switch(command) {
1581   case SILC_COMMAND_WHOIS:
1582     {
1583       char buf[1024], *nickname, *username, *realname, *nick;
1584       unsigned char *fingerprint;
1585       SilcUInt32 idle, mode, *user_modes;
1586       SilcDList channels;
1587       SilcClientEntry client_entry;
1588       SilcDList attrs;
1589
1590       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1591         /* Print the unknown nick for user */
1592         char *tmp = va_arg(vp, char *);
1593         if (tmp)
1594           silc_say_error("%s: %s", tmp, silc_get_status_message(status));
1595         break;
1596       } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1597         /* Try to find the entry for the unknown client ID, since we
1598            might have, and print the nickname of it for user. */
1599         SilcClientID *id = va_arg(vp, SilcClientID *);
1600         if (id) {
1601           client_entry = silc_client_get_client_by_id(client, conn, id);
1602           if (client_entry && client_entry->nickname[0])
1603             silc_say_error("%s: %s", client_entry->nickname,
1604                            silc_get_status_message(status));
1605           silc_client_unref_client(client, conn, client_entry);
1606         }
1607         break;
1608       } else if (SILC_STATUS_IS_ERROR(status)) {
1609         silc_say_error("WHOIS: %s", silc_get_status_message(status));
1610         return;
1611       }
1612
1613       client_entry = va_arg(vp, SilcClientEntry);
1614       nickname = va_arg(vp, char *);
1615       username = va_arg(vp, char *);
1616       realname = va_arg(vp, char *);
1617       channels = va_arg(vp, SilcDList);
1618       mode = va_arg(vp, SilcUInt32);
1619       idle = va_arg(vp, SilcUInt32);
1620       fingerprint = va_arg(vp, unsigned char *);
1621       user_modes = va_arg(vp, SilcUInt32 *);
1622       attrs = va_arg(vp, SilcDList);
1623
1624       silc_client_nickname_parse(client, conn, client_entry->nickname, &nick);
1625       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1626                          SILCTXT_WHOIS_USERINFO, nickname,
1627                          client_entry->username, client_entry->hostname,
1628                          nick, client_entry->nickname);
1629       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1630                          SILCTXT_WHOIS_REALNAME, realname);
1631       silc_free(nick);
1632
1633       if (channels && user_modes) {
1634         SilcChannelPayload entry;
1635         int i = 0;
1636
1637         memset(buf, 0, sizeof(buf));
1638         silc_dlist_start(channels);
1639         while ((entry = silc_dlist_get(channels))) {
1640           SilcUInt32 name_len;
1641           char *m = silc_client_chumode_char(user_modes[i++]);
1642           char *name = silc_channel_get_name(entry, &name_len);
1643
1644           if (m)
1645             silc_strncat(buf, sizeof(buf) - 1, m, strlen(m));
1646           silc_strncat(buf, sizeof(buf) - 1, name, name_len);
1647           silc_strncat(buf, sizeof(buf) - 1, " ", 1);
1648           silc_free(m);
1649         }
1650
1651         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1652                            SILCTXT_WHOIS_CHANNELS, buf);
1653       }
1654
1655       if (mode) {
1656         memset(buf, 0, sizeof(buf));
1657         silc_get_umode_string(mode, buf, sizeof(buf - 1));
1658         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1659                            SILCTXT_WHOIS_MODES, buf);
1660       }
1661
1662       if (idle && nickname) {
1663         memset(buf, 0, sizeof(buf));
1664         snprintf(buf, sizeof(buf) - 1, "%lu %s",
1665                  idle > 60 ? (idle / 60) : idle,
1666                  idle > 60 ? "minutes" : "seconds");
1667
1668         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1669                            SILCTXT_WHOIS_IDLE, buf);
1670       }
1671
1672       if (fingerprint) {
1673         fingerprint = silc_fingerprint(fingerprint, 20);
1674         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1675                            SILCTXT_WHOIS_FINGERPRINT, fingerprint);
1676         silc_free(fingerprint);
1677       }
1678
1679       if (attrs)
1680         silc_query_attributes_print(server, silc_client, conn, attrs,
1681                                     client_entry);
1682     }
1683     break;
1684
1685   case SILC_COMMAND_WHOWAS:
1686     {
1687       char *nickname, *username, *realname;
1688
1689       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1690         char *tmp = va_arg(vp, char *);
1691         if (tmp)
1692           silc_say_error("%s: %s", tmp,
1693                          silc_get_status_message(status));
1694         break;
1695       } else if (SILC_STATUS_IS_ERROR(status)) {
1696         silc_say_error("WHOWAS: %s", silc_get_status_message(status));
1697         return;
1698       }
1699
1700       (void)va_arg(vp, SilcClientEntry);
1701       nickname = va_arg(vp, char *);
1702       username = va_arg(vp, char *);
1703       realname = va_arg(vp, char *);
1704
1705       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1706                          SILCTXT_WHOWAS_USERINFO, nickname, username,
1707                          realname ? realname : "");
1708     }
1709     break;
1710
1711   case SILC_COMMAND_INVITE:
1712     {
1713       SilcChannelEntry channel;
1714       SilcArgumentPayload invite_list;
1715
1716       if (SILC_STATUS_IS_ERROR(status))
1717         return;
1718
1719       channel = va_arg(vp, SilcChannelEntry);
1720       invite_list = va_arg(vp, SilcArgumentPayload);
1721
1722       if (invite_list)
1723         silc_parse_inviteban_list(client, conn, server, channel,
1724                                   "invite", invite_list);
1725     }
1726     break;
1727
1728   case SILC_COMMAND_JOIN:
1729     {
1730       char *channel, *mode, *topic, *cipher, *hmac;
1731       SilcUInt32 modei;
1732       SilcHashTableList *user_list;
1733       SilcChannelEntry channel_entry;
1734       SilcChannelUser chu;
1735       SilcClientEntry founder = NULL;
1736       NICK_REC *ownnick;
1737
1738       if (SILC_STATUS_IS_ERROR(status)) {
1739         if (status == SILC_STATUS_ERR_NO_SUCH_SERVER) {
1740           char *tmp = va_arg(vp, char *);
1741           if (tmp)
1742             silc_say_error("JOIN: %s: %s", tmp,
1743                            silc_get_status_message(status));
1744           return;
1745         }
1746         if (status == SILC_STATUS_ERR_NO_SUCH_CHANNEL) {
1747           char *tmp = va_arg(vp, char *);
1748           if (tmp)
1749             silc_say_error("JOIN: %s: %s", tmp,
1750                            silc_get_status_message(status));
1751           return;
1752         }
1753         silc_say_error("JOIN: %s", silc_get_status_message(status));
1754         return;
1755       }
1756
1757       channel = va_arg(vp, char *);
1758       channel_entry = va_arg(vp, SilcChannelEntry);
1759       modei = va_arg(vp, SilcUInt32);
1760       user_list = va_arg(vp, SilcHashTableList *);
1761       topic = va_arg(vp, char *);
1762       cipher = va_arg(vp, char *);
1763       hmac = va_arg(vp, char *);
1764
1765       chanrec = silc_channel_find(server, channel);
1766       if (!chanrec)
1767         chanrec = silc_channel_create(server, channel, channel, TRUE);
1768
1769       if (topic) {
1770         char tmp[256], *cp, *dm = NULL;
1771         g_free_not_null(chanrec->topic);
1772
1773         if (!silc_term_utf8() && silc_utf8_valid(topic, strlen(topic))) {
1774           memset(tmp, 0, sizeof(tmp));
1775           cp = tmp;
1776           if (strlen(topic) > sizeof(tmp) - 1) {
1777             dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
1778             cp = dm;
1779           }
1780
1781           silc_utf8_decode(topic, strlen(topic), SILC_STRING_LOCALE,
1782                            cp, strlen(topic));
1783           topic = cp;
1784         }
1785
1786         chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1787         signal_emit("channel topic changed", 1, chanrec);
1788
1789         silc_free(dm);
1790       }
1791
1792       mode = silc_client_chmode(modei, cipher ? cipher : "", hmac ? hmac : "");
1793       g_free_not_null(chanrec->mode);
1794       chanrec->mode = g_strdup(mode == NULL ? "" : mode);
1795       signal_emit("channel mode changed", 1, chanrec);
1796
1797       /* Get user list */
1798       while (silc_hash_table_get(user_list, NULL, (void *)&chu)) {
1799         if (!chu->client->nickname[0])
1800           continue;
1801         if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
1802           founder = chu->client;
1803         silc_nicklist_insert(chanrec, chu, FALSE);
1804       }
1805
1806       ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
1807       if (!ownnick)
1808         break;
1809       nicklist_set_own(CHANNEL(chanrec), ownnick);
1810       signal_emit("channel joined", 1, chanrec);
1811       chanrec->entry = channel_entry;
1812
1813       if (chanrec->topic)
1814         printformat_module("fe-common/silc", server,
1815                            channel_entry->channel_name,
1816                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
1817                            channel_entry->channel_name, chanrec->topic);
1818
1819       if (founder) {
1820         if (founder == conn->local_entry) {
1821           printformat_module("fe-common/silc",
1822                              server, channel_entry->channel_name,
1823                              MSGLEVEL_CRAP, SILCTXT_CHANNEL_FOUNDER_YOU,
1824                              channel_entry->channel_name);
1825           signal_emit("nick mode changed", 2, chanrec, ownnick);
1826         } else
1827           printformat_module("fe-common/silc",
1828                              server, channel_entry->channel_name,
1829                              MSGLEVEL_CRAP, SILCTXT_CHANNEL_FOUNDER,
1830                              channel_entry->channel_name, founder->nickname);
1831       }
1832
1833       break;
1834     }
1835
1836   case SILC_COMMAND_NICK:
1837     {
1838       char *old;
1839       SilcClientEntry client_entry = va_arg(vp, SilcClientEntry);
1840       GSList *nicks;
1841
1842       if (SILC_STATUS_IS_ERROR(status)) {
1843         silc_say_error("NICK: %s", silc_get_status_message(status));
1844         return;
1845       }
1846
1847       nicks = nicklist_get_same(SERVER(server), client_entry->nickname);
1848       if ((nicks != NULL) &&
1849           (strcmp(SERVER(server)->nick, client_entry->nickname))) {
1850         char buf[512];
1851         SilcClientEntry collider, old;
1852
1853         old = ((SILC_NICK_REC *)(nicks->next->data))->silc_user->client;
1854         collider = silc_client_get_client_by_id(client, conn, &old->id);
1855         if (collider != client_entry) {
1856           memset(buf, 0, sizeof(buf));
1857           snprintf(buf, sizeof(buf) - 1, "%s@%s",
1858                    collider->username, collider->hostname);
1859           nicklist_rename_unique(SERVER(server),
1860                                  old, old->nickname,
1861                                  collider, collider->nickname);
1862           silc_print_nick_change(server, collider->nickname,
1863                                  client_entry->nickname, buf);
1864         }
1865         silc_client_unref_client(client, conn, collider);
1866       }
1867
1868       if (nicks != NULL)
1869         g_slist_free(nicks);
1870
1871       old = g_strdup(server->nick);
1872       server_change_nick(SERVER(server), client_entry->nickname);
1873       nicklist_rename_unique(SERVER(server),
1874                              server->conn->local_entry, server->nick,
1875                              client_entry, client_entry->nickname);
1876       signal_emit("message own_nick", 4, server, server->nick, old, "");
1877       g_free(old);
1878
1879       /* when connecting to a server, the last thing we receive
1880          is a SILC_COMMAND_LIST reply. Since we enable queueing
1881          during the connection, we can now safely disable it again */
1882       silc_queue_disable(conn);
1883       break;
1884     }
1885
1886   case SILC_COMMAND_LIST:
1887     {
1888       char *topic, *name;
1889       int usercount;
1890       char users[20];
1891       char tmp[256], *cp, *dm = NULL;
1892
1893       if (SILC_STATUS_IS_ERROR(status))
1894         return;
1895
1896       (void)va_arg(vp, SilcChannelEntry);
1897       name = va_arg(vp, char *);
1898       topic = va_arg(vp, char *);
1899       usercount = va_arg(vp, int);
1900
1901       if (topic && !silc_term_utf8() &&
1902           silc_utf8_valid(topic, strlen(topic))) {
1903         memset(tmp, 0, sizeof(tmp));
1904         cp = tmp;
1905         if (strlen(topic) > sizeof(tmp) - 1) {
1906           dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
1907           cp = dm;
1908         }
1909
1910         silc_utf8_decode(topic, strlen(topic), SILC_STRING_LOCALE,
1911                          cp, strlen(topic));
1912         topic = cp;
1913       }
1914
1915       if (status == SILC_STATUS_LIST_START ||
1916           status == SILC_STATUS_OK)
1917         printformat_module("fe-common/silc", server, NULL,
1918                            MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
1919
1920       if (!usercount)
1921         snprintf(users, sizeof(users) - 1, "N/A");
1922       else
1923         snprintf(users, sizeof(users) - 1, "%d", usercount);
1924       printformat_module("fe-common/silc", server, NULL,
1925                          MSGLEVEL_CRAP, SILCTXT_LIST,
1926                          name, users, topic ? topic : "");
1927       silc_free(dm);
1928     }
1929     break;
1930
1931   case SILC_COMMAND_UMODE:
1932     {
1933       SilcUInt32 mode;
1934       char *reason;
1935
1936       if (SILC_STATUS_IS_ERROR(status))
1937         return;
1938
1939       mode = va_arg(vp, SilcUInt32);
1940
1941       if (mode & SILC_UMODE_SERVER_OPERATOR &&
1942           !(server->umode & SILC_UMODE_SERVER_OPERATOR))
1943         printformat_module("fe-common/silc", server, NULL,
1944                            MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1945
1946       if (mode & SILC_UMODE_ROUTER_OPERATOR &&
1947           !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
1948         printformat_module("fe-common/silc", server, NULL,
1949                            MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1950
1951       if ((mode & SILC_UMODE_GONE) != (server->umode & SILC_UMODE_GONE)) {
1952         if (mode & SILC_UMODE_GONE) {
1953           if ((server->away_reason != NULL) && (server->away_reason[0] != '\0'))
1954             reason = g_strdup(server->away_reason);
1955           else
1956             reason = g_strdup("away");
1957         } else
1958           reason = g_strdup("");
1959
1960         silc_set_away(reason, server);
1961
1962         g_free(reason);
1963       }
1964
1965       server->umode = mode;
1966       signal_emit("user mode changed", 2, server, NULL);
1967     }
1968     break;
1969
1970   case SILC_COMMAND_OPER:
1971     if (SILC_STATUS_IS_ERROR(status)) {
1972       silc_say_error("OPER: %s", silc_get_status_message(status));
1973       return;
1974     }
1975
1976     server->umode |= SILC_UMODE_SERVER_OPERATOR;
1977     signal_emit("user mode changed", 2, server, NULL);
1978
1979     printformat_module("fe-common/silc", server, NULL,
1980                        MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1981     break;
1982
1983   case SILC_COMMAND_SILCOPER:
1984     if (SILC_STATUS_IS_ERROR(status)) {
1985       silc_say_error("SILCOPER: %s", silc_get_status_message(status));
1986       return;
1987     }
1988
1989     server->umode |= SILC_UMODE_ROUTER_OPERATOR;
1990     signal_emit("user mode changed", 2, server, NULL);
1991
1992     printformat_module("fe-common/silc", server, NULL,
1993                        MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1994     break;
1995
1996   case SILC_COMMAND_USERS:
1997     {
1998       SilcHashTableList htl;
1999       SilcChannelEntry channel;
2000       SilcChannelUser chu;
2001
2002       if (SILC_STATUS_IS_ERROR(status)) {
2003         silc_say_error("USERS: %s", silc_get_status_message(status));
2004         return;
2005       }
2006
2007       channel = va_arg(vp, SilcChannelEntry);
2008
2009       printformat_module("fe-common/silc", server, channel->channel_name,
2010                          MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
2011                          channel->channel_name);
2012
2013       silc_hash_table_list(channel->user_list, &htl);
2014       while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
2015         SilcClientEntry e = chu->client;
2016         char stat[5], *mode;
2017
2018         if (!e->nickname[0])
2019           continue;
2020
2021         memset(stat, 0, sizeof(stat));
2022         mode = silc_client_chumode_char(chu->mode);
2023         if (e->mode & SILC_UMODE_GONE)
2024           strcat(stat, "G");
2025         else if (e->mode & SILC_UMODE_INDISPOSED)
2026           strcat(stat, "I");
2027         else if (e->mode & SILC_UMODE_BUSY)
2028           strcat(stat, "B");
2029         else if (e->mode & SILC_UMODE_PAGE)
2030           strcat(stat, "P");
2031         else if (e->mode & SILC_UMODE_HYPER)
2032           strcat(stat, "H");
2033         else if (e->mode & SILC_UMODE_ROBOT)
2034           strcat(stat, "R");
2035         else if (e->mode & SILC_UMODE_ANONYMOUS)
2036           strcat(stat, "?");
2037         else
2038           strcat(stat, "A");
2039         if (mode)
2040           strcat(stat, mode);
2041
2042         printformat_module("fe-common/silc", server, channel->channel_name,
2043                            MSGLEVEL_CRAP, SILCTXT_USERS,
2044                            e->nickname, stat,
2045                            e->username[0] ? e->username : "",
2046                            e->hostname[0] ? e->hostname : "",
2047                            e->realname ? e->realname : "");
2048         if (mode)
2049           silc_free(mode);
2050       }
2051       silc_hash_table_list_reset(&htl);
2052     }
2053     break;
2054
2055   case SILC_COMMAND_BAN:
2056     {
2057       SilcChannelEntry channel;
2058       SilcArgumentPayload invite_list;
2059
2060       if (SILC_STATUS_IS_ERROR(status))
2061         return;
2062
2063       channel = va_arg(vp, SilcChannelEntry);
2064       invite_list = va_arg(vp, SilcArgumentPayload);
2065
2066       if (invite_list)
2067         silc_parse_inviteban_list(client, conn, server, channel,
2068                                   "ban", invite_list);
2069     }
2070     break;
2071
2072   case SILC_COMMAND_GETKEY:
2073     {
2074       SilcIdType id_type;
2075       void *entry;
2076       SilcPublicKey public_key;
2077       GetkeyContext getkey;
2078       char *name;
2079
2080       if (SILC_STATUS_IS_ERROR(status)) {
2081         silc_say_error("GETKEY: %s", silc_get_status_message(status));
2082         return;
2083       }
2084
2085       id_type = va_arg(vp, SilcUInt32);
2086       entry = va_arg(vp, void *);
2087       public_key = va_arg(vp, SilcPublicKey);
2088
2089       if (public_key) {
2090         getkey = silc_calloc(1, sizeof(*getkey));
2091         getkey->entry = entry;
2092         getkey->id_type = id_type;
2093         getkey->client = client;
2094         getkey->conn = conn;
2095
2096         name = (id_type == SILC_ID_CLIENT ?
2097                 ((SilcClientEntry)entry)->nickname :
2098                 ((SilcServerEntry)entry)->server_name);
2099
2100         silc_verify_public_key_internal(client, conn, name,
2101                                         (id_type == SILC_ID_CLIENT ?
2102                                          SILC_CONN_CLIENT :
2103                                          SILC_CONN_SERVER),
2104                                         public_key, silc_getkey_cb, getkey);
2105       } else {
2106         printformat_module("fe-common/silc", server, NULL,
2107                            MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOKEY);
2108       }
2109     }
2110     break;
2111
2112   case SILC_COMMAND_INFO:
2113     {
2114       SilcServerEntry server_entry;
2115       char *server_name;
2116       char *server_info;
2117
2118       if (SILC_STATUS_IS_ERROR(status))
2119         return;
2120
2121       server_entry = va_arg(vp, SilcServerEntry);
2122       server_name = va_arg(vp, char *);
2123       server_info = va_arg(vp, char *);
2124
2125       if (server_name && server_info )
2126         {
2127           printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
2128           printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
2129         }
2130     }
2131     break;
2132
2133   case SILC_COMMAND_TOPIC:
2134     {
2135       SilcChannelEntry channel;
2136       char *topic;
2137       char tmp[256], *cp, *dm = NULL;
2138
2139       if (SILC_STATUS_IS_ERROR(status))
2140         return;
2141
2142       channel = va_arg(vp, SilcChannelEntry);
2143       topic = va_arg(vp, char *);
2144
2145       if (topic && !silc_term_utf8() &&
2146           silc_utf8_valid(topic, strlen(topic))) {
2147         memset(tmp, 0, sizeof(tmp));
2148         cp = tmp;
2149         if (strlen(topic) > sizeof(tmp) - 1) {
2150           dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
2151           cp = dm;
2152         }
2153
2154         silc_utf8_decode(topic, strlen(topic), SILC_STRING_LOCALE,
2155                          cp, strlen(topic));
2156         topic = cp;
2157       }
2158
2159       if (topic) {
2160         chanrec = silc_channel_find_entry(server, channel);
2161         if (chanrec) {
2162           g_free_not_null(chanrec->topic);
2163           chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
2164           signal_emit("channel topic changed", 1, chanrec);
2165         }
2166         printformat_module("fe-common/silc", server, channel->channel_name,
2167                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
2168                            channel->channel_name, topic);
2169       } else {
2170         printformat_module("fe-common/silc", server, channel->channel_name,
2171                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
2172                            channel->channel_name);
2173       }
2174       silc_free(dm);
2175     }
2176     break;
2177
2178   case SILC_COMMAND_WATCH:
2179     break;
2180
2181   case SILC_COMMAND_STATS:
2182     {
2183       SilcClientStats *cstats;
2184       char tmp[40];
2185       const char *tmptime;
2186       int days, hours, mins, secs;
2187
2188       if (SILC_STATUS_IS_ERROR(status))
2189         return;
2190
2191       cstats = va_arg(vp, SilcClientStats *);
2192       if (!cstats) {
2193         printtext(server, NULL, MSGLEVEL_CRAP, "No statistics available");
2194         return;
2195       }
2196
2197       tmptime = silc_time_string(cstats->starttime);
2198       printformat_module("fe-common/silc", server, NULL,
2199                          MSGLEVEL_CRAP, SILCTXT_STATS,
2200                          "Local server start time", tmptime);
2201
2202       days = cstats->uptime / (24 * 60 * 60);
2203       cstats->uptime -= days * (24 * 60 * 60);
2204       hours = cstats->uptime / (60 * 60);
2205       cstats->uptime -= hours * (60 * 60);
2206       mins = cstats->uptime / 60;
2207       cstats->uptime -= mins * 60;
2208       secs = cstats->uptime;
2209       snprintf(tmp, sizeof(tmp) - 1, "%d days %d hours %d mins %d secs",
2210                days, hours, mins, secs);
2211       printformat_module("fe-common/silc", server, NULL,
2212                          MSGLEVEL_CRAP, SILCTXT_STATS,
2213                          "Local server uptime", tmp);
2214
2215       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->my_clients);
2216       printformat_module("fe-common/silc", server, NULL,
2217                          MSGLEVEL_CRAP, SILCTXT_STATS,
2218                          "Local server clients", tmp);
2219
2220       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->my_channels);
2221       printformat_module("fe-common/silc", server, NULL,
2222                          MSGLEVEL_CRAP, SILCTXT_STATS,
2223                          "Local server channels", tmp);
2224
2225       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->my_server_ops);
2226       printformat_module("fe-common/silc", server, NULL,
2227                          MSGLEVEL_CRAP, SILCTXT_STATS,
2228                          "Local server operators", tmp);
2229
2230       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->my_router_ops);
2231       printformat_module("fe-common/silc", server, NULL,
2232                          MSGLEVEL_CRAP, SILCTXT_STATS,
2233                          "Local router operators", tmp);
2234
2235       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->cell_clients);
2236       printformat_module("fe-common/silc", server, NULL,
2237                          MSGLEVEL_CRAP, SILCTXT_STATS,
2238                          "Local cell clients", tmp);
2239
2240       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->cell_channels);
2241       printformat_module("fe-common/silc", server, NULL,
2242                          MSGLEVEL_CRAP, SILCTXT_STATS,
2243                          "Local cell channels", tmp);
2244
2245       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->cell_servers);
2246       printformat_module("fe-common/silc", server, NULL,
2247                          MSGLEVEL_CRAP, SILCTXT_STATS,
2248                          "Local cell servers", tmp);
2249
2250       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->clients);
2251       printformat_module("fe-common/silc", server, NULL,
2252                          MSGLEVEL_CRAP, SILCTXT_STATS,
2253                          "Total clients", tmp);
2254
2255       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->channels);
2256       printformat_module("fe-common/silc", server, NULL,
2257                          MSGLEVEL_CRAP, SILCTXT_STATS,
2258                          "Total channels", tmp);
2259
2260       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->servers);
2261       printformat_module("fe-common/silc", server, NULL,
2262                          MSGLEVEL_CRAP, SILCTXT_STATS,
2263                          "Total servers", tmp);
2264
2265       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->routers);
2266       printformat_module("fe-common/silc", server, NULL,
2267                          MSGLEVEL_CRAP, SILCTXT_STATS,
2268                          "Total routers", tmp);
2269
2270       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->server_ops);
2271       printformat_module("fe-common/silc", server, NULL,
2272                          MSGLEVEL_CRAP, SILCTXT_STATS,
2273                            "Total server operators", tmp);
2274
2275       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->router_ops);
2276       printformat_module("fe-common/silc", server, NULL,
2277                          MSGLEVEL_CRAP, SILCTXT_STATS,
2278                          "Total router operators", tmp);
2279     }
2280     break;
2281
2282   case SILC_COMMAND_CMODE:
2283     {
2284       SilcChannelEntry channel_entry;
2285       SilcDList chpks;
2286
2287       channel_entry = va_arg(vp, SilcChannelEntry);
2288       (void)va_arg(vp, SilcUInt32);
2289       (void)va_arg(vp, SilcPublicKey);
2290       chpks = va_arg(vp, SilcDList);
2291
2292       if (SILC_STATUS_IS_ERROR(status) || !cmode_list_chpks ||
2293           !channel_entry || !channel_entry->channel_name)
2294         return;
2295
2296       /* Print the channel public key list */
2297       if (chpks)
2298         silc_parse_channel_public_keys(server, channel_entry, chpks);
2299       else
2300         printformat_module("fe-common/silc", server, NULL,
2301                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_PK_NO_LIST,
2302                          channel_entry->channel_name);
2303
2304     }
2305     break;
2306
2307   case SILC_COMMAND_LEAVE:
2308     {
2309       if (SILC_STATUS_IS_ERROR(status))
2310         return;
2311
2312       /* We might be cycling, so disable queueing again */
2313       silc_queue_disable(conn);
2314     }
2315     break;
2316
2317   case SILC_COMMAND_DETACH:
2318     {
2319       /* Save the detachment data to file. */
2320       char *file;
2321       SilcBuffer detach;
2322
2323       if (SILC_STATUS_IS_ERROR(status))
2324         return;
2325
2326       detach = va_arg(vp, SilcBuffer);
2327       file = silc_get_session_filename(server);
2328       silc_file_writefile(file, silc_buffer_data(detach),
2329                           silc_buffer_len(detach));
2330       silc_free(file);
2331     }
2332     break;
2333
2334   case SILC_COMMAND_KILL:
2335     {
2336       SilcClientEntry client_entry;
2337
2338       if (SILC_STATUS_IS_ERROR(status)) {
2339         silc_say_error("KILL: %s", silc_get_status_message(status));
2340         return;
2341       }
2342
2343       client_entry = va_arg(vp, SilcClientEntry);
2344       if (!client_entry || !client_entry->nickname[0])
2345         break;
2346
2347       /* Print this only if the killed client isn't joined on channels.
2348          If it is, we receive KILLED notify and we'll print this there. */
2349       if (!silc_hash_table_count(client_entry->channels))
2350         printformat_module("fe-common/silc", server, NULL,
2351                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
2352                            client_entry->nickname,
2353                            conn->local_entry->nickname, "");
2354     }
2355   }
2356 }
2357
2358 typedef struct {
2359   SilcClient client;
2360   SilcClientConnection conn;
2361   char *filename;
2362   char *entity;
2363   char *entity_name;
2364   SilcPublicKey public_key;
2365   SilcVerifyPublicKey completion;
2366   void *context;
2367 } *PublicKeyVerify;
2368
2369 static void verify_public_key_completion(const char *line, void *context)
2370 {
2371   PublicKeyVerify verify = (PublicKeyVerify)context;
2372
2373   if (line[0] == 'Y' || line[0] == 'y') {
2374     /* Call the completion */
2375     if (verify->completion)
2376       verify->completion(TRUE, verify->context);
2377
2378     /* Save the key for future checking */
2379     silc_pkcs_save_public_key(verify->filename, verify->public_key,
2380                               SILC_PKCS_FILE_BASE64);
2381   } else {
2382     /* Call the completion */
2383     if (verify->completion)
2384       verify->completion(FALSE, verify->context);
2385
2386     printformat_module("fe-common/silc", NULL, NULL,
2387                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD,
2388                        verify->entity_name ? verify->entity_name :
2389                        verify->entity);
2390   }
2391
2392   silc_free(verify->filename);
2393   silc_free(verify->entity);
2394   silc_free(verify->entity_name);
2395   silc_free(verify);
2396 }
2397
2398 /* Internal routine to verify public key. If the `completion' is provided
2399    it will be called to indicate whether public was verified or not. For
2400    server/router public key this will check for filename that includes the
2401    remote host's IP address and remote host's hostname. */
2402
2403 static void
2404 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
2405                                 const char *name,
2406                                 SilcConnectionType conn_type,
2407                                 SilcPublicKey public_key,
2408                                 SilcVerifyPublicKey completion, void *context)
2409 {
2410   PublicKeyVerify verify;
2411   char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
2412   char *fingerprint, *babbleprint, *format;
2413   SilcPublicKey local_pubkey;
2414   SilcSILCPublicKey silc_pubkey;
2415   SilcUInt16 port;
2416   const char *hostname, *ip;
2417   unsigned char *pk;
2418   SilcUInt32 pk_len;
2419   struct passwd *pw;
2420   struct stat st;
2421   char *entity = ((conn_type == SILC_CONN_SERVER ||
2422                    conn_type == SILC_CONN_ROUTER) ?
2423                   "server" : "client");
2424   int i;
2425
2426   if (silc_pkcs_get_type(public_key) != SILC_PKCS_SILC) {
2427     printformat_module("fe-common/silc", NULL, NULL,
2428                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
2429                        entity, silc_pkcs_get_type(public_key));
2430     if (completion)
2431       completion(FALSE, context);
2432     return;
2433   }
2434
2435   /* Encode public key */
2436   pk = silc_pkcs_public_key_encode(public_key, &pk_len);
2437   if (!pk) {
2438     if (completion)
2439       completion(FALSE, context);
2440     return;
2441   }
2442
2443   silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
2444
2445   pw = getpwuid(getuid());
2446   if (!pw) {
2447     if (completion)
2448       completion(FALSE, context);
2449     silc_free(pk);
2450     return;
2451   }
2452
2453   memset(filename, 0, sizeof(filename));
2454   memset(filename2, 0, sizeof(filename2));
2455   memset(file, 0, sizeof(file));
2456
2457   /* Get remote host information */
2458   silc_socket_stream_get_info(silc_packet_stream_get_stream(conn->stream),
2459                               NULL, &hostname, &ip, &port);
2460
2461   if (conn_type == SILC_CONN_SERVER ||
2462       conn_type == SILC_CONN_ROUTER) {
2463     if (!name) {
2464       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, ip, port);
2465       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2466                get_irssi_dir(), entity, file);
2467
2468       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2469                hostname, port);
2470       snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s",
2471                get_irssi_dir(), entity, file);
2472
2473       ipf = filename;
2474       hostf = filename2;
2475     } else {
2476       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2477                name, port);
2478       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2479                get_irssi_dir(), entity, file);
2480
2481       ipf = filename;
2482     }
2483   } else {
2484     /* Replace all whitespaces with `_'. */
2485     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2486     for (i = 0; i < strlen(fingerprint); i++)
2487       if (fingerprint[i] == ' ')
2488         fingerprint[i] = '_';
2489
2490     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
2491     snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2492              get_irssi_dir(), entity, file);
2493     silc_free(fingerprint);
2494
2495     ipf = filename;
2496   }
2497
2498   /* Take fingerprint of the public key */
2499   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2500   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
2501
2502   verify = silc_calloc(1, sizeof(*verify));
2503   verify->client = client;
2504   verify->conn = conn;
2505   verify->filename = strdup(ipf);
2506   verify->entity = strdup(entity);
2507   verify->entity_name = (conn_type != SILC_CONN_CLIENT ?
2508                          (name ? strdup(name) : strdup(hostname))
2509                          : NULL);
2510   verify->public_key = public_key;
2511   verify->completion = completion;
2512   verify->context = context;
2513
2514   /* Check whether this key already exists */
2515   if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
2516     /* Key does not exist, ask user to verify the key and save it */
2517
2518     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2519                        SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2520                        verify->entity_name : entity);
2521     if (conn_type == SILC_CONN_CLIENT && name &&
2522         silc_pubkey->identifier.realname)
2523       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2524                          SILCTXT_PUBKEY_RECEIVED_CLIENT, name,
2525                          silc_pubkey->identifier.realname,
2526                          silc_pubkey->identifier.email ?
2527                          silc_pubkey->identifier.email : "");
2528     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2529                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2530     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2531                        SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2532     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2533                              SILCTXT_PUBKEY_ACCEPT);
2534     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2535                             format, 0, verify);
2536     g_free(format);
2537     silc_free(fingerprint);
2538     silc_free(babbleprint);
2539     silc_free(pk);
2540     return;
2541   } else {
2542     /* The key already exists, verify it. */
2543     unsigned char *encpk;
2544     SilcUInt32 encpk_len;
2545
2546     /* Load the key file, try for both IP filename and hostname filename */
2547     if (!silc_pkcs_load_public_key(ipf, &local_pubkey) &&
2548         (!hostf || (!silc_pkcs_load_public_key(hostf, &local_pubkey)))) {
2549       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2550                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2551                          verify->entity_name : entity);
2552       if (conn_type == SILC_CONN_CLIENT && name &&
2553           silc_pubkey->identifier.realname)
2554         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2555                            SILCTXT_PUBKEY_RECEIVED_CLIENT, name,
2556                            silc_pubkey->identifier.realname,
2557                            silc_pubkey->identifier.email ?
2558                            silc_pubkey->identifier.email : "");
2559       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2560                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2561       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2562                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2563       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2564                          SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
2565       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2566                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2567       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2568                               format, 0, verify);
2569       g_free(format);
2570       silc_free(fingerprint);
2571       silc_free(babbleprint);
2572       silc_free(pk);
2573       return;
2574     }
2575
2576     /* Encode the key data */
2577     encpk = silc_pkcs_public_key_encode(local_pubkey, &encpk_len);
2578     if (!encpk) {
2579       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2580                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2581                          verify->entity_name : entity);
2582       if (conn_type == SILC_CONN_CLIENT && name &&
2583           silc_pubkey->identifier.realname)
2584         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2585                            SILCTXT_PUBKEY_RECEIVED_CLIENT, name,
2586                            silc_pubkey->identifier.realname,
2587                            silc_pubkey->identifier.email ?
2588                            silc_pubkey->identifier.email : "");
2589       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2590                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2591       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2592                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2593       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2594                          SILCTXT_PUBKEY_MALFORMED, entity);
2595       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2596                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2597       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2598                               format, 0, verify);
2599       g_free(format);
2600       silc_free(fingerprint);
2601       silc_free(babbleprint);
2602       silc_free(pk);
2603       return;
2604     }
2605     silc_pkcs_public_key_free(local_pubkey);
2606
2607     /* Compare the keys */
2608     if (memcmp(encpk, pk, encpk_len)) {
2609       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2610                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2611                          verify->entity_name : entity);
2612       if (conn_type == SILC_CONN_CLIENT && name &&
2613           silc_pubkey->identifier.realname)
2614         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2615                            SILCTXT_PUBKEY_RECEIVED_CLIENT, name,
2616                            silc_pubkey->identifier.realname,
2617                            silc_pubkey->identifier.email ?
2618                            silc_pubkey->identifier.email : "");
2619       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2620                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2621       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2622                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2623       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2624                          SILCTXT_PUBKEY_NO_MATCH, entity);
2625       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2626                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
2627       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2628                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
2629
2630       /* Ask user to verify the key and save it */
2631       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2632                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2633       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2634                               format, 0, verify);
2635       g_free(format);
2636       silc_free(fingerprint);
2637       silc_free(babbleprint);
2638       silc_free(encpk);
2639       silc_free(pk);
2640       return;
2641     }
2642
2643     /* Local copy matched */
2644     if (completion)
2645       completion(TRUE, context);
2646     silc_free(encpk);
2647     silc_free(fingerprint);
2648     silc_free(babbleprint);
2649     silc_free(verify->filename);
2650     silc_free(verify->entity);
2651     silc_free(verify->entity_name);
2652     silc_free(verify);
2653     silc_free(pk);
2654   }
2655 }
2656
2657 /* Verifies received public key. The `conn_type' indicates which entity
2658    (server, client etc.) has sent the public key. If user decides to trust
2659    the key may be saved as trusted public key for later use. The
2660    `completion' must be called after the public key has been verified. */
2661
2662 void
2663 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
2664                        SilcConnectionType conn_type,
2665                        SilcPublicKey public_key,
2666                        SilcVerifyPublicKey completion, void *context)
2667 {
2668   silc_verify_public_key_internal(client, conn, NULL, conn_type, public_key,
2669                                   completion, context);
2670 }
2671
2672 /* Asks passphrase from user on the input line. */
2673
2674 typedef struct {
2675   SilcAskPassphrase completion;
2676   void *context;
2677 } *AskPassphrase;
2678
2679 void ask_passphrase_completion(const char *passphrase, void *context)
2680 {
2681   AskPassphrase p = (AskPassphrase)context;
2682   if (passphrase && passphrase[0] == '\0')
2683     passphrase = NULL;
2684   p->completion((unsigned char *)passphrase,
2685                 passphrase ? strlen(passphrase) : 0, p->context);
2686   silc_free(p);
2687 }
2688
2689 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
2690                          SilcAskPassphrase completion, void *context)
2691 {
2692   AskPassphrase p = silc_calloc(1, sizeof(*p));
2693   p->completion = completion;
2694   p->context = context;
2695
2696   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
2697                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
2698 }
2699
2700 typedef struct {
2701   SilcGetAuthMeth completion;
2702   void *context;
2703 } *GetAuthMethod;
2704
2705 static void silc_get_auth_ask_passphrase(const unsigned char *passphrase,
2706                                          SilcUInt32 passphrase_len,
2707                                          void *context)
2708 {
2709   GetAuthMethod a = context;
2710   a->completion(passphrase ? SILC_AUTH_PASSWORD : SILC_AUTH_NONE,
2711                 passphrase, passphrase_len, a->context);
2712   silc_free(a);
2713 }
2714
2715 /* Find authentication data by hostname and port. The hostname may be IP
2716    address as well.*/
2717
2718 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
2719                           char *hostname, SilcUInt16 port,
2720                           SilcAuthMethod auth_meth,
2721                           SilcGetAuthMeth completion, void *context)
2722 {
2723   SERVER_SETUP_REC *setup;
2724
2725   SILC_LOG_DEBUG(("Start"));
2726
2727   if (auth_meth == SILC_AUTH_PUBLIC_KEY) {
2728     /* Returning NULL will cause library to use our private key configured
2729        for this connection */
2730     completion(SILC_AUTH_PUBLIC_KEY, NULL, 0, context);
2731     return;
2732   }
2733
2734   /* Check whether we find the password for this server in our
2735      configuration.  If it's set, always send it server. */
2736   setup = server_setup_find_port(hostname, port);
2737   if (setup && setup->password) {
2738     completion(SILC_AUTH_PASSWORD, setup->password, strlen(setup->password),
2739                context);
2740     return;
2741   }
2742
2743   /* Didn't find password.  If server wants it, ask it from user. */
2744   if (auth_meth == SILC_AUTH_PASSWORD) {
2745     GetAuthMethod a;
2746     a = silc_calloc(1, sizeof(*a));
2747     if (a) {
2748       a->completion = completion;
2749       a->context = context;
2750       silc_ask_passphrase(client, conn, silc_get_auth_ask_passphrase, a);
2751       return;
2752     }
2753   }
2754
2755   /* No authentication */
2756   completion(SILC_AUTH_NONE, NULL, 0, context);
2757 }
2758
2759 /* Asks whether the user would like to perform the key agreement protocol.
2760    This is called after we have received an key agreement packet or an
2761    reply to our key agreement packet. This returns TRUE if the user wants
2762    the library to perform the key agreement protocol and FALSE if it is not
2763    desired (application may start it later by calling the function
2764    silc_client_perform_key_agreement). */
2765
2766 void silc_key_agreement(SilcClient client, SilcClientConnection conn,
2767                         SilcClientEntry client_entry, const char *hostname,
2768                         SilcUInt16 protocol, SilcUInt16 port)
2769 {
2770   char portstr[12], protostr[5];
2771
2772   SILC_LOG_DEBUG(("Start"));
2773
2774   /* We will just display the info on the screen and return FALSE and user
2775      will have to start the key agreement with a command. */
2776
2777   if (hostname) {
2778     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2779     snprintf(protostr, sizeof(protostr) - 1, "%s", protocol == 1 ? "UDP" :
2780              "TCP");
2781   }
2782
2783   if (!hostname)
2784     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2785                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
2786   else
2787     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2788                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
2789                        client_entry->nickname, hostname, portstr, protostr);
2790 }
2791
2792 /* Notifies application that file transfer protocol session is being
2793    requested by the remote client indicated by the `client_entry' from
2794    the `hostname' and `port'. The `session_id' is the file transfer
2795    session and it can be used to either accept or reject the file
2796    transfer request, by calling the silc_client_file_receive or
2797    silc_client_file_close, respectively. */
2798
2799 void silc_ftp(SilcClient client, SilcClientConnection conn,
2800               SilcClientEntry client_entry, SilcUInt32 session_id,
2801               const char *hostname, SilcUInt16 port)
2802 {
2803   SILC_SERVER_REC *server;
2804   char portstr[12];
2805   FtpSession ftp = NULL;
2806
2807   SILC_LOG_DEBUG(("Start"));
2808
2809   server = conn->context;
2810
2811   silc_dlist_start(server->ftp_sessions);
2812   while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
2813     if (ftp->client_entry == client_entry &&
2814         ftp->session_id == session_id) {
2815       server->current_session = ftp;
2816       break;
2817     }
2818   }
2819   if (ftp == SILC_LIST_END) {
2820     ftp = silc_calloc(1, sizeof(*ftp));
2821     ftp->client_entry = client_entry;
2822     ftp->session_id = session_id;
2823     ftp->send = FALSE;
2824     ftp->conn = conn;
2825     silc_dlist_add(server->ftp_sessions, ftp);
2826     server->current_session = ftp;
2827   }
2828
2829   if (hostname)
2830     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2831
2832   if (!hostname)
2833     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2834                        SILCTXT_FILE_REQUEST, client_entry->nickname);
2835   else
2836     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2837                        SILCTXT_FILE_REQUEST_HOST,
2838                        client_entry->nickname, hostname, portstr);
2839 }
2840
2841 /* SILC client operations */
2842 SilcClientOperations ops = {
2843   silc_say,
2844   silc_channel_message,
2845   silc_private_message,
2846   silc_notify,
2847   silc_command,
2848   silc_command_reply,
2849   silc_get_auth_method,
2850   silc_verify_public_key,
2851   silc_ask_passphrase,
2852   silc_key_agreement,
2853   silc_ftp,
2854 };