/**************************************/
/* INCLUDES */

#include "tcp_by_http_tunnel.h"


/**************************************/
/* FUNCTIONS */

int
 http_l_encoder::encode(round_buf * in_rb_ptr, round_buf * out_rb_ptr)
{
    // Check if we have a message to send.
    if (msg_to_flush) {
	return flush_msg(out_rb_ptr);
    }

    else {
	return build_message(in_rb_ptr, out_rb_ptr);
    }
}

int http_l_encoder::build_message(round_buf * in_rb_ptr,
				  round_buf * out_rb_ptr)
{
    // Build the message.
    int avail_read;
    char trans_buf[TRANS_BUF_SIZE + 1];

    // Read some data.
    avail_read = in_rb_ptr->used_size();

    if (avail_read == 0)
	return 0;

    if (avail_read > TRANS_BUF_SIZE) {
	avail_read = TRANS_BUF_SIZE;
    }

    in_rb_ptr->read_buf(trans_buf, avail_read);

    // Build the paquet.

    // URL-encrypt the data. The worst case is three time the initial url
    // size.
    char *encoded_data = new char[avail_read * 3];

    int encoded_size = url_encode(trans_buf, avail_read, encoded_data);

    // Let's forge the header.
    char *begin = HTTP_BEGIN_STRING_C;
    int begin_size = sizeof(HTTP_BEGIN_STRING_C) - 1;
    char *end = HTTP_END_STRING_C;
    int end_size = sizeof(HTTP_END_STRING_C) - 1;

    msg_size = begin_size + encoded_size + end_size;
    msg = new char[msg_size];

    memcpy(msg, begin, begin_size);
    memcpy(msg + begin_size, encoded_data, encoded_size);
    memcpy(msg + begin_size + encoded_size, end, end_size);

    delete[]encoded_data;

    msg_to_flush = 1;
    msg_index = 0;

    return flush_msg(out_rb_ptr);
}

int http_l_encoder::flush_msg(round_buf * out_rb_ptr)
{
    http_flush_msg(msg, &msg_index, msg_size, out_rb_ptr);
    if (msg_index == msg_size) {
	msg_to_flush = 0;
	delete[]msg;
    }

    return 0;
}

bool http_l_encoder::has_unflushed_data()
{
    return msg_to_flush;
}


/**************************************/
int http_l_decoder::decode(round_buf * in_rb_ptr, round_buf * out_rb_ptr)
{
    // Check if we have a message to send.
    if (msg_to_flush) {
	return flush_msg(out_rb_ptr);
    }

    else {
	return build_message(in_rb_ptr, out_rb_ptr);
    }
}

int http_l_decoder::build_message(round_buf * in_rb_ptr,
				  round_buf * out_rb_ptr)
{
    // Two cases are possibles. 
    // - The header was not read yet.
    // - The header was read, but the data is still missing.
    // We use msg_index to distinguish between these two cases 
    // (because it is yet unused).
    // If msg_index is 0, it's the first case. Else, it's the second case.
    if (msg_index == 0) {
	return get_header(in_rb_ptr, out_rb_ptr);
    }

    else {
	return get_data(in_rb_ptr, out_rb_ptr);
    }
}

int http_l_decoder::get_header(round_buf * in_rb_ptr,
			       round_buf * out_rb_ptr)
{
    // What we want is to get a complete message. Since we don't know
    // the size of the complete message, we have to read (possibly too much)
    // data until we find the end of the message.
    int seek_size;
    int old_used_size;
    bool found_it = 0;

    int avail_read = in_rb_ptr->used_size();

    if (avail_read == 0)
	return 0;

    // Resize buffer if necessary.
    if (buf_used_size + avail_read > buf_size) {
	buf_size = (buf_used_size + avail_read) * 2;
	buf = (char *) my_realloc(buf, buf_size);
    }
    // Read (lookahead) in buffer.
    old_used_size = buf_used_size;
    in_rb_ptr->peek_buf(buf + old_used_size, avail_read);
    buf_used_size += avail_read;

    // Scan for '\r\n\r\n'.
    seek_size = avail_read;

    for (int index = 0; index < buf_used_size - 3; index++) {
	// We found the end of the header.
	if (buf[index + 0] == '\r'
	    && buf[index + 1] == '\n'
	    && buf[index + 2] == '\r' && buf[index + 3] == '\n') {
	    seek_size = index + 4 - old_used_size;
	    assert(seek_size >= 0);

	    // Adjust buf_used_size.
	    buf_used_size = index + 4;

	    found_it = 1;
	}
    }

    // Seek in the round buffer.
    in_rb_ptr->seek(seek_size);

    if (!found_it)
	return 0;

    // We got the header. Extract the content size.
    int begin_size = sizeof(HTTP_BEGIN_STRING_S) - 1;
    encoded_data_size = atoi(&buf[begin_size]);

    // OK. Set msg_index = 1, and go fetch the data.
    // We can now discard what we read so far.
    msg_index = 1;
    buf_used_size = 0;

    return get_data(in_rb_ptr, out_rb_ptr);
}

