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