Added support for setting specific founder public key in
[silc.git] / apps / irssi / src / silc / core / silc-channels.c
1 /*
2   silc-channels.c : irssi
3
4   Copyright (C) 2000 - 2001 Timo Sirainen
5                             Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2 of the License, or
10   (at your option) any later version.
11   
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16   
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20 */
21
22 #include "module.h"
23
24 #include "net-nonblock.h"
25 #include "net-sendbuffer.h"
26 #include "signals.h"
27 #include "servers.h"
28 #include "commands.h"
29 #include "levels.h"
30 #include "modules.h"
31 #include "rawlog.h"
32 #include "misc.h"
33 #include "settings.h"
34
35 #include "channels-setup.h"
36
37 #include "silc-servers.h"
38 #include "silc-channels.h"
39 #include "silc-queries.h"
40 #include "silc-nicklist.h"
41 #include "window-item-def.h"
42
43 #include "fe-common/core/printtext.h"
44 #include "fe-common/silc/module-formats.h"
45
46 #include "silc-commands.h"
47
48 SILC_CHANNEL_REC *silc_channel_create(SILC_SERVER_REC *server,
49                                       const char *name,
50                                       const char *visible_name,
51                                       int automatic)
52 {
53   SILC_CHANNEL_REC *rec;
54
55   g_return_val_if_fail(server == NULL || IS_SILC_SERVER(server), NULL);
56   g_return_val_if_fail(name != NULL, NULL);
57
58   rec = g_new0(SILC_CHANNEL_REC, 1);
59   rec->chat_type = SILC_PROTOCOL;
60   channel_init((CHANNEL_REC *)rec, (SERVER_REC *)server, name, name,
61                automatic);
62   return rec;
63 }
64
65 static void sig_channel_destroyed(SILC_CHANNEL_REC *channel)
66 {
67   if (!IS_SILC_CHANNEL(channel))
68     return;
69   if (channel->server && channel->server->disconnected)
70     return;
71
72   if (channel->server != NULL && !channel->left && !channel->kicked) {
73     /* destroying channel record without actually
74        having left the channel yet */
75     silc_command_exec(channel->server, "LEAVE", channel->name);
76   }
77 }
78
79 static void silc_channels_join(SILC_SERVER_REC *server,
80                                const char *channels, int automatic)
81 {
82   char **list, **tmp;
83   SILC_CHANNEL_REC *chanrec;
84
85   list = g_strsplit(channels, ",", -1);
86   for (tmp = list; *tmp != NULL; tmp++) {
87     chanrec = silc_channel_find(server, *tmp);
88     if (chanrec)
89       continue;
90
91     silc_command_exec(server, "JOIN", *tmp);
92   }
93
94   g_strfreev(list);
95 }
96
97 static void sig_connected(SILC_SERVER_REC *server)
98 {
99   if (IS_SILC_SERVER(server))
100     server->channels_join = (void *) silc_channels_join;
101 }
102
103 /* "server quit" signal from the core to indicate that QUIT command
104    was called. */
105
106 static void sig_server_quit(SILC_SERVER_REC *server, const char *msg)
107 {
108   if (IS_SILC_SERVER(server) && server->conn && server->conn->sock)
109     silc_command_exec(server, "QUIT", msg);
110 }
111
112 static void sig_gui_quit(SILC_SERVER_REC *server, const char *msg)
113 {
114   silc_client_stop(silc_client);
115 }
116
117 /* Find Irssi channel entry by SILC channel entry */
118
119 SILC_CHANNEL_REC *silc_channel_find_entry(SILC_SERVER_REC *server,
120                                           SilcChannelEntry entry)
121 {
122   GSList *tmp;
123
124   g_return_val_if_fail(IS_SILC_SERVER(server), NULL);
125
126   for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
127     SILC_CHANNEL_REC *rec = tmp->data;
128
129     if (rec->entry == entry)
130       return rec;
131   }
132
133   return NULL;
134 }
135
136 /* PART (LEAVE) command. */
137
138 static void command_part(const char *data, SILC_SERVER_REC *server,
139                          WI_ITEM_REC *item)
140 {
141   SILC_CHANNEL_REC *chanrec;
142   char userhost[256];
143   
144   CMD_SILC_SERVER(server);
145
146   if (!IS_SILC_SERVER(server) || !server->connected)
147     cmd_return_error(CMDERR_NOT_CONNECTED);
148
149   if (!strcmp(data, "*") || *data == '\0') {
150     if (!IS_SILC_CHANNEL(item))
151       cmd_return_error(CMDERR_NOT_JOINED);
152     data = item->visible_name;
153   }
154
155   chanrec = silc_channel_find(server, data);
156   if (chanrec == NULL) 
157     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
158
159   memset(userhost, 0, sizeof(userhost));
160   snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
161            server->conn->local_entry->username, 
162            server->conn->local_entry->hostname);
163   signal_emit("message part", 5, server, chanrec->name,
164               server->nick, userhost, "");
165   
166   chanrec->left = TRUE;
167   silc_command_exec(server, "LEAVE", chanrec->name);
168   signal_stop();
169
170   channel_destroy(CHANNEL(chanrec));
171 }
172
173 /* ME local command. */
174
175 static void command_me(const char *data, SILC_SERVER_REC *server,
176                        WI_ITEM_REC *item)
177 {
178   SILC_CHANNEL_REC *chanrec;
179   char *tmpcmd = "ME", *tmp;
180   SilcUInt32 argc = 0;
181   unsigned char *message = NULL;
182   unsigned char **argv;
183   SilcUInt32 *argv_lens, *argv_types;
184   int i;
185  
186   CMD_SILC_SERVER(server);
187
188   if (!IS_SILC_SERVER(server) || !server->connected)
189     cmd_return_error(CMDERR_NOT_CONNECTED);
190
191   if (!IS_SILC_CHANNEL(item))
192     cmd_return_error(CMDERR_NOT_JOINED);
193
194   /* Now parse all arguments */
195   tmp = g_strconcat(tmpcmd, " ", data, NULL);
196   silc_parse_command_line(tmp, &argv, &argv_lens,
197                           &argv_types, &argc, 2);
198   g_free(tmp);
199
200   if (argc < 2)
201     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
202
203   chanrec = silc_channel_find(server, item->visible_name);
204   if (chanrec == NULL) 
205     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
206
207   if (!silc_term_utf8()) {
208     int len = silc_utf8_encoded_len(argv[1], argv_lens[1],
209                                     SILC_STRING_LANGUAGE);
210     message = silc_calloc(len + 1, sizeof(*message));
211     g_return_if_fail(message != NULL);
212     silc_utf8_encode(argv[1], argv_lens[1], SILC_STRING_LANGUAGE,
213                      message, len);
214   }
215
216   /* Send the action message */
217   silc_client_send_channel_message(silc_client, server->conn, 
218                                    chanrec->entry, NULL,
219                                    SILC_MESSAGE_FLAG_ACTION |
220                                    SILC_MESSAGE_FLAG_UTF8,
221                                    message ? message : argv[1],
222                                    message ? strlen(message) : argv_lens[1],
223                                    TRUE);
224
225   printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
226                      MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_OWNACTION, 
227                      server->conn->local_entry->nickname, argv[1]);
228
229   for (i = 0; i < argc; i++)
230     silc_free(argv[i]);
231   silc_free(argv_lens);
232   silc_free(argv_types);
233   silc_free(message);
234 }
235
236 /* ACTION local command. Same as ME but takes the channel as mandatory
237    argument. */
238
239 static void command_action(const char *data, SILC_SERVER_REC *server,
240                            WI_ITEM_REC *item)
241 {
242   SILC_CHANNEL_REC *chanrec;
243   char *tmpcmd = "ME", *tmp;
244   SilcUInt32 argc = 0;
245   unsigned char *message = NULL;
246   unsigned char **argv;
247   SilcUInt32 *argv_lens, *argv_types;
248   int i;
249  
250   CMD_SILC_SERVER(server);
251   if (!IS_SILC_SERVER(server) || !server->connected)
252     cmd_return_error(CMDERR_NOT_CONNECTED);
253
254   if (!IS_SILC_CHANNEL(item))
255     cmd_return_error(CMDERR_NOT_JOINED);
256
257   /* Now parse all arguments */
258   tmp = g_strconcat(tmpcmd, " ", data, NULL);
259   silc_parse_command_line(tmp, &argv, &argv_lens,
260                           &argv_types, &argc, 3);
261   g_free(tmp);
262
263   if (argc < 3)
264     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
265
266   chanrec = silc_channel_find(server, argv[1]);
267   if (chanrec == NULL) 
268     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
269
270   if (!silc_term_utf8()) {
271     int len = silc_utf8_encoded_len(argv[2], argv_lens[2],
272                                     SILC_STRING_LANGUAGE);
273     message = silc_calloc(len + 1, sizeof(*message));
274     g_return_if_fail(message != NULL);
275     silc_utf8_encode(argv[2], argv_lens[2], SILC_STRING_LANGUAGE,
276                      message, len);
277   }
278
279   /* Send the action message */
280   silc_client_send_channel_message(silc_client, server->conn, 
281                                    chanrec->entry, NULL,
282                                    SILC_MESSAGE_FLAG_ACTION |
283                                    SILC_MESSAGE_FLAG_UTF8,
284                                    message ? message : argv[2],
285                                    message ? strlen(message) : argv_lens[2],
286                                    TRUE);
287
288   printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
289                      MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_OWNACTION, 
290                      server->conn->local_entry->nickname, argv[2]);
291
292   for (i = 0; i < argc; i++)
293     silc_free(argv[i]);
294   silc_free(argv_lens);
295   silc_free(argv_types);
296   silc_free(message);
297 }
298
299 /* NOTICE local command. */
300
301 static void command_notice(const char *data, SILC_SERVER_REC *server,
302                            WI_ITEM_REC *item)
303 {
304   SILC_CHANNEL_REC *chanrec;
305   char *tmpcmd = "ME", *tmp;
306   SilcUInt32 argc = 0;
307   unsigned char *message = NULL;
308   unsigned char **argv;
309   SilcUInt32 *argv_lens, *argv_types;
310   int i;
311  
312   CMD_SILC_SERVER(server);
313   if (!IS_SILC_SERVER(server) || !server->connected)
314     cmd_return_error(CMDERR_NOT_CONNECTED);
315
316   if (!IS_SILC_CHANNEL(item))
317     cmd_return_error(CMDERR_NOT_JOINED);
318
319   /* Now parse all arguments */
320   tmp = g_strconcat(tmpcmd, " ", data, NULL);
321   silc_parse_command_line(tmp, &argv, &argv_lens,
322                           &argv_types, &argc, 2);
323   g_free(tmp);
324
325   if (argc < 2)
326     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
327
328   chanrec = silc_channel_find(server, item->visible_name);
329   if (chanrec == NULL) 
330     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
331
332   if (!silc_term_utf8()) {
333     int len = silc_utf8_encoded_len(argv[1], argv_lens[1],
334                                     SILC_STRING_LANGUAGE);
335     message = silc_calloc(len + 1, sizeof(*message));
336     g_return_if_fail(message != NULL);
337     silc_utf8_encode(argv[1], argv_lens[1], SILC_STRING_LANGUAGE,
338                      message, len);
339   }
340
341   /* Send the action message */
342   silc_client_send_channel_message(silc_client, server->conn, 
343                                    chanrec->entry, NULL,
344                                    SILC_MESSAGE_FLAG_NOTICE |
345                                    SILC_MESSAGE_FLAG_UTF8,
346                                    message ? message : argv[1],
347                                    message ? strlen(message) : argv_lens[1],
348                                    TRUE);
349
350   printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
351                      MSGLEVEL_NOTICES, SILCTXT_CHANNEL_OWNNOTICE, 
352                      server->conn->local_entry->nickname, argv[1]);
353
354   for (i = 0; i < argc; i++)
355     silc_free(argv[i]);
356   silc_free(argv_lens);
357   silc_free(argv_types);
358   silc_free(message);
359 }
360
361 /* AWAY local command.  Sends UMODE command that sets the SILC_UMODE_GONE
362    flag. */
363
364 bool silc_set_away(const char *reason, SILC_SERVER_REC *server)
365 {
366   bool set;
367   
368   if (!IS_SILC_SERVER(server) || !server->connected)
369     return FALSE;
370   
371   if (*reason == '\0') {
372     /* Remove any possible away message */
373     silc_client_set_away_message(silc_client, server->conn, NULL);
374     set = FALSE;
375
376     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, 
377                        SILCTXT_UNSET_AWAY);
378   } else {
379     /* Set the away message */
380     silc_client_set_away_message(silc_client, server->conn, (char *)reason);
381     set = TRUE;
382
383     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, 
384                        SILCTXT_SET_AWAY, reason);
385   }
386
387   server->usermode_away = set;
388   g_free_and_null(server->away_reason);
389   if (set)
390     server->away_reason = g_strdup((char *)reason);
391
392   signal_emit("away mode changed", 1, server);
393
394   return set;
395 }
396
397 static void command_away(const char *data, SILC_SERVER_REC *server,
398                          WI_ITEM_REC *item)
399 {
400   CMD_SILC_SERVER(server);
401
402   if (!IS_SILC_SERVER(server) || !server->connected)
403     cmd_return_error(CMDERR_NOT_CONNECTED);
404
405   g_free_and_null(server->away_reason);
406   if ((data) && (*data != '\0'))
407     server->away_reason = g_strdup(data);
408   
409   silc_command_exec(server, "UMODE", 
410                     (server->away_reason != NULL) ? "+g" : "-g");
411 }
412
413 typedef struct {
414   int type;                     /* 1 = msg, 2 = channel */
415   bool responder;
416   SILC_SERVER_REC *server;
417 } *KeyInternal;
418
419 /* Key agreement callback that is called after the key agreement protocol
420    has been performed. This is called also if error occured during the
421    key agreement protocol. The `key' is the allocated key material and
422    the caller is responsible of freeing it. The `key' is NULL if error
423    has occured. The application can freely use the `key' to whatever
424    purpose it needs. See lib/silcske/silcske.h for the definition of
425    the SilcSKEKeyMaterial structure. */
426
427 static void keyagr_completion(SilcClient client,
428                               SilcClientConnection conn,
429                               SilcClientEntry client_entry,
430                               SilcKeyAgreementStatus status,
431                               SilcSKEKeyMaterial *key,
432                               void *context)
433 {
434   KeyInternal i = (KeyInternal)context;
435
436   switch(status) {
437   case SILC_KEY_AGREEMENT_OK:
438     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
439                        SILCTXT_KEY_AGREEMENT_OK, client_entry->nickname);
440
441     if (i->type == 1) {
442       /* Set the private key for this client */
443       silc_client_del_private_message_key(client, conn, client_entry);
444       silc_client_add_private_message_key_ske(client, conn, client_entry,
445                                               NULL, NULL, key, i->responder);
446       printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
447                          SILCTXT_KEY_AGREEMENT_PRIVMSG, 
448                          client_entry->nickname);
449       silc_ske_free_key_material(key);
450     }
451     
452     break;
453     
454   case SILC_KEY_AGREEMENT_ERROR:
455     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
456                        SILCTXT_KEY_AGREEMENT_ERROR, client_entry->nickname);
457     break;
458     
459   case SILC_KEY_AGREEMENT_FAILURE:
460     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
461                        SILCTXT_KEY_AGREEMENT_FAILURE, client_entry->nickname);
462     break;
463     
464   case SILC_KEY_AGREEMENT_TIMEOUT:
465     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
466                        SILCTXT_KEY_AGREEMENT_TIMEOUT, client_entry->nickname);
467     break;
468     
469   case SILC_KEY_AGREEMENT_ABORTED:
470     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
471                        SILCTXT_KEY_AGREEMENT_ABORTED, client_entry->nickname);
472     break;
473
474   case SILC_KEY_AGREEMENT_ALREADY_STARTED:
475     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
476                        SILCTXT_KEY_AGREEMENT_ALREADY_STARTED,
477                        client_entry->nickname);
478     break;
479     
480   case SILC_KEY_AGREEMENT_SELF_DENIED:
481     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
482                        SILCTXT_KEY_AGREEMENT_SELF_DENIED);
483     break;
484     
485   default:
486     break;
487   } 
488
489   if (i)
490     silc_free(i);
491 }
492
493 /* Local command KEY. This command is used to set and unset private
494    keys for channels, set and unset private keys for private messages
495    with remote clients and to send key agreement requests and
496    negotiate the key agreement protocol with remote client.  The
497    key agreement is supported only to negotiate private message keys,
498    it currently cannot be used to negotiate private keys for channels,
499    as it is not convenient for that purpose. */
500
501 typedef struct {
502   SILC_SERVER_REC *server;
503   char *data;
504   char *nick;
505   WI_ITEM_REC *item;
506 } *KeyGetClients;
507
508 /* Callback to be called after client information is resolved from the
509    server. */
510
511 static void silc_client_command_key_get_clients(SilcClient client,
512                                                 SilcClientConnection conn,
513                                                 SilcClientEntry *clients,
514                                                 SilcUInt32 clients_count,
515                                                 void *context)
516 {
517   KeyGetClients internal = (KeyGetClients)context;
518
519   if (!clients) {
520     printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Unknown nick: %s", 
521               internal->nick);
522     silc_free(internal->data);
523     silc_free(internal->nick);
524     silc_free(internal);
525     return;
526   }
527
528   signal_emit("command key", 3, internal->data, internal->server,
529               internal->item);
530
531   silc_free(internal->data);
532   silc_free(internal->nick);
533   silc_free(internal);
534 }
535
536 static void command_key(const char *data, SILC_SERVER_REC *server,
537                         WI_ITEM_REC *item)
538 {
539   SilcClientConnection conn;
540   SilcClientEntry *entrys, client_entry = NULL;
541   SilcUInt32 entry_count;
542   SILC_CHANNEL_REC *chanrec = NULL;
543   SilcChannelEntry channel_entry = NULL;
544   char *nickname = NULL, *tmp;
545   int command = 0, port = 0, type = 0;
546   char *hostname = NULL;
547   KeyInternal internal = NULL;
548   SilcUInt32 argc = 0;
549   unsigned char **argv;
550   SilcUInt32 *argv_lens, *argv_types;
551   char *bindhost = NULL;
552  
553   CMD_SILC_SERVER(server);
554
555   if (!server || !IS_SILC_SERVER(server) || !server->connected)
556     cmd_return_error(CMDERR_NOT_CONNECTED);
557
558   conn = server->conn;
559
560   /* Now parse all arguments */
561   tmp = g_strconcat("KEY", " ", data, NULL);
562   silc_parse_command_line(tmp, &argv, &argv_lens, &argv_types, &argc, 7);
563   g_free(tmp);
564
565   if (argc < 4)
566     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
567
568   /* Get type */
569   if (!strcasecmp(argv[1], "msg"))
570     type = 1;
571   if (!strcasecmp(argv[1], "channel"))
572     type = 2;
573
574   if (type == 0)
575     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
576
577   if (type == 1) {
578     if (argv[2][0] == '*') {
579       nickname = strdup("*");
580     } else {
581       /* Parse the typed nickname. */
582       if (!silc_parse_userfqdn(argv[2], &nickname, NULL)) {
583         printformat_module("fe-common/silc", server, NULL,
584                            MSGLEVEL_CRAP, SILCTXT_BAD_NICK, argv[2]);
585         return;
586       }
587       
588       /* Find client entry */
589       entrys = silc_client_get_clients_local(silc_client, conn, nickname,
590                                              argv[2], &entry_count);
591       if (!entrys) {
592         KeyGetClients inter = silc_calloc(1, sizeof(*inter));
593         inter->server = server;
594         inter->data = strdup(data);
595         inter->nick = strdup(nickname);
596         inter->item = item;
597         silc_client_get_clients(silc_client, conn, nickname, argv[2],
598                                 silc_client_command_key_get_clients, inter);
599         goto out;
600       }
601       client_entry = entrys[0];
602       silc_free(entrys);
603     }
604   }
605
606   if (type == 2) {
607     /* Get channel entry */
608     char *name;
609
610     if (argv[2][0] == '*') {
611       if (!conn->current_channel) {
612         silc_free(nickname);
613         cmd_return_error(CMDERR_NOT_JOINED);
614       }
615       name = conn->current_channel->channel_name;
616     } else {
617       name = argv[2];
618     }
619
620     chanrec = silc_channel_find(server, name);
621     if (chanrec == NULL) {
622       silc_free(nickname);
623       cmd_return_error(CMDERR_CHAN_NOT_FOUND);
624     }
625     channel_entry = chanrec->entry;
626   }
627
628   /* Set command */
629   if (!strcasecmp(argv[3], "set")) {
630     command = 1;
631
632     if (argc >= 5) {
633       char *cipher = NULL, *hmac = NULL;
634
635       if (type == 1 && client_entry) {
636         /* Set private message key */
637         bool responder = FALSE;
638         
639         silc_client_del_private_message_key(silc_client, conn, client_entry);
640
641         if (argc >= 6) {
642           if (!strcasecmp(argv[5], "-responder"))
643             responder = TRUE;
644           else
645             cipher = argv[5];
646         }
647         if (argc >= 7) {
648           if (!strcasecmp(argv[6], "-responder"))
649             responder = TRUE;
650           else
651             hmac = argv[6];
652         }
653         if (argc >= 8) {
654           if (!strcasecmp(argv[7], "-responder"))
655             responder = TRUE;
656         }
657
658         silc_client_add_private_message_key(silc_client, conn, client_entry,
659                                             cipher, hmac,
660                                             argv[4], argv_lens[4],
661                                             (argv[4][0] == '*' ?
662                                              TRUE : FALSE), responder);
663
664         /* Send the key to the remote client so that it starts using it
665            too. */
666         /* XXX for now we don't do this.  This feature is pretty stupid
667            and should perhaps be removed altogether from SILC.
668         silc_client_send_private_message_key(silc_client, conn, 
669                                              client_entry, TRUE);
670         */
671       } else if (type == 2) {
672         /* Set private channel key */
673         if (!(channel_entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
674           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
675                              SILCTXT_CH_PRIVATE_KEY_NOMODE, 
676                              channel_entry->channel_name);
677           goto out;
678         }
679
680         if (argc >= 6)
681           cipher = argv[5];
682         if (argc >= 7)
683           hmac = argv[6];
684
685         if (!silc_client_add_channel_private_key(silc_client, conn, 
686                                                  channel_entry, NULL,
687                                                  cipher, hmac,
688                                                  argv[4],
689                                                  argv_lens[4])) {
690           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
691                              SILCTXT_CH_PRIVATE_KEY_ERROR, 
692                              channel_entry->channel_name);
693           goto out;
694         }
695
696         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
697                            SILCTXT_CH_PRIVATE_KEY_ADD, 
698                            channel_entry->channel_name);
699       }
700     }
701
702     goto out;
703   }
704   
705   /* Unset command */
706   if (!strcasecmp(argv[3], "unset")) {
707     command = 2;
708
709     if (type == 1 && client_entry) {
710       /* Unset private message key */
711       silc_client_del_private_message_key(silc_client, conn, client_entry);
712     } else if (type == 2) {
713       /* Unset channel key(s) */
714       SilcChannelPrivateKey *keys;
715       SilcUInt32 keys_count;
716       int number;
717
718       if (argc == 4)
719         silc_client_del_channel_private_keys(silc_client, conn, 
720                                              channel_entry);
721
722       if (argc > 4) {
723         number = atoi(argv[4]);
724         keys = silc_client_list_channel_private_keys(silc_client, conn, 
725                                                      channel_entry,
726                                                      &keys_count);
727         if (!keys)
728           goto out;
729
730         if (!number || number > keys_count) {
731           silc_client_free_channel_private_keys(keys, keys_count);
732           goto out;
733         }
734
735         silc_client_del_channel_private_key(silc_client, conn, channel_entry,
736                                             keys[number - 1]);
737         silc_client_free_channel_private_keys(keys, keys_count);
738       }
739
740       goto out;
741     }
742   }
743
744   /* List command */
745   if (!strcasecmp(argv[3], "list")) {
746     command = 3;
747
748     if (type == 1) {
749       SilcPrivateMessageKeys keys;
750       SilcUInt32 keys_count;
751       int k, i, len;
752       char buf[1024];
753
754       keys = silc_client_list_private_message_keys(silc_client, conn, 
755                                                    &keys_count);
756       if (!keys)
757         goto out;
758
759       /* list the private message key(s) */
760       if (nickname[0] == '*') {
761         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
762                            SILCTXT_PRIVATE_KEY_LIST);
763         for (k = 0; k < keys_count; k++) {
764           memset(buf, 0, sizeof(buf));
765           strncat(buf, "  ", 2);
766           len = strlen(keys[k].client_entry->nickname);
767           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
768           if (len < 30)
769             for (i = 0; i < 30 - len; i++)
770               strcat(buf, " ");
771           strcat(buf, " ");
772           
773           len = strlen(keys[k].cipher);
774           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
775           if (len < 14)
776             for (i = 0; i < 14 - len; i++)
777               strcat(buf, " ");
778           strcat(buf, " ");
779
780           if (keys[k].key)
781             strcat(buf, "<hidden>");
782           else
783             strcat(buf, "*generated*");
784
785           silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
786         }
787       } else {
788         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
789                            SILCTXT_PRIVATE_KEY_LIST_NICK,
790                            client_entry->nickname);
791         for (k = 0; k < keys_count; k++) {
792           if (keys[k].client_entry != client_entry)
793             continue;
794
795           memset(buf, 0, sizeof(buf));
796           strncat(buf, "  ", 2);
797           len = strlen(keys[k].client_entry->nickname);
798           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
799           if (len < 30)
800             for (i = 0; i < 30 - len; i++)
801               strcat(buf, " ");
802           strcat(buf, " ");
803           
804           len = strlen(keys[k].cipher);
805           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
806           if (len < 14)
807             for (i = 0; i < 14 - len; i++)
808               strcat(buf, " ");
809           strcat(buf, " ");
810
811           if (keys[k].key)
812             strcat(buf, "<hidden>");
813           else
814             strcat(buf, "*generated*");
815
816           silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
817         }
818       }
819
820       silc_client_free_private_message_keys(keys, keys_count);
821
822     } else if (type == 2) {
823       SilcChannelPrivateKey *keys;
824       SilcUInt32 keys_count;
825       int k, i, len;
826       char buf[1024];
827
828       keys = silc_client_list_channel_private_keys(silc_client, conn, 
829                                                    channel_entry,
830                                                    &keys_count);
831
832       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
833                          SILCTXT_CH_PRIVATE_KEY_LIST,
834                          channel_entry->channel_name);
835
836       if (!keys)
837         goto out;
838       
839       for (k = 0; k < keys_count; k++) {
840         memset(buf, 0, sizeof(buf));
841         strncat(buf, "  ", 2);
842
843         len = strlen(silc_cipher_get_name(keys[k]->cipher));
844         strncat(buf, silc_cipher_get_name(keys[k]->cipher),
845                 len > 16 ? 16 : len);
846         if (len < 16)
847           for (i = 0; i < 16 - len; i++)
848             strcat(buf, " ");
849         strcat(buf, " ");
850         
851         len = strlen(silc_hmac_get_name(keys[k]->hmac));
852         strncat(buf, silc_hmac_get_name(keys[k]->hmac), len > 16 ? 16 : len);
853         if (len < 16)
854           for (i = 0; i < 16 - len; i++)
855             strcat(buf, " ");
856         strcat(buf, " ");
857         
858         strcat(buf, "<hidden>");
859
860         silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
861       }
862       
863       silc_client_free_channel_private_keys(keys, keys_count);
864     }
865
866     goto out;
867   }
868
869   /* Send command is used to send key agreement */
870   if (!strcasecmp(argv[3], "agreement")) {
871     command = 4;
872
873     if (argc >= 5)
874       hostname = argv[4];
875     if (argc >= 6)
876       port = atoi(argv[5]);
877
878     internal = silc_calloc(1, sizeof(*internal));
879     internal->type = type;
880     internal->server = server;
881     
882     if (!hostname) {
883       if (settings_get_bool("use_auto_addr")) {
884        
885         hostname = (char *)settings_get_str("auto_public_ip");
886
887         /* If the hostname isn't set, treat this case as if auto_public_ip 
888            wasn't set. */
889         if ((hostname) && (*hostname == '\0')) {
890            hostname = NULL;
891         } else {
892           bindhost = (char *)settings_get_str("auto_bind_ip");
893             
894           /* if the bind_ip isn't set, but the public_ip IS, then assume then
895              public_ip is the same value as the bind_ip. */
896           if ((bindhost) && (*bindhost == '\0'))
897             bindhost = hostname;
898           port = settings_get_int("auto_bind_port");
899         }
900       }  /* if use_auto_addr */
901     }
902   }
903
904   /* Start command is used to start key agreement (after receiving the
905      key_agreement client operation). */
906   if (!strcasecmp(argv[3], "negotiate")) {
907     command = 5;
908
909     if (argc >= 5)
910       hostname = argv[4];
911     if (argc >= 6)
912       port = atoi(argv[5]);
913
914     internal = silc_calloc(1, sizeof(*internal));
915     internal->type = type;
916     internal->server = server;
917   }
918
919   /* Change current channel private key */
920   if (!strcasecmp(argv[3], "change")) {
921     command = 6;
922     if (type == 2) {
923       /* Unset channel key(s) */
924       SilcChannelPrivateKey *keys;
925       SilcUInt32 keys_count;
926       int number;
927
928       keys = silc_client_list_channel_private_keys(silc_client, conn, 
929                                                    channel_entry,
930                                                    &keys_count);
931       if (!keys)
932         goto out;
933
934       if (argc == 4) {
935         chanrec->cur_key++;
936         if (chanrec->cur_key >= keys_count)
937           chanrec->cur_key = 0;
938       }
939
940       if (argc > 4) {
941         number = atoi(argv[4]);
942         if (!number || number > keys_count)
943           chanrec->cur_key = 0;
944         else
945           chanrec->cur_key = number - 1;
946       }
947
948       /* Set the current channel private key */
949       silc_client_current_channel_private_key(silc_client, conn, 
950                                               channel_entry, 
951                                               keys[chanrec->cur_key]);
952       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
953                          SILCTXT_CH_PRIVATE_KEY_CHANGE, chanrec->cur_key + 1,
954                          channel_entry->channel_name);
955
956       silc_client_free_channel_private_keys(keys, keys_count);
957       goto out;
958     }
959   }
960
961   if (command == 0) {
962     silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO,
963              "Usage: /KEY msg|channel <nickname|channel> "
964              "set|unset|agreement|negotiate [<arguments>]");
965     goto out;
966   }
967
968   if (command == 4 && client_entry) {
969     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
970                        SILCTXT_KEY_AGREEMENT, argv[2]);
971     internal->responder = TRUE;
972     silc_client_send_key_agreement(
973                            silc_client, conn, client_entry, hostname, 
974                            bindhost, port, 
975                            settings_get_int("key_exchange_timeout_secs"), 
976                            keyagr_completion, internal);
977     if (!hostname)
978       silc_free(internal);
979     goto out;
980   }
981
982   if (command == 5 && client_entry && hostname) {
983     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
984                        SILCTXT_KEY_AGREEMENT_NEGOTIATE, argv[2]);
985     internal->responder = FALSE;
986     silc_client_perform_key_agreement(silc_client, conn, client_entry, 
987                                       hostname, port, keyagr_completion, 
988                                       internal);
989     goto out;
990   }
991
992  out:
993   silc_free(nickname);
994 }
995
996 /* Lists locally saved client and server public keys. */
997
998 static void command_listkeys(const char *data, SILC_SERVER_REC *server,
999                              WI_ITEM_REC *item)
1000 {
1001
1002 }
1003
1004 void silc_channels_init(void)
1005 {
1006   signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1007   signal_add("server connected", (SIGNAL_FUNC) sig_connected);
1008   signal_add("server quit", (SIGNAL_FUNC) sig_server_quit);
1009   signal_add("gui exit", (SIGNAL_FUNC) sig_gui_quit);
1010
1011   command_bind_silc("part", MODULE_NAME, (SIGNAL_FUNC) command_part);
1012   command_bind_silc("me", MODULE_NAME, (SIGNAL_FUNC) command_me);
1013   command_bind_silc("action", MODULE_NAME, (SIGNAL_FUNC) command_action);
1014   command_bind_silc("notice", MODULE_NAME, (SIGNAL_FUNC) command_notice);
1015   command_bind_silc("away", MODULE_NAME, (SIGNAL_FUNC) command_away);
1016   command_bind_silc("key", MODULE_NAME, (SIGNAL_FUNC) command_key);
1017   command_bind_silc("listkeys", MODULE_NAME, (SIGNAL_FUNC) command_listkeys);
1018
1019   silc_nicklist_init();
1020 }
1021
1022 void silc_channels_deinit(void)
1023 {
1024   signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1025   signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
1026   signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit);
1027   signal_remove("gui exit", (SIGNAL_FUNC) sig_gui_quit);
1028
1029   command_unbind("part", (SIGNAL_FUNC) command_part);
1030   command_unbind("me", (SIGNAL_FUNC) command_me);
1031   command_unbind("action", (SIGNAL_FUNC) command_action);
1032   command_unbind("notice", (SIGNAL_FUNC) command_notice);
1033   command_unbind("away", (SIGNAL_FUNC) command_away);
1034   command_unbind("key", (SIGNAL_FUNC) command_key);
1035   command_unbind("listkeys", (SIGNAL_FUNC) command_listkeys);
1036
1037   silc_nicklist_deinit();
1038 }