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