Wed Mar 7 18:38:18 CET 2007 Jochen Eisinger <coffee@silcnet.org>
[silc.git] / apps / irssi / src / silc / core / silc-channels.c
1 /*
2   silc-channels.c : irssi
3
4   Copyright (C) 2000 - 2001, 2004 Timo Sirainen
5                             Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20 */
21
22 #include "module.h"
23
24 #include "net-nonblock.h"
25 #include "net-sendbuffer.h"
26 #include "signals.h"
27 #include "servers.h"
28 #include "commands.h"
29 #include "levels.h"
30 #include "modules.h"
31 #include "rawlog.h"
32 #include "misc.h"
33 #include "settings.h"
34
35 #include "channels-setup.h"
36
37 #include "silc-servers.h"
38 #include "silc-channels.h"
39 #include "silc-queries.h"
40 #include "silc-nicklist.h"
41 #include "silc-cmdqueue.h"
42 #include "window-item-def.h"
43
44 #include "fe-common/core/printtext.h"
45 #include "fe-common/silc/module-formats.h"
46
47 #include "silc-commands.h"
48
49 void sig_mime(SILC_SERVER_REC *server, SILC_CHANNEL_REC *channel,
50                const char *blob, const char *nick, int verified)
51 {
52   char type[128], enc[128];
53   unsigned char *data, *message;
54   SilcUInt32 data_len, message_len;
55
56   if (!(IS_SILC_SERVER(server)))
57     return;
58
59   message = silc_unescape_data(blob, &message_len);
60
61   memset(type, 0, sizeof(type));
62   memset(enc, 0, sizeof(enc));
63
64   if (!silc_mime_parse(message, message_len, NULL, 0, type, sizeof(type) - 1,
65                  enc, sizeof(enc) - 1, &data, &data_len)) {
66     silc_free(message);
67     return;
68   }
69
70   printformat_module("fe-common/silc", server,
71                       channel == NULL ? NULL : channel->name,
72                       MSGLEVEL_CRAP, SILCTXT_MESSAGE_DATA,
73                       nick == NULL ? "[<unknown>]" : nick, type);
74
75   silc_free(message);
76
77 }
78
79 SILC_CHANNEL_REC *silc_channel_create(SILC_SERVER_REC *server,
80                                       const char *name,
81                                       const char *visible_name,
82                                       int automatic)
83 {
84   SILC_CHANNEL_REC *rec;
85
86   g_return_val_if_fail(server == NULL || IS_SILC_SERVER(server), NULL);
87   g_return_val_if_fail(name != NULL, NULL);
88
89   rec = g_new0(SILC_CHANNEL_REC, 1);
90   rec->chat_type = SILC_PROTOCOL;
91   channel_init((CHANNEL_REC *)rec, (SERVER_REC *)server, name, name,
92                automatic);
93   return rec;
94 }
95
96 static void sig_channel_destroyed(SILC_CHANNEL_REC *channel)
97 {
98   if (!IS_SILC_CHANNEL(channel))
99     return;
100   if (channel->server && channel->server->disconnected)
101     return;
102
103   if (channel->server != NULL && !channel->left && !channel->kicked) {
104     /* destroying channel record without actually
105        having left the channel yet */
106     silc_command_exec(channel->server, "LEAVE", channel->name);
107     /* enable queueing because we destroy the channel immedially */
108     silc_queue_enable(channel->server->conn);
109   }
110 }
111
112 static void silc_channels_join(SILC_SERVER_REC *server,
113                                const char *channels, int automatic)
114 {
115   char **list, **tmp;
116   char *channel, *key;
117   SILC_CHANNEL_REC *chanrec;
118   CHANNEL_SETUP_REC *schannel;
119   GString *tmpstr;
120
121   list = g_strsplit(channels, ",", -1);
122   for (tmp = list; *tmp != NULL; tmp++) {
123     chanrec = silc_channel_find(server, *tmp);
124     if (chanrec)
125       continue;
126
127     channel = *tmp;
128     key = strchr(channel, ' ');
129     if (key != NULL) {
130             *key = '\0';
131             key++;
132     }
133     tmpstr = g_string_new(NULL);
134
135     schannel = channel_setup_find(channel, server->connrec->chatnet);
136     if (key && *key != '\0')
137             g_string_sprintfa(tmpstr, "%s %s", channel, key);
138     else if (schannel && schannel->password && schannel->password[0] != '\0')
139             g_string_sprintfa(tmpstr, "%s %s", channel, schannel->password);
140     else
141             g_string_sprintfa(tmpstr, "%s", channel);
142
143     silc_command_exec(server, "JOIN", tmpstr->str);
144
145     g_string_free(tmpstr, FALSE);
146   }
147
148   g_strfreev(list);
149 }
150
151 static void sig_connected(SILC_SERVER_REC *server)
152 {
153   if (IS_SILC_SERVER(server))
154     server->channels_join = (void *) silc_channels_join;
155 }
156
157 /* "server quit" signal from the core to indicate that QUIT command
158    was called. */
159
160 static void sig_server_quit(SILC_SERVER_REC *server, const char *msg)
161 {
162   if (IS_SILC_SERVER(server) && server->conn && server->conn->sock)
163     silc_command_exec(server, "QUIT", msg);
164 }
165
166 static void sig_gui_quit(SILC_SERVER_REC *server, const char *msg)
167 {
168   silc_client_stop(silc_client);
169 }
170
171 /* Find Irssi channel entry by SILC channel entry */
172
173 SILC_CHANNEL_REC *silc_channel_find_entry(SILC_SERVER_REC *server,
174                                           SilcChannelEntry entry)
175 {
176   GSList *tmp;
177
178   g_return_val_if_fail(IS_SILC_SERVER(server), NULL);
179
180   for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
181     SILC_CHANNEL_REC *rec = tmp->data;
182
183     if (rec->entry == entry)
184       return rec;
185   }
186
187   return NULL;
188 }
189
190 /* PART (LEAVE) command. */
191
192 static void command_part(const char *data, SILC_SERVER_REC *server,
193                          WI_ITEM_REC *item)
194 {
195   SILC_CHANNEL_REC *chanrec;
196   char userhost[256];
197
198   CMD_SILC_SERVER(server);
199
200   if (!IS_SILC_SERVER(server) || !server->connected)
201     cmd_return_error(CMDERR_NOT_CONNECTED);
202
203   if (!strcmp(data, "*") || *data == '\0') {
204     if (!IS_SILC_CHANNEL(item))
205       cmd_return_error(CMDERR_NOT_JOINED);
206     data = item->visible_name;
207   }
208
209   chanrec = silc_channel_find(server, data);
210   if (chanrec == NULL)
211     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
212
213   memset(userhost, 0, sizeof(userhost));
214   snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
215            server->conn->local_entry->username,
216            server->conn->local_entry->hostname);
217   signal_emit("message part", 5, server, chanrec->name,
218               server->nick, userhost, "");
219
220   chanrec->left = TRUE;
221   silc_command_exec(server, "LEAVE", chanrec->name);
222   /* enable queueing because we destroy the channel immedially */
223   silc_queue_enable(server->conn);
224   signal_stop();
225
226   channel_destroy(CHANNEL(chanrec));
227 }
228
229
230 /* ACTION local command. */
231
232 static void command_action(const char *data, SILC_SERVER_REC *server,
233                            WI_ITEM_REC *item)
234 {
235   GHashTable *optlist;
236   char *target, *msg;
237   char *message = NULL;
238   int target_type;
239   void *free_arg;
240
241   CMD_SILC_SERVER(server);
242   if (!IS_SILC_SERVER(server) || !server->connected)
243     cmd_return_error(CMDERR_NOT_CONNECTED);
244
245   if ((item != NULL) && (!IS_SILC_CHANNEL(item) && !IS_SILC_QUERY(item)))
246     cmd_return_error(CMDERR_NOT_JOINED);
247
248   /* Now parse all arguments */
249   if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS |
250                       PARAM_FLAG_GETREST,
251                       "action", &optlist, &target, &msg))
252     return;
253
254   if (*target == '\0' || *msg == '\0')
255     cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
256
257   if (strcmp(target, "*") == 0) {
258     /* send to active channel/query */
259     if (item == NULL)
260       cmd_param_error(CMDERR_NOT_JOINED);
261
262     target_type = IS_SILC_CHANNEL(item) ?
263             SEND_TARGET_CHANNEL : SEND_TARGET_NICK;
264     target = (char *)window_item_get_target(item);
265   } else if (g_hash_table_lookup(optlist, "channel") != NULL)
266     target_type = SEND_TARGET_CHANNEL;
267   else {
268     target_type = SEND_TARGET_NICK;
269   }
270
271   if (!silc_term_utf8()) {
272     int len = silc_utf8_encoded_len(msg, strlen(msg),
273                                     SILC_STRING_LOCALE);
274     message = silc_calloc(len + 1, sizeof(*message));
275     g_return_if_fail(message != NULL);
276     silc_utf8_encode(msg, strlen(msg), SILC_STRING_LOCALE,
277                      message, len);
278   }
279
280   if (target != NULL) {
281     if (target_type == SEND_TARGET_CHANNEL) {
282       if (silc_send_channel(server, target, (message != NULL ? message : msg),
283                             SILC_MESSAGE_FLAG_ACTION | SILC_MESSAGE_FLAG_UTF8 |
284                             (g_hash_table_lookup(optlist, "sign") != NULL ?
285                              SILC_MESSAGE_FLAG_SIGNED : 0))) {
286         if (g_hash_table_lookup(optlist, "sign"))
287           signal_emit("message silc signed_own_action", 3, server, msg, target);
288         else
289           signal_emit("message silc own_action", 3, server, msg, target);
290       }
291     } else {
292       if (silc_send_msg(server, target, (message != NULL ? message : msg),
293                         (message != NULL ? strlen(message) : strlen(msg)),
294                         SILC_MESSAGE_FLAG_ACTION | SILC_MESSAGE_FLAG_UTF8 |
295                         (g_hash_table_lookup(optlist, "sign") != NULL ?
296                          SILC_MESSAGE_FLAG_SIGNED : 0))) {
297         if (g_hash_table_lookup(optlist, "sign"))
298           signal_emit("message silc signed_own_private_action", 3,
299                           server, msg, target);
300         else
301           signal_emit("message silc own_private_action", 3,
302                           server, msg, target);
303       }
304     }
305   }
306
307   cmd_params_free(free_arg);
308   silc_free(message);
309 }
310
311 /* ME local command. */
312
313 static void command_me(const char *data, SILC_SERVER_REC *server,
314                        WI_ITEM_REC *item)
315 {
316   char *tmpcmd;
317
318   CMD_SILC_SERVER(server);
319   if (!IS_SILC_SERVER(server) || !server->connected)
320     cmd_return_error(CMDERR_NOT_CONNECTED);
321
322   if (!IS_SILC_CHANNEL(item) && !IS_SILC_QUERY(item))
323     cmd_return_error(CMDERR_NOT_JOINED);
324
325   if (IS_SILC_CHANNEL(item))
326     tmpcmd = g_strdup_printf("-channel %s %s", item->visible_name, data);
327   else
328     tmpcmd = g_strdup_printf("%s %s", item->visible_name, data);
329
330   command_action(tmpcmd, server, item);
331   g_free(tmpcmd);
332 }
333
334 /* NOTICE local command. */
335
336 static void command_notice(const char *data, SILC_SERVER_REC *server,
337                            WI_ITEM_REC *item)
338 {
339   GHashTable *optlist;
340   char *target, *msg;
341   char *message = NULL;
342   int target_type;
343   void *free_arg;
344
345   CMD_SILC_SERVER(server);
346   if (!IS_SILC_SERVER(server) || !server->connected)
347     cmd_return_error(CMDERR_NOT_CONNECTED);
348
349   if ((item != NULL) && (!IS_SILC_CHANNEL(item) && !IS_SILC_QUERY(item)))
350     cmd_return_error(CMDERR_NOT_JOINED);
351
352   /* Now parse all arguments */
353   if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS |
354                       PARAM_FLAG_GETREST,
355                       "notice", &optlist, &target, &msg))
356     return;
357
358   if (*target == '\0' || *msg == '\0')
359     cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
360
361   if (strcmp(target, "*") == 0) {
362     /* send to active channel/query */
363     if (item == NULL)
364       cmd_param_error(CMDERR_NOT_JOINED);
365
366     target_type = IS_SILC_CHANNEL(item) ?
367             SEND_TARGET_CHANNEL : SEND_TARGET_NICK;
368     target = (char *)window_item_get_target(item);
369   } else if (g_hash_table_lookup(optlist, "channel") != NULL)
370     target_type = SEND_TARGET_CHANNEL;
371   else {
372     target_type = SEND_TARGET_NICK;
373   }
374
375   if (!silc_term_utf8()) {
376     int len = silc_utf8_encoded_len(msg, strlen(msg),
377                                     SILC_STRING_LOCALE);
378     message = silc_calloc(len + 1, sizeof(*message));
379     g_return_if_fail(message != NULL);
380     silc_utf8_encode(msg, strlen(msg), SILC_STRING_LOCALE,
381                      message, len);
382   }
383
384   if (target != NULL) {
385     if (target_type == SEND_TARGET_CHANNEL) {
386       if (silc_send_channel(server, target, (message != NULL ? message : msg),
387                             SILC_MESSAGE_FLAG_NOTICE | SILC_MESSAGE_FLAG_UTF8 |
388                             (g_hash_table_lookup(optlist, "sign") != NULL ?
389                              SILC_MESSAGE_FLAG_SIGNED : 0))) {
390         if (g_hash_table_lookup(optlist, "sign"))
391           signal_emit("message silc signed_own_notice", 3, server, msg, target);
392         else
393           signal_emit("message silc own_notice", 3, server, msg, target);
394       }
395     } else {
396       if (silc_send_msg(server, target, (message != NULL ? message : msg),
397                         (message != NULL ? strlen(message) : strlen(msg)),
398                         SILC_MESSAGE_FLAG_NOTICE | SILC_MESSAGE_FLAG_UTF8 |
399                         (g_hash_table_lookup(optlist, "sign") != NULL ?
400                          SILC_MESSAGE_FLAG_SIGNED : 0))) {
401         if (g_hash_table_lookup(optlist, "sign"))
402           signal_emit("message silc signed_own_private_notice", 3,
403                           server, msg, target);
404         else
405           signal_emit("message silc own_private_notice", 3,
406                           server, msg, target);
407       }
408     }
409   }
410
411   cmd_params_free(free_arg);
412   silc_free(message);
413 }
414
415 /* AWAY local command.  Sends UMODE command that sets the SILC_UMODE_GONE
416    flag. */
417
418 bool silc_set_away(const char *reason, SILC_SERVER_REC *server)
419 {
420   bool set;
421
422   if (!IS_SILC_SERVER(server) || !server->connected)
423     return FALSE;
424
425   if (*reason == '\0') {
426     /* Remove any possible away message */
427     silc_client_set_away_message(silc_client, server->conn, NULL);
428     set = FALSE;
429
430     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
431                        SILCTXT_UNSET_AWAY);
432   } else {
433     /* Set the away message */
434     silc_client_set_away_message(silc_client, server->conn, (char *)reason);
435     set = TRUE;
436
437     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
438                        SILCTXT_SET_AWAY, reason);
439   }
440
441   server->usermode_away = set;
442   g_free_and_null(server->away_reason);
443   if (set)
444     server->away_reason = g_strdup((char *)reason);
445
446   signal_emit("away mode changed", 1, server);
447
448   return set;
449 }
450
451 static void command_away(const char *data, SILC_SERVER_REC *server,
452                          WI_ITEM_REC *item)
453 {
454   CMD_SILC_SERVER(server);
455
456   if (!IS_SILC_SERVER(server) || !server->connected)
457     cmd_return_error(CMDERR_NOT_CONNECTED);
458
459   g_free_and_null(server->away_reason);
460   if ((data) && (*data != '\0'))
461     server->away_reason = g_strdup(data);
462
463   silc_command_exec(server, "UMODE",
464                     (server->away_reason != NULL) ? "+g" : "-g");
465 }
466
467 typedef struct {
468   int type;                     /* 1 = msg, 2 = channel */
469   bool responder;
470   SILC_SERVER_REC *server;
471 } *KeyInternal;
472
473 /* Key agreement callback that is called after the key agreement protocol
474    has been performed. This is called also if error occured during the
475    key agreement protocol. The `key' is the allocated key material and
476    the caller is responsible of freeing it. The `key' is NULL if error
477    has occured. The application can freely use the `key' to whatever
478    purpose it needs. See lib/silcske/silcske.h for the definition of
479    the SilcSKEKeyMaterial structure. */
480
481 static void keyagr_completion(SilcClient client,
482                               SilcClientConnection conn,
483                               SilcClientEntry client_entry,
484                               SilcKeyAgreementStatus status,
485                               SilcSKEKeyMaterial *key,
486                               void *context)
487 {
488   KeyInternal i = (KeyInternal)context;
489
490   switch(status) {
491   case SILC_KEY_AGREEMENT_OK:
492     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
493                        SILCTXT_KEY_AGREEMENT_OK, client_entry->nickname);
494
495     if (i->type == 1) {
496       /* Set the private key for this client */
497       silc_client_del_private_message_key(client, conn, client_entry);
498       silc_client_add_private_message_key_ske(client, conn, client_entry,
499                                               NULL, NULL, key, i->responder);
500       printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
501                          SILCTXT_KEY_AGREEMENT_PRIVMSG,
502                          client_entry->nickname);
503       silc_ske_free_key_material(key);
504     }
505
506     break;
507
508   case SILC_KEY_AGREEMENT_ERROR:
509     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
510                        SILCTXT_KEY_AGREEMENT_ERROR, client_entry->nickname);
511     break;
512
513   case SILC_KEY_AGREEMENT_FAILURE:
514     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
515                        SILCTXT_KEY_AGREEMENT_FAILURE, client_entry->nickname);
516     break;
517
518   case SILC_KEY_AGREEMENT_TIMEOUT:
519     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
520                        SILCTXT_KEY_AGREEMENT_TIMEOUT, client_entry->nickname);
521     break;
522
523   case SILC_KEY_AGREEMENT_ABORTED:
524     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
525                        SILCTXT_KEY_AGREEMENT_ABORTED, client_entry->nickname);
526     break;
527
528   case SILC_KEY_AGREEMENT_ALREADY_STARTED:
529     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
530                        SILCTXT_KEY_AGREEMENT_ALREADY_STARTED,
531                        client_entry->nickname);
532     break;
533
534   case SILC_KEY_AGREEMENT_SELF_DENIED:
535     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
536                        SILCTXT_KEY_AGREEMENT_SELF_DENIED);
537     break;
538
539   default:
540     break;
541   }
542
543   if (i)
544     silc_free(i);
545 }
546
547 /* Local command KEY. This command is used to set and unset private
548    keys for channels, set and unset private keys for private messages
549    with remote clients and to send key agreement requests and
550    negotiate the key agreement protocol with remote client.  The
551    key agreement is supported only to negotiate private message keys,
552    it currently cannot be used to negotiate private keys for channels,
553    as it is not convenient for that purpose. */
554
555 typedef struct {
556   SILC_SERVER_REC *server;
557   char *data;
558   char *nick;
559   WI_ITEM_REC *item;
560 } *KeyGetClients;
561
562 /* Callback to be called after client information is resolved from the
563    server. */
564
565 static void silc_client_command_key_get_clients(SilcClient client,
566                                                 SilcClientConnection conn,
567                                                 SilcClientEntry *clients,
568                                                 SilcUInt32 clients_count,
569                                                 void *context)
570 {
571   KeyGetClients internal = (KeyGetClients)context;
572
573   if (!clients) {
574     printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Unknown nick: %s",
575               internal->nick);
576     silc_free(internal->data);
577     silc_free(internal->nick);
578     silc_free(internal);
579     return;
580   }
581
582   signal_emit("command key", 3, internal->data, internal->server,
583               internal->item);
584
585   silc_free(internal->data);
586   silc_free(internal->nick);
587   silc_free(internal);
588 }
589
590 static void command_key(const char *data, SILC_SERVER_REC *server,
591                         WI_ITEM_REC *item)
592 {
593   SilcClientConnection conn;
594   SilcClientEntry *entrys, client_entry = NULL;
595   SilcUInt32 entry_count;
596   SILC_CHANNEL_REC *chanrec = NULL;
597   SilcChannelEntry channel_entry = NULL;
598   char *nickname = NULL, *tmp;
599   int command = 0, port = 0, type = 0;
600   char *hostname = NULL;
601   KeyInternal internal = NULL;
602   SilcUInt32 argc = 0;
603   unsigned char **argv;
604   SilcUInt32 *argv_lens, *argv_types;
605   char *bindhost = NULL;
606
607   CMD_SILC_SERVER(server);
608
609   if (!server || !IS_SILC_SERVER(server) || !server->connected)
610     cmd_return_error(CMDERR_NOT_CONNECTED);
611
612   conn = server->conn;
613
614   /* Now parse all arguments */
615   tmp = g_strconcat("KEY", " ", data, NULL);
616   silc_parse_command_line(tmp, &argv, &argv_lens, &argv_types, &argc, 7);
617   g_free(tmp);
618
619   if (argc < 4)
620     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
621
622   /* Get type */
623   if (!strcasecmp(argv[1], "msg"))
624     type = 1;
625   if (!strcasecmp(argv[1], "channel"))
626     type = 2;
627
628   if (type == 0)
629     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
630
631   if (type == 1) {
632     if (argv[2][0] == '*') {
633       nickname = strdup("*");
634     } else {
635       /* Parse the typed nickname. */
636       if (!silc_parse_userfqdn(argv[2], &nickname, NULL)) {
637         printformat_module("fe-common/silc", server, NULL,
638                            MSGLEVEL_CRAP, SILCTXT_BAD_NICK, argv[2]);
639         return;
640       }
641
642       /* Find client entry */
643       entrys = silc_client_get_clients_local(silc_client, conn, nickname,
644                                              argv[2], &entry_count);
645       if (!entrys) {
646         KeyGetClients inter = silc_calloc(1, sizeof(*inter));
647         inter->server = server;
648         inter->data = strdup(data);
649         inter->nick = strdup(nickname);
650         inter->item = item;
651         silc_client_get_clients(silc_client, conn, nickname, argv[2],
652                                 silc_client_command_key_get_clients, inter);
653         goto out;
654       }
655       client_entry = entrys[0];
656       silc_free(entrys);
657     }
658   }
659
660   if (type == 2) {
661     /* Get channel entry */
662     char *name;
663
664     if (argv[2][0] == '*') {
665       if (!conn->current_channel) {
666         silc_free(nickname);
667         cmd_return_error(CMDERR_NOT_JOINED);
668       }
669       name = conn->current_channel->channel_name;
670     } else {
671       name = argv[2];
672     }
673
674     chanrec = silc_channel_find(server, name);
675     if (chanrec == NULL) {
676       silc_free(nickname);
677       cmd_return_error(CMDERR_CHAN_NOT_FOUND);
678     }
679     channel_entry = chanrec->entry;
680   }
681
682   /* Set command */
683   if (!strcasecmp(argv[3], "set")) {
684     command = 1;
685
686     if (argc >= 5) {
687       char *cipher = NULL, *hmac = NULL;
688
689       if (type == 1 && client_entry) {
690         /* Set private message key */
691         bool responder = FALSE;
692
693         silc_client_del_private_message_key(silc_client, conn, client_entry);
694
695         if (argc >= 6) {
696           if (!strcasecmp(argv[5], "-responder"))
697             responder = TRUE;
698           else
699             cipher = argv[5];
700         }
701         if (argc >= 7) {
702           if (!strcasecmp(argv[6], "-responder"))
703             responder = TRUE;
704           else
705             hmac = argv[6];
706         }
707         if (argc >= 8) {
708           if (!strcasecmp(argv[7], "-responder"))
709             responder = TRUE;
710         }
711
712         silc_client_add_private_message_key(silc_client, conn, client_entry,
713                                             cipher, hmac,
714                                             argv[4], argv_lens[4],
715                                             (argv[4][0] == '*' ?
716                                              TRUE : FALSE), responder);
717
718         /* Send the key to the remote client so that it starts using it
719            too. */
720         /* XXX for now we don't do this.  This feature is pretty stupid
721            and should perhaps be removed altogether from SILC.
722         silc_client_send_private_message_key(silc_client, conn,
723                                              client_entry, TRUE);
724         */
725       } else if (type == 2) {
726         /* Set private channel key */
727         if (!(channel_entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
728           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
729                              SILCTXT_CH_PRIVATE_KEY_NOMODE,
730                              channel_entry->channel_name);
731           goto out;
732         }
733
734         if (argc >= 6)
735           cipher = argv[5];
736         if (argc >= 7)
737           hmac = argv[6];
738
739         if (!silc_client_add_channel_private_key(silc_client, conn,
740                                                  channel_entry, NULL,
741                                                  cipher, hmac,
742                                                  argv[4],
743                                                  argv_lens[4], NULL)) {
744           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
745                              SILCTXT_CH_PRIVATE_KEY_ERROR,
746                              channel_entry->channel_name);
747           goto out;
748         }
749
750         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
751                            SILCTXT_CH_PRIVATE_KEY_ADD,
752                            channel_entry->channel_name);
753       }
754     }
755
756     goto out;
757   }
758
759   /* Unset command */
760   if (!strcasecmp(argv[3], "unset")) {
761     command = 2;
762
763     if (type == 1 && client_entry) {
764       /* Unset private message key */
765       silc_client_del_private_message_key(silc_client, conn, client_entry);
766     } else if (type == 2) {
767       /* Unset channel key(s) */
768       SilcChannelPrivateKey *keys;
769       SilcUInt32 keys_count;
770       int number;
771
772       if (argc == 4)
773         silc_client_del_channel_private_keys(silc_client, conn,
774                                              channel_entry);
775
776       if (argc > 4) {
777         number = atoi(argv[4]);
778         keys = silc_client_list_channel_private_keys(silc_client, conn,
779                                                      channel_entry,
780                                                      &keys_count);
781         if (!keys)
782           goto out;
783
784         if (!number || number > keys_count) {
785           silc_client_free_channel_private_keys(keys, keys_count);
786           goto out;
787         }
788
789         silc_client_del_channel_private_key(silc_client, conn, channel_entry,
790                                             keys[number - 1]);
791         silc_client_free_channel_private_keys(keys, keys_count);
792       }
793
794       goto out;
795     }
796   }
797
798   /* List command */
799   if (!strcasecmp(argv[3], "list")) {
800     command = 3;
801
802     if (type == 1) {
803       SilcPrivateMessageKeys keys;
804       SilcUInt32 keys_count;
805       int k, i, len;
806       char buf[1024];
807
808       keys = silc_client_list_private_message_keys(silc_client, conn,
809                                                    &keys_count);
810       if (!keys)
811         goto out;
812
813       /* list the private message key(s) */
814       if (nickname[0] == '*') {
815         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
816                            SILCTXT_PRIVATE_KEY_LIST);
817         for (k = 0; k < keys_count; k++) {
818           memset(buf, 0, sizeof(buf));
819           strncat(buf, "  ", 2);
820           len = strlen(keys[k].client_entry->nickname);
821           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
822           if (len < 30)
823             for (i = 0; i < 30 - len; i++)
824               strcat(buf, " ");
825           strcat(buf, " ");
826
827           len = strlen(keys[k].cipher);
828           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
829           if (len < 14)
830             for (i = 0; i < 14 - len; i++)
831               strcat(buf, " ");
832           strcat(buf, " ");
833
834           if (keys[k].key)
835             strcat(buf, "<hidden>");
836           else
837             strcat(buf, "*generated*");
838
839           silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
840         }
841       } else {
842         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
843                            SILCTXT_PRIVATE_KEY_LIST_NICK,
844                            client_entry->nickname);
845         for (k = 0; k < keys_count; k++) {
846           if (keys[k].client_entry != client_entry)
847             continue;
848
849           memset(buf, 0, sizeof(buf));
850           strncat(buf, "  ", 2);
851           len = strlen(keys[k].client_entry->nickname);
852           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
853           if (len < 30)
854             for (i = 0; i < 30 - len; i++)
855               strcat(buf, " ");
856           strcat(buf, " ");
857
858           len = strlen(keys[k].cipher);
859           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
860           if (len < 14)
861             for (i = 0; i < 14 - len; i++)
862               strcat(buf, " ");
863           strcat(buf, " ");
864
865           if (keys[k].key)
866             strcat(buf, "<hidden>");
867           else
868             strcat(buf, "*generated*");
869
870           silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
871         }
872       }
873
874       silc_client_free_private_message_keys(keys, keys_count);
875
876     } else if (type == 2) {
877       SilcChannelPrivateKey *keys;
878       SilcUInt32 keys_count;
879       int k, i, len;
880       char buf[1024];
881
882       keys = silc_client_list_channel_private_keys(silc_client, conn,
883                                                    channel_entry,
884                                                    &keys_count);
885
886       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
887                          SILCTXT_CH_PRIVATE_KEY_LIST,
888                          channel_entry->channel_name);
889
890       if (!keys)
891         goto out;
892
893       for (k = 0; k < keys_count; k++) {
894         memset(buf, 0, sizeof(buf));
895         strncat(buf, "  ", 2);
896
897         len = strlen(silc_cipher_get_name(keys[k]->cipher));
898         strncat(buf, silc_cipher_get_name(keys[k]->cipher),
899                 len > 16 ? 16 : len);
900         if (len < 16)
901           for (i = 0; i < 16 - len; i++)
902             strcat(buf, " ");
903         strcat(buf, " ");
904
905         len = strlen(silc_hmac_get_name(keys[k]->hmac));
906         strncat(buf, silc_hmac_get_name(keys[k]->hmac), len > 16 ? 16 : len);
907         if (len < 16)
908           for (i = 0; i < 16 - len; i++)
909             strcat(buf, " ");
910         strcat(buf, " ");
911
912         strcat(buf, "<hidden>");
913
914         silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
915       }
916
917       silc_client_free_channel_private_keys(keys, keys_count);
918     }
919
920     goto out;
921   }
922
923   /* Send command is used to send key agreement */
924   if (!strcasecmp(argv[3], "agreement")) {
925     command = 4;
926
927     if (argc >= 5)
928       hostname = argv[4];
929     if (argc >= 6)
930       port = atoi(argv[5]);
931
932     internal = silc_calloc(1, sizeof(*internal));
933     internal->type = type;
934     internal->server = server;
935
936     if (!hostname) {
937       if (settings_get_bool("use_auto_addr")) {
938
939         hostname = (char *)settings_get_str("auto_public_ip");
940
941         /* If the hostname isn't set, treat this case as if auto_public_ip
942            wasn't set. */
943         if ((hostname) && (*hostname == '\0')) {
944            hostname = NULL;
945         } else {
946           bindhost = (char *)settings_get_str("auto_bind_ip");
947
948           /* if the bind_ip isn't set, but the public_ip IS, then assume then
949              public_ip is the same value as the bind_ip. */
950           if ((bindhost) && (*bindhost == '\0'))
951             bindhost = hostname;
952           port = settings_get_int("auto_bind_port");
953         }
954       }  /* if use_auto_addr */
955     }
956   }
957
958   /* Start command is used to start key agreement (after receiving the
959      key_agreement client operation). */
960   if (!strcasecmp(argv[3], "negotiate")) {
961     command = 5;
962
963     if (argc >= 5)
964       hostname = argv[4];
965     if (argc >= 6)
966       port = atoi(argv[5]);
967
968     internal = silc_calloc(1, sizeof(*internal));
969     internal->type = type;
970     internal->server = server;
971   }
972
973   /* Change current channel private key */
974   if (!strcasecmp(argv[3], "change")) {
975     command = 6;
976     if (type == 2) {
977       /* Unset channel key(s) */
978       SilcChannelPrivateKey *keys;
979       SilcUInt32 keys_count;
980       int number;
981
982       keys = silc_client_list_channel_private_keys(silc_client, conn,
983                                                    channel_entry,
984                                                    &keys_count);
985       if (!keys)
986         goto out;
987
988       if (argc == 4) {
989         chanrec->cur_key++;
990         if (chanrec->cur_key >= keys_count)
991           chanrec->cur_key = 0;
992       }
993
994       if (argc > 4) {
995         number = atoi(argv[4]);
996         if (!number || number > keys_count)
997           chanrec->cur_key = 0;
998         else
999           chanrec->cur_key = number - 1;
1000       }
1001
1002       /* Set the current channel private key */
1003       silc_client_current_channel_private_key(silc_client, conn,
1004                                               channel_entry,
1005                                               keys[chanrec->cur_key]);
1006       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1007                          SILCTXT_CH_PRIVATE_KEY_CHANGE, chanrec->cur_key + 1,
1008                          channel_entry->channel_name);
1009
1010       silc_client_free_channel_private_keys(keys, keys_count);
1011       goto out;
1012     }
1013   }
1014
1015   if (command == 0) {
1016     silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO,
1017              "Usage: /KEY msg|channel <nickname|channel> "
1018              "set|unset|agreement|negotiate [<arguments>]");
1019     goto out;
1020   }
1021
1022   if (command == 4 && client_entry) {
1023     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1024                        SILCTXT_KEY_AGREEMENT, argv[2]);
1025     internal->responder = TRUE;
1026     silc_client_send_key_agreement(
1027                            silc_client, conn, client_entry, hostname,
1028                            bindhost, port,
1029                            settings_get_int("key_exchange_timeout_secs"),
1030                            keyagr_completion, internal);
1031     if (!hostname)
1032       silc_free(internal);
1033     goto out;
1034   }
1035
1036   if (command == 5 && client_entry && hostname) {
1037     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1038                        SILCTXT_KEY_AGREEMENT_NEGOTIATE, argv[2]);
1039     internal->responder = FALSE;
1040     silc_client_perform_key_agreement(silc_client, conn, client_entry,
1041                                       hostname, port, keyagr_completion,
1042                                       internal);
1043     goto out;
1044   }
1045
1046  out:
1047   silc_free(nickname);
1048 }
1049
1050 void silc_list_key(const char *pub_filename, int verbose)
1051 {
1052   SilcPublicKey public_key;
1053   SilcPublicKeyIdentifier ident;
1054   char *fingerprint, *babbleprint;
1055   unsigned char *pk;
1056   SilcUInt32 pk_len;
1057   SilcPKCS pkcs;
1058   SilcUInt32 key_len = 0;
1059   int is_server_key = (strstr(pub_filename, "serverkeys") != NULL);
1060
1061   if (silc_pkcs_load_public_key((char *)pub_filename, &public_key,
1062                                 SILC_PKCS_FILE_PEM) == FALSE)
1063     if (silc_pkcs_load_public_key((char *)pub_filename, &public_key,
1064                                   SILC_PKCS_FILE_BIN) == FALSE) {
1065       printformat_module("fe-common/silc", NULL, NULL,
1066                          MSGLEVEL_CRAP, SILCTXT_LISTKEY_LOADPUB,
1067                          pub_filename);
1068       return;
1069     }
1070
1071   ident = silc_pkcs_decode_identifier(public_key->identifier);
1072
1073   pk = silc_pkcs_public_key_encode(public_key, &pk_len);
1074   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1075   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
1076
1077   if (silc_pkcs_alloc(public_key->name, &pkcs)) {
1078     key_len = silc_pkcs_public_key_set(pkcs, public_key);
1079     silc_pkcs_free(pkcs);
1080   }
1081
1082   printformat_module("fe-common/silc", NULL, NULL,
1083                      MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_FILE,
1084                      pub_filename);
1085
1086   if (verbose)
1087     printformat_module("fe-common/silc", NULL, NULL,
1088                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_ALG,
1089                        public_key->name);
1090   if (key_len && verbose)
1091     printformat_module("fe-common/silc", NULL, NULL,
1092                         MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_BITS,
1093                         (unsigned int)key_len);
1094   if (ident->realname && (!is_server_key || verbose))
1095     printformat_module("fe-common/silc", NULL, NULL,
1096                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_RN,
1097                        ident->realname);
1098   if (ident->username && verbose)
1099     printformat_module("fe-common/silc", NULL, NULL,
1100                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_UN,
1101                        ident->username);
1102   if (ident->host && (is_server_key || verbose))
1103     printformat_module("fe-common/silc", NULL, NULL,
1104                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_HN,
1105                        ident->host);
1106   if (ident->email && verbose)
1107     printformat_module("fe-common/silc", NULL, NULL,
1108                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_EMAIL,
1109                        ident->email);
1110   if (ident->org && verbose)
1111     printformat_module("fe-common/silc", NULL, NULL,
1112                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_ORG,
1113                        ident->org);
1114   if (ident->country && verbose)
1115     printformat_module("fe-common/silc", NULL, NULL,
1116                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_C,
1117                        ident->country);
1118
1119   if (verbose) {
1120     printformat_module("fe-common/silc", NULL, NULL,
1121                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_FINGER,
1122                        fingerprint);
1123     printformat_module("fe-common/silc", NULL, NULL,
1124                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_BABL,
1125                        babbleprint);
1126   }
1127
1128   silc_free(fingerprint);
1129   silc_free(babbleprint);
1130   silc_free(pk);
1131   silc_pkcs_public_key_free(public_key);
1132   silc_pkcs_free_identifier(ident);
1133
1134 }
1135
1136
1137 void silc_list_keys_in_dir(const char *dirname, const char *where)
1138 {
1139   DIR *dir;
1140   struct dirent *entry;
1141
1142   dir = opendir(dirname);
1143
1144   if (dir == NULL)
1145           cmd_return_error(CMDERR_ERRNO);
1146
1147   printformat_module("fe-common/silc", NULL, NULL,
1148                      MSGLEVEL_CRAP, SILCTXT_LISTKEY_LIST,
1149                      where);
1150
1151   rewinddir(dir);
1152
1153   while ((entry = readdir(dir)) != NULL) {
1154     /* try to open everything that isn't a directory */
1155     struct stat buf;
1156     char filename[256];
1157
1158     snprintf(filename, sizeof(filename) - 1, "%s/%s", dirname, entry->d_name);
1159     if (!stat(filename, &buf) && S_ISREG(buf.st_mode))
1160       silc_list_key(filename, FALSE);
1161   }
1162
1163   closedir(dir);
1164 }
1165
1166 void silc_list_file(const char *filename)
1167 {
1168
1169   char path[256];
1170   struct stat buf;
1171
1172   snprintf(path, sizeof(path) - 1, "%s", filename);
1173   if (!stat(path, &buf) && S_ISREG(buf.st_mode))
1174     goto list_key;
1175
1176   snprintf(path, sizeof(path) - 1, "%s/%s", get_irssi_dir(), filename);
1177   if (!stat(path, &buf) && S_ISREG(buf.st_mode))
1178     goto list_key;
1179
1180   snprintf(path,sizeof(path) - 1, "%s/clientkeys/%s", get_irssi_dir(),
1181            filename);
1182   if (!stat(path, &buf) && S_ISREG(buf.st_mode))
1183     goto list_key;
1184
1185   snprintf(path,sizeof(path) - 1, "%s/serverkeys/%s", get_irssi_dir(),
1186            filename);
1187   if (!stat(path, &buf) && S_ISREG(buf.st_mode))
1188     goto list_key;
1189
1190   return;
1191
1192 list_key:
1193
1194   silc_list_key(path, TRUE);
1195
1196 }
1197
1198 /* Lists locally saved client and server public keys. */
1199 static void command_listkeys(const char *data, SILC_SERVER_REC *server,
1200                              WI_ITEM_REC *item)
1201 {
1202   GHashTable *optlist;
1203   char *filename;
1204   void *free_arg;
1205   char dirname[256];
1206
1207   if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
1208                       PARAM_FLAG_GETREST, "listkeys", &optlist,
1209                       &filename))
1210     return;
1211
1212   if (*filename != '\0') {
1213
1214     silc_list_file(filename);
1215
1216   } else {
1217     int clients, servers;
1218
1219     clients = (g_hash_table_lookup(optlist, "clients") != NULL);
1220     servers = (g_hash_table_lookup(optlist, "servers") != NULL);
1221
1222     if (!(clients || servers))
1223       clients = servers = 1;
1224
1225     if (servers) {
1226       snprintf(dirname, sizeof(dirname) - 1, "%s/serverkeys", get_irssi_dir());
1227       silc_list_keys_in_dir(dirname, "server");
1228     }
1229
1230     if (clients) {
1231       snprintf(dirname, sizeof(dirname) - 1, "%s/clientkeys", get_irssi_dir());
1232       silc_list_keys_in_dir(dirname, "client");
1233     }
1234   }
1235   cmd_params_free(free_arg);
1236 }
1237
1238 void silc_channels_init(void)
1239 {
1240   signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1241   signal_add("server connected", (SIGNAL_FUNC) sig_connected);
1242   signal_add("server quit", (SIGNAL_FUNC) sig_server_quit);
1243   signal_add("gui exit", (SIGNAL_FUNC) sig_gui_quit);
1244   signal_add("mime", (SIGNAL_FUNC) sig_mime);
1245
1246   command_bind_silc("part", MODULE_NAME, (SIGNAL_FUNC) command_part);
1247   command_bind_silc("me", MODULE_NAME, (SIGNAL_FUNC) command_me);
1248   command_bind_silc("action", MODULE_NAME, (SIGNAL_FUNC) command_action);
1249   command_bind_silc("notice", MODULE_NAME, (SIGNAL_FUNC) command_notice);
1250   command_bind_silc("away", MODULE_NAME, (SIGNAL_FUNC) command_away);
1251   command_bind_silc("key", MODULE_NAME, (SIGNAL_FUNC) command_key);
1252   command_bind("listkeys", MODULE_NAME, (SIGNAL_FUNC) command_listkeys);
1253
1254   command_set_options("listkeys", "clients servers");
1255   command_set_options("action", "sign channel");
1256   command_set_options("notice", "sign channel");
1257
1258   silc_nicklist_init();
1259 }
1260
1261 void silc_channels_deinit(void)
1262 {
1263   signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1264   signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
1265   signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit);
1266   signal_remove("gui exit", (SIGNAL_FUNC) sig_gui_quit);
1267   signal_remove("mime", (SIGNAL_FUNC) sig_mime);
1268
1269   command_unbind("part", (SIGNAL_FUNC) command_part);
1270   command_unbind("me", (SIGNAL_FUNC) command_me);
1271   command_unbind("action", (SIGNAL_FUNC) command_action);
1272   command_unbind("notice", (SIGNAL_FUNC) command_notice);
1273   command_unbind("away", (SIGNAL_FUNC) command_away);
1274   command_unbind("key", (SIGNAL_FUNC) command_key);
1275   command_unbind("listkeys", (SIGNAL_FUNC) command_listkeys);
1276
1277   silc_nicklist_deinit();
1278 }