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