// DIMEParser.cpp: implementation of the DIMEParser class.
//
//
//////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2002 Robert Cunnings. All rights reserved.
//
// You may copy, modify, distribute or publish this code free of charge.
//
// NO WARRANTY: This software is provided AS IS, without warranty of any kind.
//
//////////////////////////////////////////////////////////////////////
//
// DIMEParser class - implementation of DIME parser, in accordance
// with Internet-Draft "Direct Internet Message Encapsulation (DIME)"
// (draft-nielson-dime-02 June 17, 2002)
//  
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "DIMEParser.h"
#include "DimeSerializer.h"
#include "DimePayload.h"
#include "charencode.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

DIMEParser::DIMEParser()
{
}

DIMEParser::~DIMEParser()
{
	Clear();
}

void DIMEParser::Clear()
{
	for (unsigned int i = 0; i < m_arrPayloads.size(); i++)
	{
		delete m_arrPayloads[i];
	}
	m_arrPayloads.clear();
}

bool DIMEParser::AddPayload(IStorage *pIStorage,
							const string& strId,
							const string& strType,
							unsigned long ulTNF,
							unsigned char *pOptionData,
							unsigned long ulOptionLength,
							unsigned long ulOptionType)
{
	DIMEPayload *pPayload = new DIMEPayload(pIStorage,
											strId,
											strType,
											ulTNF);
	if (pPayload == NULL)
	{
		return false;
	}
	if (pOptionData != NULL)
	{
		DIMEOption *pOption = new DIMEOption(   ulOptionType,
												ulOptionLength,
												pOptionData);
		if (pOption == NULL)
		{
			delete pPayload;
			return false;
		}
		pPayload->AddOption(pOption);
	}
	m_arrPayloads.push_back(pPayload);
	return true;
}

bool DIMEParser::AddPayload(IStorage *pIStorage,
							const string& strId,
							const string& strType,
							unsigned long ulTNF,
							SAFEARRAY *pOptions)
{
	DIMEPayload *pPayload = new DIMEPayload(pIStorage,
											strId,
											strType,
											ulTNF);
	if (pPayload == NULL)
	{
		return false;
	}
	if (pOptions != NULL)
	{
		void *pData;
		SafeArrayAccessData(pOptions, &pData);
		pPayload->SetOptions((unsigned char *)pData, pOptions->rgsabound[0].cElements);
		SafeArrayUnaccessData(pOptions);
	}
	m_arrPayloads.push_back(pPayload);
	return true;
}

bool DIMEParser::AddPayload(const char *pszBuffer,
							unsigned long ulSize,
							const string& strId,
							const string& strType,
							unsigned long ulTNF,
							unsigned char *pOptionData,
							unsigned long ulOptionLength,
							unsigned long ulOptionType)
{
	IStorage *pIStorage;
	HRESULT hRes = StgCreateDocfile(NULL,
									STGM_DELETEONRELEASE | STGM_DIRECT | STGM_READWRITE| STGM_CREATE | STGM_SHARE_EXCLUSIVE,
									0,
									&pIStorage);
	if (FAILED(hRes))
	{
		return false;
	}
	IStream *pIStream;
	wstring wstrId;
	UTF8ToWideString(strId, wstrId);
	hRes = pIStorage->CreateStream( L"",
									STGM_CREATE | STGM_WRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE,
									0,
									NULL,
									&pIStream);
	if (FAILED(hRes))
	{
		pIStorage->Release();
		return false;
	}
	unsigned long ulBytesWritten = 0;
	hRes = pIStream->Write(pszBuffer, ulSize, &ulBytesWritten);
	if (FAILED(hRes))
	{
		pIStream->Release();
		pIStorage->Release();
		return false;
	}
	pIStream->Release();
	DIMEPayload *pPayload = new DIMEPayload(pIStorage,
											strId,
											strType,
											ulTNF);
	if (pPayload == NULL)
	{
		pIStorage->Release();
		return false;
	}
	if (pOptionData != NULL)
	{
		DIMEOption *pOption = new DIMEOption(   ulOptionType,
												ulOptionLength,
												pOptionData);
		if (pOption == NULL)
		{
			delete pPayload;
			return false;
		}
		pPayload->AddOption(pOption);
	}
	m_arrPayloads.push_back(pPayload);
	pIStorage->Release();
	return true;
}

