Skip to content
Snippets Groups Projects
utils.c 73.6 KiB
Newer Older
  • Learn to ignore specific revisions
  •  * Asterisk -- An open source telephony toolkit.
    
     * Copyright (C) 1999 - 2006, Digium, Inc.
    
     * See http://www.asterisk.org for more information about
     * the Asterisk project. Please do not directly contact
     * any of the maintainers of this project for assistance;
     * the project provides a web site, mailing lists and IRC
     * channels for your use.
    
     *
     * This program is free software, distributed under the terms of
    
     * the GNU General Public License Version 2. See the LICENSE file
     * at the top of the source tree.
     */
    
    
     * \brief Utility functions
    
     * \note These are important for portability and security,
     * so please use them in favour of other routines.
     * Please consult the CODING GUIDELINES for more information.
    
    /*** MODULEINFO
    	<support_level>core</support_level>
     ***/
    
    
    #include <ctype.h>
    
    David M. Lee's avatar
    David M. Lee committed
    #include <fcntl.h>
    
    David M. Lee's avatar
    David M. Lee committed
    #include <unistd.h>
    
    #if defined(__APPLE__)
    #include <mach/mach.h>
    
    #elif defined(__NetBSD__)
    #include <lwp.h>
    
    #elif defined(HAVE_SYS_THR_H)
    #include <sys/thr.h>
    #endif
    
    
    #include "asterisk/network.h"
    
    #include "asterisk/ast_version.h"
    
    #define AST_API_MODULE		/* ensure that inlinable API functions will be built in lock.h if required */
    
    #include "asterisk/lock.h"
    #include "asterisk/io.h"
    #include "asterisk/md5.h"
    
    #include "asterisk/cli.h"
    #include "asterisk/linkedlists.h"
    
    #define AST_API_MODULE		/* ensure that inlinable API functions will be built in this module if required */
    #include "asterisk/strings.h"
    
    #define AST_API_MODULE		/* ensure that inlinable API functions will be built in this module if required */
    #include "asterisk/time.h"
    
    
    #define AST_API_MODULE		/* ensure that inlinable API functions will be built in this module if required */
    #include "asterisk/utils.h"
    
    
    #define AST_API_MODULE
    #include "asterisk/threadstorage.h"
    
    
    #define AST_API_MODULE
    #include "asterisk/config.h"
    
    
    #define AST_API_MODULE
    #include "asterisk/alertpipe.h"
    
    
    /* These arrays are global static variables because they are only modified
     * once - in base64_init. The only purpose they have is to serve as a dictionary
     * for encoding and decoding base64 and base64 URL, so there's no harm in
     * accessing these arrays in multiple threads.
     */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static char base64[64];
    
    static char base64url[64];
    
    Mark Spencer's avatar
    Mark Spencer committed
    static char b2a[256];
    
    static char b2a_url[256];
    
    #if !defined(HAVE_GETHOSTBYNAME_R_5) && !defined(HAVE_GETHOSTBYNAME_R_6)
    
    Olle Johansson's avatar
    Olle Johansson committed
    #define ERANGE 34	/*!< duh? ERANGE value copied from web... */
    
    AST_MUTEX_DEFINE_STATIC(__mutex);
    
    
    /*! \brief Reentrant replacement for gethostbyname for BSD-based systems.
    
    Olle Johansson's avatar
    Olle Johansson committed
    \note This
    
    routine is derived from code originally written and placed in the public
    
    domain by Enzo Michelangeli <em@em.no-ip.com> */
    
    
    static int gethostbyname_r (const char *name, struct hostent *ret, char *buf,
    
    				size_t buflen, struct hostent **result,
    				int *h_errnop)
    
    {
    	int hsave;
    	struct hostent *ph;
    	ast_mutex_lock(&__mutex); /* begin critical area */
    	hsave = h_errno;
    
    	ph = gethostbyname(name);
    	*h_errnop = h_errno; /* copy h_errno to *h_herrnop */
    	if (ph == NULL) {
    		*result = NULL;
    	} else {
    		char **p, **q;
    		char *pbuf;
    
    		int nbytes = 0;
    		int naddr = 0, naliases = 0;
    
    		/* determine if we have enough space in buf */
    
    		/* count how many addresses */
    		for (p = ph->h_addr_list; *p != 0; p++) {
    			nbytes += ph->h_length; /* addresses */
    			nbytes += sizeof(*p); /* pointers */
    			naddr++;
    		}
    		nbytes += sizeof(*p); /* one more for the terminating NULL */
    
    		/* count how many aliases, and total length of strings */
    		for (p = ph->h_aliases; *p != 0; p++) {
    			nbytes += (strlen(*p)+1); /* aliases */
    			nbytes += sizeof(*p);  /* pointers */
    			naliases++;
    		}
    		nbytes += sizeof(*p); /* one more for the terminating NULL */
    
    		/* here nbytes is the number of bytes required in buffer */
    		/* as a terminator must be there, the minimum value is ph->h_length */
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    		if (nbytes > buflen) {
    
    			*result = NULL;
    			ast_mutex_unlock(&__mutex); /* end critical area */
    			return ERANGE; /* not enough space in buf!! */
    		}
    
    		/* There is enough space. Now we need to do a deep copy! */
    		/* Allocation in buffer:
    			from [0] to [(naddr-1) * sizeof(*p)]:
    			pointers to addresses
    			at [naddr * sizeof(*p)]:
    			NULL
    			from [(naddr+1) * sizeof(*p)] to [(naddr+naliases) * sizeof(*p)] :
    			pointers to aliases
    			at [(naddr+naliases+1) * sizeof(*p)]:
    			NULL
    			then naddr addresses (fixed length), and naliases aliases (asciiz).
    		*/
    
    		*ret = *ph;   /* copy whole structure (not its address!) */
    
    		/* copy addresses */
    		q = (char **)buf; /* pointer to pointers area (type: char **) */
    		ret->h_addr_list = q; /* update pointer to address list */
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    		pbuf = buf + ((naddr + naliases + 2) * sizeof(*p)); /* skip that area */
    
    		for (p = ph->h_addr_list; *p != 0; p++) {
    			memcpy(pbuf, *p, ph->h_length); /* copy address bytes */
    			*q++ = pbuf; /* the pointer is the one inside buf... */
    			pbuf += ph->h_length; /* advance pbuf */
    		}
    		*q++ = NULL; /* address list terminator */
    
    		/* copy aliases */
    		ret->h_aliases = q; /* update pointer to aliases list */
    		for (p = ph->h_aliases; *p != 0; p++) {
    			strcpy(pbuf, *p); /* copy alias strings */
    			*q++ = pbuf; /* the pointer is the one inside buf... */
    			pbuf += strlen(*p); /* advance pbuf */
    			*pbuf++ = 0; /* string terminator */
    		}
    		*q++ = NULL; /* terminator */
    
    		strcpy(pbuf, ph->h_name); /* copy alias strings */
    		ret->h_name = pbuf;
    		pbuf += strlen(ph->h_name); /* advance pbuf */
    		*pbuf++ = 0; /* string terminator */
    
    		*result = ret;  /* and let *result point to structure */
    
    	}
    	h_errno = hsave;  /* restore h_errno */
    	ast_mutex_unlock(&__mutex); /* end critical area */
    
    
    	return (*result == NULL); /* return 0 on success, non-zero on error */
    
    /*! \brief Re-entrant (thread safe) version of gethostbyname that replaces the
    
       standard gethostbyname (which is not thread safe)
    
    struct hostent *ast_gethostbyname(const char *host, struct ast_hostent *hp)
    {
    
    #ifndef HAVE_GETHOSTBYNAME_R_5
    
    	int dots = 0;
    
    	const char *s;
    
    	struct hostent *result = NULL;
    
    	/* Although it is perfectly legitimate to lookup a pure integer, for
    	   the sake of the sanity of people who like to name their peers as
    	   integers, we break with tradition and refuse to look up a
    	   pure integer */
    	s = host;
    
    		if (*s == '.')
    			dots++;
    		else if (!isdigit(*s))
    
    	if (!s || !*s) {
    		/* Forge a reply for IP's to avoid octal IP's being interpreted as octal */
    		if (dots != 3)
    			return NULL;
    
    		memset(hp, 0, sizeof(struct ast_hostent));
    
    		hp->hp.h_addrtype = AF_INET;
    
    		hp->hp.h_addr_list = (void *) hp->buf;
    
    		hp->hp.h_addr = hp->buf + sizeof(void *);
    
    		/* For AF_INET, this will always be 4 */
    		hp->hp.h_length = 4;
    
    		if (inet_pton(AF_INET, host, hp->hp.h_addr) > 0)
    			return &hp->hp;
    
    #ifdef HAVE_GETHOSTBYNAME_R_5
    
    	result = gethostbyname_r(host, &hp->hp, hp->buf, sizeof(hp->buf), &herrno);
    
    	if (!result || !hp->hp.h_addr_list || !hp->hp.h_addr_list[0])
    		return NULL;
    #else
    
    	res = gethostbyname_r(host, &hp->hp, hp->buf, sizeof(hp->buf), &result, &herrno);
    
    
    	if (res || !result || !hp->hp.h_addr_list || !hp->hp.h_addr_list[0])
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief Produce 32 char MD5 hash of value. */
    
    void ast_md5_hash(char *output, const char *input)
    
    	struct MD5Context md5;
    	unsigned char digest[16];
    	char *ptr;
    	int x;
    
    	MD5Init(&md5);
    
    	MD5Update(&md5, (const unsigned char *) input, strlen(input));
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	for (x = 0; x < 16; x++)
    
    		ptr += sprintf(ptr, "%02hhx", digest[x]);
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief Produce 40 char SHA1 hash of value. */
    
    void ast_sha1_hash(char *output, const char *input)
    
    {
    	struct SHA1Context sha;
    	char *ptr;
    	int x;
    	uint8_t Message_Digest[20];
    
    	SHA1Reset(&sha);
    
    	SHA1Input(&sha, (const unsigned char *) input, strlen(input));
    
    	SHA1Result(&sha, Message_Digest);
    	ptr = output;
    	for (x = 0; x < 20; x++)
    
    		ptr += sprintf(ptr, "%02hhx", Message_Digest[x]);
    
    /*! \brief Produce a 20 byte SHA1 hash of value. */
    void ast_sha1_hash_uint(uint8_t *digest, const char *input)
    {
            struct SHA1Context sha;
    
            SHA1Reset(&sha);
    
            SHA1Input(&sha, (const unsigned char *) input, strlen(input));
    
            SHA1Result(&sha, digest);
    }
    
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief decode BASE64 encoded text */
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_base64decode(unsigned char *dst, const char *src, int max)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int cnt = 0;
    	unsigned int byte = 0;
    	unsigned int bits = 0;
    	int incnt = 0;
    
    	while(*src && *src != '=' && (cnt < max)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Shift in 6 bits of input */
    		byte <<= 6;
    		byte |= (b2a[(int)(*src)]) & 0x3f;
    		bits += 6;
    		src++;
    		incnt++;
    
    		/* If we have at least 8 bits left over, take that character
    
    Mark Spencer's avatar
    Mark Spencer committed
    		   off the top */
    		if (bits >= 8)  {
    			bits -= 8;
    			*dst = (byte >> bits) & 0xff;
    			dst++;
    			cnt++;
    		}
    	}
    
    	/* Don't worry about left over bits, they're extra anyway */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return cnt;
    }
    
    
    /*! \brief Decode BASE64 encoded text and return the string */
    char *ast_base64decode_string(const char *src)
    {
    	size_t encoded_len;
    	size_t decoded_len;
    	int padding = 0;
    	unsigned char *decoded_string;
    
    	if (ast_strlen_zero(src)) {
    		return NULL;
    	}
    
    	encoded_len = strlen(src);
    	if (encoded_len > 2 && src[encoded_len - 1] == '=') {
    		padding++;
    		if (src[encoded_len - 2] == '=') {
    			padding++;
    		}
    	}
    
    	decoded_len = (encoded_len / 4 * 3) - padding;
    
    	decoded_string = ast_malloc(decoded_len + 1);
    
    	if (!decoded_string) {
    		return NULL;
    	}
    
    	ast_base64decode(decoded_string, src, decoded_len);
    
    	decoded_string[decoded_len] = '\0';
    
    
    	return (char *)decoded_string;
    }
    
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief encode text to BASE64 coding */
    
    int ast_base64encode_full(char *dst, const unsigned char *src, int srclen, int max, int linebreaks)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int cnt = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	unsigned int byte = 0;
    	int bits = 0;
    	int cntin = 0;
    
    	/* Reserve space for null byte at end of string */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	max--;
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	while ((cntin < srclen) && (cnt < max)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		byte <<= 8;
    		byte |= *(src++);
    		bits += 8;
    		cntin++;
    
    		if ((bits == 24) && (cnt + 4 <= max)) {
    
    			*dst++ = base64[(byte >> 18) & 0x3f];
    			*dst++ = base64[(byte >> 12) & 0x3f];
    			*dst++ = base64[(byte >> 6) & 0x3f];
    			*dst++ = base64[byte & 0x3f];
    			cnt += 4;
    			col += 4;
    			bits = 0;
    			byte = 0;
    		}
    		if (linebreaks && (cnt < max) && (col == 64)) {
    			*dst++ = '\n';
    
    Mark Spencer's avatar
    Mark Spencer committed
    			cnt++;
    
    	if (bits && (cnt + 4 <= max)) {
    
    		/* Add one last character for the remaining bits,
    
    Mark Spencer's avatar
    Mark Spencer committed
    		   padding the rest with 0 */
    
    		byte <<= 24 - bits;
    		*dst++ = base64[(byte >> 18) & 0x3f];
    		*dst++ = base64[(byte >> 12) & 0x3f];
    		if (bits == 16)
    			*dst++ = base64[(byte >> 6) & 0x3f];
    		else
    			*dst++ = '=';
    		*dst++ = '=';
    		cnt += 4;
    	}
    	if (linebreaks && (cnt < max)) {
    		*dst++ = '\n';
    
    Mark Spencer's avatar
    Mark Spencer committed
    		cnt++;
    	}
    	*dst = '\0';
    	return cnt;
    }
    
    
    int ast_base64encode(char *dst, const unsigned char *src, int srclen, int max)
    {
    	return ast_base64encode_full(dst, src, srclen, max, 0);
    }
    
    
    /*! \brief Encode to BASE64 and return encoded string */
    char *ast_base64encode_string(const char *src)
    {
    	size_t encoded_len;
    	char *encoded_string;
    
    	if (ast_strlen_zero(src)) {
    		return NULL;
    	}
    
    	encoded_len = ((strlen(src) * 4 / 3 + 3) & ~3) + 1;
    	encoded_string = ast_calloc(1, encoded_len);
    
    	ast_base64encode(encoded_string, (const unsigned char *)src, strlen(src), encoded_len);
    
    	return encoded_string;
    }
    
    
    int ast_base64url_decode(unsigned char *dst, const char *src, int max)
    {
    	int cnt = 0;
    	unsigned int byte = 0;
    	unsigned int bits = 0;
    
    	while (*src && (cnt < max)) {
    		byte <<= 6;
    		byte |= (b2a_url[(int)(*src)]) & 0x3f;
    		bits += 6;
    		src++;
    		if (bits >= 8) {
    			bits -= 8;
    			*dst = (byte >> bits) & 0xff;
    			dst++;
    			cnt++;
    		}
    	}
    	return cnt;
    }
    
    char *ast_base64url_decode_string(const char *src)
    {
    	size_t decoded_len;
    	unsigned char *decoded_string;
    
    	if (ast_strlen_zero(src)) {
    		return NULL;
    	}
    
    	decoded_len = strlen(src) * 3 / 4;
    	decoded_string = ast_malloc(decoded_len + 1);
    	if (!decoded_string) {
    		return NULL;
    	}
    
    	ast_base64url_decode(decoded_string, src, decoded_len);
    	decoded_string[decoded_len] = '\0';
    
    	return (char *)decoded_string;
    }
    
    int ast_base64url_encode_full(char *dst, const unsigned char *src, int srclen, int max, int linebreaks)
    {
    	int cnt = 0;
    	int col = 0;
    	unsigned int byte = 0;
    	int bits = 0;
    	int cntin = 0;
    
    	max--;
    	while ((cntin < srclen) && (cnt < max)) {
    		byte <<= 8;
    		byte |= *(src++);
    		bits += 8;
    		cntin++;
    		if ((bits == 24) && (cnt + 4 <= max)) {
    			*dst++ = base64url[(byte >> 18) & 0x3f];
    			*dst++ = base64url[(byte >> 12) & 0x3f];
    			*dst++ = base64url[(byte >> 6) & 0x3f];
    			*dst++ = base64url[(byte) & 0x3f];
    			cnt += 4;
    			col += 4;
    			bits = 0;
    			byte = 0;
    		}
    		if (linebreaks && (cnt < max) && (col == 64)) {
    			*dst++ = '\n';
    			cnt++;
    			col = 0;
    		}
    	}
    	if (bits && (cnt + 4 <= max)) {
    		byte <<= 24 - bits;
    		*dst++ = base64url[(byte >> 18) & 0x3f];
    		*dst++ = base64url[(byte >> 12) & 0x3f];
    		if (bits == 16) {
    			*dst++ = base64url[(byte >> 6) & 0x3f];
    		}
    		cnt += 4;
    	}
    	if (linebreaks && (cnt < max)) {
    		*dst++ = '\n';
    		cnt++;
    	}
    	*dst = '\0';
    	return cnt;
    }
    
    int ast_base64url_encode(char *dst, const unsigned char *src, int srclen, int max)
    {
    	return ast_base64url_encode_full(dst, src, srclen, max, 0);
    }
    
    char *ast_base64url_encode_string(const char *src)
    {
    	size_t encoded_len;
    	char *encoded_string;
    
    	if (ast_strlen_zero(src)) {
    		return NULL;
    	}
    
    	encoded_len = ((strlen(src) * 4 / 3 + 3) & ~3) + 1;
    	encoded_string = ast_malloc(encoded_len);
    
    	ast_base64url_encode(encoded_string, (const unsigned char *)src, strlen(src), encoded_len);
    
    	return encoded_string;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static void base64_init(void)
    {
    	int x;
    	memset(b2a, -1, sizeof(b2a));
    
    	memset(b2a_url, -1, sizeof(b2a_url));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Initialize base-64 Conversion table */
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	for (x = 0; x < 26; x++) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* A-Z */
    		base64[x] = 'A' + x;
    
    		base64url[x] = 'A' + x;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		b2a['A' + x] = x;
    
    		b2a_url['A' + x] = x;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* a-z */
    		base64[x + 26] = 'a' + x;
    
    		base64url[x + 26] = 'a' + x;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		b2a['a' + x] = x + 26;
    
    		b2a_url['a' + x] = x + 26;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* 0-9 */
    		if (x < 10) {
    			base64[x + 52] = '0' + x;
    
    			base64url[x + 52] = '0' + x;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			b2a['0' + x] = x + 52;
    
    			b2a_url['0' + x] = x + 52;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    	}
    	base64[62] = '+';
    	base64[63] = '/';
    
    	base64url[62] = '-';
    	base64url[63] = '_';
    
    Mark Spencer's avatar
    Mark Spencer committed
    	b2a[(int)'+'] = 62;
    	b2a[(int)'/'] = 63;
    
    	b2a_url[(int)'-'] = 62;
    	b2a_url[(int)'_'] = 63;
    
    #define BASELINELEN    72  /*!< Line length for Base 64 encoded messages */
    #define BASEMAXINLINE  256 /*!< Buffer size for Base 64 attachment encoding */
    
    /*! \brief Structure used for base64 encoding */
    struct baseio {
    	int iocp;
    	int iolen;
    	int linelength;
    	int ateof;
    	unsigned char iobuf[BASEMAXINLINE];
    };
    
    /*!
     * \brief utility used by inchar(), for base_encode()
     */
    static int inbuf(struct baseio *bio, FILE *fi)
    {
    	int l;
    
    	if (bio->ateof) {
    		return 0;
    	}
    
    	if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) != BASEMAXINLINE) {
    		bio->ateof = 1;
    		if (l == 0) {
    			/* Assume EOF */
    			return 0;
    		}
    	}
    
    	bio->iolen = l;
    	bio->iocp = 0;
    
    	return 1;
    }
    
    /*!
     * \brief utility used by base_encode()
     */
    static int inchar(struct baseio *bio, FILE *fi)
    {
    	if (bio->iocp >= bio->iolen) {
    		if (!inbuf(bio, fi)) {
    			return EOF;
    		}
    	}
    
    	return bio->iobuf[bio->iocp++];
    }
    
    /*!
     * \brief utility used by base_encode()
     */
    static int ochar(struct baseio *bio, int c, FILE *so, const char *endl)
    {
    	if (bio->linelength >= BASELINELEN) {
    		if (fputs(endl, so) == EOF) {
    			return -1;
    		}
    
    		bio->linelength = 0;
    	}
    
    	if (putc(((unsigned char) c), so) == EOF) {
    		return -1;
    	}
    
    	bio->linelength++;
    
    	return 1;
    }
    
    int ast_base64_encode_file(FILE *inputfile, FILE *outputfile, const char *endl)
    {
    	static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
    		'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
    		'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
    		'1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
    	int i, hiteof = 0;
    	struct baseio bio;
    
    	memset(&bio, 0, sizeof(bio));
    	bio.iocp = BASEMAXINLINE;
    
    	while (!hiteof){
    		unsigned char igroup[3], ogroup[4];
    		int c, n;
    
    		memset(igroup, 0, sizeof(igroup));
    
    		for (n = 0; n < 3; n++) {
    			if ((c = inchar(&bio, inputfile)) == EOF) {
    				hiteof = 1;
    				break;
    			}
    
    			igroup[n] = (unsigned char) c;
    		}
    
    		if (n > 0) {
    			ogroup[0]= dtable[igroup[0] >> 2];
    			ogroup[1]= dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
    			ogroup[2]= dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
    			ogroup[3]= dtable[igroup[2] & 0x3F];
    
    			if (n < 3) {
    				ogroup[3] = '=';
    
    				if (n < 2) {
    					ogroup[2] = '=';
    				}
    			}
    
    			for (i = 0; i < 4; i++) {
    				ochar(&bio, ogroup[i], outputfile, endl);
    			}
    		}
    	}
    
    	if (fputs(endl, outputfile) == EOF) {
    		return 0;
    	}
    
    	return 1;
    }
    
    int ast_base64_encode_file_path(const char *filename, FILE *outputfile, const char *endl)
    {
    	FILE *fi;
    	int res;
    
    	if (!(fi = fopen(filename, "rb"))) {
    		ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
    		return -1;
    	}
    
    	res = ast_base64_encode_file(fi, outputfile, endl);
    
    	fclose(fi);
    
    	return res;
    }
    
    
    const struct ast_flags ast_uri_http = {AST_URI_UNRESERVED};
    const struct ast_flags ast_uri_http_legacy = {AST_URI_LEGACY_SPACE | AST_URI_UNRESERVED};
    const struct ast_flags ast_uri_sip_user = {AST_URI_UNRESERVED | AST_URI_SIP_USER_UNRESERVED};
    
    char *ast_uri_encode(const char *string, char *outbuf, int buflen, struct ast_flags spec)
    
    	const char *ptr  = string;	/* Start with the string */
    
    	char *out = outbuf;
    
    	const char *mark = "-_.!~*'()"; /* no encode set, RFC 2396 section 2.3, RFC 3261 sec 25 */
    
    	const char *user_unreserved = "&=+$,;?/"; /* user-unreserved set, RFC 3261 sec 25 */
    
    	while (*ptr && out - outbuf < buflen - 1) {
    
    		if (ast_test_flag(&spec, AST_URI_LEGACY_SPACE) && *ptr == ' ') {
    			/* for legacy encoding, encode spaces as '+' */
    			*out = '+';
    			out++;
    		} else if (!(ast_test_flag(&spec, AST_URI_MARK)
    				&& strchr(mark, *ptr))
    			&& !(ast_test_flag(&spec, AST_URI_ALPHANUM)
    				&& ((*ptr >= '0' && *ptr <= '9')
    				|| (*ptr >= 'A' && *ptr <= 'Z')
    				|| (*ptr >= 'a' && *ptr <= 'z')))
    			&& !(ast_test_flag(&spec, AST_URI_SIP_USER_UNRESERVED)
    				&& strchr(user_unreserved, *ptr))) {
    
    
    			if (out - outbuf >= buflen - 3) {
    				break;
    
    			out += sprintf(out, "%%%02hhX", (unsigned char) *ptr);
    
    			*out = *ptr;	/* Continue copying the string */
    			out++;
    
    void ast_uri_decode(char *s, struct ast_flags spec)
    
    {
    	char *o;
    	unsigned int tmp;
    
    	for (o = s; *s; s++, o++) {
    
    		if (ast_test_flag(&spec, AST_URI_LEGACY_SPACE) && *s == '+') {
    			/* legacy mode, decode '+' as space */
    			*o = ' ';
    		} else if (*s == '%' && s[1] != '\0' && s[2] != '\0' && sscanf(s + 1, "%2x", &tmp) == 1) {
    
    			/* have '%', two chars and correct parsing */
    			*o = tmp;
    			s += 2;	/* Will be incremented once more when we break out */
    		} else /* all other cases, just copy */
    			*o = *s;
    	}
    	*o = '\0';
    }
    
    
    char *ast_escape_quoted(const char *string, char *outbuf, int buflen)
    {
    	const char *ptr  = string;
    	char *out = outbuf;
    	char *allow = "\t\v !"; /* allow LWS (minus \r and \n) and "!" */
    
    	while (*ptr && out - outbuf < buflen - 1) {
    		if (!(strchr(allow, *ptr))
    			&& !(*ptr >= '#' && *ptr <= '[') /* %x23 - %x5b */
    			&& !(*ptr >= ']' && *ptr <= '~') /* %x5d - %x7e */
    
    			&& !((unsigned char) *ptr > 0x7f)) {             /* UTF8-nonascii */
    
    
    			if (out - outbuf >= buflen - 2) {
    				break;
    			}
    			out += sprintf(out, "\\%c", (unsigned char) *ptr);
    		} else {
    			*out = *ptr;
    			out++;
    		}
    		ptr++;
    	}
    
    	if (buflen) {
    		*out = '\0';
    	}
    
    	return outbuf;
    }
    
    char *ast_escape_semicolons(const char *string, char *outbuf, int buflen)
    {
    	const char *ptr = string;
    	char *out = outbuf;
    
    	if (string == NULL || outbuf == NULL) {
    		ast_assert(string != NULL && outbuf != NULL);
    		return NULL;
    	}
    
    	while (*ptr && out - outbuf < buflen - 1) {
    		if (*ptr == ';') {
    			if (out - outbuf >= buflen - 2) {
    				break;
    			}
    			strcpy(out, "\\;");
    			out += 2;
    		} else {
    			*out = *ptr;
    			out++;
    		}
    		ptr++;
    	}
    
    	if (buflen) {
    		*out = '\0';
    	}
    
    	return outbuf;
    }
    
    
    void ast_unescape_quoted(char *quote_str)
    {
    	int esc_pos;
    	int unesc_pos;
    	int quote_str_len = strlen(quote_str);
    
    	for (esc_pos = 0, unesc_pos = 0;
    		esc_pos < quote_str_len;
    		esc_pos++, unesc_pos++) {
    		if (quote_str[esc_pos] == '\\') {
    			/* at least one more char and current is \\ */
    			esc_pos++;
    			if (esc_pos >= quote_str_len) {
    				break;
    			}
    		}
    
    		quote_str[unesc_pos] = quote_str[esc_pos];
    	}
    	quote_str[unesc_pos] = '\0';
    }
    
    
    int ast_xml_escape(const char *string, char * const outbuf, const size_t buflen)
    {
    	char *dst = outbuf;
    	char *end = outbuf + buflen - 1; /* save one for the null terminator */
    
    	/* Handle the case for the empty output buffer */
    	if (buflen == 0) {
    		return -1;
    	}
    
    	/* Escaping rules from http://www.w3.org/TR/REC-xml/#syntax */
    	/* This also prevents partial entities at the end of a string */
    	while (*string && dst < end) {
    		const char *entity = NULL;
    		int len = 0;
    
    		switch (*string) {
    		case '<':
    			entity = "&lt;";
    			len = 4;
    			break;
    		case '&':
    			entity = "&amp;";
    			len = 5;
    			break;
    		case '>':
    			/* necessary if ]]> is in the string; easier to escape them all */
    			entity = "&gt;";
    			len = 4;
    			break;
    		case '\'':
    			/* necessary in single-quoted strings; easier to escape them all */
    			entity = "&apos;";
    			len = 6;
    			break;
    		case '"':
    			/* necessary in double-quoted strings; easier to escape them all */
    			entity = "&quot;";
    			len = 6;
    			break;
    		default:
    			*dst++ = *string++;
    			break;
    		}
    
    		if (entity) {
    			ast_assert(len == strlen(entity));
    			if (end - dst < len) {
    				/* no room for the entity; stop */
    				break;
    			}
    			/* just checked for length; strcpy is fine */
    			strcpy(dst, entity);
    			dst += len;
    			++string;
    		}
    	}
    	/* Write null terminator */
    	*dst = '\0';
    	/* If any chars are left in string, return failure */
    	return *string == '\0' ? 0 : -1;
    }
    
    
    /*! \brief  ast_inet_ntoa: Recursive thread safe replacement of inet_ntoa */
    
    const char *ast_inet_ntoa(struct in_addr ia)
    
    	if (!(buf = ast_threadstorage_get(&inet_ntoa_buf, INET_ADDRSTRLEN)))
    		return "";
    
    
    	return inet_ntop(AF_INET, &ia, buf, INET_ADDRSTRLEN);
    
    static int dev_urandom_fd = -1;
    
    #ifndef __linux__
    #undef pthread_create /* For ast_pthread_create function only */
    
    #ifdef DEBUG_THREADS
    
    
    #if !defined(LOW_MEMORY)
    
    /*! \brief A reasonable maximum number of locks a thread would be holding ... */
    
    #define AST_MAX_LOCKS 64
    
    
    /* Allow direct use of pthread_mutex_t and friends */
    #undef pthread_mutex_t
    #undef pthread_mutex_lock
    #undef pthread_mutex_unlock
    #undef pthread_mutex_init
    #undef pthread_mutex_destroy
    
    
    /*!
     * \brief Keep track of which locks a thread holds
    
     *
     * There is an instance of this struct for every active thread
     */
    struct thr_lock_info {
    	/*! The thread's ID */
    	pthread_t thread_id;
    	/*! The thread name which includes where the thread was started */
    	const char *thread_name;
    	/*! This is the actual container of info for what locks this thread holds */
    	struct {
    		const char *file;
    		const char *func;
    		const char *lock_name;
    		void *lock_addr;
    		int times_locked;
    
    		enum ast_lock_type type;
    
    		/*! This thread is waiting on this lock */
    
    		/*! A condition has suspended this lock */
    		int suspended:1;
    
    #ifdef HAVE_BKTR
    		struct ast_bt *backtrace;
    #endif
    
    	} locks[AST_MAX_LOCKS];
    	/*! This is the number of locks currently held by this thread.
    	 *  The index (num_locks - 1) has the info on the last one in the
    	 *  locks member */
    	unsigned int num_locks;
    
    	/*! The LWP id (which GDB prints) */
    	int lwp;
    
    	/*! Protects the contents of the locks member
    
    	 * Intentionally not ast_mutex_t */
    	pthread_mutex_t lock;
    	AST_LIST_ENTRY(thr_lock_info) entry;
    };
    
    
    /*!
     * \brief Locked when accessing the lock_infos list
    
     */
    AST_MUTEX_DEFINE_STATIC(lock_infos_lock);
    /*!