Fixed public key saving in WHOIS -details. Get user public key
[silc.git] / apps / irssi / src / silc / core / silc-channels.c
1 /*
2   silc-channels.c : irssi
3
4   Copyright (C) 2000 - 2001, 2004, 2006, 2007 Timo Sirainen
5                 Pekka Riikonen <priikone@silcnet.org>
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20 */
21
22 #include "module.h"
23
24 #include "net-nonblock.h"
25 #include "net-sendbuffer.h"
26 #include "signals.h"
27 #include "servers.h"
28 #include "commands.h"
29 #include "levels.h"
30 #include "modules.h"
31 #include "rawlog.h"
32 #include "misc.h"
33 #include "settings.h"
34
35 #include "channels-setup.h"
36
37 #include "silc-servers.h"
38 #include "silc-channels.h"
39 #include "silc-queries.h"
40 #include "silc-nicklist.h"
41 #include "silc-cmdqueue.h"
42 #include "window-item-def.h"
43
44 #include "fe-common/core/printtext.h"
45 #include "fe-common/silc/module-formats.h"
46
47 #include "silc-commands.h"
48
49 void sig_mime(SILC_SERVER_REC *server, SILC_CHANNEL_REC *channel,
50               const char *blob, const char *nick, int verified)
51 {
52   unsigned char *message;
53   SilcUInt32 message_len;
54   SilcMime mime;
55
56   if (!(IS_SILC_SERVER(server)))
57     return;
58
59   message = silc_unescape_data(blob, &message_len);
60
61   mime = silc_mime_decode(NULL, message, message_len);
62   if (!mime) {
63     silc_free(message);
64     return;
65   }
66
67   printformat_module("fe-common/silc", server,
68                      channel == NULL ? NULL : channel->name,
69                      MSGLEVEL_CRAP, SILCTXT_MESSAGE_DATA,
70                      nick == NULL ? "[<unknown>]" : nick,
71                      silc_mime_get_field(mime, "Content-Type"));
72
73   silc_free(message);
74   silc_mime_free(mime);
75 }
76
77 SILC_CHANNEL_REC *silc_channel_create(SILC_SERVER_REC *server,
78                                       const char *name,
79                                       const char *visible_name,
80                                       int automatic)
81 {
82   SILC_CHANNEL_REC *rec;
83
84   g_return_val_if_fail(server == NULL || IS_SILC_SERVER(server), NULL);
85   g_return_val_if_fail(name != NULL, NULL);
86
87   rec = g_new0(SILC_CHANNEL_REC, 1);
88   rec->chat_type = SILC_PROTOCOL;
89   channel_init((CHANNEL_REC *)rec, (SERVER_REC *)server, name, name,
90                automatic);
91   return rec;
92 }
93
94 static void sig_channel_destroyed(SILC_CHANNEL_REC *channel)
95 {
96   if (!IS_SILC_CHANNEL(channel))
97     return;
98   if (channel->server && channel->server->disconnected)
99     return;
100
101   if (channel->server != NULL && !channel->left && !channel->kicked) {
102     /* destroying channel record without actually
103        having left the channel yet */
104     silc_command_exec(channel->server, "LEAVE", channel->name);
105     /* enable queueing because we destroy the channel immedially */
106     silc_queue_enable(channel->server->conn);
107   }
108 }
109
110 static void silc_channels_join(SILC_SERVER_REC *server,
111                                const char *channels, int automatic)
112 {
113   char **list, **tmp;
114   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   SilcChannelPrivateKey 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   /* 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 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       while ((ch = silc_dlist_get(ckeys))) {
847         memset(buf, 0, sizeof(buf));
848         strncat(buf, "  ", 2);
849
850         len = strlen(silc_cipher_get_name(ch->cipher));
851         strncat(buf, silc_cipher_get_name(ch->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(ch->hmac));
859         strncat(buf, silc_hmac_get_name(ch->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_dlist_uninit(ckeys);
871     }
872
873     goto out;
874   }
875
876   /* Send command is used to send key agreement */
877   if (!strcasecmp(argv[3], "agreement")) {
878     command = 4;
879
880     if (argc >= 5)
881       hostname = argv[4];
882     if (argc >= 6) {
883       if (!strcasecmp(argv[5], "UDP"))
884         udp = TRUE;
885       else
886         port = atoi(argv[5]);
887     }
888     if (argc >= 7)
889       udp = TRUE;
890
891     internal = silc_calloc(1, sizeof(*internal));
892     internal->type = type;
893     internal->server = server;
894
895     if (!hostname) {
896       if (settings_get_bool("use_auto_addr")) {
897         hostname = (char *)settings_get_str("auto_public_ip");
898
899         /* If the hostname isn't set, treat this case as if auto_public_ip
900            wasn't set. */
901         if ((hostname) && (*hostname == '\0')) {
902            hostname = NULL;
903         } else {
904           bindhost = (char *)settings_get_str("auto_bind_ip");
905
906           /* if the bind_ip isn't set, but the public_ip IS, then assume then
907              public_ip is the same value as the bind_ip. */
908           if ((bindhost) && (*bindhost == '\0'))
909             bindhost = hostname;
910           port = settings_get_int("auto_bind_port");
911         }
912       }  /* if use_auto_addr */
913     }
914   }
915
916   /* Start command is used to start key agreement (after receiving the
917      key_agreement client operation). */
918   if (!strcasecmp(argv[3], "negotiate")) {
919     command = 5;
920
921     if (argc >= 5)
922       hostname = argv[4];
923     if (argc >= 6) {
924       if (!strcasecmp(argv[5], "UDP"))
925         udp = TRUE;
926       else
927         port = atoi(argv[5]);
928     }
929     if (argc >= 7)
930       udp = TRUE;
931
932     internal = silc_calloc(1, sizeof(*internal));
933     internal->type = type;
934     internal->server = server;
935   }
936
937   /* Change current channel private key */
938   if (!strcasecmp(argv[3], "change")) {
939     command = 6;
940     if (type == 2) {
941       /* Unset channel key(s) */
942       int number;
943
944       ckeys = silc_client_list_channel_private_keys(silc_client, conn,
945                                                     channel_entry);
946       if (!ckeys)
947         goto out;
948
949       if (argc == 4) {
950         chanrec->cur_key++;
951         if (chanrec->cur_key >= silc_dlist_count(ckeys))
952           chanrec->cur_key = 0;
953       }
954
955       if (argc > 4) {
956         number = atoi(argv[4]);
957         if (!number || number > silc_dlist_count(ckeys))
958           chanrec->cur_key = 0;
959         else
960           chanrec->cur_key = number - 1;
961       }
962
963       for (i = 0; i < chanrec->cur_key; i++)
964         ch = silc_dlist_get(ckeys);
965       if (!ch)
966         goto out;
967
968       /* Set the current channel private key */
969       silc_client_current_channel_private_key(silc_client, conn,
970                                               channel_entry, ch);
971       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
972                          SILCTXT_CH_PRIVATE_KEY_CHANGE, i + 1,
973                          channel_entry->channel_name);
974
975       silc_dlist_uninit(ckeys);
976       goto out;
977     }
978   }
979
980   if (command == 0) {
981     silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO,
982              "Usage: /KEY msg|channel <nickname|channel> "
983              "set|unset|agreement|negotiate [<arguments>]");
984     goto out;
985   }
986
987   if (command == 4 && client_entry) {
988     SilcClientConnectionParams params;
989
990     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
991                        SILCTXT_KEY_AGREEMENT, argv[2]);
992     internal->responder = TRUE;
993
994     memset(&params, 0, sizeof(params));
995     params.local_ip = hostname;
996     params.bind_ip = bindhost;
997     params.local_port = port;
998     params.udp = udp;
999     params.timeout_secs = settings_get_int("key_exchange_timeout_secs");
1000
1001     silc_client_send_key_agreement(
1002                            silc_client, conn, client_entry, &params,
1003                            irssi_pubkey, irssi_privkey,
1004                            keyagr_completion, internal);
1005     if (!hostname)
1006       silc_free(internal);
1007     goto out;
1008   }
1009
1010   if (command == 5 && client_entry && hostname) {
1011     SilcClientConnectionParams params;
1012
1013     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1014                        SILCTXT_KEY_AGREEMENT_NEGOTIATE, argv[2]);
1015     internal->responder = FALSE;
1016
1017     memset(&params, 0, sizeof(params));
1018     if (udp) {
1019       if (settings_get_bool("use_auto_addr")) {
1020         params.local_ip = (char *)settings_get_str("auto_public_ip");
1021         if ((params.local_ip) && (*params.local_ip == '\0')) {
1022           params.local_ip = silc_net_localip();
1023         } else {
1024           params.bind_ip = (char *)settings_get_str("auto_bind_ip");
1025           if ((params.bind_ip) && (*params.bind_ip == '\0'))
1026             params.bind_ip = NULL;
1027           params.local_port = settings_get_int("auto_bind_port");
1028         }
1029       }
1030       if (!params.local_ip)
1031         params.local_ip = silc_net_localip();
1032     }
1033     params.udp = udp;
1034     params.timeout_secs = settings_get_int("key_exchange_timeout_secs");
1035
1036     silc_client_perform_key_agreement(silc_client, conn, client_entry, &params,
1037                                       irssi_pubkey, irssi_privkey,
1038                                       hostname, port, keyagr_completion,
1039                                       internal);
1040     goto out;
1041   }
1042
1043  out:
1044   return;
1045 }
1046
1047 void silc_list_key(const char *pub_filename, int verbose)
1048 {
1049   SilcPublicKey public_key;
1050   SilcPublicKeyIdentifier ident;
1051   SilcSILCPublicKey silc_pubkey;
1052   char *fingerprint, *babbleprint;
1053   unsigned char *pk;
1054   SilcUInt32 pk_len;
1055   SilcUInt32 key_len = 0;
1056   int is_server_key = (strstr(pub_filename, "serverkeys") != NULL);
1057
1058   if (!silc_pkcs_load_public_key((char *)pub_filename, &public_key)) {
1059     printformat_module("fe-common/silc", NULL, NULL,
1060                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_LOADPUB,
1061                        pub_filename);
1062     return;
1063   }
1064
1065   /* Print only SILC public keys */
1066   if (silc_pkcs_get_type(public_key) != SILC_PKCS_SILC) {
1067     printformat_module("fe-common/silc", NULL, NULL,
1068                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_LOADPUB,
1069                        pub_filename);
1070     return;
1071   }
1072
1073   silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
1074   ident = &silc_pubkey->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   key_len = silc_pkcs_public_key_get_len(public_key);
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                        silc_pkcs_get_name(public_key));
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->version && verbose)
1094     printformat_module("fe-common/silc", NULL, NULL,
1095                         MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_VER,
1096                         ident->version);
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 }
1136
1137 void silc_list_keys_in_dir(const char *dirname, const char *where)
1138 {
1139   DIR *dir;
1140   struct dirent *entry;
1141
1142   dir = opendir(dirname);
1143
1144   if (dir == NULL)
1145     cmd_return_error(CMDERR_ERRNO);
1146
1147   printformat_module("fe-common/silc", NULL, NULL,
1148                      MSGLEVEL_CRAP, SILCTXT_LISTKEY_LIST,
1149                      where);
1150
1151   rewinddir(dir);
1152
1153   while ((entry = readdir(dir)) != NULL) {
1154     /* try to open everything that isn't a directory */
1155     struct stat buf;
1156     char filename[256];
1157
1158     snprintf(filename, sizeof(filename) - 1, "%s/%s", dirname, entry->d_name);
1159     if (!stat(filename, &buf) && S_ISREG(buf.st_mode))
1160       silc_list_key(filename, FALSE);
1161   }
1162
1163   closedir(dir);
1164 }
1165
1166 void silc_list_file(const char *filename)
1167 {
1168
1169   char path[256];
1170   struct stat buf;
1171
1172   snprintf(path, sizeof(path) - 1, "%s", filename);
1173   if (!stat(path, &buf) && S_ISREG(buf.st_mode))
1174     goto list_key;
1175
1176   snprintf(path, sizeof(path) - 1, "%s/%s", get_irssi_dir(), filename);
1177   if (!stat(path, &buf) && S_ISREG(buf.st_mode))
1178     goto list_key;
1179
1180   snprintf(path,sizeof(path) - 1, "%s/clientkeys/%s", get_irssi_dir(),
1181            filename);
1182   if (!stat(path, &buf) && S_ISREG(buf.st_mode))
1183     goto list_key;
1184
1185   snprintf(path,sizeof(path) - 1, "%s/serverkeys/%s", get_irssi_dir(),
1186            filename);
1187   if (!stat(path, &buf) && S_ISREG(buf.st_mode))
1188     goto list_key;
1189
1190   return;
1191
1192 list_key:
1193
1194   silc_list_key(path, TRUE);
1195 }
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("mime", (SIGNAL_FUNC) sig_mime);
1243
1244   command_bind_silc("part", MODULE_NAME, (SIGNAL_FUNC) command_part);
1245   command_bind_silc("me", MODULE_NAME, (SIGNAL_FUNC) command_me);
1246   command_bind_silc("action", MODULE_NAME, (SIGNAL_FUNC) command_action);
1247   command_bind_silc("notice", MODULE_NAME, (SIGNAL_FUNC) command_notice);
1248   command_bind_silc("away", MODULE_NAME, (SIGNAL_FUNC) command_away);
1249   command_bind_silc("key", MODULE_NAME, (SIGNAL_FUNC) command_key);
1250   command_bind("listkeys", MODULE_NAME, (SIGNAL_FUNC) command_listkeys);
1251
1252   command_set_options("listkeys", "clients servers");
1253   command_set_options("action", "sign channel");
1254   command_set_options("notice", "sign channel");
1255
1256   silc_nicklist_init();
1257 }
1258
1259 void silc_channels_deinit(void)
1260 {
1261   signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1262   signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
1263   signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit);
1264   signal_remove("mime", (SIGNAL_FUNC) sig_mime);
1265
1266   command_unbind("part", (SIGNAL_FUNC) command_part);
1267   command_unbind("me", (SIGNAL_FUNC) command_me);
1268   command_unbind("action", (SIGNAL_FUNC) command_action);
1269   command_unbind("notice", (SIGNAL_FUNC) command_notice);
1270   command_unbind("away", (SIGNAL_FUNC) command_away);
1271   command_unbind("key", (SIGNAL_FUNC) command_key);
1272   command_unbind("listkeys", (SIGNAL_FUNC) command_listkeys);
1273
1274   silc_nicklist_deinit();
1275 }