/*
 *   Unreal Internet Relay Chat Daemon, src/s_conf.c
 *   (C) 1998-2000 Chris Behrens & Fred Jacobs (comstud, moogle)
 *   (C) 2000-2002 Carsten V. Munk and the UnrealIRCd Team
 *
 *   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 "url.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "channel.h"
#include "macros.h"
#include <fcntl.h>
#ifndef _WIN32
#include <sys/socket.h>
#include <sys/wait.h>
#else
#include <io.h>
#endif
#include <sys/stat.h>
#ifdef __hpux
#include "inet.h"
#endif
#if defined(PCS) || defined(AIX) || defined(SVR3)
#include <time.h>
#endif
#include <string.h>
#ifdef GLOBH
#include <glob.h>
#endif
#ifdef STRIPBADWORDS
#include "badwords.h"
#endif
#include "h.h"
#include "inet.h"
#include "proto.h"
#ifdef _WIN32
#undef GLOBH
#endif
#include "badwords.h"

#define ircstrdup(x,y) do { if (x) MyFree(x); if (!y) x = NULL; else x = strdup(y); } while(0)
#define ircfree(x) do { if (x) MyFree(x); x = NULL; } while(0)

/* 
 * Some typedefs..
*/
typedef struct _confcommand ConfigCommand;
struct	_confcommand
{
	char	*name;
	int	(*conffunc)(ConfigFile *conf, ConfigEntry *ce);
	int 	(*testfunc)(ConfigFile *conf, ConfigEntry *ce);
};

typedef struct _conf_operflag OperFlag;
struct _conf_operflag
{
	long	flag;
	char	*name;
};


/* Config commands */

static int	_conf_admin		(ConfigFile *conf, ConfigEntry *ce);
static int	_conf_me		(ConfigFile *conf, ConfigEntry *ce);
static int	_conf_files		(ConfigFile *conf, ConfigEntry *ce);
static int	_conf_oper		(ConfigFile *conf, ConfigEntry *ce);
static int	_conf_class		(ConfigFile *conf, ConfigEntry *ce);
static int	_conf_drpass		(ConfigFile *conf, ConfigEntry *ce);
static int	_conf_ulines		(ConfigFile *conf, ConfigEntry *ce);
static int	_conf_include		(ConfigFile *conf, ConfigEntry *ce);
static int	_conf_tld		(ConfigFile *conf, ConfigEntry *ce);
static int	_conf_listen		(ConfigFile *conf, ConfigEntry *ce);
static int	_conf_allow		(ConfigFile *conf, ConfigEntry *ce);
static int	_conf_except		(ConfigFile *conf, ConfigEntry *ce);
static int	_conf_vhost		(ConfigFile *conf, ConfigEntry *ce);
static int	_conf_link		(ConfigFile *conf, ConfigEntry *ce);
static int	_conf_ban		(ConfigFile *conf, ConfigEntry *ce);
static int	_conf_set		(ConfigFile *conf, ConfigEntry *ce);
#ifdef STRIPBADWORDS
static int	_conf_badword		(ConfigFile *conf, ConfigEntry *ce);
#endif
static int	_conf_deny		(ConfigFile *conf, ConfigEntry *ce);
static int	_conf_deny_dcc		(ConfigFile *conf, ConfigEntry *ce);
static int	_conf_deny_link		(ConfigFile *conf, ConfigEntry *ce);
static int	_conf_deny_channel	(ConfigFile *conf, ConfigEntry *ce);
static int	_conf_deny_version	(ConfigFile *conf, ConfigEntry *ce);
static int	_conf_allow_channel	(ConfigFile *conf, ConfigEntry *ce);
static int	_conf_allow_dcc		(ConfigFile *conf, ConfigEntry *ce);
static int	_conf_loadmodule	(ConfigFile *conf, ConfigEntry *ce);
static int	_conf_log		(ConfigFile *conf, ConfigEntry *ce);
static int	_conf_alias		(ConfigFile *conf, ConfigEntry *ce);
static int	_conf_help		(ConfigFile *conf, ConfigEntry *ce);
static int	_conf_offchans		(ConfigFile *conf, ConfigEntry *ce);
static int	_conf_spamfilter	(ConfigFile *conf, ConfigEntry *ce);
static int	_conf_cgiirc	(ConfigFile *conf, ConfigEntry *ce);

/* 
 * Validation commands 
*/

static int	_test_admin		(ConfigFile *conf, ConfigEntry *ce);
static int	_test_me		(ConfigFile *conf, ConfigEntry *ce);
static int	_test_files		(ConfigFile *conf, ConfigEntry *ce);
static int	_test_oper		(ConfigFile *conf, ConfigEntry *ce);
static int	_test_class		(ConfigFile *conf, ConfigEntry *ce);
static int	_test_drpass		(ConfigFile *conf, ConfigEntry *ce);
static int	_test_ulines		(ConfigFile *conf, ConfigEntry *ce);
static int	_test_include		(ConfigFile *conf, ConfigEntry *ce);
static int	_test_tld		(ConfigFile *conf, ConfigEntry *ce);
static int	_test_listen		(ConfigFile *conf, ConfigEntry *ce);
static int	_test_allow		(ConfigFile *conf, ConfigEntry *ce);
static int	_test_except		(ConfigFile *conf, ConfigEntry *ce);
static int	_test_vhost		(ConfigFile *conf, ConfigEntry *ce);
static int	_test_link		(ConfigFile *conf, ConfigEntry *ce);
static int	_test_ban		(ConfigFile *conf, ConfigEntry *ce);
static int	_test_set		(ConfigFile *conf, ConfigEntry *ce);
#ifdef STRIPBADWORDS
static int	_test_badword		(ConfigFile *conf, ConfigEntry *ce);
#endif
static int	_test_deny		(ConfigFile *conf, ConfigEntry *ce);
static int	_test_allow_channel	(ConfigFile *conf, ConfigEntry *ce);
static int	_test_allow_dcc		(ConfigFile *conf, ConfigEntry *ce);
static int	_test_loadmodule	(ConfigFile *conf, ConfigEntry *ce);
static int	_test_log		(ConfigFile *conf, ConfigEntry *ce);
static int	_test_alias		(ConfigFile *conf, ConfigEntry *ce);
static int	_test_help		(ConfigFile *conf, ConfigEntry *ce);
static int	_test_offchans		(ConfigFile *conf, ConfigEntry *ce);
static int	_test_spamfilter	(ConfigFile *conf, ConfigEntry *ce);
static int	_test_cgiirc	(ConfigFile *conf, ConfigEntry *ce);
 
/* This MUST be alphabetized */
static ConfigCommand _ConfigCommands[] = {
	{ "admin", 		_conf_admin,		_test_admin 	},
	{ "alias",		_conf_alias,		_test_alias	},
	{ "allow",		_conf_allow,		_test_allow	},
#ifdef STRIPBADWORDS
	{ "badword",		_conf_badword,		_test_badword	},
#endif
	{ "ban", 		_conf_ban,		_test_ban	},
	{ "cgiirc", 	_conf_cgiirc,	_test_cgiirc	},
	{ "class", 		_conf_class,		_test_class	},
	{ "deny",		_conf_deny,		_test_deny	},
	{ "drpass",		_conf_drpass,		_test_drpass	},
	{ "except",		_conf_except,		_test_except	},
	{ "files",		_conf_files,		_test_files	},
	{ "help",		_conf_help,		_test_help	},
	{ "include",		NULL,	  		_test_include	},
	{ "link", 		_conf_link,		_test_link	},
	{ "listen", 		_conf_listen,		_test_listen	},
	{ "loadmodule",		NULL,		 	_test_loadmodule},
	{ "log",		_conf_log,		_test_log	},
	{ "me", 		_conf_me,		_test_me	},
	{ "official-channels", 		_conf_offchans,		_test_offchans	},
	{ "oper", 		_conf_oper,		_test_oper	},
	{ "set",		_conf_set,		_test_set	},
	{ "spamfilter",	_conf_spamfilter,	_test_spamfilter	},
	{ "tld",		_conf_tld,		_test_tld	},
	{ "ulines",		_conf_ulines,		_test_ulines	},
	{ "vhost", 		_conf_vhost,		_test_vhost	},
};

static int _OldOperFlags[] = {
	OFLAG_LOCAL, 'o',
	OFLAG_GLOBAL, 'O',
	OFLAG_REHASH, 'r',
	OFLAG_HELPOP, 'h',
	OFLAG_GLOBOP, 'g',
	OFLAG_WALLOP, 'w',
	OFLAG_LOCOP, 'l',
	OFLAG_LROUTE, 'c',
	OFLAG_GROUTE, 'L',
	OFLAG_LKILL, 'k',
	OFLAG_GKILL, 'K',
	OFLAG_KLINE, 'b',
	OFLAG_UNKLINE, 'B',
	OFLAG_LNOTICE, 'n',
	OFLAG_GNOTICE, 'G',
	OFLAG_ADMIN_, 'A',
	OFLAG_SADMIN_, 'a',
	OFLAG_NADMIN, 'N',
	OFLAG_TADMIN, 'e',
	OFLAG_RADMIN, 'X',
	OFLAG_NOWNER, 'J',
	OFLAG_SUPADMIN, 'U',
	OFLAG_COADMIN, 'C',
	OFLAG_ZLINE, 'z',
	OFLAG_WHOIS, 'W',
	OFLAG_HIDE, 'H',
	OFLAG_TKL, 't',
	OFLAG_GZL, 'Z',
	OFLAG_OVERRIDE, 'v',
	OFLAG_UMODEQ, 'q',
	OFLAG_DCCDENY, 'd',
	OFLAG_ADDLINE, 'V',
	0, 0
};

/* This MUST be alphabetized */
static OperFlag _OperFlags[] = {
	{ OFLAG_ADMIN_,		"admin"},
	{ OFLAG_ADDLINE,	"can_addline"},
	{ OFLAG_DCCDENY,	"can_dccdeny"},
	{ OFLAG_TKL,		"can_gkline"},
	{ OFLAG_GKILL,		"can_globalkill" },
	{ OFLAG_GNOTICE,	"can_globalnotice" },
	{ OFLAG_GROUTE,		"can_globalroute" },
	{ OFLAG_GLOBOP,         "can_globops" },
	{ OFLAG_GZL,		"can_gzline"},
	{ OFLAG_KLINE,		"can_kline" },
	{ OFLAG_LKILL,		"can_localkill" },
	{ OFLAG_LNOTICE,	"can_localnotice" },
	{ OFLAG_LROUTE,		"can_localroute" },
	{ OFLAG_OVERRIDE,	"can_override" },
	{ OFLAG_REHASH,		"can_rehash" },
	{ OFLAG_UMODEQ,		"can_setq" },
	{ OFLAG_UNKLINE,	"can_unkline" },
	{ OFLAG_WALLOP,         "can_wallops" },
	{ OFLAG_ZLINE,		"can_zline"},
	{ OFLAG_COADMIN_,	"coadmin"},
	{ OFLAG_HIDE,		"get_host"},
	{ OFLAG_WHOIS,		"get_umodew"},
	{ OFLAG_GLOBAL,		"global" },
	{ OFLAG_HELPOP,         "helpop" },
	{ OFLAG_LOCAL,		"local" },
	{ OFLAG_LOCOP,		"locop"},
	{ OFLAG_NADMIN,		"netadmin"},
	{ OFLAG_RADMIN,		"rootadmin"},
	{ OFLAG_SADMIN_,	"services-admin"},
	{ OFLAG_TADMIN,		"techadmin"},
	{ OFLAG_OWNER,		"owner"},
	{ OFLAG_SUPADMIN,	"superadmin"},
};

/* This MUST be alphabetized */
static OperFlag _ListenerFlags[] = {
	{ LISTENER_CLIENTSONLY, "clientsonly"},
	{ LISTENER_JAVACLIENT, 	"java"},
	{ LISTENER_MASK, 	"mask"},
	{ LISTENER_REMOTEADMIN, "remoteadmin"},
	{ LISTENER_SERVERSONLY, "serversonly"},
	{ LISTENER_SSL, 	"ssl"},
	{ LISTENER_NORMAL, 	"standard"},
};

/* This MUST be alphabetized */
static OperFlag _LinkFlags[] = {
	{ CONNECT_AUTO,	"autoconnect" },
	{ CONNECT_NODNSCACHE, "nodnscache" },
	{ CONNECT_NOHOSTCHECK, "nohostcheck" },
	{ CONNECT_QUARANTINE, "quarantine"},
	{ CONNECT_SSL,	"ssl"		  },
	{ CONNECT_ZIP,	"zip"		  },
};

/* This MUST be alphabetized */
static OperFlag _LogFlags[] = {
	{ LOG_CHGCMDS, "chg-commands" },
	{ LOG_CLIENT, "connects" },
	{ LOG_ERROR, "errors" },
	{ LOG_KILL, "kills" },
	{ LOG_KLINE, "kline" },
	{ LOG_OPER, "oper" },
	{ LOG_OVERRIDE, "oper-override" },
	{ LOG_SACMDS, "sadmin-commands" },
	{ LOG_SERVER, "server-connects" },
	{ LOG_SPAMFILTER, "spamfilter" },
	{ LOG_TKL, "tkl" },
};

/* This MUST be alphabetized */
static OperFlag ExceptTklFlags[] = {
	{ 0, "all" },
	{ TKL_GLOBAL|TKL_KILL,	"gline" },
	{ TKL_GLOBAL|TKL_NICK,	"gqline" },
	{ TKL_GLOBAL|TKL_ZAP,	"gzline" },
	{ TKL_NICK,		"qline" },
	{ TKL_GLOBAL|TKL_SHUN,	"shun" }
};

#ifdef USE_SSL
/* This MUST be alphabetized */
static OperFlag _SSLFlags[] = {
	{ SSLFLAG_FAILIFNOCERT, "fail-if-no-clientcert" },
	{ SSLFLAG_DONOTACCEPTSELFSIGNED, "no-self-signed" },
	{ SSLFLAG_NOSTARTTLS, "no-starttls" },
	{ SSLFLAG_VERIFYCERT, "verify-certificate" },
};
#endif

struct {
	unsigned conf_me : 1;
	unsigned conf_admin : 1;
	unsigned conf_listen : 1;
} requiredstuff;
struct SetCheck settings;
/*
 * Utilities
*/

void	ipport_seperate(char *string, char **ip, char **port);
void	port_range(char *string, int *start, int *end);
long	config_checkval(char *value, unsigned short flags);

/*
 * Parser
*/

ConfigFile		*config_load(char *filename);
void			config_free(ConfigFile *cfptr);
static ConfigFile 	*config_parse(char *filename, char *confdata);
static void 		config_entry_free(ConfigEntry *ceptr);
ConfigEntry		*config_find_entry(ConfigEntry *ce, char *name);
/*
 * Error handling
*/

void			config_warn(char *format, ...);
void 			config_error(char *format, ...);
void 			config_status(char *format, ...);
void 			config_progress(char *format, ...);

#ifdef _WIN32
extern void 	win_log(char *format, ...);
extern void		win_error();
#endif
extern void add_entropy_configfile(struct stat st, char *buf);
extern void unload_all_unused_snomasks();
extern void unload_all_unused_umodes();
extern void unload_all_unused_extcmodes(void);

extern int charsys_test_language(char *name);
extern void charsys_add_language(char *name);
extern void charsys_reset_pretest(void);
int charsys_postconftest(void);
void charsys_finish(void);
void delete_cgiircblock(ConfigItem_cgiirc *e);

/*
 * Config parser (IRCd)
*/
int			init_conf(char *rootconf, int rehash);
int			load_conf(char *filename, const char *original_path);
void			config_rehash();
int			config_run();
/*
 * Configuration linked lists
*/
ConfigItem_me		*conf_me = NULL;
ConfigItem_files	*conf_files = NULL;
ConfigItem_class 	*conf_class = NULL;
ConfigItem_class	*default_class = NULL;
ConfigItem_admin 	*conf_admin = NULL;
ConfigItem_admin	*conf_admin_tail = NULL;
ConfigItem_drpass	*conf_drpass = NULL;
ConfigItem_ulines	*conf_ulines = NULL;
ConfigItem_tld		*conf_tld = NULL;
ConfigItem_oper		*conf_oper = NULL;
ConfigItem_listen	*conf_listen = NULL;
ConfigItem_allow	*conf_allow = NULL;
ConfigItem_except	*conf_except = NULL;
ConfigItem_vhost	*conf_vhost = NULL;
ConfigItem_link		*conf_link = NULL;
ConfigItem_cgiirc	*conf_cgiirc = NULL;
ConfigItem_ban		*conf_ban = NULL;
ConfigItem_deny_dcc     *conf_deny_dcc = NULL;
ConfigItem_deny_channel *conf_deny_channel = NULL;
ConfigItem_allow_channel *conf_allow_channel = NULL;
ConfigItem_allow_dcc    *conf_allow_dcc = NULL;
ConfigItem_deny_link	*conf_deny_link = NULL;
ConfigItem_deny_version *conf_deny_version = NULL;
ConfigItem_log		*conf_log = NULL;
ConfigItem_alias	*conf_alias = NULL;
ConfigItem_include	*conf_include = NULL;
ConfigItem_help		*conf_help = NULL;
#ifdef STRIPBADWORDS
ConfigItem_badword	*conf_badword_channel = NULL;
ConfigItem_badword      *conf_badword_message = NULL;
ConfigItem_badword	*conf_badword_quit = NULL;
#endif
ConfigItem_offchans	*conf_offchans = NULL;

aConfiguration		iConf;
MODVAR aConfiguration		tempiConf;
MODVAR ConfigFile		*conf = NULL;

MODVAR int			config_error_flag = 0;
int			config_verbose = 0;

void add_include(const char *filename, const char *included_from, int included_from_line);
#ifdef USE_LIBCURL
void add_remote_include(const char *, const char *, int, const char *, const char *included_from, int included_from_line);
void update_remote_include(ConfigItem_include *inc, const char *file, int, const char *errorbuf);
int remote_include(ConfigEntry *ce);
#endif
void unload_notloaded_includes(void);
void load_includes(void);
void unload_loaded_includes(void);
int rehash_internal(aClient *cptr, aClient *sptr, int sig);


/* Pick out the ip address and the port number from a string.
 * The string syntax is:  ip:port.  ip must be enclosed in brackets ([]) if its an ipv6
 * address because they contain colon (:) separators.  The ip part is optional.  If the string
 * contains a single number its assumed to be a port number.
 *
 * Returns with ip pointing to the ip address (if one was specified), a "*" (if only a port 
 * was specified), or an empty string if there was an error.  port is returned pointing to the 
 * port number if one was specified, otherwise it points to a empty string.
 */
void ipport_seperate(char *string, char **ip, char **port)
{
	char *f;
	
	/* assume failure */
	*ip = *port = "";

	/* sanity check */
	if (string && strlen(string) > 0)
	{
		/* handle ipv6 type of ip address */
		if (*string == '[')
		{
			if ((f = strrchr(string, ']')))
			{
				*ip = string + 1;	/* skip [ */
				*f = '\0';			/* terminate the ip string */
				/* next char must be a : if a port was specified */
				if (*++f == ':')
				{
					*port = ++f;
				}
			}
		}
		/* handle ipv4 and port */
		else if ((f = strchr(string, ':')))
		{
			/* we found a colon... we may have ip:port or just :port */
			if (f == string)
			{
				/* we have just :port */
				*ip = "*";
			}
			else
			{
				/* we have ip:port */
				*ip = string;
				*f = '\0';
			}
			*port = ++f;
		}
		/* no ip was specified, just a port number */
		else if (!strcmp(string, my_itoa(atoi(string))))
		{
			*ip = "*";
			*port = string;
		}
	}
}

void port_range(char *string, int *start, int *end)
{
	char *c = strchr(string, '-');
	if (!c)
	{
		int tmp = atoi(string);
		*start = tmp;
		*end = tmp;
		return;
	}
	*c = '\0';
	*start = atoi(string);
	*end = atoi((c+1));
}

/** Parses '5:60s' config values.
 * orig: original string
 * times: pointer to int, first value (before the :)
 * period: pointer to int, second value (after the :) in seconds
 * RETURNS: 0 for parse error, 1 if ok.
 * REMARK: times&period should be ints!
 */
int config_parse_flood(char *orig, int *times, int *period)
{
char *x;

	*times = *period = 0;
	x = strchr(orig, ':');
	/* 'blah', ':blah', '1:' */
	if (!x || (x == orig) || (*(x+1) == '\0'))
		return 0;

	*x = '\0';
	*times = atoi(orig);
	*period = config_checkval(x+1, CFG_TIME);
	*x = ':'; /* restore */
	return 1;
}

long config_checkval(char *orig, unsigned short flags) {
	char *value;
	char *text;
	long ret = 0;

	value = strdup(orig);

	if (flags == CFG_YESNO) {
		for (text = value; *text; text++) {
			if (!isalnum(*text))
				continue;
			if (tolower(*text) == 'y' || (tolower(*text) == 'o' &&
			    tolower(*(text+1)) == 'n') || *text == '1' || tolower(*text) == 't') {
				ret = 1;
				break;
			}
		}
	}
	else if (flags == CFG_SIZE) {
		int mfactor = 1;
		char *sz;
		for (text = value; *text; text++) {
			if (isalpha(*text)) {
				if (tolower(*text) == 'k') 
					mfactor = 1024;
				else if (tolower(*text) == 'm') 
					mfactor = 1048576;
				else if (tolower(*text) == 'g') 
					mfactor = 1073741824;
				else 
					mfactor = 1;
				sz = text;
				while (isalpha(*text))
					text++;

				*sz-- = 0;
				while (sz-- > value && *sz) {
					if (isspace(*sz)) 
						*sz = 0;
					if (!isdigit(*sz)) 
						break;
				}
				ret += atoi(sz+1)*mfactor;
				if (*text == '\0') {
					text++;
					break;
				}
			}
		}
		mfactor = 1;
		sz = text;
		sz--;
		while (sz-- > value) {
			if (isspace(*sz)) 
				*sz = 0;
			if (!isdigit(*sz)) 
				break;
		}
		ret += atoi(sz+1)*mfactor;
	}
	else if (flags == CFG_TIME) {
		int mfactor = 1;
		char *sz;
		for (text = value; *text; text++) {
			if (isalpha(*text)) {
				if (tolower(*text) == 'w')
					mfactor = 604800;	
				else if (tolower(*text) == 'd') 
					mfactor = 86400;
				else if (tolower(*text) == 'h') 
					mfactor = 3600;
				else if (tolower(*text) == 'm') 
					mfactor = 60;
				else 
					mfactor = 1;
				sz = text;
				while (isalpha(*text))
					text++;

				*sz-- = 0;
				while (sz-- > value && *sz) {
					if (isspace(*sz)) 
						*sz = 0;
					if (!isdigit(*sz)) 
						break;
				}
				ret += atoi(sz+1)*mfactor;
				if (*text == '\0') {
					text++;
					break;
				}
			}
		}
		mfactor = 1;
		sz = text;
		sz--;
		while (sz-- > value) {
			if (isspace(*sz)) 
				*sz = 0;
			if (!isdigit(*sz)) 
				break;
		}
		ret += atoi(sz+1)*mfactor;
	}
	free(value);
	return ret;
}

int iplist_onlist(IPList *iplist, char *ip)
{
IPList *e;

	for (e = iplist; e; e = e->next)
		if (!match(e->mask, ip))
			return 1;
	return 0;
}

void set_channelmodes(char *modes, struct ChMode *store, int warn)
{
	aCtab *tab;
	char *params = strchr(modes, ' ');
	char *parambuf = NULL;
	char *param = NULL;
	char *save = NULL;
	
	warn = 0; // warn is broken
	
	if (params)
	{
		params++;
		parambuf = MyMalloc(strlen(params)+1);
		strcpy(parambuf, params);
		param = strtoken(&save, parambuf, " ");
	}		

	for (; *modes && *modes != ' '; modes++)
	{
		if (*modes == '+')
			continue;
		if (*modes == '-')
		/* When a channel is created it has no modes, so just ignore if the
		 * user asks us to unset anything -- codemastr 
		 */
		{
			while (*modes && *modes != '+')
				modes++;
			continue;
		}
		switch (*modes)
		{
			case 'f':
			{
#ifdef NEWCHFLOODPROT
				char *myparam = param;

				ChanFloodProt newf;
				
				memset(&newf, 0, sizeof(newf));
				if (!myparam)
					break;
				/* Go to next parameter */
				param = strtoken(&save, NULL, " ");

				if (myparam[0] != '[')
				{
					if (warn)
						config_status("set::modes-on-join: please use the new +f format: '10:5' becomes '[10t]:5' "
					                  "and '*10:5' becomes '[10t#b]:5'.");
				} else
				{
					char xbuf[256], c, a, *p, *p2, *x = xbuf+1;
					int v;
					unsigned short breakit;
					unsigned char r;
					
					/* '['<number><1 letter>[optional: '#'+1 letter],[next..]']'':'<number> */
					strlcpy(xbuf, myparam, sizeof(xbuf));
					p2 = strchr(xbuf+1, ']');
					if (!p2)
						break;
					*p2 = '\0';
					if (*(p2+1) != ':')
						break;
					breakit = 0;
					for (x = strtok(xbuf+1, ","); x; x = strtok(NULL, ","))
					{
						/* <number><1 letter>[optional: '#'+1 letter] */
						p = x;
						while(isdigit(*p)) { p++; }
						if ((*p == '\0') ||
						    !((*p == 'c') || (*p == 'j') || (*p == 'k') ||
						    (*p == 'm') || (*p == 'n') || (*p == 't')))
							break;
						c = *p;
						*p = '\0';
						v = atoi(x);
						if ((v < 1) || (v > 999)) /* out of range... */
							break;
						p++;
						a = '\0';
						r = 0;
						if (*p != '\0')
						{
							if (*p == '#')
							{
								p++;
								a = *p;
								p++;
								if (*p != '\0')
								{
									int tv;
									tv = atoi(p);
									if (tv <= 0)
										tv = 0; /* (ignored) */
									if (tv > 255)
										tv = 255; /* set to max */
									r = tv;
								}
							}
						}

						switch(c)
						{
							case 'c':
								newf.l[FLD_CTCP] = v;
								if ((a == 'm') || (a == 'M'))
									newf.a[FLD_CTCP] = a;
								else
									newf.a[FLD_CTCP] = 'C';
								newf.r[FLD_CTCP] = r;
								break;
							case 'j':
								newf.l[FLD_JOIN] = v;
								if (a == 'R')
									newf.a[FLD_JOIN] = a;
								else
									newf.a[FLD_JOIN] = 'i';
								newf.r[FLD_JOIN] = r;
								break;
							case 'k':
								newf.l[FLD_KNOCK] = v;
								newf.a[FLD_KNOCK] = 'K';
								newf.r[FLD_KNOCK] = r;
								break;
							case 'm':
								newf.l[FLD_MSG] = v;
								if (a == 'M')
									newf.a[FLD_MSG] = a;
								else
									newf.a[FLD_MSG] = 'm';
								newf.r[FLD_MSG] = r;
								break;
							case 'n':
								newf.l[FLD_NICK] = v;
								newf.a[FLD_NICK] = 'N';
								newf.r[FLD_NICK] = r;
								break;
							case 't':
								newf.l[FLD_TEXT] = v;
								if (a == 'b')
									newf.a[FLD_TEXT] = 'b';
								/** newf.r[FLD_TEXT] ** not supported */
								break;
							default:
								breakit=1;
								break;
						}
						if (breakit)
							break;
					} /* for strtok.. */
					if (breakit)
						break;
					/* parse 'per' */
					p2++;
					if (*p2 != ':')
						break;
					p2++;
					if (!*p2)
						break;
					v = atoi(p2);
					if ((v < 1) || (v > 999)) /* 'per' out of range */
						break;
					newf.per = v;
					/* Is anything turned on? (to stop things like '+f []:15' */
					breakit = 1;
					for (v=0; v < NUMFLD; v++)
						if (newf.l[v])
							breakit=0;
					if (breakit)
						break;
					
					/* w00t, we passed... */
					memcpy(&store->floodprot, &newf, sizeof(newf));
					store->mode |= MODE_FLOODLIMIT;
					break;
				}
#else
				char *myparam = param;
				char kmode = 0;
				char *xp;
				int msgs=0, per=0;
				int hascolon = 0;
				if (!myparam)
					break;
				/* Go to next parameter */
				param = strtoken(&save, NULL, " ");

				if (*myparam == '*')
					kmode = 1;
				for (xp = myparam; *xp; xp++)
				{
					if (*xp == ':')
					{
						hascolon++;
						continue;
					}
					if (((*xp < '0') || (*xp > '9')) && *xp != '*')
						break;
					if (*xp == '*' && *myparam != '*')
						break;
				}
				if (hascolon != 1)
					break;
				xp = strchr(myparam, ':');
					*xp = 0;
				msgs = atoi((*myparam == '*') ? (myparam+1) : myparam);
				xp++;
				per = atoi(xp);
				xp--;
				*xp = ':';
				if (msgs == 0 || msgs > 500 || per == 0 || per > 500)
					break;
				store->msgs = msgs;
				store->per = per;
				store->kmode = kmode; 					     
				store->mode |= MODE_FLOODLIMIT;
#endif
				break;
			}
			default:
				for (tab = &cFlagTab[0]; tab->mode; tab++)
				{
					if (tab->flag == *modes)
					{
						if (tab->parameters)
						{
							/* INCOMPATIBLE */
							break;
						}
						store->mode |= tab->mode;
						break;
					}
				}
#ifdef EXTCMODE
				/* Try extcmodes */
				if (!tab->mode)
				{
					int i;
					for (i=0; i <= Channelmode_highest; i++)
					{
						if (!(Channelmode_Table[i].flag))
							continue;
						if (*modes == Channelmode_Table[i].flag)
						{
							if (Channelmode_Table[i].paracount)
							{
								if (!param)
									break;
								param = Channelmode_Table[i].conv_param(param);
								if (!param)
									break; /* invalid parameter fmt, do not set mode. */
								store->extparams[i] = strdup(param);
								/* Get next parameter */
								param = strtoken(&save, NULL, " ");
							}
							store->extmodes |= Channelmode_Table[i].mode;
							break;
						}
					}
				}
#endif
		}
	}
	if (parambuf)
		free(parambuf);
}

void chmode_str(struct ChMode modes, char *mbuf, char *pbuf)
{
	aCtab *tab;
	int i;
	*pbuf = 0;
	*mbuf++ = '+';
	for (tab = &cFlagTab[0]; tab->mode; tab++)
	{
		if (modes.mode & tab->mode)
		{
			if (!tab->parameters)
				*mbuf++ = tab->flag;
		}
	}
#ifdef EXTCMODE
	for (i=0; i <= Channelmode_highest; i++)
	{
		if (!(Channelmode_Table[i].flag))
			continue;
	
		if (modes.extmodes & Channelmode_Table[i].mode)
		{
			*mbuf++ = Channelmode_Table[i].flag;
			if (Channelmode_Table[i].paracount)
			{
				strcat(pbuf, modes.extparams[i]);
				strcat(pbuf, " ");
			}
		}
	}
#endif
#ifdef NEWCHFLOODPROT
	if (modes.floodprot.per)
	{
		*mbuf++ = 'f';
		strcat(pbuf, channel_modef_string(&modes.floodprot));
	}
#else
	if (modes.per)
	{
		*mbuf++ = 'f';
		if (modes.kmode)
			strcat(pbuf, "*");
		strcat(pbuf, my_itoa(modes.msgs));
		strcat(pbuf, ":");
		strcat(pbuf, my_itoa(modes.per));
	}
#endif
	*mbuf++=0;
}

int channellevel_to_int(char *s)
{
	/* Requested at http://bugs.unrealircd.org/view.php?id=3852 */
	if (!strcmp(s, "none"))
		return CHFL_DEOPPED;
	if (!strcmp(s, "voice")) 
		return CHFL_VOICE;
	if (!strcmp(s, "halfop"))
		return CHFL_HALFOP;
	if (!strcmp(s, "op") || !strcmp(s, "chanop"))
		return CHFL_CHANOP;
	if (!strcmp(s, "protect") || !strcmp(s, "chanprot"))
#ifdef PREFIX_AQ
		return CHFL_CHANPROT;
#else
		return CHFL_CHANOP|CHFL_CHANPROT;
#endif
	if (!strcmp(s, "owner") || !strcmp(s, "chanowner"))
#ifdef PREFIX_AQ
		return CHFL_CHANOWNER;
#else
		return CHFL_CHANOP|CHFL_CHANOWNER;
#endif
	
	return 0; /* unknown or unsupported */
}

/* Channel flag (eg: CHFL_CHANOWNER) to SJOIN symbol (eg: *).
 * WARNING: Do not confuse SJOIN symbols with prefixes in /NAMES!
 */
char *chfl_to_sjoin_symbol(int s)
{
	switch(s)
	{
		case CHFL_VOICE:
			return "+";
		case CHFL_HALFOP:
			return "%";
		case CHFL_CHANOP:
			return "@";
		case CHFL_CHANPROT:
#ifdef PREFIX_AQ
			return "~";
#else
			return "~@";
#endif
		case CHFL_CHANOWNER:
#ifdef PREFIX_AQ
			return "*";
#else
			return "*@";
#endif
		case CHFL_DEOPPED:
		default:
			return "";
	}
	/* NOT REACHED */
}

char chfl_to_chanmode(int s)
{
	switch(s)
	{
		case CHFL_VOICE:
			return 'v';
		case CHFL_HALFOP:
			return 'h';
		case CHFL_CHANOP:
			return 'o';
		case CHFL_CHANPROT:
			return 'a';
		case CHFL_CHANOWNER:
			return 'q';
		case CHFL_DEOPPED:
		default:
			return '\0';
	}
	/* NOT REACHED */
}

ConfigFile *config_load(char *filename)
{
	struct stat sb;
	int			fd;
	int			ret;
	char		*buf = NULL;
	ConfigFile	*cfptr;

#ifndef _WIN32
	fd = open(filename, O_RDONLY);
#else
	fd = open(filename, O_RDONLY|O_BINARY);
#endif
	if (fd == -1)
	{
		config_error("Couldn't open \"%s\": %s\n", filename, strerror(errno));
		return NULL;
	}
	if (fstat(fd, &sb) == -1)
	{
		config_error("Couldn't fstat \"%s\": %s\n", filename, strerror(errno));
		close(fd);
		return NULL;
	}
	if (!sb.st_size)
	{
		/* Workaround for empty files */
		cfptr = config_parse(filename, " ");
		return cfptr;
	}
	buf = MyMalloc(sb.st_size+1);
	if (buf == NULL)
	{
		config_error("Out of memory trying to load \"%s\"\n", filename);
		close(fd);
		return NULL;
	}
	ret = read(fd, buf, sb.st_size);
	if (ret != sb.st_size)
	{
		config_error("Error reading \"%s\": %s\n", filename,
			ret == -1 ? strerror(errno) : strerror(EFAULT));
		free(buf);
		close(fd);
		return NULL;
	}
	/* Just me or could this cause memory corrupted when ret <0 ? */
	buf[ret] = '\0';
	close(fd);
	add_entropy_configfile(sb, buf);
	cfptr = config_parse(filename, buf);
	free(buf);
	return cfptr;
}

void config_free(ConfigFile *cfptr)
{
	ConfigFile	*nptr;

	for(;cfptr;cfptr=nptr)
	{
		nptr = cfptr->cf_next;
		if (cfptr->cf_entries)
			config_entry_free(cfptr->cf_entries);
		if (cfptr->cf_filename)
			free(cfptr->cf_filename);
		free(cfptr);
	}
}

