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