]> jfr.im git - irc/evilnet/x3.git/blame - src/gline.c
Fixed header comments
[irc/evilnet/x3.git] / src / gline.c
CommitLineData
d76ed9a9 1/* gline.c - Gline database
2 * Copyright 2000-2004 srvx Development Team
3 *
83ff05c3 4 * This file is part of x3.
d76ed9a9 5 *
d0f04f71 6 * x3 is free software; you can redistribute it and/or modify
d76ed9a9 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.
10 *
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.
15 *
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.
19 */
20
21#include "heap.h"
22#include "helpfile.h"
23#include "log.h"
24#include "saxdb.h"
25#include "timeq.h"
26#include "gline.h"
27
28#ifdef HAVE_SYS_SOCKET_H
29#include <sys/socket.h>
30#endif
31#ifdef HAVE_NETINET_IN_H
32#include <netinet/in.h>
33#endif
34#ifdef HAVE_ARPA_INET_H
35#include <arpa/inet.h>
36#endif
37#ifdef HAVE_NETDB_H
38#include <netdb.h>
39#endif
40
41#define KEY_REASON "reason"
42#define KEY_EXPIRES "expires"
43#define KEY_ISSUER "issuer"
44#define KEY_ISSUED "issued"
45
46static heap_t gline_heap; /* key: expiry time, data: struct gline_entry* */
47static dict_t gline_dict; /* key: target, data: struct gline_entry* */
48
49static int
50gline_comparator(const void *a, const void *b)
51{
52 const struct gline *ga=a, *gb=b;
53 return ga->expires - gb->expires;
54}
55
56static void
57free_gline_from_dict(void *data)
58{
59 struct gline *ent = data;
60 free(ent->issuer);
61 free(ent->target);
62 free(ent->reason);
63 free(ent);
64}
65
66static void
67free_gline(struct gline *ent)
68{
69 dict_remove(gline_dict, ent->target);
70}
71
72static int
73gline_for_p(UNUSED_ARG(void *key), void *data, void *extra)
74{
75 struct gline *ge = data;
76 return !irccasecmp(ge->target, extra);
77}
78
79static int
80delete_gline_for_p(UNUSED_ARG(void *key), void *data, void *extra)
81{
82 struct gline *ge = data;
83
84 if (!irccasecmp(ge->target, extra)) {
85 free_gline(ge);
86 return 1;
87 } else {
88 return 0;
89 }
90}
91
92static void
93gline_expire(UNUSED_ARG(void *data))
94{
95 time_t stopped;
96 void *wraa;
97
98 stopped = 0;
99 while (heap_size(gline_heap)) {
100 heap_peek(gline_heap, 0, &wraa);
101 stopped = ((struct gline*)wraa)->expires;
102 if (stopped > now)
103 break;
104 heap_pop(gline_heap);
105 free_gline(wraa);
106 }
107 if (heap_size(gline_heap))
108 timeq_add(stopped, gline_expire, NULL);
109}
110
111int
112gline_remove(const char *target, int announce)
113{
114 int res = dict_find(gline_dict, target, NULL) ? 1 : 0;
115 if (heap_remove_pred(gline_heap, delete_gline_for_p, (char*)target)) {
116 void *argh;
117 struct gline *new_first;
118 heap_peek(gline_heap, 0, &argh);
119 if (argh) {
120 new_first = argh;
121 timeq_del(0, gline_expire, 0, TIMEQ_IGNORE_WHEN|TIMEQ_IGNORE_DATA);
122 timeq_add(new_first->expires, gline_expire, 0);
123 }
124 }
125#ifdef WITH_PROTOCOL_BAHAMUT
126 /* Bahamut is sort of lame: It permanently remembers any AKILLs
127 * with durations longer than a day, and will never auto-expire
128 * them. So when the time comes, we'd better remind it. */
129 announce = 1;
130#endif
131 if (announce)
132 irc_ungline(target);
133 return res;
134}
135
136struct gline *
9a75756e 137gline_add(const char *issuer, const char *target, unsigned long duration, const char *reason, time_t issued, int announce, int silent)
d76ed9a9 138{
139 struct gline *ent;
140 struct gline *prev_first;
141 void *argh;
142
143 heap_peek(gline_heap, 0, &argh);
144 prev_first = argh;
145 ent = dict_find(gline_dict, target, NULL);
146 if (ent) {
147 heap_remove_pred(gline_heap, gline_for_p, (char*)target);
148 if (ent->expires < (time_t)(now + duration))
149 ent->expires = now + duration;
150 } else {
151 ent = malloc(sizeof(*ent));
152 ent->issued = issued;
153 ent->issuer = strdup(issuer);
154 ent->target = strdup(target);
155 ent->expires = now + duration;
156 ent->reason = strdup(reason);
157 dict_insert(gline_dict, ent->target, ent);
158 }
159 heap_insert(gline_heap, ent, ent);
160 if (!prev_first || (ent->expires < prev_first->expires)) {
161 timeq_del(0, gline_expire, 0, TIMEQ_IGNORE_WHEN|TIMEQ_IGNORE_DATA);
162 timeq_add(ent->expires, gline_expire, 0);
163 }
164 if (announce)
9a75756e 165 irc_gline(NULL, ent, silent);
d76ed9a9 166 return ent;
167}
168
169static char *
170gline_alternate_target(const char *target)
171{
172 const char *hostname;
d76ed9a9 173
174 /* If no host part, bail. */
175 if (!(hostname = strchr(target, '@')))
176 return NULL;
177 /* If host part contains wildcards, bail. */
178 if (hostname[strcspn(hostname, "*?/")])
179 return NULL;
2f61d1d7 180#if 0
181 irc_in_addr_t in; /* move this to the right place */
182 if (irc_pton(&in, NULL, hostname+1)) {
183 if (getnameinfo(/*TODO*/))
184 return NULL;
185 } else if (!getaddrinfo(/*TODO*/)) {
186 } else return NULL;
187#else
188 return NULL;
189#endif
d76ed9a9 190}
191
192struct gline *
193gline_find(const char *target)
194{
195 struct gline *res;
196 dict_iterator_t it;
197 char *alt_target;
198
199 res = dict_find(gline_dict, target, NULL);
200 if (res)
201 return res;
202 /* Stock ircu requires BADCHANs to match exactly. */
203 if ((target[0] == '#') || (target[0] == '&'))
204 return NULL;
205 else if (target[strcspn(target, "*?")]) {
206 /* Wildcard: do an obnoxiously long search. */
207 for (it = dict_first(gline_dict); it; it = iter_next(it)) {
208 res = iter_data(it);
209 if (match_ircglob(target, res->target))
210 return res;
211 }
212 }
213 /* See if we can resolve the hostname part of the mask. */
214 if ((alt_target = gline_alternate_target(target))) {
215 res = gline_find(alt_target);
216 free(alt_target);
217 return res;
218 }
219 return NULL;
220}
221
222static int
223gline_refresh_helper(UNUSED_ARG(void *key), void *data, void *extra)
224{
225 struct gline *ge = data;
9a75756e 226 irc_gline(extra, ge, 0);
d76ed9a9 227 return 0;
228}
229
230void
231gline_refresh_server(struct server *srv)
232{
233 heap_remove_pred(gline_heap, gline_refresh_helper, srv);
234}
235
236void
237gline_refresh_all(void)
238{
239 heap_remove_pred(gline_heap, gline_refresh_helper, 0);
240}
241
242unsigned int
243gline_count(void)
244{
245 return dict_size(gline_dict);
246}
247
248static int
249gline_add_record(const char *key, void *data, UNUSED_ARG(void *extra))
250{
251 struct record_data *rd = data;
252 const char *issuer, *reason, *dstr;
253 time_t issued, expiration;
254
255 if (!(reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING))) {
256 log_module(MAIN_LOG, LOG_ERROR, "Missing reason for gline %s", key);
257 return 0;
258 }
259 if (!(dstr = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING))) {
260 log_module(MAIN_LOG, LOG_ERROR, "Missing expiration for gline %s", key);
261 return 0;
262 }
263 expiration = strtoul(dstr, NULL, 0);
264 if ((dstr = database_get_data(rd->d.object, KEY_ISSUED, RECDB_QSTRING))) {
265 issued = strtoul(dstr, NULL, 0);
266 } else {
267 issued = now;
268 }
269 if (!(issuer = database_get_data(rd->d.object, KEY_ISSUER, RECDB_QSTRING))) {
270 issuer = "<unknown>";
271 }
272 if (expiration > now)
9a75756e 273 gline_add(issuer, key, expiration - now, reason, issued, 0, 0);
d76ed9a9 274 return 0;
275}
276
277static int
278gline_saxdb_read(struct dict *db)
279{
280 return dict_foreach(db, gline_add_record, 0) != NULL;
281}
282
283static int
284gline_write_entry(UNUSED_ARG(void *key), void *data, void *extra)
285{
286 struct gline *ent = data;
287 struct saxdb_context *ctx = extra;
288
289 saxdb_start_record(ctx, ent->target, 0);
290 saxdb_write_int(ctx, KEY_EXPIRES, ent->expires);
291 saxdb_write_int(ctx, KEY_ISSUED, ent->issued);
292 saxdb_write_string(ctx, KEY_REASON, ent->reason);
293 saxdb_write_string(ctx, KEY_ISSUER, ent->issuer);
294 saxdb_end_record(ctx);
295 return 0;
296}
297
298static int
299gline_saxdb_write(struct saxdb_context *ctx)
300{
301 heap_remove_pred(gline_heap, gline_write_entry, ctx);
302 return 0;
303}
304
305static void
306gline_db_cleanup(void)
307{
308 heap_delete(gline_heap);
309 dict_delete(gline_dict);
310}
311
312void
313gline_init(void)
314{
315 gline_heap = heap_new(gline_comparator);
316 gline_dict = dict_new();
317 dict_set_free_data(gline_dict, free_gline_from_dict);
318 saxdb_register("gline", gline_saxdb_read, gline_saxdb_write);
319 reg_exit_func(gline_db_cleanup);
320}
321
322struct gline_discrim *
323gline_discrim_create(struct userNode *user, struct userNode *src, unsigned int argc, char *argv[])
324{
325 unsigned int i;
326 struct gline_discrim *discrim;
327
328 discrim = calloc(1, sizeof(*discrim));
329 discrim->max_issued = now;
330 discrim->limit = 50;
331
332 for (i=0; i<argc; i++) {
333 if (i + 2 > argc) {
334 send_message(user, src, "MSG_MISSING_PARAMS", argv[i]);
335 goto fail;
336 } else if (!irccasecmp(argv[i], "mask") || !irccasecmp(argv[i], "host")) {
337 if (!irccasecmp(argv[++i], "exact"))
338 discrim->target_mask_type = EXACT;
339 else if (!irccasecmp(argv[i], "subset"))
340 discrim->target_mask_type = SUBSET;
341 else if (!irccasecmp(argv[i], "superset"))
342 discrim->target_mask_type = SUPERSET;
343 else
344 discrim->target_mask_type = SUBSET, i--;
345 if (++i == argc) {
346 send_message(user, src, "MSG_MISSING_PARAMS", argv[i-1]);
347 goto fail;
348 }
349 if (!is_gline(argv[i]) && !IsChannelName(argv[i])) {
350 send_message(user, src, "MSG_INVALID_GLINE", argv[i]);
351 goto fail;
352 }
353 discrim->target_mask = argv[i];
354 discrim->alt_target_mask = gline_alternate_target(discrim->target_mask);
355 } else if (!irccasecmp(argv[i], "limit"))
356 discrim->limit = strtoul(argv[++i], NULL, 0);
357 else if (!irccasecmp(argv[i], "reason"))
358 discrim->reason_mask = argv[++i];
359 else if (!irccasecmp(argv[i], "issuer"))
360 discrim->issuer_mask = argv[++i];
361 else if (!irccasecmp(argv[i], "after"))
362 discrim->min_expire = now + ParseInterval(argv[++i]);
363 else if (!irccasecmp(argv[i], "before"))
364 discrim->max_issued = now - ParseInterval(argv[++i]);
365 else {
366 send_message(user, src, "MSG_INVALID_CRITERIA", argv[i]);
367 goto fail;
368 }
369 }
370 return discrim;
371 fail:
372 free(discrim->alt_target_mask);
373 free(discrim);
374 return NULL;
375}
376
377struct gline_search {
378 struct gline_discrim *discrim;
379 gline_search_func func;
380 void *data;
381 unsigned int hits;
382};
383
384static int
385gline_discrim_match(struct gline *gline, struct gline_discrim *discrim)
386{
387 if ((discrim->issuer_mask && !match_ircglob(gline->issuer, discrim->issuer_mask))
388 || (discrim->reason_mask && !match_ircglob(gline->reason, discrim->reason_mask))
389 || (discrim->target_mask
390 && (((discrim->target_mask_type == SUBSET)
391 && !match_ircglobs(discrim->target_mask, gline->target)
392 && (!discrim->alt_target_mask
393 || !match_ircglobs(discrim->alt_target_mask, gline->target)))
394 || ((discrim->target_mask_type == EXACT)
395 && irccasecmp(discrim->target_mask, gline->target)
396 && (!discrim->alt_target_mask
397 || !irccasecmp(discrim->alt_target_mask, gline->target)))
398 || ((discrim->target_mask_type == SUPERSET)
399 && !match_ircglobs(gline->target, discrim->target_mask)
400 && (!discrim->alt_target_mask
401 || !match_ircglobs(discrim->alt_target_mask, gline->target)))))
402 || (discrim->max_issued < gline->issued)
403 || (discrim->min_expire > gline->expires)) {
404 return 0;
405 }
406 return 1;
407}
408
409static int
410gline_search_helper(UNUSED_ARG(void *key), void *data, void *extra)
411{
412 struct gline *gline = data;
413 struct gline_search *search = extra;
414
415 if (gline_discrim_match(gline, search->discrim)
416 && (search->hits++ < search->discrim->limit)) {
417 search->func(gline, search->data);
418 }
419 return 0;
420}
421
422unsigned int
423gline_discrim_search(struct gline_discrim *discrim, gline_search_func gsf, void *data)
424{
425 struct gline_search search;
426 search.discrim = discrim;
427 search.func = gsf;
428 search.data = data;
429 search.hits = 0;
430 heap_remove_pred(gline_heap, gline_search_helper, &search);
431 return search.hits;
432}