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