1 /*
2  * atheme-services: A collection of minimalist IRC services
3  * channels.c: Channel event and state tracking
4  *
5  * Copyright (c) 2005-2007 Atheme Project (http://www.atheme.org)
6  *
7  * Permission to use, copy, modify, and/or distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
12  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
13  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
14  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
15  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
16  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
17  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
18  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
19  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
20  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
21  * POSSIBILITY OF SUCH DAMAGE.
22  */
23 
24 #include "atheme.h"
25 
26 mowgli_patricia_t *chanlist;
27 
28 mowgli_heap_t *chan_heap;
29 mowgli_heap_t *chanuser_heap;
30 mowgli_heap_t *chanban_heap;
31 
32 /*
33  * init_channels()
34  *
35  * Initializes the channel-related heaps and DTree structures.
36  *
37  * Inputs:
38  *     - nothing
39  *
40  * Outputs:
41  *     - nothing
42  *
43  * Side Effects:
44  *     - if the heaps or DTrees fail to initialize, the program will abort.
45  */
init_channels(void)46 void init_channels(void)
47 {
48 	chan_heap = sharedheap_get(sizeof(channel_t));
49 	chanuser_heap = sharedheap_get(sizeof(chanuser_t));
50 	chanban_heap = sharedheap_get(sizeof(chanban_t));
51 
52 	if (chan_heap == NULL || chanuser_heap == NULL || chanban_heap == NULL)
53 	{
54 		slog(LG_INFO, "init_channels(): block allocator failure.");
55 		exit(EXIT_FAILURE);
56 	}
57 
58 	chanlist = mowgli_patricia_create(irccasecanon);
59 }
60 
61 /*
62  * channel_add(const char *name, time_t ts, server_t *creator)
63  *
64  * Channel object factory.
65  *
66  * Inputs:
67  *     - channel name
68  *     - timestamp of channel creation
69  *     - server that is creating the channel
70  *
71  * Outputs:
72  *     - on success, a channel object
73  *     - on failure, NULL
74  *
75  * Side Effects:
76  *     - the channel is automatically inserted into the channel DTree
77  *     - if the creator is not me.me:
78  *       - channel_add hook is called
79  *       - all services are joined if this is the snoop channel
80  *     - if the creator is me.me these actions must be performed by the
81  *       caller (i.e. join()) after joining the service
82  */
channel_add(const char * name,time_t ts,server_t * creator)83 channel_t *channel_add(const char *name, time_t ts, server_t *creator)
84 {
85 	channel_t *c;
86 	mychan_t *mc;
87 
88 	if (!VALID_GLOBAL_CHANNEL_PFX(name))
89 	{
90 		slog(LG_DEBUG, "channel_add(): got channel with invalid global prefix: %s", name);
91 		return NULL;
92 	}
93 
94 	c = channel_find(name);
95 
96 	if (c)
97 	{
98 		slog(LG_DEBUG, "channel_add(): channel already exists: %s", name);
99 		return c;
100 	}
101 
102 	slog(LG_DEBUG, "channel_add(): %s by %s", name, creator->name);
103 
104 	c = mowgli_heap_alloc(chan_heap);
105 
106 	c->name = sstrdup(name);
107 	c->ts = ts;
108 
109 	c->topic = NULL;
110 	c->topic_setter = NULL;
111 
112 	if (ignore_mode_list_size != 0)
113 		c->extmodes = scalloc(sizeof(char *), ignore_mode_list_size);
114 
115 	c->bans.head = NULL;
116 	c->bans.tail = NULL;
117 	c->bans.count = 0;
118 
119 	if ((mc = mychan_find(c->name)))
120 		mc->chan = c;
121 
122 	mowgli_patricia_add(chanlist, c->name, c);
123 
124 	cnt.chan++;
125 
126 	if (creator != me.me)
127 	{
128 		hook_call_channel_add(c);
129 
130 	}
131 
132 	return c;
133 }
134 
135 /*
136  * channel_delete(channel_t *c)
137  *
138  * Destroys a channel object and its children member objects.
139  *
140  * Inputs:
141  *     - channel object to destroy
142  *
143  * Outputs:
144  *     - nothing
145  *
146  * Side Effects:
147  *     - channel_delete hook is called
148  *     - a channel and all attached structures are destroyed
149  *     - no protocol messages are sent for any remaining members
150  */
channel_delete(channel_t * c)151 void channel_delete(channel_t *c)
152 {
153 	mychan_t *mc;
154 	mowgli_node_t *n, *tn;
155 	chanuser_t *cu;
156 
157 	return_if_fail(c != NULL);
158 
159 	slog(LG_DEBUG, "channel_delete(): %s", c->name);
160 
161 	modestack_finalize_channel(c);
162 
163 	/* If this is called from uplink_close(), there may still be services
164 	 * in the channel. Remove them. Calling chanuser_delete() could lead
165 	 * to a recursive call, so don't do that.
166 	 * -- jilles */
167 	MOWGLI_ITER_FOREACH_SAFE(n, tn, c->members.head)
168 	{
169 		cu = n->data;
170 		soft_assert(is_internal_client(cu->user) && !me.connected);
171 		mowgli_node_delete(&cu->cnode, &c->members);
172 		mowgli_node_delete(&cu->unode, &cu->user->channels);
173 		mowgli_heap_free(chanuser_heap, cu);
174 		cnt.chanuser--;
175 	}
176 	c->nummembers = 0;
177 	c->numsvcmembers = 0;
178 
179 	hook_call_channel_delete(c);
180 
181 	mowgli_patricia_delete(chanlist, c->name);
182 
183 	if ((mc = mychan_find(c->name)))
184 		mc->chan = NULL;
185 
186 	clear_simple_modes(c);
187 	chanban_clear(c);
188 
189 	if (c->extmodes != NULL)
190 		free(c->extmodes);
191 
192 	free(c->name);
193 	if (c->topic != NULL)
194 		free(c->topic);
195 	if (c->topic_setter != NULL)
196 		free(c->topic_setter);
197 
198 	mowgli_heap_free(chan_heap, c);
199 
200 	cnt.chan--;
201 }
202 
203 /*
204  * chanban_add(channel_t *chan, const char *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  */
chanban_add(channel_t * chan,const char * mask,int type)220 chanban_t *chanban_add(channel_t *chan, const char *mask, int type)
221 {
222 	chanban_t *c;
223 
224 	return_val_if_fail(chan != NULL, NULL);
225 	return_val_if_fail(mask != NULL, NULL);
226 
227 	/* this would break protocol and/or cause crashes */
228 	if (*mask == '\0' || *mask == ':' || strchr(mask, ' '))
229 	{
230 		slog(LG_ERROR, "chanban_add(): trying to add invalid +%c %s to channel %s", type, mask, chan->name);
231 		return NULL;
232 	}
233 
234 	c = chanban_find(chan, mask, type);
235 
236 	if (c)
237 	{
238 		slog(LG_DEBUG, "chanban_add(): channel ban %s:%s already exists", chan->name, c->mask);
239 		return NULL;
240 	}
241 
242 	slog(LG_DEBUG, "chanban_add(): %s +%c %s", chan->name, type, mask);
243 
244 	c = mowgli_heap_alloc(chanban_heap);
245 
246 	c->chan = chan;
247 	c->mask = sstrdup(mask);
248 	c->type = type;
249 
250 	mowgli_node_add(c, &c->node, &chan->bans);
251 
252 	return c;
253 }
254 
255 /*
256  * chanban_delete(chanban_t *c)
257  *
258  * Destroys a channel ban.
259  *
260  * Inputs:
261  *     - channel ban object to destroy
262  *
263  * Outputs:
264  *     - nothing
265  *
266  * Side Effects:
267  *     - the channel ban is automatically removed from the channel that owned it
268  */
chanban_delete(chanban_t * c)269 void chanban_delete(chanban_t * c)
270 {
271 	return_if_fail(c != NULL);
272 
273 	mowgli_node_delete(&c->node, &c->chan->bans);
274 
275 	free(c->mask);
276 	mowgli_heap_free(chanban_heap, c);
277 }
278 
279 /*
280  * chanban_find(channel_t *chan, const char *mask, int type)
281  *
282  * Looks up a channel ban.
283  *
284  * Inputs:
285  *     - channel that the ban is supposedly on
286  *     - mask that is being looked for
287  *     - type of ban that is being looked for
288  *
289  * Outputs:
290  *     - on success, returns the channel ban object requested
291  *     - on failure, returns NULL
292  *
293  * Side Effects:
294  *     - none
295  */
chanban_find(channel_t * chan,const char * mask,int type)296 chanban_t *chanban_find(channel_t *chan, const char *mask, int type)
297 {
298 	chanban_t *c;
299 	mowgli_node_t *n;
300 
301 	return_val_if_fail(chan != NULL, NULL);
302 	return_val_if_fail(mask != NULL, NULL);
303 
304 	MOWGLI_ITER_FOREACH(n, chan->bans.head)
305 	{
306 		c = n->data;
307 
308 		if (c->type == type && !irccasecmp(c->mask, mask))
309 			return c;
310 	}
311 
312 	return NULL;
313 }
314 
315 /*
316  * chanuser_add(channel_t *chan, const char *nick)
317  *
318  * Channel user factory.
319  *
320  * Inputs:
321  *     - channel that the user should belong to
322  *     - nick/UID with any appropriate prefixes (e.g. ~, &, @, %, +)
323  *       (if the user has a UID it must be used)
324  *
325  * Outputs:
326  *     - on success, a new channel user object
327  *     - on failure (NULL channel, or user kicked by channel_join hook), NULL
328  *
329  * Side Effects:
330  *     - the channel user object is automatically associated to its parents
331  *     - channel_join hook is called
332  */
333 
334 /*
335  * Rewritten 06/23/05 by nenolod:
336  *
337  * Iterate through the list of prefix characters we know about.
338  * Continue to do so until all prefixes are covered. Then add the
339  * nick to the channel, with the privs they have acquired thus far.
340  *
341  * Once, and only once we have done that do we start in on checking
342  * privileges. Otherwise we have a very inefficient way of doing
343  * things. It worked fine for shrike, but the old code was restricted
344  * to handling only @, @+ and + as prefixes.
345  */
chanuser_add(channel_t * chan,const char * nick)346 chanuser_t *chanuser_add(channel_t *chan, const char *nick)
347 {
348 	user_t *u;
349 	chanuser_t *cu, *tcu;
350 	unsigned int flags = 0;
351 	int i = 0;
352 	hook_channel_joinpart_t hdata;
353 
354 	return_val_if_fail(chan != NULL, NULL);
355 	return_val_if_fail(chan->name != NULL, NULL);
356 	return_val_if_fail(nick != NULL, NULL);
357 
358 	if (!VALID_GLOBAL_CHANNEL_PFX(chan->name))
359 	{
360 		slog(LG_DEBUG, "chanuser_add(): got an invalid global channel prefix: %s", chan->name);
361 		return NULL;
362 	}
363 
364 	while (*nick != '\0')
365 	{
366 		for (i = 0; prefix_mode_list[i].mode; i++)
367 			if (*nick == prefix_mode_list[i].mode)
368 			{
369 				flags |= prefix_mode_list[i].value;
370 				break;
371 			}
372 		if (!prefix_mode_list[i].mode)
373 			break;
374 		nick++;
375 	}
376 
377 	u = user_find(nick);
378 	if (u == NULL)
379 	{
380 		slog(LG_DEBUG, "chanuser_add(): nonexist user: %s", nick);
381 		return NULL;
382 	}
383 
384 	tcu = chanuser_find(chan, u);
385 	if (tcu != NULL)
386 	{
387 		slog(LG_DEBUG, "chanuser_add(): user is already present: %s -> %s", chan->name, u->nick);
388 
389 		/* could be an OPME or other desyncher... */
390 		tcu->modes |= flags;
391 
392 		return tcu;
393 	}
394 
395 	slog(LG_DEBUG, "chanuser_add(): %s -> %s", chan->name, u->nick);
396 
397 	cu = mowgli_heap_alloc(chanuser_heap);
398 
399 	cu->chan = chan;
400 	cu->user = u;
401 	cu->modes = flags;
402 
403 	chan->nummembers++;
404 	if (is_internal_client(u))
405 		chan->numsvcmembers++;
406 
407 	mowgli_node_add(cu, &cu->cnode, &chan->members);
408 	mowgli_node_add(cu, &cu->unode, &u->channels);
409 
410 	cnt.chanuser++;
411 
412 	hdata.cu = cu;
413 	hook_call_channel_join(&hdata);
414 
415 	/* Return NULL if a hook function kicked the user out */
416 	return hdata.cu;
417 }
418 
419 /*
420  * chanuser_delete(channel_t *chan, user_t *user)
421  *
422  * Destroys a channel user object.
423  *
424  * Inputs:
425  *     - channel the user is on
426  *     - the user itself
427  *
428  * Outputs:
429  *     - nothing
430  *
431  * Side Effects:
432  *     if the user is on the channel:
433  *     - a channel user object is removed from the
434  *       channel's userlist and the user's channellist.
435  *     - channel_part hook is called
436  *     - if this empties the channel and the channel is not set permanent
437  *       (ircd->perm_mode), channel_delete() is called (q.v.)
438  */
chanuser_delete(channel_t * chan,user_t * user)439 void chanuser_delete(channel_t *chan, user_t *user)
440 {
441 	chanuser_t *cu;
442 	hook_channel_joinpart_t hdata;
443 
444 	return_if_fail(chan != NULL);
445 	return_if_fail(user != NULL);
446 
447 	cu = chanuser_find(chan, user);
448 	if (cu == NULL)
449 		return;
450 
451 	/* this is called BEFORE we remove the user */
452 	hdata.cu = cu;
453 	hook_call_channel_part(&hdata);
454 
455 	slog(LG_DEBUG, "chanuser_delete(): %s -> %s (%d)", cu->chan->name, cu->user->nick, cu->chan->nummembers - 1);
456 
457 	mowgli_node_delete(&cu->cnode, &chan->members);
458 	mowgli_node_delete(&cu->unode, &user->channels);
459 
460 	mowgli_heap_free(chanuser_heap, cu);
461 
462 	chan->nummembers--;
463 	cnt.chanuser--;
464 
465 	if (is_internal_client(user))
466 		chan->numsvcmembers--;
467 
468 	if (chan->nummembers == 0 && !(chan->modes & ircd->perm_mode))
469 	{
470 		/* empty channels die */
471 		slog(LG_DEBUG, "chanuser_delete(): `%s' is empty, removing", chan->name);
472 
473 		channel_delete(chan);
474 	}
475 }
476 
477 /*
478  * chanuser_find(channel_t *chan, user_t *user)
479  *
480  * Looks up a channel user object.
481  *
482  * Inputs:
483  *     - channel object that the user is on
484  *     - target user object
485  *
486  * Outputs:
487  *     - on success, a channel user object
488  *     - on failure, NULL
489  *
490  * Side Effects:
491  *     - none
492  */
chanuser_find(channel_t * chan,user_t * user)493 chanuser_t *chanuser_find(channel_t *chan, user_t *user)
494 {
495 	mowgli_node_t *n;
496 	chanuser_t *cu;
497 
498 	return_val_if_fail(chan != NULL, NULL);
499 	return_val_if_fail(user != NULL, NULL);
500 
501 	/* choose shortest list to search -- jilles */
502 	if (MOWGLI_LIST_LENGTH(&user->channels) < MOWGLI_LIST_LENGTH(&chan->members))
503 	{
504 		MOWGLI_ITER_FOREACH(n, user->channels.head)
505 		{
506 			cu = (chanuser_t *)n->data;
507 
508 			if (cu->chan == chan)
509 				return cu;
510 		}
511 	}
512 	else
513 	{
514 		MOWGLI_ITER_FOREACH(n, chan->members.head)
515 		{
516 			cu = (chanuser_t *)n->data;
517 
518 			if (cu->user == user)
519 				return cu;
520 		}
521 	}
522 
523 	return NULL;
524 }
525 
526 /* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
527  * vim:ts=8
528  * vim:sw=8
529  * vim:noexpandtab
530  */
531