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