1 |
/** |
2 |
* chanacs.C: Channel access lists |
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 |
#include "atheme.h" |
14 |
#include <libermyth.h> |
15 |
#include <account/chanacs.h> |
16 |
#include <account/mychan.h> |
17 |
#include <account/myuser.h> |
18 |
|
19 |
chanacs_t::callbacks chanacs_t::callback; |
20 |
|
21 |
/***************** |
22 |
* C H A N A C S * |
23 |
*****************/ |
24 |
|
25 |
/* private destructor for chanacs_t */ |
26 |
static void |
27 |
chanacs_delete (chanacs_t *ca) |
28 |
{ |
29 |
node_t *n; |
30 |
|
31 |
return_if_fail (ca != NULL); |
32 |
return_if_fail (ca->mychan != NULL); |
33 |
|
34 |
if (!(runflags & RF_STARTING)) |
35 |
slog (LG_DEBUG, "chanacs_delete(): %s -> %s", ca->mychan->name, ca->myuser != NULL ? ca->myuser->name : ca->host); |
36 |
n = node_find (ca, &ca->mychan->chanacs); |
37 |
node_del (n, &ca->mychan->chanacs); |
38 |
node_free (n); |
39 |
|
40 |
if (ca->myuser != NULL) |
41 |
{ |
42 |
n = node_find (ca, &ca->myuser->chanacs); |
43 |
node_del (n, &ca->myuser->chanacs); |
44 |
node_free (n); |
45 |
} |
46 |
|
47 |
ca->clear_metadata (); |
48 |
|
49 |
delete ca; |
50 |
|
51 |
cnt.chanacs--; |
52 |
} |
53 |
|
54 |
/* |
55 |
* chanacs_add(mychan_t *mychan, myuser_t *myuser, unsigned int level, time_t ts) |
56 |
* |
57 |
* Creates an access entry mapping between a user and channel. |
58 |
* |
59 |
* Inputs: |
60 |
* - a channel to create a mapping for |
61 |
* - a user to create a mapping for |
62 |
* - a bitmask which describes the access entry's privileges |
63 |
* - a timestamp for this access entry |
64 |
* |
65 |
* Outputs: |
66 |
* - a chanacs_t object which describes the mapping |
67 |
* |
68 |
* Side Effects: |
69 |
* - the channel access list is updated for mychan. |
70 |
*/ |
71 |
chanacs_t * |
72 |
chanacs_add (mychan_t *mychan, myuser_t *myuser, unsigned int level, time_t ts) |
73 |
{ |
74 |
chanacs_t *ca; |
75 |
node_t *n1; |
76 |
node_t *n2; |
77 |
|
78 |
return_val_if_fail (mychan != NULL && myuser != NULL, NULL); |
79 |
|
80 |
if (*mychan->name != '#') |
81 |
{ |
82 |
slog (LG_DEBUG, "chanacs_add(): got non #channel: %s", mychan->name); |
83 |
return NULL; |
84 |
} |
85 |
|
86 |
if (!(runflags & RF_STARTING)) |
87 |
slog (LG_DEBUG, "chanacs_add(): %s -> %s", mychan->name, myuser->name); |
88 |
|
89 |
n1 = node_create (); |
90 |
n2 = node_create (); |
91 |
|
92 |
ca = new chanacs_t; |
93 |
|
94 |
object_init (asobject (ca), NULL, (destructor_t) chanacs_delete); |
95 |
ca->mychan = mychan; |
96 |
ca->myuser = myuser; |
97 |
ca->level = level & ca_all; |
98 |
ca->ts = ts; |
99 |
|
100 |
node_add (ca, n1, &mychan->chanacs); |
101 |
node_add (ca, n2, &myuser->chanacs); |
102 |
|
103 |
cnt.chanacs++; |
104 |
|
105 |
return ca; |
106 |
} |
107 |
|
108 |
/* |
109 |
* chanacs_add_host(mychan_t *mychan, char *host, unsigned int level, time_t ts) |
110 |
* |
111 |
* Creates an access entry mapping between a hostmask and channel. |
112 |
* |
113 |
* Inputs: |
114 |
* - a channel to create a mapping for |
115 |
* - a hostmask to create a mapping for |
116 |
* - a bitmask which describes the access entry's privileges |
117 |
* - a timestamp for this access entry |
118 |
* |
119 |
* Outputs: |
120 |
* - a chanacs_t object which describes the mapping |
121 |
* |
122 |
* Side Effects: |
123 |
* - the channel access list is updated for mychan. |
124 |
*/ |
125 |
chanacs_t * |
126 |
chanacs_add_host (mychan_t *mychan, char const * const host, unsigned int level, time_t ts) |
127 |
{ |
128 |
chanacs_t *ca; |
129 |
node_t *n; |
130 |
|
131 |
return_val_if_fail (mychan != NULL && host != NULL, NULL); |
132 |
|
133 |
if (*mychan->name != '#') |
134 |
{ |
135 |
slog (LG_DEBUG, "chanacs_add_host(): got non #channel: %s", mychan->name); |
136 |
return NULL; |
137 |
} |
138 |
|
139 |
if (!(runflags & RF_STARTING)) |
140 |
slog (LG_DEBUG, "chanacs_add_host(): %s -> %s", mychan->name, host); |
141 |
|
142 |
n = node_create (); |
143 |
|
144 |
ca = new chanacs_t; |
145 |
|
146 |
object_init (asobject (ca), NULL, (destructor_t) chanacs_delete); |
147 |
ca->mychan = mychan; |
148 |
ca->myuser = NULL; |
149 |
strlcpy (ca->host, host, HOSTLEN); |
150 |
ca->level |= level; |
151 |
ca->ts = ts; |
152 |
|
153 |
node_add (ca, n, &mychan->chanacs); |
154 |
|
155 |
cnt.chanacs++; |
156 |
|
157 |
return ca; |
158 |
} |
159 |
|
160 |
chanacs_t * |
161 |
chanacs_find (mychan_t *mychan, myuser_t const * const myuser, unsigned int level) |
162 |
{ |
163 |
node_t *n; |
164 |
chanacs_t *ca; |
165 |
|
166 |
return_val_if_fail (mychan != NULL && myuser != NULL, NULL); |
167 |
|
168 |
LIST_FOREACH (n, mychan->chanacs.head) |
169 |
{ |
170 |
ca = static_cast<chanacs_t *> (n->data); |
171 |
|
172 |
if (level != 0x0) |
173 |
{ |
174 |
if ((ca->myuser == myuser) && ((ca->level & level) == level)) |
175 |
return ca; |
176 |
} |
177 |
else if (ca->myuser == myuser) |
178 |
return ca; |
179 |
} |
180 |
|
181 |
return NULL; |
182 |
} |
183 |
|
184 |
chanacs_t * |
185 |
chanacs_find_host (mychan_t *mychan, char const * const host, unsigned int level) |
186 |
{ |
187 |
node_t *n; |
188 |
chanacs_t *ca; |
189 |
|
190 |
return_val_if_fail (mychan != NULL && host != NULL, NULL); |
191 |
|
192 |
LIST_FOREACH (n, mychan->chanacs.head) |
193 |
{ |
194 |
ca = static_cast<chanacs_t *> (n->data); |
195 |
|
196 |
if (level != 0x0) |
197 |
{ |
198 |
if ((ca->myuser == NULL) && (!match (ca->host, host)) && ((ca->level & level) == level)) |
199 |
return ca; |
200 |
} |
201 |
else if ((ca->myuser == NULL) && (!match (ca->host, host))) |
202 |
return ca; |
203 |
} |
204 |
|
205 |
return NULL; |
206 |
} |
207 |
|
208 |
unsigned int |
209 |
chanacs_host_flags (mychan_t *mychan, char const * const host) |
210 |
{ |
211 |
node_t *n; |
212 |
chanacs_t *ca; |
213 |
unsigned int result = 0; |
214 |
|
215 |
return_val_if_fail (mychan != NULL && host != NULL, 0); |
216 |
|
217 |
LIST_FOREACH (n, mychan->chanacs.head) |
218 |
{ |
219 |
ca = (chanacs_t *) n->data; |
220 |
|
221 |
if (ca->myuser == NULL && !match (ca->host, host)) |
222 |
result |= ca->level; |
223 |
} |
224 |
|
225 |
return result; |
226 |
} |
227 |
|
228 |
chanacs_t * |
229 |
chanacs_find_host_literal (mychan_t *mychan, char const * const host, unsigned int level) |
230 |
{ |
231 |
node_t *n; |
232 |
chanacs_t *ca; |
233 |
|
234 |
if ((!mychan) || (!host)) |
235 |
return NULL; |
236 |
|
237 |
LIST_FOREACH (n, mychan->chanacs.head) |
238 |
{ |
239 |
ca = (chanacs_t *) n->data; |
240 |
|
241 |
if (level != 0x0) |
242 |
{ |
243 |
if ((ca->myuser == NULL) && (!strcasecmp (ca->host, host)) && ((ca->level & level) == level)) |
244 |
return ca; |
245 |
} |
246 |
else if ((ca->myuser == NULL) && (!strcasecmp (ca->host, host))) |
247 |
return ca; |
248 |
} |
249 |
|
250 |
return NULL; |
251 |
} |
252 |
|
253 |
chanacs_t * |
254 |
chanacs_find_host_by_user (mychan_t *mychan, user_t *u, unsigned int level) |
255 |
{ |
256 |
node_t *n; |
257 |
chanacs_t *ca; |
258 |
|
259 |
return_val_if_fail (mychan != NULL && u != NULL, 0); |
260 |
|
261 |
for (n = phandler->next_matching_host_chanacs (mychan, u, mychan->chanacs.head); n != NULL; n = phandler->next_matching_host_chanacs (mychan, u, n->next)) |
262 |
{ |
263 |
ca = static_cast<chanacs_t *> (n->data); |
264 |
if ((ca->level & level) == level) |
265 |
return ca; |
266 |
} |
267 |
|
268 |
return NULL; |
269 |
} |
270 |
|
271 |
unsigned int |
272 |
chanacs_host_flags_by_user (mychan_t *mychan, user_t *u) |
273 |
{ |
274 |
node_t *n; |
275 |
unsigned int result = 0; |
276 |
chanacs_t *ca; |
277 |
|
278 |
return_val_if_fail (mychan != NULL && u != NULL, 0); |
279 |
|
280 |
for (n = phandler->next_matching_host_chanacs (mychan, u, mychan->chanacs.head); n != NULL; n = phandler->next_matching_host_chanacs (mychan, u, n->next)) |
281 |
{ |
282 |
ca = static_cast<chanacs_t *> (n->data); |
283 |
result |= ca->level; |
284 |
} |
285 |
|
286 |
return result; |
287 |
} |
288 |
|
289 |
chanacs_t * |
290 |
chanacs_find_by_mask (mychan_t *mychan, char const * const mask, unsigned int level) |
291 |
{ |
292 |
myuser_t *mu; |
293 |
chanacs_t *ca; |
294 |
|
295 |
return_val_if_fail (mychan != NULL && mask != NULL, NULL); |
296 |
|
297 |
mu = myuser_t::find (mask); |
298 |
|
299 |
if (mu != NULL) |
300 |
{ |
301 |
ca = chanacs_find (mychan, mu, level); |
302 |
|
303 |
if (ca) |
304 |
return ca; |
305 |
} |
306 |
|
307 |
return chanacs_find_host_literal (mychan, mask, level); |
308 |
} |
309 |
|
310 |
bool |
311 |
chanacs_user_has_flag (mychan_t *mychan, user_t *u, unsigned int level) |
312 |
{ |
313 |
myuser_t *mu; |
314 |
|
315 |
return_val_if_fail (mychan != NULL && u != NULL, false); |
316 |
|
317 |
mu = u->myuser; |
318 |
if (mu != NULL) |
319 |
{ |
320 |
if (chanacs_find (mychan, mu, level)) |
321 |
return true; |
322 |
} |
323 |
|
324 |
if (chanacs_find_host_by_user (mychan, u, level)) |
325 |
return true; |
326 |
|
327 |
return false; |
328 |
} |
329 |
|
330 |
unsigned int |
331 |
chanacs_user_flags (mychan_t *mychan, user_t *u) |
332 |
{ |
333 |
myuser_t *mu; |
334 |
chanacs_t *ca; |
335 |
unsigned int result = 0; |
336 |
|
337 |
return_val_if_fail (mychan != NULL && u != NULL, 0); |
338 |
|
339 |
mu = u->myuser; |
340 |
if (mu != NULL) |
341 |
{ |
342 |
ca = chanacs_find (mychan, mu, 0); |
343 |
if (ca != NULL) |
344 |
result |= ca->level; |
345 |
} |
346 |
|
347 |
result |= chanacs_host_flags_by_user (mychan, u); |
348 |
|
349 |
return result; |
350 |
} |
351 |
|
352 |
bool |
353 |
chanacs_source_has_flag (mychan_t *mychan, sourceinfo_t *si, unsigned int level) |
354 |
{ |
355 |
return si->su != NULL ? chanacs_user_has_flag (mychan, si->su, level) : chanacs_find (mychan, si->smu, level) != NULL; |
356 |
} |
357 |
|
358 |
unsigned int |
359 |
chanacs_source_flags (mychan_t *mychan, sourceinfo_t *si) |
360 |
{ |
361 |
chanacs_t *ca; |
362 |
|
363 |
if (si->su != NULL) |
364 |
{ |
365 |
return chanacs_user_flags (mychan, si->su); |
366 |
} |
367 |
else |
368 |
{ |
369 |
ca = chanacs_find (mychan, si->smu, 0); |
370 |
return ca != NULL ? ca->level : 0; |
371 |
} |
372 |
} |
373 |
|
374 |
/* Look for the chanacs exactly matching mu or host (exactly one of mu and |
375 |
* host must be non-NULL). If not found, and create is true, create a new |
376 |
* chanacs with no flags. |
377 |
*/ |
378 |
chanacs_t * |
379 |
chanacs_open (mychan_t *mychan, myuser_t *mu, char const * const hostmask, bool create) |
380 |
{ |
381 |
chanacs_t *ca; |
382 |
|
383 |
/* wrt the second assert: only one of mu or hostmask can be not-NULL --nenolod */ |
384 |
return_val_if_fail (mychan != NULL, false); |
385 |
return_val_if_fail ((mu != NULL && hostmask == NULL) || (mu == NULL && hostmask != NULL), false); |
386 |
|
387 |
if (mu != NULL) |
388 |
{ |
389 |
ca = chanacs_find (mychan, mu, 0); |
390 |
if (ca != NULL) |
391 |
return ca; |
392 |
else if (create) |
393 |
return chanacs_add (mychan, mu, 0, NOW); |
394 |
} |
395 |
else |
396 |
{ |
397 |
ca = chanacs_find_host_literal (mychan, hostmask, 0); |
398 |
if (ca != NULL) |
399 |
return ca; |
400 |
else if (create) |
401 |
return chanacs_add_host (mychan, hostmask, 0, NOW); |
402 |
} |
403 |
return NULL; |
404 |
} |
405 |
|
406 |
/* Destroy a chanacs if it has no flags */ |
407 |
void |
408 |
chanacs_close (chanacs_t *ca) |
409 |
{ |
410 |
if (ca->level == 0) |
411 |
object_unref (ca); |
412 |
} |
413 |
|
414 |
/* Call this with a chanacs_t with level==0 */ |
415 |
bool |
416 |
chanacs_is_table_full (chanacs_t *ca) |
417 |
{ |
418 |
return chansvs.maxchanacs > 0 && LIST_LENGTH (&ca->mychan->chanacs) > chansvs.maxchanacs; |
419 |
} |
420 |
|
421 |
/* Change channel access |
422 |
* |
423 |
* Either mu or hostmask must be specified. |
424 |
* Add the flags in *addflags and remove the flags in *removeflags, updating |
425 |
* these to reflect the actual change. Only allow changes to restrictflags. |
426 |
* Returns true if successful, false if an unallowed change was attempted. |
427 |
* -- jilles */ |
428 |
bool |
429 |
chanacs_modify (chanacs_t *ca, unsigned int *addflags, unsigned int *removeflags, unsigned int restrictflags) |
430 |
{ |
431 |
return_val_if_fail (ca != NULL, false); |
432 |
return_val_if_fail (addflags != NULL && removeflags != NULL, false); |
433 |
|
434 |
*addflags &= ~ca->level; |
435 |
*removeflags &= ca->level & ~*addflags; |
436 |
/* no change? */ |
437 |
if ((*addflags | *removeflags) == 0) |
438 |
return true; |
439 |
/* attempting to add bad flag? */ |
440 |
if (~restrictflags & *addflags) |
441 |
return false; |
442 |
/* attempting to remove bad flag? */ |
443 |
if (~restrictflags & *removeflags) |
444 |
return false; |
445 |
/* attempting to manipulate user with more privs? */ |
446 |
if (~restrictflags & ca->level) |
447 |
return false; |
448 |
ca->level = (ca->level | *addflags) & ~*removeflags; |
449 |
ca->ts = NOW; |
450 |
|
451 |
return true; |
452 |
} |
453 |
|
454 |
/* version that doesn't return the changes made */ |
455 |
bool |
456 |
chanacs_modify_simple (chanacs_t *ca, unsigned int addflags, unsigned int removeflags) |
457 |
{ |
458 |
unsigned int a, r; |
459 |
|
460 |
a = addflags & ca_all; |
461 |
r = removeflags & ca_all; |
462 |
return chanacs_modify (ca, &a, &r, ca_all); |
463 |
} |
464 |
|
465 |
/* Change channel access |
466 |
* |
467 |
* Either mu or hostmask must be specified. |
468 |
* Add the flags in *addflags and remove the flags in *removeflags, updating |
469 |
* these to reflect the actual change. Only allow changes to restrictflags. |
470 |
* Returns true if successful, false if an unallowed change was attempted. |
471 |
* -- jilles */ |
472 |
bool |
473 |
chanacs_change (mychan_t *mychan, myuser_t *mu, char const * const hostmask, unsigned int *addflags, unsigned int *removeflags, unsigned int restrictflags) |
474 |
{ |
475 |
chanacs_t *ca; |
476 |
|
477 |
/* wrt the second assert: only one of mu or hostmask can be not-NULL --nenolod */ |
478 |
return_val_if_fail (mychan != NULL, false); |
479 |
return_val_if_fail ((mu != NULL && hostmask == NULL) || (mu == NULL && hostmask != NULL), false); |
480 |
return_val_if_fail (addflags != NULL && removeflags != NULL, false); |
481 |
|
482 |
if (mu != NULL) |
483 |
{ |
484 |
ca = chanacs_find (mychan, mu, 0); |
485 |
if (ca == NULL) |
486 |
{ |
487 |
*removeflags = 0; |
488 |
/* no change? */ |
489 |
if ((*addflags | *removeflags) == 0) |
490 |
return true; |
491 |
/* attempting to add bad flag? */ |
492 |
if (~restrictflags & *addflags) |
493 |
return false; |
494 |
chanacs_add (mychan, mu, *addflags, NOW); |
495 |
} |
496 |
else |
497 |
{ |
498 |
*addflags &= ~ca->level; |
499 |
*removeflags &= ca->level & ~*addflags; |
500 |
/* no change? */ |
501 |
if ((*addflags | *removeflags) == 0) |
502 |
return true; |
503 |
/* attempting to add bad flag? */ |
504 |
if (~restrictflags & *addflags) |
505 |
return false; |
506 |
/* attempting to remove bad flag? */ |
507 |
if (~restrictflags & *removeflags) |
508 |
return false; |
509 |
/* attempting to manipulate user with more privs? */ |
510 |
if (~restrictflags & ca->level) |
511 |
return false; |
512 |
ca->level = (ca->level | *addflags) & ~*removeflags; |
513 |
ca->ts = NOW; |
514 |
if (ca->level == 0) |
515 |
object_unref (ca); |
516 |
} |
517 |
} |
518 |
else /* hostmask != NULL */ |
519 |
{ |
520 |
ca = chanacs_find_host_literal (mychan, hostmask, 0); |
521 |
if (ca == NULL) |
522 |
{ |
523 |
*removeflags = 0; |
524 |
/* no change? */ |
525 |
if ((*addflags | *removeflags) == 0) |
526 |
return true; |
527 |
/* attempting to add bad flag? */ |
528 |
if (~restrictflags & *addflags) |
529 |
return false; |
530 |
chanacs_add_host (mychan, hostmask, *addflags, NOW); |
531 |
} |
532 |
else |
533 |
{ |
534 |
*addflags &= ~ca->level; |
535 |
*removeflags &= ca->level & ~*addflags; |
536 |
/* no change? */ |
537 |
if ((*addflags | *removeflags) == 0) |
538 |
return true; |
539 |
/* attempting to add bad flag? */ |
540 |
if (~restrictflags & *addflags) |
541 |
return false; |
542 |
/* attempting to remove bad flag? */ |
543 |
if (~restrictflags & *removeflags) |
544 |
return false; |
545 |
/* attempting to manipulate user with more privs? */ |
546 |
if (~restrictflags & ca->level) |
547 |
return false; |
548 |
ca->level = (ca->level | *addflags) & ~*removeflags; |
549 |
ca->ts = NOW; |
550 |
if (ca->level == 0) |
551 |
object_unref (ca); |
552 |
} |
553 |
} |
554 |
return true; |
555 |
} |
556 |
|
557 |
/* version that doesn't return the changes made */ |
558 |
bool |
559 |
chanacs_change_simple (mychan_t *mychan, myuser_t *mu, char const * const hostmask, unsigned int addflags, unsigned int removeflags) |
560 |
{ |
561 |
unsigned int a, r; |
562 |
|
563 |
a = addflags & ca_all; |
564 |
r = removeflags & ca_all; |
565 |
return chanacs_change (mychan, mu, hostmask, &a, &r, ca_all); |
566 |
} |