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