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