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