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