/* This is the internal parser, made by Chris Behrens & Fred Jacobs */
static ConfigFile *config_parse(char *filename, char *confdata)
{
	char		*ptr;
	char		*start;
	int		linenumber = 1;
	ConfigEntry	*curce;
	ConfigEntry	**lastce;
	ConfigEntry	*cursection;

	ConfigFile	*curcf;
	ConfigFile	*lastcf;

	lastcf = curcf = MyMalloc(sizeof(ConfigFile));
	memset(curcf, 0, sizeof(ConfigFile));
	curcf->cf_filename = strdup(filename);
	lastce = &(curcf->cf_entries);
	curce = NULL;
	cursection = NULL;
	/* Replace \r's with spaces .. ugly ugly -Stskeeps */
	for (ptr=confdata; *ptr; ptr++)
		if (*ptr == '\r')
			*ptr = ' ';

	for(ptr=confdata;*ptr;ptr++)
	{
		switch(*ptr)
		{
			case ';':
				if (!curce)
				{
					config_status("%s:%i Ignoring extra semicolon\n",
						filename, linenumber);
					break;
				}
				*lastce = curce;
				lastce = &(curce->ce_next);
				curce->ce_fileposend = (ptr - confdata);
				curce = NULL;
				break;
			case '{':
				if (!curce)
				{
					config_status("%s:%i: No name for section start\n",
							filename, linenumber);
					continue;
				}
				else if (curce->ce_entries)
				{
					config_status("%s:%i: Ignoring extra section start\n",
							filename, linenumber);
					continue;
				}
				curce->ce_sectlinenum = linenumber;
				lastce = &(curce->ce_entries);
				cursection = curce;
				curce = NULL;
				break;
			case '}':
				if (curce)
				{
					config_error("%s:%i: Missing semicolon before close brace\n",
						filename, linenumber);
					config_entry_free(curce);
					config_free(curcf);

					return NULL;
				}
				else if (!cursection)
				{
					config_status("%s:%i: Ignoring extra close brace\n",
						filename, linenumber);
					continue;
				}
				curce = cursection;
				cursection->ce_fileposend = (ptr - confdata);
				cursection = cursection->ce_prevlevel;
				if (!cursection)
					lastce = &(curcf->cf_entries);
				else
					lastce = &(cursection->ce_entries);
				for(;*lastce;lastce = &((*lastce)->ce_next))
					continue;
				break;
			case '#':
				ptr++;
				while(*ptr && (*ptr != '\n'))
					 ptr++;
				if (!*ptr)
					break;
				ptr--;
				continue;
			case '/':
				if (*(ptr+1) == '/')
				{
					ptr += 2;
					while(*ptr && (*ptr != '\n'))
						ptr++;
					if (!*ptr)
						break;
					ptr--; /* grab the \n on next loop thru */
					continue;
				}
				else if (*(ptr+1) == '*')
				{
					int commentstart = linenumber;
					int commentlevel = 1;

					for(ptr+=2;*ptr;ptr++)
					{
						if ((*ptr == '/') && (*(ptr+1) == '*'))
						{
							commentlevel++;
							ptr++;
						}

						else if ((*ptr == '*') && (*(ptr+1) == '/'))
						{
							commentlevel--;
							ptr++;
						}

						else if (*ptr == '\n')
							linenumber++;

						if (!commentlevel)
							break;
					}
					if (!*ptr)
					{
						config_error("%s:%i Comment on this line does not end\n",
							filename, commentstart);
						config_entry_free(curce);
						config_free(curcf);
						return NULL;
					}
				}
				break;
			case '\"':
				if (curce && curce->ce_varlinenum != linenumber && cursection)
				{
					config_warn("%s:%i: Missing semicolon at end of line\n",
						filename, curce->ce_varlinenum);
					
					*lastce = curce;
					lastce = &(curce->ce_next);
					curce->ce_fileposend = (ptr - confdata);
					curce = NULL;
				}

				start = ++ptr;
				for(;*ptr;ptr++)
				{
					if ((*ptr == '\\'))
					{
					
						if (*(ptr+1) == '\\' || *(ptr+1) == '\"')
						{
							char *tptr = ptr;
							while((*tptr = *(tptr+1)))
								tptr++;
						}
					}
					else if ((*ptr == '\"') || (*ptr == '\n'))
						break;
				}
				if (!*ptr || (*ptr == '\n'))
				{
					config_error("%s:%i: Unterminated quote found\n",
							filename, linenumber);
					config_entry_free(curce);
					config_free(curcf);
					return NULL;
				}
				if (curce)
				{
					if (curce->ce_vardata)
					{
						config_status("%s:%i: Ignoring extra data\n",
							filename, linenumber);
					}
					else
					{
						curce->ce_vardata = MyMalloc(ptr-start+1);
						strncpy(curce->ce_vardata, start, ptr-start);
						curce->ce_vardata[ptr-start] = '\0';
					}
				}
				else
				{
					curce = MyMalloc(sizeof(ConfigEntry));
					memset(curce, 0, sizeof(ConfigEntry));
					curce->ce_varname = MyMalloc((ptr-start)+1);
					strncpy(curce->ce_varname, start, ptr-start);
					curce->ce_varname[ptr-start] = '\0';
					curce->ce_varlinenum = linenumber;
					curce->ce_fileptr = curcf;
					curce->ce_prevlevel = cursection;
					curce->ce_fileposstart = (start - confdata);
				}
				break;
			case '\n':
				linenumber++;
				/* fall through */
			case '\t':
			case ' ':
			case '=':
			case '\r':
				break;
			default:
				if ((*ptr == '*') && (*(ptr+1) == '/'))
				{
					config_status("%s:%i Ignoring extra end comment\n",
						filename, linenumber);
					ptr++;
					break;
				}
				start = ptr;
				for(;*ptr;ptr++)
				{
					if ((*ptr == ' ') || (*ptr == '=') || (*ptr == '\t') || (*ptr == '\n') || (*ptr == ';'))
						break;
				}
				if (!*ptr)
				{
					if (curce)
						config_error("%s: Unexpected EOF for variable starting at %i\n",
							filename, curce->ce_varlinenum);
					else if (cursection) 
						config_error("%s: Unexpected EOF for section starting at %i\n",
							filename, cursection->ce_sectlinenum);
					else
						config_error("%s: Unexpected EOF.\n", filename);
					config_entry_free(curce);
					config_free(curcf);
					return NULL;
				}
				if (curce)
				{
					if (curce->ce_vardata)
					{
						config_status("%s:%i: Ignoring extra data\n",
							filename, linenumber);
					}
					else
					{
						curce->ce_vardata = MyMalloc(ptr-start+1);
						strncpy(curce->ce_vardata, start, ptr-start);
						curce->ce_vardata[ptr-start] = '\0';
					}
				}
				else
				{
					curce = MyMalloc(sizeof(ConfigEntry));
					memset(curce, 0, sizeof(ConfigEntry));
					curce->ce_varname = MyMalloc((ptr-start)+1);
					strncpy(curce->ce_varname, start, ptr-start);
					curce->ce_varname[ptr-start] = '\0';
					curce->ce_varlinenum = linenumber;
					curce->ce_fileptr = curcf;
					curce->ce_prevlevel = cursection;
					curce->ce_fileposstart = (start - confdata);
				}
				if ((*ptr == ';') || (*ptr == '\n'))
					ptr--;
				break;
		} /* switch */
		if (!*ptr) /* This IS possible. -- Syzop */
			break;
	} /* for */
	if (curce)
	{
		config_error("%s: Unexpected EOF for variable starting on line %i\n",
			filename, curce->ce_varlinenum);
		config_entry_free(curce);
		config_free(curcf);
		return NULL;
	}
	else if (cursection)
	{
		config_error("%s: Unexpected EOF for section starting on line %i\n",
				filename, cursection->ce_sectlinenum);
		config_free(curcf);
		return NULL;
	}
	return curcf;
}

static void config_entry_free(ConfigEntry *ceptr)
{
	ConfigEntry	*nptr;

	for(;ceptr;ceptr=nptr)
	{
		nptr = ceptr->ce_next;
		if (ceptr->ce_entries)
			config_entry_free(ceptr->ce_entries);
		if (ceptr->ce_varname)
			free(ceptr->ce_varname);
		if (ceptr->ce_vardata)
			free(ceptr->ce_vardata);
		free(ceptr);
	}
}

ConfigEntry		*config_find_entry(ConfigEntry *ce, char *name)
{
	ConfigEntry *cep;
	
	for (cep = ce; cep; cep = cep->ce_next)
		if (cep->ce_varname && !strcmp(cep->ce_varname, name))
			break;
	return cep;
}

void config_error(char *format, ...)
{
	va_list		ap;
	char		buffer[1024];
	char		*ptr;

	va_start(ap, format);
	vsprintf(buffer, format, ap);
	va_end(ap);
	if ((ptr = strchr(buffer, '\n')) != NULL)
		*ptr = '\0';
	if (!loop.ircd_booted)
#ifndef _WIN32
		fprintf(stderr, "[error] %s\n", buffer);
#else
		win_log("[error] %s", buffer);
#endif
	else
		ircd_log(LOG_ERROR, "config error: %s", buffer);
	sendto_realops("error: %s", buffer);
	/* We cannot live with this */
	config_error_flag = 1;
}

static void inline config_error_missing(const char *filename, int line, const char *entry)
{
	config_error("%s:%d: %s is missing", filename, line, entry);
}

static void inline config_error_unknown(const char *filename, int line, const char *block, 
	const char *entry)
{
	config_error("%s:%d: Unknown directive '%s::%s'", filename, line, block, entry);
}

static void inline config_error_unknownflag(const char *filename, int line, const char *block,
	const char *entry)
{
	config_error("%s:%d: Unknown %s flag '%s'", filename, line, block, entry);
}

static void inline config_error_unknownopt(const char *filename, int line, const char *block,
	const char *entry)
{
	config_error("%s:%d: Unknown %s option '%s'", filename, line, block, entry);
}

static void inline config_error_noname(const char *filename, int line, const char *block)
{
	config_error("%s:%d: %s block has no name", filename, line, block);
}

static void inline config_error_blank(const char *filename, int line, const char *block)
{
	config_error("%s:%d: Blank %s entry", filename, line, block);
}

static void inline config_error_empty(const char *filename, int line, const char *block, 
	const char *entry)
{
	config_error("%s:%d: %s::%s specified without a value",
		filename, line, block, entry);
}

/* Like above */
void config_status(char *format, ...)
{
	va_list		ap;
	char		buffer[1024];
	char		*ptr;

	va_start(ap, format);
	vsnprintf(buffer, 1023, format, ap);
	va_end(ap);
	if ((ptr = strchr(buffer, '\n')) != NULL)
		*ptr = '\0';
	if (!loop.ircd_booted)
#ifndef _WIN32
		fprintf(stderr, "* %s\n", buffer);
#else
		win_log("* %s", buffer);
#endif
	sendto_realops("%s", buffer);
}

void config_warn(char *format, ...)
{
	va_list		ap;
	char		buffer[1024];
	char		*ptr;

	va_start(ap, format);
	vsnprintf(buffer, 1023, format, ap);
	va_end(ap);
	if ((ptr = strchr(buffer, '\n')) != NULL)
		*ptr = '\0';
	if (!loop.ircd_booted)
#ifndef _WIN32
		fprintf(stderr, "[warning] %s\n", buffer);
#else
		win_log("[warning] %s", buffer);
#endif
	sendto_realops("[warning] %s", buffer);
}

static void inline config_warn_duplicate(const char *filename, int line, const char *entry)
{
	config_warn("%s:%d: Duplicate %s directive", filename, line, entry);
}

/* returns 1 if the test fails */
int config_test_openfile(ConfigEntry *cep, int flags, mode_t mode, const char *entry, int fatal, int allow_url)
{
	int fd;

	if(!cep->ce_vardata)
	{
		if(fatal)
			config_error("%s:%i: %s: <no file specified>: no file specified",
				     cep->ce_fileptr->cf_filename,
				     cep->ce_varlinenum,
				     entry);
		else
			
			config_warn("%s:%i: %s: <no file specified>: no file specified",
				    cep->ce_fileptr->cf_filename,
				    cep->ce_varlinenum,
				    entry);
		return 1;
	}

	/* There's not much checking that can be done for asynchronously downloaded files */
#ifdef USE_LIBCURL
	if(url_is_valid(cep->ce_vardata))
	{
		if(allow_url)
			return 0;

		/* but we can check if a URL is used wrongly :-) */
		config_warn("%s:%i: %s: %s: URL used where not allowed",
			    cep->ce_fileptr->cf_filename,
			    cep->ce_varlinenum,
			    entry, cep->ce_vardata);
		if(fatal)
			return 1;
		else
			return 0;
	}
#endif /* USE_LIBCURL */

	/* 
	 * Make sure that files are created with the correct mode. This is 
	 * because we don't feel like unlink()ing them...which would require
	 * stat()ing them to make sure that we don't delete existing ones
	 * and that we deal with all of the bugs that come with complexity. 
	 * The only files we may be creating are the tunefile and pidfile so far.
	 */
	if(flags & O_CREAT)
		fd = open(cep->ce_vardata, flags, mode);
	else
		fd = open(cep->ce_vardata, flags);
	if(fd == -1)
	{
		if(fatal)
			config_error("%s:%i: %s: %s: %s",
				     cep->ce_fileptr->cf_filename,
				     cep->ce_varlinenum,
				     entry,
				     cep->ce_vardata,
				     strerror(errno));
		else
			config_warn("%s:%i: %s: %s: %s",
				     cep->ce_fileptr->cf_filename,
				     cep->ce_varlinenum,
				     entry,
				     cep->ce_vardata,
				     strerror(errno));
		return 1;
	}
	close(fd);
	return 0;
}

void config_progress(char *format, ...)
{
	va_list		ap;
	char		buffer[1024];
	char		*ptr;

	va_start(ap, format);
	vsnprintf(buffer, 1023, format, ap);
	va_end(ap);
	if ((ptr = strchr(buffer, '\n')) != NULL)
		*ptr = '\0';
	if (!loop.ircd_booted)
#ifndef _WIN32
		fprintf(stderr, "* %s\n", buffer);
#else
		win_log("* %s", buffer);
#endif
	sendto_realops("%s", buffer);
}

static int inline config_is_blankorempty(ConfigEntry *cep, const char *block)
{
	if (!cep->ce_varname)
	{
		config_error_blank(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, block);
		return 1;
	}
	if (!cep->ce_vardata)
	{
		config_error_empty(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, block,
			cep->ce_varname);
		return 1;
	}
	return 0;
}

ConfigCommand *config_binary_search(char *cmd) {
	int start = 0;
	int stop = ARRAY_SIZEOF(_ConfigCommands)-1;
	int mid;
	while (start <= stop) {
		mid = (start+stop)/2;
		if (smycmp(cmd,_ConfigCommands[mid].name) < 0) {
			stop = mid-1;
		}
		else if (strcmp(cmd,_ConfigCommands[mid].name) == 0) {
			return &_ConfigCommands[mid];
		}
		else
			start = mid+1;
	}
	return NULL;
}

void	free_iConf(aConfiguration *i)
{
	ircfree(i->name_server);
	ircfree(i->kline_address);
	ircfree(i->gline_address);
	ircfree(i->auto_join_chans);
	ircfree(i->oper_auto_join_chans);
	ircfree(i->oper_only_stats);
	ircfree(i->channel_command_prefix);
	ircfree(i->oper_snomask);
	ircfree(i->user_snomask);
	ircfree(i->egd_path);
	ircfree(i->static_quit);
#ifdef USE_SSL
	ircfree(i->x_server_cert_pem);
	ircfree(i->x_server_key_pem);
	ircfree(i->x_server_cipher_list);
	ircfree(i->trusted_ca_file);
#endif	
	ircfree(i->restrict_usermodes);
	ircfree(i->restrict_channelmodes);
	ircfree(i->restrict_extendedbans);
	ircfree(i->network.x_ircnetwork);
	ircfree(i->network.x_ircnet005);	
	ircfree(i->network.x_defserv);
	ircfree(i->network.x_services_name);
	ircfree(i->network.x_oper_host);
	ircfree(i->network.x_admin_host);
	ircfree(i->network.x_locop_host);	
	ircfree(i->network.x_sadmin_host);
	ircfree(i->network.x_netadmin_host);
	ircfree(i->network.x_techadmin_host);
	ircfree(i->network.x_rootadmin_host);
	ircfree(i->network.x_owner_host);
	ircfree(i->network.x_superadmin_host);
	ircfree(i->network.x_coadmin_host);
	ircfree(i->network.x_hidden_host);
	ircfree(i->network.x_prefix_quit);
	ircfree(i->network.x_helpchan);
	ircfree(i->network.x_stats_server);
	ircfree(i->spamfilter_ban_reason);
	ircfree(i->spamfilter_virus_help_channel);
	ircfree(i->spamexcept_line);
}

int	config_test();

void config_setdefaultsettings(aConfiguration *i)
{
	i->unknown_flood_amount = 4;
	i->unknown_flood_bantime = 600;
	i->oper_snomask = strdup(SNO_DEFOPER);
	i->ident_read_timeout = 30;
	i->ident_connect_timeout = 10;
	i->nick_count = 3; i->nick_period = 60; /* nickflood protection: max 3 per 60s */
#ifdef NO_FLOOD_AWAY
	i->away_count = 4; i->away_period = 120; /* awayflood protection: max 4 per 120s */
#endif
#ifdef NEWCHFLOODPROT
	i->modef_default_unsettime = 0;
	i->modef_max_unsettime = 60; /* 1 hour seems enough :p */
#endif
	i->ban_version_tkl_time = 86400; /* 1d */
	i->spamfilter_ban_time = 86400; /* 1d */
	i->spamfilter_ban_reason = strdup("Spam/advertising");
	i->spamfilter_virus_help_channel = strdup("#help");
	i->spamfilter_detectslow_warn = 250;
	i->spamfilter_detectslow_fatal = 500;
	i->spamfilter_stop_on_first_match = 1;
	i->maxdccallow = 10;
	i->channel_command_prefix = strdup("`!.");
	i->check_target_nick_bans = 1;
	i->maxbans = 60;
	i->maxbanlength = 2048;
	i->timesynch_enabled = 1;
	i->timesynch_timeout = 3;
	i->timesynch_server = strdup("193.67.79.202,192.43.244.18,128.250.36.3"); /* nlnet (EU), NIST (US), uni melbourne (AU). All open acces, nonotify, nodns. */
	i->name_server = strdup("127.0.0.1"); /* default, especially needed for w2003+ in some rare cases */
	i->level_on_join = CHFL_CHANOP;
	i->watch_away_notification = 1;
	i->new_linking_protocol = 1;
	i->uhnames = 1;
	i->ping_cookie = 1;
#ifdef INET6
	i->default_ipv6_clone_mask = 64;
#endif /* INET6 */
}

/* 1: needed for set::options::allow-part-if-shunned,
 * we can't just make it M_SHUN and do a ALLOW_PART_IF_SHUNNED in
 * m_part itself because that will also block internal calls (like sapart). -- Syzop
 * 2: now also used by spamfilter entries added by config...
 * we got a chicken-and-egg problem here.. antries added without reason or ban-time
 * field should use the config default (set::spamfilter::ban-reason/ban-time) but
 * this isn't (or might not) be known yet when parsing spamfilter entries..
 * so we do a VERY UGLY mass replace here.. unless someone else has a better idea.
 */
static void do_weird_shun_stuff()
{
aCommand *cmptr;
aTKline *tk;
char *encoded;

	if ((cmptr = find_Command_simple("PART")))
	{
		if (ALLOW_PART_IF_SHUNNED)
			cmptr->flags |= M_SHUN;
		else
			cmptr->flags &= ~M_SHUN;
	}

	encoded = unreal_encodespace(SPAMFILTER_BAN_REASON);
	if (!encoded)
		abort(); /* hack to trace 'impossible' bug... */
	for (tk = tklines[tkl_hash('q')]; tk; tk = tk->next)
	{
		if (tk->type != TKL_NICK)
			continue;
		if (!tk->setby)
		{
			if (me.name[0] != '\0')
				tk->setby = strdup(me.name);
			else
				tk->setby = strdup(conf_me->name ? conf_me->name : "~server~");
		}
	}

	for (tk = tklines[tkl_hash('f')]; tk; tk = tk->next)
	{
		if (tk->type != TKL_SPAMF)
			continue; /* global entry or something else.. */
		if (!strcmp(tk->ptr.spamf->tkl_reason, "<internally added by ircd>"))
		{
			MyFree(tk->ptr.spamf->tkl_reason);
			tk->ptr.spamf->tkl_reason = strdup(encoded);
			tk->ptr.spamf->tkl_duration = SPAMFILTER_BAN_TIME;
		}
		/* This one is even more ugly, but our config crap is VERY confusing :[ */
		if (!tk->setby)
		{
			if (me.name[0] != '\0')
				tk->setby = strdup(me.name);
			else
				tk->setby = strdup(conf_me->name ? conf_me->name : "~server~");
		}
	}
	if (loop.ircd_booted) /* only has to be done for rehashes, api-isupport takes care of boot */
	{
		if (WATCH_AWAY_NOTIFICATION)
		{
			IsupportAdd(NULL, "WATCHOPTS", "A");
		} else {
			Isupport *hunted = IsupportFind("WATCHOPTS");
			if (hunted)
				IsupportDel(hunted);
		}
		if (UHNAMES_ENABLED)
		{
			IsupportAdd(NULL, "UHNAMES", NULL);
		} else {
			Isupport *hunted = IsupportFind("UHNAMES");
			if (hunted)
				IsupportDel(hunted);
		}
	}
}

static void make_default_logblock(void)
{
ConfigItem_log *ca = MyMallocEx(sizeof(ConfigItem_log));

	config_status("No log { } block found -- using default: errors will be logged to 'ircd.log'");

	ca->file = strdup("ircd.log");
	ca->flags |= LOG_ERROR;
	AddListItem(ca, conf_log);
}

int isanyserverlinked(void)
{
int i;
aClient *acptr;

	for (i = LastSlot; i >= 0; i--)
		if ((acptr = local[i]) && (acptr != &me) && IsServer(acptr))
			return 1;

	return 0;
}

void applymeblock(void)
{
	if (!conf_me || !me.serv)
		return; /* uh-huh? */
	
	/* Numeric change? */
	if (conf_me->numeric != me.serv->numeric)
	{
		/* Can we apply ? */
		if (!isanyserverlinked())
		{
			me.serv->numeric = conf_me->numeric;
		} else {
			config_warn("me::numeric: Numeric change detected, but change cannot be applied "
			            "due to being linked to other servers. Unlink all servers and /REHASH to "
			            "try again.");
		}
	}
}

int	init_conf(char *rootconf, int rehash)
{
	char *old_pid_file = NULL;
  
	config_status("IRCd yapilandirmasi yukleniyor ..");
	if (conf)
	{
		config_error("%s:%i - Someone forgot to clean up", __FILE__, __LINE__);
		return -1;
	}
	bzero(&tempiConf, sizeof(iConf));
	bzero(&settings, sizeof(settings));
	bzero(&requiredstuff, sizeof(requiredstuff));
	config_setdefaultsettings(&tempiConf);
	/*
	 * the rootconf must be listed in the conf_include for include
	 * recursion prevention code and sanity checking code to be
	 * made happy :-). Think of it as us implicitly making an
	 * in-memory config file that looks like:
	 *
	 * include "unrealircd.conf";
	 */
	add_include(rootconf, "[thin air]", -1);
	if (load_conf(rootconf, rootconf) > 0)
	{
		charsys_reset_pretest();
		if ((config_test() < 0) || (callbacks_check() < 0) || (efunctions_check() < 0) ||
		    (charsys_postconftest() < 0))
		{
			config_error("IRCd configuration failed to pass testing");
#ifdef _WIN32
			if (!rehash)
				win_error();
#endif
#ifndef STATIC_LINKING
			Unload_all_testing_modules();
#endif
			unload_notloaded_includes();
			config_free(conf);
			conf = NULL;
			free_iConf(&tempiConf);
			return -1;
		}
		callbacks_switchover();
		efunctions_switchover();
		if (rehash)
		{
			Hook *h;
			old_pid_file = conf_files->pid_file;
			unrealdns_delasyncconnects();
			config_rehash();
#ifndef STATIC_LINKING
			Unload_all_loaded_modules();

			/* Notify permanent modules of the rehash */
			for (h = Hooks[HOOKTYPE_REHASH]; h; h = h->next)
		        {
				if (!h->owner)
					continue;
				if (!(h->owner->options & MOD_OPT_PERM))
					continue;
				(*(h->func.intfunc))();
			}
#else
			RunHook0(HOOKTYPE_REHASH);
#endif
			unload_loaded_includes();
		}
		load_includes();
#ifndef STATIC_LINKING
		Init_all_testing_modules();
#else
		if (!rehash) {
			ModuleInfo ModCoreInfo;
			ModCoreInfo.size = sizeof(ModuleInfo);
			ModCoreInfo.module_load = 0;
			ModCoreInfo.handle = NULL;
			l_commands_Init(&ModCoreInfo);
		}
#endif
		charsys_reset();
		if (config_run() < 0)
		{
			config_error("Bad case of config errors. Server will now die. This really shouldn't happen");
#ifdef _WIN32
			if (!rehash)
				win_error();
#endif
			abort();
		}
		charsys_finish();
		applymeblock();
		if(old_pid_file &&
		   strcmp(old_pid_file, conf_files->pid_file))
			{
				sendto_ops("pidfile is being rewritten to %s, please delete %s",
					   conf_files->pid_file,
					   old_pid_file);
				ircfree(old_pid_file);
				
				write_pidfile();
			}
		   }
	else	
	{
		config_error("IRCd configuration failed to load");
#ifndef STATIC_LINKING
		Unload_all_testing_modules();
#endif
		unload_notloaded_includes();
		config_free(conf);
		conf = NULL;
		free_iConf(&tempiConf);
#ifdef _WIN32
		if (!rehash)
			win_error();
#endif
		return -1;
	}
	config_free(conf);
	conf = NULL;
	if (rehash)
	{
#ifndef STATIC_LINKING
		module_loadall(0);
#endif
		RunHook0(HOOKTYPE_REHASH_COMPLETE);
	}
	do_weird_shun_stuff();
	if (!conf_log)
		make_default_logblock();
	nextconnect = TStime() + 1; /* check for autoconnects */
	config_status("Konfigurasyon sorunsuz yuklendi..");
	return 0;
}

/**
 * Processes filename as part of the IRCd's configuration.
 *
 * One _must_ call add_include() or add_remote_include() before
 * calling load_conf(). This way, include recursion may be detected
 * and reported to the user as an error instead of causing the IRCd to
 * hang in an infinite recursion, eat up memory, and eventually
 * overflow its stack ;-). (reported by warg).
 *
 * This function will set INCLUDE_USED on the config_include list
 * entry if the config file loaded without error.
 *
 * @param filename the file where the conf may be read from
 * @param original_path the path or URL used to refer to this file.
 *        (mostly to support remote includes' URIs for recursive include detection).
 * @return 1 on success, a negative number on error
 */
int	load_conf(char *filename, const char *original_path)
{
	ConfigFile 	*cfptr, *cfptr2, **cfptr3;
	ConfigEntry 	*ce;
	ConfigItem_include *inc, *my_inc;
	int		ret;
	int counter;

	if (config_verbose > 0)
		config_status("Loading config file %s ..", filename);

	/*
	 * Check if we're accidentally including a file a second
	 * time. We should expect to find one entry in this list: the
	 * entry for our current file.
	 */
	counter = 0;
	my_inc = NULL;
	for (inc = conf_include; inc; inc = (ConfigItem_include *)inc->next)
	{
		/*
		 * ignore files which were part of a _previous_
		 * successful rehash.
		 */
		if (!(inc->flag.type & INCLUDE_NOTLOADED))
			continue;

		if (!counter)
			my_inc = inc;

		if (!strcmp(filename, inc->file))
		{
			counter ++;
			continue;
		}
#ifdef _WIN32
		if (!stricmp(filename, inc->file))
		{
			counter ++;
			continue;
		}
#endif
#ifdef USE_LIBCURL
		if (inc->url && !strcmp(original_path, inc->url))
		{
			counter ++;
			continue;
		}
#endif
	}
	if (counter > 1 || my_inc->flag.type & INCLUDE_USED)
	{
		config_error("%s:%d:include: Config file %s has been loaded before %d time."
			     " You may include each file only once.",
			     my_inc->included_from, my_inc->included_from_line,
			     filename, counter - 1);
		return -1;
	}
	if (counter < 1 || !my_inc)
	{
		/*
		 * The following is simply for debugging/[sanity
		 * checking]. To make sure that functions call
		 * add_include() or add_remote_include() before
		 * calling us.
		 */
		config_error("I don't have a record for %s being included."
			     " Perhaps someone forgot to call add_include()?",
			     filename);
	}
	/* end include recursion checking code */

	if ((cfptr = config_load(filename)))
	{
		for (cfptr3 = &conf, cfptr2 = conf; cfptr2; cfptr2 = cfptr2->cf_next)
			cfptr3 = &cfptr2->cf_next;
		*cfptr3 = cfptr;
		if (config_verbose > 1)
			config_status("Loading modules in %s", filename);
		for (ce = cfptr->cf_entries; ce; ce = ce->ce_next)
			if (!strcmp(ce->ce_varname, "loadmodule"))
			{
				 ret = _conf_loadmodule(cfptr, ce);
				 if (ret < 0) 
					 	return ret;
			}
		if (config_verbose > 1)
			config_status("Searching through %s for include files..", filename);
		for (ce = cfptr->cf_entries; ce; ce = ce->ce_next)
			if (!strcmp(ce->ce_varname, "include"))
			{
				 ret = _conf_include(cfptr, ce);
				 if (ret < 0) 
					 	return ret;
			}
		my_inc->flag.type |= INCLUDE_USED;
		return 1;
	}
	else
	{
		config_error("Could not load config file %s", filename);
		return -1;
	}
}

void	config_rehash()
{
	ConfigItem_oper			*oper_ptr;
	ConfigItem_class 		*class_ptr;
	ConfigItem_ulines 		*uline_ptr;
	ConfigItem_allow 		*allow_ptr;
	ConfigItem_except 		*except_ptr;
	ConfigItem_ban 			*ban_ptr;
	ConfigItem_link 		*link_ptr;
	ConfigItem_cgiirc 		*cgiirc_ptr;
	ConfigItem_listen	 	*listen_ptr;
	ConfigItem_tld			*tld_ptr;
	ConfigItem_vhost		*vhost_ptr;
	ConfigItem_badword		*badword_ptr;
	ConfigItem_deny_dcc		*deny_dcc_ptr;
	ConfigItem_allow_dcc		*allow_dcc_ptr;
	ConfigItem_deny_link		*deny_link_ptr;
	ConfigItem_deny_channel		*deny_channel_ptr;
	ConfigItem_allow_channel	*allow_channel_ptr;
	ConfigItem_admin		*admin_ptr;
	ConfigItem_deny_version		*deny_version_ptr;
	ConfigItem_log			*log_ptr;
	ConfigItem_alias		*alias_ptr;
	ConfigItem_help			*help_ptr;
	ConfigItem_offchans		*of_ptr;
	OperStat 			*os_ptr;
	ListStruct 	*next, *next2;
	aTKline *tk, *tk_next;
	SpamExcept *spamex_ptr;
	int i;

	USE_BAN_VERSION = 0;
	/* clean out stuff that we don't use */	
	for (admin_ptr = conf_admin; admin_ptr; admin_ptr = (ConfigItem_admin *)next)
	{
		next = (ListStruct *)admin_ptr->next;
		ircfree(admin_ptr->line);
		DelListItem(admin_ptr, conf_admin);
		MyFree(admin_ptr);
	}
	/* wipe the fckers out ..*/
	for (oper_ptr = conf_oper; oper_ptr; oper_ptr = (ConfigItem_oper *)next)
	{
		ConfigItem_oper_from *oper_from;
		next = (ListStruct *)oper_ptr->next;
		ircfree(oper_ptr->name);
		ircfree(oper_ptr->swhois);
		ircfree(oper_ptr->snomask);
		Auth_DeleteAuthStruct(oper_ptr->auth);
		for (oper_from = (ConfigItem_oper_from *) oper_ptr->from; oper_from; oper_from = (ConfigItem_oper_from *) next2)
		{
			next2 = (ListStruct *)oper_from->next;
			ircfree(oper_from->name);
			if (oper_from->netmask)
			{
				MyFree(oper_from->netmask);
			}
			DelListItem(oper_from, oper_ptr->from);
			MyFree(oper_from);
		}
		DelListItem(oper_ptr, conf_oper);
		MyFree(oper_ptr);
	}
	for (link_ptr = conf_link; link_ptr; link_ptr = (ConfigItem_link *) next)
	{
		next = (ListStruct *)link_ptr->next;
		if (link_ptr->refcount == 0)
		{
			Debug((DEBUG_ERROR, "s_conf: deleting block %s (refcount 0)", link_ptr->servername));
			delete_linkblock(link_ptr);
		}
		else
		{
			Debug((DEBUG_ERROR, "s_conf: marking block %s (refcount %d) as temporary",
				link_ptr->servername, link_ptr->refcount));
			link_ptr->flag.temporary = 1;
		}
	}
	for (class_ptr = conf_class; class_ptr; class_ptr = (ConfigItem_class *) next)
	{
		next = (ListStruct *)class_ptr->next;
		if (class_ptr->flag.permanent == 1)
			continue;
		class_ptr->flag.temporary = 1;
		/* We'll wipe it out when it has no clients */
		if (!class_ptr->clients && !class_ptr->xrefcount)
		{
			delete_classblock(class_ptr);
		}
	}
	for (uline_ptr = conf_ulines; uline_ptr; uline_ptr = (ConfigItem_ulines *) next)
	{
		next = (ListStruct *)uline_ptr->next;
		/* We'll wipe it out when it has no clients */
		ircfree(uline_ptr->servername);
		DelListItem(uline_ptr, conf_ulines);
		MyFree(uline_ptr);
	}
	for (allow_ptr = conf_allow; allow_ptr; allow_ptr = (ConfigItem_allow *) next)
	{
		next = (ListStruct *)allow_ptr->next;
		ircfree(allow_ptr->ip);
		ircfree(allow_ptr->hostname);
		if (allow_ptr->netmask)
			MyFree(allow_ptr->netmask);
		Auth_DeleteAuthStruct(allow_ptr->auth);
		DelListItem(allow_ptr, conf_allow);
		MyFree(allow_ptr);
	}
	for (except_ptr = conf_except; except_ptr; except_ptr = (ConfigItem_except *) next)
	{
		next = (ListStruct *)except_ptr->next;
		ircfree(except_ptr->mask);
		if (except_ptr->netmask)
			MyFree(except_ptr->netmask);
		DelListItem(except_ptr, conf_except);
		MyFree(except_ptr);
	}
	for (ban_ptr = conf_ban; ban_ptr; ban_ptr = (ConfigItem_ban *) next)
	{
		next = (ListStruct *)ban_ptr->next;
		if (ban_ptr->flag.type2 == CONF_BAN_TYPE_CONF || ban_ptr->flag.type2 == CONF_BAN_TYPE_TEMPORARY)
		{
			ircfree(ban_ptr->mask);
			ircfree(ban_ptr->reason);
			if (ban_ptr->netmask)
				MyFree(ban_ptr->netmask);
			DelListItem(ban_ptr, conf_ban);
			MyFree(ban_ptr);
		}
	}
	for (listen_ptr = conf_listen; listen_ptr; listen_ptr = (ConfigItem_listen *)listen_ptr->next)
	{
		listen_ptr->flag.temporary = 1;
	}
	for (tld_ptr = conf_tld; tld_ptr; tld_ptr = (ConfigItem_tld *) next)
	{
		next = (ListStruct *)tld_ptr->next;
		ircfree(tld_ptr->motd_file);
		ircfree(tld_ptr->rules_file);
		ircfree(tld_ptr->smotd_file);
		ircfree(tld_ptr->opermotd_file);
		ircfree(tld_ptr->botmotd_file);

		free_motd(&tld_ptr->motd);
		free_motd(&tld_ptr->rules);
		free_motd(&tld_ptr->smotd);
		free_motd(&tld_ptr->opermotd);
		free_motd(&tld_ptr->botmotd);

		DelListItem(tld_ptr, conf_tld);
		MyFree(tld_ptr);
	}
	for (vhost_ptr = conf_vhost; vhost_ptr; vhost_ptr = (ConfigItem_vhost *) next)
	{
		ConfigItem_oper_from *vhost_from;
		
		next = (ListStruct *)vhost_ptr->next;
		
		ircfree(vhost_ptr->login);
		Auth_DeleteAuthStruct(vhost_ptr->auth);
		ircfree(vhost_ptr->virthost);
		ircfree(vhost_ptr->virtuser);
		for (vhost_from = (ConfigItem_oper_from *) vhost_ptr->from; vhost_from;
			vhost_from = (ConfigItem_oper_from *) next2)
		{
			next2 = (ListStruct *)vhost_from->next;
			ircfree(vhost_from->name);
			DelListItem(vhost_from, vhost_ptr->from);
			MyFree(vhost_from);
		}
		DelListItem(vhost_ptr, conf_vhost);
		MyFree(vhost_ptr);
	}

#ifdef STRIPBADWORDS
	for (badword_ptr = conf_badword_channel; badword_ptr;
		badword_ptr = (ConfigItem_badword *) next) {
		next = (ListStruct *)badword_ptr->next;
		ircfree(badword_ptr->word);
		if (badword_ptr->replace)
			ircfree(badword_ptr->replace);
		regfree(&badword_ptr->expr);
		DelListItem(badword_ptr, conf_badword_channel);
		MyFree(badword_ptr);
	}
	for (badword_ptr = conf_badword_message; badword_ptr;
		badword_ptr = (ConfigItem_badword *) next) {
		next = (ListStruct *)badword_ptr->next;
		ircfree(badword_ptr->word);
		if (badword_ptr->replace)
			ircfree(badword_ptr->replace);
		regfree(&badword_ptr->expr);
		DelListItem(badword_ptr, conf_badword_message);
		MyFree(badword_ptr);
	}
	for (badword_ptr = conf_badword_quit; badword_ptr;
		badword_ptr = (ConfigItem_badword *) next) {
		next = (ListStruct *)badword_ptr->next;
		ircfree(badword_ptr->word);
		if (badword_ptr->replace)
			ircfree(badword_ptr->replace);
		regfree(&badword_ptr->expr);
		DelListItem(badword_ptr, conf_badword_quit);
		MyFree(badword_ptr);
	}
#endif
	/* Clean up local spamfilter entries... */
	for (tk = tklines[tkl_hash('f')]; tk; tk = tk_next)
	{
		if (tk->type == TKL_SPAMF)
			tk_next = tkl_del_line(tk);
		else /* global spamfilter.. don't touch! */
			tk_next = tk->next;
	}

	for (tk = tklines[tkl_hash('q')]; tk; tk = tk_next)
	{
		if (tk->type == TKL_NICK)
			tk_next = tkl_del_line(tk);
		else 
			tk_next = tk->next;
	}

	for (deny_dcc_ptr = conf_deny_dcc; deny_dcc_ptr; deny_dcc_ptr = (ConfigItem_deny_dcc *)next)
	{
		next = (ListStruct *)deny_dcc_ptr->next;
		if (deny_dcc_ptr->flag.type2 == CONF_BAN_TYPE_CONF)
		{
			ircfree(deny_dcc_ptr->filename);
			ircfree(deny_dcc_ptr->reason);
			DelListItem(deny_dcc_ptr, conf_deny_dcc);
			MyFree(deny_dcc_ptr);
		}
	}
	for (deny_link_ptr = conf_deny_link; deny_link_ptr; deny_link_ptr = (ConfigItem_deny_link *) next) {
		next = (ListStruct *)deny_link_ptr->next;
		ircfree(deny_link_ptr->prettyrule);
		ircfree(deny_link_ptr->mask);
		crule_free(&deny_link_ptr->rule);
		DelListItem(deny_link_ptr, conf_deny_link);
		MyFree(deny_link_ptr);
	}
	for (deny_version_ptr = conf_deny_version; deny_version_ptr; deny_version_ptr = (ConfigItem_deny_version *) next) {
		next = (ListStruct *)deny_version_ptr->next;
		ircfree(deny_version_ptr->mask);
		ircfree(deny_version_ptr->version);
		ircfree(deny_version_ptr->flags);
		DelListItem(deny_version_ptr, conf_deny_version);
		MyFree(deny_version_ptr);
	}
	for (deny_channel_ptr = conf_deny_channel; deny_channel_ptr; deny_channel_ptr = (ConfigItem_deny_channel *) next)
	{
		next = (ListStruct *)deny_channel_ptr->next;
		ircfree(deny_channel_ptr->redirect);
		ircfree(deny_channel_ptr->channel);
		ircfree(deny_channel_ptr->reason);
		ircfree(deny_channel_ptr->class);
		DelListItem(deny_channel_ptr, conf_deny_channel);
		MyFree(deny_channel_ptr);
	}

	for (allow_channel_ptr = conf_allow_channel; allow_channel_ptr; allow_channel_ptr = (ConfigItem_allow_channel *) next)
	{
		next = (ListStruct *)allow_channel_ptr->next;
		ircfree(allow_channel_ptr->channel);
		ircfree(allow_channel_ptr->class);
		DelListItem(allow_channel_ptr, conf_allow_channel);
		MyFree(allow_channel_ptr);
	}
	for (allow_dcc_ptr = conf_allow_dcc; allow_dcc_ptr; allow_dcc_ptr = (ConfigItem_allow_dcc *)next)
	{
		next = (ListStruct *)allow_dcc_ptr->next;
		if (allow_dcc_ptr->flag.type2 == CONF_BAN_TYPE_CONF)
		{
			ircfree(allow_dcc_ptr->filename);
			DelListItem(allow_dcc_ptr, conf_allow_dcc);
			MyFree(allow_dcc_ptr);
		}
	}

	if (conf_drpass)
	{
		Auth_DeleteAuthStruct(conf_drpass->restartauth);
		conf_drpass->restartauth = NULL;
		Auth_DeleteAuthStruct(conf_drpass->dieauth);
		conf_drpass->dieauth = NULL;
		ircfree(conf_drpass);
	}
	for (log_ptr = conf_log; log_ptr; log_ptr = (ConfigItem_log *)next) {
		next = (ListStruct *)log_ptr->next;
		ircfree(log_ptr->file);
		DelListItem(log_ptr, conf_log);
		MyFree(log_ptr);
	}
	for (alias_ptr = conf_alias; alias_ptr; alias_ptr = (ConfigItem_alias *)next) {
		aCommand *cmptr = find_Command(alias_ptr->alias, 0, 0);
		ConfigItem_alias_format *fmt;
		next = (ListStruct *)alias_ptr->next;		
		ircfree(alias_ptr->nick);
		del_Command(alias_ptr->alias, NULL, cmptr->func);
		ircfree(alias_ptr->alias);
		if (alias_ptr->format && (alias_ptr->type == ALIAS_COMMAND)) {
			for (fmt = (ConfigItem_alias_format *) alias_ptr->format; fmt; fmt = (ConfigItem_alias_format *) next2)
			{
				next2 = (ListStruct *)fmt->next;
				ircfree(fmt->format);
				ircfree(fmt->nick);
				ircfree(fmt->parameters);
				regfree(&fmt->expr);
				DelListItem(fmt, alias_ptr->format);
				MyFree(fmt);
			}
		}
		DelListItem(alias_ptr, conf_alias);
		MyFree(alias_ptr);
	}
	for (help_ptr = conf_help; help_ptr; help_ptr = (ConfigItem_help *)next) {
		aMotdLine *text;
		next = (ListStruct *)help_ptr->next;
		ircfree(help_ptr->command);
		while (help_ptr->text) {
			text = help_ptr->text->next;
			ircfree(help_ptr->text->line);
			ircfree(help_ptr->text);
			help_ptr->text = text;
		}
		DelListItem(help_ptr, conf_help);
		MyFree(help_ptr);
	}
	for (os_ptr = iConf.oper_only_stats_ext; os_ptr; os_ptr = (OperStat *)next)
	{
		next = (ListStruct *)os_ptr->next;
		ircfree(os_ptr->flag);
		MyFree(os_ptr);
	}
	iConf.oper_only_stats_ext = NULL;
	for (spamex_ptr = iConf.spamexcept; spamex_ptr; spamex_ptr = (SpamExcept *)next)
	{
		next = (ListStruct *)spamex_ptr->next;
		MyFree(spamex_ptr);
	}
	iConf.spamexcept = NULL;
	for (of_ptr = conf_offchans; of_ptr; of_ptr = (ConfigItem_offchans *)next)
	{
		next = (ListStruct *)of_ptr->next;
		ircfree(of_ptr->topic);
		MyFree(of_ptr);
	}
	conf_offchans = NULL;
	
#ifdef EXTCMODE
	for (i = 0; i < EXTCMODETABLESZ; i++)
	{
		if (iConf.modes_on_join.extparams[i])
			free(iConf.modes_on_join.extparams[i]);
	}
#endif

	for (cgiirc_ptr = conf_cgiirc; cgiirc_ptr; cgiirc_ptr = (ConfigItem_cgiirc *) next)
	{
		next = (ListStruct *)cgiirc_ptr->next;
		delete_cgiircblock(cgiirc_ptr);
	}

	/*
	  reset conf_files -- should this be in its own function? no, because
	  it's only used here
	 */
	ircfree(conf_files->motd_file);
	ircfree(conf_files->smotd_file);
	ircfree(conf_files->opermotd_file);
	ircfree(conf_files->svsmotd_file);
	ircfree(conf_files->botmotd_file);
	ircfree(conf_files->rules_file);
	ircfree(conf_files->tune_file);
	/* 
	   Don't free conf_files->pid_file here; the old value is used to determine if 
	   the pidfile location has changed and write_pidfile() needs to be called 
	   again.
	*/
	ircfree(conf_files);
	conf_files = NULL;
}

