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