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