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, "Usage: /KEY msg|channel <nickname|channel> "
804              "set|unset|agreement|negotiate [<arguments>]");
805     return;
806   }
807
808   /* Get type */
809   if (!strcasecmp(argv[1], "msg"))
810     type = 1;
811   if (!strcasecmp(argv[1], "channel"))
812     type = 2;
813
814   if (type == 0) {
815     silc_say(silc_client, conn, "Usage: /KEY msg|channel <nickname|channel> "
816              "set|unset|agreement|negotiate [<arguments>]");
817     return;
818   }
819
820   if (type == 1) {
821     if (argv[2][0] == '*') {
822       nickname = "*";
823     } else {
824       /* Parse the typed nickname. */
825       if (!silc_parse_nickname(argv[2], &nickname, &serv, &num)) {
826         printformat_module("fe-common/silc", server, NULL,
827                            MSGLEVEL_CRAP, SILCTXT_BAD_NICK, argv[2]);
828         return;
829       }
830       
831       /* Find client entry */
832       client_entry = silc_idlist_get_client(silc_client, conn, nickname, 
833                                             serv, num, TRUE);
834       if (!client_entry) {
835         KeyGetClients inter = silc_calloc(1, sizeof(*inter));
836         inter->server = server;
837         inter->data = strdup(data);
838         inter->item = item;
839
840         /* Client entry not found, it was requested thus mark this to be
841            pending command. */
842         silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
843                                     conn->cmd_ident, 
844                                     NULL, silc_client_command_key_get_clients, 
845                                     inter);
846         goto out;
847       }
848     }
849   }
850
851   if (type == 2) {
852     /* Get channel entry */
853     char *name;
854
855     if (argv[2][0] == '*') {
856       if (!conn->current_channel) {
857         if (nickname)
858           silc_free(nickname);
859         if (serv)
860           silc_free(serv);
861         cmd_return_error(CMDERR_NOT_JOINED);
862       }
863       name = conn->current_channel->channel_name;
864     } else {
865       name = argv[2];
866     }
867
868     channel_entry = silc_client_get_channel(silc_client, conn, name);
869     if (!channel_entry) {
870       if (nickname)
871         silc_free(nickname);
872       if (serv)
873         silc_free(serv);
874       cmd_return_error(CMDERR_NOT_JOINED);
875     }
876   }
877
878   /* Set command */
879   if (!strcasecmp(argv[3], "set")) {
880     command = 1;
881
882     if (argc >= 5) {
883       if (type == 1 && client_entry) {
884         /* Set private message key */
885         
886         silc_client_del_private_message_key(silc_client, conn, client_entry);
887
888         if (argc >= 6)
889           silc_client_add_private_message_key(silc_client, conn, client_entry,
890                                               argv[5], argv[4],
891                                               argv_lens[4],
892                                               (argv[4][0] == '*' ?
893                                                TRUE : FALSE), FALSE);
894         else
895           silc_client_add_private_message_key(silc_client, conn, client_entry,
896                                               NULL, argv[4],
897                                               argv_lens[4],
898                                               (argv[4][0] == '*' ?
899                                                TRUE : FALSE), FALSE);
900
901         /* Send the key to the remote client so that it starts using it
902            too. */
903         silc_client_send_private_message_key(silc_client, conn, 
904                                              client_entry, TRUE);
905       } else if (type == 2) {
906         /* Set private channel key */
907         char *cipher = NULL, *hmac = NULL;
908
909         if (!(channel_entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
910           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
911                              SILCTXT_CH_PRIVATE_KEY_NOMODE, 
912                              channel_entry->channel_name);
913           goto out;
914         }
915
916         if (argc >= 6)
917           cipher = argv[5];
918         if (argc >= 7)
919           hmac = argv[6];
920
921         if (!silc_client_add_channel_private_key(silc_client, conn, 
922                                                  channel_entry,
923                                                  cipher, hmac,
924                                                  argv[4],
925                                                  argv_lens[4])) {
926           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
927                              SILCTXT_CH_PRIVATE_KEY_ERROR, 
928                              channel_entry->channel_name);
929           goto out;
930         }
931
932         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
933                            SILCTXT_CH_PRIVATE_KEY_ADD, 
934                            channel_entry->channel_name);
935       }
936     }
937
938     goto out;
939   }
940   
941   /* Unset command */
942   if (!strcasecmp(argv[3], "unset")) {
943     command = 2;
944
945     if (type == 1 && client_entry) {
946       /* Unset private message key */
947       silc_client_del_private_message_key(silc_client, conn, client_entry);
948     } else if (type == 2) {
949       /* Unset channel key(s) */
950       SilcChannelPrivateKey *keys;
951       uint32 keys_count;
952       int number;
953
954       if (argc == 4)
955         silc_client_del_channel_private_keys(silc_client, conn, 
956                                              channel_entry);
957
958       if (argc > 4) {
959         number = atoi(argv[4]);
960         keys = silc_client_list_channel_private_keys(silc_client, conn, 
961                                                      channel_entry,
962                                                      &keys_count);
963         if (!keys)
964           goto out;
965
966         if (!number || number > keys_count) {
967           silc_client_free_channel_private_keys(keys, keys_count);
968           goto out;
969         }
970
971         silc_client_del_channel_private_key(silc_client, conn, channel_entry,
972                                             keys[number - 1]);
973         silc_client_free_channel_private_keys(keys, keys_count);
974       }
975
976       goto out;
977     }
978   }
979
980   /* List command */
981   if (!strcasecmp(argv[3], "list")) {
982     command = 3;
983
984     if (type == 1) {
985       SilcPrivateMessageKeys keys;
986       uint32 keys_count;
987       int k, i, len;
988       char buf[1024];
989
990       keys = silc_client_list_private_message_keys(silc_client, conn, 
991                                                    &keys_count);
992       if (!keys)
993         goto out;
994
995       /* list the private message key(s) */
996       if (nickname[0] == '*') {
997         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
998                            SILCTXT_PRIVATE_KEY_LIST);
999         for (k = 0; k < keys_count; k++) {
1000           memset(buf, 0, sizeof(buf));
1001           strncat(buf, "  ", 2);
1002           len = strlen(keys[k].client_entry->nickname);
1003           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
1004           if (len < 30)
1005             for (i = 0; i < 30 - len; i++)
1006               strcat(buf, " ");
1007           strcat(buf, " ");
1008           
1009           len = strlen(keys[k].cipher);
1010           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
1011           if (len < 14)
1012             for (i = 0; i < 14 - len; i++)
1013               strcat(buf, " ");
1014           strcat(buf, " ");
1015
1016           if (keys[k].key)
1017             strcat(buf, "<hidden>");
1018           else
1019             strcat(buf, "*generated*");
1020
1021           silc_say(silc_client, conn, "%s", buf);
1022         }
1023       } else {
1024         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1025                            SILCTXT_PRIVATE_KEY_LIST_NICK,
1026                            client_entry->nickname);
1027         for (k = 0; k < keys_count; k++) {
1028           if (keys[k].client_entry != client_entry)
1029             continue;
1030
1031           memset(buf, 0, sizeof(buf));
1032           strncat(buf, "  ", 2);
1033           len = strlen(keys[k].client_entry->nickname);
1034           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
1035           if (len < 30)
1036             for (i = 0; i < 30 - len; i++)
1037               strcat(buf, " ");
1038           strcat(buf, " ");
1039           
1040           len = strlen(keys[k].cipher);
1041           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
1042           if (len < 14)
1043             for (i = 0; i < 14 - len; i++)
1044               strcat(buf, " ");
1045           strcat(buf, " ");
1046
1047           if (keys[k].key)
1048             strcat(buf, "<hidden>");
1049           else
1050             strcat(buf, "*generated*");
1051
1052           silc_say(silc_client, conn, "%s", buf);
1053         }
1054       }
1055
1056       silc_client_free_private_message_keys(keys, keys_count);
1057     } else if (type == 2) {
1058       SilcChannelPrivateKey *keys;
1059       uint32 keys_count;
1060       int k, i, len;
1061       char buf[1024];
1062
1063       keys = silc_client_list_channel_private_keys(silc_client, conn, 
1064                                                    channel_entry,
1065                                                    &keys_count);
1066       if (!keys)
1067         goto out;
1068       
1069       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1070                          SILCTXT_CH_PRIVATE_KEY_LIST,
1071                          channel_entry->channel_name);
1072       for (k = 0; k < keys_count; k++) {
1073         memset(buf, 0, sizeof(buf));
1074         strncat(buf, "  ", 2);
1075
1076         len = strlen(keys[k]->cipher->cipher->name);
1077         strncat(buf, keys[k]->cipher->cipher->name, len > 16 ? 16 : len);
1078         if (len < 16)
1079           for (i = 0; i < 16 - len; i++)
1080             strcat(buf, " ");
1081         strcat(buf, " ");
1082         
1083         len = strlen(keys[k]->hmac->hmac->name);
1084         strncat(buf, keys[k]->hmac->hmac->name, len > 16 ? 16 : len);
1085         if (len < 16)
1086           for (i = 0; i < 16 - len; i++)
1087             strcat(buf, " ");
1088         strcat(buf, " ");
1089         
1090         strcat(buf, "<hidden>");
1091
1092         silc_say(silc_client, conn, "%s", buf);
1093       }
1094       
1095       silc_client_free_channel_private_keys(keys, keys_count);
1096     }
1097
1098     goto out;
1099   }
1100
1101   /* Send command is used to send key agreement */
1102   if (!strcasecmp(argv[3], "agreement")) {
1103     command = 4;
1104
1105     if (argc >= 5)
1106       hostname = argv[4];
1107     if (argc >= 6)
1108       port = atoi(argv[5]);
1109
1110     internal = silc_calloc(1, sizeof(*internal));
1111     internal->type = type;
1112     internal->server = server;
1113   }
1114
1115   /* Start command is used to start key agreement (after receiving the
1116      key_agreement client operation). */
1117   if (!strcasecmp(argv[3], "negotiate")) {
1118     command = 5;
1119
1120     if (argc >= 5)
1121       hostname = argv[4];
1122     if (argc >= 6)
1123       port = atoi(argv[5]);
1124
1125     internal = silc_calloc(1, sizeof(*internal));
1126     internal->type = type;
1127     internal->server = server;
1128   }
1129
1130   if (command == 0) {
1131     silc_say(silc_client, conn, "Usage: /KEY msg|channel <nickname|channel> "
1132              "set|unset|agreement|negotiate [<arguments>]");
1133     goto out;
1134   }
1135
1136   if (command == 4 && client_entry) {
1137     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_NOTICES,
1138                        SILCTXT_KEY_AGREEMENT, argv[2]);
1139     internal->responder = TRUE;
1140     silc_client_send_key_agreement(silc_client, conn, client_entry, hostname, 
1141                                    port, 120, keyagr_completion, internal);
1142     if (!hostname)
1143       silc_free(internal);
1144     goto out;
1145   }
1146
1147   if (command == 5 && client_entry && hostname) {
1148     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_NOTICES,
1149                        SILCTXT_KEY_AGREEMENT_NEGOTIATE, argv[2]);
1150     internal->responder = FALSE;
1151     silc_client_perform_key_agreement(silc_client, conn, client_entry, 
1152                                       hostname, port, keyagr_completion, 
1153                                       internal);
1154     goto out;
1155   }
1156
1157  out:
1158   if (nickname)
1159     silc_free(nickname);
1160   if (serv)
1161     silc_free(serv);
1162 }
1163
1164 void silc_channels_init(void)
1165 {
1166   signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1167   signal_add("server connected", (SIGNAL_FUNC) sig_connected);
1168   signal_add("server quit", (SIGNAL_FUNC) sig_server_quit);
1169
1170   signal_add("silc event join", (SIGNAL_FUNC) event_join);
1171   signal_add("silc event leave", (SIGNAL_FUNC) event_leave);
1172   signal_add("silc event signoff", (SIGNAL_FUNC) event_signoff);
1173   signal_add("silc event topic", (SIGNAL_FUNC) event_topic);
1174   signal_add("silc event invite", (SIGNAL_FUNC) event_invite);
1175   signal_add("silc event nick", (SIGNAL_FUNC) event_nick);
1176   signal_add("silc event cmode", (SIGNAL_FUNC) event_cmode);
1177   signal_add("silc event cumode", (SIGNAL_FUNC) event_cumode);
1178   signal_add("silc event motd", (SIGNAL_FUNC) event_motd);
1179   signal_add("silc event channel_change", (SIGNAL_FUNC) event_channel_change);
1180   signal_add("silc event server_signoff", (SIGNAL_FUNC) event_server_signoff);
1181   signal_add("silc event kick", (SIGNAL_FUNC) event_kick);
1182   signal_add("silc event kill", (SIGNAL_FUNC) event_kill);
1183   
1184   command_bind("part", MODULE_NAME, (SIGNAL_FUNC) command_part);
1185   command_bind("me", MODULE_NAME, (SIGNAL_FUNC) command_me);
1186   command_bind("action", MODULE_NAME, (SIGNAL_FUNC) command_action);
1187   command_bind("notice", MODULE_NAME, (SIGNAL_FUNC) command_notice);
1188   command_bind("away", MODULE_NAME, (SIGNAL_FUNC) command_away);
1189   command_bind("key", MODULE_NAME, (SIGNAL_FUNC) command_key);
1190
1191   silc_nicklist_init();
1192 }
1193
1194 void silc_channels_deinit(void)
1195 {
1196   signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1197   signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
1198   signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit);
1199
1200   signal_remove("silc event join", (SIGNAL_FUNC) event_join);
1201   signal_remove("silc event leave", (SIGNAL_FUNC) event_leave);
1202   signal_remove("silc event signoff", (SIGNAL_FUNC) event_signoff);
1203   signal_remove("silc event topic", (SIGNAL_FUNC) event_topic);
1204   signal_remove("silc event invite", (SIGNAL_FUNC) event_invite);
1205   signal_remove("silc event nick", (SIGNAL_FUNC) event_nick);
1206   signal_remove("silc event cmode", (SIGNAL_FUNC) event_cmode);
1207   signal_remove("silc event cumode", (SIGNAL_FUNC) event_cumode);
1208   signal_remove("silc event motd", (SIGNAL_FUNC) event_motd);
1209   signal_remove("silc event channel_change", 
1210                 (SIGNAL_FUNC) event_channel_change);
1211   signal_remove("silc event server_signoff", 
1212                 (SIGNAL_FUNC) event_server_signoff);
1213   signal_remove("silc event kick", (SIGNAL_FUNC) event_kick);
1214   signal_remove("silc event kill", (SIGNAL_FUNC) event_kill);
1215   
1216   command_unbind("part", (SIGNAL_FUNC) command_part);
1217   command_unbind("me", (SIGNAL_FUNC) command_me);
1218   command_unbind("action", (SIGNAL_FUNC) command_action);
1219   command_unbind("notice", (SIGNAL_FUNC) command_notice);
1220   command_unbind("away", (SIGNAL_FUNC) command_away);
1221   command_unbind("key", (SIGNAL_FUNC) command_key);
1222
1223   silc_nicklist_deinit();
1224 }