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