1 /*
2 * Copyright (c) 2009 Atheme Development Group
3 * Copyright (c) 2009 Rizon Development Team <http://redmine.rizon.net>
4 * Rights to this code are documented in doc/LICENSE.
5 *
6 * This file contains functionality which implements
7 * the OperServ SGLINE command.
8 */
9
10 #include "atheme.h"
11
12 DECLARE_MODULE_V1
13 (
14 "operserv/sgline", false, _modinit, _moddeinit,
15 PACKAGE_STRING,
16 VENDOR_STRING
17 );
18
19 static void os_sgline_newuser(hook_user_nick_t *data);
20
21 static void os_cmd_sgline(sourceinfo_t *si, int parc, char *parv[]);
22 static void os_cmd_sgline_add(sourceinfo_t *si, int parc, char *parv[]);
23 static void os_cmd_sgline_del(sourceinfo_t *si, int parc, char *parv[]);
24 static void os_cmd_sgline_list(sourceinfo_t *si, int parc, char *parv[]);
25 static void os_cmd_sgline_sync(sourceinfo_t *si, int parc, char *parv[]);
26
27 command_t os_sgline = { "SGLINE", N_("Manages network realname bans."), PRIV_MASS_AKILL, 3, os_cmd_sgline, { .path = "oservice/sgline" } };
28
29 command_t os_sgline_add = { "ADD", N_("Adds a network realname ban"), AC_NONE, 2, os_cmd_sgline_add, { .path = "" } };
30 command_t os_sgline_del = { "DEL", N_("Deletes a network realname ban"), AC_NONE, 1, os_cmd_sgline_del, { .path = "" } };
31 command_t os_sgline_list = { "LIST", N_("Lists all network realname bans"), AC_NONE, 1, os_cmd_sgline_list, { .path = "" } };
32 command_t os_sgline_sync = { "SYNC", N_("Synchronises network realname bans to servers"), AC_NONE, 0, os_cmd_sgline_sync, { .path = "" } };
33
34 mowgli_patricia_t *os_sgline_cmds;
35
_modinit(module_t * m)36 void _modinit(module_t *m)
37 {
38 if (ircd != NULL && xline_sts == generic_xline_sts)
39 {
40 slog(LG_INFO, "Module %s requires xline support, refusing to load.",
41 m->name);
42 m->mflags = MODTYPE_FAIL;
43 return;
44 }
45
46 service_named_bind_command("operserv", &os_sgline);
47
48 os_sgline_cmds = mowgli_patricia_create(strcasecanon);
49
50 /* Add sub-commands */
51 command_add(&os_sgline_add, os_sgline_cmds);
52 command_add(&os_sgline_del, os_sgline_cmds);
53 command_add(&os_sgline_list, os_sgline_cmds);
54 command_add(&os_sgline_sync, os_sgline_cmds);
55
56 hook_add_event("user_add");
57 hook_add_user_add(os_sgline_newuser);
58 }
59
_moddeinit(module_unload_intent_t intent)60 void _moddeinit(module_unload_intent_t intent)
61 {
62 service_named_unbind_command("operserv", &os_sgline);
63
64 /* Delete sub-commands */
65 command_delete(&os_sgline_add, os_sgline_cmds);
66 command_delete(&os_sgline_del, os_sgline_cmds);
67 command_delete(&os_sgline_list, os_sgline_cmds);
68 command_delete(&os_sgline_sync, os_sgline_cmds);
69
70 hook_del_user_add(os_sgline_newuser);
71
72 mowgli_patricia_destroy(os_sgline_cmds, NULL, NULL);
73 }
74
os_sgline_newuser(hook_user_nick_t * data)75 static void os_sgline_newuser(hook_user_nick_t *data)
76 {
77 user_t *u = data->u;
78 xline_t *x;
79
80 /* If the user has been killed, don't do anything. */
81 if (!u)
82 return;
83
84 if (is_internal_client(u))
85 return;
86 x = xline_find_user(u);
87 if (x != NULL)
88 {
89 /* Server didn't have that xline, send it again.
90 * To ensure xline exempt works on sglines too, do
91 * not send a KILL. -- jilles */
92 xline_sts("*", x->realname, x->duration ? x->expires - CURRTIME : 0, x->reason);
93 }
94 }
95
os_cmd_sgline(sourceinfo_t * si,int parc,char * parv[])96 static void os_cmd_sgline(sourceinfo_t *si, int parc, char *parv[])
97 {
98 /* Grab args */
99 char *cmd = parv[0];
100 command_t *c;
101
102 /* Bad/missing arg */
103 if (!cmd)
104 {
105 command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "SGLINE");
106 command_fail(si, fault_needmoreparams, _("Syntax: SGLINE ADD|DEL|LIST"));
107 return;
108 }
109
110 c = command_find(os_sgline_cmds, cmd);
111 if(c == NULL)
112 {
113 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);
114 return;
115 }
116
117 command_exec(si->service, si, c, parc - 1, parv + 1);
118 }
119
os_cmd_sgline_add(sourceinfo_t * si,int parc,char * parv[])120 static void os_cmd_sgline_add(sourceinfo_t *si, int parc, char *parv[])
121 {
122 char *target = parv[0];
123 char *token = strtok(parv[1], " ");
124 char *treason, reason[BUFSIZE];
125 long duration;
126 char *s;
127 xline_t *x;
128
129 if (!target || !token)
130 {
131 command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "SGLINE ADD");
132 command_fail(si, fault_needmoreparams, _("Syntax: SGLINE ADD <gecos> [!P|!T <minutes>] <reason>"));
133 return;
134 }
135
136 if (!strcasecmp(token, "!P"))
137 {
138 duration = 0;
139 treason = strtok(NULL, "");
140
141 if (treason)
142 mowgli_strlcpy(reason, treason, BUFSIZE);
143 else
144 mowgli_strlcpy(reason, "No reason given", BUFSIZE);
145 }
146 else if (!strcasecmp(token, "!T"))
147 {
148 s = strtok(NULL, " ");
149 treason = strtok(NULL, "");
150 if (treason)
151 mowgli_strlcpy(reason, treason, BUFSIZE);
152 else
153 mowgli_strlcpy(reason, "No reason given", BUFSIZE);
154 if (s)
155 {
156 duration = (atol(s) * 60);
157 while (isdigit((unsigned char)*s))
158 s++;
159 if (*s == 'h' || *s == 'H')
160 duration *= 60;
161 else if (*s == 'd' || *s == 'D')
162 duration *= 1440;
163 else if (*s == 'w' || *s == 'W')
164 duration *= 10080;
165 else if (*s == '\0')
166 ;
167 else
168 duration = 0;
169 if (duration == 0)
170 {
171 command_fail(si, fault_badparams, _("Invalid duration given."));
172 command_fail(si, fault_badparams, _("Syntax: SGLINE ADD <gecos> [!P|!T <minutes>] <reason>"));
173 return;
174 }
175 }
176 else {
177 command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "SGLINE ADD");
178 command_fail(si, fault_needmoreparams, _("Syntax: SGLINE ADD <gecos> [!P|!T <minutes>] <reason>"));
179 return;
180 }
181
182 }
183 else
184 {
185 duration = config_options.kline_time;
186 mowgli_strlcpy(reason, token, BUFSIZE);
187 treason = strtok(NULL, "");
188
189 if (treason)
190 {
191 mowgli_strlcat(reason, " ", BUFSIZE);
192 mowgli_strlcat(reason, treason, BUFSIZE);
193 }
194 }
195
196 char *p;
197 int i = 0;
198
199 /* make sure there's at least 3 non-wildcards */
200 /* except if there are no wildcards at all */
201 for (p = target; *p; p++)
202 {
203 if (*p != '*' && *p != '?')
204 i++;
205 }
206
207 if (i < 3 && (strchr(target, '*') || strchr(target, '?')) && !has_priv(si, PRIV_AKILL_ANYMASK))
208 {
209 command_fail(si, fault_badparams, _("Invalid gecos: \2%s\2. At least three non-wildcard characters are required."), target);
210 return;
211 }
212
213 if (strlen(target) > GECOSLEN * 2)
214 {
215 command_fail(si, fault_badparams, _("The mask provided is too long."));
216 return;
217 }
218
219 if (xline_find(target))
220 {
221 command_fail(si, fault_nochange, _("SGLINE \2%s\2 is already matched in the database."), target);
222 return;
223 }
224
225 x = xline_add(target, reason, duration, get_storage_oper_name(si));
226
227 if (duration)
228 command_success_nodata(si, _("Timed SGLINE on \2%s\2 was successfully added and will expire in %s."), x->realname, timediff(duration));
229 else
230 command_success_nodata(si, _("SGLINE on \2%s\2 was successfully added."), x->realname);
231
232 verbose_wallops("\2%s\2 is \2adding\2 an \2SGLINE\2 for \2%s\2 -- reason: \2%s\2", get_oper_name(si), x->realname,
233 x->reason);
234 logcommand(si, CMDLOG_ADMIN, "SGLINE:ADD: \2%s\2 (reason: \2%s\2)", x->realname, x->reason);
235 }
236
os_cmd_sgline_del(sourceinfo_t * si,int parc,char * parv[])237 static void os_cmd_sgline_del(sourceinfo_t *si, int parc, char *parv[])
238 {
239 char *target = parv[0];
240 xline_t *x;
241 unsigned int number;
242 char *s;
243
244 if (!target)
245 {
246 command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "SGLINE DEL");
247 command_fail(si, fault_needmoreparams, _("Syntax: SGLINE DEL <gecos>"));
248 return;
249 }
250
251 if (strchr(target, ','))
252 {
253 unsigned int start = 0, end = 0, i;
254 char t[16];
255
256 s = strtok(target, ",");
257
258 do
259 {
260 if (strchr(s, ':'))
261 {
262 for (i = 0; *s != ':'; s++, i++)
263 t[i] = *s;
264
265 t[++i] = '\0';
266 start = atoi(t);
267
268 s++; /* skip past the : */
269
270 for (i = 0; *s != '\0'; s++, i++)
271 t[i] = *s;
272
273 t[++i] = '\0';
274 end = atoi(t);
275
276 for (i = start; i <= end; i++)
277 {
278 if (!(x = xline_find_num(i)))
279 {
280 command_fail(si, fault_nosuch_target, _("No such SGLINE with number \2%d\2."), i);
281 continue;
282 }
283
284 command_success_nodata(si, _("SGLINE on \2%s\2 has been successfully removed."), x->realname);
285 verbose_wallops("\2%s\2 is \2removing\2 an \2SGLINE\2 for \2%s\2 -- reason: \2%s\2",
286 get_oper_name(si), x->realname, x->reason);
287
288 logcommand(si, CMDLOG_ADMIN, "SGLINE:DEL: \2%s\2", x->realname);
289 xline_delete(x->realname);
290 }
291
292 continue;
293 }
294
295 number = atoi(s);
296
297 if (!(x = xline_find_num(number)))
298 {
299 command_fail(si, fault_nosuch_target, _("No such SGLINE with number \2%d\2."), number);
300 return;
301 }
302
303 command_success_nodata(si, _("SGLINE on \2%s\2 has been successfully removed."), x->realname);
304 verbose_wallops("\2%s\2 is \2removing\2 an \2SGLINE\2 for \2%s\2 -- reason: \2%s\2",
305 get_oper_name(si), x->realname, x->reason);
306
307 logcommand(si, CMDLOG_ADMIN, "SGLINE:DEL: \2%s\2", x->realname);
308 xline_delete(x->realname);
309 } while ((s = strtok(NULL, ",")));
310
311 return;
312 }
313
314 if (!(x = xline_find(target)))
315 {
316 command_fail(si, fault_nosuch_target, _("No such SGLINE: \2%s\2."), target);
317 return;
318 }
319
320 command_success_nodata(si, _("SGLINE on \2%s\2 has been successfully removed."), target);
321
322 verbose_wallops("\2%s\2 is \2removing\2 an \2SGLINE\2 for \2%s\2 -- reason: \2%s\2",
323 get_oper_name(si), x->realname, x->reason);
324
325 logcommand(si, CMDLOG_ADMIN, "SGLINE:DEL: \2%s\2", target);
326 xline_delete(target);
327 }
328
os_cmd_sgline_list(sourceinfo_t * si,int parc,char * parv[])329 static void os_cmd_sgline_list(sourceinfo_t *si, int parc, char *parv[])
330 {
331 char *param = parv[0];
332 bool full = false;
333 mowgli_node_t *n;
334 xline_t *x;
335
336 if (param != NULL && !strcasecmp(param, "FULL"))
337 full = true;
338
339 if (full)
340 command_success_nodata(si, _("SGLINE list (with reasons):"));
341 else
342 command_success_nodata(si, _("SGLINE list:"));
343
344 MOWGLI_ITER_FOREACH(n, xlnlist.head)
345 {
346 x = (xline_t *)n->data;
347
348 if (x->duration && full)
349 command_success_nodata(si, _("%d: %s - by \2%s\2 - expires in \2%s\2 - (%s)"), x->number, x->realname, x->setby, timediff(x->expires > CURRTIME ? x->expires - CURRTIME : 0), x->reason);
350 else if (x->duration && !full)
351 command_success_nodata(si, _("%d: %s - by \2%s\2 - expires in \2%s\2"), x->number, x->realname, x->setby, timediff(x->expires > CURRTIME ? x->expires - CURRTIME : 0));
352 else if (!x->duration && full)
353 command_success_nodata(si, _("%d: %s - by \2%s\2 - \2permanent\2 - (%s)"), x->number, x->realname, x->setby, x->reason);
354 else
355 command_success_nodata(si, _("%d: %s - by \2%s\2 - \2permanent\2"), x->number, x->realname, x->setby);
356 }
357
358 command_success_nodata(si, _("Total of \2%zu\2 %s in SGLINE list."), xlnlist.count, (xlnlist.count == 1) ? "entry" : "entries");
359 logcommand(si, CMDLOG_GET, "SGLINE:LIST: \2%s\2", full ? " FULL" : "");
360 }
361
os_cmd_sgline_sync(sourceinfo_t * si,int parc,char * parv[])362 static void os_cmd_sgline_sync(sourceinfo_t *si, int parc, char *parv[])
363 {
364 mowgli_node_t *n;
365 xline_t *x;
366
367 logcommand(si, CMDLOG_DO, "SGLINE:SYNC");
368
369 MOWGLI_ITER_FOREACH(n, xlnlist.head)
370 {
371 x = (xline_t *)n->data;
372
373 if (x->duration == 0)
374 xline_sts("*", x->realname, 0, x->reason);
375 else if (x->expires > CURRTIME)
376 xline_sts("*", x->realname, x->expires - CURRTIME, x->reason);
377 }
378
379 command_success_nodata(si, _("SGLINE list synchronized to servers."));
380 }
381
382 /* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
383 * vim:ts=8
384 * vim:sw=8
385 * vim:noexpandtab
386 */
387