updates.
[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     silc_free(tmp);
549     break;
550
551   case SILC_NOTIFY_TYPE_MOTD:
552     /*
553      * Received MOTD.
554      */
555
556     SILC_LOG_DEBUG(("Notify: MOTD"));
557
558     tmp = va_arg(va, char *);
559
560     if (!settings_get_bool("skip_motd"))
561       printtext_multiline(server, NULL, MSGLEVEL_CRAP, "%s", tmp);
562     break;
563
564   case SILC_NOTIFY_TYPE_KICKED:
565     /*
566      * Someone was kicked from channel.
567      */
568
569     SILC_LOG_DEBUG(("Notify: KICKED"));
570
571     client_entry = va_arg(va, SilcClientEntry);
572     tmp = va_arg(va, char *);
573     client_entry2 = va_arg(va, SilcClientEntry);
574     channel = va_arg(va, SilcChannelEntry);
575
576     chanrec = silc_channel_find_entry(server, channel);
577   
578     if (client_entry == conn->local_entry) {
579       printformat_module("fe-common/silc", server, channel->channel_name,
580                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED_YOU, 
581                          channel->channel_name, 
582                          client_entry ? client_entry2->nickname : "",
583                          tmp ? tmp : "");
584       if (chanrec) {
585         chanrec->kicked = TRUE;
586         channel_destroy((CHANNEL_REC *)chanrec);
587       }
588     } else {
589       printformat_module("fe-common/silc", server, channel->channel_name,
590                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED, 
591                          client_entry->nickname, channel->channel_name, 
592                          client_entry2 ? client_entry2->nickname : "", 
593                          tmp ? tmp : "");
594
595       if (chanrec) {
596         SILC_NICK_REC *nickrec = silc_nicklist_find(chanrec, client_entry);
597         if (nickrec != NULL)
598           nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
599       }
600     }
601     break;
602
603   case SILC_NOTIFY_TYPE_KILLED:
604     /*
605      * Someone was killed from the network.
606      */
607
608     SILC_LOG_DEBUG(("Notify: KILLED"));
609
610     client_entry = va_arg(va, SilcClientEntry);
611     tmp = va_arg(va, char *);
612     idtype = va_arg(va, int);
613     entry = va_arg(va, SilcClientEntry);
614   
615     if (client_entry == conn->local_entry) {
616       if (idtype == SILC_ID_CLIENT) {
617         client_entry2 = (SilcClientEntry)entry;
618         printformat_module("fe-common/silc", server, NULL,
619                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU, 
620                            client_entry2 ? client_entry2->nickname : "",
621                            tmp ? tmp : "");
622       } else if (idtype == SILC_ID_SERVER) {
623         server_entry = (SilcServerEntry)entry;
624         printformat_module("fe-common/silc", server, NULL,
625                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU, 
626                            server_entry->server_name, tmp ? tmp : "");
627       } else if (idtype == SILC_ID_CHANNEL) {
628         channel = (SilcChannelEntry)entry;
629         printformat_module("fe-common/silc", server, NULL,
630                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU, 
631                            channel->channel_name, tmp ? tmp : "");
632       }
633     } else {
634       list1 = nicklist_get_same_unique(SERVER(server), client_entry);
635       for (list_tmp = list1; list_tmp != NULL; list_tmp = 
636              list_tmp->next->next) {
637         CHANNEL_REC *channel = list_tmp->data;
638         NICK_REC *nickrec = list_tmp->next->data;
639         nicklist_remove(channel, nickrec);
640       }
641
642       if (idtype == SILC_ID_CLIENT) {
643         client_entry2 = (SilcClientEntry)entry;
644         printformat_module("fe-common/silc", server, NULL,
645                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED, 
646                            client_entry->nickname,
647                            client_entry2 ? client_entry2->nickname : "",
648                            tmp ? tmp : "");
649       } else if (idtype == SILC_ID_SERVER) {
650         server_entry = (SilcServerEntry)entry;
651         printformat_module("fe-common/silc", server, NULL,
652                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED, 
653                            client_entry->nickname,
654                            server_entry->server_name, tmp ? tmp : "");
655       } else if (idtype == SILC_ID_CHANNEL) {
656         channel = (SilcChannelEntry)entry;
657         printformat_module("fe-common/silc", server, NULL,
658                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED, 
659                            client_entry->nickname,
660                            channel->channel_name, tmp ? tmp : "");
661       }
662     }
663     break;
664
665   case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
666     break;
667
668   case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
669     {
670       /*
671        * Server has quit the network.
672        */
673       int i;
674       SilcClientEntry *clients;
675       SilcUInt32 clients_count;
676
677       SILC_LOG_DEBUG(("Notify: SIGNOFF"));
678       
679       (void)va_arg(va, void *);
680       clients = va_arg(va, SilcClientEntry *);
681       clients_count = va_arg(va, SilcUInt32);
682   
683       for (i = 0; i < clients_count; i++) {
684         memset(buf, 0, sizeof(buf));
685         if (clients[i]->username)
686           snprintf(buf, sizeof(buf) - 1, "%s@%s",
687                    clients[i]->username, clients[i]->hostname);
688         signal_emit("message quit", 4, server, clients[i]->nickname,
689                     clients[i]->username ? buf : "", 
690                     "server signoff");
691
692         silc_server_free_ftp(server, clients[i]);
693         
694         list1 = nicklist_get_same_unique(SERVER(server), clients[i]);
695         for (list_tmp = list1; list_tmp != NULL; list_tmp = 
696                list_tmp->next->next) {
697           CHANNEL_REC *channel = list_tmp->data;
698           NICK_REC *nickrec = list_tmp->next->data;
699           nicklist_remove(channel, nickrec);
700         }
701       }
702     }
703     break;
704
705   case SILC_NOTIFY_TYPE_ERROR:
706     {
707       SilcStatus error = va_arg(va, int);
708
709       silc_say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
710                 "%s", silc_get_status_message(error));
711     }
712     break;
713
714   case SILC_NOTIFY_TYPE_WATCH:
715     {
716       SilcNotifyType notify;
717
718       client_entry = va_arg(va, SilcClientEntry);
719       name = va_arg(va, char *);          /* Maybe NULL */
720       mode = va_arg(va, SilcUInt32);
721       notify = va_arg(va, int);
722
723       if (notify == SILC_NOTIFY_TYPE_NICK_CHANGE) {
724         if (name)
725           printformat_module("fe-common/silc", server, NULL,
726                              MSGLEVEL_CRAP, SILCTXT_WATCH_NICK_CHANGE,
727                              client_entry->nickname, name);
728         else
729           printformat_module("fe-common/silc", server, NULL,
730                              MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
731                              client_entry->nickname);
732       } else if (notify == SILC_NOTIFY_TYPE_UMODE_CHANGE) {
733         /* See if client was away and is now present */
734         if (!(mode & (SILC_UMODE_GONE | SILC_UMODE_INDISPOSED |
735                       SILC_UMODE_BUSY | SILC_UMODE_PAGE |
736                       SILC_UMODE_DETACHED)) &&
737             (client_entry->mode & SILC_UMODE_GONE ||
738              client_entry->mode & SILC_UMODE_INDISPOSED ||
739              client_entry->mode & SILC_UMODE_BUSY ||
740              client_entry->mode & SILC_UMODE_PAGE ||
741              client_entry->mode & SILC_UMODE_DETACHED)) {
742           printformat_module("fe-common/silc", server, NULL,
743                              MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
744                              client_entry->nickname);
745         }
746
747         if (mode) {
748           memset(buf, 0, sizeof(buf));
749           silc_get_umode_string(mode, buf, sizeof(buf) - 1);
750           printformat_module("fe-common/silc", server, NULL,
751                              MSGLEVEL_CRAP, SILCTXT_WATCH_UMODE_CHANGE,
752                              client_entry->nickname, buf);
753         }
754       } else if (notify == SILC_NOTIFY_TYPE_KILLED) {
755         printformat_module("fe-common/silc", server, NULL,
756                            MSGLEVEL_CRAP, SILCTXT_WATCH_KILLED,
757                            client_entry->nickname);
758       } else if (notify == SILC_NOTIFY_TYPE_SIGNOFF ||
759                  notify == SILC_NOTIFY_TYPE_SERVER_SIGNOFF) {
760         printformat_module("fe-common/silc", server, NULL,
761                            MSGLEVEL_CRAP, SILCTXT_WATCH_SIGNOFF,
762                            client_entry->nickname);
763       } else if (notify == SILC_NOTIFY_TYPE_NONE) {
764         /* Client logged in to the network */
765         printformat_module("fe-common/silc", server, NULL,
766                            MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
767                            client_entry->nickname);
768       }
769     }
770     break;
771
772   default:
773     /* Unknown notify */
774     printformat_module("fe-common/silc", server, NULL,
775                        MSGLEVEL_CRAP, SILCTXT_UNKNOWN_NOTIFY, type);
776     break;
777   }
778
779   va_end(va);
780 }
781
782 /* Called to indicate that connection was either successfully established
783    or connecting failed.  This is also the first time application receives
784    the SilcClientConnection object which it should save somewhere. */
785
786 void silc_connect(SilcClient client, SilcClientConnection conn,
787                   SilcClientConnectionStatus status)
788 {
789   SILC_SERVER_REC *server = conn->context;
790
791   if (!server) {
792     silc_client_close_connection(client, conn);
793     return;
794   }
795
796   switch (status) {
797   case SILC_CLIENT_CONN_SUCCESS:
798     /* We have successfully connected to server */
799     server->connected = TRUE;
800     signal_emit("event connected", 1, server);
801     break;
802
803   case SILC_CLIENT_CONN_SUCCESS_RESUME:
804     /* We have successfully resumed old detached session */
805     server->connected = TRUE;
806     signal_emit("event connected", 1, server);
807
808     /* If we resumed old session check whether we need to update 
809        our nickname */
810     if (strcmp(server->nick, conn->local_entry->nickname)) {
811       char *old;
812       old = g_strdup(server->nick);
813       server_change_nick(SERVER(server), conn->local_entry->nickname);
814       nicklist_rename_unique(SERVER(server), 
815                              conn->local_entry, server->nick,
816                              conn->local_entry, conn->local_entry->nickname);
817       signal_emit("message own_nick", 4, server, server->nick, old, "");
818       g_free(old);
819     }
820     break;
821
822   default:
823     server->connection_lost = TRUE;
824     if (server->conn)
825       server->conn->context = NULL;
826     server_disconnect(SERVER(server));
827     break;
828   }
829 }
830
831 /* Called to indicate that connection was disconnected to the server. */
832
833 void silc_disconnect(SilcClient client, SilcClientConnection conn)
834 {
835   SILC_SERVER_REC *server = conn->context;
836
837   SILC_LOG_DEBUG(("Start"));
838
839   if (!server || server->connection_lost)
840     return;
841
842   if (server->conn && server->conn->local_entry) {
843     nicklist_rename_unique(SERVER(server),
844                            server->conn->local_entry, server->nick,
845                            server->conn->local_entry, 
846                            silc_client->username);
847     silc_change_nick(server, silc_client->username);
848   }
849
850   server->conn->context = NULL;
851   server->conn = NULL;
852   server->connection_lost = TRUE;
853   server_disconnect(SERVER(server));
854 }
855
856 /* Command handler. This function is called always in the command function.
857    If error occurs it will be called as well. `conn' is the associated
858    client connection. `cmd_context' is the command context that was
859    originally sent to the command. `success' is FALSE if error occured
860    during command. `command' is the command being processed. It must be
861    noted that this is not reply from server. This is merely called just
862    after application has called the command. Just to tell application
863    that the command really was processed. */
864
865 void silc_command(SilcClient client, SilcClientConnection conn, 
866                   SilcClientCommandContext cmd_context, bool success,
867                   SilcCommand command, SilcStatus status)
868 {
869   SILC_SERVER_REC *server = conn->context;
870
871   SILC_LOG_DEBUG(("Start"));
872
873   if (!success)
874     return;
875
876   switch(command) {
877   case SILC_COMMAND_INVITE:
878     printformat_module("fe-common/silc", server, NULL,
879                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING,
880                        cmd_context->argv[2], 
881                        (cmd_context->argv[1][0] == '*' ?
882                         (char *)conn->current_channel->channel_name :
883                         (char *)cmd_context->argv[1]));
884     break;
885   default:
886     break;
887   }
888 }
889
890 /* Client info resolving callback when JOIN command reply is received.
891    This will cache all users on the channel. */
892
893 static void silc_client_join_get_users(SilcClient client,
894                                        SilcClientConnection conn,
895                                        SilcClientEntry *clients,
896                                        SilcUInt32 clients_count,
897                                        void *context)
898 {
899   SilcChannelEntry channel = (SilcChannelEntry)context;
900   SilcHashTableList htl;
901   SilcChannelUser chu;
902   SILC_SERVER_REC *server = conn->context;
903   SILC_CHANNEL_REC *chanrec;
904   SilcClientEntry founder = NULL;
905   NICK_REC *ownnick;
906
907   SILC_LOG_DEBUG(("Start, channel %s, %d users", channel->channel_name,
908                   silc_hash_table_count(channel->user_list)));
909
910   if (!clients)
911     return;
912
913   chanrec = silc_channel_find(server, channel->channel_name);
914   if (chanrec == NULL)
915     return;
916
917   silc_hash_table_list(channel->user_list, &htl);
918   while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
919     if (!chu->client->nickname)
920       continue;
921     if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
922       founder = chu->client;
923     silc_nicklist_insert(chanrec, chu, FALSE);
924   }
925   silc_hash_table_list_reset(&htl);
926
927   ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
928   nicklist_set_own(CHANNEL(chanrec), ownnick);
929   signal_emit("channel joined", 1, chanrec);
930   chanrec->entry = channel;
931
932   if (chanrec->topic)
933     printformat_module("fe-common/silc", server, channel->channel_name,
934                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
935                        channel->channel_name, chanrec->topic);
936
937   fe_channels_nicklist(CHANNEL(chanrec), CHANNEL_NICKLIST_FLAG_ALL);
938
939   if (founder) {
940     if (founder == conn->local_entry)
941       printformat_module("fe-common/silc", 
942                          server, channel->channel_name, MSGLEVEL_CRAP,
943                          SILCTXT_CHANNEL_FOUNDER_YOU,
944                          channel->channel_name);
945     else
946       printformat_module("fe-common/silc", 
947                          server, channel->channel_name, MSGLEVEL_CRAP,
948                          SILCTXT_CHANNEL_FOUNDER,
949                          channel->channel_name, founder->nickname);
950   }
951 }
952
953 typedef struct {
954   SilcClient client;
955   SilcClientConnection conn;
956   void *entry;
957   SilcIdType id_type;
958   char *fingerprint;
959 } *GetkeyContext;
960
961 void silc_getkey_cb(bool success, void *context)
962 {
963   GetkeyContext getkey = (GetkeyContext)context;
964   char *entity = (getkey->id_type == SILC_ID_CLIENT ? "user" : "server");
965   char *name = (getkey->id_type == SILC_ID_CLIENT ? 
966                 ((SilcClientEntry)getkey->entry)->nickname :
967                 ((SilcServerEntry)getkey->entry)->server_name);
968
969   if (success) {
970     printformat_module("fe-common/silc", NULL, NULL,
971                        MSGLEVEL_CRAP, SILCTXT_GETKEY_VERIFIED, entity, name);
972   } else {
973     printformat_module("fe-common/silc", NULL, NULL,
974                        MSGLEVEL_CRAP, SILCTXT_GETKEY_DISCARD, entity, name);
975   }
976
977   silc_free(getkey->fingerprint);
978   silc_free(getkey);
979 }
980
981 /* Command reply handler. This function is called always in the command reply
982    function. If error occurs it will be called as well. Normal scenario
983    is that it will be called after the received command data has been parsed
984    and processed. The function is used to pass the received command data to
985    the application. 
986
987    `conn' is the associated client connection. `cmd_payload' is the command
988    payload data received from server and it can be ignored. It is provided
989    if the application would like to re-parse the received command data,
990    however, it must be noted that the data is parsed already by the library
991    thus the payload can be ignored. `success' is FALSE if error occured.
992    In this case arguments are not sent to the application. `command' is the
993    command reply being processed. The function has variable argument list
994    and each command defines the number and type of arguments it passes to the
995    application (on error they are not sent). */
996
997 void 
998 silc_command_reply(SilcClient client, SilcClientConnection conn,
999                    SilcCommandPayload cmd_payload, bool success,
1000                    SilcCommand command, SilcStatus status, ...)
1001
1002 {
1003   SILC_SERVER_REC *server = conn->context;
1004   SILC_CHANNEL_REC *chanrec;
1005   va_list vp;
1006
1007   va_start(vp, status);
1008
1009   SILC_LOG_DEBUG(("Start"));
1010
1011   switch(command) {
1012   case SILC_COMMAND_WHOIS:
1013     {
1014       char buf[1024], *nickname, *username, *realname, *nick;
1015       unsigned char *fingerprint;
1016       SilcUInt32 idle, mode;
1017       SilcBuffer channels, user_modes;
1018       SilcClientEntry client_entry;
1019       
1020       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1021         /* Print the unknown nick for user */
1022         unsigned char *tmp =
1023           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1024                                      3, NULL);
1025         if (tmp)
1026           silc_say_error("%s: %s", tmp, 
1027                          silc_get_status_message(status));
1028         break;
1029       } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1030         /* Try to find the entry for the unknown client ID, since we
1031            might have, and print the nickname of it for user. */
1032         SilcUInt32 tmp_len;
1033         unsigned char *tmp =
1034           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1035                                      2, &tmp_len);
1036         if (tmp) {
1037           SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, 
1038                                                              NULL);
1039           if (client_id) {
1040             client_entry = silc_client_get_client_by_id(client, conn,
1041                                                         client_id);
1042             if (client_entry && client_entry->nickname)
1043               silc_say_error("%s: %s", client_entry->nickname,
1044                              silc_get_status_message(status));
1045             silc_free(client_id);
1046           }
1047         }
1048         break;
1049       }
1050       
1051       if (!success)
1052         return;
1053       
1054       client_entry = va_arg(vp, SilcClientEntry);
1055       nickname = va_arg(vp, char *);
1056       username = va_arg(vp, char *);
1057       realname = va_arg(vp, char *);
1058       channels = va_arg(vp, SilcBuffer);
1059       mode = va_arg(vp, SilcUInt32);
1060       idle = va_arg(vp, SilcUInt32);
1061       fingerprint = va_arg(vp, unsigned char *);
1062       user_modes = va_arg(vp, SilcBuffer);
1063       
1064       silc_parse_userfqdn(nickname, &nick, NULL);
1065       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1066                          SILCTXT_WHOIS_USERINFO, nickname, 
1067                          client_entry->username, client_entry->hostname,
1068                          nick, client_entry->nickname);
1069       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1070                          SILCTXT_WHOIS_REALNAME, realname);
1071       silc_free(nick);
1072
1073       if (channels && user_modes) {
1074         SilcUInt32 *umodes;
1075         SilcDList list = silc_channel_payload_parse_list(channels->data,
1076                                                          channels->len);
1077         if (list && silc_get_mode_list(user_modes, silc_dlist_count(list),
1078                                        &umodes)) {
1079           SilcChannelPayload entry;
1080           int i = 0;
1081
1082           memset(buf, 0, sizeof(buf));
1083           silc_dlist_start(list);
1084           while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
1085             SilcUInt32 name_len;
1086             char *m = silc_client_chumode_char(umodes[i++]);
1087             char *name = silc_channel_get_name(entry, &name_len);
1088             
1089             if (m)
1090               strncat(buf, m, strlen(m));
1091             strncat(buf, name, name_len);
1092             strncat(buf, " ", 1);
1093             silc_free(m);
1094           }
1095
1096           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1097                              SILCTXT_WHOIS_CHANNELS, buf);
1098           silc_channel_payload_list_free(list);
1099           silc_free(umodes);
1100         }
1101       }
1102       
1103       if (mode) {
1104         memset(buf, 0, sizeof(buf));
1105         silc_get_umode_string(mode, buf, sizeof(buf - 1));
1106         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1107                            SILCTXT_WHOIS_MODES, buf);
1108       }
1109       
1110       if (idle && nickname) {
1111         memset(buf, 0, sizeof(buf));
1112         snprintf(buf, sizeof(buf) - 1, "%lu %s",
1113                  idle > 60 ? (idle / 60) : idle,
1114                  idle > 60 ? "minutes" : "seconds");
1115
1116         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1117                            SILCTXT_WHOIS_IDLE, buf);
1118       }
1119
1120       if (fingerprint) {
1121         fingerprint = silc_fingerprint(fingerprint, 20);
1122         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1123                            SILCTXT_WHOIS_FINGERPRINT, fingerprint);
1124         silc_free(fingerprint);
1125       }
1126     }
1127     break;
1128     
1129   case SILC_COMMAND_IDENTIFY:
1130     {
1131       SilcClientEntry client_entry;
1132       
1133       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1134         /* Print the unknown nick for user */
1135         unsigned char *tmp =
1136           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1137                                      3, NULL);
1138         if (tmp)
1139           silc_say_error("%s: %s", tmp, 
1140                          silc_get_status_message(status));
1141         break;
1142       } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1143         /* Try to find the entry for the unknown client ID, since we
1144            might have, and print the nickname of it for user. */
1145         SilcUInt32 tmp_len;
1146         unsigned char *tmp =
1147           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1148                                      2, &tmp_len);
1149         if (tmp) {
1150           SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len,
1151                                                              NULL);
1152           if (client_id) {
1153             client_entry = silc_client_get_client_by_id(client, conn,
1154                                                         client_id);
1155             if (client_entry && client_entry->nickname)
1156               silc_say_error("%s: %s", client_entry->nickname,
1157                              silc_get_status_message(status));
1158             silc_free(client_id);
1159           }
1160         }
1161         break;
1162       }
1163
1164       break;
1165     }
1166
1167   case SILC_COMMAND_WHOWAS:
1168     {
1169       char *nickname, *username, *realname;
1170       
1171       if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
1172           status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1173         char *tmp;
1174         tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1175                                          3, NULL);
1176         if (tmp)
1177           silc_say_error("%s: %s", tmp, 
1178                          silc_get_status_message(status));
1179         break;
1180       }
1181       
1182       if (!success)
1183         return;
1184       
1185       (void)va_arg(vp, SilcClientEntry);
1186       nickname = va_arg(vp, char *);
1187       username = va_arg(vp, char *);
1188       realname = va_arg(vp, char *);
1189       
1190       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1191                          SILCTXT_WHOWAS_USERINFO, nickname, username, 
1192                          realname ? realname : "");
1193     }
1194     break;
1195     
1196   case SILC_COMMAND_INVITE:
1197     {
1198       SilcChannelEntry channel;
1199       char *invite_list;
1200       SilcArgumentPayload args;
1201       int argc = 0;
1202       
1203       if (!success)
1204         return;
1205       
1206       channel = va_arg(vp, SilcChannelEntry);
1207       invite_list = va_arg(vp, char *);
1208
1209       args = silc_command_get_args(cmd_payload);
1210       if (args)
1211         argc = silc_argument_get_arg_num(args);
1212
1213       if (invite_list)
1214         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1215                            SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
1216                            invite_list);
1217       else if (argc == 3)
1218         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1219                            SILCTXT_CHANNEL_NO_INVITE_LIST, 
1220                            channel->channel_name);
1221     }
1222     break;
1223
1224   case SILC_COMMAND_JOIN: 
1225     {
1226       char *channel, *mode, *topic;
1227       SilcUInt32 modei;
1228       SilcChannelEntry channel_entry;
1229       SilcBuffer client_id_list;
1230       SilcUInt32 list_count;
1231
1232       if (!success)
1233         return;
1234
1235       channel = va_arg(vp, char *);
1236       channel_entry = va_arg(vp, SilcChannelEntry);
1237       modei = va_arg(vp, SilcUInt32);
1238       (void)va_arg(vp, SilcUInt32);
1239       (void)va_arg(vp, unsigned char *);
1240       (void)va_arg(vp, unsigned char *);
1241       (void)va_arg(vp, unsigned char *);
1242       topic = va_arg(vp, char *);
1243       (void)va_arg(vp, unsigned char *);
1244       list_count = va_arg(vp, SilcUInt32);
1245       client_id_list = va_arg(vp, SilcBuffer);
1246
1247       chanrec = silc_channel_find(server, channel);
1248       if (!chanrec)
1249         chanrec = silc_channel_create(server, channel, TRUE);
1250
1251       if (topic) {
1252         g_free_not_null(chanrec->topic);
1253         chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1254         signal_emit("channel topic changed", 1, chanrec);
1255       }
1256
1257       mode = silc_client_chmode(modei, 
1258                                 channel_entry->channel_key ? 
1259                                 channel_entry->channel_key->cipher->name : "",
1260                                 channel_entry->hmac ? 
1261                                 silc_hmac_get_name(channel_entry->hmac) : "");
1262       g_free_not_null(chanrec->mode);
1263       chanrec->mode = g_strdup(mode == NULL ? "" : mode);
1264       signal_emit("channel mode changed", 1, chanrec);
1265
1266       /* Resolve the client information */
1267       silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
1268                                       silc_client_join_get_users, 
1269                                       channel_entry);
1270
1271       break;
1272     }
1273
1274   case SILC_COMMAND_NICK: 
1275     {
1276       char *old;
1277       SilcClientEntry client = va_arg(vp, SilcClientEntry);
1278       
1279       if (!success)
1280         return;
1281
1282       old = g_strdup(server->nick);
1283       server_change_nick(SERVER(server), client->nickname);
1284       nicklist_rename_unique(SERVER(server),
1285                              server->conn->local_entry, server->nick,
1286                              client, client->nickname);
1287       signal_emit("message own_nick", 4, server, server->nick, old, "");
1288       g_free(old);
1289       break;
1290     }
1291     
1292   case SILC_COMMAND_LIST:
1293     {
1294       char *topic, *name;
1295       int usercount;
1296       char users[20];
1297       
1298       if (!success)
1299         return;
1300       
1301       (void)va_arg(vp, SilcChannelEntry);
1302       name = va_arg(vp, char *);
1303       topic = va_arg(vp, char *);
1304       usercount = va_arg(vp, int);
1305       
1306       if (status == SILC_STATUS_LIST_START ||
1307           status == SILC_STATUS_OK)
1308         printformat_module("fe-common/silc", server, NULL,
1309                            MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
1310
1311       if (!usercount)
1312         snprintf(users, sizeof(users) - 1, "N/A");
1313       else
1314         snprintf(users, sizeof(users) - 1, "%d", usercount);
1315       printformat_module("fe-common/silc", server, NULL,
1316                          MSGLEVEL_CRAP, SILCTXT_LIST,
1317                          name, users, topic ? topic : "");
1318     }
1319     break;
1320     
1321   case SILC_COMMAND_UMODE:
1322     {
1323       SilcUInt32 mode;
1324       
1325       if (!success)
1326         return;
1327       
1328       mode = va_arg(vp, SilcUInt32);
1329       
1330       if (mode & SILC_UMODE_SERVER_OPERATOR &&
1331           !(server->umode & SILC_UMODE_SERVER_OPERATOR))
1332         printformat_module("fe-common/silc", server, NULL,
1333                            MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1334
1335       if (mode & SILC_UMODE_ROUTER_OPERATOR &&
1336           !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
1337         printformat_module("fe-common/silc", server, NULL,
1338                            MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1339
1340       server->umode = mode;
1341       signal_emit("user mode changed", 2, server, NULL);
1342     }
1343     break;
1344     
1345   case SILC_COMMAND_OPER:
1346     if (!success)
1347       return;
1348
1349     server->umode |= SILC_UMODE_SERVER_OPERATOR;
1350     signal_emit("user mode changed", 2, server, NULL);
1351
1352     printformat_module("fe-common/silc", server, NULL,
1353                        MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1354     break;
1355     
1356   case SILC_COMMAND_SILCOPER:
1357     if (!success)
1358       return;
1359
1360     server->umode |= SILC_UMODE_ROUTER_OPERATOR;
1361     signal_emit("user mode changed", 2, server, NULL);
1362
1363     printformat_module("fe-common/silc", server, NULL,
1364                        MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1365     break;
1366     
1367   case SILC_COMMAND_USERS: 
1368     {
1369       SilcHashTableList htl;
1370       SilcChannelEntry channel;
1371       SilcChannelUser chu;
1372       
1373       if (!success)
1374         return;
1375       
1376       channel = va_arg(vp, SilcChannelEntry);
1377       
1378       printformat_module("fe-common/silc", server, channel->channel_name,
1379                          MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
1380                          channel->channel_name);
1381
1382       silc_hash_table_list(channel->user_list, &htl);
1383       while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1384         SilcClientEntry e = chu->client;
1385         char stat[5], *mode;
1386
1387         if (!e->nickname)
1388           continue;
1389         
1390         memset(stat, 0, sizeof(stat));
1391         mode = silc_client_chumode_char(chu->mode);
1392         if (e->mode & SILC_UMODE_GONE)
1393           strcat(stat, "G");
1394         else if (e->mode & SILC_UMODE_INDISPOSED)
1395           strcat(stat, "I");
1396         else if (e->mode & SILC_UMODE_BUSY)
1397           strcat(stat, "B");
1398         else if (e->mode & SILC_UMODE_PAGE)
1399           strcat(stat, "P");
1400         else if (e->mode & SILC_UMODE_HYPER)
1401           strcat(stat, "H");
1402         else if (e->mode & SILC_UMODE_ROBOT)
1403           strcat(stat, "R");
1404         else if (e->mode & SILC_UMODE_ANONYMOUS)
1405           strcat(stat, "?");
1406         else
1407           strcat(stat, "A");
1408         if (mode)
1409           strcat(stat, mode);
1410
1411         printformat_module("fe-common/silc", server, channel->channel_name,
1412                            MSGLEVEL_CRAP, SILCTXT_USERS,
1413                            e->nickname, stat, 
1414                            e->username ? e->username : "",
1415                            e->hostname ? e->hostname : "",
1416                            e->realname ? e->realname : "");
1417         if (mode)
1418           silc_free(mode);
1419       }
1420       silc_hash_table_list_reset(&htl);
1421     }
1422     break;
1423
1424   case SILC_COMMAND_BAN:
1425     {
1426       SilcChannelEntry channel;
1427       char *ban_list;
1428       
1429       if (!success)
1430         return;
1431       
1432       channel = va_arg(vp, SilcChannelEntry);
1433       ban_list = va_arg(vp, char *);
1434       
1435       if (ban_list)
1436         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1437                            SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
1438                            ban_list);
1439       else
1440         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1441                            SILCTXT_CHANNEL_NO_BAN_LIST, 
1442                            channel->channel_name);
1443     }
1444     break;
1445     
1446   case SILC_COMMAND_GETKEY:
1447     {
1448       SilcIdType id_type;
1449       void *entry;
1450       SilcPublicKey public_key;
1451       unsigned char *pk;
1452       SilcUInt32 pk_len;
1453       GetkeyContext getkey;
1454       char *name;
1455       
1456       if (!success)
1457         return;
1458       
1459       id_type = va_arg(vp, SilcUInt32);
1460       entry = va_arg(vp, void *);
1461       public_key = va_arg(vp, SilcPublicKey);
1462
1463       if (public_key) {
1464         pk = silc_pkcs_public_key_encode(public_key, &pk_len);
1465
1466         getkey = silc_calloc(1, sizeof(*getkey));
1467         getkey->entry = entry;
1468         getkey->id_type = id_type;
1469         getkey->client = client;
1470         getkey->conn = conn;
1471         getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1472
1473         name = (id_type == SILC_ID_CLIENT ? 
1474                 ((SilcClientEntry)entry)->nickname :
1475                 ((SilcServerEntry)entry)->server_name);
1476
1477         silc_verify_public_key_internal(client, conn, name,
1478                                         (id_type == SILC_ID_CLIENT ?
1479                                          SILC_SOCKET_TYPE_CLIENT :
1480                                          SILC_SOCKET_TYPE_SERVER),
1481                                         pk, pk_len, SILC_SKE_PK_TYPE_SILC,
1482                                         silc_getkey_cb, getkey);
1483         silc_free(pk);
1484       } else {
1485         printformat_module("fe-common/silc", server, NULL,
1486                            MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
1487       }
1488     }
1489     break;
1490
1491   case SILC_COMMAND_INFO:
1492     {
1493       SilcServerEntry server_entry;
1494       char *server_name;
1495       char *server_info;
1496
1497       if (!success)
1498         return;
1499       
1500       server_entry = va_arg(vp, SilcServerEntry);
1501       server_name = va_arg(vp, char *);
1502       server_info = va_arg(vp, char *);
1503
1504       if (server_name && server_info )
1505         {
1506           printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
1507           printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
1508         }
1509     }
1510     break;
1511     
1512   case SILC_COMMAND_TOPIC:
1513     {
1514       SilcChannelEntry channel;
1515       char *topic;
1516       
1517       if (!success)
1518         return;
1519       
1520       channel = va_arg(vp, SilcChannelEntry);
1521       topic = va_arg(vp, char *);
1522       
1523       if (topic) {
1524         chanrec = silc_channel_find_entry(server, channel);
1525         if (chanrec) {
1526           g_free_not_null(chanrec->topic);
1527           chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1528           signal_emit("channel topic changed", 1, chanrec);
1529         }
1530         printformat_module("fe-common/silc", server, channel->channel_name,
1531                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
1532                            channel->channel_name, topic);
1533       } else {
1534         printformat_module("fe-common/silc", server, channel->channel_name,
1535                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
1536                            channel->channel_name);
1537       }
1538     }
1539     break;
1540
1541   case SILC_COMMAND_WATCH:
1542     break;
1543   }
1544
1545   va_end(vp);
1546 }
1547
1548 typedef struct {
1549   SilcClient client;
1550   SilcClientConnection conn;
1551   char *filename;
1552   char *entity;
1553   char *entity_name;
1554   unsigned char *pk;
1555   SilcUInt32 pk_len;
1556   SilcSKEPKType pk_type;
1557   SilcVerifyPublicKey completion;
1558   void *context;
1559 } *PublicKeyVerify;
1560
1561 static void verify_public_key_completion(const char *line, void *context)
1562 {
1563   PublicKeyVerify verify = (PublicKeyVerify)context;
1564
1565   if (line[0] == 'Y' || line[0] == 'y') {
1566     /* Call the completion */
1567     if (verify->completion)
1568       verify->completion(TRUE, verify->context);
1569
1570     /* Save the key for future checking */
1571     silc_pkcs_save_public_key_data(verify->filename, verify->pk, 
1572                                    verify->pk_len, SILC_PKCS_FILE_PEM);
1573   } else {
1574     /* Call the completion */
1575     if (verify->completion)
1576       verify->completion(FALSE, verify->context);
1577
1578     printformat_module("fe-common/silc", NULL, NULL,
1579                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, 
1580                        verify->entity_name ? verify->entity_name :
1581                        verify->entity);
1582   }
1583
1584   silc_free(verify->filename);
1585   silc_free(verify->entity);
1586   silc_free(verify->entity_name);
1587   silc_free(verify->pk);
1588   silc_free(verify);
1589 }
1590
1591 /* Internal routine to verify public key. If the `completion' is provided
1592    it will be called to indicate whether public was verified or not. For
1593    server/router public key this will check for filename that includes the
1594    remote host's IP address and remote host's hostname. */
1595
1596 static void 
1597 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
1598                                 const char *name, SilcSocketType conn_type, 
1599                                 unsigned char *pk, SilcUInt32 pk_len, 
1600                                 SilcSKEPKType pk_type,
1601                                 SilcVerifyPublicKey completion, void *context)
1602 {
1603   int i;
1604   char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
1605   char *fingerprint, *babbleprint, *format;
1606   struct passwd *pw;
1607   struct stat st;
1608   char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
1609                    conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
1610                   "server" : "client");
1611   PublicKeyVerify verify;
1612
1613   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
1614     printformat_module("fe-common/silc", NULL, NULL,
1615                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED, 
1616                        entity, pk_type);
1617     if (completion)
1618       completion(FALSE, context);
1619     return;
1620   }
1621
1622   pw = getpwuid(getuid());
1623   if (!pw) {
1624     if (completion)
1625       completion(FALSE, context);
1626     return;
1627   }
1628
1629   memset(filename, 0, sizeof(filename));
1630   memset(filename2, 0, sizeof(filename2));
1631   memset(file, 0, sizeof(file));
1632
1633   if (conn_type == SILC_SOCKET_TYPE_SERVER ||
1634       conn_type == SILC_SOCKET_TYPE_ROUTER) {
1635     if (!name) {
1636       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1637                conn->sock->ip, conn->sock->port);
1638       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
1639                get_irssi_dir(), entity, file);
1640       
1641       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1642                conn->sock->hostname, conn->sock->port);
1643       snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s", 
1644                get_irssi_dir(), entity, file);
1645       
1646       ipf = filename;
1647       hostf = filename2;
1648     } else {
1649       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1650                name, conn->sock->port);
1651       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
1652                get_irssi_dir(), entity, file);
1653       
1654       ipf = filename;
1655     }
1656   } else {
1657     /* Replace all whitespaces with `_'. */
1658     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1659     for (i = 0; i < strlen(fingerprint); i++)
1660       if (fingerprint[i] == ' ')
1661         fingerprint[i] = '_';
1662     
1663     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
1664     snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
1665              get_irssi_dir(), entity, file);
1666     silc_free(fingerprint);
1667
1668     ipf = filename;
1669   }
1670
1671   /* Take fingerprint of the public key */
1672   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1673   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
1674
1675   verify = silc_calloc(1, sizeof(*verify));
1676   verify->client = client;
1677   verify->conn = conn;
1678   verify->filename = strdup(ipf);
1679   verify->entity = strdup(entity);
1680   verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ?
1681                          (name ? strdup(name) : strdup(conn->sock->hostname))
1682                          : NULL);
1683   verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
1684   memcpy(verify->pk, pk, pk_len);
1685   verify->pk_len = pk_len;
1686   verify->pk_type = pk_type;
1687   verify->completion = completion;
1688   verify->context = context;
1689
1690   /* Check whether this key already exists */
1691   if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
1692     /* Key does not exist, ask user to verify the key and save it */
1693
1694     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1695                        SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1696                        verify->entity_name : entity);
1697     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1698                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1699     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1700                        SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1701     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1702                              SILCTXT_PUBKEY_ACCEPT);
1703     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1704                             format, 0, verify);
1705     g_free(format);
1706     silc_free(fingerprint);
1707     return;
1708   } else {
1709     /* The key already exists, verify it. */
1710     SilcPublicKey public_key;
1711     unsigned char *encpk;
1712     SilcUInt32 encpk_len;
1713
1714     /* Load the key file, try for both IP filename and hostname filename */
1715     if (!silc_pkcs_load_public_key(ipf, &public_key, 
1716                                    SILC_PKCS_FILE_PEM) &&
1717         !silc_pkcs_load_public_key(ipf, &public_key, 
1718                                    SILC_PKCS_FILE_BIN) &&
1719         (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key, 
1720                                                SILC_PKCS_FILE_PEM) &&
1721                     !silc_pkcs_load_public_key(hostf, &public_key, 
1722                                                SILC_PKCS_FILE_BIN)))) {
1723       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1724                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1725                          verify->entity_name : entity);
1726       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1727                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1728       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1729                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1730       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1731                          SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
1732       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1733                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1734       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1735                               format, 0, verify);
1736       g_free(format);
1737       silc_free(fingerprint);
1738       return;
1739     }
1740
1741     /* Encode the key data */
1742     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
1743     if (!encpk) {
1744       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1745                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1746                          verify->entity_name : entity);
1747       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1748                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1749       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1750                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1751       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1752                          SILCTXT_PUBKEY_MALFORMED, entity);
1753       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1754                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1755       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1756                               format, 0, verify);
1757       g_free(format);
1758       silc_free(fingerprint);
1759       return;
1760     }
1761
1762     /* Compare the keys */
1763     if (memcmp(encpk, pk, encpk_len)) {
1764       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1765                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1766                          verify->entity_name : entity);
1767       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1768                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1769       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1770                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1771       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1772                          SILCTXT_PUBKEY_NO_MATCH, entity);
1773       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1774                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
1775       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1776                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
1777
1778       /* Ask user to verify the key and save it */
1779       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1780                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1781       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1782                               format, 0, verify);
1783       g_free(format);
1784       silc_free(fingerprint);
1785       return;
1786     }
1787
1788     /* Local copy matched */
1789     if (completion)
1790       completion(TRUE, context);
1791     silc_free(fingerprint);
1792   }
1793 }
1794
1795 /* Verifies received public key. The `conn_type' indicates which entity
1796    (server, client etc.) has sent the public key. If user decides to trust
1797    the key may be saved as trusted public key for later use. The 
1798    `completion' must be called after the public key has been verified. */
1799
1800 void 
1801 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
1802                        SilcSocketType conn_type, unsigned char *pk, 
1803                        SilcUInt32 pk_len, SilcSKEPKType pk_type,
1804                        SilcVerifyPublicKey completion, void *context)
1805 {
1806   silc_verify_public_key_internal(client, conn, NULL, conn_type, pk,
1807                                   pk_len, pk_type,
1808                                   completion, context);
1809 }
1810
1811 /* Asks passphrase from user on the input line. */
1812
1813 typedef struct {
1814   SilcAskPassphrase completion;
1815   void *context;
1816 } *AskPassphrase;
1817
1818 void ask_passphrase_completion(const char *passphrase, void *context)
1819 {
1820   AskPassphrase p = (AskPassphrase)context;
1821   if (passphrase && passphrase[0] == '\0')
1822     passphrase = NULL;
1823   p->completion((unsigned char *)passphrase, 
1824                 passphrase ? strlen(passphrase) : 0, p->context);
1825   silc_free(p);
1826 }
1827
1828 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1829                          SilcAskPassphrase completion, void *context)
1830 {
1831   AskPassphrase p = silc_calloc(1, sizeof(*p));
1832   p->completion = completion;
1833   p->context = context;
1834
1835   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
1836                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
1837 }
1838
1839 typedef struct {
1840   SilcGetAuthMeth completion;
1841   void *context;
1842 } *InternalGetAuthMethod;
1843
1844 /* Callback called when we've received the authentication method information
1845    from the server after we've requested it. This will get the authentication
1846    data from the user if needed. */
1847
1848 static void silc_get_auth_method_callback(SilcClient client,
1849                                           SilcClientConnection conn,
1850                                           SilcAuthMethod auth_meth,
1851                                           void *context)
1852 {
1853   InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
1854
1855   SILC_LOG_DEBUG(("Start"));
1856
1857   switch (auth_meth) {
1858   case SILC_AUTH_NONE:
1859     /* No authentication required. */
1860     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1861     break;
1862   case SILC_AUTH_PASSWORD:
1863     /* Do not ask the passphrase from user, the library will ask it if
1864        we do not provide it here. */
1865     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1866     break;
1867   case SILC_AUTH_PUBLIC_KEY:
1868     /* Do not get the authentication data now, the library will generate
1869        it using our default key, if we do not provide it here. */
1870     /* XXX In the future when we support multiple local keys and multiple
1871        local certificates we will need to ask from user which one to use. */
1872     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1873     break;
1874   }
1875
1876   silc_free(internal);
1877 }
1878
1879 /* Find authentication method and authentication data by hostname and
1880    port. The hostname may be IP address as well. The found authentication
1881    method and authentication data is returned to `auth_meth', `auth_data'
1882    and `auth_data_len'. The function returns TRUE if authentication method
1883    is found and FALSE if not. `conn' may be NULL. */
1884
1885 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1886                           char *hostname, SilcUInt16 port,
1887                           SilcGetAuthMeth completion, void *context)
1888 {
1889   InternalGetAuthMethod internal;
1890
1891   SILC_LOG_DEBUG(("Start"));
1892
1893   /* XXX must resolve from configuration whether this connection has
1894      any specific authentication data */
1895
1896   /* If we do not have this connection configured by the user in a
1897      configuration file then resolve the authentication method from the
1898      server for this session. */
1899   internal = silc_calloc(1, sizeof(*internal));
1900   internal->completion = completion;
1901   internal->context = context;
1902
1903   silc_client_request_authentication_method(client, conn, 
1904                                             silc_get_auth_method_callback,
1905                                             internal);
1906 }
1907
1908 /* Notifies application that failure packet was received.  This is called
1909    if there is some protocol active in the client.  The `protocol' is the
1910    protocol context.  The `failure' is opaque pointer to the failure
1911    indication.  Note, that the `failure' is protocol dependant and application
1912    must explicitly cast it to correct type.  Usually `failure' is 32 bit
1913    failure type (see protocol specs for all protocol failure types). */
1914
1915 void silc_failure(SilcClient client, SilcClientConnection conn, 
1916                   SilcProtocol protocol, void *failure)
1917 {
1918   SILC_LOG_DEBUG(("Start"));
1919
1920   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1921     SilcSKEStatus status = (SilcSKEStatus)failure;
1922     
1923     if (status == SILC_SKE_STATUS_BAD_VERSION)
1924       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1925                          SILCTXT_KE_BAD_VERSION);
1926     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1927       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1928                          SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1929     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1930       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1931                          SILCTXT_KE_UNKNOWN_GROUP);
1932     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1933       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1934                          SILCTXT_KE_UNKNOWN_CIPHER);
1935     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1936       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1937                          SILCTXT_KE_UNKNOWN_PKCS);
1938     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1939       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1940                          SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1941     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1942       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1943                          SILCTXT_KE_UNKNOWN_HMAC);
1944     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1945       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1946                          SILCTXT_KE_INCORRECT_SIGNATURE);
1947     if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1948       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1949                          SILCTXT_KE_INVALID_COOKIE);
1950   }
1951
1952   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1953     SilcUInt32 err = (SilcUInt32)failure;
1954
1955     if (err == SILC_AUTH_FAILED)
1956       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1957                          SILCTXT_AUTH_FAILED);
1958   }
1959 }
1960
1961 /* Asks whether the user would like to perform the key agreement protocol.
1962    This is called after we have received an key agreement packet or an
1963    reply to our key agreement packet. This returns TRUE if the user wants
1964    the library to perform the key agreement protocol and FALSE if it is not
1965    desired (application may start it later by calling the function
1966    silc_client_perform_key_agreement). */
1967
1968 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1969                        SilcClientEntry client_entry, const char *hostname,
1970                        SilcUInt16 port, SilcKeyAgreementCallback *completion,
1971                        void **context)
1972 {
1973   char portstr[12];
1974
1975   SILC_LOG_DEBUG(("Start"));
1976
1977   /* We will just display the info on the screen and return FALSE and user
1978      will have to start the key agreement with a command. */
1979
1980   if (hostname) 
1981     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1982
1983   if (!hostname)
1984     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1985                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1986   else
1987     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1988                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST, 
1989                        client_entry->nickname, hostname, portstr);
1990
1991   *completion = NULL;
1992   *context = NULL;
1993
1994   return FALSE;
1995 }
1996
1997 /* Notifies application that file transfer protocol session is being
1998    requested by the remote client indicated by the `client_entry' from
1999    the `hostname' and `port'. The `session_id' is the file transfer
2000    session and it can be used to either accept or reject the file
2001    transfer request, by calling the silc_client_file_receive or
2002    silc_client_file_close, respectively. */
2003
2004 void silc_ftp(SilcClient client, SilcClientConnection conn,
2005               SilcClientEntry client_entry, SilcUInt32 session_id,
2006               const char *hostname, SilcUInt16 port)
2007 {
2008   SILC_SERVER_REC *server;
2009   char portstr[12];
2010   FtpSession ftp = silc_calloc(1, sizeof(*ftp));
2011
2012   SILC_LOG_DEBUG(("Start"));
2013
2014   server = conn->context;
2015
2016   ftp->client_entry = client_entry;
2017   ftp->session_id = session_id;
2018   ftp->send = FALSE;
2019   ftp->conn = conn;
2020   silc_dlist_add(server->ftp_sessions, ftp);
2021   server->current_session = ftp;
2022
2023   if (hostname) 
2024     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2025
2026   if (!hostname)
2027     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2028                        SILCTXT_FILE_REQUEST, client_entry->nickname);
2029   else
2030     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2031                        SILCTXT_FILE_REQUEST_HOST, 
2032                        client_entry->nickname, hostname, portstr);
2033 }
2034
2035 /* Delivers SILC session detachment data indicated by `detach_data' to the
2036    application.  If application has issued SILC_COMMAND_DETACH command
2037    the client session in the SILC network is not quit.  The client remains
2038    in the network but is detached.  The detachment data may be used later
2039    to resume the session in the SILC Network.  The appliation is
2040    responsible of saving the `detach_data', to for example in a file.
2041
2042    The detachment data can be given as argument to the functions
2043    silc_client_connect_to_server, or silc_client_add_connection when
2044    creating connection to remote server, inside SilcClientConnectionParams
2045    structure.  If it is provided the client library will attempt to resume
2046    the session in the network.  After the connection is created
2047    successfully, the application is responsible of setting the user
2048    interface for user into the same state it was before detaching (showing
2049    same channels, channel modes, etc).  It can do this by fetching the
2050    information (like joined channels) from the client library. */
2051
2052 void
2053 silc_detach(SilcClient client, SilcClientConnection conn,
2054             const unsigned char *detach_data, SilcUInt32 detach_data_len)
2055 {
2056   char file[256];
2057
2058   /* Save the detachment data to file. */
2059
2060   memset(file, 0, sizeof(file));
2061   snprintf(file, sizeof(file) - 1, "%s/session", get_irssi_dir());
2062   silc_file_writefile(file, detach_data, detach_data_len);
2063 }
2064
2065
2066 /* SILC client operations */
2067 SilcClientOperations ops = {
2068   silc_say,
2069   silc_channel_message,
2070   silc_private_message,
2071   silc_notify,
2072   silc_command,
2073   silc_command_reply,
2074   silc_connect,
2075   silc_disconnect,
2076   silc_get_auth_method,
2077   silc_verify_public_key,
2078   silc_ask_passphrase,
2079   silc_failure,
2080   silc_key_agreement,
2081   silc_ftp,
2082   silc_detach,
2083 };