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