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