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