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