#include "links.h"

static void base64_decode(unsigned char **d, int *dl, unsigned char *s, int sl)
{
	int bits = 0;
	unsigned tmp = 0;
	for (; sl > 0; s++, sl--) {
		unsigned char val;
		unsigned char c = *s;
		if (c >= 'A' && c <= 'Z') val = c - 'A';
		else if (c >= 'a' && c <= 'z') val = c - 'a' + 26;
		else if (c >= '0' && c <= '9') val = c - '0' + 52;
		else if (c == '+') val = 62;
		else if (c == '/') val = 63;
		else continue;
		tmp <<= 6;
		tmp |= val;
		bits += 6;
		if (bits >= 8) {
			bits -= 8;
			add_chr_to_str(d, dl, tmp >> bits);
			tmp &= (1 << bits) - 1;
		}
	}
}

void data_func(struct connection *c)
{
	unsigned char *data, *flags, *mime, *str;
	size_t length;
	int strl;
	struct cache_entry *e;
	int r;

	int base64 = 0;
	int was_charset = 0;

	flags = cast_uchar strchr(cast_const_char c->url, ':');
	if (!flags) {
		bad_url:
		setcstate(c, S_BAD_URL);
		abort_connection(c);
		return;
	}
	flags++;
	while (*flags == '/') flags++;

	length = strcspn(cast_const_char flags, ";,");
	mime = memacpy(flags, length);

	while (*(flags += length) == ';') {
		unsigned char *arg;
		flags++;
		length = strcspn(cast_const_char flags, ";,");
		arg = memacpy(flags, length);
		if (!casestrcmp(arg, cast_uchar "base64")) {
			base64 = 1;
		} else if (!casecmp(arg, cast_uchar "charset=", 8)) {
			if (!was_charset) {
				add_to_strn(&mime, cast_uchar ";");
				add_to_strn(&mime, arg);
				was_charset = 1;
			}
		}
		mem_free(arg);
	}

	if (*flags != ',') {
		mem_free(mime);
		goto bad_url;
	}
	data = flags + 1;

	if (!c->cache) {
		if (get_connection_cache_entry(c)) {
			mem_free(mime);
			setcstate(c, S_OUT_OF_MEM);
			abort_connection(c);
			return;
		}
		c->cache->refcount--;
	}
	e = c->cache;
	if (e->head) mem_free(e->head);
	e->head = stracpy(cast_uchar "");
	if (*mime) {
		add_to_strn(&e->head, cast_uchar "\r\nContent-type: ");
		add_to_strn(&e->head, mime);
		add_to_strn(&e->head, cast_uchar "\r\n");
	}
	mem_free(mime);

	str = init_str();
	strl = 0;

	add_conv_str(&str, &strl, data, (int)strlen(cast_const_char data), -2);

	if (!base64) {
		r = add_fragment(e, 0, str, strl);
	} else {
		unsigned char *b64 = init_str();
		int b64l = 0;

		base64_decode(&b64, &b64l, str, strl);

		r = add_fragment(e, 0, b64, b64l);
		mem_free(b64);
	}
	mem_free(str);
	if (r < 0) {
		setcstate(c, r);
		abort_connection(c);
		return;
	}
	truncate_entry(e, strl, 1);
	c->cache->incomplete = 0;

	setcstate(c, S__OK);
	abort_connection(c);
}
