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->lifetime - gb->lifetime;
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_equal_p(UNUSED_ARG(void *key), void *data, void *extra)
81 gline_expire(UNUSED_ARG(void *data))
83 unsigned long stopped;
87 while (heap_size(gline_heap)) {
88 heap_peek(gline_heap, 0, &wraa);
89 stopped = ((struct gline*)wraa)->lifetime;
95 if (heap_size(gline_heap))
96 timeq_add(stopped, gline_expire, NULL);
100 gline_remove(const char *target, int announce)
104 gl = dict_find(gline_dict, target, NULL);
113 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)
116 struct gline *prev_first;
118 unsigned long expires;
120 heap_peek(gline_heap, 0, &argh);
122 expires = now + duration;
123 if (lifetime < expires)
125 ent = dict_find(gline_dict, target, NULL);
127 heap_remove_pred(gline_heap, gline_equal_p, ent);
128 if (ent->issued > lastmod)
129 ent->issued = lastmod;
130 if (ent->lastmod < lastmod) {
131 ent->lastmod = lastmod;
132 ent->expires = expires;
133 if (strcmp(ent->reason, reason)) {
135 ent->reason = strdup(reason);
138 if (ent->lifetime < lifetime)
139 ent->lifetime = lifetime;
141 ent = malloc(sizeof(*ent));
142 ent->issued = issued;
143 ent->lastmod = lastmod;
144 ent->issuer = strdup(issuer);
145 ent->target = strdup(target);
146 ent->expires = expires;
147 ent->lifetime = lifetime;
148 ent->reason = strdup(reason);
149 dict_insert(gline_dict, ent->target, ent);
151 heap_insert(gline_heap, ent, ent);
152 if (!prev_first || (ent->lifetime < prev_first->lifetime)) {
153 timeq_del(0, gline_expire, 0, TIMEQ_IGNORE_WHEN|TIMEQ_IGNORE_DATA);
154 timeq_add(ent->lifetime, gline_expire, 0);
157 irc_gline(NULL, ent);
162 gline_alternate_target(const char *target)
164 const char *hostname;
166 /* If no host part, bail. */
167 if (!(hostname = strchr(target, '@')))
169 /* If host part contains wildcards, bail. */
170 if (hostname[strcspn(hostname, "*?/")])
172 /* Get parsed address and canonical name for host. */
174 irc_in_addr_t in; /* move this to the right place */
175 if (irc_pton(&in, NULL, hostname+1)) {
176 if (getnameinfo(/*TODO*/))
178 } else if (!getaddrinfo(/*TODO*/)) {
186 gline_find(const char *target)
192 res = dict_find(gline_dict, target, NULL);
195 /* Stock ircu requires BADCHANs to match exactly. */
196 if ((target[0] == '#') || (target[0] == '&'))
198 else if (target[strcspn(target, "*?")]) {
199 /* Wildcard: do an obnoxiously long search. */
200 for (it = dict_first(gline_dict); it; it = iter_next(it)) {
202 if (match_ircglob(target, res->target))
206 /* See if we can resolve the hostname part of the mask. */
207 if ((alt_target = gline_alternate_target(target))) {
208 res = gline_find(alt_target);
216 gline_refresh_helper(UNUSED_ARG(void *key), void *data, void *extra)
218 struct gline *ge = data;
219 irc_gline(extra, ge);
224 gline_refresh_server(struct server *srv)
226 heap_remove_pred(gline_heap, gline_refresh_helper, srv);
230 gline_refresh_all(void)
232 heap_remove_pred(gline_heap, gline_refresh_helper, 0);
238 return dict_size(gline_dict);
242 gline_add_record(const char *key, void *data, UNUSED_ARG(void *extra))
244 struct record_data *rd = data;
245 const char *issuer, *reason, *dstr;
246 unsigned long issued, expiration, lastmod, lifetime;
248 if (!(reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING))) {
249 log_module(MAIN_LOG, LOG_ERROR, "Missing reason for gline %s", key);
252 if (!(dstr = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING))) {
253 log_module(MAIN_LOG, LOG_ERROR, "Missing expiration for gline %s", key);
256 expiration = strtoul(dstr, NULL, 0);
257 dstr = database_get_data(rd->d.object, KEY_LIFETIME, RECDB_QSTRING);
258 lifetime = dstr ? strtoul(dstr, NULL, 0) : expiration;
259 dstr = database_get_data(rd->d.object, KEY_LASTMOD, RECDB_QSTRING);
260 lastmod = dstr ? strtoul(dstr, NULL, 0) : 0;
261 dstr = database_get_data(rd->d.object, KEY_ISSUED, RECDB_QSTRING);
262 issued = dstr ? strtoul(dstr, NULL, 0) : now;
263 if (!(issuer = database_get_data(rd->d.object, KEY_ISSUER, RECDB_QSTRING)))
264 issuer = "<unknown>";
266 gline_add(issuer, key, expiration - now, reason, issued, lastmod, lifetime, 0);
271 gline_saxdb_read(struct dict *db)
273 return dict_foreach(db, gline_add_record, 0) != NULL;
277 gline_write_entry(UNUSED_ARG(void *key), void *data, void *extra)
279 struct gline *ent = data;
280 struct saxdb_context *ctx = extra;
282 saxdb_start_record(ctx, ent->target, 0);
283 saxdb_write_int(ctx, KEY_EXPIRES, ent->expires);
284 saxdb_write_int(ctx, KEY_ISSUED, ent->issued);
285 saxdb_write_int(ctx, KEY_LIFETIME, ent->lifetime);
287 saxdb_write_int(ctx, KEY_LASTMOD, ent->lastmod);
288 saxdb_write_string(ctx, KEY_REASON, ent->reason);
289 saxdb_write_string(ctx, KEY_ISSUER, ent->issuer);
290 saxdb_end_record(ctx);
295 gline_saxdb_write(struct saxdb_context *ctx)
297 heap_remove_pred(gline_heap, gline_write_entry, ctx);
302 gline_db_cleanup(void)
304 heap_delete(gline_heap);
305 dict_delete(gline_dict);
311 gline_heap = heap_new(gline_comparator);
312 gline_dict = dict_new();
313 dict_set_free_data(gline_dict, free_gline_from_dict);
314 saxdb_register("gline", gline_saxdb_read, gline_saxdb_write);
315 reg_exit_func(gline_db_cleanup);
318 struct gline_discrim *
319 gline_discrim_create(struct userNode *user, struct userNode *src, unsigned int argc, char *argv[])
322 struct gline_discrim *discrim;
324 discrim = calloc(1, sizeof(*discrim));
326 discrim->max_issued = ULONG_MAX;
327 discrim->max_lastmod = ULONG_MAX;
328 discrim->max_lifetime = ULONG_MAX;
330 for (i=0; i<argc; i++) {
332 send_message(user, src, "MSG_MISSING_PARAMS", argv[i]);
334 } else if (!irccasecmp(argv[i], "mask") || !irccasecmp(argv[i], "host")) {
335 if (!irccasecmp(argv[++i], "exact"))
336 discrim->target_mask_type = EXACT;
337 else if (!irccasecmp(argv[i], "subset"))
338 discrim->target_mask_type = SUBSET;
339 else if (!irccasecmp(argv[i], "superset"))
340 discrim->target_mask_type = SUPERSET;
342 discrim->target_mask_type = SUBSET, i--;
344 send_message(user, src, "MSG_MISSING_PARAMS", argv[i-1]);
347 if (!is_gline(argv[i]) && !IsChannelName(argv[i])) {
348 send_message(user, src, "MSG_INVALID_GLINE", argv[i]);
351 discrim->target_mask = argv[i];
352 discrim->alt_target_mask = gline_alternate_target(discrim->target_mask);
353 } else if (!irccasecmp(argv[i], "limit"))
354 discrim->limit = strtoul(argv[++i], NULL, 0);
355 else if (!irccasecmp(argv[i], "reason"))
356 discrim->reason_mask = argv[++i];
357 else if (!irccasecmp(argv[i], "issuer"))
358 discrim->issuer_mask = argv[++i];
359 else if (!irccasecmp(argv[i], "after"))
360 discrim->min_expire = now + ParseInterval(argv[++i]);
361 else if (!irccasecmp(argv[i], "before"))
362 discrim->max_issued = now - ParseInterval(argv[++i]);
363 else if (!irccasecmp(argv[i], "lastmod")) {
364 const char *cmp = argv[++i];
367 discrim->min_lastmod = now - ParseInterval(cmp + 2);
369 discrim->min_lastmod = now - (ParseInterval(cmp + 1) - 1);
371 } else if (cmp[0] == '>') {
373 discrim->max_lastmod = now - ParseInterval(cmp + 2);
375 discrim->max_lastmod = now - (ParseInterval(cmp + 1) - 1);
378 discrim->min_lastmod = now - ParseInterval(cmp + 2);
380 } else if (!irccasecmp(argv[i], "lifetime")) {
381 const char *cmp = argv[++i];
384 discrim->min_lifetime = now - ParseInterval(cmp + 2);
386 discrim->min_lifetime = now - (ParseInterval(cmp + 1) - 1);
388 } else if (cmp[0] == '>') {
390 discrim->max_lifetime = now - ParseInterval(cmp + 2);
392 discrim->max_lifetime = now - (ParseInterval(cmp + 1) - 1);
395 discrim->min_lifetime = now - ParseInterval(cmp + 2);
398 send_message(user, src, "MSG_INVALID_CRITERIA", argv[i]);
404 free(discrim->alt_target_mask);
409 struct gline_search {
410 struct gline_discrim *discrim;
411 gline_search_func func;
417 gline_discrim_match(struct gline *gline, struct gline_discrim *discrim)
419 if ((discrim->issuer_mask && !match_ircglob(gline->issuer, discrim->issuer_mask))
420 || (discrim->reason_mask && !match_ircglob(gline->reason, discrim->reason_mask))
421 || (discrim->target_mask
422 && (((discrim->target_mask_type == SUBSET)
423 && !match_ircglobs(discrim->target_mask, gline->target)
424 && (!discrim->alt_target_mask
425 || !match_ircglobs(discrim->alt_target_mask, gline->target)))
426 || ((discrim->target_mask_type == EXACT)
427 && irccasecmp(discrim->target_mask, gline->target)
428 && (!discrim->alt_target_mask
429 || !irccasecmp(discrim->alt_target_mask, gline->target)))
430 || ((discrim->target_mask_type == SUPERSET)
431 && !match_ircglobs(gline->target, discrim->target_mask)
432 && (!discrim->alt_target_mask
433 || !match_ircglobs(discrim->alt_target_mask, gline->target)))))
434 || (discrim->max_issued < gline->issued)
435 || (discrim->min_expire > gline->expires)
436 || (discrim->min_lastmod > gline->lastmod)
437 || (discrim->max_lastmod < gline->lastmod)
438 || (discrim->min_lifetime > gline->lifetime)
439 || (discrim->max_lifetime < gline->lifetime)) {
446 gline_search_helper(UNUSED_ARG(void *key), void *data, void *extra)
448 struct gline *gline = data;
449 struct gline_search *search = extra;
451 if (gline_discrim_match(gline, search->discrim)
452 && (search->hits++ < search->discrim->limit)) {
453 search->func(gline, search->data);
459 gline_discrim_search(struct gline_discrim *discrim, gline_search_func gsf, void *data)
461 struct gline_search search;
462 search.discrim = discrim;
466 heap_remove_pred(gline_heap, gline_search_helper, &search);