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