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       if (!success)
461         return;
462
463       channel = va_arg(vp, char *);
464       channel_entry = va_arg(vp, SilcChannelEntry);
465       modei = va_arg(vp, uint32);
466       (void)va_arg(vp, uint32);
467       (void)va_arg(vp, unsigned char *);
468       (void)va_arg(vp, unsigned char *);
469       (void)va_arg(vp, unsigned char *);
470       topic = va_arg(vp, char *);
471       (void)va_arg(vp, unsigned char *);
472       list_count = va_arg(vp, uint32);
473       client_id_list = va_arg(vp, SilcBuffer);
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, 
488                                 channel_entry->channel_key->cipher->name,
489                                 channel_entry->hmac->hmac->name);
490       g_free_not_null(chanrec->mode);
491       chanrec->mode = g_strdup(mode == NULL ? "" : mode);
492       signal_emit("channel mode changed", 1, chanrec);
493
494       /* Resolve the client information */
495       silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
496                                       silc_client_join_get_users, 
497                                       channel_entry);
498       break;
499     }
500
501   case SILC_COMMAND_NICK: 
502     {
503       SilcClientEntry client = va_arg(vp, SilcClientEntry);
504       char *old;
505       
506       if (!success)
507         return;
508
509       old = g_strdup(server->nick);
510       server_change_nick(SERVER(server), client->nickname);
511       nicklist_rename_unique(SERVER(server),
512                              server->conn->local_entry, server->nick,
513                              client, client->nickname);
514       
515       signal_emit("message own_nick", 4, server, server->nick, old, "");
516       g_free(old);
517       break;
518     }
519     
520   case SILC_COMMAND_LIST:
521     {
522       char *topic, *name;
523       int usercount;
524       char users[20];
525       
526       if (!success)
527         return;
528       
529       (void)va_arg(vp, SilcChannelEntry);
530       name = va_arg(vp, char *);
531       topic = va_arg(vp, char *);
532       usercount = va_arg(vp, int);
533       
534       if (status == SILC_STATUS_LIST_START ||
535           status == SILC_STATUS_OK)
536         printformat_module("fe-common/silc", server, NULL,
537                            MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
538
539       snprintf(users, sizeof(users) - 1, "%d", usercount);
540       printformat_module("fe-common/silc", server, NULL,
541                          MSGLEVEL_CRAP, SILCTXT_LIST,
542                          name, users, topic ? topic : "");
543     }
544     break;
545     
546   case SILC_COMMAND_UMODE:
547     {
548       uint32 mode;
549       
550       if (!success)
551         return;
552       
553       mode = va_arg(vp, uint32);
554       
555       if (mode & SILC_UMODE_SERVER_OPERATOR)
556         printformat_module("fe-common/silc", server, NULL,
557                            MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
558
559       if (mode & SILC_UMODE_ROUTER_OPERATOR)
560         printformat_module("fe-common/silc", server, NULL,
561                            MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
562     }
563     break;
564     
565   case SILC_COMMAND_OPER:
566     if (!success)
567       return;
568
569     printformat_module("fe-common/silc", server, NULL,
570                        MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
571     break;
572     
573   case SILC_COMMAND_SILCOPER:
574     if (!success)
575       return;
576
577     printformat_module("fe-common/silc", server, NULL,
578                        MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
579     break;
580     
581   case SILC_COMMAND_USERS: 
582     {
583       SilcChannelEntry channel;
584       SilcChannelUser chu;
585       
586       if (!success)
587         return;
588       
589       channel = va_arg(vp, SilcChannelEntry);
590       
591       printformat_module("fe-common/silc", server, channel->channel_name,
592                          MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
593                          channel->channel_name);
594
595       silc_list_start(channel->clients);
596       while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
597         SilcClientEntry e = chu->client;
598         char stat[5], *mode;
599         
600         memset(stat, 0, sizeof(stat));
601         mode = silc_client_chumode_char(chu->mode);
602         if (e->mode & SILC_UMODE_GONE)
603           strcat(stat, "G");
604         else
605           strcat(stat, "H");
606         if (mode)
607           strcat(stat, mode);
608
609         printformat_module("fe-common/silc", server, channel->channel_name,
610                            MSGLEVEL_CRAP, SILCTXT_USERS,
611                            e->nickname, stat, e->username, 
612                            e->realname ? e->realname : "");
613         if (mode)
614           silc_free(mode);
615       }
616     }
617     break;
618
619   case SILC_COMMAND_BAN:
620     {
621       SilcChannelEntry channel;
622       char *ban_list;
623       
624       if (!success)
625         return;
626       
627       channel = va_arg(vp, SilcChannelEntry);
628       ban_list = va_arg(vp, char *);
629       
630       if (ban_list)
631         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
632                            SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
633                            ban_list);
634       else
635         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
636                            SILCTXT_CHANNEL_NO_BAN_LIST, 
637                            channel->channel_name);
638     }
639     break;
640     
641   case SILC_COMMAND_GETKEY:
642     {
643       SilcIdType id_type;
644       void *entry;
645       SilcPublicKey public_key;
646       unsigned char *pk;
647       uint32 pk_len;
648       
649       if (!success)
650         return;
651       
652       id_type = va_arg(vp, uint32);
653       entry = va_arg(vp, void *);
654       public_key = va_arg(vp, SilcPublicKey);
655       
656       pk = silc_pkcs_public_key_encode(public_key, &pk_len);
657       
658       if (id_type == SILC_ID_CLIENT)
659         silc_verify_public_key_internal(client, conn, SILC_SOCKET_TYPE_CLIENT,
660                                         pk, pk_len, SILC_SKE_PK_TYPE_SILC,
661                                         NULL, NULL);
662       silc_free(pk);
663     }
664     break;
665     
666   case SILC_COMMAND_TOPIC:
667     {
668       SilcChannelEntry channel;
669       char *topic;
670       
671       if (!success)
672         return;
673       
674       channel = va_arg(vp, SilcChannelEntry);
675       topic = va_arg(vp, char *);
676       
677       if (topic) {
678         chanrec = silc_channel_find_entry(server, channel);
679         if (chanrec) {
680           g_free_not_null(chanrec->topic);
681           chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
682           signal_emit("channel topic changed", 1, chanrec);
683         }
684         printformat_module("fe-common/silc", server, channel->channel_name,
685                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
686                            channel->channel_name, topic);
687       } else {
688         printformat_module("fe-common/silc", server, channel->channel_name,
689                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
690                            channel->channel_name);
691       }
692     }
693     break;
694
695   }
696
697   va_end(vp);
698 }
699
700 /* Internal routine to verify public key. If the `completion' is provided
701    it will be called to indicate whether public was verified or not. */
702
703 typedef struct {
704   SilcClient client;
705   SilcClientConnection conn;
706   char *filename;
707   char *entity;
708   unsigned char *pk;
709   uint32 pk_len;
710   SilcSKEPKType pk_type;
711   SilcVerifyPublicKey completion;
712   void *context;
713 } *PublicKeyVerify;
714
715 static void verify_public_key_completion(const char *line, void *context)
716 {
717   PublicKeyVerify verify = (PublicKeyVerify)context;
718
719   if (line[0] == 'Y' || line[0] == 'y') {
720     /* Call the completion */
721     if (verify->completion)
722       verify->completion(TRUE, verify->context);
723
724     /* Save the key for future checking */
725     silc_pkcs_save_public_key_data(verify->filename, verify->pk, 
726                                    verify->pk_len, SILC_PKCS_FILE_PEM);
727   } else {
728     /* Call the completion */
729     if (verify->completion)
730       verify->completion(FALSE, verify->context);
731
732     printformat_module("fe-common/silc", NULL, NULL,
733                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, verify->entity);
734   }
735
736   silc_free(verify->filename);
737   silc_free(verify->entity);
738   silc_free(verify->pk);
739   silc_free(verify);
740 }
741
742 static void 
743 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
744                                 SilcSocketType conn_type, unsigned char *pk, 
745                                 uint32 pk_len, SilcSKEPKType pk_type,
746                                 SilcVerifyPublicKey completion, void *context)
747 {
748   int i;
749   char file[256], filename[256], *fingerprint, *format;
750   struct passwd *pw;
751   struct stat st;
752   char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
753                    conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
754                   "server" : "client");
755   PublicKeyVerify verify;
756
757   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
758     printformat_module("fe-common/silc", NULL, NULL,
759                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED, 
760                        entity, pk_type);
761     if (completion)
762       completion(FALSE, context);
763     return;
764   }
765
766   pw = getpwuid(getuid());
767   if (!pw) {
768     if (completion)
769       completion(FALSE, context);
770     return;
771   }
772
773   memset(filename, 0, sizeof(filename));
774   memset(file, 0, sizeof(file));
775
776   if (conn_type == SILC_SOCKET_TYPE_SERVER ||
777       conn_type == SILC_SOCKET_TYPE_ROUTER) {
778     snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
779              conn->sock->hostname, conn->sock->port);
780     snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
781              pw->pw_dir, entity, file);
782   } else {
783     /* Replace all whitespaces with `_'. */
784     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
785     for (i = 0; i < strlen(fingerprint); i++)
786       if (fingerprint[i] == ' ')
787         fingerprint[i] = '_';
788     
789     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
790     snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
791              pw->pw_dir, entity, file);
792     silc_free(fingerprint);
793   }
794
795   /* Take fingerprint of the public key */
796   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
797
798   verify = silc_calloc(1, sizeof(*verify));
799   verify->client = client;
800   verify->conn = conn;
801   verify->filename = strdup(filename);
802   verify->entity = strdup(entity);
803   verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
804   memcpy(verify->pk, pk, pk_len);
805   verify->pk_len = pk_len;
806   verify->pk_type = pk_type;
807   verify->completion = completion;
808   verify->context = context;
809
810   /* Check whether this key already exists */
811   if (stat(filename, &st) < 0) {
812     /* Key does not exist, ask user to verify the key and save it */
813
814     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
815                        SILCTXT_PUBKEY_RECEIVED, entity);
816     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
817                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
818     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
819                              SILCTXT_PUBKEY_ACCEPT);
820     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
821                             format, 0, verify);
822     g_free(format);
823     silc_free(fingerprint);
824     return;
825   } else {
826     /* The key already exists, verify it. */
827     SilcPublicKey public_key;
828     unsigned char *encpk;
829     uint32 encpk_len;
830
831     /* Load the key file */
832     if (!silc_pkcs_load_public_key(filename, &public_key, 
833                                    SILC_PKCS_FILE_PEM))
834       if (!silc_pkcs_load_public_key(filename, &public_key, 
835                                      SILC_PKCS_FILE_BIN)) {
836         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
837                            SILCTXT_PUBKEY_RECEIVED, entity);
838         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
839                            SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
840         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
841                            SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
842         format = format_get_text("fe-common/silc", NULL, NULL, NULL,
843                                  SILCTXT_PUBKEY_ACCEPT_ANYWAY);
844         keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
845                                 format, 0, verify);
846         g_free(format);
847         silc_free(fingerprint);
848         return;
849       }
850   
851     /* Encode the key data */
852     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
853     if (!encpk) {
854       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
855                          SILCTXT_PUBKEY_RECEIVED, entity);
856       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
857                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
858       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
859                          SILCTXT_PUBKEY_MALFORMED, entity);
860       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
861                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
862       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
863                               format, 0, verify);
864       g_free(format);
865       silc_free(fingerprint);
866       return;
867     }
868
869     /* Compare the keys */
870     if (memcmp(encpk, pk, encpk_len)) {
871       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
872                          SILCTXT_PUBKEY_RECEIVED, entity);
873       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
874                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
875       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
876                          SILCTXT_PUBKEY_NO_MATCH, entity);
877       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
878                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
879       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
880                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
881
882       /* Ask user to verify the key and save it */
883       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
884                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
885       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
886                               format, 0, verify);
887       g_free(format);
888       silc_free(fingerprint);
889       return;
890     }
891
892     /* Local copy matched */
893     if (completion)
894       completion(TRUE, context);
895     silc_free(fingerprint);
896   }
897 }
898
899 /* Verifies received public key. The `conn_type' indicates which entity
900    (server, client etc.) has sent the public key. If user decides to trust
901    the key may be saved as trusted public key for later use. The 
902    `completion' must be called after the public key has been verified. */
903
904 void 
905 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
906                        SilcSocketType conn_type, unsigned char *pk, 
907                        uint32 pk_len, SilcSKEPKType pk_type,
908                        SilcVerifyPublicKey completion, void *context)
909 {
910   silc_verify_public_key_internal(client, conn, conn_type, pk,
911                                   pk_len, pk_type,
912                                   completion, context);
913 }
914
915 /* Asks passphrase from user on the input line. */
916
917 typedef struct {
918   SilcAskPassphrase completion;
919   void *context;
920 } *AskPassphrase;
921
922 void ask_passphrase_completion(const char *passphrase, void *context)
923 {
924   AskPassphrase p = (AskPassphrase)context;
925   p->completion((unsigned char *)passphrase, 
926                 passphrase ? strlen(passphrase) : 0, p->context);
927   silc_free(p);
928 }
929
930 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
931                                 SilcAskPassphrase completion, void *context)
932 {
933   AskPassphrase p = silc_calloc(1, sizeof(*p));
934   p->completion = completion;
935   p->context = context;
936
937   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
938                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
939 }
940
941 /* Find authentication method and authentication data by hostname and
942    port. The hostname may be IP address as well. The found authentication
943    method and authentication data is returned to `auth_meth', `auth_data'
944    and `auth_data_len'. The function returns TRUE if authentication method
945    is found and FALSE if not. `conn' may be NULL. */
946
947 int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
948                          char *hostname, uint16 port,
949                          SilcProtocolAuthMeth *auth_meth,
950                          unsigned char **auth_data,
951                          uint32 *auth_data_len)
952 {
953   bool ret = TRUE;
954   SILC_SERVER_REC *server = conn ? conn->context : NULL;
955
956   /* XXX must resolve from configuration whether this connection has
957      any specific authentication data */
958
959   *auth_meth = SILC_AUTH_NONE;
960   *auth_data = NULL;
961   *auth_data_len = 0;
962
963   if (ret == FALSE) {
964     printformat_module("fe-common/silc", server, NULL,
965                        MSGLEVEL_MODES, SILCTXT_AUTH_METH_UNRESOLVED);
966   }
967
968   return ret;
969 }
970
971 /* Notifies application that failure packet was received.  This is called
972    if there is some protocol active in the client.  The `protocol' is the
973    protocol context.  The `failure' is opaque pointer to the failure
974    indication.  Note, that the `failure' is protocol dependant and application
975    must explicitly cast it to correct type.  Usually `failure' is 32 bit
976    failure type (see protocol specs for all protocol failure types). */
977
978 void silc_failure(SilcClient client, SilcClientConnection conn, 
979                   SilcProtocol protocol, void *failure)
980 {
981   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
982     SilcSKEStatus status = (SilcSKEStatus)failure;
983     
984     if (status == SILC_SKE_STATUS_BAD_VERSION)
985       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
986                          SILCTXT_KE_BAD_VERSION);
987     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
988       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
989                          SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
990     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
991       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
992                          SILCTXT_KE_UNKNOWN_GROUP);
993     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
994       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
995                          SILCTXT_KE_UNKNOWN_CIPHER);
996     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
997       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
998                          SILCTXT_KE_UNKNOWN_PKCS);
999     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1000       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1001                          SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1002     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1003       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1004                          SILCTXT_KE_UNKNOWN_HMAC);
1005     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1006       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1007                          SILCTXT_KE_INCORRECT_SIGNATURE);
1008   }
1009
1010   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1011     uint32 err = (uint32)failure;
1012
1013     if (err == SILC_AUTH_FAILED)
1014       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1015                          SILCTXT_AUTH_FAILED);
1016   }
1017 }
1018
1019 /* Asks whether the user would like to perform the key agreement protocol.
1020    This is called after we have received an key agreement packet or an
1021    reply to our key agreement packet. This returns TRUE if the user wants
1022    the library to perform the key agreement protocol and FALSE if it is not
1023    desired (application may start it later by calling the function
1024    silc_client_perform_key_agreement). */
1025
1026 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1027                        SilcClientEntry client_entry, char *hostname,
1028                        int port,
1029                        SilcKeyAgreementCallback *completion,
1030                        void **context)
1031 {
1032   char portstr[6];
1033
1034   /* We will just display the info on the screen and return FALSE and user
1035      will have to start the key agreement with a command. */
1036
1037   if (hostname) 
1038     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1039
1040   if (!hostname)
1041     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1042                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1043   else
1044     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_NOTICES,
1045                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST, 
1046                        client_entry->nickname, hostname, portstr);
1047
1048   *completion = NULL;
1049   *context = NULL;
1050
1051   return FALSE;
1052 }
1053
1054 /* SILC client operations */
1055 SilcClientOperations ops = {
1056   silc_say,
1057   silc_channel_message,
1058   silc_private_message,
1059   silc_notify,
1060   silc_command,
1061   silc_command_reply,
1062   silc_connect,
1063   silc_disconnect,
1064   silc_get_auth_method,
1065   silc_verify_public_key,
1066   silc_ask_passphrase,
1067   silc_failure,
1068   silc_key_agreement,
1069 };