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   SILC_SERVER_REC *server;
692 } *KeyInternal;
693
694 /* Key agreement callback that is called after the key agreement protocol
695    has been performed. This is called also if error occured during the
696    key agreement protocol. The `key' is the allocated key material and
697    the caller is responsible of freeing it. The `key' is NULL if error
698    has occured. The application can freely use the `key' to whatever
699    purpose it needs. See lib/silcske/silcske.h for the definition of
700    the SilcSKEKeyMaterial structure. */
701
702 static void keyagr_completion(SilcClient client,
703                               SilcClientConnection conn,
704                               SilcClientEntry client_entry,
705                               SilcKeyAgreementStatus status,
706                               SilcSKEKeyMaterial *key,
707                               void *context)
708 {
709   KeyInternal i = (KeyInternal)context;
710
711   switch(status) {
712   case SILC_KEY_AGREEMENT_OK:
713     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_NOTICES,
714                        SILCTXT_KEY_AGREEMENT_OK, client_entry->nickname);
715
716     if (i->type == 1) {
717       /* Set the private key for this client */
718       silc_client_del_private_message_key(client, conn, client_entry);
719       silc_client_add_private_message_key_ske(client, conn, client_entry,
720                                               NULL, key);
721       printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_NOTICES,
722                          SILCTXT_KEY_AGREEMENT_PRIVMSG, 
723                          client_entry->nickname);
724       silc_ske_free_key_material(key);
725     }
726     
727     break;
728     
729   case SILC_KEY_AGREEMENT_ERROR:
730     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_NOTICES,
731                        SILCTXT_KEY_AGREEMENT_ERROR, client_entry->nickname);
732     break;
733     
734   case SILC_KEY_AGREEMENT_FAILURE:
735     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_NOTICES,
736                        SILCTXT_KEY_AGREEMENT_FAILURE, client_entry->nickname);
737     break;
738     
739   case SILC_KEY_AGREEMENT_TIMEOUT:
740     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_NOTICES,
741                        SILCTXT_KEY_AGREEMENT_TIMEOUT, client_entry->nickname);
742     break;
743     
744   default:
745     break;
746   } 
747
748   if (i)
749     silc_free(i);
750 }
751
752 /* Local command KEY. This command is used to set and unset private
753    keys for channels, set and unset private keys for private messages
754    with remote clients and to send key agreement requests and
755    negotiate the key agreement protocol with remote client.  The
756    key agreement is supported only to negotiate private message keys,
757    it currently cannot be used to negotiate private keys for channels,
758    as it is not convenient for that purpose. */
759
760 typedef struct {
761   SILC_SERVER_REC *server;
762   char *data;
763   WI_ITEM_REC *item;
764 } *KeyGetClients;
765
766 /* Callback to be called after client information is resolved from the
767    server. */
768
769 SILC_CLIENT_CMD_FUNC(key_get_clients)
770 {
771   KeyGetClients internal = (KeyGetClients)context;
772   signal_emit("command key", 3, internal->data, internal->server,
773               internal->item);
774   silc_free(internal->data);
775   silc_free(internal);
776 }
777
778 static void command_key(const char *data, SILC_SERVER_REC *server,
779                         WI_ITEM_REC *item)
780 {
781   SilcClientConnection conn = server->conn;
782   SilcClientEntry client_entry = NULL;
783   SilcChannelEntry channel_entry = NULL;
784   uint32 num = 0;
785   char *nickname = NULL, *serv = NULL, *tmp;
786   int command = 0, port = 0, type = 0;
787   char *hostname = NULL;
788   KeyInternal internal = NULL;
789   uint32 argc = 0;
790   unsigned char **argv;
791   uint32 *argv_lens, *argv_types;
792  
793   if (!IS_SILC_SERVER(server) || !server->connected)
794     cmd_return_error(CMDERR_NOT_CONNECTED);
795
796   /* Now parse all arguments */
797   tmp = g_strconcat("KEY", " ", data, NULL);
798   silc_parse_command_line(tmp, &argv, &argv_lens, &argv_types, &argc, 7);
799   g_free(tmp);
800
801   if (argc < 4) {
802     silc_say(silc_client, conn, "Usage: /KEY msg|channel <nickname|channel> "
803              "set|unset|agreement|negotiate [<arguments>]");
804     return;
805   }
806
807   /* Get type */
808   if (!strcasecmp(argv[1], "msg"))
809     type = 1;
810   if (!strcasecmp(argv[1], "channel"))
811     type = 2;
812
813   if (type == 0) {
814     silc_say(silc_client, conn, "Usage: /KEY msg|channel <nickname|channel> "
815              "set|unset|agreement|negotiate [<arguments>]");
816     return;
817   }
818
819   if (type == 1) {
820     if (argv[2][0] == '*') {
821       nickname = "*";
822     } else {
823       /* Parse the typed nickname. */
824       if (!silc_parse_nickname(argv[2], &nickname, &serv, &num)) {
825         printformat_module("fe-common/silc", server, NULL,
826                            MSGLEVEL_CRAP, SILCTXT_BAD_NICK, argv[2]);
827         return;
828       }
829       
830       /* Find client entry */
831       client_entry = silc_idlist_get_client(silc_client, conn, nickname, 
832                                             serv, num, TRUE);
833       if (!client_entry) {
834         KeyGetClients inter = silc_calloc(1, sizeof(*inter));
835         inter->server = server;
836         inter->data = strdup(data);
837         inter->item = item;
838
839         /* Client entry not found, it was requested thus mark this to be
840            pending command. */
841         silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
842                                     conn->cmd_ident, 
843                                     NULL, silc_client_command_key_get_clients, 
844                                     inter);
845         goto out;
846       }
847     }
848   }
849
850   if (type == 2) {
851     /* Get channel entry */
852     char *name;
853
854     if (argv[2][0] == '*') {
855       if (!conn->current_channel) {
856         if (nickname)
857           silc_free(nickname);
858         if (serv)
859           silc_free(serv);
860         cmd_return_error(CMDERR_NOT_JOINED);
861       }
862       name = conn->current_channel->channel_name;
863     } else {
864       name = argv[2];
865     }
866
867     channel_entry = silc_client_get_channel(silc_client, conn, name);
868     if (!channel_entry) {
869       if (nickname)
870         silc_free(nickname);
871       if (serv)
872         silc_free(serv);
873       cmd_return_error(CMDERR_NOT_JOINED);
874     }
875   }
876
877   /* Set command */
878   if (!strcasecmp(argv[3], "set")) {
879     command = 1;
880
881     if (argc >= 5) {
882       if (type == 1 && client_entry) {
883         /* Set private message key */
884         
885         silc_client_del_private_message_key(silc_client, conn, client_entry);
886
887         if (argc >= 6)
888           silc_client_add_private_message_key(silc_client, conn, client_entry,
889                                               argv[5], argv[4],
890                                               argv_lens[4],
891                                               (argv[4][0] == '*' ?
892                                                TRUE : FALSE));
893         else
894           silc_client_add_private_message_key(silc_client, conn, client_entry,
895                                               NULL, argv[4],
896                                               argv_lens[4],
897                                               (argv[4][0] == '*' ?
898                                                TRUE : FALSE));
899
900         /* Send the key to the remote client so that it starts using it
901            too. */
902         silc_client_send_private_message_key(silc_client, conn, 
903                                              client_entry, TRUE);
904       } else if (type == 2) {
905         /* Set private channel key */
906         char *cipher = NULL, *hmac = NULL;
907
908         if (!(channel_entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
909           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
910                              SILCTXT_CH_PRIVATE_KEY_NOMODE, 
911                              channel_entry->channel_name);
912           goto out;
913         }
914
915         if (argc >= 6)
916           cipher = argv[5];
917         if (argc >= 7)
918           hmac = argv[6];
919
920         if (!silc_client_add_channel_private_key(silc_client, conn, 
921                                                  channel_entry,
922                                                  cipher, hmac,
923                                                  argv[4],
924                                                  argv_lens[4])) {
925           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
926                              SILCTXT_CH_PRIVATE_KEY_ERROR, 
927                              channel_entry->channel_name);
928           goto out;
929         }
930
931         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
932                            SILCTXT_CH_PRIVATE_KEY_ADD, 
933                            channel_entry->channel_name);
934       }
935     }
936
937     goto out;
938   }
939   
940   /* Unset command */
941   if (!strcasecmp(argv[3], "unset")) {
942     command = 2;
943
944     if (type == 1 && client_entry) {
945       /* Unset private message key */
946       silc_client_del_private_message_key(silc_client, conn, client_entry);
947     } else if (type == 2) {
948       /* Unset channel key(s) */
949       SilcChannelPrivateKey *keys;
950       uint32 keys_count;
951       int number;
952
953       if (argc == 4)
954         silc_client_del_channel_private_keys(silc_client, conn, 
955                                              channel_entry);
956
957       if (argc > 4) {
958         number = atoi(argv[4]);
959         keys = silc_client_list_channel_private_keys(silc_client, conn, 
960                                                      channel_entry,
961                                                      &keys_count);
962         if (!keys)
963           goto out;
964
965         if (!number || number > keys_count) {
966           silc_client_free_channel_private_keys(keys, keys_count);
967           goto out;
968         }
969
970         silc_client_del_channel_private_key(silc_client, conn, channel_entry,
971                                             keys[number - 1]);
972         silc_client_free_channel_private_keys(keys, keys_count);
973       }
974
975       goto out;
976     }
977   }
978
979   /* List command */
980   if (!strcasecmp(argv[3], "list")) {
981     command = 3;
982
983     if (type == 1) {
984       SilcPrivateMessageKeys keys;
985       uint32 keys_count;
986       int k, i, len;
987       char buf[1024];
988
989       keys = silc_client_list_private_message_keys(silc_client, conn, 
990                                                    &keys_count);
991       if (!keys)
992         goto out;
993
994       /* list the private message key(s) */
995       if (nickname[0] == '*') {
996         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
997                            SILCTXT_PRIVATE_KEY_LIST);
998         for (k = 0; k < keys_count; k++) {
999           memset(buf, 0, sizeof(buf));
1000           strncat(buf, "  ", 2);
1001           len = strlen(keys[k].client_entry->nickname);
1002           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
1003           if (len < 30)
1004             for (i = 0; i < 30 - len; i++)
1005               strcat(buf, " ");
1006           strcat(buf, " ");
1007           
1008           len = strlen(keys[k].cipher);
1009           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
1010           if (len < 14)
1011             for (i = 0; i < 14 - len; i++)
1012               strcat(buf, " ");
1013           strcat(buf, " ");
1014
1015           if (keys[k].key)
1016             strcat(buf, "<hidden>");
1017           else
1018             strcat(buf, "*generated*");
1019
1020           silc_say(silc_client, conn, "%s", buf);
1021         }
1022       } else {
1023         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1024                            SILCTXT_PRIVATE_KEY_LIST_NICK,
1025                            client_entry->nickname);
1026         for (k = 0; k < keys_count; k++) {
1027           if (keys[k].client_entry != client_entry)
1028             continue;
1029
1030           memset(buf, 0, sizeof(buf));
1031           strncat(buf, "  ", 2);
1032           len = strlen(keys[k].client_entry->nickname);
1033           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
1034           if (len < 30)
1035             for (i = 0; i < 30 - len; i++)
1036               strcat(buf, " ");
1037           strcat(buf, " ");
1038           
1039           len = strlen(keys[k].cipher);
1040           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
1041           if (len < 14)
1042             for (i = 0; i < 14 - len; i++)
1043               strcat(buf, " ");
1044           strcat(buf, " ");
1045
1046           if (keys[k].key)
1047             strcat(buf, "<hidden>");
1048           else
1049             strcat(buf, "*generated*");
1050
1051           silc_say(silc_client, conn, "%s", buf);
1052         }
1053       }
1054
1055       silc_client_free_private_message_keys(keys, keys_count);
1056     } else if (type == 2) {
1057       SilcChannelPrivateKey *keys;
1058       uint32 keys_count;
1059       int k, i, len;
1060       char buf[1024];
1061
1062       keys = silc_client_list_channel_private_keys(silc_client, conn, 
1063                                                    channel_entry,
1064                                                    &keys_count);
1065       if (!keys)
1066         goto out;
1067       
1068       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1069                          SILCTXT_CH_PRIVATE_KEY_LIST,
1070                          channel_entry->channel_name);
1071       for (k = 0; k < keys_count; k++) {
1072         memset(buf, 0, sizeof(buf));
1073         strncat(buf, "  ", 2);
1074
1075         len = strlen(keys[k]->cipher->cipher->name);
1076         strncat(buf, keys[k]->cipher->cipher->name, len > 16 ? 16 : len);
1077         if (len < 16)
1078           for (i = 0; i < 16 - len; i++)
1079             strcat(buf, " ");
1080         strcat(buf, " ");
1081         
1082         len = strlen(keys[k]->hmac->hmac->name);
1083         strncat(buf, keys[k]->hmac->hmac->name, len > 16 ? 16 : len);
1084         if (len < 16)
1085           for (i = 0; i < 16 - len; i++)
1086             strcat(buf, " ");
1087         strcat(buf, " ");
1088         
1089         strcat(buf, "<hidden>");
1090
1091         silc_say(silc_client, conn, "%s", buf);
1092       }
1093       
1094       silc_client_free_channel_private_keys(keys, keys_count);
1095     }
1096
1097     goto out;
1098   }
1099
1100   /* Send command is used to send key agreement */
1101   if (!strcasecmp(argv[3], "agreement")) {
1102     command = 4;
1103
1104     if (argc >= 5)
1105       hostname = argv[4];
1106     if (argc >= 6)
1107       port = atoi(argv[5]);
1108
1109     internal = silc_calloc(1, sizeof(*internal));
1110     internal->type = type;
1111     internal->server = server;
1112   }
1113
1114   /* Start command is used to start key agreement (after receiving the
1115      key_agreement client operation). */
1116   if (!strcasecmp(argv[3], "negotiate")) {
1117     command = 5;
1118
1119     if (argc >= 5)
1120       hostname = argv[4];
1121     if (argc >= 6)
1122       port = atoi(argv[5]);
1123
1124     internal = silc_calloc(1, sizeof(*internal));
1125     internal->type = type;
1126     internal->server = server;
1127   }
1128
1129   if (command == 0) {
1130     silc_say(silc_client, conn, "Usage: /KEY msg|channel <nickname|channel> "
1131              "set|unset|agreement|negotiate [<arguments>]");
1132     goto out;
1133   }
1134
1135   if (command == 4 && client_entry) {
1136     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_NOTICES,
1137                        SILCTXT_KEY_AGREEMENT, argv[2]);
1138     silc_client_send_key_agreement(silc_client, conn, client_entry, hostname, 
1139                                    port, 120, keyagr_completion, internal);
1140     goto out;
1141   }
1142
1143   if (command == 5 && client_entry && hostname) {
1144     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_NOTICES,
1145                        SILCTXT_KEY_AGREEMENT_NEGOTIATE, argv[2]);
1146     silc_client_perform_key_agreement(silc_client, conn, client_entry, 
1147                                       hostname, port, keyagr_completion, 
1148                                       internal);
1149     goto out;
1150   }
1151
1152  out:
1153   if (nickname)
1154     silc_free(nickname);
1155   if (serv)
1156     silc_free(serv);
1157 }
1158
1159 void silc_channels_init(void)
1160 {
1161   signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1162   signal_add("server connected", (SIGNAL_FUNC) sig_connected);
1163   signal_add("server quit", (SIGNAL_FUNC) sig_server_quit);
1164
1165   signal_add("silc event join", (SIGNAL_FUNC) event_join);
1166   signal_add("silc event leave", (SIGNAL_FUNC) event_leave);
1167   signal_add("silc event signoff", (SIGNAL_FUNC) event_signoff);
1168   signal_add("silc event topic", (SIGNAL_FUNC) event_topic);
1169   signal_add("silc event invite", (SIGNAL_FUNC) event_invite);
1170   signal_add("silc event nick", (SIGNAL_FUNC) event_nick);
1171   signal_add("silc event cmode", (SIGNAL_FUNC) event_cmode);
1172   signal_add("silc event cumode", (SIGNAL_FUNC) event_cumode);
1173   signal_add("silc event motd", (SIGNAL_FUNC) event_motd);
1174   signal_add("silc event channel_change", (SIGNAL_FUNC) event_channel_change);
1175   signal_add("silc event server_signoff", (SIGNAL_FUNC) event_server_signoff);
1176   signal_add("silc event kick", (SIGNAL_FUNC) event_kick);
1177   signal_add("silc event kill", (SIGNAL_FUNC) event_kill);
1178   
1179   command_bind("part", MODULE_NAME, (SIGNAL_FUNC) command_part);
1180   command_bind("me", MODULE_NAME, (SIGNAL_FUNC) command_me);
1181   command_bind("action", MODULE_NAME, (SIGNAL_FUNC) command_action);
1182   command_bind("notice", MODULE_NAME, (SIGNAL_FUNC) command_notice);
1183   command_bind("away", MODULE_NAME, (SIGNAL_FUNC) command_away);
1184   command_bind("key", MODULE_NAME, (SIGNAL_FUNC) command_key);
1185
1186   silc_nicklist_init();
1187 }
1188
1189 void silc_channels_deinit(void)
1190 {
1191   signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1192   signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
1193   signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit);
1194
1195   signal_remove("silc event join", (SIGNAL_FUNC) event_join);
1196   signal_remove("silc event leave", (SIGNAL_FUNC) event_leave);
1197   signal_remove("silc event signoff", (SIGNAL_FUNC) event_signoff);
1198   signal_remove("silc event topic", (SIGNAL_FUNC) event_topic);
1199   signal_remove("silc event invite", (SIGNAL_FUNC) event_invite);
1200   signal_remove("silc event nick", (SIGNAL_FUNC) event_nick);
1201   signal_remove("silc event cmode", (SIGNAL_FUNC) event_cmode);
1202   signal_remove("silc event cumode", (SIGNAL_FUNC) event_cumode);
1203   signal_remove("silc event motd", (SIGNAL_FUNC) event_motd);
1204   signal_remove("silc event channel_change", 
1205                 (SIGNAL_FUNC) event_channel_change);
1206   signal_remove("silc event server_signoff", 
1207                 (SIGNAL_FUNC) event_server_signoff);
1208   signal_remove("silc event kick", (SIGNAL_FUNC) event_kick);
1209   signal_remove("silc event kill", (SIGNAL_FUNC) event_kill);
1210   
1211   command_unbind("part", (SIGNAL_FUNC) command_part);
1212   command_unbind("me", (SIGNAL_FUNC) command_me);
1213   command_unbind("action", (SIGNAL_FUNC) command_action);
1214   command_unbind("notice", (SIGNAL_FUNC) command_notice);
1215   command_unbind("away", (SIGNAL_FUNC) command_away);
1216   command_unbind("key", (SIGNAL_FUNC) command_key);
1217
1218   silc_nicklist_deinit();
1219 }