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