int	config_post_test()
{
#define Error(x) { config_error((x)); errors++; }
	int 	errors = 0;
	Hook *h;
	
	if (!requiredstuff.conf_me)
		Error("me {} block is missing");
	if (!requiredstuff.conf_admin)
		Error("admin {} block is missing");
	if (!requiredstuff.conf_listen)
		Error("listen {} block is missing");
	if (!settings.has_kline_address)
		Error("set::kline-address is missing");
	if (!settings.has_maxchannelsperuser)
		Error("set::maxchannelsperuser is missing");
#if 0
	if (!settings.has_dns_nameserver)
		Error("set::dns::nameserver is missing");
	if (!settings.has_dns_timeout)
		Error("set::dns::timeout is missing");
	if (!settings.has_dns_retries)
		Error("set::dns::retries is missing");
#endif
	if (!settings.has_services_server)
		Error("set::services-server is missing");
	if (!settings.has_default_server)
		Error("set::default-server is missing");
	if (!settings.has_network_name)
		Error("set::network-name is missing");
	if (!settings.has_hosts_global)
		Error("set::hosts::global is missing");
	if (!settings.has_hosts_admin)
		Error("set::hosts::admin is missing");
	if (!settings.has_hosts_servicesadmin)
		Error("set::hosts::servicesadmin is missing");
	if (!settings.has_hosts_netadmin)
		Error("set::hosts::netadmin is missing");
	if (!settings.has_hosts_rootadmin)
		Error("set::hosts::rootadmin is missing");
	if (!settings.has_hosts_owner)
		Error("set::hosts::owner is missing");
	if (!settings.has_hosts_superadmin)
		Error("set::hosts::superadmin is missing");
	if (!settings.has_hosts_techadmin)
		Error("set::hosts::techadmin is missing");
	if (!settings.has_hosts_coadmin)
		Error("set::hosts::coadmin is missing");
	if (!settings.has_help_channel)
		Error("set::help-channel is missing");
	if (!settings.has_hiddenhost_prefix)
		Error("set::hiddenhost-prefix is missing");
		
	for (h = Hooks[HOOKTYPE_CONFIGPOSTTEST]; h; h = h->next) 
	{
		int value, errs = 0;
		if (h->owner && !(h->owner->flags & MODFLAG_TESTING) &&
		                !(h->owner->options & MOD_OPT_PERM))
			continue;
		value = (*(h->func.intfunc))(&errs);
		if (value == -1)
		{
			errors += errs;
			break;
		}
		if (value == -2)
			errors += errs;
	}
	return errors;	
}

int	config_run()
{
	ConfigEntry 	*ce;
	ConfigFile	*cfptr;
	ConfigCommand	*cc;
	int		errors = 0;
	Hook *h;
#ifdef INET6
	ConfigItem_allow *allow;
#endif /* INET6 */
	for (cfptr = conf; cfptr; cfptr = cfptr->cf_next)
	{
		if (config_verbose > 1)
			config_status("Running %s", cfptr->cf_filename);
		for (ce = cfptr->cf_entries; ce; ce = ce->ce_next)
		{
			if ((cc = config_binary_search(ce->ce_varname))) {
				if ((cc->conffunc) && (cc->conffunc(cfptr, ce) < 0))
					errors++;
			}
			else
			{
				int value;
				for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next)
				{
					value = (*(h->func.intfunc))(cfptr,ce,CONFIG_MAIN);
					if (value == 1)
						break;
				}
			}
		}
	}
#ifdef INET6
	/*
	 * transfer default values from set::ipv6_clones_mask into
	 * each individual allow block. If other similar things like
	 * this stack up here, perhaps this shoul be moved to another
	 * function.
	 */
	for(allow = conf_allow; allow; allow = (ConfigItem_allow *)allow->next)
		if(!allow->ipv6_clone_mask)
			allow->ipv6_clone_mask = tempiConf.default_ipv6_clone_mask;
#endif /* INET6 */

	close_listeners();
	listen_cleanup();
	close_listeners();
	loop.do_bancheck = 1;
	free_iConf(&iConf);
	bcopy(&tempiConf, &iConf, sizeof(aConfiguration));
	bzero(&tempiConf, sizeof(aConfiguration));
#ifdef THROTTLING
	{
		EventInfo eInfo;
		long v;
		eInfo.flags = EMOD_EVERY;
		if (!THROTTLING_PERIOD)
			v = 120;
		else
		{
			v = THROTTLING_PERIOD/2;
			if (v > 5)
				v = 5; /* accuracy, please */
		}
		eInfo.every = v;
		EventMod(EventFind("bucketcleaning"), &eInfo);
	}
#endif

	/* initialize conf_files with defaults if the block isn't set: */
	if(!conf_files)
	  _conf_files(NULL, NULL);

	if (errors > 0)
	{
		config_error("%i fatal errors encountered", errors);
	}
	return (errors > 0 ? -1 : 1);
}


OperFlag *config_binary_flags_search(OperFlag *table, char *cmd, int size) {
	int start = 0;
	int stop = size-1;
	int mid;
	while (start <= stop) {
		mid = (start+stop)/2;

		if (smycmp(cmd,table[mid].name) < 0) {
			stop = mid-1;
		}
		else if (strcmp(cmd,table[mid].name) == 0) {
			return &(table[mid]);
		}
		else
			start = mid+1;
	}
	return NULL;
}


int	config_test()
{
	ConfigEntry 	*ce;
	ConfigFile	*cfptr;
	ConfigCommand	*cc;
	int		errors = 0;
	Hook *h;

	for (cfptr = conf; cfptr; cfptr = cfptr->cf_next)
	{
		if (config_verbose > 1)
			config_status("Testing %s", cfptr->cf_filename);
		for (ce = cfptr->cf_entries; ce; ce = ce->ce_next)
		{
			if (!ce->ce_varname)
			{
				config_error("%s:%i: %s:%i: null ce->ce_varname",
					ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
					__FILE__, __LINE__);
				return -1;
			}
			if ((cc = config_binary_search(ce->ce_varname))) {
				if (cc->testfunc)
					errors += (cc->testfunc(cfptr, ce));
			}
			else 
			{
				int used = 0;
				for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next) 
				{
					int value, errs = 0;
					if (h->owner && !(h->owner->flags & MODFLAG_TESTING)
					    && !(h->owner->options & MOD_OPT_PERM))


						continue;
					value = (*(h->func.intfunc))(cfptr,ce,CONFIG_MAIN,&errs);
					if (value == 2)
						used = 1;
					if (value == 1)
					{
						used = 1;
						break;
					}
					if (value == -1)
					{
						used = 1;
						errors += errs;
						break;
					}
					if (value == -2) 
					{
						used = 1;
						errors += errs;
					}
						
				}
				if (!used)
					config_status("%s:%i: unknown directive %s", 
						ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
						ce->ce_varname);
			}
		}
	}
	errors += config_post_test();
	if (errors > 0)
	{
		config_error("%i errors encountered", errors);
	}
	return (errors > 0 ? -1 : 1);
}

/*
 * Service functions
*/

ConfigItem_deny_dcc	*Find_deny_dcc(char *name)
{
	ConfigItem_deny_dcc	*p;

	if (!name)
		return NULL;

	for (p = conf_deny_dcc; p; p = (ConfigItem_deny_dcc *) p->next)
	{
		if (!match(name, p->filename))
			return (p);
	}
	return NULL;
}

ConfigItem_alias *Find_alias(char *name) {
	ConfigItem_alias *alias;

	if (!name)
		return NULL;

	for (alias = conf_alias; alias; alias = (ConfigItem_alias *)alias->next) {
		if (!stricmp(alias->alias, name))
			return alias;
	}
	return NULL;
}

ConfigItem_class	*Find_class(char *name)
{
	ConfigItem_class	*p;

	if (!name)
		return NULL;

	for (p = conf_class; p; p = (ConfigItem_class *) p->next)
	{
		if (!strcmp(name, p->name))
			return (p);
	}
	return NULL;
}

ConfigItem_oper	*Find_oper(char *name)
{
	ConfigItem_oper	*p;

	if (!name)
		return NULL;

	for (p = conf_oper; p; p = (ConfigItem_oper *) p->next)
	{
		if (!strcmp(name, p->name))
			return (p);
	}
	return NULL;
}

int count_oper_sessions(char *name)
{
int i, count = 0;
aClient *cptr;

#ifdef NO_FDLIST
	for (i = 0; i <= LastSlot; i++)
#else
int j;
	for (i = oper_fdlist.entry[j = 1]; j <= oper_fdlist.last_entry; i = oper_fdlist.entry[++j])
#endif
		if ((cptr = local[i]) && IsPerson(cptr) && IsAnOper(cptr) &&
		    cptr->user && cptr->user->operlogin && !strcmp(cptr->user->operlogin,name))
			count++;

	return count;
}

ConfigItem_listen	*Find_listen(char *ipmask, int port)
{
	ConfigItem_listen	*p;

	if (!ipmask)
		return NULL;

	for (p = conf_listen; p; p = (ConfigItem_listen *) p->next)
	{
		if (!match(p->ip, ipmask) && (port == p->port))
			return (p);
		if (!match(ipmask, p->ip) && (port == p->port))
			return (p);
	}
	return NULL;
}

ConfigItem_ulines *Find_uline(char *host) {
	ConfigItem_ulines *ulines;

	if (!host)
		return NULL;

	for(ulines = conf_ulines; ulines; ulines =(ConfigItem_ulines *) ulines->next) {
		if (!stricmp(host, ulines->servername))
			return ulines;
	}
	return NULL;
}


ConfigItem_except *Find_except(aClient *sptr, char *host, short type) {
	ConfigItem_except *excepts;

	if (!host)
		return NULL;

	for(excepts = conf_except; excepts; excepts =(ConfigItem_except *) excepts->next) {
		if (excepts->flag.type == type)
		{
			if (match_ip(sptr->ip, host, excepts->mask, excepts->netmask))
				return excepts;
		}
	}
	return NULL;
}

ConfigItem_tld *Find_tld(aClient *cptr, char *uhost) {
	ConfigItem_tld *tld;

	if (!uhost || !cptr)
		return NULL;

	for(tld = conf_tld; tld; tld = (ConfigItem_tld *) tld->next)
	{
		if (!match(tld->mask, uhost))
		{
			if ((tld->options & TLD_SSL) && !IsSecure(cptr))
				continue;
			if ((tld->options & TLD_REMOTE) && MyClient(cptr))
				continue;
			return tld;
		}
	}
	return NULL;
}


ConfigItem_link *Find_link(char *username,
			   char *hostname,
			   char *ip,
			   char *servername)
{
	ConfigItem_link	*link;

	if (!username || !hostname || !servername || !ip)
		return NULL;

	for(link = conf_link; link; link = (ConfigItem_link *) link->next)
	{
		if (!match(link->servername, servername) &&
		    !match(link->username, username) &&
		    (!match(link->hostname, hostname) || !match(link->hostname, ip)))
			return link;
	}
	return NULL;

}

/* ugly ugly ugly */
int match_ip46(char *a, char *b)
{
#ifdef INET6
	if (!strncmp(a, "::ffff:", 7) && !strcmp(a+7, b))
		return 0; // match
#endif
	return 1; //nomatch
}

ConfigItem_cgiirc *Find_cgiirc(char *username, char *hostname, char *ip, CGIIRCType type)
{
ConfigItem_cgiirc *e;

	if (!username || !hostname || !ip)
		return NULL;

	for (e = conf_cgiirc; e; e = (ConfigItem_cgiirc *)e->next)
	{
		if ((e->type == type) && (!e->username || !match(e->username, username)) &&
		    (!match(e->hostname, hostname) || !match(e->hostname, ip) || !match_ip46(e->hostname, ip)))
			return e;
	}

	return NULL;
}

ConfigItem_ban 	*Find_ban(aClient *sptr, char *host, short type)
{
	ConfigItem_ban *ban;

	/* Check for an except ONLY if we find a ban, makes it
	 * faster since most users will not have a ban so excepts
	 * don't need to be searched -- codemastr
	 */

	for (ban = conf_ban; ban; ban = (ConfigItem_ban *) ban->next)
	{
		if (ban->flag.type == type)
		{
			if (sptr)
			{
				if (match_ip(sptr->ip, host, ban->mask, ban->netmask))
				{
					/* Person got a exception */
					if ((type == CONF_BAN_USER || type == CONF_BAN_IP)
					    && Find_except(sptr, host, CONF_EXCEPT_BAN))
						return NULL;
					return ban;
				}
			}
			else if (!match(ban->mask, host)) /* We don't worry about exceptions */
				return ban;
		}
	}
	return NULL;
}

ConfigItem_ban 	*Find_banEx(aClient *sptr, char *host, short type, short type2)
{
	ConfigItem_ban *ban;

	/* Check for an except ONLY if we find a ban, makes it
	 * faster since most users will not have a ban so excepts
	 * don't need to be searched -- codemastr
	 */

	for (ban = conf_ban; ban; ban = (ConfigItem_ban *) ban->next)
	{
		if ((ban->flag.type == type) && (ban->flag.type2 == type2))
		{
			if (sptr)
			{
				if (match_ip(sptr->ip, host, ban->mask, ban->netmask)) {
					/* Person got a exception */
					if (Find_except(sptr, host, type))
						return NULL;
					return ban;
				}
			}
			else if (!match(ban->mask, host)) /* We don't worry about exceptions */
				return ban;
		}
	}
	return NULL;
}

int	AllowClient(aClient *cptr, struct hostent *hp, char *sockhost, char *username)
{
	ConfigItem_allow *aconf;
	char *hname;
	int  i, ii = 0;
	static char uhost[HOSTLEN + USERLEN + 3];
	static char fullname[HOSTLEN + 1];
#ifdef INET6
	short is_ipv4;
#endif /* INET6 */

	for (aconf = conf_allow; aconf; aconf = (ConfigItem_allow *) aconf->next)
	{
		if (!aconf->hostname || !aconf->ip)
			goto attach;
		if (aconf->auth && !cptr->passwd && aconf->flags.nopasscont)
			continue;
		if (aconf->flags.ssl && !IsSecure(cptr))
			continue;
		if (hp && hp->h_name)
		{
			hname = hp->h_name;
			strncpyzt(fullname, hname, sizeof(fullname));
			add_local_domain(fullname, HOSTLEN - strlen(fullname));
			Debug((DEBUG_DNS, "a_il: %s->%s", sockhost, fullname));
			if (index(aconf->hostname, '@'))
			{
				if (aconf->flags.noident)
					strlcpy(uhost, username, sizeof(uhost));
				else
					strlcpy(uhost, cptr->username, sizeof(uhost));
				strlcat(uhost, "@", sizeof(uhost));
			}
			else
				*uhost = '\0';
			strlcat(uhost, fullname, sizeof(uhost));
			if (!match(aconf->hostname, uhost))
				goto attach;
		}

		if (index(aconf->ip, '@'))
		{
			if (aconf->flags.noident)
				strncpyzt(uhost, username, sizeof(uhost));
			else
				strncpyzt(uhost, cptr->username, sizeof(uhost));
			(void)strlcat(uhost, "@", sizeof(uhost));
		}
		else
			*uhost = '\0';
		strlcat(uhost, sockhost, sizeof(uhost));
		/* Check the IP */
		if (match_ip(cptr->ip, uhost, aconf->ip, aconf->netmask))
			goto attach;

		/* Hmm, localhost is a special case, hp == NULL and sockhost contains
		 * 'localhost' instead of an ip... -- Syzop. */
		if (!strcmp(sockhost, "localhost"))
		{
			if (index(aconf->hostname, '@'))
			{
				if (aconf->flags.noident)
					strcpy(uhost, username);
				else
					strcpy(uhost, cptr->username);
				strcat(uhost, "@localhost");
			}
			else
				strcpy(uhost, "localhost");

			if (!match(aconf->hostname, uhost))
				goto attach;
		}
		
		continue;
	      attach:
/*		if (index(uhost, '@'))  now flag based -- codemastr */
		if (!aconf->flags.noident)
			cptr->flags |= FLAGS_DOID;
		if (!aconf->flags.useip && hp) 
			strncpyzt(uhost, fullname, sizeof(uhost));
		else
			strncpyzt(uhost, sockhost, sizeof(uhost));
		get_sockhost(cptr, uhost);
#ifdef INET6
		is_ipv4 = IN6_IS_ADDR_V4MAPPED(&cptr->ip);
#endif /* INET6 */

		/* FIXME */
		if (aconf->maxperip)
		{
			ii = 1;
			for (i = LastSlot; i >= 0; i--)
				if (local[i] && MyClient(local[i])
#ifndef INET6
				    && local[i]->ip.S_ADDR == cptr->ip.S_ADDR)
#else
				    /*
				     * match IPv4 exactly and the ipv6
				     * based on ipv6_clone_mask.
				     */
				    && (is_ipv4
					? !bcmp(local[i]->ip.S_ADDR, cptr->ip.S_ADDR, sizeof(cptr->ip.S_ADDR))
					: match_ipv6(&local[i]->ip, &cptr->ip, aconf->ipv6_clone_mask)))
						
#endif
				{
					ii++;
					if (ii > aconf->maxperip)
					{
						exit_client(cptr, cptr, &me,
							"Too many connections from your IP");
						return -5;	/* Already got one with that ip# */
					}
				}
		}
		if ((i = Auth_Check(cptr, aconf->auth, cptr->passwd)) == -1)
		{
			exit_client(cptr, cptr, &me,
				"Password mismatch");
			return -5;
		}
		if ((i == 2) && (cptr->passwd))
		{
			MyFree(cptr->passwd);
			cptr->passwd = NULL;
		}
		if (!((aconf->class->clients + 1) > aconf->class->maxclients))
		{
			cptr->class = aconf->class;
			cptr->class->clients++;
		}
		else
		{
			sendto_one(cptr, rpl_str(RPL_REDIR), me.name, cptr->name, aconf->server ? aconf->server : defserv, aconf->port ? aconf->port : 6667);
			return -3;
		}
		return 0;
	}
	return -1;
}

ConfigItem_vhost *Find_vhost(char *name) {
	ConfigItem_vhost *vhost;

	for (vhost = conf_vhost; vhost; vhost = (ConfigItem_vhost *)vhost->next) {
		if (!strcmp(name, vhost->login))
			return vhost;
	}
	return NULL;
}


/** returns NULL if allowed and struct if denied */
ConfigItem_deny_channel *Find_channel_allowed(aClient *cptr, char *name)
{
	ConfigItem_deny_channel *dchannel;
	ConfigItem_allow_channel *achannel;

	for (dchannel = conf_deny_channel; dchannel; dchannel = (ConfigItem_deny_channel *)dchannel->next)
	{
		if (!match(dchannel->channel, name) && (dchannel->class ? !strcmp(cptr->class->name, dchannel->class) : 1))
			break;
	}
	if (dchannel)
	{
		for (achannel = conf_allow_channel; achannel; achannel = (ConfigItem_allow_channel *)achannel->next)
		{
			if (!match(achannel->channel, name) && (achannel->class ? !strcmp(cptr->class->name, achannel->class) : 1))
				break;
		}
		if (achannel)
			return NULL;
		else
			return (dchannel);
	}
	return NULL;
}

void init_dynconf(void)
{
	bzero(&iConf, sizeof(iConf));
	bzero(&tempiConf, sizeof(iConf));
}

char *pretty_time_val(long timeval)
{
	static char buf[512];

	if (timeval == 0)
		return "0";

	buf[0] = 0;

	if (timeval/86400)
		sprintf(buf, "%ld day%s ", timeval/86400, timeval/86400 != 1 ? "s" : "");
	if ((timeval/3600) % 24)
		sprintf(buf, "%s%ld hour%s ", buf, (timeval/3600)%24, (timeval/3600)%24 != 1 ? "s" : "");
	if ((timeval/60)%60)
		sprintf(buf, "%s%ld minute%s ", buf, (timeval/60)%60, (timeval/60)%60 != 1 ? "s" : "");
	if ((timeval%60))
		sprintf(buf, "%s%ld second%s", buf, timeval%60, timeval%60 != 1 ? "s" : "");
	return buf;
}

/*
 * Actual config parser funcs
*/

int	_conf_include(ConfigFile *conf, ConfigEntry *ce)
{
	int	ret = 0;
#ifdef GLOBH
	glob_t files;
	int i;
#elif defined(_WIN32)
	HANDLE hFind;
	WIN32_FIND_DATA FindData;
	char cPath[MAX_PATH], *cSlash = NULL, *path;
#endif
	if (!ce->ce_vardata)
	{
		config_status("%s:%i: include: no filename given",
			ce->ce_fileptr->cf_filename,
			ce->ce_varlinenum);
		return -1;
	}
#ifdef USE_LIBCURL
	if (url_is_valid(ce->ce_vardata))
		return remote_include(ce);
#endif
#if !defined(_WIN32) && !defined(_AMIGA) && !defined(OSXTIGER) && DEFAULT_PERMISSIONS != 0
	chmod(ce->ce_vardata, DEFAULT_PERMISSIONS);
#endif
#ifdef GLOBH
#if defined(__OpenBSD__) && defined(GLOB_LIMIT)
	glob(ce->ce_vardata, GLOB_NOSORT|GLOB_NOCHECK|GLOB_LIMIT, NULL, &files);
#else
	glob(ce->ce_vardata, GLOB_NOSORT|GLOB_NOCHECK, NULL, &files);
#endif
	if (!files.gl_pathc) {
		globfree(&files);
		config_status("%s:%i: include %s: invalid file given",
			ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			ce->ce_vardata);
		return -1;
	}	
	for (i = 0; i < files.gl_pathc; i++) {
		add_include(files.gl_pathv[i], ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
		ret = load_conf(files.gl_pathv[i], files.gl_pathv[i]);
		if (ret < 0)
		{
			globfree(&files);
			return ret;
		}
	}
	globfree(&files);
#elif defined(_WIN32)
	bzero(cPath,MAX_PATH);
	if (strchr(ce->ce_vardata, '/') || strchr(ce->ce_vardata, '\\')) {
		strncpyzt(cPath,ce->ce_vardata,MAX_PATH);
		cSlash=cPath+strlen(cPath);
		while(*cSlash != '\\' && *cSlash != '/' && cSlash > cPath)
			cSlash--; 
		*(cSlash+1)=0;
	}
	if ( (hFind = FindFirstFile(ce->ce_vardata, &FindData)) == INVALID_HANDLE_VALUE )
	{
		config_status("%s:%i: include %s: invalid file given",
			ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			ce->ce_vardata);
		return -1;
	}
	if (cPath) {
		path = MyMalloc(strlen(cPath) + strlen(FindData.cFileName)+1);
		strcpy(path, cPath);
		strcat(path, FindData.cFileName);

		add_include(path, ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
		ret = load_conf(path, path);
		free(path);

	}
	else
	{
		add_include(FindData.cFileName, ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
		ret = load_conf(FindData.cFileName, FindData.cFileName);
	}
	if (ret < 0)
	{
		FindClose(hFind);
		return ret;
	}

	ret = 0;
	while (FindNextFile(hFind, &FindData) != 0) {
		if (cPath) {
			path = MyMalloc(strlen(cPath) + strlen(FindData.cFileName)+1);
			strcpy(path,cPath);
			strcat(path,FindData.cFileName);

			add_include(path, ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
			ret = load_conf(path, path);
			free(path);
			if (ret < 0)
				break;
		}
		else
		{
			add_include(FindData.cFileName, ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
			ret = load_conf(FindData.cFileName, FindData.cFileName);
		}
	}
	FindClose(hFind);
	if (ret < 0)
		return ret;
#else
	add_include(ce->ce_vardata, ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
	ret = load_conf(ce->ce_vardata, ce->ce_vardata);
	return ret;
#endif
	return 1;
}

int	_test_include(ConfigFile *conf, ConfigEntry *ce)
{
	return 0;
}

int	_conf_admin(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry *cep;
	ConfigItem_admin *ca;

	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		ca = MyMallocEx(sizeof(ConfigItem_admin));
		if (!conf_admin)
			conf_admin_tail = ca;
		ircstrdup(ca->line, cep->ce_varname);
		AddListItem(ca, conf_admin);
	}
	return 1;
}

int	_test_admin(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry *cep;
	int 	    errors = 0;

	if (requiredstuff.conf_admin)
	{
		config_warn_duplicate(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "admin");
		return 0;
	}

	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!cep->ce_varname)
		{
			config_error("%s:%i: blank admin item",
				cep->ce_fileptr->cf_filename,
				cep->ce_varlinenum);
			errors++;
			continue;
		}
	}
	requiredstuff.conf_admin = 1;
	return errors;
}

int	_conf_me(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry *cep;

	if (!conf_me)
		conf_me = MyMallocEx(sizeof(ConfigItem_me));

	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!strcmp(cep->ce_varname, "name"))
		{
			ircstrdup(conf_me->name, cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "info"))
		{
			ircstrdup(conf_me->info, cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "numeric"))
		{
			conf_me->numeric = atol(cep->ce_vardata);
		}
	}
	return 1;
}

int	_test_me(ConfigFile *conf, ConfigEntry *ce)
{
	char has_name = 0, has_info = 0, has_numeric = 0;
	ConfigEntry *cep;
	int	    errors = 0;

	if (requiredstuff.conf_me)
	{
		config_warn_duplicate(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "me");
		return 0;
	}
	
	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (config_is_blankorempty(cep, "me"))
			continue;

		/* me::name */
		if (!strcmp(cep->ce_varname, "name"))
		{
			if (has_name)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "me::name");
				continue;
			}
			has_name = 1;
			if (!strchr(cep->ce_vardata, '.'))
			{
				config_error("%s:%i: illegal me::name, must be fully qualified hostname",
					cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum);
				errors++;
			}
			if (!valid_host(cep->ce_vardata))
			{
				config_error("%s:%i: illegal me::name contains invalid character(s) [only a-z, 0-9, _, -, . are allowed]",
					cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum);
				errors++;
			}
			if (strlen(cep->ce_vardata) > HOSTLEN)
			{
				config_error("%s:%i: illegal me::name, must be less or equal to %i characters",
					cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, HOSTLEN);
				errors++;
			}
		}
		/* me::info */
		else if (!strcmp(cep->ce_varname, "info"))
		{
			char *p;
			char valid = 0;
			if (has_info)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "me::info");
				continue;
			}
			has_info = 1;
			if (strlen(cep->ce_vardata) > (REALLEN-1))
			{
				config_error("%s:%i: too long me::info, must be max. %i characters",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum, 
					REALLEN-1);
				errors++;
			}
		
			/* Valid me::info? Any data except spaces is ok */
			for (p=cep->ce_vardata; *p; p++)
			{
				if (*p != ' ')
				{
					valid = 1;
					break;
				}
			}
			if (!valid)
			{
				config_error("%s:%i: empty me::info, should be a server description.",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
				errors++;
			}
		}
		/* me::numeric */
		else if (!strcmp(cep->ce_varname, "numeric"))
		{
			long l;

			has_numeric = 1;
			l = atol(cep->ce_vardata);
			if ((l < 0) || (l > 254))
			{
				config_error("%s:%i: illegal me::numeric error (must be between 0 and 254)",
					cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum);
				errors++;
			}
		}
		/* Unknown entry */
		else
		{
			config_error_unknown(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, 
				"me", cep->ce_varname);
			errors++;
		}
	}
	if (!has_name)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "me::name");
		errors++;
	}
	if (!has_info)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "me::info");
		errors++;
	}
	if (!has_numeric)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, 
			"me::numeric");
		errors++;
	}
	requiredstuff.conf_me = 1;
	return errors;
}

/*
 * The files {} block
 */
int	_conf_files(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry *cep;

	if (!conf_files)
	{
		conf_files = MyMallocEx(sizeof(ConfigItem_files));

		/* set defaults */
		conf_files->motd_file = strdup(MPATH);
		conf_files->rules_file = strdup(RPATH);
		conf_files->smotd_file = strdup(SMPATH);
		conf_files->botmotd_file = strdup(BPATH);
		conf_files->opermotd_file = strdup(OPATH);
		conf_files->svsmotd_file = strdup(VPATH);
		
		conf_files->pid_file = strdup(IRCD_PIDFILE);
		conf_files->tune_file = strdup(IRCDTUNE);
		
		/* we let actual files get read in later by the motd caching mechanism */
	}
	/*
	 * hack to allow initialization of conf_files (above) when there is no files block in 
	 * CPATH. The caller calls _conf_files(NULL, NULL); to do this. We return here because 
	 * the for loop's initialization of cep would segfault otherwise. We return 1 because
	 * if config_run() calls us with a NULL ce, it's got a bug...but we can't detect that.
	 */
	if(!ce)
	  return 1;
	
	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!strcmp(cep->ce_varname, "motd"))
			ircstrdup(conf_files->motd_file, cep->ce_vardata);
		else if (!strcmp(cep->ce_varname, "shortmotd"))
			ircstrdup(conf_files->smotd_file, cep->ce_vardata);
		else if (!strcmp(cep->ce_varname, "opermotd"))
			ircstrdup(conf_files->opermotd_file, cep->ce_vardata);
		else if (!strcmp(cep->ce_varname, "svsmotd"))
			ircstrdup(conf_files->svsmotd_file, cep->ce_vardata);
		else if (!strcmp(cep->ce_varname, "botmotd"))
			ircstrdup(conf_files->botmotd_file, cep->ce_vardata);
		else if (!strcmp(cep->ce_varname, "rules"))
			ircstrdup(conf_files->rules_file, cep->ce_vardata);
		else if (!strcmp(cep->ce_varname, "tunefile"))
			ircstrdup(conf_files->tune_file, cep->ce_vardata);
		else if (!strcmp(cep->ce_varname, "pidfile"))
			ircstrdup(conf_files->pid_file, cep->ce_vardata);
	}
	return 1;
}

