

//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
//_/_/
//_/_/  This file is part of CMSDK version 2.0.0.170915_Beta4 (15 September 2017)
//_/_/  
//_/_/  developed by
//_/_/  
//_/_/  Communicative Machines Ltd. 
//_/_/    http://cmlabs.com/
//_/_/  Copyright (c) 2017
//_/_/  
//_/_/  in collaboration with 
//_/_/  
//_/_/  Icelandic Institute for Intelligent Machines ses
//_/_/    Menntavegur 1
//_/_/    101 Reykjavik
//_/_/    ICELAND
//_/_/    http://www.iiim.is/
//_/_/
//_/_/  and is released under the following
//_/_/  
//_/_/  EXTENDED BSD License
//_/_/
//_/_/  Redistribution and use in source and binary forms, with or without
//_/_/  modification, is permitted provided that the following conditions 
//_/_/  are met:
//_/_/
//_/_/  - Redistributions of source code must retain the above copyright 
//_/_/    and collaboration notice, this list of conditions and the following 
//_/_/    disclaimer.
//_/_/
//_/_/  - Redistributions in binary form must reproduce the above copyright 
//_/_/    notice, this list of conditions and the following disclaimer in the 
//_/_/    documentation and/or other materials provided with the distribution.
//_/_/
//_/_/  - Neither the name of its copyright holders nor the names of its 
//_/_/    contributors may be used to endorse or promote products derived from 
//_/_/    this software without specific prior written permission.
//_/_/
//_/_/  - CADIA Clause: The license granted in and to the software under this 
//_/_/    agreement is a limited-use license. The software may not be used in 
//_/_/    furtherance of: (i) intentionally causing bodily injury or severe 
//_/_/    emotional harm to any person; (ii) invading the personal privacy or 
//_/_/    violating the human rights of any person; or (iii) committing or 
//_/_/    preparing for any act of war. 
//_/_/
//_/_/  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
//_/_/  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
//_/_/  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
//_/_/  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 
//_/_/  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
//_/_/  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
//_/_/  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
//_/_/  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
//_/_/  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
//_/_/  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
//_/_/  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//_/_/
//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
#include "NetworkProtocols.h"

