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   if (!IS_SILC_SERVER(server) || !server->connected)
543     cmd_return_error(CMDERR_NOT_CONNECTED);
544
545   /* XXX TODO */
546 }
547
548 typedef struct {
549   int type;                     /* 1 = msg, 2 = channel */
550   SILC_SERVER_REC *server;
551 } *KeyInternal;
552
553 /* Key agreement callback that is called after the key agreement protocol
554    has been performed. This is called also if error occured during the
555    key agreement protocol. The `key' is the allocated key material and
556    the caller is responsible of freeing it. The `key' is NULL if error
557    has occured. The application can freely use the `key' to whatever
558    purpose it needs. See lib/silcske/silcske.h for the definition of
559    the SilcSKEKeyMaterial structure. */
560
561 static void keyagr_completion(SilcClient client,
562                               SilcClientConnection conn,
563                               SilcClientEntry client_entry,
564                               SilcKeyAgreementStatus status,
565                               SilcSKEKeyMaterial *key,
566                               void *context)
567 {
568   KeyInternal i = (KeyInternal)context;
569
570   switch(status) {
571   case SILC_KEY_AGREEMENT_OK:
572     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_NOTICES,
573                        SILCTXT_KEY_AGREEMENT_OK, client_entry->nickname);
574
575     if (i->type == 1) {
576       /* Set the private key for this client */
577       silc_client_del_private_message_key(client, conn, client_entry);
578       silc_client_add_private_message_key_ske(client, conn, client_entry,
579                                               NULL, key);
580       printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_NOTICES,
581                          SILCTXT_KEY_AGREEMENT_PRIVMSG, 
582                          client_entry->nickname);
583       silc_ske_free_key_material(key);
584     }
585     
586     break;
587     
588   case SILC_KEY_AGREEMENT_ERROR:
589     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_NOTICES,
590                        SILCTXT_KEY_AGREEMENT_ERROR, client_entry->nickname);
591     break;
592     
593   case SILC_KEY_AGREEMENT_FAILURE:
594     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_NOTICES,
595                        SILCTXT_KEY_AGREEMENT_FAILURE, client_entry->nickname);
596     break;
597     
598   case SILC_KEY_AGREEMENT_TIMEOUT:
599     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_NOTICES,
600                        SILCTXT_KEY_AGREEMENT_TIMEOUT, client_entry->nickname);
601     break;
602     
603   default:
604     break;
605   } 
606
607   if (i)
608     silc_free(i);
609 }
610
611 /* Local command KEY. This command is used to set and unset private
612    keys for channels, set and unset private keys for private messages
613    with remote clients and to send key agreement requests and
614    negotiate the key agreement protocol with remote client.  The
615    key agreement is supported only to negotiate private message keys,
616    it currently cannot be used to negotiate private keys for channels,
617    as it is not convenient for that purpose. */
618
619 typedef struct {
620   SILC_SERVER_REC *server;
621   char *data;
622   WI_ITEM_REC *item;
623 } *KeyGetClients;
624
625 /* Callback to be called after client information is resolved from the
626    server. */
627
628 SILC_CLIENT_CMD_FUNC(key_get_clients)
629 {
630   KeyGetClients internal = (KeyGetClients)context;
631   signal_emit("command key", 3, internal->data, internal->server,
632               internal->item);
633   silc_free(internal->data);
634   silc_free(internal);
635 }
636
637 static void command_key(const char *data, SILC_SERVER_REC *server,
638                         WI_ITEM_REC *item)
639 {
640   SilcClientConnection conn = server->conn;
641   SilcClientEntry client_entry = NULL;
642   SilcChannelEntry channel_entry = NULL;
643   uint32 num = 0;
644   char *nickname = NULL, *serv = NULL, *tmp;
645   int command = 0, port = 0, type = 0;
646   char *hostname = NULL;
647   KeyInternal internal = NULL;
648   uint32 argc = 0;
649   unsigned char **argv;
650   uint32 *argv_lens, *argv_types;
651  
652   if (!IS_SILC_SERVER(server) || !server->connected)
653     cmd_return_error(CMDERR_NOT_CONNECTED);
654
655   /* Now parse all arguments */
656   tmp = g_strconcat("KEY", " ", data, NULL);
657   silc_parse_command_line(tmp, &argv, &argv_lens, &argv_types, &argc, 7);
658   g_free(tmp);
659
660   if (argc < 4) {
661     silc_say(silc_client, conn, "Usage: /KEY msg|channel <nickname|channel> "
662              "set|unset|agreement|negotiate [<arguments>]");
663     return;
664   }
665
666   /* Get type */
667   if (!strcasecmp(argv[1], "msg"))
668     type = 1;
669   if (!strcasecmp(argv[1], "channel"))
670     type = 2;
671
672   if (type == 0) {
673     silc_say(silc_client, conn, "Usage: /KEY msg|channel <nickname|channel> "
674              "set|unset|agreement|negotiate [<arguments>]");
675     return;
676   }
677
678   if (type == 1) {
679     if (argv[2][0] == '*') {
680       nickname = "*";
681     } else {
682       /* Parse the typed nickname. */
683       if (!silc_parse_nickname(argv[2], &nickname, &serv, &num)) {
684         printformat_module("fe-common/silc", server, NULL,
685                            MSGLEVEL_CRAP, SILCTXT_BAD_NICK, argv[2]);
686         return;
687       }
688       
689       /* Find client entry */
690       client_entry = silc_idlist_get_client(silc_client, conn, nickname, 
691                                             serv, num, TRUE);
692       if (!client_entry) {
693         KeyGetClients inter = silc_calloc(1, sizeof(*inter));
694         inter->server = server;
695         inter->data = strdup(data);
696         inter->item = item;
697
698         /* Client entry not found, it was requested thus mark this to be
699            pending command. */
700         silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
701                                     conn->cmd_ident, 
702                                     NULL, silc_client_command_key_get_clients, 
703                                     inter);
704         goto out;
705       }
706     }
707   }
708
709   if (type == 2) {
710     /* Get channel entry */
711     char *name;
712
713     if (argv[2][0] == '*') {
714       if (!conn->current_channel) {
715         if (nickname)
716           silc_free(nickname);
717         if (serv)
718           silc_free(serv);
719         cmd_return_error(CMDERR_NOT_JOINED);
720       }
721       name = conn->current_channel->channel_name;
722     } else {
723       name = argv[2];
724     }
725
726     channel_entry = silc_client_get_channel(silc_client, conn, name);
727     if (!channel_entry) {
728       if (nickname)
729         silc_free(nickname);
730       if (serv)
731         silc_free(serv);
732       cmd_return_error(CMDERR_NOT_JOINED);
733     }
734   }
735
736   /* Set command */
737   if (!strcasecmp(argv[3], "set")) {
738     command = 1;
739
740     if (argc >= 5) {
741       if (type == 1 && client_entry) {
742         /* Set private message key */
743         
744         silc_client_del_private_message_key(silc_client, conn, client_entry);
745
746         if (argc >= 6)
747           silc_client_add_private_message_key(silc_client, conn, client_entry,
748                                               argv[5], argv[4],
749                                               argv_lens[4],
750                                               (argv[4][0] == '*' ?
751                                                TRUE : FALSE));
752         else
753           silc_client_add_private_message_key(silc_client, conn, client_entry,
754                                               NULL, argv[4],
755                                               argv_lens[4],
756                                               (argv[4][0] == '*' ?
757                                                TRUE : FALSE));
758
759         /* Send the key to the remote client so that it starts using it
760            too. */
761         silc_client_send_private_message_key(silc_client, conn, 
762                                              client_entry, TRUE);
763       } else if (type == 2) {
764         /* Set private channel key */
765         char *cipher = NULL, *hmac = NULL;
766
767         if (!(channel_entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
768           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
769                              SILCTXT_CH_PRIVATE_KEY_NOMODE, 
770                              channel_entry->channel_name);
771           goto out;
772         }
773
774         if (argc >= 6)
775           cipher = argv[5];
776         if (argc >= 7)
777           hmac = argv[6];
778
779         if (!silc_client_add_channel_private_key(silc_client, conn, 
780                                                  channel_entry,
781                                                  cipher, hmac,
782                                                  argv[4],
783                                                  argv_lens[4])) {
784           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
785                              SILCTXT_CH_PRIVATE_KEY_ERROR, 
786                              channel_entry->channel_name);
787           goto out;
788         }
789
790         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
791                            SILCTXT_CH_PRIVATE_KEY_ADD, 
792                            channel_entry->channel_name);
793       }
794     }
795
796     goto out;
797   }
798   
799   /* Unset command */
800   if (!strcasecmp(argv[3], "unset")) {
801     command = 2;
802
803     if (type == 1 && client_entry) {
804       /* Unset private message key */
805       silc_client_del_private_message_key(silc_client, conn, client_entry);
806     } else if (type == 2) {
807       /* Unset channel key(s) */
808       SilcChannelPrivateKey *keys;
809       uint32 keys_count;
810       int number;
811
812       if (argc == 4)
813         silc_client_del_channel_private_keys(silc_client, conn, 
814                                              channel_entry);
815
816       if (argc > 4) {
817         number = atoi(argv[4]);
818         keys = silc_client_list_channel_private_keys(silc_client, conn, 
819                                                      channel_entry,
820                                                      &keys_count);
821         if (!keys)
822           goto out;
823
824         if (!number || number > keys_count) {
825           silc_client_free_channel_private_keys(keys, keys_count);
826           goto out;
827         }
828
829         silc_client_del_channel_private_key(silc_client, conn, channel_entry,
830                                             keys[number - 1]);
831         silc_client_free_channel_private_keys(keys, keys_count);
832       }
833
834       goto out;
835     }
836   }
837
838   /* List command */
839   if (!strcasecmp(argv[3], "list")) {
840     command = 3;
841
842     if (type == 1) {
843       SilcPrivateMessageKeys keys;
844       uint32 keys_count;
845       int k, i, len;
846       char buf[1024];
847
848       keys = silc_client_list_private_message_keys(silc_client, conn, 
849                                                    &keys_count);
850       if (!keys)
851         goto out;
852
853       /* list the private message key(s) */
854       if (nickname[0] == '*') {
855         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
856                            SILCTXT_PRIVATE_KEY_LIST);
857         for (k = 0; k < keys_count; k++) {
858           memset(buf, 0, sizeof(buf));
859           strncat(buf, "  ", 2);
860           len = strlen(keys[k].client_entry->nickname);
861           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
862           if (len < 30)
863             for (i = 0; i < 30 - len; i++)
864               strcat(buf, " ");
865           strcat(buf, " ");
866           
867           len = strlen(keys[k].cipher);
868           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
869           if (len < 14)
870             for (i = 0; i < 14 - len; i++)
871               strcat(buf, " ");
872           strcat(buf, " ");
873
874           if (keys[k].key)
875             strcat(buf, "<hidden>");
876           else
877             strcat(buf, "*generated*");
878
879           silc_say(silc_client, conn, "%s", buf);
880         }
881       } else {
882         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
883                            SILCTXT_PRIVATE_KEY_LIST_NICK,
884                            client_entry->nickname);
885         for (k = 0; k < keys_count; k++) {
886           if (keys[k].client_entry != client_entry)
887             continue;
888
889           memset(buf, 0, sizeof(buf));
890           strncat(buf, "  ", 2);
891           len = strlen(keys[k].client_entry->nickname);
892           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
893           if (len < 30)
894             for (i = 0; i < 30 - len; i++)
895               strcat(buf, " ");
896           strcat(buf, " ");
897           
898           len = strlen(keys[k].cipher);
899           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
900           if (len < 14)
901             for (i = 0; i < 14 - len; i++)
902               strcat(buf, " ");
903           strcat(buf, " ");
904
905           if (keys[k].key)
906             strcat(buf, "<hidden>");
907           else
908             strcat(buf, "*generated*");
909
910           silc_say(silc_client, conn, "%s", buf);
911         }
912       }
913
914       silc_client_free_private_message_keys(keys, keys_count);
915     } else if (type == 2) {
916       SilcChannelPrivateKey *keys;
917       uint32 keys_count;
918       int k, i, len;
919       char buf[1024];
920
921       keys = silc_client_list_channel_private_keys(silc_client, conn, 
922                                                    channel_entry,
923                                                    &keys_count);
924       if (!keys)
925         goto out;
926       
927       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
928                          SILCTXT_CH_PRIVATE_KEY_LIST,
929                          channel_entry->channel_name);
930       for (k = 0; k < keys_count; k++) {
931         memset(buf, 0, sizeof(buf));
932         strncat(buf, "  ", 2);
933
934         len = strlen(keys[k]->cipher->cipher->name);
935         strncat(buf, keys[k]->cipher->cipher->name, len > 16 ? 16 : len);
936         if (len < 16)
937           for (i = 0; i < 16 - len; i++)
938             strcat(buf, " ");
939         strcat(buf, " ");
940         
941         len = strlen(keys[k]->hmac->hmac->name);
942         strncat(buf, keys[k]->hmac->hmac->name, len > 16 ? 16 : len);
943         if (len < 16)
944           for (i = 0; i < 16 - len; i++)
945             strcat(buf, " ");
946         strcat(buf, " ");
947         
948         strcat(buf, "<hidden>");
949
950         silc_say(silc_client, conn, "%s", buf);
951       }
952       
953       silc_client_free_channel_private_keys(keys, keys_count);
954     }
955
956     goto out;
957   }
958
959   /* Send command is used to send key agreement */
960   if (!strcasecmp(argv[3], "agreement")) {
961     command = 4;
962
963     if (argc >= 5)
964       hostname = argv[4];
965     if (argc >= 6)
966       port = atoi(argv[5]);
967
968     internal = silc_calloc(1, sizeof(*internal));
969     internal->type = type;
970     internal->server = server;
971   }
972
973   /* Start command is used to start key agreement (after receiving the
974      key_agreement client operation). */
975   if (!strcasecmp(argv[3], "negotiate")) {
976     command = 5;
977
978     if (argc >= 5)
979       hostname = argv[4];
980     if (argc >= 6)
981       port = atoi(argv[5]);
982
983     internal = silc_calloc(1, sizeof(*internal));
984     internal->type = type;
985     internal->server = server;
986   }
987
988   if (command == 0) {
989     silc_say(silc_client, conn, "Usage: /KEY msg|channel <nickname|channel> "
990              "set|unset|agreement|negotiate [<arguments>]");
991     goto out;
992   }
993
994   if (command == 4 && client_entry) {
995     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_NOTICES,
996                        SILCTXT_KEY_AGREEMENT, argv[2]);
997     silc_client_send_key_agreement(silc_client, conn, client_entry, hostname, 
998                                    port, 120, keyagr_completion, internal);
999     goto out;
1000   }
1001
1002   if (command == 5 && client_entry && hostname) {
1003     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_NOTICES,
1004                        SILCTXT_KEY_AGREEMENT_NEGOTIATE, argv[2]);
1005     silc_client_perform_key_agreement(silc_client, conn, client_entry, 
1006                                       hostname, port, keyagr_completion, 
1007                                       internal);
1008     goto out;
1009   }
1010
1011  out:
1012   if (nickname)
1013     silc_free(nickname);
1014   if (serv)
1015     silc_free(serv);
1016 }
1017
1018 void silc_channels_init(void)
1019 {
1020   signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1021   signal_add("server connected", (SIGNAL_FUNC) sig_connected);
1022   signal_add("server quit", (SIGNAL_FUNC) sig_server_quit);
1023
1024   signal_add("silc event join", (SIGNAL_FUNC) event_join);
1025   signal_add("silc event leave", (SIGNAL_FUNC) event_leave);
1026   signal_add("silc event signoff", (SIGNAL_FUNC) event_signoff);
1027   signal_add("silc event topic", (SIGNAL_FUNC) event_topic);
1028   signal_add("silc event invite", (SIGNAL_FUNC) event_invite);
1029   signal_add("silc event nick", (SIGNAL_FUNC) event_nick);
1030   signal_add("silc event cmode", (SIGNAL_FUNC) event_cmode);
1031   signal_add("silc event cumode", (SIGNAL_FUNC) event_cumode);
1032   signal_add("silc event motd", (SIGNAL_FUNC) event_motd);
1033   signal_add("silc event channel_change", (SIGNAL_FUNC) event_channel_change);
1034   signal_add("silc event server_signoff", (SIGNAL_FUNC) event_server_signoff);
1035   signal_add("silc event kick", (SIGNAL_FUNC) event_kick);
1036   signal_add("silc event kill", (SIGNAL_FUNC) event_kill);
1037   signal_add("silc event ban", (SIGNAL_FUNC) event_ban);
1038   
1039   command_bind("part", MODULE_NAME, (SIGNAL_FUNC) command_part);
1040   command_bind("me", MODULE_NAME, (SIGNAL_FUNC) command_me);
1041   command_bind("notice", MODULE_NAME, (SIGNAL_FUNC) command_notice);
1042   command_bind("away", MODULE_NAME, (SIGNAL_FUNC) command_away);
1043   command_bind("key", MODULE_NAME, (SIGNAL_FUNC) command_key);
1044
1045   silc_nicklist_init();
1046 }
1047
1048 void silc_channels_deinit(void)
1049 {
1050   signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1051   signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
1052   signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit);
1053
1054   signal_remove("silc event join", (SIGNAL_FUNC) event_join);
1055   signal_remove("silc event leave", (SIGNAL_FUNC) event_leave);
1056   signal_remove("silc event signoff", (SIGNAL_FUNC) event_signoff);
1057   signal_remove("silc event topic", (SIGNAL_FUNC) event_topic);
1058   signal_remove("silc event invite", (SIGNAL_FUNC) event_invite);
1059   signal_remove("silc event nick", (SIGNAL_FUNC) event_nick);
1060   signal_remove("silc event cmode", (SIGNAL_FUNC) event_cmode);
1061   signal_remove("silc event cumode", (SIGNAL_FUNC) event_cumode);
1062   signal_remove("silc event motd", (SIGNAL_FUNC) event_motd);
1063   signal_remove("silc event channel_change", 
1064                 (SIGNAL_FUNC) event_channel_change);
1065   signal_remove("silc event server_signoff", 
1066                 (SIGNAL_FUNC) event_server_signoff);
1067   signal_remove("silc event kick", (SIGNAL_FUNC) event_kick);
1068   signal_remove("silc event kill", (SIGNAL_FUNC) event_kill);
1069   signal_remove("silc event ban", (SIGNAL_FUNC) event_ban);
1070   
1071   command_unbind("part", (SIGNAL_FUNC) command_part);
1072   command_unbind("me", (SIGNAL_FUNC) command_me);
1073   command_unbind("notice", (SIGNAL_FUNC) command_notice);
1074   command_unbind("away", (SIGNAL_FUNC) command_away);
1075   command_unbind("key", (SIGNAL_FUNC) command_key);
1076
1077   silc_nicklist_deinit();
1078 }