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