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