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     GSList *nicks, *tmp;
453
454     memset(userhost, 0, sizeof(userhost));
455     if (clients[i]->username)
456       snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
457                clients[i]->username, clients[i]->hostname);
458     signal_emit("message quit", 4, server, clients[i]->nickname,
459                 clients[i]->username ? userhost : "", 
460                 "server signoff");
461
462     nicks = nicklist_get_same_unique(SERVER(server), clients[i]);
463     for (tmp = nicks; tmp != NULL; tmp = tmp->next->next) {
464       CHANNEL_REC *channel = tmp->data;
465       NICK_REC *nickrec = tmp->next->data;
466       nicklist_remove(channel, nickrec);
467     }
468   }
469 }
470
471 /*
472  * "event kick". Someone was kicked from channel.
473  */
474
475 static void event_kick(SILC_SERVER_REC *server, va_list va)
476 {
477   SilcClientConnection conn = server->conn;
478   SilcClientEntry client_entry, kicker;
479   SilcChannelEntry channel_entry;
480   char *tmp;
481   SILC_CHANNEL_REC *chanrec;
482
483   client_entry = va_arg(va, SilcClientEntry);
484   tmp = va_arg(va, char *);
485   kicker = va_arg(va, SilcClientEntry);
486   channel_entry = va_arg(va, SilcChannelEntry);
487
488   chanrec = silc_channel_find_entry(server, channel_entry);
489   
490   if (client_entry == conn->local_entry) {
491     printformat_module("fe-common/silc", server, channel_entry->channel_name,
492                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED_YOU, 
493                        kicker->nickname,
494                        channel_entry->channel_name, tmp ? tmp : "");
495     if (chanrec) {
496       chanrec->kicked = TRUE;
497       channel_destroy((CHANNEL_REC *)chanrec);
498     }
499   } else {
500     printformat_module("fe-common/silc", server, channel_entry->channel_name,
501                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED, 
502                        client_entry->nickname,
503                        kicker->nickname,
504                        channel_entry->channel_name, tmp ? tmp : "");
505
506     if (chanrec) {
507       SILC_NICK_REC *nickrec = silc_nicklist_find(chanrec, client_entry);
508       if (nickrec != NULL)
509         nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
510     }
511   }
512 }
513
514 /*
515  * "event kill". Someone was killed from the network.
516  */
517
518 static void event_kill(SILC_SERVER_REC *server, va_list va)
519 {
520   SilcClientConnection conn = server->conn;
521   SilcClientEntry client_entry;
522   char *tmp;
523
524   client_entry = va_arg(va, SilcClientEntry);
525   tmp = va_arg(va, char *);
526   
527   if (client_entry == conn->local_entry) {
528     printformat_module("fe-common/silc", server, NULL,
529                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU, 
530                        tmp ? tmp : "");
531   } else {
532     GSList *nicks, *tmpn;
533     nicks = nicklist_get_same_unique(SERVER(server), client_entry);
534     for (tmpn = nicks; tmpn != NULL; tmpn = tmpn->next->next) {
535       CHANNEL_REC *channel = tmpn->data;
536       NICK_REC *nickrec = tmpn->next->data;
537       nicklist_remove(channel, nickrec);
538     }
539
540     printformat_module("fe-common/silc", server, NULL,
541                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED, 
542                        client_entry->nickname,
543                        tmp ? tmp : "");
544   }
545 }
546
547 /* PART (LEAVE) command. */
548
549 static void command_part(const char *data, SILC_SERVER_REC *server,
550                          WI_ITEM_REC *item)
551 {
552   SILC_CHANNEL_REC *chanrec;
553   char userhost[256];
554   
555   if (!IS_SILC_SERVER(server) || !server->connected)
556     cmd_return_error(CMDERR_NOT_CONNECTED);
557
558   if (!strcmp(data, "*") || *data == '\0') {
559     if (!IS_SILC_CHANNEL(item))
560       cmd_return_error(CMDERR_NOT_JOINED);
561     data = item->name;
562   }
563
564   chanrec = silc_channel_find(server, data);
565   if (chanrec == NULL) 
566     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
567
568   memset(userhost, 0, sizeof(userhost));
569   snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
570            server->conn->local_entry->username, 
571            server->conn->local_entry->hostname);
572   signal_emit("message part", 5, server, chanrec->name,
573               server->nick, userhost, "");
574   
575   silc_command_exec(server, "LEAVE", chanrec->name);
576   signal_stop();
577   
578   channel_destroy(CHANNEL(chanrec));
579 }
580
581 /* ME local command. */
582
583 static void command_me(const char *data, SILC_SERVER_REC *server,
584                        WI_ITEM_REC *item)
585 {
586   SILC_CHANNEL_REC *chanrec;
587   char *tmpcmd = "ME", *tmp;
588   uint32 argc = 0;
589   unsigned char **argv;
590   uint32 *argv_lens, *argv_types;
591   int i;
592  
593   if (!IS_SILC_SERVER(server) || !server->connected)
594     cmd_return_error(CMDERR_NOT_CONNECTED);
595
596   if (!IS_SILC_CHANNEL(item))
597     cmd_return_error(CMDERR_NOT_JOINED);
598
599   /* Now parse all arguments */
600   tmp = g_strconcat(tmpcmd, " ", data, NULL);
601   silc_parse_command_line(tmp, &argv, &argv_lens,
602                           &argv_types, &argc, 2);
603   g_free(tmp);
604
605   if (argc < 2)
606     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
607
608   chanrec = silc_channel_find(server, item->name);
609   if (chanrec == NULL) 
610     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
611
612   /* Send the action message */
613   silc_client_send_channel_message(silc_client, server->conn, 
614                                    chanrec->entry, NULL,
615                                    SILC_MESSAGE_FLAG_ACTION, 
616                                    argv[1], argv_lens[1], TRUE);
617
618   printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
619                      MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_OWNACTION, 
620                      server->conn->local_entry->nickname, argv[1]);
621
622   for (i = 0; i < argc; i++)
623     silc_free(argv[i]);
624   silc_free(argv_lens);
625   silc_free(argv_types);
626 }
627
628 /* ACTION local command. Same as ME but takes the channel as mandatory
629    argument. */
630
631 static void command_action(const char *data, SILC_SERVER_REC *server,
632                            WI_ITEM_REC *item)
633 {
634   SILC_CHANNEL_REC *chanrec;
635   char *tmpcmd = "ME", *tmp;
636   uint32 argc = 0;
637   unsigned char **argv;
638   uint32 *argv_lens, *argv_types;
639   int i;
640  
641   if (!IS_SILC_SERVER(server) || !server->connected)
642     cmd_return_error(CMDERR_NOT_CONNECTED);
643
644   if (!IS_SILC_CHANNEL(item))
645     cmd_return_error(CMDERR_NOT_JOINED);
646
647   /* Now parse all arguments */
648   tmp = g_strconcat(tmpcmd, " ", data, NULL);
649   silc_parse_command_line(tmp, &argv, &argv_lens,
650                           &argv_types, &argc, 3);
651   g_free(tmp);
652
653   if (argc < 3)
654     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
655
656   chanrec = silc_channel_find(server, argv[1]);
657   if (chanrec == NULL) 
658     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
659
660   /* Send the action message */
661   silc_client_send_channel_message(silc_client, server->conn, 
662                                    chanrec->entry, NULL,
663                                    SILC_MESSAGE_FLAG_ACTION, 
664                                    argv[2], argv_lens[2], TRUE);
665
666   printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
667                      MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_OWNACTION, 
668                      server->conn->local_entry->nickname, argv[2]);
669
670   for (i = 0; i < argc; i++)
671     silc_free(argv[i]);
672   silc_free(argv_lens);
673   silc_free(argv_types);
674 }
675
676 /* NOTICE local command. */
677
678 static void command_notice(const char *data, SILC_SERVER_REC *server,
679                            WI_ITEM_REC *item)
680 {
681   SILC_CHANNEL_REC *chanrec;
682   char *tmpcmd = "ME", *tmp;
683   uint32 argc = 0;
684   unsigned char **argv;
685   uint32 *argv_lens, *argv_types;
686   int i;
687  
688   if (!IS_SILC_SERVER(server) || !server->connected)
689     cmd_return_error(CMDERR_NOT_CONNECTED);
690
691   if (!IS_SILC_CHANNEL(item))
692     cmd_return_error(CMDERR_NOT_JOINED);
693
694   /* Now parse all arguments */
695   tmp = g_strconcat(tmpcmd, " ", data, NULL);
696   silc_parse_command_line(tmp, &argv, &argv_lens,
697                           &argv_types, &argc, 2);
698   g_free(tmp);
699
700   if (argc < 2)
701     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
702
703   chanrec = silc_channel_find(server, item->name);
704   if (chanrec == NULL) 
705     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
706
707   /* Send the action message */
708   silc_client_send_channel_message(silc_client, server->conn, 
709                                    chanrec->entry, NULL,
710                                    SILC_MESSAGE_FLAG_NOTICE, 
711                                    argv[1], argv_lens[1], TRUE);
712
713   printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
714                      MSGLEVEL_NOTICES, SILCTXT_CHANNEL_OWNNOTICE, 
715                      server->conn->local_entry->nickname, argv[1]);
716
717   for (i = 0; i < argc; i++)
718     silc_free(argv[i]);
719   silc_free(argv_lens);
720   silc_free(argv_types);
721 }
722
723 /* AWAY local command.  Sends UMODE command that sets the SILC_UMODE_GONE
724    flag. */
725
726 static void command_away(const char *data, SILC_SERVER_REC *server,
727                          WI_ITEM_REC *item)
728 {
729   bool set;
730
731   if (!IS_SILC_SERVER(server) || !server->connected)
732     cmd_return_error(CMDERR_NOT_CONNECTED);
733
734   if (*data == '\0') {
735     /* Remove any possible away message */
736     silc_client_set_away_message(silc_client, server->conn, NULL);
737     set = FALSE;
738
739     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, 
740                        SILCTXT_UNSET_AWAY);
741   } else {
742     /* Set the away message */
743     silc_client_set_away_message(silc_client, server->conn, (char *)data);
744     set = TRUE;
745
746     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, 
747                        SILCTXT_SET_AWAY, data);
748   }
749
750   signal_emit("away mode changed", 1, server);
751
752   silc_command_exec(server, "UMODE", set ? "+g" : "-g");
753 }
754
755 typedef struct {
756   int type;                     /* 1 = msg, 2 = channel */
757   bool responder;
758   SILC_SERVER_REC *server;
759 } *KeyInternal;
760
761 /* Key agreement callback that is called after the key agreement protocol
762    has been performed. This is called also if error occured during the
763    key agreement protocol. The `key' is the allocated key material and
764    the caller is responsible of freeing it. The `key' is NULL if error
765    has occured. The application can freely use the `key' to whatever
766    purpose it needs. See lib/silcske/silcske.h for the definition of
767    the SilcSKEKeyMaterial structure. */
768
769 static void keyagr_completion(SilcClient client,
770                               SilcClientConnection conn,
771                               SilcClientEntry client_entry,
772                               SilcKeyAgreementStatus status,
773                               SilcSKEKeyMaterial *key,
774                               void *context)
775 {
776   KeyInternal i = (KeyInternal)context;
777
778   switch(status) {
779   case SILC_KEY_AGREEMENT_OK:
780     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
781                        SILCTXT_KEY_AGREEMENT_OK, client_entry->nickname);
782
783     if (i->type == 1) {
784       /* Set the private key for this client */
785       silc_client_del_private_message_key(client, conn, client_entry);
786       silc_client_add_private_message_key_ske(client, conn, client_entry,
787                                               NULL, key, i->responder);
788       printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
789                          SILCTXT_KEY_AGREEMENT_PRIVMSG, 
790                          client_entry->nickname);
791       silc_ske_free_key_material(key);
792     }
793     
794     break;
795     
796   case SILC_KEY_AGREEMENT_ERROR:
797     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
798                        SILCTXT_KEY_AGREEMENT_ERROR, client_entry->nickname);
799     break;
800     
801   case SILC_KEY_AGREEMENT_FAILURE:
802     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
803                        SILCTXT_KEY_AGREEMENT_FAILURE, client_entry->nickname);
804     break;
805     
806   case SILC_KEY_AGREEMENT_TIMEOUT:
807     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
808                        SILCTXT_KEY_AGREEMENT_TIMEOUT, client_entry->nickname);
809     break;
810     
811   case SILC_KEY_AGREEMENT_ABORTED:
812     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
813                        SILCTXT_KEY_AGREEMENT_ABORTED, client_entry->nickname);
814     break;
815     
816   default:
817     break;
818   } 
819
820   if (i)
821     silc_free(i);
822 }
823
824 /* Local command KEY. This command is used to set and unset private
825    keys for channels, set and unset private keys for private messages
826    with remote clients and to send key agreement requests and
827    negotiate the key agreement protocol with remote client.  The
828    key agreement is supported only to negotiate private message keys,
829    it currently cannot be used to negotiate private keys for channels,
830    as it is not convenient for that purpose. */
831
832 typedef struct {
833   SILC_SERVER_REC *server;
834   char *data;
835   char *nick;
836   WI_ITEM_REC *item;
837 } *KeyGetClients;
838
839 /* Callback to be called after client information is resolved from the
840    server. */
841
842 static void silc_client_command_key_get_clients(SilcClient client,
843                                                 SilcClientConnection conn,
844                                                 SilcClientEntry *clients,
845                                                 uint32 clients_count,
846                                                 void *context)
847 {
848   KeyGetClients internal = (KeyGetClients)context;
849
850   if (!clients) {
851     printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Unknown nick: %s", 
852               internal->nick);
853     silc_free(internal->data);
854     silc_free(internal->nick);
855     silc_free(internal);
856     return;
857   }
858
859   signal_emit("command key", 3, internal->data, internal->server,
860               internal->item);
861
862   silc_free(internal->data);
863   silc_free(internal->nick);
864   silc_free(internal);
865 }
866
867 static void command_key(const char *data, SILC_SERVER_REC *server,
868                         WI_ITEM_REC *item)
869 {
870   SilcClientConnection conn;
871   SilcClientEntry *entrys, client_entry = NULL;
872   uint32 entry_count;
873   SilcChannelEntry channel_entry = NULL;
874   char *nickname = NULL, *tmp;
875   int command = 0, port = 0, type = 0;
876   char *hostname = NULL;
877   KeyInternal internal = NULL;
878   uint32 argc = 0;
879   unsigned char **argv;
880   uint32 *argv_lens, *argv_types;
881   char *bindhost = NULL;
882  
883   if (!server || !IS_SILC_SERVER(server) || !server->connected)
884     cmd_return_error(CMDERR_NOT_CONNECTED);
885
886   conn = server->conn;
887
888   /* Now parse all arguments */
889   tmp = g_strconcat("KEY", " ", data, NULL);
890   silc_parse_command_line(tmp, &argv, &argv_lens, &argv_types, &argc, 7);
891   g_free(tmp);
892
893   if (argc < 4)
894     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
895
896   /* Get type */
897   if (!strcasecmp(argv[1], "msg"))
898     type = 1;
899   if (!strcasecmp(argv[1], "channel"))
900     type = 2;
901
902   if (type == 0)
903     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
904
905   if (type == 1) {
906     if (argv[2][0] == '*') {
907       nickname = strdup("*");
908     } else {
909       /* Parse the typed nickname. */
910       if (!silc_parse_userfqdn(argv[2], &nickname, NULL)) {
911         printformat_module("fe-common/silc", server, NULL,
912                            MSGLEVEL_CRAP, SILCTXT_BAD_NICK, argv[2]);
913         return;
914       }
915       
916       /* Find client entry */
917       entrys = silc_client_get_clients_local(silc_client, conn, nickname,
918                                              argv[2], &entry_count);
919       if (!entrys) {
920         KeyGetClients inter = silc_calloc(1, sizeof(*inter));
921         inter->server = server;
922         inter->data = strdup(data);
923         inter->nick = strdup(nickname);
924         inter->item = item;
925         silc_client_get_clients(silc_client, conn, nickname, argv[2],
926                                 silc_client_command_key_get_clients, inter);
927         goto out;
928       }
929       client_entry = entrys[0];
930       silc_free(entrys);
931     }
932   }
933
934   if (type == 2) {
935     /* Get channel entry */
936     char *name;
937
938     if (argv[2][0] == '*') {
939       if (!conn->current_channel) {
940         silc_free(nickname);
941         cmd_return_error(CMDERR_NOT_JOINED);
942       }
943       name = conn->current_channel->channel_name;
944     } else {
945       name = argv[2];
946     }
947
948     channel_entry = silc_client_get_channel(silc_client, conn, name);
949     if (!channel_entry) {
950       silc_free(nickname);
951       cmd_return_error(CMDERR_NOT_JOINED);
952     }
953   }
954
955   /* Set command */
956   if (!strcasecmp(argv[3], "set")) {
957     command = 1;
958
959     if (argc >= 5) {
960       if (type == 1 && client_entry) {
961         /* Set private message key */
962         
963         silc_client_del_private_message_key(silc_client, conn, client_entry);
964
965         if (argc >= 6)
966           silc_client_add_private_message_key(silc_client, conn, client_entry,
967                                               argv[5], argv[4],
968                                               argv_lens[4],
969                                               (argv[4][0] == '*' ?
970                                                TRUE : FALSE), FALSE);
971         else
972           silc_client_add_private_message_key(silc_client, conn, client_entry,
973                                               NULL, argv[4],
974                                               argv_lens[4],
975                                               (argv[4][0] == '*' ?
976                                                TRUE : FALSE), FALSE);
977
978         /* Send the key to the remote client so that it starts using it
979            too. */
980         silc_client_send_private_message_key(silc_client, conn, 
981                                              client_entry, TRUE);
982       } else if (type == 2) {
983         /* Set private channel key */
984         char *cipher = NULL, *hmac = NULL;
985
986         if (!(channel_entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
987           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
988                              SILCTXT_CH_PRIVATE_KEY_NOMODE, 
989                              channel_entry->channel_name);
990           goto out;
991         }
992
993         if (argc >= 6)
994           cipher = argv[5];
995         if (argc >= 7)
996           hmac = argv[6];
997
998         if (!silc_client_add_channel_private_key(silc_client, conn, 
999                                                  channel_entry,
1000                                                  cipher, hmac,
1001                                                  argv[4],
1002                                                  argv_lens[4])) {
1003           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1004                              SILCTXT_CH_PRIVATE_KEY_ERROR, 
1005                              channel_entry->channel_name);
1006           goto out;
1007         }
1008
1009         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1010                            SILCTXT_CH_PRIVATE_KEY_ADD, 
1011                            channel_entry->channel_name);
1012       }
1013     }
1014
1015     goto out;
1016   }
1017   
1018   /* Unset command */
1019   if (!strcasecmp(argv[3], "unset")) {
1020     command = 2;
1021
1022     if (type == 1 && client_entry) {
1023       /* Unset private message key */
1024       silc_client_del_private_message_key(silc_client, conn, client_entry);
1025     } else if (type == 2) {
1026       /* Unset channel key(s) */
1027       SilcChannelPrivateKey *keys;
1028       uint32 keys_count;
1029       int number;
1030
1031       if (argc == 4)
1032         silc_client_del_channel_private_keys(silc_client, conn, 
1033                                              channel_entry);
1034
1035       if (argc > 4) {
1036         number = atoi(argv[4]);
1037         keys = silc_client_list_channel_private_keys(silc_client, conn, 
1038                                                      channel_entry,
1039                                                      &keys_count);
1040         if (!keys)
1041           goto out;
1042
1043         if (!number || number > keys_count) {
1044           silc_client_free_channel_private_keys(keys, keys_count);
1045           goto out;
1046         }
1047
1048         silc_client_del_channel_private_key(silc_client, conn, channel_entry,
1049                                             keys[number - 1]);
1050         silc_client_free_channel_private_keys(keys, keys_count);
1051       }
1052
1053       goto out;
1054     }
1055   }
1056
1057   /* List command */
1058   if (!strcasecmp(argv[3], "list")) {
1059     command = 3;
1060
1061     if (type == 1) {
1062       SilcPrivateMessageKeys keys;
1063       uint32 keys_count;
1064       int k, i, len;
1065       char buf[1024];
1066
1067       keys = silc_client_list_private_message_keys(silc_client, conn, 
1068                                                    &keys_count);
1069       if (!keys)
1070         goto out;
1071
1072       /* list the private message key(s) */
1073       if (nickname[0] == '*') {
1074         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1075                            SILCTXT_PRIVATE_KEY_LIST);
1076         for (k = 0; k < keys_count; k++) {
1077           memset(buf, 0, sizeof(buf));
1078           strncat(buf, "  ", 2);
1079           len = strlen(keys[k].client_entry->nickname);
1080           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
1081           if (len < 30)
1082             for (i = 0; i < 30 - len; i++)
1083               strcat(buf, " ");
1084           strcat(buf, " ");
1085           
1086           len = strlen(keys[k].cipher);
1087           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
1088           if (len < 14)
1089             for (i = 0; i < 14 - len; i++)
1090               strcat(buf, " ");
1091           strcat(buf, " ");
1092
1093           if (keys[k].key)
1094             strcat(buf, "<hidden>");
1095           else
1096             strcat(buf, "*generated*");
1097
1098           silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
1099         }
1100       } else {
1101         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1102                            SILCTXT_PRIVATE_KEY_LIST_NICK,
1103                            client_entry->nickname);
1104         for (k = 0; k < keys_count; k++) {
1105           if (keys[k].client_entry != client_entry)
1106             continue;
1107
1108           memset(buf, 0, sizeof(buf));
1109           strncat(buf, "  ", 2);
1110           len = strlen(keys[k].client_entry->nickname);
1111           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
1112           if (len < 30)
1113             for (i = 0; i < 30 - len; i++)
1114               strcat(buf, " ");
1115           strcat(buf, " ");
1116           
1117           len = strlen(keys[k].cipher);
1118           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
1119           if (len < 14)
1120             for (i = 0; i < 14 - len; i++)
1121               strcat(buf, " ");
1122           strcat(buf, " ");
1123
1124           if (keys[k].key)
1125             strcat(buf, "<hidden>");
1126           else
1127             strcat(buf, "*generated*");
1128
1129           silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
1130         }
1131       }
1132
1133       silc_client_free_private_message_keys(keys, keys_count);
1134
1135     } else if (type == 2) {
1136       SilcChannelPrivateKey *keys;
1137       uint32 keys_count;
1138       int k, i, len;
1139       char buf[1024];
1140
1141       keys = silc_client_list_channel_private_keys(silc_client, conn, 
1142                                                    channel_entry,
1143                                                    &keys_count);
1144
1145       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1146                          SILCTXT_CH_PRIVATE_KEY_LIST,
1147                          channel_entry->channel_name);
1148
1149       if (!keys)
1150         goto out;
1151       
1152       for (k = 0; k < keys_count; k++) {
1153         memset(buf, 0, sizeof(buf));
1154         strncat(buf, "  ", 2);
1155
1156         len = strlen(keys[k]->cipher->cipher->name);
1157         strncat(buf, keys[k]->cipher->cipher->name, len > 16 ? 16 : len);
1158         if (len < 16)
1159           for (i = 0; i < 16 - len; i++)
1160             strcat(buf, " ");
1161         strcat(buf, " ");
1162         
1163         len = strlen(silc_hmac_get_name(keys[k]->hmac));
1164         strncat(buf, silc_hmac_get_name(keys[k]->hmac), len > 16 ? 16 : len);
1165         if (len < 16)
1166           for (i = 0; i < 16 - len; i++)
1167             strcat(buf, " ");
1168         strcat(buf, " ");
1169         
1170         strcat(buf, "<hidden>");
1171
1172         silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
1173       }
1174       
1175       silc_client_free_channel_private_keys(keys, keys_count);
1176     }
1177
1178     goto out;
1179   }
1180
1181   /* Send command is used to send key agreement */
1182   if (!strcasecmp(argv[3], "agreement")) {
1183     command = 4;
1184
1185     if (argc >= 5)
1186       hostname = argv[4];
1187     if (argc >= 6)
1188       port = atoi(argv[5]);
1189
1190     internal = silc_calloc(1, sizeof(*internal));
1191     internal->type = type;
1192     internal->server = server;
1193     
1194     if (!hostname) {
1195       if (settings_get_bool("use_auto_addr")) {
1196        
1197         hostname = (char *)settings_get_str("auto_public_ip");
1198
1199 /* If the hostname isn't set, treat this case as if auto_public_ip wasn't
1200  * set.
1201  */
1202         if ((hostname) && (*hostname == '\0')) {
1203            hostname = NULL;
1204         }
1205         else {
1206           bindhost = (char *)settings_get_str("auto_bind_ip");
1207             
1208 /* if the bind_ip isn't set, but the public_ip IS, then assume then
1209  * public_ip is the same value as the bind_ip.
1210  */
1211           if ((bindhost) && (*bindhost == '\0')) {
1212             bindhost = hostname;
1213           }
1214            port = settings_get_int("auto_bind_port");
1215         }
1216       }  /* if use_auto_addr */
1217     }
1218   }
1219
1220   /* Start command is used to start key agreement (after receiving the
1221      key_agreement client operation). */
1222   if (!strcasecmp(argv[3], "negotiate")) {
1223     command = 5;
1224
1225     if (argc >= 5)
1226       hostname = argv[4];
1227     if (argc >= 6)
1228       port = atoi(argv[5]);
1229
1230     internal = silc_calloc(1, sizeof(*internal));
1231     internal->type = type;
1232     internal->server = server;
1233   }
1234
1235   if (command == 0) {
1236     silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO,
1237              "Usage: /KEY msg|channel <nickname|channel> "
1238              "set|unset|agreement|negotiate [<arguments>]");
1239     goto out;
1240   }
1241
1242   if (command == 4 && client_entry) {
1243     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1244                        SILCTXT_KEY_AGREEMENT, argv[2]);
1245     internal->responder = TRUE;
1246     silc_client_send_key_agreement(silc_client, conn, client_entry, hostname, 
1247                                    bindhost, port, 120, keyagr_completion, 
1248                                    internal);
1249     if (!hostname)
1250       silc_free(internal);
1251     goto out;
1252   }
1253
1254   if (command == 5 && client_entry && hostname) {
1255     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1256                        SILCTXT_KEY_AGREEMENT_NEGOTIATE, argv[2]);
1257     internal->responder = FALSE;
1258     silc_client_perform_key_agreement(silc_client, conn, client_entry, 
1259                                       hostname, port, keyagr_completion, 
1260                                       internal);
1261     goto out;
1262   }
1263
1264  out:
1265   silc_free(nickname);
1266 }
1267
1268 /* Lists locally saved client and server public keys. */
1269
1270 static void command_listkeys(const char *data, SILC_SERVER_REC *server,
1271                              WI_ITEM_REC *item)
1272 {
1273
1274 }
1275
1276 void silc_channels_init(void)
1277 {
1278   signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1279   signal_add("server connected", (SIGNAL_FUNC) sig_connected);
1280   signal_add("server quit", (SIGNAL_FUNC) sig_server_quit);
1281
1282   signal_add("silc event join", (SIGNAL_FUNC) event_join);
1283   signal_add("silc event leave", (SIGNAL_FUNC) event_leave);
1284   signal_add("silc event signoff", (SIGNAL_FUNC) event_signoff);
1285   signal_add("silc event topic", (SIGNAL_FUNC) event_topic);
1286   signal_add("silc event invite", (SIGNAL_FUNC) event_invite);
1287   signal_add("silc event nick", (SIGNAL_FUNC) event_nick);
1288   signal_add("silc event cmode", (SIGNAL_FUNC) event_cmode);
1289   signal_add("silc event cumode", (SIGNAL_FUNC) event_cumode);
1290   signal_add("silc event motd", (SIGNAL_FUNC) event_motd);
1291   signal_add("silc event channel_change", (SIGNAL_FUNC) event_channel_change);
1292   signal_add("silc event server_signoff", (SIGNAL_FUNC) event_server_signoff);
1293   signal_add("silc event kick", (SIGNAL_FUNC) event_kick);
1294   signal_add("silc event kill", (SIGNAL_FUNC) event_kill);
1295   
1296   command_bind("part", MODULE_NAME, (SIGNAL_FUNC) command_part);
1297   command_bind("me", MODULE_NAME, (SIGNAL_FUNC) command_me);
1298   command_bind("action", MODULE_NAME, (SIGNAL_FUNC) command_action);
1299   command_bind("notice", MODULE_NAME, (SIGNAL_FUNC) command_notice);
1300   command_bind("away", MODULE_NAME, (SIGNAL_FUNC) command_away);
1301   command_bind("key", MODULE_NAME, (SIGNAL_FUNC) command_key);
1302   command_bind("listkeys", MODULE_NAME, (SIGNAL_FUNC) command_listkeys);
1303
1304   silc_nicklist_init();
1305 }
1306
1307 void silc_channels_deinit(void)
1308 {
1309   signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1310   signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
1311   signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit);
1312
1313   signal_remove("silc event join", (SIGNAL_FUNC) event_join);
1314   signal_remove("silc event leave", (SIGNAL_FUNC) event_leave);
1315   signal_remove("silc event signoff", (SIGNAL_FUNC) event_signoff);
1316   signal_remove("silc event topic", (SIGNAL_FUNC) event_topic);
1317   signal_remove("silc event invite", (SIGNAL_FUNC) event_invite);
1318   signal_remove("silc event nick", (SIGNAL_FUNC) event_nick);
1319   signal_remove("silc event cmode", (SIGNAL_FUNC) event_cmode);
1320   signal_remove("silc event cumode", (SIGNAL_FUNC) event_cumode);
1321   signal_remove("silc event motd", (SIGNAL_FUNC) event_motd);
1322   signal_remove("silc event channel_change", 
1323                 (SIGNAL_FUNC) event_channel_change);
1324   signal_remove("silc event server_signoff", 
1325                 (SIGNAL_FUNC) event_server_signoff);
1326   signal_remove("silc event kick", (SIGNAL_FUNC) event_kick);
1327   signal_remove("silc event kill", (SIGNAL_FUNC) event_kill);
1328   
1329   command_unbind("part", (SIGNAL_FUNC) command_part);
1330   command_unbind("me", (SIGNAL_FUNC) command_me);
1331   command_unbind("action", (SIGNAL_FUNC) command_action);
1332   command_unbind("notice", (SIGNAL_FUNC) command_notice);
1333   command_unbind("away", (SIGNAL_FUNC) command_away);
1334   command_unbind("key", (SIGNAL_FUNC) command_key);
1335   command_unbind("listkeys", (SIGNAL_FUNC) command_listkeys);
1336
1337   silc_nicklist_deinit();
1338 }