ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/src/channels.C
Revision: 1.4
Committed: Tue Aug 28 17:08:12 2007 UTC (16 years, 9 months ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.3: +71 -88 lines
Log Message:
- changed name
- updated the example config to the new system
- added more documentation
- enhanced documentation generators
- added a link to the pdf to the website
- added an RSS feed generator
- transitioned hooks to c++ callbacks
- did various merges with upstream along the way
- added const where appropriate
- removed the old block allocator
- fixed most memory leaks
- transitioned some dictionaries to std::map
- transitioned some lists to std::vector
- made some free functions members where appropriate
- renamed string to dynstr and added a static string ststr
- use NOW instead of time (NULL) if possible
- completely reworked database backends, crypto handlers and protocol handlers
  to use an object factory
- removed the old module system. ermyth does not do any dynamic loading anymore
- fixed most of the build system
- reworked how protocol commands work

File Contents

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