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