namespace	cmlabs{

/////////////////////////////////////////////////////////////
// Data Types
/////////////////////////////////////////////////////////////

//HTTPRequest::HTTPRequest(uint64 startRecTime) {
//	if (startRecTime)
//		time = startRecTime;
//	else
//		time = GetTimeNow();
//	endReceiveTime = 0;
//	this->source = 0;
//	type = 0;
//	headerLength = 0;
//	contentLength = 0;
//	ifModifiedSince = 0;
//	keepAlive = true;
//	data = NULL;
//}

HTTPRequest::HTTPRequest(uint64 source, uint64 startRecTime) {
	if (startRecTime)
		time = startRecTime;
	else
		time = GetTimeNow();
	endReceiveTime = 0;
	this->source = source;
	type = 0;
	headerLength = 0;
	contentLength = 0;
	ifModifiedSince = 0;
	keepAlive = true;
	data = NULL;
}

HTTPRequest::HTTPRequest(HTTPRequest* req) {
	time = req->time;
	endReceiveTime = req->endReceiveTime;
	source = req->source;
	type = req->type;
	headerLength = req->headerLength;
	contentLength = req->contentLength;
	ifModifiedSince = req->ifModifiedSince;
	keepAlive = req->keepAlive;
	data = new char[headerLength+contentLength+1];
	memcpy(req->data, data, headerLength+contentLength+1);
	entries = req->entries;
	params = req->params;
}


HTTPRequest::~HTTPRequest() {
	if (data != NULL)
		delete(data);
	data = NULL;
	std::map<std::string, HTTPPostEntry*>::iterator i = postEntries.begin(), e = postEntries.end();
	while (i != e) {
		delete(i->second);
		i++;
	}
	postEntries.clear();
}

// Reading it in from a socket
bool HTTPRequest::processHeader(const char* buffer, uint32 size, bool &isInvalid) {
	isInvalid = false;
	if (!buffer || (size < 5))
		return false;

	if (utils::stristr(buffer, "GET ") == buffer)
		type = HTTP_GET;
	else if (utils::stristr(buffer, "PUT ") == buffer)
		type = HTTP_PUT;
	else if (utils::stristr(buffer, "POST ") == buffer)
		type = HTTP_POST;
	else if (utils::stristr(buffer, "HEAD ") == buffer)
		type = HTTP_HEAD;
	else if (utils::stristr(buffer, "DELETE ") == buffer)
		type = HTTP_DELETE;
	else if (utils::stristr(buffer, "OPTIONS ") == buffer)
		type = HTTP_OPTIONS;
	else {
		isInvalid = true;
		return false;
	}

	// Find end of header
	uint32 n;
	for (n = 3; n < size; n++) {
		if (buffer[n] == 10) {
			if ( (size-n >= 2) && (buffer[n+1] == 10) ) {
				// Just using LF, no CR
				headerLength = n+2;
				break;
			}
			else if ( (size-n >= 3) && (buffer[n+1] == 13) && (buffer[n+2] == 10) && (buffer[n-1] == 13) ) {
				// Using CRLF
				headerLength = n+3;
				break;
			}
		}
	}

	if (headerLength == 0)
		return false;

	uint32 tempContentLength = 1024;
	data = new char[headerLength+tempContentLength+1];
	memcpy(data, buffer, headerLength);
	data[headerLength] = 0; // temp sting end

	// Now process header entries
	std::string key, val, uri;
	const char* s = data, *t;
	n = 0;
	uint32 crSize = 0;
	uint32 left = headerLength;
	size_t i, j;
	while (n < headerLength) {
		if (!utils::GetNextLineEnd(s, left, n, crSize))
			break;
		// Process line
		if ((t = strchr(s, ' ')) && (t - s < (int32)n)) {
			t -= 1;
			if (*t != ':') {
				t++;
				key.assign(s, t - s);
			}
			else {
				key.assign(s, t - s);
				t++;
			}
			while (*t == 32) t++;
			if (*t > 32)
				val.assign(t, n + s - t);
			else
				val = "";
		}
		else {
			key.assign(s, n);
			val = "";
		}
		// Record entry
		if (s == data) {
			entries["http_fullrequest"] = val;
			if ((i = val.find(' ')) != std::string::npos) {
				uri = val.substr(0, i);
				entries["http_uri"] = uri;
				//printf("*** URI: %s\n", uri.c_str());
				entries["http_protocol"] = val.substr(i+1);
				if ((j = uri.find('?')) != std::string::npos) {
					entries["http_request"] = uri.substr(1, j-1); // without the first /
					parseURIParameters(uri.substr(j+1).c_str());
				}
				else
					entries["http_request"] = uri.substr(1, i-1); // without the first /
			}
			else {
				uri = val;
				entries["http_uri"] = uri;
				entries["http_protocol"] = "";
				if ((j = uri.find('?')) != std::string::npos) {
					entries["http_request"] = uri.substr(1, j-1); // without the first /
					parseURIParameters(uri.substr(j+1).c_str());
				}
				else
					entries["http_request"] = uri.substr(1); // without the first /
			}
		}
		else if (key.length() > 0) {
			if (entries.find(key) != entries.end())
				entries[key] = entries[key] + ";" + val;
			else
				entries[key] = val;
			// Check entry
			if (stricmp(key.c_str(), "content-length") == 0)
				contentLength = atoi(val.c_str());
			else if (stricmp(key.c_str(), "if-modified-since") == 0)
				ifModifiedSince = GetTimeFromString(val.c_str());
			else if (stricmp(key.c_str(), "connection") == 0)
				keepAlive = (stricmp(val.c_str(), "close") != 0);
			else if (stricmp(key.c_str(), "content-type") == 0) {
				std::multimap<std::string,std::string> conTypes = utils::TextMultiMapSplit(val.c_str(), ";", "=");
				std::multimap<std::string,std::string>::iterator ci = conTypes.find("boundary");
				if (ci != conTypes.end())
					postBoundary = utils::StringFormat("--%s", ci->second.c_str());
			}
		}
		// Next line
		s += n + crSize;
		left -= n + crSize;
	}

	if (contentLength > tempContentLength) {
		delete(data);
		data = new char[headerLength+contentLength+1];
		memcpy(data, buffer, headerLength);
		data[headerLength] = 0; // temp string end
		data[headerLength+contentLength] = 0;
	}

	return true;
}

bool HTTPRequest::processContent(const char* buffer, uint32 size) {
	if (!buffer || (size != contentLength))
		return false;
	memcpy(data+headerLength, buffer, size);
	data[headerLength+contentLength] = 0;
	parseContentParameters(data+headerLength, size);
	endReceiveTime = GetTimeNow();
	return true;
}

const char* HTTPRequest::getContent(uint32& size) {
	size = contentLength;
	return data+headerLength;
}

const char* HTTPRequest::getHeaderEntry(const char* entry) {
	std::map<std::string, std::string>::iterator it = entries.find(entry);
	if (it != entries.end())
		return it->second.c_str();
	else
		return NULL;
}

const char* HTTPRequest::getRequest() {
	return getHeaderEntry("http_request");
}

const char* HTTPRequest::getURI() {
	return getHeaderEntry("http_uri");
}

const char* HTTPRequest::getProtocol() {
	return getHeaderEntry("http_protocol");
}

bool HTTPRequest::parseContentParameters(const char* content, uint32 size) {
//	"------WebKitFormBoundarynP2yLpjkwmT3R8j5\r\nContent-Disposition: form-data; name="Username"\r\n\r\nundefined\r\n"
//	"------WebKitFormBoundarynP2yLpjkwmT3R8j5\r\nContent-Disposition: form-data; name="Password"\r\n\r\nundefined\r\n"
//	"------WebKitFormBoundarynP2yLpjkwmT3R8j5\r\nContent-Disposition: form-data; name="Authentication"\r\n\r\nundefined\r\n"
//	"------WebKitFormBoundarynP2yLpjkwmT3R8j5--\r\n"
	if (!content)
		return false;

	// check main content-type
	std::string contentType = entries["Content-Type"];

	if (contentType.find("application/x-www-form-urlencoded") != std::string::npos) {
		// post just contains one content chunk
		parseContentChunk(content, size);
	}
	else if (contentType.find("multipart/form-data") != std::string::npos) {
		// Check if using boundaries
		if (!postBoundary.length() || (size < (2 * postBoundary.length())) )
			return false;
		//printf("[%u] %s\n", size, content);
		uint32 boundarySize = (uint32)postBoundary.length();

		// Check content
		const char* boundary = postBoundary.c_str();
		if (memcmp(content, boundary, boundarySize) != 0)
			return false;

		const char* lastStart = content + boundarySize + 2;
		const char* c = lastStart;

		for (uint32 n=boundarySize; n<size; n++) {
			//if ((*c == '-') && (memcmp(c, boundary, boundarySize) == 0)) {
			if (memcmp(c, boundary, boundarySize) == 0) {
				parseContentChunk(lastStart, (uint32)(c - lastStart));
				lastStart = c += boundarySize + 2;
				n += boundarySize + 2;
			}
			else
				c++;
		}
	}
	else {
		// Post content is something else
		HTTPPostEntry* postEntry = new HTTPPostEntry();
		postEntry->name = "POST";
		postEntry->type = contentType;
		postEntry->setContent(content, size);
		postEntries[postEntry->name] = postEntry;
	}


	return true;
}

bool HTTPRequest::parseContentChunk(const char* chunk, uint32 size) {
//	"Content-Disposition: form-data; name="Username"\r\n\r\nundefined\r\n"

	const char* valContent = strstr(chunk, "\r\n\r\n");
	if (!valContent)
		return false;
	uint32 headerSize = (uint32)(valContent-chunk);
	valContent += 4;
	uint32 valContentSize = size - headerSize - 6;

	HTTPPostEntry* postEntry = new HTTPPostEntry();
	char* headerText = new char[headerSize+1];
	memcpy(headerText, chunk, headerSize);
	headerText[headerSize] = 0;

	//printf("*** %s\n", headerText);

	std::multimap<std::string, std::string> entryMap;
	std::multimap<std::string, std::string>::iterator ei;

	std::vector<std::string> headerEntries = utils::TextListSplit(headerText, "\r\n", true, true);
	std::vector<std::string>::iterator i = headerEntries.begin(), e = headerEntries.end();
	while (i != e) {
		if (utils::TextStartsWith((*i).c_str(), "Content-Disposition:", false)) {
			entryMap = utils::TextMultiMapSplit((*i).c_str(), ";", "=");
			if ( (ei = entryMap.find("name")) != entryMap.end())
				postEntry->name = utils::TextTrimQuotes(ei->second.c_str());
			if ( (ei = entryMap.find("filename")) != entryMap.end())
				postEntry->filename = utils::TextTrimQuotes(ei->second.c_str());
		}
		else if (utils::TextStartsWith((*i).c_str(), "Content-Type:", false)) {
			postEntry->type = (*i).substr(14);
		}
		i++;
	}
	delete [] headerText;
	if (postEntry->name.length())
		postEntry->setContent(valContent, valContentSize);

	if (postEntry->isValid()) {
		postEntries[postEntry->name] = postEntry;
		return true;
	}
	else {
		delete(postEntry);
		return false;
	}


	//CGIRequestEntry reqEntry;

	//int pos;
	//QStringList entries;
	//for (int n=0; n<headerList.size(); n++) {
	//	if ((entry = headerList.at(n).trimmed()).length()) {
	//		if (entry.startsWith("Content-Disposition:", Qt::CaseInsensitive)) {
	//			entries = entry.split(";");
	//			for (int m=0; m<entries.size(); m++) {
	//				if ( (pos = entries.at(m).indexOf(" name=")) >= 0) {
	//					reqEntry.name = entries.at(m).mid(pos+6).replace("\"","");
	//				}
	//				else if ( (pos = entries.at(m).indexOf(" filename=")) >= 0) {
	//					reqEntry.filename = entries.at(m).mid(pos+10).replace("\"","");
	//				}
	//			}
	//		}
	//		else if (entry.startsWith("Content-Type:", Qt::CaseInsensitive)) {
	//			reqEntry.type = entry.split(":").at(1).trimmed().replace("\"","");
	//		}
	//	}
	//}

	//if (!reqEntry.name.length())
	//	return false;

	//reqEntry.data = dataChunk.mid(doubleBreakPos+4);
	//requestEntries[reqEntry.name] = reqEntry;
	////log(QString("Found [%1] = {%2}").arg(reqEntry.name).arg(reqEntry.data.data()), false);
	//return true;
}

bool HTTPRequest::parseURIParameters(const char* text) {
	if (!text)
		return false;

	std::multimap<std::string, std::string> paramMap = utils::TextMultiMapSplit(text, "&", "=");
	std::multimap<std::string, std::string>::iterator i = paramMap.begin(), e = paramMap.end();
	while (i != e) {
		params[html::DecodeHTML(i->first)] = html::DecodeHTML(i->second);
		i++;
	}
	return true;

//	uint32 len = (uint32)strlen(text);
//	const char* s = text;
//	const char* e = strchr(s, '=');
//	const char* p = strchr(s, '&');

//	if (!e && !p) {
//		key.assign(s);
//		params[html::DecodeHTML(key)] = "";
//		return true;
//	}

//	std::string key, val;

//	while ( e ) {
//		key.assign(s, e-s);
//		if (p) {
//			val.assign(e+1, p-e-1);
//			params[html::DecodeHTML(key)] = html::DecodeHTML(val);
//			s = p+1;
//			e = strchr(s, '=');
//			p = strchr(s, '&');
//		}
//		else {
//			val.assign(e+1);
//			params[html::DecodeHTML(key)] = html::DecodeHTML(val);
//			break;
//		}
//	}
//	return true;
}

DataMessage* HTTPRequest::convertToMessage() {
	DataMessage* msg = new DataMessage();
	std::map<std::string, std::string>::iterator i, e;

	msg->setInt("HTTP_OPERATION", type);
	msg->setString("REQUEST", this->getRequest());
	msg->setString("URI", this->getURI());

	for (i=entries.begin(), e=entries.end(); i!=e; i++)
		msg->setString(i->first.c_str(), i->second.c_str());

	for (i=params.begin(), e=params.end(); i!=e; i++)
		msg->setString(i->first.c_str(), i->second.c_str());

	std::map<std::string, HTTPPostEntry*>::iterator pi = postEntries.begin(), pe = postEntries.end();
	while (pi != pe) {
		if (pi->second->type.length()) {
			msg->setString((pi->first+"_CONTENT_TYPE_").c_str(), pi->second->type.c_str());
			if (utils::stristr(pi->second->type.c_str(), "json"))
				msg->setString(pi->first.c_str(), pi->second->content);
			else if (utils::stristr(pi->second->type.c_str(), "xml"))
				msg->setString(pi->first.c_str(), pi->second->content);
			else if (utils::stristr(pi->second->type.c_str(), "text"))
				msg->setString(pi->first.c_str(), pi->second->content);
			else
				msg->setData(pi->first.c_str(), pi->second->content, pi->second->contentSize);
		}
		else
			msg->setString(pi->first.c_str(), pi->second->content);
		pi++;
	}
	msg->setCreatedTime(time);
	msg->setRecvTime(endReceiveTime);

	return msg;
}


const char* HTTPRequest::getParameter(const char* entry) {
	std::map<std::string, std::string>::iterator it = params.find(entry);
	if (it != params.end())
		return it->second.c_str();
	else
		return NULL;
}

const char* HTTPRequest::getPostData(const char* entry, uint32& size, const char** type) {
	std::map<std::string, HTTPPostEntry*>::iterator i = postEntries.find(entry);
	if (i == postEntries.end())
		return NULL;
	size = i->second->contentSize;
	*type = i->second->type.c_str();
	return i->second->content;
}

const char* HTTPRequest::getPostData(const char* entry, uint32& size) {
	std::map<std::string, HTTPPostEntry*>::iterator i = postEntries.find(entry);
	if (i == postEntries.end())
		return NULL;
	size = i->second->contentSize;
	return i->second->content;
}

const char* HTTPRequest::getPostDataType(const char* entry) {
	std::map<std::string, HTTPPostEntry*>::iterator i = postEntries.find(entry);
	if (i == postEntries.end())
		return NULL;
	return i->second->type.c_str();
}


bool HTTPRequest::createRequest(
	uint8 type, const char* host, const char* uri, const char* content,
	uint32 contentSize, bool keepAlive, uint64 ifModifiedSince) {

	char timeString[1024];
	if (ifModifiedSince > 0) {
		if (!GetHTTPTime(TIME_YEAR_1970, timeString, 1024))
			timeString[0] = 0;
	}
	else {
		if (!GetHTTPTime(ifModifiedSince, timeString, 1024))
			timeString[0] = 0;
	}

	if (data)
		delete(data);
	data = new char[4096 + contentSize];
	if (!content) {
		snprintf(data, 4096+contentSize,
"%s %s HTTP/1.1\r\n\
Host: %s\r\n\
From: someuser@somedomain.com\r\n\
User-Agent: HTTPTool/1.0\r\n\
if-modified-since: %s\r\n\r\n",
			HTTP_Type[type], uri, host, timeString);
	}
	else {
		snprintf(data, 4096+contentSize,
"%s %s HTTP/1.1\r\n\
Host: %s\r\n\
From: someuser@somedomain.com\r\n\
User-Agent: HTTPTool/1.0\r\n\
Content-Type: application/x-www-form-urlencoded\r\n\
Content-Length: %u\r\n\r\n",
			HTTP_Type[type], uri, host, (uint32) (contentSize ? contentSize : strlen(content)));
	}

	headerLength = (uint32)strlen(data);
	memcpy(data+headerLength, content, contentSize);
	contentLength = contentSize;
	data[headerLength + contentLength] = 0;

	return true;
}

uint32 HTTPRequest::getSize() {
	if (data)
		return headerLength + contentLength;
	else
		return 0;
}














TelnetLine::TelnetLine(uint64 source) {
	this->source = source;
	data = NULL;
	time = GetTimeNow();
	source = 0;
	size = 0;
	user = 0;
	cr[0] = 13;
	cr[1] = 10;
	cr[2] = 0;
}

TelnetLine::~TelnetLine() {
	if (data != NULL)
		delete(data);
	data=NULL;
}

bool TelnetLine::setCR(uint8 type) {
	switch(type) {
		default:
		case TELNET_WINDOWS:
			cr[0] = 13;
			cr[1] = 10;
			cr[2] = 0;
			break;
		case TELNET_UNIX:
			cr[0] = 10;
			cr[1] = 0;
			break;
	}
	return true;
}

uint32 TelnetLine::getSize() {
	if (!data)
		return 0;
	if (size)
		return size;
	else
		return (uint32)strlen(data);
}

bool TelnetLine::setLine(const char* buffer, bool addCR) {
	return setLine(buffer, (uint32)strlen(buffer), addCR);
}

bool TelnetLine::setLine(const char* buffer, uint32 len, bool addCR) {
	if (data)
		delete(data);

	data = new char[len+3];
	if (buffer && len > 0) {
		memcpy(data, buffer, len);
		if (addCR)
			memcpy(data+len, cr, 3);
		else
			data[len] = 0;
	}
	else
		data[len] = 0;
	return true;
}

bool TelnetLine::giveData(char* buffer, uint32 len) {
	if (data)
		delete(data);
	data = buffer;
	size = len;
	return true;
}












HTTPReply* HTTPReply::CreateErrorReply(uint8 type) {
	HTTPReply* reply = new HTTPReply((uint64)0);
	reply->type = type;
	return reply;
}



HTTPReply::HTTPReply(uint64 source) {
	time = GetTimeNow();
	this->source = source;
	data = NULL;
	type = 0;
	headerLength = contentLength = 0;
	keepAlive = true;
}

HTTPReply::HTTPReply(HTTPReply* reply) {
	time = reply->time;
	source = reply->source;
	type = reply->type;
	headerLength = reply->headerLength;
	contentLength = reply->contentLength;
	keepAlive = reply->keepAlive;
	data = new char[headerLength+contentLength+1];
	memcpy(reply->data, data, headerLength+contentLength+1); // incl. string end
	entries = reply->entries;
	params = reply->params;
}

HTTPReply::~HTTPReply() {
	if (data != NULL)
		delete(data);
	data = NULL;
}

// Generating the data

bool HTTPReply::createOptionsResponse(uint8 status, uint64 time, const char* serverName, bool keepAlive, const char* origin, const char* operations) {

	//OPTIONS /cors HTTP/1.1
	//Origin: http://api.bob.com
	//Access-Control-Request-Method: PUT
	//Access-Control-Request-Headers: X-Custom-Header
	//Host: api.alice.com
	//Accept-Language: en-US
	//Connection: keep-alive
	//User-Agent: Mozilla/5.0...
	//
	//Preflight Response:
	//
	//Access-Control-Allow-Origin: http://api.bob.com
	//Access-Control-Allow-Methods: GET, POST, PUT
	//Access-Control-Allow-Headers: X-Custom-Header
	//Content-Type: text/html; charset=utf-8

	char timeString[1024];
	if (!GetHTTPTime(time, timeString, 1024))
		timeString[0] = 0;

	if (data)
		delete(data);
	data = new char[4096];
	snprintf(data, 4096,
"Access-Control-Allow-Origin: *\r\nAccess-Control-Allow-Methods: GET, PUT, POST, DELETE\r\nAccess-Control-Allow-Headers: accept, authorization, origin\r\n\r\n");
//"Access-Control-Allow-Origin: *\r\nAccess-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept\r\n\r\n");

//"%s\r\nDate: %s\r\n
//Server: %s\r\n
//Connection: %s\r\n
//"Access-Control-Allow-Origin: %s\r\n
//Access-Control-Allow-Methods: %s\r\n
//Access-Control-Allow-Headers: X-Custom-Header\r\n
//Content-Type: text/html\r\n\r\n",
//		//HTTP_Status[status], timeString, serverName,
//		//keepAlive ? "Keep-Alive" : "close",
//		origin, operations);

	headerLength = (uint32)strlen(data);
	contentLength = 0;
	return true;
}


bool HTTPReply::createErrorPage(uint8 status, uint64 time, const char* serverName, bool keepAlive) {

	char timeString[1024];
	if (!GetHTTPTime(time, timeString, 1024))
		timeString[0] = 0;

	if (data)
		delete(data);
	data = new char[4096];
	snprintf(data, 4096,
"%s\r\nDate: %s\r\nServer: %s\r\n\
Connection: %s\r\n\r\n",
		HTTP_Status[status], timeString, serverName,
		keepAlive ? "Keep-Alive" : "close");

	headerLength = (uint32)strlen(data);
	contentLength = 0;
	return true;
}

bool HTTPReply::createPage(uint8 status, uint64 time, const char* serverName,
	uint64 lastMod, bool keepAlive, bool cache, const char* contentType,
	const char* content, uint32 contentSize) {

	char timeString[1024];
	if (!GetHTTPTime(time, timeString, 1024))
		timeString[0] = 0;

	contentLength = (contentSize > 0) ? contentSize : (uint32)strlen(content);

	if (data)
		delete(data);
	data = new char[4096 + contentLength];
	snprintf(data, 4096+contentLength,
"%s\r\nDate: %s\r\nServer: %s\r\n\
Last-Modified: %s\r\nContent-Length: %u\r\n\
Connection: %s\r\nContent-Type: %s\r\nCache-Control: %s\r\n\r\n",
		HTTP_Status[status], timeString, serverName, timeString, contentLength,
		keepAlive ? "Keep-Alive" : "close", contentType, cache ? "Public" : "No-Cache");

	headerLength = (uint32)strlen(data);
	memcpy(data+headerLength, content, contentLength);
	data[headerLength + contentLength] = 0;

	return true;
}

bool HTTPReply::createFromFile(uint64 time, const char* serverName,
	uint64 ifLastMod, bool keepAlive, bool cache,
	const char* filename) {

	uint64 lastModifiedTime = 0;
	if (ifLastMod) {
		utils::FileDetails fileInfo = utils::GetFileDetails(filename);
		if (fileInfo.doesExist && !fileInfo.isDirectory && (fileInfo.lastModifyTime < ifLastMod) )
			return createErrorPage(HTTP_USE_LOCAL_COPY, time, serverName, keepAlive);
	}

	char* filedata = NULL;
	uint32 datasize = 0;
	std::string html;
	bool isBinary = false;
	char* exttype = new char[1024];
	if (!html::GetMimeType(exttype, filename, 1024, isBinary)) {
		html = utils::StringFormat("Unsupported mime type for file '%s'...", filename);
	}
	else if (!isBinary) {
		if (!(html = utils::ReadAFileString(filename)).size()) {
			//html = utils::StringFormat("Could read file '%s'", filename);
		}
	}
	else {
		if ( !(filedata = utils::ReadAFile(filename, datasize, true))) {
			//html = utils::StringFormat("Could read file '%s'", filename);
		}
	}

	if (filedata)
		createPage(HTTP_OK, GetTimeNow(), serverName, 0, keepAlive, cache, exttype, filedata, datasize);
	else if (html.length())
		createPage(HTTP_OK, GetTimeNow(), serverName, 0, keepAlive, cache, exttype, html.c_str());
	else {
		html = "<html><head><title>404 Not Found</title></head><body><h1>Not Found</h1><p>The requested URL was not found on this server.</p></body></html>";
		createPage(HTTP_FILE_NOT_FOUND, GetTimeNow(), serverName, 0, keepAlive, cache, "text/html", html.c_str());
	}

	delete [] filedata;
	delete [] exttype;

	return true;
}

// Reading it in from a socket
bool HTTPReply::processHeader(const char* buffer, uint32 size, bool &isInvalid) {
	isInvalid = false;
	if (!buffer || (size < 5))
		return false;

	// Find end of header
	uint32 n;
	for (n = 3; n < size; n++) {
		if (buffer[n] == 10) {
			if ( (size-n >= 2) && (buffer[n+1] == 10) ) {
				// Just using LF, no CR
				headerLength = n+2;
				break;
			}
			else if ( (size-n >= 3) && (buffer[n+1] == 13) && (buffer[n+2] == 10) && (buffer[n-1] == 13) ) {
				// Using CRLF
				headerLength = n+3;
				break;
			}
		}
	}

	if (headerLength == 0) {
		isInvalid = true;
		return false;
	}

	uint32 tempContentLength = 4096;
	data = new char[headerLength+tempContentLength+1];
	memcpy(data, buffer, headerLength);
	data[headerLength] = 0; // temp string end

	// Now process header entries
	std::string key, val, uri;
	const char* s = data, *t;
	n = 0;
	uint32 crSize = 0;
	uint32 left = headerLength;
	while (n < headerLength) {
		if (!utils::GetNextLineEnd(s, left, n, crSize))
			break;
		// Process line
		//if (t = strchr(s, ' ')) {
		if ((t = strchr(s, ' ')) && (t - s < (int32)n)) {
				t -= 1;
			if (*t != ':') {
				t++;
				key.assign(s, t-s);
			}
			else {
				key.assign(s, t-s);
				t++;
			}
			while (*t == 32) t++;
			if (*t > 32)
				val.assign(t, n+s-t);
			else
				val = "";
		}
		else {
			key.assign(s, n);
			val = "";
		}
		// Record entry
		if (s == data) {
			entries["http_protocol"] = key;
			if (val.find("200") != val.npos)
				type = HTTP_OK;
			else if (val.find("301") != val.npos)
				type = HTTP_MOVED_PERMANENTLY;
			else if (val.find("304") != val.npos)
				type = HTTP_USE_LOCAL_COPY;
			else if (val.find("403") != val.npos)
				type = HTTP_ACCESS_DENIED;
			else if (val.find("404") != val.npos)
				type = HTTP_FILE_NOT_FOUND;
			else if (val.find("500") != val.npos)
				type = HTTP_INTERNAL_ERROR;
			else
				type = atoi(val.c_str());
		}
		else {
			entries[key] = val;
			// Check entry
			if (stricmp(key.c_str(), "content-length") == 0)
				contentLength = atoi(val.c_str());
			else if (stricmp(key.c_str(), "date") == 0)
				time = GetTimeFromString(val.c_str());
			else if (stricmp(key.c_str(), "connection") == 0)
				keepAlive = (stricmp(val.c_str(), "close") != 0);
		}
		// Next line
		s += n + crSize;
		left -= n + crSize;
	}

	if (contentLength > tempContentLength) {
		delete(data);
		data = new char[headerLength+contentLength+1];
		memcpy(data, buffer, headerLength);
		data[headerLength] = 0; // temp string end
		data[headerLength+contentLength] = 0;
	}

	return true;
}

bool HTTPReply::processContent(const char* buffer, uint32 size) {
	if (!buffer || (size != contentLength))
		return false;
	memcpy(data+headerLength, buffer, size);
	data[headerLength+contentLength] = 0;
	return true;
}

const char* HTTPReply::getContent(uint32& size) {
	size = contentLength;
	return data+headerLength;
}

const char* HTTPReply::getHeaderEntry(const char* entry) {
	std::map<std::string, std::string>::iterator it = entries.find(entry);
	if (it != entries.end())
		return it->second.c_str();
	else
		return NULL;
}

const char* HTTPReply::getProtocol() {
	return getHeaderEntry("http_protocol");
}

uint32 HTTPReply::getSize() {
	if (data)
		return headerLength + contentLength;
	else
		return 0;
}


/////////////////////////////////////////////////////////////
// HTTP Server Protocol
/////////////////////////////////////////////////////////////

bool HTTPProtocol::CheckBufferForCompatibility(const char* buffer, uint32 length) {
	if (length < 4)
		return false;
	return ( utils::stristr(buffer, "GET ") == buffer ||
		utils::stristr(buffer, "PUT ") == buffer ||
		utils::stristr(buffer, "POST ") == buffer ||
		utils::stristr(buffer, "DELETE ") == buffer ||
		utils::stristr(buffer, "OPTIONS ") == buffer ||
		utils::stristr(buffer, "HEAD ") == buffer );
}

bool HTTPProtocol::InitialiseConversation(NetworkConnection* con) {
	return false;
}

bool HTTPProtocol::SendHTTPReply(NetworkConnection* con, HTTPReply* reply) {
	if (!con || !reply)
		return false;
	//return con->send((char*)reply->data, reply->getSize());
	if (con->send((char*)reply->data, reply->getSize())) {
		// printf("--- HTTP sent reply (%u)...\n", reply->contentLength);
		return true;
	}
	else {
		// printf("--- HTTP failed to send reply (%u)...\n", reply->contentLength);
		return false;
	}

}

HTTPReply* HTTPProtocol::ReceiveHTTPReply(NetworkConnection* con, uint32 timeout) {

	if (!con || !con->waitForDataToRead(timeout))
		return HTTPReply::CreateErrorReply(HTTP_SERVER_UNAVAILABLE);

	uint32 maxSize = 4096, size;
	char* buffer = new char[maxSize];

	uint64 start = GetTimeNow();
	uint32 wait = 0;

	bool isInvalid = false;
	HTTPReply* reply = NULL;
	while (true) {
		if (!con->receiveAvailable(buffer, size, maxSize, wait, true)) {
			delete [] buffer;
			return HTTPReply::CreateErrorReply(HTTP_SERVER_NOREPLY);
		}
		if (size > 0) {
			reply = new HTTPReply(con->getRemoteAddress());
			if (reply->processHeader(buffer, size, isInvalid))
				break;
			delete(reply);
			reply = NULL;
			if (isInvalid) {
				// ##############
			}
		}
		if (GetTimeAgeMS(start) < (int32)timeout) {
			delete [] buffer;
			return HTTPReply::CreateErrorReply(HTTP_SERVER_NOREPLY);
		}
		wait = 20;
	}

	// Now we know how much to actually expect
	// First read the header proper and ignore
	if (!con->discard(reply->headerLength)) {
		delete [] buffer;
		delete(reply);
		return HTTPReply::CreateErrorReply(HTTP_SERVER_NOREPLY);
	}

//	printf("Receiving HTTPReply content (%u)...\n\n", reply->contentLength);
	// Then read the rest of the content
	if (reply->contentLength) {
		if (reply->contentLength > maxSize - reply->headerLength) {
			delete [] buffer;
			buffer = new char[reply->contentLength];
			// We have to read the rest, otherwise we will mess up the connection
			if (!con->receive(buffer, reply->contentLength, 3000)) {
				printf("Error receiving HTTPReply content (%u)...\n\n", reply->contentLength);
				delete [] buffer;
				delete(reply);
				return HTTPReply::CreateErrorReply(HTTP_SERVER_MALFORMED_REPLY);
			}

			if (!reply->processContent(buffer, reply->contentLength)) {
				delete [] buffer;
				delete(reply);
				return HTTPReply::CreateErrorReply(HTTP_SERVER_MALFORMED_REPLY);
			}
		}
		else {
			// We have to read the rest, otherwise we will mess up the connection
			if (!con->receive(buffer, reply->contentLength, 3000)) {
				printf("Error receiving HTTPReply content (%u)...\n\n", reply->contentLength);
				delete [] buffer;
				delete(reply);
				return HTTPReply::CreateErrorReply(HTTP_SERVER_MALFORMED_REPLY);
			}

			if (!reply->processContent(buffer, reply->contentLength)) {
				delete [] buffer;
				delete(reply);
				return HTTPReply::CreateErrorReply(HTTP_SERVER_MALFORMED_REPLY);
			}
		}
	}

//	printf("Got HTTPReply...\n\n");
	delete [] buffer;
	return reply;
}

bool HTTPProtocol::SendHTTPRequest(NetworkConnection* con, HTTPRequest* req) {
	if (!con || !req)
		return false;
	return con->send((char*)req->data, req->getSize());
}

HTTPRequest* HTTPProtocol::ReceiveHTTPRequest(NetworkConnection* con, uint32 timeout) {

	if (!con || !con->waitForDataToRead(timeout))
		return NULL;

	uint32 maxSize = 4096, size;
	char* buffer = new char[maxSize];

	uint64 start = GetTimeNow();
	uint64 startReceive = 0;
	uint32 wait = 0;

	bool isInvalid;
	HTTPRequest* req = NULL;
	while (true) {
		if (!con->receiveAvailable(buffer, size, maxSize, wait, true)) {
			delete [] buffer;
			return NULL;
		}
		if (size > 0) {
			if (!startReceive)
				startReceive = GetTimeNow();
			req = new HTTPRequest(con->getRemoteAddress(), startReceive);
			//utils::PrintBinary(buffer, size, false, "HTTP: ");
			if (req->processHeader(buffer, size, isInvalid)) {
				//printf("HTTP Done\n");
				break;
			}
			else if (isInvalid) {
				//utils::WriteAFile("d:\\http_invalid.bin", buffer, size, true);
				DataMessage* test = new DataMessage(buffer);
				delete [] buffer;
				con->discard(size);
				return req;
			}
			else if (size == maxSize) {
				// maxSize isn't big enough to satisfy processHeader
				//printf("HTTP MaxSize, waiting for more...\n");
				//utils::PrintBinary(buffer, size, false, "HTTP MaxSize: ");
				//utils::WriteAFile("d:\\http_maxsize.bin", buffer, size, true);
				utils::Sleep(10);
			}
			else {
				//utils::PrintBinary(buffer, size, false, "HTTP WaitMore: ");
				//printf("HTTP waiting for more...\n");
				//utils::WriteAFile("d:\\http_notenough.bin", buffer, size, true);
				utils::Sleep(10);
			}
			delete(req);
			req = NULL;
		}
		if (GetTimeAgeMS(start) < (int32)timeout) {
			delete [] buffer;
			return NULL;
		}
		wait = 20;
	}

	// Now we know how much to actually expect
	// First read the header proper and ignore
	if (!con->discard(req->headerLength)) {
		delete [] buffer;
		delete(req);
		return NULL;
	}
	// Then read the rest of the content
	if (req->contentLength) {
		if (req->contentLength > maxSize) {
			delete [] buffer;
			buffer = new char[req->contentLength];
		}
		// We have to read the rest, otherwise we will mess up the connection
		if (!con->receive(buffer, req->contentLength, 30000)) {
			delete [] buffer;
			delete(req);
			return NULL;
		}

		if (!req->processContent(buffer, req->contentLength)) {
			delete [] buffer;
			delete(req);
			return NULL;
		}
	}

	delete [] buffer;
	return req;
}



/////////////////////////////////////////////////////////////
// HTTP Client Protocol
/////////////////////////////////////////////////////////////

bool HTTPClientProtocol::CheckBufferForCompatibility(const char* buffer, uint32 length) {
	return false;
}

bool HTTPClientProtocol::InitialiseConversation(NetworkConnection* con) {
	return false;
}

bool HTTPClientProtocol::SendHTTPReply(NetworkConnection* con, HTTPReply* reply) {
	return false;
}

HTTPReply* HTTPClientProtocol::ReceiveHTTPReply(NetworkConnection* con, uint32 timeout) {
	return NULL;
}

bool HTTPClientProtocol::SendHTTPRequest(NetworkConnection* con, HTTPRequest* req) {
	return false;
}

HTTPRequest* HTTPClientProtocol::ReceiveHTTPRequest(NetworkConnection* con, uint32 timeout) {
	return NULL;
}



/////////////////////////////////////////////////////////////
// Message Protocol
/////////////////////////////////////////////////////////////

bool MessageProtocol::CheckBufferForCompatibility(const char* buffer, uint32 length) {
	if (length < 2*sizeof(uint32))
		return false;
	return (GetObjID(buffer) == DATAMESSAGEID);
}

bool MessageProtocol::InitialiseConversation(NetworkConnection* con) {
	return true;
}

bool MessageProtocol::SendMessage(NetworkConnection* con, DataMessage* msg, uint64 receiver) {
	if (!con || !msg)
		return false;
//	uint64 addr = con->getRemoteAddress();
//	LogPrint(0,0,0,"Protocol sending msg %llu to %u.%u.%u.%u:%u", msg->getType(), GETIPADDRESSQUAD(addr), GETIPPORT(addr));
//	LogPrint(0,0,0,"Protocol sending msg: %u", msg->getType()[15]);
	return con->send((char*)msg->data, msg->getSize(), receiver);
}

DataMessage* MessageProtocol::ReceiveMessage(NetworkConnection* con, uint32 timeout) {
//	uint64 t = GetTimeNow();
	if (!con) {
//	if (!con || !con->waitForDataToRead(timeout)) {
//		printf("Failed ReceiveMessage:      %ld\n", GetTimeAgeMS(t));
		return NULL;
	}

	uint32 maxSize = 1024;
	char* buffer = (char*) malloc(maxSize);
	if (!con->receive(buffer, 2*sizeof(uint32), timeout, true)) {
		free(buffer);
		return NULL;
	}
//	printf("ReceiveMessage:      %ld\n", GetTimeAgeMS(t));
	if (GetObjID(buffer) != DATAMESSAGEID) {
		free(buffer);
		return NULL;
	}
	uint32 size = *(uint32*)buffer;
	if (size > maxSize) {
		free(buffer);
		buffer = (char*) malloc(size);
	}
	if (!con->receive(buffer, size, timeout)) {
		free(buffer);
		return NULL;
	}
//	printf("ReceiveMessage 2:    %ld\n", GetTimeAgeMS(t));
	// Give data to the new message object
	DataMessage* msg = new DataMessage(buffer);
//	uint64 addr = con->getRemoteAddress();
//	LogPrint(0,0,0,"Protocol received msg %llu from %u.%u.%u.%u:%u", msg->getType(), GETIPADDRESSQUAD(addr), GETIPPORT(addr));
	//printf("Protocol received msg: %u.%u.%u (%p)...\n", msg->getType()[0], msg->getType()[1], msg->getType()[2], msg);
	return msg;
}


/////////////////////////////////////////////////////////////
// Telnet Protocol
/////////////////////////////////////////////////////////////

bool TelnetProtocol::CheckBufferForCompatibility(const char* buffer, uint32 length) {
	if (length < 1)
		return false;
	return ((buffer[0] == 13) || (buffer[0] == 10));
}

bool TelnetProtocol::InitialiseConversation(NetworkConnection* con) {
	return true;
}

bool TelnetProtocol::SendTelnetLine(NetworkConnection* con, TelnetLine* line) {
	if (!con || !line)
		return false;
	return con->send((char*)line->data, line->getSize());
}

TelnetLine* TelnetProtocol::ReceiveTelnetLine(NetworkConnection* con, uint32 timeout) {
	if (!con || !con->waitForDataToRead(timeout))
		return NULL;

	uint32 maxSize = 1024, size;
	char* buffer = new char[maxSize];
	TelnetLine* line = NULL;

	uint64 start = GetTimeNow();
	uint32 wait = 0, n;

	while (GetTimeAgeMS(start) < (int32)timeout) {
		if (!con->receiveAvailable(buffer, size, maxSize, wait, true)) {
			delete [] buffer;
			return NULL;
		}
		for ( n=0; n<size; n++) {
			if (buffer[n] == 13) {
				line = new TelnetLine(con->getRemoteAddress());
				line->setCR(TELNET_WINDOWS);
				line->setLine(buffer, n);
				if ( (n < size-1) && (buffer[n+1] == 10) )
					n++;
				con->discard(n+1);
				delete [] buffer;
			//	printf("\n");
				return line;
			}
			else if (buffer[n] == 10) {
				line = new TelnetLine(con->getRemoteAddress());
				line->setCR(TELNET_UNIX);
				line->setLine(buffer, n);
				if ( (n < size-1) && (buffer[n+1] == 13) )
					n++;
				con->discard(n+1);
				delete [] buffer;
			//	printf("\n");
				return line;
			}
			//else
			//	printf("[%c]", buffer[n]);
		}
		if (size == maxSize) {
			delete [] buffer;
			maxSize *= 4;
			buffer = new char[maxSize];
			wait = 0;
		}
		else
			wait = 20;
	}
	delete [] buffer;
	return NULL;
}

TelnetLine* TelnetProtocol::ReceiveTelnetLine(NetworkConnection* con, uint32 timeout, uint32 size) {
	if (!con || !con->waitForDataToRead(timeout))
		return NULL;

	char* buffer = new char[size];

	if (!con->receive(buffer, size, timeout)) {
		delete [] buffer;
		return NULL;
	}

	TelnetLine* reply = new TelnetLine(con->getRemoteAddress());
	reply->data = buffer;
	reply->size = size;
	return reply;
}




}
