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