/* util.c: libshout utility/portability functions
 *
 *  Copyright (C) 2002-2003 the Icecast team <team@icecast.org>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the Free
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <sys/types.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_WINSOCK2_H
#include <winsock2.h>
#endif

#include "shout.h"
#include "util.h"


#include <config.h>

char *_shout_util_strdup(const char *s)
{
	if (!s)
		return NULL;

	return strdup(s);
}

int _shout_util_read_header(int sock, char *buff, unsigned long len)
{
	int read_bytes, ret;
	unsigned long pos;
	char c;

	read_bytes = 1;
	pos = 0;
	ret = 0;

	while ((read_bytes == 1) && (pos < (len - 1))) {
		read_bytes = 0;

		if ((read_bytes = recv(sock, &c, 1, 0))) {
			if (c != '\r')
				buff[pos++] = c;
			if ((pos > 1) && (buff[pos - 1] == '\n' && buff[pos - 2] == '\n')) {
				ret = 1;
				break;
			}
		} else {
			break;
		}
	}

	if (ret) buff[pos] = '\0';

	return ret;
}

static char base64table[65] = {
    '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','+','/',
};

/* This isn't efficient, but it doesn't need to be */
char *_shout_util_base64_encode(char *data)
{
	int len = strlen(data);
	char *out = malloc(len*4/3 + 4);
	char *result = out;
	int chunk;

	while(len > 0) {
		chunk = (len >3)?3:len;
		*out++ = base64table[(*data & 0xFC)>>2];
		*out++ = base64table[((*data & 0x03)<<4) | ((*(data+1) & 0xF0) >> 4)];

		switch(chunk) {
		case 3:
			*out++ = base64table[((*(data+1) & 0x0F)<<2) | ((*(data+2) & 0xC0)>>6)];
			*out++ = base64table[(*(data+2)) & 0x3F];
			break;
		case 2:
			*out++ = base64table[((*(data+1) & 0x0F)<<2)];
			*out++ = '=';
			break;
		case 1:
			*out++ = '=';
			*out++ = '=';
			break;
		}
		data += chunk;
		len -= chunk;
	}
	*out = 0;

	return result;
}

static char urltable[16] = {
	'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
};

static char safechars[256] = {
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  0,  0,  0,  0,
      0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
      1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  0,  0,  0,
      0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
      1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
};

/* modified from libshout1, which credits Rick Franchuk <rickf@transpect.net>.
 * Caller must free result. */
char *_shout_util_url_encode(const char *data) {
	const char *p;
	char *q, *dest;
	int digit;
	size_t n;

	for (p = data, n = 0; *p; p++) {
		n++;
		if (!safechars[(unsigned char)(*p)])
			n += 2;
	}
	if (!(dest = malloc(n+1)))
		return NULL;
		
	for (p = data, q = dest; *p; p++, q++) {
		if (safechars[(unsigned char)(*p)]) {
			*q = *p;
		} else {
			*q++ = '%';
			digit = (*p >> 4) & 0xF;
			*q++ = urltable[digit];
			digit = *p & 0xf;
			*q = urltable[digit];
			n += 2;
		}
	}
	*q = '\0';

	return dest;
}

util_dict *_shout_util_dict_new(void)
{
	return (util_dict *)calloc(1, sizeof(util_dict));
}

void _shout_util_dict_free(util_dict *dict)
{
	util_dict *next;

	while (dict) {
		next = dict->next;

		if (dict->key)
			free (dict->key);
		if (dict->val)
			free (dict->val);
		free (dict);

		dict = next;
	}
}

const char *_shout_util_dict_get(util_dict *dict, const char *key)
{
	while (dict) {
		if (dict->key && !strcmp(key, dict->key))
			return dict->val;
		dict = dict->next;
	}

	return NULL;
}

int _shout_util_dict_set(util_dict *dict, const char *key, const char *val)
{
	util_dict *prev;

	if (!dict || !key)
		return SHOUTERR_INSANE;

	prev = NULL;
	while (dict) {
		if (!dict->key || !strcmp(dict->key, key))
			break;
		prev = dict;
		dict = dict->next;
	}

	if (!dict) {
		dict = _shout_util_dict_new();
		if (!dict)
			return SHOUTERR_MALLOC;
		if (prev)
			prev->next = dict;
	}

	if (dict->key)
		free (dict->val);
	else if (!(dict->key = strdup(key))) {
		if (prev)
			prev->next = NULL;
		_shout_util_dict_free (dict);

		return SHOUTERR_MALLOC;
	}

	dict->val = strdup(val);
	if (!dict->val) {
		return SHOUTERR_MALLOC;
	}

	return SHOUTERR_SUCCESS;
}

/* given a dictionary, URL-encode each key and val and stringify them in order as
  key=val&key=val... if val is set, or just key&key if val is NULL.
  TODO: Memory management needs overhaul. */
char *_shout_util_dict_urlencode(util_dict *dict, char delim)
{
	char *res, *tmp;
	char *enc;
	int start = 1;

	for (res = NULL; dict; dict = dict->next) {
		/* encode key */
		if (!dict->key)
			continue;
		if (!(enc = _shout_util_url_encode(dict->key))) {
			if (res)
				free(res);
			return NULL;
		}
		if (start) {
			if (!(res = malloc(strlen(enc) + 1))) {
				free(enc);
				return NULL;
			}
			sprintf(res, "%s", enc);
			free(enc);
			start = 0;
		} else {
			if (!(tmp = realloc(res, strlen(res) + strlen(enc) + 2))) {
				free(enc);
				free(res);
				return NULL;
			} else
				res = tmp;
			sprintf(res + strlen(res), "%c%s", delim, enc);
			free(enc);
		}

		/* encode value */
		if (!dict->val)
			continue;
		if (!(enc = _shout_util_url_encode(dict->val))) {
			free(res);
			return NULL;
		}

		if (!(tmp = realloc(res, strlen(res) + strlen(enc) + 2))) {
			free(enc);
			free(res);
			return NULL;
		} else
			res = tmp;
		sprintf(res + strlen(res), "=%s", enc);
		free(enc);
	}

	return res;
}
