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