a0e1dcd6146732d04cd0bfadec187fabf4d369ee
[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
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   SILC_SERVER_REC *server;
446   int type;                     /* 1 = msg, 2 = channel */
447   SilcBool responder;
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);
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   case SILC_KEY_AGREEMENT_NO_MEMORY:
487     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
488                        SILCTXT_KEY_AGREEMENT_ERROR, client_entry->nickname);
489     break;
490
491   case SILC_KEY_AGREEMENT_FAILURE:
492     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
493                        SILCTXT_KEY_AGREEMENT_FAILURE, client_entry->nickname);
494     break;
495
496   case SILC_KEY_AGREEMENT_TIMEOUT:
497     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
498                        SILCTXT_KEY_AGREEMENT_TIMEOUT, client_entry->nickname);
499     break;
500
501   case SILC_KEY_AGREEMENT_ABORTED:
502     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
503                        SILCTXT_KEY_AGREEMENT_ABORTED, client_entry->nickname);
504     break;
505
506   case SILC_KEY_AGREEMENT_ALREADY_STARTED:
507     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
508                        SILCTXT_KEY_AGREEMENT_ALREADY_STARTED,
509                        client_entry->nickname);
510     break;
511
512   case SILC_KEY_AGREEMENT_SELF_DENIED:
513     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
514                        SILCTXT_KEY_AGREEMENT_SELF_DENIED);
515     break;
516
517   default:
518     break;
519   }
520
521   if (i)
522     silc_free(i);
523 }
524
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                                                 SilcStatus status,
546                                                 SilcDList clients,
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 client_entry = NULL;
573   SilcDList clients;
574   SILC_CHANNEL_REC *chanrec = NULL;
575   SilcChannelEntry channel_entry = NULL;
576   char nickname[128 + 1], *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   SilcChannelEntry ch = NULL;
585   SilcDList ckeys;
586   SilcBool udp = FALSE;
587   int i;
588
589   CMD_SILC_SERVER(server);
590
591   if (!server || !IS_SILC_SERVER(server) || !server->connected)
592     cmd_return_error(CMDERR_NOT_CONNECTED);
593
594   conn = server->conn;
595
596   /* Now parse all arguments */
597   tmp = g_strconcat("KEY", " ", data, NULL);
598   silc_parse_command_line(tmp, &argv, &argv_lens, &argv_types, &argc, 7);
599   g_free(tmp);
600
601   if (argc < 4)
602     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
603
604   /* Get type */
605   if (!strcasecmp(argv[1], "msg"))
606     type = 1;
607   if (!strcasecmp(argv[1], "channel"))
608     type = 2;
609
610   if (type == 0)
611     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
612
613   if (type == 1) {
614     if (argv[2][0] == '*') {
615       strcpy(nickname, "*");
616     } else {
617       /* Parse the typed nickname. */
618       if (!silc_parse_userfqdn(argv[2], nickname, sizeof(nickname), NULL, 0)) {
619         printformat_module("fe-common/silc", server, NULL,
620                            MSGLEVEL_CRAP, SILCTXT_BAD_NICK, argv[2]);
621         return;
622       }
623
624       /* Find client entry */
625       clients = silc_client_get_clients_local(silc_client, conn, nickname,
626                                               argv[2]);
627       if (!clients) {
628         KeyGetClients inter = silc_calloc(1, sizeof(*inter));
629         inter->server = server;
630         inter->data = strdup(data);
631         inter->nick = strdup(nickname);
632         inter->item = item;
633         silc_client_get_clients(silc_client, conn, nickname, argv[2],
634                                 silc_client_command_key_get_clients, inter);
635         goto out;
636       }
637
638       client_entry = silc_dlist_get(clients);
639       silc_client_list_free(silc_client, conn, clients);
640     }
641   }
642
643   if (type == 2) {
644     /* Get channel entry */
645     char *name;
646
647     if (argv[2][0] == '*') {
648       if (!conn->current_channel) {
649         silc_free(nickname);
650         cmd_return_error(CMDERR_NOT_JOINED);
651       }
652       name = conn->current_channel->channel_name;
653     } else {
654       name = argv[2];
655     }
656
657     chanrec = silc_channel_find(server, name);
658     if (chanrec == NULL) {
659       silc_free(nickname);
660       cmd_return_error(CMDERR_CHAN_NOT_FOUND);
661     }
662     channel_entry = chanrec->entry;
663   }
664
665   /* Set command */
666   if (!strcasecmp(argv[3], "set")) {
667     command = 1;
668
669     if (argc >= 5) {
670       char *cipher = NULL, *hmac = NULL;
671
672       if (argc >= 6)
673         cipher = argv[5];
674       if (argc >= 7)
675         hmac = argv[6];
676
677       if (type == 1 && client_entry) {
678         /* Set private message key */
679         silc_client_del_private_message_key(silc_client, conn, client_entry);
680         silc_client_add_private_message_key(silc_client, conn, client_entry,
681                                             cipher, hmac,
682                                             argv[4], argv_lens[4]);
683       } else if (type == 2) {
684         /* Set private channel key */
685         if (!(channel_entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
686           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
687                              SILCTXT_CH_PRIVATE_KEY_NOMODE,
688                              channel_entry->channel_name);
689           goto out;
690         }
691
692         if (!silc_client_add_channel_private_key(silc_client, conn,
693                                                  channel_entry, NULL,
694                                                  cipher, hmac,
695                                                  argv[4],
696                                                  argv_lens[4], NULL)) {
697           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
698                              SILCTXT_CH_PRIVATE_KEY_ERROR,
699                              channel_entry->channel_name);
700           goto out;
701         }
702
703         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
704                            SILCTXT_CH_PRIVATE_KEY_ADD,
705                            channel_entry->channel_name);
706       }
707     }
708
709     goto out;
710   }
711
712   /* Unset command */
713   if (!strcasecmp(argv[3], "unset")) {
714     command = 2;
715
716     if (type == 1 && client_entry) {
717       /* Unset private message key */
718       silc_client_del_private_message_key(silc_client, conn, client_entry);
719     } else if (type == 2) {
720       /* Unset channel key(s) */
721       int number;
722
723       if (argc == 4)
724         silc_client_del_channel_private_keys(silc_client, conn,
725                                              channel_entry);
726
727       if (argc > 4) {
728         number = atoi(argv[4]);
729         ckeys = silc_client_list_channel_private_keys(silc_client, conn,
730                                                       channel_entry);
731         if (!ckeys)
732           goto out;
733
734         if (!number || number > silc_dlist_count(ckeys)) {
735           silc_dlist_uninit(ckeys);
736           goto out;
737         }
738
739         for (i = 0; i < number; i++)
740           ch = silc_dlist_get(ckeys);
741         if (!ch)
742           goto out;
743
744         silc_client_del_channel_private_key(silc_client, conn, channel_entry,
745                                             ch);
746         silc_dlist_uninit(ckeys);
747       }
748
749       goto out;
750     }
751   }
752
753 #if 0
754   /* List command */
755   if (!strcasecmp(argv[3], "list")) {
756     command = 3;
757
758     if (type == 1) {
759       SilcPrivateMessageKeys keys;
760       SilcUInt32 keys_count;
761       int k, i, len;
762       char buf[1024];
763
764       keys = silc_client_list_private_message_keys(silc_client, conn,
765                                                    &keys_count);
766       if (!keys)
767         goto out;
768
769       /* list the private message key(s) */
770       if (nickname[0] == '*') {
771         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
772                            SILCTXT_PRIVATE_KEY_LIST);
773         for (k = 0; k < keys_count; k++) {
774           memset(buf, 0, sizeof(buf));
775           strncat(buf, "  ", 2);
776           len = strlen(keys[k].client_entry->nickname);
777           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
778           if (len < 30)
779             for (i = 0; i < 30 - len; i++)
780               strcat(buf, " ");
781           strcat(buf, " ");
782
783           len = strlen(keys[k].cipher);
784           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
785           if (len < 14)
786             for (i = 0; i < 14 - len; i++)
787               strcat(buf, " ");
788           strcat(buf, " ");
789
790           if (keys[k].key)
791             strcat(buf, "<hidden>");
792           else
793             strcat(buf, "*generated*");
794
795           silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
796         }
797       } else {
798         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
799                            SILCTXT_PRIVATE_KEY_LIST_NICK,
800                            client_entry->nickname);
801         for (k = 0; k < keys_count; k++) {
802           if (keys[k].client_entry != client_entry)
803             continue;
804
805           memset(buf, 0, sizeof(buf));
806           strncat(buf, "  ", 2);
807           len = strlen(keys[k].client_entry->nickname);
808           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
809           if (len < 30)
810             for (i = 0; i < 30 - len; i++)
811               strcat(buf, " ");
812           strcat(buf, " ");
813
814           len = strlen(keys[k].cipher);
815           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
816           if (len < 14)
817             for (i = 0; i < 14 - len; i++)
818               strcat(buf, " ");
819           strcat(buf, " ");
820
821           if (keys[k].key)
822             strcat(buf, "<hidden>");
823           else
824             strcat(buf, "*generated*");
825
826           silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
827         }
828       }
829
830       silc_client_free_private_message_keys(keys, keys_count);
831
832     } else if (type == 2) {
833       int k, len;
834       char buf[1024];
835
836       ckeys = silc_client_list_channel_private_keys(silc_client, conn,
837                                                     channel_entry);
838
839       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
840                          SILCTXT_CH_PRIVATE_KEY_LIST,
841                          channel_entry->channel_name);
842
843       if (!ckeys)
844         goto out;
845
846       for (k = 0; k < keys_count; k++) {
847         memset(buf, 0, sizeof(buf));
848         strncat(buf, "  ", 2);
849
850         len = strlen(silc_cipher_get_name(keys[k]->cipher));
851         strncat(buf, silc_cipher_get_name(keys[k]->cipher),
852                 len > 16 ? 16 : len);
853         if (len < 16)
854           for (i = 0; i < 16 - len; i++)
855             strcat(buf, " ");
856         strcat(buf, " ");
857
858         len = strlen(silc_hmac_get_name(keys[k]->hmac));
859         strncat(buf, silc_hmac_get_name(keys[k]->hmac), len > 16 ? 16 : len);
860         if (len < 16)
861           for (i = 0; i < 16 - len; i++)
862             strcat(buf, " ");
863         strcat(buf, " ");
864
865         strcat(buf, "<hidden>");
866
867         silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
868       }
869
870       silc_client_free_channel_private_keys(keys, keys_count);
871     }
872
873     goto out;
874   }
875 #endif /* 0 */
876
877   /* Send command is used to send key agreement */
878   if (!strcasecmp(argv[3], "agreement")) {
879     command = 4;
880
881     if (argc >= 5)
882       hostname = argv[4];
883     if (argc >= 6) {
884       if (!strcasecmp(argv[5], "UDP"))
885         udp = TRUE;
886       else
887         port = atoi(argv[5]);
888     }
889     if (argc >= 7)
890       udp = TRUE;
891
892     internal = silc_calloc(1, sizeof(*internal));
893     internal->type = type;
894     internal->server = server;
895
896     if (!hostname) {
897       if (settings_get_bool("use_auto_addr")) {
898         hostname = (char *)settings_get_str("auto_public_ip");
899
900         /* If the hostname isn't set, treat this case as if auto_public_ip
901            wasn't set. */
902         if ((hostname) && (*hostname == '\0')) {
903            hostname = NULL;
904         } else {
905           bindhost = (char *)settings_get_str("auto_bind_ip");
906
907           /* if the bind_ip isn't set, but the public_ip IS, then assume then
908              public_ip is the same value as the bind_ip. */
909           if ((bindhost) && (*bindhost == '\0'))
910             bindhost = hostname;
911           port = settings_get_int("auto_bind_port");
912         }
913       }  /* if use_auto_addr */
914     }
915   }
916
917   /* Start command is used to start key agreement (after receiving the
918      key_agreement client operation). */
919   if (!strcasecmp(argv[3], "negotiate")) {
920     command = 5;
921
922     if (argc >= 5)
923       hostname = argv[4];
924     if (argc >= 6) {
925       if (!strcasecmp(argv[5], "UDP"))
926         udp = TRUE;
927       else
928         port = atoi(argv[5]);
929     }
930     if (argc >= 7)
931       udp = TRUE;
932
933     internal = silc_calloc(1, sizeof(*internal));
934     internal->type = type;
935     internal->server = server;
936   }
937
938   /* Change current channel private key */
939   if (!strcasecmp(argv[3], "change")) {
940     command = 6;
941     if (type == 2) {
942       /* Unset channel key(s) */
943       int number;
944
945       ckeys = silc_client_list_channel_private_keys(silc_client, conn,
946                                                     channel_entry);
947       if (!ckeys)
948         goto out;
949
950       if (argc == 4) {
951         chanrec->cur_key++;
952         if (chanrec->cur_key >= silc_dlist_count(ckeys))
953           chanrec->cur_key = 0;
954       }
955
956       if (argc > 4) {
957         number = atoi(argv[4]);
958         if (!number || number > silc_dlist_count(ckeys))
959           chanrec->cur_key = 0;
960         else
961           chanrec->cur_key = number - 1;
962       }
963
964       for (i = 0; i < chanrec->cur_key; i++)
965         ch = silc_dlist_get(ckeys);
966       if (!ch)
967         goto out;
968
969       /* Set the current channel private key */
970       silc_client_current_channel_private_key(silc_client, conn,
971                                               channel_entry, ch);
972       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
973                          SILCTXT_CH_PRIVATE_KEY_CHANGE, i + 1,
974                          channel_entry->channel_name);
975
976       silc_dlist_uninit(ckeys);
977       goto out;
978     }
979   }
980
981   if (command == 0) {
982     silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO,
983              "Usage: /KEY msg|channel <nickname|channel> "
984              "set|unset|agreement|negotiate [<arguments>]");
985     goto out;
986   }
987
988   if (command == 4 && client_entry) {
989     SilcClientConnectionParams params;
990
991     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
992                        SILCTXT_KEY_AGREEMENT, argv[2]);
993     internal->responder = TRUE;
994
995     memset(&params, 0, sizeof(params));
996     params.local_ip = hostname;
997     params.bind_ip = bindhost;
998     params.local_port = port;
999     params.udp = udp;
1000     params.timeout_secs = settings_get_int("key_exchange_timeout_secs");
1001
1002     silc_client_send_key_agreement(
1003                            silc_client, conn, client_entry, &params,
1004                            irssi_pubkey, irssi_privkey,
1005                            keyagr_completion, internal);
1006     if (!hostname)
1007       silc_free(internal);
1008     goto out;
1009   }
1010
1011   if (command == 5 && client_entry && hostname) {
1012     SilcClientConnectionParams params;
1013
1014     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1015                        SILCTXT_KEY_AGREEMENT_NEGOTIATE, argv[2]);
1016     internal->responder = FALSE;
1017
1018     memset(&params, 0, sizeof(params));
1019     if (udp) {
1020       if (settings_get_bool("use_auto_addr")) {
1021         params.local_ip = (char *)settings_get_str("auto_public_ip");
1022         if ((params.local_ip) && (*params.local_ip == '\0')) {
1023           params.local_ip = silc_net_localip();
1024         } else {
1025           params.bind_ip = (char *)settings_get_str("auto_bind_ip");
1026           if ((params.bind_ip) && (*params.bind_ip == '\0'))
1027             params.bind_ip = NULL;
1028           params.local_port = settings_get_int("auto_bind_port");
1029         }
1030       }
1031       if (!params.local_ip)
1032         params.local_ip = silc_net_localip();
1033     }
1034     params.udp = udp;
1035     params.timeout_secs = settings_get_int("key_exchange_timeout_secs");
1036
1037     silc_client_perform_key_agreement(silc_client, conn, client_entry, &params,
1038                                       irssi_pubkey, irssi_privkey,
1039                                       hostname, port, keyagr_completion,
1040                                       internal);
1041     goto out;
1042   }
1043
1044  out:
1045   return;
1046 }
1047
1048 #if 0
1049 void silc_list_key(const char *pub_filename, int verbose)
1050 {
1051   SilcPublicKey public_key;
1052   SilcPublicKeyIdentifier ident;
1053   char *fingerprint, *babbleprint;
1054   unsigned char *pk;
1055   SilcUInt32 pk_len;
1056   SilcPKCS pkcs;
1057   SilcUInt32 key_len = 0;
1058   int is_server_key = (strstr(pub_filename, "serverkeys") != NULL);
1059
1060   if (silc_pkcs_load_public_key((char *)pub_filename, &public_key,
1061                                 SILC_PKCS_FILE_PEM) == FALSE)
1062     if (silc_pkcs_load_public_key((char *)pub_filename, &public_key,
1063                                   SILC_PKCS_FILE_BIN) == FALSE) {
1064       printformat_module("fe-common/silc", NULL, NULL,
1065                          MSGLEVEL_CRAP, SILCTXT_LISTKEY_LOADPUB,
1066                          pub_filename);
1067       return;
1068     }
1069
1070   ident = silc_pkcs_decode_identifier(public_key->identifier);
1071
1072   pk = silc_pkcs_public_key_encode(public_key, &pk_len);
1073   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1074   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
1075
1076   if (silc_pkcs_alloc(public_key->name, &pkcs)) {
1077     key_len = silc_pkcs_public_key_set(pkcs, public_key);
1078     silc_pkcs_free(pkcs);
1079   }
1080
1081   printformat_module("fe-common/silc", NULL, NULL,
1082                      MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_FILE,
1083                      pub_filename);
1084
1085   if (verbose)
1086     printformat_module("fe-common/silc", NULL, NULL,
1087                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_ALG,
1088                        public_key->name);
1089   if (key_len && verbose)
1090     printformat_module("fe-common/silc", NULL, NULL,
1091                         MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_BITS,
1092                         (unsigned int)key_len);
1093   if (ident->realname && (!is_server_key || verbose))
1094     printformat_module("fe-common/silc", NULL, NULL,
1095                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_RN,
1096                        ident->realname);
1097   if (ident->username && verbose)
1098     printformat_module("fe-common/silc", NULL, NULL,
1099                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_UN,
1100                        ident->username);
1101   if (ident->host && (is_server_key || verbose))
1102     printformat_module("fe-common/silc", NULL, NULL,
1103                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_HN,
1104                        ident->host);
1105   if (ident->email && verbose)
1106     printformat_module("fe-common/silc", NULL, NULL,
1107                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_EMAIL,
1108                        ident->email);
1109   if (ident->org && verbose)
1110     printformat_module("fe-common/silc", NULL, NULL,
1111                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_ORG,
1112                        ident->org);
1113   if (ident->country && verbose)
1114     printformat_module("fe-common/silc", NULL, NULL,
1115                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_C,
1116                        ident->country);
1117
1118   if (verbose) {
1119     printformat_module("fe-common/silc", NULL, NULL,
1120                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_FINGER,
1121                        fingerprint);
1122     printformat_module("fe-common/silc", NULL, NULL,
1123                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_BABL,
1124                        babbleprint);
1125   }
1126
1127   silc_free(fingerprint);
1128   silc_free(babbleprint);
1129   silc_free(pk);
1130   silc_pkcs_public_key_free(public_key);
1131   silc_pkcs_free_identifier(ident);
1132
1133 }
1134
1135 void silc_list_keys_in_dir(const char *dirname, const char *where)
1136 {
1137   DIR *dir;
1138   struct dirent *entry;
1139
1140   dir = opendir(dirname);
1141
1142   if (dir == NULL)
1143           cmd_return_error(CMDERR_ERRNO);
1144
1145   printformat_module("fe-common/silc", NULL, NULL,
1146                      MSGLEVEL_CRAP, SILCTXT_LISTKEY_LIST,
1147                      where);
1148
1149   rewinddir(dir);
1150
1151   while ((entry = readdir(dir)) != NULL) {
1152     /* try to open everything that isn't a directory */
1153     struct stat buf;
1154     char filename[256];
1155
1156     snprintf(filename, sizeof(filename) - 1, "%s/%s", dirname, entry->d_name);
1157     if (!stat(filename, &buf) && S_ISREG(buf.st_mode))
1158       silc_list_key(filename, FALSE);
1159   }
1160
1161   closedir(dir);
1162 }
1163
1164 void silc_list_file(const char *filename)
1165 {
1166
1167   char path[256];
1168   struct stat buf;
1169
1170   snprintf(path, sizeof(path) - 1, "%s", filename);
1171   if (!stat(path, &buf) && S_ISREG(buf.st_mode))
1172     goto list_key;
1173
1174   snprintf(path, sizeof(path) - 1, "%s/%s", get_irssi_dir(), filename);
1175   if (!stat(path, &buf) && S_ISREG(buf.st_mode))
1176     goto list_key;
1177
1178   snprintf(path,sizeof(path) - 1, "%s/clientkeys/%s", get_irssi_dir(),
1179            filename);
1180   if (!stat(path, &buf) && S_ISREG(buf.st_mode))
1181     goto list_key;
1182
1183   snprintf(path,sizeof(path) - 1, "%s/serverkeys/%s", get_irssi_dir(),
1184            filename);
1185   if (!stat(path, &buf) && S_ISREG(buf.st_mode))
1186     goto list_key;
1187
1188   return;
1189
1190 list_key:
1191
1192   silc_list_key(path, TRUE);
1193
1194 }
1195 #endif /* 0 */
1196
1197 /* Lists locally saved client and server public keys. */
1198 static void command_listkeys(const char *data, SILC_SERVER_REC *server,
1199                              WI_ITEM_REC *item)
1200 {
1201   GHashTable *optlist;
1202   char *filename;
1203   void *free_arg;
1204   char dirname[256];
1205
1206   if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
1207                       PARAM_FLAG_GETREST, "listkeys", &optlist,
1208                       &filename))
1209     return;
1210
1211   if (*filename != '\0') {
1212
1213     silc_list_file(filename);
1214
1215   } else {
1216     int clients, servers;
1217
1218     clients = (g_hash_table_lookup(optlist, "clients") != NULL);
1219     servers = (g_hash_table_lookup(optlist, "servers") != NULL);
1220
1221     if (!(clients || servers))
1222       clients = servers = 1;
1223
1224     if (servers) {
1225       snprintf(dirname, sizeof(dirname) - 1, "%s/serverkeys", get_irssi_dir());
1226       silc_list_keys_in_dir(dirname, "server");
1227     }
1228
1229     if (clients) {
1230       snprintf(dirname, sizeof(dirname) - 1, "%s/clientkeys", get_irssi_dir());
1231       silc_list_keys_in_dir(dirname, "client");
1232     }
1233   }
1234   cmd_params_free(free_arg);
1235 }
1236
1237 void silc_channels_init(void)
1238 {
1239   signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1240   signal_add("server connected", (SIGNAL_FUNC) sig_connected);
1241   signal_add("server quit", (SIGNAL_FUNC) sig_server_quit);
1242   signal_add("gui exit", (SIGNAL_FUNC) sig_gui_quit);
1243   signal_add("mime", (SIGNAL_FUNC) sig_mime);
1244
1245   command_bind_silc("part", MODULE_NAME, (SIGNAL_FUNC) command_part);
1246   command_bind_silc("me", MODULE_NAME, (SIGNAL_FUNC) command_me);
1247   command_bind_silc("action", MODULE_NAME, (SIGNAL_FUNC) command_action);
1248   command_bind_silc("notice", MODULE_NAME, (SIGNAL_FUNC) command_notice);
1249   command_bind_silc("away", MODULE_NAME, (SIGNAL_FUNC) command_away);
1250   command_bind_silc("key", MODULE_NAME, (SIGNAL_FUNC) command_key);
1251   //  command_bind("listkeys", MODULE_NAME, (SIGNAL_FUNC) command_listkeys);
1252
1253   //command_set_options("listkeys", "clients servers");
1254   command_set_options("action", "sign channel");
1255   command_set_options("notice", "sign channel");
1256
1257   silc_nicklist_init();
1258 }
1259
1260 void silc_channels_deinit(void)
1261 {
1262   signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1263   signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
1264   signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit);
1265   signal_remove("gui exit", (SIGNAL_FUNC) sig_gui_quit);
1266   signal_remove("mime", (SIGNAL_FUNC) sig_mime);
1267
1268   command_unbind("part", (SIGNAL_FUNC) command_part);
1269   command_unbind("me", (SIGNAL_FUNC) command_me);
1270   command_unbind("action", (SIGNAL_FUNC) command_action);
1271   command_unbind("notice", (SIGNAL_FUNC) command_notice);
1272   command_unbind("away", (SIGNAL_FUNC) command_away);
1273   command_unbind("key", (SIGNAL_FUNC) command_key);
1274   //  command_unbind("listkeys", (SIGNAL_FUNC) command_listkeys);
1275
1276   silc_nicklist_deinit();
1277 }