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