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         if (mode & SILC_UMODE_ANONYMOUS)
904           strcat(buf, " anonymous");
905         if (mode & SILC_UMODE_BLOCK_PRIVMSG)
906           strcat(buf, " blocks private messages");
907
908         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
909                            SILCTXT_WHOIS_MODES, buf);
910       }
911       
912       if (idle && nickname) {
913         memset(buf, 0, sizeof(buf));
914         snprintf(buf, sizeof(buf) - 1, "%lu %s",
915                  idle > 60 ? (idle / 60) : idle,
916                  idle > 60 ? "minutes" : "seconds");
917
918         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
919                            SILCTXT_WHOIS_IDLE, buf);
920       }
921
922       if (fingerprint) {
923         fingerprint = silc_fingerprint(fingerprint, 20);
924         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
925                            SILCTXT_WHOIS_FINGERPRINT, fingerprint);
926         silc_free(fingerprint);
927       }
928     }
929     break;
930     
931   case SILC_COMMAND_IDENTIFY:
932     {
933       SilcClientEntry client_entry;
934       
935       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
936         /* Print the unknown nick for user */
937         unsigned char *tmp =
938           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
939                                      3, NULL);
940         if (tmp)
941           silc_say_error("%s: %s", tmp, 
942                          silc_client_command_status_message(status));
943         break;
944       } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
945         /* Try to find the entry for the unknown client ID, since we
946            might have, and print the nickname of it for user. */
947         SilcUInt32 tmp_len;
948         unsigned char *tmp =
949           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
950                                      2, &tmp_len);
951         if (tmp) {
952           SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len,
953                                                              NULL);
954           if (client_id) {
955             client_entry = silc_client_get_client_by_id(client, conn,
956                                                         client_id);
957             if (client_entry && client_entry->nickname)
958               silc_say_error("%s: %s", client_entry->nickname,
959                              silc_client_command_status_message(status));
960             silc_free(client_id);
961           }
962         }
963         break;
964       }
965
966       break;
967     }
968
969   case SILC_COMMAND_WHOWAS:
970     {
971       char *nickname, *username, *realname;
972       
973       if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
974           status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
975         char *tmp;
976         tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
977                                          3, NULL);
978         if (tmp)
979           silc_say_error("%s: %s", tmp, 
980                          silc_client_command_status_message(status));
981         break;
982       }
983       
984       if (!success)
985         return;
986       
987       (void)va_arg(vp, SilcClientEntry);
988       nickname = va_arg(vp, char *);
989       username = va_arg(vp, char *);
990       realname = va_arg(vp, char *);
991       
992       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
993                          SILCTXT_WHOWAS_USERINFO, nickname, username, 
994                          realname ? realname : "");
995     }
996     break;
997     
998   case SILC_COMMAND_INVITE:
999     {
1000       SilcChannelEntry channel;
1001       char *invite_list;
1002       SilcArgumentPayload args;
1003       int argc = 0;
1004       
1005       if (!success)
1006         return;
1007       
1008       channel = va_arg(vp, SilcChannelEntry);
1009       invite_list = va_arg(vp, char *);
1010
1011       args = silc_command_get_args(cmd_payload);
1012       if (args)
1013         argc = silc_argument_get_arg_num(args);
1014
1015       if (invite_list)
1016         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1017                            SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
1018                            invite_list);
1019       else if (argc == 3)
1020         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1021                            SILCTXT_CHANNEL_NO_INVITE_LIST, 
1022                            channel->channel_name);
1023     }
1024     break;
1025
1026   case SILC_COMMAND_JOIN: 
1027     {
1028       char *channel, *mode, *topic;
1029       SilcUInt32 modei;
1030       SilcChannelEntry channel_entry;
1031       SilcBuffer client_id_list;
1032       SilcUInt32 list_count;
1033
1034       if (!success)
1035         return;
1036
1037       channel = va_arg(vp, char *);
1038       channel_entry = va_arg(vp, SilcChannelEntry);
1039       modei = va_arg(vp, SilcUInt32);
1040       (void)va_arg(vp, SilcUInt32);
1041       (void)va_arg(vp, unsigned char *);
1042       (void)va_arg(vp, unsigned char *);
1043       (void)va_arg(vp, unsigned char *);
1044       topic = va_arg(vp, char *);
1045       (void)va_arg(vp, unsigned char *);
1046       list_count = va_arg(vp, SilcUInt32);
1047       client_id_list = va_arg(vp, SilcBuffer);
1048
1049       chanrec = silc_channel_find(server, channel);
1050       if (!chanrec)
1051         chanrec = silc_channel_create(server, channel, TRUE);
1052
1053       if (topic) {
1054         g_free_not_null(chanrec->topic);
1055         chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1056         signal_emit("channel topic changed", 1, chanrec);
1057       }
1058
1059       mode = silc_client_chmode(modei, 
1060                                 channel_entry->channel_key ? 
1061                                 channel_entry->channel_key->cipher->name : "",
1062                                 channel_entry->hmac ? 
1063                                 silc_hmac_get_name(channel_entry->hmac) : "");
1064       g_free_not_null(chanrec->mode);
1065       chanrec->mode = g_strdup(mode == NULL ? "" : mode);
1066       signal_emit("channel mode changed", 1, chanrec);
1067
1068       /* Resolve the client information */
1069       silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
1070                                       silc_client_join_get_users, 
1071                                       channel_entry);
1072
1073       break;
1074     }
1075
1076   case SILC_COMMAND_NICK: 
1077     {
1078       SilcClientEntry client = va_arg(vp, SilcClientEntry);
1079       char *old;
1080       
1081       if (!success)
1082         return;
1083
1084       old = g_strdup(server->nick);
1085       server_change_nick(SERVER(server), client->nickname);
1086       nicklist_rename_unique(SERVER(server),
1087                              server->conn->local_entry, server->nick,
1088                              client, client->nickname);
1089       signal_emit("message own_nick", 4, server, server->nick, old, "");
1090       g_free(old);
1091       break;
1092     }
1093     
1094   case SILC_COMMAND_LIST:
1095     {
1096       char *topic, *name;
1097       int usercount;
1098       char users[20];
1099       
1100       if (!success)
1101         return;
1102       
1103       (void)va_arg(vp, SilcChannelEntry);
1104       name = va_arg(vp, char *);
1105       topic = va_arg(vp, char *);
1106       usercount = va_arg(vp, int);
1107       
1108       if (status == SILC_STATUS_LIST_START ||
1109           status == SILC_STATUS_OK)
1110         printformat_module("fe-common/silc", server, NULL,
1111                            MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
1112
1113       if (!usercount)
1114         snprintf(users, sizeof(users) - 1, "N/A");
1115       else
1116         snprintf(users, sizeof(users) - 1, "%d", usercount);
1117       printformat_module("fe-common/silc", server, NULL,
1118                          MSGLEVEL_CRAP, SILCTXT_LIST,
1119                          name, users, topic ? topic : "");
1120     }
1121     break;
1122     
1123   case SILC_COMMAND_UMODE:
1124     {
1125       SilcUInt32 mode;
1126       
1127       if (!success)
1128         return;
1129       
1130       mode = va_arg(vp, SilcUInt32);
1131       
1132       if (mode & SILC_UMODE_SERVER_OPERATOR &&
1133           !(server->umode & SILC_UMODE_SERVER_OPERATOR))
1134         printformat_module("fe-common/silc", server, NULL,
1135                            MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1136
1137       if (mode & SILC_UMODE_ROUTER_OPERATOR &&
1138           !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
1139         printformat_module("fe-common/silc", server, NULL,
1140                            MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1141
1142       server->umode = mode;
1143       signal_emit("user mode changed", 2, server, NULL);
1144     }
1145     break;
1146     
1147   case SILC_COMMAND_OPER:
1148     if (!success)
1149       return;
1150
1151     server->umode |= SILC_UMODE_SERVER_OPERATOR;
1152     signal_emit("user mode changed", 2, server, NULL);
1153
1154     printformat_module("fe-common/silc", server, NULL,
1155                        MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1156     break;
1157     
1158   case SILC_COMMAND_SILCOPER:
1159     if (!success)
1160       return;
1161
1162     server->umode |= SILC_UMODE_ROUTER_OPERATOR;
1163     signal_emit("user mode changed", 2, server, NULL);
1164
1165     printformat_module("fe-common/silc", server, NULL,
1166                        MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1167     break;
1168     
1169   case SILC_COMMAND_USERS: 
1170     {
1171       SilcHashTableList htl;
1172       SilcChannelEntry channel;
1173       SilcChannelUser chu;
1174       
1175       if (!success)
1176         return;
1177       
1178       channel = va_arg(vp, SilcChannelEntry);
1179       
1180       printformat_module("fe-common/silc", server, channel->channel_name,
1181                          MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
1182                          channel->channel_name);
1183
1184       silc_hash_table_list(channel->user_list, &htl);
1185       while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1186         SilcClientEntry e = chu->client;
1187         char stat[5], *mode;
1188
1189         if (!e->nickname)
1190           continue;
1191         
1192         memset(stat, 0, sizeof(stat));
1193         mode = silc_client_chumode_char(chu->mode);
1194         if (e->mode & SILC_UMODE_GONE)
1195           strcat(stat, "G");
1196         else if (e->mode & SILC_UMODE_INDISPOSED)
1197           strcat(stat, "I");
1198         else if (e->mode & SILC_UMODE_BUSY)
1199           strcat(stat, "B");
1200         else if (e->mode & SILC_UMODE_PAGE)
1201           strcat(stat, "P");
1202         else if (e->mode & SILC_UMODE_HYPER)
1203           strcat(stat, "H");
1204         else if (e->mode & SILC_UMODE_ROBOT)
1205           strcat(stat, "R");
1206         else if (e->mode & SILC_UMODE_ANONYMOUS)
1207           strcat(stat, "?");
1208         else
1209           strcat(stat, "A");
1210         if (mode)
1211           strcat(stat, mode);
1212
1213         printformat_module("fe-common/silc", server, channel->channel_name,
1214                            MSGLEVEL_CRAP, SILCTXT_USERS,
1215                            e->nickname, stat, 
1216                            e->username ? e->username : "",
1217                            e->hostname ? e->hostname : "",
1218                            e->realname ? e->realname : "");
1219         if (mode)
1220           silc_free(mode);
1221       }
1222       silc_hash_table_list_reset(&htl);
1223     }
1224     break;
1225
1226   case SILC_COMMAND_BAN:
1227     {
1228       SilcChannelEntry channel;
1229       char *ban_list;
1230       
1231       if (!success)
1232         return;
1233       
1234       channel = va_arg(vp, SilcChannelEntry);
1235       ban_list = va_arg(vp, char *);
1236       
1237       if (ban_list)
1238         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1239                            SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
1240                            ban_list);
1241       else
1242         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1243                            SILCTXT_CHANNEL_NO_BAN_LIST, 
1244                            channel->channel_name);
1245     }
1246     break;
1247     
1248   case SILC_COMMAND_GETKEY:
1249     {
1250       SilcIdType id_type;
1251       void *entry;
1252       SilcPublicKey public_key;
1253       unsigned char *pk;
1254       SilcUInt32 pk_len;
1255       GetkeyContext getkey;
1256       char *name;
1257       
1258       if (!success)
1259         return;
1260       
1261       id_type = va_arg(vp, SilcUInt32);
1262       entry = va_arg(vp, void *);
1263       public_key = va_arg(vp, SilcPublicKey);
1264
1265       if (public_key) {
1266         pk = silc_pkcs_public_key_encode(public_key, &pk_len);
1267
1268         getkey = silc_calloc(1, sizeof(*getkey));
1269         getkey->entry = entry;
1270         getkey->id_type = id_type;
1271         getkey->client = client;
1272         getkey->conn = conn;
1273         getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1274
1275         name = (id_type == SILC_ID_CLIENT ? 
1276                 ((SilcClientEntry)entry)->nickname :
1277                 ((SilcServerEntry)entry)->server_name);
1278
1279         silc_verify_public_key_internal(client, conn, name,
1280                                         (id_type == SILC_ID_CLIENT ?
1281                                          SILC_SOCKET_TYPE_CLIENT :
1282                                          SILC_SOCKET_TYPE_SERVER),
1283                                         pk, pk_len, SILC_SKE_PK_TYPE_SILC,
1284                                         silc_getkey_cb, getkey);
1285         silc_free(pk);
1286       } else {
1287         printformat_module("fe-common/silc", server, NULL,
1288                            MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
1289       }
1290     }
1291     break;
1292
1293   case SILC_COMMAND_INFO:
1294     {
1295       SilcServerEntry server_entry;
1296       char *server_name;
1297       char *server_info;
1298
1299       if (!success)
1300         return;
1301       
1302       server_entry = va_arg(vp, SilcServerEntry);
1303       server_name = va_arg(vp, char *);
1304       server_info = va_arg(vp, char *);
1305
1306       if (server_name && server_info )
1307         {
1308           printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
1309           printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
1310         }
1311     }
1312     break;
1313     
1314   case SILC_COMMAND_TOPIC:
1315     {
1316       SilcChannelEntry channel;
1317       char *topic;
1318       
1319       if (!success)
1320         return;
1321       
1322       channel = va_arg(vp, SilcChannelEntry);
1323       topic = va_arg(vp, char *);
1324       
1325       if (topic) {
1326         chanrec = silc_channel_find_entry(server, channel);
1327         if (chanrec) {
1328           g_free_not_null(chanrec->topic);
1329           chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1330           signal_emit("channel topic changed", 1, chanrec);
1331         }
1332         printformat_module("fe-common/silc", server, channel->channel_name,
1333                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
1334                            channel->channel_name, topic);
1335       } else {
1336         printformat_module("fe-common/silc", server, channel->channel_name,
1337                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
1338                            channel->channel_name);
1339       }
1340     }
1341     break;
1342
1343   }
1344
1345   va_end(vp);
1346 }
1347
1348 typedef struct {
1349   SilcClient client;
1350   SilcClientConnection conn;
1351   char *filename;
1352   char *entity;
1353   char *entity_name;
1354   unsigned char *pk;
1355   SilcUInt32 pk_len;
1356   SilcSKEPKType pk_type;
1357   SilcVerifyPublicKey completion;
1358   void *context;
1359 } *PublicKeyVerify;
1360
1361 static void verify_public_key_completion(const char *line, void *context)
1362 {
1363   PublicKeyVerify verify = (PublicKeyVerify)context;
1364
1365   if (line[0] == 'Y' || line[0] == 'y') {
1366     /* Call the completion */
1367     if (verify->completion)
1368       verify->completion(TRUE, verify->context);
1369
1370     /* Save the key for future checking */
1371     silc_pkcs_save_public_key_data(verify->filename, verify->pk, 
1372                                    verify->pk_len, SILC_PKCS_FILE_PEM);
1373   } else {
1374     /* Call the completion */
1375     if (verify->completion)
1376       verify->completion(FALSE, verify->context);
1377
1378     printformat_module("fe-common/silc", NULL, NULL,
1379                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, 
1380                        verify->entity_name ? verify->entity_name :
1381                        verify->entity);
1382   }
1383
1384   silc_free(verify->filename);
1385   silc_free(verify->entity);
1386   silc_free(verify->entity_name);
1387   silc_free(verify->pk);
1388   silc_free(verify);
1389 }
1390
1391 /* Internal routine to verify public key. If the `completion' is provided
1392    it will be called to indicate whether public was verified or not. For
1393    server/router public key this will check for filename that includes the
1394    remote host's IP address and remote host's hostname. */
1395
1396 static void 
1397 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
1398                                 const char *name, SilcSocketType conn_type, 
1399                                 unsigned char *pk, SilcUInt32 pk_len, 
1400                                 SilcSKEPKType pk_type,
1401                                 SilcVerifyPublicKey completion, void *context)
1402 {
1403   int i;
1404   char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
1405   char *fingerprint, *babbleprint, *format;
1406   struct passwd *pw;
1407   struct stat st;
1408   char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
1409                    conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
1410                   "server" : "client");
1411   PublicKeyVerify verify;
1412
1413   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
1414     printformat_module("fe-common/silc", NULL, NULL,
1415                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED, 
1416                        entity, pk_type);
1417     if (completion)
1418       completion(FALSE, context);
1419     return;
1420   }
1421
1422   pw = getpwuid(getuid());
1423   if (!pw) {
1424     if (completion)
1425       completion(FALSE, context);
1426     return;
1427   }
1428
1429   memset(filename, 0, sizeof(filename));
1430   memset(filename2, 0, sizeof(filename2));
1431   memset(file, 0, sizeof(file));
1432
1433   if (conn_type == SILC_SOCKET_TYPE_SERVER ||
1434       conn_type == SILC_SOCKET_TYPE_ROUTER) {
1435     if (!name) {
1436       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1437                conn->sock->ip, conn->sock->port);
1438       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
1439                get_irssi_dir(), entity, file);
1440       
1441       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1442                conn->sock->hostname, conn->sock->port);
1443       snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s", 
1444                get_irssi_dir(), entity, file);
1445       
1446       ipf = filename;
1447       hostf = filename2;
1448     } else {
1449       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1450                name, conn->sock->port);
1451       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
1452                get_irssi_dir(), entity, file);
1453       
1454       ipf = filename;
1455     }
1456   } else {
1457     /* Replace all whitespaces with `_'. */
1458     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1459     for (i = 0; i < strlen(fingerprint); i++)
1460       if (fingerprint[i] == ' ')
1461         fingerprint[i] = '_';
1462     
1463     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
1464     snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
1465              get_irssi_dir(), entity, file);
1466     silc_free(fingerprint);
1467
1468     ipf = filename;
1469   }
1470
1471   /* Take fingerprint of the public key */
1472   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1473   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
1474
1475   verify = silc_calloc(1, sizeof(*verify));
1476   verify->client = client;
1477   verify->conn = conn;
1478   verify->filename = strdup(ipf);
1479   verify->entity = strdup(entity);
1480   verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ?
1481                          (name ? strdup(name) : strdup(conn->sock->hostname))
1482                          : NULL);
1483   verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
1484   memcpy(verify->pk, pk, pk_len);
1485   verify->pk_len = pk_len;
1486   verify->pk_type = pk_type;
1487   verify->completion = completion;
1488   verify->context = context;
1489
1490   /* Check whether this key already exists */
1491   if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
1492     /* Key does not exist, ask user to verify the key and save it */
1493
1494     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1495                        SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1496                        verify->entity_name : entity);
1497     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1498                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1499     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1500                        SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1501     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1502                              SILCTXT_PUBKEY_ACCEPT);
1503     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1504                             format, 0, verify);
1505     g_free(format);
1506     silc_free(fingerprint);
1507     return;
1508   } else {
1509     /* The key already exists, verify it. */
1510     SilcPublicKey public_key;
1511     unsigned char *encpk;
1512     SilcUInt32 encpk_len;
1513
1514     /* Load the key file, try for both IP filename and hostname filename */
1515     if (!silc_pkcs_load_public_key(ipf, &public_key, 
1516                                    SILC_PKCS_FILE_PEM) &&
1517         !silc_pkcs_load_public_key(ipf, &public_key, 
1518                                    SILC_PKCS_FILE_BIN) &&
1519         (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key, 
1520                                                SILC_PKCS_FILE_PEM) &&
1521                     !silc_pkcs_load_public_key(hostf, &public_key, 
1522                                                SILC_PKCS_FILE_BIN)))) {
1523       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1524                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1525                          verify->entity_name : entity);
1526       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1527                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1528       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1529                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1530       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1531                          SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
1532       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1533                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1534       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1535                               format, 0, verify);
1536       g_free(format);
1537       silc_free(fingerprint);
1538       return;
1539     }
1540
1541     /* Encode the key data */
1542     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
1543     if (!encpk) {
1544       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1545                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1546                          verify->entity_name : entity);
1547       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1548                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1549       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1550                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1551       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1552                          SILCTXT_PUBKEY_MALFORMED, entity);
1553       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1554                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1555       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1556                               format, 0, verify);
1557       g_free(format);
1558       silc_free(fingerprint);
1559       return;
1560     }
1561
1562     /* Compare the keys */
1563     if (memcmp(encpk, pk, encpk_len)) {
1564       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1565                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1566                          verify->entity_name : entity);
1567       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1568                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1569       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1570                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1571       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1572                          SILCTXT_PUBKEY_NO_MATCH, entity);
1573       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1574                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
1575       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1576                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
1577
1578       /* Ask user to verify the key and save it */
1579       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1580                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1581       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1582                               format, 0, verify);
1583       g_free(format);
1584       silc_free(fingerprint);
1585       return;
1586     }
1587
1588     /* Local copy matched */
1589     if (completion)
1590       completion(TRUE, context);
1591     silc_free(fingerprint);
1592   }
1593 }
1594
1595 /* Verifies received public key. The `conn_type' indicates which entity
1596    (server, client etc.) has sent the public key. If user decides to trust
1597    the key may be saved as trusted public key for later use. The 
1598    `completion' must be called after the public key has been verified. */
1599
1600 void 
1601 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
1602                        SilcSocketType conn_type, unsigned char *pk, 
1603                        SilcUInt32 pk_len, SilcSKEPKType pk_type,
1604                        SilcVerifyPublicKey completion, void *context)
1605 {
1606   silc_verify_public_key_internal(client, conn, NULL, conn_type, pk,
1607                                   pk_len, pk_type,
1608                                   completion, context);
1609 }
1610
1611 /* Asks passphrase from user on the input line. */
1612
1613 typedef struct {
1614   SilcAskPassphrase completion;
1615   void *context;
1616 } *AskPassphrase;
1617
1618 void ask_passphrase_completion(const char *passphrase, void *context)
1619 {
1620   AskPassphrase p = (AskPassphrase)context;
1621   p->completion((unsigned char *)passphrase, 
1622                 passphrase ? strlen(passphrase) : 0, p->context);
1623   silc_free(p);
1624 }
1625
1626 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1627                          SilcAskPassphrase completion, void *context)
1628 {
1629   AskPassphrase p = silc_calloc(1, sizeof(*p));
1630   p->completion = completion;
1631   p->context = context;
1632
1633   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
1634                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
1635 }
1636
1637 typedef struct {
1638   SilcGetAuthMeth completion;
1639   void *context;
1640 } *InternalGetAuthMethod;
1641
1642 /* Callback called when we've received the authentication method information
1643    from the server after we've requested it. This will get the authentication
1644    data from the user if needed. */
1645
1646 static void silc_get_auth_method_callback(SilcClient client,
1647                                           SilcClientConnection conn,
1648                                           SilcAuthMethod auth_meth,
1649                                           void *context)
1650 {
1651   InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
1652
1653   SILC_LOG_DEBUG(("Start"));
1654
1655   switch (auth_meth) {
1656   case SILC_AUTH_NONE:
1657     /* No authentication required. */
1658     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1659     break;
1660   case SILC_AUTH_PASSWORD:
1661     /* Do not ask the passphrase from user, the library will ask it if
1662        we do not provide it here. */
1663     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1664     break;
1665   case SILC_AUTH_PUBLIC_KEY:
1666     /* Do not get the authentication data now, the library will generate
1667        it using our default key, if we do not provide it here. */
1668     /* XXX In the future when we support multiple local keys and multiple
1669        local certificates we will need to ask from user which one to use. */
1670     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1671     break;
1672   }
1673
1674   silc_free(internal);
1675 }
1676
1677 /* Find authentication method and authentication data by hostname and
1678    port. The hostname may be IP address as well. The found authentication
1679    method and authentication data is returned to `auth_meth', `auth_data'
1680    and `auth_data_len'. The function returns TRUE if authentication method
1681    is found and FALSE if not. `conn' may be NULL. */
1682
1683 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1684                           char *hostname, SilcUInt16 port,
1685                           SilcGetAuthMeth completion, void *context)
1686 {
1687   InternalGetAuthMethod internal;
1688
1689   SILC_LOG_DEBUG(("Start"));
1690
1691   /* XXX must resolve from configuration whether this connection has
1692      any specific authentication data */
1693
1694   /* If we do not have this connection configured by the user in a
1695      configuration file then resolve the authentication method from the
1696      server for this session. */
1697   internal = silc_calloc(1, sizeof(*internal));
1698   internal->completion = completion;
1699   internal->context = context;
1700
1701   silc_client_request_authentication_method(client, conn, 
1702                                             silc_get_auth_method_callback,
1703                                             internal);
1704 }
1705
1706 /* Notifies application that failure packet was received.  This is called
1707    if there is some protocol active in the client.  The `protocol' is the
1708    protocol context.  The `failure' is opaque pointer to the failure
1709    indication.  Note, that the `failure' is protocol dependant and application
1710    must explicitly cast it to correct type.  Usually `failure' is 32 bit
1711    failure type (see protocol specs for all protocol failure types). */
1712
1713 void silc_failure(SilcClient client, SilcClientConnection conn, 
1714                   SilcProtocol protocol, void *failure)
1715 {
1716   SILC_LOG_DEBUG(("Start"));
1717
1718   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1719     SilcSKEStatus status = (SilcSKEStatus)failure;
1720     
1721     if (status == SILC_SKE_STATUS_BAD_VERSION)
1722       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1723                          SILCTXT_KE_BAD_VERSION);
1724     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1725       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1726                          SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1727     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1728       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1729                          SILCTXT_KE_UNKNOWN_GROUP);
1730     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1731       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1732                          SILCTXT_KE_UNKNOWN_CIPHER);
1733     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1734       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1735                          SILCTXT_KE_UNKNOWN_PKCS);
1736     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1737       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1738                          SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1739     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1740       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1741                          SILCTXT_KE_UNKNOWN_HMAC);
1742     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1743       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1744                          SILCTXT_KE_INCORRECT_SIGNATURE);
1745     if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1746       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1747                          SILCTXT_KE_INVALID_COOKIE);
1748   }
1749
1750   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1751     SilcUInt32 err = (SilcUInt32)failure;
1752
1753     if (err == SILC_AUTH_FAILED)
1754       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1755                          SILCTXT_AUTH_FAILED);
1756   }
1757 }
1758
1759 /* Asks whether the user would like to perform the key agreement protocol.
1760    This is called after we have received an key agreement packet or an
1761    reply to our key agreement packet. This returns TRUE if the user wants
1762    the library to perform the key agreement protocol and FALSE if it is not
1763    desired (application may start it later by calling the function
1764    silc_client_perform_key_agreement). */
1765
1766 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1767                        SilcClientEntry client_entry, const char *hostname,
1768                        SilcUInt16 port, SilcKeyAgreementCallback *completion,
1769                        void **context)
1770 {
1771   char portstr[12];
1772
1773   SILC_LOG_DEBUG(("Start"));
1774
1775   /* We will just display the info on the screen and return FALSE and user
1776      will have to start the key agreement with a command. */
1777
1778   if (hostname) 
1779     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1780
1781   if (!hostname)
1782     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1783                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1784   else
1785     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1786                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST, 
1787                        client_entry->nickname, hostname, portstr);
1788
1789   *completion = NULL;
1790   *context = NULL;
1791
1792   return FALSE;
1793 }
1794
1795 void silc_ftp(SilcClient client, SilcClientConnection conn,
1796               SilcClientEntry client_entry, SilcUInt32 session_id,
1797               const char *hostname, SilcUInt16 port)
1798 {
1799   SILC_SERVER_REC *server;
1800   char portstr[12];
1801   FtpSession ftp = silc_calloc(1, sizeof(*ftp));
1802
1803   SILC_LOG_DEBUG(("Start"));
1804
1805   server = conn->context;
1806
1807   ftp->client_entry = client_entry;
1808   ftp->session_id = session_id;
1809   ftp->send = FALSE;
1810   ftp->conn = conn;
1811   silc_dlist_add(server->ftp_sessions, ftp);
1812   server->current_session = ftp;
1813
1814   if (hostname) 
1815     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1816
1817   if (!hostname)
1818     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1819                        SILCTXT_FILE_REQUEST, client_entry->nickname);
1820   else
1821     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1822                        SILCTXT_FILE_REQUEST_HOST, 
1823                        client_entry->nickname, hostname, portstr);
1824 }
1825
1826 /* SILC client operations */
1827 SilcClientOperations ops = {
1828   silc_say,
1829   silc_channel_message,
1830   silc_private_message,
1831   silc_notify,
1832   silc_command,
1833   silc_command_reply,
1834   silc_connect,
1835   silc_disconnect,
1836   silc_get_auth_method,
1837   silc_verify_public_key,
1838   silc_ask_passphrase,
1839   silc_failure,
1840   silc_key_agreement,
1841   silc_ftp,
1842 };