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 (SILC_STATUS_IS_ERROR(status)) {
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 (SILC_STATUS_IS_ERROR(status)) {
1599         silc_say_error("WHOWAS: %s", silc_get_status_message(status));
1600         return;
1601       }
1602
1603       (void)va_arg(vp, SilcClientEntry);
1604       nickname = va_arg(vp, char *);
1605       username = va_arg(vp, char *);
1606       realname = va_arg(vp, char *);
1607
1608       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1609                          SILCTXT_WHOWAS_USERINFO, nickname, username,
1610                          realname ? realname : "");
1611     }
1612     break;
1613
1614   case SILC_COMMAND_INVITE:
1615     {
1616       SilcChannelEntry channel;
1617       SilcArgumentPayload invite_list;
1618
1619       if (SILC_STATUS_IS_ERROR(status))
1620         return;
1621
1622       channel = va_arg(vp, SilcChannelEntry);
1623       invite_list = va_arg(vp, SilcArgumentPayload);
1624
1625       if (invite_list)
1626         silc_parse_inviteban_list(client, conn, server, channel,
1627                                   "invite", invite_list);
1628     }
1629     break;
1630
1631   case SILC_COMMAND_JOIN:
1632     {
1633       char *channel, *mode, *topic, *cipher, *hmac;
1634       SilcUInt32 modei;
1635       SilcHashTableList *user_list;
1636       SilcChannelEntry channel_entry;
1637       SilcChannelUser chu;
1638       SilcClientEntry founder = NULL;
1639       NICK_REC *ownnick;
1640
1641       if (SILC_STATUS_IS_ERROR(status))
1642         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 (SILC_STATUS_IS_ERROR(status))
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 (SILC_STATUS_IS_ERROR(status))
1777         return;
1778
1779       (void)va_arg(vp, SilcChannelEntry);
1780       name = va_arg(vp, char *);
1781       topic = va_arg(vp, char *);
1782       usercount = va_arg(vp, int);
1783
1784       if (topic && !silc_term_utf8() &&
1785           silc_utf8_valid(topic, strlen(topic))) {
1786         memset(tmp, 0, sizeof(tmp));
1787         cp = tmp;
1788         if (strlen(topic) > sizeof(tmp) - 1) {
1789           dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
1790           cp = dm;
1791         }
1792
1793         silc_utf8_decode(topic, strlen(topic), SILC_STRING_LOCALE,
1794                          cp, strlen(topic));
1795         topic = cp;
1796       }
1797
1798       if (status == SILC_STATUS_LIST_START ||
1799           status == SILC_STATUS_OK)
1800         printformat_module("fe-common/silc", server, NULL,
1801                            MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
1802
1803       if (!usercount)
1804         snprintf(users, sizeof(users) - 1, "N/A");
1805       else
1806         snprintf(users, sizeof(users) - 1, "%d", usercount);
1807       printformat_module("fe-common/silc", server, NULL,
1808                          MSGLEVEL_CRAP, SILCTXT_LIST,
1809                          name, users, topic ? topic : "");
1810       silc_free(dm);
1811     }
1812     break;
1813
1814   case SILC_COMMAND_UMODE:
1815     {
1816       SilcUInt32 mode;
1817       char *reason;
1818
1819       if (SILC_STATUS_IS_ERROR(status))
1820         return;
1821
1822       mode = va_arg(vp, SilcUInt32);
1823
1824       if (mode & SILC_UMODE_SERVER_OPERATOR &&
1825           !(server->umode & SILC_UMODE_SERVER_OPERATOR))
1826         printformat_module("fe-common/silc", server, NULL,
1827                            MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1828
1829       if (mode & SILC_UMODE_ROUTER_OPERATOR &&
1830           !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
1831         printformat_module("fe-common/silc", server, NULL,
1832                            MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1833
1834       if ((mode & SILC_UMODE_GONE) != (server->umode & SILC_UMODE_GONE)) {
1835         if (mode & SILC_UMODE_GONE) {
1836           if ((server->away_reason != NULL) && (server->away_reason[0] != '\0'))
1837             reason = g_strdup(server->away_reason);
1838           else
1839             reason = g_strdup("away");
1840         } else
1841           reason = g_strdup("");
1842
1843         silc_set_away(reason, server);
1844
1845         g_free(reason);
1846       }
1847
1848       server->umode = mode;
1849       signal_emit("user mode changed", 2, server, NULL);
1850     }
1851     break;
1852
1853   case SILC_COMMAND_OPER:
1854     if (SILC_STATUS_IS_ERROR(status))
1855       return;
1856
1857     server->umode |= SILC_UMODE_SERVER_OPERATOR;
1858     signal_emit("user mode changed", 2, server, NULL);
1859
1860     printformat_module("fe-common/silc", server, NULL,
1861                        MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1862     break;
1863
1864   case SILC_COMMAND_SILCOPER:
1865     if (SILC_STATUS_IS_ERROR(status))
1866       return;
1867
1868     server->umode |= SILC_UMODE_ROUTER_OPERATOR;
1869     signal_emit("user mode changed", 2, server, NULL);
1870
1871     printformat_module("fe-common/silc", server, NULL,
1872                        MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1873     break;
1874
1875   case SILC_COMMAND_USERS:
1876     {
1877       SilcHashTableList htl;
1878       SilcChannelEntry channel;
1879       SilcChannelUser chu;
1880
1881       if (SILC_STATUS_IS_ERROR(status))
1882         return;
1883
1884       channel = va_arg(vp, SilcChannelEntry);
1885
1886       printformat_module("fe-common/silc", server, channel->channel_name,
1887                          MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
1888                          channel->channel_name);
1889
1890       silc_hash_table_list(channel->user_list, &htl);
1891       while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1892         SilcClientEntry e = chu->client;
1893         char stat[5], *mode;
1894
1895         if (!e->nickname)
1896           continue;
1897
1898         memset(stat, 0, sizeof(stat));
1899         mode = silc_client_chumode_char(chu->mode);
1900         if (e->mode & SILC_UMODE_GONE)
1901           strcat(stat, "G");
1902         else if (e->mode & SILC_UMODE_INDISPOSED)
1903           strcat(stat, "I");
1904         else if (e->mode & SILC_UMODE_BUSY)
1905           strcat(stat, "B");
1906         else if (e->mode & SILC_UMODE_PAGE)
1907           strcat(stat, "P");
1908         else if (e->mode & SILC_UMODE_HYPER)
1909           strcat(stat, "H");
1910         else if (e->mode & SILC_UMODE_ROBOT)
1911           strcat(stat, "R");
1912         else if (e->mode & SILC_UMODE_ANONYMOUS)
1913           strcat(stat, "?");
1914         else
1915           strcat(stat, "A");
1916         if (mode)
1917           strcat(stat, mode);
1918
1919         printformat_module("fe-common/silc", server, channel->channel_name,
1920                            MSGLEVEL_CRAP, SILCTXT_USERS,
1921                            e->nickname, stat,
1922                            e->username ? e->username : "",
1923                            e->hostname ? e->hostname : "",
1924                            e->realname ? e->realname : "");
1925         if (mode)
1926           silc_free(mode);
1927       }
1928       silc_hash_table_list_reset(&htl);
1929     }
1930     break;
1931
1932   case SILC_COMMAND_BAN:
1933     {
1934       SilcChannelEntry channel;
1935       SilcArgumentPayload invite_list;
1936
1937       if (SILC_STATUS_IS_ERROR(status))
1938         return;
1939
1940       channel = va_arg(vp, SilcChannelEntry);
1941       invite_list = va_arg(vp, SilcArgumentPayload);
1942
1943       if (invite_list)
1944         silc_parse_inviteban_list(client, conn, server, channel,
1945                                   "ban", invite_list);
1946     }
1947     break;
1948
1949   case SILC_COMMAND_GETKEY:
1950     {
1951       SilcIdType id_type;
1952       void *entry;
1953       SilcPublicKey public_key;
1954       GetkeyContext getkey;
1955       char *name;
1956
1957       if (SILC_STATUS_IS_ERROR(status))
1958         return;
1959
1960       id_type = va_arg(vp, SilcUInt32);
1961       entry = va_arg(vp, void *);
1962       public_key = va_arg(vp, SilcPublicKey);
1963
1964       if (public_key) {
1965         getkey = silc_calloc(1, sizeof(*getkey));
1966         getkey->entry = entry;
1967         getkey->id_type = id_type;
1968         getkey->client = client;
1969         getkey->conn = conn;
1970
1971         name = (id_type == SILC_ID_CLIENT ?
1972                 ((SilcClientEntry)entry)->nickname :
1973                 ((SilcServerEntry)entry)->server_name);
1974
1975         silc_verify_public_key_internal(client, conn, name,
1976                                         (id_type == SILC_ID_CLIENT ?
1977                                          SILC_CONN_CLIENT :
1978                                          SILC_CONN_SERVER),
1979                                         public_key, silc_getkey_cb, getkey);
1980       } else {
1981         printformat_module("fe-common/silc", server, NULL,
1982                            MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOKEY);
1983       }
1984     }
1985     break;
1986
1987   case SILC_COMMAND_INFO:
1988     {
1989       SilcServerEntry server_entry;
1990       char *server_name;
1991       char *server_info;
1992
1993       if (SILC_STATUS_IS_ERROR(status))
1994         return;
1995
1996       server_entry = va_arg(vp, SilcServerEntry);
1997       server_name = va_arg(vp, char *);
1998       server_info = va_arg(vp, char *);
1999
2000       if (server_name && server_info )
2001         {
2002           printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
2003           printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
2004         }
2005     }
2006     break;
2007
2008   case SILC_COMMAND_TOPIC:
2009     {
2010       SilcChannelEntry channel;
2011       char *topic;
2012       char tmp[256], *cp, *dm = NULL;
2013
2014       if (SILC_STATUS_IS_ERROR(status))
2015         return;
2016
2017       channel = va_arg(vp, SilcChannelEntry);
2018       topic = va_arg(vp, char *);
2019
2020       if (topic && !silc_term_utf8() &&
2021           silc_utf8_valid(topic, strlen(topic))) {
2022         memset(tmp, 0, sizeof(tmp));
2023         cp = tmp;
2024         if (strlen(topic) > sizeof(tmp) - 1) {
2025           dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
2026           cp = dm;
2027         }
2028
2029         silc_utf8_decode(topic, strlen(topic), SILC_STRING_LOCALE,
2030                          cp, strlen(topic));
2031         topic = cp;
2032       }
2033
2034       if (topic) {
2035         chanrec = silc_channel_find_entry(server, channel);
2036         if (chanrec) {
2037           g_free_not_null(chanrec->topic);
2038           chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
2039           signal_emit("channel topic changed", 1, chanrec);
2040         }
2041         printformat_module("fe-common/silc", server, channel->channel_name,
2042                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
2043                            channel->channel_name, topic);
2044       } else {
2045         printformat_module("fe-common/silc", server, channel->channel_name,
2046                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
2047                            channel->channel_name);
2048       }
2049       silc_free(dm);
2050     }
2051     break;
2052
2053   case SILC_COMMAND_WATCH:
2054     break;
2055
2056   case SILC_COMMAND_STATS:
2057     {
2058       SilcClientStats *cstats;
2059       SilcUInt32 buf_len;
2060       SilcBufferStruct buf;
2061       unsigned char *tmp_buf;
2062       char tmp[40];
2063       const char *tmptime;
2064       int days, hours, mins, secs;
2065
2066       if (SILC_STATUS_IS_ERROR(status))
2067         return;
2068
2069       cstats = va_arg(vp, SilcClientStats *);
2070       if (!cstats) {
2071         printtext(server, NULL, MSGLEVEL_CRAP, "No statistics available");
2072         return;
2073       }
2074
2075       tmptime = silc_time_string(cstats->starttime);
2076       printformat_module("fe-common/silc", server, NULL,
2077                          MSGLEVEL_CRAP, SILCTXT_STATS,
2078                          "Local server start time", tmptime);
2079
2080       days = cstats->uptime / (24 * 60 * 60);
2081       cstats->uptime -= days * (24 * 60 * 60);
2082       hours = cstats->uptime / (60 * 60);
2083       cstats->uptime -= hours * (60 * 60);
2084       mins = cstats->uptime / 60;
2085       cstats->uptime -= mins * 60;
2086       secs = cstats->uptime;
2087       snprintf(tmp, sizeof(tmp) - 1, "%d days %d hours %d mins %d secs",
2088                days, hours, mins, secs);
2089       printformat_module("fe-common/silc", server, NULL,
2090                          MSGLEVEL_CRAP, SILCTXT_STATS,
2091                          "Local server uptime", tmp);
2092
2093       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->my_clients);
2094       printformat_module("fe-common/silc", server, NULL,
2095                          MSGLEVEL_CRAP, SILCTXT_STATS,
2096                          "Local server clients", tmp);
2097
2098       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->my_channels);
2099       printformat_module("fe-common/silc", server, NULL,
2100                          MSGLEVEL_CRAP, SILCTXT_STATS,
2101                          "Local server channels", tmp);
2102
2103       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->my_server_ops);
2104       printformat_module("fe-common/silc", server, NULL,
2105                          MSGLEVEL_CRAP, SILCTXT_STATS,
2106                          "Local server operators", tmp);
2107
2108       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->my_router_ops);
2109       printformat_module("fe-common/silc", server, NULL,
2110                          MSGLEVEL_CRAP, SILCTXT_STATS,
2111                          "Local router operators", tmp);
2112
2113       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->cell_clients);
2114       printformat_module("fe-common/silc", server, NULL,
2115                          MSGLEVEL_CRAP, SILCTXT_STATS,
2116                          "Local cell clients", tmp);
2117
2118       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->cell_channels);
2119       printformat_module("fe-common/silc", server, NULL,
2120                          MSGLEVEL_CRAP, SILCTXT_STATS,
2121                          "Local cell channels", tmp);
2122
2123       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->cell_servers);
2124       printformat_module("fe-common/silc", server, NULL,
2125                          MSGLEVEL_CRAP, SILCTXT_STATS,
2126                          "Local cell servers", tmp);
2127
2128       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->clients);
2129       printformat_module("fe-common/silc", server, NULL,
2130                          MSGLEVEL_CRAP, SILCTXT_STATS,
2131                          "Total clients", tmp);
2132
2133       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->channels);
2134       printformat_module("fe-common/silc", server, NULL,
2135                          MSGLEVEL_CRAP, SILCTXT_STATS,
2136                          "Total channels", tmp);
2137
2138       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->servers);
2139       printformat_module("fe-common/silc", server, NULL,
2140                          MSGLEVEL_CRAP, SILCTXT_STATS,
2141                          "Total servers", tmp);
2142
2143       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->routers);
2144       printformat_module("fe-common/silc", server, NULL,
2145                          MSGLEVEL_CRAP, SILCTXT_STATS,
2146                          "Total routers", tmp);
2147
2148       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->server_ops);
2149       printformat_module("fe-common/silc", server, NULL,
2150                          MSGLEVEL_CRAP, SILCTXT_STATS,
2151                            "Total server operators", tmp);
2152
2153       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->router_ops);
2154       printformat_module("fe-common/silc", server, NULL,
2155                          MSGLEVEL_CRAP, SILCTXT_STATS,
2156                          "Total router operators", tmp);
2157     }
2158     break;
2159
2160   case SILC_COMMAND_CMODE:
2161     {
2162       SilcChannelEntry channel_entry;
2163       SilcDList chpks;
2164
2165       channel_entry = va_arg(vp, SilcChannelEntry);
2166       (void)va_arg(vp, SilcUInt32);
2167       (void)va_arg(vp, SilcPublicKey);
2168       chpks = va_arg(vp, SilcDList);
2169
2170       if (SILC_STATUS_IS_ERROR(status) || !cmode_list_chpks ||
2171           !channel_entry || !channel_entry->channel_name)
2172         return;
2173
2174       /* Print the channel public key list */
2175       if (chpks)
2176         silc_parse_channel_public_keys(server, channel_entry, chpks);
2177       else
2178         printformat_module("fe-common/silc", server, NULL,
2179                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_PK_NO_LIST,
2180                          channel_entry->channel_name);
2181
2182     }
2183     break;
2184
2185   case SILC_COMMAND_LEAVE:
2186     {
2187       /* we might be cycling, so disable queueing again */
2188       silc_queue_disable(conn);
2189     }
2190     break;
2191
2192   }
2193 }
2194
2195 typedef struct {
2196   SilcClient client;
2197   SilcClientConnection conn;
2198   char *filename;
2199   char *entity;
2200   char *entity_name;
2201   SilcPublicKey public_key;
2202   SilcVerifyPublicKey completion;
2203   void *context;
2204 } *PublicKeyVerify;
2205
2206 static void verify_public_key_completion(const char *line, void *context)
2207 {
2208   PublicKeyVerify verify = (PublicKeyVerify)context;
2209
2210   if (line[0] == 'Y' || line[0] == 'y') {
2211     /* Call the completion */
2212     if (verify->completion)
2213       verify->completion(TRUE, verify->context);
2214
2215     /* Save the key for future checking */
2216     silc_pkcs_save_public_key(verify->filename, verify->public_key,
2217                               SILC_PKCS_FILE_BASE64);
2218   } else {
2219     /* Call the completion */
2220     if (verify->completion)
2221       verify->completion(FALSE, verify->context);
2222
2223     printformat_module("fe-common/silc", NULL, NULL,
2224                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD,
2225                        verify->entity_name ? verify->entity_name :
2226                        verify->entity);
2227   }
2228
2229   silc_free(verify->filename);
2230   silc_free(verify->entity);
2231   silc_free(verify->entity_name);
2232   silc_free(verify);
2233 }
2234
2235 /* Internal routine to verify public key. If the `completion' is provided
2236    it will be called to indicate whether public was verified or not. For
2237    server/router public key this will check for filename that includes the
2238    remote host's IP address and remote host's hostname. */
2239
2240 static void
2241 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
2242                                 const char *name,
2243                                 SilcConnectionType conn_type,
2244                                 SilcPublicKey public_key,
2245                                 SilcVerifyPublicKey completion, void *context)
2246 {
2247   PublicKeyVerify verify;
2248   char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
2249   char *fingerprint, *babbleprint, *format;
2250   SilcPublicKey local_pubkey;
2251   SilcUInt16 port;
2252   const char *hostname, *ip;
2253   unsigned char *pk;
2254   SilcUInt32 pk_len;
2255   struct passwd *pw;
2256   struct stat st;
2257   char *entity = ((conn_type == SILC_CONN_SERVER ||
2258                    conn_type == SILC_CONN_ROUTER) ?
2259                   "server" : "client");
2260   int i;
2261
2262   if (silc_pkcs_get_type(public_key) != SILC_PKCS_SILC) {
2263     printformat_module("fe-common/silc", NULL, NULL,
2264                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
2265                        entity, silc_pkcs_get_type(public_key));
2266     if (completion)
2267       completion(FALSE, context);
2268     return;
2269   }
2270
2271   /* Encode public key */
2272   pk = silc_pkcs_public_key_encode(public_key, &pk_len);
2273   if (!pk) {
2274     if (completion)
2275       completion(FALSE, context);
2276     return;
2277   }
2278
2279   pw = getpwuid(getuid());
2280   if (!pw) {
2281     if (completion)
2282       completion(FALSE, context);
2283     return;
2284   }
2285
2286   memset(filename, 0, sizeof(filename));
2287   memset(filename2, 0, sizeof(filename2));
2288   memset(file, 0, sizeof(file));
2289
2290   /* Get remote host information */
2291   silc_socket_stream_get_info(silc_packet_stream_get_stream(conn->stream),
2292                               NULL, &hostname, &ip, &port);
2293
2294   if (conn_type == SILC_CONN_SERVER ||
2295       conn_type == SILC_CONN_ROUTER) {
2296     if (!name) {
2297       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, ip, port);
2298       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2299                get_irssi_dir(), entity, file);
2300
2301       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2302                hostname, port);
2303       snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s",
2304                get_irssi_dir(), entity, file);
2305
2306       ipf = filename;
2307       hostf = filename2;
2308     } else {
2309       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2310                name, port);
2311       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2312                get_irssi_dir(), entity, file);
2313
2314       ipf = filename;
2315     }
2316   } else {
2317     /* Replace all whitespaces with `_'. */
2318     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2319     for (i = 0; i < strlen(fingerprint); i++)
2320       if (fingerprint[i] == ' ')
2321         fingerprint[i] = '_';
2322
2323     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
2324     snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2325              get_irssi_dir(), entity, file);
2326     silc_free(fingerprint);
2327
2328     ipf = filename;
2329   }
2330
2331   /* Take fingerprint of the public key */
2332   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2333   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
2334
2335   verify = silc_calloc(1, sizeof(*verify));
2336   verify->client = client;
2337   verify->conn = conn;
2338   verify->filename = strdup(ipf);
2339   verify->entity = strdup(entity);
2340   verify->entity_name = (conn_type != SILC_CONN_CLIENT ?
2341                          (name ? strdup(name) : strdup(hostname))
2342                          : NULL);
2343   verify->public_key = public_key;
2344   verify->completion = completion;
2345   verify->context = context;
2346
2347   /* Check whether this key already exists */
2348   if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
2349     /* Key does not exist, ask user to verify the key and save it */
2350
2351     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2352                        SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2353                        verify->entity_name : entity);
2354     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2355                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2356     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2357                        SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2358     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2359                              SILCTXT_PUBKEY_ACCEPT);
2360     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2361                             format, 0, verify);
2362     g_free(format);
2363     silc_free(fingerprint);
2364     return;
2365   } else {
2366     /* The key already exists, verify it. */
2367     unsigned char *encpk;
2368     SilcUInt32 encpk_len;
2369
2370     /* Load the key file, try for both IP filename and hostname filename */
2371     if (!silc_pkcs_load_public_key(ipf, &local_pubkey) &&
2372         (!hostf || (!silc_pkcs_load_public_key(hostf, &local_pubkey)))) {
2373       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2374                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2375                          verify->entity_name : entity);
2376       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2377                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2378       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2379                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2380       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2381                          SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
2382       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2383                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2384       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2385                               format, 0, verify);
2386       g_free(format);
2387       silc_free(fingerprint);
2388       return;
2389     }
2390
2391     /* Encode the key data */
2392     encpk = silc_pkcs_public_key_encode(local_pubkey, &encpk_len);
2393     if (!encpk) {
2394       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2395                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2396                          verify->entity_name : entity);
2397       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2398                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2399       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2400                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2401       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2402                          SILCTXT_PUBKEY_MALFORMED, entity);
2403       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2404                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2405       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2406                               format, 0, verify);
2407       g_free(format);
2408       silc_free(fingerprint);
2409       return;
2410     }
2411
2412     /* Compare the keys */
2413     if (memcmp(encpk, pk, encpk_len)) {
2414       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2415                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2416                          verify->entity_name : entity);
2417       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2418                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2419       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2420                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2421       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2422                          SILCTXT_PUBKEY_NO_MATCH, entity);
2423       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2424                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
2425       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2426                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
2427
2428       /* Ask user to verify the key and save it */
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       silc_free(encpk);
2436       return;
2437     }
2438
2439     /* Local copy matched */
2440     if (completion)
2441       completion(TRUE, context);
2442     silc_free(encpk);
2443     silc_free(fingerprint);
2444     silc_free(verify->filename);
2445     silc_free(verify->entity);
2446     silc_free(verify->entity_name);
2447     silc_free(verify);
2448   }
2449 }
2450
2451 /* Verifies received public key. The `conn_type' indicates which entity
2452    (server, client etc.) has sent the public key. If user decides to trust
2453    the key may be saved as trusted public key for later use. The
2454    `completion' must be called after the public key has been verified. */
2455
2456 void
2457 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
2458                        SilcConnectionType conn_type,
2459                        SilcPublicKey public_key,
2460                        SilcVerifyPublicKey completion, void *context)
2461 {
2462   silc_verify_public_key_internal(client, conn, NULL, conn_type, public_key,
2463                                   completion, context);
2464 }
2465
2466 /* Asks passphrase from user on the input line. */
2467
2468 typedef struct {
2469   SilcAskPassphrase completion;
2470   void *context;
2471 } *AskPassphrase;
2472
2473 void ask_passphrase_completion(const char *passphrase, void *context)
2474 {
2475   AskPassphrase p = (AskPassphrase)context;
2476   if (passphrase && passphrase[0] == '\0')
2477     passphrase = NULL;
2478   p->completion((unsigned char *)passphrase,
2479                 passphrase ? strlen(passphrase) : 0, p->context);
2480   silc_free(p);
2481 }
2482
2483 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
2484                          SilcAskPassphrase completion, void *context)
2485 {
2486   AskPassphrase p = silc_calloc(1, sizeof(*p));
2487   p->completion = completion;
2488   p->context = context;
2489
2490   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
2491                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
2492 }
2493
2494 typedef struct {
2495   SilcGetAuthMeth completion;
2496   void *context;
2497 } *InternalGetAuthMethod;
2498
2499 /* Callback called when we've received the authentication method information
2500    from the server after we've requested it. This will get the authentication
2501    data from the user if needed. */
2502
2503 static void silc_get_auth_method_callback(SilcClient client,
2504                                           SilcClientConnection conn,
2505                                           SilcAuthMethod auth_meth,
2506                                           void *context)
2507 {
2508   InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
2509
2510   SILC_LOG_DEBUG(("Start"));
2511
2512   switch (auth_meth) {
2513   case SILC_AUTH_NONE:
2514     /* No authentication required. */
2515     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2516     break;
2517   case SILC_AUTH_PASSWORD:
2518     {
2519       /* Check whether we find the password for this server in our
2520          configuration.  If not, then don't provide so library will ask
2521          it from the user. */
2522       SERVER_SETUP_REC *setup = server_setup_find_port(conn->remote_host,
2523                                                        conn->remote_port);
2524       if (!setup || !setup->password) {
2525         (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2526         break;
2527       }
2528
2529       (*internal->completion)(TRUE, auth_meth, setup->password,
2530                               strlen(setup->password), internal->context);
2531     }
2532     break;
2533   case SILC_AUTH_PUBLIC_KEY:
2534     /* Do not get the authentication data now, the library will generate
2535        it using our default key, if we do not provide it here. */
2536     /* XXX In the future when we support multiple local keys and multiple
2537        local certificates we will need to ask from user which one to use. */
2538     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2539     break;
2540   }
2541
2542   silc_free(internal);
2543 }
2544
2545 /* Find authentication method and authentication data by hostname and
2546    port. The hostname may be IP address as well. The found authentication
2547    method and authentication data is returned to `auth_meth', `auth_data'
2548    and `auth_data_len'. The function returns TRUE if authentication method
2549    is found and FALSE if not. `conn' may be NULL. */
2550
2551 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
2552                           char *hostname, SilcUInt16 port,
2553                           SilcGetAuthMeth completion, void *context)
2554 {
2555   InternalGetAuthMethod internal;
2556
2557   SILC_LOG_DEBUG(("Start"));
2558
2559   /* If we do not have this connection configured by the user in a
2560      configuration file then resolve the authentication method from the
2561      server for this session. */
2562   internal = silc_calloc(1, sizeof(*internal));
2563   internal->completion = completion;
2564   internal->context = context;
2565
2566 #if 0
2567   silc_client_request_authentication_method(client, conn,
2568                                             silc_get_auth_method_callback,
2569                                             internal);
2570 #else
2571   completion(TRUE, SILC_AUTH_NONE, NULL, 0, context);
2572 #endif
2573 }
2574
2575 /* Asks whether the user would like to perform the key agreement protocol.
2576    This is called after we have received an key agreement packet or an
2577    reply to our key agreement packet. This returns TRUE if the user wants
2578    the library to perform the key agreement protocol and FALSE if it is not
2579    desired (application may start it later by calling the function
2580    silc_client_perform_key_agreement). */
2581
2582 bool silc_key_agreement(SilcClient client, SilcClientConnection conn,
2583                         SilcClientEntry client_entry, const char *hostname,
2584                         SilcUInt16 port, SilcKeyAgreementCallback *completion,
2585                         void **context)
2586 {
2587   char portstr[12];
2588
2589   SILC_LOG_DEBUG(("Start"));
2590
2591   /* We will just display the info on the screen and return FALSE and user
2592      will have to start the key agreement with a command. */
2593
2594   if (hostname)
2595     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2596
2597   if (!hostname)
2598     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2599                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
2600   else
2601     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2602                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
2603                        client_entry->nickname, hostname, portstr);
2604
2605   *completion = NULL;
2606   *context = NULL;
2607
2608   return FALSE;
2609 }
2610
2611 /* Notifies application that file transfer protocol session is being
2612    requested by the remote client indicated by the `client_entry' from
2613    the `hostname' and `port'. The `session_id' is the file transfer
2614    session and it can be used to either accept or reject the file
2615    transfer request, by calling the silc_client_file_receive or
2616    silc_client_file_close, respectively. */
2617
2618 void silc_ftp(SilcClient client, SilcClientConnection conn,
2619               SilcClientEntry client_entry, SilcUInt32 session_id,
2620               const char *hostname, SilcUInt16 port)
2621 {
2622   SILC_SERVER_REC *server;
2623   char portstr[12];
2624   FtpSession ftp = NULL;
2625
2626   SILC_LOG_DEBUG(("Start"));
2627
2628   server = conn->context;
2629
2630   silc_dlist_start(server->ftp_sessions);
2631   while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
2632     if (ftp->client_entry == client_entry &&
2633         ftp->session_id == session_id) {
2634       server->current_session = ftp;
2635       break;
2636     }
2637   }
2638   if (ftp == SILC_LIST_END) {
2639     ftp = silc_calloc(1, sizeof(*ftp));
2640     ftp->client_entry = client_entry;
2641     ftp->session_id = session_id;
2642     ftp->send = FALSE;
2643     ftp->conn = conn;
2644     silc_dlist_add(server->ftp_sessions, ftp);
2645     server->current_session = ftp;
2646   }
2647
2648   if (hostname)
2649     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2650
2651   if (!hostname)
2652     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2653                        SILCTXT_FILE_REQUEST, client_entry->nickname);
2654   else
2655     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2656                        SILCTXT_FILE_REQUEST_HOST,
2657                        client_entry->nickname, hostname, portstr);
2658 }
2659
2660 /* Delivers SILC session detachment data indicated by `detach_data' to the
2661    application.  If application has issued SILC_COMMAND_DETACH command
2662    the client session in the SILC network is not quit.  The client remains
2663    in the network but is detached.  The detachment data may be used later
2664    to resume the session in the SILC Network.  The appliation is
2665    responsible of saving the `detach_data', to for example in a file.
2666
2667    The detachment data can be given as argument to the functions
2668    silc_client_connect_to_server, or silc_client_add_connection when
2669    creating connection to remote server, inside SilcClientConnectionParams
2670    structure.  If it is provided the client library will attempt to resume
2671    the session in the network.  After the connection is created
2672    successfully, the application is responsible of setting the user
2673    interface for user into the same state it was before detaching (showing
2674    same channels, channel modes, etc).  It can do this by fetching the
2675    information (like joined channels) from the client library. */
2676
2677 void
2678 silc_detach(SilcClient client, SilcClientConnection conn,
2679             const unsigned char *detach_data, SilcUInt32 detach_data_len)
2680 {
2681   SILC_SERVER_REC *server = conn->context;
2682   char *file;
2683
2684   /* Save the detachment data to file. */
2685
2686   file = silc_get_session_filename(server);
2687   silc_file_writefile(file, detach_data, detach_data_len);
2688   silc_free(file);
2689 }
2690
2691 /* Called to indicate the client library is running. */
2692
2693 static void
2694 silc_running(SilcClient client, void *application)
2695 {
2696   SILC_LOG_DEBUG(("Client library is running"));
2697 }
2698
2699 /* SILC client operations */
2700 SilcClientOperations ops = {
2701   silc_say,
2702   silc_channel_message,
2703   silc_private_message,
2704   silc_notify,
2705   silc_command,
2706   silc_command_reply,
2707   silc_get_auth_method,
2708   silc_verify_public_key,
2709   silc_ask_passphrase,
2710   silc_key_agreement,
2711   silc_ftp,
2712   silc_detach,
2713   silc_running,
2714 };