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