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