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