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
80   list = g_strsplit(channels, ",", -1);
81   for (tmp = list; *tmp != NULL; tmp++) {
82     channel = **tmp == '#' ? g_strdup(*tmp) :
83       g_strconcat("#", *tmp, NULL);
84     silc_channel_create(server, channel, FALSE);
85     silc_command_exec(server, "JOIN", channel);
86     g_free(channel);
87   }
88   g_strfreev(list);
89 }
90
91 static void sig_connected(SILC_SERVER_REC *server)
92 {
93   if (IS_SILC_SERVER(server))
94     server->channels_join = (void *) silc_channels_join;
95 }
96
97 /* "server quit" signal from the core to indicate that QUIT command
98    was called. */
99
100 static void sig_server_quit(SILC_SERVER_REC *server, const char *msg)
101 {
102   if (IS_SILC_SERVER(server))
103     silc_command_exec(server, "QUIT", msg);
104 }
105
106 /*
107  * "event join". Joined to a channel.
108  */
109
110 SILC_CHANNEL_REC *silc_channel_find_entry(SILC_SERVER_REC *server,
111                                           SilcChannelEntry entry)
112 {
113   GSList *tmp;
114
115   g_return_val_if_fail(IS_SILC_SERVER(server), NULL);
116
117   for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
118     SILC_CHANNEL_REC *rec = tmp->data;
119
120     if (rec->entry == entry)
121       return rec;
122   }
123
124   return NULL;
125 }
126
127 static void event_join(SILC_SERVER_REC *server, va_list va)
128 {
129   SILC_CHANNEL_REC *chanrec;
130   SILC_NICK_REC *nickrec;
131   SilcClientEntry client;
132   SilcChannelEntry channel;
133
134   client = va_arg(va, SilcClientEntry);
135   channel = va_arg(va, SilcChannelEntry);
136
137   if (client == server->conn->local_entry) {
138     /* You joined to channel */
139     chanrec = silc_channel_find(server, channel->channel_name);
140     if (chanrec != NULL && !chanrec->joined)
141       chanrec->entry = channel;
142   } else {
143     chanrec = silc_channel_find_entry(server, channel);
144     if (chanrec != NULL) {
145       SilcChannelUser user;
146
147       silc_list_start(chanrec->entry->clients);
148       while ((user = silc_list_get(chanrec->entry->clients)) != NULL)
149         if (user->client == client) {
150           nickrec = silc_nicklist_insert(chanrec, user, TRUE);
151           break;
152         }
153     }
154   }
155
156   signal_emit("message join", 4, server, channel->channel_name,
157               client->nickname,
158               client->username == NULL ? "" : client->username);
159 }
160
161 /*
162  * "event leave". Left a channel.
163  */
164
165 static void event_leave(SILC_SERVER_REC *server, va_list va)
166 {
167   SILC_CHANNEL_REC *chanrec;
168   SILC_NICK_REC *nickrec;
169   SilcClientEntry client;
170   SilcChannelEntry channel;
171
172   client = va_arg(va, SilcClientEntry);
173   channel = va_arg(va, SilcChannelEntry);
174
175   signal_emit("message part", 5, server, channel->channel_name,
176               client->nickname,  client->username ?  client->username : "", 
177               client->nickname);
178
179   chanrec = silc_channel_find_entry(server, channel);
180   if (chanrec != NULL) {
181     nickrec = silc_nicklist_find(chanrec, client);
182     if (nickrec != NULL)
183       nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
184   }
185 }
186
187 /*
188  * "event signoff". Left the network.
189  */
190
191 static void event_signoff(SILC_SERVER_REC *server, va_list va)
192 {
193   SilcClientEntry client;
194   GSList *nicks, *tmp;
195   char *message;
196
197   client = va_arg(va, SilcClientEntry);
198   message = va_arg(va, char *);
199
200   signal_emit("message quit", 4, server, client->nickname,
201               client->username ? client->username : "", 
202               message ? message : "");
203
204   nicks = nicklist_get_same_unique(SERVER(server), client);
205   for (tmp = nicks; tmp != NULL; tmp = tmp->next->next) {
206     CHANNEL_REC *channel = tmp->data;
207     NICK_REC *nickrec = tmp->next->data;
208     
209     nicklist_remove(channel, nickrec);
210   }
211 }
212
213 /*
214  * "event topic". Changed topic.
215  */
216
217 static void event_topic(SILC_SERVER_REC *server, va_list va)
218 {
219   SILC_CHANNEL_REC *chanrec;
220   SilcClientEntry client;
221   SilcChannelEntry channel;
222   char *topic;
223
224   client = va_arg(va, SilcClientEntry);
225   topic = va_arg(va, char *);
226   channel = va_arg(va, SilcChannelEntry);
227
228   chanrec = silc_channel_find_entry(server, channel);
229   if (chanrec != NULL) {
230     g_free_not_null(chanrec->topic);
231     chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
232     signal_emit("channel topic changed", 1, chanrec);
233   }
234
235   signal_emit("message topic", 5, server, channel->channel_name,
236               topic, client->nickname, client->username);
237 }
238
239 /*
240  * "event invite". Invited or modified invite list.
241  */
242
243 static void event_invite(SILC_SERVER_REC *server, va_list va)
244 {
245   SilcClientEntry client;
246   SilcChannelEntry channel;
247   
248   client = va_arg(va, SilcClientEntry);
249   channel = va_arg(va, SilcChannelEntry);
250
251   signal_emit("message invite", 4, server, channel->channel_name,
252               client->nickname, client->username);
253 }
254
255 /*
256  * "event nick". Changed nickname.
257  */
258
259 static void event_nick(SILC_SERVER_REC *server, va_list va)
260 {
261   SilcClientEntry oldclient, newclient;
262
263   oldclient = va_arg(va, SilcClientEntry);
264   newclient = va_arg(va, SilcClientEntry);
265
266   nicklist_rename_unique(SERVER(server),
267                          oldclient, oldclient->nickname,
268                          newclient, newclient->nickname);
269
270   signal_emit("message nick", 4, server, newclient->nickname, 
271               oldclient->nickname, newclient->username);
272 }
273
274 /*
275  * "event cmode". Changed channel mode.
276  */
277
278 static void event_cmode(SILC_SERVER_REC *server, va_list va)
279 {
280   SILC_CHANNEL_REC *chanrec;
281   SilcClientEntry client;
282   SilcChannelEntry channel;
283   char *mode;
284   uint32 modei;
285
286   client = va_arg(va, SilcClientEntry);
287   modei = va_arg(va, uint32);
288   (void)va_arg(va, char *);
289   (void)va_arg(va, char *);
290   channel = va_arg(va, SilcChannelEntry);
291
292   mode = silc_client_chmode(modei, channel);
293   
294   chanrec = silc_channel_find_entry(server, channel);
295   if (chanrec != NULL) {
296     g_free_not_null(chanrec->mode);
297     chanrec->mode = g_strdup(mode == NULL ? "" : mode);
298     signal_emit("channel mode changed", 1, chanrec);
299   }
300   
301   printformat_module("fe-common/silc", server, channel->channel_name,
302                      MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
303                      channel->channel_name, mode ? mode : "removed all",
304                      client->nickname);
305   
306   g_free(mode);
307 }
308
309 /*
310  * "event cumode". Changed user's mode on channel.
311  */
312
313 static void event_cumode(SILC_SERVER_REC *server, va_list va)
314 {
315   SILC_CHANNEL_REC *chanrec;
316   SilcClientEntry client, destclient;
317   SilcChannelEntry channel;
318   int mode;
319   char *modestr;
320   
321   client = va_arg(va, SilcClientEntry);
322   mode = va_arg(va, uint32);
323   destclient = va_arg(va, SilcClientEntry);
324   channel = va_arg(va, SilcChannelEntry);
325   
326   modestr = silc_client_chumode(mode);
327   chanrec = silc_channel_find_entry(server, channel);
328   if (chanrec != NULL) {
329     SILC_NICK_REC *nick;
330     
331     if (destclient == server->conn->local_entry) {
332       chanrec->chanop =
333         (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
334     }
335
336     nick = silc_nicklist_find(chanrec, destclient);
337     if (nick != NULL) {
338       nick->op = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
339       signal_emit("nick mode changed", 2, chanrec, nick);
340     }
341   }
342   
343   printformat_module("fe-common/silc", server, channel->channel_name,
344                      MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
345                      channel->channel_name, destclient->nickname, 
346                      modestr ? modestr : "removed all",
347                      client->nickname);
348
349   if (mode & SILC_CHANNEL_UMODE_CHANFO)
350     printformat_module("fe-common/silc", 
351                        server, channel->channel_name, MSGLEVEL_CRAP,
352                        SILCTXT_CHANNEL_FOUNDER,
353                        channel->channel_name, destclient->nickname);
354
355   g_free(modestr);
356 }
357
358 /*
359  * "event motd". Received MOTD.
360  */
361
362 static void event_motd(SILC_SERVER_REC *server, va_list va)
363 {
364   char *text = va_arg(va, char *);
365
366   if (!settings_get_bool("skip_motd"))
367     printtext_multiline(server, NULL, MSGLEVEL_CRAP, "%s", text);
368 }
369
370 /*
371  * "event channel_change". Channel ID has changed.
372  */
373
374 static void event_channel_change(SILC_SERVER_REC *server, va_list va)
375 {
376
377 }
378
379 /*
380  * "event server_signoff". Server has quit the network.
381  */
382
383 static void event_server_signoff(SILC_SERVER_REC *server, va_list va)
384 {
385
386 }
387
388 /*
389  * "event kick". Someone was kicked from channel.
390  */
391
392 static void event_kick(SILC_SERVER_REC *server, va_list va)
393 {
394
395 }
396
397 /*
398  * "event kill". Someone was killed from the network.
399  */
400
401 static void event_kill(SILC_SERVER_REC *server, va_list va)
402 {
403
404 }
405
406 /*
407  * "event ban". Someone was banned or ban list was modified.
408  */
409
410 static void event_ban(SILC_SERVER_REC *server, va_list va)
411 {
412
413 }
414
415 /* PART (LEAVE) command. */
416
417 static void command_part(const char *data, SILC_SERVER_REC *server,
418                          WI_ITEM_REC *item)
419 {
420   SILC_CHANNEL_REC *chanrec;
421   
422   if (!IS_SILC_SERVER(server) || !server->connected)
423     cmd_return_error(CMDERR_NOT_CONNECTED);
424
425   if (*data == '\0') {
426     if (!IS_SILC_CHANNEL(item))
427       cmd_return_error(CMDERR_NOT_JOINED);
428     data = item->name;
429   }
430
431   chanrec = silc_channel_find(server, data);
432   if (chanrec == NULL) 
433     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
434
435   signal_emit("message part", 5, server, chanrec->name,
436               server->nick, server->conn->local_entry->username, "");
437   
438   silc_command_exec(server, "LEAVE", chanrec->name);
439   signal_stop();
440   
441   channel_destroy(CHANNEL(chanrec));
442 }
443
444 /* ME local command. */
445
446 static void command_me(const char *data, SILC_SERVER_REC *server,
447                        WI_ITEM_REC *item)
448 {
449   SILC_CHANNEL_REC *chanrec;
450   char *tmpcmd = "ME", *tmp;
451   uint32 argc = 0;
452   unsigned char **argv;
453   uint32 *argv_lens, *argv_types;
454   int i;
455  
456   if (!IS_SILC_SERVER(server) || !server->connected)
457     cmd_return_error(CMDERR_NOT_CONNECTED);
458
459   if (!IS_SILC_CHANNEL(item))
460     cmd_return_error(CMDERR_NOT_JOINED);
461
462   /* Now parse all arguments */
463   tmp = g_strconcat(tmpcmd, " ", data, NULL);
464   silc_parse_command_line(tmp, &argv, &argv_lens,
465                           &argv_types, &argc, 2);
466   g_free(tmp);
467
468   if (argc < 2)
469     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
470
471   chanrec = silc_channel_find(server, item->name);
472   if (chanrec == NULL) 
473     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
474
475   /* Send the action message */
476   silc_client_send_channel_message(silc_client, server->conn, 
477                                    chanrec->entry, NULL,
478                                    SILC_MESSAGE_FLAG_ACTION, 
479                                    argv[1], argv_lens[1], TRUE);
480
481   printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
482                      MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_OWNACTION, argv[1]);
483
484   for (i = 0; i < argc; i++)
485     silc_free(argv[i]);
486   silc_free(argv_lens);
487   silc_free(argv_types);
488 }
489
490 /* NOTICE local command. */
491
492 static void command_notice(const char *data, SILC_SERVER_REC *server,
493                            WI_ITEM_REC *item)
494 {
495   SILC_CHANNEL_REC *chanrec;
496   char *tmpcmd = "ME", *tmp;
497   uint32 argc = 0;
498   unsigned char **argv;
499   uint32 *argv_lens, *argv_types;
500   int i;
501  
502   if (!IS_SILC_SERVER(server) || !server->connected)
503     cmd_return_error(CMDERR_NOT_CONNECTED);
504
505   if (!IS_SILC_CHANNEL(item))
506     cmd_return_error(CMDERR_NOT_JOINED);
507
508   /* Now parse all arguments */
509   tmp = g_strconcat(tmpcmd, " ", data, NULL);
510   silc_parse_command_line(tmp, &argv, &argv_lens,
511                           &argv_types, &argc, 2);
512   g_free(tmp);
513
514   if (argc < 2)
515     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
516
517   chanrec = silc_channel_find(server, item->name);
518   if (chanrec == NULL) 
519     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
520
521   /* Send the action message */
522   silc_client_send_channel_message(silc_client, server->conn, 
523                                    chanrec->entry, NULL,
524                                    SILC_MESSAGE_FLAG_NOTICE, 
525                                    argv[1], argv_lens[1], TRUE);
526
527   printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
528                      MSGLEVEL_NOTICES, SILCTXT_CHANNEL_OWNNOTICE, argv[1]);
529
530   for (i = 0; i < argc; i++)
531     silc_free(argv[i]);
532   silc_free(argv_lens);
533   silc_free(argv_types);
534 }
535
536 /* AWAY local command.  Sends UMODE command that sets the SILC_UMODE_GONE
537    flag. */
538
539 static void command_away(const char *data, SILC_SERVER_REC *server,
540                          WI_ITEM_REC *item)
541 {
542   if (!IS_SILC_SERVER(server) || !server->connected)
543     cmd_return_error(CMDERR_NOT_CONNECTED);
544
545   /* XXX TODO */
546 }
547
548 void silc_channels_init(void)
549 {
550   signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
551   signal_add("server connected", (SIGNAL_FUNC) sig_connected);
552   signal_add("server quit", (SIGNAL_FUNC) sig_server_quit);
553
554   signal_add("silc event join", (SIGNAL_FUNC) event_join);
555   signal_add("silc event leave", (SIGNAL_FUNC) event_leave);
556   signal_add("silc event signoff", (SIGNAL_FUNC) event_signoff);
557   signal_add("silc event topic", (SIGNAL_FUNC) event_topic);
558   signal_add("silc event invite", (SIGNAL_FUNC) event_invite);
559   signal_add("silc event nick", (SIGNAL_FUNC) event_nick);
560   signal_add("silc event cmode", (SIGNAL_FUNC) event_cmode);
561   signal_add("silc event cumode", (SIGNAL_FUNC) event_cumode);
562   signal_add("silc event motd", (SIGNAL_FUNC) event_motd);
563   signal_add("silc event channel_change", (SIGNAL_FUNC) event_channel_change);
564   signal_add("silc event server_signoff", (SIGNAL_FUNC) event_server_signoff);
565   signal_add("silc event kick", (SIGNAL_FUNC) event_kick);
566   signal_add("silc event kill", (SIGNAL_FUNC) event_kill);
567   signal_add("silc event ban", (SIGNAL_FUNC) event_ban);
568   
569   command_bind("part", MODULE_NAME, (SIGNAL_FUNC) command_part);
570   command_bind("me", MODULE_NAME, (SIGNAL_FUNC) command_me);
571   command_bind("notice", MODULE_NAME, (SIGNAL_FUNC) command_notice);
572   command_bind("away", MODULE_NAME, (SIGNAL_FUNC) command_away);
573
574   silc_nicklist_init();
575 }
576
577 void silc_channels_deinit(void)
578 {
579   signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
580   signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
581   signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit);
582
583   signal_remove("silc event join", (SIGNAL_FUNC) event_join);
584   signal_remove("silc event leave", (SIGNAL_FUNC) event_leave);
585   signal_remove("silc event signoff", (SIGNAL_FUNC) event_signoff);
586   signal_remove("silc event topic", (SIGNAL_FUNC) event_topic);
587   signal_remove("silc event invite", (SIGNAL_FUNC) event_invite);
588   signal_remove("silc event nick", (SIGNAL_FUNC) event_nick);
589   signal_remove("silc event cmode", (SIGNAL_FUNC) event_cmode);
590   signal_remove("silc event cumode", (SIGNAL_FUNC) event_cumode);
591   signal_remove("silc event motd", (SIGNAL_FUNC) event_motd);
592   signal_remove("silc event channel_change", 
593                 (SIGNAL_FUNC) event_channel_change);
594   signal_remove("silc event server_signoff", 
595                 (SIGNAL_FUNC) event_server_signoff);
596   signal_remove("silc event kick", (SIGNAL_FUNC) event_kick);
597   signal_remove("silc event kill", (SIGNAL_FUNC) event_kill);
598   signal_remove("silc event ban", (SIGNAL_FUNC) event_ban);
599   
600   command_unbind("part", (SIGNAL_FUNC) command_part);
601   command_unbind("me", (SIGNAL_FUNC) command_me);
602   command_unbind("notice", (SIGNAL_FUNC) command_notice);
603   command_unbind("away", (SIGNAL_FUNC) command_away);
604
605   silc_nicklist_deinit();
606 }