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