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