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