Sun Mar 11 17:43:18 CET 2007 Jochen Eisinger <coffee@silcnet.org>
[runtime.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[128 + 1], *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       strcpy(nickname, "*");
638     } else {
639       /* Parse the typed nickname. */
640       if (!silc_parse_userfqdn(argv[2], nickname, sizeof(nickname), NULL, 0)) {
641         printformat_module("fe-common/silc", server, NULL,
642                            MSGLEVEL_CRAP, SILCTXT_BAD_NICK, argv[2]);
643         return;
644       }
645
646       /* Find client entry */
647       clients = silc_client_get_clients_local(silc_client, conn, nickname,
648                                               argv[2]);
649       if (!clients) {
650         KeyGetClients inter = silc_calloc(1, sizeof(*inter));
651         inter->server = server;
652         inter->data = strdup(data);
653         inter->nick = strdup(nickname);
654         inter->item = item;
655         silc_client_get_clients(silc_client, conn, nickname, argv[2],
656                                 silc_client_command_key_get_clients, inter);
657         goto out;
658       }
659
660       client_entry = silc_dlist_get(clients);
661       silc_client_list_free(silc_client, conn, clients);
662     }
663   }
664
665   if (type == 2) {
666     /* Get channel entry */
667     char *name;
668
669     if (argv[2][0] == '*') {
670       if (!conn->current_channel) {
671         silc_free(nickname);
672         cmd_return_error(CMDERR_NOT_JOINED);
673       }
674       name = conn->current_channel->channel_name;
675     } else {
676       name = argv[2];
677     }
678
679     chanrec = silc_channel_find(server, name);
680     if (chanrec == NULL) {
681       silc_free(nickname);
682       cmd_return_error(CMDERR_CHAN_NOT_FOUND);
683     }
684     channel_entry = chanrec->entry;
685   }
686
687   /* Set command */
688   if (!strcasecmp(argv[3], "set")) {
689     command = 1;
690
691     if (argc >= 5) {
692       char *cipher = NULL, *hmac = NULL;
693
694       if (argc >= 6)
695         cipher = argv[5];
696       if (argc >= 7)
697         hmac = argv[6];
698
699       if (type == 1 && client_entry) {
700         /* Set private message key */
701         silc_client_del_private_message_key(silc_client, conn, client_entry);
702         silc_client_add_private_message_key(silc_client, conn, client_entry,
703                                             cipher, hmac,
704                                             argv[4], argv_lens[4]);
705       } else if (type == 2) {
706         /* Set private channel key */
707         if (!(channel_entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
708           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
709                              SILCTXT_CH_PRIVATE_KEY_NOMODE,
710                              channel_entry->channel_name);
711           goto out;
712         }
713
714         if (!silc_client_add_channel_private_key(silc_client, conn,
715                                                  channel_entry, NULL,
716                                                  cipher, hmac,
717                                                  argv[4],
718                                                  argv_lens[4], NULL)) {
719           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
720                              SILCTXT_CH_PRIVATE_KEY_ERROR,
721                              channel_entry->channel_name);
722           goto out;
723         }
724
725         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
726                            SILCTXT_CH_PRIVATE_KEY_ADD,
727                            channel_entry->channel_name);
728       }
729     }
730
731     goto out;
732   }
733
734   /* Unset command */
735   if (!strcasecmp(argv[3], "unset")) {
736     command = 2;
737
738     if (type == 1 && client_entry) {
739       /* Unset private message key */
740       silc_client_del_private_message_key(silc_client, conn, client_entry);
741     } else if (type == 2) {
742       /* Unset channel key(s) */
743       int number;
744
745       if (argc == 4)
746         silc_client_del_channel_private_keys(silc_client, conn,
747                                              channel_entry);
748
749       if (argc > 4) {
750         number = atoi(argv[4]);
751         ckeys = silc_client_list_channel_private_keys(silc_client, conn,
752                                                       channel_entry);
753         if (!ckeys)
754           goto out;
755
756         if (!number || number > silc_dlist_count(ckeys)) {
757           silc_dlist_uninit(ckeys);
758           goto out;
759         }
760
761         for (i = 0; i < number; i++)
762           ch = silc_dlist_get(ckeys);
763         if (!ch)
764           goto out;
765
766         silc_client_del_channel_private_key(silc_client, conn, channel_entry,
767                                             ch);
768         silc_dlist_uninit(ckeys);
769       }
770
771       goto out;
772     }
773   }
774
775   /* List command */
776   if (!strcasecmp(argv[3], "list")) {
777     command = 3;
778
779     if (type == 1) {
780       SilcPrivateMessageKeys keys;
781       SilcUInt32 keys_count;
782       int k, i, len;
783       char buf[1024];
784
785       keys = silc_client_list_private_message_keys(silc_client, conn,
786                                                    &keys_count);
787       if (!keys)
788         goto out;
789
790       /* list the private message key(s) */
791       if (nickname[0] == '*') {
792         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
793                            SILCTXT_PRIVATE_KEY_LIST);
794         for (k = 0; k < keys_count; k++) {
795           memset(buf, 0, sizeof(buf));
796           strncat(buf, "  ", 2);
797           len = strlen(keys[k].client_entry->nickname);
798           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
799           if (len < 30)
800             for (i = 0; i < 30 - len; i++)
801               strcat(buf, " ");
802           strcat(buf, " ");
803
804           len = strlen(keys[k].cipher);
805           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
806           if (len < 14)
807             for (i = 0; i < 14 - len; i++)
808               strcat(buf, " ");
809           strcat(buf, " ");
810
811           if (keys[k].key)
812             strcat(buf, "<hidden>");
813           else
814             strcat(buf, "*generated*");
815
816           silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
817         }
818       } else {
819         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
820                            SILCTXT_PRIVATE_KEY_LIST_NICK,
821                            client_entry->nickname);
822         for (k = 0; k < keys_count; k++) {
823           if (keys[k].client_entry != client_entry)
824             continue;
825
826           memset(buf, 0, sizeof(buf));
827           strncat(buf, "  ", 2);
828           len = strlen(keys[k].client_entry->nickname);
829           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
830           if (len < 30)
831             for (i = 0; i < 30 - len; i++)
832               strcat(buf, " ");
833           strcat(buf, " ");
834
835           len = strlen(keys[k].cipher);
836           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
837           if (len < 14)
838             for (i = 0; i < 14 - len; i++)
839               strcat(buf, " ");
840           strcat(buf, " ");
841
842           if (keys[k].key)
843             strcat(buf, "<hidden>");
844           else
845             strcat(buf, "*generated*");
846
847           silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
848         }
849       }
850
851       silc_client_free_private_message_keys(keys, keys_count);
852
853     } else if (type == 2) {
854       int len;
855       char buf[1024];
856
857       ckeys = silc_client_list_channel_private_keys(silc_client, conn,
858                                                     channel_entry);
859
860       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
861                          SILCTXT_CH_PRIVATE_KEY_LIST,
862                          channel_entry->channel_name);
863
864       if (!ckeys)
865         goto out;
866
867       while ((ch = silc_dlist_get(ckeys))) {
868         memset(buf, 0, sizeof(buf));
869         strncat(buf, "  ", 2);
870
871         len = strlen(silc_cipher_get_name(ch->cipher));
872         strncat(buf, silc_cipher_get_name(ch->cipher),
873                 len > 16 ? 16 : len);
874         if (len < 16)
875           for (i = 0; i < 16 - len; i++)
876             strcat(buf, " ");
877         strcat(buf, " ");
878
879         len = strlen(silc_hmac_get_name(ch->hmac));
880         strncat(buf, silc_hmac_get_name(ch->hmac), len > 16 ? 16 : len);
881         if (len < 16)
882           for (i = 0; i < 16 - len; i++)
883             strcat(buf, " ");
884         strcat(buf, " ");
885
886         strcat(buf, "<hidden>");
887
888         silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
889       }
890
891       silc_dlist_uninit(ckeys);
892     }
893
894     goto out;
895   }
896
897   /* Send command is used to send key agreement */
898   if (!strcasecmp(argv[3], "agreement")) {
899     command = 4;
900
901     if (argc >= 5)
902       hostname = argv[4];
903     if (argc >= 6) {
904       if (!strcasecmp(argv[5], "UDP"))
905         udp = TRUE;
906       else
907         port = atoi(argv[5]);
908     }
909     if (argc >= 7)
910       udp = TRUE;
911
912     internal = silc_calloc(1, sizeof(*internal));
913     internal->type = type;
914     internal->server = server;
915
916     if (!hostname) {
917       if (settings_get_bool("use_auto_addr")) {
918         hostname = (char *)settings_get_str("auto_public_ip");
919
920         /* If the hostname isn't set, treat this case as if auto_public_ip
921            wasn't set. */
922         if ((hostname) && (*hostname == '\0')) {
923            hostname = NULL;
924         } else {
925           bindhost = (char *)settings_get_str("auto_bind_ip");
926
927           /* if the bind_ip isn't set, but the public_ip IS, then assume then
928              public_ip is the same value as the bind_ip. */
929           if ((bindhost) && (*bindhost == '\0'))
930             bindhost = hostname;
931           port = settings_get_int("auto_bind_port");
932         }
933       }  /* if use_auto_addr */
934     }
935   }
936
937   /* Start command is used to start key agreement (after receiving the
938      key_agreement client operation). */
939   if (!strcasecmp(argv[3], "negotiate")) {
940     command = 5;
941
942     if (argc >= 5)
943       hostname = argv[4];
944     if (argc >= 6) {
945       if (!strcasecmp(argv[5], "UDP"))
946         udp = TRUE;
947       else
948         port = atoi(argv[5]);
949     }
950     if (argc >= 7)
951       udp = TRUE;
952
953     internal = silc_calloc(1, sizeof(*internal));
954     internal->type = type;
955     internal->server = server;
956   }
957
958   /* Change current channel private key */
959   if (!strcasecmp(argv[3], "change")) {
960     command = 6;
961     if (type == 2) {
962       /* Unset channel key(s) */
963       int number;
964
965       ckeys = silc_client_list_channel_private_keys(silc_client, conn,
966                                                     channel_entry);
967       if (!ckeys)
968         goto out;
969
970       if (argc == 4) {
971         chanrec->cur_key++;
972         if (chanrec->cur_key >= silc_dlist_count(ckeys))
973           chanrec->cur_key = 0;
974       }
975
976       if (argc > 4) {
977         number = atoi(argv[4]);
978         if (!number || number > silc_dlist_count(ckeys))
979           chanrec->cur_key = 0;
980         else
981           chanrec->cur_key = number - 1;
982       }
983
984       for (i = 0; i < chanrec->cur_key; i++)
985         ch = silc_dlist_get(ckeys);
986       if (!ch)
987         goto out;
988
989       /* Set the current channel private key */
990       silc_client_current_channel_private_key(silc_client, conn,
991                                               channel_entry, ch);
992       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
993                          SILCTXT_CH_PRIVATE_KEY_CHANGE, i + 1,
994                          channel_entry->channel_name);
995
996       silc_dlist_uninit(ckeys);
997       goto out;
998     }
999   }
1000
1001   if (command == 0) {
1002     silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO,
1003              "Usage: /KEY msg|channel <nickname|channel> "
1004              "set|unset|agreement|negotiate [<arguments>]");
1005     goto out;
1006   }
1007
1008   if (command == 4 && client_entry) {
1009     SilcClientConnectionParams params;
1010
1011     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1012                        SILCTXT_KEY_AGREEMENT, argv[2]);
1013     internal->responder = TRUE;
1014
1015     memset(&params, 0, sizeof(params));
1016     params.local_ip = hostname;
1017     params.bind_ip = bindhost;
1018     params.local_port = port;
1019     params.udp = udp;
1020     params.timeout_secs = settings_get_int("key_exchange_timeout_secs");
1021
1022     silc_client_send_key_agreement(
1023                            silc_client, conn, client_entry, &params,
1024                            irssi_pubkey, irssi_privkey,
1025                            keyagr_completion, internal);
1026     if (!hostname)
1027       silc_free(internal);
1028     goto out;
1029   }
1030
1031   if (command == 5 && client_entry && hostname) {
1032     SilcClientConnectionParams params;
1033
1034     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1035                        SILCTXT_KEY_AGREEMENT_NEGOTIATE, argv[2]);
1036     internal->responder = FALSE;
1037
1038     memset(&params, 0, sizeof(params));
1039     if (udp) {
1040       if (settings_get_bool("use_auto_addr")) {
1041         params.local_ip = (char *)settings_get_str("auto_public_ip");
1042         if ((params.local_ip) && (*params.local_ip == '\0')) {
1043           params.local_ip = silc_net_localip();
1044         } else {
1045           params.bind_ip = (char *)settings_get_str("auto_bind_ip");
1046           if ((params.bind_ip) && (*params.bind_ip == '\0'))
1047             params.bind_ip = NULL;
1048           params.local_port = settings_get_int("auto_bind_port");
1049         }
1050       }
1051       if (!params.local_ip)
1052         params.local_ip = silc_net_localip();
1053     }
1054     params.udp = udp;
1055     params.timeout_secs = settings_get_int("key_exchange_timeout_secs");
1056
1057     silc_client_perform_key_agreement(silc_client, conn, client_entry, &params,
1058                                       irssi_pubkey, irssi_privkey,
1059                                       hostname, port, keyagr_completion,
1060                                       internal);
1061     goto out;
1062   }
1063
1064  out:
1065   return;
1066 }
1067
1068 void silc_list_key(const char *pub_filename, int verbose)
1069 {
1070   SilcPublicKey public_key;
1071   SilcPublicKeyIdentifier ident;
1072   SilcSILCPublicKey silc_pubkey;
1073   char *fingerprint, *babbleprint;
1074   unsigned char *pk;
1075   SilcUInt32 pk_len;
1076   SilcUInt32 key_len = 0;
1077   int is_server_key = (strstr(pub_filename, "serverkeys") != NULL);
1078
1079   if (!silc_pkcs_load_public_key((char *)pub_filename, &public_key)) {
1080     printformat_module("fe-common/silc", NULL, NULL,
1081                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_LOADPUB,
1082                        pub_filename);
1083     return;
1084   }
1085
1086   /* Print only SILC public keys */
1087   if (silc_pkcs_get_type(public_key) != SILC_PKCS_SILC) {
1088     printformat_module("fe-common/silc", NULL, NULL,
1089                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_LOADPUB,
1090                        pub_filename);
1091     return;
1092   }
1093
1094   silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
1095   ident = &silc_pubkey->identifier;
1096
1097   pk = silc_pkcs_public_key_encode(public_key, &pk_len);
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 }