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