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