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