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 || server->connection_lost)
623     return;
624
625   if (server->conn && server->conn->local_entry) {
626     nicklist_rename_unique(SERVER(server),
627                            server->conn->local_entry, server->nick,
628                            server->conn->local_entry, 
629                            silc_client->username);
630     silc_change_nick(server, silc_client->username);
631   }
632
633   server->conn->context = NULL;
634   server->conn = NULL;
635   server->connection_lost = TRUE;
636   server_disconnect(SERVER(server));
637 }
638
639 /* Command handler. This function is called always in the command function.
640    If error occurs it will be called as well. `conn' is the associated
641    client connection. `cmd_context' is the command context that was
642    originally sent to the command. `success' is FALSE if error occured
643    during command. `command' is the command being processed. It must be
644    noted that this is not reply from server. This is merely called just
645    after application has called the command. Just to tell application
646    that the command really was processed. */
647
648 void silc_command(SilcClient client, SilcClientConnection conn, 
649                   SilcClientCommandContext cmd_context, int success,
650                   SilcCommand command)
651 {
652   SILC_SERVER_REC *server = conn->context;
653
654   SILC_LOG_DEBUG(("Start"));
655
656   if (!success)
657     return;
658
659   switch(command) {
660   case SILC_COMMAND_INVITE:
661     printformat_module("fe-common/silc", server, NULL,
662                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING,
663                        cmd_context->argv[2], 
664                        (cmd_context->argv[1][0] == '*' ?
665                         (char *)conn->current_channel->channel_name :
666                         (char *)cmd_context->argv[1]));
667     break;
668   default:
669     break;
670   }
671 }
672
673 /* Client info resolving callback when JOIN command reply is received.
674    This will cache all users on the channel. */
675
676 static void silc_client_join_get_users(SilcClient client,
677                                        SilcClientConnection conn,
678                                        SilcClientEntry *clients,
679                                        SilcUInt32 clients_count,
680                                        void *context)
681 {
682   SilcChannelEntry channel = (SilcChannelEntry)context;
683   SilcHashTableList htl;
684   SilcChannelUser chu;
685   SILC_SERVER_REC *server = conn->context;
686   SILC_CHANNEL_REC *chanrec;
687   SilcClientEntry founder = NULL;
688   NICK_REC *ownnick;
689
690   if (!clients)
691     return;
692
693   chanrec = silc_channel_find(server, channel->channel_name);
694   if (chanrec == NULL)
695     return;
696
697   silc_hash_table_list(channel->user_list, &htl);
698   while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
699     if (!chu->client->nickname)
700       continue;
701     if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
702       founder = chu->client;
703     silc_nicklist_insert(chanrec, chu, FALSE);
704   }
705   silc_hash_table_list_reset(&htl);
706
707   ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
708   nicklist_set_own(CHANNEL(chanrec), ownnick);
709   signal_emit("channel joined", 1, chanrec);
710
711   if (chanrec->topic)
712     printformat_module("fe-common/silc", server, channel->channel_name,
713                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
714                        channel->channel_name, chanrec->topic);
715
716   fe_channels_nicklist(CHANNEL(chanrec), CHANNEL_NICKLIST_FLAG_ALL);
717
718   if (founder) {
719     if (founder == conn->local_entry)
720       printformat_module("fe-common/silc", 
721                          server, channel->channel_name, MSGLEVEL_CRAP,
722                          SILCTXT_CHANNEL_FOUNDER_YOU,
723                          channel->channel_name);
724     else
725       printformat_module("fe-common/silc", 
726                          server, channel->channel_name, MSGLEVEL_CRAP,
727                          SILCTXT_CHANNEL_FOUNDER,
728                          channel->channel_name, founder->nickname);
729   }
730 }
731
732 typedef struct {
733   SilcClient client;
734   SilcClientConnection conn;
735   void *entry;
736   SilcIdType id_type;
737   char *fingerprint;
738 } *GetkeyContext;
739
740 void silc_getkey_cb(bool success, void *context)
741 {
742   GetkeyContext getkey = (GetkeyContext)context;
743   char *entity = (getkey->id_type == SILC_ID_CLIENT ? "user" : "server");
744   char *name = (getkey->id_type == SILC_ID_CLIENT ? 
745                 ((SilcClientEntry)getkey->entry)->nickname :
746                 ((SilcServerEntry)getkey->entry)->server_name);
747
748   if (success) {
749     printformat_module("fe-common/silc", NULL, NULL,
750                        MSGLEVEL_CRAP, SILCTXT_GETKEY_VERIFIED, entity, name);
751   } else {
752     printformat_module("fe-common/silc", NULL, NULL,
753                        MSGLEVEL_CRAP, SILCTXT_GETKEY_DISCARD, entity, name);
754   }
755
756   silc_free(getkey->fingerprint);
757   silc_free(getkey);
758 }
759
760 /* Command reply handler. This function is called always in the command reply
761    function. If error occurs it will be called as well. Normal scenario
762    is that it will be called after the received command data has been parsed
763    and processed. The function is used to pass the received command data to
764    the application. 
765
766    `conn' is the associated client connection. `cmd_payload' is the command
767    payload data received from server and it can be ignored. It is provided
768    if the application would like to re-parse the received command data,
769    however, it must be noted that the data is parsed already by the library
770    thus the payload can be ignored. `success' is FALSE if error occured.
771    In this case arguments are not sent to the application. `command' is the
772    command reply being processed. The function has variable argument list
773    and each command defines the number and type of arguments it passes to the
774    application (on error they are not sent). */
775
776 void 
777 silc_command_reply(SilcClient client, SilcClientConnection conn,
778                    SilcCommandPayload cmd_payload, int success,
779                    SilcCommand command, SilcCommandStatus status, ...)
780
781 {
782   SILC_SERVER_REC *server = conn->context;
783   SILC_CHANNEL_REC *chanrec;
784   va_list vp;
785
786   va_start(vp, status);
787
788   SILC_LOG_DEBUG(("Start"));
789
790   switch(command) {
791   case SILC_COMMAND_WHOIS:
792     {
793       char buf[1024], *nickname, *username, *realname, *nick;
794       unsigned char *fingerprint;
795       SilcUInt32 idle, mode;
796       SilcBuffer channels;
797       SilcClientEntry client_entry;
798       
799       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
800         /* Print the unknown nick for user */
801         unsigned char *tmp =
802           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
803                                      3, NULL);
804         if (tmp)
805           silc_say_error("%s: %s", tmp, 
806                          silc_client_command_status_message(status));
807         break;
808       } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
809         /* Try to find the entry for the unknown client ID, since we
810            might have, and print the nickname of it for user. */
811         SilcUInt32 tmp_len;
812         unsigned char *tmp =
813           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
814                                      2, &tmp_len);
815         if (tmp) {
816           SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, 
817                                                              NULL);
818           if (client_id) {
819             client_entry = silc_client_get_client_by_id(client, conn,
820                                                         client_id);
821             if (client_entry && client_entry->nickname)
822               silc_say_error("%s: %s", client_entry->nickname,
823                              silc_client_command_status_message(status));
824             silc_free(client_id);
825           }
826         }
827         break;
828       }
829       
830       if (!success)
831         return;
832       
833       client_entry = va_arg(vp, SilcClientEntry);
834       nickname = va_arg(vp, char *);
835       username = va_arg(vp, char *);
836       realname = va_arg(vp, char *);
837       channels = va_arg(vp, SilcBuffer);
838       mode = va_arg(vp, SilcUInt32);
839       idle = va_arg(vp, SilcUInt32);
840       fingerprint = va_arg(vp, unsigned char *);
841       
842       silc_parse_userfqdn(nickname, &nick, NULL);
843       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
844                          SILCTXT_WHOIS_USERINFO, nickname, 
845                          client_entry->username, client_entry->hostname,
846                          nick, client_entry->nickname);
847       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
848                          SILCTXT_WHOIS_REALNAME, realname);
849       silc_free(nick);
850
851       if (channels) {
852         SilcDList list = silc_channel_payload_parse_list(channels->data,
853                                                          channels->len);
854         if (list) {
855           SilcChannelPayload entry;
856           memset(buf, 0, sizeof(buf));
857           silc_dlist_start(list);
858           while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
859             char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
860             SilcUInt32 name_len;
861             char *name = silc_channel_get_name(entry, &name_len);
862             
863             if (m)
864               strncat(buf, m, strlen(m));
865             strncat(buf, name, name_len);
866             strncat(buf, " ", 1);
867             silc_free(m);
868           }
869
870           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
871                              SILCTXT_WHOIS_CHANNELS, buf);
872           silc_channel_payload_list_free(list);
873         }
874       }
875       
876       if (mode) {
877         memset(buf, 0, sizeof(buf));
878
879         if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
880             (mode & SILC_UMODE_ROUTER_OPERATOR)) {
881           strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
882                  "Server Operator " :
883                  (mode & SILC_UMODE_ROUTER_OPERATOR) ?
884                  "SILC Operator " : "[Unknown mode] ");
885         }
886         if (mode & SILC_UMODE_GONE)
887           strcat(buf, "away");
888
889         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
890                            SILCTXT_WHOIS_MODES, buf);
891       }
892       
893       if (idle && nickname) {
894         memset(buf, 0, sizeof(buf));
895         snprintf(buf, sizeof(buf) - 1, "%lu %s",
896                  idle > 60 ? (idle / 60) : idle,
897                  idle > 60 ? "minutes" : "seconds");
898
899         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
900                            SILCTXT_WHOIS_IDLE, buf);
901       }
902
903       if (fingerprint) {
904         fingerprint = silc_fingerprint(fingerprint, 20);
905         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
906                            SILCTXT_WHOIS_FINGERPRINT, fingerprint);
907         silc_free(fingerprint);
908       }
909     }
910     break;
911     
912   case SILC_COMMAND_IDENTIFY:
913     {
914       SilcClientEntry client_entry;
915       
916       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
917         /* Print the unknown nick for user */
918         unsigned char *tmp =
919           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
920                                      3, NULL);
921         if (tmp)
922           silc_say_error("%s: %s", tmp, 
923                          silc_client_command_status_message(status));
924         break;
925       } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
926         /* Try to find the entry for the unknown client ID, since we
927            might have, and print the nickname of it for user. */
928         SilcUInt32 tmp_len;
929         unsigned char *tmp =
930           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
931                                      2, &tmp_len);
932         if (tmp) {
933           SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len,
934                                                              NULL);
935           if (client_id) {
936             client_entry = silc_client_get_client_by_id(client, conn,
937                                                         client_id);
938             if (client_entry && client_entry->nickname)
939               silc_say_error("%s: %s", client_entry->nickname,
940                              silc_client_command_status_message(status));
941             silc_free(client_id);
942           }
943         }
944         break;
945       }
946
947       break;
948     }
949
950   case SILC_COMMAND_WHOWAS:
951     {
952       char *nickname, *username, *realname;
953       
954       if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
955           status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
956         char *tmp;
957         tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
958                                          3, NULL);
959         if (tmp)
960           silc_say_error("%s: %s", tmp, 
961                          silc_client_command_status_message(status));
962         break;
963       }
964       
965       if (!success)
966         return;
967       
968       (void)va_arg(vp, SilcClientEntry);
969       nickname = va_arg(vp, char *);
970       username = va_arg(vp, char *);
971       realname = va_arg(vp, char *);
972       
973       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
974                          SILCTXT_WHOWAS_USERINFO, nickname, username, 
975                          realname ? realname : "");
976     }
977     break;
978     
979   case SILC_COMMAND_INVITE:
980     {
981       SilcChannelEntry channel;
982       char *invite_list;
983       SilcArgumentPayload args;
984       int argc = 0;
985       
986       if (!success)
987         return;
988       
989       channel = va_arg(vp, SilcChannelEntry);
990       invite_list = va_arg(vp, char *);
991
992       args = silc_command_get_args(cmd_payload);
993       if (args)
994         argc = silc_argument_get_arg_num(args);
995
996       if (invite_list)
997         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
998                            SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
999                            invite_list);
1000       else if (argc == 3)
1001         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1002                            SILCTXT_CHANNEL_NO_INVITE_LIST, 
1003                            channel->channel_name);
1004     }
1005     break;
1006
1007   case SILC_COMMAND_JOIN: 
1008     {
1009       char *channel, *mode, *topic;
1010       SilcUInt32 modei;
1011       SilcChannelEntry channel_entry;
1012       SilcBuffer client_id_list;
1013       SilcUInt32 list_count;
1014
1015       if (!success)
1016         return;
1017
1018       channel = va_arg(vp, char *);
1019       channel_entry = va_arg(vp, SilcChannelEntry);
1020       modei = va_arg(vp, SilcUInt32);
1021       (void)va_arg(vp, SilcUInt32);
1022       (void)va_arg(vp, unsigned char *);
1023       (void)va_arg(vp, unsigned char *);
1024       (void)va_arg(vp, unsigned char *);
1025       topic = va_arg(vp, char *);
1026       (void)va_arg(vp, unsigned char *);
1027       list_count = va_arg(vp, SilcUInt32);
1028       client_id_list = va_arg(vp, SilcBuffer);
1029
1030       chanrec = silc_channel_find(server, channel);
1031       if (!chanrec)
1032         chanrec = silc_channel_create(server, channel, TRUE);
1033
1034       if (topic) {
1035         g_free_not_null(chanrec->topic);
1036         chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1037         signal_emit("channel topic changed", 1, chanrec);
1038       }
1039
1040       mode = silc_client_chmode(modei, 
1041                                 channel_entry->channel_key ? 
1042                                 channel_entry->channel_key->cipher->name : "",
1043                                 channel_entry->hmac ? 
1044                                 silc_hmac_get_name(channel_entry->hmac) : "");
1045       g_free_not_null(chanrec->mode);
1046       chanrec->mode = g_strdup(mode == NULL ? "" : mode);
1047       signal_emit("channel mode changed", 1, chanrec);
1048
1049       /* Resolve the client information */
1050       silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
1051                                       silc_client_join_get_users, 
1052                                       channel_entry);
1053
1054       break;
1055     }
1056
1057   case SILC_COMMAND_NICK: 
1058     {
1059       SilcClientEntry client = va_arg(vp, SilcClientEntry);
1060       char *old;
1061       
1062       if (!success)
1063         return;
1064
1065       old = g_strdup(server->nick);
1066       server_change_nick(SERVER(server), client->nickname);
1067       nicklist_rename_unique(SERVER(server),
1068                              server->conn->local_entry, server->nick,
1069                              client, client->nickname);
1070       signal_emit("message own_nick", 4, server, server->nick, old, "");
1071       g_free(old);
1072       break;
1073     }
1074     
1075   case SILC_COMMAND_LIST:
1076     {
1077       char *topic, *name;
1078       int usercount;
1079       char users[20];
1080       
1081       if (!success)
1082         return;
1083       
1084       (void)va_arg(vp, SilcChannelEntry);
1085       name = va_arg(vp, char *);
1086       topic = va_arg(vp, char *);
1087       usercount = va_arg(vp, int);
1088       
1089       if (status == SILC_STATUS_LIST_START ||
1090           status == SILC_STATUS_OK)
1091         printformat_module("fe-common/silc", server, NULL,
1092                            MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
1093
1094       if (!usercount)
1095         snprintf(users, sizeof(users) - 1, "N/A");
1096       else
1097         snprintf(users, sizeof(users) - 1, "%d", usercount);
1098       printformat_module("fe-common/silc", server, NULL,
1099                          MSGLEVEL_CRAP, SILCTXT_LIST,
1100                          name, users, topic ? topic : "");
1101     }
1102     break;
1103     
1104   case SILC_COMMAND_UMODE:
1105     {
1106       SilcUInt32 mode;
1107       
1108       if (!success)
1109         return;
1110       
1111       mode = va_arg(vp, SilcUInt32);
1112       
1113       if (mode & SILC_UMODE_SERVER_OPERATOR &&
1114           !(server->umode & SILC_UMODE_SERVER_OPERATOR))
1115         printformat_module("fe-common/silc", server, NULL,
1116                            MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1117
1118       if (mode & SILC_UMODE_ROUTER_OPERATOR &&
1119           !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
1120         printformat_module("fe-common/silc", server, NULL,
1121                            MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1122
1123       server->umode = mode;
1124       signal_emit("user mode changed", 2, server, NULL);
1125     }
1126     break;
1127     
1128   case SILC_COMMAND_OPER:
1129     if (!success)
1130       return;
1131
1132     server->umode |= SILC_UMODE_SERVER_OPERATOR;
1133     signal_emit("user mode changed", 2, server, NULL);
1134
1135     printformat_module("fe-common/silc", server, NULL,
1136                        MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1137     break;
1138     
1139   case SILC_COMMAND_SILCOPER:
1140     if (!success)
1141       return;
1142
1143     server->umode |= SILC_UMODE_ROUTER_OPERATOR;
1144     signal_emit("user mode changed", 2, server, NULL);
1145
1146     printformat_module("fe-common/silc", server, NULL,
1147                        MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1148     break;
1149     
1150   case SILC_COMMAND_USERS: 
1151     {
1152       SilcHashTableList htl;
1153       SilcChannelEntry channel;
1154       SilcChannelUser chu;
1155       
1156       if (!success)
1157         return;
1158       
1159       channel = va_arg(vp, SilcChannelEntry);
1160       
1161       printformat_module("fe-common/silc", server, channel->channel_name,
1162                          MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
1163                          channel->channel_name);
1164
1165       silc_hash_table_list(channel->user_list, &htl);
1166       while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1167         SilcClientEntry e = chu->client;
1168         char stat[5], *mode;
1169
1170         if (!e->nickname)
1171           continue;
1172         
1173         memset(stat, 0, sizeof(stat));
1174         mode = silc_client_chumode_char(chu->mode);
1175         if (e->mode & SILC_UMODE_GONE)
1176           strcat(stat, "G");
1177         else
1178           strcat(stat, "H");
1179         if (mode)
1180           strcat(stat, mode);
1181
1182         printformat_module("fe-common/silc", server, channel->channel_name,
1183                            MSGLEVEL_CRAP, SILCTXT_USERS,
1184                            e->nickname, stat, 
1185                            e->username ? e->username : "",
1186                            e->hostname ? e->hostname : "",
1187                            e->realname ? e->realname : "");
1188         if (mode)
1189           silc_free(mode);
1190       }
1191       silc_hash_table_list_reset(&htl);
1192     }
1193     break;
1194
1195   case SILC_COMMAND_BAN:
1196     {
1197       SilcChannelEntry channel;
1198       char *ban_list;
1199       
1200       if (!success)
1201         return;
1202       
1203       channel = va_arg(vp, SilcChannelEntry);
1204       ban_list = va_arg(vp, char *);
1205       
1206       if (ban_list)
1207         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1208                            SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
1209                            ban_list);
1210       else
1211         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1212                            SILCTXT_CHANNEL_NO_BAN_LIST, 
1213                            channel->channel_name);
1214     }
1215     break;
1216     
1217   case SILC_COMMAND_GETKEY:
1218     {
1219       SilcIdType id_type;
1220       void *entry;
1221       SilcPublicKey public_key;
1222       unsigned char *pk;
1223       SilcUInt32 pk_len;
1224       GetkeyContext getkey;
1225       char *name;
1226       
1227       if (!success)
1228         return;
1229       
1230       id_type = va_arg(vp, SilcUInt32);
1231       entry = va_arg(vp, void *);
1232       public_key = va_arg(vp, SilcPublicKey);
1233
1234       if (public_key) {
1235         pk = silc_pkcs_public_key_encode(public_key, &pk_len);
1236
1237         getkey = silc_calloc(1, sizeof(*getkey));
1238         getkey->entry = entry;
1239         getkey->id_type = id_type;
1240         getkey->client = client;
1241         getkey->conn = conn;
1242         getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1243
1244         name = (id_type == SILC_ID_CLIENT ? 
1245                 ((SilcClientEntry)entry)->nickname :
1246                 ((SilcServerEntry)entry)->server_name);
1247
1248         silc_verify_public_key_internal(client, conn, name,
1249                                         (id_type == SILC_ID_CLIENT ?
1250                                          SILC_SOCKET_TYPE_CLIENT :
1251                                          SILC_SOCKET_TYPE_SERVER),
1252                                         pk, pk_len, SILC_SKE_PK_TYPE_SILC,
1253                                         silc_getkey_cb, getkey);
1254         silc_free(pk);
1255       } else {
1256         printformat_module("fe-common/silc", server, NULL,
1257                            MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
1258       }
1259     }
1260     break;
1261
1262   case SILC_COMMAND_INFO:
1263     {
1264       SilcServerEntry server_entry;
1265       char *server_name;
1266       char *server_info;
1267
1268       if (!success)
1269         return;
1270       
1271       server_entry = va_arg(vp, SilcServerEntry);
1272       server_name = va_arg(vp, char *);
1273       server_info = va_arg(vp, char *);
1274
1275       if (server_name && server_info )
1276         {
1277           printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
1278           printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
1279         }
1280     }
1281     break;
1282     
1283   case SILC_COMMAND_TOPIC:
1284     {
1285       SilcChannelEntry channel;
1286       char *topic;
1287       
1288       if (!success)
1289         return;
1290       
1291       channel = va_arg(vp, SilcChannelEntry);
1292       topic = va_arg(vp, char *);
1293       
1294       if (topic) {
1295         chanrec = silc_channel_find_entry(server, channel);
1296         if (chanrec) {
1297           g_free_not_null(chanrec->topic);
1298           chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1299           signal_emit("channel topic changed", 1, chanrec);
1300         }
1301         printformat_module("fe-common/silc", server, channel->channel_name,
1302                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
1303                            channel->channel_name, topic);
1304       } else {
1305         printformat_module("fe-common/silc", server, channel->channel_name,
1306                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
1307                            channel->channel_name);
1308       }
1309     }
1310     break;
1311
1312   }
1313
1314   va_end(vp);
1315 }
1316
1317 typedef struct {
1318   SilcClient client;
1319   SilcClientConnection conn;
1320   char *filename;
1321   char *entity;
1322   char *entity_name;
1323   unsigned char *pk;
1324   SilcUInt32 pk_len;
1325   SilcSKEPKType pk_type;
1326   SilcVerifyPublicKey completion;
1327   void *context;
1328 } *PublicKeyVerify;
1329
1330 static void verify_public_key_completion(const char *line, void *context)
1331 {
1332   PublicKeyVerify verify = (PublicKeyVerify)context;
1333
1334   if (line[0] == 'Y' || line[0] == 'y') {
1335     /* Call the completion */
1336     if (verify->completion)
1337       verify->completion(TRUE, verify->context);
1338
1339     /* Save the key for future checking */
1340     silc_pkcs_save_public_key_data(verify->filename, verify->pk, 
1341                                    verify->pk_len, SILC_PKCS_FILE_PEM);
1342   } else {
1343     /* Call the completion */
1344     if (verify->completion)
1345       verify->completion(FALSE, verify->context);
1346
1347     printformat_module("fe-common/silc", NULL, NULL,
1348                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, 
1349                        verify->entity_name ? verify->entity_name :
1350                        verify->entity);
1351   }
1352
1353   silc_free(verify->filename);
1354   silc_free(verify->entity);
1355   silc_free(verify->entity_name);
1356   silc_free(verify->pk);
1357   silc_free(verify);
1358 }
1359
1360 /* Internal routine to verify public key. If the `completion' is provided
1361    it will be called to indicate whether public was verified or not. For
1362    server/router public key this will check for filename that includes the
1363    remote host's IP address and remote host's hostname. */
1364
1365 static void 
1366 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
1367                                 const char *name, SilcSocketType conn_type, 
1368                                 unsigned char *pk, SilcUInt32 pk_len, 
1369                                 SilcSKEPKType pk_type,
1370                                 SilcVerifyPublicKey completion, void *context)
1371 {
1372   int i;
1373   char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
1374   char *fingerprint, *babbleprint, *format;
1375   struct passwd *pw;
1376   struct stat st;
1377   char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
1378                    conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
1379                   "server" : "client");
1380   PublicKeyVerify verify;
1381
1382   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
1383     printformat_module("fe-common/silc", NULL, NULL,
1384                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED, 
1385                        entity, pk_type);
1386     if (completion)
1387       completion(FALSE, context);
1388     return;
1389   }
1390
1391   pw = getpwuid(getuid());
1392   if (!pw) {
1393     if (completion)
1394       completion(FALSE, context);
1395     return;
1396   }
1397
1398   memset(filename, 0, sizeof(filename));
1399   memset(filename2, 0, sizeof(filename2));
1400   memset(file, 0, sizeof(file));
1401
1402   if (conn_type == SILC_SOCKET_TYPE_SERVER ||
1403       conn_type == SILC_SOCKET_TYPE_ROUTER) {
1404     if (!name) {
1405       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1406                conn->sock->ip, conn->sock->port);
1407       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
1408                get_irssi_dir(), entity, file);
1409       
1410       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1411                conn->sock->hostname, conn->sock->port);
1412       snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s", 
1413                get_irssi_dir(), entity, file);
1414       
1415       ipf = filename;
1416       hostf = filename2;
1417     } else {
1418       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1419                name, conn->sock->port);
1420       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
1421                get_irssi_dir(), entity, file);
1422       
1423       ipf = filename;
1424     }
1425   } else {
1426     /* Replace all whitespaces with `_'. */
1427     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1428     for (i = 0; i < strlen(fingerprint); i++)
1429       if (fingerprint[i] == ' ')
1430         fingerprint[i] = '_';
1431     
1432     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
1433     snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
1434              get_irssi_dir(), entity, file);
1435     silc_free(fingerprint);
1436
1437     ipf = filename;
1438   }
1439
1440   /* Take fingerprint of the public key */
1441   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1442   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
1443
1444   verify = silc_calloc(1, sizeof(*verify));
1445   verify->client = client;
1446   verify->conn = conn;
1447   verify->filename = strdup(ipf);
1448   verify->entity = strdup(entity);
1449   verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ?
1450                          (name ? strdup(name) : strdup(conn->sock->hostname))
1451                          : NULL);
1452   verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
1453   memcpy(verify->pk, pk, pk_len);
1454   verify->pk_len = pk_len;
1455   verify->pk_type = pk_type;
1456   verify->completion = completion;
1457   verify->context = context;
1458
1459   /* Check whether this key already exists */
1460   if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
1461     /* Key does not exist, ask user to verify the key and save it */
1462
1463     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1464                        SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1465                        verify->entity_name : entity);
1466     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1467                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1468     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1469                        SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1470     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1471                              SILCTXT_PUBKEY_ACCEPT);
1472     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1473                             format, 0, verify);
1474     g_free(format);
1475     silc_free(fingerprint);
1476     return;
1477   } else {
1478     /* The key already exists, verify it. */
1479     SilcPublicKey public_key;
1480     unsigned char *encpk;
1481     SilcUInt32 encpk_len;
1482
1483     /* Load the key file, try for both IP filename and hostname filename */
1484     if (!silc_pkcs_load_public_key(ipf, &public_key, 
1485                                    SILC_PKCS_FILE_PEM) &&
1486         !silc_pkcs_load_public_key(ipf, &public_key, 
1487                                    SILC_PKCS_FILE_BIN) &&
1488         (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key, 
1489                                                SILC_PKCS_FILE_PEM) &&
1490                     !silc_pkcs_load_public_key(hostf, &public_key, 
1491                                                SILC_PKCS_FILE_BIN)))) {
1492       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1493                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1494                          verify->entity_name : entity);
1495       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1496                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1497       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1498                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1499       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1500                          SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
1501       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1502                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
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     }
1509
1510     /* Encode the key data */
1511     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
1512     if (!encpk) {
1513       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1514                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1515                          verify->entity_name : entity);
1516       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1517                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1518       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1519                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1520       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1521                          SILCTXT_PUBKEY_MALFORMED, entity);
1522       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1523                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1524       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1525                               format, 0, verify);
1526       g_free(format);
1527       silc_free(fingerprint);
1528       return;
1529     }
1530
1531     /* Compare the keys */
1532     if (memcmp(encpk, pk, encpk_len)) {
1533       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1534                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1535                          verify->entity_name : entity);
1536       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1537                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1538       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1539                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1540       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1541                          SILCTXT_PUBKEY_NO_MATCH, entity);
1542       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1543                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
1544       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1545                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
1546
1547       /* Ask user to verify the key and save it */
1548       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1549                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1550       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1551                               format, 0, verify);
1552       g_free(format);
1553       silc_free(fingerprint);
1554       return;
1555     }
1556
1557     /* Local copy matched */
1558     if (completion)
1559       completion(TRUE, context);
1560     silc_free(fingerprint);
1561   }
1562 }
1563
1564 /* Verifies received public key. The `conn_type' indicates which entity
1565    (server, client etc.) has sent the public key. If user decides to trust
1566    the key may be saved as trusted public key for later use. The 
1567    `completion' must be called after the public key has been verified. */
1568
1569 void 
1570 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
1571                        SilcSocketType conn_type, unsigned char *pk, 
1572                        SilcUInt32 pk_len, SilcSKEPKType pk_type,
1573                        SilcVerifyPublicKey completion, void *context)
1574 {
1575   silc_verify_public_key_internal(client, conn, NULL, conn_type, pk,
1576                                   pk_len, pk_type,
1577                                   completion, context);
1578 }
1579
1580 /* Asks passphrase from user on the input line. */
1581
1582 typedef struct {
1583   SilcAskPassphrase completion;
1584   void *context;
1585 } *AskPassphrase;
1586
1587 void ask_passphrase_completion(const char *passphrase, void *context)
1588 {
1589   AskPassphrase p = (AskPassphrase)context;
1590   p->completion((unsigned char *)passphrase, 
1591                 passphrase ? strlen(passphrase) : 0, p->context);
1592   silc_free(p);
1593 }
1594
1595 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1596                          SilcAskPassphrase completion, void *context)
1597 {
1598   AskPassphrase p = silc_calloc(1, sizeof(*p));
1599   p->completion = completion;
1600   p->context = context;
1601
1602   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
1603                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
1604 }
1605
1606 typedef struct {
1607   SilcGetAuthMeth completion;
1608   void *context;
1609 } *InternalGetAuthMethod;
1610
1611 /* Callback called when we've received the authentication method information
1612    from the server after we've requested it. This will get the authentication
1613    data from the user if needed. */
1614
1615 static void silc_get_auth_method_callback(SilcClient client,
1616                                           SilcClientConnection conn,
1617                                           SilcAuthMethod auth_meth,
1618                                           void *context)
1619 {
1620   InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
1621
1622   SILC_LOG_DEBUG(("Start"));
1623
1624   switch (auth_meth) {
1625   case SILC_AUTH_NONE:
1626     /* No authentication required. */
1627     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1628     break;
1629   case SILC_AUTH_PASSWORD:
1630     /* Do not ask the passphrase from user, the library will ask it if
1631        we do not provide it here. */
1632     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1633     break;
1634   case SILC_AUTH_PUBLIC_KEY:
1635     /* Do not get the authentication data now, the library will generate
1636        it using our default key, if we do not provide it here. */
1637     /* XXX In the future when we support multiple local keys and multiple
1638        local certificates we will need to ask from user which one to use. */
1639     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1640     break;
1641   }
1642
1643   silc_free(internal);
1644 }
1645
1646 /* Find authentication method and authentication data by hostname and
1647    port. The hostname may be IP address as well. The found authentication
1648    method and authentication data is returned to `auth_meth', `auth_data'
1649    and `auth_data_len'. The function returns TRUE if authentication method
1650    is found and FALSE if not. `conn' may be NULL. */
1651
1652 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1653                           char *hostname, SilcUInt16 port,
1654                           SilcGetAuthMeth completion, void *context)
1655 {
1656   InternalGetAuthMethod internal;
1657
1658   SILC_LOG_DEBUG(("Start"));
1659
1660   /* XXX must resolve from configuration whether this connection has
1661      any specific authentication data */
1662
1663   /* If we do not have this connection configured by the user in a
1664      configuration file then resolve the authentication method from the
1665      server for this session. */
1666   internal = silc_calloc(1, sizeof(*internal));
1667   internal->completion = completion;
1668   internal->context = context;
1669
1670   silc_client_request_authentication_method(client, conn, 
1671                                             silc_get_auth_method_callback,
1672                                             internal);
1673 }
1674
1675 /* Notifies application that failure packet was received.  This is called
1676    if there is some protocol active in the client.  The `protocol' is the
1677    protocol context.  The `failure' is opaque pointer to the failure
1678    indication.  Note, that the `failure' is protocol dependant and application
1679    must explicitly cast it to correct type.  Usually `failure' is 32 bit
1680    failure type (see protocol specs for all protocol failure types). */
1681
1682 void silc_failure(SilcClient client, SilcClientConnection conn, 
1683                   SilcProtocol protocol, void *failure)
1684 {
1685   SILC_LOG_DEBUG(("Start"));
1686
1687   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1688     SilcSKEStatus status = (SilcSKEStatus)failure;
1689     
1690     if (status == SILC_SKE_STATUS_BAD_VERSION)
1691       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1692                          SILCTXT_KE_BAD_VERSION);
1693     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1694       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1695                          SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1696     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1697       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1698                          SILCTXT_KE_UNKNOWN_GROUP);
1699     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1700       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1701                          SILCTXT_KE_UNKNOWN_CIPHER);
1702     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1703       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1704                          SILCTXT_KE_UNKNOWN_PKCS);
1705     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1706       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1707                          SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1708     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1709       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1710                          SILCTXT_KE_UNKNOWN_HMAC);
1711     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1712       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1713                          SILCTXT_KE_INCORRECT_SIGNATURE);
1714     if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1715       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1716                          SILCTXT_KE_INVALID_COOKIE);
1717   }
1718
1719   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1720     SilcUInt32 err = (SilcUInt32)failure;
1721
1722     if (err == SILC_AUTH_FAILED)
1723       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1724                          SILCTXT_AUTH_FAILED);
1725   }
1726 }
1727
1728 /* Asks whether the user would like to perform the key agreement protocol.
1729    This is called after we have received an key agreement packet or an
1730    reply to our key agreement packet. This returns TRUE if the user wants
1731    the library to perform the key agreement protocol and FALSE if it is not
1732    desired (application may start it later by calling the function
1733    silc_client_perform_key_agreement). */
1734
1735 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1736                        SilcClientEntry client_entry, const char *hostname,
1737                        SilcUInt16 port, SilcKeyAgreementCallback *completion,
1738                        void **context)
1739 {
1740   char portstr[12];
1741
1742   SILC_LOG_DEBUG(("Start"));
1743
1744   /* We will just display the info on the screen and return FALSE and user
1745      will have to start the key agreement with a command. */
1746
1747   if (hostname) 
1748     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1749
1750   if (!hostname)
1751     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1752                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1753   else
1754     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1755                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST, 
1756                        client_entry->nickname, hostname, portstr);
1757
1758   *completion = NULL;
1759   *context = NULL;
1760
1761   return FALSE;
1762 }
1763
1764 void silc_ftp(SilcClient client, SilcClientConnection conn,
1765               SilcClientEntry client_entry, SilcUInt32 session_id,
1766               const char *hostname, SilcUInt16 port)
1767 {
1768   SILC_SERVER_REC *server;
1769   char portstr[12];
1770   FtpSession ftp = silc_calloc(1, sizeof(*ftp));
1771
1772   SILC_LOG_DEBUG(("Start"));
1773
1774   server = conn->context;
1775
1776   ftp->client_entry = client_entry;
1777   ftp->session_id = session_id;
1778   ftp->send = FALSE;
1779   ftp->conn = conn;
1780   silc_dlist_add(server->ftp_sessions, ftp);
1781   server->current_session = ftp;
1782
1783   if (hostname) 
1784     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1785
1786   if (!hostname)
1787     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1788                        SILCTXT_FILE_REQUEST, client_entry->nickname);
1789   else
1790     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1791                        SILCTXT_FILE_REQUEST_HOST, 
1792                        client_entry->nickname, hostname, portstr);
1793 }
1794
1795 /* SILC client operations */
1796 SilcClientOperations ops = {
1797   silc_say,
1798   silc_channel_message,
1799   silc_private_message,
1800   silc_notify,
1801   silc_command,
1802   silc_command_reply,
1803   silc_connect,
1804   silc_disconnect,
1805   silc_get_auth_method,
1806   silc_verify_public_key,
1807   silc_ask_passphrase,
1808   silc_failure,
1809   silc_key_agreement,
1810   silc_ftp,
1811 };