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