/* 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
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#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/shout.h>
#include "util.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;
}