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