// ServerThread.cpp : implementation file
//

#include "stdafx.h"
#include "HttpSSL.h"
#include "ServerThread.h"


#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

enum method
{
	GET,
	HEAD,
	POST,
	PUT,
	PATCH,
	COPY,
	MOVE,
	DEL,
	LINK,
	UNLINK,
	OPTION,
	UNDEFINED
};

extern SSL_CTX* ctx;
extern void context_init();
extern void set_options();
extern void context_free();

typedef struct req
{
	enum method methtype;
	char url[1024];
} REQUEST;

struct FieldData
{
	char field[2][1024];
	char data[2][1024];
};


// Case insensitive version of strstr
char *stristr(const char *string, const char *strCharSet)
{
	if(string == NULL || strCharSet == NULL)
		return(NULL);

	const unsigned int stringLen  = strlen(string);
	const unsigned int charSetLen = strlen(strCharSet);
	
	char *buf = new char[stringLen + 1];
	if(buf == NULL)
		return(NULL);
	
	char *tmpCharSet = new char[charSetLen + 1];
	if(tmpCharSet == NULL)
	{	
		delete [] buf;
		return(NULL);
	}
	
	buf[stringLen] = '\0';
	tmpCharSet[charSetLen] = '\0';

	// Convert both buffers to all lower case, then run normal strstr against these.
	for(unsigned int i=0; i<stringLen; i++)
		buf[i] = (char)tolower(string[i]);

	for(i=0; i<charSetLen; i++)
		tmpCharSet[i] = (char)tolower(strCharSet[i]);

	char *retval = strstr(buf, tmpCharSet);

	// Repoint retval from the temporary buf to the original string.
	if(retval != NULL)
		retval = (char*)string + (retval - buf);

	delete buf;
	delete tmpCharSet;

	return(retval);
}

// Replace all %<HEX><HEX> escape values with their decimal equivalent
void ConvertEscapeCharacters(const char *src, char *dst, const unsigned int dstLen, bool replacePlusWithSpace)
{
	if(src == NULL || *src == '\0' || dst == NULL || dstLen == 0)
		return;

	memset(dst, 0, dstLen);

	// If there is nothing to convert, just copy and return
	if(strchr(src, '%') == NULL)
	{
		strcpy(dst, src);
		return;
	}

	unsigned int dstPos = 0;
	
	for(unsigned int i=0; i<dstLen; i++)
	{
		if(src[i] == '\0')
			break;

		if(src[i] != '%')
		{
			if(replacePlusWithSpace && src[i] == '+')
				dst[dstPos++] = ' ';
			else
				dst[dstPos++] = src[i];
		}
		else
		{
			if(i + 2 > dstLen)
				break;

			if(IsHex(src[i+1]) && IsHex(src[i+2]))
			{
				char c[3];
				c[2] = '\0';
				sprintf(c, "%c%c", src[i+1], src[i+2]);

				char *stopstring;
				unsigned long ul = strtoul(c, &stopstring, 16);

				dst[dstPos++] = (char)ul;
				i+=2;
			}
		}
	}
}

bool IsHex(char c)
{
	char hex = (char)tolower(c);
	return((hex >= '0' && hex <= '9') || (hex >= 'a' && hex <= 'f'));
}

/////////////////////////////////////////////////////////////////////////////
// CServerThread

IMPLEMENT_DYNCREATE(CServerThread, CWinThread)

CServerThread::CServerThread()
{
}

CServerThread::~CServerThread()
{
}

