/////////////////////////////////////////////////////////////////////
// ITBLibrary.cpp

#include <stdafx.h>
#include "blocksock.h"
#include "utility.h"
#include "ITBLibrary.h"
#include "PictureSet.h"
#include "PageSet.h"
#include "UsrPicNoteQuery.h"
#include "UsrPicNoteSet.h"
#include "UsersSet.h"
#include "UsrHighTextQuery.h"
#include "UsrHighTextSet.h"
#include "CommonConst.h"
#include "TextLineParser.h"

char hdrFmt[] = "HTTP/1.0 200 OK\r\n"
	"Server: ITB Library\r\n"
	"Date: %s\r\n"
	"Content-Type: text/html\r\n"
	"Accept-Ranges: bytes\r\n"
	"Content-Length: %d\r\n\r\n";	// double \r\n or single and strcat(g_headers, "\r\n"); every time

char hdrErr[] = "HTTP/1.0 404 Object Not Found\r\n"
	"Server: ITB Library\r\n"
	"Content-Type: text/html\r\n"
	"Accept-Ranges: bytes\r\n"
	"Content-Length: 66\r\n\r\n" // WinInet wants correct length
	"<html><h1><body>HTTP/1.0 404 Object Not Found</h1></body></html>\r\n";

char g_headers[500];
extern CString g_strIndexPage;

__declspec(thread) CString* g_pStrGmtNow = NULL;

CDaoDatabase g_library;

bool ITB_ParseCGILine(char* p, ITBDATA& data)
{
	//e.g. UserName=xxx&Password=yyy&Page=zzz&Index=wwww

	char *var, *val;

	do {
		var = p;
		val = strchr(var, '=');
		if (!val) return false;
		
		*val++ = '\0';
		p = strchr(val, '&');
		if (p) *p++ = '\0';

		if (strcmp(var, "UserName") == 0)
			data.strUser = val;
		
		else if (strcmp(var, "Password") == 0)
			data.strPassword = val;

		else if (strcmp(var, "Book") == 0)
			data.nBook = atoi(val);

		else if (strcmp(var, "Page") == 0)
			data.nPage = atoi(val);

		else if (strcmp(var, "Index") == 0)
			data.nIndex = atoi(val);

	} while(p);

	return true;
}

bool ITB_ReadPostedData(CString& text, char* pRequestBuffer, CHttpBlockingSocket& sConnect)
{
	char *p, *pText;
	bool bDataRead = false;
	int nLen;

	// It should read all headers to empty the headers section

	do {
		sConnect.ReadHttpHeaderLine(pRequestBuffer, MAXLINELENGTH, 10);

		p = strchr(pRequestBuffer, ':');
		if(p != NULL && bDataRead == false) {
			*p++ = '\0';
			if (strcmp(pRequestBuffer, "X-Text") == 0)
			{
				bDataRead = true;
				if ((nLen = atoi(p)) <= 0) 
					continue;

				pText = text.GetBuffer(nLen);
				memcpy(pText, strchr(p, ',') + 1, nLen);
				text.ReleaseBuffer(nLen);
			}
		}
	} while(strcmp(pRequestBuffer, "\r\n"));

	// read the data line sent by the client
	sConnect.ReadHttpHeaderLine(pRequestBuffer, MAXLINELENGTH, 10);
	TRACE("SERVER: POST PARAMETERS = %s\n", pRequestBuffer);
	//LogRequest(pParam, pRequestBuffer, saClient);

	return bDataRead;
}