bool DIMEParser::AddPayload(const string& strFilename,
							const string& strId,
							const string& strType,
							unsigned long ulTNF,
							unsigned char *pOptionData,
							unsigned long ulOptionLength,
							unsigned long ulOptionType)
{
	IStorage *pIStorage;
	HRESULT hRes = StgCreateDocfile(NULL,
									STGM_DELETEONRELEASE | STGM_DIRECT | STGM_READWRITE| STGM_CREATE | STGM_SHARE_EXCLUSIVE,
									0,
									&pIStorage);
	if (FAILED(hRes))
	{
		return false;
	}
	IStream *pIStream;
	wstring wstrId;
	UTF8ToWideString(strId, wstrId);
	hRes = pIStorage->CreateStream( L"",
									STGM_CREATE | STGM_WRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE,
									0,
									NULL,
									&pIStream);
	if (FAILED(hRes))
	{
		pIStorage->Release();
		return false;
	}
	// open the file
    FILE* pFile = fopen(strFilename.c_str(), "rb");
    if(pFile == NULL)
	{
        return false;
	}
	// copy contents to stream
	const unsigned long ulSize = 4096;
	char szBuffer[ulSize];
	unsigned long ulBytesWritten = 0;
	unsigned long ulCount;
	do
	{
		ulCount = fread(szBuffer, sizeof(char), ulSize, pFile);
		hRes = pIStream->Write(szBuffer, ulCount, &ulBytesWritten);
		if (FAILED(hRes) || ulCount != ulBytesWritten)
		{
		    fclose(pFile);
			pIStream->Release();
			pIStorage->Release();
			return false;
		}
	} while (ulCount == ulSize);
    // close the file
    fclose(pFile);
	// done with stream
	pIStream->Release();
	// create payload instance and store it
	DIMEPayload *pPayload = new DIMEPayload(pIStorage,
											strId,
											strType,
											ulTNF);
	if (pPayload == NULL)
	{
		pIStorage->Release();
		return false;
	}
	if (pOptionData != NULL)
	{
		DIMEOption *pOption = new DIMEOption(   ulOptionType,
												ulOptionLength,
												pOptionData);
		if (pOption == NULL)
		{
			delete pPayload;
			return false;
		}
		pPayload->AddOption(pOption);
	}
	m_arrPayloads.push_back(pPayload);
	pIStorage->Release();
	return true;
}

bool DIMEParser::AddPayload(DIMEPayload *pPayload)
{
	if (pPayload == NULL)
	{
		return false;
	}
	m_arrPayloads.push_back(pPayload);
	return true;
}

bool DIMEParser::SerializeInit(unsigned long ulRecordSize)
{
	// this MUST be called before attempting to serialize
	bool bResult = m_Serializer.Init(ulRecordSize, &m_arrPayloads);
	if (m_Serializer.Error() == true)
	{
		throw new DIMEParserException(m_Serializer.GetLastError());
	}
	Clear();
	return bResult;
}

bool DIMEParser::Serialize(char *pszBuffer,
						   unsigned long ulSize,
						   unsigned long *pBytesWritten)
{
	// Buffer must be >= the record size established by the
	// preceding call to SerializeInit()
	m_Serializer.SerializeRecord(   pszBuffer,
									ulSize,
									pBytesWritten);

	if (m_Serializer.Error() == true)
	{
		throw new DIMEParserException(m_Serializer.GetLastError());
	}
	return (m_Serializer.Finished() == false);
}

bool DIMEParser::DeserializeInit()
{
	// this MUST be called before attempting to deserialize
	bool bResult = m_Deserializer.Init(&m_arrPayloads);
	if (m_Deserializer.Error() == true)
	{
		throw new DIMEParserException(m_Deserializer.GetLastError());
	}
	Clear();
	return bResult;
}

bool DIMEParser::Deserialize(   char *pszBuffer,
								unsigned long ulSize)
{
	m_Deserializer.Deserialize( pszBuffer,
								ulSize);

	if (m_Deserializer.Error() == true)
	{
		throw new DIMEParserException(m_Deserializer.GetLastError());
	}
	return (m_Deserializer.Finished() == false);
}

wstring DIMEParser::GenerateId()
{
	GUID g;
	CoCreateGuid(&g);
	OLECHAR wszBuffer1[64];
	OLECHAR wszBuffer2[64];
	StringFromGUID2(g, wszBuffer1, 64);
	wszBuffer1[37] = 0;
	swprintf(wszBuffer2, L"uuid:%s", &wszBuffer1[1]);
	return wstring(wszBuffer2);
}