int http_l_decoder::get_data(round_buf * in_rb_ptr, round_buf * out_rb_ptr)
{
    // Attempt to read the rest of the message.
    int old_used_size;

    int to_read = encoded_data_size - buf_used_size;

    assert(to_read > 0);

    int avail_read = in_rb_ptr->used_size();

    if (avail_read > to_read) {
	avail_read = to_read;
    }
    // Resize buffer if necessary.
    if (buf_used_size + avail_read > buf_size) {
	buf_size = (buf_used_size + avail_read) * 2;
	buf = (char *) my_realloc(buf, buf_size);
    }
    // Read in buffer.
    old_used_size = buf_used_size;
    in_rb_ptr->read_buf(buf + old_used_size, avail_read);
    buf_used_size += avail_read;

    // Not enough data yet.
    if (avail_read != to_read) {
	return 0;
    }
    // Url-decode the data.
    int decoded_size = url_decode(buf, encoded_data_size);

    buf_used_size = decoded_size;
    msg_index = 0;
    msg_to_flush = 1;

    // Flush the message.
    return flush_msg(out_rb_ptr);
}

int http_l_decoder::flush_msg(round_buf * out_rb_ptr)
{
    http_flush_msg(buf, &msg_index, buf_used_size, out_rb_ptr);
    if (msg_index == buf_used_size) {
	buf_used_size = 0;

	// Reset msg_to_flush to 0: First case.
	msg_to_flush = 0;

	msg_index = 0;
    }

    return 0;
}

bool http_l_decoder::has_unflushed_data()
{
    return msg_to_flush;
}


/**************************************/
int http_r_encoder::encode(round_buf * in_rb_ptr, round_buf * out_rb_ptr)
{
    // Check if we have a message to send.
    if (msg_to_flush) {
	return flush_msg(out_rb_ptr);
    }

    else {
	return build_message(in_rb_ptr, out_rb_ptr);
    }
}

int http_r_encoder::build_message(round_buf * in_rb_ptr,
				  round_buf * out_rb_ptr)
{
    // Build the message.
    int avail_read;
    char trans_buf[TRANS_BUF_SIZE + 1];

    // Read some data.
    avail_read = in_rb_ptr->used_size();

    if (avail_read == 0)
	return 0;

    if (avail_read > TRANS_BUF_SIZE) {
	avail_read = TRANS_BUF_SIZE;
    }

    in_rb_ptr->read_buf(trans_buf, avail_read);

    // Build the paquet.

    // Suppose we encode the data here... For now it's url-encryption, not a very good encryption. FIX ME.
    char *encoded_data = new char[avail_read * 3];

    int encoded_size = url_encode(trans_buf, avail_read, encoded_data);

    // Let's forge the header.
    my_str header;
    header.af(HTTP_BEGIN_STRING_S);
    header.sf(STRA, HTTP_LENGTH_STRING_S, encoded_size);
    header.af(HTTP_END_STRING_S);
    int header_size = strlen(header.str());

    msg_size = header_size + encoded_size;
    msg = new char[msg_size];

    memcpy(msg, header.str(), header_size);
    memcpy(msg + header_size, encoded_data, encoded_size);

    delete[]encoded_data;

    msg_to_flush = 1;
    msg_index = 0;

    return flush_msg(out_rb_ptr);
}

int http_r_encoder::flush_msg(round_buf * out_rb_ptr)
{
    http_flush_msg(msg, &msg_index, msg_size, out_rb_ptr);
    if (msg_index == msg_size) {
	msg_to_flush = 0;
	delete[]msg;
    }

    return 0;
}

bool http_r_encoder::has_unflushed_data()
{
    return msg_to_flush;
}


/**************************************/
int http_r_decoder::decode(round_buf * in_rb_ptr, round_buf * out_rb_ptr)
{
    // Check if we have a message to send.
    if (msg_to_flush) {
	return flush_msg(out_rb_ptr);
    }

    else {
	return build_message(in_rb_ptr, out_rb_ptr);
    }
}

