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