1 /* gline.c - Gline database
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
28 #ifdef HAVE_SYS_SOCKET_H
29 #include <sys/socket.h>
31 #ifdef HAVE_NETINET_IN_H
32 #include <netinet/in.h>
34 #ifdef HAVE_ARPA_INET_H
35 #include <arpa/inet.h>
41 #define KEY_REASON "reason"
42 #define KEY_EXPIRES "expires"
43 #define KEY_LASTMOD "lastmod"
44 #define KEY_ISSUER "issuer"
45 #define KEY_ISSUED "issued"
46 #define KEY_LIFETIME "lifetime"
48 static heap_t gline_heap; /* key: expiry time, data: struct gline_entry* */
49 static dict_t gline_dict; /* key: target, data: struct gline_entry* */
52 gline_comparator(const void *a, const void *b)
54 const struct gline *ga=a, *gb=b;
55 return ga->expires - gb->expires;
59 free_gline_from_dict(void *data)
61 struct gline *ent = data;
69 free_gline(struct gline *ent)
71 dict_remove(gline_dict, ent->target);
75 gline_for_p(UNUSED_ARG(void *key), void *data, void *extra)
77 struct gline *ge = data;
78 return !irccasecmp(ge->target, extra);
82 gline_expire(UNUSED_ARG(void *data))
84 unsigned long stopped;
88 while (heap_size(gline_heap)) {
89 heap_peek(gline_heap, 0, &wraa);
90 stopped = ((struct gline*)wraa)->lifetime;
96 if (heap_size(gline_heap))
97 timeq_add(stopped, gline_expire, NULL);
101 gline_remove(const char *target, int announce)
105 gl = dict_find(gline_dict, target, NULL);
114 gline_add(const char *issuer, const char *target, unsigned long duration, const char *reason, unsigned long issued, unsigned long lastmod, unsigned long lifetime, int announce)
117 struct gline *prev_first;
119 unsigned long expires;
121 heap_peek(gline_heap, 0, &argh);
123 expires = now + duration;
124 if (lifetime < expires)
126 ent = dict_find(gline_dict, target, NULL);
128 heap_remove_pred(gline_heap, gline_for_p, (char*)target);
129 if (ent->expires != expires)
130 ent->expires = expires;
131 if (ent->lifetime < lifetime)
132 ent->lifetime = lifetime;
133 if (ent->lastmod < lastmod)
134 ent->lastmod = lastmod;
135 if (strcmp(ent->issuer, issuer)) {
137 ent->issuer = strdup(issuer);
139 if (strcmp(ent->reason, reason)) {
141 ent->reason = strdup(reason);
144 ent = malloc(sizeof(*ent));
145 ent->issued = issued;
146 ent->lastmod = lastmod;
147 ent->issuer = strdup(issuer);
148 ent->target = strdup(target);
149 ent->expires = expires;
150 ent->lifetime = lifetime;
151 ent->reason = strdup(reason);
152 dict_insert(gline_dict, ent->target, ent);
154 heap_insert(gline_heap, ent, ent);
155 if (!prev_first || (ent->lifetime < prev_first->lifetime)) {
156 timeq_del(0, gline_expire, 0, TIMEQ_IGNORE_WHEN|TIMEQ_IGNORE_DATA);
157 timeq_add(ent->lifetime, gline_expire, 0);
160 irc_gline(NULL, ent);
165 gline_alternate_target(const char *target)
167 const char *hostname;
169 /* If no host part, bail. */
170 if (!(hostname = strchr(target, '@')))
172 /* If host part contains wildcards, bail. */
173 if (hostname[strcspn(hostname, "*?/")])
175 /* Get parsed address and canonical name for host. */
177 irc_in_addr_t in; /* move this to the right place */
178 if (irc_pton(&in, NULL, hostname+1)) {
179 if (getnameinfo(/*TODO*/))
181 } else if (!getaddrinfo(/*TODO*/)) {
189 gline_find(const char *target)
195 res = dict_find(gline_dict, target, NULL);
198 /* Stock ircu requires BADCHANs to match exactly. */
199 if ((target[0] == '#') || (target[0] == '&'))
201 else if (target[strcspn(target, "*?")]) {
202 /* Wildcard: do an obnoxiously long search. */
203 for (it = dict_first(gline_dict); it; it = iter_next(it)) {
205 if (match_ircglob(target, res->target))
209 /* See if we can resolve the hostname part of the mask. */
210 if ((alt_target = gline_alternate_target(target))) {
211 res = gline_find(alt_target);
219 gline_refresh_helper(UNUSED_ARG(void *key), void *data, void *extra)
221 struct gline *ge = data;
222 irc_gline(extra, ge);
227 gline_refresh_server(struct server *srv)
229 heap_remove_pred(gline_heap, gline_refresh_helper, srv);
233 gline_refresh_all(void)
235 heap_remove_pred(gline_heap, gline_refresh_helper, 0);
241 return dict_size(gline_dict);
245 gline_add_record(const char *key, void *data, UNUSED_ARG(void *extra))
247 struct record_data *rd = data;
248 const char *issuer, *reason, *dstr;
249 unsigned long issued, expiration, lastmod, lifetime;
251 if (!(reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING))) {
252 log_module(MAIN_LOG, LOG_ERROR, "Missing reason for gline %s", key);
255 if (!(dstr = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING))) {
256 log_module(MAIN_LOG, LOG_ERROR, "Missing expiration for gline %s", key);
259 expiration = strtoul(dstr, NULL, 0);
260 dstr = database_get_data(rd->d.object, KEY_LIFETIME, RECDB_QSTRING);
261 lifetime = dstr ? strtoul(dstr, NULL, 0) : expiration;
262 dstr = database_get_data(rd->d.object, KEY_LASTMOD, RECDB_QSTRING);
263 lastmod = dstr ? strtoul(dstr, NULL, 0) : 0;
264 dstr = database_get_data(rd->d.object, KEY_ISSUED, RECDB_QSTRING);
265 issued = dstr ? strtoul(dstr, NULL, 0) : now;
266 if (!(issuer = database_get_data(rd->d.object, KEY_ISSUER, RECDB_QSTRING)))
267 issuer = "<unknown>";
269 gline_add(issuer, key, expiration - now, reason, issued, lastmod, lifetime, 0);
274 gline_saxdb_read(struct dict *db)
276 return dict_foreach(db, gline_add_record, 0) != NULL;
280 gline_write_entry(UNUSED_ARG(void *key), void *data, void *extra)
282 struct gline *ent = data;
283 struct saxdb_context *ctx = extra;
285 saxdb_start_record(ctx, ent->target, 0);
286 saxdb_write_int(ctx, KEY_EXPIRES, ent->expires);
287 saxdb_write_int(ctx, KEY_ISSUED, ent->issued);
288 saxdb_write_int(ctx, KEY_LIFETIME, ent->lifetime);
290 saxdb_write_int(ctx, KEY_LASTMOD, ent->lastmod);
291 saxdb_write_string(ctx, KEY_REASON, ent->reason);
292 saxdb_write_string(ctx, KEY_ISSUER, ent->issuer);
293 saxdb_end_record(ctx);
298 gline_saxdb_write(struct saxdb_context *ctx)
300 heap_remove_pred(gline_heap, gline_write_entry, ctx);
305 gline_db_cleanup(void)
307 heap_delete(gline_heap);
308 dict_delete(gline_dict);
314 gline_heap = heap_new(gline_comparator);
315 gline_dict = dict_new();
316 dict_set_free_data(gline_dict, free_gline_from_dict);
317 saxdb_register("gline", gline_saxdb_read, gline_saxdb_write);
318 reg_exit_func(gline_db_cleanup);
321 struct gline_discrim *
322 gline_discrim_create(struct userNode *user, struct userNode *src, unsigned int argc, char *argv[])
325 struct gline_discrim *discrim;
327 discrim = calloc(1, sizeof(*discrim));
329 discrim->max_issued = ULONG_MAX;
330 discrim->max_lastmod = ULONG_MAX;
331 discrim->max_lifetime = ULONG_MAX;
333 for (i=0; i<argc; i++) {
335 send_message(user, src, "MSG_MISSING_PARAMS", argv[i]);
337 } else if (!irccasecmp(argv[i], "mask") || !irccasecmp(argv[i], "host")) {
338 if (!irccasecmp(argv[++i], "exact"))
339 discrim->target_mask_type = EXACT;
340 else if (!irccasecmp(argv[i], "subset"))
341 discrim->target_mask_type = SUBSET;
342 else if (!irccasecmp(argv[i], "superset"))
343 discrim->target_mask_type = SUPERSET;
345 discrim->target_mask_type = SUBSET, i--;
347 send_message(user, src, "MSG_MISSING_PARAMS", argv[i-1]);
350 if (!is_gline(argv[i]) && !IsChannelName(argv[i])) {
351 send_message(user, src, "MSG_INVALID_GLINE", argv[i]);
354 discrim->target_mask = argv[i];
355 discrim->alt_target_mask = gline_alternate_target(discrim->target_mask);
356 } else if (!irccasecmp(argv[i], "limit"))
357 discrim->limit = strtoul(argv[++i], NULL, 0);
358 else if (!irccasecmp(argv[i], "reason"))
359 discrim->reason_mask = argv[++i];
360 else if (!irccasecmp(argv[i], "issuer"))
361 discrim->issuer_mask = argv[++i];
362 else if (!irccasecmp(argv[i], "after"))
363 discrim->min_expire = now + ParseInterval(argv[++i]);
364 else if (!irccasecmp(argv[i], "before"))
365 discrim->max_issued = now - ParseInterval(argv[++i]);
366 else if (!irccasecmp(argv[i], "lastmod")) {
367 const char *cmp = argv[++i];
370 discrim->min_lastmod = now - ParseInterval(cmp + 2);
372 discrim->min_lastmod = now - (ParseInterval(cmp + 1) - 1);
374 } else if (cmp[0] == '>') {
376 discrim->max_lastmod = now - ParseInterval(cmp + 2);
378 discrim->max_lastmod = now - (ParseInterval(cmp + 1) - 1);
381 discrim->min_lastmod = now - ParseInterval(cmp + 2);
383 } else if (!irccasecmp(argv[i], "lifetime")) {
384 const char *cmp = argv[++i];
387 discrim->min_lifetime = now - ParseInterval(cmp + 2);
389 discrim->min_lifetime = now - (ParseInterval(cmp + 1) - 1);
391 } else if (cmp[0] == '>') {
393 discrim->max_lifetime = now - ParseInterval(cmp + 2);
395 discrim->max_lifetime = now - (ParseInterval(cmp + 1) - 1);
398 discrim->min_lifetime = now - ParseInterval(cmp + 2);
401 send_message(user, src, "MSG_INVALID_CRITERIA", argv[i]);
407 free(discrim->alt_target_mask);
412 struct gline_search {
413 struct gline_discrim *discrim;
414 gline_search_func func;
420 gline_discrim_match(struct gline *gline, struct gline_discrim *discrim)
422 if ((discrim->issuer_mask && !match_ircglob(gline->issuer, discrim->issuer_mask))
423 || (discrim->reason_mask && !match_ircglob(gline->reason, discrim->reason_mask))
424 || (discrim->target_mask
425 && (((discrim->target_mask_type == SUBSET)
426 && !match_ircglobs(discrim->target_mask, gline->target)
427 && (!discrim->alt_target_mask
428 || !match_ircglobs(discrim->alt_target_mask, gline->target)))
429 || ((discrim->target_mask_type == EXACT)
430 && irccasecmp(discrim->target_mask, gline->target)
431 && (!discrim->alt_target_mask
432 || !irccasecmp(discrim->alt_target_mask, gline->target)))
433 || ((discrim->target_mask_type == SUPERSET)
434 && !match_ircglobs(gline->target, discrim->target_mask)
435 && (!discrim->alt_target_mask
436 || !match_ircglobs(discrim->alt_target_mask, gline->target)))))
437 || (discrim->max_issued < gline->issued)
438 || (discrim->min_expire > gline->expires)
439 || (discrim->min_lastmod > gline->lastmod)
440 || (discrim->max_lastmod < gline->lastmod)
441 || (discrim->min_lifetime > gline->lifetime)
442 || (discrim->max_lifetime < gline->lifetime)) {
449 gline_search_helper(UNUSED_ARG(void *key), void *data, void *extra)
451 struct gline *gline = data;
452 struct gline_search *search = extra;
454 if (gline_discrim_match(gline, search->discrim)
455 && (search->hits++ < search->discrim->limit)) {
456 search->func(gline, search->data);
462 gline_discrim_search(struct gline_discrim *discrim, gline_search_func gsf, void *data)
464 struct gline_search search;
465 search.discrim = discrim;
469 heap_remove_pred(gline_heap, gline_search_helper, &search);