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