BOOL CServerThread::InitInstance()
{
	SSL_library_init();
	SSL_load_error_strings();
	set_options();
	context_init();
	CRYPTO_malloc_debug_init();
	CRYPTO_dbg_set_options(V_CRYPTO_MDEBUG_ALL);
	CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);

	m_Socket = 0;
	m_Quit = FALSE;
	m_Socket = socket(AF_INET,SOCK_STREAM,0);
	if (m_Socket == INVALID_SOCKET)
	{
		int err;
		
		err = WSAGetLastError();
		if (err == WSANOTINITIALISED)
		{
			AfxMessageBox("Communications was initialized unsuccessfully");
			AfxGetApp()->PostThreadMessage(WM_QUIT,0,0);
			ERR_free_strings();
			ERR_remove_state(0);
			RAND_cleanup();
			EVP_cleanup();
//			context_free();
			return FALSE;
		}
		else
		{
			AfxMessageBox("Cannot create socket!");
			AfxGetApp()->PostThreadMessage(WM_QUIT,0,0);
			RAND_cleanup();
			ERR_free_strings();
			ERR_remove_state(0);
			EVP_cleanup();
			context_free();
//			return FALSE;
		}
	}

	
	setsockopt(m_Socket,SOL_SOCKET,SO_DONTLINGER,"1",sizeof(int));
	setsockopt(m_Socket,SOL_SOCKET,SO_REUSEADDR,"1",sizeof(int));

	sockaddr_in sockr;
	char buf[1024];
	hostent *h;
	
	gethostname(buf,1024);
	h = gethostbyname(buf);

	sockr.sin_family = AF_INET;
	sockr.sin_port = htons(((CHttpSSLApp*)AfxGetApp())->m_iPort);
	sockr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);//ntohl(inet_addr(h->h_addr));
	if ((bind(m_Socket,(LPSOCKADDR)&sockr,sizeof(sockr))) == SOCKET_ERROR)
	{
		sprintf(buf,"Bind Failed, error %d",WSAGetLastError());
		AfxMessageBox(buf);
		AfxGetApp()->PostThreadMessage(WM_QUIT,0,0);
		ERR_free_strings();
		ERR_remove_state(0);
		RAND_cleanup();
		EVP_cleanup();
		context_free();
		return FALSE;
	}
	return TRUE;
}

int CServerThread::ExitInstance()
{
	if (m_Socket != INVALID_SOCKET)
		closesocket(m_Socket);
	ERR_free_strings();
	ERR_remove_state(0);
	RAND_cleanup();
	EVP_cleanup(); 
	context_free();

	FILE *fp;
	fp = fopen("memleak.txt","w");

	CRYPTO_mem_leaks_fp(fp);
	fclose(fp);

	return CWinThread::ExitInstance();
}

BEGIN_MESSAGE_MAP(CServerThread, CWinThread)
	//{{AFX_MSG_MAP(CServerThread)
		// NOTE - the ClassWizard will add and remove mapping macros here.
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CServerThread message handlers

void CServerThread::Close()
{

	::PostQuitMessage(0);
}

int CServerThread::Run()
{
	CString text, timestring;
	time_t t = 0;
	int ret = 0;
	CListBox *lb = (CListBox*)GetMainWnd()->GetDlgItem(IDC_STATUS_LIST);


	t = time(NULL);
	timestring.Format("%s",ctime(&t));
	timestring.TrimRight("\n\r");

	if  ((ret = listen(m_Socket,5)) != SOCKET_ERROR)
	{
		text.Format("(%s)Listening on port %d...",(LPCTSTR)timestring,
			((CHttpSSLApp*)AfxGetApp())->m_iPort);
		lb->AddString((LPCTSTR)text);
	}
	else
	{
		text.Format("(%s)Listen Failed on Port %d.",(LPCTSTR)timestring,
			((CHttpSSLApp*)AfxGetApp())->m_iPort);
		lb->AddString((LPCTSTR)text);
	}


	while(1)
	{
		SSL *ssl = NULL;
		SOCKET connecting = 0;
		SOCKADDR cli;
		int len = sizeof(cli);
		unsigned long val = 1;

		memset(&cli,0,sizeof(SOCKADDR));
		connecting = accept(m_Socket, (struct sockaddr*)&cli, &len);

		if (ctx)
			ssl = SSL_new(ctx);
		else
		{
			return ExitInstance();
		}
		
		if (ssl == NULL)
		{
			char buf[400];
			CString tmp;

			memset(buf,0,400);

			ERR_error_string(SSL_get_error(ssl,ret),buf);
			tmp = "Cannot create New SSL instance. ";
			tmp += buf;
			AfxMessageBox((LPCTSTR)tmp);
			//::PostQuitMessage(1);
			AfxGetApp()->PostThreadMessage(WM_QUIT,0,0);
			if (m_Socket != INVALID_SOCKET)
				closesocket(m_Socket);
			ERR_free_strings();
			ERR_remove_state(0);
			RAND_cleanup();
			EVP_cleanup();
//			context_free();
			return 0;
		}

		SSL_set_fd(ssl,connecting);
		do{
			ret = SSL_accept(ssl);
		}while(ret == 0);

		if(ret == 1)
		{
			t = time(NULL);
			timestring.Format("%s",ctime(&t));
			timestring.TrimRight("\n\r");

			text.Format("(%s)Receiving connection.",timestring);
			lb->AddString((LPCTSTR)text);
			HttpThread(ssl);
		}
		else
		{
			char buf[400];
			CString tmp;
			FILE *fp;

			fp = fopen("errors.txt","a");

			ERR_error_string(SSL_get_error(ssl,ret),buf);
			tmp = "Accept failed. ";
			tmp += buf;
			fprintf(fp,(LPCTSTR)tmp);
			fclose(fp);
			AfxMessageBox((LPCTSTR)tmp);
			//::PostQuitMessage(1);
			AfxGetApp()->PostThreadMessage(WM_QUIT,0,0);
			ERR_free_strings();
			ERR_remove_state(0);
			RAND_cleanup();
			EVP_cleanup();
//			context_free();
			if (ssl)
				SSL_free(ssl);
			return 0;
		}
	}

	return ExitInstance();
	
}

