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