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