UINT HttpThread(SSL* ssl)
{
	char buf[4095];
	//SSL* ssl= (SSL*)pParam;
	REQUEST rq_head;
	char file[MAX_PATH];
	int ret;
	struct FieldData fielddata;
//	_CrtMemState s1, s2, s3;

//	_CrtMemCheckpoint( &s1 );
//	_CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_DEBUG );
//	_CrtMemCheckpoint( &s2 );
//	if ( _CrtMemDifference( &s3, &s1, &s2 ) )
//		_CrtMemDumpStatistics( &s3 );

	ret = SSL_read(ssl,buf,4095);
	if (ret == 0)
	{
		SSL_shutdown(ssl);
		closesocket(SSL_get_fd(ssl));
		ERR_free_strings();
		ERR_remove_state(0);
		RAND_cleanup();
		EVP_cleanup();
		SSL_free(ssl);
		return 0;	
	}
	buf[ret] = '\0';

	rq_head.methtype = getmethtype(buf);
	geturl(buf,rq_head.url);

	sprintf(file,"%s%s",(LPCTSTR)((CHttpSSLApp*)AfxGetApp())->m_cszHtmlDir,rq_head.url);

	if (rq_head.methtype == UNDEFINED)
		SendInternalServerErrorMessage(ssl);
	if (rq_head.methtype == GET)
		SendFile(ssl,file,rq_head.url,NULL);
	else if (rq_head.methtype == POST)
		if (!ParsePost(ssl,buf,&fielddata))
			SendInternalServerErrorMessage(ssl);
		else
			HandlePostRequest(ssl,rq_head.url,fielddata);
	else
		SendNotSupportedMessage(ssl);
	SSL_shutdown(ssl);
	closesocket(SSL_get_fd(ssl));
	ERR_free_strings();
	ERR_remove_state(0);
	EVP_cleanup();
	SSL_free(ssl);
	
	return 0;
}

enum method getmethtype( char *buf )
{
	enum method meth;

	switch(buf[0])
	{
	case 'C':
		if (strstr(buf,"COPY"))
			meth = COPY;
		break;
	case 'D':
		if (strstr(buf,"DEL"))
			meth = DEL;
		break;
	case 'G':
		if (strstr(buf,"GET"))
			meth = GET;
		break;
	case 'H':
		if (strstr(buf,"HEAD"))
			meth = HEAD;
		break;
	case 'L':
		if (strstr(buf,"LINK"))
			meth = LINK;
		break;
	case 'M':
		if (strstr(buf,"MOVE"))
			meth = MOVE;
		break;
	case 'P':
		if (strstr(buf,"PATCH"))
			meth = PATCH;
		if (strstr(buf,"POST"))
			meth = POST;
		if (strstr(buf,"PUT"))
			meth = PUT;
		break;
	case 'O':
		if (strstr(buf,"OPTION"))
			meth = OPTION;
		break;
	case 'U':
		if (strstr(buf,"UNLINK"))
			meth = UNLINK;
		break;
	default:
		meth = UNDEFINED;
		break;
	}

