106ff70647242928b51d8fbf4749c91a122f2a4c
[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         if (!number || number > silc_dlist_count(ckeys)) {
751           silc_dlist_uninit(ckeys);
752           goto out;
753         }
754
755         for (i = 0; i < number; i++)
756           ch = silc_dlist_get(ckeys);
757         if (!ch)
758           goto out;
759
760         silc_client_del_channel_private_key(silc_client, conn, channel_entry,
761                                             ch);
762         silc_dlist_uninit(ckeys);
763       }
764
765       goto out;
766     }
767   }
768
769   /* List command */
770   if (!strcasecmp(argv[3], "list")) {
771     command = 3;
772
773     if (type == 1) {
774       SilcPrivateMessageKeys keys;
775       SilcUInt32 keys_count;
776       int k, i, len;
777       char buf[1024];
778
779       keys = silc_client_list_private_message_keys(silc_client, conn,
780                                                    &keys_count);
781       if (!keys)
782         goto out;
783
784       /* list the private message key(s) */
785       if (nickname[0] == '*') {
786         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
787                            SILCTXT_PRIVATE_KEY_LIST);
788         for (k = 0; k < keys_count; k++) {
789           memset(buf, 0, sizeof(buf));
790           strncat(buf, "  ", 2);
791           len = strlen(keys[k].client_entry->nickname);
792           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
793           if (len < 30)
794             for (i = 0; i < 30 - len; i++)
795               strcat(buf, " ");
796           strcat(buf, " ");
797
798           len = strlen(keys[k].cipher);
799           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
800           if (len < 14)
801             for (i = 0; i < 14 - len; i++)
802               strcat(buf, " ");
803           strcat(buf, " ");
804
805           if (keys[k].key)
806             strcat(buf, "<hidden>");
807           else
808             strcat(buf, "*generated*");
809
810           silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
811         }
812       } else {
813         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
814                            SILCTXT_PRIVATE_KEY_LIST_NICK,
815                            client_entry->nickname);
816         for (k = 0; k < keys_count; k++) {
817           if (keys[k].client_entry != client_entry)
818             continue;
819
820           memset(buf, 0, sizeof(buf));
821           strncat(buf, "  ", 2);
822           len = strlen(keys[k].client_entry->nickname);
823           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
824           if (len < 30)
825             for (i = 0; i < 30 - len; i++)
826               strcat(buf, " ");
827           strcat(buf, " ");
828
829           len = strlen(keys[k].cipher);
830           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
831           if (len < 14)
832             for (i = 0; i < 14 - len; i++)
833               strcat(buf, " ");
834           strcat(buf, " ");
835
836           if (keys[k].key)
837             strcat(buf, "<hidden>");
838           else
839             strcat(buf, "*generated*");
840
841           silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
842         }
843       }
844
845       silc_client_free_private_message_keys(keys, keys_count);
846
847     } else if (type == 2) {
848       int len;
849       char buf[1024];
850
851       ckeys = silc_client_list_channel_private_keys(silc_client, conn,
852                                                     channel_entry);
853
854       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
855                          SILCTXT_CH_PRIVATE_KEY_LIST,
856                          channel_entry->channel_name);
857
858       if (!ckeys)
859         goto out;
860
861       while ((ch = silc_dlist_get(ckeys))) {
862         memset(buf, 0, sizeof(buf));
863         strncat(buf, "  ", 2);
864
865         len = strlen(silc_cipher_get_name(ch->send_key));
866         strncat(buf, silc_cipher_get_name(ch->send_key),
867                 len > 16 ? 16 : len);
868         if (len < 16)
869           for (i = 0; i < 16 - len; i++)
870             strcat(buf, " ");
871         strcat(buf, " ");
872
873         len = strlen(silc_hmac_get_name(ch->hmac));
874         strncat(buf, silc_hmac_get_name(ch->hmac), len > 16 ? 16 : len);
875         if (len < 16)
876           for (i = 0; i < 16 - len; i++)
877             strcat(buf, " ");
878         strcat(buf, " ");
879
880         strcat(buf, "<hidden>");
881
882         silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
883       }
884
885       silc_dlist_uninit(ckeys);
886     }
887
888     goto out;
889   }
890
891   /* Send command is used to send key agreement */
892   if (!strcasecmp(argv[3], "agreement")) {
893     command = 4;
894
895     if (argc >= 5)
896       hostname = argv[4];
897     if (argc >= 6) {
898       if (!strcasecmp(argv[5], "UDP"))
899         udp = TRUE;
900       else
901         port = atoi(argv[5]);
902     }
903     if (argc >= 7)
904       udp = TRUE;
905
906     internal = silc_calloc(1, sizeof(*internal));
907     internal->type = type;
908     internal->server = server;
909
910     if (!hostname) {
911       if (settings_get_bool("use_auto_addr")) {
912         hostname = (char *)settings_get_str("auto_public_ip");
913
914         /* If the hostname isn't set, treat this case as if auto_public_ip
915            wasn't set. */
916         if ((hostname) && (*hostname == '\0')) {
917            hostname = NULL;
918         } else {
919           bindhost = (char *)settings_get_str("auto_bind_ip");
920
921           /* if the bind_ip isn't set, but the public_ip IS, then assume then
922              public_ip is the same value as the bind_ip. */
923           if ((bindhost) && (*bindhost == '\0'))
924             bindhost = hostname;
925           port = settings_get_int("auto_bind_port");
926         }
927       }  /* if use_auto_addr */
928     }
929   }
930
931   /* Start command is used to start key agreement (after receiving the
932      key_agreement client operation). */
933   if (!strcasecmp(argv[3], "negotiate")) {
934     command = 5;
935
936     if (argc >= 5)
937       hostname = argv[4];
938     if (argc >= 6) {
939       if (!strcasecmp(argv[5], "UDP"))
940         udp = TRUE;
941       else
942         port = atoi(argv[5]);
943     }
944     if (argc >= 7)
945       udp = TRUE;
946
947     internal = silc_calloc(1, sizeof(*internal));
948     internal->type = type;
949     internal->server = server;
950   }
951
952   /* Change current channel private key */
953   if (!strcasecmp(argv[3], "change")) {
954     command = 6;
955     if (type == 2) {
956       /* Unset channel key(s) */
957       int number;
958
959       ckeys = silc_client_list_channel_private_keys(silc_client, conn,
960                                                     channel_entry);
961       if (!ckeys)
962         goto out;
963
964       if (argc == 4) {
965         chanrec->cur_key++;
966         if (chanrec->cur_key >= silc_dlist_count(ckeys))
967           chanrec->cur_key = 0;
968       }
969
970       if (argc > 4) {
971         number = atoi(argv[4]);
972         if (!number || number > silc_dlist_count(ckeys))
973           chanrec->cur_key = 0;
974         else
975           chanrec->cur_key = number - 1;
976       }
977
978       for (i = 0; i < chanrec->cur_key; i++)
979         ch = silc_dlist_get(ckeys);
980       if (!ch)
981         goto out;
982
983       /* Set the current channel private key */
984       silc_client_current_channel_private_key(silc_client, conn,
985                                               channel_entry, ch);
986       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
987                          SILCTXT_CH_PRIVATE_KEY_CHANGE, i + 1,
988                          channel_entry->channel_name);
989
990       silc_dlist_uninit(ckeys);
991       goto out;
992     }
993   }
994
995   if (command == 0) {
996     silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO,
997              "Usage: /KEY msg|channel <nickname|channel> "
998              "set|unset|agreement|negotiate [<arguments>]");
999     goto out;
1000   }
1001
1002   if (command == 4 && client_entry) {
1003     SilcClientConnectionParams params;
1004
1005     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1006                        SILCTXT_KEY_AGREEMENT, argv[2]);
1007     internal->responder = TRUE;
1008
1009     memset(&params, 0, sizeof(params));
1010     params.local_ip = hostname;
1011     params.bind_ip = bindhost;
1012     params.local_port = port;
1013     params.udp = udp;
1014     params.timeout_secs = settings_get_int("key_exchange_timeout_secs");
1015
1016     silc_client_send_key_agreement(
1017                            silc_client, conn, client_entry, &params,
1018                            irssi_pubkey, irssi_privkey,
1019                            keyagr_completion, internal);
1020     if (!hostname)
1021       silc_free(internal);
1022     goto out;
1023   }
1024
1025   if (command == 5 && client_entry && hostname) {
1026     SilcClientConnectionParams params;
1027
1028     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1029                        SILCTXT_KEY_AGREEMENT_NEGOTIATE, argv[2]);
1030     internal->responder = FALSE;
1031
1032     memset(&params, 0, sizeof(params));
1033     if (udp) {
1034       if (settings_get_bool("use_auto_addr")) {
1035         params.local_ip = (char *)settings_get_str("auto_public_ip");
1036         if ((params.local_ip) && (*params.local_ip == '\0')) {
1037           params.local_ip = silc_net_localip();
1038         } else {
1039           params.bind_ip = (char *)settings_get_str("auto_bind_ip");
1040           if ((params.bind_ip) && (*params.bind_ip == '\0'))
1041             params.bind_ip = NULL;
1042           params.local_port = settings_get_int("auto_bind_port");
1043         }
1044       }
1045       if (!params.local_ip)
1046         params.local_ip = silc_net_localip();
1047     }
1048     params.udp = udp;
1049     params.timeout_secs = settings_get_int("key_exchange_timeout_secs");
1050
1051     silc_client_perform_key_agreement(silc_client, conn, client_entry, &params,
1052                                       irssi_pubkey, irssi_privkey,
1053                                       hostname, port, keyagr_completion,
1054                                       internal);
1055     goto out;
1056   }
1057
1058  out:
1059   silc_free(nickname);
1060   return;
1061 }
1062
1063 void silc_list_key(const char *pub_filename, int verbose)
1064 {
1065   SilcPublicKey public_key;
1066   SilcPublicKeyIdentifier ident;
1067   SilcSILCPublicKey silc_pubkey;
1068   char *fingerprint, *babbleprint;
1069   unsigned char *pk;
1070   SilcUInt32 pk_len;
1071   SilcUInt32 key_len = 0;
1072   int is_server_key = (strstr(pub_filename, "serverkeys") != NULL);
1073
1074   if (!silc_pkcs_load_public_key((char *)pub_filename, &public_key)) {
1075     printformat_module("fe-common/silc", NULL, NULL,
1076                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_LOADPUB,
1077                        pub_filename);
1078     return;
1079   }
1080
1081   /* Print only SILC public keys */
1082   if (silc_pkcs_get_type(public_key) != SILC_PKCS_SILC) {
1083     printformat_module("fe-common/silc", NULL, NULL,
1084                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_LOADPUB,
1085                        pub_filename);
1086     return;
1087   }
1088
1089   silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
1090   ident = &silc_pubkey->identifier;
1091
1092   pk = silc_pkcs_public_key_encode(public_key, &pk_len);
1093   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1094   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
1095   key_len = silc_pkcs_public_key_get_len(public_key);
1096
1097   printformat_module("fe-common/silc", NULL, NULL,
1098                      MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_FILE,
1099                      pub_filename);
1100
1101   if (verbose)
1102     printformat_module("fe-common/silc", NULL, NULL,
1103                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_ALG,
1104                        silc_pkcs_get_name(public_key));
1105   if (key_len && verbose)
1106     printformat_module("fe-common/silc", NULL, NULL,
1107                         MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_BITS,
1108                         (unsigned int)key_len);
1109   if (ident->version && verbose)
1110     printformat_module("fe-common/silc", NULL, NULL,
1111                         MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_VER,
1112                         ident->version);
1113   if (ident->realname && (!is_server_key || verbose))
1114     printformat_module("fe-common/silc", NULL, NULL,
1115                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_RN,
1116                        ident->realname);
1117   if (ident->username && verbose)
1118     printformat_module("fe-common/silc", NULL, NULL,
1119                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_UN,
1120                        ident->username);
1121   if (ident->host && (is_server_key || verbose))
1122     printformat_module("fe-common/silc", NULL, NULL,
1123                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_HN,
1124                        ident->host);
1125   if (ident->email && verbose)
1126     printformat_module("fe-common/silc", NULL, NULL,
1127                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_EMAIL,
1128                        ident->email);
1129   if (ident->org && verbose)
1130     printformat_module("fe-common/silc", NULL, NULL,
1131                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_ORG,
1132                        ident->org);
1133   if (ident->country && verbose)
1134     printformat_module("fe-common/silc", NULL, NULL,
1135                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_C,
1136                        ident->country);
1137
1138   if (verbose) {
1139     printformat_module("fe-common/silc", NULL, NULL,
1140                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_FINGER,
1141                        fingerprint);
1142     printformat_module("fe-common/silc", NULL, NULL,
1143                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_BABL,
1144                        babbleprint);
1145   }
1146
1147   silc_free(fingerprint);
1148   silc_free(babbleprint);
1149   silc_free(pk);
1150   silc_pkcs_public_key_free(public_key);
1151 }
1152
1153 void silc_list_keys_in_dir(const char *dirname, const char *where)
1154 {
1155   DIR *dir;
1156   struct dirent *entry;
1157
1158   dir = opendir(dirname);
1159
1160   if (dir == NULL)
1161     cmd_return_error(CMDERR_ERRNO);
1162
1163   printformat_module("fe-common/silc", NULL, NULL,
1164                      MSGLEVEL_CRAP, SILCTXT_LISTKEY_LIST,
1165                      where);
1166
1167   rewinddir(dir);
1168
1169   while ((entry = readdir(dir)) != NULL) {
1170     /* try to open everything that isn't a directory */
1171     struct stat buf;
1172     char filename[256];
1173
1174     snprintf(filename, sizeof(filename) - 1, "%s/%s", dirname, entry->d_name);
1175     if (!stat(filename, &buf) && S_ISREG(buf.st_mode))
1176       silc_list_key(filename, FALSE);
1177   }
1178
1179   closedir(dir);
1180 }
1181
1182 void silc_list_file(const char *filename)
1183 {
1184
1185   char path[256];
1186   struct stat buf;
1187
1188   snprintf(path, sizeof(path) - 1, "%s", filename);
1189   if (!stat(path, &buf) && S_ISREG(buf.st_mode))
1190     goto list_key;
1191
1192   snprintf(path, sizeof(path) - 1, "%s/%s", get_irssi_dir(), filename);
1193   if (!stat(path, &buf) && S_ISREG(buf.st_mode))
1194     goto list_key;
1195
1196   snprintf(path,sizeof(path) - 1, "%s/clientkeys/%s", get_irssi_dir(),
1197            filename);
1198   if (!stat(path, &buf) && S_ISREG(buf.st_mode))
1199     goto list_key;
1200
1201   snprintf(path,sizeof(path) - 1, "%s/serverkeys/%s", get_irssi_dir(),
1202            filename);
1203   if (!stat(path, &buf) && S_ISREG(buf.st_mode))
1204     goto list_key;
1205
1206   return;
1207
1208 list_key:
1209
1210   silc_list_key(path, TRUE);
1211 }
1212
1213 /* Lists locally saved client and server public keys. */
1214 static void command_listkeys(const char *data, SILC_SERVER_REC *server,
1215                              WI_ITEM_REC *item)
1216 {
1217   GHashTable *optlist;
1218   char *filename;
1219   void *free_arg;
1220   char dirname[256];
1221
1222   if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
1223                       PARAM_FLAG_GETREST, "listkeys", &optlist,
1224                       &filename))
1225     return;
1226
1227   if (*filename != '\0') {
1228
1229     silc_list_file(filename);
1230
1231   } else {
1232     int clients, servers;
1233
1234     clients = (g_hash_table_lookup(optlist, "clients") != NULL);
1235     servers = (g_hash_table_lookup(optlist, "servers") != NULL);
1236
1237     if (!(clients || servers))
1238       clients = servers = 1;
1239
1240     if (servers) {
1241       snprintf(dirname, sizeof(dirname) - 1, "%s/serverkeys", get_irssi_dir());
1242       silc_list_keys_in_dir(dirname, "server");
1243     }
1244
1245     if (clients) {
1246       snprintf(dirname, sizeof(dirname) - 1, "%s/clientkeys", get_irssi_dir());
1247       silc_list_keys_in_dir(dirname, "client");
1248     }
1249   }
1250   cmd_params_free(free_arg);
1251 }
1252
1253 void silc_channels_init(void)
1254 {
1255   signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1256   signal_add("server connected", (SIGNAL_FUNC) sig_connected);
1257   signal_add("server quit", (SIGNAL_FUNC) sig_server_quit);
1258   signal_add("mime", (SIGNAL_FUNC) sig_mime);
1259
1260   command_bind_silc("part", MODULE_NAME, (SIGNAL_FUNC) command_part);
1261   command_bind_silc("me", MODULE_NAME, (SIGNAL_FUNC) command_me);
1262   command_bind_silc("action", MODULE_NAME, (SIGNAL_FUNC) command_action);
1263   command_bind_silc("notice", MODULE_NAME, (SIGNAL_FUNC) command_notice);
1264   command_bind_silc("away", MODULE_NAME, (SIGNAL_FUNC) command_away);
1265   command_bind_silc("key", MODULE_NAME, (SIGNAL_FUNC) command_key);
1266   command_bind("listkeys", MODULE_NAME, (SIGNAL_FUNC) command_listkeys);
1267
1268   command_set_options("listkeys", "clients servers");
1269   command_set_options("action", "sign channel");
1270   command_set_options("notice", "sign channel");
1271
1272   silc_nicklist_init();
1273 }
1274
1275 void silc_channels_deinit(void)
1276 {
1277   signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1278   signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
1279   signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit);
1280   signal_remove("mime", (SIGNAL_FUNC) sig_mime);
1281
1282   command_unbind("part", (SIGNAL_FUNC) command_part);
1283   command_unbind("me", (SIGNAL_FUNC) command_me);
1284   command_unbind("action", (SIGNAL_FUNC) command_action);
1285   command_unbind("notice", (SIGNAL_FUNC) command_notice);
1286   command_unbind("away", (SIGNAL_FUNC) command_away);
1287   command_unbind("key", (SIGNAL_FUNC) command_key);
1288   command_unbind("listkeys", (SIGNAL_FUNC) command_listkeys);
1289
1290   silc_nicklist_deinit();
1291 }