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