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