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

# Content
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 */