int	_test_files(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry *cep;
	int	    errors = 0;
	char has_motd = 0, has_smotd = 0, has_rules = 0;
	char has_botmotd = 0, has_opermotd = 0, has_svsmotd = 0;
	char has_pidfile = 0, has_tunefile = 0;
	
	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		/* files::motd */
		if (!strcmp(cep->ce_varname, "motd"))
		{
			if (has_motd)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "files::motd");
				continue;
			}
			config_test_openfile(cep, O_RDONLY, 0, "files::motd", 0, 1);
			has_motd = 1;
		}
		/* files::smotd */
		else if (!strcmp(cep->ce_varname, "shortmotd")) 
		{
			if (has_smotd)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "files::shortmotd");
				continue;
			}
			config_test_openfile(cep, O_RDONLY, 0, "files::shortmotd", 0, 1);
			has_smotd = 1;
		}
		/* files::rules */
		else if (!strcmp(cep->ce_varname, "rules")) 
		{
			if (has_rules)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "files::rules");
				continue;
			}
			config_test_openfile(cep, O_RDONLY, 0, "files::rules", 0, 1);
			has_rules = 1;
		}
		/* files::botmotd */
		else if (!strcmp(cep->ce_varname, "botmotd")) 
		{
			if (has_botmotd)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "files::botmotd");
				continue;
			}
			config_test_openfile(cep, O_RDONLY, 0, "files::botmotd", 0, 1);
			has_botmotd = 1;
		}
		/* files::opermotd */
		else if (!strcmp(cep->ce_varname, "opermotd")) 
		{
			if (has_opermotd)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "files::opermotd");
				continue;
			}
			config_test_openfile(cep, O_RDONLY, 0, "files::opermotd", 0, 1);
			has_opermotd = 1;
		}
		/* files::svsmotd 
		 * This config stuff should somehow be inside of modules/m_svsmotd.c!!!... right?
		 */
		else if (!strcmp(cep->ce_varname, "svsmotd")) 
		{
			if (has_svsmotd)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "files::svsmotd");
				continue;
			}
			/* svsmotd can't be a URL because we have to be able to write to it */
			config_test_openfile(cep, O_RDONLY, 0, "files::svsmotd", 0, 0);
			has_svsmotd = 1;
		}
		/* files::pidfile */
		else if (!strcmp(cep->ce_varname, "pidfile")) 
		{
			if (has_pidfile)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "files::pidfile");
				continue;
			}
			
			errors += config_test_openfile(cep, O_WRONLY | O_CREAT, 0600, "files::pidfile", 1, 0);
			has_pidfile = 1;
		}
		/* files::tunefile */
		else if (!strcmp(cep->ce_varname, "tunefile")) 
		{
			if (has_tunefile)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "files::tunefile");
				continue;
			}
			errors += config_test_openfile(cep, O_RDWR | O_CREAT, 0600, "files::tunefile", 1, 0);
			has_tunefile = 1;
		}
		/* <random directive here> */
		else
		{
			config_error("%s:%d: Unknown directive: \"%s\" in files {}", cep->ce_fileptr->cf_filename,
				     cep->ce_varlinenum, cep->ce_varname);
			errors ++;
		}
	}
	return errors;
}

/*
 * The oper {} block parser
*/

int	_conf_oper(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry *cep;
	ConfigEntry *cepp;
	ConfigItem_oper *oper = NULL;
	ConfigItem_oper_from *from;
	OperFlag *ofp = NULL;
	struct irc_netmask tmp;

	oper =  MyMallocEx(sizeof(ConfigItem_oper));
	oper->name = strdup(ce->ce_vardata);
	
	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!strcmp(cep->ce_varname, "password"))
			oper->auth = Auth_ConvertConf2AuthStruct(cep);
		else if (!strcmp(cep->ce_varname, "class"))
		{
			oper->class = Find_class(cep->ce_vardata);
			if (!oper->class || (oper->class->flag.temporary == 1))
			{
				config_status("%s:%i: illegal oper::class, unknown class '%s' using default of class 'default'",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
					cep->ce_vardata);
				oper->class = default_class;
			}
		}
		else if (!strcmp(cep->ce_varname, "flags"))
		{
			if (!cep->ce_entries)
			{
				char *m = "*";
				int *i, flag;

				for (m = (*cep->ce_vardata) ? cep->ce_vardata : m; *m; m++) 
				{
					for (i = _OldOperFlags; (flag = *i); i += 2)
						if (*m == (char)(*(i + 1))) 
						{
							oper->oflags |= flag;
							break;
						}
				}
			}
			else
			{
				for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next)
				{
					if ((ofp = config_binary_flags_search(_OperFlags, cepp->ce_varname, ARRAY_SIZEOF(_OperFlags)))) 
						oper->oflags |= ofp->flag;
				}
			}
		}
		else if (!strcmp(cep->ce_varname, "swhois"))
		{
			ircstrdup(oper->swhois, cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "snomask"))
		{
			ircstrdup(oper->snomask, cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "modes"))
		{
			oper->modes = set_usermode(cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "require-modes"))
		{
			oper->require_modes = set_usermode(cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "maxlogins"))
		{
			oper->maxlogins = atoi(cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "from"))
		{
			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next)
			{
				if (!strcmp(cepp->ce_varname, "userhost"))
				{
					from = MyMallocEx(sizeof(ConfigItem_oper_from));
					ircstrdup(from->name, cepp->ce_vardata);
					tmp.type = parse_netmask(from->name, &tmp);
					if (tmp.type != HM_HOST)
					{
						from->netmask = MyMallocEx(sizeof(struct irc_netmask));
						bcopy(&tmp, from->netmask, sizeof(struct irc_netmask));
					}
					AddListItem(from, oper->from);
				}
			}
		}
	}
	AddListItem(oper, conf_oper);
	return 1;
}

int	_test_oper(ConfigFile *conf, ConfigEntry *ce)
{
	char has_class = 0, has_password = 0, has_flags = 0, has_swhois = 0, has_snomask = 0;
	char has_modes = 0, has_require_modes = 0, has_from = 0, has_maxlogins = 0;
	int oper_flags = 0;
	ConfigEntry *cep;
	ConfigEntry *cepp;
	OperFlag *ofp;
	int	errors = 0;

	if (!ce->ce_vardata)
	{
		config_error_noname(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "oper");
		errors++;
	}
	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!cep->ce_varname)
		{
			config_error_blank(cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
				"oper");
			errors++; 
			continue;
		}
		if (!strcmp(cep->ce_varname, "password"))
		{
			if (has_password)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "oper::password");
				continue;
			}
			has_password = 1;
			if (Auth_CheckError(cep) < 0)
				errors++;
			continue;
		}
		/* Regular variables */
		if (!cep->ce_entries)
		{
			if (config_is_blankorempty(cep, "oper"))
			{
				errors++; 
				continue;
			}
			/* oper::class */
			if (!strcmp(cep->ce_varname, "class"))
			{
				if (has_class)
				{
					config_warn_duplicate(cep->ce_fileptr->cf_filename, 
						cep->ce_varlinenum, "oper::class");
					continue;
				}
				has_class = 1;
			}
			/* oper::swhois */
			else if (!strcmp(cep->ce_varname, "swhois")) 
			{
				if (has_swhois)
				{
					config_warn_duplicate(cep->ce_fileptr->cf_filename,
						cep->ce_varlinenum, "oper::swhois");
					continue;
				}
				has_swhois = 1;
			}
			/* oper::snomask */
			else if (!strcmp(cep->ce_varname, "snomask")) 
			{
				if (has_snomask)
				{
					config_warn_duplicate(cep->ce_fileptr->cf_filename,
						cep->ce_varlinenum, "oper::snomask");
					continue;
				}
				has_snomask = 1;
			}
			/* oper::modes */
			else if (!strcmp(cep->ce_varname, "modes"))
			{
				char *p;
				for (p = cep->ce_vardata; *p; p++)
					if (strchr("oOaANCrzS", *p))
					{
						config_error("%s:%i: oper::modes may not include mode '%c'",
							cep->ce_fileptr->cf_filename, cep->ce_varlinenum, *p);
						errors++;
					}
				if (has_modes)
				{
					config_warn_duplicate(cep->ce_fileptr->cf_filename,
						cep->ce_varlinenum, "oper::modes");
					continue;
				}
				has_modes = 1;
			}
			/* oper::require-modes */
			else if (!strcmp(cep->ce_varname, "require-modes"))
			{
				char *p;
				for (p = cep->ce_vardata; *p; p++)
					if (strchr("oOaANC", *p))
					{
						config_warn("%s:%i: oper::require-modes probably shouldn't include mode '%c'",
							cep->ce_fileptr->cf_filename, cep->ce_varlinenum, *p);
					}
				if (has_require_modes)
				{
					config_warn_duplicate(cep->ce_fileptr->cf_filename,
						cep->ce_varlinenum, "oper::require-modes");
					continue;
				}
				has_require_modes = 1;
			}
			/* oper::maxlogins */
			else if (!strcmp(cep->ce_varname, "maxlogins"))
			{
				int l;

				if (has_maxlogins)
				{
					config_warn_duplicate(cep->ce_fileptr->cf_filename,
						cep->ce_varlinenum, "oper::maxlogins");
					continue;
				}
				has_maxlogins = 1;

				l = atoi(cep->ce_vardata);
				if ((l < 0) || (l > 5000))
				{
					config_error("%s:%i: oper::maxlogins: value out of range (%d) should be 0-5000",
						cep->ce_fileptr->cf_filename, cep->ce_varlinenum, l);
					errors++; 
					continue;
				}
			}
			/* oper::flags */
			else if (!strcmp(cep->ce_varname, "flags"))
			{
				if (has_flags)
				{
					config_warn_duplicate(cep->ce_fileptr->cf_filename,
						cep->ce_varlinenum, "oper::flags");
					continue;
				}
				has_flags = 1;
			}
			else
			{
				config_error_unknown(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "oper", cep->ce_varname);
				errors++;
				continue;
			}
		}
		/* Sections */
		else
		{
			/* oper::flags {} */
			if (!strcmp(cep->ce_varname, "flags"))
			{
				if (has_flags)
				{
					config_warn_duplicate(cep->ce_fileptr->cf_filename,
						cep->ce_varlinenum, "oper::flags");
					continue;
				}
				has_flags = 1;
				for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next)
				{
					if (!cepp->ce_varname)
					{
						config_error_empty(cepp->ce_fileptr->cf_filename,
							cepp->ce_varlinenum, "oper::flags",
							cep->ce_varname);
						errors++; 
						continue;
					}
					if (!(ofp = config_binary_flags_search(_OperFlags, cepp->ce_varname, ARRAY_SIZEOF(_OperFlags)))) {
						config_error_unknownflag(cepp->ce_fileptr->cf_filename,
							cepp->ce_varlinenum, "oper",
							cepp->ce_varname);
						errors++; 
					} else
						oper_flags |= ofp->flag;
				}
				continue;
			}
			/* oper::from {} */
			else if (!strcmp(cep->ce_varname, "from"))
			{
				if (has_from)
				{
					config_warn_duplicate(cep->ce_fileptr->cf_filename,
						cep->ce_varlinenum, "oper::from");
					continue;
				}
				has_from = 1;
				for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next)
				{
					if (config_is_blankorempty(cepp, "oper::from"))
					{
						errors++;
						continue;
					}
					/* Unknown Entry */
					if (strcmp(cepp->ce_varname, "userhost"))
					{
						config_error_unknown(cepp->ce_fileptr->cf_filename,
							cepp->ce_varlinenum, "oper::from",
							cepp->ce_varname);
						errors++;
						continue;
					}
				}
				continue;
			}
			else
			{
				config_error_unknown(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "oper", cep->ce_varname);
				errors++; 
				continue;
			}
		}
	}
	if (!has_password)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"oper::password");
		errors++;
	}	
	if (!has_from)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"oper::from");
		errors++;
	}	
	if (!has_flags)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"oper::flags");
		errors++;
	} else {
		/* Check oper flags -- warning needed only (autoconvert) */
		if (!(oper_flags & (OFLAG_GROUTE|OFLAG_GKILL|OFLAG_GNOTICE)) &&
		    (oper_flags & (OFLAG_GZL|OFLAG_TKL|OFLAG_OVERRIDE)))
		{
			config_warn("%s:%i: oper::oflags: can_gzline/can_gkline/can_override (global privileges) "
			            "are incompatible with local oper -- user will be globop",
			            ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
		}
	}
	if (!has_class)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"oper::class");
		errors++;
	}
	return errors;
	
}

/*
 * The class {} block parser
*/
int	_conf_class(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry *cep, *cep2;
	ConfigItem_class *class;
	unsigned char isnew = 0;

	if (!(class = Find_class(ce->ce_vardata)))
	{
		class = MyMallocEx(sizeof(ConfigItem_class));
		ircstrdup(class->name, ce->ce_vardata);
		isnew = 1;
	}
	else
	{
		isnew = 0;
		class->flag.temporary = 0;
		class->options = 0; /* RESET OPTIONS */
	}
	ircstrdup(class->name, ce->ce_vardata);

	class->connfreq = 60; /* default */

	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!strcmp(cep->ce_varname, "pingfreq"))
			class->pingfreq = atol(cep->ce_vardata);
		else if (!strcmp(cep->ce_varname, "connfreq"))
			class->connfreq = atol(cep->ce_vardata);
		else if (!strcmp(cep->ce_varname, "maxclients"))
			class->maxclients = atol(cep->ce_vardata);
		else if (!strcmp(cep->ce_varname, "sendq"))
			class->sendq = atol(cep->ce_vardata);
		else if (!strcmp(cep->ce_varname, "recvq"))
			class->recvq = atol(cep->ce_vardata);
		else if (!strcmp(cep->ce_varname, "options"))
		{
			for (cep2 = cep->ce_entries; cep2; cep2 = cep2->ce_next)
				if (!strcmp(cep2->ce_varname, "nofakelag"))
					class->options |= CLASS_OPT_NOFAKELAG;
		}
	}
	if (isnew)
		AddListItem(class, conf_class);
	return 1;
}

int	_test_class(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry 	*cep, *cep2;
	int		errors = 0;
	char has_pingfreq = 0, has_connfreq = 0, has_maxclients = 0, has_sendq = 0;
	char has_recvq = 0;

	if (!ce->ce_vardata)
	{
		config_error_noname(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "class");
		errors++;
	}
	if (!strcasecmp(ce->ce_vardata, "default"))
	{
		config_error("%s:%d: Class cannot be named 'default', this class name is reserved for internal use.",
			ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
		errors++;
	}
	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!strcmp(cep->ce_varname, "options"))
		{
			for (cep2 = cep->ce_entries; cep2; cep2 = cep2->ce_next)
			{
#ifdef FAKELAG_CONFIGURABLE
				if (!strcmp(cep2->ce_varname, "nofakelag"))
					;
				else
#endif
				{
					config_error("%s:%d: Unknown option '%s' in class::options",
						cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, cep2->ce_varname);
					errors++;
				}
			}
		}
		else if (config_is_blankorempty(cep, "class"))
		{
			errors++;
			continue;
		}
		/* class::pingfreq */
		else if (!strcmp(cep->ce_varname, "pingfreq"))
		{
			int v = atol(cep->ce_vardata);
			if (has_pingfreq)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "class::pingfreq");
				continue;
			}
			has_pingfreq = 1;
			if ((v < 30) || (v > 600))
			{
				config_error("%s:%i: class::pingfreq should be a reasonable value (30-600)",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
				errors++;
				continue;
			}
		} 
		/* class::maxclients */
		else if (!strcmp(cep->ce_varname, "maxclients"))
		{
			long l;
			if (has_maxclients)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "class::maxclients");
				continue;
			}
			has_maxclients = 1;
			l = atol(cep->ce_vardata);
			if ((l < 1) || (l > 1000000))
			{
				config_error("%s:%i: class::maxclients with illegal value",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
				errors++;
			}
		}
		/* class::connfreq */
		else if (!strcmp(cep->ce_varname, "connfreq"))
		{
			long l;
			if (has_connfreq)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "class::connfreq");
				continue;
			}
			has_connfreq = 1;
			l = atol(cep->ce_vardata);
			if ((l < 10) || (l > 604800))
			{
				config_error("%s:%i: class::connfreq with illegal value (must be >10 and <7d)",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
				errors++;
			}
		}
		/* class::sendq */
		else if (!strcmp(cep->ce_varname, "sendq"))
		{
			long l;
			if (has_sendq)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "class::sendq");
				continue;
			}
			has_sendq = 1;
			l = atol(cep->ce_vardata);
			if ((l <= 0) || (l > 2000000000))
			{
				config_error("%s:%i: class::sendq with illegal value",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
				errors++;
			}
		}
		/* class::recvq */
		else if (!strcmp(cep->ce_varname, "recvq"))
		{
			long l;
			if (has_recvq)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "class::recvq");
				continue;
			}
			has_recvq = 1;
			l = atol(cep->ce_vardata);
			if ((l < 512) || (l > 32768))
			{
				config_error("%s:%i: class::recvq with illegal value (must be >512 and <32k)",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
				errors++;
			}
		}
		/* Unknown */
		else
		{
			config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
				"class", cep->ce_varname);
			errors++;
			continue;
		}
	}
	if (!has_pingfreq)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"class::pingfreq");
		errors++;
	}
	if (!has_maxclients)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"class::maxclients");
		errors++;
	}
	if (!has_sendq)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"class::sendq");
		errors++;
	}
	
	return errors;
}

int     _conf_drpass(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry *cep;

	if (!conf_drpass) 
	{
		conf_drpass =  MyMallocEx(sizeof(ConfigItem_drpass));
	}

	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!strcmp(cep->ce_varname, "restart"))
		{
			if (conf_drpass->restartauth)
				Auth_DeleteAuthStruct(conf_drpass->restartauth);
			
			conf_drpass->restartauth = Auth_ConvertConf2AuthStruct(cep);
		}
		else if (!strcmp(cep->ce_varname, "die"))
		{
			if (conf_drpass->dieauth)
				Auth_DeleteAuthStruct(conf_drpass->dieauth);
			
			conf_drpass->dieauth = Auth_ConvertConf2AuthStruct(cep);
		}
	}
	return 1;
}

int     _test_drpass(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry *cep;
	int errors = 0;
	char has_restart = 0, has_die = 0;

	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (config_is_blankorempty(cep, "drpass"))
		{
			errors++;
			continue;
		}
		/* drpass::restart */
		if (!strcmp(cep->ce_varname, "restart"))
		{
			if (has_restart)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "drpass::restart");
				continue;
			}
			has_restart = 1;
			if (Auth_CheckError(cep) < 0)
				errors++;
			continue;
		}
		/* drpass::die */
		else if (!strcmp(cep->ce_varname, "die"))
		{
			if (has_die)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "drpass::die");
				continue;
			}
			has_die = 1;
			if (Auth_CheckError(cep) < 0)
				errors++;
			continue;
		}
		/* Unknown */
		else
		{
			config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
				"drpass", cep->ce_varname);
			errors++;
			continue;
		}
	}
	return errors;
}

/*
 * The ulines {} block parser
*/
int	_conf_ulines(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry *cep;
	ConfigItem_ulines *ca;

	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		ca = MyMallocEx(sizeof(ConfigItem_ulines));
		ircstrdup(ca->servername, cep->ce_varname);
		AddListItem(ca, conf_ulines);
	}
	return 1;
}

int	_test_ulines(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry *cep;
	int 	    errors = 0;
	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!cep->ce_varname)
		{
			config_error_blank(cep->ce_fileptr->cf_filename,
				cep->ce_varlinenum, "ulines");
			errors++;
			continue;
		}
	}
	return errors;
}

int     _conf_tld(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry *cep;
	ConfigItem_tld *ca;

	ca = MyMallocEx(sizeof(ConfigItem_tld));

	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!strcmp(cep->ce_varname, "mask"))
			ca->mask = strdup(cep->ce_vardata);
		else if (!strcmp(cep->ce_varname, "motd"))
		{
			ca->motd_file = strdup(cep->ce_vardata);
			read_motd(cep->ce_vardata, &ca->motd);
		}
		else if (!strcmp(cep->ce_varname, "shortmotd"))
		{
			ca->smotd_file = strdup(cep->ce_vardata);
			read_motd(cep->ce_vardata, &ca->smotd);
		}
		else if (!strcmp(cep->ce_varname, "opermotd"))
		{
			ca->opermotd_file = strdup(cep->ce_vardata);
			read_motd(cep->ce_vardata, &ca->opermotd);
		}
		else if (!strcmp(cep->ce_varname, "botmotd"))
		{
			ca->botmotd_file = strdup(cep->ce_vardata);
			read_motd(cep->ce_vardata, &ca->botmotd);
		}
		else if (!strcmp(cep->ce_varname, "rules"))
		{
			ca->rules_file = strdup(cep->ce_vardata);
			read_motd(cep->ce_vardata, &ca->rules);
		}
		else if (!strcmp(cep->ce_varname, "options"))
		{
			ConfigEntry *cepp;
			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next)
			{
				if (!strcmp(cepp->ce_varname, "ssl"))
					ca->options |= TLD_SSL;
				else if (!strcmp(cepp->ce_varname, "remote"))
					ca->options |= TLD_REMOTE;
			}
		}
		else if (!strcmp(cep->ce_varname, "channel"))
			ca->channel = strdup(cep->ce_vardata);
	}
	AddListItem(ca, conf_tld);
	return 1;
}

int     _test_tld(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry *cep;
	int	    errors = 0;
	int	    fd = -1;
	char has_mask = 0, has_motd = 0, has_rules = 0, has_shortmotd = 0, has_channel = 0;
	char has_opermotd = 0, has_botmotd = 0, has_options = 0;

        for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!cep->ce_varname)
		{
			config_error_blank(cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
				"tld");
			errors++;
			continue;
		}
		if (!cep->ce_vardata && strcmp(cep->ce_varname, "options"))
		{
			config_error_empty(cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
				"tld", cep->ce_varname);
			errors++;
			continue;
		}
		/* tld::mask */
		if (!strcmp(cep->ce_varname, "mask")) 
		{
			if (has_mask)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "tld::mask");
				continue;
			}
			has_mask = 1;
		}
		/* tld::motd */
		else if (!strcmp(cep->ce_varname, "motd")) 
		{
			if (has_motd)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "tld::motd");
				continue;
			}
			has_motd = 1;
			if (((fd = open(cep->ce_vardata, O_RDONLY)) == -1))
			{
				config_error("%s:%i: tld::motd: %s: %s",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
					cep->ce_vardata, strerror(errno));
				errors++;
			}
			else
				close(fd);
		}
		/* tld::rules */
		else if (!strcmp(cep->ce_varname, "rules"))
		{
			if (has_rules)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "tld::rules");
				continue;
			}
			has_rules = 1;
			if (((fd = open(cep->ce_vardata, O_RDONLY)) == -1))
			{
				config_error("%s:%i: tld::rules: %s: %s",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
					cep->ce_vardata, strerror(errno));
				errors++;
			}
			else
				close(fd);
		}
		/* tld::channel */
		else if (!strcmp(cep->ce_varname, "channel"))
		{
			if (has_channel)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "tld::channel");
				continue;
			}
			has_channel = 1;
		}
		/* tld::shortmotd */
		else if (!strcmp(cep->ce_varname, "shortmotd"))
		{
			if (has_shortmotd)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "tld::shortmotd");
				continue;
			}
			has_shortmotd = 1;
			if (((fd = open(cep->ce_vardata, O_RDONLY)) == -1))
			{
				config_error("%s:%i: tld::shortmotd: %s: %s",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
					cep->ce_vardata, strerror(errno));
				errors++;
			}
			else
				close(fd);
		}
		/* tld::opermotd */
		else if (!strcmp(cep->ce_varname, "opermotd"))
		{
			if (has_opermotd)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "tld::opermotd");
				continue;
			}
			has_opermotd = 1;
			if (((fd = open(cep->ce_vardata, O_RDONLY)) == -1))
			{
				config_error("%s:%i: tld::opermotd: %s: %s",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
					cep->ce_vardata, strerror(errno));
				errors++;
			}
			else
				close(fd);
		}
		/* tld::botmotd */
		else if (!strcmp(cep->ce_varname, "botmotd"))
		{
			if (has_botmotd)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "tld::botmotd");
				continue;
			}
			has_botmotd = 1;
			if (((fd = open(cep->ce_vardata, O_RDONLY)) == -1))
			{
				config_error("%s:%i: tld::botmotd: %s: %s",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
					cep->ce_vardata, strerror(errno));
				errors++;
			}
			else
				close(fd);
		}
		/* tld::options */
		else if (!strcmp(cep->ce_varname, "options")) {
			ConfigEntry *cep2;

			if (has_options)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "tld::options");
				continue;
			}
			has_options = 1;

			for (cep2 = cep->ce_entries; cep2; cep2 = cep2->ce_next)
			{
				if (!cep2->ce_varname)
				{
					config_error_blank(cep2->ce_fileptr->cf_filename,
						cep2->ce_varlinenum, "tld::options");
					continue;
				}
				if (strcmp(cep2->ce_varname, "ssl") && 
					strcmp(cep2->ce_varname, "remote")) 
				{
					config_error_unknownopt(cep2->ce_fileptr->cf_filename,
						cep2->ce_varlinenum, "tld", cep2->ce_varname);
					errors++;
				}
			}
		}
		else
		{
			config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
				"tld", cep->ce_varname);
			errors++;
			continue;
		}
	}
	if (!has_mask)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"tld::mask");
		errors++;
	}
	if (!has_motd)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"tld::motd");
		errors++;
	}
	if (!has_rules)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"tld::rules");
		errors++;
	}
	return errors;
}

int	_conf_listen(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry *cep;
	ConfigEntry *cepp;
	ConfigItem_listen *listen = NULL;
	OperFlag    *ofp;
	char	    copy[256];
	char	    *ip;
	char	    *port;
	int	    start, end, iport, isnew;
	int tmpflags =0;

	strcpy(copy, ce->ce_vardata);
	/* Seriously cheap hack to make listen <port> work -Stskeeps */
	ipport_seperate(copy, &ip, &port);
	if (!ip || !*ip)
	{
		return -1;
	}
	if (strchr(ip, '*') && strcmp(ip, "*"))
	{
		return -1;
	}
	if (!port || !*port)
	{
		return -1;
	}
	port_range(port, &start, &end);
	if ((start < 0) || (start > 65535) || (end < 0) || (end > 65535))
	{
		return -1;
	}
	end++;
	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!strcmp(cep->ce_varname, "options"))
		{
			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next)
			{
				if ((ofp = config_binary_flags_search(_ListenerFlags, cepp->ce_varname, ARRAY_SIZEOF(_ListenerFlags))))
					tmpflags |= ofp->flag;
			}
		}
	}
#ifndef USE_SSL
	tmpflags &= ~LISTENER_SSL;
#endif
	for (iport = start; iport < end; iport++)
	{
		if (!(listen = Find_listen(ip, iport)))
		{
			listen = MyMallocEx(sizeof(ConfigItem_listen));
			listen->ip = strdup(ip);
			listen->port = iport;
			isnew = 1;
		} else
			isnew = 0;

		if (listen->options & LISTENER_BOUND)
			tmpflags |= LISTENER_BOUND;

		listen->options = tmpflags;
		if (isnew)
			AddListItem(listen, conf_listen);
		listen->flag.temporary = 0;
	}
	return 1;
}

int	_test_listen(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry *cep;
	ConfigEntry *cepp;
	char	    copy[256];
	char	    *ip;
	char	    *port;
	int	    start, end;
	int	    errors = 0;
	char has_options = 0;
	OperFlag    *ofp;

	if (!ce->ce_vardata)
	{
		config_error("%s:%i: listen without ip:port",
			ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
		return 1;
	}

	strcpy(copy, ce->ce_vardata);
	/* Seriously cheap hack to make listen <port> work -Stskeeps */
	ipport_seperate(copy, &ip, &port);
	if (!ip || !*ip)
	{
		config_error("%s:%i: listen: illegal ip:port mask",
			ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
		return 1;
	}
	if (strchr(ip, '*') && strcmp(ip, "*"))
	{
		config_error("%s:%i: listen: illegal ip, (mask, and not '*')",
			ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
		return 1;
	}
	if (!port || !*port)
	{
		config_error("%s:%i: listen: missing port in mask",
			ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
		return 1;
	}
#ifdef INET6
	if ((strlen(ip) > 6) && !strchr(ip, ':') && isdigit(ip[strlen(ip)-1]))
	{
		char crap[32];
		if (inet_pton(AF_INET, ip, crap) != 0)
		{
			char ipv6buf[128];
			snprintf(ipv6buf, sizeof(ipv6buf), "[::ffff:%s]:%s", ip, port);
			ce->ce_vardata = strdup(ipv6buf);
		} else {
		/* Insert IPv6 validation here */
			config_error("%s:%i: listen: '%s' looks like it might be IPv4, but is not a valid address.",
					ce->ce_fileptr->cf_filename, ce->ce_varlinenum, ip);
			return 1;
		}
	}
#endif
	port_range(port, &start, &end);
	if (start == end)
	{
		if ((start < 0) || (start > 65535))
		{
			config_error("%s:%i: listen: illegal port (must be 0..65535)",
				ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
			return 1;
		}
	}
	else 
	{
		if (end < start)
		{
			config_error("%s:%i: listen: illegal port range end value is less than starting value",
				ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
			return 1;
		}
		if (end - start >= 100)
		{
			config_error("%s:%i: listen: you requested port %d-%d, that's %d ports "
				"(and thus consumes %d sockets) this is probably not what you want.",
				ce->ce_fileptr->cf_filename, ce->ce_varlinenum, start, end,
				end - start + 1, end - start + 1);
			return 1;
		}
		if ((start < 0) || (start > 65535) || (end < 0) || (end > 65535))
		{
			config_error("%s:%i: listen: illegal port range values must be between 0 and 65535",
				ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
			return 1;
		}
	}
	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!cep->ce_varname)
		{
			config_error_blank(cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
				"listen");
			errors++;
			continue;
		}
		if (!strcmp(cep->ce_varname, "options"))
		{
			if (has_options)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "listen::options");
				continue;
			}
			has_options = 1;
			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next)
			{
				if (!cepp->ce_varname)
				{
					config_error_blank(cepp->ce_fileptr->cf_filename, 
						cepp->ce_varlinenum, "listen::options");
					errors++;
					continue;
				}
				if (!(ofp = config_binary_flags_search(_ListenerFlags, cepp->ce_varname, ARRAY_SIZEOF(_ListenerFlags))))
				{
					config_error_unknownopt(cepp->ce_fileptr->cf_filename, 
						cepp->ce_varlinenum, "class", cepp->ce_varname);
					errors++;
					continue;
				}
#ifndef USE_SSL
				else if (ofp->flag & LISTENER_SSL)
				{
					config_warn("%s:%i: listen with SSL flag enabled on a non SSL compile",
						cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
				}
#endif
			}
		}
		else
		{
			config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
				"listen", cep->ce_varname);
			errors++;
			continue;
		}

	}
	requiredstuff.conf_listen = 1;
	return errors;
}


int	_conf_allow(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry *cep, *cepp;
	ConfigItem_allow *allow;
	Hook *h;
	struct irc_netmask tmp;
	if (ce->ce_vardata)
	{
		if (!strcmp(ce->ce_vardata, "channel"))
			return (_conf_allow_channel(conf, ce));
		else if (!strcmp(ce->ce_vardata, "dcc"))
			return (_conf_allow_dcc(conf, ce));
		else
		{
			int value;
			for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next)
			{
				value = (*(h->func.intfunc))(conf,ce,CONFIG_ALLOW);
				if (value == 1)
					break;
			}
			return 0;
		}
	}
	allow = MyMallocEx(sizeof(ConfigItem_allow));
	
	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!strcmp(cep->ce_varname, "ip"))
		{
			allow->ip = strdup(cep->ce_vardata);
			/* CIDR */
			tmp.type = parse_netmask(allow->ip, &tmp);
			if (tmp.type != HM_HOST)
			{
				allow->netmask = MyMallocEx(sizeof(struct irc_netmask));
				bcopy(&tmp, allow->netmask, sizeof(struct irc_netmask));
			}
		}
		else if (!strcmp(cep->ce_varname, "hostname"))
			allow->hostname = strdup(cep->ce_vardata);
		else if (!strcmp(cep->ce_varname, "password"))
			allow->auth = Auth_ConvertConf2AuthStruct(cep);
		else if (!strcmp(cep->ce_varname, "class"))
		{
			allow->class = Find_class(cep->ce_vardata);
			if (!allow->class || (allow->class->flag.temporary == 1))
			{
				config_status("%s:%i: illegal allow::class, unknown class '%s' using default of class 'default'",
					cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum,
					cep->ce_vardata);
					allow->class = default_class;
			}
		}
		else if (!strcmp(cep->ce_varname, "maxperip"))
			allow->maxperip = atoi(cep->ce_vardata);
		else if (!strcmp(cep->ce_varname, "redirect-server"))
			allow->server = strdup(cep->ce_vardata);
		else if (!strcmp(cep->ce_varname, "redirect-port"))
			allow->port = atoi(cep->ce_vardata);
		else if (!strcmp(cep->ce_varname, "ipv6-clone-mask"))
		{
#ifdef INET6
			/*
			 * If this item isn't set explicitly by the
			 * user, the value will temporarily be
			 * zero. Defaults are applied in config_run().
			 */
			allow->ipv6_clone_mask = atoi(cep->ce_vardata);
#endif /* INET6 */
		}
		else if (!strcmp(cep->ce_varname, "options"))
		{
			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) 
			{
				if (!strcmp(cepp->ce_varname, "noident"))
					allow->flags.noident = 1;
				else if (!strcmp(cepp->ce_varname, "useip")) 
					allow->flags.useip = 1;
				else if (!strcmp(cepp->ce_varname, "ssl")) 
					allow->flags.ssl = 1;
				else if (!strcmp(cepp->ce_varname, "nopasscont")) 
					allow->flags.nopasscont = 1;
			}
		}
	}
	AddListItem(allow, conf_allow);
	return 1;
}

int	_test_allow(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry *cep, *cepp;
	int		errors = 0;
	Hook *h;
	char has_ip = 0, has_hostname = 0, has_maxperip = 0, has_password = 0, has_class = 0;
	char has_redirectserver = 0, has_redirectport = 0, has_options = 0;
	
	if (ce->ce_vardata)
	{
		if (!strcmp(ce->ce_vardata, "channel"))
			return (_test_allow_channel(conf, ce));
		else if (!strcmp(ce->ce_vardata, "dcc"))
			return (_test_allow_dcc(conf, ce));
		else
		{
			int used = 0;
			for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next) 
			{
				int value, errs = 0;
				if (h->owner && !(h->owner->flags & MODFLAG_TESTING)
				    && !(h->owner->options & MOD_OPT_PERM))
					continue;
				value = (*(h->func.intfunc))(conf,ce,CONFIG_ALLOW,&errs);
				if (value == 2)
					used = 1;
				if (value == 1)
				{
					used = 1;
					break;
				}
				if (value == -1)
				{
					used = 1;
					errors += errs;
					break;
				}
				if (value == -2)
				{
					used = 1;
					errors += errs;
				}
			}
			if (!used) {
				config_error("%s:%i: allow item with unknown type",
					ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
				return 1;
			}
			return errors;
		}
	}

	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (strcmp(cep->ce_varname, "options") && config_is_blankorempty(cep, "allow"))
		{
			errors++;
			continue;
		}
		if (!strcmp(cep->ce_varname, "ip"))
		{
			if (has_ip)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "allow::ip");
				continue;
			}
			has_ip = 1;
		} 
		else if (!strcmp(cep->ce_varname, "maxperip"))
		{
			int v = atoi(cep->ce_vardata);
			if (has_maxperip)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "allow::maxperip");
				continue;
			}
			has_maxperip = 1;
			if ((v <= 0) || (v > 65535))
			{
				config_error("%s:%i: allow::maxperip with illegal value (must be 1-65535)",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
				errors++;
			}
		}
		else if (!strcmp(cep->ce_varname, "ipv6-clone-mask"))
		{
			/* keep this in sync with _test_set() */
			int ipv6mask;
			ipv6mask = atoi(cep->ce_vardata);
			if (ipv6mask == 0)
			{
				config_error("%s:%d: allow::ipv6-clone-mask given a value of zero. This cannnot be correct, as it would treat all IPv6 hosts as one host.",
					     cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
				errors++;
			}
			if (ipv6mask > 128)
			{
				config_error("%s:%d: set::default-ipv6-clone-mask was set to %d. The maximum value is 128.",
					     cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
					     ipv6mask);
				errors++;
			}
			if (ipv6mask <= 32)
			{
				config_warn("%s:%d: allow::ipv6-clone-mask was given a very small value.",
					    cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
			}
		}
		else if (!strcmp(cep->ce_varname, "hostname"))
		{
			if (has_hostname)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "allow::hostname");
				continue;
			}
			has_hostname = 1;
		} 
		else if (!strcmp(cep->ce_varname, "password"))
		{
			if (has_password)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "allow::password");
				continue;
			}
			has_password = 1;
			/* some auth check stuff? */
			if (Auth_CheckError(cep) < 0)
				errors++;
		}
		else if (!strcmp(cep->ce_varname, "class"))
		{
			if (has_class)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "allow::class");
				continue;
			}
			has_class = 1;
		}
		else if (!strcmp(cep->ce_varname, "redirect-server"))
		{
			if (has_redirectserver)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "allow::redirect-server");
				continue;
			}
			has_redirectserver = 1;
		}
		else if (!strcmp(cep->ce_varname, "redirect-port"))
		{
			if (has_redirectport)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "allow::redirect-port");
				continue;
			}
			has_redirectport = 1;
		}
		else if (!strcmp(cep->ce_varname, "options"))
		{
			if (has_options)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "allow::options");
				continue;
			}
			has_options = 1;
			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next)
			{
				if (!strcmp(cepp->ce_varname, "noident"))
				{}
				else if (!strcmp(cepp->ce_varname, "useip")) 
				{}
				else if (!strcmp(cepp->ce_varname, "ssl")) 
				{}
				else if (!strcmp(cepp->ce_varname, "nopasscont")) 
				{}
				else
				{
					config_error_unknownopt(cepp->ce_fileptr->cf_filename,
						cepp->ce_varlinenum, "allow", cepp->ce_varname);
					errors++;
				}
			}
		}
		else
		{
			config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
				"allow", cep->ce_varname);
			errors++;
			continue;
		}
	}
	if (!has_ip)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"allow::ip");
		errors++;
	}
	if (!has_hostname)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"allow::hostname");
		errors++;
	}
	if (!has_class)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"allow::class");
		errors++;
	}
	return errors;
}

