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