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