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