int	_conf_allow_channel(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigItem_allow_channel 	*allow = NULL;
	ConfigEntry 	    	*cep;
	char *class = NULL;
	
	/* First, search for ::class, if any */
	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!strcmp(cep->ce_varname, "class"))
			class = cep->ce_vardata;
	}

	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!strcmp(cep->ce_varname, "channel"))
		{
			/* This way, we permit multiple ::channel items in one allow block */
			allow = MyMallocEx(sizeof(ConfigItem_allow_channel));
			ircstrdup(allow->channel, cep->ce_vardata);
			if (class)
				ircstrdup(allow->class, class);
			AddListItem(allow, conf_allow_channel);
		}
	}
	return 1;
}

int	_test_allow_channel(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry		*cep;
	int			errors = 0;
	char			has_channel = 0, has_class = 0;
	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (config_is_blankorempty(cep, "allow channel"))
		{
			errors++;
			continue;
		}

		if (!strcmp(cep->ce_varname, "channel"))
		{
			has_channel = 1;
		}	
		else if (!strcmp(cep->ce_varname, "class"))
		{

			if (has_class)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "allow channel::class");
				continue;
			}
			has_class = 1;
		}
		else
		{
			config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
				"allow channel", cep->ce_varname);
			errors++;
		}
	}
	if (!has_channel)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"allow channel::channel");
		errors++;
	}
	return errors;
}

int	_conf_allow_dcc(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigItem_allow_dcc *allow = NULL;
	ConfigEntry *cep;

	allow = MyMallocEx(sizeof(ConfigItem_allow_dcc));
	
	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!strcmp(cep->ce_varname, "filename"))
			ircstrdup(allow->filename, cep->ce_vardata);
		else if (!strcmp(cep->ce_varname, "soft"))
		{
			int x = config_checkval(cep->ce_vardata,CFG_YESNO);
			if (x)
				allow->flag.type = DCCDENY_SOFT;
		}
	}
	AddListItem(allow, conf_allow_dcc);
	return 1;
}

int	_test_allow_dcc(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry *cep;
	int errors = 0, has_filename = 0, has_soft = 0;
	
	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (config_is_blankorempty(cep, "allow dcc"))
		{
			errors++;
			continue;
		}
		if (!strcmp(cep->ce_varname, "filename"))
		{
			if (has_filename)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "allow dcc::filename");
				continue;
			}				
			has_filename = 1;
		}
		else if (!strcmp(cep->ce_varname, "soft"))
		{
			if (has_soft)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "allow dcc::soft");
				continue;
			}
			has_soft = 1;
		}
		else
		{
			config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
				"allow dcc", cep->ce_varname);
			errors++;
		}
	}
	if (!has_filename)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"allow dcc::filename");
		errors++;
	}
	return errors;
}

void create_tkl_except_ii(char *mask, char *type)
{
	ConfigItem_except *ca;
	struct irc_netmask tmp;
	OperFlag *opf;
	ca = MyMallocEx(sizeof(ConfigItem_except));
	ca->mask = strdup(mask);
	
	opf = config_binary_flags_search(ExceptTklFlags, type, ARRAY_SIZEOF(ExceptTklFlags));
	ca->type = opf->flag;
	
	if (ca->type & TKL_KILL || ca->type & TKL_ZAP || ca->type & TKL_SHUN)
	{
		tmp.type = parse_netmask(ca->mask, &tmp);
		if (tmp.type != HM_HOST)
		{
			ca->netmask = MyMallocEx(sizeof(struct irc_netmask));
			bcopy(&tmp, ca->netmask, sizeof(struct irc_netmask));
		}
	}
	ca->flag.type = CONF_EXCEPT_TKL;
	AddListItem(ca, conf_except);
}

void create_tkl_except(char *mask, char *type)
{
	if (!strcmp(type, "all"))
	{
		/* Special treatment */
		int i;
		for (i = 0; i < ARRAY_SIZEOF(ExceptTklFlags); i++)
			if (ExceptTklFlags[i].flag)
				create_tkl_except_ii(mask, ExceptTklFlags[i].name);
	}
	else
		create_tkl_except_ii(mask, type);
}

int     _conf_except(ConfigFile *conf, ConfigEntry *ce)
{

	ConfigEntry *cep;
	ConfigItem_except *ca;
	Hook *h;
	struct irc_netmask tmp;

	if (!strcmp(ce->ce_vardata, "ban")) {
		for (cep = ce->ce_entries; cep; cep = cep->ce_next)
		{
			if (!strcmp(cep->ce_varname, "mask")) {
				ca = MyMallocEx(sizeof(ConfigItem_except));
				ca->mask = strdup(cep->ce_vardata);
				tmp.type = parse_netmask(ca->mask, &tmp);
				if (tmp.type != HM_HOST)
				{
					ca->netmask = MyMallocEx(sizeof(struct irc_netmask));
					bcopy(&tmp, ca->netmask, sizeof(struct irc_netmask));
				}
				ca->flag.type = CONF_EXCEPT_BAN;
				AddListItem(ca, conf_except);
			}
			else {
			}
		}
	}
#ifdef THROTTLING
	else if (!strcmp(ce->ce_vardata, "throttle")) {
		for (cep = ce->ce_entries; cep; cep = cep->ce_next)
		{
			if (!strcmp(cep->ce_varname, "mask")) {
				ca = MyMallocEx(sizeof(ConfigItem_except));
				ca->mask = strdup(cep->ce_vardata);
				tmp.type = parse_netmask(ca->mask, &tmp);
				if (tmp.type != HM_HOST)
				{
					ca->netmask = MyMallocEx(sizeof(struct irc_netmask));
					bcopy(&tmp, ca->netmask, sizeof(struct irc_netmask));
				}
				ca->flag.type = CONF_EXCEPT_THROTTLE;
				AddListItem(ca, conf_except);
			}
			else {
			}
		}

	}
#endif
	else if (!strcmp(ce->ce_vardata, "tkl")) {
		ConfigEntry *mask = NULL, *type = NULL;
		for (cep = ce->ce_entries; cep; cep = cep->ce_next)
		{
			if (!strcmp(cep->ce_varname, "mask"))
				mask = cep;
			else if (!strcmp(cep->ce_varname, "type"))
				type = cep;
		}
		if (type->ce_vardata)
			create_tkl_except(mask->ce_vardata, type->ce_vardata);
		else
		{
			ConfigEntry *cepp;
			for (cepp = type->ce_entries; cepp; cepp = cepp->ce_next)
				create_tkl_except(mask->ce_vardata, cepp->ce_varname);
		}
	}
	else {
		int value;
		for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next)
		{
			value = (*(h->func.intfunc))(conf,ce,CONFIG_EXCEPT);
			if (value == 1)
				break;
		}
	}
	return 1;
}

int     _test_except(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry *cep;
	int	    errors = 0;
	Hook *h;
	char has_mask = 0;

	if (!ce->ce_vardata)
	{
		config_error("%s:%i: except without type",
			ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
		return 1;
	}

	if (!strcmp(ce->ce_vardata, "ban")) 
	{
		for (cep = ce->ce_entries; cep; cep = cep->ce_next)
		{
			if (config_is_blankorempty(cep, "except ban"))
			{
				errors++;
				continue;
			}
			if (!strcmp(cep->ce_varname, "mask"))
			{
				if (has_mask)
				{
					config_warn_duplicate(cep->ce_fileptr->cf_filename, 
						cep->ce_varlinenum, "except ban::mask");
					continue;
				}
				has_mask = 1;
			}
			else
			{
				config_error_unknown(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "except ban", cep->ce_varname);
				errors++;
				continue;
			}
		}
		if (!has_mask)
		{
			config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
				"except ban::mask");
			errors++;
		}
		return errors;
	}
#ifdef THROTTLING
	else if (!strcmp(ce->ce_vardata, "throttle")) {
		for (cep = ce->ce_entries; cep; cep = cep->ce_next)
		{
			if (config_is_blankorempty(cep, "except throttle"))
			{
				errors++;
				continue;
			}
			if (!strcmp(cep->ce_varname, "mask"))
			{
				if (has_mask)
				{
					config_warn_duplicate(cep->ce_fileptr->cf_filename, 
						cep->ce_varlinenum, "except throttle::mask");
					continue;
				}
				has_mask = 1;
			}
			else
			{
				config_error_unknown(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "except throttle", cep->ce_varname);
				errors++;
				continue;
			}
		}
		if (!has_mask)
		{
			config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
				"except throttle::mask");
			errors++;
		}
		return errors;
	}
#endif
	else if (!strcmp(ce->ce_vardata, "tkl")) {
		char has_type = 0;

		for (cep = ce->ce_entries; cep; cep = cep->ce_next)
		{
			if (!cep->ce_varname)
			{
				config_error_blank(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "except tkl");
				errors++;
				continue;
			}
			if (!strcmp(cep->ce_varname, "mask"))
			{
				if (!cep->ce_vardata)
				{
					config_error_empty(cep->ce_fileptr->cf_filename,
						cep->ce_varlinenum, "except tkl", "mask");
					errors++;
					continue;
				}
				if (has_mask)
				{
					config_warn_duplicate(cep->ce_fileptr->cf_filename,
						cep->ce_varlinenum, "except tkl::mask");
					continue;
				}
				has_mask = 1;
			}
			else if (!strcmp(cep->ce_varname, "type"))
			{
				if (has_type)
				{
					config_warn_duplicate(cep->ce_fileptr->cf_filename,
						cep->ce_varlinenum, "except tkl::type");
					continue;
				}					
				if (cep->ce_vardata)
				{
					if (!strcmp(cep->ce_vardata, "tkline") || 
					    !strcmp(cep->ce_vardata, "tzline"))
					{
						config_error("%s:%i: except tkl of type %s is"
							     " deprecated. Use except ban {}"
							     " instead", 
							     cep->ce_fileptr->cf_filename,
							     cep->ce_varlinenum, 
							     cep->ce_vardata);
						errors++;
					}
					if (!config_binary_flags_search(ExceptTklFlags, 
					     cep->ce_vardata, ARRAY_SIZEOF(ExceptTklFlags)))
					{
						config_error("%s:%i: unknown except tkl type %s",
							     cep->ce_fileptr->cf_filename, 
							     cep->ce_varlinenum,
							     cep->ce_vardata);
						return 1;
					}
				}
				else if (cep->ce_entries)
				{
					ConfigEntry *cepp;
					for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next)
					{
						if (!strcmp(cepp->ce_varname, "tkline") || 
						    !strcmp(cepp->ce_varname, "tzline"))
						{
							config_error("%s:%i: except tkl of type %s is"
								     " deprecated. Use except ban {}"
								     " instead", 
								     cepp->ce_fileptr->cf_filename,
								     cepp->ce_varlinenum, 
								     cepp->ce_varname);
							errors++;
						}
						if (!config_binary_flags_search(ExceptTklFlags, 
						     cepp->ce_varname, ARRAY_SIZEOF(ExceptTklFlags)))
						{
							config_error("%s:%i: unknown except tkl type %s",
								     cepp->ce_fileptr->cf_filename, 
								     cepp->ce_varlinenum,
								     cepp->ce_varname);
							return 1;
						}
					}
				}
				else
				{
					config_error_empty(cep->ce_fileptr->cf_filename, 
						cep->ce_varlinenum, "except tkl", "type");
					errors++;
					continue;
				}
				has_type = 1;
			}
			else
			{
				config_error_unknown(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "except tkl", cep->ce_varname);
				errors++;
				continue;
			}
		}
		if (!has_mask)
		{
			config_error("%s:%i: except tkl without mask item",
				ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
			return 1;
		}
		if (!has_type)
		{
			config_error("%s:%i: except tkl without type item",
				ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
			return 1;
		}
		return errors;
	}
	else {
		int used = 0;
		for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next) 
		{
			int value, errs = 0;
			if (h->owner && !(h->owner->flags & MODFLAG_TESTING)
			    && !(h->owner->options & MOD_OPT_PERM))
				continue;
			value = (*(h->func.intfunc))(conf,ce,CONFIG_EXCEPT,&errs);
			if (value == 2)
				used = 1;
			if (value == 1)
			{
				used = 1;
				break;
			}
			if (value == -1)
			{
				used = 1;
				errors += errs;
				break;
			}
			if (value == -2)
			{
				used = 1;
				errors += errs;
			}
		}
		if (!used) {
			config_error("%s:%i: unknown except type %s",
				ce->ce_fileptr->cf_filename, ce->ce_varlinenum, 
				ce->ce_vardata);
			return 1;
		}
	}
	return errors;
}

/*
 * vhost {} block parser
*/
int	_conf_vhost(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigItem_vhost *vhost;
	ConfigItem_oper_from *from;
	ConfigEntry *cep, *cepp;
	vhost = MyMallocEx(sizeof(ConfigItem_vhost));

	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!strcmp(cep->ce_varname, "vhost"))
		{
			char *user, *host;
			user = strtok(cep->ce_vardata, "@");
			host = strtok(NULL, "");
			if (!host)
				vhost->virthost = strdup(user);
			else 
			{
				vhost->virtuser = strdup(user);
				vhost->virthost = strdup(host);
			}
		}
		else if (!strcmp(cep->ce_varname, "login"))
			vhost->login = strdup(cep->ce_vardata);	
		else if (!strcmp(cep->ce_varname, "password"))
			vhost->auth = Auth_ConvertConf2AuthStruct(cep);
		else if (!strcmp(cep->ce_varname, "from"))
		{
			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next)
			{
				if (!strcmp(cepp->ce_varname, "userhost"))
				{
					from = MyMallocEx(sizeof(ConfigItem_oper_from));
					ircstrdup(from->name, cepp->ce_vardata);
					AddListItem(from, vhost->from);
				}
			}
		}
		else if (!strcmp(cep->ce_varname, "swhois"))
			vhost->swhois = strdup(cep->ce_vardata);
	}
	AddListItem(vhost, conf_vhost);
	return 1;
}

int	_test_vhost(ConfigFile *conf, ConfigEntry *ce)
{
	int errors = 0;
	ConfigEntry *cep;
	char has_vhost = 0, has_login = 0, has_password = 0, has_swhois = 0, has_from = 0;
	char has_userhost = 0;

	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!cep->ce_varname)
		{
			config_error_blank(cep->ce_fileptr->cf_filename,
				cep->ce_varlinenum, "vhost");
			errors++;
			continue;
		}
		if (!strcmp(cep->ce_varname, "vhost"))
		{
			char *at, *tmp, *host;
			if (has_vhost)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "vhost::vhost");
				continue;
			}
			has_vhost = 1;
			if (!cep->ce_vardata)
			{
				config_error_empty(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "vhost", "vhost");
				errors++;
				continue;
			}	
			if ((at = strchr(cep->ce_vardata, '@')))
			{
				for (tmp = cep->ce_vardata; tmp != at; tmp++)
				{
					if (*tmp == '~' && tmp == cep->ce_vardata)
						continue;
					if (!isallowed(*tmp))
						break;
				}
				if (tmp != at)
				{
					config_error("%s:%i: vhost::vhost contains an invalid ident",
						cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
					errors++;
				}
				host = at+1;
			}
			else
				host = cep->ce_vardata;
			if (!*host)
			{
				config_error("%s:%i: vhost::vhost does not have a host set",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
				errors++;
			}
			else
			{
				if (!valid_host(host))
				{
					config_error("%s:%i: vhost::vhost contains an invalid host",
						cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
					errors++;
				}
			}
		}
		else if (!strcmp(cep->ce_varname, "login"))
		{
			if (has_login)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "vhost::login");
			}
			has_login = 1;
			if (!cep->ce_vardata)
			{
				config_error_empty(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "vhost", "login");
				errors++;
				continue;
			}	
		}
		else if (!strcmp(cep->ce_varname, "password"))
		{
			if (has_password)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "vhost::password");
			}
			has_password = 1;
			if (!cep->ce_vardata)
			{
				config_error_empty(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "vhost", "password");
				errors++;
				continue;
			}	
			if (Auth_CheckError(cep) < 0)
				errors++;
		}
		else if (!strcmp(cep->ce_varname, "from"))
		{
			ConfigEntry *cepp;

			if (has_from)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "vhost::from");
				continue;
			}
			has_from = 1;
			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next)
			{
				if (config_is_blankorempty(cepp, "vhost::from"))
				{
					errors++;
					continue;
				}
				if (!strcmp(cepp->ce_varname, "userhost"))
					has_userhost = 1;
				else
				{
					config_error_unknown(cepp->ce_fileptr->cf_filename,
						cepp->ce_varlinenum, "vhost::from",
						cepp->ce_varname);
					errors++;
					continue;	
				}
			}
		}
		else if (!strcmp(cep->ce_varname, "swhois"))
		{
			if (has_swhois)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "vhost::swhois");
				continue;
			}
			has_swhois = 1;
		}
		else
		{
			config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
				"vhost", cep->ce_varname);
			errors++;
		}
	}
	if (!has_vhost)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"vhost::vhost");
		errors++;
	}
	if (!has_login)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"vhost::login");
		errors++;
		
	}
	if (!has_password)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"vhost::password");
		errors++;
	}
	if (!has_from)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"vhost::from");
		errors++;
	}
	if (!has_userhost)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"vhost::userhost");
		errors++;
	}
	return errors;
}

#ifdef STRIPBADWORDS

static ConfigItem_badword *copy_badword_struct(ConfigItem_badword *ca, int regex, int regflags)
{
	ConfigItem_badword *x = MyMalloc(sizeof(ConfigItem_badword));
	memcpy(x, ca, sizeof(ConfigItem_badword));
	x->word = strdup(ca->word);
	if (ca->replace)
		x->replace = strdup(ca->replace);
	if (regex) 
	{
		memset(&x->expr, 0, sizeof(regex_t));
		regcomp(&x->expr, x->word, regflags);
	}
	return x;
}

int     _conf_badword(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry *cep, *word = NULL;
	ConfigItem_badword *ca;
	char *tmp;
	short regex = 0;
	int regflags = 0;
#ifdef FAST_BADWORD_REPLACE
	int ast_l = 0, ast_r = 0;
#endif

	ca = MyMallocEx(sizeof(ConfigItem_badword));
	ca->action = BADWORD_REPLACE;
	regflags = REG_ICASE|REG_EXTENDED;

	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!strcmp(cep->ce_varname, "action"))
		{
			if (!strcmp(cep->ce_vardata, "block"))
			{
				ca->action = BADWORD_BLOCK;
				/* If it is set to just block, then we don't need to worry about
				 * replacements 
				 */
				regflags |= REG_NOSUB;
			}
		}
		else if (!strcmp(cep->ce_varname, "replace"))
		{
			ircstrdup(ca->replace, cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "word"))
			word = cep;
	}
#ifdef FAST_BADWORD_REPLACE
	/* The fast badwords routine can do: "blah" "*blah" "blah*" and "*blah*",
	 * in all other cases use regex.
	 */
	for (tmp = word->ce_vardata; *tmp; tmp++) {
		if (!isalnum(*tmp) && !(*tmp >= 128)) {
			if ((word->ce_vardata == tmp) && (*tmp == '*')) {
				ast_l = 1; /* Asterisk at the left */
				continue;
			}
			if ((*(tmp + 1) == '\0') && (*tmp == '*')) {
				ast_r = 1; /* Asterisk at the right */
				continue;
			}
			regex = 1;
			break;
		}
	}
	if (regex) 
	{
		ca->type = BADW_TYPE_REGEX;
		ircstrdup(ca->word, word->ce_vardata);
		regcomp(&ca->expr, ca->word, regflags);
	}
	else
	{
		char *tmpw;
		ca->type = BADW_TYPE_FAST;
		ca->word = tmpw = MyMalloc(strlen(word->ce_vardata) - ast_l - ast_r + 1);
		/* Copy except for asterisks */
		for (tmp = word->ce_vardata; *tmp; tmp++)
			if (*tmp != '*')
				*tmpw++ = *tmp;
		*tmpw = '\0';
		if (ast_l)
			ca->type |= BADW_TYPE_FAST_L;
		if (ast_r)
			ca->type |= BADW_TYPE_FAST_R;
	}
#else
	for (tmp = word->ce_vardata; *tmp; tmp++)
	{
		if (!isalnum(*tmp) && !(*tmp >= 128))
		{
			regex = 1;
			break;
		}
	}
	if (regex)
	{
		ircstrdup(ca->word, word->ce_vardata);
	}
	else
	{
		ca->word = MyMalloc(strlen(word->ce_vardata) + strlen(PATTERN) -1);
		ircsprintf(ca->word, PATTERN, word->ce_vardata);
	}
	/* Yes this is called twice, once in test, and once here, but it is still MUCH
	   faster than calling it each time a message is received like before. -- codemastr
	 */
	regcomp(&ca->expr, ca->word, regflags);
#endif
	if (!strcmp(ce->ce_vardata, "channel"))
		AddListItem(ca, conf_badword_channel);
	else if (!strcmp(ce->ce_vardata, "message"))
		AddListItem(ca, conf_badword_message);
	else if (!strcmp(ce->ce_vardata, "quit"))
		AddListItem(ca, conf_badword_quit);
	else if (!strcmp(ce->ce_vardata, "all"))
	{
		AddListItem(ca, conf_badword_channel);
		AddListItem(copy_badword_struct(ca,regex,regflags), conf_badword_message);
		AddListItem(copy_badword_struct(ca,regex,regflags), conf_badword_quit);
	}
	return 1;
}

int _test_badword(ConfigFile *conf, ConfigEntry *ce) 
{ 
	int errors = 0;
	ConfigEntry *cep;
	char has_word = 0, has_replace = 0, has_action = 0, action = 'r';

	if (!ce->ce_vardata)
	{
		config_error("%s:%i: badword without type",
			ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
		return 1;
	}
	else if (strcmp(ce->ce_vardata, "channel") && strcmp(ce->ce_vardata, "message") && 
	         strcmp(ce->ce_vardata, "quit") && strcmp(ce->ce_vardata, "all")) {
			config_error("%s:%i: badword with unknown type",
				ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
		return 1;
	}
	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (config_is_blankorempty(cep, "badword"))
		{
			errors++;
			continue;
		}
		if (!strcmp(cep->ce_varname, "word"))
		{
			char *errbuf;
			if (has_word)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "badword::word");
				continue;
			}
			has_word = 1;
			if ((errbuf = unreal_checkregex(cep->ce_vardata,1,1)))
			{
				config_error("%s:%i: badword::%s contains an invalid regex: %s",
					cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum,
					cep->ce_varname, errbuf);
				errors++;
			}
		}
		else if (!strcmp(cep->ce_varname, "replace"))
		{
			if (has_replace)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "badword::replace");
				continue;
			}
			has_replace = 1;
		}
		else if (!strcmp(cep->ce_varname, "action"))
		{
			if (has_action)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "badword::action");
				continue;
			}
			has_action = 1;
			if (!strcmp(cep->ce_vardata, "replace"))
				action = 'r';
			else if (!strcmp(cep->ce_vardata, "block"))
				action = 'b';
			else
			{
				config_error("%s:%d: Unknown badword::action '%s'",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
					cep->ce_vardata);
				errors++;
			}
				
		}
		else
		{
			config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
				"badword", cep->ce_varname);
			errors++;
		}
	}

	if (!has_word)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"badword::word");
		errors++;
	}
	if (has_action)
	{
		if (has_replace && action == 'b')
		{
			config_error("%s:%i: badword::action is block but badword::replace exists",
				ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
			errors++;
		}
	}
	return errors; 
}
#endif

int _conf_spamfilter(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry *cep;
	ConfigEntry *cepp;
	aTKline *nl = MyMallocEx(sizeof(aTKline));
	char *word = NULL, *reason = NULL, *bantime = NULL;
	int action = 0, target = 0;
	char has_reason = 0, has_bantime = 0;
	
	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!strcmp(cep->ce_varname, "regex"))
		{
			nl->reason = strdup(cep->ce_vardata);

			word = cep->ce_vardata;
		}
		else if (!strcmp(cep->ce_varname, "target"))
		{
			if (cep->ce_vardata)
				target = spamfilter_getconftargets(cep->ce_vardata);
			else
			{
				for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next)
					target |= spamfilter_getconftargets(cepp->ce_varname);
			}
		}
		else if (!strcmp(cep->ce_varname, "action"))
		{
			action = banact_stringtoval(cep->ce_vardata);
			nl->hostmask = strdup(cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "reason"))
		{
			has_reason = 1;
			reason = cep->ce_vardata;
		}
		else if (!strcmp(cep->ce_varname, "ban-time"))
		{
			has_bantime = 1;
			bantime = cep->ce_vardata;
		}
	}
	nl->type = TKL_SPAMF;
	nl->expire_at = 0;
	nl->set_at = TStime();

	strncpyzt(nl->usermask, spamfilter_target_inttostring(target), sizeof(nl->usermask));
	nl->subtype = target;

	nl->setby = BadPtr(me.name) ? NULL : strdup(me.name); /* Hmm! */
	nl->ptr.spamf = unreal_buildspamfilter(word);
	nl->ptr.spamf->action = action;

	if (has_reason && reason)
		nl->ptr.spamf->tkl_reason = strdup(unreal_encodespace(reason));
	else
		nl->ptr.spamf->tkl_reason = strdup("<internally added by ircd>");

	if (has_bantime)
		nl->ptr.spamf->tkl_duration = config_checkval(bantime, CFG_TIME);
	else
		nl->ptr.spamf->tkl_duration = (SPAMFILTER_BAN_TIME ? SPAMFILTER_BAN_TIME : 86400);
		
	AddListItem(nl, tklines[tkl_hash('f')]);
	return 1;
}

int _test_spamfilter(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry *cep, *cepp;
	int errors = 0;
	char *regex = NULL, *reason = NULL;
	char has_target = 0, has_regex = 0, has_action = 0, has_reason = 0, has_bantime = 0;
	
	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!cep->ce_varname)
		{
			config_error_blank(cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
				"spamfilter");
			errors++;
			continue;
		}
		if (!strcmp(cep->ce_varname, "target"))
		{
			if (has_target)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "spamfilter::target");
				continue;
			}
			has_target = 1;
			if (cep->ce_vardata)
			{
				if (!spamfilter_getconftargets(cep->ce_vardata))
				{
					config_error("%s:%i: unknown spamfiler target type '%s'",
						cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata);
					errors++;
				}
			}
			else if (cep->ce_entries)
			{
				for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next)
				{
					if (!cepp->ce_varname)
					{
						config_error_blank(cepp->ce_fileptr->cf_filename,
							cepp->ce_varlinenum, 
							"spamfilter::target");
						errors++;
						continue;
					}
					if (!spamfilter_getconftargets(cepp->ce_varname))
					{
						config_error("%s:%i: unknown spamfiler target type '%s'",
							cepp->ce_fileptr->cf_filename,
							cepp->ce_varlinenum, cepp->ce_varname);
						errors++;
					}
				}
			}
			else
			{
				config_error_empty(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "spamfilter", cep->ce_varname);
				errors++;
			}
			continue;
		}
		if (!cep->ce_vardata)
		{
			config_error_empty(cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
				"spamfilter", cep->ce_varname);
			errors++;
			continue;
		}
		if (!strcmp(cep->ce_varname, "reason"))
		{
			if (has_reason)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "spamfilter::reason");
				continue;
			}
			has_reason = 1;
			reason = cep->ce_vardata;
		}
		else if (!strcmp(cep->ce_varname, "regex"))
		{
			char *errbuf;
			if (has_regex)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "spamfilter::regex");
				continue;
			}
			has_regex = 1;
			if ((errbuf = unreal_checkregex(cep->ce_vardata,0,0)))
			{
				config_error("%s:%i: spamfilter::regex contains an invalid regex: %s",
					cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum,
					errbuf);
				errors++;
				continue;
			}
			regex = cep->ce_vardata;
		}
		else if (!strcmp(cep->ce_varname, "action"))
		{
			if (has_action)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "spamfilter::action");
				continue;
			}
			has_action = 1;
			if (!banact_stringtoval(cep->ce_vardata))
			{
				config_error("%s:%i: spamfilter::action has unknown action type '%s'",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata);
				errors++;
			}
		}
		else if (!strcmp(cep->ce_varname, "ban-time"))
		{
			if (has_bantime)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "spamfilter::ban-time");
				continue;
			}
			has_bantime = 1;
		}
		else
		{
			config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
				"spamfilter", cep->ce_varname);
			errors++;
			continue;
		}
	}

	if (!has_regex)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"spamfilter::regex");
		errors++;
	} 
	if (!has_target)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"spamfilter::target");
		errors++;
	}
	if (!has_action)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"spamfilter::action");
		errors++;
	}
	if (regex && reason && (strlen(regex) + strlen(reason) > 505))
	{
		config_error("%s:%i: spamfilter block problem: regex + reason field are together over 505 bytes, "
		             "please choose a shorter regex or reason",
		             ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
		errors++;
	}

	return errors;
}

int     _conf_help(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry *cep;
	ConfigItem_help *ca;
	aMotdLine *last = NULL, *temp;
	ca = MyMallocEx(sizeof(ConfigItem_help));

	if (!ce->ce_vardata)
		ca->command = NULL;
	else
		ca->command = strdup(ce->ce_vardata);

	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		temp = MyMalloc(sizeof(aMotdLine));
		temp->line = strdup(cep->ce_varname);
		temp->next = NULL;
		if (!ca->text)
			ca->text = temp;
		else
			last->next = temp;
		last = temp;
	}
	AddListItem(ca, conf_help);
	return 1;

}

int _test_help(ConfigFile *conf, ConfigEntry *ce) { 
	int errors = 0;
	ConfigEntry *cep;
	if (!ce->ce_entries)
	{
		config_error("%s:%i: empty help block", 
			ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
		return 1;
	}
	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!cep->ce_varname)
		{
			config_error_blank(cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
				"help");
			errors++;
			continue;
		}
	}
	return errors; 
}

int     _conf_log(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry *cep, *cepp;
	ConfigItem_log *ca;
	OperFlag *ofp = NULL;

	ca = MyMallocEx(sizeof(ConfigItem_log));
	ircstrdup(ca->file, ce->ce_vardata);

	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!strcmp(cep->ce_varname, "maxsize")) 
		{
			ca->maxsize = config_checkval(cep->ce_vardata,CFG_SIZE);
		}
		else if (!strcmp(cep->ce_varname, "flags")) 
		{
			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next)
			{
				if ((ofp = config_binary_flags_search(_LogFlags, cepp->ce_varname, ARRAY_SIZEOF(_LogFlags)))) 
					ca->flags |= ofp->flag;
			}
		}
	}
	AddListItem(ca, conf_log);
	return 1;

}

int _test_log(ConfigFile *conf, ConfigEntry *ce) { 
	int errors = 0;
	ConfigEntry *cep, *cepp;
	char has_flags = 0, has_maxsize = 0;

	if (!ce->ce_vardata)
	{
		config_error("%s:%i: log block without filename", 
			ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
		return 1;
	}
	if (!ce->ce_entries)
	{
		config_error("%s:%i: empty log block", 
			ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
		return 1;
	}
	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!cep->ce_varname)
		{
			config_error_blank(cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
				"log");
			errors++;
			continue;
		}
		if (!strcmp(cep->ce_varname, "flags")) 
		{
			if (has_flags)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "log::flags");
				continue;
			}
			has_flags = 1;
			if (!cep->ce_entries)
			{
				config_error_empty(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "log", cep->ce_varname);
				errors++;
				continue;
			}
			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next)
			{
				if (!cepp->ce_varname)
				{
					config_error_blank(cepp->ce_fileptr->cf_filename, 
						cepp->ce_varlinenum, "log::flags");
					errors++;
					continue;
				}
				if (!config_binary_flags_search(_LogFlags, cepp->ce_varname, ARRAY_SIZEOF(_LogFlags))) 
				{
					config_error_unknownflag(cepp->ce_fileptr->cf_filename,
						cepp->ce_varlinenum, "log", cepp->ce_varname);
					errors++; 
				}
			}
		}
		else if (!strcmp(cep->ce_varname, "maxsize")) 
		{
			if (has_maxsize)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "log::maxsize");
				continue;
			}
			has_maxsize = 1;
			if (!cep->ce_vardata)
			{
				config_error_empty(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "log", cep->ce_varname);
				errors++;
			}
		}
		else 
		{
			config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
				"log", cep->ce_varname);
			errors++;
			continue;
		}
	}
	if (!has_flags)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"log::flags");
		errors++;
	}
	return errors; 
}


int	_conf_link(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry *cep;
	ConfigEntry *cepp;
	ConfigItem_link *link = NULL;
	OperFlag    *ofp;

	link = (ConfigItem_link *) MyMallocEx(sizeof(ConfigItem_link));
	link->servername = strdup(ce->ce_vardata);
	/* ugly, but it works. if it fails, we know _test_link failed miserably */
	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!strcmp(cep->ce_varname, "username"))
			link->username = strdup(cep->ce_vardata);
		else if (!strcmp(cep->ce_varname, "hostname"))
			link->hostname = strdup(cep->ce_vardata);
		else if (!strcmp(cep->ce_varname, "bind-ip"))
			link->bindip = strdup(cep->ce_vardata);
		else if (!strcmp(cep->ce_varname, "port"))
			link->port = atol(cep->ce_vardata);
		else if (!strcmp(cep->ce_varname, "password-receive"))
			link->recvauth = Auth_ConvertConf2AuthStruct(cep);
		else if (!strcmp(cep->ce_varname, "password-connect"))
			link->connpwd = strdup(cep->ce_vardata);
		else if (!strcmp(cep->ce_varname, "class"))
		{
			link->class = Find_class(cep->ce_vardata);
			if (!link->class || (link->class->flag.temporary == 1))
			{
				config_status("%s:%i: illegal link::class, unknown class '%s' using default of class 'default'",
					cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum,
					cep->ce_vardata);
				link->class = default_class;
			}
			link->class->xrefcount++;
		}
		else if (!strcmp(cep->ce_varname, "options"))
		{
			link->options = 0;
			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next)
			{
				if ((ofp = config_binary_flags_search(_LinkFlags, cepp->ce_varname, ARRAY_SIZEOF(_LinkFlags)))) 
					link->options |= ofp->flag;
			}
		}
		else if (!strcmp(cep->ce_varname, "hub"))
			link->hubmask = strdup(cep->ce_vardata);
		else if (!strcmp(cep->ce_varname, "leaf"))
			link->leafmask = strdup(cep->ce_vardata);
		else if (!strcmp(cep->ce_varname, "leafdepth"))
			link->leafdepth = atol(cep->ce_vardata);
#ifdef USE_SSL
		else if (!strcmp(cep->ce_varname, "ciphers"))
			link->ciphers = strdup(cep->ce_vardata);
#endif
#ifdef ZIP_LINKS
		else if (!strcmp(cep->ce_varname, "compression-level"))
			link->compression_level = atoi(cep->ce_vardata);
#endif
	}
	AddListItem(link, conf_link);
	return 0;
}

int	_test_link(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry	*cep, *cepp;
	OperFlag 	*ofp;
	int		errors = 0;
	char has_username = 0, has_hostname = 0, has_bindip = 0, has_port = 0;
	char has_passwordreceive = 0, has_passwordconnect = 0, has_class = 0;
	char has_hub = 0, has_leaf = 0, has_leafdepth = 0, has_ciphers = 0;
	char has_options = 0;
	char has_autoconnect = 0;
	char has_hostname_wildcards = 0;
#ifdef ZIP_LINKS
	char has_compressionlevel = 0;
#endif
	if (!ce->ce_vardata)
	{
		config_error("%s:%i: link without servername",
			ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
		return 1;

	}
	if (!strchr(ce->ce_vardata, '.'))
	{
		config_error("%s:%i: link: bogus server name",
			ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
		return 1;
	}
	
	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!cep->ce_varname)
		{
			config_error_blank(cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
				"link");
			errors++;
			continue;
		}
		if (!strcmp(cep->ce_varname, "options"))
		{
			if (has_options)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "link::options");
				continue;
			}
			has_options = 1;
			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next)
			{
				if (!cepp->ce_varname)
				{
					config_error_blank(cepp->ce_fileptr->cf_filename,
						cepp->ce_varlinenum, "link::options");
					errors++; 
					continue;
				}
				if (!(ofp = config_binary_flags_search(_LinkFlags, cepp->ce_varname, ARRAY_SIZEOF(_LinkFlags)))) 
				{
					config_error_unknownopt(cepp->ce_fileptr->cf_filename,
						cepp->ce_varlinenum, "link", cepp->ce_varname);
					errors++;
					continue;
				}
#ifndef USE_SSL
				if (ofp->flag == CONNECT_SSL)
				{
					config_error("%s:%i: link %s with SSL option enabled on a non-SSL compile",
						cep->ce_fileptr->cf_filename, cep->ce_varlinenum, ce->ce_vardata);
					errors++;
				}
#endif
#ifndef ZIP_LINKS
				if (ofp->flag == CONNECT_ZIP)
				{
					config_error("%s:%i: link %s with ZIP option enabled on a non-ZIP compile",
						cep->ce_fileptr->cf_filename, cep->ce_varlinenum, ce->ce_vardata);
					errors++;
				}
#endif				
				if (ofp->flag == CONNECT_AUTO)
				{
					has_autoconnect = 1;
				}
			}
			continue;
		}
		if (!cep->ce_vardata)
		{
			config_error_empty(cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
				"link", cep->ce_varname);
			errors++;
			continue;
		}
		if (!strcmp(cep->ce_varname, "username"))
		{
			if (has_username)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "link::username");
				continue;
			}
			has_username = 1;
		}
		else if (!strcmp(cep->ce_varname, "hostname"))
		{
			if (has_hostname)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "link::hostname");
				continue;
			}
			has_hostname = 1;