bool ITB_LibraryProcedure(char* pVerb, char* pFirstLine, char* pRequestBuffer, 
		CHttpBlockingSocket& sConnect, HWND	hWnd, CSockAddr* pSockAddr)
{
	ITBDATA data;
	CString gmt = CTime::GetCurrentTime().FormatGmt("%a, %d %b %Y %H:%M:%S GMT");
	g_pStrGmtNow = &gmt;

	//e.g. /ITBLibrady.dll&FuncName?Param1=xxx&Param2=yyy&...

	char* pFunction = strchr(pFirstLine, '?');
	if (!pFunction) return false;

	*pFunction++ = '\0';
	if (strcmp(pFirstLine, "/ITBLibrary.dll") == 0)
	{
		char* pParam = strchr(pFunction, '?');
		if (!pParam) {
			LogRequest((LPVOID)hWnd, "ITB_ERROR: No function name!!!", *pSockAddr);
			return false;
		}
		
		*pParam++ = '\0';

		if (!ITB_ParseCGILine(pParam, data)) {
			LogRequest((LPVOID)hWnd, "ITB_ERROR: Bad CGI line!!!", *pSockAddr);
			return false;
		}

		if (stricmp(pVerb, "POST") == 0)
			ITB_ReadPostedData(data.strText, pRequestBuffer, sConnect);
		
		/////////////////////////////////////////
		// Choose the correct function
		try {
			if (strcmp(pFunction, "Connect") == 0)
				ITB_Connect(data, sConnect);

			else if (strcmp(pFunction, "GetPage") == 0)
				ITB_GetPage(data, sConnect);
			
			else if (strcmp(pFunction, "GetSmallPicture") == 0)
				ITB_GetSmallPicture(data, sConnect);
			
			else if (strcmp(pFunction, "GetLargePicture") == 0)
				ITB_GetLargePicture(data, sConnect);
			
			else if (strcmp(pFunction, "GetPictureHeaders") == 0)
				ITB_GetPictureHeaders(data, sConnect);
			
			else if (strcmp(pFunction, "GetNote") == 0)
				ITB_GetNote(data, sConnect);
			
			else if (strcmp(pFunction, "AddNote") == 0)
				ITB_AddNote(data, sConnect);

			else if (strcmp(pFunction, "ReplaceNote") == 0)
				ITB_ReplaceNote(data, sConnect);

			else if (strcmp(pFunction, "DeleteNote") == 0)
				ITB_DeleteNote(data, sConnect);

	
			else if (strcmp(pFunction, "GetContents") == 0)
				ITB_GetContents(data, sConnect);

			else if (strcmp(pFunction, "Find") == 0)
				ITB_Find(data, sConnect);

			else if (strcmp(pFunction, "GetIndexWord") == 0)
				ITB_GetIndexWord(data, sConnect);

			else if (strcmp(pFunction, "GetAuthorInfo") == 0)
				ITB_GetAuthorInfo(data, sConnect);

			else if (strcmp(pFunction, "GetBookInfo") == 0)
				ITB_GetBookInfo(data, sConnect);

			else if (strcmp(pFunction, "GetHighlightedText") == 0)
				ITB_GetHighlightedText(data, sConnect);

			else if (strcmp(pFunction, "SetHighlightedText") == 0)
				ITB_SetHighlightedText(data, sConnect);

			else
				return false;
		}
		catch (CDaoException* e) {
			sConnect.Write(hdrErr, strlen(hdrErr), 10);
			e->Delete();
		}
		catch (CMemoryException* e) {
			sConnect.Write(hdrErr, strlen(hdrErr), 10);
			e->Delete();
		}
	}
	else
		return false;

	return true;
}

void ITB_GetPage(ITBDATA& data, CHttpBlockingSocket& sConnect)
{
	CPageSet set(&g_library);


	if (data.nPage == -1) {
		wsprintf(g_headers, hdrFmt, (const char*) *g_pStrGmtNow, 
			strlen(g_strIndexPage));
		sConnect.Write(g_headers, strlen(g_headers), 10);
		sConnect.Write(g_strIndexPage, strlen(g_strIndexPage), 10);
		return;
	}

	set.m_strFilter.Format("[BookID] = %d AND [PageNumber] = %d",
		data.nBook, data.nPage);

	set.Open();

	if (!set.IsEOF())
	{
		wsprintf(g_headers, hdrFmt, (const char*) *g_pStrGmtNow, 
			set.m_strText.GetLength() + 2);
		sConnect.Write(g_headers, strlen(g_headers), 10);
		
		sConnect.Write(set.m_strText, set.m_strText.GetLength(), 10);
		sConnect.Write("\r\n", 2, 10);
	}
	else
		sConnect.Write(hdrErr, strlen(hdrErr), 10);

	set.Close();
}

