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