#ifdef INET6
			/* I'm nice... I'll help those poor ipv6 users. -- Syzop */
			/* [ not null && len>6 && has not a : in it && last character is a digit ] */
			if (cep->ce_vardata && (strlen(cep->ce_vardata) > 6) && !strchr(cep->ce_vardata, ':') &&
			    isdigit(cep->ce_vardata[strlen(cep->ce_vardata)-1]))
			{
				char crap[32];
				if (inet_pton(AF_INET, cep->ce_vardata, crap) != 0)
				{
					char ipv6buf[48];
					snprintf(ipv6buf, sizeof(ipv6buf), "::ffff:%s", cep->ce_vardata);
					MyFree(cep->ce_vardata);
					cep->ce_vardata = strdup(ipv6buf);
				} else {
				/* Insert IPv6 validation here */
					config_error( "%s:%i: listen: '%s' looks like "
						"it might be IPv4, but is not a valid address.",
						ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
						cep->ce_vardata);
					errors++;
				}
			}
#endif
			if (strchr(cep->ce_vardata, '*') != NULL || strchr(cep->ce_vardata, '?'))
			{
				has_hostname_wildcards = 1;
			}
		}
		else if (!strcmp(cep->ce_varname, "bind-ip"))
		{
			if (has_bindip)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "link::bind-ip");
				continue;
			}
			has_bindip = 1;
#ifdef INET6
			/* I'm nice... I'll help those poor ipv6 users. -- Syzop */
			/* [ not null && len>6 && has not a : in it && last character is a digit ] */
			if (cep->ce_vardata && (strlen(cep->ce_vardata) > 6) && !strchr(cep->ce_vardata, ':') &&
			    isdigit(cep->ce_vardata[strlen(cep->ce_vardata)-1]))
			{
				char crap[32];
				if (inet_pton(AF_INET, cep->ce_vardata, crap) != 0)
				{
					char ipv6buf[48];
					snprintf(ipv6buf, sizeof(ipv6buf), "::ffff:%s", cep->ce_vardata);
					MyFree(cep->ce_vardata);
					cep->ce_vardata = strdup(ipv6buf);
				} else {
				/* Insert IPv6 validation here */
					config_error( "%s:%i: bind-ip: '%s' looks like "
						"it might be IPv4, but is not a valid address.",
						ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
						cep->ce_vardata);
					errors++;
				}
			}
#endif
		}
		else if (!strcmp(cep->ce_varname, "port"))
		{
			if (has_port)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "link::port");
				continue;
			}
			has_port = 1;
		}
		else if (!strcmp(cep->ce_varname, "password-receive"))
		{
			if (has_passwordreceive)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "link::password-receive");
				continue;
			}
			has_passwordreceive = 1;
			if (Auth_CheckError(cep) < 0)
				errors++;
		}
		else if (!strcmp(cep->ce_varname, "password-connect"))
		{
			if (has_passwordconnect)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "link::password-connect");
				continue;
			}
			has_passwordconnect = 1;
			if (cep->ce_entries)
			{
				config_error("%s:%i: link::password-connect cannot be encrypted",
					     ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
				errors++;
			}
			if (strlen(cep->ce_vardata) > PASSWDLEN) 
			{
				config_error("%s:%i: link::password-connect cannot exceed %d characters in length",
					     ce->ce_fileptr->cf_filename, ce->ce_varlinenum, PASSWDLEN);
				errors++;
			}
		}
		else if (!strcmp(cep->ce_varname, "class"))
		{
			if (has_class)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "link::class");
				continue;
			}
			has_class = 1;
		}
		else if (!strcmp(cep->ce_varname, "hub"))
		{
			if (has_hub)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "link::hub");
				continue;
			}
			has_hub = 1;
		}
		else if (!strcmp(cep->ce_varname, "leaf"))
		{
			if (has_leaf)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "link::leaf");
				continue;
			}
			has_leaf = 1;
		}
		else if (!strcmp(cep->ce_varname, "leafdepth"))
		{
			if (has_leafdepth)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "link::leafdepth");
				continue;
			}
			has_leafdepth = 1;
		}
		else if (!strcmp(cep->ce_varname, "ciphers"))
		{
			if (has_ciphers)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "link::ciphers");
				continue;
			}
			has_ciphers = 1;
		}
#ifdef ZIP_LINKS
		else if (!strcmp(cep->ce_varname, "compression-level"))
		{
			if (has_compressionlevel)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "link::compression-level");
				continue;
			}
			has_compressionlevel = 1;
			if ((atoi(cep->ce_vardata) < 1) || (atoi(cep->ce_vardata) > 9))
			{
				config_error("%s:%i: compression-level should be in range 1..9",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
				errors++;
			}
		}
#endif
		else
		{
			config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
				"link", cep->ce_varname);
			errors++;
		}
	}
	if (!has_username)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"link::username");
		errors++;
	}
	if (!has_hostname)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"link::hostname");
		errors++;
	}
	if (!has_port)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"link::port");
		errors++;
	}
	if (!has_passwordreceive)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"link::password-receive");
		errors++;
	}
	if (!has_passwordconnect)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"link::password-connect");
		errors++;
	}
	if (!has_class)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"link::class");
		errors++;
	}
	if (has_autoconnect && has_hostname_wildcards)
	{
		config_error("%s:%i: link block with autoconnect and wildcards (* and/or ? in hostname)",
				ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
		errors++;
	}
	if (errors > 0)
		return errors;
	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!strcmp(cep->ce_varname, "options")) 
		{
			continue;
		}
	}
	return errors;
		
}

int	_conf_cgiirc(ConfigFile *conf, ConfigEntry *ce)
{
ConfigEntry *cep;
ConfigEntry *cepp;
ConfigItem_cgiirc *cgiirc = NULL;

	cgiirc = (ConfigItem_cgiirc *) MyMallocEx(sizeof(ConfigItem_cgiirc));

	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!strcmp(cep->ce_varname, "username"))
			cgiirc->username = strdup(cep->ce_vardata);
		else if (!strcmp(cep->ce_varname, "hostname"))
			cgiirc->hostname = strdup(cep->ce_vardata);
		else if (!strcmp(cep->ce_varname, "password"))
			cgiirc->auth = Auth_ConvertConf2AuthStruct(cep);
		else if (!strcmp(cep->ce_varname, "type"))
		{
			if (!strcmp(cep->ce_vardata, "webirc"))
				cgiirc->type = CGIIRC_WEBIRC;
			else if (!strcmp(cep->ce_vardata, "old"))
				cgiirc->type = CGIIRC_PASS;
			else
				abort();
		}
	}
	AddListItem(cgiirc, conf_cgiirc);
	return 0;
}

int	_test_cgiirc(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry	*cep, *cepp;
	OperFlag 	*ofp;
	int		errors = 0;
	char has_username = 0; /* dup checking only, not mandatory */
	char has_type     = 0; /* mandatory */
	char has_hostname = 0; /* mandatory */
	char has_password = 0; /* mandatory */
	CGIIRCType type;

	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!cep->ce_varname)
		{
			config_error_blank(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, "cgiirc");
			errors++;
			continue;
		}
		if (!cep->ce_vardata)
		{
			config_error_empty(cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
				"cgiirc", cep->ce_varname);
			errors++;
			continue;
		}
		if (!strcmp(cep->ce_varname, "username"))
		{
			if (has_username)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "cgiirc::username");
				continue;
			}
			has_username = 1;
		}
		else if (!strcmp(cep->ce_varname, "hostname"))
		{
			if (has_hostname)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "cgiirc::hostname");
				continue;
			}
			has_hostname = 1;
#ifdef INET6
			/* I'm nice... I'll help those poor ipv6 users. -- Syzop */
			/* [ not null && len>6 && has not a : in it && last character is a digit ] */
			if (cep->ce_vardata && (strlen(cep->ce_vardata) > 6) && !strchr(cep->ce_vardata, ':') &&
			    isdigit(cep->ce_vardata[strlen(cep->ce_vardata)-1]))
			{
				char crap[32];
				if (inet_pton(AF_INET, cep->ce_vardata, crap) != 0)
				{
					char ipv6buf[48];
					snprintf(ipv6buf, sizeof(ipv6buf), "::ffff:%s", cep->ce_vardata);
					MyFree(cep->ce_vardata);
					cep->ce_vardata = strdup(ipv6buf);
				} else {
				/* Insert IPv6 validation here */
					config_error( "%s:%i: cgiirc::hostname: '%s' looks like "
						"it might be IPv4, but is not a valid address.",
						ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
						cep->ce_vardata);
					errors++;
				}
			}
#endif
		}
		else if (!strcmp(cep->ce_varname, "password"))
		{
			if (has_password)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "cgiirc::password");
				continue;
			}
			has_password = 1;
			if (Auth_CheckError(cep) < 0)
				errors++;
		}
		else if (!strcmp(cep->ce_varname, "type"))
		{
			if (has_type)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "cgiirc::type");
			}
			has_type = 1;
			if (!strcmp(cep->ce_vardata, "webirc"))
				type = CGIIRC_WEBIRC;
			else if (!strcmp(cep->ce_vardata, "old"))
				type = CGIIRC_PASS;
			else
			{
				config_error("%s:%i: unknown cgiirc::type '%s', should be either 'webirc' or 'old'",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata);
				errors++;
			}
		}
		else
		{
			config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
				"cgiirc", cep->ce_varname);
			errors++;
		}
	}
	if (!has_hostname)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"cgiirc::hostname");
		errors++;
	}
	if (!has_type)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"cgiirc::type");
		errors++;
	} else
	{
		if (!has_password && (type == CGIIRC_WEBIRC))
		{
			config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
				"cgiirc::password");
			errors++;
		} else
		if (has_password && (type == CGIIRC_PASS))
		{
			config_error("%s:%i: cgiirc block has type set to 'old' but has a password set. "
			             "Passwords are not used with type 'old'. Either remove the password or "
			             "use the 'webirc' method instead.",
			             ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
			errors++;
		}
	}

	return errors;
}

int     _conf_ban(ConfigFile *conf, ConfigEntry *ce)
{

	ConfigEntry *cep;
	ConfigItem_ban *ca;
	Hook *h;

	ca = MyMallocEx(sizeof(ConfigItem_ban));
	if (!strcmp(ce->ce_vardata, "nick"))
	{
		aTKline *nl = MyMallocEx(sizeof(aTKline));
		nl->type = TKL_NICK;
		for (cep = ce->ce_entries; cep; cep = cep->ce_next)
		{
			if (!strcmp(cep->ce_varname, "mask"))
				nl->hostmask = strdup(cep->ce_vardata);
			else if (!strcmp(cep->ce_varname, "reason"))
				nl->reason = strdup(cep->ce_vardata);
		}
		strcpy(nl->usermask, "*");
		AddListItem(nl, tklines[tkl_hash('q')]);
		free(ca);
		return 0;
	}
	else if (!strcmp(ce->ce_vardata, "ip"))
		ca->flag.type = CONF_BAN_IP;
	else if (!strcmp(ce->ce_vardata, "server"))
		ca->flag.type = CONF_BAN_SERVER;
	else if (!strcmp(ce->ce_vardata, "user"))
		ca->flag.type = CONF_BAN_USER;
	else if (!strcmp(ce->ce_vardata, "realname"))
		ca->flag.type = CONF_BAN_REALNAME;
	else if (!strcmp(ce->ce_vardata, "version"))
	{
		ca->flag.type = CONF_BAN_VERSION;
		tempiConf.use_ban_version = 1;
	}
	else {
		int value;
		free(ca); /* ca isn't used, modules have their own list. */
		for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next)
		{
			value = (*(h->func.intfunc))(conf,ce,CONFIG_BAN);
			if (value == 1)
				break;
		}
		return 0;
	}
	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!strcmp(cep->ce_varname, "mask"))
		{
			ca->mask = strdup(cep->ce_vardata);
			if (ca->flag.type == CONF_BAN_IP || ca->flag.type == CONF_BAN_USER)
			{
				struct irc_netmask tmp;
				tmp.type = parse_netmask(ca->mask, &tmp);
				if (tmp.type != HM_HOST)
				{
					ca->netmask = MyMallocEx(sizeof(struct irc_netmask));
					bcopy(&tmp, ca->netmask, sizeof(struct irc_netmask));
				}
			}
		}
		else if (!strcmp(cep->ce_varname, "reason"))
			ca->reason = strdup(cep->ce_vardata);
		else if (!strcmp(cep->ce_varname, "action"))
			ca ->action = banact_stringtoval(cep->ce_vardata);
	}
	AddListItem(ca, conf_ban);
	return 0;
}

int     _test_ban(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry *cep;
	int	    errors = 0;
	Hook *h;
	char type = 0;
	char has_mask = 0, has_action = 0, has_reason = 0;
	
	if (!ce->ce_vardata)
	{
		config_error("%s:%i: ban without type",	
			ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
		return 1;
	}
	if (!strcmp(ce->ce_vardata, "nick"))
	{}
	else if (!strcmp(ce->ce_vardata, "ip"))
	{}
	else if (!strcmp(ce->ce_vardata, "server"))
	{}
	else if (!strcmp(ce->ce_vardata, "user"))
	{}
	else if (!strcmp(ce->ce_vardata, "realname"))
	{}
	else if (!strcmp(ce->ce_vardata, "version"))
		type = 'v';
	else
	{
		int used = 0;
		for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next) 
		{
			int value, errs = 0;
			if (h->owner && !(h->owner->flags & MODFLAG_TESTING)
			    && !(h->owner->options & MOD_OPT_PERM))
				continue;
			value = (*(h->func.intfunc))(conf,ce,CONFIG_BAN, &errs);
			if (value == 2)
				used = 1;
			if (value == 1)
			{
				used = 1;
				break;
			}
			if (value == -1)
			{
				used = 1;
				errors += errs;
				break;
			}
			if (value == -2)
			{
				used = 1;
				errors += errs;
			}
		}
		if (!used) {
			config_error("%s:%i: unknown ban type %s",
				ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
				ce->ce_vardata);
			return 1;
		}
		return errors;
	}
	
	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (config_is_blankorempty(cep, "ban"))
		{
			errors++;
			continue;
		}
		if (!strcmp(cep->ce_varname, "mask"))
		{
			if (has_mask)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "ban::mask");
				continue;
			}
			has_mask = 1;
		}
		else if (!strcmp(cep->ce_varname, "reason"))
		{
			if (has_reason)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "ban::reason");
				continue;
			}
			has_reason = 1;
		}
		else if (!strcmp(cep->ce_varname, "action"))
		{
			if (has_action)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "ban::action");
			}
			has_action = 1;
			if (!banact_stringtoval(cep->ce_vardata))
			{
				config_error("%s:%i: ban::action has unknown action type '%s'",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum, 
					cep->ce_vardata);
				errors++;
			}
		}
	}

	if (!has_mask)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"ban::mask");
		errors++;
	}
	if (!has_reason)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, 
			"ban::reason");
		errors++;
	}
	if (has_action && type != 'v')
	{
		config_error("%s:%d: ban::action specified even though type is not 'version'",
			ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
		errors++;
	}
	return errors;	
}

int	_conf_set(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry *cep, *cepp, *ceppp;
	OperFlag 	*ofl = NULL;
	Hook *h;

	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!strcmp(cep->ce_varname, "kline-address")) {
			ircstrdup(tempiConf.kline_address, cep->ce_vardata);
		}
		if (!strcmp(cep->ce_varname, "gline-address")) {
			ircstrdup(tempiConf.gline_address, cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "modes-on-connect")) {
			tempiConf.conn_modes = (long) set_usermode(cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "modes-on-oper")) {
			tempiConf.oper_modes = (long) set_usermode(cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "modes-on-join")) {
			set_channelmodes(cep->ce_vardata, &tempiConf.modes_on_join, 0);
		}
		else if (!strcmp(cep->ce_varname, "snomask-on-oper")) {
			ircstrdup(tempiConf.oper_snomask, cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "snomask-on-connect")) {
			ircstrdup(tempiConf.user_snomask, cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "level-on-join")) {
			tempiConf.level_on_join = channellevel_to_int(cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "static-quit")) {
			ircstrdup(tempiConf.static_quit, cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "static-part")) {
			ircstrdup(tempiConf.static_part, cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "who-limit")) {
			tempiConf.who_limit = atol(cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "maxbans")) {
			tempiConf.maxbans = atol(cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "maxbanlength")) {
			tempiConf.maxbanlength = atol(cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "silence-limit")) {
			tempiConf.silence_limit = atol(cep->ce_vardata);
			if (loop.ircd_booted)
				IsupportSetValue(IsupportFind("SILENCE"), cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "auto-join")) {
			ircstrdup(tempiConf.auto_join_chans, cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "oper-auto-join")) {
			ircstrdup(tempiConf.oper_auto_join_chans, cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "check-target-nick-bans")) {
			tempiConf.check_target_nick_bans = config_checkval(cep->ce_vardata, CFG_YESNO);
		}
		else if (!strcmp(cep->ce_varname, "pingpong-warning")) {
			tempiConf.pingpong_warning = config_checkval(cep->ce_vardata, CFG_YESNO);
		}
		else if (!strcmp(cep->ce_varname, "ping-cookie")) {
			tempiConf.ping_cookie = config_checkval(cep->ce_vardata, CFG_YESNO);
		}
		else if (!strcmp(cep->ce_varname, "watch-away-notification")) {
			tempiConf.watch_away_notification = config_checkval(cep->ce_vardata, CFG_YESNO);
		}
		else if (!strcmp(cep->ce_varname, "uhnames")) {
			tempiConf.uhnames = config_checkval(cep->ce_vardata, CFG_YESNO);
		}
		else if (!strcmp(cep->ce_varname, "allow-userhost-change")) {
			if (!stricmp(cep->ce_vardata, "always"))
				tempiConf.userhost_allowed = UHALLOW_ALWAYS;
			else if (!stricmp(cep->ce_vardata, "never"))
				tempiConf.userhost_allowed = UHALLOW_NEVER;
			else if (!stricmp(cep->ce_vardata, "not-on-channels"))
				tempiConf.userhost_allowed = UHALLOW_NOCHANS;
			else
				tempiConf.userhost_allowed = UHALLOW_REJOIN;
		}
		else if (!strcmp(cep->ce_varname, "allowed-nickchars")) {
			for (cepp = cep->ce_entries; cepp; cepp=cepp->ce_next)
				charsys_add_language(cepp->ce_varname);
		}
		else if (!strcmp(cep->ce_varname, "channel-command-prefix")) {
			ircstrdup(tempiConf.channel_command_prefix, cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "restrict-usermodes")) {
			int i;
			char *p = MyMalloc(strlen(cep->ce_vardata) + 1), *x = p;
			/* The data should be something like 'Gw' or something,
			 * but just in case users use '+Gw' then ignore the + (and -).
			 */
			for (i=0; i < strlen(cep->ce_vardata); i++)
				if ((cep->ce_vardata[i] != '+') && (cep->ce_vardata[i] != '-'))
					*x++ = cep->ce_vardata[i];
			*x = '\0';
			tempiConf.restrict_usermodes = p;
		}
		else if (!strcmp(cep->ce_varname, "restrict-channelmodes")) {
			int i;
			char *p = MyMalloc(strlen(cep->ce_vardata) + 1), *x = p;
			/* The data should be something like 'GL' or something,
			 * but just in case users use '+GL' then ignore the + (and -).
			 */
			for (i=0; i < strlen(cep->ce_vardata); i++)
				if ((cep->ce_vardata[i] != '+') && (cep->ce_vardata[i] != '-'))
					*x++ = cep->ce_vardata[i];
			*x = '\0';
			tempiConf.restrict_channelmodes = p;
		}
		else if (!strcmp(cep->ce_varname, "restrict-extendedbans")) {
			ircstrdup(tempiConf.restrict_extendedbans, cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "new-linking-protocol")) {
			tempiConf.new_linking_protocol = atoi(cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "anti-spam-quit-message-time")) {
			tempiConf.anti_spam_quit_message_time = config_checkval(cep->ce_vardata,CFG_TIME);
		}
		else if (!strcmp(cep->ce_varname, "oper-only-stats")) {
			if (!cep->ce_entries)
			{
				ircstrdup(tempiConf.oper_only_stats, cep->ce_vardata);
			}
			else
			{
				for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next)
				{
					OperStat *os = MyMallocEx(sizeof(OperStat));
					ircstrdup(os->flag, cepp->ce_varname);
					AddListItem(os, tempiConf.oper_only_stats_ext);
				}
			}
		}
		else if (!strcmp(cep->ce_varname, "maxchannelsperuser")) {
			tempiConf.maxchannelsperuser = atoi(cep->ce_vardata);
			if (loop.ircd_booted)
			{
				char tmpbuf[512];
				IsupportSetValue(IsupportFind("MAXCHANNELS"), cep->ce_vardata);
				ircsprintf(tmpbuf, "#:%s", cep->ce_vardata);
				IsupportSetValue(IsupportFind("CHANLIMIT"), tmpbuf);
			}
		}
		else if (!strcmp(cep->ce_varname, "maxdccallow")) {
			tempiConf.maxdccallow = atoi(cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "network-name")) {
			char *tmp;
			ircstrdup(tempiConf.network.x_ircnetwork, cep->ce_vardata);
			for (tmp = cep->ce_vardata; *cep->ce_vardata; cep->ce_vardata++) {
				if (*cep->ce_vardata == ' ')
					*cep->ce_vardata='-';
			}
			ircstrdup(tempiConf.network.x_ircnet005, tmp);
			if (loop.ircd_booted)
				IsupportSetValue(IsupportFind("NETWORK"), tmp);
			cep->ce_vardata = tmp;
		}
		else if (!strcmp(cep->ce_varname, "default-server")) {
			ircstrdup(tempiConf.network.x_defserv, cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "services-server")) {
			ircstrdup(tempiConf.network.x_services_name, cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "sasl-server")) {
			ircstrdup(tempiConf.network.x_sasl_server, cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "stats-server")) {
			ircstrdup(tempiConf.network.x_stats_server, cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "help-channel")) {
			ircstrdup(tempiConf.network.x_helpchan, cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "hiddenhost-prefix")) {
			ircstrdup(tempiConf.network.x_hidden_host, cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "prefix-quit")) {
			if (*cep->ce_vardata == '0')
			{
				ircstrdup(tempiConf.network.x_prefix_quit, "");
			}
			else
				ircstrdup(tempiConf.network.x_prefix_quit, cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "dns")) {
			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) {
				if (!strcmp(cepp->ce_varname, "timeout")) {
					tempiConf.host_timeout = config_checkval(cepp->ce_vardata,CFG_TIME);
				}
				else if (!strcmp(cepp->ce_varname, "retries")) {
					tempiConf.host_retries = config_checkval(cepp->ce_vardata,CFG_TIME);
				}
				else if (!strcmp(cepp->ce_varname, "nameserver")) {
					ircstrdup(tempiConf.name_server, cepp->ce_vardata);
				}
				else if (!strcmp(cepp->ce_varname, "bind-ip")) {
					ircstrdup(tempiConf.dns_bindip, cepp->ce_vardata);
				}
			}
		}
#ifdef THROTTLING
		else if (!strcmp(cep->ce_varname, "throttle")) {
			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) {
				if (!strcmp(cepp->ce_varname, "period")) 
					tempiConf.throttle_period = config_checkval(cepp->ce_vardata,CFG_TIME);
				else if (!strcmp(cepp->ce_varname, "connections"))
					tempiConf.throttle_count = atoi(cepp->ce_vardata);
			}
		}
#endif
		else if (!strcmp(cep->ce_varname, "anti-flood")) {
			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) {
				if (!strcmp(cepp->ce_varname, "unknown-flood-bantime")) 
					tempiConf.unknown_flood_bantime = config_checkval(cepp->ce_vardata,CFG_TIME);
				else if (!strcmp(cepp->ce_varname, "unknown-flood-amount"))
					tempiConf.unknown_flood_amount = atol(cepp->ce_vardata);
#ifdef NO_FLOOD_AWAY
				else if (!strcmp(cepp->ce_varname, "away-count"))
					tempiConf.away_count = atol(cepp->ce_vardata);
				else if (!strcmp(cepp->ce_varname, "away-period"))
					tempiConf.away_period = config_checkval(cepp->ce_vardata, CFG_TIME);
				else if (!strcmp(cepp->ce_varname, "away-flood"))
				{
					int cnt, period;
					config_parse_flood(cepp->ce_vardata, &cnt, &period);
					tempiConf.away_count = cnt;
					tempiConf.away_period = period;
				}
#endif
				else if (!strcmp(cepp->ce_varname, "nick-flood"))
				{
					int cnt, period;
					config_parse_flood(cepp->ce_vardata, &cnt, &period);
					tempiConf.nick_count = cnt;
					tempiConf.nick_period = period;
				}

			}
		}
		else if (!strcmp(cep->ce_varname, "options")) {
			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) {
				if (!strcmp(cepp->ce_varname, "webtv-support")) {
					tempiConf.webtv_support = 1;
				}
				else if (!strcmp(cepp->ce_varname, "hide-ulines")) {
					tempiConf.hide_ulines = 1;
				}
				else if (!strcmp(cepp->ce_varname, "flat-map")) {
					tempiConf.flat_map = 1;
				}
				else if (!strcmp(cepp->ce_varname, "no-stealth")) {
					tempiConf.no_oper_hiding = 1;
				}
				else if (!strcmp(cepp->ce_varname, "show-opermotd")) {
					tempiConf.som = 1;
				}
				else if (!strcmp(cepp->ce_varname, "identd-check")) {
					tempiConf.ident_check = 1;
				}
				else if (!strcmp(cepp->ce_varname, "fail-oper-warn")) {
					tempiConf.fail_oper_warn = 1;
				}
				else if (!strcmp(cepp->ce_varname, "show-connect-info")) {
					tempiConf.show_connect_info = 1;
				}
				else if (!strcmp(cepp->ce_varname, "dont-resolve")) {
					tempiConf.dont_resolve = 1;
				}
				else if (!strcmp(cepp->ce_varname, "mkpasswd-for-everyone")) {
					tempiConf.mkpasswd_for_everyone = 1;
				}
				else if (!strcmp(cepp->ce_varname, "allow-insane-bans")) {
					tempiConf.allow_insane_bans = 1;
				}
				else if (!strcmp(cepp->ce_varname, "allow-part-if-shunned")) {
					tempiConf.allow_part_if_shunned = 1;
				}
				else if (!strcmp(cepp->ce_varname, "disable-cap")) {
					tempiConf.disable_cap = 1;
				}
			}
		}
		else if (!strcmp(cep->ce_varname, "hosts")) {
			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next)
			{
				if (!strcmp(cepp->ce_varname, "local")) {
					ircstrdup(tempiConf.network.x_locop_host, cepp->ce_vardata);
				}
				else if (!strcmp(cepp->ce_varname, "global")) {
					ircstrdup(tempiConf.network.x_oper_host, cepp->ce_vardata);
				}
				else if (!strcmp(cepp->ce_varname, "coadmin")) {
					ircstrdup(tempiConf.network.x_coadmin_host, cepp->ce_vardata);
				}
				else if (!strcmp(cepp->ce_varname, "admin")) {
					ircstrdup(tempiConf.network.x_admin_host, cepp->ce_vardata);
				}
				else if (!strcmp(cepp->ce_varname, "servicesadmin")) {
					ircstrdup(tempiConf.network.x_sadmin_host, cepp->ce_vardata);
				}
				else if (!strcmp(cepp->ce_varname, "netadmin")) {
					ircstrdup(tempiConf.network.x_netadmin_host, cepp->ce_vardata);
				}
				else if (!strcmp(cepp->ce_varname, "techadmin")) {
					ircstrdup(tempiConf.network.x_techadmin_host, cepp->ce_vardata);
				}
				else if (!strcmp(cepp->ce_varname, "rootadmin")) {
					ircstrdup(tempiConf.network.x_rootadmin_host, cepp->ce_vardata);
				}
				else if (!strcmp(cepp->ce_varname, "owner")) {
					ircstrdup(tempiConf.network.x_owner_host, cepp->ce_vardata);
				}
				else if (!strcmp(cepp->ce_varname, "superadmin")) {
					ircstrdup(tempiConf.network.x_superadmin_host, cepp->ce_vardata);
				}
				else if (!strcmp(cepp->ce_varname, "host-on-oper-up")) {
					tempiConf.network.x_inah = config_checkval(cepp->ce_vardata,CFG_YESNO);
				}
			}
		}
		else if (!strcmp(cep->ce_varname, "cloak-keys"))
		{
			for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next)
			{
				int value;
				value = (*(h->func.intfunc))(conf, cep, CONFIG_CLOAKKEYS);
				if (value == 1)
					break;
			}
		}
		else if (!strcmp(cep->ce_varname, "ident"))
		{
			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next)
			{
				if (!strcmp(cepp->ce_varname, "connect-timeout"))
					tempiConf.ident_connect_timeout = config_checkval(cepp->ce_vardata,CFG_TIME);
				if (!strcmp(cepp->ce_varname, "read-timeout"))
					tempiConf.ident_read_timeout = config_checkval(cepp->ce_vardata,CFG_TIME);
			}
		}
		else if (!strcmp(cep->ce_varname, "timesync") || !strcmp(cep->ce_varname, "timesynch"))
		{
			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next)
			{
				if (!strcmp(cepp->ce_varname, "enabled"))
					tempiConf.timesynch_enabled = config_checkval(cepp->ce_vardata,CFG_YESNO);
				else if (!strcmp(cepp->ce_varname, "timeout"))
					tempiConf.timesynch_timeout = config_checkval(cepp->ce_vardata,CFG_TIME);
				else if (!strcmp(cepp->ce_varname, "server"))
					ircstrdup(tempiConf.timesynch_server, cepp->ce_vardata);
			}
		}
		else if (!strcmp(cep->ce_varname, "spamfilter"))
		{
			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next)
			{
				if (!strcmp(cepp->ce_varname, "ban-time"))
					tempiConf.spamfilter_ban_time = config_checkval(cepp->ce_vardata,CFG_TIME);
				else if (!strcmp(cepp->ce_varname, "ban-reason"))
					ircstrdup(tempiConf.spamfilter_ban_reason, cepp->ce_vardata);
				else if (!strcmp(cepp->ce_varname, "virus-help-channel"))
					ircstrdup(tempiConf.spamfilter_virus_help_channel, cepp->ce_vardata);
				else if (!strcmp(cepp->ce_varname, "virus-help-channel-deny"))
					tempiConf.spamfilter_vchan_deny = config_checkval(cepp->ce_vardata,CFG_YESNO);
				else if (!strcmp(cepp->ce_varname, "except"))
				{
					char *name, *p;
					SpamExcept *e;
					ircstrdup(tempiConf.spamexcept_line, cepp->ce_vardata);
					for (name = strtoken(&p, cepp->ce_vardata, ","); name; name = strtoken(&p, NULL, ","))
					{
						if (*name == ' ')
							name++;
						if (*name)
						{
							e = MyMallocEx(sizeof(SpamExcept) + strlen(name));
							strcpy(e->name, name);
							AddListItem(e, tempiConf.spamexcept);
						}
					}
				}
				else if (!strcmp(cepp->ce_varname, "detect-slow-warn"))
				{
					tempiConf.spamfilter_detectslow_warn = atol(cepp->ce_vardata);
				}
				else if (!strcmp(cepp->ce_varname, "detect-slow-fatal"))
				{
					tempiConf.spamfilter_detectslow_fatal = atol(cepp->ce_vardata);
				}
				else if (!strcmp(cepp->ce_varname, "stop-on-first-match"))
				{
					tempiConf.spamfilter_stop_on_first_match = config_checkval(cepp->ce_vardata, CFG_YESNO);
				}
			}
		}
		else if (!strcmp(cep->ce_varname, "default-bantime"))
		{
			tempiConf.default_bantime = config_checkval(cep->ce_vardata,CFG_TIME);
		}
		else if (!strcmp(cep->ce_varname, "ban-version-tkl-time"))
		{
			tempiConf.ban_version_tkl_time = config_checkval(cep->ce_vardata,CFG_TIME);
		}
#ifdef NEWCHFLOODPROT
		else if (!strcmp(cep->ce_varname, "modef-default-unsettime")) {
			int v = atoi(cep->ce_vardata);
			tempiConf.modef_default_unsettime = (unsigned char)v;
		}
		else if (!strcmp(cep->ce_varname, "modef-max-unsettime")) {
			int v = atoi(cep->ce_vardata);
			tempiConf.modef_max_unsettime = (unsigned char)v;
		}
#endif
		else if (!strcmp(cep->ce_varname, "ssl")) {
#ifdef USE_SSL
			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) {
				if (!strcmp(cepp->ce_varname, "egd")) {
					tempiConf.use_egd = 1;
					if (cepp->ce_vardata)
						tempiConf.egd_path = strdup(cepp->ce_vardata);
				}
				else if (!strcmp(cepp->ce_varname, "server-cipher-list"))
				{
					ircstrdup(tempiConf.x_server_cipher_list, cepp->ce_vardata);
				}
				else if (!strcmp(cepp->ce_varname, "certificate"))
				{
					ircstrdup(tempiConf.x_server_cert_pem, cepp->ce_vardata);	
				}
				else if (!strcmp(cepp->ce_varname, "key"))
				{
					ircstrdup(tempiConf.x_server_key_pem, cepp->ce_vardata);	
				}
				else if (!strcmp(cepp->ce_varname, "trusted-ca-file"))
				{
					ircstrdup(tempiConf.trusted_ca_file, cepp->ce_vardata);
				}
				else if (!strcmp(cepp->ce_varname, "renegotiate-bytes"))
				{
					tempiConf.ssl_renegotiate_bytes = config_checkval(cepp->ce_vardata, CFG_SIZE);
				}
				else if (!strcmp(cepp->ce_varname, "renegotiate-timeout"))
				{
					tempiConf.ssl_renegotiate_timeout = config_checkval(cepp->ce_vardata, CFG_TIME);
				}
				else if (!strcmp(cepp->ce_varname, "options"))
				{
					tempiConf.ssl_options = 0;
					for (ceppp = cepp->ce_entries; ceppp; ceppp = ceppp->ce_next)
					{
						ofl = config_binary_flags_search(_SSLFlags, ceppp->ce_varname, ARRAY_SIZEOF(_SSLFlags));
						if (ofl) /* this should always be true */
							tempiConf.ssl_options |= ofl->flag;
					}
					if (tempiConf.ssl_options & SSLFLAG_DONOTACCEPTSELFSIGNED)
						if (!tempiConf.ssl_options & SSLFLAG_VERIFYCERT)
							tempiConf.ssl_options |= SSLFLAG_VERIFYCERT;
				}	
				
			}
#endif /* USE_SSL */
		}
		else if (!strcmp(cep->ce_varname, "default-ipv6-clone-mask"))
		{
#ifdef INET6
			tempiConf.default_ipv6_clone_mask = atoi(cep->ce_vardata);
#endif /* INET6 */
		}
		else 
		{
			int value;
			for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next)
			{
				value = (*(h->func.intfunc))(conf,cep,CONFIG_SET);
				if (value == 1)
					break;
			}
		}
	}
	return 0;
}

