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