	return meth;
}

void geturl( char * buf, char *url )
{
	char tmp[4095];
	char *token;
	enum method meth;

	if (url == NULL)
		return;
	
	strcpy(tmp,buf);
	token = strtok(tmp," ");
	if ((meth = getmethtype(token)) == UNDEFINED)
	{
		url[0] = '\0';
		return;
	}

	token = strtok(NULL," ");

	strcpy(url,token);
}

void SendHTTPHeader(SSL *ssl, 
					const char *httpResponse, 
					const char *additionalResponseHeader, 
					const char *contentType, 
					const unsigned int msgLen)
{
	const char *Status_Line			= "HTTP/1.0 %s\r\n";
	const char *General_Header_1	= "Date: %a, %d %b %Y %H:%M:%S GMT\r\n";
	const char *General_Header_2	= "Pragma: no-cache\r\n";
	const char *Response_Header_1	= "Server: ScreenDoor Standalone Authentication Daemon\r\n";
	const char *Entity_Header_1		= "Content-Length: %d\r\n";
	const char *Entity_Header_2		= "Content-Type: %s\r\n";

	CString httpHeader;

	// Status Line
	CString data;
	data.Format(Status_Line, httpResponse);
	httpHeader += data;

	// General Header Lines
	time_t ttime;
    time(&ttime);
    tm *gtm = gmtime(&ttime);
	char buffer[256];
	strftime(buffer, 255, General_Header_1, gtm);
	httpHeader += buffer;

	httpHeader += General_Header_2;

	// Response Header Lines
	httpHeader += Response_Header_1;
	if(additionalResponseHeader != NULL && *additionalResponseHeader != '\0')
		httpHeader += additionalResponseHeader;

	// Entity Header Lines
	data.Format(Entity_Header_1, msgLen);
	httpHeader += data;

	data.Format(Entity_Header_2, contentType);
	httpHeader += data;

	httpHeader += "\r\n";

	SSL_write(ssl,(LPCTSTR)httpHeader,httpHeader.GetLength());
}

void SendOKHeader(SSL *ssl, const char *mimeType, const unsigned int msgLen)
{
	SendHTTPHeader(ssl, "200 OK", "", mimeType, msgLen);
}

void SendNotSupportedMessage(SSL *ssl)
{
	CString msg = "<HTML><HEAD><TITLE>501 Not Implemented</TITLE></HEAD><BODY><H2>The requested operation is not supported</H2>\n</BODY></HTML>";
	SendHTTPHeader(ssl, "501 Not Implemented", "", "text/html", msg.GetLength());
	SSL_write(ssl,(LPCTSTR)msg,msg.GetLength());
}

void SendInternalServerErrorMessage(SSL *ssl)
{
	CString msg = "<HTML><HEAD><TITLE>500 Internal Server Error</TITLE></HEAD><BODY><H2>The server encountered an unexpected condition which prevented it from fulfilling the request.</H2>\n</BODY></HTML>";
	SendHTTPHeader(ssl, "500 Internal Server Error", "", "text/html", msg.GetLength());
	SSL_write(ssl,(LPCTSTR)msg,msg.GetLength());
}

void SendNotFoundMessage(SSL *ssl, const char *uri)
{
	CString msg;
	msg.Format("<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD><BODY><H2>Unable to find file %s</H2>\n</BODY></HTML>", uri);
	SendHTTPHeader(ssl, "404 Not Found", "", "text/html", msg.GetLength());
	SSL_write(ssl,(LPCTSTR)msg,msg.GetLength());
}

