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