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