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