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