d979a640abfb4c574cd7829b709184061e15fcb6
[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   if (server->conn)
1114     server->conn->context = NULL;
1115   server->conn = NULL;
1116   server->connection_lost = TRUE;
1117   server_disconnect(SERVER(server));
1118 }
1119
1120 /* Command handler. This function is called always in the command function.
1121    If error occurs it will be called as well. `conn' is the associated
1122    client connection. `cmd_context' is the command context that was
1123    originally sent to the command. `success' is FALSE if error occured
1124    during command. `command' is the command being processed. It must be
1125    noted that this is not reply from server. This is merely called just
1126    after application has called the command. Just to tell application
1127    that the command really was processed. */
1128
1129 void silc_command(SilcClient client, SilcClientConnection conn, 
1130                   SilcClientCommandContext cmd_context, bool success,
1131                   SilcCommand command, SilcStatus status)
1132 {
1133   SILC_SERVER_REC *server = conn->context;
1134
1135   SILC_LOG_DEBUG(("Start"));
1136
1137   if (!success) {
1138     silc_say_error("%s", silc_get_status_message(status));
1139     return;
1140   }
1141
1142   switch (command) {
1143
1144   case SILC_COMMAND_INVITE:
1145     if (cmd_context->argc > 2)
1146       printformat_module("fe-common/silc", server, NULL,
1147                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING,
1148                          cmd_context->argv[2], 
1149                          (cmd_context->argv[1][0] == '*' ?
1150                           (char *)conn->current_channel->channel_name :
1151                           (char *)cmd_context->argv[1]));
1152     break;
1153
1154   case SILC_COMMAND_DETACH:
1155     server->no_reconnect = TRUE;
1156     break;
1157
1158   default:
1159     break;
1160   }
1161 }
1162
1163 /* Client info resolving callback when JOIN command reply is received.
1164    This will cache all users on the channel. */
1165
1166 static void silc_client_join_get_users(SilcClient client,
1167                                        SilcClientConnection conn,
1168                                        SilcClientEntry *clients,
1169                                        SilcUInt32 clients_count,
1170                                        void *context)
1171 {
1172   SilcChannelEntry channel = (SilcChannelEntry)context;
1173   SilcHashTableList htl;
1174   SilcChannelUser chu;
1175   SILC_SERVER_REC *server = conn->context;
1176   SILC_CHANNEL_REC *chanrec;
1177   SilcClientEntry founder = NULL;
1178   NICK_REC *ownnick;
1179
1180   SILC_LOG_DEBUG(("Start, channel %s, %d users", channel->channel_name,
1181                   silc_hash_table_count(channel->user_list)));
1182
1183   if (!clients)
1184     return;
1185
1186   chanrec = silc_channel_find(server, channel->channel_name);
1187   if (chanrec == NULL)
1188     return;
1189
1190   silc_hash_table_list(channel->user_list, &htl);
1191   while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1192     if (!chu->client->nickname)
1193       continue;
1194     if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
1195       founder = chu->client;
1196     silc_nicklist_insert(chanrec, chu, FALSE);
1197   }
1198   silc_hash_table_list_reset(&htl);
1199
1200   ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
1201   nicklist_set_own(CHANNEL(chanrec), ownnick);
1202   signal_emit("channel joined", 1, chanrec);
1203   chanrec->entry = channel;
1204
1205   if (chanrec->topic)
1206     printformat_module("fe-common/silc", server, channel->channel_name,
1207                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
1208                        channel->channel_name, chanrec->topic);
1209
1210   if (founder) {
1211     if (founder == conn->local_entry) {
1212       printformat_module("fe-common/silc", 
1213                          server, channel->channel_name, MSGLEVEL_CRAP,
1214                          SILCTXT_CHANNEL_FOUNDER_YOU,
1215                          channel->channel_name);
1216       signal_emit("nick mode changed", 2, chanrec, ownnick);
1217     } else
1218       printformat_module("fe-common/silc", 
1219                          server, channel->channel_name, MSGLEVEL_CRAP,
1220                          SILCTXT_CHANNEL_FOUNDER,
1221                          channel->channel_name, founder->nickname);
1222   }
1223 }
1224
1225 typedef struct {
1226   SilcClient client;
1227   SilcClientConnection conn;
1228   void *entry;
1229   SilcIdType id_type;
1230   char *fingerprint;
1231 } *GetkeyContext;
1232
1233 void silc_getkey_cb(bool success, void *context)
1234 {
1235   GetkeyContext getkey = (GetkeyContext)context;
1236   char *entity = (getkey->id_type == SILC_ID_CLIENT ? "user" : "server");
1237   char *name = (getkey->id_type == SILC_ID_CLIENT ? 
1238                 ((SilcClientEntry)getkey->entry)->nickname :
1239                 ((SilcServerEntry)getkey->entry)->server_name);
1240
1241   if (success) {
1242     printformat_module("fe-common/silc", NULL, NULL,
1243                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_VERIFIED, entity, name);
1244   } else {
1245     printformat_module("fe-common/silc", NULL, NULL,
1246                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOTVERIFIED,
1247                        entity, name);
1248   }
1249
1250   silc_free(getkey->fingerprint);
1251   silc_free(getkey);
1252 }
1253
1254 /* Parse an invite or ban list */
1255 void  silc_parse_inviteban_list(SilcClient client,
1256                                 SilcClientConnection conn,
1257                                 SILC_SERVER_REC *server, 
1258                                 SilcChannelEntry channel, 
1259                                 const char *list_type,
1260                                 SilcArgumentPayload list)
1261 {
1262   unsigned char *tmp;
1263   SilcUInt32 type, len;
1264   SILC_CHANNEL_REC *chanrec = silc_channel_find_entry(server, channel);
1265   int counter=0, resolving = FALSE;
1266
1267   if (!silc_argument_get_arg_num(list)) {
1268     printformat_module("fe-common/silc", server,
1269                        (chanrec ? chanrec->visible_name : NULL),
1270                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_NO_INVITEBAN_LIST,
1271                        channel->channel_name, list_type);
1272     return;
1273   }
1274   
1275   printformat_module("fe-common/silc", server,
1276                      (chanrec ? chanrec->visible_name : NULL),
1277                      MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_LIST,
1278                      channel->channel_name, list_type);
1279
1280   /* parse the list */
1281   tmp = silc_argument_get_first_arg(list, &type, &len);
1282   while (tmp) {
1283     switch (type) {
1284       case 1:
1285         {
1286           /* an invite string */
1287           char **list;
1288           int i=0;
1289                 
1290           if (tmp[len-1] == ',')
1291             tmp[len-1] = '\0';
1292           
1293           list = g_strsplit(tmp, ",", -1);
1294           while (list[i])
1295             printformat_module("fe-common/silc", server,
1296                                (chanrec ? chanrec->visible_name : NULL),
1297                                MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_STRING,
1298                                ++counter, channel->channel_name, list_type,
1299                                list[i++]);
1300           g_strfreev(list);
1301         }
1302         break;
1303
1304       case 2:
1305         {
1306           /* a public key */
1307           char *fingerprint, *babbleprint;
1308
1309           /* tmp is Public Key Payload, take public key from it. */
1310           fingerprint = silc_hash_fingerprint(NULL, tmp + 4, len - 4);
1311           babbleprint = silc_hash_babbleprint(NULL, tmp + 4, len - 4);
1312           
1313           printformat_module("fe-common/silc", server,
1314                              (chanrec ? chanrec->visible_name : NULL),
1315                              MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_PUBKEY,
1316                              ++counter, channel->channel_name, list_type,
1317                              fingerprint, babbleprint);
1318         }
1319         break;
1320         
1321       case 3:
1322         {
1323           /* a client ID */
1324           SilcClientID *client_id;
1325           SilcClientEntry client_entry;
1326           
1327           client_id = silc_id_payload_parse_id(tmp, len, NULL);
1328                           
1329           if (client_id == NULL) {
1330             silc_say_error("Invalid data in %s list encountered", list_type);
1331             break;
1332           }
1333
1334           client_entry = silc_client_get_client_by_id(client, conn, client_id);
1335
1336           if (client_entry) {
1337             printformat_module("fe-common/silc", server,
1338                                (chanrec ? chanrec->visible_name : NULL),
1339                                MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_STRING,
1340                                ++counter, channel->channel_name, list_type,
1341                                client_entry->nickname);
1342           } else {
1343             resolving = TRUE;
1344             silc_client_get_client_by_id_resolve(client, conn, client_id,
1345                                                  NULL, NULL, NULL);
1346           }
1347
1348           silc_free(client_id);
1349         }
1350         break;
1351         
1352       default:
1353         /* "trash" */
1354         silc_say_error("Unkown type in %s list: %u (len %u)",
1355                        list_type, type, len);
1356     }
1357     tmp = silc_argument_get_next_arg(list, &type, &len);
1358   }
1359
1360   if (resolving)
1361     printformat_module("fe-common/silc", server, 
1362                        (chanrec ? chanrec->visible_name : NULL),
1363                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_REGET,
1364                        list_type, channel->channel_name);
1365 }
1366
1367 /* Command reply handler. This function is called always in the command reply
1368    function. If error occurs it will be called as well. Normal scenario
1369    is that it will be called after the received command data has been parsed
1370    and processed. The function is used to pass the received command data to
1371    the application. 
1372
1373    `conn' is the associated client connection. `cmd_payload' is the command
1374    payload data received from server and it can be ignored. It is provided
1375    if the application would like to re-parse the received command data,
1376    however, it must be noted that the data is parsed already by the library
1377    thus the payload can be ignored. `success' is FALSE if error occured.
1378    In this case arguments are not sent to the application. `command' is the
1379    command reply being processed. The function has variable argument list
1380    and each command defines the number and type of arguments it passes to the
1381    application (on error they are not sent). */
1382
1383 void 
1384 silc_command_reply(SilcClient client, SilcClientConnection conn,
1385                    SilcCommandPayload cmd_payload, bool success,
1386                    SilcCommand command, SilcStatus status, ...)
1387
1388 {
1389   SILC_SERVER_REC *server = conn->context;
1390   SILC_CHANNEL_REC *chanrec;
1391   va_list vp;
1392
1393   va_start(vp, status);
1394
1395   SILC_LOG_DEBUG(("Start"));
1396
1397   switch(command) {
1398   case SILC_COMMAND_WHOIS:
1399     {
1400       char buf[1024], *nickname, *username, *realname, *nick;
1401       unsigned char *fingerprint;
1402       SilcUInt32 idle, mode;
1403       SilcBuffer channels, user_modes;
1404       SilcClientEntry client_entry;
1405       SilcDList attrs;
1406       
1407       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1408         /* Print the unknown nick for user */
1409         unsigned char *tmp =
1410           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1411                                      3, NULL);
1412         if (tmp)
1413           silc_say_error("%s: %s", tmp, 
1414                          silc_get_status_message(status));
1415         break;
1416       } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1417         /* Try to find the entry for the unknown client ID, since we
1418            might have, and print the nickname of it for user. */
1419         SilcUInt32 tmp_len;
1420         unsigned char *tmp =
1421           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1422                                      2, &tmp_len);
1423         if (tmp) {
1424           SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, 
1425                                                              NULL);
1426           if (client_id) {
1427             client_entry = silc_client_get_client_by_id(client, conn,
1428                                                         client_id);
1429             if (client_entry && client_entry->nickname)
1430               silc_say_error("%s: %s", client_entry->nickname,
1431                              silc_get_status_message(status));
1432             silc_free(client_id);
1433           }
1434         }
1435         break;
1436       } else if (!success) {
1437         silc_say_error("WHOIS: %s", silc_get_status_message(status));
1438         return;
1439       }
1440
1441       client_entry = va_arg(vp, SilcClientEntry);
1442       nickname = va_arg(vp, char *);
1443       username = va_arg(vp, char *);
1444       realname = va_arg(vp, char *);
1445       channels = va_arg(vp, SilcBuffer);
1446       mode = va_arg(vp, SilcUInt32);
1447       idle = va_arg(vp, SilcUInt32);
1448       fingerprint = va_arg(vp, unsigned char *);
1449       user_modes = va_arg(vp, SilcBuffer);
1450       attrs = va_arg(vp, SilcDList);
1451       
1452       silc_parse_userfqdn(nickname, &nick, NULL);
1453       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1454                          SILCTXT_WHOIS_USERINFO, nickname, 
1455                          client_entry->username, client_entry->hostname,
1456                          nick, client_entry->nickname);
1457       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1458                          SILCTXT_WHOIS_REALNAME, realname);
1459       silc_free(nick);
1460
1461       if (channels && user_modes) {
1462         SilcUInt32 *umodes;
1463         SilcDList list = silc_channel_payload_parse_list(channels->data,
1464                                                          channels->len);
1465         if (list && silc_get_mode_list(user_modes, silc_dlist_count(list),
1466                                        &umodes)) {
1467           SilcChannelPayload entry;
1468           int i = 0;
1469
1470           memset(buf, 0, sizeof(buf));
1471           silc_dlist_start(list);
1472           while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
1473             SilcUInt32 name_len;
1474             char *m = silc_client_chumode_char(umodes[i++]);
1475             char *name = silc_channel_get_name(entry, &name_len);
1476             
1477             if (m)
1478               strncat(buf, m, strlen(m));
1479             strncat(buf, name, name_len);
1480             strncat(buf, " ", 1);
1481             silc_free(m);
1482           }
1483
1484           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1485                              SILCTXT_WHOIS_CHANNELS, buf);
1486           silc_channel_payload_list_free(list);
1487           silc_free(umodes);
1488         }
1489       }
1490       
1491       if (mode) {
1492         memset(buf, 0, sizeof(buf));
1493         silc_get_umode_string(mode, buf, sizeof(buf - 1));
1494         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1495                            SILCTXT_WHOIS_MODES, buf);
1496       }
1497       
1498       if (idle && nickname) {
1499         memset(buf, 0, sizeof(buf));
1500         snprintf(buf, sizeof(buf) - 1, "%lu %s",
1501                  idle > 60 ? (idle / 60) : idle,
1502                  idle > 60 ? "minutes" : "seconds");
1503
1504         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1505                            SILCTXT_WHOIS_IDLE, buf);
1506       }
1507
1508       if (fingerprint) {
1509         fingerprint = silc_fingerprint(fingerprint, 20);
1510         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1511                            SILCTXT_WHOIS_FINGERPRINT, fingerprint);
1512         silc_free(fingerprint);
1513       }
1514
1515       if (attrs)
1516         silc_query_attributes_print(server, silc_client, conn, attrs,
1517                                     client_entry);
1518     }
1519     break;
1520     
1521   case SILC_COMMAND_IDENTIFY:
1522     {
1523       SilcClientEntry client_entry;
1524       
1525       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1526         /* Print the unknown nick for user */
1527         unsigned char *tmp =
1528           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1529                                      3, NULL);
1530         if (tmp)
1531           silc_say_error("%s: %s", tmp, 
1532                          silc_get_status_message(status));
1533         break;
1534       } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1535         /* Try to find the entry for the unknown client ID, since we
1536            might have, and print the nickname of it for user. */
1537         SilcUInt32 tmp_len;
1538         unsigned char *tmp =
1539           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1540                                      2, &tmp_len);
1541         if (tmp) {
1542           SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len,
1543                                                              NULL);
1544           if (client_id) {
1545             client_entry = silc_client_get_client_by_id(client, conn,
1546                                                         client_id);
1547             if (client_entry && client_entry->nickname)
1548               silc_say_error("%s: %s", client_entry->nickname,
1549                              silc_get_status_message(status));
1550             silc_free(client_id);
1551           }
1552         }
1553         break;
1554       }
1555
1556       break;
1557     }
1558
1559   case SILC_COMMAND_WHOWAS:
1560     {
1561       char *nickname, *username, *realname;
1562       
1563       if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
1564           status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1565         char *tmp;
1566         tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1567                                          3, NULL);
1568         if (tmp)
1569           silc_say_error("%s: %s", tmp, 
1570                          silc_get_status_message(status));
1571         break;
1572       } else if (!success) {
1573         silc_say_error("WHOWAS: %s", silc_get_status_message(status));
1574         return;
1575       }
1576       
1577       (void)va_arg(vp, SilcClientEntry);
1578       nickname = va_arg(vp, char *);
1579       username = va_arg(vp, char *);
1580       realname = va_arg(vp, char *);
1581       
1582       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1583                          SILCTXT_WHOWAS_USERINFO, nickname, username, 
1584                          realname ? realname : "");
1585     }
1586     break;
1587     
1588   case SILC_COMMAND_INVITE:
1589     {
1590       SilcChannelEntry channel;
1591       SilcBuffer payload;
1592       SilcArgumentPayload invite_list;
1593       SilcUInt32 argc;
1594       
1595       if (!success)
1596         return;
1597       
1598       channel = va_arg(vp, SilcChannelEntry);
1599       payload = va_arg(vp, SilcBuffer);
1600
1601       if (payload) {
1602         SILC_GET16_MSB(argc, payload->data);
1603         invite_list = silc_argument_payload_parse(payload->data + 2, 
1604                                                   payload->len - 2, argc);
1605         if (invite_list) {
1606           silc_parse_inviteban_list(client, conn, server, channel, 
1607                                     "invite", invite_list);
1608           silc_argument_payload_free(invite_list);
1609         }
1610       }
1611     }
1612     break;
1613
1614   case SILC_COMMAND_JOIN: 
1615     {
1616       char *channel, *mode, *topic;
1617       SilcUInt32 modei;
1618       SilcChannelEntry channel_entry;
1619       SilcBuffer client_id_list;
1620       SilcUInt32 list_count;
1621
1622       if (!success)
1623         return;
1624
1625       channel = va_arg(vp, char *);
1626       channel_entry = va_arg(vp, SilcChannelEntry);
1627       modei = va_arg(vp, SilcUInt32);
1628       (void)va_arg(vp, SilcUInt32);
1629       (void)va_arg(vp, unsigned char *);
1630       (void)va_arg(vp, unsigned char *);
1631       (void)va_arg(vp, unsigned char *);
1632       topic = va_arg(vp, char *);
1633       (void)va_arg(vp, unsigned char *);
1634       list_count = va_arg(vp, SilcUInt32);
1635       client_id_list = va_arg(vp, SilcBuffer);
1636
1637       chanrec = silc_channel_find(server, channel);
1638       if (!chanrec)
1639         chanrec = silc_channel_create(server, channel, channel, TRUE);
1640
1641       if (topic) {
1642         char tmp[256], *cp, *dm = NULL;
1643         g_free_not_null(chanrec->topic);
1644
1645         if (!silc_term_utf8() && silc_utf8_valid(topic, strlen(topic))) {
1646           memset(tmp, 0, sizeof(tmp));
1647           cp = tmp;
1648           if (strlen(topic) > sizeof(tmp) - 1) {
1649             dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
1650             cp = dm;
1651           }
1652
1653           silc_utf8_decode(topic, strlen(topic), SILC_STRING_LANGUAGE,
1654                            cp, strlen(topic));
1655           topic = cp;
1656         }
1657
1658         chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1659         signal_emit("channel topic changed", 1, chanrec);
1660
1661         silc_free(dm);
1662       }
1663
1664       mode = silc_client_chmode(modei, 
1665                                 channel_entry->channel_key ? 
1666                                 silc_cipher_get_name(channel_entry->
1667                                                      channel_key) : "",
1668                                 channel_entry->hmac ? 
1669                                 silc_hmac_get_name(channel_entry->hmac) : "");
1670       g_free_not_null(chanrec->mode);
1671       chanrec->mode = g_strdup(mode == NULL ? "" : mode);
1672       signal_emit("channel mode changed", 1, chanrec);
1673
1674       /* Resolve the client information */
1675       silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
1676                                       silc_client_join_get_users, 
1677                                       channel_entry);
1678
1679       break;
1680     }
1681
1682   case SILC_COMMAND_NICK: 
1683     {
1684       char *old;
1685       SilcClientEntry client_entry = va_arg(vp, SilcClientEntry);
1686       GSList *nicks;
1687       
1688       if (!success)
1689         return;
1690
1691       nicks = nicklist_get_same(SERVER(server), client_entry->nickname);
1692       if ((nicks != NULL) && 
1693         (strcmp(SERVER(server)->nick, client_entry->nickname))) {
1694         char buf[512];
1695         SilcClientEntry collider, old;
1696
1697         old = ((SILC_NICK_REC *)(nicks->next->data))->silc_user->client;
1698         collider = silc_client_get_client_by_id(client, conn,
1699                                                 old->id);
1700         
1701         memset(buf, 0, sizeof(buf));
1702         snprintf(buf, sizeof(buf) - 1, "%s@%s",
1703                  collider->username, collider->hostname);
1704         nicklist_rename_unique(SERVER(server),
1705                                old, old->nickname,
1706                                collider, collider->nickname);
1707         silc_print_nick_change(server, collider->nickname,
1708                                client_entry->nickname, buf);
1709         g_slist_free(nicks);
1710       }
1711
1712       old = g_strdup(server->nick);
1713       server_change_nick(SERVER(server), client_entry->nickname);
1714       nicklist_rename_unique(SERVER(server),
1715                              server->conn->local_entry, server->nick,
1716                              client_entry, client_entry->nickname);
1717       signal_emit("message own_nick", 4, server, server->nick, old, "");
1718       g_free(old);
1719       break;
1720     }
1721     
1722   case SILC_COMMAND_LIST:
1723     {
1724       char *topic, *name;
1725       int usercount;
1726       char users[20];
1727       char tmp[256], *cp, *dm = NULL;
1728       
1729       if (!success)
1730         return;
1731       
1732       (void)va_arg(vp, SilcChannelEntry);
1733       name = va_arg(vp, char *);
1734       topic = va_arg(vp, char *);
1735       usercount = va_arg(vp, int);
1736
1737       if (topic && !silc_term_utf8() &&
1738           silc_utf8_valid(topic, strlen(topic))) {
1739         memset(tmp, 0, sizeof(tmp));
1740         cp = tmp;
1741         if (strlen(topic) > sizeof(tmp) - 1) {
1742           dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
1743           cp = dm;
1744         }
1745
1746         silc_utf8_decode(topic, strlen(topic), SILC_STRING_LANGUAGE,
1747                          cp, strlen(topic));
1748         topic = cp;
1749       }
1750       
1751       if (status == SILC_STATUS_LIST_START ||
1752           status == SILC_STATUS_OK)
1753         printformat_module("fe-common/silc", server, NULL,
1754                            MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
1755
1756       if (!usercount)
1757         snprintf(users, sizeof(users) - 1, "N/A");
1758       else
1759         snprintf(users, sizeof(users) - 1, "%d", usercount);
1760       printformat_module("fe-common/silc", server, NULL,
1761                          MSGLEVEL_CRAP, SILCTXT_LIST,
1762                          name, users, topic ? topic : "");
1763       silc_free(dm);
1764     }
1765     break;
1766     
1767   case SILC_COMMAND_UMODE:
1768     {
1769       SilcUInt32 mode;
1770       char *reason;
1771       
1772       if (!success)
1773         return;
1774       
1775       mode = va_arg(vp, SilcUInt32);
1776       
1777       if (mode & SILC_UMODE_SERVER_OPERATOR &&
1778           !(server->umode & SILC_UMODE_SERVER_OPERATOR))
1779         printformat_module("fe-common/silc", server, NULL,
1780                            MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1781
1782       if (mode & SILC_UMODE_ROUTER_OPERATOR &&
1783           !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
1784         printformat_module("fe-common/silc", server, NULL,
1785                            MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1786
1787       if ((mode & SILC_UMODE_GONE) != (server->umode & SILC_UMODE_GONE)) {
1788         if (mode & SILC_UMODE_GONE) {      
1789           if ((server->away_reason != NULL) && (server->away_reason[0] != '\0'))
1790             reason = g_strdup(server->away_reason);
1791           else
1792             reason = g_strdup("away");
1793         } else
1794           reason = g_strdup("");
1795
1796         silc_set_away(reason, server);
1797
1798         g_free(reason);
1799       }
1800
1801       server->umode = mode;
1802       signal_emit("user mode changed", 2, server, NULL);
1803     }
1804     break;
1805     
1806   case SILC_COMMAND_OPER:
1807     if (!success)
1808       return;
1809
1810     server->umode |= SILC_UMODE_SERVER_OPERATOR;
1811     signal_emit("user mode changed", 2, server, NULL);
1812
1813     printformat_module("fe-common/silc", server, NULL,
1814                        MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1815     break;
1816     
1817   case SILC_COMMAND_SILCOPER:
1818     if (!success)
1819       return;
1820
1821     server->umode |= SILC_UMODE_ROUTER_OPERATOR;
1822     signal_emit("user mode changed", 2, server, NULL);
1823
1824     printformat_module("fe-common/silc", server, NULL,
1825                        MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1826     break;
1827     
1828   case SILC_COMMAND_USERS: 
1829     {
1830       SilcHashTableList htl;
1831       SilcChannelEntry channel;
1832       SilcChannelUser chu;
1833       
1834       if (!success)
1835         return;
1836       
1837       channel = va_arg(vp, SilcChannelEntry);
1838       
1839       printformat_module("fe-common/silc", server, channel->channel_name,
1840                          MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
1841                          channel->channel_name);
1842
1843       silc_hash_table_list(channel->user_list, &htl);
1844       while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1845         SilcClientEntry e = chu->client;
1846         char stat[5], *mode;
1847
1848         if (!e->nickname)
1849           continue;
1850         
1851         memset(stat, 0, sizeof(stat));
1852         mode = silc_client_chumode_char(chu->mode);
1853         if (e->mode & SILC_UMODE_GONE)
1854           strcat(stat, "G");
1855         else if (e->mode & SILC_UMODE_INDISPOSED)
1856           strcat(stat, "I");
1857         else if (e->mode & SILC_UMODE_BUSY)
1858           strcat(stat, "B");
1859         else if (e->mode & SILC_UMODE_PAGE)
1860           strcat(stat, "P");
1861         else if (e->mode & SILC_UMODE_HYPER)
1862           strcat(stat, "H");
1863         else if (e->mode & SILC_UMODE_ROBOT)
1864           strcat(stat, "R");
1865         else if (e->mode & SILC_UMODE_ANONYMOUS)
1866           strcat(stat, "?");
1867         else
1868           strcat(stat, "A");
1869         if (mode)
1870           strcat(stat, mode);
1871
1872         printformat_module("fe-common/silc", server, channel->channel_name,
1873                            MSGLEVEL_CRAP, SILCTXT_USERS,
1874                            e->nickname, stat, 
1875                            e->username ? e->username : "",
1876                            e->hostname ? e->hostname : "",
1877                            e->realname ? e->realname : "");
1878         if (mode)
1879           silc_free(mode);
1880       }
1881       silc_hash_table_list_reset(&htl);
1882     }
1883     break;
1884
1885   case SILC_COMMAND_BAN:
1886     {
1887       SilcChannelEntry channel;
1888       SilcBuffer payload;
1889       SilcArgumentPayload ban_list;
1890       SilcUInt32 argc;
1891       
1892       if (!success)
1893         return;
1894       
1895       channel = va_arg(vp, SilcChannelEntry);
1896       payload = va_arg(vp, SilcBuffer);
1897
1898       if (payload) {
1899         SILC_GET16_MSB(argc, payload->data);
1900         ban_list = silc_argument_payload_parse(payload->data + 2, 
1901                                                payload->len - 2, argc);
1902         if (ban_list) {
1903           silc_parse_inviteban_list(client, conn, server, channel, 
1904                                     "ban", ban_list);
1905           silc_argument_payload_free(ban_list);
1906         }
1907       }
1908     }
1909     break;
1910     
1911   case SILC_COMMAND_GETKEY:
1912     {
1913       SilcIdType id_type;
1914       void *entry;
1915       SilcPublicKey public_key;
1916       unsigned char *pk;
1917       SilcUInt32 pk_len;
1918       GetkeyContext getkey;
1919       char *name;
1920       
1921       if (!success)
1922         return;
1923       
1924       id_type = va_arg(vp, SilcUInt32);
1925       entry = va_arg(vp, void *);
1926       public_key = va_arg(vp, SilcPublicKey);
1927
1928       if (public_key) {
1929         pk = silc_pkcs_public_key_encode(public_key, &pk_len);
1930
1931         getkey = silc_calloc(1, sizeof(*getkey));
1932         getkey->entry = entry;
1933         getkey->id_type = id_type;
1934         getkey->client = client;
1935         getkey->conn = conn;
1936         getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1937
1938         name = (id_type == SILC_ID_CLIENT ? 
1939                 ((SilcClientEntry)entry)->nickname :
1940                 ((SilcServerEntry)entry)->server_name);
1941
1942         silc_verify_public_key_internal(client, conn, name,
1943                                         (id_type == SILC_ID_CLIENT ?
1944                                          SILC_SOCKET_TYPE_CLIENT :
1945                                          SILC_SOCKET_TYPE_SERVER),
1946                                         pk, pk_len, SILC_SKE_PK_TYPE_SILC,
1947                                         silc_getkey_cb, getkey);
1948         silc_free(pk);
1949       } else {
1950         printformat_module("fe-common/silc", server, NULL,
1951                            MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOKEY);
1952       }
1953     }
1954     break;
1955
1956   case SILC_COMMAND_INFO:
1957     {
1958       SilcServerEntry server_entry;
1959       char *server_name;
1960       char *server_info;
1961
1962       if (!success)
1963         return;
1964       
1965       server_entry = va_arg(vp, SilcServerEntry);
1966       server_name = va_arg(vp, char *);
1967       server_info = va_arg(vp, char *);
1968
1969       if (server_name && server_info )
1970         {
1971           printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
1972           printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
1973         }
1974     }
1975     break;
1976     
1977   case SILC_COMMAND_TOPIC:
1978     {
1979       SilcChannelEntry channel;
1980       char *topic;
1981       char tmp[256], *cp, *dm = NULL;
1982       
1983       if (!success)
1984         return;
1985       
1986       channel = va_arg(vp, SilcChannelEntry);
1987       topic = va_arg(vp, char *);
1988
1989       if (topic && !silc_term_utf8() &&
1990           silc_utf8_valid(topic, strlen(topic))) {
1991         memset(tmp, 0, sizeof(tmp));
1992         cp = tmp;
1993         if (strlen(topic) > sizeof(tmp) - 1) {
1994           dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
1995           cp = dm;
1996         }
1997
1998         silc_utf8_decode(topic, strlen(topic), SILC_STRING_LANGUAGE,
1999                          cp, strlen(topic));
2000         topic = cp;
2001       }
2002
2003       if (topic) {
2004         chanrec = silc_channel_find_entry(server, channel);
2005         if (chanrec) {
2006           g_free_not_null(chanrec->topic);
2007           chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
2008           signal_emit("channel topic changed", 1, chanrec);
2009         }
2010         printformat_module("fe-common/silc", server, channel->channel_name,
2011                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
2012                            channel->channel_name, topic);
2013       } else {
2014         printformat_module("fe-common/silc", server, channel->channel_name,
2015                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
2016                            channel->channel_name);
2017       }
2018       silc_free(dm);
2019     }
2020     break;
2021
2022   case SILC_COMMAND_WATCH:
2023     break;
2024   
2025   case SILC_COMMAND_STATS:
2026     {
2027       SilcUInt32 starttime, uptime, my_clients, my_channels, my_server_ops,
2028                  my_router_ops, cell_clients, cell_channels, cell_servers,
2029                  clients, channels, servers, routers, server_ops, router_ops;
2030       SilcUInt32 buf_len;
2031       SilcBufferStruct buf;
2032       unsigned char *tmp_buf;
2033       char tmp[40];
2034       const char *tmptime;
2035       int days, hours, mins, secs;
2036
2037       if (!success)
2038         return;
2039
2040       tmp_buf = va_arg(vp, unsigned char *);
2041       buf_len = va_arg(vp, SilcUInt32);
2042
2043       if (!tmp_buf || !buf_len) {
2044         printtext(server, NULL, MSGLEVEL_CRAP, "No statistics available");
2045         return;
2046       }
2047
2048       /* Get statistics structure */
2049       silc_buffer_set(&buf, tmp_buf, buf_len);
2050       silc_buffer_unformat(&buf,
2051                            SILC_STR_UI_INT(&starttime),
2052                            SILC_STR_UI_INT(&uptime),
2053                            SILC_STR_UI_INT(&my_clients),
2054                            SILC_STR_UI_INT(&my_channels),
2055                            SILC_STR_UI_INT(&my_server_ops),
2056                            SILC_STR_UI_INT(&my_router_ops),
2057                            SILC_STR_UI_INT(&cell_clients),
2058                            SILC_STR_UI_INT(&cell_channels),
2059                            SILC_STR_UI_INT(&cell_servers),
2060                            SILC_STR_UI_INT(&clients),
2061                            SILC_STR_UI_INT(&channels),
2062                            SILC_STR_UI_INT(&servers),
2063                            SILC_STR_UI_INT(&routers),
2064                            SILC_STR_UI_INT(&server_ops),
2065                            SILC_STR_UI_INT(&router_ops),
2066                            SILC_STR_END);
2067
2068       tmptime = silc_get_time(starttime);
2069       printformat_module("fe-common/silc", server, NULL,
2070                          MSGLEVEL_CRAP, SILCTXT_STATS,
2071                          "Local server start time", tmptime);
2072
2073       days = uptime / (24 * 60 * 60);
2074       uptime -= days * (24 * 60 * 60);
2075       hours = uptime / (60 * 60);
2076       uptime -= hours * (60 * 60);
2077       mins = uptime / 60;
2078       uptime -= mins * 60;
2079       secs = uptime;
2080       snprintf(tmp, sizeof(tmp) - 1, "%d days %d hours %d mins %d secs",
2081                days, hours, mins, secs);
2082       printformat_module("fe-common/silc", server, NULL,
2083                          MSGLEVEL_CRAP, SILCTXT_STATS,
2084                          "Local server uptime", tmp);
2085
2086       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_clients);
2087       printformat_module("fe-common/silc", server, NULL,
2088                          MSGLEVEL_CRAP, SILCTXT_STATS,
2089                          "Local server clients", tmp);
2090
2091       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_channels);
2092       printformat_module("fe-common/silc", server, NULL,
2093                          MSGLEVEL_CRAP, SILCTXT_STATS,
2094                          "Local server channels", tmp);
2095
2096       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_server_ops);
2097       printformat_module("fe-common/silc", server, NULL,
2098                          MSGLEVEL_CRAP, SILCTXT_STATS,
2099                          "Local server operators", tmp);
2100
2101       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_router_ops);
2102       printformat_module("fe-common/silc", server, NULL,
2103                          MSGLEVEL_CRAP, SILCTXT_STATS,
2104                          "Local router operators", tmp);
2105
2106       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_clients);
2107       printformat_module("fe-common/silc", server, NULL,
2108                          MSGLEVEL_CRAP, SILCTXT_STATS,
2109                          "Local cell clients", tmp);
2110
2111       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_channels);
2112       printformat_module("fe-common/silc", server, NULL,
2113                          MSGLEVEL_CRAP, SILCTXT_STATS,
2114                          "Local cell channels", tmp);
2115
2116       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_servers);
2117       printformat_module("fe-common/silc", server, NULL,
2118                          MSGLEVEL_CRAP, SILCTXT_STATS,
2119                          "Local cell servers", tmp);
2120
2121       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)clients);
2122       printformat_module("fe-common/silc", server, NULL,
2123                          MSGLEVEL_CRAP, SILCTXT_STATS,
2124                          "Total clients", tmp);
2125
2126       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)channels);
2127       printformat_module("fe-common/silc", server, NULL,
2128                          MSGLEVEL_CRAP, SILCTXT_STATS,
2129                          "Total channels", tmp);
2130
2131       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)servers);
2132       printformat_module("fe-common/silc", server, NULL,
2133                          MSGLEVEL_CRAP, SILCTXT_STATS,
2134                          "Total servers", tmp);
2135
2136       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)routers);
2137       printformat_module("fe-common/silc", server, NULL,
2138                          MSGLEVEL_CRAP, SILCTXT_STATS,
2139                          "Total routers", tmp);
2140
2141       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)server_ops);
2142       printformat_module("fe-common/silc", server, NULL,
2143                          MSGLEVEL_CRAP, SILCTXT_STATS,
2144                            "Total server operators", tmp);
2145
2146       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)router_ops);
2147       printformat_module("fe-common/silc", server, NULL,
2148                          MSGLEVEL_CRAP, SILCTXT_STATS,
2149                          "Total router operators", tmp);
2150     }
2151     break;
2152
2153   }
2154
2155   va_end(vp);
2156 }
2157
2158 typedef struct {
2159   SilcClient client;
2160   SilcClientConnection conn;
2161   char *filename;
2162   char *entity;
2163   char *entity_name;
2164   unsigned char *pk;
2165   SilcUInt32 pk_len;
2166   SilcSKEPKType pk_type;
2167   SilcVerifyPublicKey completion;
2168   void *context;
2169 } *PublicKeyVerify;
2170
2171 static void verify_public_key_completion(const char *line, void *context)
2172 {
2173   PublicKeyVerify verify = (PublicKeyVerify)context;
2174
2175   if (line[0] == 'Y' || line[0] == 'y') {
2176     /* Call the completion */
2177     if (verify->completion)
2178       verify->completion(TRUE, verify->context);
2179
2180     /* Save the key for future checking */
2181     silc_pkcs_save_public_key_data(verify->filename, verify->pk, 
2182                                    verify->pk_len, SILC_PKCS_FILE_PEM);
2183   } else {
2184     /* Call the completion */
2185     if (verify->completion)
2186       verify->completion(FALSE, verify->context);
2187
2188     printformat_module("fe-common/silc", NULL, NULL,
2189                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, 
2190                        verify->entity_name ? verify->entity_name :
2191                        verify->entity);
2192   }
2193
2194   silc_free(verify->filename);
2195   silc_free(verify->entity);
2196   silc_free(verify->entity_name);
2197   silc_free(verify->pk);
2198   silc_free(verify);
2199 }
2200
2201 /* Internal routine to verify public key. If the `completion' is provided
2202    it will be called to indicate whether public was verified or not. For
2203    server/router public key this will check for filename that includes the
2204    remote host's IP address and remote host's hostname. */
2205
2206 static void 
2207 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
2208                                 const char *name, SilcSocketType conn_type, 
2209                                 unsigned char *pk, SilcUInt32 pk_len, 
2210                                 SilcSKEPKType pk_type,
2211                                 SilcVerifyPublicKey completion, void *context)
2212 {
2213   int i;
2214   char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
2215   char *fingerprint, *babbleprint, *format;
2216   struct passwd *pw;
2217   struct stat st;
2218   char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
2219                    conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
2220                   "server" : "client");
2221   PublicKeyVerify verify;
2222
2223   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
2224     printformat_module("fe-common/silc", NULL, NULL,
2225                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED, 
2226                        entity, pk_type);
2227     if (completion)
2228       completion(FALSE, context);
2229     return;
2230   }
2231
2232   pw = getpwuid(getuid());
2233   if (!pw) {
2234     if (completion)
2235       completion(FALSE, context);
2236     return;
2237   }
2238
2239   memset(filename, 0, sizeof(filename));
2240   memset(filename2, 0, sizeof(filename2));
2241   memset(file, 0, sizeof(file));
2242
2243   if (conn_type == SILC_SOCKET_TYPE_SERVER ||
2244       conn_type == SILC_SOCKET_TYPE_ROUTER) {
2245     if (!name) {
2246       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
2247                conn->sock->ip, conn->sock->port);
2248       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
2249                get_irssi_dir(), entity, file);
2250       
2251       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
2252                conn->sock->hostname, conn->sock->port);
2253       snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s", 
2254                get_irssi_dir(), entity, file);
2255       
2256       ipf = filename;
2257       hostf = filename2;
2258     } else {
2259       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
2260                name, conn->sock->port);
2261       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
2262                get_irssi_dir(), entity, file);
2263       
2264       ipf = filename;
2265     }
2266   } else {
2267     /* Replace all whitespaces with `_'. */
2268     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2269     for (i = 0; i < strlen(fingerprint); i++)
2270       if (fingerprint[i] == ' ')
2271         fingerprint[i] = '_';
2272     
2273     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
2274     snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
2275              get_irssi_dir(), entity, file);
2276     silc_free(fingerprint);
2277
2278     ipf = filename;
2279   }
2280
2281   /* Take fingerprint of the public key */
2282   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2283   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
2284
2285   verify = silc_calloc(1, sizeof(*verify));
2286   verify->client = client;
2287   verify->conn = conn;
2288   verify->filename = strdup(ipf);
2289   verify->entity = strdup(entity);
2290   verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ?
2291                          (name ? strdup(name) : strdup(conn->sock->hostname))
2292                          : NULL);
2293   verify->pk = silc_memdup(pk, pk_len);
2294   verify->pk_len = pk_len;
2295   verify->pk_type = pk_type;
2296   verify->completion = completion;
2297   verify->context = context;
2298
2299   /* Check whether this key already exists */
2300   if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
2301     /* Key does not exist, ask user to verify the key and save it */
2302
2303     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2304                        SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
2305                        verify->entity_name : entity);
2306     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2307                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2308     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2309                        SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2310     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2311                              SILCTXT_PUBKEY_ACCEPT);
2312     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2313                             format, 0, verify);
2314     g_free(format);
2315     silc_free(fingerprint);
2316     return;
2317   } else {
2318     /* The key already exists, verify it. */
2319     SilcPublicKey public_key;
2320     unsigned char *encpk;
2321     SilcUInt32 encpk_len;
2322
2323     /* Load the key file, try for both IP filename and hostname filename */
2324     if (!silc_pkcs_load_public_key(ipf, &public_key, 
2325                                    SILC_PKCS_FILE_PEM) &&
2326         !silc_pkcs_load_public_key(ipf, &public_key, 
2327                                    SILC_PKCS_FILE_BIN) &&
2328         (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key, 
2329                                                SILC_PKCS_FILE_PEM) &&
2330                     !silc_pkcs_load_public_key(hostf, &public_key, 
2331                                                SILC_PKCS_FILE_BIN)))) {
2332       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2333                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
2334                          verify->entity_name : entity);
2335       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2336                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2337       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2338                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2339       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2340                          SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
2341       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2342                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2343       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2344                               format, 0, verify);
2345       g_free(format);
2346       silc_free(fingerprint);
2347       return;
2348     }
2349
2350     /* Encode the key data */
2351     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
2352     if (!encpk) {
2353       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2354                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
2355                          verify->entity_name : entity);
2356       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2357                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2358       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2359                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2360       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2361                          SILCTXT_PUBKEY_MALFORMED, entity);
2362       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2363                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2364       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2365                               format, 0, verify);
2366       g_free(format);
2367       silc_free(fingerprint);
2368       return;
2369     }
2370
2371     /* Compare the keys */
2372     if (memcmp(encpk, pk, encpk_len)) {
2373       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2374                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
2375                          verify->entity_name : entity);
2376       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2377                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2378       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2379                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2380       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2381                          SILCTXT_PUBKEY_NO_MATCH, entity);
2382       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2383                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
2384       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2385                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
2386
2387       /* Ask user to verify the key and save it */
2388       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2389                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2390       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2391                               format, 0, verify);
2392       g_free(format);
2393       silc_free(fingerprint);
2394       return;
2395     }
2396
2397     /* Local copy matched */
2398     if (completion)
2399       completion(TRUE, context);
2400     silc_free(fingerprint);
2401     silc_free(verify->filename);
2402     silc_free(verify->entity);
2403     silc_free(verify->entity_name);
2404     silc_free(verify->pk);
2405     silc_free(verify);
2406   }
2407 }
2408
2409 /* Verifies received public key. The `conn_type' indicates which entity
2410    (server, client etc.) has sent the public key. If user decides to trust
2411    the key may be saved as trusted public key for later use. The 
2412    `completion' must be called after the public key has been verified. */
2413
2414 void 
2415 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
2416                        SilcSocketType conn_type, unsigned char *pk, 
2417                        SilcUInt32 pk_len, SilcSKEPKType pk_type,
2418                        SilcVerifyPublicKey completion, void *context)
2419 {
2420   silc_verify_public_key_internal(client, conn, NULL, conn_type, pk,
2421                                   pk_len, pk_type,
2422                                   completion, context);
2423 }
2424
2425 /* Asks passphrase from user on the input line. */
2426
2427 typedef struct {
2428   SilcAskPassphrase completion;
2429   void *context;
2430 } *AskPassphrase;
2431
2432 void ask_passphrase_completion(const char *passphrase, void *context)
2433 {
2434   AskPassphrase p = (AskPassphrase)context;
2435   if (passphrase && passphrase[0] == '\0')
2436     passphrase = NULL;
2437   p->completion((unsigned char *)passphrase, 
2438                 passphrase ? strlen(passphrase) : 0, p->context);
2439   silc_free(p);
2440 }
2441
2442 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
2443                          SilcAskPassphrase completion, void *context)
2444 {
2445   AskPassphrase p = silc_calloc(1, sizeof(*p));
2446   p->completion = completion;
2447   p->context = context;
2448
2449   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
2450                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
2451 }
2452
2453 typedef struct {
2454   SilcGetAuthMeth completion;
2455   void *context;
2456 } *InternalGetAuthMethod;
2457
2458 /* Callback called when we've received the authentication method information
2459    from the server after we've requested it. This will get the authentication
2460    data from the user if needed. */
2461
2462 static void silc_get_auth_method_callback(SilcClient client,
2463                                           SilcClientConnection conn,
2464                                           SilcAuthMethod auth_meth,
2465                                           void *context)
2466 {
2467   InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
2468
2469   SILC_LOG_DEBUG(("Start"));
2470
2471   switch (auth_meth) {
2472   case SILC_AUTH_NONE:
2473     /* No authentication required. */
2474     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2475     break;
2476   case SILC_AUTH_PASSWORD:
2477     {
2478       /* Check whether we find the password for this server in our
2479          configuration.  If not, then don't provide so library will ask
2480          it from the user. */
2481       SERVER_SETUP_REC *setup = server_setup_find_port(conn->remote_host,
2482                                                        conn->remote_port);
2483       if (!setup || !setup->password) {
2484         (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2485         break;
2486       }
2487       
2488       (*internal->completion)(TRUE, auth_meth, setup->password,
2489                               strlen(setup->password), internal->context);
2490     }
2491     break;
2492   case SILC_AUTH_PUBLIC_KEY:
2493     /* Do not get the authentication data now, the library will generate
2494        it using our default key, if we do not provide it here. */
2495     /* XXX In the future when we support multiple local keys and multiple
2496        local certificates we will need to ask from user which one to use. */
2497     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2498     break;
2499   }
2500
2501   silc_free(internal);
2502 }
2503
2504 /* Find authentication method and authentication data by hostname and
2505    port. The hostname may be IP address as well. The found authentication
2506    method and authentication data is returned to `auth_meth', `auth_data'
2507    and `auth_data_len'. The function returns TRUE if authentication method
2508    is found and FALSE if not. `conn' may be NULL. */
2509
2510 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
2511                           char *hostname, SilcUInt16 port,
2512                           SilcGetAuthMeth completion, void *context)
2513 {
2514   InternalGetAuthMethod internal;
2515
2516   SILC_LOG_DEBUG(("Start"));
2517
2518   /* If we do not have this connection configured by the user in a
2519      configuration file then resolve the authentication method from the
2520      server for this session. */
2521   internal = silc_calloc(1, sizeof(*internal));
2522   internal->completion = completion;
2523   internal->context = context;
2524
2525   silc_client_request_authentication_method(client, conn, 
2526                                             silc_get_auth_method_callback,
2527                                             internal);
2528 }
2529
2530 /* Notifies application that failure packet was received.  This is called
2531    if there is some protocol active in the client.  The `protocol' is the
2532    protocol context.  The `failure' is opaque pointer to the failure
2533    indication.  Note, that the `failure' is protocol dependant and application
2534    must explicitly cast it to correct type.  Usually `failure' is 32 bit
2535    failure type (see protocol specs for all protocol failure types). */
2536
2537 void silc_failure(SilcClient client, SilcClientConnection conn, 
2538                   SilcProtocol protocol, void *failure)
2539 {
2540   SILC_LOG_DEBUG(("Start"));
2541
2542   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
2543     SilcSKEStatus status = (SilcSKEStatus)failure;
2544     
2545     if (status == SILC_SKE_STATUS_BAD_VERSION)
2546       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2547                          SILCTXT_KE_BAD_VERSION);
2548     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
2549       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2550                          SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
2551     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
2552       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2553                          SILCTXT_KE_UNKNOWN_GROUP);
2554     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
2555       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2556                          SILCTXT_KE_UNKNOWN_CIPHER);
2557     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
2558       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2559                          SILCTXT_KE_UNKNOWN_PKCS);
2560     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
2561       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2562                          SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
2563     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
2564       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2565                          SILCTXT_KE_UNKNOWN_HMAC);
2566     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
2567       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2568                          SILCTXT_KE_INCORRECT_SIGNATURE);
2569     if (status == SILC_SKE_STATUS_INVALID_COOKIE)
2570       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2571                          SILCTXT_KE_INVALID_COOKIE);
2572   }
2573
2574   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
2575     SilcUInt32 err = (SilcUInt32)failure;
2576
2577     if (err == SILC_AUTH_FAILED)
2578       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
2579                          SILCTXT_AUTH_FAILED);
2580   }
2581 }
2582
2583 /* Asks whether the user would like to perform the key agreement protocol.
2584    This is called after we have received an key agreement packet or an
2585    reply to our key agreement packet. This returns TRUE if the user wants
2586    the library to perform the key agreement protocol and FALSE if it is not
2587    desired (application may start it later by calling the function
2588    silc_client_perform_key_agreement). */
2589
2590 bool silc_key_agreement(SilcClient client, SilcClientConnection conn,
2591                         SilcClientEntry client_entry, const char *hostname,
2592                         SilcUInt16 port, SilcKeyAgreementCallback *completion,
2593                         void **context)
2594 {
2595   char portstr[12];
2596
2597   SILC_LOG_DEBUG(("Start"));
2598
2599   /* We will just display the info on the screen and return FALSE and user
2600      will have to start the key agreement with a command. */
2601
2602   if (hostname) 
2603     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2604
2605   if (!hostname)
2606     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2607                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
2608   else
2609     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2610                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST, 
2611                        client_entry->nickname, hostname, portstr);
2612
2613   *completion = NULL;
2614   *context = NULL;
2615
2616   return FALSE;
2617 }
2618
2619 /* Notifies application that file transfer protocol session is being
2620    requested by the remote client indicated by the `client_entry' from
2621    the `hostname' and `port'. The `session_id' is the file transfer
2622    session and it can be used to either accept or reject the file
2623    transfer request, by calling the silc_client_file_receive or
2624    silc_client_file_close, respectively. */
2625
2626 void silc_ftp(SilcClient client, SilcClientConnection conn,
2627               SilcClientEntry client_entry, SilcUInt32 session_id,
2628               const char *hostname, SilcUInt16 port)
2629 {
2630   SILC_SERVER_REC *server;
2631   char portstr[12];
2632   FtpSession ftp = NULL;
2633
2634   SILC_LOG_DEBUG(("Start"));
2635
2636   server = conn->context;
2637
2638   silc_dlist_start(server->ftp_sessions);
2639   while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
2640     if (ftp->client_entry == client_entry &&
2641         ftp->session_id == session_id) {
2642       server->current_session = ftp;
2643       break;
2644     }
2645   }
2646   if (ftp == SILC_LIST_END) {
2647     ftp = silc_calloc(1, sizeof(*ftp));
2648     ftp->client_entry = client_entry;
2649     ftp->session_id = session_id;
2650     ftp->send = FALSE;
2651     ftp->conn = conn;
2652     silc_dlist_add(server->ftp_sessions, ftp);
2653     server->current_session = ftp;
2654   }
2655
2656   if (hostname) 
2657     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2658
2659   if (!hostname)
2660     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2661                        SILCTXT_FILE_REQUEST, client_entry->nickname);
2662   else
2663     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2664                        SILCTXT_FILE_REQUEST_HOST, 
2665                        client_entry->nickname, hostname, portstr);
2666 }
2667
2668 /* Delivers SILC session detachment data indicated by `detach_data' to the
2669    application.  If application has issued SILC_COMMAND_DETACH command
2670    the client session in the SILC network is not quit.  The client remains
2671    in the network but is detached.  The detachment data may be used later
2672    to resume the session in the SILC Network.  The appliation is
2673    responsible of saving the `detach_data', to for example in a file.
2674
2675    The detachment data can be given as argument to the functions
2676    silc_client_connect_to_server, or silc_client_add_connection when
2677    creating connection to remote server, inside SilcClientConnectionParams
2678    structure.  If it is provided the client library will attempt to resume
2679    the session in the network.  After the connection is created
2680    successfully, the application is responsible of setting the user
2681    interface for user into the same state it was before detaching (showing
2682    same channels, channel modes, etc).  It can do this by fetching the
2683    information (like joined channels) from the client library. */
2684
2685 void
2686 silc_detach(SilcClient client, SilcClientConnection conn,
2687             const unsigned char *detach_data, SilcUInt32 detach_data_len)
2688 {
2689   char file[256];
2690
2691   /* Save the detachment data to file. */
2692
2693   memset(file, 0, sizeof(file));
2694   snprintf(file, sizeof(file) - 1, "%s/session", get_irssi_dir());
2695   silc_file_writefile(file, detach_data, detach_data_len);
2696 }
2697
2698
2699 /* SILC client operations */
2700 SilcClientOperations ops = {
2701   silc_say,
2702   silc_channel_message,
2703   silc_private_message,
2704   silc_notify,
2705   silc_command,
2706   silc_command_reply,
2707   silc_connect,
2708   silc_disconnect,
2709   silc_get_auth_method,
2710   silc_verify_public_key,
2711   silc_ask_passphrase,
2712   silc_failure,
2713   silc_key_agreement,
2714   silc_ftp,
2715   silc_detach,
2716 };