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