9247a3c4f484b5305855ee17e89e36c8b34cc047
[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       if (public_key) {
683         pk = silc_pkcs_public_key_encode(public_key, &pk_len);
684         
685         silc_verify_public_key_internal(client, conn, 
686                                         (id_type == SILC_ID_CLIENT ?
687                                          SILC_SOCKET_TYPE_CLIENT :
688                                          SILC_SOCKET_TYPE_SERVER),
689                                         pk, pk_len, SILC_SKE_PK_TYPE_SILC,
690                                         NULL, NULL);
691         silc_free(pk);
692       } else {
693         printformat_module("fe-common/silc", server, NULL,
694                            MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
695       }
696     }
697     break;
698     
699   case SILC_COMMAND_TOPIC:
700     {
701       SilcChannelEntry channel;
702       char *topic;
703       
704       if (!success)
705         return;
706       
707       channel = va_arg(vp, SilcChannelEntry);
708       topic = va_arg(vp, char *);
709       
710       if (topic) {
711         chanrec = silc_channel_find_entry(server, channel);
712         if (chanrec) {
713           g_free_not_null(chanrec->topic);
714           chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
715           signal_emit("channel topic changed", 1, chanrec);
716         }
717         printformat_module("fe-common/silc", server, channel->channel_name,
718                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
719                            channel->channel_name, topic);
720       } else {
721         printformat_module("fe-common/silc", server, channel->channel_name,
722                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
723                            channel->channel_name);
724       }
725     }
726     break;
727
728   }
729
730   va_end(vp);
731 }
732
733 /* Internal routine to verify public key. If the `completion' is provided
734    it will be called to indicate whether public was verified or not. */
735
736 typedef struct {
737   SilcClient client;
738   SilcClientConnection conn;
739   char *filename;
740   char *entity;
741   unsigned char *pk;
742   uint32 pk_len;
743   SilcSKEPKType pk_type;
744   SilcVerifyPublicKey completion;
745   void *context;
746 } *PublicKeyVerify;
747
748 static void verify_public_key_completion(const char *line, void *context)
749 {
750   PublicKeyVerify verify = (PublicKeyVerify)context;
751
752   if (line[0] == 'Y' || line[0] == 'y') {
753     /* Call the completion */
754     if (verify->completion)
755       verify->completion(TRUE, verify->context);
756
757     /* Save the key for future checking */
758     silc_pkcs_save_public_key_data(verify->filename, verify->pk, 
759                                    verify->pk_len, SILC_PKCS_FILE_PEM);
760   } else {
761     /* Call the completion */
762     if (verify->completion)
763       verify->completion(FALSE, verify->context);
764
765     printformat_module("fe-common/silc", NULL, NULL,
766                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, verify->entity);
767   }
768
769   silc_free(verify->filename);
770   silc_free(verify->entity);
771   silc_free(verify->pk);
772   silc_free(verify);
773 }
774
775 static void 
776 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
777                                 SilcSocketType conn_type, unsigned char *pk, 
778                                 uint32 pk_len, SilcSKEPKType pk_type,
779                                 SilcVerifyPublicKey completion, void *context)
780 {
781   int i;
782   char file[256], filename[256], *fingerprint, *format;
783   struct passwd *pw;
784   struct stat st;
785   char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
786                    conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
787                   "server" : "client");
788   PublicKeyVerify verify;
789
790   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
791     printformat_module("fe-common/silc", NULL, NULL,
792                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED, 
793                        entity, pk_type);
794     if (completion)
795       completion(FALSE, context);
796     return;
797   }
798
799   pw = getpwuid(getuid());
800   if (!pw) {
801     if (completion)
802       completion(FALSE, context);
803     return;
804   }
805
806   memset(filename, 0, sizeof(filename));
807   memset(file, 0, sizeof(file));
808
809   if (conn_type == SILC_SOCKET_TYPE_SERVER ||
810       conn_type == SILC_SOCKET_TYPE_ROUTER) {
811     snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
812              conn->sock->hostname, conn->sock->port);
813     snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
814              pw->pw_dir, entity, file);
815   } else {
816     /* Replace all whitespaces with `_'. */
817     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
818     for (i = 0; i < strlen(fingerprint); i++)
819       if (fingerprint[i] == ' ')
820         fingerprint[i] = '_';
821     
822     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
823     snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
824              pw->pw_dir, entity, file);
825     silc_free(fingerprint);
826   }
827
828   /* Take fingerprint of the public key */
829   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
830
831   verify = silc_calloc(1, sizeof(*verify));
832   verify->client = client;
833   verify->conn = conn;
834   verify->filename = strdup(filename);
835   verify->entity = strdup(entity);
836   verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
837   memcpy(verify->pk, pk, pk_len);
838   verify->pk_len = pk_len;
839   verify->pk_type = pk_type;
840   verify->completion = completion;
841   verify->context = context;
842
843   /* Check whether this key already exists */
844   if (stat(filename, &st) < 0) {
845     /* Key does not exist, ask user to verify the key and save it */
846
847     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
848                        SILCTXT_PUBKEY_RECEIVED, entity);
849     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
850                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
851     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
852                              SILCTXT_PUBKEY_ACCEPT);
853     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
854                             format, 0, verify);
855     g_free(format);
856     silc_free(fingerprint);
857     return;
858   } else {
859     /* The key already exists, verify it. */
860     SilcPublicKey public_key;
861     unsigned char *encpk;
862     uint32 encpk_len;
863
864     /* Load the key file */
865     if (!silc_pkcs_load_public_key(filename, &public_key, 
866                                    SILC_PKCS_FILE_PEM))
867       if (!silc_pkcs_load_public_key(filename, &public_key, 
868                                      SILC_PKCS_FILE_BIN)) {
869         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
870                            SILCTXT_PUBKEY_RECEIVED, entity);
871         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
872                            SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
873         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
874                            SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
875         format = format_get_text("fe-common/silc", NULL, NULL, NULL,
876                                  SILCTXT_PUBKEY_ACCEPT_ANYWAY);
877         keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
878                                 format, 0, verify);
879         g_free(format);
880         silc_free(fingerprint);
881         return;
882       }
883   
884     /* Encode the key data */
885     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
886     if (!encpk) {
887       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
888                          SILCTXT_PUBKEY_RECEIVED, entity);
889       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
890                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
891       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
892                          SILCTXT_PUBKEY_MALFORMED, entity);
893       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
894                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
895       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
896                               format, 0, verify);
897       g_free(format);
898       silc_free(fingerprint);
899       return;
900     }
901
902     /* Compare the keys */
903     if (memcmp(encpk, pk, encpk_len)) {
904       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
905                          SILCTXT_PUBKEY_RECEIVED, entity);
906       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
907                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
908       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
909                          SILCTXT_PUBKEY_NO_MATCH, entity);
910       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
911                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
912       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
913                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
914
915       /* Ask user to verify the key and save it */
916       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
917                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
918       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
919                               format, 0, verify);
920       g_free(format);
921       silc_free(fingerprint);
922       return;
923     }
924
925     /* Local copy matched */
926     if (completion)
927       completion(TRUE, context);
928     silc_free(fingerprint);
929   }
930 }
931
932 /* Verifies received public key. The `conn_type' indicates which entity
933    (server, client etc.) has sent the public key. If user decides to trust
934    the key may be saved as trusted public key for later use. The 
935    `completion' must be called after the public key has been verified. */
936
937 void 
938 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
939                        SilcSocketType conn_type, unsigned char *pk, 
940                        uint32 pk_len, SilcSKEPKType pk_type,
941                        SilcVerifyPublicKey completion, void *context)
942 {
943   silc_verify_public_key_internal(client, conn, conn_type, pk,
944                                   pk_len, pk_type,
945                                   completion, context);
946 }
947
948 /* Asks passphrase from user on the input line. */
949
950 typedef struct {
951   SilcAskPassphrase completion;
952   void *context;
953 } *AskPassphrase;
954
955 void ask_passphrase_completion(const char *passphrase, void *context)
956 {
957   AskPassphrase p = (AskPassphrase)context;
958   p->completion((unsigned char *)passphrase, 
959                 passphrase ? strlen(passphrase) : 0, p->context);
960   silc_free(p);
961 }
962
963 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
964                          SilcAskPassphrase completion, void *context)
965 {
966   AskPassphrase p = silc_calloc(1, sizeof(*p));
967   p->completion = completion;
968   p->context = context;
969
970   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
971                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
972 }
973
974 typedef struct {
975   SilcGetAuthMeth completion;
976   void *context;
977 } *InternalGetAuthMethod;
978
979 /* Callback called when we've received the authentication method information
980    from the server after we've requested it. This will get the authentication
981    data from the user if needed. */
982
983 static void silc_get_auth_method_callback(SilcClient client,
984                                           SilcClientConnection conn,
985                                           SilcAuthMethod auth_meth,
986                                           void *context)
987 {
988   InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
989
990   switch (auth_meth) {
991   case SILC_AUTH_NONE:
992     /* No authentication required. */
993     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
994     break;
995   case SILC_AUTH_PASSWORD:
996     /* Do not ask the passphrase from user, the library will ask it if
997        we do not provide it here. */
998     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
999     break;
1000   case SILC_AUTH_PUBLIC_KEY:
1001     /* Do not get the authentication data now, the library will generate
1002        it using our default key, if we do not provide it here. */
1003     /* XXX In the future when we support multiple local keys and multiple
1004        local certificates we will need to ask from user which one to use. */
1005     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1006     break;
1007   }
1008
1009   silc_free(internal);
1010 }
1011
1012 /* Find authentication method and authentication data by hostname and
1013    port. The hostname may be IP address as well. The found authentication
1014    method and authentication data is returned to `auth_meth', `auth_data'
1015    and `auth_data_len'. The function returns TRUE if authentication method
1016    is found and FALSE if not. `conn' may be NULL. */
1017
1018 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1019                           char *hostname, uint16 port,
1020                           SilcGetAuthMeth completion, void *context)
1021 {
1022   InternalGetAuthMethod internal;
1023
1024   /* XXX must resolve from configuration whether this connection has
1025      any specific authentication data */
1026
1027   /* If we do not have this connection configured by the user in a
1028      configuration file then resolve the authentication method from the
1029      server for this session. */
1030   internal = silc_calloc(1, sizeof(*internal));
1031   internal->completion = completion;
1032   internal->context = context;
1033
1034   silc_client_request_authentication_method(client, conn, 
1035                                             silc_get_auth_method_callback,
1036                                             internal);
1037 }
1038
1039 /* Notifies application that failure packet was received.  This is called
1040    if there is some protocol active in the client.  The `protocol' is the
1041    protocol context.  The `failure' is opaque pointer to the failure
1042    indication.  Note, that the `failure' is protocol dependant and application
1043    must explicitly cast it to correct type.  Usually `failure' is 32 bit
1044    failure type (see protocol specs for all protocol failure types). */
1045
1046 void silc_failure(SilcClient client, SilcClientConnection conn, 
1047                   SilcProtocol protocol, void *failure)
1048 {
1049   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1050     SilcSKEStatus status = (SilcSKEStatus)failure;
1051     
1052     if (status == SILC_SKE_STATUS_BAD_VERSION)
1053       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1054                          SILCTXT_KE_BAD_VERSION);
1055     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1056       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1057                          SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1058     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1059       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1060                          SILCTXT_KE_UNKNOWN_GROUP);
1061     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1062       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1063                          SILCTXT_KE_UNKNOWN_CIPHER);
1064     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1065       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1066                          SILCTXT_KE_UNKNOWN_PKCS);
1067     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1068       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1069                          SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1070     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1071       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1072                          SILCTXT_KE_UNKNOWN_HMAC);
1073     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1074       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1075                          SILCTXT_KE_INCORRECT_SIGNATURE);
1076     if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1077       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1078                          SILCTXT_KE_INVALID_COOKIE);
1079   }
1080
1081   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1082     uint32 err = (uint32)failure;
1083
1084     if (err == SILC_AUTH_FAILED)
1085       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1086                          SILCTXT_AUTH_FAILED);
1087   }
1088 }
1089
1090 /* Asks whether the user would like to perform the key agreement protocol.
1091    This is called after we have received an key agreement packet or an
1092    reply to our key agreement packet. This returns TRUE if the user wants
1093    the library to perform the key agreement protocol and FALSE if it is not
1094    desired (application may start it later by calling the function
1095    silc_client_perform_key_agreement). */
1096
1097 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1098                        SilcClientEntry client_entry, char *hostname,
1099                        int port,
1100                        SilcKeyAgreementCallback *completion,
1101                        void **context)
1102 {
1103   char portstr[12];
1104
1105   /* We will just display the info on the screen and return FALSE and user
1106      will have to start the key agreement with a command. */
1107
1108   if (hostname) 
1109     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1110
1111   if (!hostname)
1112     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1113                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1114   else
1115     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1116                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST, 
1117                        client_entry->nickname, hostname, portstr);
1118
1119   *completion = NULL;
1120   *context = NULL;
1121
1122   return FALSE;
1123 }
1124
1125 /* SILC client operations */
1126 SilcClientOperations ops = {
1127   silc_say,
1128   silc_channel_message,
1129   silc_private_message,
1130   silc_notify,
1131   silc_command,
1132   silc_command_reply,
1133   silc_connect,
1134   silc_disconnect,
1135   silc_get_auth_method,
1136   silc_verify_public_key,
1137   silc_ask_passphrase,
1138   silc_failure,
1139   silc_key_agreement,
1140 };