]>
jfr.im git - irc/evilnet/x3.git/blob - src/shun.c
1 /* shun.c - Shun database
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of x3.
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.
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 shun_heap
; /* key: expiry time, data: struct shun_entry* */
48 static dict_t shun_dict
; /* key: target, data: struct shun_entry* */
51 shun_comparator(const void *a
, const void *b
)
53 const struct shun
*ga
=a
, *gb
=b
;
54 return ga
->expires
- gb
->expires
;
58 free_shun_from_dict(void *data
)
60 struct shun
*ent
= data
;
68 free_shun(struct shun
*ent
)
70 dict_remove(shun_dict
, ent
->target
);
74 shun_for_p(UNUSED_ARG(void *key
), void *data
, void *extra
)
76 struct shun
*ge
= data
;
77 return !irccasecmp(ge
->target
, extra
);
81 delete_shun_for_p(UNUSED_ARG(void *key
), void *data
, void *extra
)
83 struct shun
*ge
= data
;
85 if (!irccasecmp(ge
->target
, extra
)) {
94 shun_expire(UNUSED_ARG(void *data
))
100 while (heap_size(shun_heap
)) {
101 heap_peek(shun_heap
, 0, &wraa
);
102 stopped
= ((struct shun
*)wraa
)->expires
;
108 if (heap_size(shun_heap
))
109 timeq_add(stopped
, shun_expire
, NULL
);
113 shun_remove(const char *target
, int announce
)
115 int res
= dict_find(shun_dict
, target
, NULL
) ? 1 : 0;
116 if (heap_remove_pred(shun_heap
, delete_shun_for_p
, (char*)target
)) {
118 struct shun
*new_first
;
119 heap_peek(shun_heap
, 0, &argh
);
122 timeq_del(0, shun_expire
, 0, TIMEQ_IGNORE_WHEN
|TIMEQ_IGNORE_DATA
);
123 timeq_add(new_first
->expires
, shun_expire
, 0);
132 shun_add(const char *issuer
, const char *target
, unsigned long duration
, const char *reason
, time_t issued
, time_t lastmod
, int announce
)
135 struct shun
*prev_first
;
138 heap_peek(shun_heap
, 0, &argh
);
140 ent
= dict_find(shun_dict
, target
, NULL
);
142 heap_remove_pred(shun_heap
, shun_for_p
, (char*)target
);
143 if (ent
->expires
< (time_t)(now
+ duration
))
144 ent
->expires
= now
+ duration
;
145 if (ent
->lastmod
< lastmod
)
146 ent
->lastmod
= lastmod
;
148 ent
= malloc(sizeof(*ent
));
149 ent
->issued
= issued
;
150 ent
->lastmod
= lastmod
;
151 ent
->issuer
= strdup(issuer
);
152 ent
->target
= strdup(target
);
153 ent
->expires
= now
+ duration
;
154 ent
->reason
= strdup(reason
);
155 dict_insert(shun_dict
, ent
->target
, ent
);
157 heap_insert(shun_heap
, ent
, ent
);
158 if (!prev_first
|| (ent
->expires
< prev_first
->expires
)) {
159 timeq_del(0, shun_expire
, 0, TIMEQ_IGNORE_WHEN
|TIMEQ_IGNORE_DATA
);
160 timeq_add(ent
->expires
, shun_expire
, 0);
168 shun_alternate_target(const char *target
)
170 const char *hostname
;
172 /* If no host part, bail. */
173 if (!(hostname
= strchr(target
, '@')))
175 /* If host part contains wildcards, bail. */
176 if (hostname
[strcspn(hostname
, "*?/")])
178 /* Get parsed address and canonical name for host. */
180 irc_in_addr_t in
; /* move this to the right place */
181 if (irc_pton(&in
, NULL
, hostname
+1)) {
182 if (getnameinfo(/*TODO*/))
184 } else if (!getaddrinfo(/*TODO*/)) {
192 shun_find(const char *target
)
198 res
= dict_find(shun_dict
, target
, NULL
);
201 /* Stock ircu requires BADCHANs to match exactly. */
202 if ((target
[0] == '#') || (target
[0] == '&'))
204 else if (target
[strcspn(target
, "*?")]) {
205 /* Wildcard: do an obnoxiously long search. */
206 for (it
= dict_first(shun_dict
); it
; it
= iter_next(it
)) {
208 if (match_ircglob(target
, res
->target
))
212 /* See if we can resolve the hostname part of the mask. */
213 if ((alt_target
= shun_alternate_target(target
))) {
214 res
= shun_find(alt_target
);
222 shun_refresh_helper(UNUSED_ARG(void *key
), void *data
, void *extra
)
224 struct shun
*ge
= data
;
230 shun_refresh_server(struct server
*srv
)
232 heap_remove_pred(shun_heap
, shun_refresh_helper
, srv
);
236 shun_refresh_all(void)
238 heap_remove_pred(shun_heap
, shun_refresh_helper
, 0);
244 return dict_size(shun_dict
);
248 shun_add_record(const char *key
, void *data
, UNUSED_ARG(void *extra
))
250 struct record_data
*rd
= data
;
251 const char *issuer
, *reason
, *dstr
;
252 time_t issued
, expiration
, lastmod
;
254 if (!(reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
))) {
255 log_module(MAIN_LOG
, LOG_ERROR
, "Missing reason for shun %s", key
);
258 if (!(dstr
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
))) {
259 log_module(MAIN_LOG
, LOG_ERROR
, "Missing expiration for shun %s", key
);
262 expiration
= strtoul(dstr
, NULL
, 0);
263 dstr
= database_get_data(rd
->d
.object
, KEY_LASTMOD
, RECDB_QSTRING
);
264 lastmod
= dstr
? strtoul(dstr
, NULL
, 0) : 0;
265 if ((dstr
= database_get_data(rd
->d
.object
, KEY_ISSUED
, RECDB_QSTRING
))) {
266 issued
= strtoul(dstr
, NULL
, 0);
270 if (!(issuer
= database_get_data(rd
->d
.object
, KEY_ISSUER
, RECDB_QSTRING
))) {
271 issuer
= "<unknown>";
273 if (expiration
> now
)
274 shun_add(issuer
, key
, expiration
- now
, reason
, issued
, lastmod
, 0);
279 shun_saxdb_read(struct dict
*db
)
281 return dict_foreach(db
, shun_add_record
, 0) != NULL
;
285 shun_write_entry(UNUSED_ARG(void *key
), void *data
, void *extra
)
287 struct shun
*ent
= data
;
288 struct saxdb_context
*ctx
= extra
;
290 saxdb_start_record(ctx
, ent
->target
, 0);
291 saxdb_write_int(ctx
, KEY_EXPIRES
, ent
->expires
);
292 saxdb_write_int(ctx
, KEY_ISSUED
, ent
->issued
);
294 saxdb_write_int(ctx
, KEY_LASTMOD
, ent
->lastmod
);
295 saxdb_write_string(ctx
, KEY_REASON
, ent
->reason
);
296 saxdb_write_string(ctx
, KEY_ISSUER
, ent
->issuer
);
297 saxdb_end_record(ctx
);
302 shun_saxdb_write(struct saxdb_context
*ctx
)
304 heap_remove_pred(shun_heap
, shun_write_entry
, ctx
);
309 shun_db_cleanup(void)
311 heap_delete(shun_heap
);
312 dict_delete(shun_dict
);
318 shun_heap
= heap_new(shun_comparator
);
319 shun_dict
= dict_new();
320 dict_set_free_data(shun_dict
, free_shun_from_dict
);
321 saxdb_register("shun", shun_saxdb_read
, shun_saxdb_write
);
322 reg_exit_func(shun_db_cleanup
);
325 struct shun_discrim
*
326 shun_discrim_create(struct userNode
*user
, struct userNode
*src
, unsigned int argc
, char *argv
[])
329 struct shun_discrim
*discrim
;
331 discrim
= calloc(1, sizeof(*discrim
));
333 discrim
->max_issued
= INT_MAX
;
334 discrim
->max_lastmod
= INT_MAX
;
336 for (i
=0; i
<argc
; i
++) {
338 send_message(user
, src
, "MSG_MISSING_PARAMS", argv
[i
]);
340 } else if (!irccasecmp(argv
[i
], "mask") || !irccasecmp(argv
[i
], "host")) {
341 if (!irccasecmp(argv
[++i
], "exact"))
342 discrim
->target_mask_type
= SEXACT
;
343 else if (!irccasecmp(argv
[i
], "subset"))
344 discrim
->target_mask_type
= SSUBSET
;
345 else if (!irccasecmp(argv
[i
], "superset"))
346 discrim
->target_mask_type
= SSUPERSET
;
348 discrim
->target_mask_type
= SSUBSET
, i
--;
350 send_message(user
, src
, "MSG_MISSING_PARAMS", argv
[i
-1]);
353 if (!is_shun(argv
[i
]) && !IsChannelName(argv
[i
])) {
354 send_message(user
, src
, "MSG_INVALID_SHUN", argv
[i
]);
357 discrim
->target_mask
= argv
[i
];
358 discrim
->alt_target_mask
= shun_alternate_target(discrim
->target_mask
);
359 } else if (!irccasecmp(argv
[i
], "limit"))
360 discrim
->limit
= strtoul(argv
[++i
], NULL
, 0);
361 else if (!irccasecmp(argv
[i
], "reason"))
362 discrim
->reason_mask
= argv
[++i
];
363 else if (!irccasecmp(argv
[i
], "issuer"))
364 discrim
->issuer_mask
= argv
[++i
];
365 else if (!irccasecmp(argv
[i
], "after"))
366 discrim
->min_expire
= now
+ ParseInterval(argv
[++i
]);
367 else if (!irccasecmp(argv
[i
], "before"))
368 discrim
->max_issued
= now
- ParseInterval(argv
[++i
]);
369 else if (!irccasecmp(argv
[i
], "lastmod")) {
370 const char *cmp
= argv
[++i
];
373 discrim
->min_lastmod
= now
- ParseInterval(cmp
+ 2);
375 discrim
->min_lastmod
= now
- (ParseInterval(cmp
+ 1) - 1);
377 } else if (cmp
[0] == '>') {
379 discrim
->max_lastmod
= now
- ParseInterval(cmp
+ 2);
381 discrim
->max_lastmod
= now
- (ParseInterval(cmp
+ 1) - 1);
384 discrim
->min_lastmod
= now
- ParseInterval(cmp
+ 2);
387 send_message(user
, src
, "MSG_INVALID_CRITERIA", argv
[i
]);
393 free(discrim
->alt_target_mask
);
399 struct shun_discrim
*discrim
;
400 shun_search_func func
;
406 shun_discrim_match(struct shun
*shun
, struct shun_discrim
*discrim
)
408 if ((discrim
->issuer_mask
&& !match_ircglob(shun
->issuer
, discrim
->issuer_mask
))
409 || (discrim
->reason_mask
&& !match_ircglob(shun
->reason
, discrim
->reason_mask
))
410 || (discrim
->target_mask
411 && (((discrim
->target_mask_type
== SSUBSET
)
412 && !match_ircglobs(discrim
->target_mask
, shun
->target
)
413 && (!discrim
->alt_target_mask
414 || !match_ircglobs(discrim
->alt_target_mask
, shun
->target
)))
415 || ((discrim
->target_mask_type
== SEXACT
)
416 && irccasecmp(discrim
->target_mask
, shun
->target
)
417 && (!discrim
->alt_target_mask
418 || !irccasecmp(discrim
->alt_target_mask
, shun
->target
)))
419 || ((discrim
->target_mask_type
== SSUPERSET
)
420 && !match_ircglobs(shun
->target
, discrim
->target_mask
)
421 && (!discrim
->alt_target_mask
422 || !match_ircglobs(discrim
->alt_target_mask
, shun
->target
)))))
423 || (discrim
->max_issued
< shun
->issued
)
424 || (discrim
->min_expire
> shun
->expires
)
425 || (discrim
->min_lastmod
> shun
->lastmod
)
426 || (discrim
->max_lastmod
< shun
->lastmod
)) {
433 shun_search_helper(UNUSED_ARG(void *key
), void *data
, void *extra
)
435 struct shun
*shun
= data
;
436 struct shun_search
*search
= extra
;
438 if (shun_discrim_match(shun
, search
->discrim
)
439 && (search
->hits
++ < search
->discrim
->limit
)) {
440 search
->func(shun
, search
->data
);
446 shun_discrim_search(struct shun_discrim
*discrim
, shun_search_func gsf
, void *data
)
448 struct shun_search search
;
449 search
.discrim
= discrim
;
453 heap_remove_pred(shun_heap
, shun_search_helper
, &search
);