void GetMimeType(const char *fileExt, CString& mimeType)
{
	if(fileExt == NULL || *fileExt == '\0')
		return;

	char *ext = new char[strlen(fileExt) + 1];
	ASSERT(ext != NULL);
	if(ext == NULL)
		return;

	for(int i=strlen(fileExt); i>=0; i--)
	{
		if(fileExt[i] == '.')
		{
			strcpy(ext, &fileExt[i+1]);
			break;
		}
	}

	if(ext != NULL && *ext == '\0')
		strcpy(ext, (char*)fileExt);

	strcpy(ext, _strlwr((char*)ext));


	if(strcmp(ext, "html") == 0 || strcmp(ext, "htm") == 0)
	{
		mimeType = "text/html";
		delete [] ext;
		return;
	}

	if(strcmp(ext, "txt") == 0)
	{
		mimeType = "text/plain";
		delete [] ext;
		return;
	}
	
	if(strcmp(ext, "gif") == 0)
	{
		mimeType = "image/gif";
		delete [] ext;
		return;
	}

	if(strcmp(ext, "jpeg") == 0 || strcmp(ext, "jpg") == 0 || strcmp(ext, "jpe") == 0)
	{
		mimeType = "image/jpeg";
		delete [] ext;
		return;
	}

	if(strcmp(ext, "png") == 0)
	{
		mimeType = "image/png";
		delete [] ext;
		return;
	}

	if(strcmp(ext, "bin") == 0 || strcmp(ext, "dms") == 0 || strcmp(ext, "lha") == 0 ||
	   strcmp(ext, "lzh") == 0 || strcmp(ext, "exe") == 0 || strcmp(ext, "class") == 0)
	{
		mimeType = "application/octet-stream";
		delete [] ext;
		return;
	}

	if(strcmp(ext, "zip") == 0)
	{
		mimeType = "application/zip";
		delete [] ext;
		return;
	}

	if(strcmp(ext, "doc") == 0)
	{
		mimeType = "application/msword";
		delete [] ext;
		return;
	}

	if(strcmp(ext, "pdf") == 0)
	{
		mimeType = "application/pdf";
		delete [] ext;
		return;
	}

	if(strcmp(ext, "hqx") == 0)
	{
		mimeType = "application/mac-binhex40";
		delete [] ext;
		return;
	}

	if(strcmp(ext, "cpt") == 0)
	{
		mimeType = "application/mac-compactpro";
		delete [] ext;
		return;
	}

	if(strcmp(ext, "ai") == 0 || strcmp(ext, "eps") == 0 || strcmp(ext, "ps") == 0)
	{
		mimeType = "application/postscript";
		delete [] ext;
		return;
	}

	if(strcmp(ext, "ppt") == 0)
	{
		mimeType = "application/powerpoint";
		delete [] ext;
		return;
	}

	if(strcmp(ext, "rtf") == 0)
	{
		mimeType = "application/rtf";
		delete [] ext;
		return;
	}

	if(strcmp(ext, "dcr") == 0 || strcmp(ext, "dir") == 0 || strcmp(ext, "dxr") == 0)
	{
		mimeType = "application/x-director";
		delete [] ext;
		return;
	}

	if(strcmp(ext, "sit") == 0)
	{
		mimeType = "application/x-stuffit";
		delete [] ext;
		return;
	}

	if(strcmp(ext, "tar") == 0)
	{
		mimeType = "application/x-tar";
		delete [] ext;
		return;
	}

	if(strcmp(ext, "au") == 0 || strcmp(ext, "snd") == 0)
	{
		mimeType = "audio/basic";
		delete [] ext;
		return;
	}

	if(strcmp(ext, "mpg") == 0 || strcmp(ext, "mp2") == 0 || strcmp(ext, "mp3") == 0)
	{
		mimeType = "audio/mpeg";
		delete [] ext;
		return;
	}

	if(strcmp(ext, "aif") == 0 || strcmp(ext, "aiff") == 0 || strcmp(ext, "aifc") == 0)
	{
		mimeType = "audio/x-aiff";
		delete [] ext;
		return;
	}

	if(strcmp(ext, "ram") == 0)
	{
		mimeType = "audio/x-pn-realaudio";
		delete [] ext;
		return;
	}

	if(strcmp(ext, "rpm") == 0)
	{
		mimeType = "audio/x-pn-realaudio-plugin";
		delete [] ext;
		return;
	}

	if(strcmp(ext, "ra") == 0)
	{
		mimeType = "audio/x-realaudio";
		delete [] ext;
		return;
	}

	if(strcmp(ext, "wav") == 0)
	{
		mimeType = "audio/x-wav";
		delete [] ext;
		return;
	}

	if(strcmp(ext, "tif") == 0 || strcmp(ext, "tiff") == 0)
	{
		mimeType = "image/tiff";
		delete [] ext;
		return;
	}

	if(strcmp(ext, "mpeg") == 0 || strcmp(ext, "mpg") == 0 || strcmp(ext, "mpe") == 0)
	{
		mimeType = "video/mpeg";
		delete [] ext;
		return;
	}

	if(strcmp(ext, "qt") == 0 || strcmp(ext, "mov") == 0)
	{
		mimeType = "video/quicktime";
		delete [] ext;
		return;
	}

	if(strcmp(ext, "avi") == 0)
	{
		mimeType = "video/x-msvideo ";
		delete [] ext;
		return;
	}


	// Fallthrough
	mimeType = "application/octet-stream";
	delete [] ext;
}

