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