Merge branch 'topic/mm-fixes' of git://208.110.73.182/silc into 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, &public_key)) {
1078     printformat_module("fe-common/silc", NULL, NULL,
1079                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_LOADPUB,
1080                        pub_filename);
1081     return;
1082   }
1083
1084   /* Print only SILC public keys */
1085   if (silc_pkcs_get_type(public_key) != SILC_PKCS_SILC) {
1086     printformat_module("fe-common/silc", NULL, NULL,
1087                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_LOADPUB,
1088                        pub_filename);
1089     return;
1090   }
1091
1092   silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
1093   ident = &silc_pubkey->identifier;
1094
1095   pk = silc_pkcs_public_key_encode(public_key, &pk_len);
1096   if (!pk)
1097     return;
1098   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1099   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
1100   key_len = silc_pkcs_public_key_get_len(public_key);
1101
1102   printformat_module("fe-common/silc", NULL, NULL,
1103                      MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_FILE,
1104                      pub_filename);
1105
1106   if (verbose)
1107     printformat_module("fe-common/silc", NULL, NULL,
1108                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_ALG,
1109                        silc_pkcs_get_name(public_key));
1110   if (key_len && verbose)
1111     printformat_module("fe-common/silc", NULL, NULL,
1112                         MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_BITS,
1113                         (unsigned int)key_len);
1114   if (ident->version && verbose)
1115     printformat_module("fe-common/silc", NULL, NULL,
1116                         MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_VER,
1117                         ident->version);
1118   if (ident->realname && (!is_server_key || verbose))
1119     printformat_module("fe-common/silc", NULL, NULL,
1120                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_RN,
1121                        ident->realname);
1122   if (ident->username && verbose)
1123     printformat_module("fe-common/silc", NULL, NULL,
1124                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_UN,
1125                        ident->username);
1126   if (ident->host && (is_server_key || verbose))
1127     printformat_module("fe-common/silc", NULL, NULL,
1128                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_HN,
1129                        ident->host);
1130   if (ident->email && verbose)
1131     printformat_module("fe-common/silc", NULL, NULL,
1132                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_EMAIL,
1133                        ident->email);
1134   if (ident->org && verbose)
1135     printformat_module("fe-common/silc", NULL, NULL,
1136                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_ORG,
1137                        ident->org);
1138   if (ident->country && verbose)
1139     printformat_module("fe-common/silc", NULL, NULL,
1140                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_C,
1141                        ident->country);
1142
1143   if (verbose) {
1144     printformat_module("fe-common/silc", NULL, NULL,
1145                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_FINGER,
1146                        fingerprint);
1147     printformat_module("fe-common/silc", NULL, NULL,
1148                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_BABL,
1149                        babbleprint);
1150   }
1151
1152   silc_free(fingerprint);
1153   silc_free(babbleprint);
1154   silc_free(pk);
1155   silc_pkcs_public_key_free(public_key);
1156 }
1157
1158 void silc_list_keys_in_dir(const char *dirname, const char *where)
1159 {
1160   DIR *dir;
1161   struct dirent *entry;
1162
1163   dir = opendir(dirname);
1164
1165   if (dir == NULL)
1166     cmd_return_error(CMDERR_ERRNO);
1167
1168   printformat_module("fe-common/silc", NULL, NULL,
1169                      MSGLEVEL_CRAP, SILCTXT_LISTKEY_LIST,
1170                      where);
1171
1172   rewinddir(dir);
1173
1174   while ((entry = readdir(dir)) != NULL) {
1175     /* try to open everything that isn't a directory */
1176     struct stat buf;
1177     char filename[256];
1178
1179     snprintf(filename, sizeof(filename) - 1, "%s/%s", dirname, entry->d_name);
1180     if (!stat(filename, &buf) && S_ISREG(buf.st_mode))
1181       silc_list_key(filename, FALSE);
1182   }
1183
1184   closedir(dir);
1185 }
1186
1187 void silc_list_file(const char *filename)
1188 {
1189
1190   char path[256];
1191   struct stat buf;
1192
1193   snprintf(path, sizeof(path) - 1, "%s", filename);
1194   if (!stat(path, &buf) && S_ISREG(buf.st_mode))
1195     goto list_key;
1196
1197   snprintf(path, sizeof(path) - 1, "%s/%s", get_irssi_dir(), filename);
1198   if (!stat(path, &buf) && S_ISREG(buf.st_mode))
1199     goto list_key;
1200
1201   snprintf(path,sizeof(path) - 1, "%s/clientkeys/%s", get_irssi_dir(),
1202            filename);
1203   if (!stat(path, &buf) && S_ISREG(buf.st_mode))
1204     goto list_key;
1205
1206   snprintf(path,sizeof(path) - 1, "%s/serverkeys/%s", get_irssi_dir(),
1207            filename);
1208   if (!stat(path, &buf) && S_ISREG(buf.st_mode))
1209     goto list_key;
1210
1211   return;
1212
1213 list_key:
1214
1215   silc_list_key(path, TRUE);
1216 }
1217
1218 /* Lists locally saved client and server public keys. */
1219 static void command_listkeys(const char *data, SILC_SERVER_REC *server,
1220                              WI_ITEM_REC *item)
1221 {
1222   GHashTable *optlist;
1223   char *filename;
1224   void *free_arg;
1225   char dirname[256];
1226
1227   if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
1228                       PARAM_FLAG_GETREST, "listkeys", &optlist,
1229                       &filename))
1230     return;
1231
1232   if (*filename != '\0') {
1233
1234     silc_list_file(filename);
1235
1236   } else {
1237     int clients, servers;
1238
1239     clients = (g_hash_table_lookup(optlist, "clients") != NULL);
1240     servers = (g_hash_table_lookup(optlist, "servers") != NULL);
1241
1242     if (!(clients || servers))
1243       clients = servers = 1;
1244
1245     if (servers) {
1246       snprintf(dirname, sizeof(dirname) - 1, "%s/serverkeys", get_irssi_dir());
1247       silc_list_keys_in_dir(dirname, "server");
1248     }
1249
1250     if (clients) {
1251       snprintf(dirname, sizeof(dirname) - 1, "%s/clientkeys", get_irssi_dir());
1252       silc_list_keys_in_dir(dirname, "client");
1253     }
1254   }
1255   cmd_params_free(free_arg);
1256 }
1257
1258 void silc_channels_init(void)
1259 {
1260   signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1261   signal_add("server connected", (SIGNAL_FUNC) sig_connected);
1262   signal_add("server quit", (SIGNAL_FUNC) sig_server_quit);
1263   signal_add("mime", (SIGNAL_FUNC) sig_mime);
1264
1265   command_bind_silc("part", MODULE_NAME, (SIGNAL_FUNC) command_part);
1266   command_bind_silc("me", MODULE_NAME, (SIGNAL_FUNC) command_me);
1267   command_bind_silc("action", MODULE_NAME, (SIGNAL_FUNC) command_action);
1268   command_bind_silc("notice", MODULE_NAME, (SIGNAL_FUNC) command_notice);
1269   command_bind_silc("away", MODULE_NAME, (SIGNAL_FUNC) command_away);
1270   command_bind_silc("key", MODULE_NAME, (SIGNAL_FUNC) command_key);
1271   command_bind("listkeys", MODULE_NAME, (SIGNAL_FUNC) command_listkeys);
1272
1273   command_set_options("listkeys", "clients servers");
1274   command_set_options("action", "sign channel");
1275   command_set_options("notice", "sign channel");
1276
1277   silc_nicklist_init();
1278 }
1279
1280 void silc_channels_deinit(void)
1281 {
1282   signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1283   signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
1284   signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit);
1285   signal_remove("mime", (SIGNAL_FUNC) sig_mime);
1286
1287   command_unbind("part", (SIGNAL_FUNC) command_part);
1288   command_unbind("me", (SIGNAL_FUNC) command_me);
1289   command_unbind("action", (SIGNAL_FUNC) command_action);
1290   command_unbind("notice", (SIGNAL_FUNC) command_notice);
1291   command_unbind("away", (SIGNAL_FUNC) command_away);
1292   command_unbind("key", (SIGNAL_FUNC) command_key);
1293   command_unbind("listkeys", (SIGNAL_FUNC) command_listkeys);
1294
1295   silc_nicklist_deinit();
1296 }