bool SendFile(SSL *ssl, const char *filename, const char *uri, const char *forceMimeType)
{
	if(filename == NULL)
		return(false);

	CString fileName = filename;

	// Truncate filename lengths to MAX_PATH
	if(fileName.GetLength() > MAX_PATH)
	{
		TRACE("Warning - filename %s is longer (%d) than MAX_PATH (%d)...truncating\n", fileName, fileName.GetLength(), MAX_PATH);
		fileName = fileName.Left(MAX_PATH);
	}

	if(GetFileAttributes(fileName) == -1)
	{
		SOCKADDR_IN sockAddr;
		memset(&sockAddr, 0, sizeof(sockAddr));
		int sockAddrLen = sizeof(sockAddr);
		getpeername(SSL_get_fd(ssl),(LPSOCKADDR)&sockAddr, &sockAddrLen);

		CString msg;
		msg.Format("\"FILE NOT FOUND - %s\" (%s)", uri, inet_ntoa(sockAddr.sin_addr));
		SendNotFoundMessage(ssl, uri);
	}
	else
	{
		CFile file;
		if(!file.Open(fileName, CFile::modeRead | CFile::shareDenyNone))
			return(false);

		unsigned int fileSize = file.GetLength();

		CString mimeType;

		if(forceMimeType != NULL && *forceMimeType != '\0')
			mimeType = forceMimeType;
		else
			GetMimeType(fileName, mimeType);

		SendOKHeader(ssl, mimeType, fileSize);

		// For small files (under 50k), buffer the whole file in memory
		if(fileSize < 51200)
		{
			char *fileBuf = new char[fileSize];
			if(fileBuf == NULL)
				return(false);

			memset(fileBuf, 0, sizeof(fileBuf));
			
			unsigned int bytesRead = file.Read(fileBuf, fileSize);
			file.Close();

			if(bytesRead > 0)
			{
				int retval = SSL_write(ssl,fileBuf,bytesRead);
				if(retval != (int)bytesRead)
				{
					delete [] fileBuf;
				}

			}

			delete [] fileBuf;
		}
		else
		{
			const unsigned int bufSize = 32768;
			char *fileBuf = new char[bufSize];
			ASSERT(fileBuf != NULL);
			if(fileBuf == NULL)
				return(false);

			memset(fileBuf, 0, bufSize);
			unsigned int bytesRemaining = fileSize;

			while(bytesRemaining > 0)
			{
				unsigned int bytesRead = file.Read(fileBuf, bufSize);
				if(bytesRead == 0)
					break;
				
				bytesRemaining -= bytesRead;
				int retval = SSL_write(ssl,fileBuf, bytesRead);
				if(retval != (int)bytesRead)
				{
					delete [] fileBuf;
					break;
				}

			}

			file.Close();
			delete [] fileBuf;
		}
	}
	
	return(true);
}

