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