int http_r_decoder::build_message(round_buf * in_rb_ptr,
				  round_buf * out_rb_ptr)
{
    // What we want is to get a complete message. Since we don't know
    // the size of the complete message, we have to read (possibly too much)
    // data until we find the end of the message.
    int seek_size;
    int old_used_size;
    bool found_it = 0;

    int avail_read = in_rb_ptr->used_size();

    if (avail_read == 0)
	return 0;

    // Resize buffer if necessary.
    if (buf_used_size + avail_read > buf_size) {
	buf_size = (buf_used_size + avail_read) * 2;
	buf = (char *) my_realloc(buf, buf_size);
    }
    // Read (lookahead) in buffer.
    old_used_size = buf_used_size;
    in_rb_ptr->peek_buf(buf + old_used_size, avail_read);
    buf_used_size += avail_read;

    // Scan for '\r\n\r\n'.
    seek_size = avail_read;

    for (int index = 0; index < buf_used_size - 3; index++) {
	// We found the end of the message.
	if (buf[index + 0] == '\r'
	    && buf[index + 1] == '\n'
	    && buf[index + 2] == '\r' && buf[index + 3] == '\n') {
	    seek_size = index + 4 - old_used_size;
	    assert(seek_size >= 0);

	    // Adjust buf_used_size.
	    buf_used_size = index + 4;

	    found_it = 1;
	}
    }

    // Seek in the round buffer.
    in_rb_ptr->seek(seek_size);

    if (!found_it)
	return 0;

    // We got the message. Extract the data encapsulated.
    int begin_size = sizeof(HTTP_BEGIN_STRING_C) - 1;
    int end_size = sizeof(HTTP_END_STRING_C) - 1;
    int to_decode_size = buf_used_size - begin_size - end_size;

    // Url-decode the data.
    int decoded_size = url_decode(buf + begin_size,
				  to_decode_size);

    // Move the string to the beginning of the buffer.
    memmove(buf, buf + begin_size, decoded_size);

    buf_used_size = decoded_size;
    msg_index = 0;
    msg_to_flush = 1;

    // Flush the message.
    return flush_msg(out_rb_ptr);
}

int http_r_decoder::flush_msg(round_buf * out_rb_ptr)
{
    http_flush_msg(buf, &msg_index, buf_used_size, out_rb_ptr);
    if (msg_index == buf_used_size) {
	buf_used_size = 0;
	msg_to_flush = 0;
    }

    return 0;
}

bool http_r_decoder::has_unflushed_data()
{
    return msg_to_flush;
}


/**************************************/
void
http_flush_msg(char *buf, int *buf_index, int buf_size,
	       round_buf * out_rb_ptr)
{
    int byte_to_copy = buf_size - *buf_index;
    int byte_avail = out_rb_ptr->avail_size();

    if (byte_to_copy > byte_avail)
	byte_to_copy = byte_avail;

    out_rb_ptr->write_buf(buf, byte_to_copy);
    *buf_index += byte_to_copy;
}


/**************************************/
int url_decode(char *source, int size)
{
    // Return the size of the decoded string.
    int dest_index = 0;

    for (int index = 0; index < size;) {
	switch (source[index]) {
	case '+':
	    source[dest_index] = ' ';
	    index += 1;
	    break;

	case '%':
	    source[index] = source[index + 1];
	    source[index + 1] = source[index + 2];
	    source[index + 2] = 0;

	    source[dest_index] = (char) strtol(source + index, NULL, 16);
	    index += 3;
	    break;

	default:
	    source[dest_index] = source[index];
	    index += 1;
	}

	// We always create one character.
	dest_index += 1;
    }

    return dest_index;
}


/**************************************/
int url_encode(const char *source, int size, char *dest)
{
    // Encode source as dest. At worst dest will be 3 times larger than 
    // source. Returns the used size.
    static const char *hex = "0123456789abcdef";
    int dest_size = 0;

    for (int index = 0; index < size; index++) {
	unsigned char ch = source[index];

	if (ch == ' ') {
	    dest[dest_size] = '+';
	    dest_size++;
	}

	else if (isalnum(ch)
		 || ch == '.' || ch == '*' || ch == '-' || ch == '_') {
	    dest[dest_size] = ch;
	    dest_size++;
	}

	else {
	    dest[dest_size + 0] = '%';
	    dest[dest_size + 1] = hex[(ch >> 4) & 0xF];
	    dest[dest_size + 2] = hex[ch % 16];
	    dest_size += 3;
	}
    }

    return dest_size;
}
