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, 7 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

# Content
1 /*
2 * channels.C: Channel event and state tracking
3 *
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 * Rights to this code are documented in doc/pod/license.pod.
10 * Copyright © 2005-2007 Atheme Project (http://www.atheme.org)
11 */
12
13 static char const rcsid[] = "$Id: channels.C,v 1.8 2007-09-16 18:54:44 pippijn Exp $";
14
15 #include "atheme.h"
16 #include "servers.h"
17 #include <account/mychan.h>
18
19 channel_map chanlist;
20 channel_t::callbacks channel_t::callback;
21 chanuser_t::callbacks chanuser_t::callback;
22
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 #if 0
41 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 #endif
45 }
46
47 /*
48 * channel_add(char const * const name, unsigned int ts, server_t *creator)
49 *
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 channel_add (char const * const name, unsigned int ts, server_t *creator)
71 {
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 c = new channel_t;
92
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 if ((mc = mychan_t::find (c->name)))
104 mc->chan = c;
105
106 chanlist[c->name] = c;
107
108 cnt.chan++;
109
110 if (creator != me.me)
111 {
112 c->callback.add (c);
113
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 delete cu;
161 cnt.chanuser--;
162 }
163 c->nummembers = 0;
164
165 c->callback.remove (c);
166
167 chanlist.erase (c->name);
168
169 if ((mc = mychan_t::find (c->name)))
170 mc->chan = NULL;
171
172 clear_simple_modes (c);
173 chanban_clear (c);
174
175 sfree (c->name);
176 if (c->topic != NULL)
177 sfree (c->topic);
178 if (c->topic_setter != NULL)
179 sfree (c->topic_setter);
180
181 delete c;
182
183 cnt.chan--;
184 }
185
186 /*
187 * channel_find(char const * const name)
188 *
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 channel_find (char const * const name)
203 {
204 channel_map::iterator it = chanlist.find (name);
205 if (it == chanlist.end ())
206 return NULL;
207
208 return it->second;
209 }
210
211 /*
212 * chanban_add(channel_t *c, char const * const mask, int type)
213 *
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 chanban_add (channel_t *c, char const * const mask, int type)
230 {
231 chanban_t *cb;
232 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 slog (LG_ERROR, "chanban_add(): trying to add invalid +%c %s to channel %s", type, mask, c->name);
243 return NULL;
244 }
245
246 cb = chanban_find (c, mask, type);
247
248 if (cb)
249 {
250 slog (LG_DEBUG, "chanban_add(): channel ban %s:%s already exists", c->name, cb->mask);
251 return NULL;
252 }
253
254 slog (LG_DEBUG, "chanban_add(): %s +%c %s", c->name, type, mask);
255
256 n = node_create ();
257 cb = new chanban_t;
258
259 cb->chan = c;
260 cb->mask = sstrdup (mask);
261 cb->type = type;
262
263 node_add (cb, n, &c->bans);
264
265 return cb;
266 }
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 sfree (c->mask);
298 delete c;
299 }
300
301 /*
302 * chanban_find(channel_t *chan, char const * const mask, int type)
303 *
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 chanban_find (channel_t *c, char const * const mask, int type)
320 {
321 chanban_t *cb;
322 node_t *n;
323
324 LIST_FOREACH (n, c->bans.head)
325 {
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 * chanban_clear(channel_t *c)
337 *
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 chanban_clear (channel_t *c)
352 {
353 node_t *n, *tn;
354
355 LIST_FOREACH_SAFE (n, tn, c->bans.head)
356 {
357 /* inefficient but avoids code duplication -- jilles */
358 chanban_delete (static_cast<chanban_t *> (n->data));
359 }
360 }
361
362 /*
363 * chanuser_add(channel_t *c, char const * const nick)
364 *
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 chanuser_add (channel_t *c, char const * const nickname)
395 {
396 char const *nick = nickname;
397 user_t *u;
398 chanuser_t *cu, *tcu;
399 unsigned int flags = 0;
400 int i = 0;
401
402 if (c == NULL)
403 return NULL;
404
405 if (*c->name != '#')
406 {
407 slog (LG_DEBUG, "chanuser_add(): got non #channel: %s", c->name);
408 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 tcu = chanuser_find (c, u);
432 if (tcu != NULL)
433 {
434 slog (LG_DEBUG, "chanuser_add(): user is already present: %s -> %s", c->name, u->nick);
435
436 /* could be an OPME or other desyncher... */
437 tcu->modes |= flags;
438
439 return tcu;
440 }
441
442 slog (LG_DEBUG, "chanuser_add(): %s -> %s", c->name, u->nick);
443
444 cu = new chanuser_t;
445
446 cu->chan = c;
447 cu->user = u;
448 cu->modes |= flags;
449
450 c->nummembers++;
451
452 node_add (cu, &cu->cnode, &c->members);
453 node_add (cu, &cu->unode, &u->channels);
454
455 cnt.chanuser++;
456
457 /* Return NULL if a hook function kicked the user out */
458 return cu->callback.join (cu) ? cu : NULL;
459 }
460
461 /*
462 * chanuser_delete(channel_t *c, user_t *user)
463 *
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 * channel's user_t::map and the user's channellist.
477 * - 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 chanuser_delete (channel_t *c, user_t *user)
483 {
484 chanuser_t *cu;
485
486 if (!c)
487 {
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 cu = chanuser_find (c, user);
499 if (cu == NULL)
500 return;
501
502 /* this is called BEFORE we remove the user */
503 cu->callback.part (cu);
504
505 slog (LG_DEBUG, "chanuser_delete(): %s -> %s (%d)", cu->chan->name, cu->user->nick, cu->chan->nummembers - 1);
506
507 node_del (&cu->cnode, &c->members);
508 node_del (&cu->unode, &user->channels);
509
510 delete cu;
511
512 c->nummembers--;
513 cnt.chanuser--;
514
515 if (c->nummembers == 0 && !(c->modes & ircd->perm_mode))
516 {
517 /* empty channels die */
518 slog (LG_DEBUG, "chanuser_delete(): `%s' is empty, removing", c->name);
519
520 channel_delete (c);
521 }
522 }
523
524 /*
525 * chanuser_find(channel_t *c, user_t *user)
526 *
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 chanuser_find (channel_t *c, user_t *user)
542 {
543 node_t *n;
544 chanuser_t *cu;
545
546 if ((!c) || (!user))
547 return NULL;
548
549 /* choose shortest list to search -- jilles */
550 if (LIST_LENGTH (&user->channels) < LIST_LENGTH (&c->members))
551 {
552 LIST_FOREACH (n, user->channels.head)
553 {
554 cu = (chanuser_t *) n->data;
555
556 if (cu->chan == c)
557 return cu;
558 }
559 }
560 else
561 {
562 LIST_FOREACH (n, c->members.head)
563 {
564 cu = (chanuser_t *) n->data;
565
566 if (cu->user == user)
567 return cu;
568 }
569 }
570
571 return NULL;
572 }