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, *nick;
337       uint32 idle, mode;
338       SilcBuffer channels;
339       SilcClientEntry client_entry;
340       
341       if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
342           status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
343         char *tmp;
344         tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
345                                          3, NULL);
346         if (tmp)
347           silc_say_error("%s: %s", tmp, 
348                          silc_client_command_status_message(status));
349         else
350           silc_say_error("%s", silc_client_command_status_message(status));
351         break;
352       }
353       
354       if (!success)
355         return;
356       
357       client_entry = va_arg(vp, SilcClientEntry);
358       nickname = va_arg(vp, char *);
359       username = va_arg(vp, char *);
360       realname = va_arg(vp, char *);
361       channels = va_arg(vp, SilcBuffer);
362       mode = va_arg(vp, uint32);
363       idle = va_arg(vp, uint32);
364       
365       silc_parse_userfqdn(nickname, &nick, NULL);
366       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
367                          SILCTXT_WHOIS_USERINFO, nickname, 
368                          client_entry->username, client_entry->hostname,
369                          nick, client_entry->nickname);
370       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
371                          SILCTXT_WHOIS_REALNAME, realname);
372       silc_free(nick);
373
374       if (channels) {
375         SilcDList list = silc_channel_payload_parse_list(channels);
376         if (list) {
377           SilcChannelPayload entry;
378           memset(buf, 0, sizeof(buf));
379           silc_dlist_start(list);
380           while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
381             char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
382             uint32 name_len;
383             char *name = silc_channel_get_name(entry, &name_len);
384             
385             if (m)
386               strncat(buf, m, strlen(m));
387             strncat(buf, name, name_len);
388             strncat(buf, " ", 1);
389             silc_free(m);
390           }
391
392           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
393                              SILCTXT_WHOIS_CHANNELS, buf);
394           silc_channel_payload_list_free(list);
395         }
396       }
397       
398       if (mode) {
399         memset(buf, 0, sizeof(buf));
400
401         if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
402             (mode & SILC_UMODE_ROUTER_OPERATOR)) {
403           strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
404                  "Server Operator " :
405                  (mode & SILC_UMODE_ROUTER_OPERATOR) ?
406                  "SILC Operator " : "[Unknown mode] ");
407         }
408         if (mode & SILC_UMODE_GONE)
409           strcat(buf, "away");
410
411         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
412                            SILCTXT_WHOIS_MODES, buf);
413       }
414       
415       if (idle && nickname) {
416         memset(buf, 0, sizeof(buf));
417         snprintf(buf, sizeof(buf) - 1, "%lu %s",
418                  idle > 60 ? (idle / 60) : idle,
419                  idle > 60 ? "minutes" : "seconds");
420
421         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
422                            SILCTXT_WHOIS_IDLE, buf);
423       }
424     }
425     break;
426     
427   case SILC_COMMAND_WHOWAS:
428     {
429       char *nickname, *username, *realname;
430       
431       if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
432           status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
433         char *tmp;
434         tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
435                                          3, NULL);
436         if (tmp)
437           silc_say_error("%s: %s", tmp, 
438                          silc_client_command_status_message(status));
439         else
440           silc_say_error("%s", silc_client_command_status_message(status));
441         break;
442       }
443       
444       if (!success)
445         return;
446       
447       (void)va_arg(vp, SilcClientEntry);
448       nickname = va_arg(vp, char *);
449       username = va_arg(vp, char *);
450       realname = va_arg(vp, char *);
451       
452       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
453                          SILCTXT_WHOWAS_USERINFO, nickname, username, 
454                          realname ? realname : "");
455     }
456     break;
457     
458   case SILC_COMMAND_INVITE:
459     {
460       SilcChannelEntry channel;
461       char *invite_list;
462       SilcArgumentPayload args;
463       int argc = 0;
464       
465       if (!success)
466         return;
467       
468       channel = va_arg(vp, SilcChannelEntry);
469       invite_list = va_arg(vp, char *);
470
471       args = silc_command_get_args(cmd_payload);
472       if (args)
473         argc = silc_argument_get_arg_num(args);
474
475       if (invite_list)
476         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
477                            SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
478                            invite_list);
479       else if (argc == 3)
480         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
481                            SILCTXT_CHANNEL_NO_INVITE_LIST, 
482                            channel->channel_name);
483     }
484     break;
485
486   case SILC_COMMAND_JOIN: 
487     {
488       char *channel, *mode, *topic;
489       uint32 modei;
490       SilcChannelEntry channel_entry;
491       SilcBuffer client_id_list;
492       uint32 list_count;
493
494       if (!success)
495         return;
496
497       channel = va_arg(vp, char *);
498       channel_entry = va_arg(vp, SilcChannelEntry);
499       modei = va_arg(vp, uint32);
500       (void)va_arg(vp, uint32);
501       (void)va_arg(vp, unsigned char *);
502       (void)va_arg(vp, unsigned char *);
503       (void)va_arg(vp, unsigned char *);
504       topic = va_arg(vp, char *);
505       (void)va_arg(vp, unsigned char *);
506       list_count = va_arg(vp, uint32);
507       client_id_list = va_arg(vp, SilcBuffer);
508
509       chanrec = silc_channel_find(server, channel);
510       if (!chanrec)
511         chanrec = silc_channel_create(server, channel, TRUE);
512
513       if (topic) {
514         g_free_not_null(chanrec->topic);
515         chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
516         signal_emit("channel topic changed", 1, chanrec);
517       }
518
519       mode = silc_client_chmode(modei, 
520                                 channel_entry->channel_key->cipher->name,
521                                 channel_entry->hmac->hmac->name);
522       g_free_not_null(chanrec->mode);
523       chanrec->mode = g_strdup(mode == NULL ? "" : mode);
524       signal_emit("channel mode changed", 1, chanrec);
525
526       /* Resolve the client information */
527       silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
528                                       silc_client_join_get_users, 
529                                       channel_entry);
530       break;
531     }
532
533   case SILC_COMMAND_NICK: 
534     {
535       SilcClientEntry client = va_arg(vp, SilcClientEntry);
536       char *old;
537       
538       if (!success)
539         return;
540
541       old = g_strdup(server->nick);
542       server_change_nick(SERVER(server), client->nickname);
543       nicklist_rename_unique(SERVER(server),
544                              server->conn->local_entry, server->nick,
545                              client, client->nickname);
546       
547       signal_emit("message own_nick", 4, server, server->nick, old, "");
548       g_free(old);
549       break;
550     }
551     
552   case SILC_COMMAND_LIST:
553     {
554       char *topic, *name;
555       int usercount;
556       char users[20];
557       
558       if (!success)
559         return;
560       
561       (void)va_arg(vp, SilcChannelEntry);
562       name = va_arg(vp, char *);
563       topic = va_arg(vp, char *);
564       usercount = va_arg(vp, int);
565       
566       if (status == SILC_STATUS_LIST_START ||
567           status == SILC_STATUS_OK)
568         printformat_module("fe-common/silc", server, NULL,
569                            MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
570
571       snprintf(users, sizeof(users) - 1, "%d", usercount);
572       printformat_module("fe-common/silc", server, NULL,
573                          MSGLEVEL_CRAP, SILCTXT_LIST,
574                          name, users, topic ? topic : "");
575     }
576     break;
577     
578   case SILC_COMMAND_UMODE:
579     {
580       uint32 mode;
581       
582       if (!success)
583         return;
584       
585       mode = va_arg(vp, uint32);
586       
587       if (mode & SILC_UMODE_SERVER_OPERATOR)
588         printformat_module("fe-common/silc", server, NULL,
589                            MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
590
591       if (mode & SILC_UMODE_ROUTER_OPERATOR)
592         printformat_module("fe-common/silc", server, NULL,
593                            MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
594     }
595     break;
596     
597   case SILC_COMMAND_OPER:
598     if (!success)
599       return;
600
601     printformat_module("fe-common/silc", server, NULL,
602                        MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
603     break;
604     
605   case SILC_COMMAND_SILCOPER:
606     if (!success)
607       return;
608
609     printformat_module("fe-common/silc", server, NULL,
610                        MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
611     break;
612     
613   case SILC_COMMAND_USERS: 
614     {
615       SilcChannelEntry channel;
616       SilcChannelUser chu;
617       
618       if (!success)
619         return;
620       
621       channel = va_arg(vp, SilcChannelEntry);
622       
623       printformat_module("fe-common/silc", server, channel->channel_name,
624                          MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
625                          channel->channel_name);
626
627       silc_list_start(channel->clients);
628       while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
629         SilcClientEntry e = chu->client;
630         char stat[5], *mode;
631         
632         memset(stat, 0, sizeof(stat));
633         mode = silc_client_chumode_char(chu->mode);
634         if (e->mode & SILC_UMODE_GONE)
635           strcat(stat, "G");
636         else
637           strcat(stat, "H");
638         if (mode)
639           strcat(stat, mode);
640
641         printformat_module("fe-common/silc", server, channel->channel_name,
642                            MSGLEVEL_CRAP, SILCTXT_USERS,
643                            e->nickname, stat, e->username, 
644                            e->realname ? e->realname : "");
645         if (mode)
646           silc_free(mode);
647       }
648     }
649     break;
650
651   case SILC_COMMAND_BAN:
652     {
653       SilcChannelEntry channel;
654       char *ban_list;
655       
656       if (!success)
657         return;
658       
659       channel = va_arg(vp, SilcChannelEntry);
660       ban_list = va_arg(vp, char *);
661       
662       if (ban_list)
663         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
664                            SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
665                            ban_list);
666       else
667         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
668                            SILCTXT_CHANNEL_NO_BAN_LIST, 
669                            channel->channel_name);
670     }
671     break;
672     
673   case SILC_COMMAND_GETKEY:
674     {
675       SilcIdType id_type;
676       void *entry;
677       SilcPublicKey public_key;
678       unsigned char *pk;
679       uint32 pk_len;
680       
681       if (!success)
682         return;
683       
684       id_type = va_arg(vp, uint32);
685       entry = va_arg(vp, void *);
686       public_key = va_arg(vp, SilcPublicKey);
687
688       if (public_key) {
689         pk = silc_pkcs_public_key_encode(public_key, &pk_len);
690         
691         silc_verify_public_key_internal(client, conn, 
692                                         (id_type == SILC_ID_CLIENT ?
693                                          SILC_SOCKET_TYPE_CLIENT :
694                                          SILC_SOCKET_TYPE_SERVER),
695                                         pk, pk_len, SILC_SKE_PK_TYPE_SILC,
696                                         NULL, NULL);
697         silc_free(pk);
698       } else {
699         printformat_module("fe-common/silc", server, NULL,
700                            MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
701       }
702     }
703     break;
704     
705   case SILC_COMMAND_TOPIC:
706     {
707       SilcChannelEntry channel;
708       char *topic;
709       
710       if (!success)
711         return;
712       
713       channel = va_arg(vp, SilcChannelEntry);
714       topic = va_arg(vp, char *);
715       
716       if (topic) {
717         chanrec = silc_channel_find_entry(server, channel);
718         if (chanrec) {
719           g_free_not_null(chanrec->topic);
720           chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
721           signal_emit("channel topic changed", 1, chanrec);
722         }
723         printformat_module("fe-common/silc", server, channel->channel_name,
724                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
725                            channel->channel_name, topic);
726       } else {
727         printformat_module("fe-common/silc", server, channel->channel_name,
728                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
729                            channel->channel_name);
730       }
731     }
732     break;
733
734   }
735
736   va_end(vp);
737 }
738
739 /* Internal routine to verify public key. If the `completion' is provided
740    it will be called to indicate whether public was verified or not. */
741
742 typedef struct {
743   SilcClient client;
744   SilcClientConnection conn;
745   char *filename;
746   char *entity;
747   unsigned char *pk;
748   uint32 pk_len;
749   SilcSKEPKType pk_type;
750   SilcVerifyPublicKey completion;
751   void *context;
752 } *PublicKeyVerify;
753
754 static void verify_public_key_completion(const char *line, void *context)
755 {
756   PublicKeyVerify verify = (PublicKeyVerify)context;
757
758   if (line[0] == 'Y' || line[0] == 'y') {
759     /* Call the completion */
760     if (verify->completion)
761       verify->completion(TRUE, verify->context);
762
763     /* Save the key for future checking */
764     silc_pkcs_save_public_key_data(verify->filename, verify->pk, 
765                                    verify->pk_len, SILC_PKCS_FILE_PEM);
766   } else {
767     /* Call the completion */
768     if (verify->completion)
769       verify->completion(FALSE, verify->context);
770
771     printformat_module("fe-common/silc", NULL, NULL,
772                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, verify->entity);
773   }
774
775   silc_free(verify->filename);
776   silc_free(verify->entity);
777   silc_free(verify->pk);
778   silc_free(verify);
779 }
780
781 static void 
782 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
783                                 SilcSocketType conn_type, unsigned char *pk, 
784                                 uint32 pk_len, SilcSKEPKType pk_type,
785                                 SilcVerifyPublicKey completion, void *context)
786 {
787   int i;
788   char file[256], filename[256], *fingerprint, *format;
789   struct passwd *pw;
790   struct stat st;
791   char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
792                    conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
793                   "server" : "client");
794   PublicKeyVerify verify;
795
796   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
797     printformat_module("fe-common/silc", NULL, NULL,
798                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED, 
799                        entity, pk_type);
800     if (completion)
801       completion(FALSE, context);
802     return;
803   }
804
805   pw = getpwuid(getuid());
806   if (!pw) {
807     if (completion)
808       completion(FALSE, context);
809     return;
810   }
811
812   memset(filename, 0, sizeof(filename));
813   memset(file, 0, sizeof(file));
814
815   if (conn_type == SILC_SOCKET_TYPE_SERVER ||
816       conn_type == SILC_SOCKET_TYPE_ROUTER) {
817     snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
818              conn->sock->hostname, conn->sock->port);
819     snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
820              pw->pw_dir, entity, file);
821   } else {
822     /* Replace all whitespaces with `_'. */
823     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
824     for (i = 0; i < strlen(fingerprint); i++)
825       if (fingerprint[i] == ' ')
826         fingerprint[i] = '_';
827     
828     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
829     snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
830              pw->pw_dir, entity, file);
831     silc_free(fingerprint);
832   }
833
834   /* Take fingerprint of the public key */
835   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
836
837   verify = silc_calloc(1, sizeof(*verify));
838   verify->client = client;
839   verify->conn = conn;
840   verify->filename = strdup(filename);
841   verify->entity = strdup(entity);
842   verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
843   memcpy(verify->pk, pk, pk_len);
844   verify->pk_len = pk_len;
845   verify->pk_type = pk_type;
846   verify->completion = completion;
847   verify->context = context;
848
849   /* Check whether this key already exists */
850   if (stat(filename, &st) < 0) {
851     /* Key does not exist, ask user to verify the key and save it */
852
853     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
854                        SILCTXT_PUBKEY_RECEIVED, entity);
855     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
856                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
857     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
858                              SILCTXT_PUBKEY_ACCEPT);
859     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
860                             format, 0, verify);
861     g_free(format);
862     silc_free(fingerprint);
863     return;
864   } else {
865     /* The key already exists, verify it. */
866     SilcPublicKey public_key;
867     unsigned char *encpk;
868     uint32 encpk_len;
869
870     /* Load the key file */
871     if (!silc_pkcs_load_public_key(filename, &public_key, 
872                                    SILC_PKCS_FILE_PEM))
873       if (!silc_pkcs_load_public_key(filename, &public_key, 
874                                      SILC_PKCS_FILE_BIN)) {
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_COULD_NOT_LOAD, 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     /* Encode the key data */
891     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
892     if (!encpk) {
893       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
894                          SILCTXT_PUBKEY_RECEIVED, entity);
895       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
896                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
897       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
898                          SILCTXT_PUBKEY_MALFORMED, entity);
899       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
900                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
901       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
902                               format, 0, verify);
903       g_free(format);
904       silc_free(fingerprint);
905       return;
906     }
907
908     /* Compare the keys */
909     if (memcmp(encpk, pk, encpk_len)) {
910       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
911                          SILCTXT_PUBKEY_RECEIVED, entity);
912       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
913                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
914       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
915                          SILCTXT_PUBKEY_NO_MATCH, entity);
916       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
917                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
918       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
919                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
920
921       /* Ask user to verify the key and save it */
922       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
923                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
924       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
925                               format, 0, verify);
926       g_free(format);
927       silc_free(fingerprint);
928       return;
929     }
930
931     /* Local copy matched */
932     if (completion)
933       completion(TRUE, context);
934     silc_free(fingerprint);
935   }
936 }
937
938 /* Verifies received public key. The `conn_type' indicates which entity
939    (server, client etc.) has sent the public key. If user decides to trust
940    the key may be saved as trusted public key for later use. The 
941    `completion' must be called after the public key has been verified. */
942
943 void 
944 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
945                        SilcSocketType conn_type, unsigned char *pk, 
946                        uint32 pk_len, SilcSKEPKType pk_type,
947                        SilcVerifyPublicKey completion, void *context)
948 {
949   silc_verify_public_key_internal(client, conn, conn_type, pk,
950                                   pk_len, pk_type,
951                                   completion, context);
952 }
953
954 /* Asks passphrase from user on the input line. */
955
956 typedef struct {
957   SilcAskPassphrase completion;
958   void *context;
959 } *AskPassphrase;
960
961 void ask_passphrase_completion(const char *passphrase, void *context)
962 {
963   AskPassphrase p = (AskPassphrase)context;
964   p->completion((unsigned char *)passphrase, 
965                 passphrase ? strlen(passphrase) : 0, p->context);
966   silc_free(p);
967 }
968
969 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
970                          SilcAskPassphrase completion, void *context)
971 {
972   AskPassphrase p = silc_calloc(1, sizeof(*p));
973   p->completion = completion;
974   p->context = context;
975
976   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
977                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
978 }
979
980 typedef struct {
981   SilcGetAuthMeth completion;
982   void *context;
983 } *InternalGetAuthMethod;
984
985 /* Callback called when we've received the authentication method information
986    from the server after we've requested it. This will get the authentication
987    data from the user if needed. */
988
989 static void silc_get_auth_method_callback(SilcClient client,
990                                           SilcClientConnection conn,
991                                           SilcAuthMethod auth_meth,
992                                           void *context)
993 {
994   InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
995
996   switch (auth_meth) {
997   case SILC_AUTH_NONE:
998     /* No authentication required. */
999     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1000     break;
1001   case SILC_AUTH_PASSWORD:
1002     /* Do not ask the passphrase from user, the library will ask it if
1003        we do not provide it here. */
1004     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1005     break;
1006   case SILC_AUTH_PUBLIC_KEY:
1007     /* Do not get the authentication data now, the library will generate
1008        it using our default key, if we do not provide it here. */
1009     /* XXX In the future when we support multiple local keys and multiple
1010        local certificates we will need to ask from user which one to use. */
1011     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1012     break;
1013   }
1014
1015   silc_free(internal);
1016 }
1017
1018 /* Find authentication method and authentication data by hostname and
1019    port. The hostname may be IP address as well. The found authentication
1020    method and authentication data is returned to `auth_meth', `auth_data'
1021    and `auth_data_len'. The function returns TRUE if authentication method
1022    is found and FALSE if not. `conn' may be NULL. */
1023
1024 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1025                           char *hostname, uint16 port,
1026                           SilcGetAuthMeth completion, void *context)
1027 {
1028   InternalGetAuthMethod internal;
1029
1030   /* XXX must resolve from configuration whether this connection has
1031      any specific authentication data */
1032
1033   /* If we do not have this connection configured by the user in a
1034      configuration file then resolve the authentication method from the
1035      server for this session. */
1036   internal = silc_calloc(1, sizeof(*internal));
1037   internal->completion = completion;
1038   internal->context = context;
1039
1040   silc_client_request_authentication_method(client, conn, 
1041                                             silc_get_auth_method_callback,
1042                                             internal);
1043 }
1044
1045 /* Notifies application that failure packet was received.  This is called
1046    if there is some protocol active in the client.  The `protocol' is the
1047    protocol context.  The `failure' is opaque pointer to the failure
1048    indication.  Note, that the `failure' is protocol dependant and application
1049    must explicitly cast it to correct type.  Usually `failure' is 32 bit
1050    failure type (see protocol specs for all protocol failure types). */
1051
1052 void silc_failure(SilcClient client, SilcClientConnection conn, 
1053                   SilcProtocol protocol, void *failure)
1054 {
1055   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1056     SilcSKEStatus status = (SilcSKEStatus)failure;
1057     
1058     if (status == SILC_SKE_STATUS_BAD_VERSION)
1059       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1060                          SILCTXT_KE_BAD_VERSION);
1061     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1062       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1063                          SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1064     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1065       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1066                          SILCTXT_KE_UNKNOWN_GROUP);
1067     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1068       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1069                          SILCTXT_KE_UNKNOWN_CIPHER);
1070     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1071       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1072                          SILCTXT_KE_UNKNOWN_PKCS);
1073     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1074       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1075                          SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1076     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1077       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1078                          SILCTXT_KE_UNKNOWN_HMAC);
1079     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1080       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1081                          SILCTXT_KE_INCORRECT_SIGNATURE);
1082     if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1083       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1084                          SILCTXT_KE_INVALID_COOKIE);
1085   }
1086
1087   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1088     uint32 err = (uint32)failure;
1089
1090     if (err == SILC_AUTH_FAILED)
1091       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1092                          SILCTXT_AUTH_FAILED);
1093   }
1094 }
1095
1096 /* Asks whether the user would like to perform the key agreement protocol.
1097    This is called after we have received an key agreement packet or an
1098    reply to our key agreement packet. This returns TRUE if the user wants
1099    the library to perform the key agreement protocol and FALSE if it is not
1100    desired (application may start it later by calling the function
1101    silc_client_perform_key_agreement). */
1102
1103 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1104                        SilcClientEntry client_entry, char *hostname,
1105                        int port,
1106                        SilcKeyAgreementCallback *completion,
1107                        void **context)
1108 {
1109   char portstr[12];
1110
1111   /* We will just display the info on the screen and return FALSE and user
1112      will have to start the key agreement with a command. */
1113
1114   if (hostname) 
1115     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1116
1117   if (!hostname)
1118     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1119                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1120   else
1121     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1122                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST, 
1123                        client_entry->nickname, hostname, portstr);
1124
1125   *completion = NULL;
1126   *context = NULL;
1127
1128   return FALSE;
1129 }
1130
1131 /* SILC client operations */
1132 SilcClientOperations ops = {
1133   silc_say,
1134   silc_channel_message,
1135   silc_private_message,
1136   silc_notify,
1137   silc_command,
1138   silc_command_reply,
1139   silc_connect,
1140   silc_disconnect,
1141   silc_get_auth_method,
1142   silc_verify_public_key,
1143   silc_ask_passphrase,
1144   silc_failure,
1145   silc_key_agreement,
1146 };