updates.
[silc.git] / apps / irssi / src / silc / core / client_ops.c
1 /*
2
3   client_ops.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 2001 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20
21 #include "module.h"
22 #include "chat-protocols.h"
23 #include "args.h"
24
25 #include "chatnets.h"
26 #include "servers-setup.h"
27 #include "channels-setup.h"
28 #include "silc-servers.h"
29 #include "silc-channels.h"
30 #include "silc-queries.h"
31 #include "silc-nicklist.h"
32
33 #include "signals.h"
34 #include "levels.h"
35 #include "settings.h"
36 #include "fe-common/core/printtext.h"
37 #include "fe-common/core/fe-channels.h"
38 #include "fe-common/core/keyboard.h"
39 #include "fe-common/silc/module-formats.h"
40
41 #include "core.h"
42
43 static void 
44 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
45                                 const char *name, SilcSocketType conn_type, 
46                                 unsigned char *pk, SilcUInt32 pk_len, 
47                                 SilcSKEPKType pk_type,
48                                 SilcVerifyPublicKey completion, void *context);
49
50 void silc_say(SilcClient client, SilcClientConnection conn,
51               SilcClientMessageType type, char *msg, ...)
52 {
53   SILC_SERVER_REC *server;
54   va_list va;
55   char *str;
56
57   server = conn == NULL ? NULL : conn->context;
58   
59   va_start(va, msg);
60   str = g_strdup_vprintf(msg, va);
61   printtext(server, NULL, MSGLEVEL_CRAP, "%s", str);
62   g_free(str);
63   va_end(va);
64 }
65
66 void silc_say_error(char *msg, ...)
67 {
68   va_list va;
69   char *str;
70
71   va_start(va, msg);
72   str = g_strdup_vprintf(msg, va);
73   printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str);
74
75   g_free(str);
76   va_end(va);
77 }
78
79 /* Message for a channel. The `sender' is the nickname of the sender 
80    received in the packet. The `channel_name' is the name of the channel. */
81
82 void silc_channel_message(SilcClient client, SilcClientConnection conn,
83                           SilcClientEntry sender, SilcChannelEntry channel,
84                           SilcMessageFlags flags, const unsigned char *message,
85                           SilcUInt32 message_len)
86 {
87   SILC_SERVER_REC *server;
88   SILC_NICK_REC *nick;
89   SILC_CHANNEL_REC *chanrec;
90   
91   SILC_LOG_DEBUG(("Start"));
92
93   if (!message)
94     return;
95
96   server = conn == NULL ? NULL : conn->context;
97   chanrec = silc_channel_find_entry(server, channel);
98   if (!chanrec)
99     return;
100   
101   nick = silc_nicklist_find(chanrec, sender);
102   if (!nick) {
103     /* We didn't find client but it clearly exists, add it. */
104     SilcChannelUser chu = silc_client_on_channel(channel, sender);
105     if (chu)
106       nick = silc_nicklist_insert(chanrec, chu, FALSE);
107   }
108
109   if (flags & SILC_MESSAGE_FLAG_ACTION)
110     printformat_module("fe-common/silc", server, channel->channel_name,
111                        MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_ACTION, 
112                        nick == NULL ? "[<unknown>]" : nick->nick, message);
113   else if (flags & SILC_MESSAGE_FLAG_NOTICE)
114     printformat_module("fe-common/silc", server, channel->channel_name,
115                        MSGLEVEL_NOTICES, SILCTXT_CHANNEL_NOTICE, 
116                        nick == NULL ? "[<unknown>]" : nick->nick, message);
117   else
118     signal_emit("message public", 6, server, message,
119                 nick == NULL ? "[<unknown>]" : nick->nick,
120                 nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
121                 chanrec->name, nick);
122 }
123
124 /* Private message to the client. The `sender' is the nickname of the
125    sender received in the packet. */
126
127 void silc_private_message(SilcClient client, SilcClientConnection conn,
128                           SilcClientEntry sender, SilcMessageFlags flags,
129                           const unsigned char *message,
130                           SilcUInt32 message_len)
131 {
132   SILC_SERVER_REC *server;
133   char userhost[256];
134   
135   SILC_LOG_DEBUG(("Start"));
136
137   server = conn == NULL ? NULL : conn->context;
138   memset(userhost, 0, sizeof(userhost));
139   if (sender->username)
140     snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
141              sender->username, sender->hostname);
142   signal_emit("message private", 4, server, message,
143               sender->nickname ? sender->nickname : "[<unknown>]",
144               sender->username ? userhost : NULL);
145 }
146
147 /* Notify message to the client. The notify arguments are sent in the
148    same order as servers sends them. The arguments are same as received
149    from the server except for ID's.  If ID is received application receives
150    the corresponding entry to the ID. For example, if Client ID is received
151    application receives SilcClientEntry.  Also, if the notify type is
152    for channel the channel entry is sent to application (even if server
153    does not send it). */
154
155 void silc_notify(SilcClient client, SilcClientConnection conn,
156                  SilcNotifyType type, ...)
157 {
158   va_list va;
159   SILC_SERVER_REC *server;
160   SILC_CHANNEL_REC *chanrec;
161   SILC_NICK_REC *nickrec;
162   SilcClientEntry client_entry, client_entry2;
163   SilcChannelEntry channel, channel2;
164   SilcServerEntry server_entry;
165   SilcIdType idtype;
166   void *entry;
167   SilcUInt32 mode;
168   char userhost[512];
169   char *name, *tmp;
170   GSList *list1, *list_tmp;
171
172   SILC_LOG_DEBUG(("Start"));
173
174   va_start(va, type);
175
176   server = conn == NULL ? NULL : conn->context;
177   
178   switch(type) {
179   case SILC_NOTIFY_TYPE_NONE:
180     /* Some generic notice from server */
181     printtext(server, NULL, MSGLEVEL_CRAP, "%s", (char *)va_arg(va, char *));
182     break;
183
184   case SILC_NOTIFY_TYPE_INVITE:
185     /*
186      * Invited or modified invite list.
187      */
188
189     SILC_LOG_DEBUG(("Notify: INVITE"));
190
191     channel = va_arg(va, SilcChannelEntry);
192     name = va_arg(va, char *);
193     client_entry = va_arg(va, SilcClientEntry);
194
195     memset(userhost, 0, sizeof(userhost));
196     snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
197              client_entry->username, client_entry->hostname);
198     signal_emit("message invite", 4, server, channel ? channel->channel_name :
199                 name, client_entry->nickname, userhost);
200     break;
201
202   case SILC_NOTIFY_TYPE_JOIN:
203     /*
204      * Joined channel.
205      */
206  
207     SILC_LOG_DEBUG(("Notify: JOIN"));
208
209     client_entry = va_arg(va, SilcClientEntry);
210     channel = va_arg(va, SilcChannelEntry);
211
212     if (client_entry == server->conn->local_entry) {
213       /* You joined to channel */
214       chanrec = silc_channel_find(server, channel->channel_name);
215       if (chanrec != NULL && !chanrec->joined)
216         chanrec->entry = channel;
217     } else {
218       chanrec = silc_channel_find_entry(server, channel);
219       if (chanrec != NULL) {
220         SilcChannelUser chu = silc_client_on_channel(channel, client_entry);
221         if (chu)
222           nickrec = silc_nicklist_insert(chanrec, chu, TRUE);
223       }
224     }
225     
226     memset(userhost, 0, sizeof(userhost));
227     if (client_entry->username)
228     snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
229              client_entry->username, client_entry->hostname);
230     signal_emit("message join", 4, server, channel->channel_name,
231                 client_entry->nickname,
232                 client_entry->username == NULL ? "" : userhost);
233     break;
234
235   case SILC_NOTIFY_TYPE_LEAVE:
236     /*
237      * Left a channel.
238      */
239
240     SILC_LOG_DEBUG(("Notify: LEAVE"));
241
242     client_entry = va_arg(va, SilcClientEntry);
243     channel = va_arg(va, SilcChannelEntry);
244     
245     memset(userhost, 0, sizeof(userhost));
246     if (client_entry->username)
247       snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
248                client_entry->username, client_entry->hostname);
249     signal_emit("message part", 5, server, channel->channel_name,
250                 client_entry->nickname,  client_entry->username ? 
251                 userhost : "", client_entry->nickname);
252     
253     chanrec = silc_channel_find_entry(server, channel);
254     if (chanrec != NULL) {
255       nickrec = silc_nicklist_find(chanrec, client_entry);
256       if (nickrec != NULL)
257         nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
258     }
259     break;
260
261   case SILC_NOTIFY_TYPE_SIGNOFF:
262     /*
263      * Left the network.
264      */
265
266     SILC_LOG_DEBUG(("Notify: SIGNOFF"));
267
268     client_entry = va_arg(va, SilcClientEntry);
269     tmp = va_arg(va, char *);
270     
271     silc_server_free_ftp(server, client_entry);
272     
273     memset(userhost, 0, sizeof(userhost));
274     if (client_entry->username)
275       snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
276                client_entry->username, client_entry->hostname);
277     signal_emit("message quit", 4, server, client_entry->nickname,
278                 client_entry->username ? userhost : "", 
279                 tmp ? tmp : "");
280     
281     list1 = nicklist_get_same_unique(SERVER(server), client_entry);
282     for (list_tmp = list1; list_tmp != NULL; list_tmp = 
283            list_tmp->next->next) {
284       CHANNEL_REC *channel = list_tmp->data;
285       NICK_REC *nickrec = list_tmp->next->data;
286       
287       nicklist_remove(channel, nickrec);
288     }
289     break;
290
291   case SILC_NOTIFY_TYPE_TOPIC_SET:
292     /*
293      * Changed topic.
294      */
295
296     SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
297
298     idtype = va_arg(va, int);
299     entry = va_arg(va, void *);
300     tmp = va_arg(va, char *);
301     channel = va_arg(va, SilcChannelEntry);
302     
303     chanrec = silc_channel_find_entry(server, channel);
304     if (chanrec != NULL) {
305       g_free_not_null(chanrec->topic);
306       chanrec->topic = *tmp == '\0' ? NULL : g_strdup(tmp);
307       signal_emit("channel topic changed", 1, chanrec);
308     }
309     
310     if (idtype == SILC_ID_CLIENT) {
311       client_entry = (SilcClientEntry)entry;
312       memset(userhost, 0, sizeof(userhost));
313       snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
314                client_entry->username, client_entry->hostname);
315       signal_emit("message topic", 5, server, channel->channel_name,
316                   tmp, client_entry->nickname, userhost);
317     } else if (idtype == SILC_ID_SERVER) {
318       server_entry = (SilcServerEntry)entry;
319       signal_emit("message topic", 5, server, channel->channel_name,
320                   tmp, server_entry->server_name, 
321                   server_entry->server_name);
322     } else {
323       channel = (SilcChannelEntry)entry;
324       signal_emit("message topic", 5, server, channel->channel_name,
325                   tmp, channel->channel_name, channel->channel_name);
326     }
327     break;
328
329   case SILC_NOTIFY_TYPE_NICK_CHANGE:
330     /*
331      * Changed nickname.
332      */
333
334     SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
335
336     client_entry = va_arg(va, SilcClientEntry);
337     client_entry2 = va_arg(va, SilcClientEntry);
338     
339     memset(userhost, 0, sizeof(userhost));
340     snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
341              client_entry2->username, client_entry2->hostname);
342     nicklist_rename_unique(SERVER(server),
343                            client_entry, client_entry->nickname,
344                            client_entry2, client_entry2->nickname);
345     signal_emit("message nick", 4, server, client_entry2->nickname, 
346                 client_entry->nickname, userhost);
347     break;
348
349   case SILC_NOTIFY_TYPE_CMODE_CHANGE:
350     /*
351      * Changed channel mode.
352      */
353
354     SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
355
356     idtype = va_arg(va, int);
357     entry = va_arg(va, void *);
358     mode = va_arg(va, SilcUInt32);
359     (void)va_arg(va, char *);
360     (void)va_arg(va, char *);
361     channel = va_arg(va, SilcChannelEntry);
362
363     tmp = silc_client_chmode(mode,
364                              channel->channel_key ? 
365                              channel->channel_key->cipher->name : "",
366                              channel->hmac ? 
367                              silc_hmac_get_name(channel->hmac) : "");
368     
369     chanrec = silc_channel_find_entry(server, channel);
370     if (chanrec != NULL) {
371       g_free_not_null(chanrec->mode);
372       chanrec->mode = g_strdup(tmp == NULL ? "" : tmp);
373       signal_emit("channel mode changed", 1, chanrec);
374     }
375     
376     if (idtype == SILC_ID_CLIENT) {
377       client_entry = (SilcClientEntry)entry;
378       printformat_module("fe-common/silc", server, channel->channel_name,
379                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
380                          channel->channel_name, tmp ? tmp : "removed all",
381                          client_entry->nickname);
382     } else if (idtype == SILC_ID_SERVER) {
383       server_entry = (SilcServerEntry)entry;
384       printformat_module("fe-common/silc", server, channel->channel_name,
385                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
386                          channel->channel_name, tmp ? tmp : "removed all",
387                          server_entry->server_name);
388     } else {
389       channel2 = (SilcChannelEntry)entry;
390       printformat_module("fe-common/silc", server, channel->channel_name,
391                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
392                          channel->channel_name, tmp ? tmp : "removed all",
393                          channel2->channel_name);
394     }
395
396     silc_free(tmp);
397     break;
398
399   case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
400     /*
401      * Changed user's mode on channel.
402      */
403
404     SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
405
406     idtype = va_arg(va, int);
407     entry = va_arg(va, void *);
408     mode = va_arg(va, SilcUInt32);
409     client_entry2 = va_arg(va, SilcClientEntry);
410     channel = va_arg(va, SilcChannelEntry);
411
412     tmp = silc_client_chumode(mode);
413     chanrec = silc_channel_find_entry(server, channel);
414     if (chanrec != NULL) {
415       SILC_NICK_REC *nick;
416
417       if (client_entry2 == server->conn->local_entry)
418         chanrec->chanop = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
419       
420       nick = silc_nicklist_find(chanrec, client_entry2);
421       if (nick != NULL) {
422         nick->op = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
423         nick->founder = (mode & SILC_CHANNEL_UMODE_CHANFO) != 0;
424         signal_emit("nick mode changed", 2, chanrec, nick);
425       }
426     }
427
428     if (idtype == SILC_ID_CLIENT) {
429       client_entry = (SilcClientEntry)entry;
430       printformat_module("fe-common/silc", server, channel->channel_name,
431                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
432                          channel->channel_name, client_entry2->nickname, 
433                          tmp ? tmp : "removed all",
434                          client_entry->nickname);
435     } else if (idtype == SILC_ID_SERVER) {
436       server_entry = (SilcServerEntry)entry;
437       printformat_module("fe-common/silc", server, channel->channel_name,
438                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
439                          channel->channel_name, client_entry2->nickname, 
440                          tmp ? tmp : "removed all",
441                          server_entry->server_name);
442     } else {
443       channel2 = (SilcChannelEntry)entry;
444       printformat_module("fe-common/silc", server, channel->channel_name,
445                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
446                          channel->channel_name, client_entry2->nickname, 
447                          tmp ? tmp : "removed all",
448                          channel2->channel_name);
449     }
450
451     if (mode & SILC_CHANNEL_UMODE_CHANFO)
452       printformat_module("fe-common/silc", 
453                          server, channel->channel_name, MSGLEVEL_CRAP,
454                          SILCTXT_CHANNEL_FOUNDER,
455                          channel->channel_name, client_entry2->nickname);
456
457     silc_free(tmp);
458     break;
459
460   case SILC_NOTIFY_TYPE_MOTD:
461     /*
462      * Received MOTD.
463      */
464
465     SILC_LOG_DEBUG(("Notify: MOTD"));
466
467     tmp = va_arg(va, char *);
468
469     if (!settings_get_bool("skip_motd"))
470       printtext_multiline(server, NULL, MSGLEVEL_CRAP, "%s", tmp);
471     break;
472
473   case SILC_NOTIFY_TYPE_KICKED:
474     /*
475      * Someone was kicked from channel.
476      */
477
478     SILC_LOG_DEBUG(("Notify: KICKED"));
479
480     client_entry = va_arg(va, SilcClientEntry);
481     tmp = va_arg(va, char *);
482     client_entry2 = va_arg(va, SilcClientEntry);
483     channel = va_arg(va, SilcChannelEntry);
484
485     chanrec = silc_channel_find_entry(server, channel);
486   
487     if (client_entry == conn->local_entry) {
488       printformat_module("fe-common/silc", server, channel->channel_name,
489                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED_YOU, 
490                          channel->channel_name, 
491                          client_entry ? client_entry2->nickname : "",
492                          tmp ? tmp : "");
493       if (chanrec) {
494         chanrec->kicked = TRUE;
495         channel_destroy((CHANNEL_REC *)chanrec);
496       }
497     } else {
498       printformat_module("fe-common/silc", server, channel->channel_name,
499                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED, 
500                          client_entry->nickname, channel->channel_name, 
501                          client_entry2 ? client_entry2->nickname : "", 
502                          tmp ? tmp : "");
503
504       if (chanrec) {
505         SILC_NICK_REC *nickrec = silc_nicklist_find(chanrec, client_entry);
506         if (nickrec != NULL)
507           nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
508       }
509     }
510     break;
511
512   case SILC_NOTIFY_TYPE_KILLED:
513     /*
514      * Someone was killed from the network.
515      */
516
517     SILC_LOG_DEBUG(("Notify: KILLED"));
518
519     client_entry = va_arg(va, SilcClientEntry);
520     tmp = va_arg(va, char *);
521     client_entry2 = va_arg(va, SilcClientEntry);
522   
523     if (client_entry == conn->local_entry) {
524       printformat_module("fe-common/silc", server, NULL,
525                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU, 
526                          client_entry2 ? client_entry2->nickname : "",
527                          tmp ? tmp : "");
528     } else {
529       list1 = nicklist_get_same_unique(SERVER(server), client_entry);
530       for (list_tmp = list1; list_tmp != NULL; list_tmp = 
531              list_tmp->next->next) {
532         CHANNEL_REC *channel = list_tmp->data;
533         NICK_REC *nickrec = list_tmp->next->data;
534         nicklist_remove(channel, nickrec);
535       }
536
537       printformat_module("fe-common/silc", server, NULL,
538                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED, 
539                          client_entry->nickname,
540                          client_entry2 ? client_entry2->nickname : "",
541                          tmp ? tmp : "");
542     }
543     break;
544
545   case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
546     break;
547
548   case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
549     {
550       /*
551        * Server has quit the network.
552        */
553       int i;
554       SilcClientEntry *clients;
555       SilcUInt32 clients_count;
556
557       SILC_LOG_DEBUG(("Notify: SIGNOFF"));
558       
559       (void)va_arg(va, void *);
560       clients = va_arg(va, SilcClientEntry *);
561       clients_count = va_arg(va, SilcUInt32);
562   
563       for (i = 0; i < clients_count; i++) {
564         memset(userhost, 0, sizeof(userhost));
565         if (clients[i]->username)
566           snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
567                    clients[i]->username, clients[i]->hostname);
568         signal_emit("message quit", 4, server, clients[i]->nickname,
569                     clients[i]->username ? userhost : "", 
570                     "server signoff");
571
572         silc_server_free_ftp(server, clients[i]);
573         
574         list1 = nicklist_get_same_unique(SERVER(server), clients[i]);
575         for (list_tmp = list1; list_tmp != NULL; list_tmp = 
576                list_tmp->next->next) {
577           CHANNEL_REC *channel = list_tmp->data;
578           NICK_REC *nickrec = list_tmp->next->data;
579           nicklist_remove(channel, nickrec);
580         }
581       }
582     }
583     break;
584
585   default:
586     /* Unknown notify */
587     printformat_module("fe-common/silc", server, NULL,
588                        MSGLEVEL_CRAP, SILCTXT_UNKNOWN_NOTIFY, type);
589     break;
590   }
591
592   va_end(va);
593 }
594
595 /* Called to indicate that connection was either successfully established
596    or connecting failed.  This is also the first time application receives
597    the SilcClientConnection object which it should save somewhere. */
598
599 void silc_connect(SilcClient client, SilcClientConnection conn, int success)
600 {
601   SILC_SERVER_REC *server = conn->context;
602
603   if (!server && !success) {
604     silc_client_close_connection(client, conn);
605     return;
606   }
607
608   if (success) {
609     server->connected = TRUE;
610     signal_emit("event connected", 1, server);
611   } else {
612     server->connection_lost = TRUE;
613     if (server->conn)
614       server->conn->context = NULL;
615     server_disconnect(SERVER(server));
616   }
617 }
618
619 /* Called to indicate that connection was disconnected to the server. */
620
621 void silc_disconnect(SilcClient client, SilcClientConnection conn)
622 {
623   SILC_SERVER_REC *server = conn->context;
624
625   SILC_LOG_DEBUG(("Start"));
626
627   if (!server || server->connection_lost)
628     return;
629
630   if (server->conn && server->conn->local_entry) {
631     nicklist_rename_unique(SERVER(server),
632                            server->conn->local_entry, server->nick,
633                            server->conn->local_entry, 
634                            silc_client->username);
635     silc_change_nick(server, silc_client->username);
636   }
637
638   server->conn->context = NULL;
639   server->conn = NULL;
640   server->connection_lost = TRUE;
641   server_disconnect(SERVER(server));
642 }
643
644 /* Command handler. This function is called always in the command function.
645    If error occurs it will be called as well. `conn' is the associated
646    client connection. `cmd_context' is the command context that was
647    originally sent to the command. `success' is FALSE if error occured
648    during command. `command' is the command being processed. It must be
649    noted that this is not reply from server. This is merely called just
650    after application has called the command. Just to tell application
651    that the command really was processed. */
652
653 void silc_command(SilcClient client, SilcClientConnection conn, 
654                   SilcClientCommandContext cmd_context, int success,
655                   SilcCommand command)
656 {
657   SILC_SERVER_REC *server = conn->context;
658
659   SILC_LOG_DEBUG(("Start"));
660
661   if (!success)
662     return;
663
664   switch(command) {
665   case SILC_COMMAND_INVITE:
666     printformat_module("fe-common/silc", server, NULL,
667                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING,
668                        cmd_context->argv[2], 
669                        (cmd_context->argv[1][0] == '*' ?
670                         (char *)conn->current_channel->channel_name :
671                         (char *)cmd_context->argv[1]));
672     break;
673   default:
674     break;
675   }
676 }
677
678 /* Client info resolving callback when JOIN command reply is received.
679    This will cache all users on the channel. */
680
681 static void silc_client_join_get_users(SilcClient client,
682                                        SilcClientConnection conn,
683                                        SilcClientEntry *clients,
684                                        SilcUInt32 clients_count,
685                                        void *context)
686 {
687   SilcChannelEntry channel = (SilcChannelEntry)context;
688   SilcHashTableList htl;
689   SilcChannelUser chu;
690   SILC_SERVER_REC *server = conn->context;
691   SILC_CHANNEL_REC *chanrec;
692   SilcClientEntry founder = NULL;
693   NICK_REC *ownnick;
694
695   if (!clients)
696     return;
697
698   chanrec = silc_channel_find(server, channel->channel_name);
699   if (chanrec == NULL)
700     return;
701
702   silc_hash_table_list(channel->user_list, &htl);
703   while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
704     if (!chu->client->nickname)
705       continue;
706     if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
707       founder = chu->client;
708     silc_nicklist_insert(chanrec, chu, FALSE);
709   }
710   silc_hash_table_list_reset(&htl);
711
712   ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
713   nicklist_set_own(CHANNEL(chanrec), ownnick);
714   signal_emit("channel joined", 1, chanrec);
715
716   if (chanrec->topic)
717     printformat_module("fe-common/silc", server, channel->channel_name,
718                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
719                        channel->channel_name, chanrec->topic);
720
721   fe_channels_nicklist(CHANNEL(chanrec), CHANNEL_NICKLIST_FLAG_ALL);
722
723   if (founder) {
724     if (founder == conn->local_entry)
725       printformat_module("fe-common/silc", 
726                          server, channel->channel_name, MSGLEVEL_CRAP,
727                          SILCTXT_CHANNEL_FOUNDER_YOU,
728                          channel->channel_name);
729     else
730       printformat_module("fe-common/silc", 
731                          server, channel->channel_name, MSGLEVEL_CRAP,
732                          SILCTXT_CHANNEL_FOUNDER,
733                          channel->channel_name, founder->nickname);
734   }
735 }
736
737 typedef struct {
738   SilcClient client;
739   SilcClientConnection conn;
740   void *entry;
741   SilcIdType id_type;
742   char *fingerprint;
743 } *GetkeyContext;
744
745 void silc_getkey_cb(bool success, void *context)
746 {
747   GetkeyContext getkey = (GetkeyContext)context;
748   char *entity = (getkey->id_type == SILC_ID_CLIENT ? "user" : "server");
749   char *name = (getkey->id_type == SILC_ID_CLIENT ? 
750                 ((SilcClientEntry)getkey->entry)->nickname :
751                 ((SilcServerEntry)getkey->entry)->server_name);
752
753   if (success) {
754     printformat_module("fe-common/silc", NULL, NULL,
755                        MSGLEVEL_CRAP, SILCTXT_GETKEY_VERIFIED, entity, name);
756   } else {
757     printformat_module("fe-common/silc", NULL, NULL,
758                        MSGLEVEL_CRAP, SILCTXT_GETKEY_DISCARD, entity, name);
759   }
760
761   silc_free(getkey->fingerprint);
762   silc_free(getkey);
763 }
764
765 /* Command reply handler. This function is called always in the command reply
766    function. If error occurs it will be called as well. Normal scenario
767    is that it will be called after the received command data has been parsed
768    and processed. The function is used to pass the received command data to
769    the application. 
770
771    `conn' is the associated client connection. `cmd_payload' is the command
772    payload data received from server and it can be ignored. It is provided
773    if the application would like to re-parse the received command data,
774    however, it must be noted that the data is parsed already by the library
775    thus the payload can be ignored. `success' is FALSE if error occured.
776    In this case arguments are not sent to the application. `command' is the
777    command reply being processed. The function has variable argument list
778    and each command defines the number and type of arguments it passes to the
779    application (on error they are not sent). */
780
781 void 
782 silc_command_reply(SilcClient client, SilcClientConnection conn,
783                    SilcCommandPayload cmd_payload, int success,
784                    SilcCommand command, SilcCommandStatus status, ...)
785
786 {
787   SILC_SERVER_REC *server = conn->context;
788   SILC_CHANNEL_REC *chanrec;
789   va_list vp;
790
791   va_start(vp, status);
792
793   SILC_LOG_DEBUG(("Start"));
794
795   switch(command) {
796   case SILC_COMMAND_WHOIS:
797     {
798       char buf[1024], *nickname, *username, *realname, *nick;
799       unsigned char *fingerprint;
800       SilcUInt32 idle, mode;
801       SilcBuffer channels;
802       SilcClientEntry client_entry;
803       
804       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
805         /* Print the unknown nick for user */
806         unsigned char *tmp =
807           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
808                                      3, NULL);
809         if (tmp)
810           silc_say_error("%s: %s", tmp, 
811                          silc_client_command_status_message(status));
812         break;
813       } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
814         /* Try to find the entry for the unknown client ID, since we
815            might have, and print the nickname of it for user. */
816         SilcUInt32 tmp_len;
817         unsigned char *tmp =
818           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
819                                      2, &tmp_len);
820         if (tmp) {
821           SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, 
822                                                              NULL);
823           if (client_id) {
824             client_entry = silc_client_get_client_by_id(client, conn,
825                                                         client_id);
826             if (client_entry && client_entry->nickname)
827               silc_say_error("%s: %s", client_entry->nickname,
828                              silc_client_command_status_message(status));
829             silc_free(client_id);
830           }
831         }
832         break;
833       }
834       
835       if (!success)
836         return;
837       
838       client_entry = va_arg(vp, SilcClientEntry);
839       nickname = va_arg(vp, char *);
840       username = va_arg(vp, char *);
841       realname = va_arg(vp, char *);
842       channels = va_arg(vp, SilcBuffer);
843       mode = va_arg(vp, SilcUInt32);
844       idle = va_arg(vp, SilcUInt32);
845       fingerprint = va_arg(vp, unsigned char *);
846       
847       silc_parse_userfqdn(nickname, &nick, NULL);
848       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
849                          SILCTXT_WHOIS_USERINFO, nickname, 
850                          client_entry->username, client_entry->hostname,
851                          nick, client_entry->nickname);
852       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
853                          SILCTXT_WHOIS_REALNAME, realname);
854       silc_free(nick);
855
856       if (channels) {
857         SilcDList list = silc_channel_payload_parse_list(channels->data,
858                                                          channels->len);
859         if (list) {
860           SilcChannelPayload entry;
861           memset(buf, 0, sizeof(buf));
862           silc_dlist_start(list);
863           while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
864             char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
865             SilcUInt32 name_len;
866             char *name = silc_channel_get_name(entry, &name_len);
867             
868             if (m)
869               strncat(buf, m, strlen(m));
870             strncat(buf, name, name_len);
871             strncat(buf, " ", 1);
872             silc_free(m);
873           }
874
875           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
876                              SILCTXT_WHOIS_CHANNELS, buf);
877           silc_channel_payload_list_free(list);
878         }
879       }
880       
881       if (mode) {
882         memset(buf, 0, sizeof(buf));
883
884         if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
885             (mode & SILC_UMODE_ROUTER_OPERATOR)) {
886           strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
887                  "Server Operator" :
888                  (mode & SILC_UMODE_ROUTER_OPERATOR) ?
889                  "SILC Operator" : "[Unknown mode]");
890         }
891         if (mode & SILC_UMODE_GONE)
892           strcat(buf, " away");
893         if (mode & SILC_UMODE_INDISPOSED)
894           strcat(buf, " indisposed");
895         if (mode & SILC_UMODE_BUSY)
896           strcat(buf, " busy");
897         if (mode & SILC_UMODE_PAGE)
898           strcat(buf, " page to reach");
899         if (mode & SILC_UMODE_HYPER)
900           strcat(buf, " hyper active");
901         if (mode & SILC_UMODE_ROBOT)
902           strcat(buf, " robot");
903
904         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
905                            SILCTXT_WHOIS_MODES, buf);
906       }
907       
908       if (idle && nickname) {
909         memset(buf, 0, sizeof(buf));
910         snprintf(buf, sizeof(buf) - 1, "%lu %s",
911                  idle > 60 ? (idle / 60) : idle,
912                  idle > 60 ? "minutes" : "seconds");
913
914         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
915                            SILCTXT_WHOIS_IDLE, buf);
916       }
917
918       if (fingerprint) {
919         fingerprint = silc_fingerprint(fingerprint, 20);
920         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
921                            SILCTXT_WHOIS_FINGERPRINT, fingerprint);
922         silc_free(fingerprint);
923       }
924     }
925     break;
926     
927   case SILC_COMMAND_IDENTIFY:
928     {
929       SilcClientEntry client_entry;
930       
931       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
932         /* Print the unknown nick for user */
933         unsigned char *tmp =
934           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
935                                      3, NULL);
936         if (tmp)
937           silc_say_error("%s: %s", tmp, 
938                          silc_client_command_status_message(status));
939         break;
940       } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
941         /* Try to find the entry for the unknown client ID, since we
942            might have, and print the nickname of it for user. */
943         SilcUInt32 tmp_len;
944         unsigned char *tmp =
945           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
946                                      2, &tmp_len);
947         if (tmp) {
948           SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len,
949                                                              NULL);
950           if (client_id) {
951             client_entry = silc_client_get_client_by_id(client, conn,
952                                                         client_id);
953             if (client_entry && client_entry->nickname)
954               silc_say_error("%s: %s", client_entry->nickname,
955                              silc_client_command_status_message(status));
956             silc_free(client_id);
957           }
958         }
959         break;
960       }
961
962       break;
963     }
964
965   case SILC_COMMAND_WHOWAS:
966     {
967       char *nickname, *username, *realname;
968       
969       if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
970           status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
971         char *tmp;
972         tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
973                                          3, NULL);
974         if (tmp)
975           silc_say_error("%s: %s", tmp, 
976                          silc_client_command_status_message(status));
977         break;
978       }
979       
980       if (!success)
981         return;
982       
983       (void)va_arg(vp, SilcClientEntry);
984       nickname = va_arg(vp, char *);
985       username = va_arg(vp, char *);
986       realname = va_arg(vp, char *);
987       
988       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
989                          SILCTXT_WHOWAS_USERINFO, nickname, username, 
990                          realname ? realname : "");
991     }
992     break;
993     
994   case SILC_COMMAND_INVITE:
995     {
996       SilcChannelEntry channel;
997       char *invite_list;
998       SilcArgumentPayload args;
999       int argc = 0;
1000       
1001       if (!success)
1002         return;
1003       
1004       channel = va_arg(vp, SilcChannelEntry);
1005       invite_list = va_arg(vp, char *);
1006
1007       args = silc_command_get_args(cmd_payload);
1008       if (args)
1009         argc = silc_argument_get_arg_num(args);
1010
1011       if (invite_list)
1012         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1013                            SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
1014                            invite_list);
1015       else if (argc == 3)
1016         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1017                            SILCTXT_CHANNEL_NO_INVITE_LIST, 
1018                            channel->channel_name);
1019     }
1020     break;
1021
1022   case SILC_COMMAND_JOIN: 
1023     {
1024       char *channel, *mode, *topic;
1025       SilcUInt32 modei;
1026       SilcChannelEntry channel_entry;
1027       SilcBuffer client_id_list;
1028       SilcUInt32 list_count;
1029
1030       if (!success)
1031         return;
1032
1033       channel = va_arg(vp, char *);
1034       channel_entry = va_arg(vp, SilcChannelEntry);
1035       modei = va_arg(vp, SilcUInt32);
1036       (void)va_arg(vp, SilcUInt32);
1037       (void)va_arg(vp, unsigned char *);
1038       (void)va_arg(vp, unsigned char *);
1039       (void)va_arg(vp, unsigned char *);
1040       topic = va_arg(vp, char *);
1041       (void)va_arg(vp, unsigned char *);
1042       list_count = va_arg(vp, SilcUInt32);
1043       client_id_list = va_arg(vp, SilcBuffer);
1044
1045       chanrec = silc_channel_find(server, channel);
1046       if (!chanrec)
1047         chanrec = silc_channel_create(server, channel, TRUE);
1048
1049       if (topic) {
1050         g_free_not_null(chanrec->topic);
1051         chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1052         signal_emit("channel topic changed", 1, chanrec);
1053       }
1054
1055       mode = silc_client_chmode(modei, 
1056                                 channel_entry->channel_key ? 
1057                                 channel_entry->channel_key->cipher->name : "",
1058                                 channel_entry->hmac ? 
1059                                 silc_hmac_get_name(channel_entry->hmac) : "");
1060       g_free_not_null(chanrec->mode);
1061       chanrec->mode = g_strdup(mode == NULL ? "" : mode);
1062       signal_emit("channel mode changed", 1, chanrec);
1063
1064       /* Resolve the client information */
1065       silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
1066                                       silc_client_join_get_users, 
1067                                       channel_entry);
1068
1069       break;
1070     }
1071
1072   case SILC_COMMAND_NICK: 
1073     {
1074       SilcClientEntry client = va_arg(vp, SilcClientEntry);
1075       char *old;
1076       
1077       if (!success)
1078         return;
1079
1080       old = g_strdup(server->nick);
1081       server_change_nick(SERVER(server), client->nickname);
1082       nicklist_rename_unique(SERVER(server),
1083                              server->conn->local_entry, server->nick,
1084                              client, client->nickname);
1085       signal_emit("message own_nick", 4, server, server->nick, old, "");
1086       g_free(old);
1087       break;
1088     }
1089     
1090   case SILC_COMMAND_LIST:
1091     {
1092       char *topic, *name;
1093       int usercount;
1094       char users[20];
1095       
1096       if (!success)
1097         return;
1098       
1099       (void)va_arg(vp, SilcChannelEntry);
1100       name = va_arg(vp, char *);
1101       topic = va_arg(vp, char *);
1102       usercount = va_arg(vp, int);
1103       
1104       if (status == SILC_STATUS_LIST_START ||
1105           status == SILC_STATUS_OK)
1106         printformat_module("fe-common/silc", server, NULL,
1107                            MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
1108
1109       if (!usercount)
1110         snprintf(users, sizeof(users) - 1, "N/A");
1111       else
1112         snprintf(users, sizeof(users) - 1, "%d", usercount);
1113       printformat_module("fe-common/silc", server, NULL,
1114                          MSGLEVEL_CRAP, SILCTXT_LIST,
1115                          name, users, topic ? topic : "");
1116     }
1117     break;
1118     
1119   case SILC_COMMAND_UMODE:
1120     {
1121       SilcUInt32 mode;
1122       
1123       if (!success)
1124         return;
1125       
1126       mode = va_arg(vp, SilcUInt32);
1127       
1128       if (mode & SILC_UMODE_SERVER_OPERATOR &&
1129           !(server->umode & SILC_UMODE_SERVER_OPERATOR))
1130         printformat_module("fe-common/silc", server, NULL,
1131                            MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1132
1133       if (mode & SILC_UMODE_ROUTER_OPERATOR &&
1134           !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
1135         printformat_module("fe-common/silc", server, NULL,
1136                            MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1137
1138       server->umode = mode;
1139       signal_emit("user mode changed", 2, server, NULL);
1140     }
1141     break;
1142     
1143   case SILC_COMMAND_OPER:
1144     if (!success)
1145       return;
1146
1147     server->umode |= SILC_UMODE_SERVER_OPERATOR;
1148     signal_emit("user mode changed", 2, server, NULL);
1149
1150     printformat_module("fe-common/silc", server, NULL,
1151                        MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1152     break;
1153     
1154   case SILC_COMMAND_SILCOPER:
1155     if (!success)
1156       return;
1157
1158     server->umode |= SILC_UMODE_ROUTER_OPERATOR;
1159     signal_emit("user mode changed", 2, server, NULL);
1160
1161     printformat_module("fe-common/silc", server, NULL,
1162                        MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1163     break;
1164     
1165   case SILC_COMMAND_USERS: 
1166     {
1167       SilcHashTableList htl;
1168       SilcChannelEntry channel;
1169       SilcChannelUser chu;
1170       
1171       if (!success)
1172         return;
1173       
1174       channel = va_arg(vp, SilcChannelEntry);
1175       
1176       printformat_module("fe-common/silc", server, channel->channel_name,
1177                          MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
1178                          channel->channel_name);
1179
1180       silc_hash_table_list(channel->user_list, &htl);
1181       while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1182         SilcClientEntry e = chu->client;
1183         char stat[5], *mode;
1184
1185         if (!e->nickname)
1186           continue;
1187         
1188         memset(stat, 0, sizeof(stat));
1189         mode = silc_client_chumode_char(chu->mode);
1190         if (e->mode & SILC_UMODE_GONE)
1191           strcat(stat, "G");
1192         else if (e->mode & SILC_UMODE_INDISPOSED)
1193           strcat(stat, "I");
1194         else if (e->mode & SILC_UMODE_BUSY)
1195           strcat(stat, "B");
1196         else if (e->mode & SILC_UMODE_PAGE)
1197           strcat(stat, "P");
1198         else if (e->mode & SILC_UMODE_HYPER)
1199           strcat(stat, "H");
1200         else if (e->mode & SILC_UMODE_ROBOT)
1201           strcat(stat, "R");
1202         else
1203           strcat(stat, "A");
1204         if (mode)
1205           strcat(stat, mode);
1206
1207         printformat_module("fe-common/silc", server, channel->channel_name,
1208                            MSGLEVEL_CRAP, SILCTXT_USERS,
1209                            e->nickname, stat, 
1210                            e->username ? e->username : "",
1211                            e->hostname ? e->hostname : "",
1212                            e->realname ? e->realname : "");
1213         if (mode)
1214           silc_free(mode);
1215       }
1216       silc_hash_table_list_reset(&htl);
1217     }
1218     break;
1219
1220   case SILC_COMMAND_BAN:
1221     {
1222       SilcChannelEntry channel;
1223       char *ban_list;
1224       
1225       if (!success)
1226         return;
1227       
1228       channel = va_arg(vp, SilcChannelEntry);
1229       ban_list = va_arg(vp, char *);
1230       
1231       if (ban_list)
1232         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1233                            SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
1234                            ban_list);
1235       else
1236         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1237                            SILCTXT_CHANNEL_NO_BAN_LIST, 
1238                            channel->channel_name);
1239     }
1240     break;
1241     
1242   case SILC_COMMAND_GETKEY:
1243     {
1244       SilcIdType id_type;
1245       void *entry;
1246       SilcPublicKey public_key;
1247       unsigned char *pk;
1248       SilcUInt32 pk_len;
1249       GetkeyContext getkey;
1250       char *name;
1251       
1252       if (!success)
1253         return;
1254       
1255       id_type = va_arg(vp, SilcUInt32);
1256       entry = va_arg(vp, void *);
1257       public_key = va_arg(vp, SilcPublicKey);
1258
1259       if (public_key) {
1260         pk = silc_pkcs_public_key_encode(public_key, &pk_len);
1261
1262         getkey = silc_calloc(1, sizeof(*getkey));
1263         getkey->entry = entry;
1264         getkey->id_type = id_type;
1265         getkey->client = client;
1266         getkey->conn = conn;
1267         getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1268
1269         name = (id_type == SILC_ID_CLIENT ? 
1270                 ((SilcClientEntry)entry)->nickname :
1271                 ((SilcServerEntry)entry)->server_name);
1272
1273         silc_verify_public_key_internal(client, conn, name,
1274                                         (id_type == SILC_ID_CLIENT ?
1275                                          SILC_SOCKET_TYPE_CLIENT :
1276                                          SILC_SOCKET_TYPE_SERVER),
1277                                         pk, pk_len, SILC_SKE_PK_TYPE_SILC,
1278                                         silc_getkey_cb, getkey);
1279         silc_free(pk);
1280       } else {
1281         printformat_module("fe-common/silc", server, NULL,
1282                            MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
1283       }
1284     }
1285     break;
1286
1287   case SILC_COMMAND_INFO:
1288     {
1289       SilcServerEntry server_entry;
1290       char *server_name;
1291       char *server_info;
1292
1293       if (!success)
1294         return;
1295       
1296       server_entry = va_arg(vp, SilcServerEntry);
1297       server_name = va_arg(vp, char *);
1298       server_info = va_arg(vp, char *);
1299
1300       if (server_name && server_info )
1301         {
1302           printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
1303           printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
1304         }
1305     }
1306     break;
1307     
1308   case SILC_COMMAND_TOPIC:
1309     {
1310       SilcChannelEntry channel;
1311       char *topic;
1312       
1313       if (!success)
1314         return;
1315       
1316       channel = va_arg(vp, SilcChannelEntry);
1317       topic = va_arg(vp, char *);
1318       
1319       if (topic) {
1320         chanrec = silc_channel_find_entry(server, channel);
1321         if (chanrec) {
1322           g_free_not_null(chanrec->topic);
1323           chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1324           signal_emit("channel topic changed", 1, chanrec);
1325         }
1326         printformat_module("fe-common/silc", server, channel->channel_name,
1327                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
1328                            channel->channel_name, topic);
1329       } else {
1330         printformat_module("fe-common/silc", server, channel->channel_name,
1331                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
1332                            channel->channel_name);
1333       }
1334     }
1335     break;
1336
1337   }
1338
1339   va_end(vp);
1340 }
1341
1342 typedef struct {
1343   SilcClient client;
1344   SilcClientConnection conn;
1345   char *filename;
1346   char *entity;
1347   char *entity_name;
1348   unsigned char *pk;
1349   SilcUInt32 pk_len;
1350   SilcSKEPKType pk_type;
1351   SilcVerifyPublicKey completion;
1352   void *context;
1353 } *PublicKeyVerify;
1354
1355 static void verify_public_key_completion(const char *line, void *context)
1356 {
1357   PublicKeyVerify verify = (PublicKeyVerify)context;
1358
1359   if (line[0] == 'Y' || line[0] == 'y') {
1360     /* Call the completion */
1361     if (verify->completion)
1362       verify->completion(TRUE, verify->context);
1363
1364     /* Save the key for future checking */
1365     silc_pkcs_save_public_key_data(verify->filename, verify->pk, 
1366                                    verify->pk_len, SILC_PKCS_FILE_PEM);
1367   } else {
1368     /* Call the completion */
1369     if (verify->completion)
1370       verify->completion(FALSE, verify->context);
1371
1372     printformat_module("fe-common/silc", NULL, NULL,
1373                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, 
1374                        verify->entity_name ? verify->entity_name :
1375                        verify->entity);
1376   }
1377
1378   silc_free(verify->filename);
1379   silc_free(verify->entity);
1380   silc_free(verify->entity_name);
1381   silc_free(verify->pk);
1382   silc_free(verify);
1383 }
1384
1385 /* Internal routine to verify public key. If the `completion' is provided
1386    it will be called to indicate whether public was verified or not. For
1387    server/router public key this will check for filename that includes the
1388    remote host's IP address and remote host's hostname. */
1389
1390 static void 
1391 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
1392                                 const char *name, SilcSocketType conn_type, 
1393                                 unsigned char *pk, SilcUInt32 pk_len, 
1394                                 SilcSKEPKType pk_type,
1395                                 SilcVerifyPublicKey completion, void *context)
1396 {
1397   int i;
1398   char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
1399   char *fingerprint, *babbleprint, *format;
1400   struct passwd *pw;
1401   struct stat st;
1402   char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
1403                    conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
1404                   "server" : "client");
1405   PublicKeyVerify verify;
1406
1407   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
1408     printformat_module("fe-common/silc", NULL, NULL,
1409                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED, 
1410                        entity, pk_type);
1411     if (completion)
1412       completion(FALSE, context);
1413     return;
1414   }
1415
1416   pw = getpwuid(getuid());
1417   if (!pw) {
1418     if (completion)
1419       completion(FALSE, context);
1420     return;
1421   }
1422
1423   memset(filename, 0, sizeof(filename));
1424   memset(filename2, 0, sizeof(filename2));
1425   memset(file, 0, sizeof(file));
1426
1427   if (conn_type == SILC_SOCKET_TYPE_SERVER ||
1428       conn_type == SILC_SOCKET_TYPE_ROUTER) {
1429     if (!name) {
1430       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1431                conn->sock->ip, conn->sock->port);
1432       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
1433                get_irssi_dir(), entity, file);
1434       
1435       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1436                conn->sock->hostname, conn->sock->port);
1437       snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s", 
1438                get_irssi_dir(), entity, file);
1439       
1440       ipf = filename;
1441       hostf = filename2;
1442     } else {
1443       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1444                name, conn->sock->port);
1445       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
1446                get_irssi_dir(), entity, file);
1447       
1448       ipf = filename;
1449     }
1450   } else {
1451     /* Replace all whitespaces with `_'. */
1452     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1453     for (i = 0; i < strlen(fingerprint); i++)
1454       if (fingerprint[i] == ' ')
1455         fingerprint[i] = '_';
1456     
1457     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
1458     snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
1459              get_irssi_dir(), entity, file);
1460     silc_free(fingerprint);
1461
1462     ipf = filename;
1463   }
1464
1465   /* Take fingerprint of the public key */
1466   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1467   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
1468
1469   verify = silc_calloc(1, sizeof(*verify));
1470   verify->client = client;
1471   verify->conn = conn;
1472   verify->filename = strdup(ipf);
1473   verify->entity = strdup(entity);
1474   verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ?
1475                          (name ? strdup(name) : strdup(conn->sock->hostname))
1476                          : NULL);
1477   verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
1478   memcpy(verify->pk, pk, pk_len);
1479   verify->pk_len = pk_len;
1480   verify->pk_type = pk_type;
1481   verify->completion = completion;
1482   verify->context = context;
1483
1484   /* Check whether this key already exists */
1485   if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
1486     /* Key does not exist, ask user to verify the key and save it */
1487
1488     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1489                        SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1490                        verify->entity_name : entity);
1491     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1492                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1493     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1494                        SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1495     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1496                              SILCTXT_PUBKEY_ACCEPT);
1497     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1498                             format, 0, verify);
1499     g_free(format);
1500     silc_free(fingerprint);
1501     return;
1502   } else {
1503     /* The key already exists, verify it. */
1504     SilcPublicKey public_key;
1505     unsigned char *encpk;
1506     SilcUInt32 encpk_len;
1507
1508     /* Load the key file, try for both IP filename and hostname filename */
1509     if (!silc_pkcs_load_public_key(ipf, &public_key, 
1510                                    SILC_PKCS_FILE_PEM) &&
1511         !silc_pkcs_load_public_key(ipf, &public_key, 
1512                                    SILC_PKCS_FILE_BIN) &&
1513         (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key, 
1514                                                SILC_PKCS_FILE_PEM) &&
1515                     !silc_pkcs_load_public_key(hostf, &public_key, 
1516                                                SILC_PKCS_FILE_BIN)))) {
1517       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1518                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1519                          verify->entity_name : entity);
1520       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1521                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1522       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1523                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1524       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1525                          SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
1526       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1527                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1528       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1529                               format, 0, verify);
1530       g_free(format);
1531       silc_free(fingerprint);
1532       return;
1533     }
1534
1535     /* Encode the key data */
1536     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
1537     if (!encpk) {
1538       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1539                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1540                          verify->entity_name : entity);
1541       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1542                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1543       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1544                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1545       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1546                          SILCTXT_PUBKEY_MALFORMED, entity);
1547       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1548                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1549       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1550                               format, 0, verify);
1551       g_free(format);
1552       silc_free(fingerprint);
1553       return;
1554     }
1555
1556     /* Compare the keys */
1557     if (memcmp(encpk, pk, encpk_len)) {
1558       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1559                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1560                          verify->entity_name : entity);
1561       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1562                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1563       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1564                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1565       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1566                          SILCTXT_PUBKEY_NO_MATCH, entity);
1567       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1568                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
1569       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1570                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
1571
1572       /* Ask user to verify the key and save it */
1573       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1574                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1575       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1576                               format, 0, verify);
1577       g_free(format);
1578       silc_free(fingerprint);
1579       return;
1580     }
1581
1582     /* Local copy matched */
1583     if (completion)
1584       completion(TRUE, context);
1585     silc_free(fingerprint);
1586   }
1587 }
1588
1589 /* Verifies received public key. The `conn_type' indicates which entity
1590    (server, client etc.) has sent the public key. If user decides to trust
1591    the key may be saved as trusted public key for later use. The 
1592    `completion' must be called after the public key has been verified. */
1593
1594 void 
1595 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
1596                        SilcSocketType conn_type, unsigned char *pk, 
1597                        SilcUInt32 pk_len, SilcSKEPKType pk_type,
1598                        SilcVerifyPublicKey completion, void *context)
1599 {
1600   silc_verify_public_key_internal(client, conn, NULL, conn_type, pk,
1601                                   pk_len, pk_type,
1602                                   completion, context);
1603 }
1604
1605 /* Asks passphrase from user on the input line. */
1606
1607 typedef struct {
1608   SilcAskPassphrase completion;
1609   void *context;
1610 } *AskPassphrase;
1611
1612 void ask_passphrase_completion(const char *passphrase, void *context)
1613 {
1614   AskPassphrase p = (AskPassphrase)context;
1615   p->completion((unsigned char *)passphrase, 
1616                 passphrase ? strlen(passphrase) : 0, p->context);
1617   silc_free(p);
1618 }
1619
1620 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1621                          SilcAskPassphrase completion, void *context)
1622 {
1623   AskPassphrase p = silc_calloc(1, sizeof(*p));
1624   p->completion = completion;
1625   p->context = context;
1626
1627   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
1628                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
1629 }
1630
1631 typedef struct {
1632   SilcGetAuthMeth completion;
1633   void *context;
1634 } *InternalGetAuthMethod;
1635
1636 /* Callback called when we've received the authentication method information
1637    from the server after we've requested it. This will get the authentication
1638    data from the user if needed. */
1639
1640 static void silc_get_auth_method_callback(SilcClient client,
1641                                           SilcClientConnection conn,
1642                                           SilcAuthMethod auth_meth,
1643                                           void *context)
1644 {
1645   InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
1646
1647   SILC_LOG_DEBUG(("Start"));
1648
1649   switch (auth_meth) {
1650   case SILC_AUTH_NONE:
1651     /* No authentication required. */
1652     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1653     break;
1654   case SILC_AUTH_PASSWORD:
1655     /* Do not ask the passphrase from user, the library will ask it if
1656        we do not provide it here. */
1657     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1658     break;
1659   case SILC_AUTH_PUBLIC_KEY:
1660     /* Do not get the authentication data now, the library will generate
1661        it using our default key, if we do not provide it here. */
1662     /* XXX In the future when we support multiple local keys and multiple
1663        local certificates we will need to ask from user which one to use. */
1664     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1665     break;
1666   }
1667
1668   silc_free(internal);
1669 }
1670
1671 /* Find authentication method and authentication data by hostname and
1672    port. The hostname may be IP address as well. The found authentication
1673    method and authentication data is returned to `auth_meth', `auth_data'
1674    and `auth_data_len'. The function returns TRUE if authentication method
1675    is found and FALSE if not. `conn' may be NULL. */
1676
1677 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1678                           char *hostname, SilcUInt16 port,
1679                           SilcGetAuthMeth completion, void *context)
1680 {
1681   InternalGetAuthMethod internal;
1682
1683   SILC_LOG_DEBUG(("Start"));
1684
1685   /* XXX must resolve from configuration whether this connection has
1686      any specific authentication data */
1687
1688   /* If we do not have this connection configured by the user in a
1689      configuration file then resolve the authentication method from the
1690      server for this session. */
1691   internal = silc_calloc(1, sizeof(*internal));
1692   internal->completion = completion;
1693   internal->context = context;
1694
1695   silc_client_request_authentication_method(client, conn, 
1696                                             silc_get_auth_method_callback,
1697                                             internal);
1698 }
1699
1700 /* Notifies application that failure packet was received.  This is called
1701    if there is some protocol active in the client.  The `protocol' is the
1702    protocol context.  The `failure' is opaque pointer to the failure
1703    indication.  Note, that the `failure' is protocol dependant and application
1704    must explicitly cast it to correct type.  Usually `failure' is 32 bit
1705    failure type (see protocol specs for all protocol failure types). */
1706
1707 void silc_failure(SilcClient client, SilcClientConnection conn, 
1708                   SilcProtocol protocol, void *failure)
1709 {
1710   SILC_LOG_DEBUG(("Start"));
1711
1712   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1713     SilcSKEStatus status = (SilcSKEStatus)failure;
1714     
1715     if (status == SILC_SKE_STATUS_BAD_VERSION)
1716       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1717                          SILCTXT_KE_BAD_VERSION);
1718     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1719       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1720                          SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1721     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1722       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1723                          SILCTXT_KE_UNKNOWN_GROUP);
1724     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1725       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1726                          SILCTXT_KE_UNKNOWN_CIPHER);
1727     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1728       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1729                          SILCTXT_KE_UNKNOWN_PKCS);
1730     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1731       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1732                          SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1733     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1734       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1735                          SILCTXT_KE_UNKNOWN_HMAC);
1736     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1737       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1738                          SILCTXT_KE_INCORRECT_SIGNATURE);
1739     if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1740       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1741                          SILCTXT_KE_INVALID_COOKIE);
1742   }
1743
1744   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1745     SilcUInt32 err = (SilcUInt32)failure;
1746
1747     if (err == SILC_AUTH_FAILED)
1748       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1749                          SILCTXT_AUTH_FAILED);
1750   }
1751 }
1752
1753 /* Asks whether the user would like to perform the key agreement protocol.
1754    This is called after we have received an key agreement packet or an
1755    reply to our key agreement packet. This returns TRUE if the user wants
1756    the library to perform the key agreement protocol and FALSE if it is not
1757    desired (application may start it later by calling the function
1758    silc_client_perform_key_agreement). */
1759
1760 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1761                        SilcClientEntry client_entry, const char *hostname,
1762                        SilcUInt16 port, SilcKeyAgreementCallback *completion,
1763                        void **context)
1764 {
1765   char portstr[12];
1766
1767   SILC_LOG_DEBUG(("Start"));
1768
1769   /* We will just display the info on the screen and return FALSE and user
1770      will have to start the key agreement with a command. */
1771
1772   if (hostname) 
1773     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1774
1775   if (!hostname)
1776     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1777                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1778   else
1779     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1780                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST, 
1781                        client_entry->nickname, hostname, portstr);
1782
1783   *completion = NULL;
1784   *context = NULL;
1785
1786   return FALSE;
1787 }
1788
1789 void silc_ftp(SilcClient client, SilcClientConnection conn,
1790               SilcClientEntry client_entry, SilcUInt32 session_id,
1791               const char *hostname, SilcUInt16 port)
1792 {
1793   SILC_SERVER_REC *server;
1794   char portstr[12];
1795   FtpSession ftp = silc_calloc(1, sizeof(*ftp));
1796
1797   SILC_LOG_DEBUG(("Start"));
1798
1799   server = conn->context;
1800
1801   ftp->client_entry = client_entry;
1802   ftp->session_id = session_id;
1803   ftp->send = FALSE;
1804   ftp->conn = conn;
1805   silc_dlist_add(server->ftp_sessions, ftp);
1806   server->current_session = ftp;
1807
1808   if (hostname) 
1809     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1810
1811   if (!hostname)
1812     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1813                        SILCTXT_FILE_REQUEST, client_entry->nickname);
1814   else
1815     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1816                        SILCTXT_FILE_REQUEST_HOST, 
1817                        client_entry->nickname, hostname, portstr);
1818 }
1819
1820 /* SILC client operations */
1821 SilcClientOperations ops = {
1822   silc_say,
1823   silc_channel_message,
1824   silc_private_message,
1825   silc_notify,
1826   silc_command,
1827   silc_command_reply,
1828   silc_connect,
1829   silc_disconnect,
1830   silc_get_auth_method,
1831   silc_verify_public_key,
1832   silc_ask_passphrase,
1833   silc_failure,
1834   silc_key_agreement,
1835   silc_ftp,
1836 };