1
0
mirror of https://github.com/unrealircd/unrealircd.git synced 2026-06-29 15:06:37 +02:00
Files
unrealircd/src/extcmodes.c
T
Bram Matthys 2a498427bf - Fixed bug (in all Unreal versions) with parameter channelmodes, any 3rd
party module which adds an extra parameter chanmode could cause crashes.
2007-11-04 18:03:47 +00:00

555 lines
14 KiB
C

/************************************************************************
* IRC - Internet Relay Chat, extcmodes.c
* (C) 2003-2007 Bram Matthys (Syzop) and the UnrealIRCd Team
*
* See file AUTHORS in IRC package for additional names of
* the programmers.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "msg.h"
#include "proto.h"
#include "channel.h"
#include "version.h"
#include <time.h>
#ifdef _WIN32
#include <sys/timeb.h>
#endif
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <io.h>
#endif
#include <fcntl.h>
#include "h.h"
#ifdef EXTCMODE
extern char cmodestring[512];
extern void make_cmodestr(void);
char extchmstr[4][64];
Cmode *Channelmode_Table = NULL;
unsigned short Channelmode_highest = 0;
Cmode_t EXTMODE_NONOTICE = 0L;
#ifdef STRIPBADWORDS
Cmode_t EXTMODE_STRIPBADWORDS = 0L;
#endif
#ifdef JOINTHROTTLE
/* cmode j stuff... */
Cmode_t EXTMODE_JOINTHROTTLE = 0L;
int cmodej_is_ok(aClient *sptr, aChannel *chptr, char *para, int type, int what);
CmodeParam *cmodej_put_param(CmodeParam *r_in, char *param);
char *cmodej_get_param(CmodeParam *r_in);
char *cmodej_conv_param(char *param_in);
void cmodej_free_param(CmodeParam *r);
CmodeParam *cmodej_dup_struct(CmodeParam *r_in);
int cmodej_sjoin_check(aChannel *chptr, CmodeParam *ourx, CmodeParam *theirx);
#endif
int extcmode_cmodeT_requirechop(aClient *cptr, aChannel *chptr, char *para, int checkt, int what);
#ifdef STRIPBADWORDS
int extcmode_cmodeG_requirechop(aClient *cptr, aChannel *chptr, char *para, int checkt, int what);
#endif
void make_extcmodestr()
{
char *p;
int i;
extchmstr[0][0] = extchmstr[1][0] = extchmstr[2][0] = extchmstr[3][0] = '\0';
/* type 1: lists (like b/e) */
/* [NOT IMPLEMENTED IN EXTCMODES] */
/* type 2: 1 par to set/unset */
/* [NOT IMPLEMENTED] */
/* type 3: 1 param to set, 0 params to unset */
p = extchmstr[2];
for (i=0; i <= Channelmode_highest; i++)
if (Channelmode_Table[i].paracount && Channelmode_Table[i].flag)
*p++ = Channelmode_Table[i].flag;
*p = '\0';
/* type 4: paramless modes */
p = extchmstr[3];
for (i=0; i <= Channelmode_highest; i++)
if (!Channelmode_Table[i].paracount && Channelmode_Table[i].flag)
*p++ = Channelmode_Table[i].flag;
*p = '\0';
}
static void load_extendedchanmodes(void)
{
CmodeInfo req;
memset(&req, 0, sizeof(req));
req.paracount = 0;
req.is_ok = extcmode_cmodeT_requirechop;
req.flag = 'T';
CmodeAdd(NULL, req, &EXTMODE_NONOTICE);
#ifdef STRIPBADWORDS
req.flag = 'G';
req.is_ok = extcmode_cmodeG_requirechop;
CmodeAdd(NULL, req, &EXTMODE_STRIPBADWORDS);
#endif
#ifdef JOINTHROTTLE
/* +j */
memset(&req, 0, sizeof(req));
req.paracount = 1;
req.is_ok = cmodej_is_ok;
req.flag = 'j';
req.put_param = cmodej_put_param;
req.get_param = cmodej_get_param;
req.conv_param = cmodej_conv_param;
req.free_param = cmodej_free_param;
req.dup_struct = cmodej_dup_struct;
req.sjoin_check = cmodej_sjoin_check;
CmodeAdd(NULL, req, &EXTMODE_JOINTHROTTLE);
#endif
}
void extcmode_init(void)
{
Cmode_t val = 1;
int i;
Channelmode_Table = (Cmode *)MyMalloc(sizeof(Cmode) * EXTCMODETABLESZ);
bzero(Channelmode_Table, sizeof(Cmode) * EXTCMODETABLESZ);
for (i = 0; i < EXTCMODETABLESZ; i++)
{
Channelmode_Table[i].mode = val;
val *= 2;
}
Channelmode_highest = 0;
memset(&extchmstr, 0, sizeof(extchmstr));
/* And load the build-in extended chanmodes... */
load_extendedchanmodes();
}
Cmode *CmodeAdd(Module *module, CmodeInfo req, Cmode_t *mode)
{
short i = 0, j = 0;
char tmpbuf[512];
while (i < EXTCMODETABLESZ)
{
if (!Channelmode_Table[i].flag)
break;
else if (Channelmode_Table[i].flag == req.flag)
{
if (Channelmode_Table[i].unloaded)
{
Channelmode_Table[i].unloaded = 0;
break;
} else {
if (module)
module->errorcode = MODERR_EXISTS;
return NULL;
}
}
i++;
}
if (i == EXTCMODETABLESZ)
{
Debug((DEBUG_DEBUG, "CmodeAdd failed, no space"));
if (module)
module->errorcode = MODERR_NOSPACE;
return NULL;
}
*mode = Channelmode_Table[i].mode;
/* Update extended channel mode table highest */
Channelmode_Table[i].flag = req.flag;
Channelmode_Table[i].paracount = req.paracount;
Channelmode_Table[i].is_ok = req.is_ok;
Channelmode_Table[i].put_param = req.put_param;
Channelmode_Table[i].get_param = req.get_param;
Channelmode_Table[i].conv_param = req.conv_param;
Channelmode_Table[i].free_param = req.free_param;
Channelmode_Table[i].dup_struct = req.dup_struct;
Channelmode_Table[i].sjoin_check = req.sjoin_check;
Channelmode_Table[i].owner = module;
for (j = 0; j < EXTCMODETABLESZ; j++)
if (Channelmode_Table[j].flag)
if (j > Channelmode_highest)
Channelmode_highest = j;
if (module)
{
ModuleObject *cmodeobj = MyMallocEx(sizeof(ModuleObject));
cmodeobj->object.cmode = &Channelmode_Table[i];
cmodeobj->type = MOBJ_CMODE;
AddListItem(cmodeobj, module->objects);
module->errorcode = MODERR_NOERROR;
}
if (loop.ircd_booted)
{
make_cmodestr();
make_extcmodestr();
ircsprintf(tmpbuf, CHPAR1 "%s," CHPAR2 "%s," CHPAR3 "%s," CHPAR4 "%s",
EXPAR1, EXPAR2, EXPAR3, EXPAR4);
IsupportSetValue(IsupportFind("CHANMODES"), tmpbuf);
}
return &(Channelmode_Table[i]);
}
void unload_extcmode_commit(Cmode *cmode)
{
char tmpbuf[512];
aChannel *chptr;
if (!cmode)
return;
if (cmode->paracount == 1)
{
/* If we don't do this, we will crash anyway.. but then with severe corruption / suckyness */
ircd_log(LOG_ERROR, "FATAL ERROR: ChannelMode module for chanmode +%c is misbehaving: "
"all chanmode modules with parameters should be tagged PERManent.", cmode->flag);
abort();
}
for (chptr = channel; chptr; chptr = chptr->nextch)
if (chptr->mode.extmode && cmode->mode)
{
/* Unset channel mode and send MODE -<char> to other servers */
sendto_channel_butserv(chptr, &me, ":%s MODE %s -%c",
me.name, chptr->chname, cmode->flag);
sendto_serv_butone(NULL, ":%s MODE %s -%c 0",
me.name, chptr->chname, cmode->flag);
chptr->mode.extmode &= ~cmode->mode;
}
cmode->flag = '\0';
make_cmodestr();
make_extcmodestr();
ircsprintf(tmpbuf, CHPAR1 "%s," CHPAR2 "%s," CHPAR3 "%s," CHPAR4 "%s",
EXPAR1, EXPAR2, EXPAR3, EXPAR4);
IsupportSetValue(IsupportFind("CHANMODES"), tmpbuf);
}
void CmodeDel(Cmode *cmode)
{
/* It would be nice if we could abort() here if a parameter module is trying to unload which is extremely dangerous/crashy/disallowed */
if (loop.ircd_rehashing)
cmode->unloaded = 1;
else
unload_extcmode_commit(cmode);
if (cmode->owner)
{
ModuleObject *cmodeobj;
for (cmodeobj = cmode->owner->objects; cmodeobj; cmodeobj = cmodeobj->next) {
if (cmodeobj->type == MOBJ_CMODE && cmodeobj->object.cmode == cmode) {
DelListItem(cmodeobj, cmode->owner->objects);
MyFree(cmodeobj);
break;
}
}
cmode->owner = NULL;
}
}
void unload_all_unused_extcmodes(void)
{
int i;
for (i = 0; i < EXTCMODETABLESZ; i++)
if (Channelmode_Table[i].flag && Channelmode_Table[i].unloaded)
{
unload_extcmode_commit(&Channelmode_Table[i]);
}
}
/** searches in chptr extmode parameters and returns entry or NULL. */
CmodeParam *extcmode_get_struct(CmodeParam *p, char ch)
{
while(p)
{
if (p->flag == ch)
return p;
p = p->next;
}
return NULL;
}
/* bit inefficient :/ */
CmodeParam *extcmode_duplicate_paramlist(CmodeParam *lst)
{
int i;
Cmode *tbl;
CmodeParam *head = NULL, *n;
while(lst)
{
tbl = NULL;
for (i=0; i <= Channelmode_highest; i++)
{
if (Channelmode_Table[i].flag == lst->flag)
{
tbl = &Channelmode_Table[i]; /* & ? */
break;
}
}
n = tbl->dup_struct(lst);
n->next = n->prev = NULL; /* safety (required!) */
if (head)
{
AddListItem(n, head);
} else {
head = n;
}
lst = lst->next;
}
return head;
}
void extcmode_free_paramlist(CmodeParam *lst)
{
CmodeParam *n;
int i;
Cmode *tbl;
while(lst)
{
/* first remove it from the list... */
n = lst;
DelListItem(n, lst);
/* then hunt for the param free function and let it free */
tbl = NULL;
for (i=0; i <= Channelmode_highest; i++)
{
if (Channelmode_Table[i].flag == n->flag)
{
tbl = &Channelmode_Table[i]; /* & ? */
break;
}
}
tbl->free_param(n);
}
}
/* Ok this is my mistake @ EXCHK_ACCESS_ERR error msg:
* the is_ok() thing does not know which mode it belongs to,
* this is normally redundant information of course but in
* case of a default handler like these, it's required to
* know which setting of mode failed (the mode char).
* I just return '?' for now, better than nothing.
* TO SUMMARIZE: Do not use extcmode_default_requirechop for new modules :p.
* Obviously in Unreal3.3* we should fix this. -- Syzop
*/
int extcmode_default_requirechop(aClient *cptr, aChannel *chptr, char *para, int checkt, int what)
{
if (IsPerson(cptr) && is_chan_op(cptr, chptr))
return EX_ALLOW;
if (checkt == EXCHK_ACCESS_ERR) /* can only be due to being halfop */
sendto_one(cptr, err_str(ERR_NOTFORHALFOPS), me.name, cptr->name, '?');
return EX_DENY;
}
int extcmode_default_requirehalfop(aClient *cptr, aChannel *chptr, char *para, int checkt, int what)
{
if (IsPerson(cptr) &&
(is_chan_op(cptr, chptr) || is_half_op(cptr, chptr)))
return EX_ALLOW;
return EX_DENY;
}
int extcmode_cmodeT_requirechop(aClient *cptr, aChannel *chptr, char *para, int checkt, int what)
{
if (IsPerson(cptr) && is_chan_op(cptr, chptr))
return EX_ALLOW;
if (checkt == EXCHK_ACCESS_ERR) /* can only be due to being halfop */
sendto_one(cptr, err_str(ERR_NOTFORHALFOPS), me.name, cptr->name, 'T');
return EX_DENY;
}
int extcmode_cmodeG_requirechop(aClient *cptr, aChannel *chptr, char *para, int checkt, int what)
{
if (IsPerson(cptr) && is_chan_op(cptr, chptr))
return EX_ALLOW;
if (checkt == EXCHK_ACCESS_ERR) /* can only be due to being halfop */
sendto_one(cptr, err_str(ERR_NOTFORHALFOPS), me.name, cptr->name, 'G');
return EX_DENY;
}
#ifdef JOINTHROTTLE
/*** CHANNEL MODE +j STUFF ******/
int cmodej_is_ok(aClient *sptr, aChannel *chptr, char *para, int type, int what)
{
if ((type == EXCHK_ACCESS) || (type == EXCHK_ACCESS_ERR))
{
if (IsPerson(sptr) && is_chan_op(sptr, chptr))
return EX_ALLOW;
if (type == EXCHK_ACCESS_ERR) /* can only be due to being halfop */
sendto_one(sptr, err_str(ERR_NOTFORHALFOPS), me.name, sptr->name, 'j');
return EX_DENY;
} else
if (type == EXCHK_PARAM)
{
/* Check parameter.. syntax should be X:Y, X should be 1-255, Y should be 1-999 */
char buf[32], *p;
int num, t, fail = 0;
strlcpy(buf, para, sizeof(buf));
p = strchr(buf, ':');
if (!p)
{
fail = 1;
} else {
*p++ = '\0';
num = atoi(buf);
t = atoi(p);
if ((num < 1) || (num > 255) || (t < 1) || (t > 999))
fail = 1;
}
if (fail)
{
sendnotice(sptr, "Error in setting +j, syntax: +j <num>:<seconds>, where <num> must be 1-255, and <seconds> 1-999");
return EX_DENY;
}
return EX_ALLOW;
}
/* falltrough -- should not be used */
return EX_DENY;
}
CmodeParam *cmodej_put_param(CmodeParam *r_in, char *param)
{
aModejEntry *r = (aModejEntry *)r_in;
char buf[32], *p;
int num, t;
if (!r)
{
/* Need to create one */
r = (aModejEntry *)malloc(sizeof(aModejEntry));
memset(r, 0, sizeof(aModejEntry));
r->flag = 'j';
}
strlcpy(buf, param, sizeof(buf));
p = strchr(buf, ':');
if (p)
{
*p++ = '\0';
num = atoi(buf);
t = atoi(p);
if (num < 1) num = 1;
if (num > 255) num = 255;
if (t < 1) t = 1;
if (t > 999) t = 999;
r->num = num;
r->t = t;
} else {
r->num = 0;
r->t = 0;
}
return (CmodeParam *)r;
}
char *cmodej_get_param(CmodeParam *r_in)
{
aModejEntry *r = (aModejEntry *)r_in;
static char retbuf[16];
if (!r)
return NULL;
snprintf(retbuf, sizeof(retbuf), "%hu:%hu", r->num, r->t);
return retbuf;
}
char *cmodej_conv_param(char *param_in)
{
static char retbuf[32];
char param[32], *p;
int num, t, fail = 0;
strlcpy(param, param_in, sizeof(param));
p = strchr(param, ':');
if (!p)
return NULL;
*p++ = '\0';
num = atoi(param);
t = atoi(p);
if (num < 1)
num = 1;
if (num > 255)
num = 255;
if (t < 1)
t = 1;
if (t > 999)
t = 999;
snprintf(retbuf, sizeof(retbuf), "%d:%d", num, t);
return retbuf;
}
void cmodej_free_param(CmodeParam *r)
{
MyFree(r);
}
CmodeParam *cmodej_dup_struct(CmodeParam *r_in)
{
aModejEntry *r = (aModejEntry *)r_in;
aModejEntry *w = (aModejEntry *)MyMalloc(sizeof(aModejEntry));
memcpy(w, r, sizeof(aModejEntry));
w->next = w->prev = NULL;
return (CmodeParam *)w;
}
int cmodej_sjoin_check(aChannel *chptr, CmodeParam *ourx, CmodeParam *theirx)
{
aModejEntry *our = (aModejEntry *)ourx;
aModejEntry *their = (aModejEntry *)theirx;
if (our->t != their->t)
{
if (our->t > their->t)
return EXSJ_WEWON;
else
return EXSJ_THEYWON;
}
else if (our->num != their->num)
{
if (our->num > their->num)
return EXSJ_WEWON;
else
return EXSJ_THEYWON;
} else
return EXSJ_SAME;
}
#endif /* JOINTHROTTLE */
#endif /* EXTCMODE */