void ITB_GetSmallPicture(ITBDATA& data, CHttpBlockingSocket& sConnect)
{
	CPictureSet set(&g_library);

	set.m_strFilter.Format("[BookID] = %d AND [PageNumber] = %d AND [PictureNumber] = %d",
		data.nBook, data.nPage, data.nIndex);

	set.Open(IMG_KEY | IMG_SMALLPICTURE | IMG_SMALLINFO | IMG_TYPE);

	if (!set.IsEOF())
	{
		// Just DIBs by now
		if(set.m_byType == 1)
		{
			DWORD dwSize = set.m_smallPicture.GetSize();
			char* pBuffer = (char*)set.m_smallPicture.GetData();

			if (!pBuffer) {
				sConnect.Write(hdrErr, strlen(hdrErr), 10);
				return;
			}

			wsprintf(g_headers, hdrFmt, (const char*) *g_pStrGmtNow, dwSize);
			sConnect.Write(g_headers, strlen(g_headers), 10);

			DWORD dwBytesWrote = 0;
			UINT uBytesToWrite;

			// send file in small chunks (5K) to avoid big memory alloc overhead
			while(dwBytesWrote < dwSize) 
			{
				uBytesToWrite = min(SERVERMAXBUF, dwSize - dwBytesWrote);
				
				sConnect.Write(pBuffer, uBytesToWrite, 10);
				
				pBuffer += uBytesToWrite;
				dwBytesWrote += uBytesToWrite;
			}
		}
		else
			sConnect.Write(hdrErr, strlen(hdrErr), 10);
	}
	else
		sConnect.Write(hdrErr, strlen(hdrErr), 10);

	set.Close();
}

void ITB_GetLargePicture(ITBDATA& data, CHttpBlockingSocket& sConnect)
{
	CPictureSet set(&g_library);

	set.m_strFilter.Format("[BookID] = %d AND [PageNumber] = %d AND [PictureNumber] = %d",
		data.nBook, data.nPage, data.nIndex);

	set.Open(IMG_KEY | IMG_LARGEPICTURE | IMG_LARGEINFO | IMG_TYPE);

	if (!set.IsEOF())
	{
		// Just DIBs by now
		if(set.m_byType == 1)
		{
			DWORD dwSize = set.m_largePicture.GetSize();
			char* pBuffer = (char*)set.m_largePicture.GetData();

			if (!pBuffer) {
				sConnect.Write(hdrErr, strlen(hdrErr), 10);
				return;
			}

			wsprintf(g_headers, hdrFmt, (const char*) *g_pStrGmtNow, dwSize);
			sConnect.Write(g_headers, strlen(g_headers), 10);

			DWORD dwBytesWrote = 0;
			UINT uBytesToWrite;

			// send file in small chunks (5K) to avoid big memory alloc overhead
			while(dwBytesWrote < dwSize) 
			{
				uBytesToWrite = min(SERVERMAXBUF, dwSize - dwBytesWrote);
				
				sConnect.Write(pBuffer, uBytesToWrite, 10);
				
				pBuffer += uBytesToWrite;
				dwBytesWrote += uBytesToWrite;
			}
		}
		else
			sConnect.Write(hdrErr, strlen(hdrErr), 10);
	}
	else
		sConnect.Write(hdrErr, strlen(hdrErr), 10);

	set.Close();
}

