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"
47 static heap_t gline_heap; /* key: expiry time, data: struct gline_entry* */
48 static dict_t gline_dict; /* key: target, data: struct gline_entry* */
51 gline_comparator(const void *a, const void *b)
53 const struct gline *ga=a, *gb=b;
54 return ga->expires - gb->expires;
58 free_gline_from_dict(void *data)
60 struct gline *ent = data;
68 free_gline(struct gline *ent)
70 dict_remove(gline_dict, ent->target);
74 gline_for_p(UNUSED_ARG(void *key), void *data, void *extra)
76 struct gline *ge = data;
77 return !irccasecmp(ge->target, extra);
81 delete_gline_for_p(UNUSED_ARG(void *key), void *data, void *extra)
83 struct gline *ge = data;
85 if (!irccasecmp(ge->target, extra)) {
94 gline_expire(UNUSED_ARG(void *data))
96 unsigned long stopped;
100 while (heap_size(gline_heap)) {
101 heap_peek(gline_heap, 0, &wraa);
102 stopped = ((struct gline*)wraa)->expires;
105 heap_pop(gline_heap);
108 if (heap_size(gline_heap))
109 timeq_add(stopped, gline_expire, NULL);
113 gline_remove(const char *target, int announce)
115 int res = dict_find(gline_dict, target, NULL) ? 1 : 0;
116 if (heap_remove_pred(gline_heap, delete_gline_for_p, (char*)target)) {
118 struct gline *new_first;
119 heap_peek(gline_heap, 0, &argh);
122 timeq_del(0, gline_expire, 0, TIMEQ_IGNORE_WHEN|TIMEQ_IGNORE_DATA);
123 timeq_add(new_first->expires, gline_expire, 0);
126 #ifdef WITH_PROTOCOL_BAHAMUT
127 /* Bahamut is sort of lame: It permanently remembers any AKILLs
128 * with durations longer than a day, and will never auto-expire
129 * them. So when the time comes, we'd better remind it. */
138 gline_add(const char *issuer, const char *target, unsigned long duration, const char *reason, unsigned long issued, unsigned long lastmod, int announce)
141 struct gline *prev_first;
144 heap_peek(gline_heap, 0, &argh);
146 ent = dict_find(gline_dict, target, NULL);
148 heap_remove_pred(gline_heap, gline_for_p, (char*)target);
149 if (ent->expires < now + duration)
150 ent->expires = now + duration;
151 if (ent->lastmod < lastmod)
152 ent->lastmod = lastmod;
154 ent = malloc(sizeof(*ent));
155 ent->issued = issued;
156 ent->lastmod = lastmod;
157 ent->issuer = strdup(issuer);
158 ent->target = strdup(target);
159 ent->expires = now + duration;
160 ent->reason = strdup(reason);
161 dict_insert(gline_dict, ent->target, ent);
163 heap_insert(gline_heap, ent, ent);
164 if (!prev_first || (ent->expires < prev_first->expires)) {
165 timeq_del(0, gline_expire, 0, TIMEQ_IGNORE_WHEN|TIMEQ_IGNORE_DATA);
166 timeq_add(ent->expires, gline_expire, 0);
169 irc_gline(NULL, ent);
174 gline_alternate_target(const char *target)
176 const char *hostname;
178 /* If no host part, bail. */
179 if (!(hostname = strchr(target, '@')))
181 /* If host part contains wildcards, bail. */
182 if (hostname[strcspn(hostname, "*?/")])
184 /* Get parsed address and canonical name for host. */
186 irc_in_addr_t in; /* move this to the right place */
187 if (irc_pton(&in, NULL, hostname+1)) {
188 if (getnameinfo(/*TODO*/))
190 } else if (!getaddrinfo(/*TODO*/)) {
198 gline_find(const char *target)
204 res = dict_find(gline_dict, target, NULL);
207 /* Stock ircu requires BADCHANs to match exactly. */
208 if ((target[0] == '#') || (target[0] == '&'))
210 else if (target[strcspn(target, "*?")]) {
211 /* Wildcard: do an obnoxiously long search. */
212 for (it = dict_first(gline_dict); it; it = iter_next(it)) {
214 if (match_ircglob(target, res->target))
218 /* See if we can resolve the hostname part of the mask. */
219 if ((alt_target = gline_alternate_target(target))) {
220 res = gline_find(alt_target);
228 gline_refresh_helper(UNUSED_ARG(void *key), void *data, void *extra)
230 struct gline *ge = data;
231 irc_gline(extra, ge);
236 gline_refresh_server(struct server *srv)
238 heap_remove_pred(gline_heap, gline_refresh_helper, srv);
242 gline_refresh_all(void)
244 heap_remove_pred(gline_heap, gline_refresh_helper, 0);
250 return dict_size(gline_dict);
254 gline_add_record(const char *key, void *data, UNUSED_ARG(void *extra))
256 struct record_data *rd = data;
257 const char *issuer, *reason, *dstr;
258 unsigned long issued, expiration, lastmod;
260 if (!(reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING))) {
261 log_module(MAIN_LOG, LOG_ERROR, "Missing reason for gline %s", key);
264 if (!(dstr = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING))) {
265 log_module(MAIN_LOG, LOG_ERROR, "Missing expiration for gline %s", key);
268 expiration = strtoul(dstr, NULL, 0);
269 dstr = database_get_data(rd->d.object, KEY_LASTMOD, RECDB_QSTRING);
270 lastmod = dstr ? strtoul(dstr, NULL, 0) : 0;
271 if ((dstr = database_get_data(rd->d.object, KEY_ISSUED, RECDB_QSTRING))) {
272 issued = strtoul(dstr, NULL, 0);
276 if (!(issuer = database_get_data(rd->d.object, KEY_ISSUER, RECDB_QSTRING))) {
277 issuer = "<unknown>";
279 if (expiration > now)
280 gline_add(issuer, key, expiration - now, reason, issued, lastmod, 0);
285 gline_saxdb_read(struct dict *db)
287 return dict_foreach(db, gline_add_record, 0) != NULL;
291 gline_write_entry(UNUSED_ARG(void *key), void *data, void *extra)
293 struct gline *ent = data;
294 struct saxdb_context *ctx = extra;
296 saxdb_start_record(ctx, ent->target, 0);
297 saxdb_write_int(ctx, KEY_EXPIRES, ent->expires);
298 saxdb_write_int(ctx, KEY_ISSUED, ent->issued);
300 saxdb_write_int(ctx, KEY_LASTMOD, ent->lastmod);
301 saxdb_write_string(ctx, KEY_REASON, ent->reason);
302 saxdb_write_string(ctx, KEY_ISSUER, ent->issuer);
303 saxdb_end_record(ctx);
308 gline_saxdb_write(struct saxdb_context *ctx)
310 heap_remove_pred(gline_heap, gline_write_entry, ctx);
315 gline_db_cleanup(void)
317 heap_delete(gline_heap);
318 dict_delete(gline_dict);
324 gline_heap = heap_new(gline_comparator);
325 gline_dict = dict_new();
326 dict_set_free_data(gline_dict, free_gline_from_dict);
327 saxdb_register("gline", gline_saxdb_read, gline_saxdb_write);
328 reg_exit_func(gline_db_cleanup);
331 struct gline_discrim *
332 gline_discrim_create(struct userNode *user, struct userNode *src, unsigned int argc, char *argv[])
335 struct gline_discrim *discrim;
337 discrim = calloc(1, sizeof(*discrim));
339 discrim->max_issued = INT_MAX;
340 discrim->max_lastmod = INT_MAX;
342 for (i=0; i<argc; i++) {
344 send_message(user, src, "MSG_MISSING_PARAMS", argv[i]);
346 } else if (!irccasecmp(argv[i], "mask") || !irccasecmp(argv[i], "host")) {
347 if (!irccasecmp(argv[++i], "exact"))
348 discrim->target_mask_type = EXACT;
349 else if (!irccasecmp(argv[i], "subset"))
350 discrim->target_mask_type = SUBSET;
351 else if (!irccasecmp(argv[i], "superset"))
352 discrim->target_mask_type = SUPERSET;
354 discrim->target_mask_type = SUBSET, i--;
356 send_message(user, src, "MSG_MISSING_PARAMS", argv[i-1]);
359 if (!is_gline(argv[i]) && !IsChannelName(argv[i])) {
360 send_message(user, src, "MSG_INVALID_GLINE", argv[i]);
363 discrim->target_mask = argv[i];
364 discrim->alt_target_mask = gline_alternate_target(discrim->target_mask);
365 } else if (!irccasecmp(argv[i], "limit"))
366 discrim->limit = strtoul(argv[++i], NULL, 0);
367 else if (!irccasecmp(argv[i], "reason"))
368 discrim->reason_mask = argv[++i];
369 else if (!irccasecmp(argv[i], "issuer"))
370 discrim->issuer_mask = argv[++i];
371 else if (!irccasecmp(argv[i], "after"))
372 discrim->min_expire = now + ParseInterval(argv[++i]);
373 else if (!irccasecmp(argv[i], "before"))
374 discrim->max_issued = now - ParseInterval(argv[++i]);
375 else if (!irccasecmp(argv[i], "lastmod")) {
376 const char *cmp = argv[++i];
379 discrim->min_lastmod = now - ParseInterval(cmp + 2);
381 discrim->min_lastmod = now - (ParseInterval(cmp + 1) - 1);
383 } else if (cmp[0] == '>') {
385 discrim->max_lastmod = now - ParseInterval(cmp + 2);
387 discrim->max_lastmod = now - (ParseInterval(cmp + 1) - 1);
390 discrim->min_lastmod = now - ParseInterval(cmp + 2);
393 send_message(user, src, "MSG_INVALID_CRITERIA", argv[i]);
399 free(discrim->alt_target_mask);
404 struct gline_search {
405 struct gline_discrim *discrim;
406 gline_search_func func;
412 gline_discrim_match(struct gline *gline, struct gline_discrim *discrim)
414 if ((discrim->issuer_mask && !match_ircglob(gline->issuer, discrim->issuer_mask))
415 || (discrim->reason_mask && !match_ircglob(gline->reason, discrim->reason_mask))
416 || (discrim->target_mask
417 && (((discrim->target_mask_type == SUBSET)
418 && !match_ircglobs(discrim->target_mask, gline->target)
419 && (!discrim->alt_target_mask
420 || !match_ircglobs(discrim->alt_target_mask, gline->target)))
421 || ((discrim->target_mask_type == EXACT)
422 && irccasecmp(discrim->target_mask, gline->target)
423 && (!discrim->alt_target_mask
424 || !irccasecmp(discrim->alt_target_mask, gline->target)))
425 || ((discrim->target_mask_type == SUPERSET)
426 && !match_ircglobs(gline->target, discrim->target_mask)
427 && (!discrim->alt_target_mask
428 || !match_ircglobs(discrim->alt_target_mask, gline->target)))))
429 || (discrim->max_issued < gline->issued)
430 || (discrim->min_expire > gline->expires)
431 || (discrim->min_lastmod > gline->lastmod)
432 || (discrim->max_lastmod < gline->lastmod)) {
439 gline_search_helper(UNUSED_ARG(void *key), void *data, void *extra)
441 struct gline *gline = data;
442 struct gline_search *search = extra;
444 if (gline_discrim_match(gline, search->discrim)
445 && (search->hits++ < search->discrim->limit)) {
446 search->func(gline, search->data);
452 gline_discrim_search(struct gline_discrim *discrim, gline_search_func gsf, void *data)
454 struct gline_search search;
455 search.discrim = discrim;
459 heap_remove_pred(gline_heap, gline_search_helper, &search);