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