void ITB_GetPictureHeaders(ITBDATA& data, CHttpBlockingSocket& sConnect)
{
	CPictureSet set(&g_library);
	CUsrPicNoteQuery qry(&g_library);
	CString strLine, strPH;
	bool bNote;

	qry.m_strUsr = data.strUser;
	qry.m_strPsw = data.strPassword;
	qry.m_lBkID = data.nBook;
	qry.m_lPagNum = data.nPage;

	try {
		qry.Open();

		set.m_strFilter.Format("[BookID] = %d AND [PageNumber] = %d",
			data.nBook, data.nPage);

		//set.m_strSort = "[ImageNumber]";

		set.Open(IMG_KEY | IMG_NAME | IMG_TYPE |
			IMG_SMALLINFO | IMG_LARGEINFO);

		while (!set.IsEOF())
		{
			if (!qry.IsEOF() && qry.m_lPictureNumber == set.m_lPictureNumber) {
				bNote = true;
				qry.MoveNext();
			}
			else 
				bNote = false;

			strLine.Format("%s,%s,%c,%d,%d,%d,%d,%d,%d\r\n", 
				(LPCTSTR)set.m_strName,
				"DIB", 
				(bNote) ? 'Y':'N', 
				set.m_lSmallSize, 
				set.m_lLargeSize, 
				set.m_lSmallWidth,  
				set.m_lSmallHeight, 
				set.m_lLargeWidth, 
				set.m_lLargeHeight);

			strPH += strLine;
			
			set.MoveNext();
		}
		
		set.Close();
	}
	catch (CDaoException* e) {
		sConnect.Write(hdrErr, strlen(hdrErr), 10);
		e->Delete();
	}
	catch (CMemoryException* e) {
		sConnect.Write(hdrErr, strlen(hdrErr), 10);
		e->Delete();
	}

	wsprintf(g_headers, hdrFmt, (const char*) *g_pStrGmtNow, 
		strPH.GetLength() + 2);

	sConnect.Write(g_headers, strlen(g_headers), 10);
	sConnect.Write(strPH, strPH.GetLength(), 10);
	sConnect.Write("\r\n", 2, 10);
}

/*void ITB_GetPictureHeaders(ITBDATA& data, CHttpBlockingSocket& sConnect)
{
	CPictureSet set(&g_library);
	CUsrPicNoteQuery qry(&g_library);
	CString line;
	bool bNote;

	qry.m_strUsr = data.strUser;
	qry.m_strPsw = data.strPassword;
	qry.m_lBkID = data.nBook;
	qry.m_lPagNum = data.nPage;

	try {
		qry.Open();

		set.m_strFilter.Format("[BookID] = %d AND [PageNumber] = %d",
			data.nBook, data.nPage);

		//set.m_strSort = "[ImageNumber]";

		set.Open(IMG_KEY | IMG_NAME | IMG_TYPE |
			IMG_SMALLINFO | IMG_LARGEINFO);

		wsprintf(g_headers, hdrFmt, (const char*) *g_pStrGmtNow, 0);

		sConnect.Write(g_headers, strlen(g_headers), 10);
		
		while (!set.IsEOF())
		{
			if (!qry.IsEOF() && qry.m_lPictureNumber == set.m_lPictureNumber) {
				bNote = true;
				qry.MoveNext();
			}
			else 
				bNote = false;

			line.Format("%s,%s,%c,%d,%d,%d,%d,%d,%d\r\n", 
				(LPCTSTR)set.m_strName,
				"DIB", 
				(bNote) ? 'Y':'N', 
				set.m_lSmallSize, 
				set.m_lLargeSize, 
				set.m_lSmallWidth,  
				set.m_lSmallHeight, 
				set.m_lLargeWidth, 
				set.m_lLargeHeight);

			sConnect.Write(line, line.GetLength(), 10);
			set.MoveNext();
		}
		sConnect.Write("\r\n", 2, 10);
		
		set.Close();
	}
	catch (CDaoException* e) {
		sConnect.Write(hdrErr, strlen(hdrErr), 10);
		e->Delete();
	}
	catch (CMemoryException* e) {
		sConnect.Write(hdrErr, strlen(hdrErr), 10);
		e->Delete();
	}
}*/
/*void ITB_GetPictureHeaders(ITBDATA& data, CHttpBlockingSocket& sConnect)
{
	CString str("Hola Mundo\r\n");
	wsprintf(g_headers, hdrFmt, (const char*) *g_pStrGmtNow, 
		str.GetLength() + 2);
	sConnect.Write(g_headers, strlen(g_headers), 10);
		
	sConnect.Write(str, str.GetLength(), 10);
	sConnect.Write("\r\n", 2, 10);
}*/

