unrealircd

- supernets unrealircd source & configuration
git clone git://git.acid.vegas/unrealircd.git
Log | Files | Refs | Archive | README | LICENSE

conf.c (295200B)

      1 /*
      2  *   Unreal Internet Relay Chat Daemon, src/conf.c
      3  *   (C) 1998-2000 Chris Behrens & Fred Jacobs (comstud, moogle)
      4  *   (C) 2000-2002 Carsten V. Munk and the UnrealIRCd Team
      5  *
      6  *   This program is free software; you can redistribute it and/or modify
      7  *   it under the terms of the GNU General Public License as published by
      8  *   the Free Software Foundation; either version 1, or (at your option)
      9  *   any later version.
     10  *
     11  *   This program is distributed in the hope that it will be useful,
     12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14  *   GNU General Public License for more details.
     15  *
     16  *   You should have received a copy of the GNU General Public License
     17  *   along with this program; if not, write to the Free Software
     18  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     19  */
     20 
     21 #include "unrealircd.h"
     22 
     23 /*
     24  * Some typedefs..
     25 */
     26 typedef struct ConfigCommand ConfigCommand;
     27 struct ConfigCommand
     28 {
     29 	char	*name;
     30 	int	(*conffunc)(ConfigFile *conf, ConfigEntry *ce);
     31 	int 	(*testfunc)(ConfigFile *conf, ConfigEntry *ce);
     32 };
     33 
     34 
     35 /* Config commands */
     36 
     37 static int	_conf_admin		(ConfigFile *conf, ConfigEntry *ce);
     38 static int	_conf_me		(ConfigFile *conf, ConfigEntry *ce);
     39 static int	_conf_files		(ConfigFile *conf, ConfigEntry *ce);
     40 static int	_conf_oper		(ConfigFile *conf, ConfigEntry *ce);
     41 static int	_conf_operclass		(ConfigFile *conf, ConfigEntry *ce);
     42 static int	_conf_class		(ConfigFile *conf, ConfigEntry *ce);
     43 static int	_conf_drpass		(ConfigFile *conf, ConfigEntry *ce);
     44 static int	_conf_ulines		(ConfigFile *conf, ConfigEntry *ce);
     45 static int	_conf_include		(ConfigFile *conf, ConfigEntry *ce);
     46 static int	_conf_tld		(ConfigFile *conf, ConfigEntry *ce);
     47 static int	_conf_listen		(ConfigFile *conf, ConfigEntry *ce);
     48 static int	_conf_allow		(ConfigFile *conf, ConfigEntry *ce);
     49 static int	_conf_except		(ConfigFile *conf, ConfigEntry *ce);
     50 static int	_conf_vhost		(ConfigFile *conf, ConfigEntry *ce);
     51 static int	_conf_link		(ConfigFile *conf, ConfigEntry *ce);
     52 static int	_conf_ban		(ConfigFile *conf, ConfigEntry *ce);
     53 static int	_conf_set		(ConfigFile *conf, ConfigEntry *ce);
     54 static int	_conf_deny		(ConfigFile *conf, ConfigEntry *ce);
     55 static int	_conf_deny_channel	(ConfigFile *conf, ConfigEntry *ce);
     56 static int	_conf_deny_version	(ConfigFile *conf, ConfigEntry *ce);
     57 static int	_conf_require		(ConfigFile *conf, ConfigEntry *ce);
     58 static int	_conf_allow_channel	(ConfigFile *conf, ConfigEntry *ce);
     59 static int	_conf_loadmodule	(ConfigFile *conf, ConfigEntry *ce);
     60 static int	_conf_alias		(ConfigFile *conf, ConfigEntry *ce);
     61 static int	_conf_help		(ConfigFile *conf, ConfigEntry *ce);
     62 static int	_conf_offchans		(ConfigFile *conf, ConfigEntry *ce);
     63 static int	_conf_sni		(ConfigFile *conf, ConfigEntry *ce);
     64 extern int	_conf_security_group	(ConfigFile *conf, ConfigEntry *ce);
     65 static int	_conf_secret		(ConfigFile *conf, ConfigEntry *ce);
     66 
     67 /*
     68  * Validation commands
     69 */
     70 
     71 static int	_test_admin		(ConfigFile *conf, ConfigEntry *ce);
     72 static int	_test_me		(ConfigFile *conf, ConfigEntry *ce);
     73 static int	_test_files		(ConfigFile *conf, ConfigEntry *ce);
     74 static int	_test_oper		(ConfigFile *conf, ConfigEntry *ce);
     75 static int	_test_operclass		(ConfigFile *conf, ConfigEntry *ce);
     76 static int	_test_class		(ConfigFile *conf, ConfigEntry *ce);
     77 static int	_test_drpass		(ConfigFile *conf, ConfigEntry *ce);
     78 static int	_test_ulines		(ConfigFile *conf, ConfigEntry *ce);
     79 static int	_test_include		(ConfigFile *conf, ConfigEntry *ce);
     80 static int	_test_tld		(ConfigFile *conf, ConfigEntry *ce);
     81 static int	_test_listen		(ConfigFile *conf, ConfigEntry *ce);
     82 static int	_test_allow		(ConfigFile *conf, ConfigEntry *ce);
     83 static int	_test_except		(ConfigFile *conf, ConfigEntry *ce);
     84 static int	_test_vhost		(ConfigFile *conf, ConfigEntry *ce);
     85 static int	_test_link		(ConfigFile *conf, ConfigEntry *ce);
     86 static int	_test_ban		(ConfigFile *conf, ConfigEntry *ce);
     87 static int	_test_require		(ConfigFile *conf, ConfigEntry *ce);
     88 static int	_test_set		(ConfigFile *conf, ConfigEntry *ce);
     89 static int	_test_deny		(ConfigFile *conf, ConfigEntry *ce);
     90 static int	_test_allow_channel	(ConfigFile *conf, ConfigEntry *ce);
     91 static int	_test_loadmodule	(ConfigFile *conf, ConfigEntry *ce);
     92 static int	_test_blacklist_module	(ConfigFile *conf, ConfigEntry *ce);
     93 static int	_test_alias		(ConfigFile *conf, ConfigEntry *ce);
     94 static int	_test_help		(ConfigFile *conf, ConfigEntry *ce);
     95 static int	_test_offchans		(ConfigFile *conf, ConfigEntry *ce);
     96 static int	_test_sni		(ConfigFile *conf, ConfigEntry *ce);
     97 extern int	_test_security_group	(ConfigFile *conf, ConfigEntry *ce);
     98 static int	_test_secret		(ConfigFile *conf, ConfigEntry *ce);
     99 
    100 /* This MUST be alphabetized */
    101 static ConfigCommand _ConfigCommands[] = {
    102 	{ "admin", 		_conf_admin,		_test_admin 	},
    103 	{ "alias",		_conf_alias,		_test_alias	},
    104 	{ "allow",		_conf_allow,		_test_allow	},
    105 	{ "ban", 		_conf_ban,		_test_ban	},
    106 	{ "blacklist-module",	NULL,		 	NULL },
    107 	{ "class", 		_conf_class,		_test_class	},
    108 	{ "deny",		_conf_deny,		_test_deny	},
    109 	{ "drpass",		_conf_drpass,		_test_drpass	},
    110 	{ "except",		_conf_except,		_test_except	},
    111 	{ "files",		_conf_files,		_test_files	},
    112 	{ "help",		_conf_help,		_test_help	},
    113 	{ "include",		NULL,	  		_test_include	},
    114 	{ "link", 		_conf_link,		_test_link	},
    115 	{ "listen", 		_conf_listen,		_test_listen	},
    116 	{ "loadmodule",		NULL,		 	_test_loadmodule},
    117 	{ "log",		config_run_log,		config_test_log	},
    118 	{ "me", 		_conf_me,		_test_me	},
    119 	{ "official-channels", 	_conf_offchans,		_test_offchans	},
    120 	{ "oper", 		_conf_oper,		_test_oper	},
    121 	{ "operclass",		_conf_operclass,	_test_operclass	},
    122 	{ "require", 		_conf_require,		_test_require	},
    123 	{ "secret",		_conf_secret,		_test_secret	},
    124 	{ "security-group",	_conf_security_group,	_test_security_group	},
    125 	{ "set",		_conf_set,		_test_set	},
    126 	{ "sni",		_conf_sni,		_test_sni	},
    127 	{ "tld",		_conf_tld,		_test_tld	},
    128 	{ "ulines",		_conf_ulines,		_test_ulines	},
    129 	{ "vhost", 		_conf_vhost,		_test_vhost	},
    130 };
    131 
    132 /* This MUST be alphabetized */
    133 static NameValue _ListenerFlags[] = {
    134 	{ LISTENER_CLIENTSONLY,  "clientsonly"},
    135 	{ LISTENER_DEFER_ACCEPT, "defer-accept"},
    136 	{ LISTENER_SERVERSONLY,  "serversonly"},
    137 	{ LISTENER_TLS, 	 "ssl"},
    138 	{ LISTENER_NORMAL, 	 "standard"},
    139 	{ LISTENER_TLS, 	 "tls"},
    140 };
    141 
    142 /* This MUST be alphabetized */
    143 static NameValue _LinkFlags[] = {
    144 	{ CONNECT_AUTO,	"autoconnect" },
    145 	{ CONNECT_INSECURE,	"insecure" },
    146 	{ CONNECT_QUARANTINE, "quarantine"},
    147 	{ CONNECT_TLS, "ssl" },
    148 	{ CONNECT_TLS, "tls" },
    149 };
    150 
    151 /* This MUST be alphabetized */
    152 static NameValue _TLSFlags[] = {
    153 	{ TLSFLAG_FAILIFNOCERT, "fail-if-no-clientcert" },
    154 	{ TLSFLAG_DISABLECLIENTCERT, "no-client-certificate" },
    155 	{ TLSFLAG_NOSTARTTLS, "no-starttls" },
    156 };
    157 
    158 struct {
    159 	unsigned conf_me : 1;
    160 	unsigned conf_admin : 1;
    161 	unsigned conf_listen : 1;
    162 } requiredstuff;
    163 struct {
    164 	int min, max;
    165 } nicklengths;
    166 struct SetCheck settings;
    167 /*
    168  * Utilities
    169 */
    170 
    171 void	port_range(const char *string, int *start, int *end);
    172 long	config_checkval(const char *value, unsigned short flags);
    173 
    174 /*
    175  * Parser
    176 */
    177 
    178 ConfigFile		*config_load(const char *filename, const char *displayname);
    179 void			config_free(ConfigFile *cfptr);
    180 ConfigFile		*config_parse_with_offset(const char *filename, char *confdata, unsigned int line_offset);
    181 ConfigFile	 	*config_parse(const char *filename, char *confdata);
    182 ConfigEntry		*config_find_entry(ConfigEntry *ce, const char *name);
    183 
    184 extern void add_entropy_configfile(struct stat *st, const char *buf);
    185 extern void unload_all_unused_umodes(void);
    186 extern void unload_all_unused_extcmodes(void);
    187 extern void unload_all_unused_extbans(void);
    188 extern void unload_all_unused_caps(void);
    189 extern void unload_all_unused_history_backends(void);
    190 extern void unload_all_unused_rpc_handlers(void);
    191 
    192 int reloadable_perm_module_unloaded(void);
    193 int tls_tests(void);
    194 
    195 /* Conf sub-sub-functions */
    196 void test_tlsblock(ConfigFile *conf, ConfigEntry *cep, int *totalerrors);
    197 void conf_tlsblock(ConfigFile *conf, ConfigEntry *cep, TLSOptions *tlsoptions);
    198 void free_tls_options(TLSOptions *tlsoptions);
    199 
    200 /*
    201  * Config parser (IRCd)
    202 */
    203 int			config_read_file(const char *filename, const char *display_name);
    204 void			config_rehash(void);
    205 int			config_run_blocks(void);
    206 int	config_test_blocks();
    207 
    208 /*
    209  * Configuration linked lists
    210 */
    211 ConfigItem_me		*conf_me = NULL;
    212 ConfigItem_files	*conf_files = NULL;
    213 ConfigItem_class 	*conf_class = NULL;
    214 ConfigItem_class	*default_class = NULL;
    215 ConfigItem_admin 	*conf_admin = NULL;
    216 ConfigItem_admin	*conf_admin_tail = NULL;
    217 ConfigItem_drpass	*conf_drpass = NULL;
    218 ConfigItem_ulines	*conf_ulines = NULL;
    219 ConfigItem_tld		*conf_tld = NULL;
    220 ConfigItem_oper		*conf_oper = NULL;
    221 ConfigItem_operclass	*conf_operclass = NULL;
    222 ConfigItem_listen	*conf_listen = NULL;
    223 ConfigItem_sni		*conf_sni = NULL;
    224 ConfigItem_allow	*conf_allow = NULL;
    225 ConfigItem_vhost	*conf_vhost = NULL;
    226 ConfigItem_link		*conf_link = NULL;
    227 ConfigItem_ban		*conf_ban = NULL;
    228 ConfigItem_deny_channel *conf_deny_channel = NULL;
    229 ConfigItem_allow_channel *conf_allow_channel = NULL;
    230 ConfigItem_deny_version *conf_deny_version = NULL;
    231 ConfigItem_alias	*conf_alias = NULL;
    232 ConfigResource	*config_resources = NULL;
    233 ConfigItem_blacklist_module	*conf_blacklist_module = NULL;
    234 ConfigItem_help		*conf_help = NULL;
    235 ConfigItem_offchans	*conf_offchans = NULL;
    236 Secret			*secrets = NULL;
    237 
    238 MODVAR Configuration		iConf;
    239 MODVAR Configuration		tempiConf;
    240 MODVAR ConfigFile		*conf = NULL;
    241 extern NameValueList *config_defines;
    242 MODVAR int ipv6_disabled = 0;
    243 MODVAR Client *remote_rehash_client = NULL;
    244 MODVAR json_t *json_rehash_log = NULL;
    245 
    246 MODVAR int			config_error_flag = 0;
    247 int			config_verbose = 0;
    248 
    249 int need_operclass_permissions_upgrade = 0;
    250 int invalid_snomasks_encountered = 0;
    251 int have_tls_listeners = 0;
    252 char *port_6667_ip = NULL;
    253 
    254 int add_config_resource(const char *resource, int type, ConfigEntry *ce);
    255 void resource_download_complete(const char *url, const char *file, const char *errorbuf, int cached, void *rs_key);
    256 void free_all_config_resources(void);
    257 int rehash_internal(Client *client);
    258 int is_blacklisted_module(const char *name);
    259 
    260 /** Return the printable string of a 'cep' location, such as set::something::xyz */
    261 const char *config_var(ConfigEntry *cep)
    262 {
    263 	static char buf[256];
    264 	ConfigEntry *e;
    265 	char *elem[16];
    266 	int numel = 0, i;
    267 
    268 	if (!cep)
    269 		return "";
    270 
    271 	buf[0] = '\0';
    272 
    273 	/* First, walk back to the top */
    274 	for (e = cep; e; e = e->parent)
    275 	{
    276 		elem[numel++] = e->name;
    277 		if (numel == 15)
    278 			break;
    279 	}
    280 
    281 	/* Now construct the xxx::yyy::zzz string */
    282 	for (i = numel-1; i >= 0; i--)
    283 	{
    284 		strlcat(buf, elem[i], sizeof(buf));
    285 		if (i > 0)
    286 			strlcat(buf, "::", sizeof(buf));
    287 	}
    288 
    289 	return buf;
    290 }
    291 
    292 void port_range(const char *string, int *start, int *end)
    293 {
    294 	char buf[256];
    295 	char *c;
    296 	strlcpy(buf, string, sizeof(buf));
    297 	c = strchr(buf, '-');
    298 	if (!c)
    299 	{
    300 		int tmp = atoi(string);
    301 		*start = tmp;
    302 		*end = tmp;
    303 		return;
    304 	}
    305 	*c = '\0';
    306 	*start = atoi(string);
    307 	*end = atoi((c+1));
    308 	*c = '-';
    309 }
    310 
    311 /** Parses '5:60s' config values.
    312  * orig: original string
    313  * times: pointer to int, first value (before the :)
    314  * period: pointer to int, second value (after the :) in seconds
    315  * RETURNS: 0 for parse error, 1 if ok.
    316  * REMARK: times&period should be ints!
    317  */
    318 int config_parse_flood(const char *orig, int *times, int *period)
    319 {
    320 	char buf[256];
    321 	char *x;
    322 
    323 	strlcpy(buf, orig, sizeof(buf));
    324 
    325 	*times = *period = 0;
    326 	x = strchr(buf, ':');
    327 	/* 'blah', ':blah', '1:' */
    328 	if (!x || (x == buf) || (*(x+1) == '\0'))
    329 		return 0;
    330 
    331 	*x = '\0';
    332 	*times = atoi(buf);
    333 	*period = config_checkval(x+1, CFG_TIME);
    334 	*x = ':'; /* restore */
    335 	return 1;
    336 }
    337 
    338 /** Find an anti-flood settings block by name.
    339  * @param name		The name of the set::anti-flood block
    340  * @returns The FloodSettings block if found, or NULL if not found.
    341  */
    342 FloodSettings *find_floodsettings_block_ex(Configuration *conf, const char *name)
    343 {
    344 	FloodSettings *f;
    345 
    346 	for (f = conf->floodsettings; f; f = f->next)
    347 		if (!strcmp(f->name, name))
    348 			return f;
    349 
    350 	return NULL;
    351 }
    352 
    353 /** Find an anti-flood settings block by name.
    354  * @param name		The name of the set::anti-flood block
    355  * @returns The FloodSettings block if found, or NULL if not found.
    356  */
    357 FloodSettings *find_floodsettings_block(const char *name)
    358 {
    359 	return find_floodsettings_block_ex(&iConf, name);
    360 }
    361 
    362 /** Check if 'name' is in the array 'list'.
    363  * @param name		The name to check
    364  * @param list		The char *list[] with the list of valid names.
    365  * @returns 1 if found, 0 if not
    366  * @note The array in list must end in a NULL element!
    367  */
    368 int text_in_array(const char *name, const char *list[])
    369 {
    370 	int i;
    371 
    372 	for (i=0; list[i]; i++)
    373 		if (!strcmp(name, list[i]))
    374 			return 1;
    375 
    376 	return 0; /* Not found */
    377 }
    378 
    379 int flood_option_is_old(const char *name)
    380 {
    381 	const char *opts[] =
    382 	{
    383 		"max-concurrent-conversations",
    384 		"unknown-flood-amount",
    385 		"unknown-flood-bantime",
    386 		"handshake-data-flood",
    387 		"away-count",
    388 		"away-period",
    389 		"away-flood",
    390 		"nick-flood",
    391 		"join-flood",
    392 		"invite-flood",
    393 		"knock-flood",
    394 		"connect-flood",
    395 		"target-flood",
    396 		NULL
    397 	};
    398 
    399 	return text_in_array(name, opts);
    400 }
    401 
    402 int flood_option_is_for_everyone(const char *name)
    403 {
    404 	const char *opts[] =
    405 	{
    406 		"connect-flood",
    407 		"handshake-data-flood",
    408 		"unknown-flood",
    409 		"target-flood",
    410 		NULL
    411 	};
    412 
    413 	return text_in_array(name, opts);
    414 }
    415 
    416 /** Free a FloodSettings struct */
    417 void free_floodsettings(FloodSettings *f)
    418 {
    419 	safe_free(f->name);
    420 	safe_free(f);
    421 }
    422 
    423 /** Parses a value like '5:60s' into a flood setting that we can store.
    424  * @param str		The string to parse (eg: '5:60s')
    425  * @param settings	The FloodSettings block to store the result in
    426  * @param opt		The option (eg: FLD_AWAY)
    427  * @returns 1 if OK, 0 for parse error.
    428  */
    429 int config_parse_flood_generic(const char *str, Configuration *conf, char *blockname, FloodOption opt)
    430 {
    431 	char buf[64], *p;
    432 	FloodSettings *settings = find_floodsettings_block_ex(conf, blockname);
    433 
    434 	/* Create a new anti-flood block if it doesn't exist */
    435 	if (!settings)
    436 	{
    437 		settings = safe_alloc(sizeof(FloodSettings));
    438 		safe_strdup(settings->name, blockname);
    439 		AddListItem(settings, conf->floodsettings);
    440 	}
    441 
    442 	if (!strcmp(str, "unlimited") || !strcmp(str, "max"))
    443 	{
    444 		settings->limit[opt] = -1;
    445 		settings->period[opt] = 0;
    446 		return 1;
    447 	}
    448 
    449 	/* Work on a copy so we don't destroy 'str' */
    450 	strlcpy(buf, str, sizeof(buf));
    451 
    452 	p = strchr(buf, ':');
    453 
    454 	/* 'blah', ':blah', '1:' */
    455 	if (!p || (p == buf) || (*(p+1) == '\0'))
    456 		return 0;
    457 
    458 	*p++ = '\0';
    459 
    460 	settings->limit[opt] = atoi(buf);
    461 	settings->period[opt] = config_checkval(p, CFG_TIME);
    462 
    463 	return 1;
    464 }
    465 
    466 long config_checkval(const char *orig, unsigned short flags)
    467 {
    468 	char *value;
    469 	char *text;
    470 	long ret = 0;
    471 
    472 	/* Handle empty strings early, since we use +1 later in the code etc. */
    473 	if (BadPtr(orig))
    474 		return 0;
    475 
    476 	value = raw_strdup(orig);
    477 	if (flags == CFG_YESNO) {
    478 		for (text = value; *text; text++) {
    479 			if (!isalnum(*text))
    480 				continue;
    481 			if (tolower(*text) == 'y' || (tolower(*text) == 'o' &&
    482 			    tolower(*(text+1)) == 'n') || *text == '1' || tolower(*text) == 't') {
    483 				ret = 1;
    484 				break;
    485 			}
    486 		}
    487 	}
    488 	else if (flags == CFG_SIZE) {
    489 		int mfactor = 1;
    490 		char *sz;
    491 		for (text = value; *text; text++) {
    492 			if (isalpha(*text)) {
    493 				if (tolower(*text) == 'k')
    494 					mfactor = 1024;
    495 				else if (tolower(*text) == 'm')
    496 					mfactor = 1048576;
    497 				else if (tolower(*text) == 'g')
    498 					mfactor = 1073741824;
    499 				else
    500 					mfactor = 1;
    501 				sz = text;
    502 				while (isalpha(*text))
    503 					text++;
    504 
    505 				*sz = 0;
    506 				while (sz-- > value && *sz) {
    507 					if (isspace(*sz))
    508 						*sz = 0;
    509 					if (!isdigit(*sz))
    510 						break;
    511 				}
    512 				ret += atoi(sz+1)*mfactor;
    513 				if (*text == '\0') {
    514 					text++;
    515 					break;
    516 				}
    517 			}
    518 		}
    519 		mfactor = 1;
    520 		sz = text;
    521 		sz--; /* -1 because we are PAST the end of the string */
    522 		while (sz-- > value) {
    523 			if (isspace(*sz))
    524 				*sz = 0;
    525 			if (!isdigit(*sz))
    526 				break;
    527 		}
    528 		ret += atoi(sz+1)*mfactor;
    529 	}
    530 	else if (flags == CFG_TIME) {
    531 		int mfactor = 1;
    532 		char *sz;
    533 		for (text = value; *text; text++) {
    534 			if (isalpha(*text)) {
    535 				if (tolower(*text) == 'w')
    536 					mfactor = 604800;
    537 				else if (tolower(*text) == 'd')
    538 					mfactor = 86400;
    539 				else if (tolower(*text) == 'h')
    540 					mfactor = 3600;
    541 				else if (tolower(*text) == 'm')
    542 					mfactor = 60;
    543 				else
    544 					mfactor = 1;
    545 				sz = text;
    546 				while (isalpha(*text))
    547 					text++;
    548 
    549 				*sz = 0;
    550 				while (sz-- > value && *sz) {
    551 					if (isspace(*sz))
    552 						*sz = 0;
    553 					if (!isdigit(*sz))
    554 						break;
    555 				}
    556 				ret += atoi(sz+1)*mfactor;
    557 				if (*text == '\0') {
    558 					text++;
    559 					break;
    560 				}
    561 			}
    562 		}
    563 		mfactor = 1;
    564 		sz = text;
    565 		sz--; /* -1 because we are PAST the end of the string */
    566 		while (sz-- > value) {
    567 			if (isspace(*sz))
    568 				*sz = 0;
    569 			if (!isdigit(*sz))
    570 				break;
    571 		}
    572 		ret += atoi(sz+1)*mfactor;
    573 	}
    574 	safe_free(value);
    575 	return ret;
    576 }
    577 
    578 /** Free configuration setting for set::modes-on-join */
    579 void free_conf_channelmodes(struct ChMode *store)
    580 {
    581 	int i;
    582 
    583 	for (i=0; i < 255; i++)
    584 		safe_free(store->extparams[i]);
    585 
    586 	memset(store, 0, sizeof(struct ChMode));
    587 }
    588 
    589 /* Set configuration, used for set::modes-on-join */
    590 void conf_channelmodes(const char *modes, struct ChMode *store)
    591 {
    592 	Cmode *cm;
    593 	const char *m;
    594 	char *params = strchr(modes, ' ');
    595 	char *parambuf = NULL;
    596 	const char *param = NULL;
    597 	const char *param_in;
    598 	char *save = NULL;
    599 	int found;
    600 
    601 	/* Free existing parameters first (no inheritance) */
    602 	free_conf_channelmodes(store);
    603 
    604 	if (params)
    605 	{
    606 		params++;
    607 		safe_strdup(parambuf, params);
    608 		param = strtoken(&save, parambuf, " ");
    609 	}
    610 
    611 	for (m = modes; *m && *m != ' '; m++)
    612 	{
    613 		if (*m == '+')
    614 			continue;
    615 
    616 		if (*m == '-')
    617 		{
    618 			/* When a channel is created it has no modes, so just ignore if the
    619 			 * user asks us to unset anything -- codemastr
    620 			 */
    621 			while (*m && *m != '+')
    622 				m++;
    623 			continue;
    624 		}
    625 
    626 		found = 0;
    627 		for (cm=channelmodes; cm; cm = cm->next)
    628 		{
    629 			if (!(cm->letter))
    630 				continue;
    631 			if (*m == cm->letter)
    632 			{
    633 				found = 1;
    634 				if (cm->paracount)
    635 				{
    636 					if (!param)
    637 					{
    638 						config_warn("set::modes-on-join '%s'. Parameter missing for mode %c.", modes, *m);
    639 						break;
    640 					}
    641 					param_in = param; /* save it */
    642 					param = cm->conv_param(param, NULL, NULL);
    643 					if (!param)
    644 					{
    645 						config_warn("set::modes-on-join '%s'. Parameter for mode %c is invalid (%s).", modes, *m, param_in);
    646 						break; /* invalid parameter fmt, do not set mode. */
    647 					}
    648 					safe_strdup(store->extparams[cm->letter], param);
    649 					/* Get next parameter */
    650 					param = strtoken(&save, NULL, " ");
    651 				}
    652 				store->extmodes |= cm->mode;
    653 				break;
    654 			}
    655 		}
    656 		if (!found)
    657 			config_warn("set::modes-on-join '%s'. Channel mode %c not found.", modes, *m);
    658 	}
    659 	safe_free(parambuf);
    660 }
    661 
    662 void chmode_str(struct ChMode *modes, char *mbuf, char *pbuf, size_t mbuf_size, size_t pbuf_size)
    663 {
    664 	Cmode *cm;
    665 
    666 	if (!(mbuf_size && pbuf_size))
    667 		return;
    668 
    669 	*pbuf = 0;
    670 	*mbuf++ = '+';
    671 
    672 	for (cm=channelmodes; cm; cm = cm->next)
    673 	{
    674 		if (!(cm->letter))
    675 			continue;
    676 
    677 		if (modes->extmodes & cm->mode)
    678 		{
    679 			if (mbuf_size)
    680 			{
    681 				*mbuf++ = cm->letter;
    682 				if (!--mbuf_size)
    683 				{
    684 					*--mbuf=0;
    685 					break;
    686 				}
    687 			}
    688 			if (cm->paracount)
    689 			{
    690 				strlcat(pbuf, modes->extparams[cm->letter], pbuf_size);
    691 				strlcat(pbuf, " ", pbuf_size);
    692 			}
    693 		}
    694 	}
    695 	*mbuf=0;
    696 }
    697 
    698 const char *channellevel_to_string(const char *s)
    699 {
    700 	/* Requested at http://bugs.unrealircd.org/view.php?id=3852 */
    701 	if (!strcmp(s, "none"))
    702 		return "";
    703 	if (!strcmp(s, "voice"))
    704 		return "v";
    705 	if (!strcmp(s, "halfop"))
    706 		return "h";
    707 	if (!strcmp(s, "op") || !strcmp(s, "chanop"))
    708 		return "o";
    709 	if (!strcmp(s, "protect") || !strcmp(s, "chanprot") || !strcmp(s, "chanadmin") || !strcmp(s, "admin"))
    710 		return "a";
    711 	if (!strcmp(s, "owner") || !strcmp(s, "chanowner"))
    712 		return "q";
    713 
    714 	return NULL; /* unknown or unsupported */
    715 }
    716 
    717 Policy policy_strtoval(const char *s)
    718 {
    719 	if (!s)
    720 		return 0;
    721 
    722 	if (!strcmp(s, "allow"))
    723 		return POLICY_ALLOW;
    724 
    725 	if (!strcmp(s, "warn"))
    726 		return POLICY_WARN;
    727 
    728 	if (!strcmp(s, "deny"))
    729 		return POLICY_DENY;
    730 
    731 	return 0;
    732 }
    733 
    734 const char *policy_valtostr(Policy policy)
    735 {
    736 	if (policy == POLICY_ALLOW)
    737 		return "allow";
    738 	if (policy == POLICY_WARN)
    739 		return "warn";
    740 	if (policy == POLICY_DENY)
    741 		return "deny";
    742 	return "???";
    743 }
    744 
    745 char policy_valtochar(Policy policy)
    746 {
    747 	if (policy == POLICY_ALLOW)
    748 		return 'a';
    749 	if (policy == POLICY_WARN)
    750 		return 'w';
    751 	if (policy == POLICY_DENY)
    752 		return 'd';
    753 	return '?';
    754 }
    755 
    756 AllowedChannelChars allowed_channelchars_strtoval(const char *str)
    757 {
    758 	if (!strcmp(str, "ascii"))
    759 		return ALLOWED_CHANNELCHARS_ASCII;
    760 	else if (!strcmp(str, "utf8"))
    761 		return ALLOWED_CHANNELCHARS_UTF8;
    762 	else if (!strcmp(str, "any"))
    763 		return ALLOWED_CHANNELCHARS_ANY;
    764 	return 0;
    765 }
    766 
    767 const char *allowed_channelchars_valtostr(AllowedChannelChars v)
    768 {
    769 	switch(v)
    770 	{
    771 		case ALLOWED_CHANNELCHARS_ASCII:
    772 			return "ascii";
    773 		case ALLOWED_CHANNELCHARS_UTF8:
    774 			return "utf8";
    775 		case ALLOWED_CHANNELCHARS_ANY:
    776 			return "any";
    777 		default:
    778 			/* Not possible */
    779 			abort();
    780 			return "NOTREACHED"; /* Windows.. */
    781 	}
    782 }
    783 
    784 /* Used for set::automatic-ban-target and set::manual-ban-target */
    785 BanTarget ban_target_strtoval(const char *str)
    786 {
    787 	if (!strcmp(str, "ip"))
    788 		return BAN_TARGET_IP;
    789 	else if (!strcmp(str, "userip"))
    790 		return BAN_TARGET_USERIP;
    791 	else if (!strcmp(str, "host"))
    792 		return BAN_TARGET_HOST;
    793 	else if (!strcmp(str, "userhost"))
    794 		return BAN_TARGET_USERHOST;
    795 	else if (!strcmp(str, "account"))
    796 		return BAN_TARGET_ACCOUNT;
    797 	else if (!strcmp(str, "certfp"))
    798 		return BAN_TARGET_CERTFP;
    799 	return 0; /* invalid */
    800 }
    801 
    802 /* Used for set::automatic-ban-target and set::manual-ban-target */
    803 const char *ban_target_valtostr(BanTarget v)
    804 {
    805 	switch(v)
    806 	{
    807 		case BAN_TARGET_IP:
    808 			return "ip";
    809 		case BAN_TARGET_USERIP:
    810 			return "userip";
    811 		case BAN_TARGET_HOST:
    812 			return "host";
    813 		case BAN_TARGET_USERHOST:
    814 			return "userhost";
    815 		case BAN_TARGET_ACCOUNT:
    816 			return "account";
    817 		case BAN_TARGET_CERTFP:
    818 			return "certfp";
    819 		default:
    820 			return "???";
    821 	}
    822 }
    823 
    824 HideIdleTimePolicy hideidletime_strtoval(const char *str)
    825 {
    826 	if (!strcmp(str, "never"))
    827 		return HIDE_IDLE_TIME_NEVER;
    828 	else if (!strcmp(str, "always"))
    829 		return HIDE_IDLE_TIME_ALWAYS;
    830 	else if (!strcmp(str, "usermode"))
    831 		return HIDE_IDLE_TIME_USERMODE;
    832 	else if (!strcmp(str, "oper-usermode"))
    833 		return HIDE_IDLE_TIME_OPER_USERMODE;
    834 	return 0;
    835 }
    836 
    837 const char *hideidletime_valtostr(HideIdleTimePolicy v)
    838 {
    839 	switch(v)
    840 	{
    841 		case HIDE_IDLE_TIME_NEVER:
    842 			return "never";
    843 		case HIDE_IDLE_TIME_ALWAYS:
    844 			return "always";
    845 		case HIDE_IDLE_TIME_USERMODE:
    846 			return "usermode";
    847 		case HIDE_IDLE_TIME_OPER_USERMODE:
    848 			return "oper-usermode";
    849 		default:
    850 			return "INVALID";
    851 	}
    852 }
    853 
    854 ConfigFile *config_load(const char *filename, const char *displayname)
    855 {
    856 	struct stat sb;
    857 	int			fd;
    858 	int			ret;
    859 	char		*buf = NULL;
    860 	ConfigFile	*cfptr;
    861 
    862 	if (!displayname)
    863 		displayname = filename;
    864 
    865 #ifndef _WIN32
    866 	fd = open(filename, O_RDONLY);
    867 #else
    868 	fd = open(filename, O_RDONLY|O_BINARY);
    869 #endif
    870 	if (fd == -1)
    871 	{
    872 		config_error("Couldn't open \"%s\": %s\n", filename, strerror(errno));
    873 		return NULL;
    874 	}
    875 	if (fstat(fd, &sb) == -1)
    876 	{
    877 		config_error("Couldn't fstat \"%s\": %s\n", filename, strerror(errno));
    878 		close(fd);
    879 		return NULL;
    880 	}
    881 	if (!sb.st_size)
    882 	{
    883 		/* Workaround for empty files */
    884 		cfptr = config_parse(filename, " ");
    885 		close(fd);
    886 		return cfptr;
    887 	}
    888 	buf = safe_alloc(sb.st_size+1);
    889 	if (buf == NULL)
    890 	{
    891 		config_error("Out of memory trying to load \"%s\"\n", filename);
    892 		close(fd);
    893 		return NULL;
    894 	}
    895 	ret = read(fd, buf, sb.st_size);
    896 	if (ret != sb.st_size)
    897 	{
    898 		config_error("Error reading \"%s\": %s\n", filename,
    899 			ret == -1 ? strerror(errno) : strerror(EFAULT));
    900 		safe_free(buf);
    901 		close(fd);
    902 		return NULL;
    903 	}
    904 	/* Just me or could this cause memory corrupted when ret <0 ? */
    905 	buf[ret] = '\0';
    906 	close(fd);
    907 	add_entropy_configfile(&sb, buf);
    908 	cfptr = config_parse(displayname, buf);
    909 	safe_free(buf);
    910 	return cfptr;
    911 }
    912 
    913 void config_free(ConfigFile *cfptr)
    914 {
    915 	ConfigFile	*nptr;
    916 
    917 	for(;cfptr;cfptr=nptr)
    918 	{
    919 		nptr = cfptr->next;
    920 		if (cfptr->items)
    921 			config_entry_free_all(cfptr->items);
    922 		safe_free(cfptr->filename);
    923 		safe_free(cfptr);
    924 	}
    925 }
    926 
    927 /** Remove quotes so that 'hello \"all\" \\ lala' becomes 'hello "all" \ lala' */
    928 void unreal_del_quotes(char *i)
    929 {
    930 	char *o;
    931 
    932 	for (o = i; *i; i++)
    933 	{
    934 		if (*i == '\\')
    935 		{
    936 			if ((i[1] == '\\') || (i[1] == '"'))
    937 			{
    938 				i++; /* skip original \ */
    939 				if (*i == '\0')
    940 					break;
    941 			}
    942 		}
    943 		*o++ = *i;
    944 	}
    945 	*o = '\0';
    946 }
    947 
    948 /** Add quotes to a line, eg some"thing becomes some\"thing - extended version */
    949 int unreal_add_quotes_r(const char *i, char *o, size_t len)
    950 {
    951 	if (len == 0)
    952 		return 0;
    953 	
    954 	len--; /* reserve room for nul byte */
    955 
    956 	if (len == 0)
    957 	{
    958 		*o = '\0';
    959 		return 0;
    960 	}
    961 	
    962 	for (; *i; i++)
    963 	{
    964 		if ((*i == '"') || (*i == '\\')) /* only " and \ need to be quoted */
    965 		{
    966 			if (len < 2)
    967 				break;
    968 			*o++ = '\\';
    969 			*o++ = *i;
    970 			len -= 2;
    971 		} else
    972 		{
    973 			if (len == 0)
    974 				break;
    975 			*o++ = *i;
    976 			len--;
    977 		}
    978 	}
    979 	*o = '\0';
    980 	
    981 	return 1;
    982 }	
    983 
    984 /** Add quotes to a line, eg some"thing becomes some\"thing */
    985 const char *unreal_add_quotes(const char *str)
    986 {
    987 	static char qbuf[2048];
    988 	
    989 	*qbuf = '\0';
    990 	unreal_add_quotes_r(str, qbuf, sizeof(qbuf));
    991 	return qbuf;
    992 }
    993 
    994 ConfigFile *config_parse(const char *filename, char *confdata)
    995 {
    996 	return config_parse_with_offset(filename, confdata, 0);
    997 }
    998 
    999 /* This is the internal parser, made by Chris Behrens & Fred Jacobs <2005.
   1000  * Enhanced (or mutilated) by Bram Matthys over the years (2015-2019).
   1001  */
   1002 ConfigFile *config_parse_with_offset(const char *filename, char *confdata, unsigned int line_offset)
   1003 {
   1004 	char		*ptr;
   1005 	char		*start;
   1006 	int		linenumber = 1+line_offset;
   1007 	int errors = 0;
   1008 	int n;
   1009 	ConfigEntry	*curce;
   1010 	ConfigEntry	**lastce;
   1011 	ConfigEntry	*cursection;
   1012 	ConfigFile	*curcf;
   1013 	int preprocessor_level = 0;
   1014 	ConditionalConfig *cc, *cc_list = NULL;
   1015 
   1016 	curcf = safe_alloc(sizeof(ConfigFile));
   1017 	safe_strdup(curcf->filename, filename);
   1018 	lastce = &(curcf->items);
   1019 	curce = NULL;
   1020 	cursection = NULL;
   1021 	/* Replace \r's with spaces .. ugly ugly -Stskeeps */
   1022 	for (ptr=confdata; *ptr; ptr++)
   1023 		if (*ptr == '\r')
   1024 			*ptr = ' ';
   1025 
   1026 	for(ptr=confdata;*ptr;ptr++)
   1027 	{
   1028 		switch(*ptr)
   1029 		{
   1030 			case ';':
   1031 				if (!curce)
   1032 				{
   1033 					config_status("%s:%i Ignoring extra semicolon\n",
   1034 						filename, linenumber);
   1035 					break;
   1036 				}
   1037 				*lastce = curce;
   1038 				lastce = &(curce->next);
   1039 				curce->file_position_end = (ptr - confdata);
   1040 				curce = NULL;
   1041 				break;
   1042 			case '{':
   1043 				if (!curce)
   1044 				{
   1045 					config_error("%s:%i: New section start detected on line %d but the section has no name. "
   1046 					             "Sections should start with a name like 'oper {' or 'set {'.",
   1047 							filename, linenumber, linenumber);
   1048 					errors++;
   1049 					continue;
   1050 				}
   1051 				else if (curce->items)
   1052 				{
   1053 					config_error("%s:%i: New section start but previous section did not end properly. "
   1054 					             "Check line %d and the line(s) before, you are likely missing a '};' there.\n",
   1055 							filename, linenumber, linenumber);
   1056 					errors++;
   1057 					continue;
   1058 				}
   1059 				curce->section_linenumber = linenumber;
   1060 				lastce = &(curce->items);
   1061 				cursection = curce;
   1062 				curce = NULL;
   1063 				break;
   1064 			case '}':
   1065 				if (curce)
   1066 				{
   1067 					config_error("%s:%i: Missing semicolon (';') before close brace. Check line %d and the line(s) before.\n",
   1068 						filename, linenumber, linenumber);
   1069 					config_entry_free_all(curce);
   1070 					config_free(curcf);
   1071 					errors++;
   1072 					return NULL;
   1073 				}
   1074 				else if (!cursection)
   1075 				{
   1076 					config_error("%s:%i: You have a close brace ('};') too many. "
   1077 					              "Check line %d AND the lines above it from the previous block.\n",
   1078 						filename, linenumber, linenumber);
   1079 					errors++;
   1080 					continue;
   1081 				}
   1082 				curce = cursection;
   1083 				cursection->file_position_end = (ptr - confdata);
   1084 				cursection = cursection->parent;
   1085 				if (!cursection)
   1086 					lastce = &(curcf->items);
   1087 				else
   1088 					lastce = &(cursection->items);
   1089 				for(;*lastce;lastce = &((*lastce)->next))
   1090 					continue;
   1091 				if (*(ptr+1) != ';')
   1092 				{
   1093 					/* Simulate closing ; so you can get away with } instead of ugly }; */
   1094 					*lastce = curce;
   1095 					lastce = &(curce->next);
   1096 					curce->file_position_end = (ptr - confdata);
   1097 					curce = NULL;
   1098 				}
   1099 				break;
   1100 			case '#':
   1101 				ptr++;
   1102 				while(*ptr && (*ptr != '\n'))
   1103 					 ptr++;
   1104 				if (!*ptr)
   1105 					break;
   1106 				ptr--;
   1107 				continue;
   1108 			case '/':
   1109 				if (*(ptr+1) == '/')
   1110 				{
   1111 					ptr += 2;
   1112 					while(*ptr && (*ptr != '\n'))
   1113 						ptr++;
   1114 					if (!*ptr)
   1115 						break;
   1116 					ptr--; /* grab the \n on next loop thru */
   1117 					continue;
   1118 				}
   1119 				else if (*(ptr+1) == '*')
   1120 				{
   1121 					int commentstart = linenumber;
   1122 
   1123 					for(ptr+=2;*ptr;ptr++)
   1124 					{
   1125 						if (*ptr == '\n')
   1126 						{
   1127 							linenumber++;
   1128 						} else
   1129 						if ((*ptr == '*') && (*(ptr+1) == '/'))
   1130 						{
   1131 							ptr++;
   1132 							break;
   1133 						}
   1134 					}
   1135 					if (!*ptr)
   1136 					{
   1137 						config_error("%s:%i Comment on line %d does not end\n",
   1138 							filename, commentstart, commentstart);
   1139 						errors++;
   1140 						config_entry_free_all(curce);
   1141 						config_free(curcf);
   1142 						return NULL;
   1143 					}
   1144 				}
   1145 				break;
   1146 			case '\'':
   1147 				if (curce)
   1148 					curce->escaped = 1;
   1149 				/* fallthrough */
   1150 			case '\"':
   1151 				if (curce && curce->line_number != linenumber && cursection)
   1152 				{
   1153 					config_error("%s:%i: Missing semicolon (';') at end of line. "
   1154 					             "Line %d must end with a ; character\n",
   1155 						filename, curce->line_number, curce->line_number);
   1156 					errors++;
   1157 
   1158 					*lastce = curce;
   1159 					lastce = &(curce->next);
   1160 					curce->file_position_end = (ptr - confdata);
   1161 					curce = NULL;
   1162 				}
   1163 
   1164 				start = ++ptr;
   1165 				for(;*ptr;ptr++)
   1166 				{
   1167 					if (*ptr == '\\')
   1168 					{
   1169 						if (strchr("\\\"'", ptr[1]))
   1170 						{
   1171 							/* \\ or \" in config file (escaped) */
   1172 							ptr++; /* skip */
   1173 							continue;
   1174 						}
   1175 					}
   1176 					else if (*ptr == '\n')
   1177 						break;
   1178 					else if (curce && curce->escaped && (*ptr == '\''))
   1179 						break;
   1180 					else if ((!curce || !curce->escaped) && (*ptr == '"'))
   1181 						break;
   1182 				}
   1183 				if (!*ptr || (*ptr == '\n'))
   1184 				{
   1185 					config_error("%s:%i: Unterminated quote found\n",
   1186 							filename, linenumber);
   1187 					errors++;
   1188 					config_entry_free_all(curce);
   1189 					config_free(curcf);
   1190 					return NULL;
   1191 				}
   1192 				if (curce)
   1193 				{
   1194 					if (curce->value)
   1195 					{
   1196 						config_error("%s:%i: Extra data detected. Perhaps missing a ';' or one too many?\n",
   1197 							filename, linenumber);
   1198 						errors++;
   1199 					}
   1200 					else
   1201 					{
   1202 						safe_strldup(curce->value, start, ptr-start+1);
   1203 						preprocessor_replace_defines(&curce->value, curce);
   1204 						unreal_del_quotes(curce->value);
   1205 					}
   1206 				}
   1207 				else
   1208 				{
   1209 					curce = safe_alloc(sizeof(ConfigEntry));
   1210 					curce->line_number = linenumber;
   1211 					curce->file = curcf;
   1212 					curce->parent = cursection;
   1213 					curce->file_position_start = (start - confdata);
   1214 					safe_strldup(curce->name, start, ptr-start+1);
   1215 					preprocessor_replace_defines(&curce->name, curce);
   1216 					unreal_del_quotes(curce->name);
   1217 					preprocessor_cc_duplicate_list(cc_list, &curce->conditional_config);
   1218 				}
   1219 				break;
   1220 			case '\n':
   1221 				linenumber++;
   1222 				/* fall through */
   1223 			case '\t':
   1224 			case ' ':
   1225 			case '=':
   1226 			case '\r':
   1227 				break;
   1228 			case '@':
   1229 				/* Preprocessor item, such as @if, @define, etc. */
   1230 				start = ptr;
   1231 				for (;*ptr; ptr++)
   1232 				{
   1233 					if (*ptr == '\n')
   1234 						break;
   1235 				}
   1236 				cc = NULL;
   1237 				n = parse_preprocessor_item(start, ptr, filename, linenumber, &cc);
   1238 				linenumber++;
   1239 				if (n == PREPROCESSOR_IF)
   1240 				{
   1241 					preprocessor_level++;
   1242 					cc->priority = preprocessor_level;
   1243 					AddListItem(cc, cc_list);
   1244 				} else
   1245 				if (n == PREPROCESSOR_ENDIF)
   1246 				{
   1247 					if (preprocessor_level == 0)
   1248 					{
   1249 						config_error("%s:%i: @endif unexpected. There was no preciding unclosed @if.",
   1250 							filename, linenumber);
   1251 						errors++;
   1252 					}
   1253 					preprocessor_cc_free_level(&cc_list, preprocessor_level);
   1254 					preprocessor_level--;
   1255 				} else
   1256 				if (n == PREPROCESSOR_ERROR)
   1257 				{
   1258 					errors++;
   1259 					goto breakout;
   1260 				}
   1261 
   1262 				if (!*ptr)
   1263 					goto breakout; /* special case, since we don't want the for loop to ptr++ */
   1264 
   1265 				break;
   1266 			default:
   1267 				if ((*ptr == '*') && (*(ptr+1) == '/'))
   1268 				{
   1269 					config_status("%s:%i: Ignoring extra end comment\n",
   1270 						filename, linenumber);
   1271 					config_status("WARNING: Starting with UnrealIRCd 4.2.1 a /*-style comment stops as soon as the first */ is encountered. "
   1272 					              "See https://www.unrealircd.org/docs/FAQ#Nesting_comments for more information.");
   1273 					ptr++;
   1274 					break;
   1275 				}
   1276 				start = ptr;
   1277 				for(;*ptr;ptr++)
   1278 				{
   1279 					if ((*ptr == ' ') || (*ptr == '=') || (*ptr == '\t') || (*ptr == '\n') || (*ptr == ';'))
   1280 						break;
   1281 				}
   1282 				if (!*ptr)
   1283 				{
   1284 					if (curce)
   1285 						config_error("%s: End of file reached but directive or block at line %i did not end properly. "
   1286 									 "Perhaps a missing ; (semicolon) somewhere?\n",
   1287 							filename, curce->line_number);
   1288 					else if (cursection)
   1289 						config_error("%s: End of file reached but the section which starts at line %i did never end properly. "
   1290 									 "Perhaps a missing }; ?\n",
   1291 								filename, cursection->section_linenumber);
   1292 					else
   1293 						config_error("%s: Unexpected end of file. Some line or block did not end properly. "
   1294 						             "Look for any missing } and };\n", filename);
   1295 					errors++;
   1296 					config_entry_free_all(curce);
   1297 					config_free(curcf);
   1298 					return NULL;
   1299 				}
   1300 				if (curce)
   1301 				{
   1302 					if (curce->value)
   1303 					{
   1304 						config_error("%s:%i: Extra data detected. Check for a missing ; character at or around line %d\n",
   1305 							filename, linenumber, linenumber-1);
   1306 						errors++;
   1307 					}
   1308 					else
   1309 					{
   1310 						safe_strldup(curce->value, start, ptr-start+1);
   1311 						preprocessor_replace_defines(&curce->value, curce);
   1312 					}
   1313 				}
   1314 				else
   1315 				{
   1316 					curce = safe_alloc(sizeof(ConfigEntry));
   1317 					memset(curce, 0, sizeof(ConfigEntry));
   1318 					curce->line_number = linenumber;
   1319 					curce->file = curcf;
   1320 					curce->parent = cursection;
   1321 					curce->file_position_start = (start - confdata);
   1322 					safe_strldup(curce->name, start, ptr-start+1);
   1323 					preprocessor_replace_defines(&curce->name, curce);
   1324 					if (curce->conditional_config)
   1325 						abort();
   1326 					preprocessor_cc_duplicate_list(cc_list, &curce->conditional_config);
   1327 				}
   1328 				if ((*ptr == ';') || (*ptr == '\n'))
   1329 					ptr--;
   1330 				break;
   1331 		} /* switch */
   1332 		if (!*ptr) /* This IS possible. -- Syzop */
   1333 			break;
   1334 	} /* for */
   1335 breakout:
   1336 	if (curce)
   1337 	{
   1338 		config_error("%s: End of file reached but directive or block at line %i did not end properly. "
   1339 		             "Perhaps a missing ; (semicolon) somewhere?\n",
   1340 			filename, curce->line_number);
   1341 		errors++;
   1342 		config_entry_free_all(curce);
   1343 	}
   1344 	else if (cursection)
   1345 	{
   1346 		config_error("%s: End of file reached but the section which starts at line %i did never end properly. "
   1347 		             "Perhaps a missing }; ?\n",
   1348 				filename, cursection->section_linenumber);
   1349 		errors++;
   1350 	}
   1351 
   1352 	if (errors)
   1353 	{
   1354 		config_free(curcf);
   1355 		return NULL;
   1356 	}
   1357 	return curcf;
   1358 }
   1359 
   1360 /** Free a ConfigEntry struct, all it's children, and all it's next entries.
   1361  * Consider calling config_entry_free() instead of this one.. or at least
   1362  * check which one of the two you actually need ;)
   1363  */
   1364 void config_entry_free_all(ConfigEntry *ce)
   1365 {
   1366 	ConfigEntry	*nptr;
   1367 
   1368 	for(;ce;ce=nptr)
   1369 	{
   1370 		nptr = ce->next;
   1371 		if (ce->items)
   1372 			config_entry_free_all(ce->items);
   1373 		safe_free(ce->name);
   1374 		safe_free(ce->value);
   1375 		if (ce->conditional_config)
   1376 			preprocessor_cc_free_list(ce->conditional_config);
   1377 		safe_free(ce);
   1378 	}
   1379 }
   1380 
   1381 /** Free a specific ConfigEntry struct (and it's children).
   1382  * Caller must ensure that the entry is not in the linked list anymore.
   1383  */
   1384 void config_entry_free(ConfigEntry *ce)
   1385 {
   1386 	if (ce->items)
   1387 		config_entry_free_all(ce->items);
   1388 	safe_free(ce->name);
   1389 	safe_free(ce->value);
   1390 	if (ce->conditional_config)
   1391 		preprocessor_cc_free_list(ce->conditional_config);
   1392 	safe_free(ce);
   1393 }
   1394 
   1395 ConfigEntry *config_find_entry(ConfigEntry *ce, const char *name)
   1396 {
   1397 	ConfigEntry *cep;
   1398 
   1399 	for (cep = ce; cep; cep = cep->next)
   1400 		if (cep->name && !strcmp(cep->name, name))
   1401 			break;
   1402 	return cep;
   1403 }
   1404 
   1405 void config_error(FORMAT_STRING(const char *format), ...)
   1406 {
   1407 	va_list		ap;
   1408 	char		buffer[1024];
   1409 	char		*ptr;
   1410 
   1411 	va_start(ap, format);
   1412 	vsnprintf(buffer, sizeof(buffer), format, ap);
   1413 	va_end(ap);
   1414 	if ((ptr = strchr(buffer, '\n')) != NULL)
   1415 		*ptr = '\0';
   1416 	unreal_log_raw(ULOG_ERROR, "config", "CONFIG_ERROR_GENERIC", NULL, buffer);
   1417 	/* We cannot live with this */
   1418 	config_error_flag = 1;
   1419 }
   1420 
   1421 void config_error_missing(const char *filename, int line, const char *entry)
   1422 {
   1423 	config_error("%s:%d: %s is missing", filename, line, entry);
   1424 }
   1425 
   1426 void config_error_unknown(const char *filename, int line, const char *block,
   1427 	const char *entry)
   1428 {
   1429 	config_error("%s:%d: Unknown directive '%s::%s'", filename, line, block, entry);
   1430 }
   1431 
   1432 void config_error_unknownflag(const char *filename, int line, const char *block,
   1433 	const char *entry)
   1434 {
   1435 	config_error("%s:%d: Unknown %s flag '%s'", filename, line, block, entry);
   1436 }
   1437 
   1438 void config_error_unknownopt(const char *filename, int line, const char *block,
   1439 	const char *entry)
   1440 {
   1441 	config_error("%s:%d: Unknown %s option '%s'", filename, line, block, entry);
   1442 }
   1443 
   1444 void config_error_noname(const char *filename, int line, const char *block)
   1445 {
   1446 	config_error("%s:%d: %s block has no name", filename, line, block);
   1447 }
   1448 
   1449 void config_error_blank(const char *filename, int line, const char *block)
   1450 {
   1451 	config_error("%s:%d: Blank %s entry", filename, line, block);
   1452 }
   1453 
   1454 void config_error_empty(const char *filename, int line, const char *block,
   1455 	const char *entry)
   1456 {
   1457 	config_error("%s:%d: %s::%s specified without a value",
   1458 		filename, line, block, entry);
   1459 }
   1460 
   1461 void config_status(FORMAT_STRING(const char *format), ...)
   1462 {
   1463 	va_list		ap;
   1464 	char		buffer[1024];
   1465 	char		*ptr;
   1466 
   1467 	va_start(ap, format);
   1468 	vsnprintf(buffer, 1023, format, ap);
   1469 	va_end(ap);
   1470 	if ((ptr = strchr(buffer, '\n')) != NULL)
   1471 		*ptr = '\0';
   1472 	unreal_log_raw(ULOG_INFO, "config", "CONFIG_INFO_GENERIC", NULL, buffer);
   1473 }
   1474 
   1475 void config_warn(FORMAT_STRING(const char *format), ...)
   1476 {
   1477 	va_list		ap;
   1478 	char		buffer[1024];
   1479 	char		*ptr;
   1480 
   1481 	va_start(ap, format);
   1482 	vsnprintf(buffer, 1023, format, ap);
   1483 	va_end(ap);
   1484 	if ((ptr = strchr(buffer, '\n')) != NULL)
   1485 		*ptr = '\0';
   1486 	unreal_log_raw(ULOG_WARNING, "config", "CONFIG_WARNING_GENERIC", NULL, buffer);
   1487 }
   1488 
   1489 void config_warn_duplicate(const char *filename, int line, const char *entry)
   1490 {
   1491 	config_warn("%s:%d: Duplicate %s directive", filename, line, entry);
   1492 }
   1493 
   1494 /* returns 1 if the test fails */
   1495 int config_test_openfile(ConfigEntry *cep, int flags, mode_t mode, const char *entry, int fatal, int allow_url)
   1496 {
   1497 	int fd;
   1498 
   1499 	if (!cep->value)
   1500 	{
   1501 		if (fatal)
   1502 			config_error("%s:%i: %s: <no file specified>: no file specified",
   1503 				     cep->file->filename,
   1504 				     cep->line_number,
   1505 				     entry);
   1506 		else
   1507 
   1508 			config_warn("%s:%i: %s: <no file specified>: no file specified",
   1509 				    cep->file->filename,
   1510 				    cep->line_number,
   1511 				    entry);
   1512 		return 1;
   1513 	}
   1514 
   1515 	/* There's not much checking that can be done for asynchronously downloaded files */
   1516 	if (url_is_valid(cep->value))
   1517 	{
   1518 		if (allow_url)
   1519 			return 0;
   1520 
   1521 		/* but we can check if a URL is used wrongly :-) */
   1522 		config_warn("%s:%i: %s: %s: URL used where not allowed",
   1523 			    cep->file->filename,
   1524 			    cep->line_number,
   1525 			    entry, cep->value);
   1526 		if (fatal)
   1527 			return 1;
   1528 		else
   1529 			return 0;
   1530 	}
   1531 
   1532 	/*
   1533 	 * Make sure that files are created with the correct mode. This is
   1534 	 * because we don't feel like unlink()ing them...which would require
   1535 	 * stat()ing them to make sure that we don't delete existing ones
   1536 	 * and that we deal with all of the bugs that come with complexity.
   1537 	 * The only files we may be creating are the tunefile and pidfile so far.
   1538 	 */
   1539 	if (flags & O_CREAT)
   1540 		fd = open(cep->value, flags, mode);
   1541 	else
   1542 		fd = open(cep->value, flags);
   1543 	if (fd == -1)
   1544 	{
   1545 		if (fatal)
   1546 			config_error("%s:%i: %s: %s: %s",
   1547 				     cep->file->filename,
   1548 				     cep->line_number,
   1549 				     entry,
   1550 				     cep->value,
   1551 				     strerror(errno));
   1552 		else
   1553 			config_warn("%s:%i: %s: %s: %s",
   1554 				     cep->file->filename,
   1555 				     cep->line_number,
   1556 				     entry,
   1557 				     cep->value,
   1558 				     strerror(errno));
   1559 		return 1;
   1560 	}
   1561 	close(fd);
   1562 	return 0;
   1563 }
   1564 
   1565 int config_is_blankorempty(ConfigEntry *cep, const char *block)
   1566 {
   1567 	if (!cep->value)
   1568 	{
   1569 		config_error_empty(cep->file->filename, cep->line_number, block,
   1570 			cep->name);
   1571 		return 1;
   1572 	}
   1573 	return 0;
   1574 }
   1575 
   1576 ConfigCommand *config_binary_search(const char *cmd) {
   1577 	int start = 0;
   1578 	int stop = ARRAY_SIZEOF(_ConfigCommands)-1;
   1579 	int mid;
   1580 	while (start <= stop) {
   1581 		mid = (start+stop)/2;
   1582 		if (smycmp(cmd,_ConfigCommands[mid].name) < 0) {
   1583 			stop = mid-1;
   1584 		}
   1585 		else if (strcmp(cmd,_ConfigCommands[mid].name) == 0) {
   1586 			return &_ConfigCommands[mid];
   1587 		}
   1588 		else
   1589 			start = mid+1;
   1590 	}
   1591 	return NULL;
   1592 }
   1593 
   1594 void free_iConf(Configuration *i)
   1595 {
   1596 	FloodSettings *f, *f_next;
   1597 
   1598 	safe_free(i->link_bindip);
   1599 	safe_free(i->kline_address);
   1600 	safe_free(i->gline_address);
   1601 	safe_free(i->oper_snomask);
   1602 	safe_free(i->auto_join_chans);
   1603 	safe_free(i->oper_auto_join_chans);
   1604 	safe_free(i->allow_user_stats);
   1605 	// allow_user_stats_ext is freed elsewhere
   1606 	safe_free(i->static_quit);
   1607 	safe_free(i->static_part);
   1608 	free_tls_options(i->tls_options);
   1609 	i->tls_options = NULL;
   1610 	safe_free(i->tls_options);
   1611 	safe_free_multiline(i->plaintext_policy_user_message);
   1612 	safe_free_multiline(i->plaintext_policy_oper_message);
   1613 	safe_free(i->outdated_tls_policy_user_message);
   1614 	safe_free(i->outdated_tls_policy_oper_message);
   1615 	safe_free(i->restrict_usermodes);
   1616 	safe_free(i->restrict_channelmodes);
   1617 	safe_free(i->restrict_extendedbans);
   1618 	safe_free(i->channel_command_prefix);
   1619 	safe_free(i->level_on_join);
   1620 	safe_free(i->spamfilter_ban_reason);
   1621 	safe_free(i->spamfilter_virus_help_channel);
   1622 	// spamexcept is freed elsewhere
   1623 	safe_free(i->spamexcept_line);
   1624 	safe_free(i->reject_message_too_many_connections);
   1625 	safe_free(i->reject_message_server_full);
   1626 	safe_free(i->reject_message_unauthorized);
   1627 	safe_free(i->reject_message_kline);
   1628 	safe_free(i->reject_message_gline);
   1629 	safe_free(i->network_name);
   1630 	safe_free(i->network_name_005);
   1631 	safe_free(i->default_server);
   1632 	safe_free(i->services_name);
   1633 	safe_free(i->cloak_prefix);
   1634 	safe_free(i->prefix_quit);
   1635 	safe_free(i->helpchan);
   1636 	safe_free(i->stats_server);
   1637 	safe_free(i->sasl_server);
   1638 	// anti-flood:
   1639 	for (f = i->floodsettings; f; f = f_next)
   1640 	{
   1641 		f_next = f->next;
   1642 		free_floodsettings(f);
   1643 	}
   1644 	i->floodsettings = NULL;
   1645 }
   1646 
   1647 void config_setdefaultsettings(Configuration *i)
   1648 {
   1649 	char tmp[512];
   1650 
   1651 	safe_strdup(i->oper_snomask, OPER_SNOMASKS);
   1652 	i->server_notice_colors = 1;
   1653 	i->server_notice_show_event = 1;
   1654 	i->ident_read_timeout = 7;
   1655 	i->ident_connect_timeout = 3;
   1656 	i->ban_version_tkl_time = 86400; /* 1d */
   1657 	i->spamfilter_ban_time = 86400; /* 1d */
   1658 	safe_strdup(i->spamfilter_ban_reason, "Spam/advertising");
   1659 	safe_strdup(i->spamfilter_virus_help_channel, "#help");
   1660 	i->spamfilter_detectslow_warn = 250;
   1661 	i->spamfilter_detectslow_fatal = 500;
   1662 	i->spamfilter_stop_on_first_match = 1;
   1663 	i->maxchannelsperuser = 10;
   1664 	i->maxdccallow = 10;
   1665 	safe_strdup(i->channel_command_prefix, "`!.");
   1666 	i->conn_modes = set_usermode("+ixw");
   1667 	i->check_target_nick_bans = 1;
   1668 	i->maxbans = 60;
   1669 	i->maxbanlength = 2048;
   1670 	safe_strdup(i->level_on_join, "o");
   1671 	i->watch_away_notification = 1;
   1672 	i->uhnames = 1;
   1673 	i->ping_cookie = 1;
   1674 	i->ping_warning = 15; /* default ping warning notices 15 seconds */
   1675 	i->default_ipv6_clone_mask = 64;
   1676 	nicklengths.min = i->min_nick_length = 0; /* 0 means no minimum required */
   1677 	nicklengths.max = i->nick_length = NICKLEN;
   1678 	i->topic_length = 360;
   1679 	i->away_length = 307;
   1680 	i->kick_length = 307;
   1681 	i->quit_length = 307;
   1682 	safe_strdup(i->link_bindip, "*");
   1683 	safe_strdup(i->cloak_prefix, "Clk");
   1684 	if (!ipv6_capable())
   1685 		DISABLE_IPV6 = 1;
   1686 	safe_strdup(i->prefix_quit, "Quit");
   1687 	i->max_unknown_connections_per_ip = 3;
   1688 	i->handshake_timeout = 30;
   1689 	i->sasl_timeout = 15;
   1690 	i->handshake_delay = -1;
   1691 	i->broadcast_channel_messages = BROADCAST_CHANNEL_MESSAGES_AUTO;
   1692 
   1693 	/* Flood options */
   1694 	/* - everyone */
   1695 	i->throttle_count = 3; i->throttle_period = 60; /* throttle protection: max 3 per 60s */
   1696 	i->handshake_data_flood_amount = 4096;
   1697 	i->handshake_data_flood_ban_action = BAN_ACT_ZLINE;
   1698 	i->handshake_data_flood_ban_time = 600;
   1699 	// (targetflood is in the targetflood module)
   1700 	/* - known-users */
   1701 	config_parse_flood_generic("3:60", i, "known-users", FLD_NICK); /* NICK flood protection: max 3 per 60s */
   1702 	config_parse_flood_generic("3:90", i, "known-users", FLD_JOIN); /* JOIN flood protection: max 3 per 90s */
   1703 	config_parse_flood_generic("3:90", i, "known-users", FLD_VHOST); /* MODE -x flood protection: max 3 per 90s */
   1704 	config_parse_flood_generic("4:120", i, "known-users", FLD_AWAY); /* AWAY flood protection: max 4 per 120s */
   1705 	config_parse_flood_generic("4:60", i, "known-users", FLD_INVITE); /* INVITE flood protection: max 4 per 60s */
   1706 	config_parse_flood_generic("4:120", i, "known-users", FLD_KNOCK); /* KNOCK protection: max 4 per 120s */
   1707 	config_parse_flood_generic("10:15", i, "known-users", FLD_CONVERSATIONS); /* 10 users, new user every 15s */
   1708 	config_parse_flood_generic("180:750", i, "known-users", FLD_LAG_PENALTY); /* 180 bytes / 750 msec */
   1709 	/* - unknown-users */
   1710 	config_parse_flood_generic("2:60", i, "unknown-users", FLD_NICK); /* NICK flood protection: max 2 per 60s */
   1711 	config_parse_flood_generic("2:90", i, "unknown-users", FLD_JOIN); /* JOIN flood protection: max 2 per 90s */
   1712 	config_parse_flood_generic("2:90", i, "unknown-users", FLD_VHOST); /* MODE -x flood protection: max 2 per 90s */
   1713 	config_parse_flood_generic("4:120", i, "unknown-users", FLD_AWAY); /* AWAY flood protection: max 4 per 120s */
   1714 	config_parse_flood_generic("2:60", i, "unknown-users", FLD_INVITE); /* INVITE flood protection: max 2 per 60s */
   1715 	config_parse_flood_generic("2:120", i, "unknown-users", FLD_KNOCK); /* KNOCK protection: max 2 per 120s */
   1716 	config_parse_flood_generic("4:15", i, "unknown-users", FLD_CONVERSATIONS); /* 4 users, new user every 15s */
   1717 	config_parse_flood_generic("90:1000", i, "unknown-users", FLD_LAG_PENALTY); /* 90 bytes / 1000 msec */
   1718 
   1719 	/* TLS options */
   1720 	i->tls_options = safe_alloc(sizeof(TLSOptions));
   1721 	snprintf(tmp, sizeof(tmp), "%s/tls/server.cert.pem", CONFDIR);
   1722 	safe_strdup(i->tls_options->certificate_file, tmp);
   1723 	snprintf(tmp, sizeof(tmp), "%s/tls/server.key.pem", CONFDIR);
   1724 	safe_strdup(i->tls_options->key_file, tmp);
   1725 	snprintf(tmp, sizeof(tmp), "%s/tls/curl-ca-bundle.crt", CONFDIR);
   1726 	safe_strdup(i->tls_options->trusted_ca_file, tmp);
   1727 	safe_strdup(i->tls_options->ciphers, UNREALIRCD_DEFAULT_CIPHERS);
   1728 	safe_strdup(i->tls_options->ciphersuites, UNREALIRCD_DEFAULT_CIPHERSUITES);
   1729 	i->tls_options->protocols = TLS_PROTOCOL_TLSV1_2|TLS_PROTOCOL_TLSV1_3; /* TLSv1.2 & TLSv1.3 */
   1730 #ifdef HAS_SSL_CTX_SET1_CURVES_LIST
   1731 	safe_strdup(i->tls_options->ecdh_curves, UNREALIRCD_DEFAULT_ECDH_CURVES);
   1732 #endif
   1733 	safe_strdup(i->tls_options->outdated_protocols, "TLSv1,TLSv1.1");
   1734 	/* the following may look strange but "AES*" matches all
   1735 	 * AES ciphersuites that do not have Forward Secrecy.
   1736 	 * Any decent client using AES will use ECDHE-xx-AES.
   1737 	 */
   1738 	safe_strdup(i->tls_options->outdated_ciphers, "AES*,RC4*,DES*");
   1739 
   1740 	i->plaintext_policy_user = POLICY_ALLOW;
   1741 	i->plaintext_policy_oper = POLICY_DENY;
   1742 	i->plaintext_policy_server = POLICY_DENY;
   1743 
   1744 	i->outdated_tls_policy_user = POLICY_WARN;
   1745 	i->outdated_tls_policy_oper = POLICY_DENY;
   1746 	i->outdated_tls_policy_server = POLICY_DENY;
   1747 
   1748 	safe_strdup(i->reject_message_too_many_connections, "Too many connections from your IP");
   1749 	safe_strdup(i->reject_message_server_full, "This server is full");
   1750 	safe_strdup(i->reject_message_unauthorized, "You are not authorized to connect to this server");
   1751 	safe_strdup(i->reject_message_kline, "You are not welcome on this server. $bantype: $banreason. Email $klineaddr for more information.");
   1752 	safe_strdup(i->reject_message_gline, "You are not welcome on this network. $bantype: $banreason. Email $glineaddr for more information.");
   1753 
   1754 	i->topic_setter = SETTER_NICK;
   1755 	i->ban_setter = SETTER_NICK;
   1756 	i->ban_setter_sync = 1;
   1757 
   1758 	i->allowed_channelchars = ALLOWED_CHANNELCHARS_UTF8;
   1759 
   1760 	i->automatic_ban_target = BAN_TARGET_IP;
   1761 	i->manual_ban_target = BAN_TARGET_HOST;
   1762 
   1763 	i->hide_idle_time = HIDE_IDLE_TIME_OPER_USERMODE;
   1764 
   1765 	i->who_limit = 100;
   1766 
   1767 	i->named_extended_bans = 1;
   1768 }
   1769 
   1770 /** Similar to config_setdefaultsettings but this one is applied *AFTER*
   1771  * the entire configuration has been ran (sometimes this is the only way it can be done..).
   1772  * NOTE: iConf is thus already populated with (non-default) values. Only overwrite if necessary!
   1773  */
   1774 void postconf_defaults(void)
   1775 {
   1776 	TKL *tk;
   1777 	char *encoded;
   1778 
   1779 	if (!iConf.modes_on_join_set)
   1780 	{
   1781 		/* We could not do this in config_setdefaultsettings()
   1782 		 * because the channel mode modules were not initialized yet.
   1783 		 */
   1784 		conf_channelmodes("+nt", &iConf.modes_on_join);
   1785 	}
   1786 	if (!iConf.plaintext_policy_user_message)
   1787 	{
   1788 		/* The message depends on whether it's reject or warn.. */
   1789 		if (iConf.plaintext_policy_user == POLICY_DENY)
   1790 			addmultiline(&iConf.plaintext_policy_user_message, "Insecure connection. Please reconnect using TLS.");
   1791 		else if (iConf.plaintext_policy_user == POLICY_WARN)
   1792 			addmultiline(&iConf.plaintext_policy_user_message, "WARNING: Insecure connection. Please consider using TLS.");
   1793 	}
   1794 
   1795 	if (!iConf.plaintext_policy_oper_message)
   1796 	{
   1797 		/* The message depends on whether it's reject or warn.. */
   1798 		if (iConf.plaintext_policy_oper == POLICY_DENY)
   1799 		{
   1800 			addmultiline(&iConf.plaintext_policy_oper_message, "You need to use a secure connection (TLS) in order to /OPER.");
   1801 			addmultiline(&iConf.plaintext_policy_oper_message, "See https://www.unrealircd.org/docs/FAQ#oper-requires-tls");
   1802 		}
   1803 		else if (iConf.plaintext_policy_oper == POLICY_WARN)
   1804 			addmultiline(&iConf.plaintext_policy_oper_message, "WARNING: You /OPER'ed up from an insecure connection. Please consider using TLS.");
   1805 	}
   1806 
   1807 	if (!iConf.outdated_tls_policy_user_message)
   1808 	{
   1809 		/* The message depends on whether it's reject or warn.. */
   1810 		if (iConf.outdated_tls_policy_user == POLICY_DENY)
   1811 			safe_strdup(iConf.outdated_tls_policy_user_message, "Your IRC client is using an outdated TLS protocol or ciphersuite ($protocol-$cipher). Please upgrade your IRC client.");
   1812 		else if (iConf.outdated_tls_policy_user == POLICY_WARN)
   1813 			safe_strdup(iConf.outdated_tls_policy_user_message, "WARNING: Your IRC client is using an outdated TLS protocol or ciphersuite ($protocol-$cipher). Please upgrade your IRC client.");
   1814 	}
   1815 
   1816 	if (!iConf.outdated_tls_policy_oper_message)
   1817 	{
   1818 		/* The message depends on whether it's reject or warn.. */
   1819 		if (iConf.outdated_tls_policy_oper == POLICY_DENY)
   1820 			safe_strdup(iConf.outdated_tls_policy_oper_message, "Your IRC client is using an outdated TLS protocol or ciphersuite ($protocol-$cipher). Please upgrade your IRC client.");
   1821 		else if (iConf.outdated_tls_policy_oper == POLICY_WARN)
   1822 			safe_strdup(iConf.outdated_tls_policy_oper_message, "WARNING: Your IRC client is using an outdated TLS protocol or ciphersuite ($protocol-$cipher). Please upgrade your IRC client.");
   1823 	}
   1824 
   1825 	postconf_defaults_log_block();
   1826 }
   1827 
   1828 void postconf_fixes(void)
   1829 {
   1830 	/* If set::topic-setter is set to "nick-user-host" then the
   1831 	 * maximum topic length becomes shorter.
   1832 	 */
   1833 	if ((iConf.topic_setter == SETTER_NICK_USER_HOST) &&
   1834 	    (iConf.topic_length > 340))
   1835 	{
   1836 		config_warn("set::topic-length adjusted from %d to 340, which is the maximum because "
   1837 		            "set::topic-setter is set to 'nick-user-host'.", iConf.topic_length);
   1838 		iConf.topic_length = 340;
   1839 	}
   1840 }
   1841 
   1842 /* Needed for set::options::allow-part-if-shunned,
   1843  * we can't just make it CMD_SHUN and do a ALLOW_PART_IF_SHUNNED in
   1844  * cmd_part itself because that will also block internal calls (like sapart). -- Syzop
   1845  */
   1846 static void do_weird_shun_stuff()
   1847 {
   1848 RealCommand *cmptr;
   1849 
   1850 	if ((cmptr = find_command_simple("PART")))
   1851 	{
   1852 		if (ALLOW_PART_IF_SHUNNED)
   1853 			cmptr->flags |= CMD_SHUN;
   1854 		else
   1855 			cmptr->flags &= ~CMD_SHUN;
   1856 	}
   1857 }
   1858 
   1859 /** Various things that are done at the very end after the configuration file
   1860  * has been read and almost all values have been set. This is to deal with
   1861  * things like adding a default log { } block if there is none and that kind
   1862  * of things.
   1863  * This function is called by config_test(), both on boot and on rehash.
   1864  */
   1865 void postconf(void)
   1866 {
   1867 	postconf_defaults();
   1868 	postconf_fixes();
   1869 	do_weird_shun_stuff();
   1870 	isupport_init(); /* for all the 005 values that changed.. */
   1871 	tls_check_expiry(NULL);
   1872 
   1873 #if OPENSSL_VERSION_NUMBER >= 0x10101000L
   1874 	if (loop.rehashing)
   1875 		reinit_tls();
   1876 #endif
   1877 }
   1878 
   1879 int isanyserverlinked(void)
   1880 {
   1881 	return !list_empty(&server_list);
   1882 }
   1883 
   1884 void applymeblock(void)
   1885 {
   1886 	if (!conf_me)
   1887 		return; /* uh-huh? */
   1888 
   1889 	/* Info text may always change, just wouldn't show up on other servers, that's all.. */
   1890 	strlcpy(me.info, conf_me->info, sizeof(me.info));
   1891 
   1892 	/* Name can only be set once (on boot) */
   1893 	if (!*me.name)
   1894 		strlcpy(me.name, conf_me->name, sizeof(me.name));
   1895 	else if (strcmp(me.name, conf_me->name))
   1896 	{
   1897 		config_warn("You changed the servername (me::name). "
   1898 		            "This change will NOT be effective unless you restart the IRC Server.");
   1899 	}
   1900 
   1901 	if (!*me.id)
   1902 		strlcpy(me.id, conf_me->sid, sizeof(me.id));
   1903 }
   1904 
   1905 /** Run config test and all post config tests. */
   1906 int config_test_all(void)
   1907 {
   1908 	if ((config_test_blocks() < 0) || (callbacks_check() < 0) || (efunctions_check() < 0) ||
   1909 	    reloadable_perm_module_unloaded() || !tls_tests() || !log_tests())
   1910 	{
   1911 		return 0;
   1912 	}
   1913 
   1914 	special_delayed_unloading();
   1915 
   1916 	return 1;
   1917 }
   1918 
   1919 /** Process all loadmodule directives in all includes.
   1920  * This was previously done at the same time as 'include' was called but
   1921  * that was too early now that we have blacklist-module, so moved here.
   1922  * @retval 1 on success, 0 on any failed loadmodule directive.
   1923  */
   1924 int config_loadmodules(void)
   1925 {
   1926 	ConfigFile *cfptr;
   1927 	ConfigEntry *ce;
   1928 	ConfigItem_blacklist_module *blm, *blm_next;
   1929 
   1930 	int fatal_ret = 0, ret;
   1931 
   1932 	for (cfptr = conf; cfptr; cfptr = cfptr->next)
   1933 	{
   1934 		if (config_verbose > 1)
   1935 			config_status("Testing %s", cfptr->filename);
   1936 		for (ce = cfptr->items; ce; ce = ce->next)
   1937 		{
   1938 			if (!strcmp(ce->name, "loadmodule"))
   1939 			{
   1940 				if (ce->conditional_config)
   1941 				{
   1942 					config_error("%s:%d: Currently you cannot have a 'loadmodule' statement "
   1943 						     "within an @if block, sorry.",
   1944 						     ce->file->filename, ce->line_number);
   1945 					return 0;
   1946 				}
   1947 				ret = _conf_loadmodule(cfptr, ce);
   1948 				if (ret < fatal_ret)
   1949 					fatal_ret = ret; /* lowest wins */
   1950 			}
   1951 		}
   1952 	}
   1953 
   1954 	/* Let's free the blacklist-module list here as well */
   1955 	for (blm = conf_blacklist_module; blm; blm = blm_next)
   1956 	{
   1957 		blm_next = blm->next;
   1958 		safe_free(blm->name);
   1959 		safe_free(blm);
   1960 	}
   1961 	conf_blacklist_module = NULL;
   1962 	/* End of freeing code */
   1963 
   1964 	/* If any loadmodule returned a fatal (-1) error code then we return fail status (0) */
   1965 	if (fatal_ret < 0)
   1966 		return 0; /* FAIL */
   1967 	return 1; /* SUCCESS */
   1968 }
   1969 
   1970 /** Reject the configuration load.
   1971  * This is called both from boot and from rehash.
   1972  */
   1973 void config_load_failed(void)
   1974 {
   1975 	if (conf)
   1976 		unreal_log(ULOG_ERROR, "config", "CONFIG_NOT_LOADED", NULL, "IRCd configuration failed to load");
   1977 	loop.config_status = CONFIG_STATUS_ROLLBACK;
   1978 	Unload_all_testing_modules();
   1979 	free_all_config_resources();
   1980 	config_free(conf);
   1981 	conf = NULL;
   1982 	free_iConf(&tempiConf);
   1983 #ifdef _WIN32
   1984 	if (!loop.rehashing)
   1985 		win_error(); /* GUI popup */
   1986 #endif
   1987 }
   1988 
   1989 int config_read_start(void)
   1990 {
   1991 	int ret;
   1992 
   1993 	config_status("Loading IRCd configuration..");
   1994 	loop.config_load_failed = 0;
   1995 
   1996 	if (conf)
   1997 	{
   1998 		config_error("%s:%i - Someone forgot to clean up", __FILE__, __LINE__);
   1999 		return -1;
   2000 	}
   2001 
   2002 	/* We set this to 1 because otherwise we may call rehash_internal()
   2003 	 * already from config_read_file() which is too soon (race).
   2004 	 */
   2005 	loop.rehash_download_busy = 1;
   2006 	add_config_resource(configfile, RESOURCE_INCLUDE, NULL);
   2007 	ret = config_read_file(configfile, configfile);
   2008 	loop.rehash_download_busy = 0;
   2009 	if (ret < 0)
   2010 	{
   2011 		config_load_failed();
   2012 		return -1;
   2013 	}
   2014 	return 1;
   2015 }
   2016 
   2017 int is_config_read_finished(void)
   2018 {
   2019 	ConfigResource *rs;
   2020 
   2021 	if (loop.rehash_download_busy)
   2022 		return 0;
   2023 
   2024 	for (rs = config_resources; rs; rs = rs->next)
   2025 	{
   2026 		if (rs->type & RESOURCE_DLQUEUED)
   2027 		{
   2028 			//config_status("Waiting for %s...", rs->url);
   2029 			return 0;
   2030 		}
   2031 	}
   2032 
   2033 	return 1;
   2034 }
   2035 
   2036 int config_test(void)
   2037 {
   2038 	char *old_pid_file = NULL;
   2039 
   2040 	if (loop.config_load_failed)
   2041 	{
   2042 		/* An error was already printed to the user.
   2043 		 * This happens in case of a failed loaded remote URL
   2044 		 */
   2045 		config_load_failed();
   2046 		return -1;
   2047 	}
   2048 
   2049 	config_status("Testing IRCd configuration..");
   2050 	loop.config_status = CONFIG_STATUS_TEST;
   2051 
   2052 	memset(&tempiConf, 0, sizeof(iConf));
   2053 	memset(&settings, 0, sizeof(settings));
   2054 	memset(&requiredstuff, 0, sizeof(requiredstuff));
   2055 	memset(&nicklengths, 0, sizeof(nicklengths));
   2056 	config_setdefaultsettings(&tempiConf);
   2057 	clicap_pre_rehash();
   2058 	log_pre_rehash();
   2059 	free_config_defines();
   2060 
   2061 	if (!config_loadmodules())
   2062 	{
   2063 		config_load_failed();
   2064 		return -1;
   2065 	}
   2066 
   2067 	loop.config_status = CONFIG_STATUS_POSTTEST;
   2068 
   2069 	preprocessor_resolve_conditionals_all(PREPROCESSOR_PHASE_MODULE);
   2070 
   2071 	if (!config_test_all())
   2072 	{
   2073 		config_error("IRCd configuration failed to pass testing");
   2074 		config_load_failed();
   2075 		return -1;
   2076 	}
   2077 	loop.config_status = CONFIG_STATUS_PRE_INIT;
   2078 	callbacks_switchover();
   2079 	efunctions_switchover();
   2080 	set_targmax_defaults();
   2081 	set_security_group_defaults();
   2082 	if (loop.rehashing)
   2083 	{
   2084 		Hook *h;
   2085 		safe_strdup(old_pid_file, conf_files->pid_file);
   2086 		unrealdns_delasyncconnects();
   2087 		config_rehash();
   2088 		/* Notify permanent modules of the rehash */
   2089 		for (h = Hooks[HOOKTYPE_REHASH]; h; h = h->next)
   2090 		{
   2091 			if (!h->owner)
   2092 				continue;
   2093 			if (!(h->owner->options & MOD_OPT_PERM))
   2094 				continue;
   2095 			(*(h->func.intfunc))();
   2096 		}
   2097 		/* Last step: */
   2098 		Unload_all_loaded_modules();
   2099 	}
   2100 	config_pre_run_log();
   2101 
   2102 	loop.config_status = CONFIG_STATUS_INIT;
   2103 	Init_all_testing_modules();
   2104 
   2105 	loop.config_status = CONFIG_STATUS_RUN_CONFIG;
   2106 	if (config_run_blocks() < 0)
   2107 	{
   2108 		config_error("Bad case of config errors. Server will now die. This really shouldn't happen");
   2109 #ifdef _WIN32
   2110 		if (!loop.rehashing)
   2111 			win_error();
   2112 #endif
   2113 		abort();
   2114 	}
   2115 
   2116 	applymeblock();
   2117 
   2118 	if (old_pid_file && strcmp(old_pid_file, conf_files->pid_file))
   2119 	{
   2120 		write_pidfile();
   2121 		unlink(old_pid_file);
   2122 	}
   2123 	safe_free(old_pid_file);
   2124 
   2125 	config_free(conf);
   2126 	conf = NULL;
   2127 	if (loop.rehashing)
   2128 	{
   2129 		/* loop.config_status = CONFIG_STATUS_LOAD is done by module_loadall() */
   2130 		module_loadall();
   2131 		RunHook(HOOKTYPE_REHASH_COMPLETE);
   2132 	}
   2133 	loop.config_status = CONFIG_STATUS_POSTLOAD;
   2134 	postconf();
   2135 	unreal_log(ULOG_INFO, "config", "CONFIG_LOADED", NULL, "Configuration loaded");
   2136 	unload_all_unused_mtag_handlers();
   2137 	return 0;
   2138 }
   2139 
   2140 void config_parse_and_queue_urls(ConfigEntry *ce)
   2141 {
   2142 	for (; ce; ce = ce->next)
   2143 	{
   2144 		if (loop.config_load_failed)
   2145 			break;
   2146 		if (ce->name && !strcmp(ce->name, "include"))
   2147 			continue; /* handled elsewhere */
   2148 		if (ce->value && !ce->escaped && url_is_valid(ce->value))
   2149 			add_config_resource(ce->value, 0, ce);
   2150 		if (ce->items)
   2151 			config_parse_and_queue_urls(ce->items);
   2152 	}
   2153 }
   2154 
   2155 /**
   2156  * Read configuration file into ConfigEntry items and add it to the 'conf'
   2157  * list. This checks the file for parse errors, but doesn't do much
   2158  * otherwise. Only: module blacklist checking and checking for "include"
   2159  * items to see if we need to read and parse more configuration files
   2160  * that are included from this one.
   2161  *
   2162  * One _must_ call add_config_resource() before calling config_read_file().
   2163  * This way, include recursion may be detected and reported to the user
   2164  * as an error instead of causing the IRCd to hang in an infinite
   2165  * recursion, eat up memory, and eventually overflow its stack ;-).
   2166  *
   2167  * @param filename the file where the conf may be read from
   2168  * @param display_name The path or URL used to refer to this file.
   2169  *        (mostly to support remote includes' URIs for recursive include detection).
   2170  * @return 1 on success, a negative number on error
   2171  */
   2172 int config_read_file(const char *filename, const char *display_name)
   2173 {
   2174 	ConfigFile 	*cfptr, *cfptr2, **cfptr3;
   2175 	ConfigEntry 	*ce;
   2176 	ConfigResource *rs;
   2177 	int ret;
   2178 	int counter;
   2179 
   2180 	if (config_verbose > 0)
   2181 		config_status("Loading config file %s ..", filename);
   2182 
   2183 	need_operclass_permissions_upgrade = 0;
   2184 
   2185 	/* Check if we're accidentally including a file a second
   2186 	 * time. We should expect to find one entry in this list: the
   2187 	 * entry for our current file.
   2188 	 * Note that no user should be able to trigger this, this
   2189 	 * can only happen if we have buggy code somewhere.
   2190 	 */
   2191 	counter = 0;
   2192 	for (rs = config_resources; rs; rs = rs->next)
   2193 	{
   2194 #ifndef _WIN32
   2195 		if (rs->file && !strcmp(filename, rs->file))
   2196 #else
   2197 		if (rs->file && !strcasecmp(filename, rs->file))
   2198 #endif
   2199 		{
   2200 			counter ++;
   2201 			continue;
   2202 		}
   2203 		if (rs->url && !strcmp(display_name, rs->url))
   2204 		{
   2205 			counter ++;
   2206 			continue;
   2207 		}
   2208 	}
   2209 	if (counter > 1)
   2210 	{
   2211 		unreal_log(ULOG_ERROR, "config", "CONFIG_BUG_DUPLICATE_RESOURCE", NULL,
   2212 		           "[BUG] Config file $file has been loaded $counter times. "
   2213 		           "This should not happen. Someone forgot to call "
   2214 		           "add_config_resource() or check its return value!",
   2215 		           log_data_string("file", filename),
   2216 		           log_data_integer("counter", counter));
   2217 		return -1;
   2218 	}
   2219 	/* end include recursion checking code */
   2220 
   2221 	if ((cfptr = config_load(filename, display_name)))
   2222 	{
   2223 		for (cfptr3 = &conf, cfptr2 = conf; cfptr2; cfptr2 = cfptr2->next)
   2224 			cfptr3 = &cfptr2->next;
   2225 		*cfptr3 = cfptr;
   2226 
   2227 		if (config_verbose > 1)
   2228 			config_status("Loading module blacklist in %s", filename);
   2229 
   2230 		preprocessor_resolve_conditionals_ce(&cfptr->items, PREPROCESSOR_PHASE_INITIAL);
   2231 
   2232 		for (ce = cfptr->items; ce; ce = ce->next)
   2233 			if (!strcmp(ce->name, "blacklist-module"))
   2234 				 _test_blacklist_module(cfptr, ce);
   2235 
   2236 		preprocessor_resolve_conditionals_ce(&cfptr->items, PREPROCESSOR_PHASE_SECONDARY);
   2237 
   2238 		/* Load urls */
   2239 		config_parse_and_queue_urls(cfptr->items);
   2240 
   2241 		if(loop.config_load_failed) /* something bad happened while processing urls */
   2242 			return -1;
   2243 
   2244 		/* Load includes */
   2245 		if (config_verbose > 1)
   2246 			config_status("Searching through %s for include files..", filename);
   2247 
   2248 		for (ce = cfptr->items; ce; ce = ce->next)
   2249 		{
   2250 			if (!strcmp(ce->name, "include"))
   2251 			{
   2252 				if (ce->conditional_config)
   2253 				{
   2254 					config_error("%s:%d: Currently you cannot have an 'include' statement "
   2255 					             "within an @if block, sorry. However, you CAN do it the other "
   2256 					             "way around, that is: put the @if within the included file itself.",
   2257 					             ce->file->filename, ce->line_number);
   2258 					return -1;
   2259 				}
   2260 				ret = _conf_include(cfptr, ce);
   2261 				if (ret < 0)
   2262 					return ret;
   2263 			}
   2264 		}
   2265 		return 1;
   2266 	}
   2267 	else
   2268 	{
   2269 		unreal_log(ULOG_ERROR, "config", "CONFIG_LOAD_FILE_FAILED", NULL,
   2270 		           "Could not load configuration file: $resource",
   2271 		           log_data_string("resource", display_name),
   2272 		           log_data_string("filename", filename));
   2273 #ifdef _WIN32
   2274 		if (!strcmp(filename, "conf/unrealircd.conf"))
   2275 		{
   2276 			if (file_exists("unrealircd.conf"))
   2277 			{
   2278 				config_error("Note that 'unrealircd.conf' now belongs in the 'conf' subdirectory! (So move it to there)");
   2279 			} else {
   2280 				config_error("New to UnrealIRCd? Be sure to read https://www.unrealircd.org/docs/Installing_%%28Windows%%29");
   2281 			}
   2282 		}
   2283 #endif
   2284 		return -1;
   2285 	}
   2286 }
   2287 
   2288 /** Remove all TKL's that were added by the config file(s).
   2289  * This is done after config passed testing and right before
   2290  * adding the (new) entries.
   2291  */
   2292 void remove_config_tkls(void)
   2293 {
   2294 	TKL *tk, *tk_next;
   2295 	int index, index2;
   2296 
   2297 	/* IP hashed TKL list */
   2298 	for (index = 0; index < TKLIPHASHLEN1; index++)
   2299 	{
   2300 		for (index2 = 0; index2 < TKLIPHASHLEN2; index2++)
   2301 		{
   2302 			for (tk = tklines_ip_hash[index][index2]; tk; tk = tk_next)
   2303 			{
   2304 				tk_next = tk->next;
   2305 				if (tk->flags & TKL_FLAG_CONFIG)
   2306 					tkl_del_line(tk);
   2307 			}
   2308 		}
   2309 	}
   2310 
   2311 	/* Generic TKL list */
   2312 	for (index = 0; index < TKLISTLEN; index++)
   2313 	{
   2314 		for (tk = tklines[index]; tk; tk = tk_next)
   2315 		{
   2316 			tk_next = tk->next;
   2317 			if (tk->flags & TKL_FLAG_CONFIG)
   2318 				tkl_del_line(tk);
   2319 		}
   2320 	}
   2321 }
   2322 
   2323 void config_rehash()
   2324 {
   2325 	ConfigItem_oper			*oper_ptr;
   2326 	ConfigItem_class 		*class_ptr;
   2327 	ConfigItem_ulines 		*uline_ptr;
   2328 	ConfigItem_allow 		*allow_ptr;
   2329 	ConfigItem_ban 			*ban_ptr;
   2330 	ConfigItem_link 		*link_ptr;
   2331 	ConfigItem_listen	 	*listen_ptr;
   2332 	ConfigItem_tld			*tld_ptr;
   2333 	ConfigItem_vhost		*vhost_ptr;
   2334 	ConfigItem_deny_channel		*deny_channel_ptr;
   2335 	ConfigItem_allow_channel	*allow_channel_ptr;
   2336 	ConfigItem_admin		*admin_ptr;
   2337 	ConfigItem_deny_version		*deny_version_ptr;
   2338 	ConfigItem_alias		*alias_ptr;
   2339 	ConfigItem_help			*help_ptr;
   2340 	ConfigItem_offchans		*of_ptr;
   2341 	ConfigItem_sni			*sni;
   2342 	OperStat 			*os_ptr;
   2343 	ListStruct 	*next, *next2;
   2344 	SpamExcept *spamex_ptr;
   2345 
   2346 	USE_BAN_VERSION = 0;
   2347 
   2348 	for (admin_ptr = conf_admin; admin_ptr; admin_ptr = (ConfigItem_admin *)next)
   2349 	{
   2350 		next = (ListStruct *)admin_ptr->next;
   2351 		safe_free(admin_ptr->line);
   2352 		DelListItem(admin_ptr, conf_admin);
   2353 		safe_free(admin_ptr);
   2354 	}
   2355 
   2356 	for (oper_ptr = conf_oper; oper_ptr; oper_ptr = (ConfigItem_oper *)next)
   2357 	{
   2358 		SWhois *s, *s_next;
   2359 		next = (ListStruct *)oper_ptr->next;
   2360 		safe_free(oper_ptr->name);
   2361 		safe_free(oper_ptr->snomask);
   2362 		safe_free(oper_ptr->operclass);
   2363 		safe_free(oper_ptr->vhost);
   2364 		Auth_FreeAuthConfig(oper_ptr->auth);
   2365 		free_security_group(oper_ptr->match);
   2366 		DelListItem(oper_ptr, conf_oper);
   2367 		for (s = oper_ptr->swhois; s; s = s_next)
   2368 		{
   2369 			s_next = s->next;
   2370 			safe_free(s->line);
   2371 			safe_free(s->setby);
   2372 			safe_free(s);
   2373 		}
   2374 		safe_free(oper_ptr);
   2375 	}
   2376 
   2377 	for (link_ptr = conf_link; link_ptr; link_ptr = (ConfigItem_link *) next)
   2378 	{
   2379 		next = (ListStruct *)link_ptr->next;
   2380 		if (link_ptr->refcount == 0)
   2381 		{
   2382 			delete_linkblock(link_ptr);
   2383 		}
   2384 		else
   2385 		{
   2386 			link_ptr->flag.temporary = 1;
   2387 		}
   2388 	}
   2389 	for (class_ptr = conf_class; class_ptr; class_ptr = (ConfigItem_class *) next)
   2390 	{
   2391 		next = (ListStruct *)class_ptr->next;
   2392 		if (class_ptr->flag.permanent == 1)
   2393 			continue;
   2394 		class_ptr->flag.temporary = 1;
   2395 		/* We'll wipe it out when it has no clients */
   2396 		if (!class_ptr->clients && !class_ptr->xrefcount)
   2397 		{
   2398 			delete_classblock(class_ptr);
   2399 		}
   2400 	}
   2401 	for (uline_ptr = conf_ulines; uline_ptr; uline_ptr = (ConfigItem_ulines *) next)
   2402 	{
   2403 		next = (ListStruct *)uline_ptr->next;
   2404 		/* We'll wipe it out when it has no clients */
   2405 		safe_free(uline_ptr->servername);
   2406 		DelListItem(uline_ptr, conf_ulines);
   2407 		safe_free(uline_ptr);
   2408 	}
   2409 	for (allow_ptr = conf_allow; allow_ptr; allow_ptr = (ConfigItem_allow *) next)
   2410 	{
   2411 		next = (ListStruct *)allow_ptr->next;
   2412 		free_security_group(allow_ptr->match);
   2413 		Auth_FreeAuthConfig(allow_ptr->auth);
   2414 		DelListItem(allow_ptr, conf_allow);
   2415 		safe_free(allow_ptr);
   2416 	}
   2417 	/* Free ban realname { }, ban server { } and ban version { } */
   2418 	for (ban_ptr = conf_ban; ban_ptr; ban_ptr = (ConfigItem_ban *) next)
   2419 	{
   2420 		next = (ListStruct *)ban_ptr->next;
   2421 		if (ban_ptr->flag.type2 == CONF_BAN_TYPE_CONF || ban_ptr->flag.type2 == CONF_BAN_TYPE_TEMPORARY)
   2422 		{
   2423 			safe_free(ban_ptr->mask);
   2424 			safe_free(ban_ptr->reason);
   2425 			DelListItem(ban_ptr, conf_ban);
   2426 			safe_free(ban_ptr);
   2427 		}
   2428 	}
   2429 	for (listen_ptr = conf_listen; listen_ptr; listen_ptr = listen_ptr->next)
   2430 	{
   2431 		if (!(listen_ptr->options & LISTENER_CONTROL))
   2432 			listen_ptr->flag.temporary = 1;
   2433 	}
   2434 	for (tld_ptr = conf_tld; tld_ptr; tld_ptr = (ConfigItem_tld *) next)
   2435 	{
   2436 		next = (ListStruct *)tld_ptr->next;
   2437 		safe_free(tld_ptr->motd_file);
   2438 		safe_free(tld_ptr->rules_file);
   2439 		safe_free(tld_ptr->smotd_file);
   2440 		safe_free(tld_ptr->opermotd_file);
   2441 		safe_free(tld_ptr->botmotd_file);
   2442 
   2443 		free_motd(&tld_ptr->motd);
   2444 		free_motd(&tld_ptr->rules);
   2445 		free_motd(&tld_ptr->smotd);
   2446 		free_motd(&tld_ptr->opermotd);
   2447 		free_motd(&tld_ptr->botmotd);
   2448 
   2449 		free_security_group(tld_ptr->match);
   2450 
   2451 		DelListItem(tld_ptr, conf_tld);
   2452 		safe_free(tld_ptr);
   2453 	}
   2454 	for (vhost_ptr = conf_vhost; vhost_ptr; vhost_ptr = (ConfigItem_vhost *) next)
   2455 	{
   2456 		SWhois *s, *s_next;
   2457 
   2458 		next = (ListStruct *)vhost_ptr->next;
   2459 
   2460 		safe_free(vhost_ptr->login);
   2461 		Auth_FreeAuthConfig(vhost_ptr->auth);
   2462 		safe_free(vhost_ptr->virthost);
   2463 		safe_free(vhost_ptr->virtuser);
   2464 		free_security_group(vhost_ptr->match);
   2465 		for (s = vhost_ptr->swhois; s; s = s_next)
   2466 		{
   2467 			s_next = s->next;
   2468 			safe_free(s->line);
   2469 			safe_free(s->setby);
   2470 			safe_free(s);
   2471 		}
   2472 		DelListItem(vhost_ptr, conf_vhost);
   2473 		safe_free(vhost_ptr);
   2474 	}
   2475 
   2476 	remove_config_tkls();
   2477 
   2478 	for (deny_version_ptr = conf_deny_version; deny_version_ptr; deny_version_ptr = (ConfigItem_deny_version *) next) {
   2479 		next = (ListStruct *)deny_version_ptr->next;
   2480 		safe_free(deny_version_ptr->mask);
   2481 		safe_free(deny_version_ptr->version);
   2482 		safe_free(deny_version_ptr->flags);
   2483 		DelListItem(deny_version_ptr, conf_deny_version);
   2484 		safe_free(deny_version_ptr);
   2485 	}
   2486 	for (deny_channel_ptr = conf_deny_channel; deny_channel_ptr; deny_channel_ptr = (ConfigItem_deny_channel *) next)
   2487 	{
   2488 		next = (ListStruct *)deny_channel_ptr->next;
   2489 		safe_free(deny_channel_ptr->redirect);
   2490 		safe_free(deny_channel_ptr->channel);
   2491 		safe_free(deny_channel_ptr->reason);
   2492 		safe_free(deny_channel_ptr->class);
   2493 		DelListItem(deny_channel_ptr, conf_deny_channel);
   2494 		free_security_group(deny_channel_ptr->match);
   2495 		safe_free(deny_channel_ptr);
   2496 	}
   2497 
   2498 	for (allow_channel_ptr = conf_allow_channel; allow_channel_ptr; allow_channel_ptr = (ConfigItem_allow_channel *) next)
   2499 	{
   2500 		next = (ListStruct *)allow_channel_ptr->next;
   2501 		safe_free(allow_channel_ptr->channel);
   2502 		safe_free(allow_channel_ptr->class);
   2503 		DelListItem(allow_channel_ptr, conf_allow_channel);
   2504 		free_security_group(allow_channel_ptr->match);
   2505 		safe_free(allow_channel_ptr);
   2506 	}
   2507 
   2508 	if (conf_drpass)
   2509 	{
   2510 		Auth_FreeAuthConfig(conf_drpass->restartauth);
   2511 		conf_drpass->restartauth = NULL;
   2512 		Auth_FreeAuthConfig(conf_drpass->dieauth);
   2513 		conf_drpass->dieauth = NULL;
   2514 		safe_free(conf_drpass);
   2515 	}
   2516 	for (alias_ptr = conf_alias; alias_ptr; alias_ptr = (ConfigItem_alias *)next) {
   2517 		RealCommand *cmptr = find_command(alias_ptr->alias, 0);
   2518 		ConfigItem_alias_format *fmt;
   2519 		next = (ListStruct *)alias_ptr->next;
   2520 		safe_free(alias_ptr->nick);
   2521 		if (cmptr)
   2522 			CommandDelX(NULL, cmptr);
   2523 		safe_free(alias_ptr->alias);
   2524 		if (alias_ptr->format && (alias_ptr->type == ALIAS_COMMAND)) {
   2525 			for (fmt = (ConfigItem_alias_format *) alias_ptr->format; fmt; fmt = (ConfigItem_alias_format *) next2)
   2526 			{
   2527 				next2 = (ListStruct *)fmt->next;
   2528 				safe_free(fmt->format);
   2529 				safe_free(fmt->nick);
   2530 				safe_free(fmt->parameters);
   2531 				unreal_delete_match(fmt->expr);
   2532 				DelListItem(fmt, alias_ptr->format);
   2533 				safe_free(fmt);
   2534 			}
   2535 		}
   2536 		DelListItem(alias_ptr, conf_alias);
   2537 		safe_free(alias_ptr);
   2538 	}
   2539 	for (help_ptr = conf_help; help_ptr; help_ptr = (ConfigItem_help *)next) {
   2540 		MOTDLine *text;
   2541 		next = (ListStruct *)help_ptr->next;
   2542 		safe_free(help_ptr->command);
   2543 		while (help_ptr->text) {
   2544 			text = help_ptr->text->next;
   2545 			safe_free(help_ptr->text->line);
   2546 			safe_free(help_ptr->text);
   2547 			help_ptr->text = text;
   2548 		}
   2549 		DelListItem(help_ptr, conf_help);
   2550 		safe_free(help_ptr);
   2551 	}
   2552 	for (os_ptr = iConf.allow_user_stats_ext; os_ptr; os_ptr = (OperStat *)next)
   2553 	{
   2554 		next = (ListStruct *)os_ptr->next;
   2555 		safe_free(os_ptr->flag);
   2556 		safe_free(os_ptr);
   2557 	}
   2558 	iConf.allow_user_stats_ext = NULL;
   2559 	for (spamex_ptr = iConf.spamexcept; spamex_ptr; spamex_ptr = (SpamExcept *)next)
   2560 	{
   2561 		next = (ListStruct *)spamex_ptr->next;
   2562 		safe_free(spamex_ptr);
   2563 	}
   2564 	iConf.spamexcept = NULL;
   2565 	for (of_ptr = conf_offchans; of_ptr; of_ptr = (ConfigItem_offchans *)next)
   2566 	{
   2567 		next = (ListStruct *)of_ptr->next;
   2568 		safe_free(of_ptr->topic);
   2569 		safe_free(of_ptr);
   2570 	}
   2571 	conf_offchans = NULL;
   2572 
   2573 	/* Free sni { } blocks */
   2574 	for (sni = conf_sni; sni; sni = (ConfigItem_sni *)next)
   2575 	{
   2576 	    next = (ListStruct *)sni->next;
   2577 	    SSL_CTX_free(sni->ssl_ctx);
   2578 	    free_tls_options(sni->tls_options);
   2579 	    safe_free(sni->name);
   2580 	    safe_free(sni);
   2581 	}
   2582 	conf_sni = NULL;
   2583 
   2584 	free_conf_channelmodes(&iConf.modes_on_join);
   2585 
   2586 	/*
   2587 	  reset conf_files -- should this be in its own function? no, because
   2588 	  it's only used here
   2589 	 */
   2590 	safe_free(conf_files->motd_file);
   2591 	safe_free(conf_files->smotd_file);
   2592 	safe_free(conf_files->opermotd_file);
   2593 	safe_free(conf_files->svsmotd_file);
   2594 	safe_free(conf_files->botmotd_file);
   2595 	safe_free(conf_files->rules_file);
   2596 	safe_free(conf_files->pid_file);
   2597 	safe_free(conf_files->tune_file);
   2598 	/*
   2599 	   Don't free conf_files->pid_file here; the old value is used to determine if
   2600 	   the pidfile location has changed and write_pidfile() needs to be called
   2601 	   again.
   2602 	*/
   2603 	safe_free(conf_files);
   2604 	conf_files = NULL;
   2605 }
   2606 
   2607 int	config_post_test()
   2608 {
   2609 #define Error(x) { config_error((x)); errors++; }
   2610 	int 	errors = 0;
   2611 	Hook *h;
   2612 
   2613 	if (!requiredstuff.conf_me)
   2614 		Error("me {} block is missing");
   2615 	if (!requiredstuff.conf_admin)
   2616 		Error("admin {} block is missing");
   2617 	if (!requiredstuff.conf_listen)
   2618 		Error("listen {} block is missing");
   2619 	if (!settings.has_kline_address)
   2620 		Error("set::kline-address is missing");
   2621 	if (!settings.has_default_server)
   2622 		Error("set::default-server is missing");
   2623 	if (!settings.has_network_name)
   2624 		Error("set::network-name is missing");
   2625 	if (!settings.has_help_channel)
   2626 		Error("set::help-channel is missing");
   2627 	if (nicklengths.min > nicklengths.max)
   2628 		Error("set::nick-length is smaller than set::min-nick-length");
   2629 
   2630 	for (h = Hooks[HOOKTYPE_CONFIGPOSTTEST]; h; h = h->next)
   2631 	{
   2632 		int value, errs = 0;
   2633 		if (h->owner && !(h->owner->flags & MODFLAG_TESTING) &&
   2634 		                !(h->owner->options & MOD_OPT_PERM))
   2635 			continue;
   2636 		value = (*(h->func.intfunc))(&errs);
   2637 		if (value == -1)
   2638 		{
   2639 			errors += errs;
   2640 			break;
   2641 		}
   2642 		if (value == -2)
   2643 			errors += errs;
   2644 	}
   2645 	return errors;
   2646 }
   2647 
   2648 /** Make the "read" config the "live" config */
   2649 void config_switchover(void)
   2650 {
   2651 	free_iConf(&iConf);
   2652 	memcpy(&iConf, &tempiConf, sizeof(iConf));
   2653 	memset(&tempiConf, 0, sizeof(tempiConf));
   2654 	log_blocks_switchover();
   2655 }
   2656 
   2657 /** Priority of config blocks during CONFIG_TEST stage */
   2658 static const char *config_test_priority_blocks[] =
   2659 {
   2660 	"me",
   2661 	"secret",
   2662 	"log", /* "log" needs to be before "set" in CONFIG_TEST */
   2663 	"set",
   2664 	"class",
   2665 };
   2666 
   2667 /** Priority of config blocks during CONFIG_RUN stage */
   2668 static const char *config_run_priority_blocks[] =
   2669 {
   2670 	"me",
   2671 	"secret",
   2672 	"set",
   2673 	"log", /* "log" needs to be after "set" in CONFIG_RUN */
   2674 	"class",
   2675 };
   2676 
   2677 int config_test_blocks()
   2678 {
   2679 	ConfigEntry 	*ce;
   2680 	ConfigFile	*cfptr;
   2681 	ConfigCommand	*cc;
   2682 	int		errors = 0;
   2683 	int i;
   2684 	Hook *h;
   2685 
   2686 	invalid_snomasks_encountered = 0;
   2687 
   2688 	/* Stage 1: first the priority blocks, in the order as specified
   2689 	 *          in config_test_priority_blocks[]
   2690 	 */
   2691 	for (i=0; i < ARRAY_SIZEOF(config_test_priority_blocks); i++)
   2692 	{
   2693 		const char *config_block = config_test_priority_blocks[i];
   2694 		cc = config_binary_search(config_block);
   2695 		if (!cc)
   2696 			abort(); /* internal fuckup */
   2697 		for (cfptr = conf; cfptr; cfptr = cfptr->next)
   2698 		{
   2699 			if (config_verbose > 1)
   2700 				config_status("Running %s", cfptr->filename);
   2701 			for (ce = cfptr->items; ce; ce = ce->next)
   2702 			{
   2703 				if (!strcmp(ce->name, config_block))
   2704 				{
   2705 					int n = cc->testfunc(cfptr, ce);
   2706 					errors += n;
   2707 					if (!strcmp(config_block, "secret") && (n == 0))
   2708 					{
   2709 						/* Yeah special case: secret { } blocks we run
   2710 						 * immediately here.
   2711 						 */
   2712 						_conf_secret(cfptr, ce);
   2713 					}
   2714 				}
   2715 			}
   2716 		}
   2717 	}
   2718 
   2719 	/* Stage 2: now all the other config blocks */
   2720 	for (cfptr = conf; cfptr; cfptr = cfptr->next)
   2721 	{
   2722 		if (config_verbose > 1)
   2723 			config_status("Running %s", cfptr->filename);
   2724 		for (ce = cfptr->items; ce; ce = ce->next)
   2725 		{
   2726 			char skip = 0;
   2727 			for (i=0; i < ARRAY_SIZEOF(config_test_priority_blocks); i++)
   2728 			{
   2729 				if (!strcmp(ce->name, config_test_priority_blocks[i]))
   2730 				{
   2731 					skip = 1;
   2732 					break;
   2733 				}
   2734 			}
   2735 			if (skip)
   2736 				continue;
   2737 
   2738 			if ((cc = config_binary_search(ce->name))) {
   2739 				if (cc->testfunc)
   2740 					errors += (cc->testfunc(cfptr, ce));
   2741 			}
   2742 			else
   2743 			{
   2744 				int used = 0;
   2745 				for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next)
   2746 				{
   2747 					int value, errs = 0;
   2748 					if (h->owner && !(h->owner->flags & MODFLAG_TESTING)
   2749 					    && !(h->owner->options & MOD_OPT_PERM))
   2750 
   2751 
   2752 						continue;
   2753 					value = (*(h->func.intfunc))(cfptr,ce,CONFIG_MAIN,&errs);
   2754 					if (value == 2)
   2755 						used = 1;
   2756 					if (value == 1)
   2757 					{
   2758 						used = 1;
   2759 						break;
   2760 					}
   2761 					if (value == -1)
   2762 					{
   2763 						used = 1;
   2764 						errors += errs;
   2765 						break;
   2766 					}
   2767 					if (value == -2)
   2768 					{
   2769 						used = 1;
   2770 						errors += errs;
   2771 					}
   2772 
   2773 				}
   2774 				if (!used)
   2775 				{
   2776 					config_error("%s:%i: unknown directive %s",
   2777 						ce->file->filename, ce->line_number,
   2778 						ce->name);
   2779 					errors++;
   2780 					if (strchr(ce->name, ':'))
   2781 					{
   2782 						config_error("You cannot use :: in a directive, you have to write them out. "
   2783 						             "For example 'set::auto-join #something' needs to be written as: "
   2784 						             "set { auto-join \"#something\"; }");
   2785 						config_error("See also https://www.unrealircd.org/docs/Set_block#Syntax_used_in_this_documentation");
   2786 					}
   2787 				}
   2788 			}
   2789 		}
   2790 	}
   2791 
   2792 	errors += config_post_test();
   2793 
   2794 	if (errors > 0)
   2795 	{
   2796 		config_error("%i errors encountered", errors);
   2797 	}
   2798 
   2799 	if (invalid_snomasks_encountered)
   2800 	{
   2801 		config_error("It seems your set::snomask-on-oper and/or oper::snomask needs to be updated. Are you perhaps upgrading from an older version to UnrealIRCd 6?");
   2802 		config_error("See https://www.unrealircd.org/docs/Upgrading_from_5.x#Update_your_snomasks");
   2803 	}
   2804 
   2805 	return (errors > 0 ? -1 : 1);
   2806 }
   2807 
   2808 int config_run_blocks(void)
   2809 {
   2810 	ConfigEntry 	*ce;
   2811 	ConfigFile	*cfptr;
   2812 	ConfigCommand	*cc;
   2813 	int		errors = 0;
   2814 	int i;
   2815 	Hook *h;
   2816 	ConfigItem_allow *allow;
   2817 
   2818 	/* Stage 1: first the priority blocks, in the order as specified
   2819 	 *          in config_run_priority_blocks[]
   2820 	 */
   2821 	for (i=0; i < ARRAY_SIZEOF(config_run_priority_blocks); i++)
   2822 	{
   2823 		const char *config_block = config_run_priority_blocks[i];
   2824 		cc = config_binary_search(config_block);
   2825 		if (!cc)
   2826 			abort(); /* internal fuckup */
   2827 		if (!strcmp(config_block, "secret"))
   2828 			continue; /* yeah special case, we already processed the run part in test for these */
   2829 		for (cfptr = conf; cfptr; cfptr = cfptr->next)
   2830 		{
   2831 			if (config_verbose > 1)
   2832 				config_status("Running %s", cfptr->filename);
   2833 			for (ce = cfptr->items; ce; ce = ce->next)
   2834 			{
   2835 				if (!strcmp(ce->name, config_block))
   2836 				{
   2837 					if (cc->conffunc(cfptr, ce) < 0)
   2838 						errors++;
   2839 				}
   2840 			}
   2841 		}
   2842 	}
   2843 
   2844 	/* Stage 2: now all the other config blocks */
   2845 	for (cfptr = conf; cfptr; cfptr = cfptr->next)
   2846 	{
   2847 		if (config_verbose > 1)
   2848 			config_status("Running %s", cfptr->filename);
   2849 		for (ce = cfptr->items; ce; ce = ce->next)
   2850 		{
   2851 			char skip = 0;
   2852 			for (i=0; i < ARRAY_SIZEOF(config_run_priority_blocks); i++)
   2853 			{
   2854 				if (!strcmp(ce->name, config_run_priority_blocks[i]))
   2855 				{
   2856 					skip = 1;
   2857 					break;
   2858 				}
   2859 			}
   2860 			if (skip)
   2861 				continue;
   2862 
   2863 			if ((cc = config_binary_search(ce->name))) {
   2864 				if ((cc->conffunc) && (cc->conffunc(cfptr, ce) < 0))
   2865 					errors++;
   2866 			}
   2867 			else
   2868 			{
   2869 				int value;
   2870 				for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next)
   2871 				{
   2872 					value = (*(h->func.intfunc))(cfptr,ce,CONFIG_MAIN);
   2873 					if (value == 1)
   2874 						break;
   2875 				}
   2876 			}
   2877 		}
   2878 	}
   2879 
   2880 	close_unbound_listeners();
   2881 	listen_cleanup();
   2882 	close_unbound_listeners();
   2883 	loop.do_bancheck = 1;
   2884 	config_switchover();
   2885 	update_throttling_timer_settings();
   2886 
   2887 	/* initialize conf_files with defaults if the block isn't set: */
   2888 	if (!conf_files)
   2889 	  _conf_files(NULL, NULL);
   2890 
   2891 	if (errors > 0)
   2892 	{
   2893 		config_error("%i fatal errors encountered", errors);
   2894 	}
   2895 	return (errors > 0 ? -1 : 1);
   2896 }
   2897 
   2898 /*
   2899  * Service functions
   2900 */
   2901 
   2902 ConfigItem_alias *find_alias(const char *name)
   2903 {
   2904 	ConfigItem_alias *e;
   2905 
   2906 	if (!name)
   2907 		return NULL;
   2908 
   2909 	for (e = conf_alias; e; e = e->next)
   2910 	{
   2911 		if (!strcasecmp(e->alias, name))
   2912 			return e;
   2913 	}
   2914 	return NULL;
   2915 }
   2916 
   2917 ConfigItem_class *find_class(const char *name)
   2918 {
   2919 	ConfigItem_class *e;
   2920 
   2921 	if (!name)
   2922 		return NULL;
   2923 
   2924 	for (e = conf_class; e; e = e->next)
   2925 	{
   2926 		if (!strcmp(name, e->name))
   2927 			return e;
   2928 	}
   2929 	return NULL;
   2930 }
   2931 
   2932 
   2933 ConfigItem_oper	*find_oper(const char *name)
   2934 {
   2935 	ConfigItem_oper	*e;
   2936 
   2937 	if (!name)
   2938 		return NULL;
   2939 
   2940 	for (e = conf_oper; e; e = e->next)
   2941 	{
   2942 		if (!strcmp(name, e->name))
   2943 			return e;
   2944 	}
   2945 	return NULL;
   2946 }
   2947 
   2948 ConfigItem_operclass *find_operclass(const char *name)
   2949 {
   2950 	ConfigItem_operclass *e;
   2951 
   2952 	if (!name)
   2953 		return NULL;
   2954 
   2955 	for (e = conf_operclass; e; e = e->next)
   2956 	{
   2957 		if (!strcmp(name,e->classStruct->name))
   2958 			return e;
   2959 	}
   2960 	return NULL;
   2961 }
   2962 
   2963 int count_oper_sessions(const char *name)
   2964 {
   2965 	int count = 0;
   2966 	Client *client;
   2967 
   2968 	list_for_each_entry(client, &oper_list, special_node)
   2969 	{
   2970 		if (client->user->operlogin != NULL && !strcmp(client->user->operlogin, name))
   2971 			count++;
   2972 	}
   2973 
   2974 	return count;
   2975 }
   2976 
   2977 ConfigItem_listen *find_listen(const char *ipmask, int port, SocketType socket_type)
   2978 {
   2979 	ConfigItem_listen *e;
   2980 
   2981 	if (!ipmask)
   2982 		return NULL;
   2983 
   2984 	for (e = conf_listen; e; e = e->next)
   2985 	{
   2986 		if (e->socket_type != socket_type)
   2987 			continue;
   2988 		if (e->socket_type == SOCKET_TYPE_UNIX)
   2989 		{
   2990 			if (!strcmp(e->file, ipmask))
   2991 				return e;
   2992 		} else
   2993 		{
   2994 			if ((e->socket_type == socket_type) && (e->port == port) && !strcmp(e->ip, ipmask))
   2995 				return e;
   2996 		}
   2997 	}
   2998 
   2999 	return NULL;
   3000 }
   3001 
   3002 /** Find an SNI match.
   3003  * @param name The hostname to look for (eg: irc.xyz.com).
   3004  */
   3005 ConfigItem_sni *find_sni(const char *name)
   3006 {
   3007 	ConfigItem_sni *e;
   3008 
   3009 	if (!name)
   3010 		return NULL;
   3011 
   3012 	for (e = conf_sni; e; e = e->next)
   3013 	{
   3014         if (match_simple(e->name, name))
   3015             return e;
   3016 	}
   3017 	return NULL;
   3018 }
   3019 
   3020 ConfigItem_ulines *find_uline(const char *host)
   3021 {
   3022 	ConfigItem_ulines *ulines;
   3023 
   3024 	if (!host)
   3025 		return NULL;
   3026 
   3027 	for(ulines = conf_ulines; ulines; ulines = ulines->next)
   3028 	{
   3029 		if (!strcasecmp(host, ulines->servername))
   3030 			return ulines;
   3031 	}
   3032 	return NULL;
   3033 }
   3034 
   3035 
   3036 ConfigItem_tld *find_tld(Client *client)
   3037 {
   3038 	ConfigItem_tld *tld;
   3039 
   3040 	for (tld = conf_tld; tld; tld = tld->next)
   3041 	{
   3042 		if (user_allowed_by_security_group(client, tld->match))
   3043 		{
   3044 			if ((tld->options & TLD_TLS) && !IsSecureConnect(client))
   3045 				continue;
   3046 			if ((tld->options & TLD_REMOTE) && MyUser(client))
   3047 				continue;
   3048 			return tld;
   3049 		}
   3050 	}
   3051 
   3052 	return NULL;
   3053 }
   3054 
   3055 /** Find a link block by server name (but don't check any restrictions like IP or auth) */
   3056 ConfigItem_link *find_link(const char *servername)
   3057 {
   3058 	ConfigItem_link	*link;
   3059 
   3060 	for (link = conf_link; link; link = link->next)
   3061 	{
   3062 		if (!link->flag.temporary && match_simple(link->servername, servername))
   3063 		{
   3064 		    return link;
   3065 		}
   3066 	}
   3067 	return NULL;
   3068 }
   3069 
   3070 /** Find a ban of type CONF_BAN_*, which is currently only
   3071  * CONF_BAN_SERVER, CONF_BAN_VERSION and CONF_BAN_REALNAME
   3072  */
   3073 ConfigItem_ban *find_ban(Client *client, const char *host, short type)
   3074 {
   3075 	ConfigItem_ban *ban;
   3076 
   3077 	for (ban = conf_ban; ban; ban = ban->next)
   3078 	{
   3079 		if (ban->flag.type == type)
   3080 		{
   3081 			if (client)
   3082 			{
   3083 				if (match_user(ban->mask, client, MATCH_CHECK_REAL))
   3084 					return ban;
   3085 			}
   3086 			else if (match_simple(ban->mask, host))
   3087 				return ban;
   3088 		}
   3089 	}
   3090 	return NULL;
   3091 }
   3092 
   3093 /** Find a ban of type CONF_BAN_*, which is currently only
   3094  * CONF_BAN_SERVER, CONF_BAN_VERSION and CONF_BAN_REALNAME
   3095  * This is the extended version, only used by cmd_svsnline.
   3096  */
   3097 ConfigItem_ban 	*find_banEx(Client *client, const char *host, short type, short type2)
   3098 {
   3099 	ConfigItem_ban *ban;
   3100 
   3101 	for (ban = conf_ban; ban; ban = ban->next)
   3102 	{
   3103 		if ((ban->flag.type == type) && (ban->flag.type2 == type2))
   3104 		{
   3105 			if (client)
   3106 			{
   3107 				if (match_user(ban->mask, client, MATCH_CHECK_REAL))
   3108 					return ban;
   3109 			}
   3110 			else if (match_simple(ban->mask, host))
   3111 				return ban;
   3112 		}
   3113 	}
   3114 	return NULL;
   3115 }
   3116 
   3117 ConfigItem_vhost *find_vhost(const char *name)
   3118 {
   3119 	ConfigItem_vhost *vhost;
   3120 
   3121 	for (vhost = conf_vhost; vhost; vhost = vhost->next)
   3122 	{
   3123 		if (!strcmp(name, vhost->login))
   3124 			return vhost;
   3125 	}
   3126 
   3127 	return NULL;
   3128 }
   3129 
   3130 
   3131 /** returns NULL if allowed and struct if denied */
   3132 ConfigItem_deny_channel *find_channel_allowed(Client *client, const char *name)
   3133 {
   3134 	ConfigItem_deny_channel *dchannel;
   3135 	ConfigItem_allow_channel *achannel;
   3136 
   3137 	for (dchannel = conf_deny_channel; dchannel; dchannel = dchannel->next)
   3138 	{
   3139 		if (match_simple(dchannel->channel, name))
   3140 		{
   3141 			if (dchannel->class && strcmp(client->local->class->name, dchannel->class))
   3142 				continue;
   3143 			if (dchannel->match && !user_allowed_by_security_group(client, dchannel->match))
   3144 				continue;
   3145 			break; /* MATCH deny channel { } */
   3146 		}
   3147 	}
   3148 
   3149 	if (dchannel)
   3150 	{
   3151 		/* Check exceptions... ('allow channel') */
   3152 		for (achannel = conf_allow_channel; achannel; achannel = achannel->next)
   3153 		{
   3154 			if (match_simple(achannel->channel, name))
   3155 			{
   3156 				if (achannel->class && strcmp(client->local->class->name, achannel->class))
   3157 					continue;
   3158 				if (achannel->match && !user_allowed_by_security_group(client, achannel->match))
   3159 					continue;
   3160 				break; /* MATCH allow channel { } */
   3161 			}
   3162 		}
   3163 		if (achannel)
   3164 			return NULL; /* Matches an 'allow channel' - so not forbidden */
   3165 		else
   3166 			return dchannel;
   3167 	}
   3168 	return NULL;
   3169 }
   3170 
   3171 void init_dynconf(void)
   3172 {
   3173 	memset(&iConf, 0, sizeof(iConf));
   3174 	memset(&tempiConf, 0, sizeof(iConf));
   3175 }
   3176 
   3177 const char *pretty_time_val_r(char *buf, size_t buflen, long timeval)
   3178 {
   3179 	if (timeval == 0)
   3180 		return "0";
   3181 
   3182 	buf[0] = 0;
   3183 
   3184 	if (timeval/86400)
   3185 		snprintf(buf, buflen, "%ldd", timeval/86400);
   3186 	if ((timeval/3600) % 24)
   3187 		snprintf(buf+strlen(buf), buflen-strlen(buf), "%ldh", (timeval/3600)%24);
   3188 	if ((timeval/60)%60)
   3189 		snprintf(buf+strlen(buf), buflen-strlen(buf), "%ldm", (timeval/60)%60);
   3190 	if ((timeval%60))
   3191 		snprintf(buf+strlen(buf), buflen-strlen(buf), "%lds", timeval%60);
   3192 
   3193 	return buf;
   3194 }
   3195 
   3196 const char *pretty_time_val(long timeval)
   3197 {
   3198 	static char buf[512];
   3199 	return pretty_time_val_r(buf, sizeof(buf), timeval);
   3200 }
   3201 
   3202 /* This converts a relative path to an absolute path, but only if necessary. */
   3203 void convert_to_absolute_path(char **path, const char *reldir)
   3204 {
   3205 	char *s;
   3206 
   3207 	if (!*path || !**path)
   3208 		return; /* NULL or empty */
   3209 
   3210 	if (strstr(*path, "://"))
   3211 		return; /* URL: don't touch */
   3212 
   3213 #ifdef _WIN32
   3214 	if (!strncmp(*path, "cache/", 6))
   3215 		return; /* downloaded from URL: don't touch (is only relative path on Windows) */
   3216 #endif
   3217 
   3218 	if ((**path == '/') || (**path == '\\'))
   3219 		return; /* already absolute path */
   3220 
   3221 	if (!strncmp(*path, reldir, strlen(reldir)))
   3222 		return; /* already contains reldir */
   3223 
   3224 	s = safe_alloc(strlen(reldir) + strlen(*path) + 2);
   3225 	sprintf(s, "%s/%s", reldir, *path); /* safe, see line above */
   3226 	safe_free(*path);
   3227 	*path = s;
   3228 }
   3229 
   3230 /* Similar to convert_to_absolute_path() but returns a duplicated string.
   3231  * Don't forget to free!
   3232  */
   3233 char *convert_to_absolute_path_duplicate(char *path, char *reldir)
   3234 {
   3235 	char *xpath = strdup(path);
   3236 	convert_to_absolute_path(&xpath, reldir);
   3237 	return xpath;
   3238 }
   3239 
   3240 /*
   3241  * Actual config parser funcs
   3242 */
   3243 
   3244 int _conf_include(ConfigFile *conf, ConfigEntry *ce)
   3245 {
   3246 	int	ret = 0;
   3247 #ifdef GLOBH
   3248 	glob_t files;
   3249 	int i;
   3250 #elif defined(_WIN32)
   3251 	HANDLE hFind;
   3252 	WIN32_FIND_DATA FindData;
   3253 	char cPath[MAX_PATH], *cSlash = NULL, *path;
   3254 #endif
   3255 	if (!ce->value)
   3256 	{
   3257 		config_status("%s:%i: include: no filename given",
   3258 			ce->file->filename,
   3259 			ce->line_number);
   3260 		return -1;
   3261 	}
   3262 
   3263 	convert_to_absolute_path(&ce->value, CONFDIR);
   3264 
   3265 	if (url_is_valid(ce->value))
   3266 	{
   3267 		add_config_resource(ce->value, RESOURCE_INCLUDE, ce);
   3268 		return 0;
   3269 	}
   3270 #if !defined(_WIN32) && !defined(_AMIGA) && !defined(OSXTIGER) && DEFAULT_PERMISSIONS != 0
   3271 	(void)chmod(ce->value, DEFAULT_PERMISSIONS);
   3272 #endif
   3273 #ifdef GLOBH
   3274 #if defined(__OpenBSD__) && defined(GLOB_LIMIT)
   3275 	glob(ce->value, GLOB_NOSORT|GLOB_NOCHECK|GLOB_LIMIT, NULL, &files);
   3276 #else
   3277 	glob(ce->value, GLOB_NOSORT|GLOB_NOCHECK, NULL, &files);
   3278 #endif
   3279 	if (!files.gl_pathc) {
   3280 		globfree(&files);
   3281 		config_status("%s:%i: include %s: invalid file given",
   3282 			ce->file->filename, ce->line_number,
   3283 			ce->value);
   3284 		return -1;
   3285 	}
   3286 	for (i = 0; i < files.gl_pathc; i++)
   3287 	{
   3288 		if (add_config_resource(files.gl_pathv[i], RESOURCE_INCLUDE, ce))
   3289 		{
   3290 			ret = config_read_file(files.gl_pathv[i], files.gl_pathv[i]);
   3291 			if (ret < 0)
   3292 			{
   3293 				globfree(&files);
   3294 				return ret;
   3295 			}
   3296 		}
   3297 	}
   3298 	globfree(&files);
   3299 #elif defined(_WIN32)
   3300 	memset(cPath, 0, MAX_PATH);
   3301 	if (strchr(ce->value, '/') || strchr(ce->value, '\\')) {
   3302 		strlcpy(cPath,ce->value,MAX_PATH);
   3303 		cSlash=cPath+strlen(cPath);
   3304 		while(*cSlash != '\\' && *cSlash != '/' && cSlash > cPath)
   3305 			cSlash--;
   3306 		*(cSlash+1)=0;
   3307 	}
   3308 	if ( (hFind = FindFirstFile(ce->value, &FindData)) == INVALID_HANDLE_VALUE )
   3309 	{
   3310 		config_status("%s:%i: include %s: invalid file given",
   3311 			ce->file->filename, ce->line_number,
   3312 			ce->value);
   3313 		return -1;
   3314 	}
   3315 	if (cPath) {
   3316 		path = safe_alloc(strlen(cPath) + strlen(FindData.cFileName)+1);
   3317 		strcpy(path, cPath);
   3318 		strcat(path, FindData.cFileName);
   3319 
   3320 		if (add_config_resource(path, RESOURCE_INCLUDE, ce))
   3321 		{
   3322 			ret = config_read_file(path, path);
   3323 			safe_free(path);
   3324 		}
   3325 	}
   3326 	else
   3327 	{
   3328 		if (add_config_resource(FindData.cFileName, RESOURCE_INCLUDE, ce))
   3329 			ret = config_read_file(FindData.cFileName, FindData.cFileName);
   3330 	}
   3331 	if (ret < 0)
   3332 	{
   3333 		FindClose(hFind);
   3334 		return ret;
   3335 	}
   3336 
   3337 	ret = 0;
   3338 	while (FindNextFile(hFind, &FindData) != 0) {
   3339 		if (cPath) {
   3340 			path = safe_alloc(strlen(cPath) + strlen(FindData.cFileName)+1);
   3341 			strcpy(path,cPath);
   3342 			strcat(path,FindData.cFileName);
   3343 
   3344 			if (add_config_resource(path, RESOURCE_INCLUDE, ce))
   3345 			{
   3346 				ret = config_read_file(path, path);
   3347 				safe_free(path);
   3348 				if (ret < 0)
   3349 					break;
   3350 			}
   3351 		}
   3352 		else
   3353 		{
   3354 			if (add_config_resource(FindData.cFileName, RESOURCE_INCLUDE, ce))
   3355 				ret = config_read_file(FindData.cFileName, FindData.cFileName);
   3356 		}
   3357 	}
   3358 	FindClose(hFind);
   3359 	if (ret < 0)
   3360 		return ret;
   3361 #else
   3362 	if (add_config_resource(ce->value, RESOURCE_INCLUDE, ce))
   3363 		ret = config_read_file(ce->value, ce->value);
   3364 	return ret;
   3365 #endif
   3366 	return 1;
   3367 }
   3368 
   3369 int	_test_include(ConfigFile *conf, ConfigEntry *ce)
   3370 {
   3371 	return 0;
   3372 }
   3373 
   3374 int	_conf_admin(ConfigFile *conf, ConfigEntry *ce)
   3375 {
   3376 	ConfigEntry *cep;
   3377 	ConfigItem_admin *ca;
   3378 
   3379 	for (cep = ce->items; cep; cep = cep->next)
   3380 	{
   3381 		ca = safe_alloc(sizeof(ConfigItem_admin));
   3382 		if (!conf_admin)
   3383 			conf_admin_tail = ca;
   3384 		safe_strdup(ca->line, cep->name);
   3385 		AddListItem(ca, conf_admin);
   3386 	}
   3387 	return 1;
   3388 }
   3389 
   3390 int	_test_admin(ConfigFile *conf, ConfigEntry *ce)
   3391 {
   3392 	ConfigEntry *cep;
   3393 	int 	    errors = 0;
   3394 
   3395 	if (requiredstuff.conf_admin)
   3396 	{
   3397 		config_warn_duplicate(ce->file->filename, ce->line_number, "admin");
   3398 		return 0;
   3399 	}
   3400 
   3401 	for (cep = ce->items; cep; cep = cep->next)
   3402 	{
   3403 		if (strlen(cep->name) > 500)
   3404 		{
   3405 			config_error("%s:%i: oversized data in admin block",
   3406 				cep->file->filename,
   3407 				cep->line_number);
   3408 			errors++;
   3409 			continue;
   3410 		}
   3411 	}
   3412 	requiredstuff.conf_admin = 1;
   3413 	return errors;
   3414 }
   3415 
   3416 int	_conf_me(ConfigFile *conf, ConfigEntry *ce)
   3417 {
   3418 	ConfigEntry *cep;
   3419 
   3420 	if (!conf_me)
   3421 		conf_me = safe_alloc(sizeof(ConfigItem_me));
   3422 
   3423 	for (cep = ce->items; cep; cep = cep->next)
   3424 	{
   3425 		if (!strcmp(cep->name, "name"))
   3426 		{
   3427 			safe_strdup(conf_me->name, cep->value);
   3428 		}
   3429 		else if (!strcmp(cep->name, "info"))
   3430 		{
   3431 			safe_strdup(conf_me->info, cep->value);
   3432 		}
   3433 		else if (!strcmp(cep->name, "sid"))
   3434 		{
   3435 			safe_strdup(conf_me->sid, cep->value);
   3436 		}
   3437 	}
   3438 	return 1;
   3439 }
   3440 
   3441 int	_test_me(ConfigFile *conf, ConfigEntry *ce)
   3442 {
   3443 	char has_name = 0, has_info = 0, has_sid = 0;
   3444 	ConfigEntry *cep;
   3445 	int	    errors = 0;
   3446 
   3447 	if (requiredstuff.conf_me)
   3448 	{
   3449 		config_warn_duplicate(ce->file->filename, ce->line_number, "me");
   3450 		return 0;
   3451 	}
   3452 
   3453 	for (cep = ce->items; cep; cep = cep->next)
   3454 	{
   3455 		if (config_is_blankorempty(cep, "me"))
   3456 			continue;
   3457 
   3458 		/* me::name */
   3459 		if (!strcmp(cep->name, "name"))
   3460 		{
   3461 			if (has_name)
   3462 			{
   3463 				config_warn_duplicate(cep->file->filename,
   3464 					cep->line_number, "me::name");
   3465 				continue;
   3466 			}
   3467 			has_name = 1;
   3468 			if (!strchr(cep->value, '.'))
   3469 			{
   3470 				config_error("%s:%i: illegal me::name, must be fully qualified hostname",
   3471 					cep->file->filename,
   3472 					cep->line_number);
   3473 				errors++;
   3474 			}
   3475 			if (strlen(cep->value) > HOSTLEN)
   3476 			{
   3477 				config_error("%s:%i: illegal me::name, must be less or equal to %i characters",
   3478 					cep->file->filename,
   3479 					cep->line_number, HOSTLEN);
   3480 				errors++;
   3481 			}
   3482 			if (!valid_server_name(cep->value))
   3483 			{
   3484 				config_error("%s:%i: illegal me::name contains invalid character(s) [only a-z, 0-9, _, -, . are allowed]",
   3485 					cep->file->filename,
   3486 					cep->line_number);
   3487 				errors++;
   3488 			}
   3489 		}
   3490 		/* me::info */
   3491 		else if (!strcmp(cep->name, "info"))
   3492 		{
   3493 			char *p;
   3494 			char valid = 0;
   3495 			if (has_info)
   3496 			{
   3497 				config_warn_duplicate(cep->file->filename,
   3498 					cep->line_number, "me::info");
   3499 				continue;
   3500 			}
   3501 			has_info = 1;
   3502 			if (strlen(cep->value) > (REALLEN-1))
   3503 			{
   3504 				config_error("%s:%i: too long me::info, must be max. %i characters",
   3505 					cep->file->filename, cep->line_number,
   3506 					REALLEN-1);
   3507 				errors++;
   3508 			}
   3509 
   3510 			/* Valid me::info? Any data except spaces is ok */
   3511 			for (p=cep->value; *p; p++)
   3512 			{
   3513 				if (*p != ' ')
   3514 				{
   3515 					valid = 1;
   3516 					break;
   3517 				}
   3518 			}
   3519 			if (!valid)
   3520 			{
   3521 				config_error("%s:%i: empty me::info, should be a server description.",
   3522 					cep->file->filename, cep->line_number);
   3523 				errors++;
   3524 			}
   3525 		}
   3526 		else if (!strcmp(cep->name, "numeric"))
   3527 		{
   3528 			config_error("%s:%i: me::numeric has been removed, you must now specify a Server ID (SID) instead. "
   3529 			             "Edit your configuration file and change 'numeric' to 'sid' and make up "
   3530 			             "a server id of exactly 3 characters, starting with a digit, eg: \"001\" or \"0AB\".",
   3531 			             cep->file->filename, cep->line_number);
   3532 			errors++;
   3533 		}
   3534 		else if (!strcmp(cep->name, "sid"))
   3535 		{
   3536 			if (has_sid)
   3537 			{
   3538 				config_warn_duplicate(cep->file->filename,
   3539 					cep->line_number, "me::sid");
   3540 				continue;
   3541 			}
   3542 			has_sid = 1;
   3543 
   3544 			if (!valid_sid(cep->value))
   3545 			{
   3546 				config_error("%s:%i: me::sid must be 3 characters long, begin with a number, "
   3547 				             "and the 2nd and 3rd character must be a number or uppercase letter. "
   3548 				             "Example: \"001\" and \"0AB\" is good. \"AAA\" and \"0ab\" are bad. ",
   3549 				             cep->file->filename, cep->line_number);
   3550 				errors++;
   3551 			}
   3552 
   3553 			if (!isdigit(*cep->value))
   3554 			{
   3555 				config_error("%s:%i: me::sid must be 3 characters long and begin with a number",
   3556 					cep->file->filename, cep->line_number);
   3557 				errors++;
   3558 			}
   3559 		}
   3560 		/* Unknown entry */
   3561 		else
   3562 		{
   3563 			config_error_unknown(ce->file->filename, ce->line_number,
   3564 				"me", cep->name);
   3565 			errors++;
   3566 		}
   3567 	}
   3568 	if (!has_name)
   3569 	{
   3570 		config_error_missing(ce->file->filename, ce->line_number, "me::name");
   3571 		errors++;
   3572 	}
   3573 	if (!has_info)
   3574 	{
   3575 		config_error_missing(ce->file->filename, ce->line_number, "me::info");
   3576 		errors++;
   3577 	}
   3578 	if (!has_sid)
   3579 	{
   3580 		config_error_missing(ce->file->filename, ce->line_number, "me::sid");
   3581 		errors++;
   3582 	}
   3583 	requiredstuff.conf_me = 1;
   3584 	return errors;
   3585 }
   3586 
   3587 /*
   3588  * The files {} block
   3589  */
   3590 int	_conf_files(ConfigFile *conf, ConfigEntry *ce)
   3591 {
   3592 	ConfigEntry *cep;
   3593 
   3594 	if (!conf_files)
   3595 	{
   3596 		conf_files = safe_alloc(sizeof(ConfigItem_files));
   3597 
   3598 		/* set defaults */
   3599 		safe_strdup(conf_files->motd_file, MPATH);
   3600 		safe_strdup(conf_files->rules_file, RPATH);
   3601 		safe_strdup(conf_files->smotd_file, SMPATH);
   3602 		safe_strdup(conf_files->botmotd_file, BPATH);
   3603 		safe_strdup(conf_files->opermotd_file, OPATH);
   3604 		safe_strdup(conf_files->svsmotd_file, VPATH);
   3605 
   3606 		safe_strdup(conf_files->pid_file, IRCD_PIDFILE);
   3607 		safe_strdup(conf_files->tune_file, IRCDTUNE);
   3608 
   3609 		/* we let actual files get read in later by the motd caching mechanism */
   3610 	}
   3611 	/*
   3612 	 * hack to allow initialization of conf_files (above) when there is no files block in
   3613 	 * CPATH. The caller calls _conf_files(NULL, NULL); to do this. We return here because
   3614 	 * the for loop's initialization of cep would segfault otherwise. We return 1 because
   3615 	 * if config_run_blocks() calls us with a NULL ce, it's got a bug...but we can't detect that.
   3616 	 */
   3617 	if (!ce)
   3618 	  return 1;
   3619 
   3620 	for (cep = ce->items; cep; cep = cep->next)
   3621 	{
   3622 		if (!strcmp(cep->name, "motd"))
   3623 			safe_strdup(conf_files->motd_file, cep->value);
   3624 		else if (!strcmp(cep->name, "shortmotd"))
   3625 			safe_strdup(conf_files->smotd_file, cep->value);
   3626 		else if (!strcmp(cep->name, "opermotd"))
   3627 			safe_strdup(conf_files->opermotd_file, cep->value);
   3628 		else if (!strcmp(cep->name, "svsmotd"))
   3629 			safe_strdup(conf_files->svsmotd_file, cep->value);
   3630 		else if (!strcmp(cep->name, "botmotd"))
   3631 			safe_strdup(conf_files->botmotd_file, cep->value);
   3632 		else if (!strcmp(cep->name, "rules"))
   3633 			safe_strdup(conf_files->rules_file, cep->value);
   3634 		else if (!strcmp(cep->name, "tunefile"))
   3635 			safe_strdup(conf_files->tune_file, cep->value);
   3636 		else if (!strcmp(cep->name, "pidfile"))
   3637 			safe_strdup(conf_files->pid_file, cep->value);
   3638 	}
   3639 	return 1;
   3640 }
   3641 
   3642 int	_test_files(ConfigFile *conf, ConfigEntry *ce)
   3643 {
   3644 	ConfigEntry *cep;
   3645 	int	    errors = 0;
   3646 	char has_motd = 0, has_smotd = 0, has_rules = 0;
   3647 	char has_botmotd = 0, has_opermotd = 0, has_svsmotd = 0;
   3648 	char has_pidfile = 0, has_tunefile = 0;
   3649 
   3650 	for (cep = ce->items; cep; cep = cep->next)
   3651 	{
   3652 		/* files::motd */
   3653 		if (!strcmp(cep->name, "motd"))
   3654 		{
   3655 			if (has_motd)
   3656 			{
   3657 				config_warn_duplicate(cep->file->filename,
   3658 					cep->line_number, "files::motd");
   3659 				continue;
   3660 			}
   3661 			convert_to_absolute_path(&cep->value, CONFDIR);
   3662 			config_test_openfile(cep, O_RDONLY, 0, "files::motd", 0, 1);
   3663 			has_motd = 1;
   3664 		}
   3665 		/* files::smotd */
   3666 		else if (!strcmp(cep->name, "shortmotd"))
   3667 		{
   3668 			if (has_smotd)
   3669 			{
   3670 				config_warn_duplicate(cep->file->filename,
   3671 					cep->line_number, "files::shortmotd");
   3672 				continue;
   3673 			}
   3674 			convert_to_absolute_path(&cep->value, CONFDIR);
   3675 			config_test_openfile(cep, O_RDONLY, 0, "files::shortmotd", 0, 1);
   3676 			has_smotd = 1;
   3677 		}
   3678 		/* files::rules */
   3679 		else if (!strcmp(cep->name, "rules"))
   3680 		{
   3681 			if (has_rules)
   3682 			{
   3683 				config_warn_duplicate(cep->file->filename,
   3684 					cep->line_number, "files::rules");
   3685 				continue;
   3686 			}
   3687 			convert_to_absolute_path(&cep->value, CONFDIR);
   3688 			config_test_openfile(cep, O_RDONLY, 0, "files::rules", 0, 1);
   3689 			has_rules = 1;
   3690 		}
   3691 		/* files::botmotd */
   3692 		else if (!strcmp(cep->name, "botmotd"))
   3693 		{
   3694 			if (has_botmotd)
   3695 			{
   3696 				config_warn_duplicate(cep->file->filename,
   3697 					cep->line_number, "files::botmotd");
   3698 				continue;
   3699 			}
   3700 			convert_to_absolute_path(&cep->value, CONFDIR);
   3701 			config_test_openfile(cep, O_RDONLY, 0, "files::botmotd", 0, 1);
   3702 			has_botmotd = 1;
   3703 		}
   3704 		/* files::opermotd */
   3705 		else if (!strcmp(cep->name, "opermotd"))
   3706 		{
   3707 			if (has_opermotd)
   3708 			{
   3709 				config_warn_duplicate(cep->file->filename,
   3710 					cep->line_number, "files::opermotd");
   3711 				continue;
   3712 			}
   3713 			convert_to_absolute_path(&cep->value, CONFDIR);
   3714 			config_test_openfile(cep, O_RDONLY, 0, "files::opermotd", 0, 1);
   3715 			has_opermotd = 1;
   3716 		}
   3717 		/* files::svsmotd
   3718 		 * This config stuff should somehow be inside of modules/svsmotd.c!!!... right?
   3719 		 */
   3720 		else if (!strcmp(cep->name, "svsmotd"))
   3721 		{
   3722 			if (has_svsmotd)
   3723 			{
   3724 				config_warn_duplicate(cep->file->filename,
   3725 					cep->line_number, "files::svsmotd");
   3726 				continue;
   3727 			}
   3728 			convert_to_absolute_path(&cep->value, CONFDIR);
   3729 			/* svsmotd can't be a URL because we have to be able to write to it */
   3730 			config_test_openfile(cep, O_RDONLY, 0, "files::svsmotd", 0, 0);
   3731 			has_svsmotd = 1;
   3732 		}
   3733 		/* files::pidfile */
   3734 		else if (!strcmp(cep->name, "pidfile"))
   3735 		{
   3736 			if (has_pidfile)
   3737 			{
   3738 				config_warn_duplicate(cep->file->filename,
   3739 					cep->line_number, "files::pidfile");
   3740 				continue;
   3741 			}
   3742 			convert_to_absolute_path(&cep->value, PERMDATADIR);
   3743 			errors += config_test_openfile(cep, O_WRONLY | O_CREAT, 0600, "files::pidfile", 1, 0);
   3744 			has_pidfile = 1;
   3745 		}
   3746 		/* files::tunefile */
   3747 		else if (!strcmp(cep->name, "tunefile"))
   3748 		{
   3749 			if (has_tunefile)
   3750 			{
   3751 				config_warn_duplicate(cep->file->filename,
   3752 					cep->line_number, "files::tunefile");
   3753 				continue;
   3754 			}
   3755 			convert_to_absolute_path(&cep->value, PERMDATADIR);
   3756 			errors += config_test_openfile(cep, O_RDWR | O_CREAT, 0600, "files::tunefile", 1, 0);
   3757 			has_tunefile = 1;
   3758 		}
   3759 		/* <random directive here> */
   3760 		else
   3761 		{
   3762 			config_error("%s:%d: Unknown directive: \"%s\" in files {}", cep->file->filename,
   3763 				     cep->line_number, cep->name);
   3764 			errors ++;
   3765 		}
   3766 	}
   3767 	return errors;
   3768 }
   3769 
   3770 /*
   3771  * The operclass {} block parser
   3772  */
   3773 
   3774 OperClassACLEntry* _conf_parseACLEntry(ConfigEntry *ce)
   3775 {
   3776 	ConfigEntry *cep;
   3777 	OperClassACLEntry *entry = NULL;
   3778 	entry = safe_alloc(sizeof(OperClassACLEntry));
   3779 
   3780 	if (!strcmp(ce->name,"allow"))
   3781 		entry->type = OPERCLASSENTRY_ALLOW;
   3782 	else
   3783 		entry->type = OPERCLASSENTRY_DENY;
   3784 
   3785 	for (cep = ce->items; cep; cep = cep->next)
   3786 	{
   3787 		OperClassACLEntryVar *var = safe_alloc(sizeof(OperClassACLEntryVar));
   3788 		safe_strdup(var->name, cep->name);
   3789 		if (cep->value)
   3790 		{
   3791 			safe_strdup(var->value, cep->value);
   3792 		}
   3793 		AddListItem(var,entry->variables);
   3794 	}
   3795 
   3796 	return entry;
   3797 }
   3798 
   3799 OperClassACL* _conf_parseACL(const char *name, ConfigEntry *ce)
   3800 {
   3801 	ConfigEntry *cep;
   3802 	OperClassACL *acl = NULL;
   3803 
   3804 	acl = safe_alloc(sizeof(OperClassACL));
   3805 	safe_strdup(acl->name, name);
   3806 
   3807 	for (cep = ce->items; cep; cep = cep->next)
   3808 	{
   3809 		if (!strcmp(cep->name, "deny") || !strcmp(cep->name, "allow"))
   3810 		{
   3811 			OperClassACLEntry *entry = _conf_parseACLEntry(cep);
   3812 			AddListItem(entry,acl->entries);
   3813 		}
   3814 		else {
   3815 			OperClassACL *subAcl = _conf_parseACL(cep->name,cep);
   3816 			AddListItem(subAcl,acl->acls);
   3817 		}
   3818 	}
   3819 
   3820 	return acl;
   3821 }
   3822 
   3823 int	_conf_operclass(ConfigFile *conf, ConfigEntry *ce)
   3824 {
   3825 	ConfigEntry *cep;
   3826 	ConfigEntry *cepp;
   3827 	ConfigItem_operclass *operClass = NULL;
   3828 	operClass = safe_alloc(sizeof(ConfigItem_operclass));
   3829 	operClass->classStruct = safe_alloc(sizeof(OperClass));
   3830 	safe_strdup(operClass->classStruct->name, ce->value);
   3831 
   3832 	for (cep = ce->items; cep; cep = cep->next)
   3833 	{
   3834 		if (!strcmp(cep->name, "parent"))
   3835 		{
   3836 			safe_strdup(operClass->classStruct->ISA, cep->value);
   3837 		}
   3838 		else if (!strcmp(cep->name, "permissions"))
   3839 		{
   3840 			for (cepp = cep->items; cepp; cepp = cepp->next)
   3841 			{
   3842 				OperClassACL *acl = _conf_parseACL(cepp->name,cepp);
   3843 				AddListItem(acl,operClass->classStruct->acls);
   3844 			}
   3845 		}
   3846 	}
   3847 
   3848 	AddListItem(operClass, conf_operclass);
   3849 	return 1;
   3850 }
   3851 
   3852 void new_permissions_system(ConfigFile *conf, ConfigEntry *ce)
   3853 {
   3854 	if (need_operclass_permissions_upgrade)
   3855 		return; /* error already shown */
   3856 
   3857 	config_error("%s:%i: UnrealIRCd 4.2.1 and higher have a new operclass permissions system.",
   3858 	             ce->file->filename, ce->line_number);
   3859 	config_error("Please see https://www.unrealircd.org/docs/FAQ#New_operclass_permissions");
   3860 	config_error("(additional errors regarding this are suppressed)");
   3861 	/*
   3862 	config_error("First of all, operclass::privileges has been renamed to operclass::permissions.");
   3863 	config_error("However, the permissions themselves have also been changed. You cannot simply "
   3864 	             "rename 'privileges' to 'permissions' and be done with it! ");
   3865 	config_error("See https://www.unrealircd.org/docs/Operclass_permissions for the new list of permissions.");
   3866 	config_error("Or just use the default operclasses from operclass.default.conf, then no need to change anything."); */
   3867 	need_operclass_permissions_upgrade = 1;
   3868 }
   3869 
   3870 int 	_test_operclass(ConfigFile *conf, ConfigEntry *ce)
   3871 {
   3872 	char has_permissions = 0, has_parent = 0;
   3873 	ConfigEntry *cep;
   3874 	int	errors = 0;
   3875 
   3876 	if (!ce->value)
   3877 	{
   3878 		config_error_noname(ce->file->filename, ce->line_number, "operclass");
   3879 		errors++;
   3880 	}
   3881 	for (cep = ce->items; cep; cep = cep->next)
   3882 	{
   3883 		if (!strcmp(cep->name, "parent"))
   3884 		{
   3885 			if (has_parent)
   3886 			{
   3887 				config_warn_duplicate(cep->file->filename,
   3888 					cep->line_number, "operclass::parent");
   3889 				continue;
   3890 			}
   3891 			has_parent = 1;
   3892 			continue;
   3893 		} else
   3894 		if (!strcmp(cep->name, "permissions"))
   3895 		{
   3896 			if (has_permissions)
   3897 			{
   3898 				config_warn_duplicate(cep->file->filename,
   3899 				cep->line_number, "oper::permissions");
   3900 				continue;
   3901 			}
   3902 			has_permissions = 1;
   3903 			continue;
   3904 		} else
   3905 		if (!strcmp(cep->name, "privileges"))
   3906 		{
   3907 			new_permissions_system(conf, cep);
   3908 			errors++;
   3909 			return errors;
   3910 		} else
   3911 		{
   3912 			config_error_unknown(cep->file->filename,
   3913 				cep->line_number, "operclass", cep->name);
   3914 			errors++;
   3915 			continue;
   3916 		}
   3917 	}
   3918 
   3919 	if (!has_permissions)
   3920 	{
   3921 		config_error_missing(ce->file->filename, ce->line_number,
   3922 			"oper::permissions");
   3923 		errors++;
   3924 	}
   3925 
   3926 	return errors;
   3927 }
   3928 
   3929 /*
   3930  * The oper {} block parser
   3931 */
   3932 
   3933 int	_conf_oper(ConfigFile *conf, ConfigEntry *ce)
   3934 {
   3935 	ConfigEntry *cep;
   3936 	ConfigEntry *cepp;
   3937 	ConfigItem_oper *oper = NULL;
   3938 
   3939 	oper =  safe_alloc(sizeof(ConfigItem_oper));
   3940 	safe_strdup(oper->name, ce->value);
   3941 	oper->match = safe_alloc(sizeof(SecurityGroup));
   3942 
   3943 	/* Inherit some defaults: */
   3944 	oper->server_notice_colors = tempiConf.server_notice_colors;
   3945 	oper->server_notice_show_event = tempiConf.server_notice_show_event;
   3946 
   3947 	for (cep = ce->items; cep; cep = cep->next)
   3948 	{
   3949 		if (!strcmp(cep->name, "operclass"))
   3950 			safe_strdup(oper->operclass, cep->value);
   3951 		if (!strcmp(cep->name, "password"))
   3952 			oper->auth = AuthBlockToAuthConfig(cep);
   3953 		else if (!strcmp(cep->name, "class"))
   3954 		{
   3955 			oper->class = find_class(cep->value);
   3956 			if (!oper->class || (oper->class->flag.temporary == 1))
   3957 			{
   3958 				config_status("%s:%i: illegal oper::class, unknown class '%s' using default of class 'default'",
   3959 					cep->file->filename, cep->line_number,
   3960 					cep->value);
   3961 				oper->class = default_class;
   3962 			}
   3963 		}
   3964 		else if (!strcmp(cep->name, "swhois"))
   3965 		{
   3966 			SWhois *s;
   3967 			if (cep->items)
   3968 			{
   3969 				for (cepp = cep->items; cepp; cepp = cepp->next)
   3970 				{
   3971 					s = safe_alloc(sizeof(SWhois));
   3972 					safe_strdup(s->line, cepp->name);
   3973 					safe_strdup(s->setby, "oper");
   3974 					AddListItem(s, oper->swhois);
   3975 				}
   3976 			} else
   3977 			if (cep->value)
   3978 			{
   3979 				s = safe_alloc(sizeof(SWhois));
   3980 				safe_strdup(s->line, cep->value);
   3981 				safe_strdup(s->setby, "oper");
   3982 				AddListItem(s, oper->swhois);
   3983 			}
   3984 		}
   3985 		else if (!strcmp(cep->name, "snomask"))
   3986 		{
   3987 			safe_strdup(oper->snomask, cep->value);
   3988 		}
   3989 		else if (!strcmp(cep->name, "server-notice-colors"))
   3990 		{
   3991 			oper->server_notice_colors = config_checkval(cep->value, CFG_YESNO);
   3992 		}
   3993 		else if (!strcmp(cep->name, "server-notice-show-event"))
   3994 		{
   3995 			oper->server_notice_show_event = config_checkval(cep->value, CFG_YESNO);
   3996 		}
   3997 		else if (!strcmp(cep->name, "auto-login"))
   3998 		{
   3999 			oper->auto_login = config_checkval(cep->value, CFG_YESNO);
   4000 		}
   4001 		else if (!strcmp(cep->name, "modes"))
   4002 		{
   4003 			oper->modes = set_usermode(cep->value);
   4004 		}
   4005 		else if (!strcmp(cep->name, "require-modes"))
   4006 		{
   4007 			oper->require_modes = set_usermode(cep->value);
   4008 		}
   4009 		else if (!strcmp(cep->name, "maxlogins"))
   4010 		{
   4011 			oper->maxlogins = atoi(cep->value);
   4012 		}
   4013 		else if (!strcmp(cep->name, "mask") || !strcmp(cep->name, "match"))
   4014 		{
   4015 			conf_match_block(conf, cep, &oper->match);
   4016 		}
   4017 		else if (!strcmp(cep->name, "vhost"))
   4018 		{
   4019 			safe_strdup(oper->vhost, cep->value);
   4020 		}
   4021 	}
   4022 	AddListItem(oper, conf_oper);
   4023 	return 1;
   4024 }
   4025 
   4026 int	_test_oper(ConfigFile *conf, ConfigEntry *ce)
   4027 {
   4028 	char has_class = 0, has_password = 0, has_snomask = 0;
   4029 	char has_modes = 0, has_require_modes = 0, has_mask = 0, has_match = 0, has_broad_match = 0;
   4030 	char has_maxlogins = 0, has_operclass = 0, has_vhost = 0, has_auto_login = 0;
   4031 	ConfigEntry *cep;
   4032 	int errors = 0;
   4033 
   4034 	if (!ce->value)
   4035 	{
   4036 		config_error_noname(ce->file->filename, ce->line_number, "oper");
   4037 		errors++;
   4038 	}
   4039 	for (cep = ce->items; cep; cep = cep->next)
   4040 	{
   4041 		/* Regular variables */
   4042 		if (!cep->items)
   4043 		{
   4044 			if (config_is_blankorempty(cep, "oper"))
   4045 			{
   4046 				errors++;
   4047 				continue;
   4048 			}
   4049 			/* oper::password */
   4050 			if (!strcmp(cep->name, "password"))
   4051 			{
   4052 				if (has_password)
   4053 				{
   4054 					config_warn_duplicate(cep->file->filename,
   4055 						cep->line_number, "oper::password");
   4056 					continue;
   4057 				}
   4058 				has_password = 1;
   4059 				if (Auth_CheckError(cep) < 0)
   4060 					errors++;
   4061 
   4062 				if (ce->value && cep->value &&
   4063 					!strcmp(ce->value, "bobsmith") &&
   4064 					!strcmp(cep->value, "test"))
   4065 				{
   4066 					config_error("%s:%i: please change the the name and password of the "
   4067 								 "default 'bobsmith' oper block",
   4068 								 ce->file->filename, ce->line_number);
   4069 					errors++;
   4070 				}
   4071 				continue;
   4072 			}
   4073 			/* oper::operclass */
   4074 			else if (!strcmp(cep->name, "operclass"))
   4075 			{
   4076 				if (has_operclass)
   4077 				{
   4078 					config_warn_duplicate(cep->file->filename,
   4079 					cep->line_number, "oper::operclass");
   4080 					continue;
   4081 				}
   4082 				has_operclass = 1;
   4083 				continue;
   4084 			}
   4085 			/* oper::class */
   4086 			else if (!strcmp(cep->name, "class"))
   4087 			{
   4088 				if (has_class)
   4089 				{
   4090 					config_warn_duplicate(cep->file->filename,
   4091 						cep->line_number, "oper::class");
   4092 					continue;
   4093 				}
   4094 				has_class = 1;
   4095 			}
   4096 			/* oper::swhois */
   4097 			else if (!strcmp(cep->name, "swhois"))
   4098 			{
   4099 			}
   4100 			/* oper::vhost */
   4101 			else if (!strcmp(cep->name, "vhost"))
   4102 			{
   4103 				if (has_vhost)
   4104 				{
   4105 					config_warn_duplicate(cep->file->filename,
   4106 						cep->line_number, "oper::vhost");
   4107 					continue;
   4108 				}
   4109 				if (!valid_vhost(cep->value))
   4110 				{
   4111 					config_error("%s:%i: oper::vhost contains illegal characters or is too long: '%s'",
   4112 					             cep->file->filename, cep->line_number, cep->value);
   4113 					errors++;
   4114 				}
   4115 				has_vhost = 1;
   4116 			}
   4117 			/* oper::snomask */
   4118 			else if (!strcmp(cep->name, "snomask"))
   4119 			{
   4120 				char *wrong_snomask;
   4121 				if (has_snomask)
   4122 				{
   4123 					config_warn_duplicate(cep->file->filename,
   4124 						cep->line_number, "oper::snomask");
   4125 					continue;
   4126 				}
   4127 				if (!is_valid_snomask_string_testing(cep->value, &wrong_snomask))
   4128 				{
   4129 					config_error("%s:%i: oper::snomask contains unknown snomask letter(s) '%s'",
   4130 					             cep->file->filename, cep->line_number, wrong_snomask);
   4131 					errors++;
   4132 					invalid_snomasks_encountered++;
   4133 				}
   4134 				has_snomask = 1;
   4135 			}
   4136 			else if (!strcmp(cep->name, "server-notice-colors"))
   4137 			{
   4138 			}
   4139 			else if (!strcmp(cep->name, "server-notice-show-event"))
   4140 			{
   4141 			}
   4142 			else if (!strcmp(cep->name, "auto-login"))
   4143 			{
   4144 				has_auto_login = config_checkval(cep->value, CFG_YESNO);
   4145 			}
   4146 			/* oper::modes */
   4147 			else if (!strcmp(cep->name, "modes"))
   4148 			{
   4149 				char *p;
   4150 				for (p = cep->value; *p; p++)
   4151 					if (strchr("orzS", *p))
   4152 					{
   4153 						config_error("%s:%i: oper::modes may not include mode '%c'",
   4154 							cep->file->filename, cep->line_number, *p);
   4155 						errors++;
   4156 					}
   4157 				if (has_modes)
   4158 				{
   4159 					config_warn_duplicate(cep->file->filename,
   4160 						cep->line_number, "oper::modes");
   4161 					continue;
   4162 				}
   4163 				has_modes = 1;
   4164 			}
   4165 			/* oper::require-modes */
   4166 			else if (!strcmp(cep->name, "require-modes"))
   4167 			{
   4168 				char *p;
   4169 				for (p = cep->value; *p; p++)
   4170 					if (strchr("o", *p))
   4171 					{
   4172 						config_warn("%s:%i: oper::require-modes probably shouldn't include mode '%c'",
   4173 							cep->file->filename, cep->line_number, *p);
   4174 					}
   4175 				if (has_require_modes)
   4176 				{
   4177 					config_warn_duplicate(cep->file->filename,
   4178 						cep->line_number, "oper::require-modes");
   4179 					continue;
   4180 				}
   4181 				has_require_modes = 1;
   4182 			}
   4183 			/* oper::maxlogins */
   4184 			else if (!strcmp(cep->name, "maxlogins"))
   4185 			{
   4186 				int l;
   4187 
   4188 				if (has_maxlogins)
   4189 				{
   4190 					config_warn_duplicate(cep->file->filename,
   4191 						cep->line_number, "oper::maxlogins");
   4192 					continue;
   4193 				}
   4194 				has_maxlogins = 1;
   4195 
   4196 				l = atoi(cep->value);
   4197 				if ((l < 0) || (l > 5000))
   4198 				{
   4199 					config_error("%s:%i: oper::maxlogins: value out of range (%d) should be 0-5000",
   4200 						cep->file->filename, cep->line_number, l);
   4201 					errors++;
   4202 					continue;
   4203 				}
   4204 			}
   4205 			else if (!strcmp(cep->name, "mask"))
   4206 			{
   4207 				if (cep->value || cep->items)
   4208 				{
   4209 					has_mask = 1;
   4210 					test_match_block(conf, cep, &errors);
   4211 					if (test_match_block_too_broad(conf, cep))
   4212 						has_broad_match = 1;
   4213 				}
   4214 			}
   4215 			else if (!strcmp(cep->name, "match"))
   4216 			{
   4217 				if (cep->value || cep->items)
   4218 				{
   4219 					has_match = 1;
   4220 					test_match_block(conf, cep, &errors);
   4221 					if (test_match_block_too_broad(conf, cep))
   4222 						has_broad_match = 1;
   4223 				}
   4224 			}
   4225 			else
   4226 			{
   4227 				config_error_unknown(cep->file->filename,
   4228 					cep->line_number, "oper", cep->name);
   4229 				errors++;
   4230 				continue;
   4231 			}
   4232 		}
   4233 		/* Sections */
   4234 		else
   4235 		{
   4236 			if (!strcmp(cep->name, "swhois"))
   4237 			{
   4238 				/* ok */
   4239 			}
   4240 			else if (!strcmp(cep->name, "mask"))
   4241 			{
   4242 				if (cep->value || cep->items)
   4243 				{
   4244 					has_mask = 1;
   4245 					test_match_block(conf, cep, &errors);
   4246 					if (test_match_block_too_broad(conf, cep))
   4247 						has_broad_match = 1;
   4248 				}
   4249 			}
   4250 			else if (!strcmp(cep->name, "match"))
   4251 			{
   4252 				if (cep->value || cep->items)
   4253 				{
   4254 					has_match = 1;
   4255 					test_match_block(conf, cep, &errors);
   4256 					if (test_match_block_too_broad(conf, cep))
   4257 						has_broad_match = 1;
   4258 				}
   4259 			}
   4260 			else if (!strcmp(cep->name, "password"))
   4261 			{
   4262 				if (has_password)
   4263 				{
   4264 					config_warn_duplicate(cep->file->filename,
   4265 						cep->line_number, "oper::password");
   4266 					continue;
   4267 				}
   4268 				has_password = 1;
   4269 				if (Auth_CheckError(cep) < 0)
   4270 					errors++;
   4271 			}
   4272 			else
   4273 			{
   4274 				config_error_unknown(cep->file->filename,
   4275 					cep->line_number, "oper", cep->name);
   4276 				errors++;
   4277 				continue;
   4278 			}
   4279 		}
   4280 	}
   4281 
   4282 	if (has_auto_login && has_broad_match)
   4283 	{
   4284 		config_error("%s:%i: your oper block for '%s' has auto-login but is completely unrestricted (mask *@*)!",
   4285 		             ce->file->filename, ce->line_number, ce->value);
   4286 		errors++;
   4287 	} else
   4288 	if (!has_password && has_broad_match)
   4289 	{
   4290 		config_error("%s:%i: your oper block for '%s' has no password and is completely unrestricted (mask *@*)!",
   4291 		             ce->file->filename, ce->line_number, ce->value);
   4292 		errors++;
   4293 	}
   4294 
   4295 	/* The rest should NOT be in an 'else'... */
   4296 	if (has_password && has_auto_login)
   4297 	{
   4298 		config_error("%s:%i: You have auto-login enabled for your oper block '%s' but you also have a password set. "
   4299 		             "Remove the password if you want to use auto-login.",
   4300 		             ce->file->filename, ce->line_number, ce->value);
   4301 		errors++;
   4302 	}
   4303 	if (!has_mask && !has_match)
   4304 	{
   4305 		config_error_missing(ce->file->filename, ce->line_number,
   4306 			"oper::match");
   4307 		errors++;
   4308 	}
   4309 	if (has_mask && has_match)
   4310 	{
   4311 		config_error("%s:%d: You cannot have both ::mask and ::match. "
   4312 		             "You should only use oper::match.",
   4313 		             ce->file->filename, ce->line_number);
   4314 		errors++;
   4315 	}
   4316 	if (!has_class)
   4317 	{
   4318 		config_error_missing(ce->file->filename, ce->line_number,
   4319 			"oper::class");
   4320 		errors++;
   4321 	}
   4322 	if (!has_operclass)
   4323 	{
   4324 		config_error_missing(ce->file->filename, ce->line_number,
   4325 			"oper::operclass");
   4326 		errors++;
   4327 	}
   4328 
   4329 	return errors;
   4330 
   4331 }
   4332 
   4333 /*
   4334  * The class {} block parser
   4335 */
   4336 int	_conf_class(ConfigFile *conf, ConfigEntry *ce)
   4337 {
   4338 	ConfigEntry *cep, *cep2;
   4339 	ConfigItem_class *class;
   4340 	unsigned char isnew = 0;
   4341 
   4342 	if (!(class = find_class(ce->value)))
   4343 	{
   4344 		class = safe_alloc(sizeof(ConfigItem_class));
   4345 		safe_strdup(class->name, ce->value);
   4346 		isnew = 1;
   4347 	}
   4348 	else
   4349 	{
   4350 		isnew = 0;
   4351 		class->flag.temporary = 0;
   4352 		class->options = 0; /* RESET OPTIONS */
   4353 	}
   4354 	safe_strdup(class->name, ce->value);
   4355 
   4356 	class->connfreq = 15; /* default */
   4357 
   4358 	for (cep = ce->items; cep; cep = cep->next)
   4359 	{
   4360 		if (!strcmp(cep->name, "pingfreq"))
   4361 			class->pingfreq = config_checkval(cep->value,CFG_TIME);
   4362 		else if (!strcmp(cep->name, "connfreq"))
   4363 			class->connfreq = config_checkval(cep->value,CFG_TIME);
   4364 		else if (!strcmp(cep->name, "maxclients"))
   4365 			class->maxclients = atol(cep->value);
   4366 		else if (!strcmp(cep->name, "sendq"))
   4367 			class->sendq = config_checkval(cep->value,CFG_SIZE);
   4368 		else if (!strcmp(cep->name, "recvq"))
   4369 			class->recvq = config_checkval(cep->value,CFG_SIZE);
   4370 		else if (!strcmp(cep->name, "options"))
   4371 		{
   4372 			for (cep2 = cep->items; cep2; cep2 = cep2->next)
   4373 				if (!strcmp(cep2->name, "nofakelag"))
   4374 					class->options |= CLASS_OPT_NOFAKELAG;
   4375 		}
   4376 	}
   4377 	if (isnew)
   4378 		AddListItem(class, conf_class);
   4379 	return 1;
   4380 }
   4381 
   4382 int	_test_class(ConfigFile *conf, ConfigEntry *ce)
   4383 {
   4384 	ConfigEntry 	*cep, *cep2;
   4385 	int		errors = 0;
   4386 	char has_pingfreq = 0, has_connfreq = 0, has_maxclients = 0, has_sendq = 0;
   4387 	char has_recvq = 0;
   4388 
   4389 	if (!ce->value)
   4390 	{
   4391 		config_error_noname(ce->file->filename, ce->line_number, "class");
   4392 		return 1;
   4393 	}
   4394 	if (!strcasecmp(ce->value, "default"))
   4395 	{
   4396 		config_error("%s:%d: Class cannot be named 'default', this class name is reserved for internal use.",
   4397 			ce->file->filename, ce->line_number);
   4398 		errors++;
   4399 	}
   4400 
   4401 	for (cep = ce->items; cep; cep = cep->next)
   4402 	{
   4403 		if (!strcmp(cep->name, "options"))
   4404 		{
   4405 			for (cep2 = cep->items; cep2; cep2 = cep2->next)
   4406 			{
   4407 #ifdef FAKELAG_CONFIGURABLE
   4408 				if (!strcmp(cep2->name, "nofakelag"))
   4409 					;
   4410 				else
   4411 #endif
   4412 				{
   4413 					config_error("%s:%d: Unknown option '%s' in class::options",
   4414 						cep2->file->filename, cep2->line_number, cep2->name);
   4415 					errors++;
   4416 				}
   4417 			}
   4418 		}
   4419 		else if (config_is_blankorempty(cep, "class"))
   4420 		{
   4421 			errors++;
   4422 			continue;
   4423 		}
   4424 		/* class::pingfreq */
   4425 		else if (!strcmp(cep->name, "pingfreq"))
   4426 		{
   4427 			int v = config_checkval(cep->value,CFG_TIME);
   4428 			if (has_pingfreq)
   4429 			{
   4430 				config_warn_duplicate(cep->file->filename,
   4431 					cep->line_number, "class::pingfreq");
   4432 				continue;
   4433 			}
   4434 			has_pingfreq = 1;
   4435 			if ((v < 30) || (v > 600))
   4436 			{
   4437 				config_error("%s:%i: class::pingfreq should be a reasonable value (30-600)",
   4438 					cep->file->filename, cep->line_number);
   4439 				errors++;
   4440 				continue;
   4441 			}
   4442 		}
   4443 		/* class::maxclients */
   4444 		else if (!strcmp(cep->name, "maxclients"))
   4445 		{
   4446 			long l;
   4447 			if (has_maxclients)
   4448 			{
   4449 				config_warn_duplicate(cep->file->filename,
   4450 					cep->line_number, "class::maxclients");
   4451 				continue;
   4452 			}
   4453 			has_maxclients = 1;
   4454 			l = atol(cep->value);
   4455 			if ((l < 1) || (l > 1000000))
   4456 			{
   4457 				config_error("%s:%i: class::maxclients with illegal value",
   4458 					cep->file->filename, cep->line_number);
   4459 				errors++;
   4460 			}
   4461 		}
   4462 		/* class::connfreq */
   4463 		else if (!strcmp(cep->name, "connfreq"))
   4464 		{
   4465 			long l;
   4466 			if (has_connfreq)
   4467 			{
   4468 				config_warn_duplicate(cep->file->filename,
   4469 					cep->line_number, "class::connfreq");
   4470 				continue;
   4471 			}
   4472 			has_connfreq = 1;
   4473 			l = config_checkval(cep->value,CFG_TIME);
   4474 			if ((l < 5) || (l > 604800))
   4475 			{
   4476 				config_error("%s:%i: class::connfreq with illegal value (must be >5 and <7d)",
   4477 					cep->file->filename, cep->line_number);
   4478 				errors++;
   4479 			}
   4480 		}
   4481 		/* class::sendq */
   4482 		else if (!strcmp(cep->name, "sendq"))
   4483 		{
   4484 			long l;
   4485 			if (has_sendq)
   4486 			{
   4487 				config_warn_duplicate(cep->file->filename,
   4488 					cep->line_number, "class::sendq");
   4489 				continue;
   4490 			}
   4491 			has_sendq = 1;
   4492 			l = config_checkval(cep->value,CFG_SIZE);
   4493 			if ((l <= 0) || (l > 2000000000))
   4494 			{
   4495 				config_error("%s:%i: class::sendq with illegal value",
   4496 					cep->file->filename, cep->line_number);
   4497 				errors++;
   4498 			}
   4499 		}
   4500 		/* class::recvq */
   4501 		else if (!strcmp(cep->name, "recvq"))
   4502 		{
   4503 			long l;
   4504 			if (has_recvq)
   4505 			{
   4506 				config_warn_duplicate(cep->file->filename,
   4507 					cep->line_number, "class::recvq");
   4508 				continue;
   4509 			}
   4510 			has_recvq = 1;
   4511 			l = config_checkval(cep->value,CFG_SIZE);
   4512 			if ((l < 512) || (l > 32768))
   4513 			{
   4514 				config_error("%s:%i: class::recvq with illegal value (must be >512 and <32k)",
   4515 					cep->file->filename, cep->line_number);
   4516 				errors++;
   4517 			}
   4518 		}
   4519 		/* Unknown */
   4520 		else
   4521 		{
   4522 			config_error_unknown(cep->file->filename, cep->line_number,
   4523 				"class", cep->name);
   4524 			errors++;
   4525 			continue;
   4526 		}
   4527 	}
   4528 	if (!has_pingfreq)
   4529 	{
   4530 		config_error_missing(ce->file->filename, ce->line_number,
   4531 			"class::pingfreq");
   4532 		errors++;
   4533 	}
   4534 	if (!has_maxclients)
   4535 	{
   4536 		config_error_missing(ce->file->filename, ce->line_number,
   4537 			"class::maxclients");
   4538 		errors++;
   4539 	}
   4540 	if (!has_sendq)
   4541 	{
   4542 		config_error_missing(ce->file->filename, ce->line_number,
   4543 			"class::sendq");
   4544 		errors++;
   4545 	}
   4546 
   4547 	return errors;
   4548 }
   4549 
   4550 int     _conf_drpass(ConfigFile *conf, ConfigEntry *ce)
   4551 {
   4552 	ConfigEntry *cep;
   4553 
   4554 	if (!conf_drpass)
   4555 	{
   4556 		conf_drpass =  safe_alloc(sizeof(ConfigItem_drpass));
   4557 	}
   4558 
   4559 	for (cep = ce->items; cep; cep = cep->next)
   4560 	{
   4561 		if (!strcmp(cep->name, "restart"))
   4562 		{
   4563 			if (conf_drpass->restartauth)
   4564 				Auth_FreeAuthConfig(conf_drpass->restartauth);
   4565 
   4566 			conf_drpass->restartauth = AuthBlockToAuthConfig(cep);
   4567 		}
   4568 		else if (!strcmp(cep->name, "die"))
   4569 		{
   4570 			if (conf_drpass->dieauth)
   4571 				Auth_FreeAuthConfig(conf_drpass->dieauth);
   4572 
   4573 			conf_drpass->dieauth = AuthBlockToAuthConfig(cep);
   4574 		}
   4575 	}
   4576 	return 1;
   4577 }
   4578 
   4579 int     _test_drpass(ConfigFile *conf, ConfigEntry *ce)
   4580 {
   4581 	ConfigEntry *cep;
   4582 	int errors = 0;
   4583 	char has_restart = 0, has_die = 0;
   4584 
   4585 	for (cep = ce->items; cep; cep = cep->next)
   4586 	{
   4587 		if (config_is_blankorempty(cep, "drpass"))
   4588 		{
   4589 			errors++;
   4590 			continue;
   4591 		}
   4592 		/* drpass::restart */
   4593 		if (!strcmp(cep->name, "restart"))
   4594 		{
   4595 			if (has_restart)
   4596 			{
   4597 				config_warn_duplicate(cep->file->filename,
   4598 					cep->line_number, "drpass::restart");
   4599 				continue;
   4600 			}
   4601 			has_restart = 1;
   4602 			if (Auth_CheckError(cep) < 0)
   4603 				errors++;
   4604 			continue;
   4605 		}
   4606 		/* drpass::die */
   4607 		else if (!strcmp(cep->name, "die"))
   4608 		{
   4609 			if (has_die)
   4610 			{
   4611 				config_warn_duplicate(cep->file->filename,
   4612 					cep->line_number, "drpass::die");
   4613 				continue;
   4614 			}
   4615 			has_die = 1;
   4616 			if (Auth_CheckError(cep) < 0)
   4617 				errors++;
   4618 			continue;
   4619 		}
   4620 		/* Unknown */
   4621 		else
   4622 		{
   4623 			config_error_unknown(cep->file->filename, cep->line_number,
   4624 				"drpass", cep->name);
   4625 			errors++;
   4626 			continue;
   4627 		}
   4628 	}
   4629 	return errors;
   4630 }
   4631 
   4632 /*
   4633  * The ulines {} block parser
   4634 */
   4635 int	_conf_ulines(ConfigFile *conf, ConfigEntry *ce)
   4636 {
   4637 	ConfigEntry *cep;
   4638 	ConfigItem_ulines *ca;
   4639 
   4640 	for (cep = ce->items; cep; cep = cep->next)
   4641 	{
   4642 		ca = safe_alloc(sizeof(ConfigItem_ulines));
   4643 		safe_strdup(ca->servername, cep->name);
   4644 		AddListItem(ca, conf_ulines);
   4645 	}
   4646 	return 1;
   4647 }
   4648 
   4649 int	_test_ulines(ConfigFile *conf, ConfigEntry *ce)
   4650 {
   4651 	/* No check needed */
   4652 	return 0;
   4653 }
   4654 
   4655 int     _conf_tld(ConfigFile *conf, ConfigEntry *ce)
   4656 {
   4657 	ConfigEntry *cep;
   4658 	ConfigItem_tld *ca;
   4659 
   4660 	ca = safe_alloc(sizeof(ConfigItem_tld));
   4661 
   4662 	for (cep = ce->items; cep; cep = cep->next)
   4663 	{
   4664 		if (!strcmp(cep->name, "match") || !strcmp(cep->name, "mask"))
   4665 			conf_match_block(conf, cep, &ca->match);
   4666 		else if (!strcmp(cep->name, "motd"))
   4667 		{
   4668 			safe_strdup(ca->motd_file, cep->value);
   4669 			read_motd(cep->value, &ca->motd);
   4670 		}
   4671 		else if (!strcmp(cep->name, "shortmotd"))
   4672 		{
   4673 			safe_strdup(ca->smotd_file, cep->value);
   4674 			read_motd(cep->value, &ca->smotd);
   4675 		}
   4676 		else if (!strcmp(cep->name, "opermotd"))
   4677 		{
   4678 			safe_strdup(ca->opermotd_file, cep->value);
   4679 			read_motd(cep->value, &ca->opermotd);
   4680 		}
   4681 		else if (!strcmp(cep->name, "botmotd"))
   4682 		{
   4683 			safe_strdup(ca->botmotd_file, cep->value);
   4684 			read_motd(cep->value, &ca->botmotd);
   4685 		}
   4686 		else if (!strcmp(cep->name, "rules"))
   4687 		{
   4688 			safe_strdup(ca->rules_file, cep->value);
   4689 			read_motd(cep->value, &ca->rules);
   4690 		}
   4691 		else if (!strcmp(cep->name, "options"))
   4692 		{
   4693 			ConfigEntry *cepp;
   4694 			for (cepp = cep->items; cepp; cepp = cepp->next)
   4695 			{
   4696 				if (!strcmp(cepp->name, "ssl") || !strcmp(cepp->name, "tls"))
   4697 					ca->options |= TLD_TLS;
   4698 				else if (!strcmp(cepp->name, "remote"))
   4699 					ca->options |= TLD_REMOTE;
   4700 			}
   4701 		}
   4702 		else if (!strcmp(cep->name, "channel"))
   4703 			safe_strdup(ca->channel, cep->value);
   4704 	}
   4705 	AddListItem(ca, conf_tld);
   4706 	return 1;
   4707 }
   4708 
   4709 int     _test_tld(ConfigFile *conf, ConfigEntry *ce)
   4710 {
   4711 	ConfigEntry *cep;
   4712 	int	    errors = 0;
   4713 	int	    fd = -1;
   4714 	char has_mask = 0, has_match = 0, has_motd = 0, has_rules = 0, has_shortmotd = 0;
   4715 	char has_channel = 0, has_opermotd = 0, has_botmotd = 0, has_options = 0;
   4716 
   4717 	for (cep = ce->items; cep; cep = cep->next)
   4718 	{
   4719 		if (!cep->value && strcmp(cep->name, "options") && strcmp(cep->name, "mask") && strcmp(cep->name, "match"))
   4720 		{
   4721 			config_error_empty(cep->file->filename, cep->line_number,
   4722 				"tld", cep->name);
   4723 			errors++;
   4724 			continue;
   4725 		}
   4726 		/* tld::mask */
   4727 		if (!strcmp(cep->name, "mask"))
   4728 		{
   4729 			if (cep->value || cep->items)
   4730 			{
   4731 				has_mask = 1;
   4732 				test_match_block(conf, cep, &errors);
   4733 			}
   4734 		}
   4735 		else if (!strcmp(cep->name, "match"))
   4736 		{
   4737 			if (cep->value || cep->items)
   4738 			{
   4739 				has_match = 1;
   4740 				test_match_block(conf, cep, &errors);
   4741 			}
   4742 		}
   4743 		/* tld::motd */
   4744 		else if (!strcmp(cep->name, "motd"))
   4745 		{
   4746 			if (has_motd)
   4747 			{
   4748 				config_warn_duplicate(cep->file->filename,
   4749 					cep->line_number, "tld::motd");
   4750 				continue;
   4751 			}
   4752 			has_motd = 1;
   4753 			convert_to_absolute_path(&cep->value, CONFDIR);
   4754 			if (((fd = open(cep->value, O_RDONLY)) == -1))
   4755 			{
   4756 				config_error("%s:%i: tld::motd: %s: %s",
   4757 					cep->file->filename, cep->line_number,
   4758 					cep->value, strerror(errno));
   4759 				errors++;
   4760 			}
   4761 			else
   4762 				close(fd);
   4763 		}
   4764 		/* tld::rules */
   4765 		else if (!strcmp(cep->name, "rules"))
   4766 		{
   4767 			if (has_rules)
   4768 			{
   4769 				config_warn_duplicate(cep->file->filename,
   4770 					cep->line_number, "tld::rules");
   4771 				continue;
   4772 			}
   4773 			has_rules = 1;
   4774 			convert_to_absolute_path(&cep->value, CONFDIR);
   4775 			if (((fd = open(cep->value, O_RDONLY)) == -1))
   4776 			{
   4777 				config_error("%s:%i: tld::rules: %s: %s",
   4778 					cep->file->filename, cep->line_number,
   4779 					cep->value, strerror(errno));
   4780 				errors++;
   4781 			}
   4782 			else
   4783 				close(fd);
   4784 		}
   4785 		/* tld::channel */
   4786 		else if (!strcmp(cep->name, "channel"))
   4787 		{
   4788 			if (has_channel)
   4789 			{
   4790 				config_warn_duplicate(cep->file->filename,
   4791 					cep->line_number, "tld::channel");
   4792 				continue;
   4793 			}
   4794 			has_channel = 1;
   4795 		}
   4796 		/* tld::shortmotd */
   4797 		else if (!strcmp(cep->name, "shortmotd"))
   4798 		{
   4799 			if (has_shortmotd)
   4800 			{
   4801 				config_warn_duplicate(cep->file->filename,
   4802 					cep->line_number, "tld::shortmotd");
   4803 				continue;
   4804 			}
   4805 			has_shortmotd = 1;
   4806 			convert_to_absolute_path(&cep->value, CONFDIR);
   4807 			if (((fd = open(cep->value, O_RDONLY)) == -1))
   4808 			{
   4809 				config_error("%s:%i: tld::shortmotd: %s: %s",
   4810 					cep->file->filename, cep->line_number,
   4811 					cep->value, strerror(errno));
   4812 				errors++;
   4813 			}
   4814 			else
   4815 				close(fd);
   4816 		}
   4817 		/* tld::opermotd */
   4818 		else if (!strcmp(cep->name, "opermotd"))
   4819 		{
   4820 			if (has_opermotd)
   4821 			{
   4822 				config_warn_duplicate(cep->file->filename,
   4823 					cep->line_number, "tld::opermotd");
   4824 				continue;
   4825 			}
   4826 			has_opermotd = 1;
   4827 			convert_to_absolute_path(&cep->value, CONFDIR);
   4828 			if (((fd = open(cep->value, O_RDONLY)) == -1))
   4829 			{
   4830 				config_error("%s:%i: tld::opermotd: %s: %s",
   4831 					cep->file->filename, cep->line_number,
   4832 					cep->value, strerror(errno));
   4833 				errors++;
   4834 			}
   4835 			else
   4836 				close(fd);
   4837 		}
   4838 		/* tld::botmotd */
   4839 		else if (!strcmp(cep->name, "botmotd"))
   4840 		{
   4841 			if (has_botmotd)
   4842 			{
   4843 				config_warn_duplicate(cep->file->filename,
   4844 					cep->line_number, "tld::botmotd");
   4845 				continue;
   4846 			}
   4847 			has_botmotd = 1;
   4848 			convert_to_absolute_path(&cep->value, CONFDIR);
   4849 			if (((fd = open(cep->value, O_RDONLY)) == -1))
   4850 			{
   4851 				config_error("%s:%i: tld::botmotd: %s: %s",
   4852 					cep->file->filename, cep->line_number,
   4853 					cep->value, strerror(errno));
   4854 				errors++;
   4855 			}
   4856 			else
   4857 				close(fd);
   4858 		}
   4859 		/* tld::options */
   4860 		else if (!strcmp(cep->name, "options")) {
   4861 			ConfigEntry *cep2;
   4862 
   4863 			if (has_options)
   4864 			{
   4865 				config_warn_duplicate(cep->file->filename,
   4866 					cep->line_number, "tld::options");
   4867 				continue;
   4868 			}
   4869 			has_options = 1;
   4870 
   4871 			for (cep2 = cep->items; cep2; cep2 = cep2->next)
   4872 			{
   4873 				if (strcmp(cep2->name, "ssl") &&
   4874 				    strcmp(cep2->name, "tls") &&
   4875 				    strcmp(cep2->name, "remote"))
   4876 				{
   4877 					config_error_unknownopt(cep2->file->filename,
   4878 						cep2->line_number, "tld", cep2->name);
   4879 					errors++;
   4880 				}
   4881 			}
   4882 		}
   4883 		else
   4884 		{
   4885 			config_error_unknown(cep->file->filename, cep->line_number,
   4886 				"tld", cep->name);
   4887 			errors++;
   4888 			continue;
   4889 		}
   4890 	}
   4891 	if (!has_mask && !has_match)
   4892 	{
   4893 		config_error_missing(ce->file->filename, ce->line_number,
   4894 			"tld::match");
   4895 		errors++;
   4896 	}
   4897 	if (has_mask && has_match)
   4898 	{
   4899 		config_error("%s:%d: You cannot have both ::mask and ::match. "
   4900 		             "You should only use %s::match.",
   4901 		             ce->file->filename, ce->line_number, ce->name);
   4902 		errors++;
   4903 	}
   4904 	return errors;
   4905 }
   4906 
   4907 /* Helper for _conf_listen() */
   4908 void conf_listen_configure(const char *ip, int port, SocketType socket_type, int options, ConfigEntry *ce, ConfigEntry *tlsconfig)
   4909 {
   4910 	ConfigItem_listen *listen;
   4911 	ConfigEntry *cep, *cepp;
   4912 	Hook *h;
   4913 	char isnew = 0;
   4914 
   4915 	if (!(listen = find_listen(ip, port, socket_type)))
   4916 	{
   4917 		listen = safe_alloc(sizeof(ConfigItem_listen));
   4918 		if (socket_type == SOCKET_TYPE_UNIX)
   4919 		{
   4920 			safe_strdup(listen->file, ip);
   4921 		} else {
   4922 			safe_strdup(listen->ip, ip);
   4923 			listen->port = port;
   4924 		}
   4925 		listen->fd = -1;
   4926 		listen->socket_type = socket_type;
   4927 		isnew = 1;
   4928 	}
   4929 
   4930 	if (listen->options & LISTENER_BOUND)
   4931 		options |= LISTENER_BOUND;
   4932 	listen->options = options;
   4933 
   4934 	if (isnew)
   4935 		AddListItem(listen, conf_listen);
   4936 
   4937 	/* Reset all settings of the current listener (free and set defaults): */
   4938 	listen->flag.temporary = 0;
   4939 	listen->start_handshake = start_of_normal_client_handshake;
   4940 	if (listen->ssl_ctx)
   4941 	{
   4942 		SSL_CTX_free(listen->ssl_ctx);
   4943 		listen->ssl_ctx = NULL;
   4944 	}
   4945 	if (listen->tls_options)
   4946 	{
   4947 		free_tls_options(listen->tls_options);
   4948 		listen->tls_options = NULL;
   4949 	}
   4950 	safe_free(listen->websocket_forward);
   4951 	safe_free(listen->webserver);
   4952 
   4953 	/* Now set the new settings: */
   4954 	if (tlsconfig)
   4955 	{
   4956 		listen->tls_options = safe_alloc(sizeof(TLSOptions));
   4957 		conf_tlsblock(conf, tlsconfig, listen->tls_options);
   4958 		listen->ssl_ctx = init_ctx(listen->tls_options, 1);
   4959 	}
   4960 
   4961 	/* For modules that hook CONFIG_LISTEN and CONFIG_LISTEN_OPTIONS.
   4962 	 * Yeah, ugly we have this here..
   4963 	 * and again about 100 lines down too.
   4964 	 */
   4965 	for (cep = ce->items; cep; cep = cep->next)
   4966 	{
   4967 		if (!strcmp(cep->name, "mode"))
   4968 		{
   4969 			/* Yeah, we actually do something with this one.. */
   4970 			if (cep->value)
   4971 				listen->mode = strtol(cep->value, NULL, 8); /* octal */
   4972 		}
   4973 		else if (!strcmp(cep->name, "spoof-ip"))
   4974 			safe_strdup(listen->spoof_ip, cep->value);
   4975 		else if (!strcmp(cep->name, "ip"))
   4976 			;
   4977 		else if (!strcmp(cep->name, "port"))
   4978 			;
   4979 		else if (!strcmp(cep->name, "options"))
   4980 		{
   4981 			for (cepp = cep->items; cepp; cepp = cepp->next)
   4982 			{
   4983 				NameValue *ofp;
   4984 				if (!nv_find_by_name(_ListenerFlags, cepp->name))
   4985 				{
   4986 					for (h = Hooks[HOOKTYPE_CONFIGRUN_EX]; h; h = h->next)
   4987 					{
   4988 						int value = (*(h->func.intfunc))(conf, cepp, CONFIG_LISTEN_OPTIONS, listen);
   4989 						if (value == 1)
   4990 							break;
   4991 					}
   4992 				}
   4993 			}
   4994 		} else
   4995 		if (!strcmp(cep->name, "ssl-options") || !strcmp(cep->name, "tls-options"))
   4996 			;
   4997 		else
   4998 		{
   4999 			for (h = Hooks[HOOKTYPE_CONFIGRUN_EX]; h; h = h->next)
   5000 			{
   5001 				int value = (*(h->func.intfunc))(conf, cep, CONFIG_LISTEN, listen);
   5002 				if (value == 1)
   5003 					break;
   5004 			}
   5005 		}
   5006 	}
   5007 }
   5008 
   5009 int	_conf_listen(ConfigFile *conf, ConfigEntry *ce)
   5010 {
   5011 	ConfigEntry *cep, *cepp;
   5012 	ConfigEntry *tlsconfig = NULL;
   5013 	char *file = NULL;
   5014 	char *ip = NULL;
   5015 	char *spoof_ip = NULL;
   5016 	int start=0, end=0, port;
   5017 	int listener_flags =0;
   5018 	Hook *h;
   5019 
   5020 	for (cep = ce->items; cep; cep = cep->next)
   5021 	{
   5022 		if (!strcmp(cep->name, "file"))
   5023 		{
   5024 			convert_to_absolute_path(&cep->value, PERMDATADIR);
   5025 			file = cep->value;
   5026 		} else
   5027 		if (!strcmp(cep->name, "mode"))
   5028 		{
   5029 			// Handled elsewhere, but need to be caught here as noop
   5030 		} else
   5031 		if (!strcmp(cep->name, "ip"))
   5032 		{
   5033 			ip = cep->value;
   5034 		} else
   5035 		if (!strcmp(cep->name, "spoof-ip"))
   5036 		{
   5037 			spoof_ip = cep->value;
   5038 		} else
   5039 		if (!strcmp(cep->name, "port"))
   5040 		{
   5041 			port_range(cep->value, &start, &end);
   5042 			if ((start < 0) || (start > 65535) || (end < 0) || (end > 65535))
   5043 				return -1; /* this is already validated in _test_listen, but okay.. */
   5044 		} else
   5045 		if (!strcmp(cep->name, "options"))
   5046 		{
   5047 			for (cepp = cep->items; cepp; cepp = cepp->next)
   5048 			{
   5049 				long v;
   5050 				if ((v = nv_find_by_name(_ListenerFlags, cepp->name)))
   5051 				{
   5052 					listener_flags |= v;
   5053 				} else {
   5054 					for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next)
   5055 					{
   5056 						int value = (*(h->func.intfunc))(conf, cepp, CONFIG_LISTEN_OPTIONS);
   5057 						if (value == 1)
   5058 							break;
   5059 					}
   5060 				}
   5061 			}
   5062 		} else
   5063 		if (!strcmp(cep->name, "ssl-options") || !strcmp(cep->name, "tls-options"))
   5064 		{
   5065 			tlsconfig = cep;
   5066 		} else
   5067 		{
   5068 			for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next)
   5069 			{
   5070 				int value = (*(h->func.intfunc))(conf, cep, CONFIG_LISTEN);
   5071 				if (value == 1)
   5072 					break;
   5073 			}
   5074 		}
   5075 	}
   5076 
   5077 	/* UNIX domain socket */
   5078 	if (file)
   5079 	{
   5080 		conf_listen_configure(file, 0, SOCKET_TYPE_UNIX, listener_flags, ce, tlsconfig);
   5081 		return 1;
   5082 	}
   5083 
   5084 	for (port = start; port <= end; port++)
   5085 	{
   5086 		/* First deal with IPv4 */
   5087 		if (!strchr(ip, ':'))
   5088 			conf_listen_configure(ip, port, SOCKET_TYPE_IPV4, listener_flags, ce, tlsconfig);
   5089 
   5090 		/* Then deal with IPv6 (if available/enabled) */
   5091 		if (!DISABLE_IPV6 && (strchr(ip, ':') || (*ip == '*')))
   5092 			conf_listen_configure(ip, port, SOCKET_TYPE_IPV6, listener_flags, ce, tlsconfig);
   5093 	}
   5094 
   5095 	return 1;
   5096 }
   5097 
   5098 int	_test_listen(ConfigFile *conf, ConfigEntry *ce)
   5099 {
   5100 	ConfigEntry *cep;
   5101 	ConfigEntry *cepp;
   5102 	int errors = 0;
   5103 	char has_file = 0, has_ip = 0, has_port = 0, has_options = 0, port_6667 = 0, has_spoof_ip = 0;
   5104 	char *file = NULL;
   5105 	char *ip = NULL;
   5106 	Hook *h;
   5107 
   5108 	if (ce->value)
   5109 	{
   5110 		config_error("%s:%i: listen block has a new syntax, see https://www.unrealircd.org/docs/Listen_block",
   5111 			ce->file->filename, ce->line_number);
   5112 		return 1;
   5113 	}
   5114 
   5115 	for (cep = ce->items; cep; cep = cep->next)
   5116 	{
   5117 		int used_by_module = 0;
   5118 
   5119 		/* First, check if a module knows about this listen::something */
   5120 		for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next)
   5121 		{
   5122 			int value, errs = 0;
   5123 			if (h->owner && !(h->owner->flags & MODFLAG_TESTING)
   5124 			    && !(h->owner->options & MOD_OPT_PERM))
   5125 			{
   5126 				continue;
   5127 			}
   5128 			value = (*(h->func.intfunc))(conf, cep, CONFIG_LISTEN, &errs);
   5129 			if (value == 2)
   5130 				used_by_module = 1;
   5131 			if (value == 1)
   5132 			{
   5133 				used_by_module = 1;
   5134 				break;
   5135 			}
   5136 			if (value == -1)
   5137 			{
   5138 				used_by_module = 1;
   5139 				errors += errs;
   5140 				break;
   5141 			}
   5142 			if (value == -2)
   5143 			{
   5144 				used_by_module = 1;
   5145 				errors += errs;
   5146 			}
   5147 		}
   5148 		if (!strcmp(cep->name, "options"))
   5149 		{
   5150 			if (has_options)
   5151 			{
   5152 				config_warn_duplicate(cep->file->filename,
   5153 					cep->line_number, "listen::options");
   5154 				continue;
   5155 			}
   5156 			has_options = 1;
   5157 			for (cepp = cep->items; cepp; cepp = cepp->next)
   5158 			{
   5159 				if (!nv_find_by_name(_ListenerFlags, cepp->name))
   5160 				{
   5161 					/* Check if a module knows about this listen::options::something */
   5162 					int used_by_module = 0;
   5163 					for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next)
   5164 					{
   5165 						int value, errs = 0;
   5166 						if (h->owner && !(h->owner->flags & MODFLAG_TESTING)
   5167 						    && !(h->owner->options & MOD_OPT_PERM))
   5168 						{
   5169 							continue;
   5170 						}
   5171 						value = (*(h->func.intfunc))(conf, cepp, CONFIG_LISTEN_OPTIONS, &errs);
   5172 						if (value == 2)
   5173 							used_by_module = 1;
   5174 						if (value == 1)
   5175 						{
   5176 							used_by_module = 1;
   5177 							break;
   5178 						}
   5179 						if (value == -1)
   5180 						{
   5181 							used_by_module = 1;
   5182 							errors += errs;
   5183 							break;
   5184 						}
   5185 						if (value == -2)
   5186 						{
   5187 							used_by_module = 1;
   5188 							errors += errs;
   5189 						}
   5190 					}
   5191 					if (!used_by_module)
   5192 					{
   5193 						config_error_unknownopt(cepp->file->filename,
   5194 							cepp->line_number, "listen::options", cepp->name);
   5195 						errors++;
   5196 						continue;
   5197 					}
   5198 				}
   5199 				if (!strcmp(cepp->name, "ssl") || !strcmp(cepp->name, "tls"))
   5200 					have_tls_listeners = 1; /* for ssl config test */
   5201 			}
   5202 		}
   5203 		else
   5204 		if (!strcmp(cep->name, "ssl-options") || !strcmp(cep->name, "tls-options"))
   5205 		{
   5206 			test_tlsblock(conf, cep, &errors);
   5207 		}
   5208 		else
   5209 		if (!cep->value)
   5210 		{
   5211 			if (!used_by_module)
   5212 			{
   5213 				config_error_empty(cep->file->filename,
   5214 					cep->line_number, "listen", cep->name);
   5215 				errors++;
   5216 			}
   5217 			continue; /* always */
   5218 		} else
   5219 		if (!strcmp(cep->name, "file"))
   5220 		{
   5221 			has_file = 1;
   5222 			file = cep->value;
   5223 		} else
   5224 		if (!strcmp(cep->name, "spoof-ip"))
   5225 		{
   5226 			has_spoof_ip = 1;
   5227 			if (!is_valid_ip(cep->value))
   5228 			{
   5229 				config_error("%s:%i: listen::spoof-ip is not a valid IP address (%s)",
   5230 				             cep->file->filename, cep->line_number, cep->value);
   5231 				errors++;
   5232 			}
   5233 		} else
   5234 		if (!strcmp(cep->name, "mode"))
   5235 		{
   5236 			int mode = strtol(cep->value, NULL, 8);
   5237 			if ((mode != 0700) && (mode != 0770) && (mode != 0777))
   5238 			{
   5239 				config_error("%s:%i: listen::mode must be one of: 0700 (user only, the default), "
   5240 				             "0770 (user and group readable/writable), or "
   5241 				             "0777 (world readable and writable, not recommended).",
   5242 				             cep->file->filename, cep->line_number);
   5243 				errors++;
   5244 			}
   5245 		} else
   5246 		if (!strcmp(cep->name, "ip"))
   5247 		{
   5248 			has_ip = 1;
   5249 
   5250 			if (strcmp(cep->value, "*") && !is_valid_ip(cep->value))
   5251 			{
   5252 				config_error("%s:%i: listen: illegal listen::ip (%s). Must be either '*' or contain a valid IP.",
   5253 					cep->file->filename, cep->line_number, cep->value);
   5254 				return 1;
   5255 			}
   5256 			ip = cep->value;
   5257 		} else
   5258 		if (!strcmp(cep->name, "host"))
   5259 		{
   5260 			config_error("%s:%i: listen: unknown option listen::host, did you mean listen::ip?",
   5261 				cep->file->filename, cep->line_number);
   5262 			errors++;
   5263 		} else
   5264 		if (!strcmp(cep->name, "port"))
   5265 		{
   5266 			int start = 0, end = 0;
   5267 
   5268 			has_port = 1;
   5269 
   5270 			port_range(cep->value, &start, &end);
   5271 			if (start == end)
   5272 			{
   5273 				if ((start < 1) || (start > 65535))
   5274 				{
   5275 					config_error("%s:%i: listen: illegal port (must be 1..65535)",
   5276 						cep->file->filename, cep->line_number);
   5277 					return 1;
   5278 				}
   5279 			}
   5280 			else
   5281 			{
   5282 				if (end < start)
   5283 				{
   5284 					config_error("%s:%i: listen: illegal port range end value is less than starting value",
   5285 						cep->file->filename, cep->line_number);
   5286 					return 1;
   5287 				}
   5288 				if (end - start >= 100)
   5289 				{
   5290 					config_error("%s:%i: listen: you requested port %d-%d, that's %d ports "
   5291 						"(and thus consumes %d sockets) this is probably not what you want.",
   5292 						cep->file->filename, cep->line_number, start, end,
   5293 						end - start + 1, end - start + 1);
   5294 					return 1;
   5295 				}
   5296 				if ((start < 1) || (start > 65535) || (end < 1) || (end > 65535))
   5297 				{
   5298 					config_error("%s:%i: listen: illegal port range values must be between 1 and 65535",
   5299 						cep->file->filename, cep->line_number);
   5300 					return 1;
   5301 				}
   5302 			}
   5303 
   5304 			if ((6667 >= start) && (6667 <= end))
   5305 				port_6667 = 1;
   5306 		} else
   5307 		{
   5308 			if (!used_by_module)
   5309 			{
   5310 				config_error_unknown(cep->file->filename, cep->line_number,
   5311 					"listen", cep->name);
   5312 				errors++;
   5313 			}
   5314 			continue; /* always */
   5315 		}
   5316 	}
   5317 
   5318 	if (has_file)
   5319 	{
   5320 		if (has_ip || has_port)
   5321 		{
   5322 			config_error("%s:%d: listen block should either have a 'file' (for *NIX domain socket), "
   5323 			             "OR have an 'ip' and 'port' (for IPv4/IPv6). You cannot combine both in one listen block.",
   5324 			             ce->file->filename, ce->line_number);
   5325 			errors++;
   5326 		} else {
   5327 			// TODO: check if file can be created fresh etc.
   5328 		}
   5329 	} else
   5330 	{
   5331 		if (!has_ip)
   5332 		{
   5333 			config_error("%s:%d: listen block requires an listen::ip",
   5334 				ce->file->filename, ce->line_number);
   5335 			errors++;
   5336 		}
   5337 
   5338 		if (!has_port)
   5339 		{
   5340 			config_error("%s:%d: listen block requires an listen::port",
   5341 				ce->file->filename, ce->line_number);
   5342 			errors++;
   5343 		}
   5344 	}
   5345 
   5346 	if (has_spoof_ip && !has_file)
   5347 	{
   5348 		config_error("%s:%d: listen::spoof-ip is only valid when listen::file is used (UNIX domain sockets)",
   5349 		             ce->file->filename, ce->line_number);
   5350 		errors++;
   5351 	}
   5352 
   5353 	if (port_6667)
   5354 		safe_strdup(port_6667_ip, ip);
   5355 
   5356 	requiredstuff.conf_listen = 1;
   5357 	return errors;
   5358 }
   5359 
   5360 
   5361 int	_conf_allow(ConfigFile *conf, ConfigEntry *ce)
   5362 {
   5363 	ConfigEntry *cep, *cepp;
   5364 	ConfigItem_allow *allow;
   5365 	Hook *h;
   5366 
   5367 	if (ce->value)
   5368 	{
   5369 		if (!strcmp(ce->value, "channel"))
   5370 			return (_conf_allow_channel(conf, ce));
   5371 		else
   5372 		{
   5373 			int value;
   5374 			for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next)
   5375 			{
   5376 				value = (*(h->func.intfunc))(conf,ce,CONFIG_ALLOW);
   5377 				if (value == 1)
   5378 					break;
   5379 			}
   5380 			return 0;
   5381 		}
   5382 	}
   5383 	allow = safe_alloc(sizeof(ConfigItem_allow));
   5384 	allow->ipv6_clone_mask = tempiConf.default_ipv6_clone_mask;
   5385 	allow->match = safe_alloc(sizeof(SecurityGroup));
   5386 
   5387 	for (cep = ce->items; cep; cep = cep->next)
   5388 	{
   5389 		if (!strcmp(cep->name, "match") || !strcmp(cep->name, "mask") || !strcmp(cep->name, "ip") || !strcmp(cep->name, "hostname"))
   5390 		{
   5391 			conf_match_block(conf, cep, &allow->match);
   5392 		}
   5393 		else if (!strcmp(cep->name, "password"))
   5394 			allow->auth = AuthBlockToAuthConfig(cep);
   5395 		else if (!strcmp(cep->name, "class"))
   5396 		{
   5397 			allow->class = find_class(cep->value);
   5398 			if (!allow->class || (allow->class->flag.temporary == 1))
   5399 			{
   5400 				config_status("%s:%i: illegal allow::class, unknown class '%s' using default of class 'default'",
   5401 					cep->file->filename,
   5402 					cep->line_number,
   5403 					cep->value);
   5404 					allow->class = default_class;
   5405 			}
   5406 		}
   5407 		else if (!strcmp(cep->name, "maxperip"))
   5408 			allow->maxperip = atoi(cep->value);
   5409 		else if (!strcmp(cep->name, "global-maxperip"))
   5410 			allow->global_maxperip = atoi(cep->value);
   5411 		else if (!strcmp(cep->name, "redirect-server"))
   5412 			safe_strdup(allow->server, cep->value);
   5413 		else if (!strcmp(cep->name, "redirect-port"))
   5414 			allow->port = atoi(cep->value);
   5415 		else if (!strcmp(cep->name, "ipv6-clone-mask"))
   5416 		{
   5417 			/*
   5418 			 * If this item isn't set explicitly by the
   5419 			 * user, the value will temporarily be
   5420 			 * zero. Defaults are applied in config_run_blocks().
   5421 			 */
   5422 			allow->ipv6_clone_mask = atoi(cep->value);
   5423 		}
   5424 		else if (!strcmp(cep->name, "options"))
   5425 		{
   5426 			for (cepp = cep->items; cepp; cepp = cepp->next)
   5427 			{
   5428 				if (!strcmp(cepp->name, "noident"))
   5429 					allow->flags.noident = 1;
   5430 				else if (!strcmp(cepp->name, "useip"))
   5431 					allow->flags.useip = 1;
   5432 				else if (!strcmp(cepp->name, "ssl") || !strcmp(cepp->name, "tls"))
   5433 					allow->flags.tls = 1;
   5434 				else if (!strcmp(cepp->name, "reject-on-auth-failure"))
   5435 					allow->flags.reject_on_auth_failure = 1;
   5436 			}
   5437 		}
   5438 	}
   5439 
   5440 	/* Default: global-maxperip = maxperip+1 */
   5441 	if (allow->global_maxperip == 0)
   5442 		allow->global_maxperip = allow->maxperip+1;
   5443 
   5444 	/* global-maxperip < maxperip makes no sense */
   5445 	if (allow->global_maxperip < allow->maxperip)
   5446 		allow->global_maxperip = allow->maxperip;
   5447 
   5448 	AddListItem(allow, conf_allow);
   5449 	return 1;
   5450 }
   5451 
   5452 int	_test_allow(ConfigFile *conf, ConfigEntry *ce)
   5453 {
   5454 	ConfigEntry *cep, *cepp;
   5455 	int		errors = 0;
   5456 	Hook *h;
   5457 	char has_ip = 0, has_hostname = 0, has_mask = 0, has_match = 0;
   5458 	char has_maxperip = 0, has_global_maxperip = 0, has_password = 0, has_class = 0;
   5459 	char has_redirectserver = 0, has_redirectport = 0, has_options = 0;
   5460 	int hostname_possible_silliness = 0;
   5461 
   5462 	if (ce->value)
   5463 	{
   5464 		if (!strcmp(ce->value, "channel"))
   5465 			return (_test_allow_channel(conf, ce));
   5466 		else
   5467 		{
   5468 			int used = 0;
   5469 			for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next)
   5470 			{
   5471 				int value, errs = 0;
   5472 				if (h->owner && !(h->owner->flags & MODFLAG_TESTING)
   5473 				    && !(h->owner->options & MOD_OPT_PERM))
   5474 					continue;
   5475 				value = (*(h->func.intfunc))(conf,ce,CONFIG_ALLOW,&errs);
   5476 				if (value == 2)
   5477 					used = 1;
   5478 				if (value == 1)
   5479 				{
   5480 					used = 1;
   5481 					break;
   5482 				}
   5483 				if (value == -1)
   5484 				{
   5485 					used = 1;
   5486 					errors += errs;
   5487 					break;
   5488 				}
   5489 				if (value == -2)
   5490 				{
   5491 					used = 1;
   5492 					errors += errs;
   5493 				}
   5494 			}
   5495 			if (!used) {
   5496 				config_error("%s:%i: allow item with unknown type",
   5497 					ce->file->filename, ce->line_number);
   5498 				return 1;
   5499 			}
   5500 			return errors;
   5501 		}
   5502 	}
   5503 
   5504 	for (cep = ce->items; cep; cep = cep->next)
   5505 	{
   5506 		if (strcmp(cep->name, "options") &&
   5507 		    strcmp(cep->name, "match") &&
   5508 		    strcmp(cep->name, "mask") &&
   5509 		    config_is_blankorempty(cep, "allow"))
   5510 		{
   5511 			errors++;
   5512 			continue;
   5513 		}
   5514 		if (!strcmp(cep->name, "ip"))
   5515 		{
   5516 			if (has_ip)
   5517 			{
   5518 				config_warn_duplicate(cep->file->filename,
   5519 					cep->line_number, "allow::ip");
   5520 				continue;
   5521 			}
   5522 			has_ip = 1;
   5523 		}
   5524 		else if (!strcmp(cep->name, "hostname"))
   5525 		{
   5526 			if (has_hostname)
   5527 			{
   5528 				config_warn_duplicate(cep->file->filename,
   5529 					cep->line_number, "allow::hostname");
   5530 				continue;
   5531 			}
   5532 			has_hostname = 1;
   5533 			if (!strcmp(cep->value, "*@*") || !strcmp(cep->value, "*"))
   5534 				hostname_possible_silliness = 1;
   5535 		}
   5536 		else if (!strcmp(cep->name, "mask"))
   5537 		{
   5538 			has_mask = 1;
   5539 			test_match_block(conf, cep, &errors);
   5540 		}
   5541 		else if (!strcmp(cep->name, "match"))
   5542 		{
   5543 			has_match = 1;
   5544 			test_match_block(conf, cep, &errors);
   5545 		}
   5546 		else if (!strcmp(cep->name, "maxperip"))
   5547 		{
   5548 			int v = atoi(cep->value);
   5549 			if (has_maxperip)
   5550 			{
   5551 				config_warn_duplicate(cep->file->filename,
   5552 					cep->line_number, "allow::maxperip");
   5553 				continue;
   5554 			}
   5555 			has_maxperip = 1;
   5556 			if ((v <= 0) || (v > 1000000))
   5557 			{
   5558 				config_error("%s:%i: allow::maxperip with illegal value (must be 1-1000000)",
   5559 					cep->file->filename, cep->line_number);
   5560 				errors++;
   5561 			}
   5562 		}
   5563 		else if (!strcmp(cep->name, "global-maxperip"))
   5564 		{
   5565 			int v = atoi(cep->value);
   5566 			if (has_global_maxperip)
   5567 			{
   5568 				config_warn_duplicate(cep->file->filename,
   5569 					cep->line_number, "allow::global-maxperip");
   5570 				continue;
   5571 			}
   5572 			has_global_maxperip = 1;
   5573 			if ((v <= 0) || (v > 1000000))
   5574 			{
   5575 				config_error("%s:%i: allow::global-maxperip with illegal value (must be 1-1000000)",
   5576 					cep->file->filename, cep->line_number);
   5577 				errors++;
   5578 			}
   5579 		}
   5580 		else if (!strcmp(cep->name, "ipv6-clone-mask"))
   5581 		{
   5582 			/* keep this in sync with _test_set() */
   5583 			int ipv6mask;
   5584 			ipv6mask = atoi(cep->value);
   5585 			if (ipv6mask == 0)
   5586 			{
   5587 				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.",
   5588 					     cep->file->filename, cep->line_number);
   5589 				errors++;
   5590 			}
   5591 			if (ipv6mask > 128)
   5592 			{
   5593 				config_error("%s:%d: set::default-ipv6-clone-mask was set to %d. The maximum value is 128.",
   5594 					     cep->file->filename, cep->line_number,
   5595 					     ipv6mask);
   5596 				errors++;
   5597 			}
   5598 			if (ipv6mask <= 32)
   5599 			{
   5600 				config_warn("%s:%d: allow::ipv6-clone-mask was given a very small value.",
   5601 					    cep->file->filename, cep->line_number);
   5602 			}
   5603 		}
   5604 		else if (!strcmp(cep->name, "password"))
   5605 		{
   5606 			if (has_password)
   5607 			{
   5608 				config_warn_duplicate(cep->file->filename,
   5609 					cep->line_number, "allow::password");
   5610 				continue;
   5611 			}
   5612 			has_password = 1;
   5613 			/* some auth check stuff? */
   5614 			if (Auth_CheckError(cep) < 0)
   5615 				errors++;
   5616 		}
   5617 		else if (!strcmp(cep->name, "class"))
   5618 		{
   5619 			if (has_class)
   5620 			{
   5621 				config_warn_duplicate(cep->file->filename,
   5622 					cep->line_number, "allow::class");
   5623 				continue;
   5624 			}
   5625 			has_class = 1;
   5626 		}
   5627 		else if (!strcmp(cep->name, "redirect-server"))
   5628 		{
   5629 			if (has_redirectserver)
   5630 			{
   5631 				config_warn_duplicate(cep->file->filename,
   5632 					cep->line_number, "allow::redirect-server");
   5633 				continue;
   5634 			}
   5635 			has_redirectserver = 1;
   5636 		}
   5637 		else if (!strcmp(cep->name, "redirect-port"))
   5638 		{
   5639 			if (has_redirectport)
   5640 			{
   5641 				config_warn_duplicate(cep->file->filename,
   5642 					cep->line_number, "allow::redirect-port");
   5643 				continue;
   5644 			}
   5645 			has_redirectport = 1;
   5646 		}
   5647 		else if (!strcmp(cep->name, "options"))
   5648 		{
   5649 			if (has_options)
   5650 			{
   5651 				config_warn_duplicate(cep->file->filename,
   5652 					cep->line_number, "allow::options");
   5653 				continue;
   5654 			}
   5655 			has_options = 1;
   5656 			for (cepp = cep->items; cepp; cepp = cepp->next)
   5657 			{
   5658 				if (!strcmp(cepp->name, "noident"))
   5659 				{}
   5660 				else if (!strcmp(cepp->name, "useip"))
   5661 				{}
   5662 				else if (!strcmp(cepp->name, "ssl") || !strcmp(cepp->name, "tls"))
   5663 				{}
   5664 				else if (!strcmp(cepp->name, "reject-on-auth-failure"))
   5665 				{}
   5666 				else if (!strcmp(cepp->name, "sasl"))
   5667 				{
   5668 					config_error("%s:%d: The option allow::options::sasl no longer exists. "
   5669 					             "Please use a require authentication { } block instead, which "
   5670 					             "is more flexible and provides the same functionality. See "
   5671 					             "https://www.unrealircd.org/docs/Require_authentication_block",
   5672 					             cepp->file->filename, cepp->line_number);
   5673 					errors++;
   5674 				}
   5675 				else
   5676 				{
   5677 					config_error_unknownopt(cepp->file->filename,
   5678 						cepp->line_number, "allow", cepp->name);
   5679 					errors++;
   5680 				}
   5681 			}
   5682 		}
   5683 		else
   5684 		{
   5685 			config_error_unknown(cep->file->filename, cep->line_number,
   5686 				"allow", cep->name);
   5687 			errors++;
   5688 			continue;
   5689 		}
   5690 	}
   5691 
   5692 	if ((has_mask || has_match) && (has_ip || has_hostname))
   5693 	{
   5694 		config_error("%s:%d: The allow block uses allow::match, but you also have an allow::ip and allow::hostname.",
   5695 			ce->file->filename, ce->line_number);
   5696 		config_error("Please delete your allow::ip and allow::hostname entries and/or integrate them into allow::match");
   5697 	} else
   5698 	if (has_ip)
   5699 	{
   5700 		config_warn("%s:%d: The allow block uses allow::match nowadays. Rename your allow::ip item to allow::match.",
   5701 			ce->file->filename, ce->line_number);
   5702 		config_warn("See https://www.unrealircd.org/docs/FAQ#allow-mask for more information");
   5703 	} else
   5704 	if (has_hostname)
   5705 	{
   5706 		config_warn("%s:%d: The allow block uses allow::match nowadays. Rename your allow::hostname item to allow::match.",
   5707 			ce->file->filename, ce->line_number);
   5708 		config_warn("See https://www.unrealircd.org/docs/FAQ#allow-mask for more information");
   5709 	} else
   5710 	if (has_mask && has_match)
   5711 	{
   5712 		config_error("%s:%d: You cannot have both ::mask and ::match. You should only use allow::match.",
   5713 				 ce->file->filename, ce->line_number);
   5714 		errors++;
   5715 	} else
   5716 	if (!has_match && !has_mask)
   5717 	{
   5718 		config_error("%s:%d: allow block needs an allow::match",
   5719 				 ce->file->filename, ce->line_number);
   5720 		errors++;
   5721 	}
   5722 
   5723 	if (has_ip && has_hostname)
   5724 	{
   5725 		config_error("%s:%d: allow block has both allow::ip and allow::hostname, this is no longer permitted.",
   5726 		             ce->file->filename, ce->line_number);
   5727 		config_error("Please integrate your allow::ip and allow::hostname items into a single allow::mask block");
   5728 		errors++;
   5729 	} else
   5730 	if (hostname_possible_silliness)
   5731 	{
   5732 		config_error("%s:%d: allow block contains 'hostname *;'. This means means that users "
   5733 		             "without a valid hostname (unresolved IP's) will be unable to connect. "
   5734 		             "You most likely want to use 'mask *;' instead.",
   5735 		             ce->file->filename, ce->line_number);
   5736 	}
   5737 
   5738 	if (!has_class)
   5739 	{
   5740 		config_error_missing(ce->file->filename, ce->line_number,
   5741 			"allow::class");
   5742 		errors++;
   5743 	}
   5744 
   5745 	if (!has_maxperip)
   5746 	{
   5747 		config_error_missing(ce->file->filename, ce->line_number,
   5748 			"allow::maxperip");
   5749 		errors++;
   5750 	}
   5751 	return errors;
   5752 }
   5753 
   5754 int	_conf_allow_channel(ConfigFile *conf, ConfigEntry *ce)
   5755 {
   5756 	ConfigItem_allow_channel 	*allow = NULL;
   5757 	ConfigEntry 	    	*cep;
   5758 	char *class = NULL;
   5759 	ConfigEntry *match = NULL;
   5760 
   5761 	/* First, search for ::class, if any */
   5762 	for (cep = ce->items; cep; cep = cep->next)
   5763 	{
   5764 		if (!strcmp(cep->name, "class"))
   5765 			class = cep->value;
   5766 		else if (!strcmp(cep->name, "match") || !strcmp(cep->name, "mask"))
   5767 			match = cep;
   5768 	}
   5769 
   5770 	for (cep = ce->items; cep; cep = cep->next)
   5771 	{
   5772 		if (!strcmp(cep->name, "channel"))
   5773 		{
   5774 			/* This way, we permit multiple ::channel items in one allow block */
   5775 			allow = safe_alloc(sizeof(ConfigItem_allow_channel));
   5776 			safe_strdup(allow->channel, cep->value);
   5777 			if (class)
   5778 				safe_strdup(allow->class, class);
   5779 			if (match)
   5780 				conf_match_block(conf, match, &allow->match);
   5781 			AddListItem(allow, conf_allow_channel);
   5782 		}
   5783 	}
   5784 	return 1;
   5785 }
   5786 
   5787 int	_test_allow_channel(ConfigFile *conf, ConfigEntry *ce)
   5788 {
   5789 	ConfigEntry	*cep;
   5790 	int		errors = 0;
   5791 	char		has_match = 0, has_mask = 0, has_channel = 0, has_class = 0;
   5792 
   5793 	for (cep = ce->items; cep; cep = cep->next)
   5794 	{
   5795 		if (config_is_blankorempty(cep, "allow channel"))
   5796 		{
   5797 			errors++;
   5798 			continue;
   5799 		}
   5800 
   5801 		if (!strcmp(cep->name, "channel"))
   5802 		{
   5803 			has_channel = 1;
   5804 		}
   5805 		else if (!strcmp(cep->name, "class"))
   5806 		{
   5807 
   5808 			if (has_class)
   5809 			{
   5810 				config_warn_duplicate(cep->file->filename,
   5811 					cep->line_number, "allow channel::class");
   5812 				continue;
   5813 			}
   5814 			has_class = 1;
   5815 		}
   5816 		else if (!strcmp(cep->name, "match"))
   5817 		{
   5818 			has_match = 1;
   5819 			test_match_block(conf, cep, &errors);
   5820 		}
   5821 		else if (!strcmp(cep->name, "mask"))
   5822 		{
   5823 			has_mask = 1;
   5824 			test_match_block(conf, cep, &errors);
   5825 		}
   5826 		else
   5827 		{
   5828 			config_error_unknown(cep->file->filename, cep->line_number,
   5829 				"allow channel", cep->name);
   5830 			errors++;
   5831 		}
   5832 	}
   5833 	if (has_mask && has_match)
   5834 	{
   5835 		config_error("%s:%d: You cannot have both ::mask and ::match. "
   5836 		             "You should only use %s::match.",
   5837 		             ce->file->filename, ce->line_number, ce->name);
   5838 		errors++;
   5839 	}
   5840 	if (!has_channel)
   5841 	{
   5842 		config_error_missing(ce->file->filename, ce->line_number,
   5843 			"allow channel::channel");
   5844 		errors++;
   5845 	}
   5846 	return errors;
   5847 }
   5848 
   5849 int _conf_except(ConfigFile *conf, ConfigEntry *ce)
   5850 {
   5851 	Hook *h;
   5852 	int value;
   5853 
   5854 	for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next)
   5855 	{
   5856 		value = (*(h->func.intfunc))(conf,ce,CONFIG_EXCEPT);
   5857 		if (value == 1)
   5858 			break;
   5859 	}
   5860 
   5861 	return 1;
   5862 }
   5863 
   5864 int _test_except(ConfigFile *conf, ConfigEntry *ce)
   5865 {
   5866 	int errors = 0;
   5867 	Hook *h;
   5868 	int used = 0;
   5869 
   5870 	if (!ce->value)
   5871 	{
   5872 		config_error("%s:%i: except without type",
   5873 			ce->file->filename, ce->line_number);
   5874 		return 1;
   5875 	}
   5876 
   5877 	if (!strcmp(ce->value, "tkl"))
   5878 	{
   5879 		config_warn("%s:%i: except tkl { } is now called except ban { }. "
   5880 		            "Simply rename the block from 'except tkl' to 'except ban' "
   5881 		            "to get rid of this warning.",
   5882 		            ce->file->filename, ce->line_number);
   5883 		safe_strdup(ce->value, "ban"); /* awww */
   5884 	}
   5885 
   5886 	for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next)
   5887 	{
   5888 		int value, errs = 0;
   5889 		if (h->owner && !(h->owner->flags & MODFLAG_TESTING)
   5890 		    && !(h->owner->options & MOD_OPT_PERM))
   5891 			continue;
   5892 		value = (*(h->func.intfunc))(conf,ce,CONFIG_EXCEPT,&errs);
   5893 		if (value == 2)
   5894 			used = 1;
   5895 		if (value == 1)
   5896 		{
   5897 			used = 1;
   5898 			break;
   5899 		}
   5900 		if (value == -1)
   5901 		{
   5902 			used = 1;
   5903 			errors += errs;
   5904 			break;
   5905 		}
   5906 		if (value == -2)
   5907 		{
   5908 			used = 1;
   5909 			errors += errs;
   5910 		}
   5911 	}
   5912 
   5913 	if (!used)
   5914 	{
   5915 		config_error("%s:%i: unknown except type %s",
   5916 			ce->file->filename, ce->line_number,
   5917 			ce->value);
   5918 		return 1;
   5919 	}
   5920 
   5921 	return errors;
   5922 }
   5923 
   5924 /*
   5925  * vhost {} block parser
   5926 */
   5927 int	_conf_vhost(ConfigFile *conf, ConfigEntry *ce)
   5928 {
   5929 	ConfigItem_vhost *vhost;
   5930 	ConfigEntry *cep, *cepp;
   5931 	vhost = safe_alloc(sizeof(ConfigItem_vhost));
   5932 	vhost->match = safe_alloc(sizeof(SecurityGroup));
   5933 
   5934 	for (cep = ce->items; cep; cep = cep->next)
   5935 	{
   5936 		if (!strcmp(cep->name, "vhost"))
   5937 		{
   5938 			char *user, *host;
   5939 			user = strtok(cep->value, "@");
   5940 			host = strtok(NULL, "");
   5941 			if (!host)
   5942 				safe_strdup(vhost->virthost, user);
   5943 			else
   5944 			{
   5945 				safe_strdup(vhost->virtuser, user);
   5946 				safe_strdup(vhost->virthost, host);
   5947 			}
   5948 		}
   5949 		else if (!strcmp(cep->name, "login"))
   5950 			safe_strdup(vhost->login, cep->value);
   5951 		else if (!strcmp(cep->name, "password"))
   5952 			vhost->auth = AuthBlockToAuthConfig(cep);
   5953 		else if (!strcmp(cep->name, "match") || !strcmp(cep->name, "mask"))
   5954 		{
   5955 			conf_match_block(conf, cep, &vhost->match);
   5956 		}
   5957 		else if (!strcmp(cep->name, "swhois"))
   5958 		{
   5959 			SWhois *s;
   5960 			if (cep->items)
   5961 			{
   5962 				for (cepp = cep->items; cepp; cepp = cepp->next)
   5963 				{
   5964 					s = safe_alloc(sizeof(SWhois));
   5965 					safe_strdup(s->line, cepp->name);
   5966 					safe_strdup(s->setby, "vhost");
   5967 					AddListItem(s, vhost->swhois);
   5968 				}
   5969 			} else
   5970 			if (cep->value)
   5971 			{
   5972 				s = safe_alloc(sizeof(SWhois));
   5973 				safe_strdup(s->line, cep->value);
   5974 				safe_strdup(s->setby, "vhost");
   5975 				AddListItem(s, vhost->swhois);
   5976 			}
   5977 		}
   5978 	}
   5979 	AddListItem(vhost, conf_vhost);
   5980 	return 1;
   5981 }
   5982 
   5983 int	_test_vhost(ConfigFile *conf, ConfigEntry *ce)
   5984 {
   5985 	int errors = 0;
   5986 	ConfigEntry *cep;
   5987 	char has_vhost = 0, has_login = 0, has_password = 0, has_mask = 0, has_match = 0;
   5988 
   5989 	for (cep = ce->items; cep; cep = cep->next)
   5990 	{
   5991 		if (!strcmp(cep->name, "vhost"))
   5992 		{
   5993 			char *at, *tmp, *host;
   5994 			if (has_vhost)
   5995 			{
   5996 				config_warn_duplicate(cep->file->filename,
   5997 					cep->line_number, "vhost::vhost");
   5998 				continue;
   5999 			}
   6000 			has_vhost = 1;
   6001 			if (!cep->value)
   6002 			{
   6003 				config_error_empty(cep->file->filename,
   6004 					cep->line_number, "vhost", "vhost");
   6005 				errors++;
   6006 				continue;
   6007 			}
   6008 			if (!valid_vhost(cep->value))
   6009 			{
   6010 				config_error("%s:%i: oper::vhost contains illegal characters or is too long: '%s'",
   6011 					     cep->file->filename, cep->line_number, cep->value);
   6012 				errors++;
   6013 			}
   6014 		}
   6015 		else if (!strcmp(cep->name, "login"))
   6016 		{
   6017 			if (has_login)
   6018 			{
   6019 				config_warn_duplicate(cep->file->filename,
   6020 					cep->line_number, "vhost::login");
   6021 			}
   6022 			has_login = 1;
   6023 			if (!cep->value)
   6024 			{
   6025 				config_error_empty(cep->file->filename,
   6026 					cep->line_number, "vhost", "login");
   6027 				errors++;
   6028 				continue;
   6029 			}
   6030 		}
   6031 		else if (!strcmp(cep->name, "password"))
   6032 		{
   6033 			if (has_password)
   6034 			{
   6035 				config_warn_duplicate(cep->file->filename,
   6036 					cep->line_number, "vhost::password");
   6037 			}
   6038 			has_password = 1;
   6039 			if (!cep->value)
   6040 			{
   6041 				config_error_empty(cep->file->filename,
   6042 					cep->line_number, "vhost", "password");
   6043 				errors++;
   6044 				continue;
   6045 			}
   6046 			if (Auth_CheckError(cep) < 0)
   6047 				errors++;
   6048 		}
   6049 		else if (!strcmp(cep->name, "mask"))
   6050 		{
   6051 			has_mask = 1;
   6052 			test_match_block(conf, cep, &errors);
   6053 		}
   6054 		else if (!strcmp(cep->name, "match"))
   6055 		{
   6056 			has_match = 1;
   6057 			test_match_block(conf, cep, &errors);
   6058 		}
   6059 		else if (!strcmp(cep->name, "swhois"))
   6060 		{
   6061 			/* multiple is ok */
   6062 		}
   6063 		else
   6064 		{
   6065 			config_error_unknown(cep->file->filename, cep->line_number,
   6066 				"vhost", cep->name);
   6067 			errors++;
   6068 		}
   6069 	}
   6070 	if (!has_vhost)
   6071 	{
   6072 		config_error_missing(ce->file->filename, ce->line_number,
   6073 			"vhost::vhost");
   6074 		errors++;
   6075 	}
   6076 	if (!has_login)
   6077 	{
   6078 		config_error_missing(ce->file->filename, ce->line_number,
   6079 			"vhost::login");
   6080 		errors++;
   6081 
   6082 	}
   6083 	if (!has_password)
   6084 	{
   6085 		config_error_missing(ce->file->filename, ce->line_number,
   6086 			"vhost::password");
   6087 		errors++;
   6088 	}
   6089 	if (!has_mask && !has_match)
   6090 	{
   6091 		config_error_missing(ce->file->filename, ce->line_number,
   6092 			"vhost::match");
   6093 		errors++;
   6094 	}
   6095 	if (has_mask && has_match)
   6096 	{
   6097 		config_error("%s:%d: You cannot have both ::mask and ::match. "
   6098 		             "You should only use %s::match.",
   6099 		             ce->file->filename, ce->line_number, ce->name);
   6100 		errors++;
   6101 	}
   6102 	return errors;
   6103 }
   6104 
   6105 int	_test_sni(ConfigFile *conf, ConfigEntry *ce)
   6106 {
   6107 	int errors = 0;
   6108 	ConfigEntry *cep;
   6109 
   6110 	if (!ce->value)
   6111 	{
   6112 		config_error("%s:%i: sni block needs a name, eg: sni irc.xyz.com {",
   6113 			ce->file->filename, ce->line_number);
   6114 		errors++;
   6115 	}
   6116 
   6117 	for (cep = ce->items; cep; cep = cep->next)
   6118 	{
   6119 		if (!strcmp(cep->name, "ssl-options") || !strcmp(cep->name, "tls-options"))
   6120 		{
   6121 			test_tlsblock(conf, cep, &errors);
   6122 		} else
   6123 		{
   6124 			config_error_unknown(cep->file->filename, cep->line_number,
   6125 				"sni", cep->name);
   6126 			errors++;
   6127 			continue;
   6128 		}
   6129 	}
   6130 
   6131 	return errors;
   6132 }
   6133 
   6134 int	_conf_sni(ConfigFile *conf, ConfigEntry *ce)
   6135 {
   6136 	ConfigEntry *cep;
   6137 	ConfigEntry *tlsconfig = NULL;
   6138 	char *name;
   6139 	ConfigItem_sni *sni = NULL;
   6140 
   6141 	name = ce->value;
   6142 	if (!name)
   6143 		return 0;
   6144 
   6145 	for (cep = ce->items; cep; cep = cep->next)
   6146 	{
   6147 		if (!strcmp(cep->name, "ssl-options") || !strcmp(cep->name, "tls-options"))
   6148 		{
   6149 			tlsconfig = cep;
   6150 		}
   6151 	}
   6152 
   6153 	if (!tlsconfig)
   6154 		return 0;
   6155 
   6156 	sni = safe_alloc(sizeof(ConfigItem_listen));
   6157 	safe_strdup(sni->name, name);
   6158 	sni->tls_options = safe_alloc(sizeof(TLSOptions));
   6159 	conf_tlsblock(conf, tlsconfig, sni->tls_options);
   6160 	sni->ssl_ctx = init_ctx(sni->tls_options, 1);
   6161 	AddListItem(sni, conf_sni);
   6162 
   6163 	return 1;
   6164 }
   6165 
   6166 int     _conf_help(ConfigFile *conf, ConfigEntry *ce)
   6167 {
   6168 	ConfigEntry *cep;
   6169 	ConfigItem_help *ca;
   6170 	MOTDLine *last = NULL, *temp;
   6171 	ca = safe_alloc(sizeof(ConfigItem_help));
   6172 
   6173 	if (!ce->value)
   6174 		ca->command = NULL;
   6175 	else
   6176 		safe_strdup(ca->command, ce->value);
   6177 
   6178 	for (cep = ce->items; cep; cep = cep->next)
   6179 	{
   6180 		temp = safe_alloc(sizeof(MOTDLine));
   6181 		safe_strdup(temp->line, cep->name);
   6182 		temp->next = NULL;
   6183 		if (!last)
   6184 			ca->text = temp;
   6185 		else
   6186 			last->next = temp;
   6187 		last = temp;
   6188 	}
   6189 	AddListItem(ca, conf_help);
   6190 	return 1;
   6191 
   6192 }
   6193 
   6194 int _test_help(ConfigFile *conf, ConfigEntry *ce) {
   6195 	int errors = 0;
   6196 	ConfigEntry *cep;
   6197 	if (!ce->items)
   6198 	{
   6199 		config_error("%s:%i: empty help block",
   6200 			ce->file->filename, ce->line_number);
   6201 		return 1;
   6202 	}
   6203 	for (cep = ce->items; cep; cep = cep->next)
   6204 	{
   6205 		if (strlen(cep->name) > 500)
   6206 		{
   6207 			config_error("%s:%i: oversized help item",
   6208 				ce->file->filename, ce->line_number);
   6209 			errors++;
   6210 			continue;
   6211 		}
   6212 	}
   6213 	return errors;
   6214 }
   6215 
   6216 int	_conf_link(ConfigFile *conf, ConfigEntry *ce)
   6217 {
   6218 	ConfigEntry *cep, *cepp, *ceppp;
   6219 	ConfigItem_link *link = NULL;
   6220 
   6221 	link = safe_alloc(sizeof(ConfigItem_link));
   6222 	safe_strdup(link->servername, ce->value);
   6223 
   6224 	for (cep = ce->items; cep; cep = cep->next)
   6225 	{
   6226 		if (!strcmp(cep->name, "incoming"))
   6227 		{
   6228 			for (cepp = cep->items; cepp; cepp = cepp->next)
   6229 			{
   6230 				if (!strcmp(cepp->name, "match") || !strcmp(cepp->name, "mask"))
   6231 				{
   6232 					conf_match_block(conf, cepp, &link->incoming.match);
   6233 				}
   6234 			}
   6235 		}
   6236 		else if (!strcmp(cep->name, "outgoing"))
   6237 		{
   6238 			for (cepp = cep->items; cepp; cepp = cepp->next)
   6239 			{
   6240 				if (!strcmp(cepp->name, "bind-ip"))
   6241 					safe_strdup(link->outgoing.bind_ip, cepp->value);
   6242 				else if (!strcmp(cepp->name, "file"))
   6243 					safe_strdup(link->outgoing.file, cepp->value);
   6244 				else if (!strcmp(cepp->name, "hostname"))
   6245 					safe_strdup(link->outgoing.hostname, cepp->value);
   6246 				else if (!strcmp(cepp->name, "port"))
   6247 					link->outgoing.port = atoi(cepp->value);
   6248 				else if (!strcmp(cepp->name, "options"))
   6249 				{
   6250 					link->outgoing.options = 0;
   6251 					for (ceppp = cepp->items; ceppp; ceppp = ceppp->next)
   6252 					{
   6253 						long v;
   6254 						if ((v = nv_find_by_name(_LinkFlags, ceppp->name)))
   6255 							link->outgoing.options |= v;
   6256 					}
   6257 				}
   6258 				else if (!strcmp(cepp->name, "ssl-options") || !strcmp(cepp->name, "tls-options"))
   6259 				{
   6260 					link->tls_options = safe_alloc(sizeof(TLSOptions));
   6261 					conf_tlsblock(conf, cepp, link->tls_options);
   6262 					link->ssl_ctx = init_ctx(link->tls_options, 0);
   6263 				}
   6264 			}
   6265 		}
   6266 		else if (!strcmp(cep->name, "password"))
   6267 			link->auth = AuthBlockToAuthConfig(cep);
   6268 		else if (!strcmp(cep->name, "hub"))
   6269 			safe_strdup(link->hub, cep->value);
   6270 		else if (!strcmp(cep->name, "leaf"))
   6271 			safe_strdup(link->leaf, cep->value);
   6272 		else if (!strcmp(cep->name, "leaf-depth") || !strcmp(cep->name, "leafdepth"))
   6273 			link->leaf_depth = atoi(cep->value);
   6274 		else if (!strcmp(cep->name, "class"))
   6275 		{
   6276 			link->class = find_class(cep->value);
   6277 			if (!link->class || (link->class->flag.temporary == 1))
   6278 			{
   6279 				config_status("%s:%i: illegal link::class, unknown class '%s' using default of class 'default'",
   6280 					cep->file->filename,
   6281 					cep->line_number,
   6282 					cep->value);
   6283 				link->class = default_class;
   6284 			}
   6285 			link->class->xrefcount++;
   6286 		}
   6287 		else if (!strcmp(cep->name, "verify-certificate"))
   6288 		{
   6289 			link->verify_certificate = config_checkval(cep->value, CFG_YESNO);
   6290 		}
   6291 		else if (!strcmp(cep->name, "options"))
   6292 		{
   6293 			link->options = 0;
   6294 			for (cepp = cep->items; cepp; cepp = cepp->next)
   6295 			{
   6296 				long v;
   6297 				if ((v = nv_find_by_name(_LinkFlags, cepp->name)))
   6298 					link->options |= v;
   6299 			}
   6300 		}
   6301 	}
   6302 
   6303 	/* The default is 'hub *', unless you specify leaf or hub manually. */
   6304 	if (!link->hub && !link->leaf)
   6305 		safe_strdup(link->hub, "*");
   6306 
   6307 	AppendListItem(link, conf_link);
   6308 	return 0;
   6309 }
   6310 
   6311 /** Helper function for erroring on duplicate items.
   6312  */
   6313 int config_detect_duplicate(int *var, ConfigEntry *ce, int *errors)
   6314 {
   6315 	if (*var)
   6316 	{
   6317 		config_error("%s:%d: Duplicate %s directive",
   6318 			ce->file->filename, ce->line_number,
   6319 			ce->name);
   6320 		(*errors)++;
   6321 		return 1;
   6322 	} else {
   6323 		*var = 1;
   6324 	}
   6325 	return 0;
   6326 }
   6327 
   6328 int	_test_link(ConfigFile *conf, ConfigEntry *ce)
   6329 {
   6330 	ConfigEntry *cep, *cepp, *ceppp;
   6331 	int errors = 0;
   6332 
   6333 	int has_incoming = 0, has_incoming_mask = 0, has_incoming_match = 0, has_outgoing = 0, has_outgoing_file = 0;
   6334 	int has_outgoing_bind_ip = 0, has_outgoing_hostname = 0, has_outgoing_port = 0;
   6335 	int has_outgoing_options = 0, has_hub = 0, has_leaf = 0, has_leaf_depth = 0;
   6336 	int has_password = 0, has_class = 0, has_options = 0;
   6337 
   6338 	if (!ce->value)
   6339 	{
   6340 		config_error("%s:%i: link without servername. Expected: link servername { ... }",
   6341 			ce->file->filename, ce->line_number);
   6342 		return 1;
   6343 
   6344 	}
   6345 
   6346 	if (!strchr(ce->value, '.'))
   6347 	{
   6348 		config_error("%s:%i: link: bogus server name. Expected: link servername { ... }",
   6349 			ce->file->filename, ce->line_number);
   6350 		return 1;
   6351 	}
   6352 
   6353 	for (cep = ce->items; cep; cep = cep->next)
   6354 	{
   6355 		if (!strcmp(cep->name, "incoming"))
   6356 		{
   6357 			config_detect_duplicate(&has_incoming, cep, &errors);
   6358 			for (cepp = cep->items; cepp; cepp = cepp->next)
   6359 			{
   6360 				if (!strcmp(cepp->name, "match"))
   6361 				{
   6362 					if (cepp->value || cepp->items)
   6363 					{
   6364 						has_incoming_match = 1;
   6365 						test_match_block(conf, cepp, &errors);
   6366 					} else
   6367 					if (config_is_blankorempty(cepp, "link::incoming"))
   6368 					{
   6369 						errors++;
   6370 						continue;
   6371 					}
   6372 				} else
   6373 				if (!strcmp(cepp->name, "mask"))
   6374 				{
   6375 					if (cepp->value || cepp->items)
   6376 					{
   6377 						has_incoming_mask = 1;
   6378 						test_match_block(conf, cepp, &errors);
   6379 					} else
   6380 					if (config_is_blankorempty(cepp, "link::incoming"))
   6381 					{
   6382 						errors++;
   6383 						continue;
   6384 					}
   6385 				}
   6386 			}
   6387 		}
   6388 		else if (!strcmp(cep->name, "outgoing"))
   6389 		{
   6390 			config_detect_duplicate(&has_outgoing, cep, &errors);
   6391 			for (cepp = cep->items; cepp; cepp = cepp->next)
   6392 			{
   6393 				if (!strcmp(cepp->name, "bind-ip"))
   6394 				{
   6395 					if (config_is_blankorempty(cepp, "link::outgoing"))
   6396 					{
   6397 						errors++;
   6398 						continue;
   6399 					}
   6400 					config_detect_duplicate(&has_outgoing_bind_ip, cepp, &errors);
   6401 					// todo: ipv4 vs ipv6
   6402 				}
   6403 				else if (!strcmp(cepp->name, "file"))
   6404 				{
   6405 					if (config_is_blankorempty(cepp, "link::outgoing"))
   6406 					{
   6407 						errors++;
   6408 						continue;
   6409 					}
   6410 					config_detect_duplicate(&has_outgoing_file, cepp, &errors);
   6411 				}
   6412 				else if (!strcmp(cepp->name, "hostname"))
   6413 				{
   6414 					if (config_is_blankorempty(cepp, "link::outgoing"))
   6415 					{
   6416 						errors++;
   6417 						continue;
   6418 					}
   6419 					config_detect_duplicate(&has_outgoing_hostname, cepp, &errors);
   6420 					if (strchr(cepp->value, '*') || strchr(cepp->value, '?'))
   6421 					{
   6422 						config_error("%s:%i: hostname in link::outgoing(!) cannot contain wildcards",
   6423 							cepp->file->filename, cepp->line_number);
   6424 						errors++;
   6425 					}
   6426 				}
   6427 				else if (!strcmp(cepp->name, "port"))
   6428 				{
   6429 					if (config_is_blankorempty(cepp, "link::outgoing"))
   6430 					{
   6431 						errors++;
   6432 						continue;
   6433 					}
   6434 					config_detect_duplicate(&has_outgoing_port, cepp, &errors);
   6435 				}
   6436 				else if (!strcmp(cepp->name, "options"))
   6437 				{
   6438 					config_detect_duplicate(&has_outgoing_options, cepp, &errors);
   6439 					for (ceppp = cepp->items; ceppp; ceppp = ceppp->next)
   6440 					{
   6441 						if (!strcmp(ceppp->name, "autoconnect"))
   6442 							;
   6443 						else if (!strcmp(ceppp->name, "ssl") || !strcmp(ceppp->name, "tls"))
   6444 							;
   6445 						else if (!strcmp(ceppp->name, "insecure"))
   6446 							;
   6447 						else
   6448 						{
   6449 							config_error_unknownopt(ceppp->file->filename,
   6450 								ceppp->line_number, "link::outgoing", ceppp->name);
   6451 							errors++;
   6452 						}
   6453 					}
   6454 				}
   6455 				else if (!strcmp(cepp->name, "ssl-options") || !strcmp(cepp->name, "tls-options"))
   6456 				{
   6457 					test_tlsblock(conf, cepp, &errors);
   6458 				}
   6459 				else
   6460 				{
   6461 					config_error("%s:%d: Unknown directive '%s'",
   6462 					             cepp->file->filename, cepp->line_number,
   6463 					             config_var(cepp));
   6464 					errors++;
   6465 				}
   6466 			}
   6467 		}
   6468 		else if (!strcmp(cep->name, "password"))
   6469 		{
   6470 			config_detect_duplicate(&has_password, cep, &errors);
   6471 			if (Auth_CheckError(cep) < 0)
   6472 			{
   6473 				errors++;
   6474 			} else {
   6475 				AuthConfig *auth = AuthBlockToAuthConfig(cep);
   6476 				/* hm. would be nicer if handled @auth-system I think. ah well.. */
   6477 				if ((auth->type != AUTHTYPE_PLAINTEXT) && (auth->type != AUTHTYPE_TLS_CLIENTCERT) &&
   6478 				    (auth->type != AUTHTYPE_TLS_CLIENTCERTFP) && (auth->type != AUTHTYPE_SPKIFP))
   6479 				{
   6480 					config_error("%s:%i: password in link block should be plaintext OR should be the "
   6481 					             "certificate or SPKI fingerprint of the remote link (=better)",
   6482 					             cep->file->filename, cep->line_number);
   6483 					errors++;
   6484 				}
   6485 				Auth_FreeAuthConfig(auth);
   6486 			}
   6487 		}
   6488 		else if (!strcmp(cep->name, "hub"))
   6489 		{
   6490 			if (config_is_blankorempty(cep, "link"))
   6491 			{
   6492 				errors++;
   6493 				continue;
   6494 			}
   6495 			config_detect_duplicate(&has_hub, cep, &errors);
   6496 		}
   6497 		else if (!strcmp(cep->name, "leaf"))
   6498 		{
   6499 			if (config_is_blankorempty(cep, "link"))
   6500 			{
   6501 				errors++;
   6502 				continue;
   6503 			}
   6504 			config_detect_duplicate(&has_leaf, cep, &errors);
   6505 		}
   6506 		else if (!strcmp(cep->name, "leaf-depth") || !strcmp(cep->name, "leafdepth"))
   6507 		{
   6508 			if (config_is_blankorempty(cep, "link"))
   6509 			{
   6510 				errors++;
   6511 				continue;
   6512 			}
   6513 			config_detect_duplicate(&has_leaf_depth, cep, &errors);
   6514 		}
   6515 		else if (!strcmp(cep->name, "class"))
   6516 		{
   6517 			if (config_is_blankorempty(cep, "link"))
   6518 			{
   6519 				errors++;
   6520 				continue;
   6521 			}
   6522 			config_detect_duplicate(&has_class, cep, &errors);
   6523 		}
   6524 		else if (!strcmp(cep->name, "ciphers"))
   6525 		{
   6526 			config_error("%s:%d: link::ciphers has been moved to link::outgoing::ssl-options::ciphers, "
   6527 			             "see https://www.unrealircd.org/docs/FAQ#link::ciphers_no_longer_works",
   6528 			             cep->file->filename, cep->line_number);
   6529 			errors++;
   6530 		}
   6531 		else if (!strcmp(cep->name, "verify-certificate"))
   6532 		{
   6533 			if (config_is_blankorempty(cep, "link"))
   6534 			{
   6535 				errors++;
   6536 				continue;
   6537 			}
   6538 		}
   6539 		else if (!strcmp(cep->name, "options"))
   6540 		{
   6541 			config_detect_duplicate(&has_options, cep, &errors);
   6542 			for (cepp = cep->items; cepp; cepp = cepp->next)
   6543 			{
   6544 				if (!strcmp(cepp->name, "quarantine"))
   6545 					;
   6546 				else
   6547 				{
   6548 					config_error("%s:%d: link::options only has one possible option ('quarantine', rarely used). "
   6549 					             "Option '%s' is unrecognized. "
   6550 					             "Perhaps you meant to set an outgoing option in link::outgoing::options instead?",
   6551 					             cepp->file->filename, cepp->line_number, cepp->name);
   6552 					errors++;
   6553 				}
   6554 			}
   6555 		}
   6556 		else
   6557 		{
   6558 			config_error_unknown(cep->file->filename,
   6559 			    cep->line_number, "link", cep->name);
   6560 			errors++;
   6561 			continue;
   6562 		}
   6563 	}
   6564 
   6565 	if (!has_incoming && !has_outgoing)
   6566 	{
   6567 		config_error("%s:%d: link block needs at least an incoming or outgoing section.",
   6568 			ce->file->filename, ce->line_number);
   6569 		errors++;
   6570 	}
   6571 
   6572 	if (has_incoming)
   6573 	{
   6574 		/* If we have an incoming sub-block then we need at least 'mask' and 'password' */
   6575 		if (!has_incoming_mask && !has_incoming_match)
   6576 		{
   6577 			config_error_missing(ce->file->filename, ce->line_number, "link::incoming::match");
   6578 			errors++;
   6579 		}
   6580 		if (has_incoming_mask && has_incoming_match)
   6581 		{
   6582 			config_error("%s:%d: You cannot have both link::incoming::mask and link::incoming::match. "
   6583 				     "You should only use link::incoming::match.",
   6584 				     ce->file->filename, ce->line_number);
   6585 			errors++;
   6586 		}
   6587 	}
   6588 
   6589 	if (has_outgoing)
   6590 	{
   6591 		/* If we have an outgoing sub-block then we need at least a hostname and port or a file */
   6592 		if (!has_outgoing_file)
   6593 		{
   6594 			if (!has_outgoing_hostname)
   6595 			{
   6596 				config_error_missing(ce->file->filename, ce->line_number, "link::outgoing::hostname");
   6597 				errors++;
   6598 			}
   6599 			if (!has_outgoing_port)
   6600 			{
   6601 				config_error_missing(ce->file->filename, ce->line_number, "link::outgoing::port");
   6602 				errors++;
   6603 			}
   6604 		}
   6605 		else if (has_outgoing_file && (has_outgoing_hostname || has_outgoing_port))
   6606 		{
   6607 			config_error("%s:%d: link block should either have a 'file' (for *NIX domain socket), "
   6608 			             "OR have a 'hostname' and 'port' (for IPv4/IPv6). You cannot combine both in one link block.",
   6609 			             ce->file->filename, ce->line_number);
   6610 			errors++;
   6611 		}
   6612 	}
   6613 
   6614 	/* The only other generic options that are required are 'class' and 'password' */
   6615 	if (!has_password)
   6616 	{
   6617 		config_error_missing(ce->file->filename, ce->line_number, "link::password");
   6618 		errors++;
   6619 	}
   6620 	if (!has_class)
   6621 	{
   6622 		config_error_missing(ce->file->filename, ce->line_number,
   6623 			"link::class");
   6624 		errors++;
   6625 	}
   6626 
   6627 	return errors;
   6628 }
   6629 
   6630 int     _conf_ban(ConfigFile *conf, ConfigEntry *ce)
   6631 {
   6632 	ConfigEntry *cep;
   6633 	ConfigItem_ban *ca;
   6634 	Hook *h;
   6635 
   6636 	ca = safe_alloc(sizeof(ConfigItem_ban));
   6637 	if (!strcmp(ce->value, "realname"))
   6638 		ca->flag.type = CONF_BAN_REALNAME;
   6639 	else if (!strcmp(ce->value, "server"))
   6640 		ca->flag.type = CONF_BAN_SERVER;
   6641 	else if (!strcmp(ce->value, "version"))
   6642 	{
   6643 		ca->flag.type = CONF_BAN_VERSION;
   6644 		tempiConf.use_ban_version = 1; /* enable CTCP VERSION on connect */
   6645 	}
   6646 	else {
   6647 		int value;
   6648 		safe_free(ca); /* ca isn't used, modules have their own list. */
   6649 		for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next)
   6650 		{
   6651 			value = (*(h->func.intfunc))(conf,ce,CONFIG_BAN);
   6652 			if (value == 1)
   6653 				break;
   6654 		}
   6655 		return 0;
   6656 	}
   6657 	for (cep = ce->items; cep; cep = cep->next)
   6658 	{
   6659 		if (!strcmp(cep->name, "mask"))
   6660 		{
   6661 			safe_strdup(ca->mask, cep->value);
   6662 		}
   6663 		else if (!strcmp(cep->name, "reason"))
   6664 			safe_strdup(ca->reason, cep->value);
   6665 		else if (!strcmp(cep->name, "action"))
   6666 			ca->action = banact_stringtoval(cep->value);
   6667 	}
   6668 	AddListItem(ca, conf_ban);
   6669 	return 0;
   6670 }
   6671 
   6672 int     _test_ban(ConfigFile *conf, ConfigEntry *ce)
   6673 {
   6674 	ConfigEntry *cep;
   6675 	int	    errors = 0;
   6676 	Hook *h;
   6677 	char type = 0;
   6678 	char has_mask = 0, has_action = 0, has_reason = 0;
   6679 
   6680 	if (!ce->value)
   6681 	{
   6682 		config_error("%s:%i: ban without type",
   6683 			ce->file->filename, ce->line_number);
   6684 		return 1;
   6685 	}
   6686 	else if (!strcmp(ce->value, "server"))
   6687 	{}
   6688 	else if (!strcmp(ce->value, "realname"))
   6689 	{}
   6690 	else if (!strcmp(ce->value, "version"))
   6691 		type = 'v';
   6692 	else
   6693 	{
   6694 		int used = 0;
   6695 		for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next)
   6696 		{
   6697 			int value, errs = 0;
   6698 			if (h->owner && !(h->owner->flags & MODFLAG_TESTING)
   6699 			    && !(h->owner->options & MOD_OPT_PERM))
   6700 				continue;
   6701 			value = (*(h->func.intfunc))(conf,ce,CONFIG_BAN, &errs);
   6702 			if (value == 2)
   6703 				used = 1;
   6704 			if (value == 1)
   6705 			{
   6706 				used = 1;
   6707 				break;
   6708 			}
   6709 			if (value == -1)
   6710 			{
   6711 				used = 1;
   6712 				errors += errs;
   6713 				break;
   6714 			}
   6715 			if (value == -2)
   6716 			{
   6717 				used = 1;
   6718 				errors += errs;
   6719 			}
   6720 		}
   6721 		if (!used) {
   6722 			config_error("%s:%i: unknown ban type %s",
   6723 				ce->file->filename, ce->line_number,
   6724 				ce->value);
   6725 			return 1;
   6726 		}
   6727 		return errors;
   6728 	}
   6729 
   6730 	for (cep = ce->items; cep; cep = cep->next)
   6731 	{
   6732 		if (config_is_blankorempty(cep, "ban"))
   6733 		{
   6734 			errors++;
   6735 			continue;
   6736 		}
   6737 		if (!strcmp(cep->name, "mask"))
   6738 		{
   6739 			if (has_mask)
   6740 			{
   6741 				config_warn_duplicate(cep->file->filename,
   6742 					cep->line_number, "ban::mask");
   6743 				continue;
   6744 			}
   6745 			has_mask = 1;
   6746 		}
   6747 		else if (!strcmp(cep->name, "reason"))
   6748 		{
   6749 			if (has_reason)
   6750 			{
   6751 				config_warn_duplicate(cep->file->filename,
   6752 					cep->line_number, "ban::reason");
   6753 				continue;
   6754 			}
   6755 			has_reason = 1;
   6756 		}
   6757 		else if (!strcmp(cep->name, "action"))
   6758 		{
   6759 			if (has_action)
   6760 			{
   6761 				config_warn_duplicate(cep->file->filename,
   6762 					cep->line_number, "ban::action");
   6763 			}
   6764 			has_action = 1;
   6765 			if (!banact_stringtoval(cep->value))
   6766 			{
   6767 				config_error("%s:%i: ban::action has unknown action type '%s'",
   6768 					cep->file->filename, cep->line_number,
   6769 					cep->value);
   6770 				errors++;
   6771 			}
   6772 		}
   6773 	}
   6774 
   6775 	if (!has_mask)
   6776 	{
   6777 		config_error_missing(ce->file->filename, ce->line_number,
   6778 			"ban::mask");
   6779 		errors++;
   6780 	}
   6781 	if (!has_reason)
   6782 	{
   6783 		config_error_missing(ce->file->filename, ce->line_number,
   6784 			"ban::reason");
   6785 		errors++;
   6786 	}
   6787 	if (has_action && type != 'v')
   6788 	{
   6789 		config_error("%s:%d: ban::action specified even though type is not 'version'",
   6790 			ce->file->filename, ce->line_number);
   6791 		errors++;
   6792 	}
   6793 	return errors;
   6794 }
   6795 
   6796 int _conf_require(ConfigFile *conf, ConfigEntry *ce)
   6797 {
   6798 	ConfigEntry *cep;
   6799 	Hook *h;
   6800 	char *usermask = NULL;
   6801 	char *hostmask = NULL;
   6802 	char *reason = NULL;
   6803 
   6804 	if (strcmp(ce->value, "authentication") && strcmp(ce->value, "sasl"))
   6805 	{
   6806 		/* Some other block... run modules... */
   6807 		int value;
   6808 		for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next)
   6809 		{
   6810 			value = (*(h->func.intfunc))(conf,ce,CONFIG_REQUIRE);
   6811 			if (value == 1)
   6812 				break;
   6813 		}
   6814 		return 0;
   6815 	}
   6816 
   6817 	for (cep = ce->items; cep; cep = cep->next)
   6818 	{
   6819 		if (!strcmp(cep->name, "mask"))
   6820 		{
   6821 			char buf[512], *p;
   6822 			strlcpy(buf, cep->value, sizeof(buf));
   6823 			p = strchr(buf, '@');
   6824 			if (p)
   6825 			{
   6826 				*p++ = '\0';
   6827 				safe_strdup(usermask, buf);
   6828 				safe_strdup(hostmask, p);
   6829 			} else {
   6830 				safe_strdup(hostmask, cep->value);
   6831 			}
   6832 		}
   6833 		else if (!strcmp(cep->name, "reason"))
   6834 			safe_strdup(reason, cep->value);
   6835 	}
   6836 
   6837 	if (!usermask)
   6838 		safe_strdup(usermask, "*");
   6839 
   6840 	if (!reason)
   6841 		safe_strdup(reason, "-");
   6842 
   6843 	tkl_add_serverban(TKL_KILL, usermask, hostmask, reason, "-config-", 0, TStime(), 1, TKL_FLAG_CONFIG);
   6844 	safe_free(usermask);
   6845 	safe_free(hostmask);
   6846 	safe_free(reason);
   6847 	return 0;
   6848 }
   6849 
   6850 int _test_require(ConfigFile *conf, ConfigEntry *ce)
   6851 {
   6852 	ConfigEntry *cep;
   6853 	int errors = 0;
   6854 	Hook *h;
   6855 	char has_mask = 0, has_reason = 0;
   6856 
   6857 	if (!ce->value)
   6858 	{
   6859 		config_error("%s:%i: require without type, did you mean 'require authentication'?",
   6860 			ce->file->filename, ce->line_number);
   6861 		return 1;
   6862 	}
   6863 	if (!strcmp(ce->value, "authentication"))
   6864 	{}
   6865 	else if (!strcmp(ce->value, "sasl"))
   6866 	{
   6867 		config_warn("%s:%i: the 'require sasl' block is now called 'require authentication'",
   6868 		            ce->file->filename, ce->line_number);
   6869 	}
   6870 	else
   6871 	{
   6872 		int used = 0;
   6873 		for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next)
   6874 		{
   6875 			int value, errs = 0;
   6876 			if (h->owner && !(h->owner->flags & MODFLAG_TESTING)
   6877 			    && !(h->owner->options & MOD_OPT_PERM))
   6878 				continue;
   6879 			value = (*(h->func.intfunc))(conf,ce,CONFIG_REQUIRE, &errs);
   6880 			if (value == 2)
   6881 				used = 1;
   6882 			if (value == 1)
   6883 			{
   6884 				used = 1;
   6885 				break;
   6886 			}
   6887 			if (value == -1)
   6888 			{
   6889 				used = 1;
   6890 				errors += errs;
   6891 				break;
   6892 			}
   6893 			if (value == -2)
   6894 			{
   6895 				used = 1;
   6896 				errors += errs;
   6897 			}
   6898 		}
   6899 		if (!used) {
   6900 			config_error("%s:%i: unknown require type '%s'",
   6901 				ce->file->filename, ce->line_number,
   6902 				ce->value);
   6903 			return 1;
   6904 		}
   6905 		return errors;
   6906 	}
   6907 
   6908 	for (cep = ce->items; cep; cep = cep->next)
   6909 	{
   6910 		if (config_is_blankorempty(cep, "require"))
   6911 		{
   6912 			errors++;
   6913 			continue;
   6914 		}
   6915 		if (!strcmp(cep->name, "mask"))
   6916 		{
   6917 			if (has_mask)
   6918 			{
   6919 				config_warn_duplicate(cep->file->filename,
   6920 					cep->line_number, "require::mask");
   6921 				continue;
   6922 			}
   6923 			has_mask = 1;
   6924 		}
   6925 		else if (!strcmp(cep->name, "reason"))
   6926 		{
   6927 			if (has_reason)
   6928 			{
   6929 				config_warn_duplicate(cep->file->filename,
   6930 					cep->line_number, "require::reason");
   6931 				continue;
   6932 			}
   6933 			has_reason = 1;
   6934 		}
   6935 	}
   6936 
   6937 	if (!has_mask)
   6938 	{
   6939 		config_error_missing(ce->file->filename, ce->line_number,
   6940 			"require::mask");
   6941 		errors++;
   6942 	}
   6943 	if (!has_reason)
   6944 	{
   6945 		config_error_missing(ce->file->filename, ce->line_number,
   6946 			"require::reason");
   6947 		errors++;
   6948 	}
   6949 	return errors;
   6950 }
   6951 
   6952 #define CheckDuplicate(cep, name, display) if (settings.has_##name) { config_warn_duplicate((cep)->file->filename, cep->line_number, "set::" display); continue; } else settings.has_##name = 1
   6953 
   6954 void test_tlsblock(ConfigFile *conf, ConfigEntry *cep, int *totalerrors)
   6955 {
   6956 	ConfigEntry *cepp, *ceppp;
   6957 	int errors = 0;
   6958 
   6959 	for (cepp = cep->items; cepp; cepp = cepp->next)
   6960 	{
   6961 		if (!strcmp(cepp->name, "renegotiate-timeout"))
   6962 		{
   6963 		}
   6964 		else if (!strcmp(cepp->name, "renegotiate-bytes"))
   6965 		{
   6966 		}
   6967 		else if (!strcmp(cepp->name, "ciphers") || !strcmp(cepp->name, "server-cipher-list"))
   6968 		{
   6969 			CheckNull(cepp);
   6970 		}
   6971 		else if (!strcmp(cepp->name, "ciphersuites"))
   6972 		{
   6973 			CheckNull(cepp);
   6974 		}
   6975 		else if (!strcmp(cepp->name, "ecdh-curves"))
   6976 		{
   6977 			CheckNull(cepp);
   6978 #ifndef HAS_SSL_CTX_SET1_CURVES_LIST
   6979 			config_error("ecdh-curves specified but your OpenSSL/LibreSSL library does not "
   6980 			             "support setting curves manually by name. Either upgrade to a "
   6981 			             "newer library version or remove the 'ecdh-curves' directive "
   6982 			             "from your configuration file");
   6983 			errors++;
   6984 #endif
   6985 		}
   6986 		else if (!strcmp(cepp->name, "protocols"))
   6987 		{
   6988 			char copy[512], *p, *name;
   6989 			int v = 0;
   6990 			int option;
   6991 			char modifier;
   6992 
   6993 			CheckNull(cepp);
   6994 			strlcpy(copy, cepp->value, sizeof(copy));
   6995 			for (name = strtoken(&p, copy, ","); name; name = strtoken(&p, NULL, ","))
   6996 			{
   6997 				modifier = '\0';
   6998 				option = 0;
   6999 
   7000 				if ((*name == '+') || (*name == '-'))
   7001 				{
   7002 					modifier = *name;
   7003 					name++;
   7004 				}
   7005 
   7006 				if (!strcasecmp(name, "All"))
   7007 					option = TLS_PROTOCOL_ALL;
   7008 				else if (!strcasecmp(name, "TLSv1"))
   7009 					option = TLS_PROTOCOL_TLSV1;
   7010 				else if (!strcasecmp(name, "TLSv1.1"))
   7011 					option = TLS_PROTOCOL_TLSV1_1;
   7012 				else if (!strcasecmp(name, "TLSv1.2"))
   7013 					option = TLS_PROTOCOL_TLSV1_2;
   7014 				else if (!strcasecmp(name, "TLSv1.3"))
   7015 					option = TLS_PROTOCOL_TLSV1_3;
   7016 				else
   7017 				{
   7018 #ifdef SSL_OP_NO_TLSv1_3
   7019 					config_warn("%s:%i: %s: unknown protocol '%s'. "
   7020 								 "Valid protocols are: TLSv1,TLSv1.1,TLSv1.2,TLSv1.3",
   7021 								 cepp->file->filename, cepp->line_number, config_var(cepp), name);
   7022 #else
   7023 					config_warn("%s:%i: %s: unknown protocol '%s'. "
   7024 								 "Valid protocols are: TLSv1,TLSv1.1,TLSv1.2",
   7025 								 cepp->file->filename, cepp->line_number, config_var(cepp), name);
   7026 #endif
   7027 				}
   7028 
   7029 				if (option)
   7030 				{
   7031 					if (modifier == '\0')
   7032 						v = option;
   7033 					else if (modifier == '+')
   7034 						v |= option;
   7035 					else if (modifier == '-')
   7036 						v &= ~option;
   7037 				}
   7038 			}
   7039 			if (v == 0)
   7040 			{
   7041 				config_error("%s:%i: %s: no protocols enabled. Hint: set at least TLSv1.2",
   7042 					cepp->file->filename, cepp->line_number, config_var(cepp));
   7043 				errors++;
   7044 			}
   7045 		}
   7046 		else if (!strcmp(cepp->name, "certificate") ||
   7047 		         !strcmp(cepp->name, "key") ||
   7048 		         !strcmp(cepp->name, "trusted-ca-file"))
   7049 		{
   7050 			char *path;
   7051 			CheckNull(cepp);
   7052 			path = convert_to_absolute_path_duplicate(cepp->value, CONFDIR);
   7053 			if (!file_exists(path))
   7054 			{
   7055 				config_error("%s:%i: %s: could not open '%s': %s",
   7056 					cepp->file->filename, cepp->line_number, config_var(cepp),
   7057 					path, strerror(errno));
   7058 				safe_free(path);
   7059 				errors++;
   7060 			}
   7061 			safe_free(path);
   7062 		}
   7063 		else if (!strcmp(cepp->name, "dh"))
   7064 		{
   7065 			/* Support for this undocumented option was silently dropped in 5.0.0.
   7066 			 * Since 5.0.7 we print a warning about it, since you never know
   7067 			 * someone may still have it configured. -- Syzop
   7068 			 */
   7069 			config_warn("%s:%d: Not reading DH file '%s'. UnrealIRCd does not support old DH(E), we use modern ECDHE/EECDH. "
   7070 			            "Just remove the 'dh' directive from your config file to get rid of this warning.",
   7071 				cepp->file->filename, cepp->line_number,
   7072 				cepp->value ? cepp->value : "");
   7073 		}
   7074 		else if (!strcmp(cepp->name, "outdated-protocols"))
   7075 		{
   7076 			char copy[512], *p, *name;
   7077 			int v = 0;
   7078 			int option;
   7079 			char modifier;
   7080 
   7081 			CheckNull(cepp);
   7082 			strlcpy(copy, cepp->value, sizeof(copy));
   7083 			for (name = strtoken(&p, copy, ","); name; name = strtoken(&p, NULL, ","))
   7084 			{
   7085 				if (!strcasecmp(name, "All"))
   7086 					;
   7087 				else if (!strcasecmp(name, "TLSv1"))
   7088 					;
   7089 				else if (!strcasecmp(name, "TLSv1.1"))
   7090 					;
   7091 				else if (!strcasecmp(name, "TLSv1.2"))
   7092 					;
   7093 				else if (!strcasecmp(name, "TLSv1.3"))
   7094 					;
   7095 				else
   7096 				{
   7097 #ifdef SSL_OP_NO_TLSv1_3
   7098 					config_warn("%s:%i: %s: unknown protocol '%s'. "
   7099 								 "Valid protocols are: TLSv1,TLSv1.1,TLSv1.2,TLSv1.3",
   7100 								 cepp->file->filename, cepp->line_number, config_var(cepp), name);
   7101 #else
   7102 					config_warn("%s:%i: %s: unknown protocol '%s'. "
   7103 								 "Valid protocols are: TLSv1,TLSv1.1,TLSv1.2",
   7104 								 cepp->file->filename, cepp->line_number, config_var(cepp), name);
   7105 #endif
   7106 		                }
   7107 			}
   7108 		}
   7109 		else if (!strcmp(cepp->name, "outdated-ciphers"))
   7110 		{
   7111 			CheckNull(cepp);
   7112 		}
   7113 		else if (!strcmp(cepp->name, "options"))
   7114 		{
   7115 			for (ceppp = cepp->items; ceppp; ceppp = ceppp->next)
   7116 			{
   7117 				if (!nv_find_by_name(_TLSFlags, ceppp->name))
   7118 				{
   7119 					config_error("%s:%i: unknown TLS option '%s'",
   7120 							 ceppp->file->filename,
   7121 							 ceppp->line_number, ceppp->name);
   7122 					errors ++;
   7123 				}
   7124 			}
   7125 		}
   7126 		else if (!strcmp(cepp->name, "sts-policy"))
   7127 		{
   7128 			int has_port = 0;
   7129 			int has_duration = 0;
   7130 
   7131 			for (ceppp = cepp->items; ceppp; ceppp = ceppp->next)
   7132 			{
   7133 				if (!strcmp(ceppp->name, "port"))
   7134 				{
   7135 					int port;
   7136 					CheckNull(ceppp);
   7137 					port = atoi(ceppp->value);
   7138 					if ((port < 1) || (port > 65535))
   7139 					{
   7140 						config_error("%s:%i: invalid port number specified in sts-policy::port (%d)",
   7141 						             ceppp->file->filename, ceppp->line_number, port);
   7142 						errors++;
   7143 					}
   7144 					has_port = 1;
   7145 				}
   7146 				else if (!strcmp(ceppp->name, "duration"))
   7147 				{
   7148 					long duration;
   7149 					CheckNull(ceppp);
   7150 					duration = config_checkval(ceppp->value, CFG_TIME);
   7151 					if (duration < 1)
   7152 					{
   7153 						config_error("%s:%i: invalid duration specified in sts-policy::duration (%ld seconds)",
   7154 						             ceppp->file->filename, ceppp->line_number, duration);
   7155 						errors++;
   7156 					}
   7157 					has_duration = 1;
   7158 				}
   7159 				else if (!strcmp(ceppp->name, "preload"))
   7160 				{
   7161 					CheckNull(ceppp);
   7162 				}
   7163 			}
   7164 			if (!has_port)
   7165 			{
   7166 				config_error("%s:%i: sts-policy block without port",
   7167 				             cepp->file->filename, cepp->line_number);
   7168 				errors++;
   7169 			}
   7170 			if (!has_duration)
   7171 			{
   7172 				config_error("%s:%i: sts-policy block without duration",
   7173 				             cepp->file->filename, cepp->line_number);
   7174 				errors++;
   7175 			}
   7176 		}
   7177 		else
   7178 		{
   7179 			config_error("%s:%i: unknown directive %s",
   7180 				cepp->file->filename, cepp->line_number,
   7181 				config_var(cepp));
   7182 			errors++;
   7183 		}
   7184 	}
   7185 
   7186 	*totalerrors += errors;
   7187 }
   7188 
   7189 void free_tls_options(TLSOptions *tlsoptions)
   7190 {
   7191 	if (!tlsoptions)
   7192 		return;
   7193 
   7194 	safe_free(tlsoptions->certificate_file);
   7195 	safe_free(tlsoptions->key_file);
   7196 	safe_free(tlsoptions->trusted_ca_file);
   7197 	safe_free(tlsoptions->ciphers);
   7198 	safe_free(tlsoptions->ciphersuites);
   7199 	safe_free(tlsoptions->ecdh_curves);
   7200 	safe_free(tlsoptions->outdated_protocols);
   7201 	safe_free(tlsoptions->outdated_ciphers);
   7202 	memset(tlsoptions, 0, sizeof(TLSOptions));
   7203 	safe_free(tlsoptions);
   7204 }
   7205 
   7206 void conf_tlsblock(ConfigFile *conf, ConfigEntry *cep, TLSOptions *tlsoptions)
   7207 {
   7208 	ConfigEntry *cepp, *ceppp;
   7209 	NameValue *ofl;
   7210 
   7211 	/* First, inherit settings from set::options::tls */
   7212 	if (tlsoptions != tempiConf.tls_options)
   7213 	{
   7214 		safe_strdup(tlsoptions->certificate_file, tempiConf.tls_options->certificate_file);
   7215 		safe_strdup(tlsoptions->key_file, tempiConf.tls_options->key_file);
   7216 		safe_strdup(tlsoptions->trusted_ca_file, tempiConf.tls_options->trusted_ca_file);
   7217 		tlsoptions->protocols = tempiConf.tls_options->protocols;
   7218 		safe_strdup(tlsoptions->ciphers, tempiConf.tls_options->ciphers);
   7219 		safe_strdup(tlsoptions->ciphersuites, tempiConf.tls_options->ciphersuites);
   7220 		safe_strdup(tlsoptions->ecdh_curves, tempiConf.tls_options->ecdh_curves);
   7221 		safe_strdup(tlsoptions->outdated_protocols, tempiConf.tls_options->outdated_protocols);
   7222 		safe_strdup(tlsoptions->outdated_ciphers, tempiConf.tls_options->outdated_ciphers);
   7223 		tlsoptions->options = tempiConf.tls_options->options;
   7224 		tlsoptions->renegotiate_bytes = tempiConf.tls_options->renegotiate_bytes;
   7225 		tlsoptions->renegotiate_timeout = tempiConf.tls_options->renegotiate_timeout;
   7226 		tlsoptions->sts_port = tempiConf.tls_options->sts_port;
   7227 		tlsoptions->sts_duration = tempiConf.tls_options->sts_duration;
   7228 		tlsoptions->sts_preload = tempiConf.tls_options->sts_preload;
   7229 	}
   7230 
   7231 	/* Now process the options */
   7232 	for (cepp = cep->items; cepp; cepp = cepp->next)
   7233 	{
   7234 		if (!strcmp(cepp->name, "ciphers") || !strcmp(cepp->name, "server-cipher-list"))
   7235 		{
   7236 			safe_strdup(tlsoptions->ciphers, cepp->value);
   7237 		}
   7238 		else if (!strcmp(cepp->name, "ciphersuites"))
   7239 		{
   7240 			safe_strdup(tlsoptions->ciphersuites, cepp->value);
   7241 		}
   7242 		else if (!strcmp(cepp->name, "ecdh-curves"))
   7243 		{
   7244 			safe_strdup(tlsoptions->ecdh_curves, cepp->value);
   7245 		}
   7246 		else if (!strcmp(cepp->name, "protocols"))
   7247 		{
   7248 			char copy[512], *p, *name;
   7249 			int option;
   7250 			char modifier;
   7251 
   7252 			strlcpy(copy, cepp->value, sizeof(copy));
   7253 			tlsoptions->protocols = 0;
   7254 			for (name = strtoken(&p, copy, ","); name; name = strtoken(&p, NULL, ","))
   7255 			{
   7256 				modifier = '\0';
   7257 				option = 0;
   7258 
   7259 				if ((*name == '+') || (*name == '-'))
   7260 				{
   7261 					modifier = *name;
   7262 					name++;
   7263 				}
   7264 
   7265 				if (!strcasecmp(name, "All"))
   7266 					option = TLS_PROTOCOL_ALL;
   7267 				else if (!strcasecmp(name, "TLSv1"))
   7268 					option = TLS_PROTOCOL_TLSV1;
   7269 				else if (!strcasecmp(name, "TLSv1.1"))
   7270 					option = TLS_PROTOCOL_TLSV1_1;
   7271 				else if (!strcasecmp(name, "TLSv1.2"))
   7272 					option = TLS_PROTOCOL_TLSV1_2;
   7273 				else if (!strcasecmp(name, "TLSv1.3"))
   7274 					option = TLS_PROTOCOL_TLSV1_3;
   7275 
   7276 				if (option)
   7277 				{
   7278 					if (modifier == '\0')
   7279 						tlsoptions->protocols = option;
   7280 					else if (modifier == '+')
   7281 						tlsoptions->protocols |= option;
   7282 					else if (modifier == '-')
   7283 						tlsoptions->protocols &= ~option;
   7284 				}
   7285 			}
   7286 		}
   7287 		else if (!strcmp(cepp->name, "certificate"))
   7288 		{
   7289 			convert_to_absolute_path(&cepp->value, CONFDIR);
   7290 			safe_strdup(tlsoptions->certificate_file, cepp->value);
   7291 		}
   7292 		else if (!strcmp(cepp->name, "key"))
   7293 		{
   7294 			convert_to_absolute_path(&cepp->value, CONFDIR);
   7295 			safe_strdup(tlsoptions->key_file, cepp->value);
   7296 		}
   7297 		else if (!strcmp(cepp->name, "trusted-ca-file"))
   7298 		{
   7299 			convert_to_absolute_path(&cepp->value, CONFDIR);
   7300 			safe_strdup(tlsoptions->trusted_ca_file, cepp->value);
   7301 		}
   7302 		else if (!strcmp(cepp->name, "outdated-protocols"))
   7303 		{
   7304 			safe_strdup(tlsoptions->outdated_protocols, cepp->value);
   7305 		}
   7306 		else if (!strcmp(cepp->name, "outdated-ciphers"))
   7307 		{
   7308 			safe_strdup(tlsoptions->outdated_ciphers, cepp->value);
   7309 		}
   7310 		else if (!strcmp(cepp->name, "renegotiate-bytes"))
   7311 		{
   7312 			tlsoptions->renegotiate_bytes = config_checkval(cepp->value, CFG_SIZE);
   7313 		}
   7314 		else if (!strcmp(cepp->name, "renegotiate-timeout"))
   7315 		{
   7316 			tlsoptions->renegotiate_timeout = config_checkval(cepp->value, CFG_TIME);
   7317 		}
   7318 		else if (!strcmp(cepp->name, "options"))
   7319 		{
   7320 			tlsoptions->options = 0;
   7321 			for (ceppp = cepp->items; ceppp; ceppp = ceppp->next)
   7322 			{
   7323 				long v = nv_find_by_name(_TLSFlags, ceppp->name);
   7324 				tlsoptions->options |= v;
   7325 			}
   7326 		}
   7327 		else if (!strcmp(cepp->name, "sts-policy"))
   7328 		{
   7329 			/* We do not inherit ::sts-policy if there is a specific block for this one... */
   7330 			tlsoptions->sts_port = 0;
   7331 			tlsoptions->sts_duration = 0;
   7332 			tlsoptions->sts_preload = 0;
   7333 			for (ceppp = cepp->items; ceppp; ceppp = ceppp->next)
   7334 			{
   7335 				if (!strcmp(ceppp->name, "port"))
   7336 					tlsoptions->sts_port = atoi(ceppp->value);
   7337 				else if (!strcmp(ceppp->name, "duration"))
   7338 					tlsoptions->sts_duration = config_checkval(ceppp->value, CFG_TIME);
   7339 				else if (!strcmp(ceppp->name, "preload"))
   7340 					tlsoptions->sts_preload = config_checkval(ceppp->value, CFG_YESNO);
   7341 			}
   7342 		}
   7343 	}
   7344 }
   7345 
   7346 int	_conf_set(ConfigFile *conf, ConfigEntry *ce)
   7347 {
   7348 	ConfigEntry *cep, *cepp, *ceppp, *cep4;
   7349 	Hook *h;
   7350 
   7351 	for (cep = ce->items; cep; cep = cep->next)
   7352 	{
   7353 		if (!strcmp(cep->name, "kline-address")) {
   7354 			safe_strdup(tempiConf.kline_address, cep->value);
   7355 		}
   7356 		if (!strcmp(cep->name, "gline-address")) {
   7357 			safe_strdup(tempiConf.gline_address, cep->value);
   7358 		}
   7359 		else if (!strcmp(cep->name, "modes-on-connect")) {
   7360 			tempiConf.conn_modes = (long) set_usermode(cep->value);
   7361 		}
   7362 		else if (!strcmp(cep->name, "modes-on-oper")) {
   7363 			tempiConf.oper_modes = (long) set_usermode(cep->value);
   7364 		}
   7365 		else if (!strcmp(cep->name, "modes-on-join")) {
   7366 			conf_channelmodes(cep->value, &tempiConf.modes_on_join);
   7367 			tempiConf.modes_on_join_set = 1;
   7368 		}
   7369 		else if (!strcmp(cep->name, "snomask-on-oper")) {
   7370 			safe_strdup(tempiConf.oper_snomask, cep->value);
   7371 		}
   7372 		else if (!strcmp(cep->name, "server-notice-colors")) {
   7373 			tempiConf.server_notice_colors = config_checkval(cep->value, CFG_YESNO);
   7374 		}
   7375 		else if (!strcmp(cep->name, "server-notice-show-event")) {
   7376 			tempiConf.server_notice_show_event = config_checkval(cep->value, CFG_YESNO);
   7377 		}
   7378 		else if (!strcmp(cep->name, "level-on-join")) {
   7379 			const char *res = channellevel_to_string(cep->value); /* 'halfop', etc */
   7380 			if (!res)
   7381 			{
   7382 				/* This check needs to be here, in config run, because
   7383 				 * now the channel modules are initialized and we know
   7384 				 * which ones are available. This same information is
   7385 				 * not available during config test, so we can't test
   7386 				 * for it there like we normally do.
   7387 				 */
   7388 				if (!valid_channel_access_mode_letter(*cep->value))
   7389 				{
   7390 					config_warn("%s:%d: set::level-on-join: Unknown mode (access level) '%c'. "
   7391 					            "That mode does not exist or is not a valid access mode "
   7392 					            "like vhoaq.",
   7393 					            cep->file->filename, cep->line_number,
   7394 					            *cep->value);
   7395 					config_warn("Falling back to to set::level-on-join none; now. "
   7396 					            "This is probably not what you want!!!");
   7397 				}
   7398 				res = cep->value; /* if we reach this.. then it is a single letter */
   7399 			}
   7400 			safe_strdup(tempiConf.level_on_join, res);
   7401 		}
   7402 		else if (!strcmp(cep->name, "static-quit")) {
   7403 			safe_strdup(tempiConf.static_quit, cep->value);
   7404 		}
   7405 		else if (!strcmp(cep->name, "static-part")) {
   7406 			safe_strdup(tempiConf.static_part, cep->value);
   7407 		}
   7408 		else if (!strcmp(cep->name, "who-limit")) {
   7409 			tempiConf.who_limit = atol(cep->value);
   7410 		}
   7411 		else if (!strcmp(cep->name, "maxbans")) {
   7412 			tempiConf.maxbans = atol(cep->value);
   7413 		}
   7414 		else if (!strcmp(cep->name, "maxbanlength")) {
   7415 			tempiConf.maxbanlength = atol(cep->value);
   7416 		}
   7417 		else if (!strcmp(cep->name, "silence-limit")) {
   7418 			tempiConf.silence_limit = atol(cep->value);
   7419 		}
   7420 		else if (!strcmp(cep->name, "auto-join")) {
   7421 			safe_strdup(tempiConf.auto_join_chans, cep->value);
   7422 		}
   7423 		else if (!strcmp(cep->name, "oper-auto-join")) {
   7424 			safe_strdup(tempiConf.oper_auto_join_chans, cep->value);
   7425 		}
   7426 		else if (!strcmp(cep->name, "check-target-nick-bans")) {
   7427 			tempiConf.check_target_nick_bans = config_checkval(cep->value, CFG_YESNO);
   7428 		}
   7429 		else if (!strcmp(cep->name, "ping-cookie")) {
   7430 			tempiConf.ping_cookie = config_checkval(cep->value, CFG_YESNO);
   7431 		}
   7432 		else if (!strcmp(cep->name, "watch-away-notification")) {
   7433 			tempiConf.watch_away_notification = config_checkval(cep->value, CFG_YESNO);
   7434 		}
   7435 		else if (!strcmp(cep->name, "uhnames")) {
   7436 			tempiConf.uhnames = config_checkval(cep->value, CFG_YESNO);
   7437 		}
   7438 		else if (!strcmp(cep->name, "allow-userhost-change")) {
   7439 			if (!strcasecmp(cep->value, "always"))
   7440 				tempiConf.userhost_allowed = UHALLOW_ALWAYS;
   7441 			else if (!strcasecmp(cep->value, "never"))
   7442 				tempiConf.userhost_allowed = UHALLOW_NEVER;
   7443 			else if (!strcasecmp(cep->value, "not-on-channels"))
   7444 				tempiConf.userhost_allowed = UHALLOW_NOCHANS;
   7445 			else
   7446 				tempiConf.userhost_allowed = UHALLOW_REJOIN;
   7447 		}
   7448 		else if (!strcmp(cep->name, "channel-command-prefix")) {
   7449 			safe_strdup(tempiConf.channel_command_prefix, cep->value);
   7450 		}
   7451 		else if (!strcmp(cep->name, "restrict-usermodes")) {
   7452 			int i;
   7453 			char *p = safe_alloc(strlen(cep->value) + 1), *x = p;
   7454 			/* The data should be something like 'Gw' or something,
   7455 			 * but just in case users use '+Gw' then ignore the + (and -).
   7456 			 */
   7457 			for (i=0; i < strlen(cep->value); i++)
   7458 				if ((cep->value[i] != '+') && (cep->value[i] != '-'))
   7459 					*x++ = cep->value[i];
   7460 			*x = '\0';
   7461 			tempiConf.restrict_usermodes = p;
   7462 		}
   7463 		else if (!strcmp(cep->name, "restrict-channelmodes")) {
   7464 			int i;
   7465 			char *p = safe_alloc(strlen(cep->value) + 1), *x = p;
   7466 			/* The data should be something like 'GL' or something,
   7467 			 * but just in case users use '+GL' then ignore the + (and -).
   7468 			 */
   7469 			for (i=0; i < strlen(cep->value); i++)
   7470 				if ((cep->value[i] != '+') && (cep->value[i] != '-'))
   7471 					*x++ = cep->value[i];
   7472 			*x = '\0';
   7473 			tempiConf.restrict_channelmodes = p;
   7474 		}
   7475 		else if (!strcmp(cep->name, "restrict-extendedbans")) {
   7476 			safe_strdup(tempiConf.restrict_extendedbans, cep->value);
   7477 		}
   7478 		else if (!strcmp(cep->name, "named-extended-bans")) {
   7479 			tempiConf.named_extended_bans = config_checkval(cep->value, CFG_YESNO);
   7480 		}
   7481 		else if (!strcmp(cep->name, "anti-spam-quit-message-time")) {
   7482 			tempiConf.anti_spam_quit_message_time = config_checkval(cep->value,CFG_TIME);
   7483 		}
   7484 		else if (!strcmp(cep->name, "allow-user-stats")) {
   7485 			if (!cep->items)
   7486 			{
   7487 				safe_strdup(tempiConf.allow_user_stats, cep->value);
   7488 			}
   7489 			else
   7490 			{
   7491 				for (cepp = cep->items; cepp; cepp = cepp->next)
   7492 				{
   7493 					OperStat *os = safe_alloc(sizeof(OperStat));
   7494 					safe_strdup(os->flag, cepp->name);
   7495 					AddListItem(os, tempiConf.allow_user_stats_ext);
   7496 				}
   7497 			}
   7498 		}
   7499 		else if (!strcmp(cep->name, "maxchannelsperuser")) {
   7500 			tempiConf.maxchannelsperuser = atoi(cep->value);
   7501 		}
   7502 		else if (!strcmp(cep->name, "ping-warning")) {
   7503 			tempiConf.ping_warning = atoi(cep->value);
   7504 		}
   7505 		else if (!strcmp(cep->name, "maxdccallow")) {
   7506 			tempiConf.maxdccallow = atoi(cep->value);
   7507 		}
   7508 		else if (!strcmp(cep->name, "max-targets-per-command"))
   7509 		{
   7510 			for (cepp = cep->items; cepp; cepp = cepp->next)
   7511 			{
   7512 				int v;
   7513 				if (!strcmp(cepp->value, "max"))
   7514 					v = MAXTARGETS_MAX;
   7515 				else
   7516 					v = atoi(cepp->value);
   7517 				setmaxtargets(cepp->name, v);
   7518 			}
   7519 		}
   7520 		else if (!strcmp(cep->name, "network-name")) {
   7521 			char *tmp;
   7522 			safe_strdup(tempiConf.network_name, cep->value);
   7523 			for (tmp = cep->value; *cep->value; cep->value++) {
   7524 				if (*cep->value == ' ')
   7525 					*cep->value='-';
   7526 			}
   7527 			safe_strdup(tempiConf.network_name_005, tmp);
   7528 			cep->value = tmp;
   7529 		}
   7530 		else if (!strcmp(cep->name, "default-server")) {
   7531 			safe_strdup(tempiConf.default_server, cep->value);
   7532 		}
   7533 		else if (!strcmp(cep->name, "services-server")) {
   7534 			safe_strdup(tempiConf.services_name, cep->value);
   7535 		}
   7536 		else if (!strcmp(cep->name, "sasl-server")) {
   7537 			safe_strdup(tempiConf.sasl_server, cep->value);
   7538 		}
   7539 		else if (!strcmp(cep->name, "stats-server")) {
   7540 			safe_strdup(tempiConf.stats_server, cep->value);
   7541 		}
   7542 		else if (!strcmp(cep->name, "help-channel")) {
   7543 			safe_strdup(tempiConf.helpchan, cep->value);
   7544 		}
   7545 		else if (!strcmp(cep->name, "cloak-prefix") || !strcmp(cep->name, "hiddenhost-prefix")) {
   7546 			safe_strdup(tempiConf.cloak_prefix, cep->value);
   7547 		}
   7548 		else if (!strcmp(cep->name, "hide-ban-reason")) {
   7549 			tempiConf.hide_ban_reason = config_checkval(cep->value, CFG_YESNO);
   7550 		}
   7551 		else if (!strcmp(cep->name, "prefix-quit")) {
   7552 			if (!strcmp(cep->value, "0") || !strcmp(cep->value, "no"))
   7553 				safe_free(tempiConf.prefix_quit);
   7554 			else
   7555 				safe_strdup(tempiConf.prefix_quit, cep->value);
   7556 		}
   7557 		else if (!strcmp(cep->name, "link")) {
   7558 			for (cepp = cep->items; cepp; cepp = cepp->next) {
   7559 				if (!strcmp(cepp->name, "bind-ip")) {
   7560 					safe_strdup(tempiConf.link_bindip, cepp->value);
   7561 				}
   7562 			}
   7563 		}
   7564 		else if (!strcmp(cep->name, "anti-flood")) {
   7565 			for (cepp = cep->items; cepp; cepp = cepp->next)
   7566 			{
   7567 				int lag_penalty = -1;
   7568 				int lag_penalty_bytes = -1;
   7569 				for (ceppp = cepp->items; ceppp; ceppp = ceppp->next)
   7570 				{
   7571 					/* Check hooks first */
   7572 					int used = 0;
   7573 					for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next)
   7574 					{
   7575 						used = (*(h->func.intfunc))(conf,ceppp,CONFIG_SET_ANTI_FLOOD);
   7576 						if (used == 1)
   7577 							break;
   7578 					}
   7579 					if (used == 1)
   7580 						continue; /* module handled it */
   7581 					if (used == 2)
   7582 						break; /* module handled it and we must stop entire block processing */
   7583 					if (!strcmp(ceppp->name, "handshake-data-flood"))
   7584 					{
   7585 						for (cep4 = ceppp->items; cep4; cep4 = cep4->next)
   7586 						{
   7587 							if (!strcmp(cep4->name, "amount"))
   7588 								tempiConf.handshake_data_flood_amount = config_checkval(cep4->value, CFG_SIZE);
   7589 							else if (!strcmp(cep4->name, "ban-time"))
   7590 								tempiConf.handshake_data_flood_ban_time = config_checkval(cep4->value, CFG_TIME);
   7591 							else if (!strcmp(cep4->name, "ban-action"))
   7592 								tempiConf.handshake_data_flood_ban_action = banact_stringtoval(cep4->value);
   7593 						}
   7594 					}
   7595 					else if (!strcmp(ceppp->name, "away-flood"))
   7596 					{
   7597 						config_parse_flood_generic(ceppp->value, &tempiConf, cepp->name, FLD_AWAY);
   7598 					}
   7599 					else if (!strcmp(ceppp->name, "nick-flood"))
   7600 					{
   7601 						config_parse_flood_generic(ceppp->value, &tempiConf, cepp->name, FLD_NICK);
   7602 					}
   7603 					else if (!strcmp(ceppp->name, "vhost-flood"))
   7604 					{
   7605 						config_parse_flood_generic(ceppp->value, &tempiConf, cepp->name, FLD_VHOST);
   7606 					}
   7607 					else if (!strcmp(ceppp->name, "join-flood"))
   7608 					{
   7609 						config_parse_flood_generic(ceppp->value, &tempiConf, cepp->name, FLD_JOIN);
   7610 					}
   7611 					else if (!strcmp(ceppp->name, "invite-flood"))
   7612 					{
   7613 						config_parse_flood_generic(ceppp->value, &tempiConf, cepp->name, FLD_INVITE);
   7614 					}
   7615 					else if (!strcmp(ceppp->name, "knock-flood"))
   7616 					{
   7617 						config_parse_flood_generic(ceppp->value, &tempiConf, cepp->name, FLD_KNOCK);
   7618 					}
   7619 					else if (!strcmp(ceppp->name, "lag-penalty"))
   7620 					{
   7621 						lag_penalty = atoi(ceppp->value);
   7622 					}
   7623 					else if (!strcmp(ceppp->name, "lag-penalty-bytes"))
   7624 					{
   7625 						lag_penalty_bytes = config_checkval(ceppp->value, CFG_SIZE);
   7626 						if (lag_penalty_bytes <= 0)
   7627 							lag_penalty_bytes = INT_MAX;
   7628 					}
   7629 					else if (!strcmp(ceppp->name, "connect-flood"))
   7630 					{
   7631 						int cnt, period;
   7632 						config_parse_flood(ceppp->value, &cnt, &period);
   7633 						tempiConf.throttle_count = cnt;
   7634 						tempiConf.throttle_period = period;
   7635 					}
   7636 					else if (!strcmp(ceppp->name, "max-concurrent-conversations"))
   7637 					{
   7638 						/* We use a hack here to make it fit our storage format */
   7639 						char buf[64];
   7640 						int users=0;
   7641 						long every=0;
   7642 						for (cep4 = ceppp->items; cep4; cep4 = cep4->next)
   7643 						{
   7644 							if (!strcmp(cep4->name, "users"))
   7645 							{
   7646 								users = atoi(cep4->value);
   7647 							} else
   7648 							if (!strcmp(cep4->name, "new-user-every"))
   7649 							{
   7650 								every = config_checkval(cep4->value, CFG_TIME);
   7651 							}
   7652 						}
   7653 						snprintf(buf, sizeof(buf), "%d:%ld", users, every);
   7654 						config_parse_flood_generic(buf, &tempiConf, cepp->name, FLD_CONVERSATIONS);
   7655 					}
   7656 				}
   7657 				if ((lag_penalty != -1) && (lag_penalty_bytes != -1))
   7658 				{
   7659 					/* We use a hack here to make it fit our storage format */
   7660 					char buf[64];
   7661 					snprintf(buf, sizeof(buf), "%d:%d", lag_penalty_bytes, lag_penalty);
   7662 					config_parse_flood_generic(buf, &tempiConf, cepp->name, FLD_LAG_PENALTY);
   7663 				}
   7664 			}
   7665 		}
   7666 		else if (!strcmp(cep->name, "options")) {
   7667 			for (cepp = cep->items; cepp; cepp = cepp->next) {
   7668 				if (!strcmp(cepp->name, "hide-ulines")) {
   7669 					tempiConf.hide_ulines = 1;
   7670 				}
   7671 				else if (!strcmp(cepp->name, "flat-map")) {
   7672 					tempiConf.flat_map = 1;
   7673 				}
   7674 				else if (!strcmp(cepp->name, "show-opermotd")) {
   7675 					tempiConf.show_opermotd = 1;
   7676 				}
   7677 				else if (!strcmp(cepp->name, "identd-check")) {
   7678 					tempiConf.ident_check = 1;
   7679 				}
   7680 				else if (!strcmp(cepp->name, "fail-oper-warn")) {
   7681 					tempiConf.fail_oper_warn = 1;
   7682 				}
   7683 				else if (!strcmp(cepp->name, "show-connect-info")) {
   7684 					tempiConf.show_connect_info = 1;
   7685 				}
   7686 				else if (!strcmp(cepp->name, "no-connect-tls-info")) {
   7687 					tempiConf.no_connect_tls_info = 1;
   7688 				}
   7689 				else if (!strcmp(cepp->name, "dont-resolve")) {
   7690 					tempiConf.dont_resolve = 1;
   7691 				}
   7692 				else if (!strcmp(cepp->name, "mkpasswd-for-everyone")) {
   7693 					tempiConf.mkpasswd_for_everyone = 1;
   7694 				}
   7695 				else if (!strcmp(cepp->name, "allow-insane-bans")) {
   7696 					tempiConf.allow_insane_bans = 1;
   7697 				}
   7698 				else if (!strcmp(cepp->name, "allow-part-if-shunned")) {
   7699 					tempiConf.allow_part_if_shunned = 1;
   7700 				}
   7701 				else if (!strcmp(cepp->name, "disable-cap")) {
   7702 					tempiConf.disable_cap = 1;
   7703 				}
   7704 				else if (!strcmp(cepp->name, "disable-ipv6")) {
   7705 					/* other code handles this */
   7706 				}
   7707 			}
   7708 		}
   7709 		else if (!strcmp(cep->name, "cloak-keys"))
   7710 		{
   7711 			for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next)
   7712 			{
   7713 				int value;
   7714 				value = (*(h->func.intfunc))(conf, cep, CONFIG_CLOAKKEYS);
   7715 				if (value == 1)
   7716 					break;
   7717 			}
   7718 		}
   7719 		else if (!strcmp(cep->name, "ident"))
   7720 		{
   7721 			for (cepp = cep->items; cepp; cepp = cepp->next)
   7722 			{
   7723 				if (!strcmp(cepp->name, "connect-timeout"))
   7724 					tempiConf.ident_connect_timeout = config_checkval(cepp->value,CFG_TIME);
   7725 				if (!strcmp(cepp->name, "read-timeout"))
   7726 					tempiConf.ident_read_timeout = config_checkval(cepp->value,CFG_TIME);
   7727 			}
   7728 		}
   7729 		else if (!strcmp(cep->name, "spamfilter"))
   7730 		{
   7731 			for (cepp = cep->items; cepp; cepp = cepp->next)
   7732 			{
   7733 				if (!strcmp(cepp->name, "ban-time"))
   7734 					tempiConf.spamfilter_ban_time = config_checkval(cepp->value,CFG_TIME);
   7735 				else if (!strcmp(cepp->name, "ban-reason"))
   7736 					safe_strdup(tempiConf.spamfilter_ban_reason, cepp->value);
   7737 				else if (!strcmp(cepp->name, "virus-help-channel"))
   7738 					safe_strdup(tempiConf.spamfilter_virus_help_channel, cepp->value);
   7739 				else if (!strcmp(cepp->name, "virus-help-channel-deny"))
   7740 					tempiConf.spamfilter_vchan_deny = config_checkval(cepp->value,CFG_YESNO);
   7741 				else if (!strcmp(cepp->name, "except"))
   7742 				{
   7743 					char *name, *p;
   7744 					SpamExcept *e;
   7745 					safe_strdup(tempiConf.spamexcept_line, cepp->value);
   7746 					for (name = strtoken(&p, cepp->value, ","); name; name = strtoken(&p, NULL, ","))
   7747 					{
   7748 						if (*name == ' ')
   7749 							name++;
   7750 						if (*name)
   7751 						{
   7752 							e = safe_alloc(sizeof(SpamExcept) + strlen(name));
   7753 							strcpy(e->name, name);
   7754 							AddListItem(e, tempiConf.spamexcept);
   7755 						}
   7756 					}
   7757 				}
   7758 				else if (!strcmp(cepp->name, "detect-slow-warn"))
   7759 				{
   7760 					tempiConf.spamfilter_detectslow_warn = atol(cepp->value);
   7761 				}
   7762 				else if (!strcmp(cepp->name, "detect-slow-fatal"))
   7763 				{
   7764 					tempiConf.spamfilter_detectslow_fatal = atol(cepp->value);
   7765 				}
   7766 				else if (!strcmp(cepp->name, "stop-on-first-match"))
   7767 				{
   7768 					tempiConf.spamfilter_stop_on_first_match = config_checkval(cepp->value, CFG_YESNO);
   7769 				}
   7770 				else if (!strcmp(cepp->name, "utf8"))
   7771 				{
   7772 					tempiConf.spamfilter_utf8 = config_checkval(cepp->value, CFG_YESNO);
   7773 				}
   7774 			}
   7775 		}
   7776 		else if (!strcmp(cep->name, "default-bantime"))
   7777 		{
   7778 			tempiConf.default_bantime = config_checkval(cep->value,CFG_TIME);
   7779 		}
   7780 		else if (!strcmp(cep->name, "ban-version-tkl-time"))
   7781 		{
   7782 			tempiConf.ban_version_tkl_time = config_checkval(cep->value,CFG_TIME);
   7783 		}
   7784 		else if (!strcmp(cep->name, "min-nick-length")) {
   7785 			int v = atoi(cep->value);
   7786 			tempiConf.min_nick_length = v;
   7787 		}
   7788 		else if (!strcmp(cep->name, "nick-length")) {
   7789 			int v = atoi(cep->value);
   7790 			tempiConf.nick_length = v;
   7791 		}
   7792 		else if (!strcmp(cep->name, "topic-length")) {
   7793 			int v = atoi(cep->value);
   7794 			tempiConf.topic_length = v;
   7795 		}
   7796 		else if (!strcmp(cep->name, "away-length")) {
   7797 			int v = atoi(cep->value);
   7798 			tempiConf.away_length = v;
   7799 		}
   7800 		else if (!strcmp(cep->name, "kick-length")) {
   7801 			int v = atoi(cep->value);
   7802 			tempiConf.kick_length = v;
   7803 		}
   7804 		else if (!strcmp(cep->name, "quit-length")) {
   7805 			int v = atoi(cep->value);
   7806 			tempiConf.quit_length = v;
   7807 		}
   7808 		else if (!strcmp(cep->name, "ssl") || !strcmp(cep->name, "tls")) {
   7809 			/* no need to alloc tempiConf.tls_options since config_defaults() already ensures it exists */
   7810 			conf_tlsblock(conf, cep, tempiConf.tls_options);
   7811 		}
   7812 		else if (!strcmp(cep->name, "plaintext-policy"))
   7813 		{
   7814 			for (cepp = cep->items; cepp; cepp = cepp->next)
   7815 			{
   7816 				if (!strcmp(cepp->name, "user"))
   7817 					tempiConf.plaintext_policy_user = policy_strtoval(cepp->value);
   7818 				else if (!strcmp(cepp->name, "oper"))
   7819 					tempiConf.plaintext_policy_oper = policy_strtoval(cepp->value);
   7820 				else if (!strcmp(cepp->name, "server"))
   7821 					tempiConf.plaintext_policy_server = policy_strtoval(cepp->value);
   7822 				else if (!strcmp(cepp->name, "user-message"))
   7823 					addmultiline(&tempiConf.plaintext_policy_user_message, cepp->value);
   7824 				else if (!strcmp(cepp->name, "oper-message"))
   7825 					addmultiline(&tempiConf.plaintext_policy_oper_message, cepp->value);
   7826 			}
   7827 		}
   7828 		else if (!strcmp(cep->name, "outdated-tls-policy"))
   7829 		{
   7830 			for (cepp = cep->items; cepp; cepp = cepp->next)
   7831 			{
   7832 				if (!strcmp(cepp->name, "user"))
   7833 					tempiConf.outdated_tls_policy_user = policy_strtoval(cepp->value);
   7834 				else if (!strcmp(cepp->name, "oper"))
   7835 					tempiConf.outdated_tls_policy_oper = policy_strtoval(cepp->value);
   7836 				else if (!strcmp(cepp->name, "server"))
   7837 					tempiConf.outdated_tls_policy_server = policy_strtoval(cepp->value);
   7838 				else if (!strcmp(cepp->name, "user-message"))
   7839 					safe_strdup(tempiConf.outdated_tls_policy_user_message, cepp->value);
   7840 				else if (!strcmp(cepp->name, "oper-message"))
   7841 					safe_strdup(tempiConf.outdated_tls_policy_oper_message, cepp->value);
   7842 			}
   7843 		}
   7844 		else if (!strcmp(cep->name, "default-ipv6-clone-mask"))
   7845 		{
   7846 			tempiConf.default_ipv6_clone_mask = atoi(cep->value);
   7847 		}
   7848 		else if (!strcmp(cep->name, "hide-list")) {
   7849 			for (cepp = cep->items; cepp; cepp = cepp->next)
   7850 			{
   7851 				if (!strcmp(cepp->name, "deny-channel"))
   7852 				{
   7853 					tempiConf.hide_list = 1;
   7854 					/* if we would expand this later then change this to a bitmask or struct or whatever */
   7855 				}
   7856 			}
   7857 		}
   7858 		else if (!strcmp(cep->name, "max-unknown-connections-per-ip"))
   7859 		{
   7860 			tempiConf.max_unknown_connections_per_ip = atoi(cep->value);
   7861 		}
   7862 		else if (!strcmp(cep->name, "handshake-timeout"))
   7863 		{
   7864 			tempiConf.handshake_timeout = config_checkval(cep->value, CFG_TIME);
   7865 		}
   7866 		else if (!strcmp(cep->name, "sasl-timeout"))
   7867 		{
   7868 			tempiConf.sasl_timeout = config_checkval(cep->value, CFG_TIME);
   7869 		}
   7870 		else if (!strcmp(cep->name, "handshake-delay"))
   7871 		{
   7872 			tempiConf.handshake_delay = config_checkval(cep->value, CFG_TIME);
   7873 		}
   7874 		else if (!strcmp(cep->name, "automatic-ban-target"))
   7875 		{
   7876 			tempiConf.automatic_ban_target = ban_target_strtoval(cep->value);
   7877 		}
   7878 		else if (!strcmp(cep->name, "manual-ban-target"))
   7879 		{
   7880 			tempiConf.manual_ban_target = ban_target_strtoval(cep->value);
   7881 		}
   7882 		else if (!strcmp(cep->name, "reject-message"))
   7883 		{
   7884 			for (cepp = cep->items; cepp; cepp = cepp->next)
   7885 			{
   7886 				if (!strcmp(cepp->name, "too-many-connections"))
   7887 					safe_strdup(tempiConf.reject_message_too_many_connections, cepp->value);
   7888 				else if (!strcmp(cepp->name, "server-full"))
   7889 					safe_strdup(tempiConf.reject_message_server_full, cepp->value);
   7890 				else if (!strcmp(cepp->name, "unauthorized"))
   7891 					safe_strdup(tempiConf.reject_message_unauthorized, cepp->value);
   7892 				else if (!strcmp(cepp->name, "kline"))
   7893 					safe_strdup(tempiConf.reject_message_kline, cepp->value);
   7894 				else if (!strcmp(cepp->name, "gline"))
   7895 					safe_strdup(tempiConf.reject_message_gline, cepp->value);
   7896 			}
   7897 		}
   7898 		else if (!strcmp(cep->name, "topic-setter"))
   7899 		{
   7900 			if (!strcmp(cep->value, "nick"))
   7901 				tempiConf.topic_setter = SETTER_NICK;
   7902 			else if (!strcmp(cep->value, "nick-user-host"))
   7903 				tempiConf.topic_setter = SETTER_NICK_USER_HOST;
   7904 		}
   7905 		else if (!strcmp(cep->name, "ban-setter"))
   7906 		{
   7907 			if (!strcmp(cep->value, "nick"))
   7908 				tempiConf.ban_setter = SETTER_NICK;
   7909 			else if (!strcmp(cep->value, "nick-user-host"))
   7910 				tempiConf.ban_setter = SETTER_NICK_USER_HOST;
   7911 		}
   7912 		else if (!strcmp(cep->name, "ban-setter-sync") || !strcmp(cep->name, "ban-setter-synch"))
   7913 		{
   7914 			tempiConf.ban_setter_sync = config_checkval(cep->value, CFG_YESNO);
   7915 		}
   7916 		else if (!strcmp(cep->name, "part-instead-of-quit-on-comment-change"))
   7917 		{
   7918 			tempiConf.part_instead_of_quit_on_comment_change = config_checkval(cep->value, CFG_YESNO);
   7919 		}
   7920 		else if (!strcmp(cep->name, "broadcast-channel-messages"))
   7921 		{
   7922 			if (!strcmp(cep->value, "auto"))
   7923 				tempiConf.broadcast_channel_messages = BROADCAST_CHANNEL_MESSAGES_AUTO;
   7924 			else if (!strcmp(cep->value, "always"))
   7925 				tempiConf.broadcast_channel_messages = BROADCAST_CHANNEL_MESSAGES_ALWAYS;
   7926 			else if (!strcmp(cep->value, "never"))
   7927 				tempiConf.broadcast_channel_messages = BROADCAST_CHANNEL_MESSAGES_NEVER;
   7928 		}
   7929 		else if (!strcmp(cep->name, "allowed-channelchars"))
   7930 		{
   7931 			tempiConf.allowed_channelchars = allowed_channelchars_strtoval(cep->value);
   7932 		}
   7933 		else if (!strcmp(cep->name, "hide-idle-time"))
   7934 		{
   7935 			for (cepp = cep->items; cepp; cepp = cepp->next)
   7936 			{
   7937 				if (!strcmp(cepp->name, "policy"))
   7938 					tempiConf.hide_idle_time = hideidletime_strtoval(cepp->value);
   7939 			}
   7940 		} else if (!strcmp(cep->name, "limit-svscmds"))
   7941 		{
   7942 			if (!strcmp(cep->value, "ulines"))
   7943 				tempiConf.limit_svscmds = LIMIT_SVSCMDS_ULINES;
   7944 			else
   7945 				tempiConf.limit_svscmds = LIMIT_SVSCMDS_SERVERS;
   7946 		} else
   7947 		{
   7948 			int value;
   7949 			for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next)
   7950 			{
   7951 				value = (*(h->func.intfunc))(conf,cep,CONFIG_SET);
   7952 				if (value == 1)
   7953 					break;
   7954 			}
   7955 		}
   7956 	}
   7957 	return 0;
   7958 }
   7959 
   7960 int	_test_set(ConfigFile *conf, ConfigEntry *ce)
   7961 {
   7962 	ConfigEntry *cep, *cepp, *ceppp, *cep4;
   7963 	int tempi;
   7964 	int errors = 0;
   7965 	Hook *h;
   7966 
   7967 	for (cep = ce->items; cep; cep = cep->next)
   7968 	{
   7969 		if (!strcmp(cep->name, "kline-address")) {
   7970 			CheckNull(cep);
   7971 			CheckDuplicate(cep, kline_address, "kline-address");
   7972 			if (!strchr(cep->value, '@') && !strchr(cep->value, ':'))
   7973 			{
   7974 				config_error("%s:%i: set::kline-address must be an e-mail or an URL",
   7975 					cep->file->filename, cep->line_number);
   7976 				errors++;
   7977 				continue;
   7978 			}
   7979 			else if (match_simple("*@unrealircd.com", cep->value) || match_simple("*@unrealircd.org",cep->value) || match_simple("unreal-*@lists.sourceforge.net",cep->value))
   7980 			{
   7981 				config_error("%s:%i: set::kline-address may not be an UnrealIRCd Team address",
   7982 					cep->file->filename, cep->line_number);
   7983 				errors++; continue;
   7984 			}
   7985 		}
   7986 		else if (!strcmp(cep->name, "gline-address")) {
   7987 			CheckNull(cep);
   7988 			CheckDuplicate(cep, gline_address, "gline-address");
   7989 			if (!strchr(cep->value, '@') && !strchr(cep->value, ':'))
   7990 			{
   7991 				config_error("%s:%i: set::gline-address must be an e-mail or an URL",
   7992 					cep->file->filename, cep->line_number);
   7993 				errors++;
   7994 				continue;
   7995 			}
   7996 			else if (match_simple("*@unrealircd.com", cep->value) || match_simple("*@unrealircd.org",cep->value) || match_simple("unreal-*@lists.sourceforge.net",cep->value))
   7997 			{
   7998 				config_error("%s:%i: set::gline-address may not be an UnrealIRCd Team address",
   7999 					cep->file->filename, cep->line_number);
   8000 				errors++; continue;
   8001 			}
   8002 		}
   8003 		else if (!strcmp(cep->name, "modes-on-connect")) {
   8004 			char *p;
   8005 			CheckNull(cep);
   8006 			CheckDuplicate(cep, modes_on_connect, "modes-on-connect");
   8007 			for (p = cep->value; *p; p++)
   8008 				if (strchr("orzSHqtW", *p))
   8009 				{
   8010 					config_error("%s:%i: set::modes-on-connect may not include mode '%c'",
   8011 						cep->file->filename, cep->line_number, *p);
   8012 					errors++;
   8013 				}
   8014 		}
   8015 		else if (!strcmp(cep->name, "modes-on-join")) {
   8016 			char *c;
   8017 			struct ChMode temp;
   8018 			memset(&temp, 0, sizeof(temp));
   8019 			CheckNull(cep);
   8020 			CheckDuplicate(cep, modes_on_join, "modes-on-join");
   8021 			for (c = cep->value; *c; c++)
   8022 			{
   8023 				if (*c == ' ')
   8024 					break; /* don't check the parameter ;p */
   8025 				switch (*c)
   8026 				{
   8027 					case 'q':
   8028 					case 'a':
   8029 					case 'o':
   8030 					case 'h':
   8031 					case 'v':
   8032 					case 'b':
   8033 					case 'e':
   8034 					case 'I':
   8035 						config_error("%s:%i: set::modes-on-join may not contain +%c",
   8036 							cep->file->filename, cep->line_number, *c);
   8037 						errors++;
   8038 						break;
   8039 				}
   8040 			}
   8041 			/* We can't really verify much here.
   8042 			 * The channel mode modules have not been initialized
   8043 			 * yet at this point, so we can't really verify much
   8044 			 * here.
   8045 			 */
   8046 		}
   8047 		else if (!strcmp(cep->name, "modes-on-oper")) {
   8048 			char *p;
   8049 			CheckNull(cep);
   8050 			CheckDuplicate(cep, modes_on_oper, "modes-on-oper");
   8051 			for (p = cep->value; *p; p++)
   8052 				if (strchr("orzS", *p))
   8053 				{
   8054 					config_error("%s:%i: set::modes-on-oper may not include mode '%c'",
   8055 						cep->file->filename, cep->line_number, *p);
   8056 					errors++;
   8057 				}
   8058 			set_usermode(cep->value);
   8059 		}
   8060 		else if (!strcmp(cep->name, "snomask-on-oper")) {
   8061 			char *wrong_snomask;
   8062 			CheckNull(cep);
   8063 			CheckDuplicate(cep, snomask_on_oper, "snomask-on-oper");
   8064 			if (!is_valid_snomask_string_testing(cep->value, &wrong_snomask))
   8065 			{
   8066 				config_error("%s:%i: set::snomask-on-oper contains unknown snomask letter(s) '%s'",
   8067 					     cep->file->filename, cep->line_number, wrong_snomask);
   8068 				errors++;
   8069 				invalid_snomasks_encountered++;
   8070 			}
   8071 		}
   8072 		else if (!strcmp(cep->name, "server-notice-colors")) {
   8073 			CheckNull(cep);
   8074 		}
   8075 		else if (!strcmp(cep->name, "server-notice-show-event")) {
   8076 			CheckNull(cep);
   8077 		}
   8078 		else if (!strcmp(cep->name, "level-on-join")) {
   8079 			CheckNull(cep);
   8080 			CheckDuplicate(cep, level_on_join, "level-on-join");
   8081 			if (!channellevel_to_string(cep->value) && (strlen(cep->value) != 1))
   8082 			{
   8083 				config_error("%s:%i: set::level-on-join: unknown value '%s', should be one of: "
   8084 				             "'none', 'voice', 'halfop', 'op', 'admin', 'owner', or a single letter (eg 'o')",
   8085 				             cep->file->filename, cep->line_number, cep->value);
   8086 				errors++;
   8087 			}
   8088 		}
   8089 		else if (!strcmp(cep->name, "static-quit")) {
   8090 			CheckNull(cep);
   8091 			CheckDuplicate(cep, static_quit, "static-quit");
   8092 		}
   8093 		else if (!strcmp(cep->name, "static-part")) {
   8094 			CheckNull(cep);
   8095 			CheckDuplicate(cep, static_part, "static-part");
   8096 		}
   8097 		else if (!strcmp(cep->name, "who-limit")) {
   8098 			CheckNull(cep);
   8099 			CheckDuplicate(cep, who_limit, "who-limit");
   8100 			if (!config_checkval(cep->value,CFG_SIZE))
   8101 			{
   8102 				config_error("%s:%i: set::who-limit: value must be at least 1",
   8103 					cep->file->filename, cep->line_number);
   8104 				errors++;
   8105 			}
   8106 		}
   8107 		else if (!strcmp(cep->name, "maxbans")) {
   8108 			CheckNull(cep);
   8109 			CheckDuplicate(cep, maxbans, "maxbans");
   8110 		}
   8111 		else if (!strcmp(cep->name, "maxbanlength")) {
   8112 			CheckNull(cep);
   8113 			CheckDuplicate(cep, maxbanlength, "maxbanlength");
   8114 		}
   8115 		else if (!strcmp(cep->name, "silence-limit")) {
   8116 			CheckNull(cep);
   8117 			CheckDuplicate(cep, silence_limit, "silence-limit");
   8118 		}
   8119 		else if (!strcmp(cep->name, "auto-join")) {
   8120 			CheckNull(cep);
   8121 			CheckDuplicate(cep, auto_join, "auto-join");
   8122 		}
   8123 		else if (!strcmp(cep->name, "oper-auto-join")) {
   8124 			CheckNull(cep);
   8125 			CheckDuplicate(cep, oper_auto_join, "oper-auto-join");
   8126 		}
   8127 		else if (!strcmp(cep->name, "check-target-nick-bans")) {
   8128 			CheckNull(cep);
   8129 			CheckDuplicate(cep, check_target_nick_bans, "check-target-nick-bans");
   8130 		}
   8131 		else if (!strcmp(cep->name, "pingpong-warning")) {
   8132 			config_error("%s:%i: set::pingpong-warning no longer exists (the warning is always off)",
   8133 			             cep->file->filename, cep->line_number);
   8134 			errors++;
   8135 		}
   8136 		else if (!strcmp(cep->name, "ping-cookie")) {
   8137 			CheckNull(cep);
   8138 			CheckDuplicate(cep, ping_cookie, "ping-cookie");
   8139 		}
   8140 		else if (!strcmp(cep->name, "watch-away-notification")) {
   8141 			CheckNull(cep);
   8142 			CheckDuplicate(cep, watch_away_notification, "watch-away-notification");
   8143 		}
   8144 		else if (!strcmp(cep->name, "uhnames")) {
   8145 			CheckNull(cep);
   8146 			CheckDuplicate(cep, uhnames, "uhnames");
   8147 		}
   8148 		else if (!strcmp(cep->name, "channel-command-prefix")) {
   8149 			CheckNullAllowEmpty(cep);
   8150 			CheckDuplicate(cep, channel_command_prefix, "channel-command-prefix");
   8151 		}
   8152 		else if (!strcmp(cep->name, "allow-userhost-change")) {
   8153 			CheckNull(cep);
   8154 			CheckDuplicate(cep, allow_userhost_change, "allow-userhost-change");
   8155 			if (strcasecmp(cep->value, "always") &&
   8156 			    strcasecmp(cep->value, "never") &&
   8157 			    strcasecmp(cep->value, "not-on-channels") &&
   8158 			    strcasecmp(cep->value, "force-rejoin"))
   8159 			{
   8160 				config_error("%s:%i: set::allow-userhost-change is invalid",
   8161 					cep->file->filename,
   8162 					cep->line_number);
   8163 				errors++;
   8164 				continue;
   8165 			}
   8166 		}
   8167 		else if (!strcmp(cep->name, "anti-spam-quit-message-time")) {
   8168 			CheckNull(cep);
   8169 			CheckDuplicate(cep, anti_spam_quit_message_time, "anti-spam-quit-message-time");
   8170 		}
   8171 		else if (!strcmp(cep->name, "oper-only-stats"))
   8172 		{
   8173 			config_warn("%s:%d: We no longer use a blacklist for stats (set::oper-only-stats) but "
   8174 			             "have a whitelist now instead (set::allow-user-stats). ",
   8175 			             cep->file->filename, cep->line_number);
   8176 			config_warn("Simply delete the oper-only-stats line from your configuration file %s around line %d to get rid of this warning",
   8177 			             cep->file->filename, cep->line_number);
   8178 			continue;
   8179 		}
   8180 		else if (!strcmp(cep->name, "allow-user-stats"))
   8181 		{
   8182 			CheckDuplicate(cep, allow_user_stats, "allow-user-stats");
   8183 			CheckNull(cep);
   8184 		}
   8185 		else if (!strcmp(cep->name, "maxchannelsperuser")) {
   8186 			CheckNull(cep);
   8187 			CheckDuplicate(cep, maxchannelsperuser, "maxchannelsperuser");
   8188 			tempi = atoi(cep->value);
   8189 			if (tempi < 1)
   8190 			{
   8191 				config_error("%s:%i: set::maxchannelsperuser must be > 0",
   8192 					cep->file->filename,
   8193 					cep->line_number);
   8194 				errors++;
   8195 				continue;
   8196 			}
   8197 		}
   8198 		else if (!strcmp(cep->name, "ping-warning")) {
   8199 			CheckNull(cep);
   8200 			CheckDuplicate(cep, ping_warning, "ping-warning");
   8201 			tempi = atoi(cep->value);
   8202 			/* it is pointless to allow setting higher than 170 */
   8203 			if (tempi > 170)
   8204 			{
   8205 				config_error("%s:%i: set::ping-warning must be < 170",
   8206 					cep->file->filename,
   8207 					cep->line_number);
   8208 				errors++;
   8209 				continue;
   8210 			}
   8211 		}
   8212 		else if (!strcmp(cep->name, "maxdccallow")) {
   8213 			CheckNull(cep);
   8214 			CheckDuplicate(cep, maxdccallow, "maxdccallow");
   8215 		}
   8216 		else if (!strcmp(cep->name, "max-targets-per-command"))
   8217 		{
   8218 			for (cepp = cep->items; cepp; cepp = cepp->next)
   8219 			{
   8220 				CheckNull(cepp);
   8221 				if (!strcasecmp(cepp->name, "NAMES") || !strcasecmp(cepp->name, "WHOWAS"))
   8222 				{
   8223 					if (atoi(cepp->value) != 1)
   8224 					{
   8225 						config_error("%s:%i: set::max-targets-per-command::%s: "
   8226 						             "this command is hardcoded at a maximum of 1 "
   8227 						             "and cannot be configured to accept more.",
   8228 						             cepp->file->filename,
   8229 						             cepp->line_number,
   8230 						             cepp->name);
   8231 						errors++;
   8232 					}
   8233 				} else
   8234 				if (!strcasecmp(cepp->name, "USERHOST") ||
   8235 				    !strcasecmp(cepp->name, "USERIP") ||
   8236 				    !strcasecmp(cepp->name, "ISON") ||
   8237 				    !strcasecmp(cepp->name, "WATCH"))
   8238 				{
   8239 					if (strcmp(cepp->value, "max"))
   8240 					{
   8241 						config_error("%s:%i: set::max-targets-per-command::%s: "
   8242 						             "this command is hardcoded at a maximum of 'max' "
   8243 						             "and cannot be changed. This because it is "
   8244 						             "highly discouraged to change it.",
   8245 						             cepp->file->filename,
   8246 						             cepp->line_number,
   8247 						             cepp->name);
   8248 						errors++;
   8249 					}
   8250 				}
   8251 				/* Now check the value syntax in general: */
   8252 				if (strcmp(cepp->value, "max")) /* anything other than 'max'.. */
   8253 				{
   8254 					int v = atoi(cepp->value);
   8255 					if ((v < 1) || (v > 20))
   8256 					{
   8257 						config_error("%s:%i: set::max-targets-per-command::%s: "
   8258 						             "value should be 1-20 or 'max'",
   8259 						             cepp->file->filename,
   8260 						             cepp->line_number,
   8261 						             cepp->name);
   8262 						errors++;
   8263 					}
   8264 				}
   8265 			}
   8266 		}
   8267 		else if (!strcmp(cep->name, "network-name")) {
   8268 			char *p;
   8269 			CheckNull(cep);
   8270 			CheckDuplicate(cep, network_name, "network-name");
   8271 			for (p = cep->value; *p; p++)
   8272 				if ((*p < ' ') || (*p > '~'))
   8273 				{
   8274 					config_error("%s:%i: set::network-name can only contain ASCII characters 33-126. Invalid character = '%c'",
   8275 						cep->file->filename, cep->line_number, *p);
   8276 					errors++;
   8277 					break;
   8278 				}
   8279 		}
   8280 		else if (!strcmp(cep->name, "default-server")) {
   8281 			CheckNull(cep);
   8282 			CheckDuplicate(cep, default_server, "default-server");
   8283 		}
   8284 		else if (!strcmp(cep->name, "services-server")) {
   8285 			CheckNull(cep);
   8286 			CheckDuplicate(cep, services_server, "services-server");
   8287 		}
   8288 		else if (!strcmp(cep->name, "sasl-server")) {
   8289 			CheckNull(cep);
   8290 			CheckDuplicate(cep, sasl_server, "sasl-server");
   8291 		}
   8292 		else if (!strcmp(cep->name, "stats-server")) {
   8293 			CheckNull(cep);
   8294 			CheckDuplicate(cep, stats_server, "stats-server");
   8295 		}
   8296 		else if (!strcmp(cep->name, "help-channel")) {
   8297 			CheckNull(cep);
   8298 			CheckDuplicate(cep, help_channel, "help-channel");
   8299 		}
   8300 		else if (!strcmp(cep->name, "cloak-prefix") || !strcmp(cep->name, "hiddenhost-prefix")) {
   8301 			CheckNull(cep);
   8302 			CheckDuplicate(cep, hiddenhost_prefix, "cloak-prefix");
   8303 			if (strchr(cep->value, ' ') || (*cep->value == ':'))
   8304 			{
   8305 				config_error("%s:%i: set::cloak-prefix must not contain spaces or be prefixed with ':'",
   8306 					cep->file->filename, cep->line_number);
   8307 				errors++;
   8308 				continue;
   8309 			}
   8310 		}
   8311 		else if (!strcmp(cep->name, "prefix-quit")) {
   8312 			CheckNull(cep);
   8313 			CheckDuplicate(cep, prefix_quit, "prefix-quit");
   8314 		}
   8315 		else if (!strcmp(cep->name, "hide-ban-reason")) {
   8316 			CheckNull(cep);
   8317 			CheckDuplicate(cep, hide_ban_reason, "hide-ban-reason");
   8318 		}
   8319 		else if (!strcmp(cep->name, "restrict-usermodes"))
   8320 		{
   8321 			CheckNull(cep);
   8322 			CheckDuplicate(cep, restrict_usermodes, "restrict-usermodes");
   8323 			if (cep->name) {
   8324 				int warn = 0;
   8325 				char *p;
   8326 				for (p = cep->value; *p; p++)
   8327 					if ((*p == '+') || (*p == '-'))
   8328 						warn = 1;
   8329 				if (warn) {
   8330 					config_status("%s:%i: warning: set::restrict-usermodes: should only contain modechars, no + or -.\n",
   8331 						cep->file->filename, cep->line_number);
   8332 				}
   8333 			}
   8334 		}
   8335 		else if (!strcmp(cep->name, "restrict-channelmodes"))
   8336 		{
   8337 			CheckNull(cep);
   8338 			CheckDuplicate(cep, restrict_channelmodes, "restrict-channelmodes");
   8339 			if (cep->name) {
   8340 				int warn = 0;
   8341 				char *p;
   8342 				for (p = cep->value; *p; p++)
   8343 					if ((*p == '+') || (*p == '-'))
   8344 						warn = 1;
   8345 				if (warn) {
   8346 					config_status("%s:%i: warning: set::restrict-channelmodes: should only contain modechars, no + or -.\n",
   8347 						cep->file->filename, cep->line_number);
   8348 				}
   8349 			}
   8350 		}
   8351 		else if (!strcmp(cep->name, "restrict-extendedbans"))
   8352 		{
   8353 			CheckDuplicate(cep, restrict_extendedbans, "restrict-extendedbans");
   8354 			CheckNull(cep);
   8355 		}
   8356 		else if (!strcmp(cep->name, "named-extended-bans"))
   8357 		{
   8358 			CheckNull(cep);
   8359 		}
   8360 		else if (!strcmp(cep->name, "link")) {
   8361 					for (cepp = cep->items; cepp; cepp = cepp->next) {
   8362 						CheckNull(cepp);
   8363 						if (!strcmp(cepp->name, "bind-ip")) {
   8364 							CheckDuplicate(cepp, link_bind_ip, "link::bind-ip");
   8365 							if (strcmp(cepp->value, "*"))
   8366 							{
   8367 								if (!is_valid_ip(cepp->value))
   8368 								{
   8369 									config_error("%s:%i: set::link::bind-ip (%s) is not a valid IP",
   8370 										cepp->file->filename, cepp->line_number,
   8371 										cepp->value);
   8372 									errors++;
   8373 									continue;
   8374 								}
   8375 							}
   8376 						}
   8377 					}
   8378 		}
   8379 		else if (!strcmp(cep->name, "throttle")) {
   8380 			config_error("%s:%i: set::throttle has been renamed. you now use "
   8381 			             "set::anti-flood::connect-flood <connections>:<period>. "
   8382 			             "Or just remove the throttle block and you get the default "
   8383 			             "of 3 per 60 seconds.",
   8384 			             cep->file->filename, cep->line_number);
   8385 			errors++;
   8386 			continue;
   8387 		}
   8388 		else if (!strcmp(cep->name, "anti-flood"))
   8389 		{
   8390 			int anti_flood_old = 0;
   8391 			int anti_flood_old_and_default = 0;
   8392 
   8393 			for (cepp = cep->items; cepp; cepp = cepp->next)
   8394 			{
   8395 				int has_lag_penalty = 0;
   8396 				int has_lag_penalty_bytes = 0;
   8397 
   8398 				/* Test for old options: */
   8399 				if (flood_option_is_old(cepp->name))
   8400 				{
   8401 					/* Special code if the user is using 100% of the defaults */
   8402 					if (cepp->value &&
   8403 					    ((!strcmp(cepp->name, "nick-flood") && !strcmp(cepp->value, "3:60")) ||
   8404 					     (!strcmp(cepp->name, "connect-flood") && cepp->value && !strcmp(cepp->value, "3:60")) ||
   8405 					     (!strcmp(cepp->name, "away-flood") && cepp->value && !strcmp(cepp->value, "4:120"))))
   8406 					{
   8407 						anti_flood_old_and_default = 1;
   8408 					} else
   8409 					{
   8410 						anti_flood_old = 1;
   8411 					}
   8412 					continue;
   8413 				}
   8414 
   8415 				for (ceppp = cepp->items; ceppp; ceppp = ceppp->next)
   8416 				{
   8417 					int everyone;
   8418 					int for_everyone;
   8419 					int used = 0;
   8420 					Hook *h;
   8421 
   8422 					/* First, check hooks... */
   8423 					for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next)
   8424 					{
   8425 						int value, errs = 0;
   8426 						if (h->owner && !(h->owner->flags & MODFLAG_TESTING)
   8427 							&& !(h->owner->options & MOD_OPT_PERM))
   8428 							continue;
   8429 						value = (*(h->func.intfunc))(conf,ceppp,CONFIG_SET_ANTI_FLOOD,&errs);
   8430 						if (value == 2)
   8431 						{
   8432 							used = 2;
   8433 							break;
   8434 						} else
   8435 						if (value == 1)
   8436 						{
   8437 							used = 1;
   8438 							break;
   8439 						} else
   8440 						if (value == -1)
   8441 						{
   8442 							used = 1;
   8443 							errors += errs;
   8444 							break;
   8445 						} else
   8446 						if (value == -2)
   8447 						{
   8448 							used = 2;
   8449 							errors += errs;
   8450 							break;
   8451 						}
   8452 					}
   8453 					if (used == 1)
   8454 						continue; /* module handled it */
   8455 					if (used == 2)
   8456 						break; /* module handled it and we must stop entire block processing */
   8457 
   8458 					/* Prevent users from using options that belong in "everyone"
   8459 					 * at other places, and vice-versa.
   8460 					 */
   8461 					everyone = !strcmp(cepp->name, "everyone") ? 1 : 0;
   8462 					for_everyone = flood_option_is_for_everyone(ceppp->name);
   8463 					if (everyone && !for_everyone)
   8464 					{
   8465 						config_error("%s:%i: %s cannot be in the set::anti-flood::everyone block. "
   8466 						             "You can put it in 'known-users' or 'unknown-users' instead.",
   8467 							ceppp->file->filename, ceppp->line_number,
   8468 							ceppp->name);
   8469 						errors++;
   8470 						continue;
   8471 					} else
   8472 					if (!everyone && for_everyone)
   8473 					{
   8474 						config_error("%s:%i: %s must be in the set::anti-flood::everyone block, not anywhere else.",
   8475 							ceppp->file->filename, ceppp->line_number,
   8476 							ceppp->name);
   8477 						errors++;
   8478 						continue;
   8479 					}
   8480 
   8481 					/* Now comes the actual config check for each element... */
   8482 					if (!strcmp(ceppp->name, "max-concurrent-conversations"))
   8483 					{
   8484 						for (cep4 = ceppp->items; cep4; cep4 = cep4->next)
   8485 						{
   8486 							CheckNull(cep4);
   8487 							if (!strcmp(cep4->name, "users"))
   8488 							{
   8489 								int v = atoi(cep4->value);
   8490 								if ((v < 1) || (v > MAXCCUSERS))
   8491 								{
   8492 									config_error("%s:%i: set::anti-flood::max-concurrent-conversations::users: "
   8493 										     "value should be between 1 and %d",
   8494 										     cep4->file->filename, cep4->line_number, MAXCCUSERS);
   8495 									errors++;
   8496 								}
   8497 							} else
   8498 							if (!strcmp(cep4->name, "new-user-every"))
   8499 							{
   8500 								long v = config_checkval(cep4->value, CFG_TIME);
   8501 								if ((v < 1) || (v > 120))
   8502 								{
   8503 									config_error("%s:%i: set::anti-flood::max-concurrent-conversations::new-user-every: "
   8504 										     "value should be between 1 and 120 seconds",
   8505 										     cep4->file->filename, cep4->line_number);
   8506 									errors++;
   8507 								}
   8508 							} else
   8509 							{
   8510 								config_error_unknownopt(cep4->file->filename,
   8511 									cep4->line_number, "set::anti-flood",
   8512 									cep4->name);
   8513 								errors++;
   8514 							}
   8515 						}
   8516 						continue; /* required here, due to checknull directly below */
   8517 					}
   8518 					else if (!strcmp(ceppp->name, "unknown-flood-amount") ||
   8519 						 !strcmp(ceppp->name, "unknown-flood-bantime"))
   8520 					{
   8521 						config_error("%s:%i: set::anti-flood::%s: this setting has been moved. "
   8522 							     "See https://www.unrealircd.org/docs/Anti-flood_settings#handshake-data-flood",
   8523 							     ceppp->file->filename, ceppp->line_number, ceppp->name);
   8524 						errors++;
   8525 						continue;
   8526 					}
   8527 					else if (!strcmp(ceppp->name, "handshake-data-flood"))
   8528 					{
   8529 						for (cep4 = ceppp->items; cep4; cep4 = cep4->next)
   8530 						{
   8531 							if (!strcmp(cep4->name, "amount"))
   8532 							{
   8533 								long v;
   8534 								CheckNull(cep4);
   8535 								v = config_checkval(cep4->value, CFG_SIZE);
   8536 								if (v < 1024)
   8537 								{
   8538 									config_error("%s:%i: set::anti-flood::handshake-data-flood::amount must be at least 1024 bytes",
   8539 										cep4->file->filename, cep4->line_number);
   8540 									errors++;
   8541 								}
   8542 							} else
   8543 							if (!strcmp(cep4->name, "ban-action"))
   8544 							{
   8545 								CheckNull(cep4);
   8546 								if (!banact_stringtoval(cep4->value))
   8547 								{
   8548 									config_error("%s:%i: set::anti-flood::handshake-data-flood::ban-action has unknown action type '%s'",
   8549 										cep4->file->filename, cep4->line_number,
   8550 										cep4->value);
   8551 									errors++;
   8552 								}
   8553 							} else
   8554 							if (!strcmp(cep4->name, "ban-time"))
   8555 							{
   8556 								CheckNull(cep4);
   8557 							} else
   8558 							{
   8559 								config_error_unknownopt(cep4->file->filename,
   8560 									cep4->line_number, "set::anti-flood::handshake-data-flood",
   8561 									cep4->name);
   8562 								errors++;
   8563 							}
   8564 						}
   8565 					}
   8566 					else if (!strcmp(ceppp->name, "away-count"))
   8567 					{
   8568 						int temp = atol(ceppp->value);
   8569 						CheckNull(ceppp);
   8570 						if (temp < 1 || temp > 255)
   8571 						{
   8572 							config_error("%s:%i: set::anti-flood::away-count must be between 1 and 255",
   8573 								ceppp->file->filename, ceppp->line_number);
   8574 							errors++;
   8575 						}
   8576 					}
   8577 					else if (!strcmp(ceppp->name, "away-period"))
   8578 					{
   8579 						CheckNull(ceppp);
   8580 						int temp = config_checkval(ceppp->value, CFG_TIME);
   8581 						if (temp < 10)
   8582 						{
   8583 							config_error("%s:%i: set::anti-flood::away-period must be greater than 9",
   8584 								ceppp->file->filename, ceppp->line_number);
   8585 							errors++;
   8586 						}
   8587 					}
   8588 					else if (!strcmp(ceppp->name, "away-flood"))
   8589 					{
   8590 						int cnt, period;
   8591 						CheckNull(ceppp);
   8592 						if (!config_parse_flood(ceppp->value, &cnt, &period) ||
   8593 						    (cnt < 1) || (cnt > 255) || (period < 10))
   8594 						{
   8595 							config_error("%s:%i: set::anti-flood::away-flood error. Syntax is '<count>:<period>' (eg 5:60), "
   8596 								     "count should be 1-255, period should be greater than 9",
   8597 								ceppp->file->filename, ceppp->line_number);
   8598 							errors++;
   8599 						}
   8600 					}
   8601 					else if (!strcmp(ceppp->name, "nick-flood"))
   8602 					{
   8603 						int cnt, period;
   8604 						CheckNull(ceppp);
   8605 						if (!config_parse_flood(ceppp->value, &cnt, &period) ||
   8606 						    (cnt < 1) || (cnt > 255) || (period < 5))
   8607 						{
   8608 							config_error("%s:%i: set::anti-flood::nick-flood error. Syntax is '<count>:<period>' (eg 5:60), "
   8609 								     "count should be 1-255, period should be greater than 4",
   8610 								ceppp->file->filename, ceppp->line_number);
   8611 							errors++;
   8612 						}
   8613 					}
   8614 					else if (!strcmp(ceppp->name, "vhost-flood"))
   8615 					{
   8616 						int cnt, period;
   8617 						CheckNull(ceppp);
   8618 						if (!config_parse_flood(ceppp->value, &cnt, &period) ||
   8619 						    (cnt < 1) || (cnt > 255) || (period < 5))
   8620 						{
   8621 							config_error("%s:%i: set::anti-flood::vhost-flood error. Syntax is '<count>:<period>' (eg 5:60), "
   8622 								     "count should be 1-255, period should be greater than 4",
   8623 								ceppp->file->filename, ceppp->line_number);
   8624 							errors++;
   8625 						}
   8626 					}
   8627 					else if (!strcmp(ceppp->name, "join-flood"))
   8628 					{
   8629 						int cnt, period;
   8630 						CheckNull(ceppp);
   8631 
   8632 						if (!config_parse_flood(ceppp->value, &cnt, &period) ||
   8633 						    (cnt < 1) || (cnt > 255) || (period < 5))
   8634 						{
   8635 							config_error("%s:%i: join-flood error. Syntax is '<count>:<period>' (eg 5:60), "
   8636 								     "count should be 1-255, period should be greater than 4",
   8637 								ceppp->file->filename, ceppp->line_number);
   8638 							errors++;
   8639 						}
   8640 					}
   8641 					else if (!strcmp(ceppp->name, "invite-flood"))
   8642 					{
   8643 						int cnt, period;
   8644 						CheckNull(ceppp);
   8645 						if (!config_parse_flood(ceppp->value, &cnt, &period) ||
   8646 						    (cnt < 1) || (cnt > 255) || (period < 5))
   8647 						{
   8648 							config_error("%s:%i: set::anti-flood::invite-flood error. Syntax is '<count>:<period>' (eg 5:60), "
   8649 								     "count should be 1-255, period should be greater than 4",
   8650 								ceppp->file->filename, ceppp->line_number);
   8651 							errors++;
   8652 						}
   8653 					}
   8654 					else if (!strcmp(ceppp->name, "knock-flood"))
   8655 					{
   8656 						int cnt, period;
   8657 						CheckNull(ceppp);
   8658 						if (!config_parse_flood(ceppp->value, &cnt, &period) ||
   8659 						    (cnt < 1) || (cnt > 255) || (period < 5))
   8660 						{
   8661 							config_error("%s:%i: set::anti-flood::knock-flood error. Syntax is '<count>:<period>' (eg 5:60), "
   8662 								     "count should be 1-255, period should be greater than 4",
   8663 								ceppp->file->filename, ceppp->line_number);
   8664 							errors++;
   8665 						}
   8666 					}
   8667 					else if (!strcmp(ceppp->name, "lag-penalty"))
   8668 					{
   8669 						int v;
   8670 						CheckNull(ceppp);
   8671 						v = atoi(ceppp->value);
   8672 						has_lag_penalty = 1;
   8673 						if ((v < 0) || (v > 10000))
   8674 						{
   8675 							config_error("%s:%i: set::anti-flood::%s::lag-penalty: value is in milliseconds and should be between 0 and 10000",
   8676 								ceppp->file->filename, ceppp->line_number, cepp->name);
   8677 							errors++;
   8678 						}
   8679 					}
   8680 					else if (!strcmp(ceppp->name, "lag-penalty-bytes"))
   8681 					{
   8682 						has_lag_penalty_bytes = 1;
   8683 						CheckNull(ceppp);
   8684 					}
   8685 					else if (!strcmp(ceppp->name, "connect-flood"))
   8686 					{
   8687 						int cnt, period;
   8688 						CheckNull(ceppp);
   8689 						if (strcmp(cepp->name, "everyone"))
   8690 						{
   8691 							config_error("%s:%i: connect-flood must be in the set::anti-flood::everyone block, not anywhere else.",
   8692 								ceppp->file->filename, ceppp->line_number);
   8693 							errors++;
   8694 							continue;
   8695 						}
   8696 						if (!config_parse_flood(ceppp->value, &cnt, &period) ||
   8697 						    (cnt < 1) || (cnt > 255) || (period < 1) || (period > 3600))
   8698 						{
   8699 							config_error("%s:%i: set::anti-flood::connect-flood: Syntax is '<count>:<period>' (eg 5:60), "
   8700 								     "count should be 1-255, period should be 1-3600",
   8701 								ceppp->file->filename, ceppp->line_number);
   8702 							errors++;
   8703 						}
   8704 					}
   8705 					else
   8706 					{
   8707 						config_error_unknownopt(ceppp->file->filename,
   8708 							ceppp->line_number, "set::anti-flood",
   8709 							ceppp->name);
   8710 						errors++;
   8711 					}
   8712 				}
   8713 				if (has_lag_penalty+has_lag_penalty_bytes == 1)
   8714 				{
   8715 					config_error("%s:%i: set::anti-flood::%s: if you use lag-penalty then you must also add an lag-penalty-bytes item (and vice-versa)",
   8716 						cepp->file->filename, cepp->line_number, cepp->name);
   8717 					errors++;
   8718 				}
   8719 			}
   8720 			/* Now the warnings: */
   8721 			if (anti_flood_old == 1)
   8722 			{
   8723 				config_warn("%s:%d: the set::anti-flood block has been reorganized to be more flexible. "
   8724 				            "Your custom anti-flood settings have NOT been read.",
   8725 				            cep->file->filename, cep->line_number);
   8726 				config_warn("See https://www.unrealircd.org/docs/Anti-flood_settings for the new block style,");
   8727 				config_warn("OR: simply remove all the anti-flood options from the conf to get rid of this "
   8728 				            "warning and use the built-in defaults.");
   8729 			} else
   8730 			if (anti_flood_old_and_default == 1)
   8731 			{
   8732 				config_warn("%s:%d: the set::anti-flood block has been reorganized to be more flexible.",
   8733 					    cep->file->filename, cep->line_number);
   8734 				config_warn("To fix this warning, delete the anti-flood block from your configuration file "
   8735 				            "(file %s around line %d), this will make UnrealIRCd use the built-in defaults.",
   8736 				            cep->file->filename, cep->line_number);
   8737 				config_warn("If you want to learn more about the new functionality you can visit "
   8738 				            "https://www.unrealircd.org/docs/Anti-flood_settings");
   8739 			}
   8740 		}
   8741 		else if (!strcmp(cep->name, "options")) {
   8742 			for (cepp = cep->items; cepp; cepp = cepp->next) {
   8743 				if (!strcmp(cepp->name, "hide-ulines"))
   8744 				{
   8745 					CheckDuplicate(cepp, options_hide_ulines, "options::hide-ulines");
   8746 				}
   8747 				else if (!strcmp(cepp->name, "flat-map")) {
   8748 					CheckDuplicate(cepp, options_flat_map, "options::flat-map");
   8749 				}
   8750 				else if (!strcmp(cepp->name, "show-opermotd")) {
   8751 					CheckDuplicate(cepp, options_show_opermotd, "options::show-opermotd");
   8752 				}
   8753 				else if (!strcmp(cepp->name, "identd-check")) {
   8754 					CheckDuplicate(cepp, options_identd_check, "options::identd-check");
   8755 				}
   8756 				else if (!strcmp(cepp->name, "fail-oper-warn")) {
   8757 					CheckDuplicate(cepp, options_fail_oper_warn, "options::fail-oper-warn");
   8758 				}
   8759 				else if (!strcmp(cepp->name, "show-connect-info")) {
   8760 					CheckDuplicate(cepp, options_show_connect_info, "options::show-connect-info");
   8761 				}
   8762 				else if (!strcmp(cepp->name, "no-connect-tls-info")) {
   8763 					CheckDuplicate(cepp, options_no_connect_tls_info, "options::no-connect-tls-info");
   8764 				}
   8765 				else if (!strcmp(cepp->name, "dont-resolve")) {
   8766 					CheckDuplicate(cepp, options_dont_resolve, "options::dont-resolve");
   8767 				}
   8768 				else if (!strcmp(cepp->name, "mkpasswd-for-everyone")) {
   8769 					CheckDuplicate(cepp, options_mkpasswd_for_everyone, "options::mkpasswd-for-everyone");
   8770 				}
   8771 				else if (!strcmp(cepp->name, "allow-insane-bans")) {
   8772 					CheckDuplicate(cepp, options_allow_insane_bans, "options::allow-insane-bans");
   8773 				}
   8774 				else if (!strcmp(cepp->name, "allow-part-if-shunned")) {
   8775 					CheckDuplicate(cepp, options_allow_part_if_shunned, "options::allow-part-if-shunned");
   8776 				}
   8777 				else if (!strcmp(cepp->name, "disable-cap")) {
   8778 					CheckDuplicate(cepp, options_disable_cap, "options::disable-cap");
   8779 				}
   8780 				else if (!strcmp(cepp->name, "disable-ipv6")) {
   8781 					CheckDuplicate(cepp, options_disable_ipv6, "options::disable-ipv6");
   8782 					DISABLE_IPV6 = 1; /* ugly ugly. needs to be done here because at conf runtime is too late. */
   8783 				}
   8784 				else
   8785 				{
   8786 					config_error_unknownopt(cepp->file->filename,
   8787 						cepp->line_number, "set::options",
   8788 						cepp->name);
   8789 					errors++;
   8790 					continue;
   8791 				}
   8792 			}
   8793 		}
   8794 		else if (!strcmp(cep->name, "hosts")) {
   8795 			config_error("%s:%i: set::hosts has been removed. You can use oper::vhost now.",
   8796 				cep->file->filename, cep->line_number);
   8797 			errors++;
   8798 		}
   8799 		else if (!strcmp(cep->name, "cloak-keys"))
   8800 		{
   8801 			CheckDuplicate(cep, cloak_keys, "cloak-keys");
   8802 			for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next)
   8803 			{
   8804 				int value, errs = 0;
   8805 				if (h->owner && !(h->owner->flags & MODFLAG_TESTING)
   8806 				    && !(h->owner->options & MOD_OPT_PERM))
   8807 					continue;
   8808 				value = (*(h->func.intfunc))(conf, cep, CONFIG_CLOAKKEYS, &errs);
   8809 
   8810 				if (value == 1)
   8811 					break;
   8812 				if (value == -1)
   8813 				{
   8814 					errors += errs;
   8815 					break;
   8816 				}
   8817 				if (value == -2)
   8818 					errors += errs;
   8819 			}
   8820 		}
   8821 		else if (!strcmp(cep->name, "ident")) {
   8822 			for (cepp = cep->items; cepp; cepp = cepp->next)
   8823 			{
   8824 				int is_ok = 0;
   8825 				CheckNull(cepp);
   8826 				if (!strcmp(cepp->name, "connect-timeout"))
   8827 				{
   8828 					is_ok = 1;
   8829 					CheckDuplicate(cepp, ident_connect_timeout, "ident::connect-timeout");
   8830 				}
   8831 				else if (!strcmp(cepp->name, "read-timeout"))
   8832 				{
   8833 					is_ok = 1;
   8834 					CheckDuplicate(cepp, ident_read_timeout, "ident::read-timeout");
   8835 				}
   8836 				if (is_ok)
   8837 				{
   8838 					int v = config_checkval(cepp->value,CFG_TIME);
   8839 					if ((v > 60) || (v < 1))
   8840 					{
   8841 						config_error("%s:%i: set::ident::%s value out of range (%d), should be between 1 and 60.",
   8842 							cepp->file->filename, cepp->line_number, cepp->name, v);
   8843 						errors++;
   8844 						continue;
   8845 					}
   8846 				} else {
   8847 					config_error_unknown(cepp->file->filename,
   8848 						cepp->line_number, "set::ident",
   8849 						cepp->name);
   8850 					errors++;
   8851 					continue;
   8852 				}
   8853 			}
   8854 		}
   8855 		else if (!strcmp(cep->name, "timesync") || !strcmp(cep->name, "timesynch"))
   8856 		{
   8857 			config_warn("%s:%i: Timesync support has been removed from UnrealIRCd. "
   8858 			            "Please remove any set::timesync blocks you may have.",
   8859 			            cep->file->filename, cep->line_number);
   8860 			config_warn("Use the time synchronization feature of your OS/distro instead!");
   8861 		}
   8862 		else if (!strcmp(cep->name, "spamfilter")) {
   8863 			for (cepp = cep->items; cepp; cepp = cepp->next)
   8864 			{
   8865 				CheckNull(cepp);
   8866 				if (!strcmp(cepp->name, "ban-time"))
   8867 				{
   8868 					long x;
   8869 					CheckDuplicate(cepp, spamfilter_ban_time, "spamfilter::ban-time");
   8870 					x = config_checkval(cepp->value,CFG_TIME);
   8871 					if ((x < 0) > (x > 2000000000))
   8872 					{
   8873 						config_error("%s:%i: set::spamfilter:ban-time: value '%ld' out of range",
   8874 							cep->file->filename, cep->line_number, x);
   8875 						errors++;
   8876 						continue;
   8877 					}
   8878 				} else
   8879 				if (!strcmp(cepp->name, "ban-reason"))
   8880 				{
   8881 					CheckDuplicate(cepp, spamfilter_ban_reason, "spamfilter::ban-reason");
   8882 
   8883 				}
   8884 				else if (!strcmp(cepp->name, "virus-help-channel"))
   8885 				{
   8886 					CheckDuplicate(cepp, spamfilter_virus_help_channel, "spamfilter::virus-help-channel");
   8887 					if ((cepp->value[0] != '#') || (strlen(cepp->value) > CHANNELLEN))
   8888 					{
   8889 						config_error("%s:%i: set::spamfilter:virus-help-channel: "
   8890 						             "specified channelname is too long or contains invalid characters (%s)",
   8891 						             cep->file->filename, cep->line_number,
   8892 						             cepp->value);
   8893 						errors++;
   8894 						continue;
   8895 					}
   8896 				} else
   8897 				if (!strcmp(cepp->name, "virus-help-channel-deny"))
   8898 				{
   8899 					CheckDuplicate(cepp, spamfilter_virus_help_channel_deny, "spamfilter::virus-help-channel-deny");
   8900 				} else
   8901 				if (!strcmp(cepp->name, "except"))
   8902 				{
   8903 					CheckDuplicate(cepp, spamfilter_except, "spamfilter::except");
   8904 				} else
   8905 #ifdef SPAMFILTER_DETECTSLOW
   8906 				if (!strcmp(cepp->name, "detect-slow-warn"))
   8907 				{
   8908 				} else
   8909 				if (!strcmp(cepp->name, "detect-slow-fatal"))
   8910 				{
   8911 				} else
   8912 #endif
   8913 				if (!strcmp(cepp->name, "stop-on-first-match"))
   8914 				{
   8915 				} else
   8916 				if (!strcmp(cepp->name, "utf8"))
   8917 				{
   8918 				} else
   8919 				{
   8920 					config_error_unknown(cepp->file->filename,
   8921 						cepp->line_number, "set::spamfilter",
   8922 						cepp->name);
   8923 					errors++;
   8924 					continue;
   8925 				}
   8926 			}
   8927 		}
   8928 		else if (!strcmp(cep->name, "default-bantime"))
   8929 		{
   8930 			long x;
   8931 			CheckDuplicate(cep, default_bantime, "default-bantime");
   8932 			CheckNull(cep);
   8933 			x = config_checkval(cep->value,CFG_TIME);
   8934 			if ((x < 0) > (x > 2000000000))
   8935 			{
   8936 				config_error("%s:%i: set::default-bantime: value '%ld' out of range",
   8937 					cep->file->filename, cep->line_number, x);
   8938 				errors++;
   8939 			}
   8940 		}
   8941 		else if (!strcmp(cep->name, "ban-version-tkl-time")) {
   8942 			long x;
   8943 			CheckDuplicate(cep, ban_version_tkl_time, "ban-version-tkl-time");
   8944 			CheckNull(cep);
   8945 			x = config_checkval(cep->value,CFG_TIME);
   8946 			if ((x < 0) > (x > 2000000000))
   8947 			{
   8948 				config_error("%s:%i: set::ban-version-tkl-time: value '%ld' out of range",
   8949 					cep->file->filename, cep->line_number, x);
   8950 				errors++;
   8951 			}
   8952 		}
   8953 		else if (!strcmp(cep->name, "min-nick-length")) {
   8954 			int v;
   8955 			CheckDuplicate(cep, min_nick_length, "min-nick-length");
   8956 			CheckNull(cep);
   8957 			v = atoi(cep->value);
   8958 			if ((v <= 0) || (v > NICKLEN))
   8959 			{
   8960 				config_error("%s:%i: set::min-nick-length: value '%d' out of range (should be 1-%d)",
   8961 					cep->file->filename, cep->line_number, v, NICKLEN);
   8962 				errors++;
   8963 			}
   8964 			else
   8965 				nicklengths.min = v;
   8966 		}
   8967 		else if (!strcmp(cep->name, "nick-length")) {
   8968 			int v;
   8969 			CheckDuplicate(cep, nick_length, "nick-length");
   8970 			CheckNull(cep);
   8971 			v = atoi(cep->value);
   8972 			if ((v <= 0) || (v > NICKLEN))
   8973 			{
   8974 				config_error("%s:%i: set::nick-length: value '%d' out of range (should be 1-%d)",
   8975 					cep->file->filename, cep->line_number, v, NICKLEN);
   8976 				errors++;
   8977 			}
   8978 			else
   8979 				nicklengths.max = v;
   8980 		}
   8981 		else if (!strcmp(cep->name, "topic-length")) {
   8982 			int v;
   8983 			CheckNull(cep);
   8984 			v = atoi(cep->value);
   8985 			if ((v <= 0) || (v > MAXTOPICLEN))
   8986 			{
   8987 				config_error("%s:%i: set::topic-length: value '%d' out of range (should be 1-%d)",
   8988 					cep->file->filename, cep->line_number, v, MAXTOPICLEN);
   8989 				errors++;
   8990 			}
   8991 		}
   8992 		else if (!strcmp(cep->name, "away-length")) {
   8993 			int v;
   8994 			CheckNull(cep);
   8995 			v = atoi(cep->value);
   8996 			if ((v <= 0) || (v > MAXAWAYLEN))
   8997 			{
   8998 				config_error("%s:%i: set::away-length: value '%d' out of range (should be 1-%d)",
   8999 					cep->file->filename, cep->line_number, v, MAXAWAYLEN);
   9000 				errors++;
   9001 			}
   9002 		}
   9003 		else if (!strcmp(cep->name, "kick-length")) {
   9004 			int v;
   9005 			CheckNull(cep);
   9006 			v = atoi(cep->value);
   9007 			if ((v <= 0) || (v > MAXKICKLEN))
   9008 			{
   9009 				config_error("%s:%i: set::kick-length: value '%d' out of range (should be 1-%d)",
   9010 					cep->file->filename, cep->line_number, v, MAXKICKLEN);
   9011 				errors++;
   9012 			}
   9013 		}
   9014 		else if (!strcmp(cep->name, "quit-length")) {
   9015 			int v;
   9016 			CheckNull(cep);
   9017 			v = atoi(cep->value);
   9018 			if ((v <= 0) || (v > MAXQUITLEN))
   9019 			{
   9020 				config_error("%s:%i: set::quit-length: value '%d' out of range (should be 1-%d)",
   9021 					cep->file->filename, cep->line_number, v, MAXQUITLEN);
   9022 				errors++;
   9023 			}
   9024 		}
   9025 		else if (!strcmp(cep->name, "ssl") || !strcmp(cep->name, "tls")) {
   9026 			test_tlsblock(conf, cep, &errors);
   9027 		}
   9028 		else if (!strcmp(cep->name, "plaintext-policy"))
   9029 		{
   9030 			for (cepp = cep->items; cepp; cepp = cepp->next)
   9031 			{
   9032 				if (!strcmp(cepp->name, "user") ||
   9033 					!strcmp(cepp->name, "oper") ||
   9034 					!strcmp(cepp->name, "server"))
   9035 				{
   9036 					Policy policy;
   9037 					CheckNull(cepp);
   9038 					policy = policy_strtoval(cepp->value);
   9039 					if (!policy)
   9040 					{
   9041 						config_error("%s:%i: set::plaintext-policy::%s: needs to be one of: 'allow', 'warn' or 'reject'",
   9042 							cepp->file->filename, cepp->line_number, cepp->name);
   9043 						errors++;
   9044 					}
   9045 				} else if (!strcmp(cepp->name, "user-message") ||
   9046 				           !strcmp(cepp->name, "oper-message"))
   9047 				{
   9048 					CheckNull(cepp);
   9049 				} else {
   9050 					config_error_unknown(cepp->file->filename,
   9051 						cepp->line_number, "set::plaintext-policy",
   9052 						cepp->name);
   9053 					errors++;
   9054 					continue;
   9055 				}
   9056 			}
   9057 		}
   9058 		else if (!strcmp(cep->name, "outdated-tls-policy"))
   9059 		{
   9060 			for (cepp = cep->items; cepp; cepp = cepp->next)
   9061 			{
   9062 				if (!strcmp(cepp->name, "user") ||
   9063 					!strcmp(cepp->name, "oper") ||
   9064 					!strcmp(cepp->name, "server"))
   9065 				{
   9066 					Policy policy;
   9067 					CheckNull(cepp);
   9068 					policy = policy_strtoval(cepp->value);
   9069 					if (!policy)
   9070 					{
   9071 						config_error("%s:%i: set::outdated-tls-policy::%s: needs to be one of: 'allow', 'warn' or 'reject'",
   9072 							cepp->file->filename, cepp->line_number, cepp->name);
   9073 						errors++;
   9074 					}
   9075 				} else if (!strcmp(cepp->name, "user-message") ||
   9076 				           !strcmp(cepp->name, "oper-message"))
   9077 				{
   9078 					CheckNull(cepp);
   9079 				} else {
   9080 					config_error_unknown(cepp->file->filename,
   9081 						cepp->line_number, "set::outdated-tls-policy",
   9082 						cepp->name);
   9083 					errors++;
   9084 					continue;
   9085 				}
   9086 			}
   9087 		}
   9088 		else if (!strcmp(cep->name, "default-ipv6-clone-mask"))
   9089 		{
   9090 			/* keep this in sync with _test_allow() */
   9091 			int ipv6mask;
   9092 			CheckNull(cep);
   9093 			ipv6mask = atoi(cep->value);
   9094 			if (ipv6mask == 0)
   9095 			{
   9096 				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.",
   9097 					     cep->file->filename, cep->line_number);
   9098 				errors++;
   9099 			}
   9100 			if (ipv6mask > 128)
   9101 			{
   9102 				config_error("%s:%d: set::default-ipv6-clone-mask was set to %d. The maximum value is 128.",
   9103 					     cep->file->filename, cep->line_number,
   9104 					     ipv6mask);
   9105 				errors++;
   9106 			}
   9107 			if (ipv6mask <= 32)
   9108 			{
   9109 				config_warn("%s:%d: set::default-ipv6-clone-mask was given a very small value.",
   9110 					    cep->file->filename, cep->line_number);
   9111 			}
   9112 		}
   9113 		else if (!strcmp(cep->name, "hide-list")) {
   9114 			for (cepp = cep->items; cepp; cepp = cepp->next)
   9115 			{
   9116 				if (!strcmp(cepp->name, "deny-channel"))
   9117 				{
   9118 				} else
   9119 				{
   9120 					config_error_unknown(cepp->file->filename,
   9121 						cepp->line_number, "set::hide-list",
   9122 						cepp->name);
   9123 					errors++;
   9124 					continue;
   9125 				}
   9126 			}
   9127 		}
   9128 		else if (!strcmp(cep->name, "max-unknown-connections-per-ip")) {
   9129 			int v;
   9130 			CheckNull(cep);
   9131 			v = atoi(cep->value);
   9132 			if (v < 1)
   9133 			{
   9134 				config_error("%s:%i: set::max-unknown-connections-per-ip: value should be at least 1.",
   9135 					cep->file->filename, cep->line_number);
   9136 				errors++;
   9137 			}
   9138 		}
   9139 		else if (!strcmp(cep->name, "handshake-timeout")) {
   9140 			int v;
   9141 			CheckNull(cep);
   9142 			v = config_checkval(cep->value, CFG_TIME);
   9143 			if (v < 5)
   9144 			{
   9145 				config_error("%s:%i: set::handshake-timeout: value should be at least 5 seconds.",
   9146 					cep->file->filename, cep->line_number);
   9147 				errors++;
   9148 			}
   9149 		}
   9150 		else if (!strcmp(cep->name, "sasl-timeout")) {
   9151 			int v;
   9152 			CheckNull(cep);
   9153 			v = config_checkval(cep->value, CFG_TIME);
   9154 			if (v < 5)
   9155 			{
   9156 				config_error("%s:%i: set::sasl-timeout: value should be at least 5 seconds.",
   9157 					cep->file->filename, cep->line_number);
   9158 				errors++;
   9159 			}
   9160 		}
   9161 		else if (!strcmp(cep->name, "handshake-delay"))
   9162 		{
   9163 			int v;
   9164 			CheckNull(cep);
   9165 			v = config_checkval(cep->value, CFG_TIME);
   9166 			if (v >= 10)
   9167 			{
   9168 				config_error("%s:%i: set::handshake-delay: value should be less than 10 seconds.",
   9169 					cep->file->filename, cep->line_number);
   9170 				errors++;
   9171 			}
   9172 		}
   9173 		else if (!strcmp(cep->name, "ban-include-username"))
   9174 		{
   9175 			config_error("%s:%i: set::ban-include-username is no longer supported. "
   9176 			             "Use set { automatic-ban-target userip; }; instead.",
   9177 			             cep->file->filename, cep->line_number);
   9178 			config_error("See https://www.unrealircd.org/docs/Set_block#set::automatic-ban-target "
   9179 			             "for more information and options.");
   9180 			errors++;
   9181 		}
   9182 		else if (!strcmp(cep->name, "automatic-ban-target"))
   9183 		{
   9184 			CheckNull(cep);
   9185 			if (!ban_target_strtoval(cep->value))
   9186 			{
   9187 				config_error("%s:%i: set::automatic-ban-target: value '%s' is not recognized. "
   9188 				             "See https://www.unrealircd.org/docs/Set_block#set::automatic-ban-target",
   9189 				             cep->file->filename, cep->line_number, cep->value);
   9190 				errors++;
   9191 			}
   9192 		}
   9193 		else if (!strcmp(cep->name, "manual-ban-target"))
   9194 		{
   9195 			CheckNull(cep);
   9196 			if (!ban_target_strtoval(cep->value))
   9197 			{
   9198 				config_error("%s:%i: set::manual-ban-target: value '%s' is not recognized. "
   9199 				             "See https://www.unrealircd.org/docs/Set_block#set::manual-ban-target",
   9200 				             cep->file->filename, cep->line_number, cep->value);
   9201 				errors++;
   9202 			}
   9203 		}
   9204 		else if (!strcmp(cep->name, "reject-message"))
   9205 		{
   9206 			for (cepp = cep->items; cepp; cepp = cepp->next)
   9207 			{
   9208 				CheckNull(cepp);
   9209 				if (!strcmp(cepp->name, "password-mismatch"))
   9210 					;
   9211 				else if (!strcmp(cepp->name, "too-many-connections"))
   9212 					;
   9213 				else if (!strcmp(cepp->name, "server-full"))
   9214 					;
   9215 				else if (!strcmp(cepp->name, "unauthorized"))
   9216 					;
   9217 				else if (!strcmp(cepp->name, "kline"))
   9218 					;
   9219 				else if (!strcmp(cepp->name, "gline"))
   9220 					;
   9221 				else
   9222 				{
   9223 					config_error_unknown(cepp->file->filename,
   9224 						cepp->line_number, "set::reject-message",
   9225 						cepp->name);
   9226 					errors++;
   9227 					continue;
   9228 				}
   9229 			}
   9230 		}
   9231 		else if (!strcmp(cep->name, "topic-setter"))
   9232 		{
   9233 			CheckNull(cep);
   9234 			if (strcmp(cep->value, "nick") && strcmp(cep->value, "nick-user-host"))
   9235 			{
   9236 				config_error("%s:%i: set::topic-setter: value should be 'nick' or 'nick-user-host'",
   9237 					cep->file->filename, cep->line_number);
   9238 				errors++;
   9239 			}
   9240 		}
   9241 		else if (!strcmp(cep->name, "ban-setter"))
   9242 		{
   9243 			CheckNull(cep);
   9244 			if (strcmp(cep->value, "nick") && strcmp(cep->value, "nick-user-host"))
   9245 			{
   9246 				config_error("%s:%i: set::ban-setter: value should be 'nick' or 'nick-user-host'",
   9247 					cep->file->filename, cep->line_number);
   9248 				errors++;
   9249 			}
   9250 		}
   9251 		else if (!strcmp(cep->name, "ban-setter-sync") || !strcmp(cep->name, "ban-setter-synch"))
   9252 		{
   9253 			CheckNull(cep);
   9254 		}
   9255 		else if (!strcmp(cep->name, "part-instead-of-quit-on-comment-change"))
   9256 		{
   9257 			CheckNull(cep);
   9258 		}
   9259 		else if (!strcmp(cep->name, "broadcast-channel-messages"))
   9260 		{
   9261 			CheckNull(cep);
   9262 			if (strcmp(cep->value, "auto") &&
   9263 			    strcmp(cep->value, "always") &&
   9264 			    strcmp(cep->value, "never"))
   9265 			{
   9266 				config_error("%s:%i: set::broadcast-channel-messages: value should be 'auto', 'always' or 'never'",
   9267 				             cep->file->filename, cep->line_number);
   9268 				errors++;
   9269 			}
   9270 		}
   9271 		else if (!strcmp(cep->name, "allowed-channelchars"))
   9272 		{
   9273 			CheckNull(cep);
   9274 			if (!allowed_channelchars_strtoval(cep->value))
   9275 			{
   9276 				config_error("%s:%i: set::allowed-channelchars: value should be one of: 'ascii', 'utf8' or 'any'",
   9277 				             cep->file->filename, cep->line_number);
   9278 				errors++;
   9279 			}
   9280 		}
   9281 		else if (!strcmp(cep->name, "hide-idle-time"))
   9282 		{
   9283 			for (cepp = cep->items; cepp; cepp = cepp->next)
   9284 			{
   9285 				CheckNull(cepp);
   9286 				if (!strcmp(cepp->name, "policy"))
   9287 				{
   9288 					if (!hideidletime_strtoval(cepp->value))
   9289 					{
   9290 						config_error("%s:%i: set::hide-idle-time::policy: value should be one of: 'never', 'always', 'usermode' or 'oper-usermode'",
   9291 							     cepp->file->filename, cepp->line_number);
   9292 						errors++;
   9293 					}
   9294 				}
   9295 				else
   9296 				{
   9297 					config_error_unknown(cepp->file->filename,
   9298 						cepp->line_number, "set::hide-idle-time",
   9299 						cepp->name);
   9300 					errors++;
   9301 					continue;
   9302 				}
   9303 			}
   9304 		} else if (!strcmp(cep->name, "limit-svscmds"))
   9305 		{
   9306 			CheckNull(cep);
   9307 			if (strcmp(cep->value, "servers") && strcmp(cep->value, "ulines"))
   9308 			{
   9309 				config_error("%s:%i: set::limit-svscmds: value must be one of: 'servers' or 'ulines'",
   9310 				             cep->file->filename, cep->line_number);
   9311 				errors++;
   9312 			}
   9313 		} else
   9314 		{
   9315 			int used = 0;
   9316 			for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next)
   9317 			{
   9318 				int value, errs = 0;
   9319 				if (h->owner && !(h->owner->flags & MODFLAG_TESTING) &&
   9320 				                !(h->owner->options & MOD_OPT_PERM))
   9321 					continue;
   9322 				value = (*(h->func.intfunc))(conf,cep,CONFIG_SET, &errs);
   9323 				if (value == 2)
   9324 					used = 1;
   9325 				if (value == 1)
   9326 				{
   9327 					used = 1;
   9328 					break;
   9329 				}
   9330 				if (value == -1)
   9331 				{
   9332 					used = 1;
   9333 					errors += errs;
   9334 					break;
   9335 				}
   9336 				if (value == -2)
   9337 				{
   9338 					used = 1;
   9339 					errors += errs;
   9340 				}
   9341 			}
   9342 			if (!used) {
   9343 				config_error("%s:%i: unknown directive set::%s",
   9344 					cep->file->filename, cep->line_number,
   9345 					cep->name);
   9346 				errors++;
   9347 			}
   9348 		}
   9349 	}
   9350 	return errors;
   9351 }
   9352 
   9353 int	_conf_loadmodule(ConfigFile *conf, ConfigEntry *ce)
   9354 {
   9355 	const char *ret;
   9356 	if (!ce->value)
   9357 	{
   9358 		config_status("%s:%i: loadmodule without filename",
   9359 			ce->file->filename, ce->line_number);
   9360 		return -1;
   9361 	}
   9362 
   9363 	if (is_blacklisted_module(ce->value))
   9364 	{
   9365 		/* config_warn("%s:%i: Module '%s' is blacklisted, not loading",
   9366 			ce->file->filename, ce->line_number, ce->value); */
   9367 		return 1;
   9368 	}
   9369 
   9370 	if ((ret = Module_Create(ce->value))) {
   9371 		config_error("%s:%i: loadmodule %s: failed to load: %s",
   9372 			ce->file->filename, ce->line_number,
   9373 			ce->value, ret);
   9374 		return -1;
   9375 	}
   9376 	return 1;
   9377 }
   9378 
   9379 int	_test_loadmodule(ConfigFile *conf, ConfigEntry *ce)
   9380 {
   9381 	return 0;
   9382 }
   9383 
   9384 int	_test_blacklist_module(ConfigFile *conf, ConfigEntry *ce)
   9385 {
   9386 	const char *path;
   9387 	ConfigItem_blacklist_module *m;
   9388 
   9389 	if (!ce->value)
   9390 	{
   9391 		config_status("%s:%i: blacklist-module: no module name given to blacklist",
   9392 			ce->file->filename, ce->line_number);
   9393 		return -1;
   9394 	}
   9395 
   9396 	path = Module_TransformPath(ce->value);
   9397 
   9398 	m = safe_alloc(sizeof(ConfigItem_blacklist_module));
   9399 	safe_strdup(m->name, ce->value);
   9400 	AddListItem(m, conf_blacklist_module);
   9401 
   9402 	return 0;
   9403 }
   9404 
   9405 int is_blacklisted_module(const char *name)
   9406 {
   9407 	const char *path = Module_TransformPath(name);
   9408 	ConfigItem_blacklist_module *m;
   9409 
   9410 	for (m = conf_blacklist_module; m; m = m->next)
   9411 		if (match_simple(m->name, name) || match_simple(m->name, path))
   9412 			return 1;
   9413 
   9414 	return 0;
   9415 }
   9416 
   9417 void start_listeners(void)
   9418 {
   9419 	ConfigItem_listen *listener;
   9420 	int failed = 0, ports_bound = 0;
   9421 	char boundmsg_ipv4[512], boundmsg_ipv6[512];
   9422 	int last_errno = 0;
   9423 
   9424 	*boundmsg_ipv4 = *boundmsg_ipv6 = '\0';
   9425 
   9426 	for (listener = conf_listen; listener; listener = listener->next)
   9427 	{
   9428 		/* Try to bind to any ports that are not yet bound and not marked as temporary */
   9429 		if (!(listener->options & LISTENER_BOUND) && !listener->flag.temporary)
   9430 		{
   9431 			if (add_listener(listener) == -1)
   9432 			{
   9433 				/* Error already printed upstream */
   9434 				failed = 1;
   9435 				last_errno = ERRNO;
   9436 			} else {
   9437 				if (loop.booted)
   9438 				{
   9439 					unreal_log(ULOG_INFO, "listen", "LISTEN_ADDED", NULL,
   9440 					           "UnrealIRCd is now also listening on $listen_ip:$listen_port",
   9441 					           log_data_string("listen_ip", listener->ip),
   9442 					           log_data_integer("listen_port", listener->port));
   9443 				} else {
   9444 					switch (listener->socket_type)
   9445 					{
   9446 						case SOCKET_TYPE_IPV4:
   9447 							snprintf(boundmsg_ipv4+strlen(boundmsg_ipv4), sizeof(boundmsg_ipv4)-strlen(boundmsg_ipv4),
   9448 								"%s:%d%s, ", listener->ip, listener->port,
   9449 								listener->options & LISTENER_TLS ? "(TLS)" : "");
   9450 							break;
   9451 						case SOCKET_TYPE_IPV6:
   9452 							snprintf(boundmsg_ipv6+strlen(boundmsg_ipv6), sizeof(boundmsg_ipv6)-strlen(boundmsg_ipv6),
   9453 								"%s:%d%s, ", listener->ip, listener->port,
   9454 								listener->options & LISTENER_TLS ? "(TLS)" : "");
   9455 							break;
   9456 						// TODO: show unix domain sockets ;)
   9457 						default:
   9458 							break;
   9459 					}
   9460 				}
   9461 			}
   9462 		}
   9463 
   9464 		/* NOTE: do not merge this with code above (nor in an else block),
   9465 		 * as add_listener() affects this flag.
   9466 		 */
   9467 		if (listener->options & LISTENER_BOUND)
   9468 			ports_bound++;
   9469 	}
   9470 
   9471 	if (ports_bound == 0)
   9472 	{
   9473 #ifdef _WIN32
   9474 		if (last_errno == WSAEADDRINUSE)
   9475 #else
   9476 		if (last_errno == EADDRINUSE)
   9477 #endif
   9478 		{
   9479 			/* We can be specific */
   9480 			unreal_log(ULOG_FATAL, "listen", "ALL_LISTEN_PORTS_FAILED", NULL,
   9481 				   "Unable to listen on any ports. "
   9482 				   "Most likely UnrealIRCd is already running.");
   9483 		} else {
   9484 			unreal_log(ULOG_FATAL, "listen", "ALL_LISTEN_PORTS_FAILED", NULL,
   9485 				   "Unable to listen on any ports. "
   9486 				   "Please verify that no other process is using the ports. "
   9487 				   "Also, on some IRCd shells you may have to use listen::bind-ip "
   9488 				   "with a specific IP assigned to you (rather than \"*\").");
   9489 		}
   9490 		exit(-1);
   9491 	}
   9492 
   9493 	if (failed && !loop.booted)
   9494 	{
   9495 		unreal_log(ULOG_FATAL, "listen", "SOME_LISTEN_PORTS_FAILED", NULL,
   9496 			   "Unable to listen on all ports (some of them succeeded, some of them failed). "
   9497 			   "Please verify that no other process is using the port(s). "
   9498 			   "Also, on some IRCd shells you may have to use listen::bind-ip "
   9499 			   "with a specific IP assigned to you (rather than \"*\").");
   9500 		exit(-1);
   9501 	}
   9502 
   9503 	if (!loop.booted)
   9504 	{
   9505 		if (strlen(boundmsg_ipv4) > 2)
   9506 			boundmsg_ipv4[strlen(boundmsg_ipv4)-2] = '\0';
   9507 		if (strlen(boundmsg_ipv6) > 2)
   9508 			boundmsg_ipv6[strlen(boundmsg_ipv6)-2] = '\0';
   9509 
   9510 		if (!*boundmsg_ipv4)
   9511 			strlcpy(boundmsg_ipv4, "<none>", sizeof(boundmsg_ipv4));
   9512 		if (!*boundmsg_ipv6)
   9513 			strlcpy(boundmsg_ipv6, "<none>", sizeof(boundmsg_ipv6));
   9514 
   9515 		unreal_log(ULOG_INFO, "listen", "LISTENING", NULL,
   9516 		           "UnrealIRCd is now listening on the following addresses/ports:\n"
   9517 		           "IPv4: $ipv4_port_list\n"
   9518 		           "IPv6: $ipv6_port_list\n",
   9519 		           log_data_string("ipv4_port_list", boundmsg_ipv4),
   9520 		           log_data_string("ipv6_port_list", boundmsg_ipv6));
   9521 	}
   9522 }
   9523 
   9524 /* Actually use configuration */
   9525 void config_run(void)
   9526 {
   9527 	loop.config_status = CONFIG_STATUS_POSTLOAD;
   9528 	extcmodes_check_for_changes();
   9529 	start_listeners();
   9530 	if (!loop.booted)
   9531 		add_proc_io_server();
   9532 	free_all_config_resources();
   9533 }
   9534 
   9535 int	_conf_offchans(ConfigFile *conf, ConfigEntry *ce)
   9536 {
   9537 	ConfigEntry *cep, *cepp;
   9538 
   9539 	for (cep = ce->items; cep; cep = cep->next)
   9540 	{
   9541 		ConfigItem_offchans *of = safe_alloc(sizeof(ConfigItem_offchans));
   9542 		strlcpy(of->name, cep->name, CHANNELLEN+1);
   9543 		for (cepp = cep->items; cepp; cepp = cepp->next)
   9544 		{
   9545 			if (!strcmp(cepp->name, "topic"))
   9546 				safe_strdup(of->topic, cepp->value);
   9547 		}
   9548 		AddListItem(of, conf_offchans);
   9549 	}
   9550 	return 0;
   9551 }
   9552 
   9553 int	_test_offchans(ConfigFile *conf, ConfigEntry *ce)
   9554 {
   9555 	int errors = 0;
   9556 	ConfigEntry *cep, *cep2;
   9557 
   9558 	if (!ce->items)
   9559 	{
   9560 		config_error("%s:%i: empty official-channels block",
   9561 			ce->file->filename, ce->line_number);
   9562 		return 1;
   9563 	}
   9564 
   9565 	config_warn("set::official-channels is deprecated. It often does not do what you want. "
   9566 	            "You're better of creating a channel, setting all modes, topic, etc. to your liking "
   9567 	            "and then making the channel permanent (MODE #channel +P). "
   9568 	            "The channel will then be stored in a database to preserve it between restarts.");
   9569 
   9570 	for (cep = ce->items; cep; cep = cep->next)
   9571 	{
   9572 		if (strlen(cep->name) > CHANNELLEN)
   9573 		{
   9574 			config_error("%s:%i: official-channels: '%s' name too long (max %d characters).",
   9575 				cep->file->filename, cep->line_number, cep->name, CHANNELLEN);
   9576 			errors++;
   9577 			continue;
   9578 		}
   9579 		if (!valid_channelname(cep->name))
   9580 		{
   9581 			config_error("%s:%i: official-channels: '%s' is not a valid channel name.",
   9582 				cep->file->filename, cep->line_number, cep->name);
   9583 			errors++;
   9584 			continue;
   9585 		}
   9586 		for (cep2 = cep->items; cep2; cep2 = cep2->next)
   9587 		{
   9588 			if (!cep2->value)
   9589 			{
   9590 				config_error_empty(cep2->file->filename,
   9591 					cep2->line_number, "official-channels",
   9592 					cep2->name);
   9593 				errors++;
   9594 				continue;
   9595 			}
   9596 			if (!strcmp(cep2->name, "topic"))
   9597 			{
   9598 				if (strlen(cep2->value) > MAXTOPICLEN)
   9599 				{
   9600 					config_error("%s:%i: official-channels::%s: topic too long (max %d characters).",
   9601 						cep2->file->filename, cep2->line_number, cep->name, MAXTOPICLEN);
   9602 					errors++;
   9603 					continue;
   9604 				}
   9605 			} else {
   9606 				config_error_unknown(cep2->file->filename,
   9607 					cep2->line_number, "official-channels",
   9608 					cep2->name);
   9609 				errors++;
   9610 				continue;
   9611 			}
   9612 		}
   9613 	}
   9614 	return errors;
   9615 }
   9616 
   9617 int	_conf_alias(ConfigFile *conf, ConfigEntry *ce)
   9618 {
   9619 	ConfigItem_alias *alias = NULL;
   9620 	ConfigItem_alias_format *format;
   9621 	ConfigEntry 	    	*cep, *cepp;
   9622 	RealCommand *cmptr;
   9623 
   9624 	if ((cmptr = find_command(ce->value, CMD_ALIAS)))
   9625 		CommandDelX(NULL, cmptr);
   9626 	if (find_command_simple(ce->value))
   9627 	{
   9628 		config_warn("%s:%i: Alias '%s' would conflict with command (or server token) '%s', alias not added.",
   9629 			ce->file->filename, ce->line_number,
   9630 			ce->value, ce->value);
   9631 		return 0;
   9632 	}
   9633 	if ((alias = find_alias(ce->value)))
   9634 		DelListItem(alias, conf_alias);
   9635 	alias = safe_alloc(sizeof(ConfigItem_alias));
   9636 	safe_strdup(alias->alias, ce->value);
   9637 	for (cep = ce->items; cep; cep = cep->next)
   9638 	{
   9639 		if (!strcmp(cep->name, "format")) {
   9640 			format = safe_alloc(sizeof(ConfigItem_alias_format));
   9641 			safe_strdup(format->format, cep->value);
   9642 			format->expr = unreal_create_match(MATCH_PCRE_REGEX, cep->value, NULL);
   9643 			if (!format->expr)
   9644 				abort(); /* Impossible due to _test_alias earlier */
   9645 			for (cepp = cep->items; cepp; cepp = cepp->next) {
   9646 				if (!strcmp(cepp->name, "nick") ||
   9647 				    !strcmp(cepp->name, "target") ||
   9648 				    !strcmp(cepp->name, "command")) {
   9649 					safe_strdup(format->nick, cepp->value);
   9650 				}
   9651 				else if (!strcmp(cepp->name, "parameters")) {
   9652 					safe_strdup(format->parameters, cepp->value);
   9653 				}
   9654 				else if (!strcmp(cepp->name, "type")) {
   9655 					if (!strcmp(cepp->value, "services"))
   9656 						format->type = ALIAS_SERVICES;
   9657 					else if (!strcmp(cepp->value, "stats"))
   9658 						format->type = ALIAS_STATS;
   9659 					else if (!strcmp(cepp->value, "normal"))
   9660 						format->type = ALIAS_NORMAL;
   9661 					else if (!strcmp(cepp->value, "channel"))
   9662 						format->type = ALIAS_CHANNEL;
   9663 					else if (!strcmp(cepp->value, "real"))
   9664 						format->type = ALIAS_REAL;
   9665 				}
   9666 			}
   9667 			AddListItem(format, alias->format);
   9668 		}
   9669 
   9670 		else if (!strcmp(cep->name, "nick") || !strcmp(cep->name, "target"))
   9671 		{
   9672 			safe_strdup(alias->nick, cep->value);
   9673 		}
   9674 		else if (!strcmp(cep->name, "type")) {
   9675 			if (!strcmp(cep->value, "services"))
   9676 				alias->type = ALIAS_SERVICES;
   9677 			else if (!strcmp(cep->value, "stats"))
   9678 				alias->type = ALIAS_STATS;
   9679 			else if (!strcmp(cep->value, "normal"))
   9680 				alias->type = ALIAS_NORMAL;
   9681 			else if (!strcmp(cep->value, "channel"))
   9682 				alias->type = ALIAS_CHANNEL;
   9683 			else if (!strcmp(cep->value, "command"))
   9684 				alias->type = ALIAS_COMMAND;
   9685 		}
   9686 		else if (!strcmp(cep->name, "spamfilter"))
   9687 			alias->spamfilter = config_checkval(cep->value, CFG_YESNO);
   9688 	}
   9689 	if (BadPtr(alias->nick) && alias->type != ALIAS_COMMAND) {
   9690 		safe_strdup(alias->nick, alias->alias);
   9691 	}
   9692 	AliasAdd(NULL, alias->alias, cmd_alias, 1, CMD_USER|CMD_ALIAS);
   9693 
   9694 	AddListItem(alias, conf_alias);
   9695 	return 0;
   9696 }
   9697 
   9698 
   9699 int _test_alias(ConfigFile *conf, ConfigEntry *ce) {
   9700 	int errors = 0;
   9701 	ConfigEntry *cep, *cepp;
   9702 	char has_type = 0, has_target = 0, has_format = 0;
   9703 	char type = 0;
   9704 
   9705 	if (!ce->items)
   9706 	{
   9707 		config_error("%s:%i: empty alias block",
   9708 			ce->file->filename, ce->line_number);
   9709 		return 1;
   9710 	}
   9711 	if (!ce->value)
   9712 	{
   9713 		config_error("%s:%i: alias without name",
   9714 			ce->file->filename, ce->line_number);
   9715 		errors++;
   9716 	}
   9717 	else if (!find_command(ce->value, CMD_ALIAS) && find_command(ce->value, 0)) {
   9718 		config_status("%s:%i: %s is an existing command, can not add alias",
   9719 			ce->file->filename, ce->line_number, ce->value);
   9720 		errors++;
   9721 	}
   9722 	for (cep = ce->items; cep; cep = cep->next)
   9723 	{
   9724 		if (config_is_blankorempty(cep, "alias"))
   9725 		{
   9726 			errors++;
   9727 			continue;
   9728 		}
   9729 		if (!strcmp(cep->name, "format")) {
   9730 			char *err = NULL;
   9731 			Match *expr;
   9732 			char has_type = 0, has_target = 0, has_parameters = 0;
   9733 
   9734 			has_format = 1;
   9735 			expr = unreal_create_match(MATCH_PCRE_REGEX, cep->value, &err);
   9736 			if (!expr)
   9737 			{
   9738 				config_error("%s:%i: alias::format contains an invalid regex: %s",
   9739 					cep->file->filename, cep->line_number, err);
   9740 			} else {
   9741 				unreal_delete_match(expr);
   9742 			}
   9743 
   9744 			for (cepp = cep->items; cepp; cepp = cepp->next) {
   9745 				if (config_is_blankorempty(cepp, "alias::format"))
   9746 				{
   9747 					errors++;
   9748 					continue;
   9749 				}
   9750 				if (!strcmp(cepp->name, "nick") ||
   9751 				    !strcmp(cepp->name, "command") ||
   9752 				    !strcmp(cepp->name, "target"))
   9753 				{
   9754 					if (has_target)
   9755 					{
   9756 						config_warn_duplicate(cepp->file->filename,
   9757 							cepp->line_number,
   9758 							"alias::format::target");
   9759 						continue;
   9760 					}
   9761 					has_target = 1;
   9762 				}
   9763 				else if (!strcmp(cepp->name, "type"))
   9764 				{
   9765 					if (has_type)
   9766 					{
   9767 						config_warn_duplicate(cepp->file->filename,
   9768 							cepp->line_number,
   9769 							"alias::format::type");
   9770 						continue;
   9771 					}
   9772 					has_type = 1;
   9773 					if (!strcmp(cepp->value, "services"))
   9774 						;
   9775 					else if (!strcmp(cepp->value, "stats"))
   9776 						;
   9777 					else if (!strcmp(cepp->value, "normal"))
   9778 						;
   9779 					else if (!strcmp(cepp->value, "channel"))
   9780 						;
   9781 					else if (!strcmp(cepp->value, "real"))
   9782 						;
   9783 					else
   9784 					{
   9785 						config_error("%s:%i: unknown alias type",
   9786 						cepp->file->filename, cepp->line_number);
   9787 						errors++;
   9788 					}
   9789 				}
   9790 				else if (!strcmp(cepp->name, "parameters"))
   9791 				{
   9792 					if (has_parameters)
   9793 					{
   9794 						config_warn_duplicate(cepp->file->filename,
   9795 							cepp->line_number,
   9796 							"alias::format::parameters");
   9797 						continue;
   9798 					}
   9799 					has_parameters = 1;
   9800 				}
   9801 				else
   9802 				{
   9803 					config_error_unknown(cepp->file->filename,
   9804 						cepp->line_number, "alias::format",
   9805 						cepp->name);
   9806 					errors++;
   9807 				}
   9808 			}
   9809 			if (!has_target)
   9810 			{
   9811 				config_error_missing(cep->file->filename,
   9812 					cep->line_number, "alias::format::target");
   9813 				errors++;
   9814 			}
   9815 			if (!has_type)
   9816 			{
   9817 				config_error_missing(cep->file->filename,
   9818 					cep->line_number, "alias::format::type");
   9819 				errors++;
   9820 			}
   9821 			if (!has_parameters)
   9822 			{
   9823 				config_error_missing(cep->file->filename,
   9824 					cep->line_number, "alias::format::parameters");
   9825 				errors++;
   9826 			}
   9827 		}
   9828 		else if (!strcmp(cep->name, "nick") || !strcmp(cep->name, "target"))
   9829 		{
   9830 			if (has_target)
   9831 			{
   9832 				config_warn_duplicate(cep->file->filename,
   9833 					cep->line_number, "alias::target");
   9834 				continue;
   9835 			}
   9836 			has_target = 1;
   9837 		}
   9838 		else if (!strcmp(cep->name, "type")) {
   9839 			if (has_type)
   9840 			{
   9841 				config_warn_duplicate(cep->file->filename,
   9842 					cep->line_number, "alias::type");
   9843 				continue;
   9844 			}
   9845 			has_type = 1;
   9846 			if (!strcmp(cep->value, "services"))
   9847 				;
   9848 			else if (!strcmp(cep->value, "stats"))
   9849 				;
   9850 			else if (!strcmp(cep->value, "normal"))
   9851 				;
   9852 			else if (!strcmp(cep->value, "channel"))
   9853 				;
   9854 			else if (!strcmp(cep->value, "command"))
   9855 				type = 'c';
   9856 			else {
   9857 				config_error("%s:%i: unknown alias type",
   9858 					cep->file->filename, cep->line_number);
   9859 				errors++;
   9860 			}
   9861 		}
   9862 		else if (!strcmp(cep->name, "spamfilter"))
   9863 			;
   9864 		else {
   9865 			config_error_unknown(cep->file->filename, cep->line_number,
   9866 				"alias", cep->name);
   9867 			errors++;
   9868 		}
   9869 	}
   9870 	if (!has_type)
   9871 	{
   9872 		config_error_missing(ce->file->filename, ce->line_number,
   9873 			"alias::type");
   9874 		errors++;
   9875 	}
   9876 	if (!has_format && type == 'c')
   9877 	{
   9878 		config_error("%s:%d: alias::type is 'command' but no alias::format was specified",
   9879 			ce->file->filename, ce->line_number);
   9880 		errors++;
   9881 	}
   9882 	else if (has_format && type != 'c')
   9883 	{
   9884 		config_error("%s:%d: alias::format specified when type is not 'command'",
   9885 			ce->file->filename, ce->line_number);
   9886 		errors++;
   9887 	}
   9888 	return errors;
   9889 }
   9890 
   9891 int	_conf_deny(ConfigFile *conf, ConfigEntry *ce)
   9892 {
   9893 Hook *h;
   9894 
   9895 	if (!strcmp(ce->value, "channel"))
   9896 		_conf_deny_channel(conf, ce);
   9897 	else if (!strcmp(ce->value, "version"))
   9898 		_conf_deny_version(conf, ce);
   9899 	else
   9900 	{
   9901 		int value;
   9902 		for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next)
   9903 		{
   9904 			value = (*(h->func.intfunc))(conf,ce,CONFIG_DENY);
   9905 			if (value == 1)
   9906 				break;
   9907 		}
   9908 		return 0;
   9909 	}
   9910 	return 0;
   9911 }
   9912 
   9913 int	_conf_deny_channel(ConfigFile *conf, ConfigEntry *ce)
   9914 {
   9915 	ConfigItem_deny_channel 	*deny = NULL;
   9916 	ConfigEntry 	    	*cep;
   9917 
   9918 	deny = safe_alloc(sizeof(ConfigItem_deny_channel));
   9919 	for (cep = ce->items; cep; cep = cep->next)
   9920 	{
   9921 		if (!strcmp(cep->name, "channel"))
   9922 		{
   9923 			safe_strdup(deny->channel, cep->value);
   9924 		}
   9925 		else if (!strcmp(cep->name, "redirect"))
   9926 		{
   9927 			safe_strdup(deny->redirect, cep->value);
   9928 		}
   9929 		else if (!strcmp(cep->name, "reason"))
   9930 		{
   9931 			safe_strdup(deny->reason, cep->value);
   9932 		}
   9933 		else if (!strcmp(cep->name, "warn"))
   9934 		{
   9935 			deny->warn = config_checkval(cep->value,CFG_YESNO);
   9936 		}
   9937 		else if (!strcmp(cep->name, "class"))
   9938 		{
   9939 			safe_strdup(deny->class, cep->value);
   9940 		}
   9941 		else if (!strcmp(cep->name, "match") || !strcmp(cep->name, "mask"))
   9942 		{
   9943 			conf_match_block(conf, cep, &deny->match);
   9944 		}
   9945 	}
   9946 	AddListItem(deny, conf_deny_channel);
   9947 	return 0;
   9948 }
   9949 int	_conf_deny_version(ConfigFile *conf, ConfigEntry *ce)
   9950 {
   9951 	ConfigItem_deny_version *deny = NULL;
   9952 	ConfigEntry 	    	*cep;
   9953 
   9954 	deny = safe_alloc(sizeof(ConfigItem_deny_version));
   9955 	for (cep = ce->items; cep; cep = cep->next)
   9956 	{
   9957 		if (!strcmp(cep->name, "mask"))
   9958 		{
   9959 			safe_strdup(deny->mask, cep->value);
   9960 		}
   9961 		else if (!strcmp(cep->name, "version"))
   9962 		{
   9963 			safe_strdup(deny->version, cep->value);
   9964 		}
   9965 		else if (!strcmp(cep->name, "flags"))
   9966 		{
   9967 			safe_strdup(deny->flags, cep->value);
   9968 		}
   9969 	}
   9970 	AddListItem(deny, conf_deny_version);
   9971 	return 0;
   9972 }
   9973 
   9974 int     _test_deny(ConfigFile *conf, ConfigEntry *ce)
   9975 {
   9976 	ConfigEntry *cep;
   9977 	int	    errors = 0;
   9978 	Hook	*h;
   9979 
   9980 	if (!ce->value)
   9981 	{
   9982 		config_error("%s:%i: deny without type",
   9983 			ce->file->filename, ce->line_number);
   9984 		return 1;
   9985 	}
   9986 	if (!strcmp(ce->value, "channel"))
   9987 	{
   9988 		char has_channel = 0, has_warn = 0, has_reason = 0, has_redirect = 0, has_class = 0;
   9989 		char has_mask = 0, has_match = 0;
   9990 		for (cep = ce->items; cep; cep = cep->next)
   9991 		{
   9992 			if (config_is_blankorempty(cep, "deny channel"))
   9993 			{
   9994 				errors++;
   9995 				continue;
   9996 			}
   9997 			if (!strcmp(cep->name, "channel"))
   9998 			{
   9999 				if (has_channel)
  10000 				{
  10001 					config_warn_duplicate(cep->file->filename,
  10002 						cep->line_number, "deny channel::channel");
  10003 					continue;
  10004 				}
  10005 				has_channel = 1;
  10006 			}
  10007 			else if (!strcmp(cep->name, "redirect"))
  10008 			{
  10009 				if (has_redirect)
  10010 				{
  10011 					config_warn_duplicate(cep->file->filename,
  10012 						cep->line_number, "deny channel::redirect");
  10013 					continue;
  10014 				}
  10015 				has_redirect = 1;
  10016 			}
  10017 			else if (!strcmp(cep->name, "reason"))
  10018 			{
  10019 				if (has_reason)
  10020 				{
  10021 					config_warn_duplicate(cep->file->filename,
  10022 						cep->line_number, "deny channel::reason");
  10023 					continue;
  10024 				}
  10025 				has_reason = 1;
  10026 			}
  10027 			else if (!strcmp(cep->name, "warn"))
  10028 			{
  10029 				if (has_warn)
  10030 				{
  10031 					config_warn_duplicate(cep->file->filename,
  10032 						cep->line_number, "deny channel::warn");
  10033 					continue;
  10034 				}
  10035 				has_warn = 1;
  10036 			}
  10037 			else if (!strcmp(cep->name, "class"))
  10038 			{
  10039 				if (has_class)
  10040 				{
  10041 					config_warn_duplicate(cep->file->filename,
  10042 						cep->line_number, "deny channel::class");
  10043 					continue;
  10044 				}
  10045 				has_class = 1;
  10046 			}
  10047 			else if (!strcmp(cep->name, "match"))
  10048 			{
  10049 				has_match = 1;
  10050 				test_match_block(conf, cep, &errors);
  10051 			}
  10052 			else if (!strcmp(cep->name, "mask"))
  10053 			{
  10054 				has_mask = 1;
  10055 				test_match_block(conf, cep, &errors);
  10056 			}
  10057 			else
  10058 			{
  10059 				config_error_unknown(cep->file->filename,
  10060 					cep->line_number, "deny channel", cep->name);
  10061 				errors++;
  10062 			}
  10063 		}
  10064 		if (!has_channel)
  10065 		{
  10066 			config_error_missing(ce->file->filename, ce->line_number,
  10067 				"deny channel::channel");
  10068 			errors++;
  10069 		}
  10070 		if (!has_reason)
  10071 		{
  10072 			config_error_missing(ce->file->filename, ce->line_number,
  10073 				"deny channel::reason");
  10074 			errors++;
  10075 		}
  10076 		if (has_mask && has_match)
  10077 		{
  10078 			config_error("%s:%d: You cannot have both ::mask and ::match. "
  10079 				     "You should only use %s %s::match.",
  10080 				     ce->file->filename, ce->line_number, ce->name, ce->value);
  10081 			errors++;
  10082 		}
  10083 	}
  10084 	else if (!strcmp(ce->value, "version"))
  10085 	{
  10086 		char has_mask = 0, has_version = 0, has_flags = 0;
  10087 		for (cep = ce->items; cep; cep = cep->next)
  10088 		{
  10089 			if (config_is_blankorempty(cep, "deny version"))
  10090 			{
  10091 				errors++;
  10092 				continue;
  10093 			}
  10094 			if (!strcmp(cep->name, "mask"))
  10095 			{
  10096 				if (has_mask)
  10097 				{
  10098 					config_warn_duplicate(cep->file->filename,
  10099 						cep->line_number, "deny version::mask");
  10100 					continue;
  10101 				}
  10102 				has_mask = 1;
  10103 			}
  10104 			else if (!strcmp(cep->name, "version"))
  10105 			{
  10106 				if (has_version)
  10107 				{
  10108 					config_warn_duplicate(cep->file->filename,
  10109 						cep->line_number, "deny version::version");
  10110 					continue;
  10111 				}
  10112 				has_version = 1;
  10113 			}
  10114 			else if (!strcmp(cep->name, "flags"))
  10115 			{
  10116 				if (has_flags)
  10117 				{
  10118 					config_warn_duplicate(cep->file->filename,
  10119 						cep->line_number, "deny version::flags");
  10120 					continue;
  10121 				}
  10122 				has_flags = 1;
  10123 			}
  10124 			else
  10125 			{
  10126 				config_error_unknown(cep->file->filename,
  10127 					cep->line_number, "deny version", cep->name);
  10128 				errors++;
  10129 			}
  10130 		}
  10131 		if (!has_mask)
  10132 		{
  10133 			config_error_missing(ce->file->filename, ce->line_number,
  10134 				"deny version::mask");
  10135 			errors++;
  10136 		}
  10137 		if (!has_version)
  10138 		{
  10139 			config_error_missing(ce->file->filename, ce->line_number,
  10140 				"deny version::version");
  10141 			errors++;
  10142 		}
  10143 		if (!has_flags)
  10144 		{
  10145 			config_error_missing(ce->file->filename, ce->line_number,
  10146 				"deny version::flags");
  10147 			errors++;
  10148 		}
  10149 	}
  10150 	else
  10151 	{
  10152 		int used = 0;
  10153 		for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next)
  10154 		{
  10155 			int value, errs = 0;
  10156 			if (h->owner && !(h->owner->flags & MODFLAG_TESTING)
  10157 			    && !(h->owner->options & MOD_OPT_PERM))
  10158 				continue;
  10159 			value = (*(h->func.intfunc))(conf,ce,CONFIG_DENY, &errs);
  10160 			if (value == 2)
  10161 				used = 1;
  10162 			if (value == 1)
  10163 			{
  10164 				used = 1;
  10165 				break;
  10166 			}
  10167 			if (value == -1)
  10168 			{
  10169 				used = 1;
  10170 				errors += errs;
  10171 				break;
  10172 			}
  10173 			if (value == -2)
  10174 			{
  10175 				used = 1;
  10176 				errors += errs;
  10177 			}
  10178 		}
  10179 		if (!used) {
  10180 			config_error("%s:%i: unknown deny type %s",
  10181 				ce->file->filename, ce->line_number,
  10182 				ce->value);
  10183 			return 1;
  10184 		}
  10185 		return errors;
  10186 	}
  10187 
  10188 	return errors;
  10189 }
  10190 
  10191 Secret *find_secret(const char *secret_name)
  10192 {
  10193 	Secret *s;
  10194 	for (s = secrets; s; s = s->next)
  10195 	{
  10196 		if (!strcasecmp(s->name, secret_name))
  10197 			return s;
  10198 	}
  10199 	return NULL;
  10200 }
  10201 
  10202 void free_secret_cache(SecretCache *c)
  10203 {
  10204 	unrealdb_free_config(c->config);
  10205 	safe_free(c);
  10206 }
  10207 
  10208 void free_secret(Secret *s)
  10209 {
  10210 	SecretCache *c, *c_next;
  10211 	for (c = s->cache; c; c = c_next)
  10212 	{
  10213 		c_next = c->next;
  10214 		DelListItem(c, s->cache);
  10215 		free_secret_cache(c);
  10216 	}
  10217 	safe_free(s->name);
  10218 	safe_free_sensitive(s->password);
  10219 	safe_free(s);
  10220 }
  10221 
  10222 char *_conf_secret_read_password_file(const char *fname)
  10223 {
  10224 	char *pwd, *err;
  10225 	int fd, n;
  10226 
  10227 #ifndef _WIN32
  10228 	fd = open(fname, O_RDONLY);
  10229 #else
  10230 	fd = open(fname, _O_RDONLY|_O_BINARY);
  10231 #endif
  10232 	if (fd < 0)
  10233 	{
  10234 		/* This should not happen, as we tested for file exists earlier.. */
  10235 		config_error("Could not open file '%s': %s", fname, strerror(errno));
  10236 		return NULL;
  10237 	}
  10238 
  10239 	pwd = safe_alloc_sensitive(512);
  10240 	n = read(fd, pwd, 511);
  10241 	if (n <= 0)
  10242 	{
  10243 		close(fd);
  10244 		config_error("Could not read from file '%s': %s", fname, strerror(errno));
  10245 		safe_free_sensitive(pwd);
  10246 		return NULL;
  10247 	}
  10248 	close(fd);
  10249 	stripcrlf(pwd);
  10250 	sodium_stackzero(1024);
  10251 	if (!valid_secret_password(pwd, &err))
  10252 	{
  10253 		config_error("Key from file '%s' does not meet password complexity requirements: %s", fname, err);
  10254 		safe_free_sensitive(pwd);
  10255 		return NULL;
  10256 	}
  10257 	return pwd;
  10258 }
  10259 
  10260 char *_conf_secret_read_prompt(const char *blockname)
  10261 {
  10262 	char *pwd, *pwd_prompt;
  10263 	char buf[256];
  10264 
  10265 #ifdef _WIN32
  10266 	/* FIXME: add windows support? should be possible in GUI no? */
  10267 	return NULL;
  10268 #else
  10269 	snprintf(buf, sizeof(buf), "Enter password for secret '%s': ", blockname);
  10270 	pwd_prompt = getpass(buf);
  10271 	if (pwd_prompt)
  10272 	{
  10273 		pwd = safe_alloc_sensitive(512);
  10274 		strlcpy(pwd, pwd_prompt, 512);
  10275 		memset(pwd_prompt, 0, strlen(pwd_prompt)); // zero password out
  10276 		sodium_stackzero(1024);
  10277 		return pwd;
  10278 	}
  10279 	return NULL;
  10280 #endif
  10281 }
  10282 
  10283 int _test_secret(ConfigFile *conf, ConfigEntry *ce)
  10284 {
  10285 	int errors = 0;
  10286 	int has_password = 0, has_password_file = 0, has_password_prompt = 0;
  10287 	ConfigEntry *cep;
  10288 	char *err;
  10289 	Secret *existing;
  10290 
  10291 	if (!ce->value)
  10292 	{
  10293 		config_error("%s:%i: secret block needs a name, eg: secret xyz {",
  10294 			ce->file->filename, ce->line_number);
  10295 		errors++;
  10296 		return errors; /* need to return here since we dereference ce->value later.. */
  10297 	} else {
  10298 		if (!security_group_valid_name(ce->value))
  10299 		{
  10300 			config_error("%s:%i: secret block name '%s' contains invalid characters or is too long. "
  10301 			             "Only letters, numbers, underscore and hyphen are allowed.",
  10302 			             ce->file->filename, ce->line_number, ce->value);
  10303 			errors++;
  10304 		}
  10305 	}
  10306 
  10307 	existing = find_secret(ce->value);
  10308 
  10309 	for (cep = ce->items; cep; cep = cep->next)
  10310 	{
  10311 		if (!strcmp(cep->name, "password"))
  10312 		{
  10313 			int n;
  10314 			has_password = 1;
  10315 			CheckNull(cep);
  10316 			if (cep->items ||
  10317 			    (((n = Auth_AutoDetectHashType(cep->value))) && ((n == AUTHTYPE_BCRYPT) || (n == AUTHTYPE_ARGON2))))
  10318 			{
  10319 				config_error("%s:%d: you cannot use hashed passwords here, see "
  10320 				             "https://www.unrealircd.org/docs/Secret_block#secret-plaintext",
  10321 				             cep->file->filename, cep->line_number);
  10322 				errors++;
  10323 				continue;
  10324 			}
  10325 			if (!valid_secret_password(cep->value, &err))
  10326 			{
  10327 				config_error("%s:%d: secret::password does not meet password complexity requirements: %s",
  10328 				             cep->file->filename, cep->line_number, err);
  10329 				errors++;
  10330 			}
  10331 		} else
  10332 		if (!strcmp(cep->name, "password-file"))
  10333 		{
  10334 			char *str;
  10335 			has_password_file = 1;
  10336 			CheckNull(cep);
  10337 			convert_to_absolute_path(&cep->value, CONFDIR);
  10338 			if (!file_exists(cep->value) && existing && existing->password)
  10339 			{
  10340 				/* Silently ignore the case where a secret block already
  10341 				 * has the password read and now the file is no longer available.
  10342 				 * This so secret::password-file can be used only to boot
  10343 				 * and then the media (eg: USB stick) can be pulled.
  10344 				 */
  10345 			} else
  10346 			{
  10347 				str = _conf_secret_read_password_file(cep->value);
  10348 				if (!str)
  10349 				{
  10350 					config_error("%s:%d: secret::password-file: error reading password from file, see error from above.",
  10351 						cep->file->filename, cep->line_number);
  10352 					errors++;
  10353 				}
  10354 				safe_free_sensitive(str);
  10355 			}
  10356 		} else
  10357 		if (!strcmp(cep->name, "password-prompt"))
  10358 		{
  10359 #ifdef _WIN32
  10360 			config_error("%s:%d: secret::password-prompt is not implemented in Windows at the moment, sorry!",
  10361 				cep->file->filename, cep->line_number);
  10362 			config_error("Choose a different method to enter passwords or use *NIX");
  10363 			errors++;
  10364 			return errors;
  10365 #endif
  10366 			has_password_prompt = 1;
  10367 			if (loop.booted && !find_secret(ce->value))
  10368 			{
  10369 				config_error("%s:%d: you cannot add a new secret { } block that uses password-prompt and then /REHASH. "
  10370 				             "With 'password-prompt' you can only add such a password on boot.",
  10371 				             cep->file->filename, cep->line_number);
  10372 				config_error("Either use a different method to enter passwords or restart the IRCd on the console.");
  10373 				errors++;
  10374 			}
  10375 			if (!loop.booted && !running_interactively())
  10376 			{
  10377 				config_error("ERROR: IRCd is not running interactively, but via a cron job or something similar.");
  10378 				config_error("%s:%d: unable to prompt for password since IRCd is not started in a terminal",
  10379 					cep->file->filename, cep->line_number);
  10380 				config_error("Either use a different method to enter passwords or start the IRCd in a terminal/SSH/..");
  10381 			}
  10382 		} else
  10383 		if (!strcmp(cep->name, "password-url"))
  10384 		{
  10385 			config_error("%s:%d: secret::password-url is not supported yet in this UnrealIRCd version.",
  10386 				cep->file->filename, cep->line_number);
  10387 			errors++;
  10388 		} else
  10389 		{
  10390 			config_error_unknown(cep->file->filename, cep->line_number,
  10391 				"secret", cep->name);
  10392 			errors++;
  10393 			continue;
  10394 		}
  10395 		if (cep->items)
  10396 		{
  10397 			config_error("%s:%d: secret::%s does not support sub-options (%s)",
  10398 				cep->file->filename, cep->line_number,
  10399 				cep->name, cep->items->name);
  10400 			errors++;
  10401 		}
  10402 	}
  10403 
  10404 	if (!has_password && !has_password_file && !has_password_prompt)
  10405 	{
  10406 		config_error("%s:%d: secret { } block must contain 1 of: password OR password-file OR password-prompt",
  10407 			ce->file->filename, ce->line_number);
  10408 		errors++;
  10409 	}
  10410 
  10411 	return errors;
  10412 }
  10413 
  10414 /* NOTE: contrary to all other _conf* stuff, this one actually runs during config_test,
  10415  * so during the early CONFIG TEST stage rather than CONFIG RUN.
  10416  * This so all secret { } block configuration is available already during TEST/POSTTEST
  10417  * stage for modules, so they can check if the password is correct or not.
  10418  */
  10419 int _conf_secret(ConfigFile *conf, ConfigEntry *ce)
  10420 {
  10421 	ConfigEntry *cep;
  10422 	Secret *s;
  10423 	Secret *existing = find_secret(ce->value);
  10424 
  10425 	s = safe_alloc(sizeof(Secret));
  10426 	safe_strdup(s->name, ce->value);
  10427 
  10428 	for (cep = ce->items; cep; cep = cep->next)
  10429 	{
  10430 		if (!strcmp(cep->name, "password"))
  10431 		{
  10432 			safe_strdup_sensitive(s->password, cep->value);
  10433 			destroy_string(cep->value); /* destroy the original */
  10434 		} else
  10435 		if (!strcmp(cep->name, "password-file"))
  10436 		{
  10437 			if (!file_exists(cep->value) && existing && existing->password)
  10438 			{
  10439 				/* Silently ignore the case where a secret block already
  10440 				 * has the password read and now the file is no longer available.
  10441 				 * This so secret::password-file can be used only to boot
  10442 				 * and then the media (eg: USB stick) can be pulled.
  10443 				 */
  10444 			} else
  10445 			{
  10446 				s->password = _conf_secret_read_password_file(cep->value);
  10447 			}
  10448 		} else
  10449 		if (!strcmp(cep->name, "password-prompt"))
  10450 		{
  10451 			if (!loop.booted && running_interactively())
  10452 			{
  10453 				s->password = _conf_secret_read_prompt(ce->value);
  10454 				if (!s->password || !valid_secret_password(s->password, NULL))
  10455 				{
  10456 					config_error("Invalid password entered on console (does not meet complexity requirements)");
  10457 					/* This cannot be the correct password, so exit */
  10458 					exit(-1);
  10459 				}
  10460 			}
  10461 		}
  10462 	}
  10463 
  10464 	/* This may happen if we run twice, due to destroy_string() earlier: */
  10465 	if (BadPtr(s->password))
  10466 	{
  10467 		free_secret(s);
  10468 		return 1;
  10469 	}
  10470 
  10471 	/* If there is an existing secret { } block with this name in memory
  10472 	 * and it has a different password, then free that secret block
  10473 	 */
  10474 	if (existing)
  10475 	{
  10476 		if (!strcmp(s->password, existing->password))
  10477 		{
  10478 			free_secret(s);
  10479 			return 1;
  10480 		}
  10481 		/* passwords differ, so free the old existing one,
  10482 		 * including purging the cache for it.
  10483 		 */
  10484 		DelListItem(existing, secrets);
  10485 		free_secret(existing);
  10486 	}
  10487 	AddListItem(s, secrets);
  10488 	return 1;
  10489 }
  10490 
  10491 void resource_download_complete(const char *url, const char *file, const char *errorbuf, int cached, void *rs_key)
  10492 {
  10493 	ConfigResource *rs = (ConfigResource *)rs_key;
  10494 
  10495 	rs->type &= ~RESOURCE_DLQUEUED;
  10496 
  10497 	if (config_verbose)
  10498 		config_status("resource_download_complete() for %s [%s]", url, errorbuf?errorbuf:"success");
  10499 
  10500 	if (!file && !cached)
  10501 	{
  10502 		/* DOWNLOAD FAILED */
  10503 		if (rs->cache_file)
  10504 		{
  10505 			unreal_log(ULOG_ERROR, "config", "DOWNLOAD_FAILED_SOFT", NULL,
  10506 				   "$file:$line_number: Failed to download '$url': $error_message\n"
  10507 				   "Using a cached copy instead.",
  10508 				   log_data_string("file", rs->wce->ce->file->filename),
  10509 				   log_data_integer("line_number", rs->wce->ce->line_number),
  10510 				   log_data_string("url", displayurl(url)),
  10511 				   log_data_string("error_message", errorbuf));
  10512 			safe_strdup(rs->file, rs->cache_file);
  10513 		} else {
  10514 			unreal_log(ULOG_ERROR, "config", "DOWNLOAD_FAILED_HARD", NULL,
  10515 				   "$file:$line_number: Failed to download '$url': $error_message",
  10516 				   log_data_string("file", rs->wce->ce->file->filename),
  10517 				   log_data_integer("line_number", rs->wce->ce->line_number),
  10518 				   log_data_string("url", displayurl(url)),
  10519 				   log_data_string("error_message", errorbuf));
  10520 			/* Set error condition, this so config_read_file() later will stop. */
  10521 			loop.config_load_failed = 1;
  10522 			/* We keep the other transfers running since they may raise (more) errors.
  10523 			 * Which can be helpful so you can differentiate between an error of an
  10524 			 * include on one server, or complete lack of internet connectvitity.
  10525 			 */
  10526 		}
  10527 	}
  10528 	else
  10529 	{
  10530 		if (cached)
  10531 		{
  10532 			/* Copy from cache */
  10533 			safe_strdup(rs->file, rs->cache_file);
  10534 		} else {
  10535 			/* Copy to cache */
  10536 			const char *cache_file = unreal_mkcache(url);
  10537 			unreal_copyfileex(file, cache_file, 1);
  10538 			safe_strdup(rs->file, cache_file);
  10539 		}
  10540 	}
  10541 
  10542 	if (rs->file)
  10543 	{
  10544 		if (rs->type & RESOURCE_INCLUDE)
  10545 		{
  10546 			if (config_read_file(rs->file, (char *)displayurl(rs->url)) < 0)
  10547 				loop.config_load_failed = 1;
  10548 		} else {
  10549 			ConfigEntryWrapper *wce;
  10550 			for (wce = rs->wce; wce; wce = wce->next)
  10551 				safe_strdup(wce->ce->value, rs->file); // now information of url is lost, hm!!
  10552 		}
  10553 	}
  10554 }
  10555 
  10556 /** Request to REHASH the configuration file.
  10557  * The rehash will not be done immediately, just scheduled.
  10558  * This means this function can safely be called from modules or
  10559  * other areas.
  10560  * @param client	The client requesting the /REHASH.
  10561  *                      If this is NULL then the rehash was requested
  10562  *                      via a signal to the process or GUI.
  10563  */
  10564 void request_rehash(Client *client)
  10565 {
  10566 	json_t *j;
  10567 
  10568 	if (loop.rehashing)
  10569 	{
  10570 		if (client)
  10571 			sendnotice(client, "A rehash is already in progress");
  10572 		return;
  10573 	}
  10574 
  10575 	/* Free any old json_rehash_log */
  10576 	if (json_rehash_log)
  10577 	{
  10578 		json_decref(json_rehash_log);
  10579 		json_rehash_log = NULL;
  10580 	}
  10581 
  10582 	/* Start a new json_rehash_log */
  10583 	json_rehash_log = json_object();
  10584 	if (client)
  10585 		json_expand_client(json_rehash_log, "rehash_client", client, 99);
  10586 	j = json_array();
  10587 	json_object_set_new(json_rehash_log, "log", j);
  10588 
  10589 	/* Now actually process the rehash request... */
  10590 	loop.rehashing = 1;
  10591 	loop.rehash_save_client = client;
  10592 	config_read_start();
  10593 	/* More config reading (or network I/O), and the actual rehash will
  10594 	 * happen in "the main loop". See end of SocketLoop() in src/ircd.c.
  10595 	 */
  10596 }
  10597 
  10598 int rehash_internal(Client *client)
  10599 {
  10600 	int failure;
  10601 
  10602 	/* Log it here if it is by a signal */
  10603 	if (client == NULL)
  10604 		unreal_log(ULOG_INFO, "config", "CONFIG_RELOAD", NULL, "Rehashing server configuration file [./unrealircd rehash]");
  10605 
  10606 	loop.rehashing = 2; /* now doing the actual rehash */
  10607 
  10608 	failure = config_test();
  10609 	if (failure == 0)
  10610 		config_run();
  10611 	/* TODO: uh.. are we supposed to do all this for a failed rehash too? maybe some but not all? */
  10612 	reread_motdsandrules();
  10613 	unload_all_unused_umodes();
  10614 	unload_all_unused_extcmodes();
  10615 	unload_all_unused_extbans();
  10616 	unload_all_unused_caps();
  10617 	unload_all_unused_history_backends();
  10618 	unload_all_unused_rpc_handlers();
  10619 	// unload_all_unused_moddata(); -- this will crash
  10620 	clicap_check_for_changes();
  10621 	umodes_check_for_changes();
  10622 	charsys_check_for_changes();
  10623 
  10624 	/* Clear everything now that we are done */
  10625 	loop.rehashing = 0;
  10626 	remote_rehash_client = NULL;
  10627 	procio_post_rehash(failure);
  10628 	json_object_set_new(json_rehash_log, "success", json_boolean(failure ? 0 : 1));
  10629 	RunHook(HOOKTYPE_REHASH_LOG, failure, json_rehash_log);
  10630 
  10631 	loop.config_status = CONFIG_STATUS_COMPLETE;
  10632 	return 1;
  10633 }
  10634 
  10635 void link_cleanup(ConfigItem_link *link_ptr)
  10636 {
  10637 	safe_free(link_ptr->servername);
  10638 	free_security_group(link_ptr->incoming.match);
  10639 	Auth_FreeAuthConfig(link_ptr->auth);
  10640 	safe_free(link_ptr->outgoing.file);
  10641 	safe_free(link_ptr->outgoing.bind_ip);
  10642 	safe_free(link_ptr->outgoing.hostname);
  10643 	safe_free(link_ptr->hub);
  10644 	safe_free(link_ptr->leaf);
  10645 	if (link_ptr->ssl_ctx)
  10646 	{
  10647 		SSL_CTX_free(link_ptr->ssl_ctx);
  10648 		link_ptr->ssl_ctx = NULL;
  10649 	}
  10650 	if (link_ptr->tls_options)
  10651 	{
  10652 		free_tls_options(link_ptr->tls_options);
  10653 		link_ptr->tls_options = NULL;
  10654     }
  10655 }
  10656 
  10657 void delete_linkblock(ConfigItem_link *link_ptr)
  10658 {
  10659 	if (link_ptr->class)
  10660 	{
  10661 		link_ptr->class->xrefcount--;
  10662 		/* Perhaps the class is temporary too and we need to free it... */
  10663 		if (link_ptr->class->flag.temporary &&
  10664 		    !link_ptr->class->clients && !link_ptr->class->xrefcount)
  10665 		{
  10666 			delete_classblock(link_ptr->class);
  10667 			link_ptr->class = NULL;
  10668 		}
  10669 	}
  10670 	link_cleanup(link_ptr);
  10671 	DelListItem(link_ptr, conf_link);
  10672 	safe_free(link_ptr);
  10673 }
  10674 
  10675 void delete_classblock(ConfigItem_class *class_ptr)
  10676 {
  10677 	safe_free(class_ptr->name);
  10678 	DelListItem(class_ptr, conf_class);
  10679 	safe_free(class_ptr);
  10680 }
  10681 
  10682 void	listen_cleanup()
  10683 {
  10684 	int	i = 0;
  10685 	ConfigItem_listen *listen_ptr, *next;
  10686 
  10687 	for (listen_ptr = conf_listen; listen_ptr; listen_ptr = next)
  10688 	{
  10689 		next = listen_ptr->next;
  10690 		if (listen_ptr->flag.temporary && !listen_ptr->clients)
  10691 		{
  10692 			safe_free(listen_ptr->ip);
  10693 			free_tls_options(listen_ptr->tls_options);
  10694 			DelListItem(listen_ptr, conf_listen);
  10695 			safe_free(listen_ptr->webserver);
  10696 			safe_free(listen_ptr->websocket_forward);
  10697 			safe_free(listen_ptr);
  10698 			i++;
  10699 		}
  10700 	}
  10701 
  10702 	if (i)
  10703 		close_unbound_listeners();
  10704 }
  10705 
  10706 ConfigResource *find_config_resource(const char *resource)
  10707 {
  10708 	ConfigResource *rs;
  10709 
  10710 	for (rs = config_resources; rs; rs = rs->next)
  10711 	{
  10712 #ifdef _WIN32
  10713 		if (rs->file && !strcasecmp(resource, rs->file))
  10714 			return rs;
  10715 #else
  10716 		if (rs->file && !strcmp(resource, rs->file))
  10717 			return rs;
  10718 #endif
  10719 		if (rs->url && !strcasecmp(resource, rs->url))
  10720 			return rs;
  10721 	}
  10722 	return NULL;
  10723 }
  10724 
  10725 /* Add configuration resource to list.
  10726  * For files this doesn't do terribly much, except that you can use
  10727  * the return value to judge on whether you should call config_read_file() or not.
  10728  * For urls this adds the resource to the list of links to be downloaded.
  10729  * @param resource	File or URL of the resource
  10730  * @param type		A RESOURCE_ type such as RESOURCE_INCLUDE
  10731  * @param ce		The ConfigEntry where the add_config_resource() happened
  10732  *			for, such as the include block, etc.
  10733  * @returns 0 if the file is already on our list (so no need to load it!)
  10734  */
  10735 int add_config_resource(const char *resource, int type, ConfigEntry *ce)
  10736 {
  10737 	ConfigResource *rs;
  10738 	ConfigEntryWrapper *wce;
  10739 
  10740 	if (config_verbose)
  10741 		config_status("add_config_resource() for '%s", resource);
  10742 
  10743 	wce = safe_alloc(sizeof(ConfigEntryWrapper));
  10744 	wce->ce = ce;
  10745 
  10746 	rs = find_config_resource(resource);
  10747 	if (rs)
  10748 	{
  10749 		/* Existing entry, add us to the list of
  10750 		 * items who are interested in this resource ;)
  10751 		 */
  10752 		AddListItem(wce, rs->wce);
  10753 		return 0;
  10754 	}
  10755 
  10756 	/* New entry */
  10757 	rs = safe_alloc(sizeof(ConfigResource));
  10758 	rs->wce = wce;
  10759 	AddListItem(rs, config_resources);
  10760 
  10761 	if (!url_is_valid(resource))
  10762 	{
  10763 		safe_strdup(rs->file, resource);
  10764 	} else {
  10765 		const char *cache_file;
  10766 		time_t modtime;
  10767 
  10768 		safe_strdup(rs->url, resource);
  10769 		rs->type = type|RESOURCE_REMOTE|RESOURCE_DLQUEUED;
  10770 
  10771 		cache_file = unreal_mkcache(rs->url);
  10772 		modtime = unreal_getfilemodtime(cache_file);
  10773 		if (modtime > 0)
  10774 		{
  10775 			safe_strdup(rs->cache_file, cache_file); /* Cached copy is available */
  10776 			/* Check if there is an "url-refresh" argument */
  10777 			ConfigEntry *cep, *prev = NULL;
  10778 			for (cep = ce->items; cep; cep = cep->next)
  10779 			{
  10780 				if (!strcmp(cep->name, "url-refresh"))
  10781 				{
  10782 					/* First find out the time value of url-refresh... (eg '7d' -> 86400*7) */
  10783 					long refresh_time = 0;
  10784 					if (cep->value)
  10785 						refresh_time = config_checkval(cep->value, CFG_TIME);
  10786 					/* Then remove the config item so it is not seen by the rest of unrealircd.
  10787 					 * Can't use DelListItem() here as ConfigEntry has no ->prev, only ->next.
  10788 					 */
  10789 					if (prev)
  10790 						prev->next = cep->next; /* (skip over us) */
  10791 					else
  10792 						ce->items = cep->next; /* (new head) */
  10793 					/* ..and free it */
  10794 					config_entry_free(cep);
  10795 					/* And now check if the current cached copy is recent enough */
  10796 					if (TStime() - modtime < refresh_time)
  10797 					{
  10798 						/* Don't download, use cached copy */
  10799 						//config_status("DEBUG: using cached copy due to url-refresh %ld", refresh_time);
  10800 						resource_download_complete(rs->url, NULL, NULL, 1, rs);
  10801 						return 1;
  10802 					} else {
  10803 						//config_status("DEBUG: requires download attempt, out of date url-refresh %ld < %ld", refresh_time, TStime() - modtime);
  10804 					}
  10805 					break; // MUST break now as we touched the linked list.
  10806 				}
  10807 				prev = cep;
  10808 			}
  10809 		}
  10810 		download_file_async(rs->url, modtime, resource_download_complete, (void *)rs, NULL, DOWNLOAD_MAX_REDIRECTS);
  10811 	}
  10812 	return 1;
  10813 }
  10814 
  10815 void free_all_config_resources(void)
  10816 {
  10817 	ConfigResource *rs, *next;
  10818 	ConfigEntryWrapper *wce, *wce_next;
  10819 
  10820 	for (rs = config_resources; rs; rs = next)
  10821 	{
  10822 		next = rs->next;
  10823 		for (wce = rs->wce; wce; wce = wce_next)
  10824 		{
  10825 			wce_next = wce->next;
  10826 			safe_free(wce);
  10827 		}
  10828 		rs->wce = NULL;
  10829 		if (rs->type & RESOURCE_REMOTE)
  10830 		{
  10831 			url_cancel_handle_by_callback_data(rs);
  10832 			/* Delete the file, but only if it's not a cached version */
  10833 			if (rs->file && strncmp(rs->file, CACHEDIR, strlen(CACHEDIR)))
  10834 			{
  10835 				remove(rs->file);
  10836 			}
  10837 			safe_free(rs->url);
  10838 		}
  10839 		safe_free(rs->file);
  10840 		safe_free(rs->cache_file);
  10841 		DelListItem(rs, config_resources);
  10842 		safe_free(rs);
  10843 	}
  10844 }
  10845 
  10846 int tls_tests(void)
  10847 {
  10848 	if (have_tls_listeners == 0)
  10849 	{
  10850 		config_error("Your server is not listening on any TLS ports.");
  10851 		config_status("Add this to your unrealircd.conf: listen { ip %s; port 6697; options { tls; }; };",
  10852 		            port_6667_ip ? port_6667_ip : "*");
  10853 		config_status("See https://www.unrealircd.org/docs/FAQ#no-tls-ports");
  10854 		return 0;
  10855 	}
  10856 
  10857 	return 1;
  10858 }
  10859 
  10860 /** Check if the user attempts to unload (eg: by commenting out) a module
  10861  * that is currently loaded and is tagged as MOD_OPT_PERM_RELOADABLE
  10862  * (in other words: a module that allows re-loading but not un-loading)
  10863  */
  10864 int reloadable_perm_module_unloaded(void)
  10865 {
  10866     Module *m, *m2;
  10867     extern Module *Modules;
  10868     int ret = 0;
  10869 
  10870 	for (m = Modules; m; m = m->next)
  10871 	{
  10872 		if ((m->options & MOD_OPT_PERM_RELOADABLE) && (m->flags & MODFLAG_LOADED))
  10873 		{
  10874 			/* For each module w/MOD_OPT_PERM_RELOADABLE that is currently fully loaded... */
  10875 			int found = 0;
  10876 			for (m2 = Modules; m2; m2 = m2->next)
  10877 			{
  10878 				if ((m != m2) && !strcmp(m->header->name, m2->header->name))
  10879 					found = 1;
  10880 			}
  10881 			if (!found)
  10882 			{
  10883 				config_error("Attempt to unload module '%s' is not permitted. Module is permanent and reloadable only.", m->header->name);
  10884 				ret = 1;
  10885 				/* we don't return straight away so the user gets to see all errors and not just one */
  10886 			}
  10887 		}
  10888 	}
  10889 
  10890 	return ret;
  10891 }
  10892 
  10893 const char *link_generator_spkifp(TLSOptions *tlsoptions)
  10894 {
  10895 	SSL_CTX *ctx;
  10896 	SSL *ssl;
  10897 	X509 *cert;
  10898 
  10899 	ctx = init_ctx(tlsoptions, 1);
  10900 	if (!ctx)
  10901 		exit(1);
  10902 	ssl = SSL_new(ctx);
  10903 	if (!ssl)
  10904 		exit(1);
  10905 	cert = SSL_get_certificate(ssl);
  10906 	return spki_fingerprint_ex(cert);
  10907 }
  10908 
  10909 void link_generator(void)
  10910 {
  10911 	ConfigItem_listen *lstn;
  10912 	TLSOptions *tlsopt = iConf.tls_options; /* never null */
  10913 	int port = 0;
  10914 	char *ip = NULL;
  10915 	const char *spkifp;
  10916 
  10917 	for (lstn = conf_listen; lstn; lstn = lstn->next)
  10918 	{
  10919 		if ((lstn->options & LISTENER_SERVERSONLY) &&
  10920 		    (lstn->options & LISTENER_TLS))
  10921 		{
  10922 			if (lstn->tls_options)
  10923 				tlsopt = lstn->tls_options;
  10924 			port = lstn->port;
  10925 			if (strcmp(lstn->ip, "*"))
  10926 				ip = lstn->ip;
  10927 			/* else NULL */
  10928 			break;
  10929 		}
  10930 	}
  10931 
  10932 	if (!port)
  10933 	{
  10934 		printf("You don't have any listen { } blocks that are serversonly (and have tls enabled).\n");
  10935 		printf("It is recommended to have at least one. Add this to your configuration file:\n");
  10936 		printf("listen { ip *; port 6900; options { tls; serversonly; }; };\n");
  10937 		exit(1);
  10938 	}
  10939 
  10940 	spkifp = link_generator_spkifp(tlsopt);
  10941 	if (!spkifp)
  10942 	{
  10943 		printf("Could not calculate spkifp. Maybe you have uncommon TLS options set? Odd...\n");
  10944 		exit(1);
  10945 	}
  10946 
  10947 	printf("\n");
  10948 	printf("Add the following link block to the unrealircd.conf on the OTHER side of the link\n");
  10949 	printf("(so NOT in the unrealircd.conf on THIS machine). Here it is, just copy-paste:\n");
  10950 	printf("################################################################################\n");
  10951 	printf("link %s {\n"
  10952 	       "    incoming {\n"
  10953 	       "        mask *;\n"
  10954 	       "    }\n"
  10955 	       "    outgoing {\n"
  10956 	       "        hostname %s;\n"
  10957 	       "        port %d;\n"
  10958 	       "        options { tls; autoconnect; }\n"
  10959 	       "    }\n"
  10960 	       "    password \"%s\" { spkifp; }\n"
  10961 	       "    class servers;\n"
  10962 	       "}\n",
  10963 	       conf_me->name,
  10964 	       ip ? ip : conf_me->name,
  10965 	       port,
  10966 	       spkifp);
  10967 	printf("################################################################################\n");
  10968 	exit(0);
  10969 }