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