unrealircd

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

support.c (39571B)

      1 /*
      2  *   Unreal Internet Relay Chat Daemon, src/support.c
      3  *   Copyright (C) 1990, 1991 Armin Gruner
      4  *
      5  *   This program is free software; you can redistribute it and/or modify
      6  *   it under the terms of the GNU General Public License as published by
      7  *   the Free Software Foundation; either version 1, or (at your option)
      8  *   any later version.
      9  *
     10  *   This program is distributed in the hope that it will be useful,
     11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  *   GNU General Public License for more details.
     14  *
     15  *   You should have received a copy of the GNU General Public License
     16  *   along with this program; if not, write to the Free Software
     17  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     18  */
     19 
     20 /** @file
     21  * @brief Functions that don't always exist on every OS are provided here.
     22  * That was the original idea, anyway. Right now it also contains some
     23  * functions that should probably be in src/misc.c instead.
     24  * In any case, most functions here don't have any special meaning
     25  * specific for IRC, they could just as well be used in non-IRC code.
     26  */
     27 
     28 /* support.c 2.21 4/13/94 1990, 1991 Armin Gruner; 1992, 1993 Darren Reed */
     29 
     30 #include "unrealircd.h"
     31 
     32 extern void outofmemory();
     33 
     34 #define is_enabled match
     35 
     36 /** Convert integer to string */
     37 const char *my_itoa(int i)
     38 {
     39 	static char buf[128];
     40 	ircsnprintf(buf, sizeof(buf), "%d", i);
     41 	return buf;
     42 }
     43 
     44 /** Walk through a string of tokens, using a set of separators.
     45  * @param save	Pointer used for saving between calls
     46  * @param str	String to parse (will be altered!)
     47  * @param fs	Separator character(s)
     48  * @returns substring (token)
     49  * @note This function works similar to (but not identical?) to strtok_r().
     50  * @section Ex1 Example
     51  * @code
     52  * for (name = strtoken(&p, buf, ","); name; name = strtoken(&p, NULL, ","))
     53  *      unreal_log(ULOG_INFO, "test", "TEST", "Got: $name", log_data_string(name));
     54  * @endcode
     55  */
     56 char *strtoken(char **save, char *str, char *fs)
     57 {
     58 	char *pos, *tmp;
     59 
     60 	if (str)
     61 		pos = str;	/* new string scan */
     62 	else
     63 		pos = *save; /* keep last position across calls */
     64 
     65 	while (pos && *pos && strchr(fs, *pos) != NULL)
     66 		pos++;		/* skip leading separators */
     67 
     68 	if (!pos || !*pos)
     69 		return (pos = *save = NULL);	/* string contains only sep's */
     70 
     71 	tmp = pos;		/* now, keep position of the token */
     72 
     73 	while (*pos && strchr(fs, *pos) == NULL)
     74 		pos++;		/* skip content of the token */
     75 
     76 	if (*pos)
     77 		*pos++ = '\0';	/* remove first sep after the token */
     78 	else
     79 		pos = NULL;	/* end of string */
     80 
     81 	*save = pos;
     82 	return (tmp);
     83 }
     84 
     85 /** Walk through a string of tokens, using a set of separators.
     86  * This is the special version that won't skip/merge tokens,
     87  * eg "a,,c" would return "a", then "" (empty), then "c".
     88  * This in contrast to strtoken() which would return "a" and then "c".
     89  * This strtoken_noskip() will also not skip tokens at the
     90  * beginning, eg ",,c" would return "" (empty), "" (empty), "c".
     91  *
     92  * @param save	Pointer used for saving between calls
     93  * @param str	String to parse (will be altered!)
     94  * @param fs	Separator character(s)
     95  * @returns substring (token)
     96  * @note This function works similar to (but not identical?) to strtok_r().
     97  */
     98 char *strtoken_noskip(char **save, char *str, char *fs)
     99 {
    100 	char *pos, *tmp;
    101 
    102 	if (str)
    103 	{
    104 		pos = str;	/* new string scan */
    105 	} else {
    106 		if (*save == NULL)
    107 		{
    108 			/* We reached the end of the string */
    109 			return NULL;
    110 		}
    111 		pos = *save; /* keep last position across calls */
    112 	}
    113 
    114 	tmp = pos; /* start position, used for returning later */
    115 
    116 	/* Hunt for next separator (fs in pos) */
    117 	while (*pos && !strchr(fs, *pos))
    118 		pos++;
    119 
    120 	if (!*pos)
    121 	{
    122 		/* Next call is end of string */
    123 		*save = NULL;
    124 		*pos++ = '\0';
    125 	} else {
    126 		*pos++ = '\0';
    127 		*save = pos;
    128 	}
    129 
    130 	return tmp;
    131 }
    132 
    133 /** Convert binary address to an IP string - like inet_ntop but will always return the uncompressed IPv6 form.
    134  * @param af	Address family (AF_INET, AF_INET6)
    135  * @param in	Address (binary)
    136  * @param out	Buffer to use for storing the returned IP string
    137  * @param size	Size of the 'out' buffer
    138  * @returns IP address as a string (IPv4 or IPv6, in case of the latter:
    139  *          always the uncompressed form without ::)
    140  */
    141 const char *inetntop(int af, const void *in, char *out, size_t size)
    142 {
    143 	char tmp[MYDUMMY_SIZE];
    144 
    145 	inet_ntop(af, in, tmp, size);
    146 	if (!strstr(tmp, "::"))
    147 	{
    148 		/* IPv4 or IPv6 that is already uncompressed */
    149 		strlcpy(out, tmp, size);
    150 	} else
    151 	{
    152 		char cnt = 0, *cp = tmp, *op = out;
    153 
    154 		/* It's an IPv6 compressed address that we need to expand */
    155 		while (*cp)
    156 		{
    157 			if (*cp == ':')
    158 				cnt += 1;
    159 			if (*cp++ == '.')
    160 			{
    161 				cnt += 1;
    162 				break;
    163 			}
    164 		}
    165 		cp = tmp;
    166 		while (*cp)
    167 		{
    168 			*op++ = *cp++;
    169 			if (*(cp - 1) == ':' && *cp == ':')
    170 			{
    171 				if ((cp - 1) == tmp)
    172 				{
    173 					op--;
    174 					*op++ = '0';
    175 					*op++ = ':';
    176 				}
    177 
    178 				*op++ = '0';
    179 				while (cnt++ < 7)
    180 				{
    181 					*op++ = ':';
    182 					*op++ = '0';
    183 				}
    184 			}
    185 		}
    186 		if (*(op - 1) == ':')
    187 			*op++ = '0';
    188 		*op = '\0';
    189 	}
    190 	return out;
    191 }
    192 
    193 /** Cut string off at the first occurance of CR or LF */
    194 void stripcrlf(char *c)
    195 {
    196 	for (; *c; c++)
    197 	{
    198 		if ((*c == '\n') || (*c == '\r'))
    199 		{
    200 			*c = '\0';
    201 			return;
    202 		}
    203 	}
    204 }
    205 
    206 #ifndef HAVE_STRNLEN
    207 size_t strnlen(const char *s, size_t maxlen)
    208 {
    209 	const char *end = memchr (s, 0, maxlen);
    210 	return end ? (size_t)(end - s) : maxlen;
    211 }
    212 #endif
    213 
    214 #ifndef HAVE_STRLCPY
    215 /** BSD'ish strlcpy().
    216  * The strlcpy() function copies up to size-1 characters from the
    217  * NUL-terminated string src to dst, NUL-terminating the result.
    218  * Return: total length of the string tried to create.
    219  */
    220 size_t strlcpy(char *dst, const char *src, size_t size)
    221 {
    222 	size_t len = strlen(src);
    223 	size_t ret = len;
    224 
    225 	if (size <= 0)
    226 		return 0;
    227 	if (len >= size)
    228 		len = size - 1;
    229 	memcpy(dst, src, len);
    230 	dst[len] = 0;
    231 
    232 	return ret;
    233 }
    234 #endif
    235 
    236 #ifndef HAVE_STRLNCPY
    237 /** BSD'ish strlncpy() - similar to strlcpy but never copies more then n characters.
    238  */
    239 size_t strlncpy(char *dst, const char *src, size_t size, size_t n)
    240 {
    241 	size_t len = strnlen(src, n);
    242 	size_t ret = len;
    243 
    244 	if (size <= 0)
    245 		return 0;
    246 	if (len >= size)
    247 		len = size - 1;
    248 	memcpy(dst, src, len);
    249 	dst[len] = 0;
    250 
    251 	return ret;
    252 }
    253 #endif
    254 
    255 #ifndef HAVE_STRLCAT
    256 /* BSD'ish strlcat().
    257  * The strlcat() function appends the NUL-terminated string src to the end of
    258  * dst. It will append at most size - strlen(dst) - 1 bytes, NUL-terminating
    259  * the result.
    260  */
    261 size_t strlcat(char *dst, const char *src, size_t size)
    262 {
    263 	size_t len1 = strlen(dst);
    264 	size_t len2 = strlen(src);
    265 	size_t ret = len1 + len2;
    266 
    267 	if (size <= len1)
    268 		return size;
    269 	if (len1 + len2 >= size)
    270 		len2 = size - (len1 + 1);
    271 
    272 	if (len2 > 0) {
    273 		memcpy(dst + len1, src, len2);
    274 		dst[len1 + len2] = 0;
    275 	}
    276 	
    277 	return ret;
    278 }
    279 #endif
    280 
    281 #ifndef HAVE_STRLNCAT
    282 /** BSD'ish strlncat() - similar to strlcat but never cat more then n characters.
    283  */
    284 size_t strlncat(char *dst, const char *src, size_t size, size_t n)
    285 {
    286 	size_t len1 = strlen(dst);
    287 	size_t len2 = strnlen(src, n);
    288 	size_t ret = len1 + len2;
    289 
    290 	if (size <= len1)
    291 		return size;
    292 		
    293 	if (len1 + len2 >= size)
    294 		len2 = size - (len1 + 1);
    295 
    296 	if (len2 > 0) {
    297 		memcpy(dst + len1, src, len2);
    298 		dst[len1 + len2] = 0;
    299 	}
    300 
    301 	return ret;
    302 }
    303 #endif
    304 
    305 /** Like strlcpy but concat one letter */
    306 void strlcat_letter(char *buf, char c, size_t buflen)
    307 {
    308 	int n = strlen(buf);
    309 	if (!buflen || (n >= buflen-1))
    310 		return;
    311 	buf[n] = c;
    312 	buf[n+1] = '\0';
    313 }
    314 
    315 /** Copies a string and ensure the new buffer is at most 'max' size, including NUL.
    316  * The syntax is pretty much identical to strlcpy() except that
    317  * the buffer is newly allocated.
    318  * If you wonder why not use strndup() instead?
    319  * I feel that mixing code with strlcpy() and strndup() would be
    320  * rather confusing since strlcpy() assumes buffer size INCLUDING
    321  * the nul byte and strndup() assumes WITHOUT the nul byte and
    322  * will write one character extra. Hence this strldup(). -- Syzop
    323  */
    324 char *strldup(const char *src, size_t max)
    325 {
    326 	char *ptr;
    327 	int n;
    328 
    329 	if ((max == 0) || !src)
    330 		return NULL;
    331 
    332 	n = strlen(src);
    333 	if (n > max-1)
    334 		n = max-1;
    335 
    336 	ptr = safe_alloc(n+1);
    337 	memcpy(ptr, src, n);
    338 	ptr[n] = '\0';
    339 
    340 	return ptr;
    341 }
    342 
    343 static const char Base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    344 static const char Pad64 = '=';
    345 
    346 /* (From RFC1521 and draft-ietf-dnssec-secext-03.txt)
    347    The following encoding technique is taken from RFC 1521 by Borenstein
    348    and Freed.  It is reproduced here in a slightly edited form for
    349    convenience.
    350 
    351    A 65-character subset of US-ASCII is used, enabling 6 bits to be
    352    represented per printable character. (The extra 65th character, "=",
    353    is used to signify a special processing function.)
    354 
    355    The encoding process represents 24-bit groups of input bits as output
    356    strings of 4 encoded characters. Proceeding from left to right, a
    357    24-bit input group is formed by concatenating 3 8-bit input groups.
    358    These 24 bits are then treated as 4 concatenated 6-bit groups, each
    359    of which is translated into a single digit in the base64 alphabet.
    360 
    361    Each 6-bit group is used as an index into an array of 64 printable
    362    characters. The character referenced by the index is placed in the
    363    output string.
    364 
    365                          Table 1: The Base64 Alphabet
    366 
    367       Value Encoding  Value Encoding  Value Encoding  Value Encoding
    368           0 A            17 R            34 i            51 z
    369           1 B            18 S            35 j            52 0
    370           2 C            19 T            36 k            53 1
    371           3 D            20 U            37 l            54 2
    372           4 E            21 V            38 m            55 3
    373           5 F            22 W            39 n            56 4
    374           6 G            23 X            40 o            57 5
    375           7 H            24 Y            41 p            58 6
    376           8 I            25 Z            42 q            59 7
    377           9 J            26 a            43 r            60 8
    378          10 K            27 b            44 s            61 9
    379          11 L            28 c            45 t            62 +
    380          12 M            29 d            46 u            63 /
    381          13 N            30 e            47 v
    382          14 O            31 f            48 w         (pad) =
    383          15 P            32 g            49 x
    384          16 Q            33 h            50 y
    385 
    386    Special processing is performed if fewer than 24 bits are available
    387    at the end of the data being encoded.  A full encoding quantum is
    388    always completed at the end of a quantity.  When fewer than 24 input
    389    bits are available in an input group, zero bits are added (on the
    390    right) to form an integral number of 6-bit groups.  Padding at the
    391    end of the data is performed using the '=' character.
    392 
    393    Since all base64 input is an integral number of octets, only the
    394          -------------------------------------------------                       
    395    following cases can arise:
    396    
    397        (1) the final quantum of encoding input is an integral
    398            multiple of 24 bits; here, the final unit of encoded
    399 	   output will be an integral multiple of 4 characters
    400 	   with no "=" padding,
    401        (2) the final quantum of encoding input is exactly 8 bits;
    402            here, the final unit of encoded output will be two
    403 	   characters followed by two "=" padding characters, or
    404        (3) the final quantum of encoding input is exactly 16 bits;
    405            here, the final unit of encoded output will be three
    406 	   characters followed by one "=" padding character.
    407    */
    408 
    409 /** Base64 encode data.
    410  * @param src		The data to encode (input)
    411  * @param srclength	The length of the data to encode (input length)
    412  * @param target	The output buffer to use (output)
    413  * @param targetsize	The length of the output buffer to use (maximum output length)
    414  * @returns length of the targetsize, or -1 in case of error.
    415  */
    416 int b64_encode(unsigned char const *src, size_t srclength, char *target, size_t targsize)
    417 {
    418 	size_t datalength = 0;
    419 	u_char input[3];
    420 	u_char output[4];
    421 	size_t i;
    422 
    423 	while (2 < srclength) {
    424 		input[0] = *src++;
    425 		input[1] = *src++;
    426 		input[2] = *src++;
    427 		srclength -= 3;
    428 
    429 		output[0] = input[0] >> 2;
    430 		output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
    431 		output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
    432 		output[3] = input[2] & 0x3f;
    433 
    434 		if (datalength + 4 > targsize)
    435 			return (-1);
    436 		target[datalength++] = Base64[output[0]];
    437 		target[datalength++] = Base64[output[1]];
    438 		target[datalength++] = Base64[output[2]];
    439 		target[datalength++] = Base64[output[3]];
    440 	}
    441     
    442 	/* Now we worry about padding. */
    443 	if (0 != srclength) {
    444 		/* Get what's left. */
    445 		input[0] = input[1] = input[2] = '\0';
    446 		for (i = 0; i < srclength; i++)
    447 			input[i] = *src++;
    448 	
    449 		output[0] = input[0] >> 2;
    450 		output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
    451 		output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
    452 
    453 		if (datalength + 4 > targsize)
    454 			return (-1);
    455 		target[datalength++] = Base64[output[0]];
    456 		target[datalength++] = Base64[output[1]];
    457 		if (srclength == 1)
    458 			target[datalength++] = Pad64;
    459 		else
    460 			target[datalength++] = Base64[output[2]];
    461 		target[datalength++] = Pad64;
    462 	}
    463 	if (datalength >= targsize)
    464 		return (-1);
    465 	target[datalength] = '\0';	/* Returned value doesn't count \0. */
    466 	return (datalength);
    467 }
    468 
    469 /** Base64 decode a string.
    470  * @param src		The data to decode (input)
    471  * @param srclength	The length of the data to decode (input length)
    472  * @param target	The output buffer to use (output)
    473  * @param targetsize	The length of the output buffer to use (maximum output length)
    474  * @returns length of the targetsize, or -1 in case of error.
    475  * @note Skips whitespace, and hmm.. I think we don't require padding? Not sure.
    476  */
    477 int b64_decode(char const *src, unsigned char *target, size_t targsize)
    478 {
    479 	int tarindex, state, ch;
    480 	char *pos;
    481 
    482 	state = 0;
    483 	tarindex = 0;
    484 
    485 	while ((ch = *src++) != '\0') {
    486 		if (isspace(ch))	/* Skip whitespace anywhere. */
    487 			continue;
    488 
    489 		if (ch == Pad64)
    490 			break;
    491 
    492 		pos = strchr(Base64, ch);
    493 		if (pos == 0) 		/* A non-base64 character. */
    494 			return (-1);
    495 
    496 		switch (state) {
    497 		case 0:
    498 			if (target) {
    499 				if ((size_t)tarindex >= targsize)
    500 					return (-1);
    501 				target[tarindex] = (pos - Base64) << 2;
    502 			}
    503 			state = 1;
    504 			break;
    505 		case 1:
    506 			if (target) {
    507 				if ((size_t)tarindex + 1 >= targsize)
    508 					return (-1);
    509 				target[tarindex]   |=  (pos - Base64) >> 4;
    510 				target[tarindex+1]  = ((pos - Base64) & 0x0f)
    511 							<< 4 ;
    512 			}
    513 			tarindex++;
    514 			state = 2;
    515 			break;
    516 		case 2:
    517 			if (target) {
    518 				if ((size_t)tarindex + 1 >= targsize)
    519 					return (-1);
    520 				target[tarindex]   |=  (pos - Base64) >> 2;
    521 				target[tarindex+1]  = ((pos - Base64) & 0x03)
    522 							<< 6;
    523 			}
    524 			tarindex++;
    525 			state = 3;
    526 			break;
    527 		case 3:
    528 			if (target) {
    529 				if ((size_t)tarindex >= targsize)
    530 					return (-1);
    531 				target[tarindex] |= (pos - Base64);
    532 			}
    533 			tarindex++;
    534 			state = 0;
    535 			break;
    536 		default:
    537 			abort();
    538 		}
    539 	}
    540 
    541 	/*
    542 	 * We are done decoding Base-64 chars.  Let's see if we ended
    543 	 * on a byte boundary, and/or with erroneous trailing characters.
    544 	 */
    545 
    546 	if (ch == Pad64) {		/* We got a pad char. */
    547 		ch = *src++;		/* Skip it, get next. */
    548 		switch (state) {
    549 		case 0:		/* Invalid = in first position */
    550 		case 1:		/* Invalid = in second position */
    551 			return (-1);
    552 
    553 		case 2:		/* Valid, means one byte of info */
    554 			/* Skip any number of spaces. */
    555 			for (; ch != '\0'; ch = *src++)
    556 				if (!isspace(ch))
    557 					break;
    558 			/* Make sure there is another trailing = sign. */
    559 			if (ch != Pad64)
    560 				return (-1);
    561 			ch = *src++;		/* Skip the = */
    562 			/* Fall through to "single trailing =" case. */
    563 			/* FALLTHROUGH */
    564 
    565 		case 3:		/* Valid, means two bytes of info */
    566 			/*
    567 			 * We know this char is an =.  Is there anything but
    568 			 * whitespace after it?
    569 			 */
    570 			for (; ch != '\0'; ch = *src++)
    571 				if (!isspace(ch))
    572 					return (-1);
    573 
    574 			/*
    575 			 * Now make sure for cases 2 and 3 that the "extra"
    576 			 * bits that slopped past the last full byte were
    577 			 * zeros.  If we don't check them, they become a
    578 			 * subliminal channel.
    579 			 */
    580 			if (target && target[tarindex] != 0)
    581 				return (-1);
    582 		}
    583 	} else {
    584 		/*
    585 		 * We ended by seeing the end of the string.  Make sure we
    586 		 * have no partial bytes lying around.
    587 		 */
    588 		if (state != 0)
    589 			return (-1);
    590 	}
    591 
    592 	return (tarindex);
    593 }
    594 
    595 /* Natural sort case comparison routines. The following copyright header applies:
    596   strnatcmp.c -- Perform 'natural order' comparisons of strings in C.
    597   Copyright (C) 2000, 2004 by Martin Pool <mbp sourcefrog net>
    598 
    599   This software is provided 'as-is', without any express or implied
    600   warranty.  In no event will the authors be held liable for any damages
    601   arising from the use of this software.
    602 
    603   Permission is granted to anyone to use this software for any purpose,
    604   including commercial applications, and to alter it and redistribute it
    605   freely, subject to the following restrictions:
    606 
    607   1. The origin of this software must not be misrepresented; you must not
    608      claim that you wrote the original software. If you use this software
    609      in a product, an acknowledgment in the product documentation would be
    610      appreciated but is not required.
    611   2. Altered source versions must be plainly marked as such, and must not be
    612      misrepresented as being the original software.
    613   3. This notice may not be removed or altered from any source distribution.
    614 
    615  The code was modified / UnrealIRCderized by Bram Matthys ("Syzop").
    616  We always have unsigned char and this makes it possible to get rid
    617  of various stuff.. also re-indent this monster.
    618 */
    619 
    620 static int compare_right(char const *a, char const *b)
    621 {
    622 	int bias = 0;
    623 
    624 	/* The longest run of digits wins.  That aside, the greatest
    625 	 * value wins, but we can't know that it will until we've scanned
    626 	 * both numbers to know that they have the same magnitude, so we
    627 	 * remember it in BIAS.
    628 	 */
    629 	for (;; a++, b++)
    630 	{
    631 		if (!isdigit(*a) && !isdigit(*b))
    632 			return bias;
    633 		if (!isdigit(*a))
    634 			return -1;
    635 		if (!isdigit(*b))
    636 			return +1;
    637 		if (*a < *b)
    638 		{
    639 			if (!bias)
    640 				bias = -1;
    641 		} else
    642 		if (*a > *b)
    643 		{
    644 			if (!bias)
    645 				bias = +1;
    646 		} else
    647 		if (!*a && !*b)
    648 			return bias;
    649 	}
    650 
    651 	return 0;
    652 }
    653 
    654 
    655 static int compare_left(char const *a, char const *b)
    656 {
    657 	/* Compare two left-aligned numbers: the first to have a
    658 	 * different value wins.
    659 	 */
    660 	for (;; a++, b++)
    661 	{
    662 		if (!isdigit(*a) && !isdigit(*b))
    663 			return 0;
    664 		if (!isdigit(*a))
    665 			return -1;
    666 		if (!isdigit(*b))
    667 			return +1;
    668 		if (*a < *b)
    669 			return -1;
    670 		if (*a > *b)
    671 			return +1;
    672 	}
    673 
    674 	return 0;
    675 }
    676 
    677 static int strnatcmp0(char const *a, char const *b, int fold_case)
    678 {
    679 	int ai, bi;
    680 	char ca, cb;
    681 	int fractional, result;
    682 
    683 	ai = bi = 0;
    684 	while (1)
    685 	{
    686 		ca = a[ai]; cb = b[bi];
    687 
    688 		/* skip over leading spaces or zeros */
    689 		while (isspace(ca))
    690 			ca = a[++ai];
    691 
    692 		while (isspace(cb))
    693 			cb = b[++bi];
    694 
    695 		/* process run of digits */
    696 		if (isdigit(ca)  &&  isdigit(cb))
    697 		{
    698 			fractional = (ca == '0' || cb == '0');
    699 			if (fractional)
    700 			{
    701 				if ((result = compare_left(a+ai, b+bi)) != 0)
    702 					return result;
    703 			} else {
    704 				if ((result = compare_right(a+ai, b+bi)) != 0)
    705 					return result;
    706 			}
    707 		}
    708 
    709 		if (!ca && !cb)
    710 		{
    711 			/* The strings compare the same.  Perhaps the caller
    712 			 * will want to call strcmp to break the tie.
    713 			 */
    714 			return 0;
    715 		}
    716 
    717 		if (fold_case)
    718 		{
    719 			ca = toupper(ca);
    720 			cb = toupper(cb);
    721 		}
    722 
    723 		if (ca < cb)
    724 			return -1;
    725 
    726 		if (ca > cb)
    727 			return +1;
    728 
    729 		ai++;
    730 		bi++;
    731 	}
    732 }
    733 
    734 /** Like strcmp() but with "natural sort", so that for example
    735  * the string "1.4.10" is seen as higher than "1.4.9"
    736  * This is the case sensitive version.
    737  */
    738 int strnatcmp(char const *a, char const *b)
    739 {
    740 	return strnatcmp0(a, b, 0);
    741 }
    742 
    743 /** Like strcmp() but with "natural sort", so that for example
    744  * the string "1.4.10" is seen as higher than "1.4.9"
    745  * This is the case insensitive version.
    746  */
    747 int strnatcasecmp(char const *a, char const *b)
    748 {
    749 	return strnatcmp0(a, b, 1);
    750 }
    751 
    752 /* End of natural sort case comparison functions */
    753 
    754 /* Memory allocation routines */
    755 
    756 /** Allocate memory - should always be used instead of malloc/calloc.
    757  * @param size How many bytes to allocate
    758  * @returns A pointer to the newly allocated memory.
    759  * @note If out of memory then the IRCd will exit.
    760  */
    761 void *safe_alloc(size_t size)
    762 {
    763 	void *p;
    764 	if (size == 0)
    765 		return NULL;
    766 	p = calloc(1, size);
    767 	if (!p)
    768 		outofmemory(size);
    769 	return p;
    770 }
    771 
    772 /** Safely duplicate a string */
    773 char *our_strdup(const char *str)
    774 {
    775 	char *ret = strdup(str);
    776 	if (!ret)
    777 		outofmemory(strlen(str));
    778 	return ret;
    779 }
    780 
    781 /** Safely duplicate a string with a maximum size */
    782 char *our_strldup(const char *str, size_t max)
    783 {
    784 	char *ret = strldup(str, max);
    785 	if (!ret)
    786 		outofmemory(MAX(strlen(str), max));
    787 	return ret;
    788 }
    789 
    790 /** Called when out of memory */
    791 void outofmemory(size_t bytes)
    792 {
    793 	static int log_attempt = 1;
    794 
    795 	if (log_attempt)
    796 	{
    797 		/* This will probably fail, but we can try... */
    798 		unreal_log(ULOG_ERROR, "main", "OUT_OF_MEMORY", NULL,
    799 		           "Out of memory while trying to allocate $bytes bytes!",
    800 		           log_data_integer("bytes", bytes));
    801 		log_attempt = 0;
    802 	}
    803 	exit(7);
    804 }
    805 
    806 /** Allocate sensitive memory - this should only be used for HIGHLY sensitive data, since
    807  * it wastes 8192+ bytes even if only asked to allocate for example 32 bytes (this is by design).
    808  * @param size How many bytes to allocate
    809  * @returns A pointer to the newly allocated memory.
    810  * @note If out of memory then the IRCd will exit.
    811  */
    812 void *safe_alloc_sensitive(size_t size)
    813 {
    814 	void *p;
    815 	if (size == 0)
    816 		return NULL;
    817 	p = sodium_malloc(((size/32)*32)+32);
    818 	if (!p)
    819 		outofmemory(size);
    820 	memset(p, 0, size);
    821 	return p;
    822 }
    823 
    824 /** Safely duplicate a string */
    825 char *our_strdup_sensitive(const char *str)
    826 {
    827 	char *ret = safe_alloc_sensitive(strlen(str)+1);
    828 	if (!ret)
    829 		outofmemory(strlen(str));
    830 	strcpy(ret, str); /* safe, see above */
    831 	return ret;
    832 }
    833 
    834 /** Returns a unique filename in the specified directory
    835  * using the specified suffix. The returned value will
    836  * be of the form <dir>/<random-hex>.<suffix>
    837  */
    838 char *unreal_mktemp(const char *dir, const char *suffix)
    839 {
    840 	FILE *fd;
    841 	unsigned int i;
    842 	static char tempbuf[PATH_MAX+1];
    843 
    844 	for (i = 500; i > 0; i--)
    845 	{
    846 		snprintf(tempbuf, PATH_MAX, "%s/%X.%s", dir, getrandom32(), suffix);
    847 		fd = fopen(tempbuf, "r");
    848 		if (!fd)
    849 			return tempbuf;
    850 		fclose(fd);
    851 	}
    852 	config_error("Unable to create temporary file in directory '%s': %s",
    853 		dir, strerror(errno)); /* eg: permission denied :p */
    854 	return NULL; 
    855 }
    856 
    857 /** Returns the path portion of the given path/file
    858  * in the specified location (must be at least PATH_MAX bytes).
    859  */
    860 char *unreal_getpathname(const char *filepath, char *path)
    861 {
    862 	const char *end = filepath+strlen(filepath);
    863 
    864 	while (*end != '\\' && *end != '/' && end > filepath)
    865 		end--;
    866 	if (end == filepath)
    867 		path = NULL;
    868 	else
    869 	{
    870 		int size = end-filepath;
    871 		if (size >= PATH_MAX)
    872 			path = NULL;
    873 		else
    874 		{
    875 			memcpy(path, filepath, size);
    876 			path[size] = 0;
    877 		}
    878 	}
    879 	return path;
    880 }
    881 
    882 /** Returns the filename portion of the given path.
    883  * The original string is not modified
    884  */
    885 const char *unreal_getfilename(const char *path)
    886 {
    887 	int len = strlen(path);
    888 	const char *end;
    889 
    890 	if (!len)
    891 		return NULL;
    892 
    893 	end = path+len-1;
    894 	if (*end == '\\' || *end == '/')
    895 		return NULL;
    896 
    897 	while (end > path)
    898 	{
    899 		if (*end == '\\' || *end == '/')
    900 		{
    901 			end++;
    902 			break;
    903 		}
    904 		end--;
    905 	}
    906 
    907 	return end;
    908 }
    909 
    910 /** Wrapper for mkdir() so you don't need ifdefs everywhere for Windows.
    911  * @returns 0 on failure!! (like mkdir)
    912  */
    913 int unreal_mkdir(const char *pathname, mode_t mode)
    914 {
    915 #ifdef _WIN32
    916 	return mkdir(pathname);
    917 #else
    918 	return mkdir(pathname, mode);
    919 #endif
    920 }
    921 
    922 /** Create the entire directory structure.
    923  * @param dname	The directory name, eg /home/irc/unrealircd/logs/2022/08/05
    924  * @param mode	The mode to create with, eg 0777. Ignored on Windows.
    925  * @returns 1 on success, 0 on failure.
    926  */
    927 int unreal_create_directory_structure(const char *dname, mode_t mode)
    928 {
    929 	if (unreal_mkdir(dname, mode) == 0)
    930 	{
    931 		/* Ok, that failed as well, we have some work to do:
    932 		 * for every path element run mkdir().
    933 		 */
    934 		int lastresult;
    935 		char buf[512], *p;
    936 		strlcpy(buf, dname, sizeof(buf)); /* work on a copy */
    937 		for (p=strchr(buf+1, '/'); p; p=strchr(p+1, '/'))
    938 		{
    939 			*p = '\0';
    940 			unreal_mkdir(buf,mode);
    941 			*p = '/';
    942 		}
    943 		/* Finally, try the complete path */
    944 		if (unreal_mkdir(dname, mode))
    945 			return 0; /* failed */
    946 		/* fallthrough.... */
    947 	}
    948 	return 1; /* success */
    949 }
    950 
    951 /** Create entire directory structure for a path with a filename.
    952  * @param fname	The full path name, eg /home/irc/unrealircd/logs/2022/08/05/ircd.log
    953  * @param mode	The mode to create with, eg 0777. Ignored on Windows.
    954  * @notes This is used as an easier way to call unreal_create_directory_structure()
    955  *        if you have a filename instead of the directory part.
    956  * @returns 1 on success, 0 on failure.
    957  */
    958 int unreal_create_directory_structure_for_file(const char *fname, mode_t mode)
    959 {
    960 	char buf[PATH_MAX+1];
    961 	const char *path = unreal_getpathname(fname, buf);
    962 	if (!path)
    963 		return 0;
    964 	return unreal_create_directory_structure(path, mode);
    965 }
    966 
    967 /** Returns the special module tmp name for a given path.
    968  * The original string is not modified.
    969  */
    970 const char *unreal_getmodfilename(const char *path)
    971 {
    972 	static char ret[512];
    973 	char buf[512];
    974 	char *p;
    975 	char *name = NULL;
    976 	char *directory = NULL;
    977 	
    978 	if (BadPtr(path))
    979 		return path;
    980 	
    981 	strlcpy(buf, path, sizeof(buf));
    982 	
    983 	/* Backtrack... */
    984 	for (p = buf + strlen(buf); p >= buf; p--)
    985 	{
    986 		if ((*p == '/') || (*p == '\\'))
    987 		{
    988 			name = p+1;
    989 			*p = '\0';
    990 			directory = buf; /* fallback */
    991 			for (; p >= buf; p--)
    992 			{
    993 				if ((*p == '/') || (*p == '\\'))
    994 				{
    995 					directory = p + 1;
    996 					break;
    997 				}
    998 			}
    999 			break;
   1000 		}
   1001 	}
   1002 	
   1003 	if (!name)
   1004 		name = buf;
   1005 	
   1006 	if (!directory || !strcmp(directory, "modules"))
   1007 		snprintf(ret, sizeof(ret), "%s", name);
   1008 	else
   1009 		snprintf(ret, sizeof(ret), "%s.%s", directory, name);
   1010 	
   1011 	return ret;
   1012 }
   1013 
   1014 /* Returns a consistent filename for the cache/ directory.
   1015  * Returned value will be like: cache/<hash of url>
   1016  */
   1017 const char *unreal_mkcache(const char *url)
   1018 {
   1019 	static char tempbuf[PATH_MAX+1];
   1020 	char tmp2[128];
   1021 	
   1022 	snprintf(tempbuf, PATH_MAX, "%s/%s", CACHEDIR, sha256hash(tmp2, url, strlen(url)));
   1023 	return tempbuf;
   1024 }
   1025 
   1026 /** Returns 1 if a cached version of the url exists, otherwise 0. */
   1027 int has_cached_version(const char *url)
   1028 {
   1029 	return file_exists(unreal_mkcache(url));
   1030 }
   1031 
   1032 /** Used to blow away result of bad copy or cancel file copy */
   1033 void cancel_copy(int srcfd, int destfd, const char *dest)
   1034 {
   1035 	close(srcfd);
   1036 	close(destfd);
   1037 	unlink(dest);
   1038 }
   1039 
   1040 /** Copys the contents of the src file to the dest file.
   1041  * The dest file will have permissions r-x------
   1042  */
   1043 int unreal_copyfile(const char *src, const char *dest)
   1044 {
   1045 	char buf[2048];
   1046 	time_t mtime;
   1047 	int srcfd, destfd, len;
   1048 
   1049 	mtime = unreal_getfilemodtime(src);
   1050 
   1051 #ifndef _WIN32
   1052 	srcfd = open(src, O_RDONLY);
   1053 #else
   1054 	srcfd = open(src, _O_RDONLY|_O_BINARY);
   1055 #endif
   1056 
   1057 	if (srcfd < 0)
   1058 	{
   1059 		config_error("Unable to open file '%s': %s", src, strerror(errno));
   1060 		return 0;
   1061 	}
   1062 
   1063 #ifndef _WIN32
   1064 #if defined(DEFAULT_PERMISSIONS) && (DEFAULT_PERMISSIONS != 0)
   1065 	destfd  = open(dest, O_WRONLY|O_CREAT, DEFAULT_PERMISSIONS);
   1066 #else
   1067 	destfd  = open(dest, O_WRONLY|O_CREAT, S_IRUSR | S_IXUSR);
   1068 #endif /* DEFAULT_PERMISSIONS */
   1069 #else
   1070 	destfd = open(dest, _O_BINARY|_O_WRONLY|_O_CREAT, _S_IWRITE);
   1071 #endif /* _WIN32 */
   1072 	if (destfd < 0)
   1073 	{
   1074 		config_error("Unable to create file '%s': %s", dest, strerror(errno));
   1075 		close(srcfd);
   1076 		return 0;
   1077 	}
   1078 
   1079 	while ((len = read(srcfd, buf, 1023)) > 0)
   1080 		if (write(destfd, buf, len) != len)
   1081 		{
   1082 			config_error("Write error to file '%s': %s [not enough free hd space / quota? need several mb's!]",
   1083 				dest, strerror(ERRNO));
   1084 			cancel_copy(srcfd,destfd,dest);
   1085 			return 0;
   1086 		}
   1087 
   1088 	if (len < 0) /* very unusual.. perhaps an I/O error */
   1089 	{
   1090 		config_error("Read error from file '%s': %s", src, strerror(errno));
   1091 		cancel_copy(srcfd,destfd,dest);
   1092 		return 0;
   1093 	}
   1094 
   1095 	close(srcfd);
   1096 	close(destfd);
   1097 	unreal_setfilemodtime(dest, mtime);
   1098 	return 1;
   1099 }
   1100 
   1101 /** Same as unreal_copyfile, but with an option to try hardlinking first */
   1102 int unreal_copyfileex(const char *src, const char *dest, int tryhardlink)
   1103 {
   1104 	unlink(dest);
   1105 #ifndef _WIN32
   1106 	/* Try a hardlink first... */
   1107 	if (tryhardlink && !link(src, dest))
   1108 		return 1; /* success */
   1109 #endif
   1110 	return unreal_copyfile(src, dest);
   1111 }
   1112 
   1113 /** Set the modification time on a file */
   1114 void unreal_setfilemodtime(const char *filename, time_t mtime)
   1115 {
   1116 #ifndef _WIN32
   1117 	struct utimbuf utb;
   1118 	utb.actime = utb.modtime = mtime;
   1119 	utime(filename, &utb);
   1120 #else
   1121 	FILETIME mTime;
   1122 	LONGLONG llValue;
   1123 	HANDLE hFile = CreateFile(filename, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
   1124 				  FILE_ATTRIBUTE_NORMAL, NULL);
   1125 	if (hFile == INVALID_HANDLE_VALUE)
   1126 		return;
   1127 	llValue = Int32x32To64(mtime, 10000000) + 116444736000000000;
   1128 	mTime.dwLowDateTime = (long)llValue;
   1129 	mTime.dwHighDateTime = llValue >> 32;
   1130 	
   1131 	SetFileTime(hFile, &mTime, &mTime, &mTime);
   1132 	CloseHandle(hFile);
   1133 #endif
   1134 }
   1135 
   1136 /** Get the modification time ("last modified") of a file */
   1137 time_t unreal_getfilemodtime(const char *filename)
   1138 {
   1139 #ifndef _WIN32
   1140 	struct stat sb;
   1141 	if (stat(filename, &sb))
   1142 		return 0;
   1143 	return sb.st_mtime;
   1144 #else
   1145 	/* See how much more fun WinAPI programming is??? */
   1146 	FILETIME cTime;
   1147 	SYSTEMTIME sTime, lTime;
   1148 	ULARGE_INTEGER fullTime;
   1149 	time_t result;
   1150 	HANDLE hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
   1151 				  FILE_ATTRIBUTE_NORMAL, NULL);
   1152 	if (hFile == INVALID_HANDLE_VALUE)
   1153 		return 0;
   1154 	if (!GetFileTime(hFile, NULL, NULL, &cTime))
   1155 		return 0;
   1156 
   1157 	CloseHandle(hFile);
   1158 
   1159 	FileTimeToSystemTime(&cTime, &sTime);
   1160 	SystemTimeToTzSpecificLocalTime(NULL, &sTime, &lTime);
   1161 	SystemTimeToFileTime(&sTime, &cTime);
   1162 
   1163 	fullTime.LowPart = cTime.dwLowDateTime;
   1164 	fullTime.HighPart = cTime.dwHighDateTime;
   1165 	fullTime.QuadPart -= 116444736000000000;
   1166 	fullTime.QuadPart /= 10000000;
   1167 	
   1168 	return fullTime.LowPart;	
   1169 #endif
   1170 }
   1171 
   1172 #ifndef	AF_INET6
   1173 #define	AF_INET6	AF_MAX+1	/* just to let this compile */
   1174 #endif
   1175 
   1176 /** Encode an IP string (eg: "1.2.3.4") to a BASE64 encoded value for S2S traffic */
   1177 const char *encode_ip(const char *ip)
   1178 {
   1179 	static char retbuf[25]; /* returned string */
   1180 	char addrbuf[16];
   1181 
   1182 	if (!ip)
   1183 		return "*";
   1184 
   1185 	if (strchr(ip, ':'))
   1186 	{
   1187 		/* IPv6 (likely) */
   1188 		inet_pton(AF_INET6, ip, addrbuf);
   1189 		/* hack for IPv4-in-IPv6 (::ffff:1.2.3.4) */
   1190 		if (addrbuf[0] == 0 && addrbuf[1] == 0 && addrbuf[2] == 0 && addrbuf[3] == 0
   1191 		    && addrbuf[4] == 0 && addrbuf[5] == 0 && addrbuf[6] == 0
   1192 			&& addrbuf[7] == 0 && addrbuf[8] == 0 && addrbuf[9] == 0
   1193 			&& addrbuf[10] == 0xff && addrbuf[11] == 0xff)
   1194 		{
   1195 			b64_encode(&addrbuf[12], sizeof(struct in_addr), retbuf, sizeof(retbuf));
   1196 		} else {
   1197 			b64_encode(addrbuf, 16, retbuf, sizeof(retbuf));
   1198 		}
   1199 	}
   1200 	else
   1201 	{
   1202 		/* IPv4 */
   1203 		inet_pton(AF_INET, ip, addrbuf);
   1204 		b64_encode((char *)&addrbuf, sizeof(struct in_addr), retbuf, sizeof(retbuf));
   1205 	}
   1206 	return retbuf;
   1207 }
   1208 
   1209 /** Decode a BASE64 encoded string to an IP address string. Used for S2S traffic. */
   1210 const char *decode_ip(const char *buf)
   1211 {
   1212 	int n;
   1213 	char targ[25];
   1214 	static char result[64];
   1215 
   1216 	n = b64_decode(buf, targ, sizeof(targ));
   1217 	if (n == 4) /* should be IPv4 */
   1218 		return inetntop(AF_INET, targ, result, sizeof(result));
   1219 	else if (n == 16) /* should be IPv6 */
   1220 		return inetntop(AF_INET6, targ, result, sizeof(result));
   1221 	else /* Error! */
   1222 		return NULL;
   1223 }
   1224 
   1225 /* IPv6 stuff */
   1226 
   1227 #ifndef IN6ADDRSZ
   1228 #define	IN6ADDRSZ	16
   1229 #endif
   1230 
   1231 #ifndef INT16SZ
   1232 #define	INT16SZ		 2
   1233 #endif
   1234 
   1235 #ifndef INADDRSZ
   1236 #define	INADDRSZ	 4
   1237 #endif
   1238 
   1239 #ifdef _WIN32
   1240 /* Microsoft makes things nice and fun for us! */
   1241 struct u_WSA_errors {
   1242 	int error_code;
   1243 	char *error_string;
   1244 };
   1245 
   1246 /* Must be sorted ascending by error code */
   1247 struct u_WSA_errors WSAErrors[] = {
   1248  { WSAEINTR,              "Interrupted system call" },
   1249  { WSAEBADF,              "Bad file number" },
   1250  { WSAEACCES,             "Permission denied" },
   1251  { WSAEFAULT,             "Bad address" },
   1252  { WSAEINVAL,             "Invalid argument" },
   1253  { WSAEMFILE,             "Too many open sockets" },
   1254  { WSAEWOULDBLOCK,        "Operation would block" },
   1255  { WSAEINPROGRESS,        "Operation now in progress" },
   1256  { WSAEALREADY,           "Operation already in progress" },
   1257  { WSAENOTSOCK,           "Socket operation on non-socket" },
   1258  { WSAEDESTADDRREQ,       "Destination address required" },
   1259  { WSAEMSGSIZE,           "Message too long" },
   1260  { WSAEPROTOTYPE,         "Protocol wrong type for socket" },
   1261  { WSAENOPROTOOPT,        "Bad protocol option" },
   1262  { WSAEPROTONOSUPPORT,    "Protocol not supported" },
   1263  { WSAESOCKTNOSUPPORT,    "Socket type not supported" },
   1264  { WSAEOPNOTSUPP,         "Operation not supported on socket" },
   1265  { WSAEPFNOSUPPORT,       "Protocol family not supported" },
   1266  { WSAEAFNOSUPPORT,       "Address family not supported" },
   1267  { WSAEADDRINUSE,         "Address already in use" },
   1268  { WSAEADDRNOTAVAIL,      "Can't assign requested address" },
   1269  { WSAENETDOWN,           "Network is down" },
   1270  { WSAENETUNREACH,        "Network is unreachable" },
   1271  { WSAENETRESET,          "Net connection reset" },
   1272  { WSAECONNABORTED,       "Software caused connection abort" },
   1273  { WSAECONNRESET,         "Connection reset by peer" },
   1274  { WSAENOBUFS,            "No buffer space available" },
   1275  { WSAEISCONN,            "Socket is already connected" },
   1276  { WSAENOTCONN,           "Socket is not connected" },
   1277  { WSAESHUTDOWN,          "Can't send after socket shutdown" },
   1278  { WSAETOOMANYREFS,       "Too many references, can't splice" },
   1279  { WSAETIMEDOUT,          "Connection timed out" },
   1280  { WSAECONNREFUSED,       "Connection refused" },
   1281  { WSAELOOP,              "Too many levels of symbolic links" },
   1282  { WSAENAMETOOLONG,       "File name too long" },
   1283  { WSAEHOSTDOWN,          "Host is down" },
   1284  { WSAEHOSTUNREACH,       "No route to host" },
   1285  { WSAENOTEMPTY,          "Directory not empty" },
   1286  { WSAEPROCLIM,           "Too many processes" },
   1287  { WSAEUSERS,             "Too many users" },
   1288  { WSAEDQUOT,             "Disc quota exceeded" },
   1289  { WSAESTALE,             "Stale NFS file handle" },
   1290  { WSAEREMOTE,            "Too many levels of remote in path" },
   1291  { WSASYSNOTREADY,        "Network subsystem is unavailable" },
   1292  { WSAVERNOTSUPPORTED,    "Winsock version not supported" },
   1293  { WSANOTINITIALISED,     "Winsock not yet initialized" },
   1294  { WSAHOST_NOT_FOUND,     "Host not found" },
   1295  { WSATRY_AGAIN,          "Non-authoritative host not found" },
   1296  { WSANO_RECOVERY,        "Non-recoverable errors" },
   1297  { WSANO_DATA,            "Valid name, no data record of requested type" },
   1298  { WSAEDISCON,            "Graceful disconnect in progress" },
   1299  { WSASYSCALLFAILURE,     "System call failure" },
   1300  { 0,NULL}
   1301 };
   1302 
   1303 /** Get socket error string */
   1304 const char *sock_strerror(int error)
   1305 {
   1306 	static char unkerr[64];
   1307 	int start = 0;
   1308 	int stop = sizeof(WSAErrors)/sizeof(WSAErrors[0])-1;
   1309 	int mid;
   1310 	
   1311 	if (!error)
   1312 		return "No error";
   1313 
   1314 	if (error < WSABASEERR) /* Just a regular error code */
   1315 		return strerror(error);
   1316 
   1317 	/* Microsoft decided not to use sequential numbers for the error codes,
   1318 	 * so we can't just use the array index for the code. But, at least
   1319 	 * use a binary search to make it as fast as possible. 
   1320 	 */
   1321 	while (start <= stop)
   1322 	{
   1323 		mid = (start+stop)/2;
   1324 		if (WSAErrors[mid].error_code > error)
   1325 			stop = mid-1;
   1326 		
   1327 		else if (WSAErrors[mid].error_code < error)
   1328 			start = mid+1;
   1329 		else
   1330 			return WSAErrors[mid].error_string;	
   1331 	}
   1332 	snprintf(unkerr, sizeof(unkerr), "Unknown Error: %d", error);
   1333 	return unkerr;
   1334 }
   1335 #endif
   1336 
   1337 /** Build a string and replace $variables where needed.
   1338  * See src/modules/blacklist.c for an example.
   1339  * @param inbuf		The input string
   1340  * @param outbuf	The output string
   1341  * @param len		The maximum size of the output string (including NUL)
   1342  * @param name		Array of variables names
   1343  * @param value		Array of variable values
   1344  */
   1345 void buildvarstring(const char *inbuf, char *outbuf, size_t len, const char *name[], const char *value[])
   1346 {
   1347 	const char *i, *p;
   1348 	char *o;
   1349 	int left = len - 1;
   1350 	int cnt, found;
   1351 
   1352 #ifdef DEBUGMODE
   1353 	if (len <= 0)
   1354 		abort();
   1355 #endif
   1356 
   1357 	for (i = inbuf, o = outbuf; *i; i++)
   1358 	{
   1359 		if (*i == '$')
   1360 		{
   1361 			i++;
   1362 
   1363 			/* $$ = literal $ */
   1364 			if (*i == '$')
   1365 				goto literal;
   1366 
   1367 			if (!isalnum(*i))
   1368 			{
   1369 				/* What do we do with things like '$/' ? -- treat literal */
   1370 				i--;
   1371 				goto literal;
   1372 			}
   1373 			
   1374 			/* find termination */
   1375 			for (p=i; isalnum(*p); p++);
   1376 			
   1377 			/* find variable name in list */
   1378 			found = 0;
   1379 			for (cnt = 0; name[cnt]; cnt++)
   1380 				if (!strncasecmp(name[cnt], i, strlen(name[cnt])))
   1381 				{
   1382 					/* Found */
   1383 					found = 1;
   1384 
   1385 					if (!BadPtr(value[cnt]))
   1386 					{
   1387 						strlcpy(o, value[cnt], left);
   1388 						left -= strlen(value[cnt]); /* may become <0 */
   1389 						if (left <= 0)
   1390 							return; /* return - don't write \0 to 'o'. ensured by strlcpy already */
   1391 						o += strlen(value[cnt]); /* value entirely written */
   1392 					}
   1393 
   1394 					break; /* done */
   1395 				}
   1396 			
   1397 			if (!found)
   1398 			{
   1399 				/* variable name does not exist -- treat literal */
   1400 				i--;
   1401 				goto literal;
   1402 			}
   1403 
   1404 			/* value written. we're done. */
   1405 			i = p - 1;
   1406 			continue;
   1407 		}
   1408 literal:
   1409 		if (!left)
   1410 			break;
   1411 		*o++ = *i;
   1412 		left--;
   1413 		if (!left)
   1414 			break;
   1415 	}
   1416 	*o = '\0';
   1417 }
   1418 
   1419 /** Return the PCRE2 library version in use */
   1420 const char *pcre2_version(void)
   1421 {
   1422 	static char buf[256];
   1423 
   1424 	strlcpy(buf, "PCRE2 ", sizeof(buf));
   1425 	pcre2_config(PCRE2_CONFIG_VERSION, buf+6);
   1426 	return buf;
   1427 }
   1428 
   1429 
   1430 #ifdef _WIN32
   1431 /** POSIX gettimeofday function for Windows (ignoring timezones) */
   1432 int gettimeofday(struct timeval *tp, void *tzp)
   1433 {
   1434 	// This magic number is the number of 100 nanosecond intervals since January 1, 1601 (UTC)
   1435 	// until 00:00:00 January 1, 1970
   1436 	static const uint64_t EPOCH = ((uint64_t) 116444736000000000ULL);
   1437 
   1438 	SYSTEMTIME system_time;
   1439 	FILETIME file_time;
   1440 	uint64_t time;
   1441 
   1442 	GetSystemTime( &system_time );
   1443 	SystemTimeToFileTime( &system_time, &file_time );
   1444 	time =  ((uint64_t)file_time.dwLowDateTime )      ;
   1445 	time += ((uint64_t)file_time.dwHighDateTime) << 32;
   1446 
   1447 	tp->tv_sec  = (long) ((time - EPOCH) / 10000000L);
   1448 	tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
   1449 	return 0;
   1450 }
   1451 #endif
   1452 
   1453 /** Get the numer of characters per line that fit on the terminal (the width) */
   1454 int get_terminal_width(void)
   1455 {
   1456 #if defined(_WIN32) || !defined(TIOCGWINSZ)
   1457 	return 80;
   1458 #else
   1459 	struct winsize w;
   1460 	ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
   1461 	return w.ws_col;
   1462 #endif
   1463 }
   1464 
   1465 #if defined(__GNUC__)
   1466 #pragma GCC diagnostic push
   1467 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
   1468 #endif
   1469 
   1470 /** Like strftime() but easier. */
   1471 char *unreal_strftime(const char *str)
   1472 {
   1473 	time_t t;
   1474 	struct tm *tmp;
   1475 	static char buf[512];
   1476 
   1477 	t = time(NULL);
   1478 	tmp = localtime(&t);
   1479 	if (!tmp || !strftime(buf, sizeof(buf), str, tmp))
   1480 	{
   1481 		/* If anything fails bigtime, then return the format string */
   1482 		strlcpy(buf, str, sizeof(buf));
   1483 		return buf;
   1484 	}
   1485 	return buf;
   1486 }
   1487 
   1488 #if defined(__GNUC__)
   1489 #pragma GCC diagnostic pop
   1490 #endif
   1491 
   1492 /** Convert a string to lowercase - with separate input/output buffer */
   1493 void strtolower_safe(char *dst, const char *src, int size)
   1494 {
   1495 	if (!size)
   1496 		return; /* size of 0 is unworkable */
   1497 	size--; /* for \0 */
   1498 
   1499 	for (; *src && size; src++)
   1500 	{
   1501 		*dst++ = tolower(*src);
   1502 		size--;
   1503 	}
   1504 	*dst = '\0';
   1505 }
   1506 
   1507 /** Convert a string to lowercase - modifying existing string */
   1508 void strtolower(char *str)
   1509 {
   1510 	for (; *str; str++)
   1511 		*str = tolower(*str);
   1512 }
   1513 
   1514 /** Convert a string to uppercase - with separate input/output buffer */
   1515 void strtoupper_safe(char *dst, const char *src, int size)
   1516 {
   1517 	if (!size)
   1518 		return; /* size of 0 is unworkable */
   1519 	size--; /* for \0 */
   1520 
   1521 	for (; *src && size; src++)
   1522 	{
   1523 		*dst++ = toupper(*src);
   1524 		size--;
   1525 	}
   1526 	*dst = '\0';
   1527 }
   1528 
   1529 /** Convert a string to uppercase - modifying existing string */
   1530 void strtoupper(char *str)
   1531 {
   1532 	for (; *str; str++)
   1533 		*str = toupper(*str);
   1534 }