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