]> jfr.im git - irc/evilnet/x3.git/blob - src/gline.c
Fix typos and copy errors in memoserv help
[irc/evilnet/x3.git] / src / gline.c
1 /* gline.c - Gline database
2 * Copyright 2000-2004 srvx Development Team
3 *
4 * This file is part of x3.
5 *
6 * x3 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 3 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
46 static heap_t gline_heap; /* key: expiry time, data: struct gline_entry* */
47 static dict_t gline_dict; /* key: target, data: struct gline_entry* */
48
49 static int
50 gline_comparator(const void *a, const void *b)
51 {
52 const struct gline *ga=a, *gb=b;
53 return ga->expires - gb->expires;
54 }
55
56 static void
57 free_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
66 static void
67 free_gline(struct gline *ent)
68 {
69 dict_remove(gline_dict, ent->target);
70 }
71
72 static int
73 gline_for_p(UNUSED_ARG(void *key), void *data, void *extra)
74 {
75 struct gline *ge = data;
76 return !irccasecmp(ge->target, extra);
77 }
78
79 static int
80 delete_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
92 static void
93 gline_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
111 int
112 gline_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 if (announce)
126 irc_ungline(target);
127 return res;
128 }
129
130 struct gline *
131 gline_add(const char *issuer, const char *target, unsigned long duration, const char *reason, time_t issued, int announce, int silent)
132 {
133 struct gline *ent;
134 struct gline *prev_first;
135 void *argh;
136
137 heap_peek(gline_heap, 0, &argh);
138 prev_first = argh;
139 ent = dict_find(gline_dict, target, NULL);
140 if (ent) {
141 heap_remove_pred(gline_heap, gline_for_p, (char*)target);
142 if (ent->expires < (time_t)(now + duration))
143 ent->expires = now + duration;
144 } else {
145 ent = malloc(sizeof(*ent));
146 ent->issued = issued;
147 ent->issuer = strdup(issuer);
148 ent->target = strdup(target);
149 ent->expires = now + duration;
150 ent->reason = strdup(reason);
151 dict_insert(gline_dict, ent->target, ent);
152 }
153 heap_insert(gline_heap, ent, ent);
154 if (!prev_first || (ent->expires < prev_first->expires)) {
155 timeq_del(0, gline_expire, 0, TIMEQ_IGNORE_WHEN|TIMEQ_IGNORE_DATA);
156 timeq_add(ent->expires, gline_expire, 0);
157 }
158 if (announce)
159 irc_gline(NULL, ent, silent);
160 return ent;
161 }
162
163 static char *
164 gline_alternate_target(const char *target)
165 {
166 const char *hostname;
167
168 /* If no host part, bail. */
169 if (!(hostname = strchr(target, '@')))
170 return NULL;
171 /* If host part contains wildcards, bail. */
172 if (hostname[strcspn(hostname, "*?/")])
173 return NULL;
174 #if 0
175 irc_in_addr_t in; /* move this to the right place */
176 if (irc_pton(&in, NULL, hostname+1)) {
177 if (getnameinfo(/*TODO*/))
178 return NULL;
179 } else if (!getaddrinfo(/*TODO*/)) {
180 } else return NULL;
181 #else
182 return NULL;
183 #endif
184 }
185
186 struct gline *
187 gline_find(const char *target)
188 {
189 struct gline *res;
190 dict_iterator_t it;
191 char *alt_target;
192
193 res = dict_find(gline_dict, target, NULL);
194 if (res)
195 return res;
196 /* Stock ircu requires BADCHANs to match exactly. */
197 if ((target[0] == '#') || (target[0] == '&'))
198 return NULL;
199 else if (target[strcspn(target, "*?")]) {
200 /* Wildcard: do an obnoxiously long search. */
201 for (it = dict_first(gline_dict); it; it = iter_next(it)) {
202 res = iter_data(it);
203 if (match_ircglob(target, res->target))
204 return res;
205 }
206 }
207 /* See if we can resolve the hostname part of the mask. */
208 if ((alt_target = gline_alternate_target(target))) {
209 res = gline_find(alt_target);
210 free(alt_target);
211 return res;
212 }
213 return NULL;
214 }
215
216 static int
217 gline_refresh_helper(UNUSED_ARG(void *key), void *data, void *extra)
218 {
219 struct gline *ge = data;
220 irc_gline(extra, ge, 0);
221 return 0;
222 }
223
224 void
225 gline_refresh_server(struct server *srv)
226 {
227 heap_remove_pred(gline_heap, gline_refresh_helper, srv);
228 }
229
230 void
231 gline_refresh_all(void)
232 {
233 heap_remove_pred(gline_heap, gline_refresh_helper, 0);
234 }
235
236 unsigned int
237 gline_count(void)
238 {
239 return dict_size(gline_dict);
240 }
241
242 static int
243 gline_add_record(const char *key, void *data, UNUSED_ARG(void *extra))
244 {
245 struct record_data *rd = data;
246 const char *issuer, *reason, *dstr;
247 time_t issued, expiration;
248
249 if (!(reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING))) {
250 log_module(MAIN_LOG, LOG_ERROR, "Missing reason for gline %s", key);
251 return 0;
252 }
253 if (!(dstr = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING))) {
254 log_module(MAIN_LOG, LOG_ERROR, "Missing expiration for gline %s", key);
255 return 0;
256 }
257 expiration = strtoul(dstr, NULL, 0);
258 if ((dstr = database_get_data(rd->d.object, KEY_ISSUED, RECDB_QSTRING))) {
259 issued = strtoul(dstr, NULL, 0);
260 } else {
261 issued = now;
262 }
263 if (!(issuer = database_get_data(rd->d.object, KEY_ISSUER, RECDB_QSTRING))) {
264 issuer = "<unknown>";
265 }
266 if (expiration > now)
267 gline_add(issuer, key, expiration - now, reason, issued, 0, 0);
268 return 0;
269 }
270
271 static int
272 gline_saxdb_read(struct dict *db)
273 {
274 return dict_foreach(db, gline_add_record, 0) != NULL;
275 }
276
277 static int
278 gline_write_entry(UNUSED_ARG(void *key), void *data, void *extra)
279 {
280 struct gline *ent = data;
281 struct saxdb_context *ctx = extra;
282
283 saxdb_start_record(ctx, ent->target, 0);
284 saxdb_write_int(ctx, KEY_EXPIRES, ent->expires);
285 saxdb_write_int(ctx, KEY_ISSUED, ent->issued);
286 saxdb_write_string(ctx, KEY_REASON, ent->reason);
287 saxdb_write_string(ctx, KEY_ISSUER, ent->issuer);
288 saxdb_end_record(ctx);
289 return 0;
290 }
291
292 static int
293 gline_saxdb_write(struct saxdb_context *ctx)
294 {
295 heap_remove_pred(gline_heap, gline_write_entry, ctx);
296 return 0;
297 }
298
299 static void
300 gline_db_cleanup(UNUSED_ARG(void *extra))
301 {
302 heap_delete(gline_heap);
303 dict_delete(gline_dict);
304 }
305
306 void
307 gline_init(void)
308 {
309 gline_heap = heap_new(gline_comparator);
310 gline_dict = dict_new();
311 dict_set_free_data(gline_dict, free_gline_from_dict);
312 saxdb_register("gline", gline_saxdb_read, gline_saxdb_write);
313 reg_exit_func(gline_db_cleanup, NULL);
314 }
315
316 struct gline_discrim *
317 gline_discrim_create(struct userNode *user, struct userNode *src, unsigned int argc, char *argv[])
318 {
319 unsigned int i;
320 struct gline_discrim *discrim;
321
322 discrim = calloc(1, sizeof(*discrim));
323 discrim->max_issued = now;
324 discrim->limit = 50;
325
326 for (i=0; i<argc; i++) {
327 if (i + 2 > argc) {
328 send_message(user, src, "MSG_MISSING_PARAMS", argv[i]);
329 goto fail;
330 } else if (!irccasecmp(argv[i], "mask") || !irccasecmp(argv[i], "host")) {
331 if (!irccasecmp(argv[++i], "exact"))
332 discrim->target_mask_type = EXACT;
333 else if (!irccasecmp(argv[i], "subset"))
334 discrim->target_mask_type = SUBSET;
335 else if (!irccasecmp(argv[i], "superset"))
336 discrim->target_mask_type = SUPERSET;
337 else
338 discrim->target_mask_type = SUBSET, i--;
339 if (++i == argc) {
340 send_message(user, src, "MSG_MISSING_PARAMS", argv[i-1]);
341 goto fail;
342 }
343 if (!is_gline(argv[i]) && !IsChannelName(argv[i])) {
344 send_message(user, src, "MSG_INVALID_GLINE", argv[i]);
345 goto fail;
346 }
347 discrim->target_mask = argv[i];
348 discrim->alt_target_mask = gline_alternate_target(discrim->target_mask);
349 } else if (!irccasecmp(argv[i], "limit"))
350 discrim->limit = strtoul(argv[++i], NULL, 0);
351 else if (!irccasecmp(argv[i], "reason"))
352 discrim->reason_mask = argv[++i];
353 else if (!irccasecmp(argv[i], "issuer"))
354 discrim->issuer_mask = argv[++i];
355 else if (!irccasecmp(argv[i], "after"))
356 discrim->min_expire = now + ParseInterval(argv[++i]);
357 else if (!irccasecmp(argv[i], "before"))
358 discrim->max_issued = now - ParseInterval(argv[++i]);
359 else {
360 send_message(user, src, "MSG_INVALID_CRITERIA", argv[i]);
361 goto fail;
362 }
363 }
364 return discrim;
365 fail:
366 free(discrim->alt_target_mask);
367 free(discrim);
368 return NULL;
369 }
370
371 struct gline_search {
372 struct gline_discrim *discrim;
373 gline_search_func func;
374 void *data;
375 unsigned int hits;
376 };
377
378 static int
379 gline_discrim_match(struct gline *gline, struct gline_discrim *discrim)
380 {
381 if ((discrim->issuer_mask && !match_ircglob(gline->issuer, discrim->issuer_mask))
382 || (discrim->reason_mask && !match_ircglob(gline->reason, discrim->reason_mask))
383 || (discrim->target_mask
384 && (((discrim->target_mask_type == SUBSET)
385 && !match_ircglobs(discrim->target_mask, gline->target)
386 && (!discrim->alt_target_mask
387 || !match_ircglobs(discrim->alt_target_mask, gline->target)))
388 || ((discrim->target_mask_type == EXACT)
389 && irccasecmp(discrim->target_mask, gline->target)
390 && (!discrim->alt_target_mask
391 || !irccasecmp(discrim->alt_target_mask, gline->target)))
392 || ((discrim->target_mask_type == SUPERSET)
393 && !match_ircglobs(gline->target, discrim->target_mask)
394 && (!discrim->alt_target_mask
395 || !match_ircglobs(discrim->alt_target_mask, gline->target)))))
396 || (discrim->max_issued < gline->issued)
397 || (discrim->min_expire > gline->expires)) {
398 return 0;
399 }
400 return 1;
401 }
402
403 static int
404 gline_search_helper(UNUSED_ARG(void *key), void *data, void *extra)
405 {
406 struct gline *gline = data;
407 struct gline_search *search = extra;
408
409 if (gline_discrim_match(gline, search->discrim)
410 && (search->hits++ < search->discrim->limit)) {
411 search->func(gline, search->data);
412 }
413 return 0;
414 }
415
416 unsigned int
417 gline_discrim_search(struct gline_discrim *discrim, gline_search_func gsf, void *data)
418 {
419 struct gline_search search;
420 search.discrim = discrim;
421 search.func = gsf;
422 search.data = data;
423 search.hits = 0;
424 heap_remove_pred(gline_heap, gline_search_helper, &search);
425 return search.hits;
426 }
427