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