1 /*
2 * Copyright (c) 2003-2004 E. Will et al.
3 * Copyright (c) 2005-2006 Atheme Development Group
4 * Rights to this code are documented in doc/LICENSE.
5 *
6 * This file contains functionality which implements
7 * the OperServ SQLINE command.
8 *
9 */
10
11 #include "atheme.h"
12
13 DECLARE_MODULE_V1
14 (
15 "operserv/sqline", false, _modinit, _moddeinit,
16 PACKAGE_STRING,
17 VENDOR_STRING
18 );
19
20 static void os_sqline_newuser(hook_user_nick_t *data);
21 static void os_sqline_chanjoin(hook_channel_joinpart_t *hdata);
22
23 static void os_cmd_sqline(sourceinfo_t *si, int parc, char *parv[]);
24 static void os_cmd_sqline_add(sourceinfo_t *si, int parc, char *parv[]);
25 static void os_cmd_sqline_del(sourceinfo_t *si, int parc, char *parv[]);
26 static void os_cmd_sqline_list(sourceinfo_t *si, int parc, char *parv[]);
27 static void os_cmd_sqline_sync(sourceinfo_t *si, int parc, char *parv[]);
28
29
30 command_t os_sqline = { "SQLINE", N_("Manages network name bans."), PRIV_MASS_AKILL, 3, os_cmd_sqline, { .path = "oservice/sqline" } };
31
32 command_t os_sqline_add = { "ADD", N_("Adds a network name ban"), AC_NONE, 2, os_cmd_sqline_add, { .path = "" } };
33 command_t os_sqline_del = { "DEL", N_("Deletes a network name ban"), AC_NONE, 1, os_cmd_sqline_del, { .path = "" } };
34 command_t os_sqline_list = { "LIST", N_("Lists all network name bans"), AC_NONE, 1, os_cmd_sqline_list, { .path = "" } };
35 command_t os_sqline_sync = { "SYNC", N_("Synchronises network name bans to servers"), AC_NONE, 0, os_cmd_sqline_sync, { .path = "" } };
36
37 mowgli_patricia_t *os_sqline_cmds;
38
_modinit(module_t * m)39 void _modinit(module_t *m)
40 {
41 if (ircd != NULL && qline_sts == generic_qline_sts)
42 {
43 slog(LG_INFO, "Module %s requires qline support, refusing to load.",
44 m->name);
45 m->mflags = MODTYPE_FAIL;
46 return;
47 }
48
49 service_named_bind_command("operserv", &os_sqline);
50
51 os_sqline_cmds = mowgli_patricia_create(strcasecanon);
52
53 /* Add sub-commands */
54 command_add(&os_sqline_add, os_sqline_cmds);
55 command_add(&os_sqline_del, os_sqline_cmds);
56 command_add(&os_sqline_list, os_sqline_cmds);
57 command_add(&os_sqline_sync, os_sqline_cmds);
58
59 hook_add_event("user_add");
60 hook_add_user_add(os_sqline_newuser);
61 hook_add_event("user_nickchange");
62 hook_add_user_nickchange(os_sqline_newuser);
63 hook_add_event("channel_join");
64 hook_add_channel_join(os_sqline_chanjoin);
65 }
66
_moddeinit(module_unload_intent_t intent)67 void _moddeinit(module_unload_intent_t intent)
68 {
69 service_named_unbind_command("operserv", &os_sqline);
70
71 /* Delete sub-commands */
72 command_delete(&os_sqline_add, os_sqline_cmds);
73 command_delete(&os_sqline_del, os_sqline_cmds);
74 command_delete(&os_sqline_list, os_sqline_cmds);
75 command_delete(&os_sqline_sync, os_sqline_cmds);
76
77 hook_del_user_add(os_sqline_newuser);
78 hook_del_user_nickchange(os_sqline_newuser);
79 hook_del_channel_join(os_sqline_chanjoin);
80
81 mowgli_patricia_destroy(os_sqline_cmds, NULL, NULL);
82 }
83
os_sqline_newuser(hook_user_nick_t * data)84 static void os_sqline_newuser(hook_user_nick_t *data)
85 {
86 user_t *u = data->u;
87 qline_t *q;
88
89 /* If the user has been killed, don't do anything. */
90 if (!u)
91 return;
92
93 if (is_internal_client(u))
94 return;
95 q = qline_find_user(u);
96 if (q != NULL)
97 {
98 /* Server didn't have that qline, send it again.
99 * To ensure qline exempt works on sqlines too, do
100 * not send a KILL. -- jilles */
101 qline_sts("*", q->mask, q->duration ? q->expires - CURRTIME : 0, q->reason);
102 }
103 }
104
os_sqline_chanjoin(hook_channel_joinpart_t * hdata)105 static void os_sqline_chanjoin(hook_channel_joinpart_t *hdata)
106 {
107 chanuser_t *cu = hdata->cu;
108 qline_t *q;
109
110 if (cu == NULL)
111 return;
112
113 if (is_internal_client(cu->user))
114 return;
115 q = qline_find_channel(cu->chan);
116 if (q != NULL)
117 {
118 /* Server didn't have that qline, send it again.
119 * To ensure qline exempt works on sqlines too, do
120 * not send a KICK. -- jilles */
121 qline_sts("*", q->mask, q->duration ? q->expires - CURRTIME : 0, q->reason);
122 }
123 }
124
os_cmd_sqline(sourceinfo_t * si,int parc,char * parv[])125 static void os_cmd_sqline(sourceinfo_t *si, int parc, char *parv[])
126 {
127 /* Grab args */
128 char *cmd = parv[0];
129 command_t *c;
130
131 /* Bad/missing arg */
132 if (!cmd)
133 {
134 command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "SQLINE");
135 command_fail(si, fault_needmoreparams, _("Syntax: SQLINE ADD|DEL|LIST"));
136 return;
137 }
138
139 c = command_find(os_sqline_cmds, cmd);
140 if(c == NULL)
141 {
142 command_fail(si, fault_badparams, _("Invalid command. Use \2/%s%s help\2 for a command listing."), (ircd->uses_rcommand == FALSE) ? "msg " : "", si->service->disp);
143 return;
144 }
145
146 command_exec(si->service, si, c, parc - 1, parv + 1);
147 }
148
os_cmd_sqline_add(sourceinfo_t * si,int parc,char * parv[])149 static void os_cmd_sqline_add(sourceinfo_t *si, int parc, char *parv[])
150 {
151 char *target = parv[0];
152 char *token = strtok(parv[1], " ");
153 char *treason, reason[BUFSIZE];
154 long duration;
155 char *s;
156 qline_t *q;
157
158 if (!target || !token)
159 {
160 command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "SQLINE ADD");
161 command_fail(si, fault_needmoreparams, _("Syntax: SQLINE ADD <nick|chan> [!P|!T <minutes>] <reason>"));
162 return;
163 }
164
165 if (IsDigit(*target))
166 {
167 command_fail(si, fault_badparams, _("Invalid target: \2%s\2. You can not SQLINE UIDs."), target);
168 return;
169 }
170
171 if (!strcasecmp(token, "!P"))
172 {
173 duration = 0;
174 treason = strtok(NULL, "");
175
176 if (treason)
177 mowgli_strlcpy(reason, treason, BUFSIZE);
178 else
179 mowgli_strlcpy(reason, "No reason given", BUFSIZE);
180 }
181 else if (!strcasecmp(token, "!T"))
182 {
183 s = strtok(NULL, " ");
184 treason = strtok(NULL, "");
185 if (treason)
186 mowgli_strlcpy(reason, treason, BUFSIZE);
187 else
188 mowgli_strlcpy(reason, "No reason given", BUFSIZE);
189 if (s)
190 {
191 duration = (atol(s) * 60);
192 while (isdigit((unsigned char)*s))
193 s++;
194 if (*s == 'h' || *s == 'H')
195 duration *= 60;
196 else if (*s == 'd' || *s == 'D')
197 duration *= 1440;
198 else if (*s == 'w' || *s == 'W')
199 duration *= 10080;
200 else if (*s == '\0')
201 ;
202 else
203 duration = 0;
204 if (duration == 0)
205 {
206 command_fail(si, fault_badparams, _("Invalid duration given."));
207 command_fail(si, fault_badparams, _("Syntax: SQLINE ADD <nick|chan> [!P|!T <minutes>] <reason>"));
208 return;
209 }
210 }
211 else {
212 command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "SQLINE ADD");
213 command_fail(si, fault_needmoreparams, _("Syntax: SQLINE ADD <nick|chan> [!P|!T <minutes>] <reason>"));
214 return;
215 }
216
217 }
218 else
219 {
220 duration = config_options.kline_time;
221 mowgli_strlcpy(reason, token, BUFSIZE);
222 treason = strtok(NULL, "");
223
224 if (treason)
225 {
226 mowgli_strlcat(reason, " ", BUFSIZE);
227 mowgli_strlcat(reason, treason, BUFSIZE);
228 }
229 }
230
231 char *p;
232 int i = 0;
233
234 if (!VALID_CHANNEL_PFX(target))
235 {
236 /* make sure there's at least 3 non-wildcards */
237 /* except if there are no wildcards at all */
238 for (p = target; *p; p++)
239 {
240 if (*p != '*' && *p != '?' && *p != '.')
241 i++;
242 }
243
244 if (i < 3 && (strchr(target, '*') || strchr(target, '?')) && !has_priv(si, PRIV_AKILL_ANYMASK))
245 {
246 command_fail(si, fault_badparams, _("Invalid target: \2%s\2. At least three non-wildcard characters are required."), target);
247 return;
248 }
249 }
250
251 if (qline_find(target))
252 {
253 command_fail(si, fault_nochange, _("SQLINE \2%s\2 is already matched in the database."), target);
254 return;
255 }
256
257 q = qline_add(target, reason, duration, get_storage_oper_name(si));
258
259 if (duration)
260 command_success_nodata(si, _("Timed SQLINE on \2%s\2 was successfully added and will expire in %s."), q->mask, timediff(duration));
261 else
262 command_success_nodata(si, _("SQLINE on \2%s\2 was successfully added."), q->mask);
263
264 verbose_wallops("\2%s\2 is \2adding\2 an \2SQLINE\2 for \2%s\2 -- reason: \2%s\2", get_oper_name(si), q->mask,
265 q->reason);
266 logcommand(si, CMDLOG_ADMIN, "SQLINE:ADD: \2%s\2 (reason: \2%s\2)", q->mask, q->reason);
267 }
268
os_cmd_sqline_del(sourceinfo_t * si,int parc,char * parv[])269 static void os_cmd_sqline_del(sourceinfo_t *si, int parc, char *parv[])
270 {
271 char *target = parv[0];
272 qline_t *q;
273 unsigned int number;
274 char *s;
275
276 if (!target)
277 {
278 command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "SQLINE DEL");
279 command_fail(si, fault_needmoreparams, _("Syntax: SQLINE DEL <nick|chan>"));
280 return;
281 }
282
283 if (strchr(target, ','))
284 {
285 unsigned int start = 0, end = 0, i;
286 char t[16];
287
288 s = strtok(target, ",");
289
290 do
291 {
292 if (strchr(s, ':'))
293 {
294 for (i = 0; *s != ':'; s++, i++)
295 t[i] = *s;
296
297 t[++i] = '\0';
298 start = atoi(t);
299
300 s++; /* skip past the : */
301
302 for (i = 0; *s != '\0'; s++, i++)
303 t[i] = *s;
304
305 t[++i] = '\0';
306 end = atoi(t);
307
308 for (i = start; i <= end; i++)
309 {
310 if (!(q = qline_find_num(i)))
311 {
312 command_fail(si, fault_nosuch_target, _("No such SQLINE with number \2%d\2."), i);
313 continue;
314 }
315
316 command_success_nodata(si, _("SQLINE on \2%s\2 has been successfully removed."), q->mask);
317 verbose_wallops("\2%s\2 is \2removing\2 an \2SQLINE\2 for \2%s\2 -- reason: \2%s\2",
318 get_oper_name(si), q->mask, q->reason);
319
320 logcommand(si, CMDLOG_ADMIN, "SQLINE:DEL: \2%s\2", q->mask);
321 qline_delete(q->mask);
322 }
323
324 continue;
325 }
326
327 number = atoi(s);
328
329 if (!(q = qline_find_num(number)))
330 {
331 command_fail(si, fault_nosuch_target, _("No such SQLINE with number \2%d\2."), number);
332 return;
333 }
334
335 command_success_nodata(si, _("SQLINE on \2%s\2 has been successfully removed."), q->mask);
336 verbose_wallops("\2%s\2 is \2removing\2 an \2SQLINE\2 for \2%s\2 -- reason: \2%s\2",
337 get_oper_name(si), q->mask, q->reason);
338
339 logcommand(si, CMDLOG_ADMIN, "SQLINE:DEL: \2%s\2", q->mask);
340 qline_delete(q->mask);
341 } while ((s = strtok(NULL, ",")));
342
343 return;
344 }
345
346 if (IsDigit(*target))
347 {
348 unsigned int start = 0, end = 0, i;
349 char t[16];
350
351 if (strchr(target, ':'))
352 {
353 for (i = 0; *target != ':'; target++, i++)
354 t[i] = *target;
355
356 t[++i] = '\0';
357 start = atoi(t);
358
359 target++; /* skip past the : */
360
361 for (i = 0; *target != '\0'; target++, i++)
362 t[i] = *target;
363
364 t[++i] = '\0';
365 end = atoi(t);
366
367 for (i = start; i <= end; i++)
368 {
369 if (!(q = qline_find_num(i)))
370 {
371 command_fail(si, fault_nosuch_target, _("No such SQLINE with number \2%d\2."), i);
372 continue;
373 }
374
375 command_success_nodata(si, _("SQLINE on \2%s\2 has been successfully removed."), q->mask);
376 verbose_wallops("\2%s\2 is \2removing\2 an \2SQLINE\2 for \2%s\2 -- reason: \2%s\2",
377 get_oper_name(si), q->mask, q->reason);
378
379 logcommand(si, CMDLOG_ADMIN, "SQLINE:DEL: \2%s\2", q->mask);
380 qline_delete(q->mask);
381 }
382
383 return;
384 }
385
386 number = atoi(target);
387
388 if (!(q = qline_find_num(number)))
389 {
390 command_fail(si, fault_nosuch_target, _("No such SQLINE with number \2%d\2."), number);
391 return;
392 }
393
394 command_success_nodata(si, _("SQLINE on \2%s\2 has been successfully removed."), q->mask);
395
396 verbose_wallops("\2%s\2 is \2removing\2 an \2SQLINE\2 for \2%s\2 -- reason: \2%s\2",
397 get_oper_name(si), q->mask, q->reason);
398
399 logcommand(si, CMDLOG_ADMIN, "SQLINE:DEL: \2%s\2", q->mask);
400 qline_delete(q->mask);
401 return;
402 }
403
404
405 if (!(q = qline_find(target)))
406 {
407 command_fail(si, fault_nosuch_target, _("No such SQLINE: \2%s\2."), target);
408 return;
409 }
410
411 command_success_nodata(si, _("SQLINE on \2%s\2 has been successfully removed."), target);
412
413 verbose_wallops("\2%s\2 is \2removing\2 an \2SQLINE\2 for \2%s\2 -- reason: \2%s\2",
414 get_oper_name(si), q->mask, q->reason);
415
416 logcommand(si, CMDLOG_ADMIN, "SQLINE:DEL: \2%s\2", target);
417 qline_delete(target);
418 }
419
os_cmd_sqline_list(sourceinfo_t * si,int parc,char * parv[])420 static void os_cmd_sqline_list(sourceinfo_t *si, int parc, char *parv[])
421 {
422 char *param = parv[0];
423 bool full = false;
424 mowgli_node_t *n;
425 qline_t *q;
426
427 if (param != NULL && !strcasecmp(param, "FULL"))
428 full = true;
429
430 if (full)
431 command_success_nodata(si, _("SQLINE list (with reasons):"));
432 else
433 command_success_nodata(si, _("SQLINE list:"));
434
435 MOWGLI_ITER_FOREACH(n, qlnlist.head)
436 {
437 q = (qline_t *)n->data;
438
439 if (q->duration && full)
440 command_success_nodata(si, _("%d: %s - by \2%s\2 - expires in \2%s\2 - (%s)"), q->number, q->mask, q->setby, timediff(q->expires > CURRTIME ? q->expires - CURRTIME : 0), q->reason);
441 else if (q->duration && !full)
442 command_success_nodata(si, _("%d: %s - by \2%s\2 - expires in \2%s\2"), q->number, q->mask, q->setby, timediff(q->expires > CURRTIME ? q->expires - CURRTIME : 0));
443 else if (!q->duration && full)
444 command_success_nodata(si, _("%d: %s - by \2%s\2 - \2permanent\2 - (%s)"), q->number, q->mask, q->setby, q->reason);
445 else
446 command_success_nodata(si, _("%d: %s - by \2%s\2 - \2permanent\2"), q->number, q->mask, q->setby);
447 }
448
449 command_success_nodata(si, _("Total of \2%zu\2 %s in SQLINE list."), qlnlist.count, (qlnlist.count == 1) ? "entry" : "entries");
450 logcommand(si, CMDLOG_GET, "SQLINE:LIST: \2%s\2", full ? " FULL" : "");
451 }
452
os_cmd_sqline_sync(sourceinfo_t * si,int parc,char * parv[])453 static void os_cmd_sqline_sync(sourceinfo_t *si, int parc, char *parv[])
454 {
455 mowgli_node_t *n;
456 qline_t *q;
457
458 logcommand(si, CMDLOG_DO, "SQLINE:SYNC");
459
460 MOWGLI_ITER_FOREACH(n, qlnlist.head)
461 {
462 q = (qline_t *)n->data;
463
464 if (q->duration == 0)
465 qline_sts("*", q->mask, 0, q->reason);
466 else if (q->expires > CURRTIME)
467 qline_sts("*", q->mask, q->expires - CURRTIME, q->reason);
468 }
469
470 command_success_nodata(si, _("SQLINE list synchronized to servers."));
471 }
472
473 /* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
474 * vim:ts=8
475 * vim:sw=8
476 * vim:noexpandtab
477 */
478