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