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