void ITB_GetNote(ITBDATA& data, CHttpBlockingSocket& sConnect)
{
	CUsersSet		users(&g_library);
	CUsrPicNoteSet	upn(&g_library);

	users.m_strFilter.Format("[UserName] = \"%s\" AND [Password] = \"%s\"",
		(LPCTSTR)data.strUser, (LPCTSTR)data.strPassword);

	try {
		users.Open();
		
		if (users.IsEOF()) {
			sConnect.Write(hdrErr, strlen(hdrErr), 10);
			return;
		}

		upn.m_strFilter.Format("[UserID] = %d AND [BookID] = %d AND [PageNumber] = %d AND [PictureNumber] = %d",
			users.m_lUserID, data.nBook, data.nPage, data.nIndex);
		
		upn.Open();

		if (upn.IsEOF()) {
			sConnect.Write(hdrErr, strlen(hdrErr), 10);
		}
		else {
			upn.m_strNote += "\r\n\r\n";

			wsprintf(g_headers, hdrFmt, (const char*) *g_pStrGmtNow, upn.m_strNote.GetLength());
			sConnect.Write(g_headers, strlen(g_headers), 10);

			sConnect.Write(upn.m_strNote, upn.m_strNote.GetLength(), 10);
		}

		users.Close();
		upn.Close();
	}
	catch (CDaoException* e) {
		sConnect.Write(hdrErr, strlen(hdrErr), 10);
		e->Delete();
	}
	catch (CMemoryException* e) {
		sConnect.Write(hdrErr, strlen(hdrErr), 10);
		e->Delete();
	}
}

void ITB_AddNote(ITBDATA& data, CHttpBlockingSocket& sConnect)
{
	CUsersSet		users(&g_library);
	CUsrPicNoteSet  upn(&g_library);

	users.m_strFilter.Format("[UserName] = \"%s\" AND [Password] = \"%s\"",
		(LPCTSTR)data.strUser, (LPCTSTR)data.strPassword);

	try {
		users.Open();
		
		if (users.IsEOF()) {
			sConnect.Write(hdrErr, strlen(hdrErr), 10);
		}
		else {
			upn.Open();

			upn.AddNew();

			upn.m_lUserID = users.m_lUserID;
			upn.m_lBookID = data.nBook;
			upn.m_lPageNumber = data.nPage;
			upn.m_lPictureNumber = data.nIndex;
			upn.m_strNote = data.strText;

			upn.Update();

			wsprintf(g_headers, hdrFmt, (const char*) *g_pStrGmtNow, 0);
			sConnect.Write(g_headers, strlen(g_headers), 10);
		}

		users.Close();
		upn.Close();
	}
	catch (CDaoException* e) {
		sConnect.Write(hdrErr, strlen(hdrErr), 10);
		e->Delete();
	}
	catch (CMemoryException* e) {
		sConnect.Write(hdrErr, strlen(hdrErr), 10);
		e->Delete();
	}
}

