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