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