void ITB_ReplaceNote(ITBDATA& data, CHttpBlockingSocket& sConnect)
{
	CUsersSet		users(&g_library);
	CUsrPicNoteSet	upn(&g_library);

	users.m_strFilter.Format("[UserName] = \"%s\" AND [Password] = \"%s\"",
		(LPCTSTR)data.strUser, (LPCTSTR)data.strPassword);

	try {
		users.Open();
		
		if (users.IsEOF()) {
			sConnect.Write(hdrErr, strlen(hdrErr), 10);
			return;
		}

		upn.m_strFilter.Format("[UserID] = %d AND [BookID] = %d AND [PageNumber] = %d AND [PictureNumber] = %d",
			users.m_lUserID, data.nBook, data.nPage, data.nIndex);
		
		upn.Open();

		if (upn.IsEOF()) {
			sConnect.Write(hdrErr, strlen(hdrErr), 10);
		}
		else {
			upn.Edit();

			upn.m_strNote = data.strText;

			upn.Update();

			wsprintf(g_headers, hdrFmt, (const char*) *g_pStrGmtNow, 0);
			sConnect.Write(g_headers, strlen(g_headers), 10);
		}

		users.Close();
		upn.Close();
	}
	catch (CDaoException* e) {
		sConnect.Write(hdrErr, strlen(hdrErr), 10);
		e->Delete();
	}
	catch (CMemoryException* e) {
		sConnect.Write(hdrErr, strlen(hdrErr), 10);
		e->Delete();
	}
}

void ITB_DeleteNote(ITBDATA& data, CHttpBlockingSocket& sConnect)
{
	CUsersSet		users(&g_library);
	CUsrPicNoteSet	upn(&g_library);

	users.m_strFilter.Format("[UserName] = \"%s\" AND [Password] = \"%s\"",
		(LPCTSTR)data.strUser, (LPCTSTR)data.strPassword);

	try {
		users.Open();
		
		if (users.IsEOF()) {
			sConnect.Write(hdrErr, strlen(hdrErr), 10);
			return;
		}

		upn.m_strFilter.Format("[UserID] = %d AND [BookID] = %d AND [PageNumber] = %d AND [PictureNumber] = %d",
			users.m_lUserID, data.nBook, data.nPage, data.nIndex);
		
		upn.Open();

		if (upn.IsEOF()) {
			sConnect.Write(hdrErr, strlen(hdrErr), 10);
		}
		else {
			upn.Delete();

			wsprintf(g_headers, hdrFmt, (const char*) *g_pStrGmtNow, 0);
			sConnect.Write(g_headers, strlen(g_headers), 10);
		}

		users.Close();
		upn.Close();
	}
	catch (CDaoException* e) {
		sConnect.Write(hdrErr, strlen(hdrErr), 10);
		e->Delete();
	}
	catch (CMemoryException* e) {
		sConnect.Write(hdrErr, strlen(hdrErr), 10);
		e->Delete();
	}
}

void ITB_Connect(ITBDATA& data, CHttpBlockingSocket& sConnect)
{
	CUsersSet	users(&g_library);

	users.m_strFilter.Format("[UserName] = \"%s\" AND [Password] = \"%s\"",
		(LPCTSTR)data.strUser, (LPCTSTR)data.strPassword);

	users.Open();
		
	if (users.IsEOF()) 
		sConnect.Write(hdrErr, strlen(hdrErr), 10);
	else {
		wsprintf(g_headers, hdrFmt, (const char*) *g_pStrGmtNow, 0);
		sConnect.Write(g_headers, strlen(g_headers), 10);
	}
	
	users.Close();
}

void ITB_GetContents(ITBDATA& data, CHttpBlockingSocket& sConnect)
{
}

void ITB_Find(ITBDATA& data, CHttpBlockingSocket& sConnect)
{
}

void ITB_GetIndexWord(ITBDATA& data, CHttpBlockingSocket& sConnect)
{
}

void ITB_GetAuthorInfo(ITBDATA& data, CHttpBlockingSocket& sConnect)
{
}

void ITB_GetBookInfo(ITBDATA& data, CHttpBlockingSocket& sConnect)
{
	CPageSet pages(&g_library);
	CString  strAnswer;

	try {
		pages.Open();
		pages.MoveLast();

		strAnswer.Format("%d\r\n\r\n", pages.GetRecordCount());

		wsprintf(g_headers, hdrFmt, (const char*) *g_pStrGmtNow, strAnswer.GetLength());
		sConnect.Write(g_headers, strlen(g_headers), 10);

		sConnect.Write(strAnswer, strAnswer.GetLength(), 10);

		pages.Close();
	}
	catch (CDaoException* e) {
		sConnect.Write(hdrErr, strlen(hdrErr), 10);
		e->Delete();
	}
	catch (CMemoryException* e) {
		sConnect.Write(hdrErr, strlen(hdrErr), 10);
		e->Delete();
	}
}

