ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/src/channels.C
Revision: 1.1
Committed: Thu Jul 19 08:24:56 2007 UTC (16 years, 10 months ago) by pippijn
Content type: text/plain
Branch: MAIN
Log Message:
initial import. the most important changes since Atheme are:
- fixed many memory leaks
- fixed many bugs
- converted to C++ and use more STL containers
- added a (not very enhanced yet) perl module
- greatly improved XML-RPC speed
- added a JSON-RPC module with code from json-cpp
- added a valgrind memcheck module to operserv
- added a more object oriented base64 implementation
- added a specialised unit test framework
- improved stability
- use gettimeofday() if available
- reworked adding/removing commands
- MemoServ IGNORE DEL can now remove indices

File Contents

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