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