int	_test_set(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry *cep, *cepp, *ceppp;
	long		templong;
	int		tempi;
	int	    errors = 0;
	Hook	*h;
#define CheckNull(x) if ((!(x)->ce_vardata) || (!(*((x)->ce_vardata)))) { config_error("%s:%i: missing parameter", (x)->ce_fileptr->cf_filename, (x)->ce_varlinenum); errors++; continue; }
#define CheckNullAllowEmpty(x) if ((!(x)->ce_vardata)) { config_error("%s:%i: missing parameter", (x)->ce_fileptr->cf_filename, (x)->ce_varlinenum); errors++; continue; }
#define CheckDuplicate(cep, name, display) if (settings.has_##name) { config_warn_duplicate((cep)->ce_fileptr->cf_filename, cep->ce_varlinenum, "set::" display); continue; } else settings.has_##name = 1

	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!cep->ce_varname)
		{
			config_error_blank(cep->ce_fileptr->cf_filename,
				cep->ce_varlinenum, "set");
			errors++;
			continue;
		}
		if (!strcmp(cep->ce_varname, "kline-address")) {
			CheckNull(cep);
			CheckDuplicate(cep, kline_address, "kline-address");
			if (!strchr(cep->ce_vardata, '@') && !strchr(cep->ce_vardata, ':'))
			{
				config_error("%s:%i: set::kline-address must be an e-mail or an URL",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
				errors++;
				continue;
			}
			else if (!match("*@unrealircd.com", cep->ce_vardata) || !match("*@unrealircd.org",cep->ce_vardata) || !match("unreal-*@lists.sourceforge.net",cep->ce_vardata)) 
			{
				config_error("%s:%i: set::kline-address may not be an UnrealIRCd Team address",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
				errors++; continue;
			}
		}
		else if (!strcmp(cep->ce_varname, "gline-address")) {
			CheckNull(cep);
			CheckDuplicate(cep, gline_address, "gline-address");
			if (!strchr(cep->ce_vardata, '@') && !strchr(cep->ce_vardata, ':'))
			{
				config_error("%s:%i: set::gline-address must be an e-mail or an URL",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
				errors++;
				continue;
			}
			else if (!match("*@unrealircd.com", cep->ce_vardata) || !match("*@unrealircd.org",cep->ce_vardata) || !match("unreal-*@lists.sourceforge.net",cep->ce_vardata)) 
			{
				config_error("%s:%i: set::gline-address may not be an UnrealIRCd Team address",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
				errors++; continue;
			}
		}
		else if (!strcmp(cep->ce_varname, "modes-on-connect")) {
			char *p;
			CheckNull(cep);
			CheckDuplicate(cep, modes_on_connect, "modes-on-connect");
			for (p = cep->ce_vardata; *p; p++)
				if (strchr("oOaANCrzSgHhqtW", *p))
				{
					config_error("%s:%i: set::modes-on-connect may not include mode '%c'",
						cep->ce_fileptr->cf_filename, cep->ce_varlinenum, *p);
					errors++;
				}
			templong = (long) set_usermode(cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "modes-on-join")) {
			char *c;
			struct ChMode temp;
			bzero(&temp, sizeof(temp));
			CheckNull(cep);
			CheckDuplicate(cep, modes_on_join, "modes-on-join");
			for (c = cep->ce_vardata; *c; c++)
			{
				if (*c == ' ')
					break; /* don't check the parameter ;p */
				switch (*c)
				{
					case 'q':
					case 'a':
					case 'o':
					case 'h':
					case 'v':
					case 'b':
					case 'e':
					case 'I':
					case 'O':
					case 'A':
					case 'z':
					case 'l':
					case 'k':
					case 'L':
						config_error("%s:%i: set::modes-on-join contains +%c", 
							cep->ce_fileptr->cf_filename, cep->ce_varlinenum, *c);
						errors++;
						break;
				}
			}
			set_channelmodes(cep->ce_vardata, &temp, 1);
			if (temp.mode & MODE_NOKNOCK && !(temp.mode & MODE_INVITEONLY))
			{
				config_error("%s:%i: set::modes-on-join has +K but not +i",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
				errors++;
			}
			if (temp.mode & MODE_NOCOLOR && temp.mode & MODE_STRIP)
			{
				config_error("%s:%i: set::modes-on-join has +c and +S",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
				errors++;
			}
			if (temp.mode & MODE_SECRET && temp.mode & MODE_PRIVATE)
			{
				config_error("%s:%i: set::modes-on-join has +s and +p",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
				errors++;
			}
			
		}
		else if (!strcmp(cep->ce_varname, "modes-on-oper")) {
			char *p;
			CheckNull(cep);
			CheckDuplicate(cep, modes_on_oper, "modes-on-oper");
			for (p = cep->ce_vardata; *p; p++)
				if (strchr("oOaANCrzS", *p))
				{
					config_error("%s:%i: set::modes-on-oper may not include mode '%c'",
						cep->ce_fileptr->cf_filename, cep->ce_varlinenum, *p);
					errors++;
				}
			templong = (long) set_usermode(cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "snomask-on-oper")) {
			CheckNull(cep);
			CheckDuplicate(cep, snomask_on_oper, "snomask-on-oper");
		}
		else if (!strcmp(cep->ce_varname, "snomask-on-connect")) {
			CheckNull(cep);
			CheckDuplicate(cep, snomask_on_connect, "snomask-on-connect");
		}
		else if (!strcmp(cep->ce_varname, "level-on-join")) {
			char *p;
			CheckNull(cep);
			CheckDuplicate(cep, level_on_join, "level-on-join");
			if (!channellevel_to_int(cep->ce_vardata))
			{
				config_error("%s:%i: set::level-on-join: unknown value '%s', should be one of: none, voice, halfop, op, protect, owner",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata);
				errors++;
			}
		}
		else if (!strcmp(cep->ce_varname, "static-quit")) {
			CheckNull(cep);
			CheckDuplicate(cep, static_quit, "static-quit");
		}
		else if (!strcmp(cep->ce_varname, "static-part")) {
			CheckNull(cep);
			CheckDuplicate(cep, static_part, "static-part");
		}
		else if (!strcmp(cep->ce_varname, "who-limit")) {
			CheckNull(cep);
			CheckDuplicate(cep, who_limit, "who-limit");
		}
		else if (!strcmp(cep->ce_varname, "maxbans")) {
			CheckNull(cep);
			CheckDuplicate(cep, maxbans, "maxbans");
		}
		else if (!strcmp(cep->ce_varname, "maxbanlength")) {
			CheckNull(cep);
			CheckDuplicate(cep, maxbanlength, "maxbanlength");
		}
		else if (!strcmp(cep->ce_varname, "silence-limit")) {
			CheckNull(cep);
			CheckDuplicate(cep, silence_limit, "silence-limit");
		}
		else if (!strcmp(cep->ce_varname, "auto-join")) {
			CheckNull(cep);
			CheckDuplicate(cep, auto_join, "auto-join");
		}
		else if (!strcmp(cep->ce_varname, "oper-auto-join")) {
			CheckNull(cep);
			CheckDuplicate(cep, oper_auto_join, "oper-auto-join");
		}
		else if (!strcmp(cep->ce_varname, "check-target-nick-bans")) {
			CheckNull(cep);
			CheckDuplicate(cep, check_target_nick_bans, "check-target-nick-bans");
		}
		else if (!strcmp(cep->ce_varname, "pingpong-warning")) {
			CheckNull(cep);
			CheckDuplicate(cep, pingpong_warning, "pingpong-warning");
		}
		else if (!strcmp(cep->ce_varname, "ping-cookie")) {
			CheckNull(cep);
			CheckDuplicate(cep, ping_cookie, "ping-cookie");
		}
		else if (!strcmp(cep->ce_varname, "watch-away-notification")) {
			CheckNull(cep);
			CheckDuplicate(cep, watch_away_notification, "watch-away-notification");
		}
		else if (!strcmp(cep->ce_varname, "uhnames")) {
			CheckNull(cep);
			CheckDuplicate(cep, uhnames, "uhnames");
		}
		else if (!strcmp(cep->ce_varname, "channel-command-prefix")) {
			CheckNullAllowEmpty(cep);
			CheckDuplicate(cep, channel_command_prefix, "channel-command-prefix");
		}
		else if (!strcmp(cep->ce_varname, "allow-userhost-change")) {
			CheckNull(cep);
			CheckDuplicate(cep, allow_userhost_change, "allow-userhost-change");
			if (stricmp(cep->ce_vardata, "always") && 
			    stricmp(cep->ce_vardata, "never") &&
			    stricmp(cep->ce_vardata, "not-on-channels") &&
			    stricmp(cep->ce_vardata, "force-rejoin"))
			{
				config_error("%s:%i: set::allow-userhost-change is invalid",
					cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum);
				errors++;
				continue;
			}
		}
		else if (!strcmp(cep->ce_varname, "allowed-nickchars")) {
			if (cep->ce_vardata)
			{
				config_error("%s:%i: set::allowed-nickchars: please use 'allowed-nickchars { name; };' "
				             "and not 'allowed-nickchars name;'",
				             cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
				errors++;
				continue;
			}
			for (cepp = cep->ce_entries; cepp; cepp=cepp->ce_next)
			{
				if (!charsys_test_language(cepp->ce_varname))
				{
					config_error("%s:%i: set::allowed-nickchars: Unknown (sub)language '%s'",
						cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cepp->ce_varname);
					errors++;
				}
			}
		}
		else if (!strcmp(cep->ce_varname, "anti-spam-quit-message-time")) {
			CheckNull(cep);
			CheckDuplicate(cep, anti_spam_quit_message_time, "anti-spam-quit-message-time");
		}
		else if (!strcmp(cep->ce_varname, "oper-only-stats")) {
			CheckDuplicate(cep, oper_only_stats, "oper-only-stats");
			if (!cep->ce_entries)
			{
				CheckNull(cep);
			}
			else
			{
				for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next)
				{
					if (!cepp->ce_varname)
						config_error("%s:%i: blank set::oper-only-stats item",
							cepp->ce_fileptr->cf_filename,
							cepp->ce_varlinenum);
				}
			}
		}
		else if (!strcmp(cep->ce_varname, "maxchannelsperuser")) {
			CheckNull(cep);
			CheckDuplicate(cep, maxchannelsperuser, "maxchannelsperuser");
			tempi = atoi(cep->ce_vardata);
			if (tempi < 1)
			{
				config_error("%s:%i: set::maxchannelsperuser must be > 0",
					cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum);
				errors++;
				continue;
			}
		}
		else if (!strcmp(cep->ce_varname, "maxdccallow")) {
			CheckNull(cep);
			CheckDuplicate(cep, maxdccallow, "maxdccallow");
		}
		else if (!strcmp(cep->ce_varname, "network-name")) {
			char *p;
			CheckNull(cep);
			CheckDuplicate(cep, network_name, "network-name");
			for (p = cep->ce_vardata; *p; p++)
				if ((*p < ' ') || (*p > '~'))
				{
					config_error("%s:%i: set::network-name can only contain ASCII characters 33-126. Invalid character = '%c'",
						cep->ce_fileptr->cf_filename, cep->ce_varlinenum, *p);
					errors++;
					break;
				}
		}
		else if (!strcmp(cep->ce_varname, "default-server")) {
			CheckNull(cep);
			CheckDuplicate(cep, default_server, "default-server");
		}
		else if (!strcmp(cep->ce_varname, "services-server")) {
			CheckNull(cep);
			CheckDuplicate(cep, services_server, "services-server");
		}
		else if (!strcmp(cep->ce_varname, "sasl-server")) {
			CheckNull(cep);
			CheckDuplicate(cep, sasl_server, "sasl-server");
		}
		else if (!strcmp(cep->ce_varname, "stats-server")) {
			CheckNull(cep);
			CheckDuplicate(cep, stats_server, "stats-server");
		}
		else if (!strcmp(cep->ce_varname, "help-channel")) {
			CheckNull(cep);
			CheckDuplicate(cep, help_channel, "help-channel");
		}
		else if (!strcmp(cep->ce_varname, "hiddenhost-prefix")) {
			CheckNull(cep);
			CheckDuplicate(cep, hiddenhost_prefix, "hiddenhost-prefix");
			if (strchr(cep->ce_vardata, ' ') || (*cep->ce_vardata == ':'))
			{
				config_error("%s:%i: set::hiddenhost-prefix must not contain spaces or be prefixed with ':'",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
				errors++;
				continue;
			}
		}
		else if (!strcmp(cep->ce_varname, "prefix-quit")) {
			CheckNull(cep);
			CheckDuplicate(cep, prefix_quit, "prefix-quit");
		}
		else if (!strcmp(cep->ce_varname, "restrict-usermodes"))
		{
			CheckNull(cep);
			CheckDuplicate(cep, restrict_usermodes, "restrict-usermodes");
			if (cep->ce_varname) {
				int warn = 0;
				char *p;
				for (p = cep->ce_vardata; *p; p++)
					if ((*p == '+') || (*p == '-'))
						warn = 1;
				if (warn) {
					config_status("%s:%i: warning: set::restrict-usermodes: should only contain modechars, no + or -.\n",
						cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
				}
			}
		}
		else if (!strcmp(cep->ce_varname, "restrict-channelmodes"))
		{
			CheckNull(cep);
			CheckDuplicate(cep, restrict_channelmodes, "restrict-channelmodes");
			if (cep->ce_varname) {
				int warn = 0;
				char *p;
				for (p = cep->ce_vardata; *p; p++)
					if ((*p == '+') || (*p == '-'))
						warn = 1;
				if (warn) {
					config_status("%s:%i: warning: set::restrict-channelmodes: should only contain modechars, no + or -.\n",
						cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
				}
			}
		}
		else if (!strcmp(cep->ce_varname, "restrict-extendedbans"))
		{
			CheckDuplicate(cep, restrict_extendedbans, "restrict-extendedbans");
			CheckNull(cep);
		}
		else if (!strcmp(cep->ce_varname, "new-linking-protocol"))
		{
			CheckDuplicate(cep, new_linking_protocol, "new-linking-protocol");
			CheckNull(cep);
		}
		else if (!strcmp(cep->ce_varname, "dns")) {
			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) {
				CheckNull(cepp);
				if (!strcmp(cepp->ce_varname, "timeout")) {
					CheckDuplicate(cepp, dns_timeout, "dns::timeout");
				}
				else if (!strcmp(cepp->ce_varname, "retries")) {
					CheckDuplicate(cepp, dns_retries, "dns::retries");
				}
				else if (!strcmp(cepp->ce_varname, "nameserver")) {
					struct in_addr in;
					CheckDuplicate(cepp, dns_nameserver, "dns::nameserver");
					
					in.s_addr = inet_addr(cepp->ce_vardata);
					if (strcmp((char *)inet_ntoa(in), cepp->ce_vardata))
					{
						config_error("%s:%i: set::dns::nameserver (%s) is not a valid IP",
							cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum,
							cepp->ce_vardata);
						errors++;
						continue;
					}
				}
				else if (!strcmp(cepp->ce_varname, "bind-ip")) {
					struct in_addr in;
					CheckDuplicate(cepp, dns_bind_ip, "dns::bind-ip");
					if (strcmp(cepp->ce_vardata, "*"))
					{
						in.s_addr = inet_addr(cepp->ce_vardata);
						if (strcmp((char *)inet_ntoa(in), cepp->ce_vardata))
						{
							config_error("%s:%i: set::dns::bind-ip (%s) is not a valid IP",
								cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum,
								cepp->ce_vardata);
							errors++;
							continue;
						}
					}
				}
				else
				{
					config_error_unknownopt(cepp->ce_fileptr->cf_filename,
						cepp->ce_varlinenum, "set::dns", 
						cepp->ce_varname);
						errors++;
				}
			}
		}
#ifdef THROTTLING
		else if (!strcmp(cep->ce_varname, "throttle")) {
			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) {
				CheckNull(cepp);
				if (!strcmp(cepp->ce_varname, "period")) {
					int x = config_checkval(cepp->ce_vardata,CFG_TIME);
					CheckDuplicate(cepp, throttle_period, "throttle::period");
					if (x > 86400*7)
					{
						config_error("%s:%i: insane set::throttle::period value",
							cepp->ce_fileptr->cf_filename,
							cepp->ce_varlinenum);
						errors++;
						continue;
					}
				}
				else if (!strcmp(cepp->ce_varname, "connections")) {
					int x = atoi(cepp->ce_vardata);
					CheckDuplicate(cepp, throttle_connections, "throttle::connections");
					if ((x < 1) || (x > 127))
					{
						config_error("%s:%i: set::throttle::connections out of range, should be 1-127",
							cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum);
						errors++;
						continue;
					}
				}
				else
				{
					config_error_unknownopt(cepp->ce_fileptr->cf_filename,
						cepp->ce_varlinenum, "set::throttle",
						cepp->ce_varname);
					errors++;
					continue;
				}
			}
		}
#endif
		else if (!strcmp(cep->ce_varname, "anti-flood")) {
			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) {
				CheckNull(cepp);
				if (!strcmp(cepp->ce_varname, "unknown-flood-bantime")) 
				{
					CheckDuplicate(cepp, anti_flood_unknown_flood_bantime, "anti-flood::unknown-flood-bantime");
				}
				else if (!strcmp(cepp->ce_varname, "unknown-flood-amount")) {
					CheckDuplicate(cepp, anti_flood_unknown_flood_amount, "anti-flood::unknown-flood-amount");
				}
#ifdef NO_FLOOD_AWAY
				else if (!strcmp(cepp->ce_varname, "away-count")) {
					int temp = atol(cepp->ce_vardata);
					CheckDuplicate(cepp, anti_flood_away_count, "anti-flood::away-count");
					if (temp < 1 || temp > 255)
					{
						config_error("%s:%i: set::anti-flood::away-count must be between 1 and 255",
							cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum);
						errors++;
					}
				}
				else if (!strcmp(cepp->ce_varname, "away-period")) {
					int temp = config_checkval(cepp->ce_vardata, CFG_TIME);
					CheckDuplicate(cepp, anti_flood_away_period, "anti-flood::away-period");
					if (temp < 10)
					{
						config_error("%s:%i: set::anti-flood::away-period must be greater than 9",
							cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum);
						errors++;
					}
				}
				else if (!strcmp(cepp->ce_varname, "away-flood"))
				{
					int cnt, period;
					if (settings.has_anti_flood_away_period)
					{
						config_warn("%s:%d: set::anti-flood::away-flood overrides set::anti-flood::away-period",
							cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum);
						continue;
					}
					if (settings.has_anti_flood_away_count)
					{
						config_warn("%s:%d: set::anti-flood::away-flood overrides set::anti-flood::away-count",
							cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum);
						continue;
					}
					settings.has_anti_flood_away_period = 1;
					settings.has_anti_flood_away_count = 1;
					if (!config_parse_flood(cepp->ce_vardata, &cnt, &period) ||
					    (cnt < 1) || (cnt > 255) || (period < 10))
					{
						config_error("%s:%i: set::anti-flood::away-flood error. Syntax is '<count>:<period>' (eg 5:60), "
						             "count should be 1-255, period should be greater than 9",
							cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum);
						errors++;
					}
				}
#endif
				else if (!strcmp(cepp->ce_varname, "nick-flood"))
				{
					int cnt, period;
					CheckDuplicate(cepp, anti_flood_nick_flood, "anti-flood::nick-flood");
					if (!config_parse_flood(cepp->ce_vardata, &cnt, &period) ||
					    (cnt < 1) || (cnt > 255) || (period < 5))
					{
						config_error("%s:%i: set::anti-flood::away-flood error. Syntax is '<count>:<period>' (eg 5:60), "
						             "count should be 1-255, period should be greater than 4",
							cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum);
						errors++;
					}
				}
				else
				{
					config_error_unknownopt(cepp->ce_fileptr->cf_filename,
						cepp->ce_varlinenum, "set::anti-flood",
						cepp->ce_varname);
					errors++;
					continue;
				}
			}
		}
		else if (!strcmp(cep->ce_varname, "options")) {
			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) {
				if (!strcmp(cepp->ce_varname, "webtv-support")) 
				{
					CheckDuplicate(cepp, options_webtv_support, "options::webtv-support");
				}
				else if (!strcmp(cepp->ce_varname, "hide-ulines")) 
				{
					CheckDuplicate(cepp, options_hide_ulines, "options::hide-ulines");
				}
				else if (!strcmp(cepp->ce_varname, "flat-map")) {
					CheckDuplicate(cepp, options_flat_map, "options::flat-map");
				}
				else if (!strcmp(cepp->ce_varname, "show-opermotd")) {
					CheckDuplicate(cepp, options_show_opermotd, "options::show-opermotd");
				}
				else if (!strcmp(cepp->ce_varname, "identd-check")) {
					CheckDuplicate(cepp, options_identd_check, "options::identd-check");
				}
				else if (!strcmp(cepp->ce_varname, "fail-oper-warn")) {
					CheckDuplicate(cepp, options_fail_oper_warn, "options::fail-oper-warn");
				}
				else if (!strcmp(cepp->ce_varname, "show-connect-info")) {
					CheckDuplicate(cepp, options_show_connect_info, "options::show-connect-info");
				}
				else if (!strcmp(cepp->ce_varname, "dont-resolve")) {
					CheckDuplicate(cepp, options_dont_resolve, "options::dont-resolve");
				}
				else if (!strcmp(cepp->ce_varname, "mkpasswd-for-everyone")) {
					CheckDuplicate(cepp, options_mkpasswd_for_everyone, "options::mkpasswd-for-everyone");
				}
				else if (!strcmp(cepp->ce_varname, "allow-insane-bans")) {
					CheckDuplicate(cepp, options_allow_insane_bans, "options::allow-insane-bans");
				}
				else if (!strcmp(cepp->ce_varname, "allow-part-if-shunned")) {
					CheckDuplicate(cepp, options_allow_part_if_shunned, "options::allow-part-if-shunned");
				}
				else if (!strcmp(cepp->ce_varname, "disable-cap")) {
					CheckDuplicate(cepp, options_disable_cap, "options::disable-cap");
				}
				else
				{
					config_error_unknownopt(cepp->ce_fileptr->cf_filename,
						cepp->ce_varlinenum, "set::options",
						cepp->ce_varname);
					errors++;
					continue;
				}
			}
		}
		else if (!strcmp(cep->ce_varname, "hosts")) {
			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next)
			{
				char *c, *host;
				if (!cepp->ce_vardata)
				{
					config_error_empty(cepp->ce_fileptr->cf_filename,
						cepp->ce_varlinenum, "set::hosts",
						cepp->ce_varname);
					errors++;
					continue;
				} 
				if (!strcmp(cepp->ce_varname, "local")) {
					CheckDuplicate(cepp, hosts_local, "hosts::local");
				}
				else if (!strcmp(cepp->ce_varname, "global")) {
					CheckDuplicate(cepp, hosts_global, "hosts::global");
				}
				else if (!strcmp(cepp->ce_varname, "coadmin")) {
					CheckDuplicate(cepp, hosts_coadmin, "hosts::coadmin");
				}
				else if (!strcmp(cepp->ce_varname, "admin")) {
					CheckDuplicate(cepp, hosts_admin, "hosts::admin");
				}
				else if (!strcmp(cepp->ce_varname, "servicesadmin")) {
					CheckDuplicate(cepp, hosts_servicesadmin, "hosts::servicesadmin");
				}
				else if (!strcmp(cepp->ce_varname, "netadmin")) {
					CheckDuplicate(cepp, hosts_netadmin, "hosts::netadmin");
				}
				else if (!strcmp(cepp->ce_varname, "rootadmin")) {
					CheckDuplicate(cepp, hosts_rootadmin, "hosts::rootadmin");
				}
				else if (!strcmp(cepp->ce_varname, "owner")) {
					CheckDuplicate(cepp, hosts_owner, "hosts::owner");
				}
				else if (!strcmp(cepp->ce_varname, "superadmin")) {
					CheckDuplicate(cepp, hosts_superadmin, "hosts::superadmin");
				}
				else if (!strcmp(cepp->ce_varname, "techadmin")) {
					CheckDuplicate(cepp, hosts_techadmin, "hosts::techadmin");
				}
				else if (!strcmp(cepp->ce_varname, "host-on-oper-up")) {
					CheckDuplicate(cepp, hosts_host_on_oper_up, "hosts::host-on-oper-up");
				}
				else
				{
					config_error_unknown(cepp->ce_fileptr->cf_filename,
						cepp->ce_varlinenum, "set::hosts", cepp->ce_varname);
					errors++;
					continue;

				}
				if ((c = strchr(cepp->ce_vardata, '@')))
				{
					char *tmp;
					if (!(*(c+1)) || (c-cepp->ce_vardata) > USERLEN ||
					    c == cepp->ce_vardata)
					{
						config_error("%s:%i: illegal value for set::hosts::%s",
							     cepp->ce_fileptr->cf_filename,
							     cepp->ce_varlinenum, 
							     cepp->ce_varname);
						errors++;
						continue;
					}
					for (tmp = cepp->ce_vardata; tmp != c; tmp++)
					{
						if (*tmp == '~' && tmp == cepp->ce_vardata)
							continue;
						if (!isallowed(*tmp))
							break;
					}
					if (tmp != c)
					{
						config_error("%s:%i: illegal value for set::hosts::%s",
							     cepp->ce_fileptr->cf_filename,
							     cepp->ce_varlinenum, 
							     cepp->ce_varname);
						errors++;
						continue;
					}
					host = c+1;
				}
				else
					host = cepp->ce_vardata;
				if (strlen(host) > HOSTLEN)
				{
					config_error("%s:%i: illegal value for set::hosts::%s",
						     cepp->ce_fileptr->cf_filename,
						     cepp->ce_varlinenum, 
						     cepp->ce_varname);
					errors++;
					continue;
				}
				for (; *host; host++)
				{
					if (!isallowed(*host) && *host != ':')
					{
						config_error("%s:%i: illegal value for set::hosts::%s",
							     cepp->ce_fileptr->cf_filename,
							     cepp->ce_varlinenum, 
							     cepp->ce_varname);
						errors++;
						continue;
					}
				}
			}
		}
		else if (!strcmp(cep->ce_varname, "cloak-keys"))
		{
			CheckDuplicate(cep, cloak_keys, "cloak-keys");
			for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next)
			{
				int value, errs = 0;
				if (h->owner && !(h->owner->flags & MODFLAG_TESTING)
				    && !(h->owner->options & MOD_OPT_PERM))
					continue;
				value = (*(h->func.intfunc))(conf, cep, CONFIG_CLOAKKEYS, &errs);

				if (value == 1)
					break;
				if (value == -1)
				{
					errors += errs;
					break;
				}
				if (value == -2) 
					errors += errs;
			}
		}
		else if (!strcmp(cep->ce_varname, "scan")) {
			config_status("%s:%i: set::scan: WARNING: scanner support has been removed, "
			    "use BOPM instead: http://www.blitzed.org/bopm/ (*NIX) / http://vulnscan.org/winbopm/ (Windows)",
				cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
		}
		else if (!strcmp(cep->ce_varname, "ident")) {
			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next)
			{
				int is_ok = 0;
				CheckNull(cepp);
				if (!strcmp(cepp->ce_varname, "connect-timeout"))
				{
					is_ok = 1;
					CheckDuplicate(cepp, ident_connect_timeout, "ident::connect-timeout");
				}
				else if (!strcmp(cepp->ce_varname, "read-timeout"))
				{
					is_ok = 1;
					CheckDuplicate(cepp, ident_read_timeout, "ident::read-timeout");
				}
				if (is_ok)
				{
					int v = config_checkval(cepp->ce_vardata,CFG_TIME);
					if ((v > 60) || (v < 1))
					{
						config_error("%s:%i: set::ident::%s value out of range (%d), should be between 1 and 60.",
							cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, cepp->ce_varname, v);
						errors++;
						continue;
					}
				} else {
					config_error_unknown(cepp->ce_fileptr->cf_filename,
						cepp->ce_varlinenum, "set::ident",
						cepp->ce_varname);
					errors++;
					continue;
				}
			}
		}
		else if (!strcmp(cep->ce_varname, "timesync") || !strcmp(cep->ce_varname, "timesynch")) {
			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next)
			{
				CheckNull(cepp);
				if (!strcmp(cepp->ce_varname, "enabled"))
				{
				}
				else if (!strcmp(cepp->ce_varname, "timeout"))
				{
					int v = config_checkval(cepp->ce_vardata,CFG_TIME);
					if ((v > 5) || (v < 1))
					{
						config_error("%s:%i: set::timesync::%s value out of range (%d), should be between 1 and 5 (higher=unreliable).",
							cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, cepp->ce_varname, v);
						errors++;
						continue;
					}
				} else if (!strcmp(cepp->ce_varname, "server"))
				{
				} else {
					config_error_unknown(cepp->ce_fileptr->cf_filename,
						cepp->ce_varlinenum, "set::timesync",
						cepp->ce_varname);
					errors++;
					continue;
				}
			}
		}
		else if (!strcmp(cep->ce_varname, "spamfilter")) {
			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next)
			{
				CheckNull(cepp);
				if (!strcmp(cepp->ce_varname, "ban-time"))
				{
					long x;
					CheckDuplicate(cepp, spamfilter_ban_time, "spamfilter::ban-time");
					x = config_checkval(cepp->ce_vardata,CFG_TIME);
					if ((x < 0) > (x > 2000000000))
					{
						config_error("%s:%i: set::spamfilter:ban-time: value '%ld' out of range",
							cep->ce_fileptr->cf_filename, cep->ce_varlinenum, x);
						errors++;
						continue;
					}
				} else
				if (!strcmp(cepp->ce_varname, "ban-reason"))
				{ 
					CheckDuplicate(cepp, spamfilter_ban_reason, "spamfilter::ban-reason");

				} 
				else if (!strcmp(cepp->ce_varname, "virus-help-channel"))
				{
					CheckDuplicate(cepp, spamfilter_virus_help_channel, "spamfilter::virus-help-channel");
					if ((cepp->ce_vardata[0] != '#') || (strlen(cepp->ce_vardata) > CHANNELLEN))
					{
						config_error("%s:%i: set::spamfilter:virus-help-channel: "
						             "specified channelname is too long or contains invalid characters (%s)",
						             cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
						             cepp->ce_vardata);
						errors++;
						continue;
					}
				} else 
				if (!strcmp(cepp->ce_varname, "virus-help-channel-deny"))
				{ 
					CheckDuplicate(cepp, spamfilter_virus_help_channel_deny, "spamfilter::virus-help-channel-deny");
				} else
				if (!strcmp(cepp->ce_varname, "except"))
				{ 
					CheckDuplicate(cepp, spamfilter_except, "spamfilter::except");
				} else
#ifdef SPAMFILTER_DETECTSLOW
				if (!strcmp(cepp->ce_varname, "detect-slow-warn"))
				{ 
				} else
				if (!strcmp(cepp->ce_varname, "detect-slow-fatal"))
				{ 
				} else
#endif
				if (!strcmp(cepp->ce_varname, "stop-on-first-match"))
				{
				} else
				{
					config_error_unknown(cepp->ce_fileptr->cf_filename,
						cepp->ce_varlinenum, "set::spamfilter",
						cepp->ce_varname);
					errors++;
					continue;
				}
			}
		}
/* TODO: FIX THIS */
		else if (!strcmp(cep->ce_varname, "default-bantime"))
		{
			long x;
			CheckDuplicate(cep, default_bantime, "default-bantime");
			CheckNull(cep);
			x = config_checkval(cep->ce_vardata,CFG_TIME);
			if ((x < 0) > (x > 2000000000))
			{
				config_error("%s:%i: set::default-bantime: value '%ld' out of range",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum, x);
				errors++;
			}
		}
		else if (!strcmp(cep->ce_varname, "ban-version-tkl-time")) {
			long x;
			CheckDuplicate(cep, ban_version_tkl_time, "ban-version-tkl-time");
			CheckNull(cep);
			x = config_checkval(cep->ce_vardata,CFG_TIME);
			if ((x < 0) > (x > 2000000000))
			{
				config_error("%s:%i: set::ban-version-tkl-time: value '%ld' out of range",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum, x);
				errors++;
			}
		}
#ifdef NEWCHFLOODPROT
		else if (!strcmp(cep->ce_varname, "modef-default-unsettime")) {
			int v;
			CheckDuplicate(cep, modef_default_unsettime, "modef-default-unsettime");
			CheckNull(cep);
			v = atoi(cep->ce_vardata);
			if ((v <= 0) || (v > 255))
			{
				config_error("%s:%i: set::modef-default-unsettime: value '%d' out of range (should be 1-255)",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum, v);
				errors++;
			}
		}
		else if (!strcmp(cep->ce_varname, "modef-max-unsettime")) {
			int v;
			CheckDuplicate(cep, modef_max_unsettime, "modef-max-unsettime");
			CheckNull(cep);
			v = atoi(cep->ce_vardata);
			if ((v <= 0) || (v > 255))
			{
				config_error("%s:%i: set::modef-max-unsettime: value '%d' out of range (should be 1-255)",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum, v);
				errors++;
			}
		}
#endif
		else if (!strcmp(cep->ce_varname, "ssl")) {
#ifdef USE_SSL
			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) {
				if (!strcmp(cepp->ce_varname, "egd")) {
					CheckDuplicate(cep, ssl_egd, "ssl::egd");
				}
				else if (!strcmp(cepp->ce_varname, "renegotiate-timeout"))
				{
					CheckDuplicate(cep, renegotiate_timeout, "ssl::renegotiate-timeout");
				}
				else if (!strcmp(cepp->ce_varname, "renegotiate-bytes"))
				{
					CheckDuplicate(cep, renegotiate_bytes, "ssl::renegotiate-bytes");
				}
				else if (!strcmp(cepp->ce_varname, "server-cipher-list"))
				{
					CheckNull(cepp);
					CheckDuplicate(cep, ssl_server_cipher_list, "ssl::server-cipher-list");
				}
				else if (!strcmp(cepp->ce_varname, "certificate"))
				{
					CheckNull(cepp);
					CheckDuplicate(cep, ssl_certificate, "ssl::certificate");
				}
				else if (!strcmp(cepp->ce_varname, "key"))
				{
					CheckNull(cepp);
					CheckDuplicate(cep, ssl_key, "ssl::key");
				}
				else if (!strcmp(cepp->ce_varname, "trusted-ca-file"))
				{
					CheckNull(cepp);
					CheckDuplicate(cep, ssl_trusted_ca_file, "ssl::trusted-ca-file");
				}
				else if (!strcmp(cepp->ce_varname, "options"))
				{
					CheckDuplicate(cep, ssl_options, "ssl::options");
					for (ceppp = cepp->ce_entries; ceppp; ceppp = ceppp->ce_next)
						if (!config_binary_flags_search(_SSLFlags, ceppp->ce_varname, ARRAY_SIZEOF(_SSLFlags)))
						{
							config_error("%s:%i: unknown SSL flag '%s'",
								     ceppp->ce_fileptr->cf_filename, 
								     ceppp->ce_varlinenum, ceppp->ce_varname);
							errors ++;
						}
				}
				else
				{
					config_error("%s:%i: unknown directive set::ssl::%s",
						cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum,
						cepp->ce_varname);
					errors++;
				}
			}
#endif /* USE_SSL */
		}
		else if (!strcmp(cep->ce_varname, "default-ipv6-clone-mask"))
		{
			/* keep this in sync with _test_allow() */
			int ipv6mask;
			ipv6mask = atoi(cep->ce_vardata);
			if (ipv6mask == 0)
			{
				config_error("%s:%d: set::default-ipv6-clone-mask given a value of zero. This cannnot be correct, as it would treat all IPv6 hosts as one host.",
					     cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
				errors++;
			}
			if (ipv6mask > 128)
			{
				config_error("%s:%d: set::default-ipv6-clone-mask was set to %d. The maximum value is 128.",
					     cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
					     ipv6mask);
				errors++;
			}
			if (ipv6mask <= 32)
			{
				config_warn("%s:%d: set::default-ipv6-clone-mask was given a very small value.",
					    cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
			}
		}
		else
		{
			int used = 0;
			for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next) 
			{
				int value, errs = 0;
				if (h->owner && !(h->owner->flags & MODFLAG_TESTING) &&
				                !(h->owner->options & MOD_OPT_PERM))
					continue;
				value = (*(h->func.intfunc))(conf,cep,CONFIG_SET, &errs);
				if (value == 2)
					used = 1;
				if (value == 1)
				{
					used = 1;
					break;
				}
				if (value == -1)
				{
					used = 1;
					errors += errs;
					break;
				}
				if (value == -2)
				{
					used = 1;
					errors += errs;
				}
			}
			if (!used) {
				config_error("%s:%i: unknown directive set::%s",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
					cep->ce_varname);
				errors++;
			}
		}
	}
	return errors;
}

int	_conf_loadmodule(ConfigFile *conf, ConfigEntry *ce)
{
#ifdef GLOBH
	glob_t files;
	int i;
#elif defined(_WIN32)
	HANDLE hFind;
	WIN32_FIND_DATA FindData;
	char cPath[MAX_PATH], *cSlash = NULL, *path;
#endif
	char *ret;
	if (!ce->ce_vardata)
	{
		config_status("%s:%i: loadmodule without filename",
			ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
		return -1;
	}
#ifdef GLOBH
#if defined(__OpenBSD__) && defined(GLOB_LIMIT)
	glob(ce->ce_vardata, GLOB_NOSORT|GLOB_NOCHECK|GLOB_LIMIT, NULL, &files);
#else
	glob(ce->ce_vardata, GLOB_NOSORT|GLOB_NOCHECK, NULL, &files);
#endif
	if (!files.gl_pathc) {
		globfree(&files);
		config_status("%s:%i: loadmodule %s: failed to load",
			ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			ce->ce_vardata);
		return -1;
	}	
	for (i = 0; i < files.gl_pathc; i++) {
		if ((ret = Module_Create(files.gl_pathv[i]))) {
			config_status("%s:%i: loadmodule %s: failed to load: %s",
				ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
				files.gl_pathv[i], ret);
			return -1;
		}
	}
	globfree(&files);
#elif defined(_WIN32)
	bzero(cPath,MAX_PATH);
	if (strchr(ce->ce_vardata, '/') || strchr(ce->ce_vardata, '\\')) {
		strncpyzt(cPath,ce->ce_vardata,MAX_PATH);
		cSlash=cPath+strlen(cPath);
		while(*cSlash != '\\' && *cSlash != '/' && cSlash > cPath)
			cSlash--; 
		*(cSlash+1)=0;
	}
	hFind = FindFirstFile(ce->ce_vardata, &FindData);
	if (!FindData.cFileName || hFind == INVALID_HANDLE_VALUE) {
		config_status("%s:%i: loadmodule %s: failed to load",
			ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			ce->ce_vardata);
		FindClose(hFind);
		return -1;
	}

	if (cPath) {
		path = MyMalloc(strlen(cPath) + strlen(FindData.cFileName)+1);
		strcpy(path,cPath);
		strcat(path,FindData.cFileName);
		if ((ret = Module_Create(path))) {
			config_status("%s:%i: loadmodule %s: failed to load: %s",
				ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
				path, ret);
			free(path);
			return -1;
		}
		free(path);
	}
	else
	{
		if ((ret = Module_Create(FindData.cFileName))) {
			config_status("%s:%i: loadmodule %s: failed to load: %s",
				ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
				FindData.cFileName, ret);
			return -1;
		}
	}
	while (FindNextFile(hFind, &FindData) != 0) {
		if (cPath) {
			path = MyMalloc(strlen(cPath) + strlen(FindData.cFileName)+1);
			strcpy(path,cPath);
			strcat(path,FindData.cFileName);		
			if ((ret = Module_Create(path)))
			{
				config_status("%s:%i: loadmodule %s: failed to load: %s",
					ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
					FindData.cFileName, ret);
				free(path);
				return -1;
			}
			free(path);
		}
		else
		{
			if ((ret = Module_Create(FindData.cFileName)))
			{
				config_status("%s:%i: loadmodule %s: failed to load: %s",
					ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
					FindData.cFileName, ret);
				return -1;
			}
		}
	}
	FindClose(hFind);
#else
	if ((ret = Module_Create(ce->ce_vardata))) {
			config_status("%s:%i: loadmodule %s: failed to load: %s",
				ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
				ce->ce_vardata, ret);
				return -1;
	}
#endif
	return 1;
}

int	_test_loadmodule(ConfigFile *conf, ConfigEntry *ce)
{
	return 0;
}

/*
 * Actually use configuration
*/

void	run_configuration(void)
{
	ConfigItem_listen 	*listenptr;

	for (listenptr = conf_listen; listenptr; listenptr = (ConfigItem_listen *) listenptr->next)
	{
		if (!(listenptr->options & LISTENER_BOUND))
		{
			if (add_listener2(listenptr) == -1)
			{
				ircd_log(LOG_ERROR, "Failed to bind to %s:%i", listenptr->ip, listenptr->port);
			}
				else
			{
			}
		}
		else
		{
			if (listenptr->listener)
			{
				listenptr->listener->umodes = 
					(listenptr->options & ~LISTENER_BOUND) ? listenptr->options : LISTENER_NORMAL;
				listenptr->listener->umodes |= LISTENER_BOUND;
			}
		}
	}
}

int	_conf_offchans(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry *cep, *cepp;

	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		ConfigItem_offchans *of = MyMallocEx(sizeof(ConfigItem_offchans));
		strlcpy(of->chname, cep->ce_varname, CHANNELLEN+1);
		for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next)
		{
			if (!strcmp(cepp->ce_varname, "topic"))
				of->topic = strdup(cepp->ce_vardata);
		}
		AddListItem(of, conf_offchans);
	}
	return 0;
}

