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