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