]>
Commit | Line | Data |
---|---|---|
0a30e865 SB |
1 | /* |
2 | * syn: a utility bot to manage IRC network access | |
3 | * Copyright (C) 2009-2016 Stephen Bennett | |
4 | * | |
5 | * This program is free software: you can redistribute it and/or modify | |
6 | * it under the terms of the GNU Affero General Public License as | |
7 | * published by the Free Software Foundation, either version 3 of the | |
8 | * License, or (at your option) any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU Affero General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU Affero General Public License | |
16 | * along with this program. If not, see <https://www.gnu.org/licenses/>. | |
17 | */ | |
18 | ||
19 | ||
ca6bbb10 SB |
20 | #include "atheme.h" |
21 | ||
22 | #include "syn.h" | |
23 | ||
52b278d6 | 24 | static void masks_newuser(hook_user_nick_t *data); |
ca6bbb10 SB |
25 | |
26 | static void syn_cmd_addmask(sourceinfo_t *si, int parc, char **parv); | |
27 | static void syn_cmd_delmask(sourceinfo_t *si, int parc, char **parv); | |
28 | static void syn_cmd_setmask(sourceinfo_t *si, int parc, char **parv); | |
b45a5ca4 | 29 | static void syn_cmd_listmask(sourceinfo_t *si, int parc, char **parv); |
ca6bbb10 | 30 | |
9ecbc9a7 JK |
31 | command_t syn_addmask = { "ADDMASK", N_("Adds a lethal, suspicious or exempt mask"), "syn:general", 1, syn_cmd_addmask, { .path = "syn/addmask" } }; |
32 | command_t syn_delmask = { "DELMASK", N_("Removes a lethal, suspicious or exempt mask"), "syn:general", 1, syn_cmd_delmask, { .path = "syn/delmask" } }; | |
33 | command_t syn_setmask = { "SETMASK", N_("Modifies settings for a lethal, suspicious or exempt mask"), "syn:general", 1, syn_cmd_setmask, { .path = "syn/setmask" } }; | |
34 | command_t syn_listmask = { "LISTMASK", N_("Displays configured mask lists"), "syn:general", 1, syn_cmd_listmask, { .path = "syn/listmask" } }; | |
ca6bbb10 SB |
35 | |
36 | static unsigned int lethal_mask_duration = 3600*24; | |
37 | static char *lethal_mask_message = NULL; | |
38 | ||
b918d5d9 SB |
39 | static mowgli_eventloop_timer_t *expire_masks_timer; |
40 | ||
ca6bbb10 SB |
41 | typedef enum |
42 | { | |
43 | mask_exempt, | |
44 | mask_suspicious, | |
45 | mask_lethal, | |
46 | mask_unknown | |
47 | } mask_type; | |
48 | ||
49 | typedef struct | |
50 | { | |
51 | char *regex; | |
52 | atheme_regex_t *re; | |
53 | int reflags; | |
4db6f543 | 54 | |
ca6bbb10 | 55 | mask_type type; |
4db6f543 | 56 | |
ca6bbb10 | 57 | time_t expires; |
4db6f543 | 58 | time_t added; |
52267ba7 | 59 | time_t last_match; |
4db6f543 | 60 | char setter[NICKLEN*2+2]; |
ca6bbb10 SB |
61 | } mask_t; |
62 | ||
b918d5d9 | 63 | mowgli_list_t masks; |
ca6bbb10 SB |
64 | |
65 | struct { | |
66 | const char *s; | |
67 | mask_type t; | |
68 | } mask_string_map[] = { | |
69 | { "exempt", mask_exempt }, | |
70 | { "suspicious", mask_suspicious }, | |
71 | { "lethal", mask_lethal }, | |
72 | { NULL, 0 } | |
73 | }; | |
74 | ||
75 | const char *string_from_mask_type(mask_type t) | |
76 | { | |
77 | for (int i=0; mask_string_map[i].s != NULL; ++i) | |
78 | if (mask_string_map[i].t == t) | |
79 | return mask_string_map[i].s; | |
80 | return NULL; | |
81 | } | |
82 | ||
83 | mask_type mask_type_from_string(const char *s) | |
84 | { | |
85 | for (int i=0; mask_string_map[i].s != NULL; ++i) | |
86 | if (0 == strcasecmp(mask_string_map[i].s, s)) | |
87 | return mask_string_map[i].t; | |
88 | return mask_unknown; | |
89 | } | |
90 | ||
b45a5ca4 SB |
91 | static void check_expiry(void *v) |
92 | { | |
b918d5d9 SB |
93 | mowgli_node_t *n, *tn; |
94 | MOWGLI_LIST_FOREACH_SAFE(n, tn, masks.head) | |
b45a5ca4 SB |
95 | { |
96 | mask_t *m = n->data; | |
97 | ||
98 | if (m->expires == 0) | |
99 | continue; | |
100 | if (m->expires > CURRTIME) | |
101 | continue; | |
102 | ||
103 | syn_report("Expiring %s mask \2%s\2", string_from_mask_type(m->type), m->regex); | |
104 | regex_destroy(m->re); | |
105 | free(m->regex); | |
106 | free(m); | |
b918d5d9 | 107 | mowgli_node_delete(n, &masks); |
b45a5ca4 SB |
108 | } |
109 | } | |
ca6bbb10 | 110 | |
b19af353 SB |
111 | static void save_maskdb() |
112 | { | |
b918d5d9 | 113 | mowgli_node_t *n; |
b19af353 SB |
114 | FILE *f = fopen(DATADIR "/masks.db", "w"); |
115 | if (!f) | |
116 | { | |
117 | slog(LG_ERROR, "Couldn't open masks.db for writing: %s", strerror(errno)); | |
118 | return; | |
119 | } | |
120 | ||
b918d5d9 | 121 | MOWGLI_LIST_FOREACH(n, masks.head) |
b19af353 SB |
122 | { |
123 | mask_t *m = n->data; | |
124 | ||
52267ba7 | 125 | fprintf(f, "/%s/%s %d %s %lu %lu %lu\n", |
b19af353 | 126 | m->regex, m->reflags & AREGEX_ICASE ? "i" : "", |
52267ba7 | 127 | m->type, m->setter, m->added, m->expires, m->last_match); |
b19af353 SB |
128 | } |
129 | fclose(f); | |
130 | } | |
131 | ||
132 | static void load_maskdb() | |
133 | { | |
134 | FILE *f = fopen(DATADIR "/masks.db", "r"); | |
135 | if (!f) | |
136 | { | |
137 | slog(LG_DEBUG, "Couldn't open masks db for reading: %s", strerror(errno)); | |
138 | return; | |
139 | } | |
140 | ||
141 | char line[BUFSIZE*2]; | |
142 | while(fgets(line, sizeof(line), f)) | |
143 | { | |
144 | char *args = line; | |
145 | int flags = 0; | |
146 | char *regex = regex_extract(args, &args, &flags); | |
147 | ||
148 | atheme_regex_t *re= regex_create(regex, flags); | |
149 | ||
150 | if (!re || !regex) | |
151 | { | |
152 | slog(LG_DEBUG, "Invalid entry %s in masks db", line); | |
153 | continue; | |
154 | } | |
155 | ||
156 | char setter[BUFSIZE*2]; | |
157 | int type; | |
52267ba7 | 158 | time_t added, expires, last_match = 0; |
b19af353 | 159 | |
52267ba7 | 160 | sscanf(args, "%d %s %lu %lu %lu", &type, setter, &added, &expires, &last_match); |
b19af353 SB |
161 | |
162 | mask_t *mask = malloc(sizeof(mask_t)); | |
163 | mask->regex = sstrdup(regex); | |
164 | mask->reflags = flags; | |
165 | mask->re = re; | |
166 | strncpy(mask->setter, setter, sizeof(mask->setter)); | |
167 | mask->added = added; | |
168 | mask->expires = expires; | |
52267ba7 | 169 | mask->last_match = last_match; |
b19af353 SB |
170 | mask->type = type; |
171 | ||
b918d5d9 | 172 | mowgli_node_add(mask, mowgli_node_create(), &masks); |
b19af353 SB |
173 | } |
174 | ||
175 | fclose(f); | |
176 | } | |
ca6bbb10 | 177 | |
492a4dc0 | 178 | static void mod_init(module_t *m) |
ca6bbb10 SB |
179 | { |
180 | use_syn_main_symbols(m); | |
b45a5ca4 | 181 | use_syn_util_symbols(m); |
ca6bbb10 SB |
182 | use_syn_kline_symbols(m); |
183 | ||
b918d5d9 SB |
184 | add_uint_conf_item("lethalmask_duration", &syn->conf_table, 0, &lethal_mask_duration, 0, (unsigned int)-1, 3600*24); |
185 | add_dupstr_conf_item("lethalmask_message", &syn->conf_table, 0, &lethal_mask_message, "Banned"); | |
ca6bbb10 | 186 | |
b918d5d9 SB |
187 | service_named_bind_command("syn", &syn_addmask); |
188 | service_named_bind_command("syn", &syn_delmask); | |
189 | service_named_bind_command("syn", &syn_setmask); | |
190 | service_named_bind_command("syn", &syn_listmask); | |
ca6bbb10 | 191 | |
52b278d6 SB |
192 | hook_add_event("user_nickchange"); |
193 | hook_add_user_nickchange(masks_newuser); | |
ca6bbb10 | 194 | hook_add_event("user_add"); |
a3064be2 | 195 | hook_add_user_add(masks_newuser); |
b45a5ca4 | 196 | |
b918d5d9 | 197 | expire_masks_timer = mowgli_timer_add(base_eventloop, "masks_check_expiry", check_expiry, NULL, 60); |
b19af353 SB |
198 | |
199 | load_maskdb(); | |
ca6bbb10 SB |
200 | } |
201 | ||
492a4dc0 | 202 | static void mod_deinit(module_unload_intent_t intent) |
ca6bbb10 | 203 | { |
b19af353 SB |
204 | save_maskdb(); |
205 | ||
b918d5d9 SB |
206 | service_named_unbind_command("syn", &syn_addmask); |
207 | service_named_unbind_command("syn", &syn_delmask); | |
208 | service_named_unbind_command("syn", &syn_setmask); | |
209 | service_named_unbind_command("syn", &syn_listmask); | |
b45a5ca4 | 210 | |
b918d5d9 SB |
211 | del_conf_item("lethalmask_duration", &syn->conf_table); |
212 | del_conf_item("lethalmask_message", &syn->conf_table); | |
ad487621 | 213 | |
a3064be2 | 214 | hook_del_user_add(masks_newuser); |
52b278d6 | 215 | hook_del_user_nickchange(masks_newuser); |
b45a5ca4 | 216 | |
b918d5d9 | 217 | mowgli_timer_destroy(base_eventloop, expire_masks_timer); |
ca6bbb10 SB |
218 | } |
219 | ||
52b278d6 | 220 | void masks_newuser(hook_user_nick_t *data) |
ca6bbb10 | 221 | { |
a3064be2 | 222 | user_t *u = data->u; |
ca6bbb10 | 223 | |
3cafe86e SB |
224 | /* If the user has already been killed, don't try to do anything */ |
225 | if (!u) | |
226 | return; | |
227 | ||
ca6bbb10 SB |
228 | char nuh[NICKLEN+USERLEN+HOSTLEN+GECOSLEN]; |
229 | snprintf(nuh, sizeof(nuh), "%s!%s@%s %s", u->nick, u->user, u->host, u->gecos); | |
230 | ||
231 | int blocked = 0, exempt = 0; | |
232 | char *suspicious_regex = NULL, *blocked_regex = NULL; | |
233 | ||
b918d5d9 | 234 | mowgli_node_t *n; |
52267ba7 | 235 | mask_t *m; |
b918d5d9 | 236 | MOWGLI_LIST_FOREACH(n, masks.head) |
ca6bbb10 | 237 | { |
52267ba7 | 238 | m = n->data; |
ca6bbb10 SB |
239 | |
240 | if (! regex_match(m->re, nuh)) | |
241 | continue; | |
242 | ||
0846976c JK |
243 | m->last_match = CURRTIME; |
244 | ||
ca6bbb10 SB |
245 | switch (m->type) |
246 | { | |
247 | case mask_exempt: | |
248 | exempt = 1; | |
249 | break; | |
250 | case mask_suspicious: | |
251 | suspicious_regex = m->regex; | |
252 | break; | |
253 | case mask_lethal: | |
254 | blocked = 1; | |
255 | blocked_regex = m->regex; | |
256 | break; | |
257 | case mask_unknown: | |
258 | break; | |
259 | } | |
ca6bbb10 SB |
260 | } |
261 | ||
262 | if (exempt == 1) | |
263 | return; | |
264 | ||
265 | if (blocked == 1) | |
266 | { | |
267 | syn_report("Killing client %s(%s@%s) due to lethal mask %s", | |
268 | u->nick, u->user, u->host, blocked_regex); | |
269 | syn_kill_or_kline(u, lethal_mask_duration, lethal_mask_message); | |
3cafe86e | 270 | data->u = NULL; |
ca6bbb10 SB |
271 | return; |
272 | } | |
273 | ||
274 | if (suspicious_regex) | |
275 | { | |
276 | syn_report("Client %s(%s@%s) matches suspicious mask %s", | |
277 | u->nick, u->user, u->host, suspicious_regex); | |
278 | return; | |
279 | } | |
280 | } | |
281 | ||
282 | void syn_cmd_addmask(sourceinfo_t *si, int parc, char **parv) | |
283 | { | |
284 | char *pattern; | |
285 | char *stype; | |
286 | mask_type type; | |
287 | mask_t *newmask; | |
288 | int flags; | |
b45a5ca4 | 289 | time_t duration = 0; |
ca6bbb10 SB |
290 | |
291 | char *args = parv[0]; | |
292 | ||
293 | if (args == NULL) | |
294 | { | |
767e4663 SB |
295 | command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "ADDMASK"); |
296 | command_fail(si, fault_needmoreparams, "Syntax: ADDMASK /<regex>/[i] <type>"); | |
ca6bbb10 SB |
297 | return; |
298 | } | |
299 | ||
300 | pattern = regex_extract(args, &args, &flags); | |
301 | if (pattern == NULL) | |
302 | { | |
767e4663 SB |
303 | command_fail(si, fault_badparams, STR_INVALID_PARAMS, "ADDMASK"); |
304 | command_fail(si, fault_badparams, "Syntax: ADDMASK /<regex>/[i] <type>"); | |
ca6bbb10 SB |
305 | return; |
306 | } | |
307 | ||
b45a5ca4 | 308 | stype = strtok(args, " "); |
ca6bbb10 | 309 | |
c0a3a11e | 310 | if (!stype || *stype == '\0') |
ca6bbb10 | 311 | { |
767e4663 SB |
312 | command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "ADDMASK"); |
313 | command_fail(si, fault_needmoreparams, "Syntax: ADDMASK /<regex>/[i] <type>"); | |
ca6bbb10 SB |
314 | return; |
315 | } | |
316 | ||
317 | type = mask_type_from_string(stype); | |
318 | if (type == mask_unknown) | |
319 | { | |
767e4663 | 320 | command_fail(si, fault_badparams, "Invalid mask type \2%s\2.", stype); |
ca6bbb10 SB |
321 | return; |
322 | } | |
323 | ||
b45a5ca4 SB |
324 | char *sduration = strtok(NULL, " "); |
325 | if (sduration && *sduration == '~') | |
326 | { | |
327 | duration = syn_parse_duration(++sduration); | |
328 | } | |
329 | ||
b918d5d9 SB |
330 | mowgli_node_t *n; |
331 | MOWGLI_LIST_FOREACH(n, masks.head) | |
ca6bbb10 SB |
332 | { |
333 | mask_t *m = n->data; | |
334 | ||
335 | if (0 == strcmp(m->regex, pattern)) | |
336 | { | |
767e4663 | 337 | command_fail(si, fault_nochange, "\2%s\2 was already added (%s); not re-adding", pattern, string_from_mask_type(m->type)); |
ca6bbb10 SB |
338 | return; |
339 | } | |
340 | } | |
341 | ||
342 | atheme_regex_t *regex = regex_create(pattern, flags); | |
343 | if (regex == NULL) | |
344 | { | |
767e4663 | 345 | command_fail(si, fault_badparams, "The provided regex \2%s\2 is invalid.", pattern); |
ca6bbb10 SB |
346 | return; |
347 | } | |
348 | ||
349 | newmask = malloc(sizeof(mask_t)); | |
350 | newmask->regex = sstrdup(pattern); | |
351 | newmask->reflags = flags; | |
352 | newmask->re = regex; | |
353 | newmask->type = type; | |
b45a5ca4 SB |
354 | if (duration > 0) |
355 | newmask->expires = CURRTIME + 60*duration; | |
356 | else | |
357 | newmask->expires = 0; | |
ca6bbb10 | 358 | |
4db6f543 | 359 | newmask->added = CURRTIME; |
52267ba7 | 360 | newmask->last_match = 0; |
4db6f543 SB |
361 | strncpy(newmask->setter, get_oper_name(si), sizeof(newmask->setter)); |
362 | ||
b918d5d9 | 363 | mowgli_node_add(newmask, mowgli_node_create(), &masks); |
b45a5ca4 | 364 | |
834c9c67 JK |
365 | syn_report("\002ADDMASK\002 /%s/%s (%s) by %s, expires %s", |
366 | pattern, (flags & AREGEX_ICASE) ? "i" : "", stype, get_oper_name(si), syn_format_expiry(newmask->expires)); | |
767e4663 | 367 | command_success_nodata(si, "Added \2%s\2 to %s mask list, expiring %s.", |
b45a5ca4 | 368 | pattern, stype, syn_format_expiry(newmask->expires)); |
2b3db230 SB |
369 | |
370 | save_maskdb(); | |
ca6bbb10 SB |
371 | } |
372 | ||
373 | void syn_cmd_delmask(sourceinfo_t *si, int parc, char **parv) | |
374 | { | |
b45a5ca4 SB |
375 | char *args = parv[0]; |
376 | ||
377 | if (!args) | |
378 | { | |
767e4663 SB |
379 | command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "DELMASK"); |
380 | command_fail(si, fault_needmoreparams, "Syntax: DELMASK /<regex>/"); | |
b45a5ca4 SB |
381 | return; |
382 | } | |
383 | ||
384 | int flags = 0; | |
385 | char *pattern = regex_extract(args, &args, &flags); | |
386 | ||
387 | if (!pattern) | |
388 | { | |
767e4663 SB |
389 | command_fail(si, fault_badparams, STR_INVALID_PARAMS, "DELMASK"); |
390 | command_fail(si, fault_needmoreparams, "Syntax: DELMASK /<regex>/"); | |
b45a5ca4 SB |
391 | return; |
392 | } | |
393 | ||
b918d5d9 SB |
394 | mowgli_node_t *n, *tn; |
395 | MOWGLI_LIST_FOREACH_SAFE(n, tn, masks.head) | |
b45a5ca4 SB |
396 | { |
397 | mask_t *m = n->data; | |
398 | if (0 == strcmp(pattern, m->regex)) | |
399 | { | |
834c9c67 | 400 | syn_report("\002DELMASK\002 /%s/%s (%s) by %s", pattern, (m->reflags & AREGEX_ICASE) ? "i" : "", string_from_mask_type(m->type), get_oper_name(si)); |
767e4663 | 401 | command_success_nodata(si, "Removing \2%s\2 from %s mask list", pattern, string_from_mask_type(m->type)); |
b45a5ca4 SB |
402 | regex_destroy(m->re); |
403 | free(m->regex); | |
404 | free(m); | |
b918d5d9 | 405 | mowgli_node_delete(n, &masks); |
2b3db230 SB |
406 | |
407 | save_maskdb(); | |
408 | ||
b45a5ca4 SB |
409 | return; |
410 | } | |
411 | } | |
412 | ||
767e4663 | 413 | command_fail(si, fault_nochange, "\2%s\2 was not found in any mask list", pattern); |
ca6bbb10 SB |
414 | } |
415 | ||
416 | void syn_cmd_setmask(sourceinfo_t *si, int parc, char **parv) | |
417 | { | |
b45a5ca4 SB |
418 | char *args = parv[0]; |
419 | ||
420 | if (!args) | |
421 | { | |
767e4663 | 422 | command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "SETMASK"); |
867a1c56 | 423 | command_fail(si, fault_needmoreparams, "Syntax: SETMASK /<regex>/ <type|~expiry>"); |
b45a5ca4 SB |
424 | return; |
425 | } | |
426 | ||
427 | int flags = 0; | |
428 | char *pattern = regex_extract(args, &args, &flags); | |
429 | ||
430 | if (!pattern) | |
431 | { | |
767e4663 | 432 | command_fail(si, fault_badparams, STR_INVALID_PARAMS, "SETMASK"); |
867a1c56 | 433 | command_fail(si, fault_needmoreparams, "Syntax: SETMASK /<regex>/ <type|~expiry>"); |
b45a5ca4 SB |
434 | return; |
435 | } | |
436 | ||
b918d5d9 | 437 | mowgli_node_t *n, *tn; |
b45a5ca4 | 438 | mask_t *m; |
b918d5d9 | 439 | MOWGLI_LIST_FOREACH_SAFE(n, tn, masks.head) |
b45a5ca4 SB |
440 | { |
441 | m = n->data; | |
442 | if (0 == strcmp(pattern, m->regex)) | |
443 | break; | |
444 | m = NULL; | |
445 | } | |
446 | ||
447 | if (!m) | |
448 | { | |
767e4663 | 449 | command_fail(si, fault_nochange, "\2%s\2 was not found in any mask list", pattern); |
b45a5ca4 SB |
450 | return; |
451 | } | |
452 | ||
453 | char *nextarg = strtok(args, " "); | |
867a1c56 SB |
454 | |
455 | if (!nextarg) | |
456 | { | |
457 | command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "SETMASK"); | |
458 | command_fail(si, fault_needmoreparams, "Syntax: SETMASK /<regex>/ <type|~expiry>"); | |
459 | return; | |
460 | } | |
461 | ||
b45a5ca4 SB |
462 | mask_type t = mask_type_from_string(nextarg); |
463 | if (t != mask_unknown) | |
464 | { | |
465 | m->type = t; | |
834c9c67 | 466 | syn_report("\002SETMASK\002 /%s/%s type->%s by %s", pattern, (m->reflags & AREGEX_ICASE) ? "i" : "", nextarg, get_oper_name(si)); |
767e4663 | 467 | command_success_nodata(si, "Changed type of mask \2%s\2 to %s", pattern, nextarg); |
2b3db230 SB |
468 | |
469 | save_maskdb(); | |
470 | ||
b45a5ca4 SB |
471 | return; |
472 | } | |
473 | ||
474 | if (*nextarg == '~') | |
475 | { | |
476 | time_t duration = syn_parse_duration(++nextarg); | |
477 | if (duration > 0) | |
478 | { | |
479 | m->expires = CURRTIME + duration * 60; | |
834c9c67 | 480 | syn_report("\002SETMASK\002 /%s/%s duration->%d by %s", pattern, (m->reflags & AREGEX_ICASE) ? "i" : "", duration, get_oper_name(si)); |
767e4663 | 481 | command_success_nodata(si, "Changed expiry of mask \2%s\2 to %ld minutes", pattern, duration); |
b45a5ca4 SB |
482 | } |
483 | else | |
484 | { | |
485 | m->expires = 0; | |
834c9c67 | 486 | syn_report("\002SETMASK\002 /%s/%s expiry->off by %s", pattern, (m->reflags & AREGEX_ICASE) ? "i" : "", get_oper_name(si)); |
767e4663 | 487 | command_success_nodata(si, "Expiry disabled for mask \2%s\2.", pattern); |
b45a5ca4 | 488 | } |
2b3db230 SB |
489 | |
490 | save_maskdb(); | |
491 | ||
b45a5ca4 SB |
492 | return; |
493 | } | |
494 | ||
767e4663 | 495 | command_fail(si, fault_badparams, STR_INVALID_PARAMS, "SETMASK"); |
867a1c56 | 496 | command_fail(si, fault_badparams, "Syntax: SETMASK /<regex>/ <type|~expiry>"); |
ca6bbb10 SB |
497 | } |
498 | ||
b45a5ca4 SB |
499 | void syn_cmd_listmask(sourceinfo_t *si, int parc, char **parv) |
500 | { | |
501 | mask_type t = mask_unknown; | |
502 | ||
503 | if (parc > 0) | |
504 | { | |
505 | t = mask_type_from_string(parv[0]); | |
506 | } | |
507 | ||
508 | int count = 0; | |
509 | ||
b918d5d9 SB |
510 | mowgli_node_t *n; |
511 | MOWGLI_LIST_FOREACH(n, masks.head) | |
b45a5ca4 SB |
512 | { |
513 | mask_t *m = n->data; | |
514 | ||
515 | if (t != mask_unknown && t != m->type) | |
516 | continue; | |
517 | ||
52267ba7 JK |
518 | char added[BUFSIZE], expires[BUFSIZE], last_match[BUFSIZE]; |
519 | strncpy(added, syn_format_expiry(m->added), BUFSIZE); | |
520 | strncpy(expires, syn_format_expiry(m->expires), BUFSIZE); | |
521 | strncpy(last_match, syn_format_expiry(m->last_match), BUFSIZE); | |
522 | command_success_nodata(si, "\2/%s/%s\2 (%s), set by %s on %s, expires %s, last matched %s", | |
834c9c67 | 523 | m->regex, (m->reflags & AREGEX_ICASE) ? "i" : "", string_from_mask_type(m->type), m->setter, |
52267ba7 | 524 | added, expires, last_match); |
b45a5ca4 SB |
525 | |
526 | ++count; | |
527 | } | |
528 | ||
767e4663 | 529 | command_success_nodata(si, "%d masks found", count); |
b45a5ca4 | 530 | } |
492a4dc0 JK |
531 | |
532 | DECLARE_MODULE_V1 | |
533 | ( | |
534 | "syn/masks", false, mod_init, mod_deinit, | |
535 | "$Revision$", | |
536 | "Stephen Bennett <stephen -at- freenode.net>" | |
537 | ); |