1 /*
2  *  m_flags.c: Implements comstud-style mode flags.
3  *
4  *  Copyright 2002 by W. Campbell and the ircd-hybrid development team
5  *
6  *  Redistribution and use in source and binary forms, with or without
7  *  modification, are permitted provided that the following conditions are
8  *  met:
9  *
10  *  1.Redistributions of source code must retain the above copyright notice,
11  *    this list of conditions and the following disclaimer.
12  *  2.Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *  3.The name of the author may not be used to endorse or promote products
16  *    derived from this software without specific prior written permission.
17  *
18  *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  *  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  *  DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
22  *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24  *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26  *  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27  *  IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  *  POSSIBILITY OF SUCH DAMAGE.
29  *
30  *  $Id: m_flags.c 28681 2015-10-02 16:49:39Z androsyn $
31  */
32 
33 /* List of ircd includes from ../include/ */
34 #include "stdinc.h"
35 #include "ratbox_lib.h"
36 #include "struct.h"
37 #include "client.h"
38 #include "common.h"
39 #include "ircd.h"
40 #include "match.h"
41 #include "numeric.h"
42 #include "s_conf.h"
43 #include "s_log.h"
44 #include "s_serv.h"
45 #include "send.h"
46 #include "parse.h"
47 #include "modules.h"
48 #include "s_user.h"		/* send_umode_out() */
49 #include "s_newconf.h"
50 
51 static int m_flags(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]);
52 static int mo_flags(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]);
53 
54 static char *set_flags_to_string(struct Client *client_p);
55 static char *unset_flags_to_string(struct Client *client_p);
56 
57 
58 struct Message test_msgtab = {
59 	"FLAGS", 0, 0, 0, MFLG_SLOW,
60 	{mg_unreg, {m_flags, 0}, {m_flags, 0}, mg_ignore, mg_ignore, {mo_flags, 0}}
61 };
62 
63 
64 mapi_clist_av1 test_clist[] = { &test_msgtab, NULL };
65 
66 DECLARE_MODULE_AV1(test, NULL, NULL, test_clist, NULL, NULL, "$Revision: 28681 $");
67 
68 
69 /* FLAGS requires it's own mini parser, since the last parameter in it can
70 ** contain a number of FLAGS.  CS handles FLAGS mode1 mode2 OR
71 ** FLAGS :mode1 mode2, but not both mixed.
72 **
73 ** The best way to match a flag to a mode is with a simple table
74 */
75 
76 struct FlagTable
77 {
78 	const char *name;
79 	int mode;
80 	int oper;
81 };
82 
83 /* *INDENT-OFF* */
84 static struct FlagTable flag_table[] = {
85 	/* name               mode it represents      oper only? */
86 #if 0
87 	/* This one is special...controlled via an oper block option */
88 	{ "OWALLOPS",		UMODE_OPERWALL,		1 },
89 #endif
90 	{ "SWALLOPS",		UMODE_WALLOP,		0 },
91 	{ "STATSNOTICES",	UMODE_SPY,		1 },
92 	/* We don't have a separate OKILL and SKILL modes */
93 	{ "OKILLS",		UMODE_SERVNOTICE,	0 },
94 	{ "SKILLS",		UMODE_SKILL,		0 },
95 	{ "SNOTICES",		UMODE_SERVNOTICE,	0 },
96 	/* We don't have separate client connect and disconnect modes */
97 	{ "CLICONNECTS",	UMODE_CCONN,		1 },
98 	{ "CLIDISCONNECTS",	UMODE_CCONN,		1 },
99 	/* I'm taking a wild guess here... */
100 	{ "THROTTLES",		UMODE_REJ,		1 },
101 #if 0
102 	/* This one is special...controlled via an oper block option */
103 	{ "NICKCHANGES",	UMODE_NCHANGE,		1 },
104 	/* NICKCHANGES must be checked for separately */
105 #endif
106 	/* I'm assuming this is correct... */
107 	{ "IPMISMATCHES",	UMODE_UNAUTH,		1 },
108 	{ "LWALLOPS",		UMODE_LOCOPS,		1 },
109 	/* These aren't separate on Hybrid */
110 	{ "CONNECTS",		UMODE_EXTERNAL,		1 },
111 	{ "SQUITS",		UMODE_EXTERNAL,		1 },
112 	/* Now we have our Hybrid specific flags */
113 	{ "FULL",		UMODE_FULL,		1 },
114 	/* Not in CS, but we might as well put it here */
115 	{ "INVISIBLE",		UMODE_INVISIBLE,	0 },
116 	{ "BOTS",		UMODE_BOTS,		1 },
117 	{ "CALLERID",		UMODE_CALLERID,		0 },
118 	{ "UNAUTH",		UMODE_UNAUTH,		1 },
119 	{ "DEBUG",		UMODE_DEBUG,		1 },
120 	{ NULL, 0, 0}
121 };
122 /* *INDENT-ON* */
123 
124 /* We won't control CALLERID or INVISIBLE in here */
125 
126 #define FL_ALL_USER_FLAGS (UMODE_WALLOP | UMODE_SKILL | UMODE_SERVNOTICE )
127 
128 /* and we don't control NCHANGES here either */
129 
130 #define FL_ALL_OPER_FLAGS (FL_ALL_USER_FLAGS | UMODE_CCONN | UMODE_REJ |\
131                            UMODE_FULL | UMODE_SPY | UMODE_DEBUG |\
132                            UMODE_BOTS | UMODE_EXTERNAL |\
133                            UMODE_UNAUTH | UMODE_LOCOPS )
134 
135 /*
136 ** m_flags
137 **      parv[0] = sender prefix
138 **      parv[1] = parameter
139 */
140 static int
m_flags(struct Client * client_p,struct Client * source_p,int parc,const char * parv[])141 m_flags(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
142 {
143 	int i, j;
144 	int isadd;
145 	int setflags;
146 	int isgood;
147 	char *p;
148 	char *flag;
149 
150 	if(parc < 2)
151 	{
152 		/* Generate a list of what flags you have and what you are missing,
153 		 ** and send it to the user
154 		 */
155 		sendto_one(source_p, ":%s NOTICE %s :Current flags:%s",
156 			   me.name, parv[0], set_flags_to_string(source_p));
157 		sendto_one(source_p, ":%s NOTICE %s :Current missing flags:%s",
158 			   me.name, parv[0], unset_flags_to_string(source_p));
159 		return 1;
160 	}
161 
162 	/* Preserve the current flags */
163 	setflags = source_p->umodes;
164 
165 /* XXX - change this to support a multiple last parameter like ISON */
166 
167 	for(i = 1; i < parc; i++)
168 	{
169 		char *s = LOCAL_COPY(parv[i]);
170 		for(flag = rb_strtok_r(s, " ", &p); flag; flag = rb_strtok_r(NULL, " ", &p))
171 		{
172 			/* We default to being in ADD mode */
173 			isadd = 1;
174 
175 			/* We default to being in BAD mode */
176 			isgood = 0;
177 
178 			if(!isalpha(flag[0]))
179 			{
180 				if(flag[0] == '-')
181 					isadd = 0;
182 				else if(flag[0] == '+')
183 					isadd = 1;
184 				flag++;
185 			}
186 
187 			/* support ALL here */
188 			if(!irccmp(flag, "ALL"))
189 			{
190 				if(isadd)
191 					source_p->umodes |= FL_ALL_USER_FLAGS;
192 				else
193 					source_p->umodes &= ~FL_ALL_USER_FLAGS;
194 				sendto_one(source_p, ":%s NOTICE %s :Current flags:%s",
195 					   me.name, parv[0], set_flags_to_string(source_p));
196 				sendto_one(source_p, ":%s NOTICE %s :Current missing flags:%s",
197 					   me.name, parv[0], unset_flags_to_string(source_p));
198 				send_umode_out(client_p, source_p, setflags);
199 				return 1;
200 			}
201 
202 			for(j = 0; flag_table[j].name; j++)
203 			{
204 				if(!flag_table[j].oper && !irccmp(flag, flag_table[j].name))
205 				{
206 					if(isadd)
207 						source_p->umodes |= flag_table[j].mode;
208 					else
209 						source_p->umodes &= ~(flag_table[j].mode);
210 					isgood = 1;
211 					continue;
212 				}
213 			}
214 			/* This for ended without matching a valid FLAG, here is where
215 			 ** I want to operate differently than ircd-comstud, and just ignore
216 			 ** the invalid flag, send a warning and go on.
217 			 */
218 			if(!isgood)
219 				sendto_one(source_p, ":%s NOTICE %s :Invalid FLAGS: %s (IGNORING)",
220 					   me.name, parv[0], flag);
221 		}
222 	}
223 
224 	/* All done setting the flags, print the notices out to the user
225 	 ** telling what flags they have and what flags they are missing
226 	 */
227 	sendto_one(source_p, ":%s NOTICE %s :Current flags:%s",
228 		   me.name, parv[0], set_flags_to_string(source_p));
229 	sendto_one(source_p, ":%s NOTICE %s :Current missing flags:%s",
230 		   me.name, parv[0], unset_flags_to_string(source_p));
231 
232 	send_umode_out(client_p, source_p, setflags);
233 	return 0;
234 }
235 
236 /*
237 ** mo_flags
238 **      parv[0] = sender prefix
239 **      parv[1] = parameter
240 */
241 static int
mo_flags(struct Client * client_p,struct Client * source_p,int parc,const char * parv[])242 mo_flags(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
243 {
244 	int i, j;
245 	int isadd;
246 	int setflags;
247 	int isgood;
248 	char *p;
249 	char *flag;
250 
251 	if(parc < 2)
252 	{
253 		/* Generate a list of what flags you have and what you are missing,
254 		 ** and send it to the user
255 		 */
256 		sendto_one(source_p, ":%s NOTICE %s :Current flags:%s",
257 			   me.name, parv[0], set_flags_to_string(source_p));
258 		sendto_one(source_p, ":%s NOTICE %s :Current missing flags:%s",
259 			   me.name, parv[0], unset_flags_to_string(source_p));
260 		return 1;
261 	}
262 
263 	/* Preserve the current flags */
264 	setflags = source_p->umodes;
265 
266 /* XXX - change this to support a multiple last parameter like ISON */
267 
268 	for(i = 1; i < parc; i++)
269 	{
270 		char *s = LOCAL_COPY(parv[i]);
271 		for(flag = rb_strtok_r(s, " ", &p); flag; flag = rb_strtok_r(NULL, " ", &p))
272 		{
273 			/* We default to being in ADD mode */
274 			isadd = 1;
275 
276 			/* We default to being in BAD mode */
277 			isgood = 0;
278 
279 			if(!isalpha(flag[0]))
280 			{
281 				if(flag[0] == '-')
282 					isadd = 0;
283 				else if(flag[0] == '+')
284 					isadd = 1;
285 				flag++;
286 			}
287 
288 			/* support ALL here */
289 			if(!irccmp(flag, "ALL"))
290 			{
291 				if(isadd)
292 					source_p->umodes |= FL_ALL_OPER_FLAGS;
293 				else
294 					source_p->umodes &= ~FL_ALL_OPER_FLAGS;
295 				sendto_one(source_p, ":%s NOTICE %s :Current flags:%s",
296 					   me.name, parv[0], set_flags_to_string(source_p));
297 				sendto_one(source_p, ":%s NOTICE %s :Current missing flags:%s",
298 					   me.name, parv[0], unset_flags_to_string(source_p));
299 				send_umode_out(client_p, source_p, setflags);
300 				return 1;
301 			}
302 
303 			if(!irccmp(flag, "NICKCHANGES"))
304 			{
305 				if(!IsOperN(source_p))
306 				{
307 					sendto_one(source_p,
308 						   ":%s NOTICE %s :*** You need oper and N flag for +n",
309 						   me.name, parv[0]);
310 					continue;
311 				}
312 				if(isadd)
313 					source_p->umodes |= UMODE_NCHANGE;
314 				else
315 					source_p->umodes &= ~UMODE_NCHANGE;
316 				isgood = 1;
317 				continue;
318 			}
319 			if(!irccmp(flag, "OWALLOPS"))
320 			{
321 				if(!IsOperOperwall(source_p))
322 				{
323 					sendto_one(source_p,
324 						   ":%s NOTICE %s :*** You need oper and operwall flag for +z",
325 						   me.name, parv[0]);
326 					continue;
327 				}
328 				if(isadd)
329 					source_p->umodes |= UMODE_OPERWALL;
330 				else
331 					source_p->umodes &= ~UMODE_OPERWALL;
332 				isgood = 1;
333 				continue;
334 			}
335 
336 			for(j = 0; flag_table[j].name; j++)
337 			{
338 				if(!irccmp(flag, flag_table[j].name))
339 				{
340 					if(isadd)
341 						source_p->umodes |= flag_table[j].mode;
342 					else
343 						source_p->umodes &= ~(flag_table[j].mode);
344 					isgood = 1;
345 					continue;
346 				}
347 			}
348 			/* This for ended without matching a valid FLAG, here is where
349 			 ** I want to operate differently than ircd-comstud, and just ignore
350 			 ** the invalid flag, send a warning and go on.
351 			 */
352 			if(!isgood)
353 				sendto_one(source_p, ":%s NOTICE %s :Invalid FLAGS: %s (IGNORING)",
354 					   me.name, parv[0], flag);
355 		}
356 	}
357 
358 	/* All done setting the flags, print the notices out to the user
359 	 ** telling what flags they have and what flags they are missing
360 	 */
361 	sendto_one(source_p, ":%s NOTICE %s :Current flags:%s",
362 		   me.name, parv[0], set_flags_to_string(source_p));
363 	sendto_one(source_p, ":%s NOTICE %s :Current missing flags:%s",
364 		   me.name, parv[0], unset_flags_to_string(source_p));
365 
366 	send_umode_out(client_p, source_p, setflags);
367 	return 0;
368 }
369 
370 static char *
set_flags_to_string(struct Client * client_p)371 set_flags_to_string(struct Client *client_p)
372 {
373 	/* XXX - list all flags that we have set on the client */
374 	static char setflags[BUFSIZE + 1];
375 	int i;
376 
377 	/* Clear it to begin with, we'll be doing a lot of rb_sprintf's */
378 	setflags[0] = '\0';
379 
380 	/* Unlike unset_flags_to_string(), we don't have to care about oper
381 	 ** flags and not showing them
382 	 */
383 
384 	if(client_p->umodes & UMODE_OPERWALL)
385 	{
386 		rb_sprintf(setflags, "%s %s", setflags, "OWALLOPS");
387 	}
388 
389 	for(i = 0; flag_table[i].name; i++)
390 	{
391 		if(client_p->umodes & flag_table[i].mode)
392 		{
393 			rb_sprintf(setflags, "%s %s", setflags, flag_table[i].name);
394 		}
395 	}
396 
397 #if 0
398 	if(IsOper(client_p) && IsOperN(client_p))
399 	{
400 #endif
401 		/* You can only be set +NICKCHANGES if you are an oper and
402 		 ** IsOperN(client_p) is true
403 		 */
404 		if(client_p->umodes & UMODE_NCHANGE)
405 		{
406 			rb_sprintf(setflags, "%s %s", setflags, "NICKCHANGES");
407 		}
408 #if 0
409 	}
410 #endif
411 
412 	return setflags;
413 }
414 
415 static char *
unset_flags_to_string(struct Client * client_p)416 unset_flags_to_string(struct Client *client_p)
417 {
418 	/* Inverse of above */
419 	/* XXX - list all flags that we do NOT have set on the client */
420 	static char setflags[BUFSIZE + 1];
421 	int i, isoper;
422 
423 	/* Clear it to begin with, we'll be doing a lot of rb_sprintf's */
424 	setflags[0] = '\0';
425 
426 	if(IsOper(client_p))
427 		isoper = 1;
428 	else
429 		isoper = 0;
430 
431 	if(IsOper(client_p) && IsOperOperwall(client_p))
432 	{
433 		if(!(client_p->umodes & UMODE_OPERWALL))
434 		{
435 			rb_sprintf(setflags, "%s %s", setflags, "OWALLOPS");
436 		}
437 	}
438 
439 	for(i = 0; flag_table[i].name; i++)
440 	{
441 		if(!(client_p->umodes & flag_table[i].mode))
442 		{
443 			if(!isoper && flag_table[i].oper)
444 				continue;
445 			rb_sprintf(setflags, "%s %s", setflags, flag_table[i].name);
446 		}
447 	}
448 
449 	if(IsOper(client_p) && IsOperN(client_p))
450 	{
451 		if(!(client_p->umodes & UMODE_NCHANGE))
452 		{
453 			rb_sprintf(setflags, "%s %s", setflags, "NICKCHANGES");
454 		}
455 	}
456 
457 	return setflags;
458 }
459