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