Merged with Irssi CVS.
[silc.git] / apps / irssi / src / silc / core / silc-channels.c
1 /*
2   silc-channels.c : irssi
3
4   Copyright (C) 2000 - 2001 Timo Sirainen
5                             Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2 of the License, or
10   (at your option) any later version.
11   
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16   
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20 */
21
22 #include "module.h"
23
24 #include "net-nonblock.h"
25 #include "net-sendbuffer.h"
26 #include "signals.h"
27 #include "servers.h"
28 #include "commands.h"
29 #include "levels.h"
30 #include "modules.h"
31 #include "rawlog.h"
32 #include "misc.h"
33 #include "settings.h"
34
35 #include "channels-setup.h"
36
37 #include "silc-servers.h"
38 #include "silc-channels.h"
39 #include "silc-queries.h"
40 #include "silc-nicklist.h"
41 #include "window-item-def.h"
42
43 #include "fe-common/core/printtext.h"
44 #include "fe-common/silc/module-formats.h"
45
46 #include "silc-commands.h"
47
48 SILC_CHANNEL_REC *silc_channel_create(SILC_SERVER_REC *server,
49                                       const char *name,
50                                       const char *visible_name,
51                                       int automatic)
52 {
53   SILC_CHANNEL_REC *rec;
54
55   g_return_val_if_fail(server == NULL || IS_SILC_SERVER(server), NULL);
56   g_return_val_if_fail(name != NULL, NULL);
57
58   rec = g_new0(SILC_CHANNEL_REC, 1);
59   rec->chat_type = SILC_PROTOCOL;
60   channel_init((CHANNEL_REC *)rec, (SERVER_REC *)server, name, name,
61                automatic);
62   return rec;
63 }
64
65 static void sig_channel_destroyed(SILC_CHANNEL_REC *channel)
66 {
67   if (!IS_SILC_CHANNEL(channel))
68     return;
69   if (channel->server && channel->server->disconnected)
70     return;
71
72   if (channel->server != NULL && !channel->left && !channel->kicked) {
73     /* destroying channel record without actually
74        having left the channel yet */
75     silc_command_exec(channel->server, "LEAVE", channel->name);
76   }
77 }
78
79 static void silc_channels_join(SILC_SERVER_REC *server,
80                                const char *channels, int automatic)
81 {
82   char **list, **tmp;
83   SILC_CHANNEL_REC *chanrec;
84
85   list = g_strsplit(channels, ",", -1);
86   for (tmp = list; *tmp != NULL; tmp++) {
87     chanrec = silc_channel_find(server, *tmp);
88     if (chanrec)
89       continue;
90
91     silc_command_exec(server, "JOIN", *tmp);
92   }
93
94   g_strfreev(list);
95 }
96
97 static void sig_connected(SILC_SERVER_REC *server)
98 {
99   if (IS_SILC_SERVER(server))
100     server->channels_join = (void *) silc_channels_join;
101 }
102
103 /* "server quit" signal from the core to indicate that QUIT command
104    was called. */
105
106 static void sig_server_quit(SILC_SERVER_REC *server, const char *msg)
107 {
108   if (IS_SILC_SERVER(server) && server->conn && server->conn->sock)
109     silc_command_exec(server, "QUIT", msg);
110 }
111
112 /* Find Irssi channel entry by SILC channel entry */
113
114 SILC_CHANNEL_REC *silc_channel_find_entry(SILC_SERVER_REC *server,
115                                           SilcChannelEntry entry)
116 {
117   GSList *tmp;
118
119   g_return_val_if_fail(IS_SILC_SERVER(server), NULL);
120
121   for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
122     SILC_CHANNEL_REC *rec = tmp->data;
123
124     if (rec->entry == entry)
125       return rec;
126   }
127
128   return NULL;
129 }
130
131 /* PART (LEAVE) command. */
132
133 static void command_part(const char *data, SILC_SERVER_REC *server,
134                          WI_ITEM_REC *item)
135 {
136   SILC_CHANNEL_REC *chanrec;
137   char userhost[256];
138   
139   CMD_SILC_SERVER(server);
140
141   if (!IS_SILC_SERVER(server) || !server->connected)
142     cmd_return_error(CMDERR_NOT_CONNECTED);
143
144   if (!strcmp(data, "*") || *data == '\0') {
145     if (!IS_SILC_CHANNEL(item))
146       cmd_return_error(CMDERR_NOT_JOINED);
147     data = item->visible_name;
148   }
149
150   chanrec = silc_channel_find(server, data);
151   if (chanrec == NULL) 
152     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
153
154   memset(userhost, 0, sizeof(userhost));
155   snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
156            server->conn->local_entry->username, 
157            server->conn->local_entry->hostname);
158   signal_emit("message part", 5, server, chanrec->name,
159               server->nick, userhost, "");
160   
161   chanrec->left = TRUE;
162   silc_command_exec(server, "LEAVE", chanrec->name);
163   signal_stop();
164
165   channel_destroy(CHANNEL(chanrec));
166 }
167
168 /* ME local command. */
169
170 static void command_me(const char *data, SILC_SERVER_REC *server,
171                        WI_ITEM_REC *item)
172 {
173   SILC_CHANNEL_REC *chanrec;
174   char *tmpcmd = "ME", *tmp;
175   SilcUInt32 argc = 0;
176   unsigned char *message = NULL;
177   unsigned char **argv;
178   SilcUInt32 *argv_lens, *argv_types;
179   int i;
180  
181   CMD_SILC_SERVER(server);
182
183   if (!IS_SILC_SERVER(server) || !server->connected)
184     cmd_return_error(CMDERR_NOT_CONNECTED);
185
186   if (!IS_SILC_CHANNEL(item))
187     cmd_return_error(CMDERR_NOT_JOINED);
188
189   /* Now parse all arguments */
190   tmp = g_strconcat(tmpcmd, " ", data, NULL);
191   silc_parse_command_line(tmp, &argv, &argv_lens,
192                           &argv_types, &argc, 2);
193   g_free(tmp);
194
195   if (argc < 2)
196     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
197
198   chanrec = silc_channel_find(server, item->visible_name);
199   if (chanrec == NULL) 
200     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
201
202   if (!silc_term_utf8()) {
203     int len = silc_utf8_encoded_len(argv[1], argv_lens[1],
204                                     SILC_STRING_LANGUAGE);
205     message = silc_calloc(len + 1, sizeof(*message));
206     g_return_if_fail(message != NULL);
207     silc_utf8_encode(argv[1], argv_lens[1], SILC_STRING_LANGUAGE,
208                      message, len);
209   }
210
211   /* Send the action message */
212   silc_client_send_channel_message(silc_client, server->conn, 
213                                    chanrec->entry, NULL,
214                                    SILC_MESSAGE_FLAG_ACTION |
215                                    SILC_MESSAGE_FLAG_UTF8,
216                                    message ? message : argv[1],
217                                    message ? strlen(message) : argv_lens[1],
218                                    TRUE);
219
220   printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
221                      MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_OWNACTION, 
222                      server->conn->local_entry->nickname, argv[1]);
223
224   for (i = 0; i < argc; i++)
225     silc_free(argv[i]);
226   silc_free(argv_lens);
227   silc_free(argv_types);
228   silc_free(message);
229 }
230
231 /* ACTION local command. Same as ME but takes the channel as mandatory
232    argument. */
233
234 static void command_action(const char *data, SILC_SERVER_REC *server,
235                            WI_ITEM_REC *item)
236 {
237   SILC_CHANNEL_REC *chanrec;
238   char *tmpcmd = "ME", *tmp;
239   SilcUInt32 argc = 0;
240   unsigned char *message = NULL;
241   unsigned char **argv;
242   SilcUInt32 *argv_lens, *argv_types;
243   int i;
244  
245   CMD_SILC_SERVER(server);
246   if (!IS_SILC_SERVER(server) || !server->connected)
247     cmd_return_error(CMDERR_NOT_CONNECTED);
248
249   if (!IS_SILC_CHANNEL(item))
250     cmd_return_error(CMDERR_NOT_JOINED);
251
252   /* Now parse all arguments */
253   tmp = g_strconcat(tmpcmd, " ", data, NULL);
254   silc_parse_command_line(tmp, &argv, &argv_lens,
255                           &argv_types, &argc, 3);
256   g_free(tmp);
257
258   if (argc < 3)
259     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
260
261   chanrec = silc_channel_find(server, argv[1]);
262   if (chanrec == NULL) 
263     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
264
265   if (!silc_term_utf8()) {
266     int len = silc_utf8_encoded_len(argv[2], argv_lens[2],
267                                     SILC_STRING_LANGUAGE);
268     message = silc_calloc(len + 1, sizeof(*message));
269     g_return_if_fail(message != NULL);
270     silc_utf8_encode(argv[2], argv_lens[2], SILC_STRING_LANGUAGE,
271                      message, len);
272   }
273
274   /* Send the action message */
275   silc_client_send_channel_message(silc_client, server->conn, 
276                                    chanrec->entry, NULL,
277                                    SILC_MESSAGE_FLAG_ACTION |
278                                    SILC_MESSAGE_FLAG_UTF8,
279                                    message ? message : argv[2],
280                                    message ? strlen(message) : argv_lens[2],
281                                    TRUE);
282
283   printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
284                      MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_OWNACTION, 
285                      server->conn->local_entry->nickname, argv[2]);
286
287   for (i = 0; i < argc; i++)
288     silc_free(argv[i]);
289   silc_free(argv_lens);
290   silc_free(argv_types);
291   silc_free(message);
292 }
293
294 /* NOTICE local command. */
295
296 static void command_notice(const char *data, SILC_SERVER_REC *server,
297                            WI_ITEM_REC *item)
298 {
299   SILC_CHANNEL_REC *chanrec;
300   char *tmpcmd = "ME", *tmp;
301   SilcUInt32 argc = 0;
302   unsigned char *message = NULL;
303   unsigned char **argv;
304   SilcUInt32 *argv_lens, *argv_types;
305   int i;
306  
307   CMD_SILC_SERVER(server);
308   if (!IS_SILC_SERVER(server) || !server->connected)
309     cmd_return_error(CMDERR_NOT_CONNECTED);
310
311   if (!IS_SILC_CHANNEL(item))
312     cmd_return_error(CMDERR_NOT_JOINED);
313
314   /* Now parse all arguments */
315   tmp = g_strconcat(tmpcmd, " ", data, NULL);
316   silc_parse_command_line(tmp, &argv, &argv_lens,
317                           &argv_types, &argc, 2);
318   g_free(tmp);
319
320   if (argc < 2)
321     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
322
323   chanrec = silc_channel_find(server, item->visible_name);
324   if (chanrec == NULL) 
325     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
326
327   if (!silc_term_utf8()) {
328     int len = silc_utf8_encoded_len(argv[1], argv_lens[1],
329                                     SILC_STRING_LANGUAGE);
330     message = silc_calloc(len + 1, sizeof(*message));
331     g_return_if_fail(message != NULL);
332     silc_utf8_encode(argv[1], argv_lens[1], SILC_STRING_LANGUAGE,
333                      message, len);
334   }
335
336   /* Send the action message */
337   silc_client_send_channel_message(silc_client, server->conn, 
338                                    chanrec->entry, NULL,
339                                    SILC_MESSAGE_FLAG_NOTICE |
340                                    SILC_MESSAGE_FLAG_UTF8,
341                                    message ? message : argv[1],
342                                    message ? strlen(message) : argv_lens[1],
343                                    TRUE);
344
345   printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
346                      MSGLEVEL_NOTICES, SILCTXT_CHANNEL_OWNNOTICE, 
347                      server->conn->local_entry->nickname, argv[1]);
348
349   for (i = 0; i < argc; i++)
350     silc_free(argv[i]);
351   silc_free(argv_lens);
352   silc_free(argv_types);
353   silc_free(message);
354 }
355
356 /* AWAY local command.  Sends UMODE command that sets the SILC_UMODE_GONE
357    flag. */
358
359 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         /* XXX for now we don't do this.  This feature is pretty stupid
636            and should perhaps be removed altogether from SILC.
637         silc_client_send_private_message_key(silc_client, conn, 
638                                              client_entry, TRUE);
639         */
640       } else if (type == 2) {
641         /* Set private channel key */
642         char *cipher = NULL, *hmac = NULL;
643
644         if (!(channel_entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
645           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
646                              SILCTXT_CH_PRIVATE_KEY_NOMODE, 
647                              channel_entry->channel_name);
648           goto out;
649         }
650
651         if (argc >= 6)
652           cipher = argv[5];
653         if (argc >= 7)
654           hmac = argv[6];
655
656         if (!silc_client_add_channel_private_key(silc_client, conn, 
657                                                  channel_entry, NULL,
658                                                  cipher, hmac,
659                                                  argv[4],
660                                                  argv_lens[4])) {
661           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
662                              SILCTXT_CH_PRIVATE_KEY_ERROR, 
663                              channel_entry->channel_name);
664           goto out;
665         }
666
667         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
668                            SILCTXT_CH_PRIVATE_KEY_ADD, 
669                            channel_entry->channel_name);
670       }
671     }
672
673     goto out;
674   }
675   
676   /* Unset command */
677   if (!strcasecmp(argv[3], "unset")) {
678     command = 2;
679
680     if (type == 1 && client_entry) {
681       /* Unset private message key */
682       silc_client_del_private_message_key(silc_client, conn, client_entry);
683     } else if (type == 2) {
684       /* Unset channel key(s) */
685       SilcChannelPrivateKey *keys;
686       SilcUInt32 keys_count;
687       int number;
688
689       if (argc == 4)
690         silc_client_del_channel_private_keys(silc_client, conn, 
691                                              channel_entry);
692
693       if (argc > 4) {
694         number = atoi(argv[4]);
695         keys = silc_client_list_channel_private_keys(silc_client, conn, 
696                                                      channel_entry,
697                                                      &keys_count);
698         if (!keys)
699           goto out;
700
701         if (!number || number > keys_count) {
702           silc_client_free_channel_private_keys(keys, keys_count);
703           goto out;
704         }
705
706         silc_client_del_channel_private_key(silc_client, conn, channel_entry,
707                                             keys[number - 1]);
708         silc_client_free_channel_private_keys(keys, keys_count);
709       }
710
711       goto out;
712     }
713   }
714
715   /* List command */
716   if (!strcasecmp(argv[3], "list")) {
717     command = 3;
718
719     if (type == 1) {
720       SilcPrivateMessageKeys keys;
721       SilcUInt32 keys_count;
722       int k, i, len;
723       char buf[1024];
724
725       keys = silc_client_list_private_message_keys(silc_client, conn, 
726                                                    &keys_count);
727       if (!keys)
728         goto out;
729
730       /* list the private message key(s) */
731       if (nickname[0] == '*') {
732         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
733                            SILCTXT_PRIVATE_KEY_LIST);
734         for (k = 0; k < keys_count; k++) {
735           memset(buf, 0, sizeof(buf));
736           strncat(buf, "  ", 2);
737           len = strlen(keys[k].client_entry->nickname);
738           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
739           if (len < 30)
740             for (i = 0; i < 30 - len; i++)
741               strcat(buf, " ");
742           strcat(buf, " ");
743           
744           len = strlen(keys[k].cipher);
745           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
746           if (len < 14)
747             for (i = 0; i < 14 - len; i++)
748               strcat(buf, " ");
749           strcat(buf, " ");
750
751           if (keys[k].key)
752             strcat(buf, "<hidden>");
753           else
754             strcat(buf, "*generated*");
755
756           silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
757         }
758       } else {
759         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
760                            SILCTXT_PRIVATE_KEY_LIST_NICK,
761                            client_entry->nickname);
762         for (k = 0; k < keys_count; k++) {
763           if (keys[k].client_entry != client_entry)
764             continue;
765
766           memset(buf, 0, sizeof(buf));
767           strncat(buf, "  ", 2);
768           len = strlen(keys[k].client_entry->nickname);
769           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
770           if (len < 30)
771             for (i = 0; i < 30 - len; i++)
772               strcat(buf, " ");
773           strcat(buf, " ");
774           
775           len = strlen(keys[k].cipher);
776           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
777           if (len < 14)
778             for (i = 0; i < 14 - len; i++)
779               strcat(buf, " ");
780           strcat(buf, " ");
781
782           if (keys[k].key)
783             strcat(buf, "<hidden>");
784           else
785             strcat(buf, "*generated*");
786
787           silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
788         }
789       }
790
791       silc_client_free_private_message_keys(keys, keys_count);
792
793     } else if (type == 2) {
794       SilcChannelPrivateKey *keys;
795       SilcUInt32 keys_count;
796       int k, i, len;
797       char buf[1024];
798
799       keys = silc_client_list_channel_private_keys(silc_client, conn, 
800                                                    channel_entry,
801                                                    &keys_count);
802
803       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
804                          SILCTXT_CH_PRIVATE_KEY_LIST,
805                          channel_entry->channel_name);
806
807       if (!keys)
808         goto out;
809       
810       for (k = 0; k < keys_count; k++) {
811         memset(buf, 0, sizeof(buf));
812         strncat(buf, "  ", 2);
813
814         len = strlen(keys[k]->cipher->cipher->name);
815         strncat(buf, keys[k]->cipher->cipher->name, len > 16 ? 16 : len);
816         if (len < 16)
817           for (i = 0; i < 16 - len; i++)
818             strcat(buf, " ");
819         strcat(buf, " ");
820         
821         len = strlen(silc_hmac_get_name(keys[k]->hmac));
822         strncat(buf, silc_hmac_get_name(keys[k]->hmac), len > 16 ? 16 : len);
823         if (len < 16)
824           for (i = 0; i < 16 - len; i++)
825             strcat(buf, " ");
826         strcat(buf, " ");
827         
828         strcat(buf, "<hidden>");
829
830         silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
831       }
832       
833       silc_client_free_channel_private_keys(keys, keys_count);
834     }
835
836     goto out;
837   }
838
839   /* Send command is used to send key agreement */
840   if (!strcasecmp(argv[3], "agreement")) {
841     command = 4;
842
843     if (argc >= 5)
844       hostname = argv[4];
845     if (argc >= 6)
846       port = atoi(argv[5]);
847
848     internal = silc_calloc(1, sizeof(*internal));
849     internal->type = type;
850     internal->server = server;
851     
852     if (!hostname) {
853       if (settings_get_bool("use_auto_addr")) {
854        
855         hostname = (char *)settings_get_str("auto_public_ip");
856
857         /* If the hostname isn't set, treat this case as if auto_public_ip 
858            wasn't set. */
859         if ((hostname) && (*hostname == '\0')) {
860            hostname = NULL;
861         } else {
862           bindhost = (char *)settings_get_str("auto_bind_ip");
863             
864           /* if the bind_ip isn't set, but the public_ip IS, then assume then
865              public_ip is the same value as the bind_ip. */
866           if ((bindhost) && (*bindhost == '\0'))
867             bindhost = hostname;
868           port = settings_get_int("auto_bind_port");
869         }
870       }  /* if use_auto_addr */
871     }
872   }
873
874   /* Start command is used to start key agreement (after receiving the
875      key_agreement client operation). */
876   if (!strcasecmp(argv[3], "negotiate")) {
877     command = 5;
878
879     if (argc >= 5)
880       hostname = argv[4];
881     if (argc >= 6)
882       port = atoi(argv[5]);
883
884     internal = silc_calloc(1, sizeof(*internal));
885     internal->type = type;
886     internal->server = server;
887   }
888
889   /* Change current channel private key */
890   if (!strcasecmp(argv[3], "change")) {
891     command = 6;
892     if (type == 2) {
893       /* Unset channel key(s) */
894       SilcChannelPrivateKey *keys;
895       SilcUInt32 keys_count;
896       int number;
897
898       keys = silc_client_list_channel_private_keys(silc_client, conn, 
899                                                    channel_entry,
900                                                    &keys_count);
901       if (!keys)
902         goto out;
903
904       if (argc == 4) {
905         chanrec->cur_key++;
906         if (chanrec->cur_key >= keys_count)
907           chanrec->cur_key = 0;
908       }
909
910       if (argc > 4) {
911         number = atoi(argv[4]);
912         if (!number || number > keys_count)
913           chanrec->cur_key = 0;
914         else
915           chanrec->cur_key = number - 1;
916       }
917
918       /* Set the current channel private key */
919       silc_client_current_channel_private_key(silc_client, conn, 
920                                               channel_entry, 
921                                               keys[chanrec->cur_key]);
922       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
923                          SILCTXT_CH_PRIVATE_KEY_CHANGE, chanrec->cur_key + 1,
924                          channel_entry->channel_name);
925
926       silc_client_free_channel_private_keys(keys, keys_count);
927       goto out;
928     }
929   }
930
931   if (command == 0) {
932     silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO,
933              "Usage: /KEY msg|channel <nickname|channel> "
934              "set|unset|agreement|negotiate [<arguments>]");
935     goto out;
936   }
937
938   if (command == 4 && client_entry) {
939     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
940                        SILCTXT_KEY_AGREEMENT, argv[2]);
941     internal->responder = TRUE;
942     silc_client_send_key_agreement(
943                            silc_client, conn, client_entry, hostname, 
944                            bindhost, port, 
945                            settings_get_int("key_exchange_timeout_secs"), 
946                            keyagr_completion, internal);
947     if (!hostname)
948       silc_free(internal);
949     goto out;
950   }
951
952   if (command == 5 && client_entry && hostname) {
953     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
954                        SILCTXT_KEY_AGREEMENT_NEGOTIATE, argv[2]);
955     internal->responder = FALSE;
956     silc_client_perform_key_agreement(silc_client, conn, client_entry, 
957                                       hostname, port, keyagr_completion, 
958                                       internal);
959     goto out;
960   }
961
962  out:
963   silc_free(nickname);
964 }
965
966 /* Lists locally saved client and server public keys. */
967
968 static void command_listkeys(const char *data, SILC_SERVER_REC *server,
969                              WI_ITEM_REC *item)
970 {
971
972 }
973
974 void silc_channels_init(void)
975 {
976   signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
977   signal_add("server connected", (SIGNAL_FUNC) sig_connected);
978   signal_add("server quit", (SIGNAL_FUNC) sig_server_quit);
979
980   command_bind_silc("part", MODULE_NAME, (SIGNAL_FUNC) command_part);
981   command_bind_silc("me", MODULE_NAME, (SIGNAL_FUNC) command_me);
982   command_bind_silc("action", MODULE_NAME, (SIGNAL_FUNC) command_action);
983   command_bind_silc("notice", MODULE_NAME, (SIGNAL_FUNC) command_notice);
984   command_bind_silc("away", MODULE_NAME, (SIGNAL_FUNC) command_away);
985   command_bind_silc("key", MODULE_NAME, (SIGNAL_FUNC) command_key);
986   command_bind_silc("listkeys", MODULE_NAME, (SIGNAL_FUNC) command_listkeys);
987
988   silc_nicklist_init();
989 }
990
991 void silc_channels_deinit(void)
992 {
993   signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
994   signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
995   signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit);
996
997   command_unbind("part", (SIGNAL_FUNC) command_part);
998   command_unbind("me", (SIGNAL_FUNC) command_me);
999   command_unbind("action", (SIGNAL_FUNC) command_action);
1000   command_unbind("notice", (SIGNAL_FUNC) command_notice);
1001   command_unbind("away", (SIGNAL_FUNC) command_away);
1002   command_unbind("key", (SIGNAL_FUNC) command_key);
1003   command_unbind("listkeys", (SIGNAL_FUNC) command_listkeys);
1004
1005   silc_nicklist_deinit();
1006 }