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