updates.
[silc.git] / apps / irssi / src / silc / core / silc-channels.c
1 /*
2   silc-channels.c : irssi
3
4   Copyright (C) 2000 - 2001 Timo Sirainen
5                             Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2 of the License, or
10   (at your option) any later version.
11   
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16   
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20 */
21
22 #include "module.h"
23
24 #include "net-nonblock.h"
25 #include "net-sendbuffer.h"
26 #include "signals.h"
27 #include "servers.h"
28 #include "commands.h"
29 #include "levels.h"
30 #include "modules.h"
31 #include "rawlog.h"
32 #include "misc.h"
33 #include "settings.h"
34
35 #include "channels-setup.h"
36
37 #include "silc-servers.h"
38 #include "silc-channels.h"
39 #include "silc-queries.h"
40 #include "silc-nicklist.h"
41 #include "window-item-def.h"
42
43 #include "fe-common/core/printtext.h"
44 #include "fe-common/silc/module-formats.h"
45
46 SILC_CHANNEL_REC *silc_channel_create(SILC_SERVER_REC *server,
47                                       const char *name, int automatic)
48 {
49   SILC_CHANNEL_REC *rec;
50
51   g_return_val_if_fail(server == NULL || IS_SILC_SERVER(server), NULL);
52   g_return_val_if_fail(name != NULL, NULL);
53
54   rec = g_new0(SILC_CHANNEL_REC, 1);
55   rec->chat_type = SILC_PROTOCOL;
56   rec->name = g_strdup(name);
57   rec->server = server;
58
59   channel_init((CHANNEL_REC *) rec, automatic);
60   return rec;
61 }
62
63 static void sig_channel_destroyed(SILC_CHANNEL_REC *channel)
64 {
65   if (!IS_SILC_CHANNEL(channel))
66     return;
67
68   if (channel->server != NULL && !channel->left && !channel->kicked) {
69     /* destroying channel record without actually
70        having left the channel yet */
71     silc_command_exec(channel->server, "PART", channel->name);
72   }
73 }
74
75 static void silc_channels_join(SILC_SERVER_REC *server,
76                                const char *channels, int automatic)
77 {
78   char **list, **tmp, *channel;
79   SILC_CHANNEL_REC *chanrec;
80
81   list = g_strsplit(channels, ",", -1);
82   for (tmp = list; *tmp != NULL; tmp++) {
83     channel = **tmp == '#' ? g_strdup(*tmp) :
84       g_strconcat("#", *tmp, NULL);
85
86     chanrec = silc_channel_find(server, channel);
87     if (chanrec) {
88       g_free(channel);
89       continue;
90     }
91
92     silc_command_exec(server, "JOIN", channel);
93     g_free(channel);
94   }
95
96   g_strfreev(list);
97 }
98
99 static void sig_connected(SILC_SERVER_REC *server)
100 {
101   if (IS_SILC_SERVER(server))
102     server->channels_join = (void *) silc_channels_join;
103 }
104
105 /* "server quit" signal from the core to indicate that QUIT command
106    was called. */
107
108 static void sig_server_quit(SILC_SERVER_REC *server, const char *msg)
109 {
110   if (IS_SILC_SERVER(server) && server->conn && server->conn->sock)
111     silc_command_exec(server, "QUIT", msg);
112 }
113
114 /*
115  * "event join". Joined to a channel.
116  */
117
118 SILC_CHANNEL_REC *silc_channel_find_entry(SILC_SERVER_REC *server,
119                                           SilcChannelEntry entry)
120 {
121   GSList *tmp;
122
123   g_return_val_if_fail(IS_SILC_SERVER(server), NULL);
124
125   for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
126     SILC_CHANNEL_REC *rec = tmp->data;
127
128     if (rec->entry == entry)
129       return rec;
130   }
131
132   return NULL;
133 }
134
135 static void event_join(SILC_SERVER_REC *server, va_list va)
136 {
137   SILC_CHANNEL_REC *chanrec;
138   SILC_NICK_REC *nickrec;
139   SilcClientEntry client;
140   SilcChannelEntry channel;
141   char userhost[256];
142
143   client = va_arg(va, SilcClientEntry);
144   channel = va_arg(va, SilcChannelEntry);
145
146   if (client == server->conn->local_entry) {
147     /* You joined to channel */
148     chanrec = silc_channel_find(server, channel->channel_name);
149     if (chanrec != NULL && !chanrec->joined)
150       chanrec->entry = channel;
151   } else {
152     chanrec = silc_channel_find_entry(server, channel);
153     if (chanrec != NULL) {
154       SilcChannelUser user;
155
156       silc_list_start(chanrec->entry->clients);
157       while ((user = silc_list_get(chanrec->entry->clients)) != NULL)
158         if (user->client == client) {
159           nickrec = silc_nicklist_insert(chanrec, user, TRUE);
160           break;
161         }
162     }
163   }
164
165   memset(userhost, 0, sizeof(userhost));
166   if (client->username)
167     snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
168              client->username, client->hostname);
169   signal_emit("message join", 4, server, channel->channel_name,
170               client->nickname,
171               client->username == NULL ? "" : userhost);
172 }
173
174 /*
175  * "event leave". Left a channel.
176  */
177
178 static void event_leave(SILC_SERVER_REC *server, va_list va)
179 {
180   SILC_CHANNEL_REC *chanrec;
181   SILC_NICK_REC *nickrec;
182   SilcClientEntry client;
183   SilcChannelEntry channel;
184   char userhost[256];
185
186   client = va_arg(va, SilcClientEntry);
187   channel = va_arg(va, SilcChannelEntry);
188
189   memset(userhost, 0, sizeof(userhost));
190   if (client->username)
191     snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
192              client->username, client->hostname);
193   signal_emit("message part", 5, server, channel->channel_name,
194               client->nickname,  client->username ?  userhost : "", 
195               client->nickname);
196
197   chanrec = silc_channel_find_entry(server, channel);
198   if (chanrec != NULL) {
199     nickrec = silc_nicklist_find(chanrec, client);
200     if (nickrec != NULL)
201       nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
202   }
203 }
204
205 /*
206  * "event signoff". Left the network.
207  */
208
209 static void event_signoff(SILC_SERVER_REC *server, va_list va)
210 {
211   SilcClientEntry client;
212   GSList *nicks, *tmp;
213   char *message;
214   char userhost[256];
215
216   client = va_arg(va, SilcClientEntry);
217   message = va_arg(va, char *);
218
219   silc_server_free_ftp(server, client);
220
221   memset(userhost, 0, sizeof(userhost));
222   if (client->username)
223     snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
224              client->username, client->hostname);
225   signal_emit("message quit", 4, server, client->nickname,
226               client->username ? userhost : "", 
227               message ? message : "");
228
229   nicks = nicklist_get_same_unique(SERVER(server), client);
230   for (tmp = nicks; tmp != NULL; tmp = tmp->next->next) {
231     CHANNEL_REC *channel = tmp->data;
232     NICK_REC *nickrec = tmp->next->data;
233     
234     nicklist_remove(channel, nickrec);
235   }
236 }
237
238 /*
239  * "event topic". Changed topic.
240  */
241
242 static void event_topic(SILC_SERVER_REC *server, va_list va)
243 {
244   SILC_CHANNEL_REC *chanrec;
245   SilcClientEntry client;
246   SilcChannelEntry channel;
247   char *topic;
248   char userhost[256];
249
250   client = va_arg(va, SilcClientEntry);
251   topic = va_arg(va, char *);
252   channel = va_arg(va, SilcChannelEntry);
253
254   silc_server_free_ftp(server, client);
255
256   chanrec = silc_channel_find_entry(server, channel);
257   if (chanrec != NULL) {
258     g_free_not_null(chanrec->topic);
259     chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
260     signal_emit("channel topic changed", 1, chanrec);
261   }
262
263   memset(userhost, 0, sizeof(userhost));
264   snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
265            client->username, client->hostname);
266   signal_emit("message topic", 5, server, channel->channel_name,
267               topic, client->nickname, userhost);
268 }
269
270 /*
271  * "event invite". Invited or modified invite list.
272  */
273
274 static void event_invite(SILC_SERVER_REC *server, va_list va)
275 {
276   SilcClientEntry client;
277   SilcChannelEntry channel;
278   char *channel_name;
279   char userhost[256];
280   
281   channel = va_arg(va, SilcChannelEntry);
282   channel_name = va_arg(va, char *);
283   client = va_arg(va, SilcClientEntry);
284
285   memset(userhost, 0, sizeof(userhost));
286   snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
287            client->username, client->hostname);
288   signal_emit("message invite", 4, server, channel ? channel->channel_name :
289               channel_name, client->nickname, userhost);
290 }
291
292 /*
293  * "event nick". Changed nickname.
294  */
295
296 static void event_nick(SILC_SERVER_REC *server, va_list va)
297 {
298   SilcClientEntry oldclient, newclient;
299   char userhost[256];
300
301   oldclient = va_arg(va, SilcClientEntry);
302   newclient = va_arg(va, SilcClientEntry);
303
304   nicklist_rename_unique(SERVER(server),
305                          oldclient, oldclient->nickname,
306                          newclient, newclient->nickname);
307
308   memset(userhost, 0, sizeof(userhost));
309   snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
310            newclient->username, newclient->hostname);
311   signal_emit("message nick", 4, server, newclient->nickname, 
312               oldclient->nickname, userhost);
313 }
314
315 /*
316  * "event cmode". Changed channel mode.
317  */
318
319 static void event_cmode(SILC_SERVER_REC *server, va_list va)
320 {
321   SILC_CHANNEL_REC *chanrec;
322   void *entry;
323   SilcClientEntry client;
324   SilcServerEntry server_entry;
325   SilcChannelEntry channel;
326   char *mode;
327   uint32 modei;
328   SilcIdType idtype;
329
330   idtype = va_arg(va, int);
331   entry = va_arg(va, void *);
332   modei = va_arg(va, uint32);
333   (void)va_arg(va, char *);
334   (void)va_arg(va, char *);
335   channel = va_arg(va, SilcChannelEntry);
336
337   mode = silc_client_chmode(modei, 
338                             channel->channel_key->cipher->name,
339                             silc_hmac_get_name(channel->hmac));
340   
341   chanrec = silc_channel_find_entry(server, channel);
342   if (chanrec != NULL) {
343     g_free_not_null(chanrec->mode);
344     chanrec->mode = g_strdup(mode == NULL ? "" : mode);
345     signal_emit("channel mode changed", 1, chanrec);
346   }
347   
348   if (idtype == SILC_ID_CLIENT) {
349     client = (SilcClientEntry)entry;
350     printformat_module("fe-common/silc", server, channel->channel_name,
351                        MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
352                        channel->channel_name, mode ? mode : "removed all",
353                        client->nickname);
354   } else {
355     server_entry = (SilcServerEntry)entry;
356     printformat_module("fe-common/silc", server, channel->channel_name,
357                        MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
358                        channel->channel_name, mode ? mode : "removed all",
359                        server_entry->server_name);
360   }
361   
362   g_free(mode);
363 }
364
365 /*
366  * "event cumode". Changed user's mode on channel.
367  */
368
369 static void event_cumode(SILC_SERVER_REC *server, va_list va)
370 {
371   SILC_CHANNEL_REC *chanrec;
372   SilcClientEntry client, destclient;
373   SilcChannelEntry channel;
374   int mode;
375   char *modestr;
376   
377   client = va_arg(va, SilcClientEntry);
378   mode = va_arg(va, uint32);
379   destclient = va_arg(va, SilcClientEntry);
380   channel = va_arg(va, SilcChannelEntry);
381   
382   modestr = silc_client_chumode(mode);
383   chanrec = silc_channel_find_entry(server, channel);
384   if (chanrec != NULL) {
385     SILC_NICK_REC *nick;
386     
387     if (destclient == server->conn->local_entry) {
388       chanrec->chanop =
389         (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
390     }
391
392     nick = silc_nicklist_find(chanrec, destclient);
393     if (nick != NULL) {
394       nick->op = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
395       nick->founder = (mode & SILC_CHANNEL_UMODE_CHANFO) != 0;
396       signal_emit("nick mode changed", 2, chanrec, nick);
397     }
398   }
399   
400   printformat_module("fe-common/silc", server, channel->channel_name,
401                      MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
402                      channel->channel_name, destclient->nickname, 
403                      modestr ? modestr : "removed all",
404                      client->nickname);
405
406   if (mode & SILC_CHANNEL_UMODE_CHANFO)
407     printformat_module("fe-common/silc", 
408                        server, channel->channel_name, MSGLEVEL_CRAP,
409                        SILCTXT_CHANNEL_FOUNDER,
410                        channel->channel_name, destclient->nickname);
411
412   g_free(modestr);
413 }
414
415 /*
416  * "event motd". Received MOTD.
417  */
418
419 static void event_motd(SILC_SERVER_REC *server, va_list va)
420 {
421   char *text = va_arg(va, char *);
422
423   if (!settings_get_bool("skip_motd"))
424     printtext_multiline(server, NULL, MSGLEVEL_CRAP, "%s", text);
425 }
426
427 /*
428  * "event channel_change". Channel ID has changed.
429  */
430
431 static void event_channel_change(SILC_SERVER_REC *server, va_list va)
432 {
433   /* Nothing interesting to do */
434 }
435
436 /*
437  * "event server_signoff". Server has quit the network.
438  */
439
440 static void event_server_signoff(SILC_SERVER_REC *server, va_list va)
441 {
442   SilcClientEntry *clients;
443   uint32 clients_count;
444   int i;
445   char userhost[256];
446   
447   (void)va_arg(va, void *);
448   clients = va_arg(va, SilcClientEntry *);
449   clients_count = va_arg(va, uint32);
450   
451   for (i = 0; i < clients_count; i++) {
452     memset(userhost, 0, sizeof(userhost));
453     if (clients[i]->username)
454       snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
455                clients[i]->username, clients[i]->hostname);
456     signal_emit("message quit", 4, server, clients[i]->nickname,
457                 clients[i]->username ? userhost : "", 
458                 "server signoff");
459   }
460 }
461
462 /*
463  * "event kick". Someone was kicked from channel.
464  */
465
466 static void event_kick(SILC_SERVER_REC *server, va_list va)
467 {
468   SilcClientConnection conn = server->conn;
469   SilcClientEntry client_entry;
470   SilcChannelEntry channel_entry;
471   char *tmp;
472   SILC_CHANNEL_REC *chanrec;
473
474   client_entry = va_arg(va, SilcClientEntry);
475   tmp = va_arg(va, char *);
476   channel_entry = va_arg(va, SilcChannelEntry);
477
478   chanrec = silc_channel_find_entry(server, channel_entry);
479   
480   if (client_entry == conn->local_entry) {
481     printformat_module("fe-common/silc", server, channel_entry->channel_name,
482                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED_YOU, 
483                        channel_entry->channel_name, tmp ? tmp : "");
484     if (chanrec) {
485       chanrec->kicked = TRUE;
486       channel_destroy((CHANNEL_REC *)chanrec);
487     }
488   } else {
489     printformat_module("fe-common/silc", server, channel_entry->channel_name,
490                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED, 
491                        client_entry->nickname,
492                        channel_entry->channel_name, tmp ? tmp : "");
493
494     if (chanrec) {
495       SILC_NICK_REC *nickrec = silc_nicklist_find(chanrec, client_entry);
496       if (nickrec != NULL)
497         nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
498     }
499   }
500 }
501
502 /*
503  * "event kill". Someone was killed from the network.
504  */
505
506 static void event_kill(SILC_SERVER_REC *server, va_list va)
507 {
508   SilcClientConnection conn = server->conn;
509   SilcClientEntry client_entry;
510   char *tmp;
511
512   client_entry = va_arg(va, SilcClientEntry);
513   tmp = va_arg(va, char *);
514   
515   if (client_entry == conn->local_entry) {
516     printformat_module("fe-common/silc", server, NULL,
517                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU, 
518                        tmp ? tmp : "");
519   } else {
520     GSList *nicks, *tmpn;
521     nicks = nicklist_get_same_unique(SERVER(server), client_entry);
522     for (tmpn = nicks; tmpn != NULL; tmpn = tmpn->next->next) {
523       CHANNEL_REC *channel = tmpn->data;
524       NICK_REC *nickrec = tmpn->next->data;
525       nicklist_remove(channel, nickrec);
526     }
527
528     printformat_module("fe-common/silc", server, NULL,
529                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED, 
530                        client_entry->nickname,
531                        tmp ? tmp : "");
532   }
533 }
534
535 /* PART (LEAVE) command. */
536
537 static void command_part(const char *data, SILC_SERVER_REC *server,
538                          WI_ITEM_REC *item)
539 {
540   SILC_CHANNEL_REC *chanrec;
541   char userhost[256];
542   
543   if (!IS_SILC_SERVER(server) || !server->connected)
544     cmd_return_error(CMDERR_NOT_CONNECTED);
545
546   if (!strcmp(data, "*") || *data == '\0') {
547     if (!IS_SILC_CHANNEL(item))
548       cmd_return_error(CMDERR_NOT_JOINED);
549     data = item->name;
550   }
551
552   chanrec = silc_channel_find(server, data);
553   if (chanrec == NULL) 
554     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
555
556   memset(userhost, 0, sizeof(userhost));
557   snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
558            server->conn->local_entry->username, 
559            server->conn->local_entry->hostname);
560   signal_emit("message part", 5, server, chanrec->name,
561               server->nick, userhost, "");
562   
563   silc_command_exec(server, "LEAVE", chanrec->name);
564   signal_stop();
565   
566   channel_destroy(CHANNEL(chanrec));
567 }
568
569 /* ME local command. */
570
571 static void command_me(const char *data, SILC_SERVER_REC *server,
572                        WI_ITEM_REC *item)
573 {
574   SILC_CHANNEL_REC *chanrec;
575   char *tmpcmd = "ME", *tmp;
576   uint32 argc = 0;
577   unsigned char **argv;
578   uint32 *argv_lens, *argv_types;
579   int i;
580  
581   if (!IS_SILC_SERVER(server) || !server->connected)
582     cmd_return_error(CMDERR_NOT_CONNECTED);
583
584   if (!IS_SILC_CHANNEL(item))
585     cmd_return_error(CMDERR_NOT_JOINED);
586
587   /* Now parse all arguments */
588   tmp = g_strconcat(tmpcmd, " ", data, NULL);
589   silc_parse_command_line(tmp, &argv, &argv_lens,
590                           &argv_types, &argc, 2);
591   g_free(tmp);
592
593   if (argc < 2)
594     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
595
596   chanrec = silc_channel_find(server, item->name);
597   if (chanrec == NULL) 
598     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
599
600   /* Send the action message */
601   silc_client_send_channel_message(silc_client, server->conn, 
602                                    chanrec->entry, NULL,
603                                    SILC_MESSAGE_FLAG_ACTION, 
604                                    argv[1], argv_lens[1], TRUE);
605
606   printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
607                      MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_OWNACTION, 
608                      server->conn->local_entry->nickname, argv[1]);
609
610   for (i = 0; i < argc; i++)
611     silc_free(argv[i]);
612   silc_free(argv_lens);
613   silc_free(argv_types);
614 }
615
616 /* ACTION local command. Same as ME but takes the channel as mandatory
617    argument. */
618
619 static void command_action(const char *data, SILC_SERVER_REC *server,
620                            WI_ITEM_REC *item)
621 {
622   SILC_CHANNEL_REC *chanrec;
623   char *tmpcmd = "ME", *tmp;
624   uint32 argc = 0;
625   unsigned char **argv;
626   uint32 *argv_lens, *argv_types;
627   int i;
628  
629   if (!IS_SILC_SERVER(server) || !server->connected)
630     cmd_return_error(CMDERR_NOT_CONNECTED);
631
632   if (!IS_SILC_CHANNEL(item))
633     cmd_return_error(CMDERR_NOT_JOINED);
634
635   /* Now parse all arguments */
636   tmp = g_strconcat(tmpcmd, " ", data, NULL);
637   silc_parse_command_line(tmp, &argv, &argv_lens,
638                           &argv_types, &argc, 3);
639   g_free(tmp);
640
641   if (argc < 3)
642     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
643
644   chanrec = silc_channel_find(server, argv[1]);
645   if (chanrec == NULL) 
646     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
647
648   /* Send the action message */
649   silc_client_send_channel_message(silc_client, server->conn, 
650                                    chanrec->entry, NULL,
651                                    SILC_MESSAGE_FLAG_ACTION, 
652                                    argv[2], argv_lens[2], TRUE);
653
654   printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
655                      MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_OWNACTION, 
656                      server->conn->local_entry->nickname, argv[2]);
657
658   for (i = 0; i < argc; i++)
659     silc_free(argv[i]);
660   silc_free(argv_lens);
661   silc_free(argv_types);
662 }
663
664 /* NOTICE local command. */
665
666 static void command_notice(const char *data, SILC_SERVER_REC *server,
667                            WI_ITEM_REC *item)
668 {
669   SILC_CHANNEL_REC *chanrec;
670   char *tmpcmd = "ME", *tmp;
671   uint32 argc = 0;
672   unsigned char **argv;
673   uint32 *argv_lens, *argv_types;
674   int i;
675  
676   if (!IS_SILC_SERVER(server) || !server->connected)
677     cmd_return_error(CMDERR_NOT_CONNECTED);
678
679   if (!IS_SILC_CHANNEL(item))
680     cmd_return_error(CMDERR_NOT_JOINED);
681
682   /* Now parse all arguments */
683   tmp = g_strconcat(tmpcmd, " ", data, NULL);
684   silc_parse_command_line(tmp, &argv, &argv_lens,
685                           &argv_types, &argc, 2);
686   g_free(tmp);
687
688   if (argc < 2)
689     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
690
691   chanrec = silc_channel_find(server, item->name);
692   if (chanrec == NULL) 
693     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
694
695   /* Send the action message */
696   silc_client_send_channel_message(silc_client, server->conn, 
697                                    chanrec->entry, NULL,
698                                    SILC_MESSAGE_FLAG_NOTICE, 
699                                    argv[1], argv_lens[1], TRUE);
700
701   printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
702                      MSGLEVEL_NOTICES, SILCTXT_CHANNEL_OWNNOTICE, 
703                      server->conn->local_entry->nickname, argv[1]);
704
705   for (i = 0; i < argc; i++)
706     silc_free(argv[i]);
707   silc_free(argv_lens);
708   silc_free(argv_types);
709 }
710
711 /* AWAY local command.  Sends UMODE command that sets the SILC_UMODE_GONE
712    flag. */
713
714 static void command_away(const char *data, SILC_SERVER_REC *server,
715                          WI_ITEM_REC *item)
716 {
717   bool set;
718
719   if (!IS_SILC_SERVER(server) || !server->connected)
720     cmd_return_error(CMDERR_NOT_CONNECTED);
721
722   if (*data == '\0') {
723     /* Remove any possible away message */
724     silc_client_set_away_message(silc_client, server->conn, NULL);
725     set = FALSE;
726
727     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, 
728                        SILCTXT_UNSET_AWAY);
729   } else {
730     /* Set the away message */
731     silc_client_set_away_message(silc_client, server->conn, (char *)data);
732     set = TRUE;
733
734     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, 
735                        SILCTXT_SET_AWAY, data);
736   }
737
738   signal_emit("away mode changed", 1, server);
739
740   silc_command_exec(server, "UMODE", set ? "+g" : "-g");
741 }
742
743 typedef struct {
744   int type;                     /* 1 = msg, 2 = channel */
745   bool responder;
746   SILC_SERVER_REC *server;
747 } *KeyInternal;
748
749 /* Key agreement callback that is called after the key agreement protocol
750    has been performed. This is called also if error occured during the
751    key agreement protocol. The `key' is the allocated key material and
752    the caller is responsible of freeing it. The `key' is NULL if error
753    has occured. The application can freely use the `key' to whatever
754    purpose it needs. See lib/silcske/silcske.h for the definition of
755    the SilcSKEKeyMaterial structure. */
756
757 static void keyagr_completion(SilcClient client,
758                               SilcClientConnection conn,
759                               SilcClientEntry client_entry,
760                               SilcKeyAgreementStatus status,
761                               SilcSKEKeyMaterial *key,
762                               void *context)
763 {
764   KeyInternal i = (KeyInternal)context;
765
766   switch(status) {
767   case SILC_KEY_AGREEMENT_OK:
768     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
769                        SILCTXT_KEY_AGREEMENT_OK, client_entry->nickname);
770
771     if (i->type == 1) {
772       /* Set the private key for this client */
773       silc_client_del_private_message_key(client, conn, client_entry);
774       silc_client_add_private_message_key_ske(client, conn, client_entry,
775                                               NULL, key, i->responder);
776       printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
777                          SILCTXT_KEY_AGREEMENT_PRIVMSG, 
778                          client_entry->nickname);
779       silc_ske_free_key_material(key);
780     }
781     
782     break;
783     
784   case SILC_KEY_AGREEMENT_ERROR:
785     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
786                        SILCTXT_KEY_AGREEMENT_ERROR, client_entry->nickname);
787     break;
788     
789   case SILC_KEY_AGREEMENT_FAILURE:
790     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
791                        SILCTXT_KEY_AGREEMENT_FAILURE, client_entry->nickname);
792     break;
793     
794   case SILC_KEY_AGREEMENT_TIMEOUT:
795     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
796                        SILCTXT_KEY_AGREEMENT_TIMEOUT, client_entry->nickname);
797     break;
798     
799   default:
800     break;
801   } 
802
803   if (i)
804     silc_free(i);
805 }
806
807 /* Local command KEY. This command is used to set and unset private
808    keys for channels, set and unset private keys for private messages
809    with remote clients and to send key agreement requests and
810    negotiate the key agreement protocol with remote client.  The
811    key agreement is supported only to negotiate private message keys,
812    it currently cannot be used to negotiate private keys for channels,
813    as it is not convenient for that purpose. */
814
815 typedef struct {
816   SILC_SERVER_REC *server;
817   char *data;
818   char *nick;
819   WI_ITEM_REC *item;
820 } *KeyGetClients;
821
822 /* Callback to be called after client information is resolved from the
823    server. */
824
825 static void silc_client_command_key_get_clients(SilcClient client,
826                                                 SilcClientConnection conn,
827                                                 SilcClientEntry *clients,
828                                                 uint32 clients_count,
829                                                 void *context)
830 {
831   KeyGetClients internal = (KeyGetClients)context;
832
833   if (!clients) {
834     printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Unknown nick: %s", 
835               internal->nick);
836     silc_free(internal->data);
837     silc_free(internal->nick);
838     silc_free(internal);
839     return;
840   }
841
842   signal_emit("command key", 3, internal->data, internal->server,
843               internal->item);
844
845   silc_free(internal->data);
846   silc_free(internal->nick);
847   silc_free(internal);
848 }
849
850 static void command_key(const char *data, SILC_SERVER_REC *server,
851                         WI_ITEM_REC *item)
852 {
853   SilcClientConnection conn;
854   SilcClientEntry *entrys, client_entry = NULL;
855   uint32 entry_count;
856   SilcChannelEntry channel_entry = NULL;
857   char *nickname = NULL, *tmp;
858   int command = 0, port = 0, type = 0;
859   char *hostname = NULL;
860   KeyInternal internal = NULL;
861   uint32 argc = 0;
862   unsigned char **argv;
863   uint32 *argv_lens, *argv_types;
864   char *bindhost = NULL;
865  
866   if (!server || !IS_SILC_SERVER(server) || !server->connected)
867     cmd_return_error(CMDERR_NOT_CONNECTED);
868
869   conn = server->conn;
870
871   /* Now parse all arguments */
872   tmp = g_strconcat("KEY", " ", data, NULL);
873   silc_parse_command_line(tmp, &argv, &argv_lens, &argv_types, &argc, 7);
874   g_free(tmp);
875
876   if (argc < 4)
877     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
878
879   /* Get type */
880   if (!strcasecmp(argv[1], "msg"))
881     type = 1;
882   if (!strcasecmp(argv[1], "channel"))
883     type = 2;
884
885   if (type == 0)
886     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
887
888   if (type == 1) {
889     if (argv[2][0] == '*') {
890       nickname = strdup("*");
891     } else {
892       /* Parse the typed nickname. */
893       if (!silc_parse_userfqdn(argv[2], &nickname, NULL)) {
894         printformat_module("fe-common/silc", server, NULL,
895                            MSGLEVEL_CRAP, SILCTXT_BAD_NICK, argv[2]);
896         return;
897       }
898       
899       /* Find client entry */
900       entrys = silc_client_get_clients_local(silc_client, conn, nickname,
901                                              argv[2], &entry_count);
902       if (!entrys) {
903         KeyGetClients inter = silc_calloc(1, sizeof(*inter));
904         inter->server = server;
905         inter->data = strdup(data);
906         inter->nick = strdup(nickname);
907         inter->item = item;
908         silc_client_get_clients(silc_client, conn, nickname, argv[2],
909                                 silc_client_command_key_get_clients, inter);
910         goto out;
911       }
912       client_entry = entrys[0];
913       silc_free(entrys);
914     }
915   }
916
917   if (type == 2) {
918     /* Get channel entry */
919     char *name;
920
921     if (argv[2][0] == '*') {
922       if (!conn->current_channel) {
923         silc_free(nickname);
924         cmd_return_error(CMDERR_NOT_JOINED);
925       }
926       name = conn->current_channel->channel_name;
927     } else {
928       name = argv[2];
929     }
930
931     channel_entry = silc_client_get_channel(silc_client, conn, name);
932     if (!channel_entry) {
933       silc_free(nickname);
934       cmd_return_error(CMDERR_NOT_JOINED);
935     }
936   }
937
938   /* Set command */
939   if (!strcasecmp(argv[3], "set")) {
940     command = 1;
941
942     if (argc >= 5) {
943       if (type == 1 && client_entry) {
944         /* Set private message key */
945         
946         silc_client_del_private_message_key(silc_client, conn, client_entry);
947
948         if (argc >= 6)
949           silc_client_add_private_message_key(silc_client, conn, client_entry,
950                                               argv[5], argv[4],
951                                               argv_lens[4],
952                                               (argv[4][0] == '*' ?
953                                                TRUE : FALSE), FALSE);
954         else
955           silc_client_add_private_message_key(silc_client, conn, client_entry,
956                                               NULL, argv[4],
957                                               argv_lens[4],
958                                               (argv[4][0] == '*' ?
959                                                TRUE : FALSE), FALSE);
960
961         /* Send the key to the remote client so that it starts using it
962            too. */
963         silc_client_send_private_message_key(silc_client, conn, 
964                                              client_entry, TRUE);
965       } else if (type == 2) {
966         /* Set private channel key */
967         char *cipher = NULL, *hmac = NULL;
968
969         if (!(channel_entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
970           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
971                              SILCTXT_CH_PRIVATE_KEY_NOMODE, 
972                              channel_entry->channel_name);
973           goto out;
974         }
975
976         if (argc >= 6)
977           cipher = argv[5];
978         if (argc >= 7)
979           hmac = argv[6];
980
981         if (!silc_client_add_channel_private_key(silc_client, conn, 
982                                                  channel_entry,
983                                                  cipher, hmac,
984                                                  argv[4],
985                                                  argv_lens[4])) {
986           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
987                              SILCTXT_CH_PRIVATE_KEY_ERROR, 
988                              channel_entry->channel_name);
989           goto out;
990         }
991
992         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
993                            SILCTXT_CH_PRIVATE_KEY_ADD, 
994                            channel_entry->channel_name);
995       }
996     }
997
998     goto out;
999   }
1000   
1001   /* Unset command */
1002   if (!strcasecmp(argv[3], "unset")) {
1003     command = 2;
1004
1005     if (type == 1 && client_entry) {
1006       /* Unset private message key */
1007       silc_client_del_private_message_key(silc_client, conn, client_entry);
1008     } else if (type == 2) {
1009       /* Unset channel key(s) */
1010       SilcChannelPrivateKey *keys;
1011       uint32 keys_count;
1012       int number;
1013
1014       if (argc == 4)
1015         silc_client_del_channel_private_keys(silc_client, conn, 
1016                                              channel_entry);
1017
1018       if (argc > 4) {
1019         number = atoi(argv[4]);
1020         keys = silc_client_list_channel_private_keys(silc_client, conn, 
1021                                                      channel_entry,
1022                                                      &keys_count);
1023         if (!keys)
1024           goto out;
1025
1026         if (!number || number > keys_count) {
1027           silc_client_free_channel_private_keys(keys, keys_count);
1028           goto out;
1029         }
1030
1031         silc_client_del_channel_private_key(silc_client, conn, channel_entry,
1032                                             keys[number - 1]);
1033         silc_client_free_channel_private_keys(keys, keys_count);
1034       }
1035
1036       goto out;
1037     }
1038   }
1039
1040   /* List command */
1041   if (!strcasecmp(argv[3], "list")) {
1042     command = 3;
1043
1044     if (type == 1) {
1045       SilcPrivateMessageKeys keys;
1046       uint32 keys_count;
1047       int k, i, len;
1048       char buf[1024];
1049
1050       keys = silc_client_list_private_message_keys(silc_client, conn, 
1051                                                    &keys_count);
1052       if (!keys)
1053         goto out;
1054
1055       /* list the private message key(s) */
1056       if (nickname[0] == '*') {
1057         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1058                            SILCTXT_PRIVATE_KEY_LIST);
1059         for (k = 0; k < keys_count; k++) {
1060           memset(buf, 0, sizeof(buf));
1061           strncat(buf, "  ", 2);
1062           len = strlen(keys[k].client_entry->nickname);
1063           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
1064           if (len < 30)
1065             for (i = 0; i < 30 - len; i++)
1066               strcat(buf, " ");
1067           strcat(buf, " ");
1068           
1069           len = strlen(keys[k].cipher);
1070           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
1071           if (len < 14)
1072             for (i = 0; i < 14 - len; i++)
1073               strcat(buf, " ");
1074           strcat(buf, " ");
1075
1076           if (keys[k].key)
1077             strcat(buf, "<hidden>");
1078           else
1079             strcat(buf, "*generated*");
1080
1081           silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
1082         }
1083       } else {
1084         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1085                            SILCTXT_PRIVATE_KEY_LIST_NICK,
1086                            client_entry->nickname);
1087         for (k = 0; k < keys_count; k++) {
1088           if (keys[k].client_entry != client_entry)
1089             continue;
1090
1091           memset(buf, 0, sizeof(buf));
1092           strncat(buf, "  ", 2);
1093           len = strlen(keys[k].client_entry->nickname);
1094           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
1095           if (len < 30)
1096             for (i = 0; i < 30 - len; i++)
1097               strcat(buf, " ");
1098           strcat(buf, " ");
1099           
1100           len = strlen(keys[k].cipher);
1101           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
1102           if (len < 14)
1103             for (i = 0; i < 14 - len; i++)
1104               strcat(buf, " ");
1105           strcat(buf, " ");
1106
1107           if (keys[k].key)
1108             strcat(buf, "<hidden>");
1109           else
1110             strcat(buf, "*generated*");
1111
1112           silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
1113         }
1114       }
1115
1116       silc_client_free_private_message_keys(keys, keys_count);
1117
1118     } else if (type == 2) {
1119       SilcChannelPrivateKey *keys;
1120       uint32 keys_count;
1121       int k, i, len;
1122       char buf[1024];
1123
1124       keys = silc_client_list_channel_private_keys(silc_client, conn, 
1125                                                    channel_entry,
1126                                                    &keys_count);
1127
1128       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1129                          SILCTXT_CH_PRIVATE_KEY_LIST,
1130                          channel_entry->channel_name);
1131
1132       if (!keys)
1133         goto out;
1134       
1135       for (k = 0; k < keys_count; k++) {
1136         memset(buf, 0, sizeof(buf));
1137         strncat(buf, "  ", 2);
1138
1139         len = strlen(keys[k]->cipher->cipher->name);
1140         strncat(buf, keys[k]->cipher->cipher->name, len > 16 ? 16 : len);
1141         if (len < 16)
1142           for (i = 0; i < 16 - len; i++)
1143             strcat(buf, " ");
1144         strcat(buf, " ");
1145         
1146         len = strlen(silc_hmac_get_name(keys[k]->hmac));
1147         strncat(buf, silc_hmac_get_name(keys[k]->hmac), len > 16 ? 16 : len);
1148         if (len < 16)
1149           for (i = 0; i < 16 - len; i++)
1150             strcat(buf, " ");
1151         strcat(buf, " ");
1152         
1153         strcat(buf, "<hidden>");
1154
1155         silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
1156       }
1157       
1158       silc_client_free_channel_private_keys(keys, keys_count);
1159     }
1160
1161     goto out;
1162   }
1163
1164   /* Send command is used to send key agreement */
1165   if (!strcasecmp(argv[3], "agreement")) {
1166     command = 4;
1167
1168     if (argc >= 5)
1169       hostname = argv[4];
1170     if (argc >= 6)
1171       port = atoi(argv[5]);
1172
1173     internal = silc_calloc(1, sizeof(*internal));
1174     internal->type = type;
1175     internal->server = server;
1176     
1177     if (!hostname) {
1178       if (settings_get_bool("use_auto_addr")) {
1179        
1180         hostname = (char *)settings_get_str("auto_public_ip");
1181
1182 /* If the hostname isn't set, treat this case as if auto_public_ip wasn't
1183  * set.
1184  */
1185         if ((hostname) && (*hostname == '\0')) {
1186            hostname = NULL;
1187         }
1188         else {
1189           bindhost = (char *)settings_get_str("auto_bind_ip");
1190             
1191 /* if the bind_ip isn't set, but the public_ip IS, then assume then
1192  * public_ip is the same value as the bind_ip.
1193  */
1194           if ((bindhost) && (*bindhost == '\0')) {
1195             bindhost = hostname;
1196           }
1197            port = settings_get_int("auto_bind_port");
1198         }
1199       }  /* if use_auto_addr */
1200     }
1201   }
1202
1203   /* Start command is used to start key agreement (after receiving the
1204      key_agreement client operation). */
1205   if (!strcasecmp(argv[3], "negotiate")) {
1206     command = 5;
1207
1208     if (argc >= 5)
1209       hostname = argv[4];
1210     if (argc >= 6)
1211       port = atoi(argv[5]);
1212
1213     internal = silc_calloc(1, sizeof(*internal));
1214     internal->type = type;
1215     internal->server = server;
1216   }
1217
1218   if (command == 0) {
1219     silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO,
1220              "Usage: /KEY msg|channel <nickname|channel> "
1221              "set|unset|agreement|negotiate [<arguments>]");
1222     goto out;
1223   }
1224
1225   if (command == 4 && client_entry) {
1226     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1227                        SILCTXT_KEY_AGREEMENT, argv[2]);
1228     internal->responder = TRUE;
1229     silc_client_send_key_agreement(silc_client, conn, client_entry, hostname, 
1230                                    bindhost, port, 120, keyagr_completion, 
1231                                    internal);
1232     if (!hostname)
1233       silc_free(internal);
1234     goto out;
1235   }
1236
1237   if (command == 5 && client_entry && hostname) {
1238     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1239                        SILCTXT_KEY_AGREEMENT_NEGOTIATE, argv[2]);
1240     internal->responder = FALSE;
1241     silc_client_perform_key_agreement(silc_client, conn, client_entry, 
1242                                       hostname, port, keyagr_completion, 
1243                                       internal);
1244     goto out;
1245   }
1246
1247  out:
1248   silc_free(nickname);
1249 }
1250
1251 /* Lists locally saved client and server public keys. */
1252
1253 static void command_listkeys(const char *data, SILC_SERVER_REC *server,
1254                              WI_ITEM_REC *item)
1255 {
1256
1257 }
1258
1259 void silc_channels_init(void)
1260 {
1261   signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1262   signal_add("server connected", (SIGNAL_FUNC) sig_connected);
1263   signal_add("server quit", (SIGNAL_FUNC) sig_server_quit);
1264
1265   signal_add("silc event join", (SIGNAL_FUNC) event_join);
1266   signal_add("silc event leave", (SIGNAL_FUNC) event_leave);
1267   signal_add("silc event signoff", (SIGNAL_FUNC) event_signoff);
1268   signal_add("silc event topic", (SIGNAL_FUNC) event_topic);
1269   signal_add("silc event invite", (SIGNAL_FUNC) event_invite);
1270   signal_add("silc event nick", (SIGNAL_FUNC) event_nick);
1271   signal_add("silc event cmode", (SIGNAL_FUNC) event_cmode);
1272   signal_add("silc event cumode", (SIGNAL_FUNC) event_cumode);
1273   signal_add("silc event motd", (SIGNAL_FUNC) event_motd);
1274   signal_add("silc event channel_change", (SIGNAL_FUNC) event_channel_change);
1275   signal_add("silc event server_signoff", (SIGNAL_FUNC) event_server_signoff);
1276   signal_add("silc event kick", (SIGNAL_FUNC) event_kick);
1277   signal_add("silc event kill", (SIGNAL_FUNC) event_kill);
1278   
1279   command_bind("part", MODULE_NAME, (SIGNAL_FUNC) command_part);
1280   command_bind("me", MODULE_NAME, (SIGNAL_FUNC) command_me);
1281   command_bind("action", MODULE_NAME, (SIGNAL_FUNC) command_action);
1282   command_bind("notice", MODULE_NAME, (SIGNAL_FUNC) command_notice);
1283   command_bind("away", MODULE_NAME, (SIGNAL_FUNC) command_away);
1284   command_bind("key", MODULE_NAME, (SIGNAL_FUNC) command_key);
1285   command_bind("listkeys", MODULE_NAME, (SIGNAL_FUNC) command_listkeys);
1286
1287   silc_nicklist_init();
1288 }
1289
1290 void silc_channels_deinit(void)
1291 {
1292   signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1293   signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
1294   signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit);
1295
1296   signal_remove("silc event join", (SIGNAL_FUNC) event_join);
1297   signal_remove("silc event leave", (SIGNAL_FUNC) event_leave);
1298   signal_remove("silc event signoff", (SIGNAL_FUNC) event_signoff);
1299   signal_remove("silc event topic", (SIGNAL_FUNC) event_topic);
1300   signal_remove("silc event invite", (SIGNAL_FUNC) event_invite);
1301   signal_remove("silc event nick", (SIGNAL_FUNC) event_nick);
1302   signal_remove("silc event cmode", (SIGNAL_FUNC) event_cmode);
1303   signal_remove("silc event cumode", (SIGNAL_FUNC) event_cumode);
1304   signal_remove("silc event motd", (SIGNAL_FUNC) event_motd);
1305   signal_remove("silc event channel_change", 
1306                 (SIGNAL_FUNC) event_channel_change);
1307   signal_remove("silc event server_signoff", 
1308                 (SIGNAL_FUNC) event_server_signoff);
1309   signal_remove("silc event kick", (SIGNAL_FUNC) event_kick);
1310   signal_remove("silc event kill", (SIGNAL_FUNC) event_kill);
1311   
1312   command_unbind("part", (SIGNAL_FUNC) command_part);
1313   command_unbind("me", (SIGNAL_FUNC) command_me);
1314   command_unbind("action", (SIGNAL_FUNC) command_action);
1315   command_unbind("notice", (SIGNAL_FUNC) command_notice);
1316   command_unbind("away", (SIGNAL_FUNC) command_away);
1317   command_unbind("key", (SIGNAL_FUNC) command_key);
1318   command_unbind("listkeys", (SIGNAL_FUNC) command_listkeys);
1319
1320   silc_nicklist_deinit();
1321 }