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