updates.
[silc.git] / apps / irssi / src / silc / core / client_ops.c
1 /*
2
3   client_ops.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 2001 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20
21 #include "module.h"
22 #include "chat-protocols.h"
23 #include "args.h"
24
25 #include "chatnets.h"
26 #include "servers-setup.h"
27 #include "channels-setup.h"
28 #include "silc-servers.h"
29 #include "silc-channels.h"
30 #include "silc-queries.h"
31 #include "silc-nicklist.h"
32
33 #include "signals.h"
34 #include "levels.h"
35 #include "settings.h"
36 #include "fe-common/core/printtext.h"
37 #include "fe-common/core/fe-channels.h"
38 #include "fe-common/core/keyboard.h"
39 #include "fe-common/silc/module-formats.h"
40
41 static void 
42 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
43                                 SilcSocketType conn_type, unsigned char *pk, 
44                                 uint32 pk_len, SilcSKEPKType pk_type,
45                                 SilcVerifyPublicKey completion, void *context);
46
47 void silc_say(SilcClient client, SilcClientConnection conn,
48               SilcClientMessageType type, char *msg, ...)
49 {
50   SILC_SERVER_REC *server;
51   va_list va;
52   char *str;
53
54   server = conn == NULL ? NULL : conn->context;
55   
56   va_start(va, msg);
57   str = g_strdup_vprintf(msg, va);
58   printtext(server, NULL, MSGLEVEL_CRAP, "%s", str);
59   g_free(str);
60   va_end(va);
61 }
62
63 void silc_say_error(char *msg, ...)
64 {
65   va_list va;
66   char *str;
67
68   va_start(va, msg);
69   str = g_strdup_vprintf(msg, va);
70   printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str);
71
72   g_free(str);
73   va_end(va);
74 }
75
76 /* Message for a channel. The `sender' is the nickname of the sender 
77    received in the packet. The `channel_name' is the name of the channel. */
78
79 void silc_channel_message(SilcClient client, SilcClientConnection conn,
80                           SilcClientEntry sender, SilcChannelEntry channel,
81                           SilcMessageFlags flags, char *msg)
82 {
83   SILC_SERVER_REC *server;
84   SILC_NICK_REC *nick;
85   SILC_CHANNEL_REC *chanrec;
86   
87   server = conn == NULL ? NULL : conn->context;
88   chanrec = silc_channel_find_entry(server, channel);
89   if (!chanrec)
90     return;
91   
92   nick = silc_nicklist_find(chanrec, sender);
93
94   if (flags & SILC_MESSAGE_FLAG_ACTION)
95     printformat_module("fe-common/silc", server, channel->channel_name,
96                        MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_ACTION, 
97                        nick == NULL ? "[<unknown>]" : nick->nick, msg);
98   else if (flags & SILC_MESSAGE_FLAG_NOTICE)
99     printformat_module("fe-common/silc", server, channel->channel_name,
100                        MSGLEVEL_NOTICES, SILCTXT_CHANNEL_NOTICE, 
101                        nick == NULL ? "[<unknown>]" : nick->nick, msg);
102   else
103     signal_emit("message public", 6, server, msg,
104                 nick == NULL ? "[<unknown>]" : nick->nick,
105                 nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
106                 chanrec->name, nick);
107 }
108
109 /* Private message to the client. The `sender' is the nickname of the
110    sender received in the packet. */
111
112 void silc_private_message(SilcClient client, SilcClientConnection conn,
113                           SilcClientEntry sender, SilcMessageFlags flags,
114                           char *msg)
115 {
116   SILC_SERVER_REC *server;
117   
118   server = conn == NULL ? NULL : conn->context;
119   signal_emit("message private", 4, server, msg,
120               sender->nickname ? sender->nickname : "[<unknown>]",
121               sender->username ? sender->username : NULL);
122 }
123
124 /* Notify message to the client. The notify arguments are sent in the
125    same order as servers sends them. The arguments are same as received
126    from the server except for ID's.  If ID is received application receives
127    the corresponding entry to the ID. For example, if Client ID is received
128    application receives SilcClientEntry.  Also, if the notify type is
129    for channel the channel entry is sent to application (even if server
130    does not send it). */
131
132 typedef struct {
133   int type;
134   const char *name;
135 } NOTIFY_REC;
136
137 #define MAX_NOTIFY (sizeof(notifies)/sizeof(notifies[0]))
138 static NOTIFY_REC notifies[] = {
139   { SILC_NOTIFY_TYPE_NONE,              NULL },
140   { SILC_NOTIFY_TYPE_INVITE,            "invite" },
141   { SILC_NOTIFY_TYPE_JOIN,              "join" },
142   { SILC_NOTIFY_TYPE_LEAVE,             "leave" },
143   { SILC_NOTIFY_TYPE_SIGNOFF,           "signoff" },
144   { SILC_NOTIFY_TYPE_TOPIC_SET,         "topic" },
145   { SILC_NOTIFY_TYPE_NICK_CHANGE,       "nick" },
146   { SILC_NOTIFY_TYPE_CMODE_CHANGE,      "cmode" },
147   { SILC_NOTIFY_TYPE_CUMODE_CHANGE,     "cumode" },
148   { SILC_NOTIFY_TYPE_MOTD,              "motd" },
149   { SILC_NOTIFY_TYPE_CHANNEL_CHANGE,    "channel_change" },
150   { SILC_NOTIFY_TYPE_SERVER_SIGNOFF,    "server_signoff" },
151   { SILC_NOTIFY_TYPE_KICKED,            "kick" },
152   { SILC_NOTIFY_TYPE_KILLED,            "kill" },
153   { SILC_NOTIFY_TYPE_UMODE_CHANGE,      "umode" },
154   { SILC_NOTIFY_TYPE_BAN,               "ban" },
155 };
156
157 void silc_notify(SilcClient client, SilcClientConnection conn,
158                  SilcNotifyType type, ...)
159 {
160   SILC_SERVER_REC *server;
161   va_list va;
162   
163   server = conn == NULL ? NULL : conn->context;
164   va_start(va, type);
165   
166   if (type == SILC_NOTIFY_TYPE_NONE) {
167     /* Some generic notice from server */
168     printtext(server, NULL, MSGLEVEL_CRAP, "%s", (char *)va_arg(va, char *));
169   } else if (type < MAX_NOTIFY) {
170     /* Send signal about the notify event */
171     char signal[50];
172     g_snprintf(signal, sizeof(signal), "silc event %s", notifies[type].name);
173     signal_emit(signal, 2, server, va);
174   } else {
175     /* Unknown notify */
176     printformat_module("fe-common/silc", server, NULL,
177                        MSGLEVEL_CRAP, SILCTXT_UNKNOWN_NOTIFY, type);
178   }
179
180   va_end(va);
181 }
182
183 /* Called to indicate that connection was either successfully established
184    or connecting failed.  This is also the first time application receives
185    the SilcClientConnection objecet which it should save somewhere. */
186
187 void silc_connect(SilcClient client, SilcClientConnection conn, int success)
188 {
189   SILC_SERVER_REC *server = conn->context;
190
191   if (!server && !success) {
192     silc_client_close_connection(client, NULL, conn);
193     return;
194   }
195
196   if (success) {
197     server->connected = TRUE;
198     signal_emit("event connected", 1, server);
199   } else {
200     server->connection_lost = TRUE;
201     server->conn->context = NULL;
202     server_disconnect(SERVER(server));
203   }
204 }
205
206 /* Called to indicate that connection was disconnected to the server. */
207
208 void silc_disconnect(SilcClient client, SilcClientConnection conn)
209 {
210   SILC_SERVER_REC *server = conn->context;
211
212   server->conn->context = NULL;
213   server->conn = NULL;
214   server->connection_lost = TRUE;
215   server_disconnect(SERVER(server));
216 }
217
218 /* Command handler. This function is called always in the command function.
219    If error occurs it will be called as well. `conn' is the associated
220    client connection. `cmd_context' is the command context that was
221    originally sent to the command. `success' is FALSE if error occured
222    during command. `command' is the command being processed. It must be
223    noted that this is not reply from server. This is merely called just
224    after application has called the command. Just to tell application
225    that the command really was processed. */
226
227 void silc_command(SilcClient client, SilcClientConnection conn, 
228                   SilcClientCommandContext cmd_context, int success,
229                   SilcCommand command)
230 {
231   SILC_SERVER_REC *server = conn->context;
232
233   if (!success)
234     return;
235
236   switch(command) {
237   case SILC_COMMAND_INVITE:
238     printformat_module("fe-common/silc", server, NULL,
239                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING,
240                        cmd_context->argv[2], 
241                        (cmd_context->argv[1][0] == '*' ?
242                         (char *)conn->current_channel->channel_name :
243                         (char *)cmd_context->argv[1]));
244     break;
245   default:
246     break;
247   }
248 }
249
250 /* Client info resolving callback when JOIN command reply is received.
251    This will cache all users on the channel. */
252
253 static void silc_client_join_get_users(SilcClient client,
254                                        SilcClientConnection conn,
255                                        SilcClientEntry *clients,
256                                        uint32 clients_count,
257                                        void *context)
258 {
259   SilcChannelEntry channel = (SilcChannelEntry)context;
260   SilcChannelUser chu;
261   SILC_SERVER_REC *server = conn->context;
262   SILC_CHANNEL_REC *chanrec;
263   SilcClientEntry founder = NULL;
264   NICK_REC *ownnick;
265
266   if (!clients)
267     return;
268
269   chanrec = silc_channel_find(server, channel->channel_name);
270   if (chanrec == NULL)
271     return;
272
273   silc_list_start(channel->clients);
274   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
275     if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
276       founder = chu->client;
277     silc_nicklist_insert(chanrec, chu, FALSE);
278   }
279
280   ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
281   nicklist_set_own(CHANNEL(chanrec), ownnick);
282   signal_emit("channel joined", 1, chanrec);
283
284   if (chanrec->topic)
285     printformat_module("fe-common/silc", server, channel->channel_name,
286                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
287                        channel->channel_name, chanrec->topic);
288
289   fe_channels_nicklist(CHANNEL(chanrec), CHANNEL_NICKLIST_FLAG_ALL);
290
291   if (founder) {
292     if (founder == conn->local_entry)
293       printformat_module("fe-common/silc", 
294                          server, channel->channel_name, MSGLEVEL_CRAP,
295                          SILCTXT_CHANNEL_FOUNDER_YOU,
296                          channel->channel_name);
297     else
298       printformat_module("fe-common/silc", 
299                          server, channel->channel_name, MSGLEVEL_CRAP,
300                          SILCTXT_CHANNEL_FOUNDER,
301                          channel->channel_name, founder->nickname);
302   }
303 }
304
305 /* Command reply handler. This function is called always in the command reply
306    function. If error occurs it will be called as well. Normal scenario
307    is that it will be called after the received command data has been parsed
308    and processed. The function is used to pass the received command data to
309    the application. 
310
311    `conn' is the associated client connection. `cmd_payload' is the command
312    payload data received from server and it can be ignored. It is provided
313    if the application would like to re-parse the received command data,
314    however, it must be noted that the data is parsed already by the library
315    thus the payload can be ignored. `success' is FALSE if error occured.
316    In this case arguments are not sent to the application. `command' is the
317    command reply being processed. The function has variable argument list
318    and each command defines the number and type of arguments it passes to the
319    application (on error they are not sent). */
320
321 void 
322 silc_command_reply(SilcClient client, SilcClientConnection conn,
323                    SilcCommandPayload cmd_payload, int success,
324                    SilcCommand command, SilcCommandStatus status, ...)
325
326 {
327   SILC_SERVER_REC *server = conn->context;
328   SILC_CHANNEL_REC *chanrec;
329   va_list vp;
330
331   va_start(vp, status);
332
333   switch(command) {
334   case SILC_COMMAND_WHOIS:
335     {
336       char buf[1024], *nickname, *username, *realname;
337       uint32 idle, mode;
338       SilcBuffer channels;
339       
340       if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
341           status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
342         char *tmp;
343         tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
344                                          3, NULL);
345         if (tmp)
346           silc_say_error("%s: %s", tmp, 
347                          silc_client_command_status_message(status));
348         else
349           silc_say_error("%s", silc_client_command_status_message(status));
350         break;
351       }
352       
353       if (!success)
354         return;
355       
356       (void)va_arg(vp, SilcClientEntry);
357       nickname = va_arg(vp, char *);
358       username = va_arg(vp, char *);
359       realname = va_arg(vp, char *);
360       channels = va_arg(vp, SilcBuffer);
361       mode = va_arg(vp, uint32);
362       idle = va_arg(vp, uint32);
363       
364       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
365                          SILCTXT_WHOIS_USERINFO, nickname, username, 
366                          realname);
367
368       if (channels) {
369         SilcDList list = silc_channel_payload_parse_list(channels);
370         if (list) {
371           SilcChannelPayload entry;
372           memset(buf, 0, sizeof(buf));
373           silc_dlist_start(list);
374           while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
375             char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
376             uint32 name_len;
377             char *name = silc_channel_get_name(entry, &name_len);
378             
379             if (m)
380               strncat(buf, m, strlen(m));
381             strncat(buf, name, name_len);
382             strncat(buf, " ", 1);
383             silc_free(m);
384           }
385
386           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
387                              SILCTXT_WHOIS_CHANNELS, buf);
388           silc_channel_payload_list_free(list);
389         }
390       }
391       
392       if (mode) {
393         memset(buf, 0, sizeof(buf));
394
395         if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
396             (mode & SILC_UMODE_ROUTER_OPERATOR)) {
397           strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
398                  "Server Operator " :
399                  (mode & SILC_UMODE_ROUTER_OPERATOR) ?
400                  "SILC Operator " : "[Unknown mode] ");
401         }
402         if (mode & SILC_UMODE_GONE)
403           strcat(buf, "away");
404
405         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
406                            SILCTXT_WHOIS_MODES, buf);
407       }
408       
409       if (idle && nickname) {
410         memset(buf, 0, sizeof(buf));
411         snprintf(buf, sizeof(buf) - 1, "%lu %s",
412                  idle > 60 ? (idle / 60) : idle,
413                  idle > 60 ? "minutes" : "seconds");
414
415         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
416                            SILCTXT_WHOIS_IDLE, buf);
417       }
418     }
419     break;
420     
421   case SILC_COMMAND_WHOWAS:
422     {
423       char *nickname, *username, *realname;
424       
425       if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
426           status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
427         char *tmp;
428         tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
429                                          3, NULL);
430         if (tmp)
431           silc_say_error("%s: %s", tmp, 
432                          silc_client_command_status_message(status));
433         else
434           silc_say_error("%s", silc_client_command_status_message(status));
435         break;
436       }
437       
438       if (!success)
439         return;
440       
441       (void)va_arg(vp, SilcClientEntry);
442       nickname = va_arg(vp, char *);
443       username = va_arg(vp, char *);
444       realname = va_arg(vp, char *);
445       
446       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
447                          SILCTXT_WHOWAS_USERINFO, nickname, username, 
448                          realname ? realname : "");
449     }
450     break;
451     
452   case SILC_COMMAND_INVITE:
453     {
454       SilcChannelEntry channel;
455       char *invite_list;
456       SilcArgumentPayload args;
457       int argc = 0;
458       
459       if (!success)
460         return;
461       
462       channel = va_arg(vp, SilcChannelEntry);
463       invite_list = va_arg(vp, char *);
464
465       args = silc_command_get_args(cmd_payload);
466       if (args)
467         argc = silc_argument_get_arg_num(args);
468
469       if (invite_list)
470         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
471                            SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
472                            invite_list);
473       else if (argc == 3)
474         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
475                            SILCTXT_CHANNEL_NO_INVITE_LIST, 
476                            channel->channel_name);
477     }
478     break;
479
480   case SILC_COMMAND_JOIN: 
481     {
482       char *channel, *mode, *topic;
483       uint32 modei;
484       SilcChannelEntry channel_entry;
485       SilcBuffer client_id_list;
486       uint32 list_count;
487
488       if (!success)
489         return;
490
491       channel = va_arg(vp, char *);
492       channel_entry = va_arg(vp, SilcChannelEntry);
493       modei = va_arg(vp, uint32);
494       (void)va_arg(vp, uint32);
495       (void)va_arg(vp, unsigned char *);
496       (void)va_arg(vp, unsigned char *);
497       (void)va_arg(vp, unsigned char *);
498       topic = va_arg(vp, char *);
499       (void)va_arg(vp, unsigned char *);
500       list_count = va_arg(vp, uint32);
501       client_id_list = va_arg(vp, SilcBuffer);
502
503       chanrec = silc_channel_find(server, channel);
504       if (!chanrec)
505         chanrec = silc_channel_create(server, channel, TRUE);
506
507       if (topic) {
508         g_free_not_null(chanrec->topic);
509         chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
510         signal_emit("channel topic changed", 1, chanrec);
511       }
512
513       mode = silc_client_chmode(modei, 
514                                 channel_entry->channel_key->cipher->name,
515                                 channel_entry->hmac->hmac->name);
516       g_free_not_null(chanrec->mode);
517       chanrec->mode = g_strdup(mode == NULL ? "" : mode);
518       signal_emit("channel mode changed", 1, chanrec);
519
520       /* Resolve the client information */
521       silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
522                                       silc_client_join_get_users, 
523                                       channel_entry);
524       break;
525     }
526
527   case SILC_COMMAND_NICK: 
528     {
529       SilcClientEntry client = va_arg(vp, SilcClientEntry);
530       char *old;
531       
532       if (!success)
533         return;
534
535       old = g_strdup(server->nick);
536       server_change_nick(SERVER(server), client->nickname);
537       nicklist_rename_unique(SERVER(server),
538                              server->conn->local_entry, server->nick,
539                              client, client->nickname);
540       
541       signal_emit("message own_nick", 4, server, server->nick, old, "");
542       g_free(old);
543       break;
544     }
545     
546   case SILC_COMMAND_LIST:
547     {
548       char *topic, *name;
549       int usercount;
550       char users[20];
551       
552       if (!success)
553         return;
554       
555       (void)va_arg(vp, SilcChannelEntry);
556       name = va_arg(vp, char *);
557       topic = va_arg(vp, char *);
558       usercount = va_arg(vp, int);
559       
560       if (status == SILC_STATUS_LIST_START ||
561           status == SILC_STATUS_OK)
562         printformat_module("fe-common/silc", server, NULL,
563                            MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
564
565       snprintf(users, sizeof(users) - 1, "%d", usercount);
566       printformat_module("fe-common/silc", server, NULL,
567                          MSGLEVEL_CRAP, SILCTXT_LIST,
568                          name, users, topic ? topic : "");
569     }
570     break;
571     
572   case SILC_COMMAND_UMODE:
573     {
574       uint32 mode;
575       
576       if (!success)
577         return;
578       
579       mode = va_arg(vp, uint32);
580       
581       if (mode & SILC_UMODE_SERVER_OPERATOR)
582         printformat_module("fe-common/silc", server, NULL,
583                            MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
584
585       if (mode & SILC_UMODE_ROUTER_OPERATOR)
586         printformat_module("fe-common/silc", server, NULL,
587                            MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
588     }
589     break;
590     
591   case SILC_COMMAND_OPER:
592     if (!success)
593       return;
594
595     printformat_module("fe-common/silc", server, NULL,
596                        MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
597     break;
598     
599   case SILC_COMMAND_SILCOPER:
600     if (!success)
601       return;
602
603     printformat_module("fe-common/silc", server, NULL,
604                        MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
605     break;
606     
607   case SILC_COMMAND_USERS: 
608     {
609       SilcChannelEntry channel;
610       SilcChannelUser chu;
611       
612       if (!success)
613         return;
614       
615       channel = va_arg(vp, SilcChannelEntry);
616       
617       printformat_module("fe-common/silc", server, channel->channel_name,
618                          MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
619                          channel->channel_name);
620
621       silc_list_start(channel->clients);
622       while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
623         SilcClientEntry e = chu->client;
624         char stat[5], *mode;
625         
626         memset(stat, 0, sizeof(stat));
627         mode = silc_client_chumode_char(chu->mode);
628         if (e->mode & SILC_UMODE_GONE)
629           strcat(stat, "G");
630         else
631           strcat(stat, "H");
632         if (mode)
633           strcat(stat, mode);
634
635         printformat_module("fe-common/silc", server, channel->channel_name,
636                            MSGLEVEL_CRAP, SILCTXT_USERS,
637                            e->nickname, stat, e->username, 
638                            e->realname ? e->realname : "");
639         if (mode)
640           silc_free(mode);
641       }
642     }
643     break;
644
645   case SILC_COMMAND_BAN:
646     {
647       SilcChannelEntry channel;
648       char *ban_list;
649       
650       if (!success)
651         return;
652       
653       channel = va_arg(vp, SilcChannelEntry);
654       ban_list = va_arg(vp, char *);
655       
656       if (ban_list)
657         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
658                            SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
659                            ban_list);
660       else
661         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
662                            SILCTXT_CHANNEL_NO_BAN_LIST, 
663                            channel->channel_name);
664     }
665     break;
666     
667   case SILC_COMMAND_GETKEY:
668     {
669       SilcIdType id_type;
670       void *entry;
671       SilcPublicKey public_key;
672       unsigned char *pk;
673       uint32 pk_len;
674       
675       if (!success)
676         return;
677       
678       id_type = va_arg(vp, uint32);
679       entry = va_arg(vp, void *);
680       public_key = va_arg(vp, SilcPublicKey);
681       
682       pk = silc_pkcs_public_key_encode(public_key, &pk_len);
683       
684       silc_verify_public_key_internal(client, conn, 
685                                       (id_type == SILC_ID_CLIENT ?
686                                        SILC_SOCKET_TYPE_CLIENT :
687                                        SILC_SOCKET_TYPE_SERVER),
688                                       pk, pk_len, SILC_SKE_PK_TYPE_SILC,
689                                       NULL, NULL);
690       silc_free(pk);
691     }
692     break;
693     
694   case SILC_COMMAND_TOPIC:
695     {
696       SilcChannelEntry channel;
697       char *topic;
698       
699       if (!success)
700         return;
701       
702       channel = va_arg(vp, SilcChannelEntry);
703       topic = va_arg(vp, char *);
704       
705       if (topic) {
706         chanrec = silc_channel_find_entry(server, channel);
707         if (chanrec) {
708           g_free_not_null(chanrec->topic);
709           chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
710           signal_emit("channel topic changed", 1, chanrec);
711         }
712         printformat_module("fe-common/silc", server, channel->channel_name,
713                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
714                            channel->channel_name, topic);
715       } else {
716         printformat_module("fe-common/silc", server, channel->channel_name,
717                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
718                            channel->channel_name);
719       }
720     }
721     break;
722
723   }
724
725   va_end(vp);
726 }
727
728 /* Internal routine to verify public key. If the `completion' is provided
729    it will be called to indicate whether public was verified or not. */
730
731 typedef struct {
732   SilcClient client;
733   SilcClientConnection conn;
734   char *filename;
735   char *entity;
736   unsigned char *pk;
737   uint32 pk_len;
738   SilcSKEPKType pk_type;
739   SilcVerifyPublicKey completion;
740   void *context;
741 } *PublicKeyVerify;
742
743 static void verify_public_key_completion(const char *line, void *context)
744 {
745   PublicKeyVerify verify = (PublicKeyVerify)context;
746
747   if (line[0] == 'Y' || line[0] == 'y') {
748     /* Call the completion */
749     if (verify->completion)
750       verify->completion(TRUE, verify->context);
751
752     /* Save the key for future checking */
753     silc_pkcs_save_public_key_data(verify->filename, verify->pk, 
754                                    verify->pk_len, SILC_PKCS_FILE_PEM);
755   } else {
756     /* Call the completion */
757     if (verify->completion)
758       verify->completion(FALSE, verify->context);
759
760     printformat_module("fe-common/silc", NULL, NULL,
761                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, verify->entity);
762   }
763
764   silc_free(verify->filename);
765   silc_free(verify->entity);
766   silc_free(verify->pk);
767   silc_free(verify);
768 }
769
770 static void 
771 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
772                                 SilcSocketType conn_type, unsigned char *pk, 
773                                 uint32 pk_len, SilcSKEPKType pk_type,
774                                 SilcVerifyPublicKey completion, void *context)
775 {
776   int i;
777   char file[256], filename[256], *fingerprint, *format;
778   struct passwd *pw;
779   struct stat st;
780   char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
781                    conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
782                   "server" : "client");
783   PublicKeyVerify verify;
784
785   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
786     printformat_module("fe-common/silc", NULL, NULL,
787                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED, 
788                        entity, pk_type);
789     if (completion)
790       completion(FALSE, context);
791     return;
792   }
793
794   pw = getpwuid(getuid());
795   if (!pw) {
796     if (completion)
797       completion(FALSE, context);
798     return;
799   }
800
801   memset(filename, 0, sizeof(filename));
802   memset(file, 0, sizeof(file));
803
804   if (conn_type == SILC_SOCKET_TYPE_SERVER ||
805       conn_type == SILC_SOCKET_TYPE_ROUTER) {
806     snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
807              conn->sock->hostname, conn->sock->port);
808     snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
809              pw->pw_dir, entity, file);
810   } else {
811     /* Replace all whitespaces with `_'. */
812     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
813     for (i = 0; i < strlen(fingerprint); i++)
814       if (fingerprint[i] == ' ')
815         fingerprint[i] = '_';
816     
817     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
818     snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
819              pw->pw_dir, entity, file);
820     silc_free(fingerprint);
821   }
822
823   /* Take fingerprint of the public key */
824   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
825
826   verify = silc_calloc(1, sizeof(*verify));
827   verify->client = client;
828   verify->conn = conn;
829   verify->filename = strdup(filename);
830   verify->entity = strdup(entity);
831   verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
832   memcpy(verify->pk, pk, pk_len);
833   verify->pk_len = pk_len;
834   verify->pk_type = pk_type;
835   verify->completion = completion;
836   verify->context = context;
837
838   /* Check whether this key already exists */
839   if (stat(filename, &st) < 0) {
840     /* Key does not exist, ask user to verify the key and save it */
841
842     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
843                        SILCTXT_PUBKEY_RECEIVED, entity);
844     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
845                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
846     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
847                              SILCTXT_PUBKEY_ACCEPT);
848     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
849                             format, 0, verify);
850     g_free(format);
851     silc_free(fingerprint);
852     return;
853   } else {
854     /* The key already exists, verify it. */
855     SilcPublicKey public_key;
856     unsigned char *encpk;
857     uint32 encpk_len;
858
859     /* Load the key file */
860     if (!silc_pkcs_load_public_key(filename, &public_key, 
861                                    SILC_PKCS_FILE_PEM))
862       if (!silc_pkcs_load_public_key(filename, &public_key, 
863                                      SILC_PKCS_FILE_BIN)) {
864         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
865                            SILCTXT_PUBKEY_RECEIVED, entity);
866         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
867                            SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
868         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
869                            SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
870         format = format_get_text("fe-common/silc", NULL, NULL, NULL,
871                                  SILCTXT_PUBKEY_ACCEPT_ANYWAY);
872         keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
873                                 format, 0, verify);
874         g_free(format);
875         silc_free(fingerprint);
876         return;
877       }
878   
879     /* Encode the key data */
880     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
881     if (!encpk) {
882       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
883                          SILCTXT_PUBKEY_RECEIVED, entity);
884       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
885                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
886       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
887                          SILCTXT_PUBKEY_MALFORMED, entity);
888       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
889                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
890       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
891                               format, 0, verify);
892       g_free(format);
893       silc_free(fingerprint);
894       return;
895     }
896
897     /* Compare the keys */
898     if (memcmp(encpk, pk, encpk_len)) {
899       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
900                          SILCTXT_PUBKEY_RECEIVED, entity);
901       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
902                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
903       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
904                          SILCTXT_PUBKEY_NO_MATCH, entity);
905       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
906                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
907       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
908                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
909
910       /* Ask user to verify the key and save it */
911       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
912                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
913       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
914                               format, 0, verify);
915       g_free(format);
916       silc_free(fingerprint);
917       return;
918     }
919
920     /* Local copy matched */
921     if (completion)
922       completion(TRUE, context);
923     silc_free(fingerprint);
924   }
925 }
926
927 /* Verifies received public key. The `conn_type' indicates which entity
928    (server, client etc.) has sent the public key. If user decides to trust
929    the key may be saved as trusted public key for later use. The 
930    `completion' must be called after the public key has been verified. */
931
932 void 
933 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
934                        SilcSocketType conn_type, unsigned char *pk, 
935                        uint32 pk_len, SilcSKEPKType pk_type,
936                        SilcVerifyPublicKey completion, void *context)
937 {
938   silc_verify_public_key_internal(client, conn, conn_type, pk,
939                                   pk_len, pk_type,
940                                   completion, context);
941 }
942
943 /* Asks passphrase from user on the input line. */
944
945 typedef struct {
946   SilcAskPassphrase completion;
947   void *context;
948 } *AskPassphrase;
949
950 void ask_passphrase_completion(const char *passphrase, void *context)
951 {
952   AskPassphrase p = (AskPassphrase)context;
953   p->completion((unsigned char *)passphrase, 
954                 passphrase ? strlen(passphrase) : 0, p->context);
955   silc_free(p);
956 }
957
958 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
959                                 SilcAskPassphrase completion, void *context)
960 {
961   AskPassphrase p = silc_calloc(1, sizeof(*p));
962   p->completion = completion;
963   p->context = context;
964
965   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
966                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
967 }
968
969 /* Find authentication method and authentication data by hostname and
970    port. The hostname may be IP address as well. The found authentication
971    method and authentication data is returned to `auth_meth', `auth_data'
972    and `auth_data_len'. The function returns TRUE if authentication method
973    is found and FALSE if not. `conn' may be NULL. */
974
975 int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
976                          char *hostname, uint16 port,
977                          SilcProtocolAuthMeth *auth_meth,
978                          unsigned char **auth_data,
979                          uint32 *auth_data_len)
980 {
981   bool ret = TRUE;
982   SILC_SERVER_REC *server = conn ? conn->context : NULL;
983
984   /* XXX must resolve from configuration whether this connection has
985      any specific authentication data */
986
987   *auth_meth = SILC_AUTH_NONE;
988   *auth_data = NULL;
989   *auth_data_len = 0;
990
991   if (ret == FALSE) {
992     printformat_module("fe-common/silc", server, NULL,
993                        MSGLEVEL_MODES, SILCTXT_AUTH_METH_UNRESOLVED);
994   }
995
996   return ret;
997 }
998
999 /* Notifies application that failure packet was received.  This is called
1000    if there is some protocol active in the client.  The `protocol' is the
1001    protocol context.  The `failure' is opaque pointer to the failure
1002    indication.  Note, that the `failure' is protocol dependant and application
1003    must explicitly cast it to correct type.  Usually `failure' is 32 bit
1004    failure type (see protocol specs for all protocol failure types). */
1005
1006 void silc_failure(SilcClient client, SilcClientConnection conn, 
1007                   SilcProtocol protocol, void *failure)
1008 {
1009   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1010     SilcSKEStatus status = (SilcSKEStatus)failure;
1011     
1012     if (status == SILC_SKE_STATUS_BAD_VERSION)
1013       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1014                          SILCTXT_KE_BAD_VERSION);
1015     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1016       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1017                          SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1018     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1019       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1020                          SILCTXT_KE_UNKNOWN_GROUP);
1021     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1022       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1023                          SILCTXT_KE_UNKNOWN_CIPHER);
1024     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1025       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1026                          SILCTXT_KE_UNKNOWN_PKCS);
1027     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1028       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1029                          SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1030     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1031       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1032                          SILCTXT_KE_UNKNOWN_HMAC);
1033     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1034       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1035                          SILCTXT_KE_INCORRECT_SIGNATURE);
1036     if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1037       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1038                          SILCTXT_KE_INVALID_COOKIE);
1039   }
1040
1041   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1042     uint32 err = (uint32)failure;
1043
1044     if (err == SILC_AUTH_FAILED)
1045       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1046                          SILCTXT_AUTH_FAILED);
1047   }
1048 }
1049
1050 /* Asks whether the user would like to perform the key agreement protocol.
1051    This is called after we have received an key agreement packet or an
1052    reply to our key agreement packet. This returns TRUE if the user wants
1053    the library to perform the key agreement protocol and FALSE if it is not
1054    desired (application may start it later by calling the function
1055    silc_client_perform_key_agreement). */
1056
1057 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1058                        SilcClientEntry client_entry, char *hostname,
1059                        int port,
1060                        SilcKeyAgreementCallback *completion,
1061                        void **context)
1062 {
1063   char portstr[12];
1064
1065   /* We will just display the info on the screen and return FALSE and user
1066      will have to start the key agreement with a command. */
1067
1068   if (hostname) 
1069     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1070
1071   if (!hostname)
1072     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1073                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1074   else
1075     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1076                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST, 
1077                        client_entry->nickname, hostname, portstr);
1078
1079   *completion = NULL;
1080   *context = NULL;
1081
1082   return FALSE;
1083 }
1084
1085 /* SILC client operations */
1086 SilcClientOperations ops = {
1087   silc_say,
1088   silc_channel_message,
1089   silc_private_message,
1090   silc_notify,
1091   silc_command,
1092   silc_command_reply,
1093   silc_connect,
1094   silc_disconnect,
1095   silc_get_auth_method,
1096   silc_verify_public_key,
1097   silc_ask_passphrase,
1098   silc_failure,
1099   silc_key_agreement,
1100 };