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