bool ParsePost(SSL *ssl, char *buffer, struct FieldData *fd)
{
	if(buffer == NULL || *buffer == '\0')
	{
		TRACE("buffer == NULL...Will send reset!");
		return(false);
	}

	//OutputDebugString(buffer);

	// Search for the Content-type string, and return false if it isn't found or isn't "application/x-www-form-urlencoded"
	char *ctStr = stristr(buffer, "Content-Type:");
	if(ctStr == NULL || stristr(ctStr, "application/x-www-form-urlencoded") == NULL)
	{
		TRACE("ProcessPost(): Could not find 'application/x-www-form-urlencoded' in Content-type\n");
		TRACE1("buffer is %s",buffer);
		return(false);
	}

	// Extract the Content-length
	char *clStr = stristr(buffer, "Content-length:");
	if(clStr == NULL)
	{
		TRACE("clStr == NULL...Will send reset!");
		return(false);
	}

	clStr += 15;

	char *tmpclStr = _strdup(clStr);
	if(tmpclStr == NULL)
	{
		TRACE("tmpclStr == NULL...Will send reset!");
		return(false);
	}
	char *tok = strtok(tmpclStr," \n\r");
	int contentLen = atoi(tok);

	clStr += strlen(tok);

	free(tmpclStr);

	if(contentLen <= 0)
	{
		TRACE("HandlePOSTRequest(): Content-length <= 0\n");
		return(false);
	}

	// Create a new entityBody string
	int offset = strlen(buffer) - contentLen - 2;
	if(offset < 0)
	{
		TRACE("Offset < 0...Will send reset!");
		return(false);
	}
	char *entityBody = _strdup(((char *)buffer + offset));

	CList<CString, CString&> args;

	// First, break apart the entity body into individual tokens
	char *token = strtok(entityBody, "&\n\r");
	while(token != NULL)
	{
		if(strcmp(token, "\n") == 0 || strcmp(token, "\r") == 0)
			continue;

		CString tmp = token;
		args.AddTail(tmp);
		token = strtok(NULL, "&\n\r");
	}
	
	POSITION pos = args.GetHeadPosition();
	int i=0;
	while(pos != NULL)
	{
		CString foo = args.GetNext(pos);
		char *fooStr = new char[foo.GetLength() + 1];
		if(fooStr == NULL)
		{
			TRACE("fooStr == NULL...Will send Reset");
			free(entityBody);
			return(false);
		}
		strcpy(fooStr, foo);
		fooStr[foo.GetLength()] = '\0';

		int tokenCount = 0;

		char *token = strtok(fooStr, "=");
		while(token != NULL)
		{
			if(tokenCount > 1)
				break;

			int newTokenLen = strlen(token);
			char *newToken = new char[newTokenLen + 1];
			if(newToken != NULL)
			{
				newToken[newTokenLen] = '\0';

				// Replace all of the %<HEX><HEX> escape characters with their decimal equivalents, and also replace all '+'s to spaces
				ConvertEscapeCharacters(token, newToken, newTokenLen + 1, true);

				if(tokenCount++ == 0)
					strcpy(fd->field[i],newToken);
				else
					strcpy(fd->data[i],newToken);

				delete [] newToken;
			}
			token = strtok(NULL, "=");	
		}
		i++;
		delete [] fooStr;
	}

	free(entityBody);
	return(true);

}

void HandlePostRequest(SSL *ssl, char *uri, struct FieldData fd)
{
	if (!strcmp(uri,"/cgi-bin/login"))
	{
		CString msg;
		msg.Format("<HTML><HEAD><TITLE>Post Results</TITLE></HEAD><BODY><H2>The Data you sent is %s is %s    %s is %s</H2>\n</BODY></HTML>",
			fd.field[0],fd.data[0],fd.field[1],fd.data[1]);
		SendOKHeader(ssl,"text/html", msg.GetLength());
		if (SSL_write(ssl,(LPCTSTR)msg,msg.GetLength()) <= 0)
		{
			FILE *fp;

			fp = fopen("posterror.txt","w");

			ERR_print_errors_fp(fp);
			fclose(fp);
		}
	}
	else
	{
		SendInternalServerErrorMessage(ssl);
		return;
	}
}
