1 /*
2  * Copyright (c) 2005-2006 William Pitcock, et al.
3  * Rights to this code are as documented in doc/LICENSE.
4  *
5  * OperServ NOOP command.
6  *
7  */
8 
9 #include "atheme.h"
10 
11 DECLARE_MODULE_V1
12 (
13 	"operserv/noop", true, _modinit, _moddeinit,
14 	PACKAGE_STRING,
15 	VENDOR_STRING
16 );
17 
18 typedef struct noop_ noop_t;
19 
20 struct noop_ {
21 	char *target;
22 	char *added_by;
23 	char *reason;
24 };
25 
26 mowgli_list_t noop_hostmask_list;
27 mowgli_list_t noop_server_list;
28 
29 static void os_cmd_noop(sourceinfo_t *si, int parc, char *parv[]);
30 static void noop_kill_users(void *dummy);
31 static void check_quit(user_t *u);
32 static void check_user(user_t *u);
33 static mowgli_list_t noop_kill_queue;
34 static mowgli_eventloop_timer_t *noop_kill_users_timer = NULL;
35 
36 command_t os_noop = { "NOOP", N_("Restricts IRCop access."), PRIV_NOOP, 4, os_cmd_noop, { .path = "oservice/noop" } };
37 
_modinit(module_t * m)38 void _modinit(module_t *m)
39 {
40 	service_named_bind_command("operserv", &os_noop);
41 	hook_add_event("user_oper");
42 	hook_add_user_oper(check_user);
43 	hook_add_event("user_delete");
44 }
45 
_moddeinit(module_unload_intent_t intent)46 void _moddeinit(module_unload_intent_t intent)
47 {
48 	mowgli_node_t *n, *tn;
49 
50 	if (MOWGLI_LIST_LENGTH(&noop_kill_queue) > 0)
51 	{
52 		/* Cannot safely delete users from here, so just forget
53 		 * about them.
54 		 */
55 		mowgli_timer_destroy(base_eventloop, noop_kill_users_timer);
56 		MOWGLI_ITER_FOREACH_SAFE(n, tn, noop_kill_queue.head)
57 		{
58 			mowgli_node_delete(n, &noop_kill_queue);
59 			mowgli_node_free(n);
60 		}
61 		hook_del_user_delete(check_quit);
62 	}
63 	service_named_unbind_command("operserv", &os_noop);
64 	hook_del_user_oper(check_user);
65 }
66 
noop_kill_users(void * dummy)67 static void noop_kill_users(void *dummy)
68 {
69 	service_t *service;
70 	mowgli_node_t *n, *tn;
71 	user_t *u;
72 
73 	hook_del_user_delete(check_quit);
74 
75 	service = service_find("operserv");
76 	MOWGLI_ITER_FOREACH_SAFE(n, tn, noop_kill_queue.head)
77 	{
78 		u = n->data;
79 		kill_user(service->me, u, "Operator access denied");
80 		mowgli_node_delete(n, &noop_kill_queue);
81 		mowgli_node_free(n);
82 	}
83 }
84 
check_quit(user_t * u)85 static void check_quit(user_t *u)
86 {
87 	mowgli_node_t *n;
88 
89 	n = mowgli_node_find(u, &noop_kill_queue);
90 	if (n != NULL)
91 	{
92 		mowgli_node_delete(n, &noop_kill_queue);
93 		mowgli_node_free(n);
94 		if (MOWGLI_LIST_LENGTH(&noop_kill_queue) == 0)
95 		{
96 			mowgli_timer_destroy(base_eventloop, noop_kill_users_timer);
97 			hook_del_user_delete(check_quit);
98 		}
99 	}
100 }
101 
check_user(user_t * u)102 static void check_user(user_t *u)
103 {
104 	mowgli_node_t *n;
105 	char hostbuf[BUFSIZE];
106 
107 	if (mowgli_node_find(u, &noop_kill_queue))
108 		return;
109 
110 	snprintf(hostbuf, BUFSIZE, "%s!%s@%s", u->nick, u->user, u->host);
111 
112 	MOWGLI_ITER_FOREACH(n, noop_hostmask_list.head)
113 	{
114 		noop_t *np = n->data;
115 
116 		if (!match(np->target, hostbuf))
117 		{
118 			if (MOWGLI_LIST_LENGTH(&noop_kill_queue) == 0)
119 			{
120 				noop_kill_users_timer = mowgli_timer_add_once(base_eventloop, "noop_kill_users", noop_kill_users, NULL, 0);
121 				hook_add_user_delete(check_quit);
122 			}
123 			if (!mowgli_node_find(u, &noop_kill_queue))
124 				mowgli_node_add(u, mowgli_node_create(), &noop_kill_queue);
125 			/* Prevent them using the privs in Atheme. */
126 			u->flags &= ~UF_IRCOP;
127 			return;
128 		}
129 	}
130 
131 	MOWGLI_ITER_FOREACH(n, noop_server_list.head)
132 	{
133 		noop_t *np = n->data;
134 
135 		if (!match(np->target, u->server->name))
136 		{
137 			if (MOWGLI_LIST_LENGTH(&noop_kill_queue) == 0)
138 			{
139 				noop_kill_users_timer = mowgli_timer_add_once(base_eventloop, "noop_kill_users", noop_kill_users, NULL, 0);
140 				hook_add_user_delete(check_quit);
141 			}
142 			if (!mowgli_node_find(u, &noop_kill_queue))
143 				mowgli_node_add(u, mowgli_node_create(), &noop_kill_queue);
144 			/* Prevent them using the privs in Atheme. */
145 			u->flags &= ~UF_IRCOP;
146 			return;
147 		}
148 	}
149 }
150 
noop_find(char * target,mowgli_list_t * list)151 static noop_t *noop_find(char *target, mowgli_list_t *list)
152 {
153 	mowgli_node_t *n;
154 
155 	MOWGLI_ITER_FOREACH(n, list->head)
156 	{
157 		noop_t *np = n->data;
158 
159 		if (!match(np->target, target))
160 			return np;
161 	}
162 
163 	return NULL;
164 }
165 
166 /* NOOP <ADD|DEL|LIST> <HOSTMASK|SERVER> [reason] */
os_cmd_noop(sourceinfo_t * si,int parc,char * parv[])167 static void os_cmd_noop(sourceinfo_t *si, int parc, char *parv[])
168 {
169 	mowgli_node_t *n;
170 	noop_t *np;
171 	char *action = parv[0];
172 	enum { type_all, type_hostmask, type_server } type;
173 	char *mask = parv[2];
174 	char *reason = parv[3];
175 
176 	if (parc < 1 || (strcasecmp(action, "LIST") && parc < 3))
177 	{
178 		command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "NOOP");
179 		command_fail(si, fault_needmoreparams, _("Syntax: NOOP <ADD|DEL|LIST> <HOSTMASK|SERVER> <mask> [reason]"));
180 		return;
181 	}
182 	if (parc < 2)
183 		type = type_all;
184 	else if (!strcasecmp(parv[1], "HOSTMASK"))
185 		type = type_hostmask;
186 	else if (!strcasecmp(parv[1], "SERVER"))
187 		type = type_server;
188 	else
189 	{
190 		command_fail(si, fault_badparams, STR_INVALID_PARAMS, "NOOP");
191 		command_fail(si, fault_badparams, _("Syntax: NOOP <ADD|DEL|LIST> <HOSTMASK|SERVER> <mask> [reason]"));
192 		return;
193 	}
194 
195 	if (!strcasecmp(action, "ADD"))
196 	{
197 		if (type == type_hostmask)
198 		{
199 			if ((np = noop_find(mask, &noop_hostmask_list)))
200 			{
201 				command_fail(si, fault_nochange, _("There is already a NOOP entry covering this target."));
202 				return;
203 			}
204 
205 			np = smalloc(sizeof *np);
206 
207 			np->target = sstrdup(mask);
208 			np->added_by = sstrdup(get_storage_oper_name(si));
209 
210 			if (reason)
211 				np->reason = sstrdup(reason);
212 			else
213 				np->reason = sstrdup("Abusive operator.");
214 
215 			n = mowgli_node_create();
216 			mowgli_node_add(np, n, &noop_hostmask_list);
217 
218 			logcommand(si, CMDLOG_ADMIN, "NOOP:ADD:HOSTMASK: \2%s\2 (reason: \2%s\2)", np->target, np->reason);
219 			command_success_nodata(si, _("Added \2%s\2 to the hostmask NOOP list."), mask);
220 
221 			return;
222 		}
223 		else if (type == type_server)
224 		{
225 			if ((np = noop_find(mask, &noop_server_list)))
226 			{
227 				command_fail(si, fault_nochange, _("There is already a NOOP entry covering this target."));
228 				return;
229 			}
230 
231 			np = smalloc(sizeof *np);
232 
233 			np->target = sstrdup(mask);
234 			np->added_by = sstrdup(get_storage_oper_name(si));
235 
236 			if (reason)
237 				np->reason = sstrdup(reason);
238 			else
239 				np->reason = sstrdup("Abusive operator.");
240 
241 			n = mowgli_node_create();
242 			mowgli_node_add(np, n, &noop_server_list);
243 
244 			logcommand(si, CMDLOG_ADMIN, "NOOP:ADD:SERVER: \2%s\2 (reason: \2%s\2)", np->target, np->reason);
245 			command_success_nodata(si, _("Added \2%s\2 to the server NOOP list."), mask);
246 
247 			return;
248 		}
249 	}
250 	else if (!strcasecmp(action, "DEL"))
251 	{
252 		if (type == type_hostmask)
253 		{
254 			if (!(np = noop_find(mask, &noop_hostmask_list)))
255 			{
256 				command_fail(si, fault_nosuch_target, _("There is no NOOP hostmask entry for this target."));
257 				return;
258 			}
259 
260 			logcommand(si, CMDLOG_ADMIN, "NOOP:DEL:HOSTMASK: \2%s\2", np->target);
261 			command_success_nodata(si, _("Removed \2%s\2 from the hostmask NOOP list."), np->target);
262 
263 			n = mowgli_node_find(np, &noop_hostmask_list);
264 
265 			free(np->target);
266 			free(np->added_by);
267 			free(np->reason);
268 
269 			mowgli_node_delete(n, &noop_hostmask_list);
270 			mowgli_node_free(n);
271 			free(np);
272 
273 			return;
274 		}
275 		else if (type == type_server)
276 		{
277 			if (!(np = noop_find(mask, &noop_server_list)))
278 			{
279 				command_fail(si, fault_nosuch_target, _("There is no NOOP server entry for this target."));
280 				return;
281 			}
282 
283 			logcommand(si, CMDLOG_ADMIN, "NOOP:DEL:SERVER: \2%s\2", np->target);
284 			command_success_nodata(si, _("Removed \2%s\2 from the server NOOP list."), np->target);
285 
286 			n = mowgli_node_find(np, &noop_server_list);
287 
288 			free(np->target);
289 			free(np->added_by);
290 			free(np->reason);
291 
292 			mowgli_node_delete(n, &noop_server_list);
293 			mowgli_node_free(n);
294 			free(np);
295 
296 			return;
297 		}
298 	}
299 	else if (!strcasecmp(action, "LIST"))
300 	{
301 		switch (type)
302 		{
303 			case type_all:
304 				logcommand(si, CMDLOG_GET, "NOOP:LIST");
305 				break;
306 			case type_hostmask:
307 				logcommand(si, CMDLOG_GET, "NOOP:LIST:HOSTMASK");
308 				break;
309 			case type_server:
310 				logcommand(si, CMDLOG_GET, "NOOP:LIST:SERVER");
311 				break;
312 		}
313 		if (type == type_all || type == type_hostmask)
314 		{
315 			unsigned int i = 1;
316 			command_success_nodata(si, _("Hostmask NOOP list (%zu entries):"), noop_hostmask_list.count);
317 			command_success_nodata(si, " ");
318 			command_success_nodata(si, _("Entry Hostmask                        Adder                 Reason"));
319 			command_success_nodata(si, "----- ------------------------------- --------------------- --------------------------");
320 
321 			MOWGLI_ITER_FOREACH(n, noop_hostmask_list.head)
322 			{
323 				np = n->data;
324 
325 				command_success_nodata(si, "%-5d %-31s %-21s %s", i, np->target, np->added_by, np->reason);
326 				i++;
327 			}
328 
329 			command_success_nodata(si, "----- ------------------------------- --------------------- --------------------------");
330 			command_success_nodata(si, _("End of Hostmask NOOP list."));
331 		}
332 		if (type == type_all || type == type_server)
333 		{
334 			unsigned int i = 1;
335 			command_success_nodata(si, _("Server NOOP list (%zu entries):"), noop_server_list.count);
336 			command_success_nodata(si, " ");
337 			command_success_nodata(si, _("Entry Hostmask                        Adder                 Reason"));
338 			command_success_nodata(si, "----- ------------------------------- --------------------- --------------------------");
339 
340 			MOWGLI_ITER_FOREACH(n, noop_server_list.head)
341 			{
342 				np = n->data;
343 
344 				command_success_nodata(si, "%-5d %-31s %-21s %s", i, np->target, np->added_by, np->reason);
345 				i++;
346 			}
347 
348 			command_success_nodata(si, "----- ------------------------------- --------------------- --------------------------");
349 			command_success_nodata(si, _("End of Server NOOP list."));
350 		}
351 	}
352 	else
353 	{
354 		command_fail(si, fault_badparams, STR_INVALID_PARAMS, "NOOP");
355 		command_fail(si, fault_badparams, _("Syntax: NOOP <ADD|DEL|LIST> <HOSTMASK|SERVER> <mask> [reason]"));
356 	}
357 }
358 
359 /* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
360  * vim:ts=8
361  * vim:sw=8
362  * vim:noexpandtab
363  */
364