/*
 * =================================================================
 * Filename:       regexban.c
 * Description:    Extended ban type ~R
 * Author:         AngryWolf <angrywolf@flashmail.com>
 * Documentation:  regexban.txt (comes with the package)
 * =================================================================
 */

#include "config.h"
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "msg.h"
#include "channel.h"
#include <time.h>
#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 STRIPBADWORDS
#include "badwords.h"
#endif
#ifdef _WIN32
#include "version.h"
#endif

extern void			sendto_one(aClient *to, char *pattern, ...);

#define FLAG_REGEXBAN		'R'
#define DEF_REGFLAGS		(REG_ICASE | REG_EXTENDED | REG_NOSUB)
#define DelExtban(x)		if (x) ExtbanDel(x); x = NULL

static Extban			*AddExtban(Module *module, ExtbanInfo *req);
static int			regexban_is_ok(aClient *, aChannel *, char *, int, int, int);
static char			*regexban_conv_param(char *);
static int			regexban_is_banned(aClient *, aChannel *, char *, int);

Extban				*ExtbanRegex;
regex_t				expr;

ModuleHeader MOD_HEADER(regexban)
  = {
	"regexban",
	"$Id: regexban.c,v 2.1 2004/07/12 21:54:19 angrywolf Exp $",
	"Extended ban type ~R (ban masks with regular expressions)",
	"3.2-b8-1",
	NULL 
    };

DLLFUNC int MOD_INIT(regexban)(ModuleInfo *modinfo)
{
	ExtbanInfo req;

	memset(&req, 0, sizeof req);

	req.flag	= FLAG_REGEXBAN;
	req.is_ok	= regexban_is_ok;
	req.conv_param	= regexban_conv_param;
	req.is_banned	= regexban_is_banned;

	ExtbanRegex	= AddExtban(modinfo->handle, &req);

	if (!ExtbanRegex)
		return MOD_FAILED;

	return MOD_SUCCESS;
}

DLLFUNC int MOD_LOAD(regexban)(int module_load)
{
	return MOD_SUCCESS;
}

DLLFUNC int MOD_UNLOAD(regexban)(int module_unload)
{
	DelExtban(ExtbanRegex);
	return MOD_FAILED;
}

Extban *AddExtban(Module *module, ExtbanInfo *req)
{
	Extban *ban = ExtbanAdd(module, *req);

#ifndef STATIC_LINKING
        if (ModuleGetError(module) != MODERR_NOERROR || !ban)
#else
        if (!ban)
#endif
	{
#ifndef STATIC_LINKING
		config_error("Error adding extended ban type %c when loading module %s: %s",
			req->flag, MOD_HEADER(regexban).name, ModuleGetErrorStr(module));
#else
		config_error("Error adding extended ban type %c when loading module %s",
			req->flag, MOD_HEADER(regexban).name);
#endif
		return NULL;
	}

	return ban;
}

/*
 * Checks the validity of a regular expression. If there was no error,
 * returns NULL, otherwise a pointer to a string which must be free'd
 * up by the caller.
 */

static char *check_regex(char *mask)
{
	int		errorcode, errorbufsize;
	char		*errorbuf = NULL;

	memset(&expr, 0, sizeof expr);
	errorcode = regcomp(&expr, mask, DEF_REGFLAGS);
	if (errorcode > 0)
	{
		errorbufsize = regerror(errorcode, &expr, NULL, 0) + 1;
		errorbuf = MyMalloc(errorbufsize);
		regerror(errorcode, &expr, errorbuf, errorbufsize);
	}
	regfree(&expr);

	return errorbuf;
}

static int regexban_is_ok(aClient *sptr, aChannel *chptr, char *tmpstr, int ctype, int what, int etype)
{
	char *mask = tmpstr + 3;

	switch (ctype)
	{
		case EXBCHK_PARAM:
		{
			char *errmsg;

			if (!*mask)
			{
			    	sendto_one(sptr, ":%s NOTICE %s :Invalid ban mask",
					me.name, sptr->name);
				return 0;
			}

		        if ((errmsg = check_regex(mask)))
			{
				sendto_one(sptr, ":%s NOTICE %s :Ban mask contains an invalid regex: %s",
					me.name, sptr->name, errmsg);
				MyFree(errmsg);
				return 0;
			}
			break;
		}

		case EXBCHK_ACCESS:
		case EXBCHK_ACCESS_ERR:
		{
			if (!is_chan_op(sptr, chptr))
			{
				if (ctype == EXBCHK_ACCESS_ERR)
					sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED),
						me.name, sptr->name, chptr->chname);
				return 0;
			}
			break;
		}
	}

	return 1;
}

static char *regexban_conv_param(char *mask)
{
	return mask;
}

static int regexban_is_banned(aClient *sptr, aChannel *chptr, char *mask, int type)
{
	if (!((type == BANCHK_JOIN) || (type == BANCHK_MSG) || (type == BANCHK_NICK)))
		return 0;

	/* Not the fastest code... */
	memset(&expr, 0, sizeof expr);
	regcomp(&expr, mask + 3, DEF_REGFLAGS);

	if ((ban_realhost && !regexec(&expr, ban_realhost, 0, NULL, 0)) ||
	     (ban_virthost && !regexec(&expr, ban_virthost, 0, NULL, 0)) ||
	     (ban_ip && !regexec(&expr, ban_ip, 0, NULL, 0)))
		return 1;

	return 0;
}
