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

# Content
1 /*
2 * channels.C: Channel event and state tracking
3 * Rights to this code are documented in doc/pod/license.pod.
4 *
5 * Copyright © 2005-2007 Atheme Project (http://www.atheme.org)
6 */
7
8 static char const rcsid[] = "$Id: channels.C,v 1.3 2007-07-21 13:23:21 pippijn Exp $";
9
10 #include "atheme.h"
11 #include <account/mychan.h>
12
13 dictionary_tree_t *chanlist;
14 channel_t::callbacks channel_t::callback;
15 chanuser_t::callbacks chanuser_t::callback;
16
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 #if 0
35 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 #endif
39
40 chanlist = dictionary_create ("channel", HASH_CHANNEL, irccasecmp);
41 }
42
43 /*
44 * channel_add(char const * const name, unsigned int ts, server_t *creator)
45 *
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 channel_add (char const * const name, unsigned int ts, server_t *creator)
67 {
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 c = new channel_t;
88
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 c->callback.add (c);
109
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 delete cu;
157 cnt.chanuser--;
158 }
159 c->nummembers = 0;
160
161 c->callback.remove (c);
162
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 sfree (c->name);
172 if (c->topic != NULL)
173 sfree (c->topic);
174 if (c->topic_setter != NULL)
175 sfree (c->topic_setter);
176
177 delete c;
178
179 cnt.chan--;
180 }
181
182 /*
183 * channel_find(char const * const name)
184 *
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 channel_find (char const * const name)
199 {
200 return static_cast<channel_t *> (dictionary_retrieve (chanlist, name));
201 }
202
203 /*
204 * chanban_add(channel_t *c, char const * const mask, int type)
205 *
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 chanban_add (channel_t *c, char const * const mask, int type)
222 {
223 chanban_t *cb;
224 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 slog (LG_ERROR, "chanban_add(): trying to add invalid +%c %s to channel %s", type, mask, c->name);
235 return NULL;
236 }
237
238 cb = chanban_find (c, mask, type);
239
240 if (cb)
241 {
242 slog (LG_DEBUG, "chanban_add(): channel ban %s:%s already exists", c->name, cb->mask);
243 return NULL;
244 }
245
246 slog (LG_DEBUG, "chanban_add(): %s +%c %s", c->name, type, mask);
247
248 n = node_create ();
249 cb = new chanban_t;
250
251 cb->chan = c;
252 cb->mask = sstrdup (mask);
253 cb->type = type;
254
255 node_add (cb, n, &c->bans);
256
257 return cb;
258 }
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 sfree (c->mask);
290 delete c;
291 }
292
293 /*
294 * chanban_find(channel_t *chan, char const * const mask, int type)
295 *
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 chanban_find (channel_t *c, char const * const mask, int type)
312 {
313 chanban_t *cb;
314 node_t *n;
315
316 LIST_FOREACH (n, c->bans.head)
317 {
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 * chanban_clear(channel_t *c)
329 *
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 chanban_clear (channel_t *c)
344 {
345 node_t *n, *tn;
346
347 LIST_FOREACH_SAFE (n, tn, c->bans.head)
348 {
349 /* inefficient but avoids code duplication -- jilles */
350 chanban_delete (static_cast<chanban_t *> (n->data));
351 }
352 }
353
354 /*
355 * chanuser_add(channel_t *c, char const * const nick)
356 *
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 chanuser_add (channel_t *c, char const * const nickname)
387 {
388 char const *nick = nickname;
389 user_t *u;
390 chanuser_t *cu, *tcu;
391 unsigned int flags = 0;
392 int i = 0;
393
394 if (c == NULL)
395 return NULL;
396
397 if (*c->name != '#')
398 {
399 slog (LG_DEBUG, "chanuser_add(): got non #channel: %s", c->name);
400 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 tcu = chanuser_find (c, u);
424 if (tcu != NULL)
425 {
426 slog (LG_DEBUG, "chanuser_add(): user is already present: %s -> %s", c->name, u->nick);
427
428 /* could be an OPME or other desyncher... */
429 tcu->modes |= flags;
430
431 return tcu;
432 }
433
434 slog (LG_DEBUG, "chanuser_add(): %s -> %s", c->name, u->nick);
435
436 cu = new chanuser_t;
437
438 cu->chan = c;
439 cu->user = u;
440 cu->modes |= flags;
441
442 c->nummembers++;
443
444 node_add (cu, &cu->cnode, &c->members);
445 node_add (cu, &cu->unode, &u->channels);
446
447 cnt.chanuser++;
448
449 /* Return NULL if a hook function kicked the user out */
450 return cu->callback.join (cu) ? cu : NULL;
451 }
452
453 /*
454 * chanuser_delete(channel_t *c, user_t *user)
455 *
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 chanuser_delete (channel_t *c, user_t *user)
475 {
476 chanuser_t *cu;
477
478 if (!c)
479 {
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 cu = chanuser_find (c, user);
491 if (cu == NULL)
492 return;
493
494 /* this is called BEFORE we remove the user */
495 cu->callback.part (cu);
496
497 slog (LG_DEBUG, "chanuser_delete(): %s -> %s (%d)", cu->chan->name, cu->user->nick, cu->chan->nummembers - 1);
498
499 node_del (&cu->cnode, &c->members);
500 node_del (&cu->unode, &user->channels);
501
502 delete cu;
503
504 c->nummembers--;
505 cnt.chanuser--;
506
507 if (c->nummembers == 0 && !(c->modes & ircd->perm_mode))
508 {
509 /* empty channels die */
510 slog (LG_DEBUG, "chanuser_delete(): `%s' is empty, removing", c->name);
511
512 channel_delete (c);
513 }
514 }
515
516 /*
517 * chanuser_find(channel_t *c, user_t *user)
518 *
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 chanuser_find (channel_t *c, user_t *user)
534 {
535 node_t *n;
536 chanuser_t *cu;
537
538 if ((!c) || (!user))
539 return NULL;
540
541 /* choose shortest list to search -- jilles */
542 if (LIST_LENGTH (&user->channels) < LIST_LENGTH (&c->members))
543 {
544 LIST_FOREACH (n, user->channels.head)
545 {
546 cu = (chanuser_t *) n->data;
547
548 if (cu->chan == c)
549 return cu;
550 }
551 }
552 else
553 {
554 LIST_FOREACH (n, c->members.head)
555 {
556 cu = (chanuser_t *) n->data;
557
558 if (cu->user == user)
559 return cu;
560 }
561 }
562
563 return NULL;
564 }