updates.
[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 SILC_CHANNEL_REC *silc_channel_create(SILC_SERVER_REC *server,
47                                       const char *name, int automatic)
48 {
49   SILC_CHANNEL_REC *rec;
50
51   g_return_val_if_fail(server == NULL || IS_SILC_SERVER(server), NULL);
52   g_return_val_if_fail(name != NULL, NULL);
53
54   rec = g_new0(SILC_CHANNEL_REC, 1);
55   rec->chat_type = SILC_PROTOCOL;
56   rec->name = g_strdup(name);
57   rec->server = server;
58
59   channel_init((CHANNEL_REC *) rec, automatic);
60   return rec;
61 }
62
63 static void sig_channel_destroyed(SILC_CHANNEL_REC *channel)
64 {
65   if (!IS_SILC_CHANNEL(channel))
66     return;
67
68   if (channel->server != NULL && !channel->left && !channel->kicked) {
69     /* destroying channel record without actually
70        having left the channel yet */
71     silc_command_exec(channel->server, "PART", channel->name);
72   }
73 }
74
75 static void silc_channels_join(SILC_SERVER_REC *server,
76                                const char *channels, int automatic)
77 {
78   char **list, **tmp, *channel;
79
80   list = g_strsplit(channels, ",", -1);
81   for (tmp = list; *tmp != NULL; tmp++) {
82     channel = **tmp == '#' ? g_strdup(*tmp) :
83       g_strconcat("#", *tmp, NULL);
84     silc_channel_create(server, channel, FALSE);
85     silc_command_exec(server, "JOIN", channel);
86     g_free(channel);
87   }
88   g_strfreev(list);
89 }
90
91 static void sig_connected(SILC_SERVER_REC *server)
92 {
93   if (IS_SILC_SERVER(server))
94     server->channels_join = (void *) silc_channels_join;
95 }
96
97 /* "server quit" signal from the core to indicate that QUIT command
98    was called. */
99
100 static void sig_server_quit(SILC_SERVER_REC *server, const char *msg)
101 {
102   if (IS_SILC_SERVER(server))
103     silc_command_exec(server, "QUIT", msg);
104 }
105
106 /*
107  * "event join". Joined to a channel.
108  */
109
110 SILC_CHANNEL_REC *silc_channel_find_entry(SILC_SERVER_REC *server,
111                                           SilcChannelEntry entry)
112 {
113   GSList *tmp;
114
115   g_return_val_if_fail(IS_SILC_SERVER(server), NULL);
116
117   for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
118     SILC_CHANNEL_REC *rec = tmp->data;
119
120     if (rec->entry == entry)
121       return rec;
122   }
123
124   return NULL;
125 }
126
127 static void event_join(SILC_SERVER_REC *server, va_list va)
128 {
129   SILC_CHANNEL_REC *chanrec;
130   SILC_NICK_REC *nickrec;
131   SilcClientEntry client;
132   SilcChannelEntry channel;
133
134   client = va_arg(va, SilcClientEntry);
135   channel = va_arg(va, SilcChannelEntry);
136
137   if (client == server->conn->local_entry) {
138     /* You joined to channel */
139     chanrec = silc_channel_find(server, channel->channel_name);
140     if (chanrec != NULL && !chanrec->joined)
141       chanrec->entry = channel;
142   } else {
143     chanrec = silc_channel_find_entry(server, channel);
144     if (chanrec != NULL) {
145       SilcChannelUser user;
146
147       silc_list_start(chanrec->entry->clients);
148       while ((user = silc_list_get(chanrec->entry->clients)) != NULL)
149         if (user->client == client) {
150           nickrec = silc_nicklist_insert(chanrec, user, TRUE);
151           break;
152         }
153     }
154   }
155
156   signal_emit("message join", 4, server, channel->channel_name,
157               client->nickname,
158               client->username == NULL ? "" : client->username);
159 }
160
161 /*
162  * "event leave". Left a channel.
163  */
164
165 static void event_leave(SILC_SERVER_REC *server, va_list va)
166 {
167   SILC_CHANNEL_REC *chanrec;
168   SILC_NICK_REC *nickrec;
169   SilcClientEntry client;
170   SilcChannelEntry channel;
171
172   client = va_arg(va, SilcClientEntry);
173   channel = va_arg(va, SilcChannelEntry);
174
175   signal_emit("message part", 5, server, channel->channel_name,
176               client->nickname,  client->username ?  client->username : "", 
177               client->nickname);
178
179   chanrec = silc_channel_find_entry(server, channel);
180   if (chanrec != NULL) {
181     nickrec = silc_nicklist_find(chanrec, client);
182     if (nickrec != NULL)
183       nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
184   }
185 }
186
187 /*
188  * "event signoff". Left the network.
189  */
190
191 static void event_signoff(SILC_SERVER_REC *server, va_list va)
192 {
193   SilcClientEntry client;
194   GSList *nicks, *tmp;
195   char *message;
196
197   client = va_arg(va, SilcClientEntry);
198   message = va_arg(va, char *);
199
200   signal_emit("message quit", 4, server, client->nickname,
201               client->username ? client->username : "", 
202               message ? message : "");
203
204   nicks = nicklist_get_same_unique(SERVER(server), client);
205   for (tmp = nicks; tmp != NULL; tmp = tmp->next->next) {
206     CHANNEL_REC *channel = tmp->data;
207     NICK_REC *nickrec = tmp->next->data;
208     
209     nicklist_remove(channel, nickrec);
210   }
211 }
212
213 /*
214  * "event topic". Changed topic.
215  */
216
217 static void event_topic(SILC_SERVER_REC *server, va_list va)
218 {
219   SILC_CHANNEL_REC *chanrec;
220   SilcClientEntry client;
221   SilcChannelEntry channel;
222   char *topic;
223
224   client = va_arg(va, SilcClientEntry);
225   topic = va_arg(va, char *);
226   channel = va_arg(va, SilcChannelEntry);
227
228   chanrec = silc_channel_find_entry(server, channel);
229   if (chanrec != NULL) {
230     g_free_not_null(chanrec->topic);
231     chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
232     signal_emit("channel topic changed", 1, chanrec);
233   }
234
235   signal_emit("message topic", 5, server, channel->channel_name,
236               topic, client->nickname, client->username);
237 }
238
239 /*
240  * "event invite". Invited or modified invite list.
241  */
242
243 static void event_invite(SILC_SERVER_REC *server, va_list va)
244 {
245   SilcClientEntry client;
246   SilcChannelEntry channel;
247   
248   client = va_arg(va, SilcClientEntry);
249   channel = va_arg(va, SilcChannelEntry);
250
251   signal_emit("message invite", 4, server, channel->channel_name,
252               client->nickname, client->username);
253 }
254
255 /*
256  * "event nick". Changed nickname.
257  */
258
259 static void event_nick(SILC_SERVER_REC *server, va_list va)
260 {
261   SilcClientEntry oldclient, newclient;
262
263   oldclient = va_arg(va, SilcClientEntry);
264   newclient = va_arg(va, SilcClientEntry);
265
266   nicklist_rename_unique(SERVER(server),
267                          oldclient, oldclient->nickname,
268                          newclient, newclient->nickname);
269
270   signal_emit("message nick", 4, server, newclient->nickname, 
271               oldclient->nickname, newclient->username);
272 }
273
274 /*
275  * "event cmode". Changed channel mode.
276  */
277
278 static void event_cmode(SILC_SERVER_REC *server, va_list va)
279 {
280   SILC_CHANNEL_REC *chanrec;
281   SilcClientEntry client;
282   SilcChannelEntry channel;
283   char *mode;
284   uint32 modei;
285
286   client = va_arg(va, SilcClientEntry);
287   modei = va_arg(va, uint32);
288   (void)va_arg(va, char *);
289   (void)va_arg(va, char *);
290   channel = va_arg(va, SilcChannelEntry);
291
292   mode = silc_client_chmode(modei, channel);
293   
294   chanrec = silc_channel_find_entry(server, channel);
295   if (chanrec != NULL) {
296     g_free_not_null(chanrec->mode);
297     chanrec->mode = g_strdup(mode == NULL ? "" : mode);
298     signal_emit("channel mode changed", 1, chanrec);
299   }
300   
301   printformat_module("fe-common/silc", server, channel->channel_name,
302                      MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
303                      channel->channel_name, mode ? mode : "removed all",
304                      client->nickname);
305   
306   g_free(mode);
307 }
308
309 /*
310  * "event cumode". Changed user's mode on channel.
311  */
312
313 static void event_cumode(SILC_SERVER_REC *server, va_list va)
314 {
315   SILC_CHANNEL_REC *chanrec;
316   SilcClientEntry client, destclient;
317   SilcChannelEntry channel;
318   int mode;
319   char *modestr;
320   
321   client = va_arg(va, SilcClientEntry);
322   mode = va_arg(va, uint32);
323   destclient = va_arg(va, SilcClientEntry);
324   channel = va_arg(va, SilcChannelEntry);
325   
326   modestr = silc_client_chumode(mode);
327   chanrec = silc_channel_find_entry(server, channel);
328   if (chanrec != NULL) {
329     SILC_NICK_REC *nick;
330     
331     if (destclient == server->conn->local_entry) {
332       chanrec->chanop =
333         (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
334     }
335
336     nick = silc_nicklist_find(chanrec, destclient);
337     if (nick != NULL) {
338       nick->op = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
339       signal_emit("nick mode changed", 2, chanrec, nick);
340     }
341   }
342   
343   printformat_module("fe-common/silc", server, channel->channel_name,
344                      MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
345                      channel->channel_name, destclient->nickname, 
346                      modestr ? modestr : "removed all",
347                      client->nickname);
348
349   if (mode & SILC_CHANNEL_UMODE_CHANFO)
350     printformat_module("fe-common/silc", 
351                        server, channel->channel_name, MSGLEVEL_CRAP,
352                        SILCTXT_CHANNEL_FOUNDER,
353                        channel->channel_name, destclient->nickname);
354
355   g_free(modestr);
356 }
357
358 /*
359  * "event motd". Received MOTD.
360  */
361
362 static void event_motd(SILC_SERVER_REC *server, va_list va)
363 {
364   char *text = va_arg(va, char *);
365
366   if (!settings_get_bool("skip_motd"))
367     printtext_multiline(server, NULL, MSGLEVEL_CRAP, "%s", text);
368 }
369
370 /*
371  * "event channel_change". Channel ID has changed.
372  */
373
374 static void event_channel_change(SILC_SERVER_REC *server, va_list va)
375 {
376
377 }
378
379 /*
380  * "event server_signoff". Server has quit the network.
381  */
382
383 static void event_server_signoff(SILC_SERVER_REC *server, va_list va)
384 {
385
386 }
387
388 /*
389  * "event kick". Someone was kicked from channel.
390  */
391
392 static void event_kick(SILC_SERVER_REC *server, va_list va)
393 {
394
395 }
396
397 /*
398  * "event kill". Someone was killed from the network.
399  */
400
401 static void event_kill(SILC_SERVER_REC *server, va_list va)
402 {
403
404 }
405
406 /*
407  * "event ban". Someone was banned or ban list was modified.
408  */
409
410 static void event_ban(SILC_SERVER_REC *server, va_list va)
411 {
412
413 }
414
415 /* PART (LEAVE) command. */
416
417 static void command_part(const char *data, SILC_SERVER_REC *server,
418                          WI_ITEM_REC *item)
419 {
420   SILC_CHANNEL_REC *chanrec;
421   
422   if (!IS_SILC_SERVER(server) || !server->connected)
423     cmd_return_error(CMDERR_NOT_CONNECTED);
424
425   if (*data == '\0') {
426     if (!IS_SILC_CHANNEL(item))
427       cmd_return_error(CMDERR_NOT_JOINED);
428     data = item->name;
429   }
430
431   chanrec = silc_channel_find(server, data);
432   if (chanrec == NULL) 
433     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
434
435   signal_emit("message part", 5, server, chanrec->name,
436               server->nick, server->conn->local_entry->username, "");
437   
438   silc_command_exec(server, "LEAVE", chanrec->name);
439   signal_stop();
440   
441   channel_destroy(CHANNEL(chanrec));
442 }
443
444 /* ME local command. */
445
446 static void command_me(const char *data, SILC_SERVER_REC *server,
447                        WI_ITEM_REC *item)
448 {
449   SILC_CHANNEL_REC *chanrec;
450   char *tmpcmd = "ME", *tmp;
451   uint32 argc = 0;
452   unsigned char **argv;
453   uint32 *argv_lens, *argv_types;
454   int i;
455  
456   if (!IS_SILC_SERVER(server) || !server->connected)
457     cmd_return_error(CMDERR_NOT_CONNECTED);
458
459   if (!IS_SILC_CHANNEL(item))
460     cmd_return_error(CMDERR_NOT_JOINED);
461
462   /* Now parse all arguments */
463   tmp = g_strconcat(tmpcmd, " ", data, NULL);
464   silc_parse_command_line(tmp, &argv, &argv_lens,
465                           &argv_types, &argc, 2);
466   g_free(tmp);
467
468   if (argc < 2)
469     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
470
471   chanrec = silc_channel_find(server, item->name);
472   if (chanrec == NULL) 
473     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
474
475   /* Send the action message */
476   silc_client_send_channel_message(silc_client, server->conn, 
477                                    chanrec->entry, NULL,
478                                    SILC_MESSAGE_FLAG_ACTION, 
479                                    argv[1], argv_lens[1], TRUE);
480
481   printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
482                      MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_OWNACTION, argv[1]);
483
484   for (i = 0; i < argc; i++)
485     silc_free(argv[i]);
486   silc_free(argv_lens);
487   silc_free(argv_types);
488 }
489
490 /* NOTICE local command. */
491
492 static void command_notice(const char *data, SILC_SERVER_REC *server,
493                            WI_ITEM_REC *item)
494 {
495   SILC_CHANNEL_REC *chanrec;
496   char *tmpcmd = "ME", *tmp;
497   uint32 argc = 0;
498   unsigned char **argv;
499   uint32 *argv_lens, *argv_types;
500   int i;
501  
502   if (!IS_SILC_SERVER(server) || !server->connected)
503     cmd_return_error(CMDERR_NOT_CONNECTED);
504
505   if (!IS_SILC_CHANNEL(item))
506     cmd_return_error(CMDERR_NOT_JOINED);
507
508   /* Now parse all arguments */
509   tmp = g_strconcat(tmpcmd, " ", data, NULL);
510   silc_parse_command_line(tmp, &argv, &argv_lens,
511                           &argv_types, &argc, 2);
512   g_free(tmp);
513
514   if (argc < 2)
515     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
516
517   chanrec = silc_channel_find(server, item->name);
518   if (chanrec == NULL) 
519     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
520
521   /* Send the action message */
522   silc_client_send_channel_message(silc_client, server->conn, 
523                                    chanrec->entry, NULL,
524                                    SILC_MESSAGE_FLAG_NOTICE, 
525                                    argv[1], argv_lens[1], TRUE);
526
527   printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
528                      MSGLEVEL_NOTICES, SILCTXT_CHANNEL_OWNNOTICE, argv[1]);
529
530   for (i = 0; i < argc; i++)
531     silc_free(argv[i]);
532   silc_free(argv_lens);
533   silc_free(argv_types);
534 }
535
536 /* AWAY local command.  Sends UMODE command that sets the SILC_UMODE_GONE
537    flag. */
538
539 static void command_away(const char *data, SILC_SERVER_REC *server,
540                          WI_ITEM_REC *item)
541 {
542   bool set;
543
544   if (!IS_SILC_SERVER(server) || !server->connected)
545     cmd_return_error(CMDERR_NOT_CONNECTED);
546
547   if (*data == '\0') {
548     /* Remove any possible away message */
549     silc_client_set_away_message(silc_client, server->conn, NULL);
550     set = FALSE;
551   } else {
552     /* Set the away message */
553     silc_client_set_away_message(silc_client, server->conn, (char *)data);
554     set = TRUE;
555   }
556
557   silc_command_exec(server, "UMODE", set ? "+g" : "-g");
558 }
559
560 typedef struct {
561   int type;                     /* 1 = msg, 2 = channel */
562   SILC_SERVER_REC *server;
563 } *KeyInternal;
564
565 /* Key agreement callback that is called after the key agreement protocol
566    has been performed. This is called also if error occured during the
567    key agreement protocol. The `key' is the allocated key material and
568    the caller is responsible of freeing it. The `key' is NULL if error
569    has occured. The application can freely use the `key' to whatever
570    purpose it needs. See lib/silcske/silcske.h for the definition of
571    the SilcSKEKeyMaterial structure. */
572
573 static void keyagr_completion(SilcClient client,
574                               SilcClientConnection conn,
575                               SilcClientEntry client_entry,
576                               SilcKeyAgreementStatus status,
577                               SilcSKEKeyMaterial *key,
578                               void *context)
579 {
580   KeyInternal i = (KeyInternal)context;
581
582   switch(status) {
583   case SILC_KEY_AGREEMENT_OK:
584     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_NOTICES,
585                        SILCTXT_KEY_AGREEMENT_OK, client_entry->nickname);
586
587     if (i->type == 1) {
588       /* Set the private key for this client */
589       silc_client_del_private_message_key(client, conn, client_entry);
590       silc_client_add_private_message_key_ske(client, conn, client_entry,
591                                               NULL, key);
592       printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_NOTICES,
593                          SILCTXT_KEY_AGREEMENT_PRIVMSG, 
594                          client_entry->nickname);
595       silc_ske_free_key_material(key);
596     }
597     
598     break;
599     
600   case SILC_KEY_AGREEMENT_ERROR:
601     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_NOTICES,
602                        SILCTXT_KEY_AGREEMENT_ERROR, client_entry->nickname);
603     break;
604     
605   case SILC_KEY_AGREEMENT_FAILURE:
606     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_NOTICES,
607                        SILCTXT_KEY_AGREEMENT_FAILURE, client_entry->nickname);
608     break;
609     
610   case SILC_KEY_AGREEMENT_TIMEOUT:
611     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_NOTICES,
612                        SILCTXT_KEY_AGREEMENT_TIMEOUT, client_entry->nickname);
613     break;
614     
615   default:
616     break;
617   } 
618
619   if (i)
620     silc_free(i);
621 }
622
623 /* Local command KEY. This command is used to set and unset private
624    keys for channels, set and unset private keys for private messages
625    with remote clients and to send key agreement requests and
626    negotiate the key agreement protocol with remote client.  The
627    key agreement is supported only to negotiate private message keys,
628    it currently cannot be used to negotiate private keys for channels,
629    as it is not convenient for that purpose. */
630
631 typedef struct {
632   SILC_SERVER_REC *server;
633   char *data;
634   WI_ITEM_REC *item;
635 } *KeyGetClients;
636
637 /* Callback to be called after client information is resolved from the
638    server. */
639
640 SILC_CLIENT_CMD_FUNC(key_get_clients)
641 {
642   KeyGetClients internal = (KeyGetClients)context;
643   signal_emit("command key", 3, internal->data, internal->server,
644               internal->item);
645   silc_free(internal->data);
646   silc_free(internal);
647 }
648
649 static void command_key(const char *data, SILC_SERVER_REC *server,
650                         WI_ITEM_REC *item)
651 {
652   SilcClientConnection conn = server->conn;
653   SilcClientEntry client_entry = NULL;
654   SilcChannelEntry channel_entry = NULL;
655   uint32 num = 0;
656   char *nickname = NULL, *serv = NULL, *tmp;
657   int command = 0, port = 0, type = 0;
658   char *hostname = NULL;
659   KeyInternal internal = NULL;
660   uint32 argc = 0;
661   unsigned char **argv;
662   uint32 *argv_lens, *argv_types;
663  
664   if (!IS_SILC_SERVER(server) || !server->connected)
665     cmd_return_error(CMDERR_NOT_CONNECTED);
666
667   /* Now parse all arguments */
668   tmp = g_strconcat("KEY", " ", data, NULL);
669   silc_parse_command_line(tmp, &argv, &argv_lens, &argv_types, &argc, 7);
670   g_free(tmp);
671
672   if (argc < 4) {
673     silc_say(silc_client, conn, "Usage: /KEY msg|channel <nickname|channel> "
674              "set|unset|agreement|negotiate [<arguments>]");
675     return;
676   }
677
678   /* Get type */
679   if (!strcasecmp(argv[1], "msg"))
680     type = 1;
681   if (!strcasecmp(argv[1], "channel"))
682     type = 2;
683
684   if (type == 0) {
685     silc_say(silc_client, conn, "Usage: /KEY msg|channel <nickname|channel> "
686              "set|unset|agreement|negotiate [<arguments>]");
687     return;
688   }
689
690   if (type == 1) {
691     if (argv[2][0] == '*') {
692       nickname = "*";
693     } else {
694       /* Parse the typed nickname. */
695       if (!silc_parse_nickname(argv[2], &nickname, &serv, &num)) {
696         printformat_module("fe-common/silc", server, NULL,
697                            MSGLEVEL_CRAP, SILCTXT_BAD_NICK, argv[2]);
698         return;
699       }
700       
701       /* Find client entry */
702       client_entry = silc_idlist_get_client(silc_client, conn, nickname, 
703                                             serv, num, TRUE);
704       if (!client_entry) {
705         KeyGetClients inter = silc_calloc(1, sizeof(*inter));
706         inter->server = server;
707         inter->data = strdup(data);
708         inter->item = item;
709
710         /* Client entry not found, it was requested thus mark this to be
711            pending command. */
712         silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
713                                     conn->cmd_ident, 
714                                     NULL, silc_client_command_key_get_clients, 
715                                     inter);
716         goto out;
717       }
718     }
719   }
720
721   if (type == 2) {
722     /* Get channel entry */
723     char *name;
724
725     if (argv[2][0] == '*') {
726       if (!conn->current_channel) {
727         if (nickname)
728           silc_free(nickname);
729         if (serv)
730           silc_free(serv);
731         cmd_return_error(CMDERR_NOT_JOINED);
732       }
733       name = conn->current_channel->channel_name;
734     } else {
735       name = argv[2];
736     }
737
738     channel_entry = silc_client_get_channel(silc_client, conn, name);
739     if (!channel_entry) {
740       if (nickname)
741         silc_free(nickname);
742       if (serv)
743         silc_free(serv);
744       cmd_return_error(CMDERR_NOT_JOINED);
745     }
746   }
747
748   /* Set command */
749   if (!strcasecmp(argv[3], "set")) {
750     command = 1;
751
752     if (argc >= 5) {
753       if (type == 1 && client_entry) {
754         /* Set private message key */
755         
756         silc_client_del_private_message_key(silc_client, conn, client_entry);
757
758         if (argc >= 6)
759           silc_client_add_private_message_key(silc_client, conn, client_entry,
760                                               argv[5], argv[4],
761                                               argv_lens[4],
762                                               (argv[4][0] == '*' ?
763                                                TRUE : FALSE));
764         else
765           silc_client_add_private_message_key(silc_client, conn, client_entry,
766                                               NULL, argv[4],
767                                               argv_lens[4],
768                                               (argv[4][0] == '*' ?
769                                                TRUE : FALSE));
770
771         /* Send the key to the remote client so that it starts using it
772            too. */
773         silc_client_send_private_message_key(silc_client, conn, 
774                                              client_entry, TRUE);
775       } else if (type == 2) {
776         /* Set private channel key */
777         char *cipher = NULL, *hmac = NULL;
778
779         if (!(channel_entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
780           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
781                              SILCTXT_CH_PRIVATE_KEY_NOMODE, 
782                              channel_entry->channel_name);
783           goto out;
784         }
785
786         if (argc >= 6)
787           cipher = argv[5];
788         if (argc >= 7)
789           hmac = argv[6];
790
791         if (!silc_client_add_channel_private_key(silc_client, conn, 
792                                                  channel_entry,
793                                                  cipher, hmac,
794                                                  argv[4],
795                                                  argv_lens[4])) {
796           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
797                              SILCTXT_CH_PRIVATE_KEY_ERROR, 
798                              channel_entry->channel_name);
799           goto out;
800         }
801
802         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
803                            SILCTXT_CH_PRIVATE_KEY_ADD, 
804                            channel_entry->channel_name);
805       }
806     }
807
808     goto out;
809   }
810   
811   /* Unset command */
812   if (!strcasecmp(argv[3], "unset")) {
813     command = 2;
814
815     if (type == 1 && client_entry) {
816       /* Unset private message key */
817       silc_client_del_private_message_key(silc_client, conn, client_entry);
818     } else if (type == 2) {
819       /* Unset channel key(s) */
820       SilcChannelPrivateKey *keys;
821       uint32 keys_count;
822       int number;
823
824       if (argc == 4)
825         silc_client_del_channel_private_keys(silc_client, conn, 
826                                              channel_entry);
827
828       if (argc > 4) {
829         number = atoi(argv[4]);
830         keys = silc_client_list_channel_private_keys(silc_client, conn, 
831                                                      channel_entry,
832                                                      &keys_count);
833         if (!keys)
834           goto out;
835
836         if (!number || number > keys_count) {
837           silc_client_free_channel_private_keys(keys, keys_count);
838           goto out;
839         }
840
841         silc_client_del_channel_private_key(silc_client, conn, channel_entry,
842                                             keys[number - 1]);
843         silc_client_free_channel_private_keys(keys, keys_count);
844       }
845
846       goto out;
847     }
848   }
849
850   /* List command */
851   if (!strcasecmp(argv[3], "list")) {
852     command = 3;
853
854     if (type == 1) {
855       SilcPrivateMessageKeys keys;
856       uint32 keys_count;
857       int k, i, len;
858       char buf[1024];
859
860       keys = silc_client_list_private_message_keys(silc_client, conn, 
861                                                    &keys_count);
862       if (!keys)
863         goto out;
864
865       /* list the private message key(s) */
866       if (nickname[0] == '*') {
867         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
868                            SILCTXT_PRIVATE_KEY_LIST);
869         for (k = 0; k < keys_count; k++) {
870           memset(buf, 0, sizeof(buf));
871           strncat(buf, "  ", 2);
872           len = strlen(keys[k].client_entry->nickname);
873           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
874           if (len < 30)
875             for (i = 0; i < 30 - len; i++)
876               strcat(buf, " ");
877           strcat(buf, " ");
878           
879           len = strlen(keys[k].cipher);
880           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
881           if (len < 14)
882             for (i = 0; i < 14 - len; i++)
883               strcat(buf, " ");
884           strcat(buf, " ");
885
886           if (keys[k].key)
887             strcat(buf, "<hidden>");
888           else
889             strcat(buf, "*generated*");
890
891           silc_say(silc_client, conn, "%s", buf);
892         }
893       } else {
894         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
895                            SILCTXT_PRIVATE_KEY_LIST_NICK,
896                            client_entry->nickname);
897         for (k = 0; k < keys_count; k++) {
898           if (keys[k].client_entry != client_entry)
899             continue;
900
901           memset(buf, 0, sizeof(buf));
902           strncat(buf, "  ", 2);
903           len = strlen(keys[k].client_entry->nickname);
904           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
905           if (len < 30)
906             for (i = 0; i < 30 - len; i++)
907               strcat(buf, " ");
908           strcat(buf, " ");
909           
910           len = strlen(keys[k].cipher);
911           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
912           if (len < 14)
913             for (i = 0; i < 14 - len; i++)
914               strcat(buf, " ");
915           strcat(buf, " ");
916
917           if (keys[k].key)
918             strcat(buf, "<hidden>");
919           else
920             strcat(buf, "*generated*");
921
922           silc_say(silc_client, conn, "%s", buf);
923         }
924       }
925
926       silc_client_free_private_message_keys(keys, keys_count);
927     } else if (type == 2) {
928       SilcChannelPrivateKey *keys;
929       uint32 keys_count;
930       int k, i, len;
931       char buf[1024];
932
933       keys = silc_client_list_channel_private_keys(silc_client, conn, 
934                                                    channel_entry,
935                                                    &keys_count);
936       if (!keys)
937         goto out;
938       
939       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
940                          SILCTXT_CH_PRIVATE_KEY_LIST,
941                          channel_entry->channel_name);
942       for (k = 0; k < keys_count; k++) {
943         memset(buf, 0, sizeof(buf));
944         strncat(buf, "  ", 2);
945
946         len = strlen(keys[k]->cipher->cipher->name);
947         strncat(buf, keys[k]->cipher->cipher->name, len > 16 ? 16 : len);
948         if (len < 16)
949           for (i = 0; i < 16 - len; i++)
950             strcat(buf, " ");
951         strcat(buf, " ");
952         
953         len = strlen(keys[k]->hmac->hmac->name);
954         strncat(buf, keys[k]->hmac->hmac->name, len > 16 ? 16 : len);
955         if (len < 16)
956           for (i = 0; i < 16 - len; i++)
957             strcat(buf, " ");
958         strcat(buf, " ");
959         
960         strcat(buf, "<hidden>");
961
962         silc_say(silc_client, conn, "%s", buf);
963       }
964       
965       silc_client_free_channel_private_keys(keys, keys_count);
966     }
967
968     goto out;
969   }
970
971   /* Send command is used to send key agreement */
972   if (!strcasecmp(argv[3], "agreement")) {
973     command = 4;
974
975     if (argc >= 5)
976       hostname = argv[4];
977     if (argc >= 6)
978       port = atoi(argv[5]);
979
980     internal = silc_calloc(1, sizeof(*internal));
981     internal->type = type;
982     internal->server = server;
983   }
984
985   /* Start command is used to start key agreement (after receiving the
986      key_agreement client operation). */
987   if (!strcasecmp(argv[3], "negotiate")) {
988     command = 5;
989
990     if (argc >= 5)
991       hostname = argv[4];
992     if (argc >= 6)
993       port = atoi(argv[5]);
994
995     internal = silc_calloc(1, sizeof(*internal));
996     internal->type = type;
997     internal->server = server;
998   }
999
1000   if (command == 0) {
1001     silc_say(silc_client, conn, "Usage: /KEY msg|channel <nickname|channel> "
1002              "set|unset|agreement|negotiate [<arguments>]");
1003     goto out;
1004   }
1005
1006   if (command == 4 && client_entry) {
1007     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_NOTICES,
1008                        SILCTXT_KEY_AGREEMENT, argv[2]);
1009     silc_client_send_key_agreement(silc_client, conn, client_entry, hostname, 
1010                                    port, 120, keyagr_completion, internal);
1011     goto out;
1012   }
1013
1014   if (command == 5 && client_entry && hostname) {
1015     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_NOTICES,
1016                        SILCTXT_KEY_AGREEMENT_NEGOTIATE, argv[2]);
1017     silc_client_perform_key_agreement(silc_client, conn, client_entry, 
1018                                       hostname, port, keyagr_completion, 
1019                                       internal);
1020     goto out;
1021   }
1022
1023  out:
1024   if (nickname)
1025     silc_free(nickname);
1026   if (serv)
1027     silc_free(serv);
1028 }
1029
1030 void silc_channels_init(void)
1031 {
1032   signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1033   signal_add("server connected", (SIGNAL_FUNC) sig_connected);
1034   signal_add("server quit", (SIGNAL_FUNC) sig_server_quit);
1035
1036   signal_add("silc event join", (SIGNAL_FUNC) event_join);
1037   signal_add("silc event leave", (SIGNAL_FUNC) event_leave);
1038   signal_add("silc event signoff", (SIGNAL_FUNC) event_signoff);
1039   signal_add("silc event topic", (SIGNAL_FUNC) event_topic);
1040   signal_add("silc event invite", (SIGNAL_FUNC) event_invite);
1041   signal_add("silc event nick", (SIGNAL_FUNC) event_nick);
1042   signal_add("silc event cmode", (SIGNAL_FUNC) event_cmode);
1043   signal_add("silc event cumode", (SIGNAL_FUNC) event_cumode);
1044   signal_add("silc event motd", (SIGNAL_FUNC) event_motd);
1045   signal_add("silc event channel_change", (SIGNAL_FUNC) event_channel_change);
1046   signal_add("silc event server_signoff", (SIGNAL_FUNC) event_server_signoff);
1047   signal_add("silc event kick", (SIGNAL_FUNC) event_kick);
1048   signal_add("silc event kill", (SIGNAL_FUNC) event_kill);
1049   signal_add("silc event ban", (SIGNAL_FUNC) event_ban);
1050   
1051   command_bind("part", MODULE_NAME, (SIGNAL_FUNC) command_part);
1052   command_bind("me", MODULE_NAME, (SIGNAL_FUNC) command_me);
1053   command_bind("notice", MODULE_NAME, (SIGNAL_FUNC) command_notice);
1054   command_bind("away", MODULE_NAME, (SIGNAL_FUNC) command_away);
1055   command_bind("key", MODULE_NAME, (SIGNAL_FUNC) command_key);
1056
1057   silc_nicklist_init();
1058 }
1059
1060 void silc_channels_deinit(void)
1061 {
1062   signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1063   signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
1064   signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit);
1065
1066   signal_remove("silc event join", (SIGNAL_FUNC) event_join);
1067   signal_remove("silc event leave", (SIGNAL_FUNC) event_leave);
1068   signal_remove("silc event signoff", (SIGNAL_FUNC) event_signoff);
1069   signal_remove("silc event topic", (SIGNAL_FUNC) event_topic);
1070   signal_remove("silc event invite", (SIGNAL_FUNC) event_invite);
1071   signal_remove("silc event nick", (SIGNAL_FUNC) event_nick);
1072   signal_remove("silc event cmode", (SIGNAL_FUNC) event_cmode);
1073   signal_remove("silc event cumode", (SIGNAL_FUNC) event_cumode);
1074   signal_remove("silc event motd", (SIGNAL_FUNC) event_motd);
1075   signal_remove("silc event channel_change", 
1076                 (SIGNAL_FUNC) event_channel_change);
1077   signal_remove("silc event server_signoff", 
1078                 (SIGNAL_FUNC) event_server_signoff);
1079   signal_remove("silc event kick", (SIGNAL_FUNC) event_kick);
1080   signal_remove("silc event kill", (SIGNAL_FUNC) event_kill);
1081   signal_remove("silc event ban", (SIGNAL_FUNC) event_ban);
1082   
1083   command_unbind("part", (SIGNAL_FUNC) command_part);
1084   command_unbind("me", (SIGNAL_FUNC) command_me);
1085   command_unbind("notice", (SIGNAL_FUNC) command_notice);
1086   command_unbind("away", (SIGNAL_FUNC) command_away);
1087   command_unbind("key", (SIGNAL_FUNC) command_key);
1088
1089   silc_nicklist_deinit();
1090 }