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