Merges from Irssi CVS.
[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   default:
410     break;
411   } 
412
413   if (i)
414     silc_free(i);
415 }
416
417 /* Local command KEY. This command is used to set and unset private
418    keys for channels, set and unset private keys for private messages
419    with remote clients and to send key agreement requests and
420    negotiate the key agreement protocol with remote client.  The
421    key agreement is supported only to negotiate private message keys,
422    it currently cannot be used to negotiate private keys for channels,
423    as it is not convenient for that purpose. */
424
425 typedef struct {
426   SILC_SERVER_REC *server;
427   char *data;
428   char *nick;
429   WI_ITEM_REC *item;
430 } *KeyGetClients;
431
432 /* Callback to be called after client information is resolved from the
433    server. */
434
435 static void silc_client_command_key_get_clients(SilcClient client,
436                                                 SilcClientConnection conn,
437                                                 SilcClientEntry *clients,
438                                                 SilcUInt32 clients_count,
439                                                 void *context)
440 {
441   KeyGetClients internal = (KeyGetClients)context;
442
443   if (!clients) {
444     printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Unknown nick: %s", 
445               internal->nick);
446     silc_free(internal->data);
447     silc_free(internal->nick);
448     silc_free(internal);
449     return;
450   }
451
452   signal_emit("command key", 3, internal->data, internal->server,
453               internal->item);
454
455   silc_free(internal->data);
456   silc_free(internal->nick);
457   silc_free(internal);
458 }
459
460 static void command_key(const char *data, SILC_SERVER_REC *server,
461                         WI_ITEM_REC *item)
462 {
463   SilcClientConnection conn;
464   SilcClientEntry *entrys, client_entry = NULL;
465   SilcUInt32 entry_count;
466   SILC_CHANNEL_REC *chanrec = NULL;
467   SilcChannelEntry channel_entry = NULL;
468   char *nickname = NULL, *tmp;
469   int command = 0, port = 0, type = 0;
470   char *hostname = NULL;
471   KeyInternal internal = NULL;
472   SilcUInt32 argc = 0;
473   unsigned char **argv;
474   SilcUInt32 *argv_lens, *argv_types;
475   char *bindhost = NULL;
476  
477   CMD_SILC_SERVER(server);
478
479   if (!server || !IS_SILC_SERVER(server) || !server->connected)
480     cmd_return_error(CMDERR_NOT_CONNECTED);
481
482   conn = server->conn;
483
484   /* Now parse all arguments */
485   tmp = g_strconcat("KEY", " ", data, NULL);
486   silc_parse_command_line(tmp, &argv, &argv_lens, &argv_types, &argc, 7);
487   g_free(tmp);
488
489   if (argc < 4)
490     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
491
492   /* Get type */
493   if (!strcasecmp(argv[1], "msg"))
494     type = 1;
495   if (!strcasecmp(argv[1], "channel"))
496     type = 2;
497
498   if (type == 0)
499     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
500
501   if (type == 1) {
502     if (argv[2][0] == '*') {
503       nickname = strdup("*");
504     } else {
505       /* Parse the typed nickname. */
506       if (!silc_parse_userfqdn(argv[2], &nickname, NULL)) {
507         printformat_module("fe-common/silc", server, NULL,
508                            MSGLEVEL_CRAP, SILCTXT_BAD_NICK, argv[2]);
509         return;
510       }
511       
512       /* Find client entry */
513       entrys = silc_client_get_clients_local(silc_client, conn, nickname,
514                                              argv[2], &entry_count);
515       if (!entrys) {
516         KeyGetClients inter = silc_calloc(1, sizeof(*inter));
517         inter->server = server;
518         inter->data = strdup(data);
519         inter->nick = strdup(nickname);
520         inter->item = item;
521         silc_client_get_clients(silc_client, conn, nickname, argv[2],
522                                 silc_client_command_key_get_clients, inter);
523         goto out;
524       }
525       client_entry = entrys[0];
526       silc_free(entrys);
527     }
528   }
529
530   if (type == 2) {
531     /* Get channel entry */
532     char *name;
533
534     if (argv[2][0] == '*') {
535       if (!conn->current_channel) {
536         silc_free(nickname);
537         cmd_return_error(CMDERR_NOT_JOINED);
538       }
539       name = conn->current_channel->channel_name;
540     } else {
541       name = argv[2];
542     }
543
544     chanrec = silc_channel_find(server, name);
545     if (chanrec == NULL) {
546       silc_free(nickname);
547       cmd_return_error(CMDERR_CHAN_NOT_FOUND);
548     }
549     channel_entry = chanrec->entry;
550   }
551
552   /* Set command */
553   if (!strcasecmp(argv[3], "set")) {
554     command = 1;
555
556     if (argc >= 5) {
557       if (type == 1 && client_entry) {
558         /* Set private message key */
559         
560         silc_client_del_private_message_key(silc_client, conn, client_entry);
561
562         if (argc >= 6)
563           silc_client_add_private_message_key(silc_client, conn, client_entry,
564                                               argv[5], argv[4],
565                                               argv_lens[4],
566                                               (argv[4][0] == '*' ?
567                                                TRUE : FALSE), FALSE);
568         else
569           silc_client_add_private_message_key(silc_client, conn, client_entry,
570                                               NULL, argv[4],
571                                               argv_lens[4],
572                                               (argv[4][0] == '*' ?
573                                                TRUE : FALSE), FALSE);
574
575         /* Send the key to the remote client so that it starts using it
576            too. */
577         silc_client_send_private_message_key(silc_client, conn, 
578                                              client_entry, TRUE);
579       } else if (type == 2) {
580         /* Set private channel key */
581         char *cipher = NULL, *hmac = NULL;
582
583         if (!(channel_entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
584           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
585                              SILCTXT_CH_PRIVATE_KEY_NOMODE, 
586                              channel_entry->channel_name);
587           goto out;
588         }
589
590         if (argc >= 6)
591           cipher = argv[5];
592         if (argc >= 7)
593           hmac = argv[6];
594
595         if (!silc_client_add_channel_private_key(silc_client, conn, 
596                                                  channel_entry, NULL,
597                                                  cipher, hmac,
598                                                  argv[4],
599                                                  argv_lens[4])) {
600           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
601                              SILCTXT_CH_PRIVATE_KEY_ERROR, 
602                              channel_entry->channel_name);
603           goto out;
604         }
605
606         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
607                            SILCTXT_CH_PRIVATE_KEY_ADD, 
608                            channel_entry->channel_name);
609       }
610     }
611
612     goto out;
613   }
614   
615   /* Unset command */
616   if (!strcasecmp(argv[3], "unset")) {
617     command = 2;
618
619     if (type == 1 && client_entry) {
620       /* Unset private message key */
621       silc_client_del_private_message_key(silc_client, conn, client_entry);
622     } else if (type == 2) {
623       /* Unset channel key(s) */
624       SilcChannelPrivateKey *keys;
625       SilcUInt32 keys_count;
626       int number;
627
628       if (argc == 4)
629         silc_client_del_channel_private_keys(silc_client, conn, 
630                                              channel_entry);
631
632       if (argc > 4) {
633         number = atoi(argv[4]);
634         keys = silc_client_list_channel_private_keys(silc_client, conn, 
635                                                      channel_entry,
636                                                      &keys_count);
637         if (!keys)
638           goto out;
639
640         if (!number || number > keys_count) {
641           silc_client_free_channel_private_keys(keys, keys_count);
642           goto out;
643         }
644
645         silc_client_del_channel_private_key(silc_client, conn, channel_entry,
646                                             keys[number - 1]);
647         silc_client_free_channel_private_keys(keys, keys_count);
648       }
649
650       goto out;
651     }
652   }
653
654   /* List command */
655   if (!strcasecmp(argv[3], "list")) {
656     command = 3;
657
658     if (type == 1) {
659       SilcPrivateMessageKeys keys;
660       SilcUInt32 keys_count;
661       int k, i, len;
662       char buf[1024];
663
664       keys = silc_client_list_private_message_keys(silc_client, conn, 
665                                                    &keys_count);
666       if (!keys)
667         goto out;
668
669       /* list the private message key(s) */
670       if (nickname[0] == '*') {
671         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
672                            SILCTXT_PRIVATE_KEY_LIST);
673         for (k = 0; k < keys_count; k++) {
674           memset(buf, 0, sizeof(buf));
675           strncat(buf, "  ", 2);
676           len = strlen(keys[k].client_entry->nickname);
677           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
678           if (len < 30)
679             for (i = 0; i < 30 - len; i++)
680               strcat(buf, " ");
681           strcat(buf, " ");
682           
683           len = strlen(keys[k].cipher);
684           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
685           if (len < 14)
686             for (i = 0; i < 14 - len; i++)
687               strcat(buf, " ");
688           strcat(buf, " ");
689
690           if (keys[k].key)
691             strcat(buf, "<hidden>");
692           else
693             strcat(buf, "*generated*");
694
695           silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
696         }
697       } else {
698         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
699                            SILCTXT_PRIVATE_KEY_LIST_NICK,
700                            client_entry->nickname);
701         for (k = 0; k < keys_count; k++) {
702           if (keys[k].client_entry != client_entry)
703             continue;
704
705           memset(buf, 0, sizeof(buf));
706           strncat(buf, "  ", 2);
707           len = strlen(keys[k].client_entry->nickname);
708           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
709           if (len < 30)
710             for (i = 0; i < 30 - len; i++)
711               strcat(buf, " ");
712           strcat(buf, " ");
713           
714           len = strlen(keys[k].cipher);
715           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
716           if (len < 14)
717             for (i = 0; i < 14 - len; i++)
718               strcat(buf, " ");
719           strcat(buf, " ");
720
721           if (keys[k].key)
722             strcat(buf, "<hidden>");
723           else
724             strcat(buf, "*generated*");
725
726           silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
727         }
728       }
729
730       silc_client_free_private_message_keys(keys, keys_count);
731
732     } else if (type == 2) {
733       SilcChannelPrivateKey *keys;
734       SilcUInt32 keys_count;
735       int k, i, len;
736       char buf[1024];
737
738       keys = silc_client_list_channel_private_keys(silc_client, conn, 
739                                                    channel_entry,
740                                                    &keys_count);
741
742       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
743                          SILCTXT_CH_PRIVATE_KEY_LIST,
744                          channel_entry->channel_name);
745
746       if (!keys)
747         goto out;
748       
749       for (k = 0; k < keys_count; k++) {
750         memset(buf, 0, sizeof(buf));
751         strncat(buf, "  ", 2);
752
753         len = strlen(keys[k]->cipher->cipher->name);
754         strncat(buf, keys[k]->cipher->cipher->name, len > 16 ? 16 : len);
755         if (len < 16)
756           for (i = 0; i < 16 - len; i++)
757             strcat(buf, " ");
758         strcat(buf, " ");
759         
760         len = strlen(silc_hmac_get_name(keys[k]->hmac));
761         strncat(buf, silc_hmac_get_name(keys[k]->hmac), len > 16 ? 16 : len);
762         if (len < 16)
763           for (i = 0; i < 16 - len; i++)
764             strcat(buf, " ");
765         strcat(buf, " ");
766         
767         strcat(buf, "<hidden>");
768
769         silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
770       }
771       
772       silc_client_free_channel_private_keys(keys, keys_count);
773     }
774
775     goto out;
776   }
777
778   /* Send command is used to send key agreement */
779   if (!strcasecmp(argv[3], "agreement")) {
780     command = 4;
781
782     if (argc >= 5)
783       hostname = argv[4];
784     if (argc >= 6)
785       port = atoi(argv[5]);
786
787     internal = silc_calloc(1, sizeof(*internal));
788     internal->type = type;
789     internal->server = server;
790     
791     if (!hostname) {
792       if (settings_get_bool("use_auto_addr")) {
793        
794         hostname = (char *)settings_get_str("auto_public_ip");
795
796         /* If the hostname isn't set, treat this case as if auto_public_ip 
797            wasn't set. */
798         if ((hostname) && (*hostname == '\0')) {
799            hostname = NULL;
800         } else {
801           bindhost = (char *)settings_get_str("auto_bind_ip");
802             
803           /* if the bind_ip isn't set, but the public_ip IS, then assume then
804              public_ip is the same value as the bind_ip. */
805           if ((bindhost) && (*bindhost == '\0'))
806             bindhost = hostname;
807           port = settings_get_int("auto_bind_port");
808         }
809       }  /* if use_auto_addr */
810     }
811   }
812
813   /* Start command is used to start key agreement (after receiving the
814      key_agreement client operation). */
815   if (!strcasecmp(argv[3], "negotiate")) {
816     command = 5;
817
818     if (argc >= 5)
819       hostname = argv[4];
820     if (argc >= 6)
821       port = atoi(argv[5]);
822
823     internal = silc_calloc(1, sizeof(*internal));
824     internal->type = type;
825     internal->server = server;
826   }
827
828   /* Change current channel private key */
829   if (!strcasecmp(argv[3], "change")) {
830     command = 6;
831     if (type == 2) {
832       /* Unset channel key(s) */
833       SilcChannelPrivateKey *keys;
834       SilcUInt32 keys_count;
835       int number;
836
837       keys = silc_client_list_channel_private_keys(silc_client, conn, 
838                                                    channel_entry,
839                                                    &keys_count);
840       if (!keys)
841         goto out;
842
843       if (argc == 4) {
844         chanrec->cur_key++;
845         if (chanrec->cur_key >= keys_count)
846           chanrec->cur_key = 0;
847       }
848
849       if (argc > 4) {
850         number = atoi(argv[4]);
851         if (!number || number > keys_count)
852           chanrec->cur_key = 0;
853         else
854           chanrec->cur_key = number - 1;
855       }
856
857       /* Set the current channel private key */
858       silc_client_current_channel_private_key(silc_client, conn, 
859                                               channel_entry, 
860                                               keys[chanrec->cur_key]);
861       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
862                          SILCTXT_CH_PRIVATE_KEY_CHANGE, chanrec->cur_key + 1,
863                          channel_entry->channel_name);
864
865       silc_client_free_channel_private_keys(keys, keys_count);
866       goto out;
867     }
868   }
869
870   if (command == 0) {
871     silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO,
872              "Usage: /KEY msg|channel <nickname|channel> "
873              "set|unset|agreement|negotiate [<arguments>]");
874     goto out;
875   }
876
877   if (command == 4 && client_entry) {
878     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
879                        SILCTXT_KEY_AGREEMENT, argv[2]);
880     internal->responder = TRUE;
881     silc_client_send_key_agreement(
882                            silc_client, conn, client_entry, hostname, 
883                            bindhost, port, 
884                            settings_get_int("key_exchange_timeout_secs"), 
885                            keyagr_completion, internal);
886     if (!hostname)
887       silc_free(internal);
888     goto out;
889   }
890
891   if (command == 5 && client_entry && hostname) {
892     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
893                        SILCTXT_KEY_AGREEMENT_NEGOTIATE, argv[2]);
894     internal->responder = FALSE;
895     silc_client_perform_key_agreement(silc_client, conn, client_entry, 
896                                       hostname, port, keyagr_completion, 
897                                       internal);
898     goto out;
899   }
900
901  out:
902   silc_free(nickname);
903 }
904
905 /* Lists locally saved client and server public keys. */
906
907 static void command_listkeys(const char *data, SILC_SERVER_REC *server,
908                              WI_ITEM_REC *item)
909 {
910
911 }
912
913 void silc_channels_init(void)
914 {
915   signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
916   signal_add("server connected", (SIGNAL_FUNC) sig_connected);
917   signal_add("server quit", (SIGNAL_FUNC) sig_server_quit);
918
919   command_bind_silc("part", MODULE_NAME, (SIGNAL_FUNC) command_part);
920   command_bind_silc("me", MODULE_NAME, (SIGNAL_FUNC) command_me);
921   command_bind_silc("action", MODULE_NAME, (SIGNAL_FUNC) command_action);
922   command_bind_silc("notice", MODULE_NAME, (SIGNAL_FUNC) command_notice);
923   command_bind_silc("away", MODULE_NAME, (SIGNAL_FUNC) command_away);
924   command_bind_silc("key", MODULE_NAME, (SIGNAL_FUNC) command_key);
925   command_bind_silc("listkeys", MODULE_NAME, (SIGNAL_FUNC) command_listkeys);
926
927   silc_nicklist_init();
928 }
929
930 void silc_channels_deinit(void)
931 {
932   signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
933   signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
934   signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit);
935
936   command_unbind("part", (SIGNAL_FUNC) command_part);
937   command_unbind("me", (SIGNAL_FUNC) command_me);
938   command_unbind("action", (SIGNAL_FUNC) command_action);
939   command_unbind("notice", (SIGNAL_FUNC) command_notice);
940   command_unbind("away", (SIGNAL_FUNC) command_away);
941   command_unbind("key", (SIGNAL_FUNC) command_key);
942   command_unbind("listkeys", (SIGNAL_FUNC) command_listkeys);
943
944   silc_nicklist_deinit();
945 }