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