int	_test_offchans(ConfigFile *conf, ConfigEntry *ce)
{
	int errors = 0;
	ConfigEntry *cep, *cep2;
	char checkchan[CHANNELLEN + 1];
	
	if (!ce->ce_entries)
	{
		config_error("%s:%i: empty official-channels block", 
			ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
		return 1;
	}
	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (strlen(cep->ce_varname) > CHANNELLEN)
		{
			config_error("%s:%i: official-channels: '%s' name too long (max %d characters).",
				cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname, CHANNELLEN);
			errors++;
			continue;
		}
		strcpy(checkchan, cep->ce_varname); /* safe */
		clean_channelname(checkchan);
		if (strcmp(checkchan, cep->ce_varname) || (*cep->ce_varname != '#'))
		{
			config_error("%s:%i: official-channels: '%s' is not a valid channel name.",
				cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname);
			errors++;
			continue;
		}
		for (cep2 = cep->ce_entries; cep2; cep2 = cep2->ce_next)
		{
			if (!cep2->ce_vardata)
			{
				config_error_empty(cep2->ce_fileptr->cf_filename, 
					cep2->ce_varlinenum, "official-channels", 
					cep2->ce_varname);
				errors++;
				continue;
			}
			if (!strcmp(cep2->ce_varname, "topic"))
			{
				if (strlen(cep2->ce_vardata) > TOPICLEN)
				{
					config_error("%s:%i: official-channels::%s: topic too long (max %d characters).",
						cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, cep->ce_varname, TOPICLEN);
					errors++;
					continue;
				}
			} else {
				config_error_unknown(cep2->ce_fileptr->cf_filename, 
					cep2->ce_varlinenum, "official-channels", 
					cep2->ce_varname);
				errors++;
				continue;
			}
		}
	}
	return errors;
}

int	_conf_alias(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigItem_alias *alias = NULL;
	ConfigItem_alias_format *format;
	ConfigEntry 	    	*cep, *cepp;
	aCommand *cmptr;

	if ((cmptr = find_Command(ce->ce_vardata, 0, M_ALIAS)))
		del_Command(ce->ce_vardata, NULL, cmptr->func);
	if (find_Command_simple(ce->ce_vardata))
	{
		config_warn("%s:%i: Alias '%s' would conflict with command (or server token) '%s', alias not added.",
			ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			ce->ce_vardata, ce->ce_vardata);
		return 0;
	}
	if ((alias = Find_alias(ce->ce_vardata)))
		DelListItem(alias, conf_alias);
	alias = MyMallocEx(sizeof(ConfigItem_alias));
	ircstrdup(alias->alias, ce->ce_vardata);
	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!strcmp(cep->ce_varname, "format")) {
			format = MyMallocEx(sizeof(ConfigItem_alias_format));
			ircstrdup(format->format, cep->ce_vardata);
			regcomp(&format->expr, cep->ce_vardata, REG_ICASE|REG_EXTENDED);
			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) {
				if (!strcmp(cepp->ce_varname, "nick") ||
				    !strcmp(cepp->ce_varname, "target") ||
				    !strcmp(cepp->ce_varname, "command")) {
					ircstrdup(format->nick, cepp->ce_vardata);
				}
				else if (!strcmp(cepp->ce_varname, "parameters")) {
					ircstrdup(format->parameters, cepp->ce_vardata);
				}
				else if (!strcmp(cepp->ce_varname, "type")) {
					if (!strcmp(cepp->ce_vardata, "services"))
						format->type = ALIAS_SERVICES;
					else if (!strcmp(cepp->ce_vardata, "stats"))
						format->type = ALIAS_STATS;
					else if (!strcmp(cepp->ce_vardata, "normal"))
						format->type = ALIAS_NORMAL;
					else if (!strcmp(cepp->ce_vardata, "channel"))
						format->type = ALIAS_CHANNEL;
					else if (!strcmp(cepp->ce_vardata, "real"))
						format->type = ALIAS_REAL;
				}
			}
			AddListItem(format, alias->format);
		}		
				
		else if (!strcmp(cep->ce_varname, "nick") || !strcmp(cep->ce_varname, "target")) 
		{
			ircstrdup(alias->nick, cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "type")) {
			if (!strcmp(cep->ce_vardata, "services"))
				alias->type = ALIAS_SERVICES;
			else if (!strcmp(cep->ce_vardata, "stats"))
				alias->type = ALIAS_STATS;
			else if (!strcmp(cep->ce_vardata, "normal"))
				alias->type = ALIAS_NORMAL;
			else if (!strcmp(cep->ce_vardata, "channel"))
				alias->type = ALIAS_CHANNEL;
			else if (!strcmp(cep->ce_vardata, "command"))
				alias->type = ALIAS_COMMAND;
		}
		else if (!strcmp(cep->ce_varname, "spamfilter"))
			alias->spamfilter = config_checkval(cep->ce_vardata, CFG_YESNO);
	}
	if (BadPtr(alias->nick) && alias->type != ALIAS_COMMAND) {
		ircstrdup(alias->nick, alias->alias); 
	}
	add_CommandX(alias->alias, NULL, m_alias, 1, M_USER|M_ALIAS);
	AddListItem(alias, conf_alias);
	return 0;
}


int _test_alias(ConfigFile *conf, ConfigEntry *ce) { 
	int errors = 0;
	ConfigEntry *cep, *cepp;
	char has_type = 0, has_target = 0, has_format = 0;
	char type = 0;

	if (!ce->ce_entries)
	{
		config_error("%s:%i: empty alias block", 
			ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
		return 1;
	}
	if (!ce->ce_vardata) 
	{
		config_error("%s:%i: alias without name", 
			ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
		errors++;
	}
	else if (!find_Command(ce->ce_vardata, 0, M_ALIAS) && find_Command(ce->ce_vardata, 0, 0)) {
		config_status("%s:%i: %s is an existing command, can not add alias",
			ce->ce_fileptr->cf_filename, ce->ce_varlinenum, ce->ce_vardata);
		errors++;
	}
	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (config_is_blankorempty(cep, "alias"))
		{
			errors++;
			continue;
		}
		if (!strcmp(cep->ce_varname, "format")) {
			int errorcode, errorbufsize;
			char *errorbuf;
			regex_t expr;
			char has_type = 0, has_target = 0, has_parameters = 0;

			has_format = 1;
			errorcode = regcomp(&expr, cep->ce_vardata, REG_ICASE|REG_EXTENDED);
                        if (errorcode > 0)
                        {
                                errorbufsize = regerror(errorcode, &expr, NULL, 0)+1;
                                errorbuf = MyMalloc(errorbufsize);
                                regerror(errorcode, &expr, errorbuf, errorbufsize);
                                config_error("%s:%i: alias::format contains an invalid regex: %s",
 					cep->ce_fileptr->cf_filename,
 					cep->ce_varlinenum,
 					errorbuf);
                                errors++;
                                free(errorbuf);
                        }
			regfree(&expr);	
			for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) {
				if (config_is_blankorempty(cepp, "alias::format"))
				{
					errors++;
					continue;
				}
				if (!strcmp(cepp->ce_varname, "nick") ||
				    !strcmp(cepp->ce_varname, "command") ||
				    !strcmp(cepp->ce_varname, "target"))
				{
					if (has_target)
					{
						config_warn_duplicate(cepp->ce_fileptr->cf_filename,
							cepp->ce_varlinenum, 
							"alias::format::target");
						continue;
					}
					has_target = 1;
				}
				else if (!strcmp(cepp->ce_varname, "type"))
				{
					if (has_type)
					{
						config_warn_duplicate(cepp->ce_fileptr->cf_filename,
							cepp->ce_varlinenum, 
							"alias::format::type");
						continue;
					}
					has_type = 1;
					if (!strcmp(cepp->ce_vardata, "services"))
						;
					else if (!strcmp(cepp->ce_vardata, "stats"))
						;
					else if (!strcmp(cepp->ce_vardata, "normal"))
						;
					else if (!strcmp(cepp->ce_vardata, "channel"))
						;
					else if (!strcmp(cepp->ce_vardata, "real"))
						;
					else 
					{
						config_error("%s:%i: unknown alias type",
						cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum);
						errors++;
					}
				}
				else if (!strcmp(cepp->ce_varname, "parameters")) 
				{
					if (has_parameters)
					{
						config_warn_duplicate(cepp->ce_fileptr->cf_filename,
							cepp->ce_varlinenum, 
							"alias::format::parameters");
						continue;
					}
					has_parameters = 1;
				}
				else 
				{
					config_error_unknown(cepp->ce_fileptr->cf_filename, 
						cepp->ce_varlinenum, "alias::format",
						cepp->ce_varname);
					errors++;
				}
			}
			if (!has_target)
			{
				config_error_missing(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "alias::format::target");
				errors++;
			}
			if (!has_type)
			{
				config_error_missing(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "alias::format::type");
				errors++;
			}
			if (!has_parameters)
			{
				config_error_missing(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "alias::format::parameters");
				errors++;
			}
		}
		else if (!strcmp(cep->ce_varname, "nick") || !strcmp(cep->ce_varname, "target")) 
		{
			if (has_target)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "alias::target");
				continue;
			}
			has_target = 1;
		}
		else if (!strcmp(cep->ce_varname, "type")) {
			if (has_type)
			{
				config_warn_duplicate(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "alias::type");
				continue;
			}
			has_type = 1;
			if (!strcmp(cep->ce_vardata, "services"))
				;
			else if (!strcmp(cep->ce_vardata, "stats"))
				;
			else if (!strcmp(cep->ce_vardata, "normal"))
				;
			else if (!strcmp(cep->ce_vardata, "channel"))
				;
			else if (!strcmp(cep->ce_vardata, "command"))
				type = 'c';
			else {
				config_error("%s:%i: unknown alias type",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
				errors++;
			}
		}
		else if (!strcmp(cep->ce_varname, "spamfilter"))
			;
		else {
			config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
				"alias", cep->ce_varname);
			errors++;
		}
	}
	if (!has_type)
	{
		config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
			"alias::type");
		errors++;
	}
	if (!has_format && type == 'c')
	{
		config_error("%s:%d: alias::type is 'command' but no alias::format was specified",
			ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
		errors++;
	}
	else if (has_format && type != 'c')
	{
		config_error("%s:%d: alias::format specified when type is not 'command'",
			ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
		errors++;
	}
	return errors; 
}

int	_conf_deny(ConfigFile *conf, ConfigEntry *ce)
{
Hook *h;

	if (!strcmp(ce->ce_vardata, "dcc"))
		_conf_deny_dcc(conf, ce);
	else if (!strcmp(ce->ce_vardata, "channel"))
		_conf_deny_channel(conf, ce);
	else if (!strcmp(ce->ce_vardata, "link"))
		_conf_deny_link(conf, ce);
	else if (!strcmp(ce->ce_vardata, "version"))
		_conf_deny_version(conf, ce);
	else
	{
		int value;
		for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next)
		{
			value = (*(h->func.intfunc))(conf,ce,CONFIG_DENY);
			if (value == 1)
				break;
		}
		return 0;
	}
	return 0;
}

int	_conf_deny_dcc(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigItem_deny_dcc 	*deny = NULL;
	ConfigEntry 	    	*cep;

	deny = MyMallocEx(sizeof(ConfigItem_deny_dcc));
	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!strcmp(cep->ce_varname, "filename"))
		{
			ircstrdup(deny->filename, cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "reason"))
		{
			ircstrdup(deny->reason, cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "soft"))
		{
			int x = config_checkval(cep->ce_vardata,CFG_YESNO);
			if (x == 1)
				deny->flag.type = DCCDENY_SOFT;
		}
	}
	if (!deny->reason)
	{
		if (deny->flag.type == DCCDENY_HARD)
			ircstrdup(deny->reason, "Possible infected virus file");
		else
			ircstrdup(deny->reason, "Possible executable content");
	}
	AddListItem(deny, conf_deny_dcc);
	return 0;
}

int	_conf_deny_channel(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigItem_deny_channel 	*deny = NULL;
	ConfigEntry 	    	*cep;

	deny = MyMallocEx(sizeof(ConfigItem_deny_channel));
	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!strcmp(cep->ce_varname, "channel"))
		{
			ircstrdup(deny->channel, cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "redirect"))
		{
			ircstrdup(deny->redirect, cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "reason"))
		{
			ircstrdup(deny->reason, cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "warn"))
		{
			deny->warn = config_checkval(cep->ce_vardata,CFG_YESNO);
		}
		else if (!strcmp(cep->ce_varname, "class"))
		{
			ircstrdup(deny->class, cep->ce_vardata);
		}
	}
	AddListItem(deny, conf_deny_channel);
	return 0;
}
int	_conf_deny_link(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigItem_deny_link 	*deny = NULL;
	ConfigEntry 	    	*cep;

	deny = MyMallocEx(sizeof(ConfigItem_deny_link));
	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!strcmp(cep->ce_varname, "mask"))
		{
			ircstrdup(deny->mask, cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "rule"))
		{
			deny->rule = (char *)crule_parse(cep->ce_vardata);
			ircstrdup(deny->prettyrule, cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "type")) {
			if (!strcmp(cep->ce_vardata, "all"))
				deny->flag.type = CRULE_ALL;
			else if (!strcmp(cep->ce_vardata, "auto"))
				deny->flag.type = CRULE_AUTO;
		}
	}
	AddListItem(deny, conf_deny_link);
	return 0;
}

int	_conf_deny_version(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigItem_deny_version *deny = NULL;
	ConfigEntry 	    	*cep;

	deny = MyMallocEx(sizeof(ConfigItem_deny_version));
	for (cep = ce->ce_entries; cep; cep = cep->ce_next)
	{
		if (!strcmp(cep->ce_varname, "mask"))
		{
			ircstrdup(deny->mask, cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "version"))
		{
			ircstrdup(deny->version, cep->ce_vardata);
		}
		else if (!strcmp(cep->ce_varname, "flags"))
		{
			ircstrdup(deny->flags, cep->ce_vardata);
		}
	}
	AddListItem(deny, conf_deny_version);
	return 0;
}

int     _test_deny(ConfigFile *conf, ConfigEntry *ce)
{
	ConfigEntry *cep;
	int	    errors = 0;
	Hook	*h;
	
	if (!ce->ce_vardata)
	{
		config_error("%s:%i: deny without type",	
			ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
		return 1;
	}
	if (!strcmp(ce->ce_vardata, "dcc"))
	{
		char has_filename = 0, has_reason = 0, has_soft = 0;
		for (cep = ce->ce_entries; cep; cep = cep->ce_next)
		{
			if (config_is_blankorempty(cep, "deny dcc"))
			{
				errors++;
				continue;
			}
			if (!strcmp(cep->ce_varname, "filename"))
			{
				if (has_filename)
				{
					config_warn_duplicate(cep->ce_fileptr->cf_filename,
						cep->ce_varlinenum, "deny dcc::filename");
					continue;
				}
				has_filename = 1;
			}
			else if (!strcmp(cep->ce_varname, "reason"))
			{
				if (has_reason)
				{
					config_warn_duplicate(cep->ce_fileptr->cf_filename,
						cep->ce_varlinenum, "deny dcc::reason");
					continue;
				}
				has_reason = 1;
			}
			else if (!strcmp(cep->ce_varname, "soft"))
			{
				if (has_soft)
				{
					config_warn_duplicate(cep->ce_fileptr->cf_filename,
						cep->ce_varlinenum, "deny dcc::soft");
					continue;
				}
				has_soft = 1;
			}
			else 
			{
				config_error_unknown(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "deny dcc", cep->ce_varname);
				errors++;
			}
		}
		if (!has_filename)
		{
			config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
				"deny dcc::filename");
			errors++;
		}
		if (!has_reason)
		{
			config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
				"deny dcc::reason");
			errors++;
		}
	}
	else if (!strcmp(ce->ce_vardata, "channel"))
	{
		char has_channel = 0, has_warn = 0, has_reason = 0, has_redirect = 0, has_class = 0;
		for (cep = ce->ce_entries; cep; cep = cep->ce_next)
		{
			if (config_is_blankorempty(cep, "deny channel"))
			{
				errors++;
				continue;
			}
			if (!strcmp(cep->ce_varname, "channel"))
			{
				if (has_channel)
				{
					config_warn_duplicate(cep->ce_fileptr->cf_filename,
						cep->ce_varlinenum, "deny channel::channel");
					continue;
				}
				has_channel = 1;
			}
			else if (!strcmp(cep->ce_varname, "redirect"))
			{
				if (has_redirect)
				{
					config_warn_duplicate(cep->ce_fileptr->cf_filename,
						cep->ce_varlinenum, "deny channel::redirect");
					continue;
				}
				has_redirect = 1;
			}
			else if (!strcmp(cep->ce_varname, "reason"))
			{
				if (has_reason)
				{
					config_warn_duplicate(cep->ce_fileptr->cf_filename,
						cep->ce_varlinenum, "deny channel::reason");
					continue;
				}
				has_reason = 1;
			}
			else if (!strcmp(cep->ce_varname, "warn"))
			{
				if (has_warn)
				{
					config_warn_duplicate(cep->ce_fileptr->cf_filename,
						cep->ce_varlinenum, "deny channel::warn");
					continue;
				}
				has_warn = 1;
			}
			else if (!strcmp(cep->ce_varname, "class"))
			{
				if (has_class)
				{
					config_warn_duplicate(cep->ce_fileptr->cf_filename,
						cep->ce_varlinenum, "deny channel::class");
					continue;
				}
				has_class = 1;
			}
			else 
			{
				config_error_unknown(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "deny channel", cep->ce_varname);
				errors++;
			}
		}
		if (!has_channel)
		{
			config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
				"deny channel::channel");
			errors++;
		}
		if (!has_reason)
		{
			config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
				"deny channel::reason");
			errors++;
		}
	}
	else if (!strcmp(ce->ce_vardata, "link"))
	{
		char has_mask = 0, has_rule = 0, has_type = 0;
		for (cep = ce->ce_entries; cep; cep = cep->ce_next)
		{
			if (config_is_blankorempty(cep, "deny link"))
			{
				errors++;
				continue;
			}
			if (!strcmp(cep->ce_varname, "mask"))
			{
				if (has_mask)
				{
					config_warn_duplicate(cep->ce_fileptr->cf_filename,
						cep->ce_varlinenum, "deny link::mask");
					continue;
				}
				has_mask = 1;
			}
			else if (!strcmp(cep->ce_varname, "rule"))
			{
				int val = 0;
				if (has_rule)
				{
					config_warn_duplicate(cep->ce_fileptr->cf_filename,
						cep->ce_varlinenum, "deny link::rule");
					continue;
				}
				has_rule = 1;
				if ((val = crule_test(cep->ce_vardata)))
				{
					config_error("%s:%i: deny link::rule contains an invalid expression: %s",
						cep->ce_fileptr->cf_filename,
						cep->ce_varlinenum,
						crule_errstring(val));
					errors++;
				}
			}
			else if (!strcmp(cep->ce_varname, "type"))
			{
				if (has_type)
				{
					config_warn_duplicate(cep->ce_fileptr->cf_filename,
						cep->ce_varlinenum, "deny link::type");
					continue;
				}
				has_type = 1;
				if (!strcmp(cep->ce_vardata, "auto"))
				;
				else if (!strcmp(cep->ce_vardata, "all"))
				;
				else {
					config_status("%s:%i: unknown deny link type",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
					errors++;
				}
			}	
			else 
			{
				config_error_unknown(cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, "deny link", cep->ce_varname);
				errors++;
			}
		}
		if (!has_mask)
		{
			config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
				"deny link::mask");
			errors++;
		}	
		if (!has_rule)
		{
			config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
				"deny link::rule");
			errors++;
		}
		if (!has_type)
		{
			config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
				"deny link::type");
			errors++;
		}
	}
	else if (!strcmp(ce->ce_vardata, "version"))
	{
		char has_mask = 0, has_version = 0, has_flags = 0;
		for (cep = ce->ce_entries; cep; cep = cep->ce_next)
		{
			if (config_is_blankorempty(cep, "deny version"))
			{
				errors++;
				continue;
			}
			if (!strcmp(cep->ce_varname, "mask"))
			{
				if (has_mask)
				{
					config_warn_duplicate(cep->ce_fileptr->cf_filename,
						cep->ce_varlinenum, "deny version::mask");
					continue;
				}
				has_mask = 1;
			}
			else if (!strcmp(cep->ce_varname, "version"))
			{
				if (has_version)
				{
					config_warn_duplicate(cep->ce_fileptr->cf_filename,
						cep->ce_varlinenum, "deny version::version");
					continue;
				}
				has_version = 1;
			}
			else if (!strcmp(cep->ce_varname, "flags"))
			{
				if (has_flags)
				{
					config_warn_duplicate(cep->ce_fileptr->cf_filename,
						cep->ce_varlinenum, "deny version::flags");
					continue;
				}
				has_flags = 1;
			}
			else 
			{
				config_error_unknown(cep->ce_fileptr->cf_filename, 
					cep->ce_varlinenum, "deny version", cep->ce_varname);
				errors++;
			}
		}
		if (!has_mask)
		{
			config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
				"deny version::mask");
			errors++;
		}
		if (!has_version)
		{
			config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
				"deny version::version");
			errors++;
		}
		if (!has_flags)
		{
			config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
				"deny version::flags");
			errors++;
		}
	}
	else
	{
		int used = 0;
		for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next) 
		{
			int value, errs = 0;
			if (h->owner && !(h->owner->flags & MODFLAG_TESTING)
			    && !(h->owner->options & MOD_OPT_PERM))
				continue;
			value = (*(h->func.intfunc))(conf,ce,CONFIG_DENY, &errs);
			if (value == 2)
				used = 1;
			if (value == 1)
			{
				used = 1;
				break;
			}
			if (value == -1)
			{
				used = 1;
				errors += errs;
				break;
			}
			if (value == -2)
			{
				used = 1;
				errors += errs;
			}
		}
		if (!used) {
			config_error("%s:%i: unknown deny type %s",
				ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
				ce->ce_vardata);
			return 1;
		}
		return errors;
	}

	return errors;	
}

#ifdef USE_LIBCURL
static void conf_download_complete(const char *url, const char *file, const char *errorbuf, int cached, void *inc_key)
{
	ConfigItem_include *inc;
	if (!loop.ircd_rehashing)
		return;

	/*
	  use inc_key to find the correct include block. This
	  should be cheaper than using the full URL.
	 */
	for (inc = conf_include; inc; inc = (ConfigItem_include *)inc->next)
	{
		if ( inc_key != (void *)inc )
			continue;
		if (!(inc->flag.type & INCLUDE_REMOTE))
			continue;
		if (inc->flag.type & INCLUDE_NOTLOADED)
			continue;
		if (stricmp(url, inc->url))
			continue;

		inc->flag.type &= ~INCLUDE_DLQUEUED;
		break;
	}
	if (!inc)
	{
		ircd_log(LOG_ERROR, "Downloaded remote include which matches no include statement.");
		return;
	}

	if (!file && !cached)
		update_remote_include(inc, file, 0, errorbuf); /* DOWNLOAD FAILED */
	else
	{
		char *urlfile = url_getfilename(url);
		char *file_basename = unreal_getfilename(urlfile);
		char *tmp = unreal_mktemp("tmp", file_basename);
		free(urlfile);

		if (cached)
		{
			unreal_copyfileex(inc->file, tmp, 1);
#ifdef REMOTEINC_SPECIALCACHE
			unreal_copyfileex(inc->file, unreal_mkcache(url), 0);
#endif
			update_remote_include(inc, tmp, 0, NULL);
		}
		else
		{
			/*
			  copy/hardlink file to another file because our caller will
			  remove(file).
			*/
			unreal_copyfileex(file, tmp, 1);
			update_remote_include(inc, tmp, 0, NULL);
#ifdef REMOTEINC_SPECIALCACHE
			unreal_copyfileex(file, unreal_mkcache(url), 0);
#endif
		}
	}
	for (inc = conf_include; inc; inc = (ConfigItem_include *)inc->next)
	{
		if (inc->flag.type & INCLUDE_DLQUEUED)
			return;
	}
	rehash_internal(loop.rehash_save_cptr, loop.rehash_save_sptr, loop.rehash_save_sig);
}
#endif

int     rehash(aClient *cptr, aClient *sptr, int sig)
{
#ifdef USE_LIBCURL
	ConfigItem_include *inc;
	char found_remote = 0;
	if (loop.ircd_rehashing)
	{
		if (!sig)
			sendto_one(sptr, ":%s NOTICE %s :A rehash is already in progress",
				me.name, sptr->name);
		return 0;
	}

	loop.ircd_rehashing = 1;
	loop.rehash_save_cptr = cptr;
	loop.rehash_save_sptr = sptr;
	loop.rehash_save_sig = sig;
	for (inc = conf_include; inc; inc = (ConfigItem_include *)inc->next)
	{
		time_t modtime;
		if (!(inc->flag.type & INCLUDE_REMOTE))
			continue;

		if (inc->flag.type & INCLUDE_NOTLOADED)
			continue;
		found_remote = 1;
		modtime = unreal_getfilemodtime(inc->file);
		inc->flag.type |= INCLUDE_DLQUEUED;

		/*
		  use (void *)inc as the key for finding which
		  include block conf_download_complete() should use.
		*/
		download_file_async(inc->url, modtime, conf_download_complete, (void *)inc);
	}
	if (!found_remote)
		return rehash_internal(cptr, sptr, sig);
	return 0;
#else
	loop.ircd_rehashing = 1;
	return rehash_internal(cptr, sptr, sig);
#endif
}

int	rehash_internal(aClient *cptr, aClient *sptr, int sig)
{
	flush_connections(&me);
	if (sig == 1)
	{
		sendto_ops("SIGHUP sinyali alindi, %s dosyasi yeniden yukleniyor", configfile);
#ifdef	ULTRIX
		if (fork() > 0)
			exit(0);
		write_pidfile();
#endif
	}
	loop.ircd_rehashing = 1; /* double checking.. */
	if (init_conf(configfile, 1) == 0)
		run_configuration();
	if (sig == 1)
		reread_motdsandrules();
	unload_all_unused_snomasks();
	unload_all_unused_umodes();
	unload_all_unused_extcmodes();
	loop.ircd_rehashing = 0;	
	return 1;
}

void	link_cleanup(ConfigItem_link *link_ptr)
{
	ircfree(link_ptr->servername);
	ircfree(link_ptr->username);
	ircfree(link_ptr->bindip);
	ircfree(link_ptr->hostname);
	ircfree(link_ptr->hubmask);
	ircfree(link_ptr->leafmask);
	ircfree(link_ptr->connpwd);
#ifdef USE_SSL
	ircfree(link_ptr->ciphers);
#endif
	Auth_DeleteAuthStruct(link_ptr->recvauth);
	link_ptr->recvauth = NULL;
}

void delete_linkblock(ConfigItem_link *link_ptr)
{
	Debug((DEBUG_ERROR, "delete_linkblock: deleting %s, refcount=%d",
		link_ptr->servername, link_ptr->refcount));
	if (link_ptr->class)
	{
		link_ptr->class->xrefcount--;
		/* Perhaps the class is temporary too and we need to free it... */
		if (link_ptr->class->flag.temporary && 
		    !link_ptr->class->clients && !link_ptr->class->xrefcount)
		{
			delete_classblock(link_ptr->class);
			link_ptr->class = NULL;
		}
	}
	link_cleanup(link_ptr);
	DelListItem(link_ptr, conf_link);
	MyFree(link_ptr);
}

void delete_cgiircblock(ConfigItem_cgiirc *e)
{
	Debug((DEBUG_ERROR, "delete_cgiircblock: deleting %s", e->hostname));
	if (e->auth)
		Auth_DeleteAuthStruct(e->auth);
	ircfree(e->hostname);
	ircfree(e->username);
	DelListItem(e, conf_cgiirc);
	MyFree(e);
}

void delete_classblock(ConfigItem_class *class_ptr)
{
	Debug((DEBUG_ERROR, "delete_classblock: deleting %s, clients=%d, xrefcount=%d",
		class_ptr->name, class_ptr->clients, class_ptr->xrefcount));
	ircfree(class_ptr->name);
	DelListItem(class_ptr, conf_class);
	MyFree(class_ptr);
}

void	listen_cleanup()
{
	int	i = 0;
	ConfigItem_listen *listen_ptr;
	ListStruct *next;
	for (listen_ptr = conf_listen; listen_ptr; listen_ptr = (ConfigItem_listen *)next)
	{
		next = (ListStruct *)listen_ptr->next;
		if (listen_ptr->flag.temporary && !listen_ptr->clients)
		{
			ircfree(listen_ptr->ip);
			DelListItem(listen_ptr, conf_listen);
			MyFree(listen_ptr);
			i++;
		}
	}
	if (i)
		close_listeners();
}

#ifdef USE_LIBCURL
char *find_remote_include(char *url, char **errorbuf)
{
	ConfigItem_include *inc;
	for (inc = conf_include; inc; inc = (ConfigItem_include *)inc->next)
	{
		if (!(inc->flag.type & INCLUDE_NOTLOADED))
			continue;
		if (!(inc->flag.type & INCLUDE_REMOTE))
			continue;
		if (!stricmp(url, inc->url))
		{
			*errorbuf = inc->errorbuf;
			return inc->file;
		}
	}
	return NULL;
}

char *find_loaded_remote_include(char *url)
{
	ConfigItem_include *inc;
	for (inc = conf_include; inc; inc = (ConfigItem_include *)inc->next)
	{
		if ((inc->flag.type & INCLUDE_NOTLOADED))
			continue;
		if (!(inc->flag.type & INCLUDE_REMOTE))
			continue;
		if (!stricmp(url, inc->url))
			return inc->file;
	}
	return NULL;
}	

/**
 * Non-asynchronous remote inclusion to give a user better feedback
 * when first starting his IRCd.
 *
 * The asynchronous friend is rehash() which merely queues remote
 * includes for download using download_file_async().
 */
int remote_include(ConfigEntry *ce)
{
	char *errorbuf = NULL;
	char *file = find_remote_include(ce->ce_vardata, &errorbuf);
	int ret;
	if (!loop.ircd_rehashing || (loop.ircd_rehashing && !file && !errorbuf))
	{
		char *error;
		if (config_verbose > 0)
			config_status("Downloading %s", ce->ce_vardata);
		file = download_file(ce->ce_vardata, &error);
		if (!file)
		{
#ifdef REMOTEINC_SPECIALCACHE
			if (has_cached_version(ce->ce_vardata))
			{
				config_warn("%s:%i: include: error downloading '%s': %s -- using cached version instead.",
					ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
					ce->ce_vardata, error);
				file = strdup(unreal_mkcache(ce->ce_vardata));
				/* Let it pass to load_conf()... */
			} else {
#endif
				config_error("%s:%i: include: error downloading '%s': %s",
					ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
					 ce->ce_vardata, error);
				return -1;
#ifdef REMOTEINC_SPECIALCACHE
			}
#endif
		}
		add_remote_include(file, ce->ce_vardata, 0, NULL, ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
		ret = load_conf(file, ce->ce_vardata);
		free(file);
		return ret;
	}
	else
	{
		if (errorbuf)
		{
#ifdef REMOTEINC_SPECIALCACHE
			if (has_cached_version(ce->ce_vardata))
			{
				config_warn("%s:%i: include: error downloading '%s': %s -- using cached version instead.",
					ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
					ce->ce_vardata, errorbuf);
				/* Let it pass to load_conf()... */
				file = strdup(unreal_mkcache(ce->ce_vardata));
			} else {
#endif
				config_error("%s:%i: include: error downloading '%s': %s",
					ce->ce_fileptr->cf_filename, ce->ce_varlinenum,
					ce->ce_vardata, errorbuf);
				return -1;
#ifdef REMOTEINC_SPECIALCACHE
			}
#endif
		}
		if (config_verbose > 0)
			config_status("Loading %s from download", ce->ce_vardata);
		add_remote_include(file, ce->ce_vardata, 0, NULL, ce->ce_fileptr->cf_filename, ce->ce_varlinenum);
		ret = load_conf(file, ce->ce_vardata);
		return ret;
	}
	return 0;
}
#endif

/**
 * Add an item to the conf_include list for the specified file.
 *
 * Checks for whether or not we're performing recursive includes
 * belong in conf_load() because that function is able to return an
 * error code. Any checks in here will end up being ignored by callers
 * and thus will gain us nothing.
 *
 * @param file path to the include file.
 */		
void add_include(const char *file, const char *included_from, int included_from_line)
{
	ConfigItem_include *inc;

	inc = MyMallocEx(sizeof(ConfigItem_include));
	inc->file = strdup(file);
	inc->flag.type = INCLUDE_NOTLOADED;
	inc->included_from = strdup(included_from);
	inc->included_from_line = included_from_line;
	AddListItem(inc, conf_include);
}

#ifdef USE_LIBCURL
/**
 * Adds a remote include entry to the config_include list.
 *
 * This is to be called whenever the included_from and
 * included_from_line parameters are known. This means that during a
 * rehash when downloads are done asynchronously, you call this with
 * the inclued_from and included_from_line information. After the
 * download is complete and you know there it is stored in the FS,
 * call update_remote_include().
 */
void add_remote_include(const char *file, const char *url, int flags, const char *errorbuf, const char *included_from, int included_from_line)
{
	ConfigItem_include *inc;

	/* we rely on MyMallocEx() zeroing the ConfigItem_include */
	inc = MyMallocEx(sizeof(ConfigItem_include));
	if (included_from)
	{
		inc->included_from = strdup(included_from);
		inc->included_from_line = included_from_line;
	}
	inc->url = strdup(url);

	update_remote_include(inc, file, INCLUDE_NOTLOADED|INCLUDE_REMOTE|flags, errorbuf);
	AddListItem(inc, conf_include);
}

/**
 * Update certain information in a remote include's config_include list entry.
 *
 * @param file the place on disk where the downloaded remote include
 *        may be found
 * @param flags additional flags to set on the config_include entry
 * @param errorbuf non-NULL if there were errors encountered in
 *        downloading. The error will be stored into the config_include
 *        entry.
 */
void update_remote_include(ConfigItem_include *inc, const char *file, int flags, const char *errorbuf)
{
	/*
	 * file may be NULL when errorbuf is non-NULL and vice-versa.
	 */
	if (file)
		inc->file = strdup(file);
	inc->flag.type |= flags;

	if (errorbuf)
		inc->errorbuf = strdup(errorbuf);
}
#endif

/**
 * Clean up conf_include after a rehash fails because of a
 * configuration file error.
 *
 * Duplicates some in unload_loaded_include().
 */
void unload_notloaded_includes(void)
{
	ConfigItem_include *inc, *next;

	for (inc = conf_include; inc; inc = next)
	{
		next = (ConfigItem_include *)inc->next;
		if ((inc->flag.type & INCLUDE_NOTLOADED) || !(inc->flag.type & INCLUDE_USED))
		{
#ifdef USE_LIBCURL
			if (inc->flag.type & INCLUDE_REMOTE)
			{
				remove(inc->file);
				free(inc->url);
				if (inc->errorbuf)
					free(inc->errorbuf);
			}
#endif
			free(inc->file);
			free(inc->included_from);
			DelListItem(inc, conf_include);
			free(inc);
		}
	}
}

/**
 * Clean up conf_include after a successful rehash to make way for
 * load_includes().
 */
void unload_loaded_includes(void)
{
	ConfigItem_include *inc, *next;

	for (inc = conf_include; inc; inc = next)
	{
		next = (ConfigItem_include *)inc->next;
		if (!(inc->flag.type & INCLUDE_NOTLOADED) || !(inc->flag.type & INCLUDE_USED))
		{
#ifdef USE_LIBCURL
			if (inc->flag.type & INCLUDE_REMOTE)
			{
				remove(inc->file);
				free(inc->url);
				if (inc->errorbuf)
					free(inc->errorbuf);
			}
#endif
			free(inc->file);
			free(inc->included_from);
			DelListItem(inc, conf_include);
			free(inc);
		}
	}
}

/**
 * Mark loaded includes as loaded by removing the INCLUDE_NOTLOADED
 * flag. Meant to be called only after calling
 * unload_loaded_includes().
 */			
void load_includes(void)
{
	ConfigItem_include *inc;

	/* Doing this for all the includes should actually be faster
	 * than only doing it for includes that are not-loaded
	 */
	for (inc = conf_include; inc; inc = (ConfigItem_include *)inc->next)
		inc->flag.type &= ~INCLUDE_NOTLOADED; 
}
