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