Added settings sign_channel_messages, sign_private_messages and
[silc.git] / apps / irssi / src / silc / core / client_ops.c
1 /*
2
3   client_ops.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2001 - 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       int i;
1154       SilcDList clients;
1155
1156       SILC_LOG_DEBUG(("Notify: SERVER_SIGNOFF"));
1157
1158       (void)va_arg(va, void *);
1159       clients = va_arg(va, SilcDList);
1160
1161       silc_dlist_start(clients);
1162       while ((client_entry = silc_dlist_get(clients))) {
1163         memset(buf, 0, sizeof(buf));
1164
1165         /* Print only if we have the nickname.  If this client has just quit
1166            when we were only resolving it, it is possible we don't have the
1167            nickname. */
1168         if (client_entry->nickname[0]) {
1169           if (client_entry->username[0])
1170             snprintf(buf, sizeof(buf) - 1, "%s@%s",
1171                      client_entry->username, client_entry->hostname);
1172           signal_emit("message quit", 4, server, client_entry->nickname,
1173                       client_entry->username[0] ? buf : "",
1174                       "server signoff");
1175         }
1176
1177 #if 0
1178         silc_server_free_ftp(server, client_entry);
1179 #endif
1180
1181         list1 = nicklist_get_same_unique(SERVER(server), client_entry);
1182         for (list_tmp = list1; list_tmp != NULL; list_tmp =
1183                list_tmp->next->next) {
1184           CHANNEL_REC *channel = list_tmp->data;
1185           NICK_REC *nickrec = list_tmp->next->data;
1186           nicklist_remove(channel, nickrec);
1187         }
1188       }
1189     }
1190     break;
1191
1192   case SILC_NOTIFY_TYPE_ERROR:
1193     {
1194       SilcStatus error = va_arg(va, int);
1195
1196       silc_say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
1197                 "%s", silc_get_status_message(error));
1198     }
1199     break;
1200
1201   case SILC_NOTIFY_TYPE_WATCH:
1202     {
1203       SilcNotifyType notify;
1204
1205       client_entry = va_arg(va, SilcClientEntry);
1206       name = va_arg(va, char *);          /* Maybe NULL */
1207       mode = va_arg(va, SilcUInt32);
1208       notify = va_arg(va, int);
1209
1210       if (notify == SILC_NOTIFY_TYPE_NICK_CHANGE) {
1211         if (name)
1212           printformat_module("fe-common/silc", server, NULL,
1213                              MSGLEVEL_CRAP, SILCTXT_WATCH_NICK_CHANGE,
1214                              client_entry->nickname, name);
1215         else
1216           printformat_module("fe-common/silc", server, NULL,
1217                              MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
1218                              client_entry->nickname);
1219       } else if (notify == SILC_NOTIFY_TYPE_UMODE_CHANGE) {
1220         /* See if client was away and is now present */
1221         if (!(mode & (SILC_UMODE_GONE | SILC_UMODE_INDISPOSED |
1222                       SILC_UMODE_BUSY | SILC_UMODE_PAGE |
1223                       SILC_UMODE_DETACHED)) &&
1224             (client_entry->mode & SILC_UMODE_GONE ||
1225              client_entry->mode & SILC_UMODE_INDISPOSED ||
1226              client_entry->mode & SILC_UMODE_BUSY ||
1227              client_entry->mode & SILC_UMODE_PAGE ||
1228              client_entry->mode & SILC_UMODE_DETACHED)) {
1229           printformat_module("fe-common/silc", server, NULL,
1230                              MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
1231                              client_entry->nickname);
1232         }
1233
1234         if (mode) {
1235           memset(buf, 0, sizeof(buf));
1236           silc_get_umode_string(mode, buf, sizeof(buf) - 1);
1237           printformat_module("fe-common/silc", server, NULL,
1238                              MSGLEVEL_CRAP, SILCTXT_WATCH_UMODE_CHANGE,
1239                              client_entry->nickname, buf);
1240         }
1241       } else if (notify == SILC_NOTIFY_TYPE_KILLED) {
1242         printformat_module("fe-common/silc", server, NULL,
1243                            MSGLEVEL_CRAP, SILCTXT_WATCH_KILLED,
1244                            client_entry->nickname);
1245       } else if (notify == SILC_NOTIFY_TYPE_SIGNOFF ||
1246                  notify == SILC_NOTIFY_TYPE_SERVER_SIGNOFF) {
1247         printformat_module("fe-common/silc", server, NULL,
1248                            MSGLEVEL_CRAP, SILCTXT_WATCH_SIGNOFF,
1249                            client_entry->nickname);
1250       } else if (notify == SILC_NOTIFY_TYPE_NONE) {
1251         /* Client logged in to the network */
1252         printformat_module("fe-common/silc", server, NULL,
1253                            MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
1254                            client_entry->nickname);
1255       }
1256     }
1257     break;
1258
1259   default:
1260     /* Unknown notify */
1261     printformat_module("fe-common/silc", server, NULL,
1262                        MSGLEVEL_CRAP, SILCTXT_UNKNOWN_NOTIFY, type);
1263     break;
1264   }
1265
1266   va_end(va);
1267 }
1268
1269 /* Command handler. This function is called always in the command function.
1270    If error occurs it will be called as well. `conn' is the associated
1271    client connection. `cmd_context' is the command context that was
1272    originally sent to the command. `success' is FALSE if error occured
1273    during command. `command' is the command being processed. It must be
1274    noted that this is not reply from server. This is merely called just
1275    after application has called the command. Just to tell application
1276    that the command really was processed. */
1277
1278 static bool cmode_list_chpks = FALSE;
1279
1280 void silc_command(SilcClient client, SilcClientConnection conn,
1281                   SilcBool success, SilcCommand command, SilcStatus status,
1282                   SilcUInt32 argc, unsigned char **argv)
1283 {
1284   SILC_SERVER_REC *server = conn->context;
1285
1286   SILC_LOG_DEBUG(("Start"));
1287
1288   if (!success) {
1289     silc_say_error("%s", silc_get_status_message(status));
1290     return;
1291   }
1292
1293   switch (command) {
1294
1295   case SILC_COMMAND_INVITE:
1296     if (argc > 2)
1297       printformat_module("fe-common/silc", server, NULL,
1298                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING,
1299                          argv[2],
1300                          (argv[1][0] == '*' ?
1301                           (char *)conn->current_channel->channel_name :
1302                           (char *)argv[1]));
1303     break;
1304
1305   case SILC_COMMAND_DETACH:
1306     server->no_reconnect = TRUE;
1307     break;
1308
1309   case SILC_COMMAND_CMODE:
1310     if (argc == 3 && !strcmp(argv[2], "+C"))
1311       cmode_list_chpks = TRUE;
1312     else
1313       cmode_list_chpks = FALSE;
1314     break;
1315
1316   default:
1317     break;
1318   }
1319 }
1320
1321 typedef struct {
1322   SilcClient client;
1323   SilcClientConnection conn;
1324   void *entry;
1325   SilcIdType id_type;
1326 } *GetkeyContext;
1327
1328 void silc_getkey_cb(bool success, void *context)
1329 {
1330   GetkeyContext getkey = (GetkeyContext)context;
1331   char *entity = (getkey->id_type == SILC_ID_CLIENT ? "user" : "server");
1332   char *name = (getkey->id_type == SILC_ID_CLIENT ?
1333                 ((SilcClientEntry)getkey->entry)->nickname :
1334                 ((SilcServerEntry)getkey->entry)->server_name);
1335
1336   if (success) {
1337     printformat_module("fe-common/silc", NULL, NULL,
1338                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_VERIFIED, entity, name);
1339   } else {
1340     printformat_module("fe-common/silc", NULL, NULL,
1341                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOTVERIFIED,
1342                        entity, name);
1343   }
1344
1345   silc_free(getkey);
1346 }
1347
1348 /* Parse an invite or ban list */
1349 void silc_parse_inviteban_list(SilcClient client,
1350                                SilcClientConnection conn,
1351                                SILC_SERVER_REC *server,
1352                                SilcChannelEntry channel,
1353                                const char *list_type,
1354                                SilcArgumentPayload list)
1355 {
1356   unsigned char *tmp;
1357   SilcUInt32 type, len;
1358   SILC_CHANNEL_REC *chanrec = silc_channel_find_entry(server, channel);
1359   int counter=0, resolving = FALSE;
1360
1361   if (!silc_argument_get_arg_num(list)) {
1362     printformat_module("fe-common/silc", server,
1363                        (chanrec ? chanrec->visible_name : NULL),
1364                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_NO_INVITEBAN_LIST,
1365                        channel->channel_name, list_type);
1366     return;
1367   }
1368
1369   printformat_module("fe-common/silc", server,
1370                      (chanrec ? chanrec->visible_name : NULL),
1371                      MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_LIST,
1372                      channel->channel_name, list_type);
1373
1374   /* Parse the list */
1375   tmp = silc_argument_get_first_arg(list, &type, &len);
1376   while (tmp) {
1377     switch (type) {
1378       case 1:
1379         {
1380           /* An invite string */
1381           char **list;
1382           int i=0;
1383
1384           if (tmp[len-1] == ',')
1385             tmp[len-1] = '\0';
1386
1387           list = g_strsplit(tmp, ",", -1);
1388           while (list[i])
1389             printformat_module("fe-common/silc", server,
1390                                (chanrec ? chanrec->visible_name : NULL),
1391                                MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_STRING,
1392                                ++counter, channel->channel_name, list_type,
1393                                list[i++]);
1394           g_strfreev(list);
1395         }
1396         break;
1397
1398       case 2:
1399         {
1400           /* A public key */
1401           char *fingerprint, *babbleprint;
1402
1403           /* tmp is Public Key Payload, take public key from it. */
1404           fingerprint = silc_hash_fingerprint(NULL, tmp + 4, len - 4);
1405           babbleprint = silc_hash_babbleprint(NULL, tmp + 4, len - 4);
1406
1407           printformat_module("fe-common/silc", server,
1408                              (chanrec ? chanrec->visible_name : NULL),
1409                              MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_PUBKEY,
1410                              ++counter, channel->channel_name, list_type,
1411                              fingerprint, babbleprint);
1412         }
1413         break;
1414
1415       case 3:
1416         {
1417           /* A Client ID */
1418           SilcClientEntry client_entry;
1419           SilcID id;
1420
1421           if (!silc_id_payload_parse_id(tmp, len, &id)) {
1422             silc_say_error("Invalid data in %s list encountered", list_type);
1423             break;
1424           }
1425
1426           client_entry = silc_client_get_client_by_id(client, conn,
1427                                                       &id.u.client_id);
1428           if (client_entry) {
1429             printformat_module("fe-common/silc", server,
1430                                (chanrec ? chanrec->visible_name : NULL),
1431                                MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_STRING,
1432                                ++counter, channel->channel_name, list_type,
1433                                client_entry->nickname);
1434             silc_client_unref_client(client, conn, client_entry);
1435           } else {
1436             resolving = TRUE;
1437             silc_client_get_client_by_id_resolve(client, conn, &id.u.client_id,
1438                                                  NULL, NULL, NULL);
1439           }
1440         }
1441         break;
1442
1443       default:
1444         /* "trash" */
1445         silc_say_error("Unkown type in %s list: %u (len %u)",
1446                        list_type, type, len);
1447         break;
1448     }
1449     tmp = silc_argument_get_next_arg(list, &type, &len);
1450   }
1451
1452   if (resolving)
1453     printformat_module("fe-common/silc", server,
1454                        (chanrec ? chanrec->visible_name : NULL),
1455                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_REGET,
1456                        list_type, channel->channel_name);
1457 }
1458
1459 /* Command reply handler. This function is called always in the command reply
1460    function. If error occurs it will be called as well. Normal scenario
1461    is that it will be called after the received command data has been parsed
1462    and processed. The function is used to pass the received command data to
1463    the application.
1464
1465    `conn' is the associated client connection. `cmd_payload' is the command
1466    payload data received from server and it can be ignored. It is provided
1467    if the application would like to re-parse the received command data,
1468    however, it must be noted that the data is parsed already by the library
1469    thus the payload can be ignored. `success' is FALSE if error occured.
1470    In this case arguments are not sent to the application. `command' is the
1471    command reply being processed. The function has variable argument list
1472    and each command defines the number and type of arguments it passes to the
1473    application (on error they are not sent). */
1474
1475 void silc_command_reply(SilcClient client, SilcClientConnection conn,
1476                         SilcCommand command, SilcStatus status,
1477                         SilcStatus error, va_list vp)
1478 {
1479   SILC_SERVER_REC *server = conn->context;
1480   SILC_CHANNEL_REC *chanrec;
1481
1482   SILC_LOG_DEBUG(("Start"));
1483
1484   switch(command) {
1485   case SILC_COMMAND_WHOIS:
1486     {
1487       char buf[1024], *nickname, *username, *realname, nick[128 + 1];
1488       unsigned char *fingerprint;
1489       SilcUInt32 idle, mode, *user_modes;
1490       SilcDList channels;
1491       SilcClientEntry client_entry;
1492       SilcDList attrs;
1493
1494       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1495         /* Print the unknown nick for user */
1496         char *tmp = va_arg(vp, char *);
1497         if (tmp)
1498           silc_say_error("%s: %s", tmp, silc_get_status_message(status));
1499         break;
1500       } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1501         /* Try to find the entry for the unknown client ID, since we
1502            might have, and print the nickname of it for user. */
1503         SilcClientID *id = va_arg(vp, SilcClientID *);
1504         if (id) {
1505           client_entry = silc_client_get_client_by_id(client, conn, id);
1506           if (client_entry && client_entry->nickname[0])
1507             silc_say_error("%s: %s", client_entry->nickname,
1508                            silc_get_status_message(status));
1509           silc_client_unref_client(client, conn, client_entry);
1510         }
1511         break;
1512       } else if (SILC_STATUS_IS_ERROR(status)) {
1513         silc_say_error("WHOIS: %s", silc_get_status_message(status));
1514         return;
1515       }
1516
1517       client_entry = va_arg(vp, SilcClientEntry);
1518       nickname = va_arg(vp, char *);
1519       username = va_arg(vp, char *);
1520       realname = va_arg(vp, char *);
1521       channels = va_arg(vp, SilcDList);
1522       mode = va_arg(vp, SilcUInt32);
1523       idle = va_arg(vp, SilcUInt32);
1524       fingerprint = va_arg(vp, unsigned char *);
1525       user_modes = va_arg(vp, SilcUInt32 *);
1526       attrs = va_arg(vp, SilcDList);
1527
1528       silc_parse_userfqdn(nickname, nick, sizeof(nick), NULL, 0);
1529       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1530                          SILCTXT_WHOIS_USERINFO, nickname,
1531                          client_entry->username, client_entry->hostname,
1532                          nick, client_entry->nickname);
1533       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1534                          SILCTXT_WHOIS_REALNAME, realname);
1535
1536       if (channels && user_modes) {
1537         SilcChannelPayload entry;
1538         int i = 0;
1539
1540         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       return;
1861
1862     server->umode |= SILC_UMODE_SERVER_OPERATOR;
1863     signal_emit("user mode changed", 2, server, NULL);
1864
1865     printformat_module("fe-common/silc", server, NULL,
1866                        MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1867     break;
1868
1869   case SILC_COMMAND_SILCOPER:
1870     if (SILC_STATUS_IS_ERROR(status))
1871       return;
1872
1873     server->umode |= SILC_UMODE_ROUTER_OPERATOR;
1874     signal_emit("user mode changed", 2, server, NULL);
1875
1876     printformat_module("fe-common/silc", server, NULL,
1877                        MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1878     break;
1879
1880   case SILC_COMMAND_USERS:
1881     {
1882       SilcHashTableList htl;
1883       SilcChannelEntry channel;
1884       SilcChannelUser chu;
1885
1886       if (SILC_STATUS_IS_ERROR(status)) {
1887         silc_say_error("USERS: %s", silc_get_status_message(status));
1888         return;
1889       }
1890
1891       channel = va_arg(vp, SilcChannelEntry);
1892
1893       printformat_module("fe-common/silc", server, channel->channel_name,
1894                          MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
1895                          channel->channel_name);
1896
1897       silc_hash_table_list(channel->user_list, &htl);
1898       while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1899         SilcClientEntry e = chu->client;
1900         char stat[5], *mode;
1901
1902         if (!e->nickname)
1903           continue;
1904
1905         memset(stat, 0, sizeof(stat));
1906         mode = silc_client_chumode_char(chu->mode);
1907         if (e->mode & SILC_UMODE_GONE)
1908           strcat(stat, "G");
1909         else if (e->mode & SILC_UMODE_INDISPOSED)
1910           strcat(stat, "I");
1911         else if (e->mode & SILC_UMODE_BUSY)
1912           strcat(stat, "B");
1913         else if (e->mode & SILC_UMODE_PAGE)
1914           strcat(stat, "P");
1915         else if (e->mode & SILC_UMODE_HYPER)
1916           strcat(stat, "H");
1917         else if (e->mode & SILC_UMODE_ROBOT)
1918           strcat(stat, "R");
1919         else if (e->mode & SILC_UMODE_ANONYMOUS)
1920           strcat(stat, "?");
1921         else
1922           strcat(stat, "A");
1923         if (mode)
1924           strcat(stat, mode);
1925
1926         printformat_module("fe-common/silc", server, channel->channel_name,
1927                            MSGLEVEL_CRAP, SILCTXT_USERS,
1928                            e->nickname, stat,
1929                            e->username ? e->username : "",
1930                            e->hostname ? e->hostname : "",
1931                            e->realname ? e->realname : "");
1932         if (mode)
1933           silc_free(mode);
1934       }
1935       silc_hash_table_list_reset(&htl);
1936     }
1937     break;
1938
1939   case SILC_COMMAND_BAN:
1940     {
1941       SilcChannelEntry channel;
1942       SilcArgumentPayload invite_list;
1943
1944       if (SILC_STATUS_IS_ERROR(status))
1945         return;
1946
1947       channel = va_arg(vp, SilcChannelEntry);
1948       invite_list = va_arg(vp, SilcArgumentPayload);
1949
1950       if (invite_list)
1951         silc_parse_inviteban_list(client, conn, server, channel,
1952                                   "ban", invite_list);
1953     }
1954     break;
1955
1956   case SILC_COMMAND_GETKEY:
1957     {
1958       SilcIdType id_type;
1959       void *entry;
1960       SilcPublicKey public_key;
1961       GetkeyContext getkey;
1962       char *name;
1963
1964       if (SILC_STATUS_IS_ERROR(status)) {
1965         silc_say_error("GETKEY: %s", silc_get_status_message(status));
1966         return;
1967       }
1968
1969       id_type = va_arg(vp, SilcUInt32);
1970       entry = va_arg(vp, void *);
1971       public_key = va_arg(vp, SilcPublicKey);
1972
1973       if (public_key) {
1974         getkey = silc_calloc(1, sizeof(*getkey));
1975         getkey->entry = entry;
1976         getkey->id_type = id_type;
1977         getkey->client = client;
1978         getkey->conn = conn;
1979
1980         name = (id_type == SILC_ID_CLIENT ?
1981                 ((SilcClientEntry)entry)->nickname :
1982                 ((SilcServerEntry)entry)->server_name);
1983
1984         silc_verify_public_key_internal(client, conn, name,
1985                                         (id_type == SILC_ID_CLIENT ?
1986                                          SILC_CONN_CLIENT :
1987                                          SILC_CONN_SERVER),
1988                                         public_key, silc_getkey_cb, getkey);
1989       } else {
1990         printformat_module("fe-common/silc", server, NULL,
1991                            MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOKEY);
1992       }
1993     }
1994     break;
1995
1996   case SILC_COMMAND_INFO:
1997     {
1998       SilcServerEntry server_entry;
1999       char *server_name;
2000       char *server_info;
2001
2002       if (SILC_STATUS_IS_ERROR(status))
2003         return;
2004
2005       server_entry = va_arg(vp, SilcServerEntry);
2006       server_name = va_arg(vp, char *);
2007       server_info = va_arg(vp, char *);
2008
2009       if (server_name && server_info )
2010         {
2011           printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
2012           printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
2013         }
2014     }
2015     break;
2016
2017   case SILC_COMMAND_TOPIC:
2018     {
2019       SilcChannelEntry channel;
2020       char *topic;
2021       char tmp[256], *cp, *dm = NULL;
2022
2023       if (SILC_STATUS_IS_ERROR(status))
2024         return;
2025
2026       channel = va_arg(vp, SilcChannelEntry);
2027       topic = va_arg(vp, char *);
2028
2029       if (topic && !silc_term_utf8() &&
2030           silc_utf8_valid(topic, strlen(topic))) {
2031         memset(tmp, 0, sizeof(tmp));
2032         cp = tmp;
2033         if (strlen(topic) > sizeof(tmp) - 1) {
2034           dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
2035           cp = dm;
2036         }
2037
2038         silc_utf8_decode(topic, strlen(topic), SILC_STRING_LOCALE,
2039                          cp, strlen(topic));
2040         topic = cp;
2041       }
2042
2043       if (topic) {
2044         chanrec = silc_channel_find_entry(server, channel);
2045         if (chanrec) {
2046           g_free_not_null(chanrec->topic);
2047           chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
2048           signal_emit("channel topic changed", 1, chanrec);
2049         }
2050         printformat_module("fe-common/silc", server, channel->channel_name,
2051                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
2052                            channel->channel_name, topic);
2053       } else {
2054         printformat_module("fe-common/silc", server, channel->channel_name,
2055                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
2056                            channel->channel_name);
2057       }
2058       silc_free(dm);
2059     }
2060     break;
2061
2062   case SILC_COMMAND_WATCH:
2063     break;
2064
2065   case SILC_COMMAND_STATS:
2066     {
2067       SilcClientStats *cstats;
2068       SilcUInt32 buf_len;
2069       SilcBufferStruct buf;
2070       unsigned char *tmp_buf;
2071       char tmp[40];
2072       const char *tmptime;
2073       int days, hours, mins, secs;
2074
2075       if (SILC_STATUS_IS_ERROR(status))
2076         return;
2077
2078       cstats = va_arg(vp, SilcClientStats *);
2079       if (!cstats) {
2080         printtext(server, NULL, MSGLEVEL_CRAP, "No statistics available");
2081         return;
2082       }
2083
2084       tmptime = silc_time_string(cstats->starttime);
2085       printformat_module("fe-common/silc", server, NULL,
2086                          MSGLEVEL_CRAP, SILCTXT_STATS,
2087                          "Local server start time", tmptime);
2088
2089       days = cstats->uptime / (24 * 60 * 60);
2090       cstats->uptime -= days * (24 * 60 * 60);
2091       hours = cstats->uptime / (60 * 60);
2092       cstats->uptime -= hours * (60 * 60);
2093       mins = cstats->uptime / 60;
2094       cstats->uptime -= mins * 60;
2095       secs = cstats->uptime;
2096       snprintf(tmp, sizeof(tmp) - 1, "%d days %d hours %d mins %d secs",
2097                days, hours, mins, secs);
2098       printformat_module("fe-common/silc", server, NULL,
2099                          MSGLEVEL_CRAP, SILCTXT_STATS,
2100                          "Local server uptime", tmp);
2101
2102       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->my_clients);
2103       printformat_module("fe-common/silc", server, NULL,
2104                          MSGLEVEL_CRAP, SILCTXT_STATS,
2105                          "Local server clients", tmp);
2106
2107       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->my_channels);
2108       printformat_module("fe-common/silc", server, NULL,
2109                          MSGLEVEL_CRAP, SILCTXT_STATS,
2110                          "Local server channels", tmp);
2111
2112       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->my_server_ops);
2113       printformat_module("fe-common/silc", server, NULL,
2114                          MSGLEVEL_CRAP, SILCTXT_STATS,
2115                          "Local server operators", tmp);
2116
2117       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->my_router_ops);
2118       printformat_module("fe-common/silc", server, NULL,
2119                          MSGLEVEL_CRAP, SILCTXT_STATS,
2120                          "Local router operators", tmp);
2121
2122       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->cell_clients);
2123       printformat_module("fe-common/silc", server, NULL,
2124                          MSGLEVEL_CRAP, SILCTXT_STATS,
2125                          "Local cell clients", tmp);
2126
2127       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->cell_channels);
2128       printformat_module("fe-common/silc", server, NULL,
2129                          MSGLEVEL_CRAP, SILCTXT_STATS,
2130                          "Local cell channels", tmp);
2131
2132       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->cell_servers);
2133       printformat_module("fe-common/silc", server, NULL,
2134                          MSGLEVEL_CRAP, SILCTXT_STATS,
2135                          "Local cell servers", tmp);
2136
2137       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->clients);
2138       printformat_module("fe-common/silc", server, NULL,
2139                          MSGLEVEL_CRAP, SILCTXT_STATS,
2140                          "Total clients", tmp);
2141
2142       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->channels);
2143       printformat_module("fe-common/silc", server, NULL,
2144                          MSGLEVEL_CRAP, SILCTXT_STATS,
2145                          "Total channels", tmp);
2146
2147       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->servers);
2148       printformat_module("fe-common/silc", server, NULL,
2149                          MSGLEVEL_CRAP, SILCTXT_STATS,
2150                          "Total servers", tmp);
2151
2152       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->routers);
2153       printformat_module("fe-common/silc", server, NULL,
2154                          MSGLEVEL_CRAP, SILCTXT_STATS,
2155                          "Total routers", tmp);
2156
2157       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->server_ops);
2158       printformat_module("fe-common/silc", server, NULL,
2159                          MSGLEVEL_CRAP, SILCTXT_STATS,
2160                            "Total server operators", tmp);
2161
2162       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->router_ops);
2163       printformat_module("fe-common/silc", server, NULL,
2164                          MSGLEVEL_CRAP, SILCTXT_STATS,
2165                          "Total router operators", tmp);
2166     }
2167     break;
2168
2169   case SILC_COMMAND_CMODE:
2170     {
2171       SilcChannelEntry channel_entry;
2172       SilcDList chpks;
2173
2174       channel_entry = va_arg(vp, SilcChannelEntry);
2175       (void)va_arg(vp, SilcUInt32);
2176       (void)va_arg(vp, SilcPublicKey);
2177       chpks = va_arg(vp, SilcDList);
2178
2179       if (SILC_STATUS_IS_ERROR(status) || !cmode_list_chpks ||
2180           !channel_entry || !channel_entry->channel_name)
2181         return;
2182
2183       /* Print the channel public key list */
2184       if (chpks)
2185         silc_parse_channel_public_keys(server, channel_entry, chpks);
2186       else
2187         printformat_module("fe-common/silc", server, NULL,
2188                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_PK_NO_LIST,
2189                          channel_entry->channel_name);
2190
2191     }
2192     break;
2193
2194   case SILC_COMMAND_LEAVE:
2195     {
2196       /* we might be cycling, so disable queueing again */
2197       silc_queue_disable(conn);
2198     }
2199     break;
2200
2201   }
2202 }
2203
2204 typedef struct {
2205   SilcClient client;
2206   SilcClientConnection conn;
2207   char *filename;
2208   char *entity;
2209   char *entity_name;
2210   SilcPublicKey public_key;
2211   SilcVerifyPublicKey completion;
2212   void *context;
2213 } *PublicKeyVerify;
2214
2215 static void verify_public_key_completion(const char *line, void *context)
2216 {
2217   PublicKeyVerify verify = (PublicKeyVerify)context;
2218
2219   if (line[0] == 'Y' || line[0] == 'y') {
2220     /* Call the completion */
2221     if (verify->completion)
2222       verify->completion(TRUE, verify->context);
2223
2224     /* Save the key for future checking */
2225     silc_pkcs_save_public_key(verify->filename, verify->public_key,
2226                               SILC_PKCS_FILE_BASE64);
2227   } else {
2228     /* Call the completion */
2229     if (verify->completion)
2230       verify->completion(FALSE, verify->context);
2231
2232     printformat_module("fe-common/silc", NULL, NULL,
2233                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD,
2234                        verify->entity_name ? verify->entity_name :
2235                        verify->entity);
2236   }
2237
2238   silc_free(verify->filename);
2239   silc_free(verify->entity);
2240   silc_free(verify->entity_name);
2241   silc_free(verify);
2242 }
2243
2244 /* Internal routine to verify public key. If the `completion' is provided
2245    it will be called to indicate whether public was verified or not. For
2246    server/router public key this will check for filename that includes the
2247    remote host's IP address and remote host's hostname. */
2248
2249 static void
2250 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
2251                                 const char *name,
2252                                 SilcConnectionType conn_type,
2253                                 SilcPublicKey public_key,
2254                                 SilcVerifyPublicKey completion, void *context)
2255 {
2256   PublicKeyVerify verify;
2257   char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
2258   char *fingerprint, *babbleprint, *format;
2259   SilcPublicKey local_pubkey;
2260   SilcUInt16 port;
2261   const char *hostname, *ip;
2262   unsigned char *pk;
2263   SilcUInt32 pk_len;
2264   struct passwd *pw;
2265   struct stat st;
2266   char *entity = ((conn_type == SILC_CONN_SERVER ||
2267                    conn_type == SILC_CONN_ROUTER) ?
2268                   "server" : "client");
2269   int i;
2270
2271   if (silc_pkcs_get_type(public_key) != SILC_PKCS_SILC) {
2272     printformat_module("fe-common/silc", NULL, NULL,
2273                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
2274                        entity, silc_pkcs_get_type(public_key));
2275     if (completion)
2276       completion(FALSE, context);
2277     return;
2278   }
2279
2280   /* Encode public key */
2281   pk = silc_pkcs_public_key_encode(public_key, &pk_len);
2282   if (!pk) {
2283     if (completion)
2284       completion(FALSE, context);
2285     return;
2286   }
2287
2288   pw = getpwuid(getuid());
2289   if (!pw) {
2290     if (completion)
2291       completion(FALSE, context);
2292     return;
2293   }
2294
2295   memset(filename, 0, sizeof(filename));
2296   memset(filename2, 0, sizeof(filename2));
2297   memset(file, 0, sizeof(file));
2298
2299   /* Get remote host information */
2300   silc_socket_stream_get_info(silc_packet_stream_get_stream(conn->stream),
2301                               NULL, &hostname, &ip, &port);
2302
2303   if (conn_type == SILC_CONN_SERVER ||
2304       conn_type == SILC_CONN_ROUTER) {
2305     if (!name) {
2306       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, ip, port);
2307       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2308                get_irssi_dir(), entity, file);
2309
2310       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2311                hostname, port);
2312       snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s",
2313                get_irssi_dir(), entity, file);
2314
2315       ipf = filename;
2316       hostf = filename2;
2317     } else {
2318       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2319                name, port);
2320       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2321                get_irssi_dir(), entity, file);
2322
2323       ipf = filename;
2324     }
2325   } else {
2326     /* Replace all whitespaces with `_'. */
2327     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2328     for (i = 0; i < strlen(fingerprint); i++)
2329       if (fingerprint[i] == ' ')
2330         fingerprint[i] = '_';
2331
2332     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
2333     snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2334              get_irssi_dir(), entity, file);
2335     silc_free(fingerprint);
2336
2337     ipf = filename;
2338   }
2339
2340   /* Take fingerprint of the public key */
2341   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2342   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
2343
2344   verify = silc_calloc(1, sizeof(*verify));
2345   verify->client = client;
2346   verify->conn = conn;
2347   verify->filename = strdup(ipf);
2348   verify->entity = strdup(entity);
2349   verify->entity_name = (conn_type != SILC_CONN_CLIENT ?
2350                          (name ? strdup(name) : strdup(hostname))
2351                          : NULL);
2352   verify->public_key = public_key;
2353   verify->completion = completion;
2354   verify->context = context;
2355
2356   /* Check whether this key already exists */
2357   if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
2358     /* Key does not exist, ask user to verify the key and save it */
2359
2360     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2361                        SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2362                        verify->entity_name : entity);
2363     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2364                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2365     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2366                        SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2367     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2368                              SILCTXT_PUBKEY_ACCEPT);
2369     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2370                             format, 0, verify);
2371     g_free(format);
2372     silc_free(fingerprint);
2373     return;
2374   } else {
2375     /* The key already exists, verify it. */
2376     unsigned char *encpk;
2377     SilcUInt32 encpk_len;
2378
2379     /* Load the key file, try for both IP filename and hostname filename */
2380     if (!silc_pkcs_load_public_key(ipf, &local_pubkey) &&
2381         (!hostf || (!silc_pkcs_load_public_key(hostf, &local_pubkey)))) {
2382       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2383                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2384                          verify->entity_name : entity);
2385       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2386                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2387       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2388                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2389       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2390                          SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
2391       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2392                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2393       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2394                               format, 0, verify);
2395       g_free(format);
2396       silc_free(fingerprint);
2397       return;
2398     }
2399
2400     /* Encode the key data */
2401     encpk = silc_pkcs_public_key_encode(local_pubkey, &encpk_len);
2402     if (!encpk) {
2403       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2404                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2405                          verify->entity_name : entity);
2406       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2407                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2408       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2409                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2410       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2411                          SILCTXT_PUBKEY_MALFORMED, entity);
2412       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2413                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2414       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2415                               format, 0, verify);
2416       g_free(format);
2417       silc_free(fingerprint);
2418       return;
2419     }
2420
2421     /* Compare the keys */
2422     if (memcmp(encpk, pk, encpk_len)) {
2423       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2424                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2425                          verify->entity_name : entity);
2426       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2427                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2428       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2429                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2430       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2431                          SILCTXT_PUBKEY_NO_MATCH, entity);
2432       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2433                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
2434       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2435                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
2436
2437       /* Ask user to verify the key and save it */
2438       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2439                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2440       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2441                               format, 0, verify);
2442       g_free(format);
2443       silc_free(fingerprint);
2444       silc_free(encpk);
2445       return;
2446     }
2447
2448     /* Local copy matched */
2449     if (completion)
2450       completion(TRUE, context);
2451     silc_free(encpk);
2452     silc_free(fingerprint);
2453     silc_free(verify->filename);
2454     silc_free(verify->entity);
2455     silc_free(verify->entity_name);
2456     silc_free(verify);
2457   }
2458 }
2459
2460 /* Verifies received public key. The `conn_type' indicates which entity
2461    (server, client etc.) has sent the public key. If user decides to trust
2462    the key may be saved as trusted public key for later use. The
2463    `completion' must be called after the public key has been verified. */
2464
2465 void
2466 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
2467                        SilcConnectionType conn_type,
2468                        SilcPublicKey public_key,
2469                        SilcVerifyPublicKey completion, void *context)
2470 {
2471   silc_verify_public_key_internal(client, conn, NULL, conn_type, public_key,
2472                                   completion, context);
2473 }
2474
2475 /* Asks passphrase from user on the input line. */
2476
2477 typedef struct {
2478   SilcAskPassphrase completion;
2479   void *context;
2480 } *AskPassphrase;
2481
2482 void ask_passphrase_completion(const char *passphrase, void *context)
2483 {
2484   AskPassphrase p = (AskPassphrase)context;
2485   if (passphrase && passphrase[0] == '\0')
2486     passphrase = NULL;
2487   p->completion((unsigned char *)passphrase,
2488                 passphrase ? strlen(passphrase) : 0, p->context);
2489   silc_free(p);
2490 }
2491
2492 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
2493                          SilcAskPassphrase completion, void *context)
2494 {
2495   AskPassphrase p = silc_calloc(1, sizeof(*p));
2496   p->completion = completion;
2497   p->context = context;
2498
2499   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
2500                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
2501 }
2502
2503 typedef struct {
2504   SilcGetAuthMeth completion;
2505   void *context;
2506 } *InternalGetAuthMethod;
2507
2508 /* Callback called when we've received the authentication method information
2509    from the server after we've requested it. This will get the authentication
2510    data from the user if needed. */
2511
2512 static void silc_get_auth_method_callback(SilcClient client,
2513                                           SilcClientConnection conn,
2514                                           SilcAuthMethod auth_meth,
2515                                           void *context)
2516 {
2517   InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
2518
2519   SILC_LOG_DEBUG(("Start"));
2520
2521   switch (auth_meth) {
2522   case SILC_AUTH_NONE:
2523     /* No authentication required. */
2524     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2525     break;
2526   case SILC_AUTH_PASSWORD:
2527     {
2528       /* Check whether we find the password for this server in our
2529          configuration.  If not, then don't provide so library will ask
2530          it from the user. */
2531       SERVER_SETUP_REC *setup = server_setup_find_port(conn->remote_host,
2532                                                        conn->remote_port);
2533       if (!setup || !setup->password) {
2534         (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2535         break;
2536       }
2537
2538       (*internal->completion)(TRUE, auth_meth, setup->password,
2539                               strlen(setup->password), internal->context);
2540     }
2541     break;
2542   case SILC_AUTH_PUBLIC_KEY:
2543     /* Do not get the authentication data now, the library will generate
2544        it using our default key, if we do not provide it here. */
2545     /* XXX In the future when we support multiple local keys and multiple
2546        local certificates we will need to ask from user which one to use. */
2547     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2548     break;
2549   }
2550
2551   silc_free(internal);
2552 }
2553
2554 /* Find authentication method and authentication data by hostname and
2555    port. The hostname may be IP address as well. The found authentication
2556    method and authentication data is returned to `auth_meth', `auth_data'
2557    and `auth_data_len'. The function returns TRUE if authentication method
2558    is found and FALSE if not. `conn' may be NULL. */
2559
2560 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
2561                           char *hostname, SilcUInt16 port,
2562                           SilcGetAuthMeth completion, void *context)
2563 {
2564   InternalGetAuthMethod internal;
2565
2566   SILC_LOG_DEBUG(("Start"));
2567
2568   /* If we do not have this connection configured by the user in a
2569      configuration file then resolve the authentication method from the
2570      server for this session. */
2571   internal = silc_calloc(1, sizeof(*internal));
2572   internal->completion = completion;
2573   internal->context = context;
2574
2575 #if 0
2576   silc_client_request_authentication_method(client, conn,
2577                                             silc_get_auth_method_callback,
2578                                             internal);
2579 #else
2580   completion(TRUE, SILC_AUTH_NONE, NULL, 0, context);
2581 #endif
2582 }
2583
2584 /* Asks whether the user would like to perform the key agreement protocol.
2585    This is called after we have received an key agreement packet or an
2586    reply to our key agreement packet. This returns TRUE if the user wants
2587    the library to perform the key agreement protocol and FALSE if it is not
2588    desired (application may start it later by calling the function
2589    silc_client_perform_key_agreement). */
2590
2591 void silc_key_agreement(SilcClient client, SilcClientConnection conn,
2592                         SilcClientEntry client_entry, const char *hostname,
2593                         SilcUInt16 protocol, SilcUInt16 port)
2594 {
2595   char portstr[12], protostr[5];
2596
2597   SILC_LOG_DEBUG(("Start"));
2598
2599   /* We will just display the info on the screen and return FALSE and user
2600      will have to start the key agreement with a command. */
2601
2602   if (hostname) {
2603     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2604     snprintf(protostr, sizeof(protostr) - 1, "%s", protocol == 1 ? "UDP" :
2605              "TCP");
2606   }
2607
2608   if (!hostname)
2609     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2610                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
2611   else
2612     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2613                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
2614                        client_entry->nickname, hostname, portstr, protostr);
2615 }
2616
2617 /* Notifies application that file transfer protocol session is being
2618    requested by the remote client indicated by the `client_entry' from
2619    the `hostname' and `port'. The `session_id' is the file transfer
2620    session and it can be used to either accept or reject the file
2621    transfer request, by calling the silc_client_file_receive or
2622    silc_client_file_close, respectively. */
2623
2624 void silc_ftp(SilcClient client, SilcClientConnection conn,
2625               SilcClientEntry client_entry, SilcUInt32 session_id,
2626               const char *hostname, SilcUInt16 port)
2627 {
2628   SILC_SERVER_REC *server;
2629   char portstr[12];
2630   FtpSession ftp = NULL;
2631
2632   SILC_LOG_DEBUG(("Start"));
2633
2634   server = conn->context;
2635
2636   silc_dlist_start(server->ftp_sessions);
2637   while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
2638     if (ftp->client_entry == client_entry &&
2639         ftp->session_id == session_id) {
2640       server->current_session = ftp;
2641       break;
2642     }
2643   }
2644   if (ftp == SILC_LIST_END) {
2645     ftp = silc_calloc(1, sizeof(*ftp));
2646     ftp->client_entry = client_entry;
2647     ftp->session_id = session_id;
2648     ftp->send = FALSE;
2649     ftp->conn = conn;
2650     silc_dlist_add(server->ftp_sessions, ftp);
2651     server->current_session = ftp;
2652   }
2653
2654   if (hostname)
2655     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2656
2657   if (!hostname)
2658     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2659                        SILCTXT_FILE_REQUEST, client_entry->nickname);
2660   else
2661     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2662                        SILCTXT_FILE_REQUEST_HOST,
2663                        client_entry->nickname, hostname, portstr);
2664 }
2665
2666 /* Delivers SILC session detachment data indicated by `detach_data' to the
2667    application.  If application has issued SILC_COMMAND_DETACH command
2668    the client session in the SILC network is not quit.  The client remains
2669    in the network but is detached.  The detachment data may be used later
2670    to resume the session in the SILC Network.  The appliation is
2671    responsible of saving the `detach_data', to for example in a file.
2672
2673    The detachment data can be given as argument to the functions
2674    silc_client_connect_to_server, or silc_client_add_connection when
2675    creating connection to remote server, inside SilcClientConnectionParams
2676    structure.  If it is provided the client library will attempt to resume
2677    the session in the network.  After the connection is created
2678    successfully, the application is responsible of setting the user
2679    interface for user into the same state it was before detaching (showing
2680    same channels, channel modes, etc).  It can do this by fetching the
2681    information (like joined channels) from the client library. */
2682
2683 void
2684 silc_detach(SilcClient client, SilcClientConnection conn,
2685             const unsigned char *detach_data, SilcUInt32 detach_data_len)
2686 {
2687   SILC_SERVER_REC *server = conn->context;
2688   char *file;
2689
2690   /* Save the detachment data to file. */
2691
2692   file = silc_get_session_filename(server);
2693   silc_file_writefile(file, detach_data, detach_data_len);
2694   silc_free(file);
2695 }
2696
2697 /* Called to indicate the client library is running. */
2698
2699 static void
2700 silc_running(SilcClient client, void *application)
2701 {
2702   SILC_LOG_DEBUG(("Client library is running"));
2703 }
2704
2705 /* SILC client operations */
2706 SilcClientOperations ops = {
2707   silc_say,
2708   silc_channel_message,
2709   silc_private_message,
2710   silc_notify,
2711   silc_command,
2712   silc_command_reply,
2713   silc_get_auth_method,
2714   silc_verify_public_key,
2715   silc_ask_passphrase,
2716   silc_key_agreement,
2717   silc_ftp,
2718   silc_detach,
2719   silc_running,
2720 };