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