1 /************************************************************************
2  *   IRC - Internet Relay Chat, extcmodes.c
3  *   (C) 2003-2007 Bram Matthys (Syzop) and the UnrealIRCd Team
4  *
5  *   See file AUTHORS in IRC package for additional names of
6  *   the programmers.
7  *
8  *   This program is free software; you can redistribute it and/or modify
9  *   it under the terms of the GNU General Public License as published by
10  *   the Free Software Foundation; either version 1, or (at your option)
11  *   any later version.
12  *
13  *   This program is distributed in the hope that it will be useful,
14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *   GNU General Public License for more details.
17  *
18  *   You should have received a copy of the GNU General Public License
19  *   along with this program; if not, write to the Free Software
20  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  */
22 
23 #include "struct.h"
24 #include "common.h"
25 #include "sys.h"
26 #include "numeric.h"
27 #include "msg.h"
28 #include "proto.h"
29 #include "channel.h"
30 #include "version.h"
31 #include <time.h>
32 #ifdef _WIN32
33 #include <sys/timeb.h>
34 #endif
35 #include <sys/stat.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #ifdef _WIN32
40 #include <io.h>
41 #endif
42 #include <fcntl.h>
43 #include "h.h"
44 
45 #ifdef EXTCMODE
46 
47 extern char cmodestring[512];
48 
49 extern void make_cmodestr(void);
50 
51 char extchmstr[4][64];
52 
53 Cmode *Channelmode_Table = NULL;
54 unsigned short Channelmode_highest = 0;
55 
56 Cmode_t EXTMODE_NONOTICE = 0L;
57 #ifdef STRIPBADWORDS
58 Cmode_t EXTMODE_STRIPBADWORDS = 0L;
59 #endif
60 
61 #ifdef JOINTHROTTLE
62 /* cmode j stuff... */
63 Cmode_t EXTMODE_JOINTHROTTLE = 0L;
64 int cmodej_is_ok(aClient *sptr, aChannel *chptr, char *para, int type, int what);
65 CmodeParam *cmodej_put_param(CmodeParam *r_in, char *param);
66 char *cmodej_get_param(CmodeParam *r_in);
67 char *cmodej_conv_param(char *param_in);
68 void cmodej_free_param(CmodeParam *r);
69 CmodeParam *cmodej_dup_struct(CmodeParam *r_in);
70 int cmodej_sjoin_check(aChannel *chptr, CmodeParam *ourx, CmodeParam *theirx);
71 #endif
72 int extcmode_cmodeT_requirechop(aClient *cptr, aChannel *chptr, char *para, int checkt, int what);
73 #ifdef STRIPBADWORDS
74 int extcmode_cmodeG_requirechop(aClient *cptr, aChannel *chptr, char *para, int checkt, int what);
75 #endif
76 
make_extcmodestr()77 void make_extcmodestr()
78 {
79 char *p;
80 int i;
81 
82 	extchmstr[0][0] = extchmstr[1][0] = extchmstr[2][0] = extchmstr[3][0] = '\0';
83 
84 	/* type 1: lists (like b/e) */
85 	/* [NOT IMPLEMENTED IN EXTCMODES] */
86 
87 	/* type 2: 1 par to set/unset */
88 	/* [NOT IMPLEMENTED] */
89 
90 	/* type 3: 1 param to set, 0 params to unset */
91 	p = extchmstr[2];
92 	for (i=0; i <= Channelmode_highest; i++)
93 		if (Channelmode_Table[i].paracount && Channelmode_Table[i].flag)
94 			*p++ = Channelmode_Table[i].flag;
95 	*p = '\0';
96 
97 	/* type 4: paramless modes */
98 	p = extchmstr[3];
99 	for (i=0; i <= Channelmode_highest; i++)
100 		if (!Channelmode_Table[i].paracount && Channelmode_Table[i].flag)
101 			*p++ = Channelmode_Table[i].flag;
102 	*p = '\0';
103 }
104 
load_extendedchanmodes(void)105 static void load_extendedchanmodes(void)
106 {
107 	CmodeInfo req;
108 	memset(&req, 0, sizeof(req));
109 
110 	req.paracount = 0;
111 	req.is_ok = extcmode_cmodeT_requirechop;
112 	req.flag = 'T';
113 	CmodeAdd(NULL, req, &EXTMODE_NONOTICE);
114 #ifdef STRIPBADWORDS
115 	req.flag = 'G';
116 	req.is_ok = extcmode_cmodeG_requirechop;
117 	CmodeAdd(NULL, req, &EXTMODE_STRIPBADWORDS);
118 #endif
119 
120 #ifdef JOINTHROTTLE
121 	/* +j */
122 	memset(&req, 0, sizeof(req));
123 	req.paracount = 1;
124 	req.is_ok = cmodej_is_ok;
125 	req.flag = 'j';
126 	req.put_param = cmodej_put_param;
127 	req.get_param = cmodej_get_param;
128 	req.conv_param = cmodej_conv_param;
129 	req.free_param = cmodej_free_param;
130 	req.dup_struct = cmodej_dup_struct;
131 	req.sjoin_check = cmodej_sjoin_check;
132 	CmodeAdd(NULL, req, &EXTMODE_JOINTHROTTLE);
133 #endif
134 }
135 
extcmode_init(void)136 void	extcmode_init(void)
137 {
138 	Cmode_t val = 1;
139 	int	i;
140 	Channelmode_Table = (Cmode *)MyMalloc(sizeof(Cmode) * EXTCMODETABLESZ);
141 	bzero(Channelmode_Table, sizeof(Cmode) * EXTCMODETABLESZ);
142 	for (i = 0; i < EXTCMODETABLESZ; i++)
143 	{
144 		Channelmode_Table[i].mode = val;
145 		val *= 2;
146 	}
147 	Channelmode_highest = 0;
148 	memset(&extchmstr, 0, sizeof(extchmstr));
149 
150 	/* And load the build-in extended chanmodes... */
151 	load_extendedchanmodes();
152 }
153 
CmodeAdd(Module * module,CmodeInfo req,Cmode_t * mode)154 Cmode *CmodeAdd(Module *module, CmodeInfo req, Cmode_t *mode)
155 {
156 	short i = 0, j = 0;
157 	char tmpbuf[512];
158 
159 	while (i < EXTCMODETABLESZ)
160 	{
161 		if (!Channelmode_Table[i].flag)
162 			break;
163 		else if (Channelmode_Table[i].flag == req.flag)
164 		{
165 			if (Channelmode_Table[i].unloaded)
166 			{
167 				Channelmode_Table[i].unloaded = 0;
168 				break;
169 			} else {
170 				if (module)
171 					module->errorcode = MODERR_EXISTS;
172 				return NULL;
173 			}
174 		}
175 		i++;
176 	}
177 	if (i == EXTCMODETABLESZ)
178 	{
179 		Debug((DEBUG_DEBUG, "CmodeAdd failed, no space"));
180 		if (module)
181 			module->errorcode = MODERR_NOSPACE;
182 		return NULL;
183 	}
184 	*mode = Channelmode_Table[i].mode;
185 	/* Update extended channel mode table highest */
186 	Channelmode_Table[i].flag = req.flag;
187 	Channelmode_Table[i].paracount = req.paracount;
188 	Channelmode_Table[i].is_ok = req.is_ok;
189 	Channelmode_Table[i].put_param = req.put_param;
190 	Channelmode_Table[i].get_param = req.get_param;
191 	Channelmode_Table[i].conv_param = req.conv_param;
192 	Channelmode_Table[i].free_param = req.free_param;
193 	Channelmode_Table[i].dup_struct = req.dup_struct;
194 	Channelmode_Table[i].sjoin_check = req.sjoin_check;
195 	Channelmode_Table[i].local = req.local;
196 	Channelmode_Table[i].owner = module;
197 
198 	for (j = 0; j < EXTCMODETABLESZ; j++)
199 		if (Channelmode_Table[j].flag)
200 			if (j > Channelmode_highest)
201 				Channelmode_highest = j;
202 	if (module)
203 	{
204 		ModuleObject *cmodeobj = MyMallocEx(sizeof(ModuleObject));
205 		cmodeobj->object.cmode = &Channelmode_Table[i];
206 		cmodeobj->type = MOBJ_CMODE;
207 		AddListItem(cmodeobj, module->objects);
208 		module->errorcode = MODERR_NOERROR;
209 	}
210 	if (loop.ircd_booted)
211 	{
212 		make_cmodestr();
213 		make_extcmodestr();
214 		ircsprintf(tmpbuf, CHPAR1 "%s," CHPAR2 "%s," CHPAR3 "%s," CHPAR4 "%s",
215 			EXPAR1, EXPAR2, EXPAR3, EXPAR4);
216 		IsupportSetValue(IsupportFind("CHANMODES"), tmpbuf);
217 	}
218 	return &(Channelmode_Table[i]);
219 }
220 
unload_extcmode_commit(Cmode * cmode)221 void unload_extcmode_commit(Cmode *cmode)
222 {
223 char tmpbuf[512];
224 aChannel *chptr;
225 
226 	if (!cmode)
227 		return;
228 	if (cmode->paracount == 1)
229 	{
230 		/* If we don't do this, we will crash anyway.. but then with severe corruption / suckyness */
231 		ircd_log(LOG_ERROR, "FATAL ERROR: ChannelMode module for chanmode +%c is misbehaving: "
232 		                    "all chanmode modules with parameters should be tagged PERManent.", cmode->flag);
233 		abort();
234 	}
235 
236 	for (chptr = channel; chptr; chptr = chptr->nextch)
237 		if (chptr->mode.extmode && cmode->mode)
238 		{
239 			/* Unset channel mode and send MODE -<char> to other servers */
240 			sendto_channel_butserv(chptr, &me, ":%s MODE %s -%c",
241 				me.name, chptr->chname, cmode->flag);
242 			sendto_serv_butone(NULL, ":%s MODE %s -%c 0",
243 				me.name, chptr->chname, cmode->flag);
244 			chptr->mode.extmode &= ~cmode->mode;
245 		}
246 
247 	cmode->flag = '\0';
248 	make_cmodestr();
249 	make_extcmodestr();
250 	ircsprintf(tmpbuf, CHPAR1 "%s," CHPAR2 "%s," CHPAR3 "%s," CHPAR4 "%s",
251 			EXPAR1, EXPAR2, EXPAR3, EXPAR4);
252 	IsupportSetValue(IsupportFind("CHANMODES"), tmpbuf);
253 }
254 
CmodeDel(Cmode * cmode)255 void CmodeDel(Cmode *cmode)
256 {
257 	/* It would be nice if we could abort() here if a parameter module is trying to unload which is extremely dangerous/crashy/disallowed */
258 
259 	if (loop.ircd_rehashing)
260 		cmode->unloaded = 1;
261 	else
262 		unload_extcmode_commit(cmode);
263 
264 	if (cmode->owner)
265 	{
266 		ModuleObject *cmodeobj;
267 		for (cmodeobj = cmode->owner->objects; cmodeobj; cmodeobj = cmodeobj->next) {
268 			if (cmodeobj->type == MOBJ_CMODE && cmodeobj->object.cmode == cmode) {
269 				DelListItem(cmodeobj, cmode->owner->objects);
270 				MyFree(cmodeobj);
271 				break;
272 			}
273 		}
274 		cmode->owner = NULL;
275 	}
276 }
277 
unload_all_unused_extcmodes(void)278 void unload_all_unused_extcmodes(void)
279 {
280 int i;
281 
282 	for (i = 0; i < EXTCMODETABLESZ; i++)
283 		if (Channelmode_Table[i].flag && Channelmode_Table[i].unloaded)
284 		{
285 			unload_extcmode_commit(&Channelmode_Table[i]);
286 		}
287 
288 }
289 
290 /** searches in chptr extmode parameters and returns entry or NULL. */
extcmode_get_struct(CmodeParam * p,char ch)291 CmodeParam *extcmode_get_struct(CmodeParam *p, char ch)
292 {
293 
294 	while(p)
295 	{
296 		if (p->flag == ch)
297 			return p;
298 		p = p->next;
299 	}
300 	return NULL;
301 }
302 
303 /* bit inefficient :/ */
extcmode_duplicate_paramlist(CmodeParam * lst)304 CmodeParam *extcmode_duplicate_paramlist(CmodeParam *lst)
305 {
306 	int i;
307 	Cmode *tbl;
308 	CmodeParam *head = NULL, *n;
309 
310 	while(lst)
311 	{
312 		tbl = NULL;
313 		for (i=0; i <= Channelmode_highest; i++)
314 		{
315 			if (Channelmode_Table[i].flag == lst->flag)
316 			{
317 				tbl = &Channelmode_Table[i]; /* & ? */
318 				break;
319 			}
320 		}
321 		n = tbl->dup_struct(lst);
322 		n->next = n->prev = NULL; /* safety (required!) */
323 		if (head)
324 		{
325 			AddListItem(n, head);
326 		} else {
327 			head = n;
328 		}
329 		lst = lst->next;
330 	}
331 	return head;
332 }
333 
extcmode_free_paramlist(CmodeParam * lst)334 void extcmode_free_paramlist(CmodeParam *lst)
335 {
336 	CmodeParam *n;
337 	int i;
338 	Cmode *tbl;
339 
340 	while(lst)
341 	{
342 		/* first remove it from the list... */
343 		n = lst;
344 		DelListItem(n, lst);
345 		/* then hunt for the param free function and let it free */
346 		tbl = NULL;
347 		for (i=0; i <= Channelmode_highest; i++)
348 		{
349 			if (Channelmode_Table[i].flag == n->flag)
350 			{
351 				tbl = &Channelmode_Table[i]; /* & ? */
352 				break;
353 			}
354 		}
355 		tbl->free_param(n);
356 	}
357 }
358 
359 /* Ok this is my mistake @ EXCHK_ACCESS_ERR error msg:
360  * the is_ok() thing does not know which mode it belongs to,
361  * this is normally redundant information of course but in
362  * case of a default handler like these, it's required to
363  * know which setting of mode failed (the mode char).
364  * I just return '?' for now, better than nothing.
365  * TO SUMMARIZE: Do not use extcmode_default_requirechop for new modules :p.
366  * Obviously in Unreal3.3* we should fix this. -- Syzop
367  */
368 
extcmode_default_requirechop(aClient * cptr,aChannel * chptr,char * para,int checkt,int what)369 int extcmode_default_requirechop(aClient *cptr, aChannel *chptr, char *para, int checkt, int what)
370 {
371 	if (IsPerson(cptr) && is_chan_op(cptr, chptr))
372 		return EX_ALLOW;
373 	if (checkt == EXCHK_ACCESS_ERR) /* can only be due to being halfop */
374 		sendto_one(cptr, err_str(ERR_NOTFORHALFOPS), me.name, cptr->name, '?');
375 	return EX_DENY;
376 }
377 
extcmode_default_requirehalfop(aClient * cptr,aChannel * chptr,char * para,int checkt,int what)378 int extcmode_default_requirehalfop(aClient *cptr, aChannel *chptr, char *para, int checkt, int what)
379 {
380 	if (IsPerson(cptr) &&
381 	    (is_chan_op(cptr, chptr) || is_half_op(cptr, chptr)))
382 		return EX_ALLOW;
383 	return EX_DENY;
384 }
385 
extcmode_cmodeT_requirechop(aClient * cptr,aChannel * chptr,char * para,int checkt,int what)386 int extcmode_cmodeT_requirechop(aClient *cptr, aChannel *chptr, char *para, int checkt, int what)
387 {
388 	if (IsPerson(cptr) && is_chan_op(cptr, chptr))
389 		return EX_ALLOW;
390 	if (checkt == EXCHK_ACCESS_ERR) /* can only be due to being halfop */
391 		sendto_one(cptr, err_str(ERR_NOTFORHALFOPS), me.name, cptr->name, 'T');
392 	return EX_DENY;
393 }
394 
extcmode_cmodeG_requirechop(aClient * cptr,aChannel * chptr,char * para,int checkt,int what)395 int extcmode_cmodeG_requirechop(aClient *cptr, aChannel *chptr, char *para, int checkt, int what)
396 {
397 	if (IsPerson(cptr) && is_chan_op(cptr, chptr))
398 		return EX_ALLOW;
399 	if (checkt == EXCHK_ACCESS_ERR) /* can only be due to being halfop */
400 		sendto_one(cptr, err_str(ERR_NOTFORHALFOPS), me.name, cptr->name, 'G');
401 	return EX_DENY;
402 }
403 
404 #ifdef JOINTHROTTLE
405 /*** CHANNEL MODE +j STUFF ******/
cmodej_is_ok(aClient * sptr,aChannel * chptr,char * para,int type,int what)406 int cmodej_is_ok(aClient *sptr, aChannel *chptr, char *para, int type, int what)
407 {
408 	if ((type == EXCHK_ACCESS) || (type == EXCHK_ACCESS_ERR))
409 	{
410 		if (IsPerson(sptr) && is_chan_op(sptr, chptr))
411 			return EX_ALLOW;
412 		if (type == EXCHK_ACCESS_ERR) /* can only be due to being halfop */
413 			sendto_one(sptr, err_str(ERR_NOTFORHALFOPS), me.name, sptr->name, 'j');
414 		return EX_DENY;
415 	} else
416 	if (type == EXCHK_PARAM)
417 	{
418 		/* Check parameter.. syntax should be X:Y, X should be 1-255, Y should be 1-999 */
419 		char buf[32], *p;
420 		int num, t, fail = 0;
421 
422 		strlcpy(buf, para, sizeof(buf));
423 		p = strchr(buf, ':');
424 		if (!p)
425 		{
426 			fail = 1;
427 		} else {
428 			*p++ = '\0';
429 			num = atoi(buf);
430 			t = atoi(p);
431 			if ((num < 1) || (num > 255) || (t < 1) || (t > 999))
432 				fail = 1;
433 		}
434 		if (fail)
435 		{
436 			sendnotice(sptr, "Error in setting +j, syntax: +j <num>:<seconds>, where <num> must be 1-255, and <seconds> 1-999");
437 			return EX_DENY;
438 		}
439 		return EX_ALLOW;
440 	}
441 
442 	/* falltrough -- should not be used */
443 	return EX_DENY;
444 }
445 
cmodej_put_param(CmodeParam * r_in,char * param)446 CmodeParam *cmodej_put_param(CmodeParam *r_in, char *param)
447 {
448 aModejEntry *r = (aModejEntry *)r_in;
449 char buf[32], *p;
450 int num, t;
451 
452 	if (!r)
453 	{
454 		/* Need to create one */
455 		r = (aModejEntry *)malloc(sizeof(aModejEntry));
456 		memset(r, 0, sizeof(aModejEntry));
457 		r->flag = 'j';
458 	}
459 	strlcpy(buf, param, sizeof(buf));
460 	p = strchr(buf, ':');
461 	if (p)
462 	{
463 		*p++ = '\0';
464 		num = atoi(buf);
465 		t = atoi(p);
466 		if (num < 1) num = 1;
467 		if (num > 255) num = 255;
468 		if (t < 1) t = 1;
469 		if (t > 999) t = 999;
470 		r->num = num;
471 		r->t = t;
472 	} else {
473 		r->num = 0;
474 		r->t = 0;
475 	}
476 	return (CmodeParam *)r;
477 }
478 
cmodej_get_param(CmodeParam * r_in)479 char *cmodej_get_param(CmodeParam *r_in)
480 {
481 aModejEntry *r = (aModejEntry *)r_in;
482 static char retbuf[16];
483 
484 	if (!r)
485 		return NULL;
486 
487 	snprintf(retbuf, sizeof(retbuf), "%hu:%hu", r->num, r->t);
488 	return retbuf;
489 }
490 
cmodej_conv_param(char * param_in)491 char *cmodej_conv_param(char *param_in)
492 {
493 static char retbuf[32];
494 char param[32], *p;
495 int num, t, fail = 0;
496 
497 	strlcpy(param, param_in, sizeof(param));
498 	p = strchr(param, ':');
499 	if (!p)
500 		return NULL;
501 	*p++ = '\0';
502 	num = atoi(param);
503 	t = atoi(p);
504 	if (num < 1)
505 		num = 1;
506 	if (num > 255)
507 		num = 255;
508 	if (t < 1)
509 		t = 1;
510 	if (t > 999)
511 		t = 999;
512 
513 	snprintf(retbuf, sizeof(retbuf), "%d:%d", num, t);
514 	return retbuf;
515 }
516 
cmodej_free_param(CmodeParam * r)517 void cmodej_free_param(CmodeParam *r)
518 {
519 	MyFree(r);
520 }
521 
cmodej_dup_struct(CmodeParam * r_in)522 CmodeParam *cmodej_dup_struct(CmodeParam *r_in)
523 {
524 aModejEntry *r = (aModejEntry *)r_in;
525 aModejEntry *w = (aModejEntry *)MyMalloc(sizeof(aModejEntry));
526 
527 	memcpy(w, r, sizeof(aModejEntry));
528 	w->next = w->prev = NULL;
529 	return (CmodeParam *)w;
530 }
531 
cmodej_sjoin_check(aChannel * chptr,CmodeParam * ourx,CmodeParam * theirx)532 int cmodej_sjoin_check(aChannel *chptr, CmodeParam *ourx, CmodeParam *theirx)
533 {
534 aModejEntry *our = (aModejEntry *)ourx;
535 aModejEntry *their = (aModejEntry *)theirx;
536 
537 	if (our->t != their->t)
538 	{
539 		if (our->t > their->t)
540 			return EXSJ_WEWON;
541 		else
542 			return EXSJ_THEYWON;
543 	}
544 	else if (our->num != their->num)
545 	{
546 		if (our->num > their->num)
547 			return EXSJ_WEWON;
548 		else
549 			return EXSJ_THEYWON;
550 	} else
551 		return EXSJ_SAME;
552 }
553 #endif /* JOINTHROTTLE */
554 
555 #endif /* EXTCMODE */
556