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