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