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