e64b6fbac7d8335dd72e280af49841b325d36c3f
[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     memset(buf, 0, sizeof(buf));
733     snprintf(buf, sizeof(buf) - 1, "%s@%s",
734              client_entry->username, client_entry->hostname);
735     signal_emit("message invite", 4, server, channel ? channel->channel_name :
736                 name, client_entry->nickname, buf);
737     break;
738
739   case SILC_NOTIFY_TYPE_JOIN:
740     /*
741      * Joined channel.
742      */
743
744     SILC_LOG_DEBUG(("Notify: JOIN"));
745
746     client_entry = va_arg(va, SilcClientEntry);
747     channel = va_arg(va, SilcChannelEntry);
748
749     if (client_entry == server->conn->local_entry) {
750       /* You joined to channel */
751       chanrec = silc_channel_find(server, channel->channel_name);
752       if (chanrec == NULL)
753         chanrec = silc_channel_create(server, channel->channel_name,
754                                         channel->channel_name, TRUE);
755       if (!chanrec->joined)
756         chanrec->entry = channel;
757     } else {
758       chanrec = silc_channel_find_entry(server, channel);
759       if (chanrec != NULL) {
760         SilcChannelUser chu = silc_client_on_channel(channel, client_entry);
761         if (chu)
762           nickrec = silc_nicklist_insert(chanrec, chu, TRUE);
763       }
764     }
765
766     memset(buf, 0, sizeof(buf));
767     if (client_entry->username[0])
768       snprintf(buf, sizeof(buf) - 1, "%s@%s",
769                client_entry->username, client_entry->hostname);
770     signal_emit("message join", 4, server, channel->channel_name,
771                 client_entry->nickname,
772                 !client_entry->username[0] ? "" : buf);
773
774     /* If there are multiple same nicknames on channel now, tell it to user. */
775     if (client_entry != server->conn->local_entry) {
776       char *nick, tmp[32];
777       int count = 0;
778
779       silc_client_nickname_parse(client, conn, client_entry->nickname, &nick);
780       clients = silc_client_get_clients_local(client, conn, nick, TRUE);
781       if (!clients || silc_dlist_count(clients) < 2) {
782         silc_free(nick);
783         silc_client_list_free(client, conn, clients);
784         break;
785       }
786       silc_dlist_start(clients);
787       while ((client_entry2 = silc_dlist_get(clients)))
788         if (silc_client_on_channel(channel, client_entry2))
789           count++;
790       if (count > 1) {
791         silc_snprintf(tmp, sizeof(tmp), "%d", silc_dlist_count(clients));
792         printformat_module("fe-common/silc", server, channel->channel_name,
793                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_MANY_NICKS,
794                            tmp, nick);
795         printformat_module("fe-common/silc", server, channel->channel_name,
796                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_USER_APPEARS,
797                            buf, client_entry->nickname);
798       }
799       silc_client_list_free(client, conn, clients);
800       silc_free(nick);
801     }
802     break;
803
804   case SILC_NOTIFY_TYPE_LEAVE:
805     /*
806      * Left a channel.
807      */
808
809     SILC_LOG_DEBUG(("Notify: LEAVE"));
810
811     client_entry = va_arg(va, SilcClientEntry);
812     channel = va_arg(va, SilcChannelEntry);
813
814     memset(buf, 0, sizeof(buf));
815     if (client_entry->username)
816       snprintf(buf, sizeof(buf) - 1, "%s@%s",
817                client_entry->username, client_entry->hostname);
818     signal_emit("message part", 5, server, channel->channel_name,
819                 client_entry->nickname,  client_entry->username[0] ?
820                 buf : "", client_entry->nickname);
821
822     chanrec = silc_channel_find_entry(server, channel);
823     if (chanrec != NULL) {
824       nickrec = silc_nicklist_find(chanrec, client_entry);
825       if (nickrec != NULL)
826         nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
827     }
828
829     /* If there is only one client with this same nickname on channel now
830        change it to the base format if it is formatted nickname. */
831     if (channel) {
832       silc_client_nickname_parse(client, conn, client_entry->nickname, &name);
833       clients = silc_client_get_clients_local(client, conn, name, TRUE);
834       if (!clients || silc_dlist_count(clients) != 2) {
835         silc_free(name);
836         silc_client_list_free(client, conn, clients);
837         break;
838       }
839       silc_dlist_start(clients);
840       client_entry2 = silc_dlist_get(clients);
841       if (client_entry2 == client_entry)
842         client_entry2 = silc_dlist_get(clients);
843       if (silc_client_on_channel(channel, client_entry2)) {
844         silc_snprintf(buf, sizeof(buf), "%s", client_entry2->nickname);
845         silc_client_nickname_format(client, conn, client_entry2, TRUE);
846         if (!silc_utf8_strcasecmp(buf, client_entry2->nickname)) {
847           nicklist_rename_unique(SERVER(server), client_entry2, buf,
848                                  client_entry2, client_entry2->nickname);
849           printformat_module("fe-common/silc", server, channel->channel_name,
850                              MSGLEVEL_CRAP, SILCTXT_CHANNEL_USER_APPEARS,
851                              buf, client_entry2->nickname);
852         }
853       }
854       silc_client_list_free(client, conn, clients);
855       silc_free(name);
856     }
857     break;
858
859   case SILC_NOTIFY_TYPE_SIGNOFF:
860     /*
861      * Left the network.
862      */
863
864     SILC_LOG_DEBUG(("Notify: SIGNOFF"));
865
866     client_entry = va_arg(va, SilcClientEntry);
867     tmp = va_arg(va, char *);
868     channel = va_arg(va, SilcChannelEntry);
869
870     silc_server_free_ftp(server, client_entry);
871
872     memset(buf, 0, sizeof(buf));
873     if (client_entry->username)
874       snprintf(buf, sizeof(buf) - 1, "%s@%s",
875                client_entry->username, client_entry->hostname);
876     signal_emit("message quit", 4, server, client_entry->nickname,
877                 client_entry->username[0] ? buf : "", tmp ? tmp : "");
878
879     list1 = nicklist_get_same_unique(SERVER(server), client_entry);
880     for (list_tmp = list1; list_tmp != NULL; list_tmp =
881            list_tmp->next->next) {
882       CHANNEL_REC *channel = list_tmp->data;
883       NICK_REC *nickrec = list_tmp->next->data;
884
885       nicklist_remove(channel, nickrec);
886     }
887
888     /* If there is only one client with this same nickname on channel now
889        change it to the base format if it is formatted nickname. */
890     if (channel) {
891       silc_client_nickname_parse(client, conn, client_entry->nickname, &name);
892       clients = silc_client_get_clients_local(client, conn, name, TRUE);
893       if (!clients || silc_dlist_count(clients) != 2) {
894         silc_free(name);
895         silc_client_list_free(client, conn, clients);
896         break;
897       }
898       silc_dlist_start(clients);
899       client_entry2 = silc_dlist_get(clients);
900       if (client_entry2 == client_entry)
901         client_entry2 = silc_dlist_get(clients);
902       if (silc_client_on_channel(channel, client_entry2)) {
903         silc_snprintf(buf, sizeof(buf), "%s", client_entry2->nickname);
904         silc_client_nickname_format(client, conn, client_entry2, TRUE);
905         if (!silc_utf8_strcasecmp(buf, client_entry2->nickname)) {
906           nicklist_rename_unique(SERVER(server), client_entry2, buf,
907                                  client_entry2, client_entry2->nickname);
908           printformat_module("fe-common/silc", server, channel->channel_name,
909                              MSGLEVEL_CRAP, SILCTXT_CHANNEL_USER_APPEARS,
910                              buf, client_entry2->nickname);
911         }
912       }
913       silc_client_list_free(client, conn, clients);
914       silc_free(name);
915     }
916     break;
917
918   case SILC_NOTIFY_TYPE_TOPIC_SET:
919     /*
920      * Changed topic.
921      */
922
923     SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
924
925     idtype = va_arg(va, int);
926     entry = va_arg(va, void *);
927     tmp = va_arg(va, char *);
928     channel = va_arg(va, SilcChannelEntry);
929
930     chanrec = silc_channel_find_entry(server, channel);
931     if (chanrec != NULL) {
932       char tmp2[256], *cp, *dm = NULL;
933
934       g_free_not_null(chanrec->topic);
935       if (tmp && !silc_term_utf8() && silc_utf8_valid(tmp, strlen(tmp))) {
936         memset(tmp2, 0, sizeof(tmp2));
937         cp = tmp2;
938         if (strlen(tmp) > sizeof(tmp2) - 1) {
939           dm = silc_calloc(strlen(tmp) + 1, sizeof(*dm));
940           cp = dm;
941         }
942
943         silc_utf8_decode(tmp, strlen(tmp), SILC_STRING_LANGUAGE,
944                          cp, strlen(tmp));
945         tmp = cp;
946       }
947
948       chanrec->topic = *tmp == '\0' ? NULL : g_strdup(tmp);
949       signal_emit("channel topic changed", 1, chanrec);
950
951       silc_free(dm);
952     }
953
954     if (idtype == SILC_ID_CLIENT) {
955       client_entry = (SilcClientEntry)entry;
956       memset(buf, 0, sizeof(buf));
957       snprintf(buf, sizeof(buf) - 1, "%s@%s",
958                client_entry->username, client_entry->hostname);
959       signal_emit("message topic", 5, server, channel->channel_name,
960                   tmp, client_entry->nickname, buf);
961     } else if (idtype == SILC_ID_SERVER) {
962       server_entry = (SilcServerEntry)entry;
963       signal_emit("message topic", 5, server, channel->channel_name,
964                   tmp, server_entry->server_name,
965                   server_entry->server_name);
966     } else if (idtype == SILC_ID_CHANNEL) {
967       channel = (SilcChannelEntry)entry;
968       signal_emit("message topic", 5, server, channel->channel_name,
969                   tmp, channel->channel_name, channel->channel_name);
970     }
971     break;
972
973   case SILC_NOTIFY_TYPE_NICK_CHANGE:
974     /*
975      * Changed nickname.
976      */
977
978     SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
979
980     client_entry = va_arg(va, SilcClientEntry);
981     name = va_arg(va, char *);                 /* old nickname */
982
983     if (!strcmp(client_entry->nickname, name))
984       break;
985
986     memset(buf, 0, sizeof(buf));
987     snprintf(buf, sizeof(buf) - 1, "%s@%s",
988              client_entry->username, client_entry->hostname);
989     nicklist_rename_unique(SERVER(server),
990                            client_entry, name,
991                            client_entry, client_entry->nickname);
992     signal_emit("message nick", 4, server, client_entry->nickname, name, buf);
993     break;
994
995   case SILC_NOTIFY_TYPE_CMODE_CHANGE:
996     /*
997      * Changed channel mode.
998      */
999
1000     SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
1001
1002     idtype = va_arg(va, int);
1003     entry = va_arg(va, void *);
1004     mode = va_arg(va, SilcUInt32);
1005     cipher = va_arg(va, char *);               /* cipher */
1006     hmac = va_arg(va, char *);                 /* hmac */
1007     (void)va_arg(va, char *);                  /* passphrase */
1008     (void)va_arg(va, SilcPublicKey);           /* founder key */
1009     chpks = va_arg(va, SilcDList);             /* channel public keys */
1010     channel = va_arg(va, SilcChannelEntry);
1011
1012     tmp = silc_client_chmode(mode, cipher ? cipher : "",
1013                              hmac ? hmac : "");
1014
1015     chanrec = silc_channel_find_entry(server, channel);
1016     if (chanrec != NULL) {
1017       g_free_not_null(chanrec->mode);
1018       chanrec->mode = g_strdup(tmp == NULL ? "" : tmp);
1019       signal_emit("channel mode changed", 1, chanrec);
1020     }
1021
1022     if (idtype == SILC_ID_CLIENT) {
1023       client_entry = (SilcClientEntry)entry;
1024       printformat_module("fe-common/silc", server, channel->channel_name,
1025                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
1026                          channel->channel_name, tmp ? tmp : "removed all",
1027                          client_entry->nickname);
1028     } else if (idtype == SILC_ID_SERVER) {
1029       server_entry = (SilcServerEntry)entry;
1030       printformat_module("fe-common/silc", server, channel->channel_name,
1031                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
1032                          channel->channel_name, tmp ? tmp : "removed all",
1033                          server_entry->server_name);
1034     } else if (idtype == SILC_ID_CHANNEL) {
1035       channel2 = (SilcChannelEntry)entry;
1036       printformat_module("fe-common/silc", server, channel->channel_name,
1037                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
1038                          channel->channel_name, tmp ? tmp : "removed all",
1039                          channel2->channel_name);
1040     }
1041
1042     /* Print the channel public key list */
1043     if (chpks)
1044       silc_parse_channel_public_keys(server, channel, chpks);
1045
1046     silc_free(tmp);
1047     break;
1048
1049   case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
1050     /*
1051      * Changed user's mode on channel.
1052      */
1053
1054     SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
1055
1056     idtype = va_arg(va, int);
1057     entry = va_arg(va, void *);
1058     mode = va_arg(va, SilcUInt32);
1059     client_entry2 = va_arg(va, SilcClientEntry);
1060     channel = va_arg(va, SilcChannelEntry);
1061
1062     tmp = silc_client_chumode(mode);
1063     chanrec = silc_channel_find_entry(server, channel);
1064     if (chanrec != NULL) {
1065       SILC_NICK_REC *nick;
1066
1067       if (client_entry2 == server->conn->local_entry)
1068         chanrec->chanop = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
1069
1070       nick = silc_nicklist_find(chanrec, client_entry2);
1071       if (nick != NULL) {
1072         nick->op = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
1073         nick->founder = (mode & SILC_CHANNEL_UMODE_CHANFO) != 0;
1074         signal_emit("nick mode changed", 2, chanrec, nick);
1075       }
1076     }
1077
1078     if (idtype == SILC_ID_CLIENT) {
1079       client_entry = (SilcClientEntry)entry;
1080       printformat_module("fe-common/silc", server, channel->channel_name,
1081                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
1082                          channel->channel_name, client_entry2->nickname,
1083                          tmp ? tmp : "removed all",
1084                          client_entry->nickname);
1085     } else if (idtype == SILC_ID_SERVER) {
1086       server_entry = (SilcServerEntry)entry;
1087       printformat_module("fe-common/silc", server, channel->channel_name,
1088                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
1089                          channel->channel_name, client_entry2->nickname,
1090                          tmp ? tmp : "removed all",
1091                          server_entry->server_name);
1092     } else if (idtype == SILC_ID_CHANNEL) {
1093       channel2 = (SilcChannelEntry)entry;
1094       printformat_module("fe-common/silc", server, channel->channel_name,
1095                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
1096                          channel->channel_name, client_entry2->nickname,
1097                          tmp ? tmp : "removed all",
1098                          channel2->channel_name);
1099     }
1100
1101     if (mode & SILC_CHANNEL_UMODE_CHANFO)
1102       printformat_module("fe-common/silc",
1103                          server, channel->channel_name, MSGLEVEL_CRAP,
1104                          SILCTXT_CHANNEL_FOUNDER,
1105                          channel->channel_name, client_entry2->nickname);
1106
1107     if (mode & SILC_CHANNEL_UMODE_QUIET && conn->local_entry == client_entry2)
1108       printformat_module("fe-common/silc",
1109                          server, channel->channel_name, MSGLEVEL_CRAP,
1110                          SILCTXT_CHANNEL_QUIETED, channel->channel_name);
1111
1112     silc_free(tmp);
1113     break;
1114
1115   case SILC_NOTIFY_TYPE_MOTD:
1116     /*
1117      * Received MOTD.
1118      */
1119
1120     SILC_LOG_DEBUG(("Notify: MOTD"));
1121
1122     tmp = va_arg(va, char *);
1123
1124     if (!settings_get_bool("skip_motd"))
1125       printtext_multiline(server, NULL, MSGLEVEL_CRAP, "%s", tmp);
1126     break;
1127
1128   case SILC_NOTIFY_TYPE_KICKED:
1129     /*
1130      * Someone was kicked from channel.
1131      */
1132
1133     SILC_LOG_DEBUG(("Notify: KICKED"));
1134
1135     client_entry = va_arg(va, SilcClientEntry);
1136     tmp = va_arg(va, char *);
1137     client_entry2 = va_arg(va, SilcClientEntry);
1138     channel = va_arg(va, SilcChannelEntry);
1139
1140     chanrec = silc_channel_find_entry(server, channel);
1141
1142     if (client_entry == conn->local_entry) {
1143       printformat_module("fe-common/silc", server, channel->channel_name,
1144                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED_YOU,
1145                          channel->channel_name,
1146                          client_entry ? client_entry2->nickname : "",
1147                          tmp ? tmp : "");
1148       if (chanrec) {
1149         chanrec->kicked = TRUE;
1150         channel_destroy((CHANNEL_REC *)chanrec);
1151       }
1152     } else {
1153       printformat_module("fe-common/silc", server, channel->channel_name,
1154                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED,
1155                          client_entry->nickname, channel->channel_name,
1156                          client_entry2 ? client_entry2->nickname : "",
1157                          tmp ? tmp : "");
1158
1159       if (chanrec) {
1160         SILC_NICK_REC *nickrec = silc_nicklist_find(chanrec, client_entry);
1161         if (nickrec != NULL)
1162           nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
1163       }
1164     }
1165     break;
1166
1167   case SILC_NOTIFY_TYPE_KILLED:
1168     /*
1169      * Someone was killed from the network.
1170      */
1171
1172     SILC_LOG_DEBUG(("Notify: KILLED"));
1173
1174     client_entry = va_arg(va, SilcClientEntry);
1175     tmp = va_arg(va, char *);
1176     idtype = va_arg(va, int);
1177     entry = va_arg(va, SilcClientEntry);
1178
1179     if (client_entry == conn->local_entry) {
1180       if (idtype == SILC_ID_CLIENT) {
1181         client_entry2 = (SilcClientEntry)entry;
1182         printformat_module("fe-common/silc", server, NULL,
1183                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
1184                            client_entry2 ? client_entry2->nickname : "",
1185                            tmp ? tmp : "");
1186       } else if (idtype == SILC_ID_SERVER) {
1187         server_entry = (SilcServerEntry)entry;
1188         printformat_module("fe-common/silc", server, NULL,
1189                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
1190                            server_entry->server_name, tmp ? tmp : "");
1191       } else if (idtype == SILC_ID_CHANNEL) {
1192         channel = (SilcChannelEntry)entry;
1193         printformat_module("fe-common/silc", server, NULL,
1194                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
1195                            channel->channel_name, tmp ? tmp : "");
1196       }
1197     } else {
1198       list1 = nicklist_get_same_unique(SERVER(server), client_entry);
1199       for (list_tmp = list1; list_tmp != NULL; list_tmp =
1200              list_tmp->next->next) {
1201         CHANNEL_REC *channel = list_tmp->data;
1202         NICK_REC *nickrec = list_tmp->next->data;
1203         nicklist_remove(channel, nickrec);
1204       }
1205
1206       if (idtype == SILC_ID_CLIENT) {
1207         client_entry2 = (SilcClientEntry)entry;
1208         printformat_module("fe-common/silc", server, NULL,
1209                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
1210                            client_entry->nickname,
1211                            client_entry2 ? client_entry2->nickname : "",
1212                            tmp ? tmp : "");
1213       } else if (idtype == SILC_ID_SERVER) {
1214         server_entry = (SilcServerEntry)entry;
1215         printformat_module("fe-common/silc", server, NULL,
1216                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
1217                            client_entry->nickname,
1218                            server_entry->server_name, tmp ? tmp : "");
1219       } else if (idtype == SILC_ID_CHANNEL) {
1220         channel = (SilcChannelEntry)entry;
1221         printformat_module("fe-common/silc", server, NULL,
1222                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
1223                            client_entry->nickname,
1224                            channel->channel_name, tmp ? tmp : "");
1225       }
1226     }
1227     break;
1228
1229   case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
1230     break;
1231
1232   case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
1233     {
1234       /*
1235        * Server has quit the network.
1236        */
1237       SilcDList clients;
1238
1239       SILC_LOG_DEBUG(("Notify: SERVER_SIGNOFF"));
1240
1241       (void)va_arg(va, void *);
1242       clients = va_arg(va, SilcDList);
1243
1244       silc_dlist_start(clients);
1245       while ((client_entry = silc_dlist_get(clients))) {
1246         memset(buf, 0, sizeof(buf));
1247
1248         /* Print only if we have the nickname.  If this client has just quit
1249            when we were only resolving it, it is possible we don't have the
1250            nickname. */
1251         if (client_entry->nickname[0]) {
1252           if (client_entry->username[0])
1253             snprintf(buf, sizeof(buf) - 1, "%s@%s",
1254                      client_entry->username, client_entry->hostname);
1255           signal_emit("message quit", 4, server, client_entry->nickname,
1256                       client_entry->username[0] ? buf : "",
1257                       "server signoff");
1258         }
1259
1260         silc_server_free_ftp(server, client_entry);
1261
1262         list1 = nicklist_get_same_unique(SERVER(server), client_entry);
1263         for (list_tmp = list1; list_tmp != NULL; list_tmp =
1264                list_tmp->next->next) {
1265           CHANNEL_REC *channel = list_tmp->data;
1266           NICK_REC *nickrec = list_tmp->next->data;
1267           nicklist_remove(channel, nickrec);
1268         }
1269       }
1270     }
1271     break;
1272
1273   case SILC_NOTIFY_TYPE_ERROR:
1274     {
1275       SilcStatus error = va_arg(va, int);
1276
1277       silc_say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
1278                 "%s", silc_get_status_message(error));
1279     }
1280     break;
1281
1282   case SILC_NOTIFY_TYPE_WATCH:
1283     {
1284       SilcNotifyType notify;
1285
1286       client_entry = va_arg(va, SilcClientEntry);
1287       name = va_arg(va, char *);          /* Maybe NULL */
1288       mode = va_arg(va, SilcUInt32);
1289       notify = va_arg(va, int);
1290
1291       if (notify == SILC_NOTIFY_TYPE_NICK_CHANGE) {
1292         if (name)
1293           printformat_module("fe-common/silc", server, NULL,
1294                              MSGLEVEL_CRAP, SILCTXT_WATCH_NICK_CHANGE,
1295                              client_entry->nickname, name);
1296         else
1297           printformat_module("fe-common/silc", server, NULL,
1298                              MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
1299                              client_entry->nickname);
1300       } else if (notify == SILC_NOTIFY_TYPE_UMODE_CHANGE) {
1301         /* See if client was away and is now present */
1302         if (!(mode & (SILC_UMODE_GONE | SILC_UMODE_INDISPOSED |
1303                       SILC_UMODE_BUSY | SILC_UMODE_PAGE |
1304                       SILC_UMODE_DETACHED)) &&
1305             (client_entry->mode & SILC_UMODE_GONE ||
1306              client_entry->mode & SILC_UMODE_INDISPOSED ||
1307              client_entry->mode & SILC_UMODE_BUSY ||
1308              client_entry->mode & SILC_UMODE_PAGE ||
1309              client_entry->mode & SILC_UMODE_DETACHED)) {
1310           printformat_module("fe-common/silc", server, NULL,
1311                              MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
1312                              client_entry->nickname);
1313         }
1314
1315         if (mode) {
1316           memset(buf, 0, sizeof(buf));
1317           silc_get_umode_string(mode, buf, sizeof(buf) - 1);
1318           printformat_module("fe-common/silc", server, NULL,
1319                              MSGLEVEL_CRAP, SILCTXT_WATCH_UMODE_CHANGE,
1320                              client_entry->nickname, buf);
1321         }
1322       } else if (notify == SILC_NOTIFY_TYPE_KILLED) {
1323         printformat_module("fe-common/silc", server, NULL,
1324                            MSGLEVEL_CRAP, SILCTXT_WATCH_KILLED,
1325                            client_entry->nickname);
1326       } else if (notify == SILC_NOTIFY_TYPE_SIGNOFF ||
1327                  notify == SILC_NOTIFY_TYPE_SERVER_SIGNOFF) {
1328         printformat_module("fe-common/silc", server, NULL,
1329                            MSGLEVEL_CRAP, SILCTXT_WATCH_SIGNOFF,
1330                            client_entry->nickname);
1331       } else if (notify == SILC_NOTIFY_TYPE_NONE) {
1332         /* Client logged in to the network */
1333         printformat_module("fe-common/silc", server, NULL,
1334                            MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
1335                            client_entry->nickname);
1336       }
1337     }
1338     break;
1339
1340   default:
1341     /* Unknown notify */
1342     printformat_module("fe-common/silc", server, NULL,
1343                        MSGLEVEL_CRAP, SILCTXT_UNKNOWN_NOTIFY, type);
1344     break;
1345   }
1346
1347   va_end(va);
1348 }
1349
1350 /* Command handler. This function is called always in the command function.
1351    If error occurs it will be called as well. `conn' is the associated
1352    client connection. `cmd_context' is the command context that was
1353    originally sent to the command. `success' is FALSE if error occured
1354    during command. `command' is the command being processed. It must be
1355    noted that this is not reply from server. This is merely called just
1356    after application has called the command. Just to tell application
1357    that the command really was processed. */
1358
1359 static SilcBool cmode_list_chpks = FALSE;
1360
1361 void silc_command(SilcClient client, SilcClientConnection conn,
1362                   SilcBool success, SilcCommand command, SilcStatus status,
1363                   SilcUInt32 argc, unsigned char **argv)
1364 {
1365   SILC_SERVER_REC *server = conn->context;
1366
1367   SILC_LOG_DEBUG(("Start"));
1368
1369   if (!success) {
1370     silc_say_error("%s", silc_get_status_message(status));
1371     return;
1372   }
1373
1374   switch (command) {
1375
1376   case SILC_COMMAND_INVITE:
1377     if (argc > 2)
1378       printformat_module("fe-common/silc", server, NULL,
1379                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING,
1380                          argv[2],
1381                          (argv[1][0] == '*' ?
1382                           (char *)conn->current_channel->channel_name :
1383                           (char *)argv[1]));
1384     break;
1385
1386   case SILC_COMMAND_DETACH:
1387     server->no_reconnect = TRUE;
1388     break;
1389
1390   case SILC_COMMAND_CMODE:
1391     if (argc == 3 && !strcmp(argv[2], "+C"))
1392       cmode_list_chpks = TRUE;
1393     else
1394       cmode_list_chpks = FALSE;
1395     break;
1396
1397   default:
1398     break;
1399   }
1400 }
1401
1402 typedef struct {
1403   SilcClient client;
1404   SilcClientConnection conn;
1405   void *entry;
1406   SilcIdType id_type;
1407 } *GetkeyContext;
1408
1409 void silc_getkey_cb(bool success, void *context)
1410 {
1411   GetkeyContext getkey = (GetkeyContext)context;
1412   char *entity = (getkey->id_type == SILC_ID_CLIENT ? "user" : "server");
1413   char *name = (getkey->id_type == SILC_ID_CLIENT ?
1414                 ((SilcClientEntry)getkey->entry)->nickname :
1415                 ((SilcServerEntry)getkey->entry)->server_name);
1416   SilcPublicKey public_key = (getkey->id_type == SILC_ID_CLIENT ?
1417                               ((SilcClientEntry)getkey->entry)->public_key :
1418                               ((SilcServerEntry)getkey->entry)->public_key);
1419   SilcSILCPublicKey silc_pubkey;
1420
1421   silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
1422
1423   if (success) {
1424     if (getkey->id_type == SILC_ID_CLIENT)
1425       printformat_module("fe-common/silc", NULL, NULL,
1426                          MSGLEVEL_CRAP, SILCTXT_PUBKEY_VERIFIED_CLIENT,
1427                          name,
1428                          silc_pubkey->identifier.realname ?
1429                          silc_pubkey->identifier.realname : "",
1430                          silc_pubkey->identifier.email ?
1431                          silc_pubkey->identifier.email : "");
1432     else
1433       printformat_module("fe-common/silc", NULL, NULL,
1434                          MSGLEVEL_CRAP, SILCTXT_PUBKEY_VERIFIED,
1435                          entity, name);
1436   } else {
1437     printformat_module("fe-common/silc", NULL, NULL,
1438                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOTVERIFIED,
1439                        entity, name);
1440   }
1441
1442   silc_free(getkey);
1443 }
1444
1445 /* Parse an invite or ban list */
1446 void silc_parse_inviteban_list(SilcClient client,
1447                                SilcClientConnection conn,
1448                                SILC_SERVER_REC *server,
1449                                SilcChannelEntry channel,
1450                                const char *list_type,
1451                                SilcArgumentPayload list)
1452 {
1453   unsigned char *tmp;
1454   SilcUInt32 type, len;
1455   SILC_CHANNEL_REC *chanrec = silc_channel_find_entry(server, channel);
1456   int counter=0, resolving = FALSE;
1457
1458   if (!silc_argument_get_arg_num(list)) {
1459     printformat_module("fe-common/silc", server,
1460                        (chanrec ? chanrec->visible_name : NULL),
1461                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_NO_INVITEBAN_LIST,
1462                        channel->channel_name, list_type);
1463     return;
1464   }
1465
1466   printformat_module("fe-common/silc", server,
1467                      (chanrec ? chanrec->visible_name : NULL),
1468                      MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_LIST,
1469                      channel->channel_name, list_type);
1470
1471   /* Parse the list */
1472   tmp = silc_argument_get_first_arg(list, &type, &len);
1473   while (tmp) {
1474     switch (type) {
1475       case 1:
1476         {
1477           /* An invite string */
1478           char **list;
1479           int i=0;
1480
1481           if (tmp[len-1] == ',')
1482             tmp[len-1] = '\0';
1483
1484           list = g_strsplit(tmp, ",", -1);
1485           while (list[i])
1486             printformat_module("fe-common/silc", server,
1487                                (chanrec ? chanrec->visible_name : NULL),
1488                                MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_STRING,
1489                                ++counter, channel->channel_name, list_type,
1490                                list[i++]);
1491           g_strfreev(list);
1492         }
1493         break;
1494
1495       case 2:
1496         {
1497           /* A public key */
1498           char *fingerprint, *babbleprint;
1499
1500           /* tmp is Public Key Payload, take public key from it. */
1501           fingerprint = silc_hash_fingerprint(NULL, tmp + 4, len - 4);
1502           babbleprint = silc_hash_babbleprint(NULL, tmp + 4, len - 4);
1503
1504           printformat_module("fe-common/silc", server,
1505                              (chanrec ? chanrec->visible_name : NULL),
1506                              MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_PUBKEY,
1507                              ++counter, channel->channel_name, list_type,
1508                              fingerprint, babbleprint);
1509         }
1510         break;
1511
1512       case 3:
1513         {
1514           /* A Client ID */
1515           SilcClientEntry client_entry;
1516           SilcID id;
1517
1518           if (!silc_id_payload_parse_id(tmp, len, &id)) {
1519             silc_say_error("Invalid data in %s list encountered", list_type);
1520             break;
1521           }
1522
1523           client_entry = silc_client_get_client_by_id(client, conn,
1524                                                       &id.u.client_id);
1525           if (client_entry) {
1526             printformat_module("fe-common/silc", server,
1527                                (chanrec ? chanrec->visible_name : NULL),
1528                                MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_STRING,
1529                                ++counter, channel->channel_name, list_type,
1530                                client_entry->nickname);
1531             silc_client_unref_client(client, conn, client_entry);
1532           } else {
1533             resolving = TRUE;
1534             silc_client_get_client_by_id_resolve(client, conn, &id.u.client_id,
1535                                                  NULL, NULL, NULL);
1536           }
1537         }
1538         break;
1539
1540       default:
1541         /* "trash" */
1542         silc_say_error("Unkown type in %s list: %u (len %u)",
1543                        list_type, type, len);
1544         break;
1545     }
1546     tmp = silc_argument_get_next_arg(list, &type, &len);
1547   }
1548
1549   if (resolving)
1550     printformat_module("fe-common/silc", server,
1551                        (chanrec ? chanrec->visible_name : NULL),
1552                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_REGET,
1553                        list_type, channel->channel_name);
1554 }
1555
1556 /* Command reply handler. This function is called always in the command reply
1557    function. If error occurs it will be called as well. Normal scenario
1558    is that it will be called after the received command data has been parsed
1559    and processed. The function is used to pass the received command data to
1560    the application.
1561
1562    `conn' is the associated client connection. `cmd_payload' is the command
1563    payload data received from server and it can be ignored. It is provided
1564    if the application would like to re-parse the received command data,
1565    however, it must be noted that the data is parsed already by the library
1566    thus the payload can be ignored. `success' is FALSE if error occured.
1567    In this case arguments are not sent to the application. `command' is the
1568    command reply being processed. The function has variable argument list
1569    and each command defines the number and type of arguments it passes to the
1570    application (on error they are not sent). */
1571
1572 void silc_command_reply(SilcClient client, SilcClientConnection conn,
1573                         SilcCommand command, SilcStatus status,
1574                         SilcStatus error, va_list vp)
1575 {
1576   SILC_SERVER_REC *server = conn->context;
1577   SILC_CHANNEL_REC *chanrec;
1578
1579   SILC_LOG_DEBUG(("Start"));
1580
1581   switch(command) {
1582   case SILC_COMMAND_WHOIS:
1583     {
1584       char buf[1024], *nickname, *username, *realname, *nick;
1585       unsigned char *fingerprint;
1586       SilcUInt32 idle, mode, *user_modes;
1587       SilcDList channels;
1588       SilcClientEntry client_entry;
1589       SilcDList attrs;
1590
1591       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1592         /* Print the unknown nick for user */
1593         char *tmp = va_arg(vp, char *);
1594         if (tmp)
1595           silc_say_error("%s: %s", tmp, silc_get_status_message(status));
1596         break;
1597       } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1598         /* Try to find the entry for the unknown client ID, since we
1599            might have, and print the nickname of it for user. */
1600         SilcClientID *id = va_arg(vp, SilcClientID *);
1601         if (id) {
1602           client_entry = silc_client_get_client_by_id(client, conn, id);
1603           if (client_entry && client_entry->nickname[0])
1604             silc_say_error("%s: %s", client_entry->nickname,
1605                            silc_get_status_message(status));
1606           silc_client_unref_client(client, conn, client_entry);
1607         }
1608         break;
1609       } else if (SILC_STATUS_IS_ERROR(status)) {
1610         silc_say_error("WHOIS: %s", silc_get_status_message(status));
1611         return;
1612       }
1613
1614       client_entry = va_arg(vp, SilcClientEntry);
1615       nickname = va_arg(vp, char *);
1616       username = va_arg(vp, char *);
1617       realname = va_arg(vp, char *);
1618       channels = va_arg(vp, SilcDList);
1619       mode = va_arg(vp, SilcUInt32);
1620       idle = va_arg(vp, SilcUInt32);
1621       fingerprint = va_arg(vp, unsigned char *);
1622       user_modes = va_arg(vp, SilcUInt32 *);
1623       attrs = va_arg(vp, SilcDList);
1624
1625       silc_client_nickname_parse(client, conn, client_entry->nickname, &nick);
1626       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1627                          SILCTXT_WHOIS_USERINFO, nickname,
1628                          client_entry->username, client_entry->hostname,
1629                          nick, client_entry->nickname);
1630       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1631                          SILCTXT_WHOIS_REALNAME, realname);
1632       silc_free(nick);
1633
1634       if (channels && user_modes) {
1635         SilcChannelPayload entry;
1636         int i = 0;
1637
1638         memset(buf, 0, sizeof(buf));
1639         silc_dlist_start(channels);
1640         while ((entry = silc_dlist_get(channels))) {
1641           SilcUInt32 name_len;
1642           char *m = silc_client_chumode_char(user_modes[i++]);
1643           char *name = silc_channel_get_name(entry, &name_len);
1644
1645           if (m)
1646             silc_strncat(buf, sizeof(buf) - 1, m, strlen(m));
1647           silc_strncat(buf, sizeof(buf) - 1, name, name_len);
1648           silc_strncat(buf, sizeof(buf) - 1, " ", 1);
1649           silc_free(m);
1650         }
1651
1652         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1653                            SILCTXT_WHOIS_CHANNELS, buf);
1654       }
1655
1656       if (mode) {
1657         memset(buf, 0, sizeof(buf));
1658         silc_get_umode_string(mode, buf, sizeof(buf - 1));
1659         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1660                            SILCTXT_WHOIS_MODES, buf);
1661       }
1662
1663       if (idle && nickname) {
1664         memset(buf, 0, sizeof(buf));
1665         snprintf(buf, sizeof(buf) - 1, "%lu %s",
1666                  idle > 60 ? (idle / 60) : idle,
1667                  idle > 60 ? "minutes" : "seconds");
1668
1669         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1670                            SILCTXT_WHOIS_IDLE, buf);
1671       }
1672
1673       if (fingerprint) {
1674         fingerprint = silc_fingerprint(fingerprint, 20);
1675         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1676                            SILCTXT_WHOIS_FINGERPRINT, fingerprint);
1677         silc_free(fingerprint);
1678       }
1679
1680       if (attrs)
1681         silc_query_attributes_print(server, silc_client, conn, attrs,
1682                                     client_entry);
1683     }
1684     break;
1685
1686   case SILC_COMMAND_WHOWAS:
1687     {
1688       char *nickname, *username, *realname;
1689
1690       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1691         char *tmp = va_arg(vp, char *);
1692         if (tmp)
1693           silc_say_error("%s: %s", tmp,
1694                          silc_get_status_message(status));
1695         break;
1696       } else if (SILC_STATUS_IS_ERROR(status)) {
1697         silc_say_error("WHOWAS: %s", silc_get_status_message(status));
1698         return;
1699       }
1700
1701       (void)va_arg(vp, SilcClientEntry);
1702       nickname = va_arg(vp, char *);
1703       username = va_arg(vp, char *);
1704       realname = va_arg(vp, char *);
1705
1706       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1707                          SILCTXT_WHOWAS_USERINFO, nickname, username,
1708                          realname ? realname : "");
1709     }
1710     break;
1711
1712   case SILC_COMMAND_INVITE:
1713     {
1714       SilcChannelEntry channel;
1715       SilcArgumentPayload invite_list;
1716
1717       if (SILC_STATUS_IS_ERROR(status))
1718         return;
1719
1720       channel = va_arg(vp, SilcChannelEntry);
1721       invite_list = va_arg(vp, SilcArgumentPayload);
1722
1723       if (invite_list)
1724         silc_parse_inviteban_list(client, conn, server, channel,
1725                                   "invite", invite_list);
1726     }
1727     break;
1728
1729   case SILC_COMMAND_JOIN:
1730     {
1731       char *channel, *mode, *topic, *cipher, *hmac;
1732       SilcUInt32 modei;
1733       SilcHashTableList *user_list;
1734       SilcChannelEntry channel_entry;
1735       SilcChannelUser chu;
1736       SilcClientEntry founder = NULL;
1737       NICK_REC *ownnick;
1738
1739       if (SILC_STATUS_IS_ERROR(status)) {
1740         silc_say_error("JOIN: %s", silc_get_status_message(status));
1741         return;
1742       }
1743
1744       channel = va_arg(vp, char *);
1745       channel_entry = va_arg(vp, SilcChannelEntry);
1746       modei = va_arg(vp, SilcUInt32);
1747       user_list = va_arg(vp, SilcHashTableList *);
1748       topic = va_arg(vp, char *);
1749       cipher = va_arg(vp, char *);
1750       hmac = va_arg(vp, char *);
1751
1752       chanrec = silc_channel_find(server, channel);
1753       if (!chanrec)
1754         chanrec = silc_channel_create(server, channel, channel, TRUE);
1755
1756       if (topic) {
1757         char tmp[256], *cp, *dm = NULL;
1758         g_free_not_null(chanrec->topic);
1759
1760         if (!silc_term_utf8() && silc_utf8_valid(topic, strlen(topic))) {
1761           memset(tmp, 0, sizeof(tmp));
1762           cp = tmp;
1763           if (strlen(topic) > sizeof(tmp) - 1) {
1764             dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
1765             cp = dm;
1766           }
1767
1768           silc_utf8_decode(topic, strlen(topic), SILC_STRING_LOCALE,
1769                            cp, strlen(topic));
1770           topic = cp;
1771         }
1772
1773         chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1774         signal_emit("channel topic changed", 1, chanrec);
1775
1776         silc_free(dm);
1777       }
1778
1779       mode = silc_client_chmode(modei, cipher ? cipher : "", hmac ? hmac : "");
1780       g_free_not_null(chanrec->mode);
1781       chanrec->mode = g_strdup(mode == NULL ? "" : mode);
1782       signal_emit("channel mode changed", 1, chanrec);
1783
1784       /* Get user list */
1785       while (silc_hash_table_get(user_list, NULL, (void *)&chu)) {
1786         if (!chu->client->nickname[0])
1787           continue;
1788         if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
1789           founder = chu->client;
1790         silc_nicklist_insert(chanrec, chu, FALSE);
1791       }
1792
1793       ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
1794       if (!ownnick)
1795         break;
1796       nicklist_set_own(CHANNEL(chanrec), ownnick);
1797       signal_emit("channel joined", 1, chanrec);
1798       chanrec->entry = channel_entry;
1799
1800       if (chanrec->topic)
1801         printformat_module("fe-common/silc", server,
1802                            channel_entry->channel_name,
1803                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
1804                            channel_entry->channel_name, chanrec->topic);
1805
1806       if (founder) {
1807         if (founder == conn->local_entry) {
1808           printformat_module("fe-common/silc",
1809                              server, channel_entry->channel_name,
1810                              MSGLEVEL_CRAP, SILCTXT_CHANNEL_FOUNDER_YOU,
1811                              channel_entry->channel_name);
1812           signal_emit("nick mode changed", 2, chanrec, ownnick);
1813         } else
1814           printformat_module("fe-common/silc",
1815                              server, channel_entry->channel_name,
1816                              MSGLEVEL_CRAP, SILCTXT_CHANNEL_FOUNDER,
1817                              channel_entry->channel_name, founder->nickname);
1818       }
1819
1820       break;
1821     }
1822
1823   case SILC_COMMAND_NICK:
1824     {
1825       char *old;
1826       SilcClientEntry client_entry = va_arg(vp, SilcClientEntry);
1827       GSList *nicks;
1828
1829       if (SILC_STATUS_IS_ERROR(status)) {
1830         silc_say_error("NICK: %s", silc_get_status_message(status));
1831         return;
1832       }
1833
1834       nicks = nicklist_get_same(SERVER(server), client_entry->nickname);
1835       if ((nicks != NULL) &&
1836           (strcmp(SERVER(server)->nick, client_entry->nickname))) {
1837         char buf[512];
1838         SilcClientEntry collider, old;
1839
1840         old = ((SILC_NICK_REC *)(nicks->next->data))->silc_user->client;
1841         collider = silc_client_get_client_by_id(client, conn, &old->id);
1842         if (collider != client_entry) {
1843           memset(buf, 0, sizeof(buf));
1844           snprintf(buf, sizeof(buf) - 1, "%s@%s",
1845                    collider->username, collider->hostname);
1846           nicklist_rename_unique(SERVER(server),
1847                                  old, old->nickname,
1848                                  collider, collider->nickname);
1849           silc_print_nick_change(server, collider->nickname,
1850                                  client_entry->nickname, buf);
1851         }
1852         silc_client_unref_client(client, conn, collider);
1853       }
1854
1855       if (nicks != NULL)
1856         g_slist_free(nicks);
1857
1858       old = g_strdup(server->nick);
1859       server_change_nick(SERVER(server), client_entry->nickname);
1860       nicklist_rename_unique(SERVER(server),
1861                              server->conn->local_entry, server->nick,
1862                              client_entry, client_entry->nickname);
1863       signal_emit("message own_nick", 4, server, server->nick, old, "");
1864       g_free(old);
1865
1866       /* when connecting to a server, the last thing we receive
1867          is a SILC_COMMAND_LIST reply. Since we enable queueing
1868          during the connection, we can now safely disable it again */
1869       silc_queue_disable(conn);
1870       break;
1871     }
1872
1873   case SILC_COMMAND_LIST:
1874     {
1875       char *topic, *name;
1876       int usercount;
1877       char users[20];
1878       char tmp[256], *cp, *dm = NULL;
1879
1880       if (SILC_STATUS_IS_ERROR(status))
1881         return;
1882
1883       (void)va_arg(vp, SilcChannelEntry);
1884       name = va_arg(vp, char *);
1885       topic = va_arg(vp, char *);
1886       usercount = va_arg(vp, int);
1887
1888       if (topic && !silc_term_utf8() &&
1889           silc_utf8_valid(topic, strlen(topic))) {
1890         memset(tmp, 0, sizeof(tmp));
1891         cp = tmp;
1892         if (strlen(topic) > sizeof(tmp) - 1) {
1893           dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
1894           cp = dm;
1895         }
1896
1897         silc_utf8_decode(topic, strlen(topic), SILC_STRING_LOCALE,
1898                          cp, strlen(topic));
1899         topic = cp;
1900       }
1901
1902       if (status == SILC_STATUS_LIST_START ||
1903           status == SILC_STATUS_OK)
1904         printformat_module("fe-common/silc", server, NULL,
1905                            MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
1906
1907       if (!usercount)
1908         snprintf(users, sizeof(users) - 1, "N/A");
1909       else
1910         snprintf(users, sizeof(users) - 1, "%d", usercount);
1911       printformat_module("fe-common/silc", server, NULL,
1912                          MSGLEVEL_CRAP, SILCTXT_LIST,
1913                          name, users, topic ? topic : "");
1914       silc_free(dm);
1915     }
1916     break;
1917
1918   case SILC_COMMAND_UMODE:
1919     {
1920       SilcUInt32 mode;
1921       char *reason;
1922
1923       if (SILC_STATUS_IS_ERROR(status))
1924         return;
1925
1926       mode = va_arg(vp, SilcUInt32);
1927
1928       if (mode & SILC_UMODE_SERVER_OPERATOR &&
1929           !(server->umode & SILC_UMODE_SERVER_OPERATOR))
1930         printformat_module("fe-common/silc", server, NULL,
1931                            MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1932
1933       if (mode & SILC_UMODE_ROUTER_OPERATOR &&
1934           !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
1935         printformat_module("fe-common/silc", server, NULL,
1936                            MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1937
1938       if ((mode & SILC_UMODE_GONE) != (server->umode & SILC_UMODE_GONE)) {
1939         if (mode & SILC_UMODE_GONE) {
1940           if ((server->away_reason != NULL) && (server->away_reason[0] != '\0'))
1941             reason = g_strdup(server->away_reason);
1942           else
1943             reason = g_strdup("away");
1944         } else
1945           reason = g_strdup("");
1946
1947         silc_set_away(reason, server);
1948
1949         g_free(reason);
1950       }
1951
1952       server->umode = mode;
1953       signal_emit("user mode changed", 2, server, NULL);
1954     }
1955     break;
1956
1957   case SILC_COMMAND_OPER:
1958     if (SILC_STATUS_IS_ERROR(status)) {
1959       silc_say_error("OPER: %s", silc_get_status_message(status));
1960       return;
1961     }
1962
1963     server->umode |= SILC_UMODE_SERVER_OPERATOR;
1964     signal_emit("user mode changed", 2, server, NULL);
1965
1966     printformat_module("fe-common/silc", server, NULL,
1967                        MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1968     break;
1969
1970   case SILC_COMMAND_SILCOPER:
1971     if (SILC_STATUS_IS_ERROR(status)) {
1972       silc_say_error("SILCOPER: %s", silc_get_status_message(status));
1973       return;
1974     }
1975
1976     server->umode |= SILC_UMODE_ROUTER_OPERATOR;
1977     signal_emit("user mode changed", 2, server, NULL);
1978
1979     printformat_module("fe-common/silc", server, NULL,
1980                        MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1981     break;
1982
1983   case SILC_COMMAND_USERS:
1984     {
1985       SilcHashTableList htl;
1986       SilcChannelEntry channel;
1987       SilcChannelUser chu;
1988
1989       if (SILC_STATUS_IS_ERROR(status)) {
1990         silc_say_error("USERS: %s", silc_get_status_message(status));
1991         return;
1992       }
1993
1994       channel = va_arg(vp, SilcChannelEntry);
1995
1996       printformat_module("fe-common/silc", server, channel->channel_name,
1997                          MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
1998                          channel->channel_name);
1999
2000       silc_hash_table_list(channel->user_list, &htl);
2001       while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
2002         SilcClientEntry e = chu->client;
2003         char stat[5], *mode;
2004
2005         if (!e->nickname[0])
2006           continue;
2007
2008         memset(stat, 0, sizeof(stat));
2009         mode = silc_client_chumode_char(chu->mode);
2010         if (e->mode & SILC_UMODE_GONE)
2011           strcat(stat, "G");
2012         else if (e->mode & SILC_UMODE_INDISPOSED)
2013           strcat(stat, "I");
2014         else if (e->mode & SILC_UMODE_BUSY)
2015           strcat(stat, "B");
2016         else if (e->mode & SILC_UMODE_PAGE)
2017           strcat(stat, "P");
2018         else if (e->mode & SILC_UMODE_HYPER)
2019           strcat(stat, "H");
2020         else if (e->mode & SILC_UMODE_ROBOT)
2021           strcat(stat, "R");
2022         else if (e->mode & SILC_UMODE_ANONYMOUS)
2023           strcat(stat, "?");
2024         else
2025           strcat(stat, "A");
2026         if (mode)
2027           strcat(stat, mode);
2028
2029         printformat_module("fe-common/silc", server, channel->channel_name,
2030                            MSGLEVEL_CRAP, SILCTXT_USERS,
2031                            e->nickname, stat,
2032                            e->username[0] ? e->username : "",
2033                            e->hostname[0] ? e->hostname : "",
2034                            e->realname ? e->realname : "");
2035         if (mode)
2036           silc_free(mode);
2037       }
2038       silc_hash_table_list_reset(&htl);
2039     }
2040     break;
2041
2042   case SILC_COMMAND_BAN:
2043     {
2044       SilcChannelEntry channel;
2045       SilcArgumentPayload invite_list;
2046
2047       if (SILC_STATUS_IS_ERROR(status))
2048         return;
2049
2050       channel = va_arg(vp, SilcChannelEntry);
2051       invite_list = va_arg(vp, SilcArgumentPayload);
2052
2053       if (invite_list)
2054         silc_parse_inviteban_list(client, conn, server, channel,
2055                                   "ban", invite_list);
2056     }
2057     break;
2058
2059   case SILC_COMMAND_GETKEY:
2060     {
2061       SilcIdType id_type;
2062       void *entry;
2063       SilcPublicKey public_key;
2064       GetkeyContext getkey;
2065       char *name;
2066
2067       if (SILC_STATUS_IS_ERROR(status)) {
2068         silc_say_error("GETKEY: %s", silc_get_status_message(status));
2069         return;
2070       }
2071
2072       id_type = va_arg(vp, SilcUInt32);
2073       entry = va_arg(vp, void *);
2074       public_key = va_arg(vp, SilcPublicKey);
2075
2076       if (public_key) {
2077         getkey = silc_calloc(1, sizeof(*getkey));
2078         getkey->entry = entry;
2079         getkey->id_type = id_type;
2080         getkey->client = client;
2081         getkey->conn = conn;
2082
2083         name = (id_type == SILC_ID_CLIENT ?
2084                 ((SilcClientEntry)entry)->nickname :
2085                 ((SilcServerEntry)entry)->server_name);
2086
2087         silc_verify_public_key_internal(client, conn, name,
2088                                         (id_type == SILC_ID_CLIENT ?
2089                                          SILC_CONN_CLIENT :
2090                                          SILC_CONN_SERVER),
2091                                         public_key, silc_getkey_cb, getkey);
2092       } else {
2093         printformat_module("fe-common/silc", server, NULL,
2094                            MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOKEY);
2095       }
2096     }
2097     break;
2098
2099   case SILC_COMMAND_INFO:
2100     {
2101       SilcServerEntry server_entry;
2102       char *server_name;
2103       char *server_info;
2104
2105       if (SILC_STATUS_IS_ERROR(status))
2106         return;
2107
2108       server_entry = va_arg(vp, SilcServerEntry);
2109       server_name = va_arg(vp, char *);
2110       server_info = va_arg(vp, char *);
2111
2112       if (server_name && server_info )
2113         {
2114           printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
2115           printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
2116         }
2117     }
2118     break;
2119
2120   case SILC_COMMAND_TOPIC:
2121     {
2122       SilcChannelEntry channel;
2123       char *topic;
2124       char tmp[256], *cp, *dm = NULL;
2125
2126       if (SILC_STATUS_IS_ERROR(status))
2127         return;
2128
2129       channel = va_arg(vp, SilcChannelEntry);
2130       topic = va_arg(vp, char *);
2131
2132       if (topic && !silc_term_utf8() &&
2133           silc_utf8_valid(topic, strlen(topic))) {
2134         memset(tmp, 0, sizeof(tmp));
2135         cp = tmp;
2136         if (strlen(topic) > sizeof(tmp) - 1) {
2137           dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
2138           cp = dm;
2139         }
2140
2141         silc_utf8_decode(topic, strlen(topic), SILC_STRING_LOCALE,
2142                          cp, strlen(topic));
2143         topic = cp;
2144       }
2145
2146       if (topic) {
2147         chanrec = silc_channel_find_entry(server, channel);
2148         if (chanrec) {
2149           g_free_not_null(chanrec->topic);
2150           chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
2151           signal_emit("channel topic changed", 1, chanrec);
2152         }
2153         printformat_module("fe-common/silc", server, channel->channel_name,
2154                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
2155                            channel->channel_name, topic);
2156       } else {
2157         printformat_module("fe-common/silc", server, channel->channel_name,
2158                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
2159                            channel->channel_name);
2160       }
2161       silc_free(dm);
2162     }
2163     break;
2164
2165   case SILC_COMMAND_WATCH:
2166     break;
2167
2168   case SILC_COMMAND_STATS:
2169     {
2170       SilcClientStats *cstats;
2171       char tmp[40];
2172       const char *tmptime;
2173       int days, hours, mins, secs;
2174
2175       if (SILC_STATUS_IS_ERROR(status))
2176         return;
2177
2178       cstats = va_arg(vp, SilcClientStats *);
2179       if (!cstats) {
2180         printtext(server, NULL, MSGLEVEL_CRAP, "No statistics available");
2181         return;
2182       }
2183
2184       tmptime = silc_time_string(cstats->starttime);
2185       printformat_module("fe-common/silc", server, NULL,
2186                          MSGLEVEL_CRAP, SILCTXT_STATS,
2187                          "Local server start time", tmptime);
2188
2189       days = cstats->uptime / (24 * 60 * 60);
2190       cstats->uptime -= days * (24 * 60 * 60);
2191       hours = cstats->uptime / (60 * 60);
2192       cstats->uptime -= hours * (60 * 60);
2193       mins = cstats->uptime / 60;
2194       cstats->uptime -= mins * 60;
2195       secs = cstats->uptime;
2196       snprintf(tmp, sizeof(tmp) - 1, "%d days %d hours %d mins %d secs",
2197                days, hours, mins, secs);
2198       printformat_module("fe-common/silc", server, NULL,
2199                          MSGLEVEL_CRAP, SILCTXT_STATS,
2200                          "Local server uptime", tmp);
2201
2202       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->my_clients);
2203       printformat_module("fe-common/silc", server, NULL,
2204                          MSGLEVEL_CRAP, SILCTXT_STATS,
2205                          "Local server clients", tmp);
2206
2207       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->my_channels);
2208       printformat_module("fe-common/silc", server, NULL,
2209                          MSGLEVEL_CRAP, SILCTXT_STATS,
2210                          "Local server channels", tmp);
2211
2212       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->my_server_ops);
2213       printformat_module("fe-common/silc", server, NULL,
2214                          MSGLEVEL_CRAP, SILCTXT_STATS,
2215                          "Local server operators", tmp);
2216
2217       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->my_router_ops);
2218       printformat_module("fe-common/silc", server, NULL,
2219                          MSGLEVEL_CRAP, SILCTXT_STATS,
2220                          "Local router operators", tmp);
2221
2222       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->cell_clients);
2223       printformat_module("fe-common/silc", server, NULL,
2224                          MSGLEVEL_CRAP, SILCTXT_STATS,
2225                          "Local cell clients", tmp);
2226
2227       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->cell_channels);
2228       printformat_module("fe-common/silc", server, NULL,
2229                          MSGLEVEL_CRAP, SILCTXT_STATS,
2230                          "Local cell channels", tmp);
2231
2232       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->cell_servers);
2233       printformat_module("fe-common/silc", server, NULL,
2234                          MSGLEVEL_CRAP, SILCTXT_STATS,
2235                          "Local cell servers", tmp);
2236
2237       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->clients);
2238       printformat_module("fe-common/silc", server, NULL,
2239                          MSGLEVEL_CRAP, SILCTXT_STATS,
2240                          "Total clients", tmp);
2241
2242       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->channels);
2243       printformat_module("fe-common/silc", server, NULL,
2244                          MSGLEVEL_CRAP, SILCTXT_STATS,
2245                          "Total channels", tmp);
2246
2247       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->servers);
2248       printformat_module("fe-common/silc", server, NULL,
2249                          MSGLEVEL_CRAP, SILCTXT_STATS,
2250                          "Total servers", tmp);
2251
2252       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->routers);
2253       printformat_module("fe-common/silc", server, NULL,
2254                          MSGLEVEL_CRAP, SILCTXT_STATS,
2255                          "Total routers", tmp);
2256
2257       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->server_ops);
2258       printformat_module("fe-common/silc", server, NULL,
2259                          MSGLEVEL_CRAP, SILCTXT_STATS,
2260                            "Total server operators", tmp);
2261
2262       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->router_ops);
2263       printformat_module("fe-common/silc", server, NULL,
2264                          MSGLEVEL_CRAP, SILCTXT_STATS,
2265                          "Total router operators", tmp);
2266     }
2267     break;
2268
2269   case SILC_COMMAND_CMODE:
2270     {
2271       SilcChannelEntry channel_entry;
2272       SilcDList chpks;
2273
2274       channel_entry = va_arg(vp, SilcChannelEntry);
2275       (void)va_arg(vp, SilcUInt32);
2276       (void)va_arg(vp, SilcPublicKey);
2277       chpks = va_arg(vp, SilcDList);
2278
2279       if (SILC_STATUS_IS_ERROR(status) || !cmode_list_chpks ||
2280           !channel_entry || !channel_entry->channel_name)
2281         return;
2282
2283       /* Print the channel public key list */
2284       if (chpks)
2285         silc_parse_channel_public_keys(server, channel_entry, chpks);
2286       else
2287         printformat_module("fe-common/silc", server, NULL,
2288                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_PK_NO_LIST,
2289                          channel_entry->channel_name);
2290
2291     }
2292     break;
2293
2294   case SILC_COMMAND_LEAVE:
2295     {
2296       if (SILC_STATUS_IS_ERROR(status))
2297         return;
2298
2299       /* We might be cycling, so disable queueing again */
2300       silc_queue_disable(conn);
2301     }
2302     break;
2303
2304   case SILC_COMMAND_DETACH:
2305     {
2306       /* Save the detachment data to file. */
2307       char *file;
2308       SilcBuffer detach;
2309
2310       if (SILC_STATUS_IS_ERROR(status))
2311         return;
2312
2313       detach = va_arg(vp, SilcBuffer);
2314       file = silc_get_session_filename(server);
2315       silc_file_writefile(file, silc_buffer_data(detach),
2316                           silc_buffer_len(detach));
2317       silc_free(file);
2318     }
2319     break;
2320
2321   case SILC_COMMAND_KILL:
2322     {
2323       SilcClientEntry client_entry;
2324
2325       if (SILC_STATUS_IS_ERROR(status)) {
2326         silc_say_error("KILL: %s", silc_get_status_message(status));
2327         return;
2328       }
2329
2330       client_entry = va_arg(vp, SilcClientEntry);
2331       if (!client_entry || !client_entry->nickname[0])
2332         break;
2333
2334       /* Print this only if the killed client isn't joined on channels.
2335          If it is, we receive KILLED notify and we'll print this there. */
2336       if (!silc_hash_table_count(client_entry->channels))
2337         printformat_module("fe-common/silc", server, NULL,
2338                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
2339                            client_entry->nickname,
2340                            conn->local_entry->nickname, "");
2341     }
2342   }
2343 }
2344
2345 typedef struct {
2346   SilcClient client;
2347   SilcClientConnection conn;
2348   char *filename;
2349   char *entity;
2350   char *entity_name;
2351   SilcPublicKey public_key;
2352   SilcVerifyPublicKey completion;
2353   void *context;
2354 } *PublicKeyVerify;
2355
2356 static void verify_public_key_completion(const char *line, void *context)
2357 {
2358   PublicKeyVerify verify = (PublicKeyVerify)context;
2359
2360   if (line[0] == 'Y' || line[0] == 'y') {
2361     /* Call the completion */
2362     if (verify->completion)
2363       verify->completion(TRUE, verify->context);
2364
2365     /* Save the key for future checking */
2366     silc_pkcs_save_public_key(verify->filename, verify->public_key,
2367                               SILC_PKCS_FILE_BASE64);
2368   } else {
2369     /* Call the completion */
2370     if (verify->completion)
2371       verify->completion(FALSE, verify->context);
2372
2373     printformat_module("fe-common/silc", NULL, NULL,
2374                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD,
2375                        verify->entity_name ? verify->entity_name :
2376                        verify->entity);
2377   }
2378
2379   silc_free(verify->filename);
2380   silc_free(verify->entity);
2381   silc_free(verify->entity_name);
2382   silc_free(verify);
2383 }
2384
2385 /* Internal routine to verify public key. If the `completion' is provided
2386    it will be called to indicate whether public was verified or not. For
2387    server/router public key this will check for filename that includes the
2388    remote host's IP address and remote host's hostname. */
2389
2390 static void
2391 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
2392                                 const char *name,
2393                                 SilcConnectionType conn_type,
2394                                 SilcPublicKey public_key,
2395                                 SilcVerifyPublicKey completion, void *context)
2396 {
2397   PublicKeyVerify verify;
2398   char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
2399   char *fingerprint, *babbleprint, *format;
2400   SilcPublicKey local_pubkey;
2401   SilcSILCPublicKey silc_pubkey;
2402   SilcUInt16 port;
2403   const char *hostname, *ip;
2404   unsigned char *pk;
2405   SilcUInt32 pk_len;
2406   struct passwd *pw;
2407   struct stat st;
2408   char *entity = ((conn_type == SILC_CONN_SERVER ||
2409                    conn_type == SILC_CONN_ROUTER) ?
2410                   "server" : "client");
2411   int i;
2412
2413   if (silc_pkcs_get_type(public_key) != SILC_PKCS_SILC) {
2414     printformat_module("fe-common/silc", NULL, NULL,
2415                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
2416                        entity, silc_pkcs_get_type(public_key));
2417     if (completion)
2418       completion(FALSE, context);
2419     return;
2420   }
2421
2422   /* Encode public key */
2423   pk = silc_pkcs_public_key_encode(public_key, &pk_len);
2424   if (!pk) {
2425     if (completion)
2426       completion(FALSE, context);
2427     return;
2428   }
2429
2430   silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
2431
2432   pw = getpwuid(getuid());
2433   if (!pw) {
2434     if (completion)
2435       completion(FALSE, context);
2436     silc_free(pk);
2437     return;
2438   }
2439
2440   memset(filename, 0, sizeof(filename));
2441   memset(filename2, 0, sizeof(filename2));
2442   memset(file, 0, sizeof(file));
2443
2444   /* Get remote host information */
2445   silc_socket_stream_get_info(silc_packet_stream_get_stream(conn->stream),
2446                               NULL, &hostname, &ip, &port);
2447
2448   if (conn_type == SILC_CONN_SERVER ||
2449       conn_type == SILC_CONN_ROUTER) {
2450     if (!name) {
2451       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, ip, port);
2452       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2453                get_irssi_dir(), entity, file);
2454
2455       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2456                hostname, port);
2457       snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s",
2458                get_irssi_dir(), entity, file);
2459
2460       ipf = filename;
2461       hostf = filename2;
2462     } else {
2463       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2464                name, port);
2465       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2466                get_irssi_dir(), entity, file);
2467
2468       ipf = filename;
2469     }
2470   } else {
2471     /* Replace all whitespaces with `_'. */
2472     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2473     for (i = 0; i < strlen(fingerprint); i++)
2474       if (fingerprint[i] == ' ')
2475         fingerprint[i] = '_';
2476
2477     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
2478     snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2479              get_irssi_dir(), entity, file);
2480     silc_free(fingerprint);
2481
2482     ipf = filename;
2483   }
2484
2485   /* Take fingerprint of the public key */
2486   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2487   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
2488
2489   verify = silc_calloc(1, sizeof(*verify));
2490   verify->client = client;
2491   verify->conn = conn;
2492   verify->filename = strdup(ipf);
2493   verify->entity = strdup(entity);
2494   verify->entity_name = (conn_type != SILC_CONN_CLIENT ?
2495                          (name ? strdup(name) : strdup(hostname))
2496                          : NULL);
2497   verify->public_key = public_key;
2498   verify->completion = completion;
2499   verify->context = context;
2500
2501   /* Check whether this key already exists */
2502   if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
2503     /* Key does not exist, ask user to verify the key and save it */
2504
2505     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2506                        SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2507                        verify->entity_name : entity);
2508     if (conn_type == SILC_CONN_CLIENT && name &&
2509         silc_pubkey->identifier.realname)
2510       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2511                          SILCTXT_PUBKEY_RECEIVED_CLIENT, name,
2512                          silc_pubkey->identifier.realname,
2513                          silc_pubkey->identifier.email ?
2514                          silc_pubkey->identifier.email : "");
2515     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2516                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2517     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2518                        SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2519     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2520                              SILCTXT_PUBKEY_ACCEPT);
2521     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2522                             format, 0, verify);
2523     g_free(format);
2524     silc_free(fingerprint);
2525     silc_free(babbleprint);
2526     silc_free(pk);
2527     return;
2528   } else {
2529     /* The key already exists, verify it. */
2530     unsigned char *encpk;
2531     SilcUInt32 encpk_len;
2532
2533     /* Load the key file, try for both IP filename and hostname filename */
2534     if (!silc_pkcs_load_public_key(ipf, &local_pubkey) &&
2535         (!hostf || (!silc_pkcs_load_public_key(hostf, &local_pubkey)))) {
2536       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2537                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2538                          verify->entity_name : entity);
2539       if (conn_type == SILC_CONN_CLIENT && name &&
2540           silc_pubkey->identifier.realname)
2541         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2542                            SILCTXT_PUBKEY_RECEIVED_CLIENT, name,
2543                            silc_pubkey->identifier.realname,
2544                            silc_pubkey->identifier.email ?
2545                            silc_pubkey->identifier.email : "");
2546       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2547                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2548       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2549                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2550       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2551                          SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
2552       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2553                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2554       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2555                               format, 0, verify);
2556       g_free(format);
2557       silc_free(fingerprint);
2558       silc_free(babbleprint);
2559       silc_free(pk);
2560       return;
2561     }
2562
2563     /* Encode the key data */
2564     encpk = silc_pkcs_public_key_encode(local_pubkey, &encpk_len);
2565     if (!encpk) {
2566       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2567                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2568                          verify->entity_name : entity);
2569       if (conn_type == SILC_CONN_CLIENT && name &&
2570           silc_pubkey->identifier.realname)
2571         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2572                            SILCTXT_PUBKEY_RECEIVED_CLIENT, name,
2573                            silc_pubkey->identifier.realname,
2574                            silc_pubkey->identifier.email ?
2575                            silc_pubkey->identifier.email : "");
2576       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2577                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2578       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2579                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2580       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2581                          SILCTXT_PUBKEY_MALFORMED, entity);
2582       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2583                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2584       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2585                               format, 0, verify);
2586       g_free(format);
2587       silc_free(fingerprint);
2588       silc_free(babbleprint);
2589       silc_free(pk);
2590       return;
2591     }
2592     silc_pkcs_public_key_free(local_pubkey);
2593
2594     /* Compare the keys */
2595     if (memcmp(encpk, pk, encpk_len)) {
2596       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2597                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2598                          verify->entity_name : entity);
2599       if (conn_type == SILC_CONN_CLIENT && name &&
2600           silc_pubkey->identifier.realname)
2601         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2602                            SILCTXT_PUBKEY_RECEIVED_CLIENT, name,
2603                            silc_pubkey->identifier.realname,
2604                            silc_pubkey->identifier.email ?
2605                            silc_pubkey->identifier.email : "");
2606       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2607                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2608       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2609                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2610       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2611                          SILCTXT_PUBKEY_NO_MATCH, entity);
2612       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2613                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
2614       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2615                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
2616
2617       /* Ask user to verify the key and save it */
2618       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2619                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2620       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2621                               format, 0, verify);
2622       g_free(format);
2623       silc_free(fingerprint);
2624       silc_free(babbleprint);
2625       silc_free(encpk);
2626       silc_free(pk);
2627       return;
2628     }
2629
2630     /* Local copy matched */
2631     if (completion)
2632       completion(TRUE, context);
2633     silc_free(encpk);
2634     silc_free(fingerprint);
2635     silc_free(babbleprint);
2636     silc_free(verify->filename);
2637     silc_free(verify->entity);
2638     silc_free(verify->entity_name);
2639     silc_free(verify);
2640     silc_free(pk);
2641   }
2642 }
2643
2644 /* Verifies received public key. The `conn_type' indicates which entity
2645    (server, client etc.) has sent the public key. If user decides to trust
2646    the key may be saved as trusted public key for later use. The
2647    `completion' must be called after the public key has been verified. */
2648
2649 void
2650 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
2651                        SilcConnectionType conn_type,
2652                        SilcPublicKey public_key,
2653                        SilcVerifyPublicKey completion, void *context)
2654 {
2655   silc_verify_public_key_internal(client, conn, NULL, conn_type, public_key,
2656                                   completion, context);
2657 }
2658
2659 /* Asks passphrase from user on the input line. */
2660
2661 typedef struct {
2662   SilcAskPassphrase completion;
2663   void *context;
2664 } *AskPassphrase;
2665
2666 void ask_passphrase_completion(const char *passphrase, void *context)
2667 {
2668   AskPassphrase p = (AskPassphrase)context;
2669   if (passphrase && passphrase[0] == '\0')
2670     passphrase = NULL;
2671   p->completion((unsigned char *)passphrase,
2672                 passphrase ? strlen(passphrase) : 0, p->context);
2673   silc_free(p);
2674 }
2675
2676 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
2677                          SilcAskPassphrase completion, void *context)
2678 {
2679   AskPassphrase p = silc_calloc(1, sizeof(*p));
2680   p->completion = completion;
2681   p->context = context;
2682
2683   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
2684                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
2685 }
2686
2687 typedef struct {
2688   SilcGetAuthMeth completion;
2689   void *context;
2690 } *GetAuthMethod;
2691
2692 static void silc_get_auth_ask_passphrase(const unsigned char *passphrase,
2693                                          SilcUInt32 passphrase_len,
2694                                          void *context)
2695 {
2696   GetAuthMethod a = context;
2697   a->completion(passphrase ? SILC_AUTH_PASSWORD : SILC_AUTH_NONE,
2698                 passphrase, passphrase_len, a->context);
2699   silc_free(a);
2700 }
2701
2702 /* Find authentication data by hostname and port. The hostname may be IP
2703    address as well.*/
2704
2705 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
2706                           char *hostname, SilcUInt16 port,
2707                           SilcAuthMethod auth_meth,
2708                           SilcGetAuthMeth completion, void *context)
2709 {
2710   SERVER_SETUP_REC *setup;
2711
2712   SILC_LOG_DEBUG(("Start"));
2713
2714   if (auth_meth == SILC_AUTH_PUBLIC_KEY) {
2715     /* Returning NULL will cause library to use our private key configured
2716        for this connection */
2717     completion(SILC_AUTH_PUBLIC_KEY, NULL, 0, context);
2718     return;
2719   }
2720
2721   /* Check whether we find the password for this server in our
2722      configuration.  If it's set, always send it server. */
2723   setup = server_setup_find_port(hostname, port);
2724   if (setup && setup->password) {
2725     completion(SILC_AUTH_PASSWORD, setup->password, strlen(setup->password),
2726                context);
2727     return;
2728   }
2729
2730   /* Didn't find password.  If server wants it, ask it from user. */
2731   if (auth_meth == SILC_AUTH_PASSWORD) {
2732     GetAuthMethod a;
2733     a = silc_calloc(1, sizeof(*a));
2734     if (a) {
2735       a->completion = completion;
2736       a->context = context;
2737       silc_ask_passphrase(client, conn, silc_get_auth_ask_passphrase, a);
2738       return;
2739     }
2740   }
2741
2742   /* No authentication */
2743   completion(SILC_AUTH_NONE, NULL, 0, context);
2744 }
2745
2746 /* Asks whether the user would like to perform the key agreement protocol.
2747    This is called after we have received an key agreement packet or an
2748    reply to our key agreement packet. This returns TRUE if the user wants
2749    the library to perform the key agreement protocol and FALSE if it is not
2750    desired (application may start it later by calling the function
2751    silc_client_perform_key_agreement). */
2752
2753 void silc_key_agreement(SilcClient client, SilcClientConnection conn,
2754                         SilcClientEntry client_entry, const char *hostname,
2755                         SilcUInt16 protocol, SilcUInt16 port)
2756 {
2757   char portstr[12], protostr[5];
2758
2759   SILC_LOG_DEBUG(("Start"));
2760
2761   /* We will just display the info on the screen and return FALSE and user
2762      will have to start the key agreement with a command. */
2763
2764   if (hostname) {
2765     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2766     snprintf(protostr, sizeof(protostr) - 1, "%s", protocol == 1 ? "UDP" :
2767              "TCP");
2768   }
2769
2770   if (!hostname)
2771     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2772                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
2773   else
2774     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2775                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
2776                        client_entry->nickname, hostname, portstr, protostr);
2777 }
2778
2779 /* Notifies application that file transfer protocol session is being
2780    requested by the remote client indicated by the `client_entry' from
2781    the `hostname' and `port'. The `session_id' is the file transfer
2782    session and it can be used to either accept or reject the file
2783    transfer request, by calling the silc_client_file_receive or
2784    silc_client_file_close, respectively. */
2785
2786 void silc_ftp(SilcClient client, SilcClientConnection conn,
2787               SilcClientEntry client_entry, SilcUInt32 session_id,
2788               const char *hostname, SilcUInt16 port)
2789 {
2790   SILC_SERVER_REC *server;
2791   char portstr[12];
2792   FtpSession ftp = NULL;
2793
2794   SILC_LOG_DEBUG(("Start"));
2795
2796   server = conn->context;
2797
2798   silc_dlist_start(server->ftp_sessions);
2799   while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
2800     if (ftp->client_entry == client_entry &&
2801         ftp->session_id == session_id) {
2802       server->current_session = ftp;
2803       break;
2804     }
2805   }
2806   if (ftp == SILC_LIST_END) {
2807     ftp = silc_calloc(1, sizeof(*ftp));
2808     ftp->client_entry = client_entry;
2809     ftp->session_id = session_id;
2810     ftp->send = FALSE;
2811     ftp->conn = conn;
2812     silc_dlist_add(server->ftp_sessions, ftp);
2813     server->current_session = ftp;
2814   }
2815
2816   if (hostname)
2817     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2818
2819   if (!hostname)
2820     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2821                        SILCTXT_FILE_REQUEST, client_entry->nickname);
2822   else
2823     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2824                        SILCTXT_FILE_REQUEST_HOST,
2825                        client_entry->nickname, hostname, portstr);
2826 }
2827
2828 /* SILC client operations */
2829 SilcClientOperations ops = {
2830   silc_say,
2831   silc_channel_message,
2832   silc_private_message,
2833   silc_notify,
2834   silc_command,
2835   silc_command_reply,
2836   silc_get_auth_method,
2837   silc_verify_public_key,
2838   silc_ask_passphrase,
2839   silc_key_agreement,
2840   silc_ftp,
2841 };