void ITB_GetHighlightedText(ITBDATA& data, CHttpBlockingSocket& sConnect)
{
	CUsrHighTextQuery qry(&g_library);
	CString strLine, strPH;

	qry.m_strUsr = data.strUser;
	qry.m_strPsw = data.strPassword;
	qry.m_lBkID = data.nBook;
	qry.m_lPagNum = data.nPage;

	try {
		qry.Open();

		while (!qry.IsEOF())
		{
			strLine.Format("%d,%d,%s\r\n", 
				qry.m_nFirstChar, qry.m_nLastChar, (LPCTSTR)qry.m_strNote);

			strPH += strLine;
			
			qry.MoveNext();
		}
		
		qry.Close();
	}
	catch (CDaoException* e) {
		sConnect.Write(hdrErr, strlen(hdrErr), 10);
		e->Delete();
	}
	catch (CMemoryException* e) {
		sConnect.Write(hdrErr, strlen(hdrErr), 10);
		e->Delete();
	}

	wsprintf(g_headers, hdrFmt, (const char*) *g_pStrGmtNow, 
		strPH.GetLength() + 2);

	sConnect.Write(g_headers, strlen(g_headers), 10);
	sConnect.Write(strPH, strPH.GetLength(), 10);
	sConnect.Write("\r\n", 2, 10);
}

void ITB_SetHighlightedText(ITBDATA& data, CHttpBlockingSocket& sConnect)
{
	CUsersSet		users(&g_library);
	CUsrHighTextSet uht(&g_library);
	CTextLineParser parser;
	TLP_FORMAT pFormat[] = {TLP_INT, TLP_INT, TLP_ZSTR};
	TLP_TYPE1 parms;
	char* p = data.strText.GetBuffer(0);

	parser.SetFormat(pFormat, sizeof(pFormat) / sizeof(TLP_FORMAT), ',');

	users.m_strFilter.Format("[UserName] = \"%s\" AND [Password] = \"%s\"",
		(LPCTSTR)data.strUser, (LPCTSTR)data.strPassword);

	try {
		users.Open();
		
		if (users.IsEOF()) {
			sConnect.Write(hdrErr, strlen(hdrErr), 10);
			return;
		}

		uht.m_strFilter.Format("[UserID] = %d AND [BookID] = %d AND [PageNumber] = %d",
			users.m_lUserID, data.nBook, data.nPage);
		
		uht.Open();

		// Delete all page's highlighted text
		while (!uht.IsEOF()) {
			uht.Delete();
			uht.MoveNext();
		}

		// Add new high. text
		
		while(p) {
			if(parser.ParseTextLine(&p, &parms)) {
				uht.AddNew();	// It resets data members

				uht.m_nUserID = users.m_lUserID;
				uht.m_nBookID = data.nBook;
				uht.m_nPageNumber = data.nPage;
				uht.m_nFirstChar = parms.nParam1;
				uht.m_nLastChar = parms.nParam2;
				uht.m_strNote = parms.szParam3;

				uht.Update();
			}
		}
		
		wsprintf(g_headers, hdrFmt, (const char*) *g_pStrGmtNow, 0);
		sConnect.Write(g_headers, strlen(g_headers), 10);

		users.Close();
		uht.Close();
	}
	catch (CDaoException* e) {
		sConnect.Write(hdrErr, strlen(hdrErr), 10);
		e->Delete();
	}
	catch (CMemoryException* e) {
		sConnect.Write(hdrErr, strlen(hdrErr), 10);
		e->Delete();
	}

	data.strText.ReleaseBuffer();
}
