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