Explictly cast SILC_SERVER_REC to SERVER_REC
[silc.git] / apps / irssi / src / silc / core / client_ops.c
1 /*
2
3   client_ops.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 2001 - 2003 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20
21 #include "module.h"
22 #include "chat-protocols.h"
23 #include "args.h"
24
25 #include "chatnets.h"
26 #include "servers-setup.h"
27 #include "channels-setup.h"
28 #include "silc-servers.h"
29 #include "silc-channels.h"
30 #include "silc-queries.h"
31 #include "silc-nicklist.h"
32
33 #include "signals.h"
34 #include "levels.h"
35 #include "settings.h"
36 #include "ignore.h"
37 #include "fe-common/core/printtext.h"
38 #include "fe-common/core/fe-channels.h"
39 #include "fe-common/core/keyboard.h"
40 #include "fe-common/core/window-items.h"
41 #include "fe-common/silc/module-formats.h"
42
43 #include "core.h"
44
45 static void
46 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
47                                 const char *name, SilcSocketType conn_type,
48                                 unsigned char *pk, SilcUInt32 pk_len,
49                                 SilcSKEPKType pk_type,
50                                 SilcVerifyPublicKey completion, void *context);
51
52 static void silc_get_umode_string(SilcUInt32 mode, char *buf,
53                                   SilcUInt32 buf_size)
54 {
55   if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
56       (mode & SILC_UMODE_ROUTER_OPERATOR)) {
57     strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
58            "[server operator]" :
59            (mode & SILC_UMODE_ROUTER_OPERATOR) ?
60            "[SILC operator]" : "[unknown mode]");
61   }
62   if (mode & SILC_UMODE_GONE)
63     strcat(buf, " [away]");
64   if (mode & SILC_UMODE_INDISPOSED)
65     strcat(buf, " [indisposed]");
66   if (mode & SILC_UMODE_BUSY)
67     strcat(buf, " [busy]");
68   if (mode & SILC_UMODE_PAGE)
69     strcat(buf, " [page to reach]");
70   if (mode & SILC_UMODE_HYPER)
71     strcat(buf, " [hyper active]");
72   if (mode & SILC_UMODE_ROBOT)
73     strcat(buf, " [robot]");
74   if (mode & SILC_UMODE_ANONYMOUS)
75     strcat(buf, " [anonymous]");
76   if (mode & SILC_UMODE_BLOCK_PRIVMSG)
77     strcat(buf, " [blocks private messages]");
78   if (mode & SILC_UMODE_DETACHED)
79     strcat(buf, " [detached]");
80   if (mode & SILC_UMODE_REJECT_WATCHING)
81     strcat(buf, " [rejects watching]");
82   if (mode & SILC_UMODE_BLOCK_INVITE)
83     strcat(buf, " [blocks invites]");
84 }
85
86 /* print "nick appears as" message to every channel of a server */
87 static void
88 silc_print_nick_change_channel(SILC_SERVER_REC *server, const char *channel,
89                               const char *newnick, const char *oldnick,
90                               const char *address)
91 {
92   if (ignore_check(SERVER(server), oldnick, address,
93                    channel, newnick, MSGLEVEL_NICKS))
94     return;
95
96   printformat_module("fe-common/silc", server, channel, MSGLEVEL_NICKS,
97                      SILCTXT_CHANNEL_APPEARS,
98                      oldnick, newnick, channel, address);
99 }
100
101 static void
102 silc_print_nick_change(SILC_SERVER_REC *server, const char *newnick,
103                        const char *oldnick, const char *address)
104 {
105   GSList *tmp, *windows;
106
107   /* Print to each channel/query where the nick is.
108      Don't print more than once to the same window. */
109   windows = NULL;
110
111   for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
112     CHANNEL_REC *channel = tmp->data;
113     WINDOW_REC *window = window_item_window((WI_ITEM_REC *) channel);
114
115     if (nicklist_find(channel, newnick) == NULL ||
116         g_slist_find(windows, window) != NULL)
117       continue;
118
119     windows = g_slist_append(windows, window);
120     silc_print_nick_change_channel(server, channel->visible_name,
121                                    newnick, oldnick, address);
122   }
123
124   g_slist_free(windows);
125 }
126
127 static void silc_parse_channel_public_keys(SILC_SERVER_REC *server,
128                                            SilcChannelEntry channel_entry,
129                                            SilcBuffer channel_pubkeys)
130 {
131   SilcUInt16 argc;
132   SilcArgumentPayload chpks;
133   unsigned char *pk;
134   SilcUInt32 pk_len, type;
135   int c = 1;
136   char *fingerprint, *babbleprint;
137   SilcPublicKey pubkey;
138   SilcPublicKeyIdentifier ident;
139
140   printformat_module("fe-common/silc", server, NULL,
141                      MSGLEVEL_CRAP, SILCTXT_CHANNEL_PK_LIST,
142                      channel_entry->channel_name);
143
144   SILC_GET16_MSB(argc, channel_pubkeys->data);
145   chpks = silc_argument_payload_parse(channel_pubkeys->data + 2,
146                                       channel_pubkeys->len - 2, argc);
147   if (!chpks)
148     return;
149
150   pk = silc_argument_get_first_arg(chpks, &type, &pk_len);
151   while (pk) {
152     fingerprint = silc_hash_fingerprint(NULL, pk + 4, pk_len - 4);
153     babbleprint = silc_hash_babbleprint(NULL, pk + 4, pk_len - 4);
154     silc_pkcs_public_key_payload_decode(pk, pk_len, &pubkey);
155     ident = silc_pkcs_decode_identifier(pubkey->identifier);
156
157     printformat_module("fe-common/silc", server, NULL,
158                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_PK_LIST_ENTRY,
159                        c++, channel_entry->channel_name,
160                        type == 0x00 ? "Added" : "Removed",
161                        ident->realname ? ident->realname : "",
162                        fingerprint, babbleprint);
163
164     silc_free(fingerprint);
165     silc_free(babbleprint);
166     silc_pkcs_public_key_free(pubkey);
167     silc_pkcs_free_identifier(ident);
168     pk = silc_argument_get_next_arg(chpks, &type, &pk_len);
169   }
170
171   silc_argument_payload_free(chpks);
172 }
173
174 void silc_say(SilcClient client, SilcClientConnection conn,
175               SilcClientMessageType type, char *msg, ...)
176 {
177   SILC_SERVER_REC *server;
178   va_list va;
179   char *str;
180
181   server = conn == NULL ? NULL : conn->context;
182
183   va_start(va, msg);
184   str = g_strdup_vprintf(msg, va);
185   printtext(server, NULL, MSGLEVEL_CRAP, "%s", str);
186   g_free(str);
187   va_end(va);
188 }
189
190 void silc_say_error(char *msg, ...)
191 {
192   va_list va;
193   char *str;
194
195   va_start(va, msg);
196   str = g_strdup_vprintf(msg, va);
197   printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str);
198
199   g_free(str);
200   va_end(va);
201 }
202
203 /* try to verify a message using locally stored public key data */
204 int verify_message_signature(SilcClientEntry sender,
205                              SilcMessageSignedPayload sig,
206                              SilcMessagePayload message)
207 {
208   SilcPublicKey pk;
209   char file[256], filename[256];
210   char *fingerprint, *fingerprint2;
211   unsigned char *pk_data;
212   SilcUInt32 pk_datalen;
213   struct stat st;
214   int ret = SILC_MSG_SIGNED_VERIFIED, i;
215
216   if (sig == NULL)
217     return SILC_MSG_SIGNED_UNKNOWN;
218
219   /* get public key from the signature payload and compare it with the
220      one stored in the client entry */
221   pk = silc_message_signed_get_public_key(sig, &pk_data, &pk_datalen);
222
223   if (pk != NULL) {
224     fingerprint = silc_hash_fingerprint(NULL, pk_data, pk_datalen);
225
226     if (sender->fingerprint) {
227       fingerprint2 = silc_fingerprint(sender->fingerprint,
228                                       sender->fingerprint_len);
229       if (strcmp(fingerprint, fingerprint2)) {
230         /* since the public key differs from the senders public key, the
231            verification _failed_ */
232         silc_pkcs_public_key_free(pk);
233         silc_free(fingerprint);
234         ret = SILC_MSG_SIGNED_UNKNOWN;
235       }
236       silc_free(fingerprint2);
237     }
238   } else if (sender->fingerprint)
239     fingerprint = silc_fingerprint(sender->fingerprint,
240                                    sender->fingerprint_len);
241   else
242     /* no idea, who or what signed that message ... */
243     return SILC_MSG_SIGNED_UNKNOWN;
244
245   /* search our local client key cache */
246   for (i = 0; i < strlen(fingerprint); i++)
247     if (fingerprint[i] == ' ')
248       fingerprint[i] = '_';
249
250   snprintf(file, sizeof(file) - 1, "clientkey_%s.pub", fingerprint);
251   snprintf(filename, sizeof(filename) - 1, "%s/clientkeys/%s",
252            get_irssi_dir(), file);
253   silc_free(fingerprint);
254
255   if (stat(filename, &st) < 0)
256     /* we don't have the public key cached ... use the one from the sig */
257     ret = SILC_MSG_SIGNED_UNKNOWN;
258   else {
259     SilcPublicKey cached_pk=NULL;
260
261     /* try to load the file */
262     if (!silc_pkcs_load_public_key(filename, &cached_pk, SILC_PKCS_FILE_PEM) &&
263         !silc_pkcs_load_public_key(filename, &cached_pk,
264                                    SILC_PKCS_FILE_BIN)) {
265       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
266                          SILCTXT_PUBKEY_COULD_NOT_LOAD, "client");
267       if (pk == NULL)
268         return SILC_MSG_SIGNED_UNKNOWN;
269       else
270         ret = SILC_MSG_SIGNED_UNKNOWN;
271     }
272
273     if (cached_pk) {
274       if (pk)
275         silc_pkcs_public_key_free(pk);
276       pk = cached_pk;
277     }
278   }
279
280   /* the public key is now in pk, our "level of trust" in ret */
281   if ((pk) && silc_message_signed_verify(sig, message, pk,
282                                          silc_client->sha1hash)!= SILC_AUTH_OK)
283     ret = SILC_MSG_SIGNED_FAILED;
284
285   if (pk)
286     silc_pkcs_public_key_free(pk);
287
288   return ret;
289 }
290
291 char * silc_unescape_data(const char *escaped_data, SilcUInt32 *length)
292 {
293     char *data, *ptr;
294     int i = 0, j = 0, len = strlen(escaped_data);
295
296     data = silc_calloc(len, sizeof(char));
297
298     while (i < len) {
299         ptr = memchr(escaped_data + i, 1, len - i);
300         if (ptr) {
301             int inc = (ptr - escaped_data) - i;
302             memcpy(data + j, escaped_data + i, inc);
303             j += inc;
304             i += inc + 2;
305             data[j++] = *(ptr + 1) - 1;
306         } else {
307             memcpy(data + j, escaped_data + i, len - i);
308             j += (len - i);
309             break;
310         }
311     }
312   
313     *length = j;
314     return data;
315 }
316
317 char * silc_escape_data(const char *data, SilcUInt32 len)
318 {
319     char *escaped_data, *ptr, *ptr0, *ptr1;
320     int i = 0, j = 0;
321   
322     escaped_data = silc_calloc(2 * len, sizeof(char));
323
324     while (i < len) {
325         ptr0 = memchr(data + i, 0, len - i);
326         ptr1 = memchr(data + i, 1, len - i);
327
328         ptr = (ptr0 < ptr1 ? (ptr0 ? ptr0 : ptr1) : (ptr1 ? ptr1 : ptr0));
329     
330         if (ptr) {
331             int inc = (ptr - data) - i;
332             if (inc) 
333                 memcpy(escaped_data + j, data + i, inc);
334             j += inc;
335             i += inc;
336             escaped_data[j++] = 1;
337             escaped_data[j++] = *(data + i++) + 1;
338         } else {
339             memcpy(escaped_data + j, data + i, len - i);
340             j += (len - i);
341             break;
342         }
343     }
344           
345     return escaped_data;
346 }
347
348 void silc_emit_mime_sig(SILC_SERVER_REC *server, WI_ITEM_REC *item,
349                const char *data, SilcUInt32 data_len, const char *nick,
350                int verified)
351 {
352    char *escaped_data;
353
354    escaped_data = silc_escape_data(data, data_len);
355
356    signal_emit("mime", 4, server, item, escaped_data, nick, verified);
357  
358    silc_free(escaped_data);
359 }
360
361
362 /* Message for a channel. The `sender' is the nickname of the sender
363    received in the packet. The `channel_name' is the name of the channel. */
364
365 void silc_channel_message(SilcClient client, SilcClientConnection conn,
366                           SilcClientEntry sender, SilcChannelEntry channel,
367                           SilcMessagePayload payload,
368                           SilcMessageFlags flags, const unsigned char *message,
369                           SilcUInt32 message_len)
370 {
371   SILC_SERVER_REC *server;
372   SILC_NICK_REC *nick;
373   SILC_CHANNEL_REC *chanrec;
374   int verified = 0;
375
376   SILC_LOG_DEBUG(("Start"));
377
378   if (!message)
379     return;
380
381   server = conn == NULL ? NULL : conn->context;
382   chanrec = silc_channel_find_entry(server, channel);
383   if (!chanrec)
384     return;
385
386   nick = silc_nicklist_find(chanrec, sender);
387   if (!nick) {
388     /* We didn't find client but it clearly exists, add it. */
389     SilcChannelUser chu = silc_client_on_channel(channel, sender);
390     if (chu)
391       nick = silc_nicklist_insert(chanrec, chu, FALSE);
392   }
393
394   /* If the messages is digitally signed, verify it, if possible. */
395   if (flags & SILC_MESSAGE_FLAG_SIGNED) {
396     if (!settings_get_bool("ignore_message_signatures")) {
397       SilcMessageSignedPayload sig = silc_message_get_signature(payload);
398       verified = verify_message_signature(sender, sig, payload);
399     } else {
400       flags &= ~SILC_MESSAGE_FLAG_SIGNED;
401     }
402   }
403
404   if (flags & SILC_MESSAGE_FLAG_DATA) {
405     silc_emit_mime_sig(server, (WI_ITEM_REC *)chanrec, message, message_len,
406                 nick == NULL ? NULL : nick->nick,
407                 flags & SILC_MESSAGE_FLAG_SIGNED ? verified : -1);
408     message = NULL;
409   }
410
411   if (!message)
412     return;
413
414   /* FIXME: replace those printformat calls with signals and add signature
415             information to them (if present) */
416   if (flags & SILC_MESSAGE_FLAG_ACTION)
417     if(flags & SILC_MESSAGE_FLAG_UTF8 && !silc_term_utf8()) {
418       char tmp[256], *cp, *dm = NULL;
419       memset(tmp, 0, sizeof(tmp));
420       cp = tmp;
421       if(message_len > sizeof(tmp) - 1) {
422         dm = silc_calloc(message_len + 1, sizeof(*dm));
423         cp = dm;
424       }
425       silc_utf8_decode(message, message_len, SILC_STRING_LANGUAGE,
426                        cp, message_len);
427       printformat_module("fe-common/silc", server, channel->channel_name,
428                          MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_ACTION,
429                          nick == NULL ? "[<unknown>]" : nick->nick, cp);
430       silc_free(dm);
431     } else {
432       printformat_module("fe-common/silc", server, channel->channel_name,
433                          MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_ACTION,
434                          nick == NULL ? "[<unknown>]" : nick->nick,
435                          message);
436     }
437   else if (flags & SILC_MESSAGE_FLAG_NOTICE)
438     if(flags & SILC_MESSAGE_FLAG_UTF8 && !silc_term_utf8()) {
439       char tmp[256], *cp, *dm = NULL;
440       memset(tmp, 0, sizeof(tmp));
441       cp = tmp;
442       if(message_len > sizeof(tmp) - 1) {
443         dm = silc_calloc(message_len + 1, sizeof(*dm));
444         cp = dm;
445       }
446       silc_utf8_decode(message, message_len, SILC_STRING_LANGUAGE,
447                        cp, message_len);
448       printformat_module("fe-common/silc", server, channel->channel_name,
449                          MSGLEVEL_NOTICES, SILCTXT_CHANNEL_NOTICE,
450                          nick == NULL ? "[<unknown>]" : nick->nick, cp);
451       silc_free(dm);
452     } else {
453       printformat_module("fe-common/silc", server, channel->channel_name,
454                          MSGLEVEL_NOTICES, SILCTXT_CHANNEL_NOTICE,
455                          nick == NULL ? "[<unknown>]" : nick->nick,
456                          message);
457     }
458   else {
459     if (flags & SILC_MESSAGE_FLAG_UTF8 && !silc_term_utf8()) {
460       char tmp[256], *cp, *dm = NULL;
461
462       memset(tmp, 0, sizeof(tmp));
463       cp = tmp;
464       if (message_len > sizeof(tmp) - 1) {
465         dm = silc_calloc(message_len + 1, sizeof(*dm));
466         cp = dm;
467       }
468
469       silc_utf8_decode(message, message_len, SILC_STRING_LANGUAGE,
470                        cp, message_len);
471       if (flags & SILC_MESSAGE_FLAG_SIGNED)
472         signal_emit("message signed_public", 6, server, cp,
473                     nick == NULL ? "[<unknown>]" : nick->nick,
474                     nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
475                     chanrec->name, verified);
476       else
477         signal_emit("message public", 6, server, cp,
478                     nick == NULL ? "[<unknown>]" : nick->nick,
479                     nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
480                     chanrec->name, nick);
481       silc_free(dm);
482       return;
483     }
484
485     if (flags & SILC_MESSAGE_FLAG_SIGNED)
486       signal_emit("message signed_public", 6, server, message,
487                   nick == NULL ? "[<unknown>]" : nick->nick,
488                   nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
489                   chanrec->name, verified);
490     else
491       signal_emit("message public", 6, server, message,
492                   nick == NULL ? "[<unknown>]" : nick->nick,
493                   nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
494                   chanrec->name, nick);
495   }
496 }
497
498 /* Private message to the client. The `sender' is the nickname of the
499    sender received in the packet. */
500
501 void silc_private_message(SilcClient client, SilcClientConnection conn,
502                           SilcClientEntry sender, SilcMessagePayload payload,
503                           SilcMessageFlags flags,
504                           const unsigned char *message,
505                           SilcUInt32 message_len)
506 {
507   SILC_SERVER_REC *server;
508   char userhost[256];
509   int verified = 0;
510
511   SILC_LOG_DEBUG(("Start"));
512
513   server = conn == NULL ? NULL : conn->context;
514   memset(userhost, 0, sizeof(userhost));
515   if (sender->username)
516     snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
517              sender->username, sender->hostname);
518
519   /* If the messages is digitally signed, verify it, if possible. */
520   if (flags & SILC_MESSAGE_FLAG_SIGNED) {
521     if (!settings_get_bool("ignore_message_signatures")) {
522       SilcMessageSignedPayload sig = silc_message_get_signature(payload);
523       verified = verify_message_signature(sender, sig, payload);
524     } else {
525       flags &= ~SILC_MESSAGE_FLAG_SIGNED;
526     }
527   }
528
529   if (flags & SILC_MESSAGE_FLAG_DATA) {
530     silc_emit_mime_sig(server,
531                 sender->nickname ? 
532                 (WI_ITEM_REC *)query_find(SERVER(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         memset(buf, 0, sizeof(buf));
1854         snprintf(buf, sizeof(buf) - 1, "%s@%s",
1855                  collider->username, collider->hostname);
1856         nicklist_rename_unique(SERVER(server),
1857                                old, old->nickname,
1858                                collider, collider->nickname);
1859         silc_print_nick_change(server, collider->nickname,
1860                                client_entry->nickname, buf);
1861         g_slist_free(nicks);
1862       }
1863
1864       old = g_strdup(server->nick);
1865       server_change_nick(SERVER(server), client_entry->nickname);
1866       nicklist_rename_unique(SERVER(server),
1867                              server->conn->local_entry, server->nick,
1868                              client_entry, client_entry->nickname);
1869       signal_emit("message own_nick", 4, server, server->nick, old, "");
1870       g_free(old);
1871       break;
1872     }
1873
1874   case SILC_COMMAND_LIST:
1875     {
1876       char *topic, *name;
1877       int usercount;
1878       char users[20];
1879       char tmp[256], *cp, *dm = NULL;
1880
1881       if (!success)
1882         return;
1883
1884       (void)va_arg(vp, SilcChannelEntry);
1885       name = va_arg(vp, char *);
1886       topic = va_arg(vp, char *);
1887       usercount = va_arg(vp, int);
1888
1889       if (topic && !silc_term_utf8() &&
1890           silc_utf8_valid(topic, strlen(topic))) {
1891         memset(tmp, 0, sizeof(tmp));
1892         cp = tmp;
1893         if (strlen(topic) > sizeof(tmp) - 1) {
1894           dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
1895           cp = dm;
1896         }
1897
1898         silc_utf8_decode(topic, strlen(topic), SILC_STRING_LANGUAGE,
1899                          cp, strlen(topic));
1900         topic = cp;
1901       }
1902
1903       if (status == SILC_STATUS_LIST_START ||
1904           status == SILC_STATUS_OK)
1905         printformat_module("fe-common/silc", server, NULL,
1906                            MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
1907
1908       if (!usercount)
1909         snprintf(users, sizeof(users) - 1, "N/A");
1910       else
1911         snprintf(users, sizeof(users) - 1, "%d", usercount);
1912       printformat_module("fe-common/silc", server, NULL,
1913                          MSGLEVEL_CRAP, SILCTXT_LIST,
1914                          name, users, topic ? topic : "");
1915       silc_free(dm);
1916     }
1917     break;
1918
1919   case SILC_COMMAND_UMODE:
1920     {
1921       SilcUInt32 mode;
1922       char *reason;
1923
1924       if (!success)
1925         return;
1926
1927       mode = va_arg(vp, SilcUInt32);
1928
1929       if (mode & SILC_UMODE_SERVER_OPERATOR &&
1930           !(server->umode & SILC_UMODE_SERVER_OPERATOR))
1931         printformat_module("fe-common/silc", server, NULL,
1932                            MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1933
1934       if (mode & SILC_UMODE_ROUTER_OPERATOR &&
1935           !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
1936         printformat_module("fe-common/silc", server, NULL,
1937                            MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1938
1939       if ((mode & SILC_UMODE_GONE) != (server->umode & SILC_UMODE_GONE)) {
1940         if (mode & SILC_UMODE_GONE) {
1941           if ((server->away_reason != NULL) && (server->away_reason[0] != '\0'))
1942             reason = g_strdup(server->away_reason);
1943           else
1944             reason = g_strdup("away");
1945         } else
1946           reason = g_strdup("");
1947
1948         silc_set_away(reason, server);
1949
1950         g_free(reason);
1951       }
1952
1953       server->umode = mode;
1954       signal_emit("user mode changed", 2, server, NULL);
1955     }
1956     break;
1957
1958   case SILC_COMMAND_OPER:
1959     if (!success)
1960       return;
1961
1962     server->umode |= SILC_UMODE_SERVER_OPERATOR;
1963     signal_emit("user mode changed", 2, server, NULL);
1964
1965     printformat_module("fe-common/silc", server, NULL,
1966                        MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1967     break;
1968
1969   case SILC_COMMAND_SILCOPER:
1970     if (!success)
1971       return;
1972
1973     server->umode |= SILC_UMODE_ROUTER_OPERATOR;
1974     signal_emit("user mode changed", 2, server, NULL);
1975
1976     printformat_module("fe-common/silc", server, NULL,
1977                        MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1978     break;
1979
1980   case SILC_COMMAND_USERS:
1981     {
1982       SilcHashTableList htl;
1983       SilcChannelEntry channel;
1984       SilcChannelUser chu;
1985
1986       if (!success)
1987         return;
1988
1989       channel = va_arg(vp, SilcChannelEntry);
1990
1991       printformat_module("fe-common/silc", server, channel->channel_name,
1992                          MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
1993                          channel->channel_name);
1994
1995       silc_hash_table_list(channel->user_list, &htl);
1996       while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1997         SilcClientEntry e = chu->client;
1998         char stat[5], *mode;
1999
2000         if (!e->nickname)
2001           continue;
2002
2003         memset(stat, 0, sizeof(stat));
2004         mode = silc_client_chumode_char(chu->mode);
2005         if (e->mode & SILC_UMODE_GONE)
2006           strcat(stat, "G");
2007         else if (e->mode & SILC_UMODE_INDISPOSED)
2008           strcat(stat, "I");
2009         else if (e->mode & SILC_UMODE_BUSY)
2010           strcat(stat, "B");
2011         else if (e->mode & SILC_UMODE_PAGE)
2012           strcat(stat, "P");
2013         else if (e->mode & SILC_UMODE_HYPER)
2014           strcat(stat, "H");
2015         else if (e->mode & SILC_UMODE_ROBOT)
2016           strcat(stat, "R");
2017         else if (e->mode & SILC_UMODE_ANONYMOUS)
2018           strcat(stat, "?");
2019         else
2020           strcat(stat, "A");
2021         if (mode)
2022           strcat(stat, mode);
2023
2024         printformat_module("fe-common/silc", server, channel->channel_name,
2025                            MSGLEVEL_CRAP, SILCTXT_USERS,
2026                            e->nickname, stat,
2027                            e->username ? e->username : "",
2028                            e->hostname ? e->hostname : "",
2029                            e->realname ? e->realname : "");
2030         if (mode)
2031           silc_free(mode);
2032       }
2033       silc_hash_table_list_reset(&htl);
2034     }
2035     break;
2036
2037   case SILC_COMMAND_BAN:
2038     {
2039       SilcChannelEntry channel;
2040       SilcBuffer payload;
2041       SilcArgumentPayload ban_list;
2042       SilcUInt16 argc;
2043
2044       if (!success)
2045         return;
2046
2047       channel = va_arg(vp, SilcChannelEntry);
2048       payload = va_arg(vp, SilcBuffer);
2049
2050       if (payload) {
2051         SILC_GET16_MSB(argc, payload->data);
2052         ban_list = silc_argument_payload_parse(payload->data + 2,
2053                                                payload->len - 2, argc);
2054         if (ban_list) {
2055           silc_parse_inviteban_list(client, conn, server, channel,
2056                                     "ban", ban_list);
2057           silc_argument_payload_free(ban_list);
2058         }
2059       }
2060     }
2061     break;
2062
2063   case SILC_COMMAND_GETKEY:
2064     {
2065       SilcIdType id_type;
2066       void *entry;
2067       SilcPublicKey public_key;
2068       unsigned char *pk;
2069       SilcUInt32 pk_len;
2070       GetkeyContext getkey;
2071       char *name;
2072
2073       if (!success)
2074         return;
2075
2076       id_type = va_arg(vp, SilcUInt32);
2077       entry = va_arg(vp, void *);
2078       public_key = va_arg(vp, SilcPublicKey);
2079
2080       if (public_key) {
2081         pk = silc_pkcs_public_key_encode(public_key, &pk_len);
2082
2083         getkey = silc_calloc(1, sizeof(*getkey));
2084         getkey->entry = entry;
2085         getkey->id_type = id_type;
2086         getkey->client = client;
2087         getkey->conn = conn;
2088         getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2089
2090         name = (id_type == SILC_ID_CLIENT ?
2091                 ((SilcClientEntry)entry)->nickname :
2092                 ((SilcServerEntry)entry)->server_name);
2093
2094         silc_verify_public_key_internal(client, conn, name,
2095                                         (id_type == SILC_ID_CLIENT ?
2096                                          SILC_SOCKET_TYPE_CLIENT :
2097                                          SILC_SOCKET_TYPE_SERVER),
2098                                         pk, pk_len, SILC_SKE_PK_TYPE_SILC,
2099                                         silc_getkey_cb, getkey);
2100         silc_free(pk);
2101       } else {
2102         printformat_module("fe-common/silc", server, NULL,
2103                            MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOKEY);
2104       }
2105     }
2106     break;
2107
2108   case SILC_COMMAND_INFO:
2109     {
2110       SilcServerEntry server_entry;
2111       char *server_name;
2112       char *server_info;
2113
2114       if (!success)
2115         return;
2116
2117       server_entry = va_arg(vp, SilcServerEntry);
2118       server_name = va_arg(vp, char *);
2119       server_info = va_arg(vp, char *);
2120
2121       if (server_name && server_info )
2122         {
2123           printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
2124           printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
2125         }
2126     }
2127     break;
2128
2129   case SILC_COMMAND_TOPIC:
2130     {
2131       SilcChannelEntry channel;
2132       char *topic;
2133       char tmp[256], *cp, *dm = NULL;
2134
2135       if (!success)
2136         return;
2137
2138       channel = va_arg(vp, SilcChannelEntry);
2139       topic = va_arg(vp, char *);
2140
2141       if (topic && !silc_term_utf8() &&
2142           silc_utf8_valid(topic, strlen(topic))) {
2143         memset(tmp, 0, sizeof(tmp));
2144         cp = tmp;
2145         if (strlen(topic) > sizeof(tmp) - 1) {
2146           dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
2147           cp = dm;
2148         }
2149
2150         silc_utf8_decode(topic, strlen(topic), SILC_STRING_LANGUAGE,
2151                          cp, strlen(topic));
2152         topic = cp;
2153       }
2154
2155       if (topic) {
2156         chanrec = silc_channel_find_entry(server, channel);
2157         if (chanrec) {
2158           g_free_not_null(chanrec->topic);
2159           chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
2160           signal_emit("channel topic changed", 1, chanrec);
2161         }
2162         printformat_module("fe-common/silc", server, channel->channel_name,
2163                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
2164                            channel->channel_name, topic);
2165       } else {
2166         printformat_module("fe-common/silc", server, channel->channel_name,
2167                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
2168                            channel->channel_name);
2169       }
2170       silc_free(dm);
2171     }
2172     break;
2173
2174   case SILC_COMMAND_WATCH:
2175     break;
2176
2177   case SILC_COMMAND_STATS:
2178     {
2179       SilcUInt32 starttime, uptime, my_clients, my_channels, my_server_ops,
2180                  my_router_ops, cell_clients, cell_channels, cell_servers,
2181                  clients, channels, servers, routers, server_ops, router_ops;
2182       SilcUInt32 buf_len;
2183       SilcBufferStruct buf;
2184       unsigned char *tmp_buf;
2185       char tmp[40];
2186       const char *tmptime;
2187       int days, hours, mins, secs;
2188
2189       if (!success)
2190         return;
2191
2192       tmp_buf = va_arg(vp, unsigned char *);
2193       buf_len = va_arg(vp, SilcUInt32);
2194
2195       if (!tmp_buf || !buf_len) {
2196         printtext(server, NULL, MSGLEVEL_CRAP, "No statistics available");
2197         return;
2198       }
2199
2200       /* Get statistics structure */
2201       silc_buffer_set(&buf, tmp_buf, buf_len);
2202       silc_buffer_unformat(&buf,
2203                            SILC_STR_UI_INT(&starttime),
2204                            SILC_STR_UI_INT(&uptime),
2205                            SILC_STR_UI_INT(&my_clients),
2206                            SILC_STR_UI_INT(&my_channels),
2207                            SILC_STR_UI_INT(&my_server_ops),
2208                            SILC_STR_UI_INT(&my_router_ops),
2209                            SILC_STR_UI_INT(&cell_clients),
2210                            SILC_STR_UI_INT(&cell_channels),
2211                            SILC_STR_UI_INT(&cell_servers),
2212                            SILC_STR_UI_INT(&clients),
2213                            SILC_STR_UI_INT(&channels),
2214                            SILC_STR_UI_INT(&servers),
2215                            SILC_STR_UI_INT(&routers),
2216                            SILC_STR_UI_INT(&server_ops),
2217                            SILC_STR_UI_INT(&router_ops),
2218                            SILC_STR_END);
2219
2220       tmptime = silc_get_time(starttime);
2221       printformat_module("fe-common/silc", server, NULL,
2222                          MSGLEVEL_CRAP, SILCTXT_STATS,
2223                          "Local server start time", tmptime);
2224
2225       days = uptime / (24 * 60 * 60);
2226       uptime -= days * (24 * 60 * 60);
2227       hours = uptime / (60 * 60);
2228       uptime -= hours * (60 * 60);
2229       mins = uptime / 60;
2230       uptime -= mins * 60;
2231       secs = uptime;
2232       snprintf(tmp, sizeof(tmp) - 1, "%d days %d hours %d mins %d secs",
2233                days, hours, mins, secs);
2234       printformat_module("fe-common/silc", server, NULL,
2235                          MSGLEVEL_CRAP, SILCTXT_STATS,
2236                          "Local server uptime", tmp);
2237
2238       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_clients);
2239       printformat_module("fe-common/silc", server, NULL,
2240                          MSGLEVEL_CRAP, SILCTXT_STATS,
2241                          "Local server clients", tmp);
2242
2243       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_channels);
2244       printformat_module("fe-common/silc", server, NULL,
2245                          MSGLEVEL_CRAP, SILCTXT_STATS,
2246                          "Local server channels", tmp);
2247
2248       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_server_ops);
2249       printformat_module("fe-common/silc", server, NULL,
2250                          MSGLEVEL_CRAP, SILCTXT_STATS,
2251                          "Local server operators", tmp);
2252
2253       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_router_ops);
2254       printformat_module("fe-common/silc", server, NULL,
2255                          MSGLEVEL_CRAP, SILCTXT_STATS,
2256                          "Local router operators", tmp);
2257
2258       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_clients);
2259       printformat_module("fe-common/silc", server, NULL,
2260                          MSGLEVEL_CRAP, SILCTXT_STATS,
2261                          "Local cell clients", tmp);
2262
2263       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_channels);
2264       printformat_module("fe-common/silc", server, NULL,
2265                          MSGLEVEL_CRAP, SILCTXT_STATS,
2266                          "Local cell channels", tmp);
2267
2268       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_servers);
2269       printformat_module("fe-common/silc", server, NULL,
2270                          MSGLEVEL_CRAP, SILCTXT_STATS,
2271                          "Local cell servers", tmp);
2272
2273       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)clients);
2274       printformat_module("fe-common/silc", server, NULL,
2275                          MSGLEVEL_CRAP, SILCTXT_STATS,
2276                          "Total clients", tmp);
2277
2278       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)channels);
2279       printformat_module("fe-common/silc", server, NULL,
2280                          MSGLEVEL_CRAP, SILCTXT_STATS,
2281                          "Total channels", tmp);
2282
2283       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)servers);
2284       printformat_module("fe-common/silc", server, NULL,
2285                          MSGLEVEL_CRAP, SILCTXT_STATS,
2286                          "Total servers", tmp);
2287
2288       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)routers);
2289       printformat_module("fe-common/silc", server, NULL,
2290                          MSGLEVEL_CRAP, SILCTXT_STATS,
2291                          "Total routers", tmp);
2292
2293       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)server_ops);
2294       printformat_module("fe-common/silc", server, NULL,
2295                          MSGLEVEL_CRAP, SILCTXT_STATS,
2296                            "Total server operators", tmp);
2297
2298       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)router_ops);
2299       printformat_module("fe-common/silc", server, NULL,
2300                          MSGLEVEL_CRAP, SILCTXT_STATS,
2301                          "Total router operators", tmp);
2302     }
2303     break;
2304
2305   case SILC_COMMAND_CMODE:
2306     {
2307       SilcChannelEntry channel_entry;
2308       SilcBuffer channel_pubkeys;
2309
2310       channel_entry = va_arg(vp, SilcChannelEntry);
2311       (void)va_arg(vp, SilcUInt32);
2312       (void)va_arg(vp, SilcPublicKey);
2313       channel_pubkeys = va_arg(vp, SilcBuffer);
2314
2315       if (!success || !cmode_list_chpks ||
2316           !channel_entry || !channel_entry->channel_name)
2317         return;
2318
2319       /* Print the channel public key list */
2320       if (channel_pubkeys)
2321         silc_parse_channel_public_keys(server, channel_entry, channel_pubkeys);
2322       else
2323         printformat_module("fe-common/silc", server, NULL,
2324                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_PK_NO_LIST,
2325                            channel_entry->channel_name);
2326     }
2327     break;
2328
2329   }
2330
2331   va_end(vp);
2332 }
2333
2334 typedef struct {
2335   SilcClient client;
2336   SilcClientConnection conn;
2337   char *filename;
2338   char *entity;
2339   char *entity_name;
2340   unsigned char *pk;
2341   SilcUInt32 pk_len;
2342   SilcSKEPKType pk_type;
2343   SilcVerifyPublicKey completion;
2344   void *context;
2345 } *PublicKeyVerify;
2346
2347 static void verify_public_key_completion(const char *line, void *context)
2348 {
2349   PublicKeyVerify verify = (PublicKeyVerify)context;
2350
2351   if (line[0] == 'Y' || line[0] == 'y') {
2352     /* Call the completion */
2353     if (verify->completion)
2354       verify->completion(TRUE, verify->context);
2355
2356     /* Save the key for future checking */
2357     silc_pkcs_save_public_key_data(verify->filename, verify->pk,
2358                                    verify->pk_len, SILC_PKCS_FILE_PEM);
2359   } else {
2360     /* Call the completion */
2361     if (verify->completion)
2362       verify->completion(FALSE, verify->context);
2363
2364     printformat_module("fe-common/silc", NULL, NULL,
2365                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD,
2366                        verify->entity_name ? verify->entity_name :
2367                        verify->entity);
2368   }
2369
2370   silc_free(verify->filename);
2371   silc_free(verify->entity);
2372   silc_free(verify->entity_name);
2373   silc_free(verify->pk);
2374   silc_free(verify);
2375 }
2376
2377 /* Internal routine to verify public key. If the `completion' is provided
2378    it will be called to indicate whether public was verified or not. For
2379    server/router public key this will check for filename that includes the
2380    remote host's IP address and remote host's hostname. */
2381
2382 static void
2383 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
2384                                 const char *name, SilcSocketType conn_type,
2385                                 unsigned char *pk, SilcUInt32 pk_len,
2386                                 SilcSKEPKType pk_type,
2387                                 SilcVerifyPublicKey completion, void *context)
2388 {
2389   int i;
2390   char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
2391   char *fingerprint, *babbleprint, *format;
2392   struct passwd *pw;
2393   struct stat st;
2394   char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
2395                    conn_type == SILC_SOCKET_TYPE_ROUTER) ?
2396                   "server" : "client");
2397   PublicKeyVerify verify;
2398
2399   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
2400     printformat_module("fe-common/silc", NULL, NULL,
2401                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
2402                        entity, pk_type);
2403     if (completion)
2404       completion(FALSE, context);
2405     return;
2406   }
2407
2408   pw = getpwuid(getuid());
2409   if (!pw) {
2410     if (completion)
2411       completion(FALSE, context);
2412     return;
2413   }
2414
2415   memset(filename, 0, sizeof(filename));
2416   memset(filename2, 0, sizeof(filename2));
2417   memset(file, 0, sizeof(file));
2418
2419   if (conn_type == SILC_SOCKET_TYPE_SERVER ||
2420       conn_type == SILC_SOCKET_TYPE_ROUTER) {
2421     if (!name) {
2422       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2423                conn->sock->ip, conn->sock->port);
2424       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2425                get_irssi_dir(), entity, file);
2426
2427       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2428                conn->sock->hostname, conn->sock->port);
2429       snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s",
2430                get_irssi_dir(), entity, file);
2431
2432       ipf = filename;
2433       hostf = filename2;
2434     } else {
2435       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2436                name, conn->sock->port);
2437       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2438                get_irssi_dir(), entity, file);
2439
2440       ipf = filename;
2441     }
2442   } else {
2443     /* Replace all whitespaces with `_'. */
2444     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2445     for (i = 0; i < strlen(fingerprint); i++)
2446       if (fingerprint[i] == ' ')
2447         fingerprint[i] = '_';
2448
2449     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
2450     snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2451              get_irssi_dir(), entity, file);
2452     silc_free(fingerprint);
2453
2454     ipf = filename;
2455   }
2456
2457   /* Take fingerprint of the public key */
2458   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2459   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
2460
2461   verify = silc_calloc(1, sizeof(*verify));
2462   verify->client = client;
2463   verify->conn = conn;
2464   verify->filename = strdup(ipf);
2465   verify->entity = strdup(entity);
2466   verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ?
2467                          (name ? strdup(name) : strdup(conn->sock->hostname))
2468                          : NULL);
2469   verify->pk = silc_memdup(pk, pk_len);
2470   verify->pk_len = pk_len;
2471   verify->pk_type = pk_type;
2472   verify->completion = completion;
2473   verify->context = context;
2474
2475   /* Check whether this key already exists */
2476   if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
2477     /* Key does not exist, ask user to verify the key and save it */
2478
2479     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2480                        SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2481                        verify->entity_name : entity);
2482     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2483                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2484     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2485                        SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2486     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2487                              SILCTXT_PUBKEY_ACCEPT);
2488     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2489                             format, 0, verify);
2490     g_free(format);
2491     silc_free(fingerprint);
2492     return;
2493   } else {
2494     /* The key already exists, verify it. */
2495     SilcPublicKey public_key;
2496     unsigned char *encpk;
2497     SilcUInt32 encpk_len;
2498
2499     /* Load the key file, try for both IP filename and hostname filename */
2500     if (!silc_pkcs_load_public_key(ipf, &public_key,
2501                                    SILC_PKCS_FILE_PEM) &&
2502         !silc_pkcs_load_public_key(ipf, &public_key,
2503                                    SILC_PKCS_FILE_BIN) &&
2504         (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key,
2505                                                SILC_PKCS_FILE_PEM) &&
2506                     !silc_pkcs_load_public_key(hostf, &public_key,
2507                                                SILC_PKCS_FILE_BIN)))) {
2508       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2509                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2510                          verify->entity_name : entity);
2511       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2512                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2513       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2514                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2515       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2516                          SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
2517       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2518                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2519       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2520                               format, 0, verify);
2521       g_free(format);
2522       silc_free(fingerprint);
2523       return;
2524     }
2525
2526     /* Encode the key data */
2527     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
2528     if (!encpk) {
2529       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2530                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2531                          verify->entity_name : entity);
2532       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2533                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2534       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2535                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2536       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2537                          SILCTXT_PUBKEY_MALFORMED, entity);
2538       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2539                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2540       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2541                               format, 0, verify);
2542       g_free(format);
2543       silc_free(fingerprint);
2544       return;
2545     }
2546
2547     /* Compare the keys */
2548     if (memcmp(encpk, pk, encpk_len)) {
2549       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2550                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2551                          verify->entity_name : entity);
2552       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2553                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2554       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2555                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2556       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2557                          SILCTXT_PUBKEY_NO_MATCH, entity);
2558       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2559                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
2560       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2561                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
2562
2563       /* Ask user to verify the key and save it */
2564       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2565                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2566       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2567                               format, 0, verify);
2568       g_free(format);
2569       silc_free(fingerprint);
2570       return;
2571     }
2572
2573     /* Local copy matched */
2574     if (completion)
2575       completion(TRUE, context);
2576     silc_free(fingerprint);
2577     silc_free(verify->filename);
2578     silc_free(verify->entity);
2579     silc_free(verify->entity_name);
2580     silc_free(verify->pk);
2581     silc_free(verify);
2582   }
2583 }
2584
2585 /* Verifies received public key. The `conn_type' indicates which entity
2586    (server, client etc.) has sent the public key. If user decides to trust
2587    the key may be saved as trusted public key for later use. The
2588    `completion' must be called after the public key has been verified. */
2589
2590 void
2591 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
2592                        SilcSocketType conn_type, unsigned char *pk,
2593                        SilcUInt32 pk_len, SilcSKEPKType pk_type,
2594                        SilcVerifyPublicKey completion, void *context)
2595 {
2596   silc_verify_public_key_internal(client, conn, NULL, conn_type, pk,
2597                                   pk_len, pk_type,
2598                                   completion, context);
2599 }
2600
2601 /* Asks passphrase from user on the input line. */
2602
2603 typedef struct {
2604   SilcAskPassphrase completion;
2605   void *context;
2606 } *AskPassphrase;
2607
2608 void ask_passphrase_completion(const char *passphrase, void *context)
2609 {
2610   AskPassphrase p = (AskPassphrase)context;
2611   if (passphrase && passphrase[0] == '\0')
2612     passphrase = NULL;
2613   p->completion((unsigned char *)passphrase,
2614                 passphrase ? strlen(passphrase) : 0, p->context);
2615   silc_free(p);
2616 }
2617
2618 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
2619                          SilcAskPassphrase completion, void *context)
2620 {
2621   AskPassphrase p = silc_calloc(1, sizeof(*p));
2622   p->completion = completion;
2623   p->context = context;
2624
2625   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
2626                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
2627 }
2628
2629 typedef struct {
2630   SilcGetAuthMeth completion;
2631   void *context;
2632 } *InternalGetAuthMethod;
2633
2634 /* Callback called when we've received the authentication method information
2635    from the server after we've requested it. This will get the authentication
2636    data from the user if needed. */
2637
2638 static void silc_get_auth_method_callback(SilcClient client,
2639                                           SilcClientConnection conn,
2640                                           SilcAuthMethod auth_meth,
2641                                           void *context)
2642 {
2643   InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
2644
2645   SILC_LOG_DEBUG(("Start"));
2646
2647   switch (auth_meth) {
2648   case SILC_AUTH_NONE:
2649     /* No authentication required. */
2650     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2651     break;
2652   case SILC_AUTH_PASSWORD:
2653     {
2654       /* Check whether we find the password for this server in our
2655          configuration.  If not, then don't provide so library will ask
2656          it from the user. */
2657       SERVER_SETUP_REC *setup = server_setup_find_port(conn->remote_host,
2658                                                        conn->remote_port);
2659       if (!setup || !setup->password) {
2660         (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2661         break;
2662       }
2663
2664       (*internal->completion)(TRUE, auth_meth, setup->password,
2665                               strlen(setup->password), internal->context);
2666     }
2667     break;
2668   case SILC_AUTH_PUBLIC_KEY:
2669     /* Do not get the authentication data now, the library will generate
2670        it using our default key, if we do not provide it here. */
2671     /* XXX In the future when we support multiple local keys and multiple
2672        local certificates we will need to ask from user which one to use. */
2673     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2674     break;
2675   }
2676
2677   silc_free(internal);
2678 }
2679
2680 /* Find authentication method and authentication data by hostname and
2681    port. The hostname may be IP address as well. The found authentication
2682    method and authentication data is returned to `auth_meth', `auth_data'
2683    and `auth_data_len'. The function returns TRUE if authentication method
2684    is found and FALSE if not. `conn' may be NULL. */
2685
2686 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
2687                           char *hostname, SilcUInt16 port,
2688                           SilcGetAuthMeth completion, void *context)
2689 {
2690   InternalGetAuthMethod internal;
2691
2692   SILC_LOG_DEBUG(("Start"));
2693
2694   /* If we do not have this connection configured by the user in a
2695      configuration file then resolve the authentication method from the
2696      server for this session. */
2697   internal = silc_calloc(1, sizeof(*internal));
2698   internal->completion = completion;
2699   internal->context = context;
2700
2701   silc_client_request_authentication_method(client, conn,
2702                                             silc_get_auth_method_callback,
2703                                             internal);
2704 }
2705
2706 /* Notifies application that failure packet was received.  This is called
2707    if there is some protocol active in the client.  The `protocol' is the
2708    protocol context.  The `failure' is opaque pointer to the failure
2709    indication.  Note, that the `failure' is protocol dependant and application
2710    must explicitly cast it to correct type.  Usually `failure' is 32 bit
2711    failure type (see protocol specs for all protocol failure types). */
2712
2713 void silc_failure(SilcClient client, SilcClientConnection conn,
2714                   SilcProtocol protocol, void *failure)
2715 {
2716   SILC_LOG_DEBUG(("Start"));
2717
2718   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
2719     SilcSKEStatus status = (SilcSKEStatus)failure;
2720
2721     if (status == SILC_SKE_STATUS_BAD_VERSION)
2722       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2723                          SILCTXT_KE_BAD_VERSION);
2724     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
2725       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2726                          SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
2727     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
2728       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2729                          SILCTXT_KE_UNKNOWN_GROUP);
2730     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
2731       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2732                          SILCTXT_KE_UNKNOWN_CIPHER);
2733     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
2734       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2735                          SILCTXT_KE_UNKNOWN_PKCS);
2736     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
2737       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2738                          SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
2739     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
2740       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2741                          SILCTXT_KE_UNKNOWN_HMAC);
2742     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
2743       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2744                          SILCTXT_KE_INCORRECT_SIGNATURE);
2745     if (status == SILC_SKE_STATUS_INVALID_COOKIE)
2746       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2747                          SILCTXT_KE_INVALID_COOKIE);
2748   }
2749
2750   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
2751     SilcUInt32 err = (SilcUInt32)failure;
2752
2753     if (err == SILC_AUTH_FAILED)
2754       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2755                          SILCTXT_AUTH_FAILED);
2756   }
2757 }
2758
2759 /* Asks whether the user would like to perform the key agreement protocol.
2760    This is called after we have received an key agreement packet or an
2761    reply to our key agreement packet. This returns TRUE if the user wants
2762    the library to perform the key agreement protocol and FALSE if it is not
2763    desired (application may start it later by calling the function
2764    silc_client_perform_key_agreement). */
2765
2766 bool silc_key_agreement(SilcClient client, SilcClientConnection conn,
2767                         SilcClientEntry client_entry, const char *hostname,
2768                         SilcUInt16 port, SilcKeyAgreementCallback *completion,
2769                         void **context)
2770 {
2771   char portstr[12];
2772
2773   SILC_LOG_DEBUG(("Start"));
2774
2775   /* We will just display the info on the screen and return FALSE and user
2776      will have to start the key agreement with a command. */
2777
2778   if (hostname)
2779     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2780
2781   if (!hostname)
2782     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2783                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
2784   else
2785     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2786                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
2787                        client_entry->nickname, hostname, portstr);
2788
2789   *completion = NULL;
2790   *context = NULL;
2791
2792   return FALSE;
2793 }
2794
2795 /* Notifies application that file transfer protocol session is being
2796    requested by the remote client indicated by the `client_entry' from
2797    the `hostname' and `port'. The `session_id' is the file transfer
2798    session and it can be used to either accept or reject the file
2799    transfer request, by calling the silc_client_file_receive or
2800    silc_client_file_close, respectively. */
2801
2802 void silc_ftp(SilcClient client, SilcClientConnection conn,
2803               SilcClientEntry client_entry, SilcUInt32 session_id,
2804               const char *hostname, SilcUInt16 port)
2805 {
2806   SILC_SERVER_REC *server;
2807   char portstr[12];
2808   FtpSession ftp = NULL;
2809
2810   SILC_LOG_DEBUG(("Start"));
2811
2812   server = conn->context;
2813
2814   silc_dlist_start(server->ftp_sessions);
2815   while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
2816     if (ftp->client_entry == client_entry &&
2817         ftp->session_id == session_id) {
2818       server->current_session = ftp;
2819       break;
2820     }
2821   }
2822   if (ftp == SILC_LIST_END) {
2823     ftp = silc_calloc(1, sizeof(*ftp));
2824     ftp->client_entry = client_entry;
2825     ftp->session_id = session_id;
2826     ftp->send = FALSE;
2827     ftp->conn = conn;
2828     silc_dlist_add(server->ftp_sessions, ftp);
2829     server->current_session = ftp;
2830   }
2831
2832   if (hostname)
2833     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2834
2835   if (!hostname)
2836     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2837                        SILCTXT_FILE_REQUEST, client_entry->nickname);
2838   else
2839     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2840                        SILCTXT_FILE_REQUEST_HOST,
2841                        client_entry->nickname, hostname, portstr);
2842 }
2843
2844 /* Delivers SILC session detachment data indicated by `detach_data' to the
2845    application.  If application has issued SILC_COMMAND_DETACH command
2846    the client session in the SILC network is not quit.  The client remains
2847    in the network but is detached.  The detachment data may be used later
2848    to resume the session in the SILC Network.  The appliation is
2849    responsible of saving the `detach_data', to for example in a file.
2850
2851    The detachment data can be given as argument to the functions
2852    silc_client_connect_to_server, or silc_client_add_connection when
2853    creating connection to remote server, inside SilcClientConnectionParams
2854    structure.  If it is provided the client library will attempt to resume
2855    the session in the network.  After the connection is created
2856    successfully, the application is responsible of setting the user
2857    interface for user into the same state it was before detaching (showing
2858    same channels, channel modes, etc).  It can do this by fetching the
2859    information (like joined channels) from the client library. */
2860
2861 void
2862 silc_detach(SilcClient client, SilcClientConnection conn,
2863             const unsigned char *detach_data, SilcUInt32 detach_data_len)
2864 {
2865   char file[256];
2866
2867   /* Save the detachment data to file. */
2868
2869   memset(file, 0, sizeof(file));
2870   snprintf(file, sizeof(file) - 1, "%s/session", get_irssi_dir());
2871   silc_file_writefile(file, detach_data, detach_data_len);
2872 }
2873
2874
2875 /* SILC client operations */
2876 SilcClientOperations ops = {
2877   silc_say,
2878   silc_channel_message,
2879   silc_private_message,
2880   silc_notify,
2881   silc_command,
2882   silc_command_reply,
2883   silc_connect,
2884   silc_disconnect,
2885   silc_get_auth_method,
2886   silc_verify_public_key,
2887   silc_ask_passphrase,
2888   silc_failure,
2889   silc_key_agreement,
2890   silc_ftp,
2891   silc_detach,
2892 };