ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/src/channels.C
Revision: 1.9
Committed: Sat Sep 22 14:27:30 2007 UTC (16 years, 8 months ago) by pippijn
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.8: +2 -2 lines
Log Message:
split up ermyth into ermyth-modules, libermyth (currently just ermyth-util) and ermyth-core

File Contents

# User Rev Content
1 pippijn 1.1 /*
2     * channels.C: Channel event and state tracking
3 pippijn 1.8 *
4     * Copyright © 2007 Pippijn van Steenhoven / The Ermyth Team
5     * Rights to this code are as documented in COPYING.
6     *
7     *
8     * Portions of this file were derived from sources bearing the following license:
9 pippijn 1.2 * Rights to this code are documented in doc/pod/license.pod.
10 pippijn 1.4 * Copyright © 2005-2007 Atheme Project (http://www.atheme.org)
11 pippijn 1.1 */
12    
13 pippijn 1.9 static char const rcsid[] = "$Id: channels.C,v 1.8 2007-09-16 18:54:44 pippijn Exp $";
14 pippijn 1.1
15     #include "atheme.h"
16 pippijn 1.7 #include "servers.h"
17 pippijn 1.1 #include <account/mychan.h>
18    
19 pippijn 1.5 channel_map chanlist;
20 pippijn 1.4 channel_t::callbacks channel_t::callback;
21     chanuser_t::callbacks chanuser_t::callback;
22 pippijn 1.1
23     /*
24     * init_channels()
25     *
26     * Initializes the channel-related heaps and DTree structures.
27     *
28     * Inputs:
29     * - nothing
30     *
31     * Outputs:
32     * - nothing
33     *
34     * Side Effects:
35     * - if the heaps or DTrees fail to initialize, the program will abort.
36     */
37     void
38     init_channels (void)
39     {
40 pippijn 1.4 #if 0
41 pippijn 1.1 chan_heap = BlockHeapCreate (sizeof (channel_t), HEAP_CHANNEL);
42     chanuser_heap = BlockHeapCreate (sizeof (chanuser_t), HEAP_CHANUSER);
43     chanban_heap = BlockHeapCreate (sizeof (chanban_t), HEAP_CHANUSER);
44 pippijn 1.4 #endif
45 pippijn 1.1 }
46    
47     /*
48 pippijn 1.4 * channel_add(char const * const name, unsigned int ts, server_t *creator)
49 pippijn 1.1 *
50     * Channel object factory.
51     *
52     * Inputs:
53     * - channel name
54     * - timestamp of channel creation
55     * - server that is creating the channel
56     *
57     * Outputs:
58     * - on success, a channel object
59     * - on failure, NULL
60     *
61     * Side Effects:
62     * - the channel is automatically inserted into the channel DTree
63     * - if the creator is not me.me:
64     * - channel_add hook is called
65     * - all services are joined if this is the snoop channel
66     * - if the creator is me.me these actions must be performed by the
67     * caller (i.e. join()) after joining the service
68     */
69     channel_t *
70 pippijn 1.4 channel_add (char const * const name, unsigned int ts, server_t *creator)
71 pippijn 1.1 {
72     channel_t *c;
73     mychan_t *mc;
74    
75     if (*name != '#')
76     {
77     slog (LG_DEBUG, "channel_add(): got non #channel: %s", name);
78     return NULL;
79     }
80    
81     c = channel_find (name);
82    
83     if (c)
84     {
85     slog (LG_DEBUG, "channel_add(): channel already exists: %s", name);
86     return c;
87     }
88    
89     slog (LG_DEBUG, "channel_add(): %s by %s", name, creator->name);
90    
91 pippijn 1.4 c = new channel_t;
92 pippijn 1.1
93     c->name = sstrdup (name);
94     c->ts = ts;
95    
96     c->topic = NULL;
97     c->topic_setter = NULL;
98    
99     c->bans.head = NULL;
100     c->bans.tail = NULL;
101     c->bans.count = 0;
102    
103 pippijn 1.6 if ((mc = mychan_t::find (c->name)))
104 pippijn 1.1 mc->chan = c;
105    
106 pippijn 1.5 chanlist[c->name] = c;
107 pippijn 1.1
108     cnt.chan++;
109    
110     if (creator != me.me)
111     {
112 pippijn 1.4 c->callback.add (c);
113 pippijn 1.1
114     if (config_options.chan != NULL && !irccasecmp (config_options.chan, name))
115     joinall (config_options.chan);
116     }
117    
118     return c;
119     }
120    
121     /*
122     * channel_delete(channel_t *c)
123     *
124     * Destroys a channel object and its children member objects.
125     *
126     * Inputs:
127     * - channel object to destroy
128     *
129     * Outputs:
130     * - nothing
131     *
132     * Side Effects:
133     * - channel_delete hook is called
134     * - a channel and all attached structures are destroyed
135     * - no protocol messages are sent for any remaining members
136     */
137     void
138     channel_delete (channel_t *c)
139     {
140     mychan_t *mc;
141     node_t *n, *tn;
142     chanuser_t *cu;
143    
144     return_if_fail (c != NULL);
145    
146     slog (LG_DEBUG, "channel_delete(): %s", c->name);
147    
148     modestack_finalize_channel (c);
149    
150     /* If this is called from uplink_close(), there may still be services
151     * in the channel. Remove them. Calling chanuser_delete() could lead
152     * to a recursive call, so don't do that.
153     * -- jilles */
154     LIST_FOREACH_SAFE (n, tn, c->members.head)
155     {
156     cu = static_cast<chanuser_t *> (n->data);
157     soft_assert (is_internal_client (cu->user) && !me.connected);
158     node_del (&cu->cnode, &c->members);
159     node_del (&cu->unode, &cu->user->channels);
160 pippijn 1.4 delete cu;
161 pippijn 1.1 cnt.chanuser--;
162     }
163     c->nummembers = 0;
164    
165 pippijn 1.4 c->callback.remove (c);
166 pippijn 1.1
167 pippijn 1.5 chanlist.erase (c->name);
168 pippijn 1.1
169 pippijn 1.6 if ((mc = mychan_t::find (c->name)))
170 pippijn 1.1 mc->chan = NULL;
171    
172     clear_simple_modes (c);
173     chanban_clear (c);
174    
175 pippijn 1.4 sfree (c->name);
176 pippijn 1.1 if (c->topic != NULL)
177 pippijn 1.4 sfree (c->topic);
178 pippijn 1.1 if (c->topic_setter != NULL)
179 pippijn 1.4 sfree (c->topic_setter);
180 pippijn 1.1
181 pippijn 1.4 delete c;
182 pippijn 1.1
183     cnt.chan--;
184     }
185    
186     /*
187 pippijn 1.4 * channel_find(char const * const name)
188 pippijn 1.1 *
189     * Looks up a channel object.
190     *
191     * Inputs:
192     * - name of channel to look up
193     *
194     * Outputs:
195     * - on success, the channel object
196     * - on failure, NULL
197     *
198     * Side Effects:
199     * - none
200     */
201     channel_t *
202 pippijn 1.4 channel_find (char const * const name)
203 pippijn 1.1 {
204 pippijn 1.5 channel_map::iterator it = chanlist.find (name);
205     if (it == chanlist.end ())
206     return NULL;
207    
208     return it->second;
209 pippijn 1.1 }
210    
211     /*
212 pippijn 1.4 * chanban_add(channel_t *c, char const * const mask, int type)
213 pippijn 1.1 *
214     * Channel ban factory.
215     *
216     * Inputs:
217     * - channel that the ban belongs to
218     * - banmask
219     * - type of ban, e.g. 'b' or 'e'
220     *
221     * Outputs:
222     * - on success, a new channel ban object
223     * - on failure, NULL
224     *
225     * Side Effects:
226     * - the created channel ban object is added to the channel automatically.
227     */
228     chanban_t *
229 pippijn 1.4 chanban_add (channel_t *c, char const * const mask, int type)
230 pippijn 1.1 {
231 pippijn 1.4 chanban_t *cb;
232 pippijn 1.1 node_t *n;
233    
234     if (mask == NULL)
235     {
236     slog (LG_ERROR, "chanban_add(): NULL +%c mask", type);
237     return NULL;
238     }
239     /* this would break protocol and/or cause crashes */
240     if (*mask == '\0' || *mask == ':' || strchr (mask, ' '))
241     {
242 pippijn 1.4 slog (LG_ERROR, "chanban_add(): trying to add invalid +%c %s to channel %s", type, mask, c->name);
243 pippijn 1.1 return NULL;
244     }
245    
246 pippijn 1.4 cb = chanban_find (c, mask, type);
247 pippijn 1.1
248 pippijn 1.4 if (cb)
249 pippijn 1.1 {
250 pippijn 1.4 slog (LG_DEBUG, "chanban_add(): channel ban %s:%s already exists", c->name, cb->mask);
251 pippijn 1.1 return NULL;
252     }
253    
254 pippijn 1.4 slog (LG_DEBUG, "chanban_add(): %s +%c %s", c->name, type, mask);
255 pippijn 1.1
256     n = node_create ();
257 pippijn 1.4 cb = new chanban_t;
258 pippijn 1.1
259 pippijn 1.4 cb->chan = c;
260     cb->mask = sstrdup (mask);
261     cb->type = type;
262 pippijn 1.1
263 pippijn 1.4 node_add (cb, n, &c->bans);
264 pippijn 1.1
265 pippijn 1.4 return cb;
266 pippijn 1.1 }
267    
268     /*
269     * chanban_delete(chanban_t *c)
270     *
271     * Destroys a channel ban.
272     *
273     * Inputs:
274     * - channel ban object to destroy
275     *
276     * Outputs:
277     * - nothing
278     *
279     * Side Effects:
280     * - the channel ban is automatically removed from the channel that owned it
281     */
282     void
283     chanban_delete (chanban_t *c)
284     {
285     node_t *n;
286    
287     if (!c)
288     {
289     slog (LG_DEBUG, "chanban_delete(): called for nonexistant ban");
290     return;
291     }
292    
293     n = node_find (c, &c->chan->bans);
294     node_del (n, &c->chan->bans);
295     node_free (n);
296    
297 pippijn 1.4 sfree (c->mask);
298     delete c;
299 pippijn 1.1 }
300    
301     /*
302 pippijn 1.4 * chanban_find(channel_t *chan, char const * const mask, int type)
303 pippijn 1.1 *
304     * Looks up a channel ban.
305     *
306     * Inputs:
307     * - channel that the ban is supposedly on
308     * - mask that is being looked for
309     * - type of ban that is being looked for
310     *
311     * Outputs:
312     * - on success, returns the channel ban object requested
313     * - on failure, returns NULL
314     *
315     * Side Effects:
316     * - none
317     */
318     chanban_t *
319 pippijn 1.4 chanban_find (channel_t *c, char const * const mask, int type)
320 pippijn 1.1 {
321     chanban_t *cb;
322     node_t *n;
323    
324 pippijn 1.4 LIST_FOREACH (n, c->bans.head)
325 pippijn 1.1 {
326     cb = static_cast<chanban_t *> (n->data);
327    
328     if (cb->type == type && !irccasecmp (cb->mask, mask))
329     return cb;
330     }
331    
332     return NULL;
333     }
334    
335     /*
336 pippijn 1.4 * chanban_clear(channel_t *c)
337 pippijn 1.1 *
338     * Destroys all channel bans attached to a channel.
339     *
340     * Inputs:
341     * - channel to clear banlist on
342     *
343     * Outputs:
344     * - nothing
345     *
346     * Side Effects:
347     * - the banlist on the channel is cleared
348     * - no protocol messages are sent
349     */
350     void
351 pippijn 1.4 chanban_clear (channel_t *c)
352 pippijn 1.1 {
353     node_t *n, *tn;
354    
355 pippijn 1.4 LIST_FOREACH_SAFE (n, tn, c->bans.head)
356 pippijn 1.1 {
357     /* inefficient but avoids code duplication -- jilles */
358     chanban_delete (static_cast<chanban_t *> (n->data));
359     }
360     }
361    
362     /*
363 pippijn 1.4 * chanuser_add(channel_t *c, char const * const nick)
364 pippijn 1.1 *
365     * Channel user factory.
366     *
367     * Inputs:
368     * - channel that the user should belong to
369     * - nick/UID with any appropriate prefixes (e.g. ~, &, @, %, +)
370     * (if the user has a UID it must be used)
371     *
372     * Outputs:
373     * - on success, a new channel user object
374     * - on failure (NULL channel, or user kicked by channel_join hook), NULL
375     *
376     * Side Effects:
377     * - the channel user object is automatically associated to its parents
378     * - channel_join hook is called
379     */
380    
381     /*
382     * Rewritten 06/23/05 by nenolod:
383     *
384     * Iterate through the list of prefix characters we know about.
385     * Continue to do so until all prefixes are covered. Then add the
386     * nick to the channel, with the privs he has acquired thus far.
387     *
388     * Once, and only once we have done that do we start in on checking
389     * privileges. Otherwise we have a very inefficient way of doing
390     * things. It worked fine for shrike, but the old code was restricted
391     * to handling only @, @+ and + as prefixes.
392     */
393     chanuser_t *
394 pippijn 1.4 chanuser_add (channel_t *c, char const * const nickname)
395 pippijn 1.1 {
396 pippijn 1.4 char const *nick = nickname;
397 pippijn 1.1 user_t *u;
398     chanuser_t *cu, *tcu;
399     unsigned int flags = 0;
400     int i = 0;
401    
402 pippijn 1.4 if (c == NULL)
403 pippijn 1.1 return NULL;
404    
405 pippijn 1.4 if (*c->name != '#')
406 pippijn 1.1 {
407 pippijn 1.4 slog (LG_DEBUG, "chanuser_add(): got non #channel: %s", c->name);
408 pippijn 1.1 return NULL;
409     }
410    
411     while (*nick != '\0')
412     {
413     for (i = 0; prefix_mode_list[i].mode; i++)
414     if (*nick == prefix_mode_list[i].mode)
415     {
416     flags |= prefix_mode_list[i].value;
417     break;
418     }
419     if (!prefix_mode_list[i].mode)
420     break;
421     nick++;
422     }
423    
424     u = user_find (nick);
425     if (u == NULL)
426     {
427     slog (LG_DEBUG, "chanuser_add(): nonexist user: %s", nick);
428     return NULL;
429     }
430    
431 pippijn 1.4 tcu = chanuser_find (c, u);
432 pippijn 1.1 if (tcu != NULL)
433     {
434 pippijn 1.4 slog (LG_DEBUG, "chanuser_add(): user is already present: %s -> %s", c->name, u->nick);
435 pippijn 1.1
436     /* could be an OPME or other desyncher... */
437     tcu->modes |= flags;
438    
439     return tcu;
440     }
441    
442 pippijn 1.4 slog (LG_DEBUG, "chanuser_add(): %s -> %s", c->name, u->nick);
443 pippijn 1.1
444 pippijn 1.4 cu = new chanuser_t;
445 pippijn 1.1
446 pippijn 1.4 cu->chan = c;
447 pippijn 1.1 cu->user = u;
448     cu->modes |= flags;
449    
450 pippijn 1.4 c->nummembers++;
451 pippijn 1.1
452 pippijn 1.4 node_add (cu, &cu->cnode, &c->members);
453 pippijn 1.1 node_add (cu, &cu->unode, &u->channels);
454    
455     cnt.chanuser++;
456    
457     /* Return NULL if a hook function kicked the user out */
458 pippijn 1.4 return cu->callback.join (cu) ? cu : NULL;
459 pippijn 1.1 }
460    
461     /*
462 pippijn 1.4 * chanuser_delete(channel_t *c, user_t *user)
463 pippijn 1.1 *
464     * Destroys a channel user object.
465     *
466     * Inputs:
467     * - channel the user is on
468     * - the user itself
469     *
470     * Outputs:
471     * - nothing
472     *
473     * Side Effects:
474     * if the user is on the channel:
475     * - a channel user object is removed from the
476 pippijn 1.9 * channel's user_t::map and the user's channellist.
477 pippijn 1.1 * - channel_part hook is called
478     * - if this empties the channel and the channel is not set permanent
479     * (ircd->perm_mode), channel_delete() is called (q.v.)
480     */
481     void
482 pippijn 1.4 chanuser_delete (channel_t *c, user_t *user)
483 pippijn 1.1 {
484     chanuser_t *cu;
485    
486 pippijn 1.4 if (!c)
487 pippijn 1.1 {
488     slog (LG_DEBUG, "chanuser_delete(): called with NULL chan");
489     return;
490     }
491    
492     if (!user)
493     {
494     slog (LG_DEBUG, "chanuser_delete(): called with NULL user");
495     return;
496     }
497    
498 pippijn 1.4 cu = chanuser_find (c, user);
499 pippijn 1.1 if (cu == NULL)
500     return;
501    
502     /* this is called BEFORE we remove the user */
503 pippijn 1.4 cu->callback.part (cu);
504 pippijn 1.1
505     slog (LG_DEBUG, "chanuser_delete(): %s -> %s (%d)", cu->chan->name, cu->user->nick, cu->chan->nummembers - 1);
506    
507 pippijn 1.4 node_del (&cu->cnode, &c->members);
508 pippijn 1.1 node_del (&cu->unode, &user->channels);
509    
510 pippijn 1.4 delete cu;
511 pippijn 1.1
512 pippijn 1.4 c->nummembers--;
513 pippijn 1.1 cnt.chanuser--;
514    
515 pippijn 1.4 if (c->nummembers == 0 && !(c->modes & ircd->perm_mode))
516 pippijn 1.1 {
517     /* empty channels die */
518 pippijn 1.4 slog (LG_DEBUG, "chanuser_delete(): `%s' is empty, removing", c->name);
519 pippijn 1.1
520 pippijn 1.4 channel_delete (c);
521 pippijn 1.1 }
522     }
523    
524     /*
525 pippijn 1.4 * chanuser_find(channel_t *c, user_t *user)
526 pippijn 1.1 *
527     * Looks up a channel user object.
528     *
529     * Inputs:
530     * - channel object that the user is on
531     * - target user object
532     *
533     * Outputs:
534     * - on success, a channel user object
535     * - on failure, NULL
536     *
537     * Side Effects:
538     * - none
539     */
540     chanuser_t *
541 pippijn 1.4 chanuser_find (channel_t *c, user_t *user)
542 pippijn 1.1 {
543     node_t *n;
544     chanuser_t *cu;
545    
546 pippijn 1.4 if ((!c) || (!user))
547 pippijn 1.1 return NULL;
548    
549     /* choose shortest list to search -- jilles */
550 pippijn 1.4 if (LIST_LENGTH (&user->channels) < LIST_LENGTH (&c->members))
551 pippijn 1.1 {
552     LIST_FOREACH (n, user->channels.head)
553     {
554     cu = (chanuser_t *) n->data;
555    
556 pippijn 1.4 if (cu->chan == c)
557 pippijn 1.1 return cu;
558     }
559     }
560     else
561     {
562 pippijn 1.4 LIST_FOREACH (n, c->members.head)
563 pippijn 1.1 {
564     cu = (chanuser_t *) n->data;
565    
566     if (cu->user == user)
567     return cu;
568     }
569     }
570    
571     return NULL;
572     }