// StartTrace.cpp : Defines the entry point for the DLL application.
//
//***************************************************************************
//
//  judyp      May 1999        
//
//***************************************************************************

#include "stdafx.h"

#pragma warning (disable : 4786)
#pragma warning (disable : 4275)

#include <iostream>
#include <strstream>
#include <fstream>
#include <fcntl.h>
#include <io.h>
#include <string>
#include <sstream>
#include <map>
#include <list>


using namespace std;

#include <malloc.h>
#include <tchar.h>
#include <windows.h>
#ifdef NONNT5
typedef unsigned long ULONG_PTR;
#endif
#include <wmistr.h>
#include <guiddef.h>
#include <initguid.h>
#include <evntrace.h>

#include <WTYPES.H>
#include "t_string.h"

#include "Persistor.h"
#include "Logger.h"
#include "TCOData.h"
#include "Utilities.h"
#include "StructureWrappers.h"
#include "StructureWapperHelpers.h"
#include "ConstantMap.h" 

#include "CollectionControl.h"
 
extern CConstantMap g_ConstantMap;

#define MAX_LINE 2048

int ParseGuids
(
	TCHAR *ptcBuffer, 
	TCOData *pstructTCOData, 
	LPTSTR *plptstrErrorDesc
);

int ParseExeData
(
	t_string &tsData, 
	int &nExes, 
	LPTSTR *&lptstrArray,
	LPTSTR *plptstrErrorDesc
);


// If an error occurs you users of this function must delete
// plptstrErrorDesc.  It will contain a string describing
// the error.
int GetAllTCOData
(
	IN LPCTSTR lpctstrFile,
	OUT TCOData **pstructTCOData,
	OUT TCOFunctionalData **pstructTCOFunctionalData,
	OUT LPTSTR *plptstrErrorDesc, // Any error we had.
	IN bool bGetFunctionalData
)
{
	*pstructTCOData = (TCOData *) malloc (sizeof(TCOData));
	RtlZeroMemory(*pstructTCOData , sizeof(TCOData));

	if (bGetFunctionalData)
	{
		*pstructTCOFunctionalData = (TCOFunctionalData *) malloc(sizeof(TCOFunctionalData));
		RtlZeroMemory(*pstructTCOFunctionalData , sizeof(TCOFunctionalData));
	}

	LPSTR lpstrFile;
#ifdef UNICODE
	lpstrFile = NewLPSTR(lpctstrFile);
#else
	lpstrFile = NewTCHAR(lpctstrFile);
#endif

	CPersistor PersistorIn
		(lpstrFile, 
		ios::in | 0x20, // ios::nocreate = 0x20 - cannot get to compile!!!
		true );

	HRESULT hr = PersistorIn.Open();

	if (FAILED(hr))
	{
		t_string tsTemp;
		tsTemp = _T("TCOData error:  Could not open file or file was not in correct character set (Unicode or ANSI) for file ");
		t_string tsFile;
#ifdef _UNICODE
		LPWSTR lpwstrTemp = NewLPWSTR(lpstrFile);
		tsFile = lpwstrTemp;
		free(lpwstrTemp);
#else
		tsFile = lpstrFile;
	
#endif
		tsTemp += tsFile;
		free (lpstrFile);
		lpstrFile = NULL;
		tsTemp += _T(".");
		*plptstrErrorDesc = NewTCHAR(tsTemp.c_str());
		return -1;
	}

	free (lpstrFile);
	lpstrFile = NULL;

	int nReturn = 
		GetTCOData
		(
			PersistorIn,
			*pstructTCOData,
			plptstrErrorDesc // Any error we had.
		);

	if (nReturn != ERROR_SUCCESS)
	{
		PersistorIn.Close();
		return nReturn;
	}

	if (bGetFunctionalData)
	{
		nReturn = 
			TCOFunctionalObjects
			(	
				PersistorIn,
				*pstructTCOFunctionalData,
				plptstrErrorDesc // Describes error this function had.
			);
	}


	PersistorIn.Close();
	return nReturn;

}

// If an error occurs you users of this function must delete
// plptstrErrorDesc.  It will contain a string describing
// the error.
int GetTCOData
(
	IN CPersistor &PersistorIn,
	OUT TCOData *pstructTCOData,
	OUT LPTSTR *plptstrErrorDesc // Any error we had.
)
{
	RtlZeroMemory(pstructTCOData , sizeof(TCOData));

	
	// We are doing line oriented serailization and assume that
	// a line in the stream is 1024 or less TCHARS.
	TCHAR *ptcBuffer = (TCHAR *) malloc(MAX_LINE * sizeof(TCHAR));

	*plptstrErrorDesc = NULL;

	// Short description
	GetALine(PersistorIn.Stream(),ptcBuffer, MAX_LINE);
	pstructTCOData->m_lptstrShortDesc = NewTCHAR(ptcBuffer);

	// Long description.
	GetALine(PersistorIn.Stream(),ptcBuffer, MAX_LINE);
	pstructTCOData->m_lptstrLongDesc = NewTCHAR(ptcBuffer);

	// Expected result had better be in the Constant map.
	// Constant map is used to map a string to an undsigned int.
	GetALine(PersistorIn.Stream(),ptcBuffer, MAX_LINE);
	t_string tsTemp;
	tsTemp = ptcBuffer;

	CONSTMAP::iterator Iterator;
	Iterator = g_ConstantMap.m_Map.find(tsTemp);

	// If you do not find your value in the map look in 
	// ConstantMap.cpp.  You probably forgot to add it;->
	if (Iterator == g_ConstantMap.m_Map.end())
	{
		*plptstrErrorDesc = NewTCHAR(_T("TCOData error:  Expected error is not in map"));
		free(ptcBuffer);
		return -1;
	}
	else
	{
		pstructTCOData->m_lptstrExpectedResult = NewTCHAR(ptcBuffer);
		pstructTCOData->m_ulExpectedResult = (*Iterator).second; 
	}

	// TraceHandle values are VALUE_VALID or VALUE_NULL
	GetALine(PersistorIn.Stream(),ptcBuffer, MAX_LINE);
	tsTemp = ptcBuffer;

	if (case_insensitive_compare(tsTemp,_T("VALUE_VALID")) == 0)
	{
		pstructTCOData->m_pTraceHandle = 
			(TRACEHANDLE *) malloc (sizeof(TRACEHANDLE));
		*pstructTCOData->m_pTraceHandle = NULL;
	}
	else if (case_insensitive_compare(tsTemp,_T("VALUE_NULL")) == 0)
	{
		pstructTCOData->m_pTraceHandle = (TRACEHANDLE *) NULL;
	}
	else
	{
		*plptstrErrorDesc = 
			NewTCHAR
			(_T("TCOData error:  Error in value of TraceHandle.  Valid values are \"VALUE_VALID\" or \"VALUE_NULL\"."));
		free(ptcBuffer);
		return -1;	
	}

	GetALine(PersistorIn.Stream(),ptcBuffer, MAX_LINE);
	tsTemp = ptcBuffer;
	InitializeTCHARVar(tsTemp , (void *) &pstructTCOData->m_lptstrInstanceName);

	// API test - valid values 0 - 6
	//  OtherTest = 0,
	//	StartTraceTest = 1,
	//	StopTraceTest = 2,
	//	EnableTraceTest = 3,
	//	QueryTraceTest = 4,
	//	UpdateTrace = 5,
	//	QueryAllTraces = 6
	GetALine(PersistorIn.Stream(),ptcBuffer, MAX_LINE);
	tsTemp = ptcBuffer;
	InitializeULONGVar(tsTemp , (void *) &pstructTCOData->m_ulAPITest);

	if (pstructTCOData->m_ulAPITest < 0 || pstructTCOData->m_ulAPITest > 6)
	{
		*plptstrErrorDesc = 
			NewTCHAR
			(_T("TCOData error:  Error in value of m_ulAPITest.  Valid values are 0 - 6.  See enum in TCOData.h"));
		free(ptcBuffer);
		return -1;	
	}

	// Valid values are KERNEL_LOGGER or PRIVATE_LOGGER
	GetALine(PersistorIn.Stream(),ptcBuffer, MAX_LINE);
	pstructTCOData->m_lptstrLoggerMode = NewTCHAR(ptcBuffer);


	// Enable is used for EnableTrace.
	GetALine(PersistorIn.Stream(),ptcBuffer, MAX_LINE);
	tsTemp = ptcBuffer;
	if (case_insensitive_compare(tsTemp.substr(0,7),_T("ENABLE:")) != 0)
	{
		*plptstrErrorDesc = 
			NewTCHAR
			(_T("TCOData error:  Enable: expected."));
		free(ptcBuffer);
		return -1;	
	}
	else
	{
		InitializeULONGVar(tsTemp.substr(7) , &pstructTCOData->m_ulEnable);
	}

	// EnableFlag is used for EnableTrace and is passed to the provider.
	GetALine(PersistorIn.Stream(),ptcBuffer, MAX_LINE);
	tsTemp = ptcBuffer;
	if (case_insensitive_compare(tsTemp.substr(0,11),_T("ENABLEFLAG:")) != 0)
	{
		*plptstrErrorDesc = 
			NewTCHAR
			(_T("TCOData error:  EnableFlag: expected."));
		free(ptcBuffer);
		return -1;	
	}
	else
	{
		InitializeHandleVar(tsTemp.substr(11) , &pstructTCOData->m_ulEnableFlag);
	}

	// EnableLevel is used for EnableTrace and is passed to the provider.
	GetALine(PersistorIn.Stream(),ptcBuffer, MAX_LINE);
	tsTemp = ptcBuffer;
	if (case_insensitive_compare(tsTemp.substr(0,12),_T("ENABLELEVEL:")) != 0)
	{
		*plptstrErrorDesc = 
			NewTCHAR
			(_T("TCOData error:  EnableLevel: expected."));
		free(ptcBuffer);
		return -1;	
	}
	else
	{
		InitializeHandleVar(tsTemp.substr(12) , &pstructTCOData->m_ulEnableLevel);
	}

	CEventTraceProperties cPropsIn;

	// This has to be mofified to allow a NULL strucutre.
	cPropsIn.Persist( PersistorIn);
	
	pstructTCOData->m_pProps = cPropsIn.GetEventTracePropertiesInstance();
	if (pstructTCOData->m_pProps &&
		case_insensitive_compare(tsTemp,_T("PRIVATE_LOGGER")) == 0)
	{
		pstructTCOData->m_pProps->LogFileMode = 
			pstructTCOData->m_pProps->LogFileMode | EVENT_TRACE_PRIVATE_LOGGER_MODE;
	}
	
	GetALine(PersistorIn.Stream(),ptcBuffer, MAX_LINE);
	int nReturn = ParseGuids(ptcBuffer, pstructTCOData, plptstrErrorDesc);

	if(nReturn != ERROR_SUCCESS)
	{
		return nReturn;
	}

	// Validator
	if (PersistorIn.Stream().eof() == false)
	{
		GetALine(PersistorIn.Stream(),ptcBuffer, MAX_LINE);
		pstructTCOData->m_lptstrValidator = NewTCHAR(ptcBuffer);
	}

	free(ptcBuffer);
	return 0;
}

// If an error occurs you users of this function must delete
// plptstrErrorDesc.  It will contain a string describing
// the error.
int TCOFunctionalObjects
(	IN CPersistor &PersistorIn,
	IN OUT TCOFunctionalData *pstructTCOFunctionalData,
	OUT LPTSTR *plptstrErrorDesc // Describes error this function had.
)
{
	// We are doing line oriented serailization and assume that
	// a line in the stream is 1024 or less TCHARS.
	TCHAR *ptcBuffer = (TCHAR *) malloc(MAX_LINE * sizeof(TCHAR));

	*plptstrErrorDesc = NULL;

	t_string tsTemp;
	t_string tsError;
	t_string tsSubstr;

	if (PersistorIn.Stream().eof() == false)
	{
		GetALine(PersistorIn.Stream(),ptcBuffer, MAX_LINE);
		tsTemp = ptcBuffer;
		tsSubstr = tsTemp.substr(0,9);
		if (case_insensitive_compare(tsSubstr,_T("provider:")) == 0)
		{
			tsSubstr = tsTemp.substr(9);
			int nReturn = 
				ParseExeData
				(
					tsSubstr, 
					pstructTCOFunctionalData->m_nProviders, 
					pstructTCOFunctionalData->m_lptstrProviderArray,
					plptstrErrorDesc
				);

			if (nReturn != ERROR_SUCCESS)
			{
				tsError = _T("Invalid providers argument: ");
				tsError += tsTemp;
				tsError += _T(".");
				*plptstrErrorDesc = NewTCHAR(tsError.c_str());
				free(ptcBuffer);
				return -1;
			}
		}
		else
		{
			tsError = _T("Invalid providers argument: ");
			tsError += tsTemp;
			tsError += _T(".");
			*plptstrErrorDesc = NewTCHAR(tsError.c_str());
			free(ptcBuffer);
			return -1;
		}
	}

	// We may have a DataProvider.  If not we us our default.
	if (PersistorIn.Stream().eof() == false)
	{
		GetALine(PersistorIn.Stream(),ptcBuffer, MAX_LINE);
		tsTemp = ptcBuffer;
		tsSubstr = tsTemp.substr(0,9);
		if (case_insensitive_compare(tsSubstr,_T("consumer:")) == 0)
		{
			tsSubstr = tsTemp.substr(9);
			int nReturn = 
				ParseExeData
				(
					tsSubstr, 
					pstructTCOFunctionalData->m_nConsumers, 
					pstructTCOFunctionalData->m_lptstrConsumerArray,
					plptstrErrorDesc
				);

			if (nReturn != ERROR_SUCCESS)
			{
				tsError = _T("Invalid consumers argument: ");
				tsError += tsTemp;
				tsError += _T(".");
				*plptstrErrorDesc = NewTCHAR(tsError.c_str());
				free(ptcBuffer);
				return -1;
			}
		}
		else
		{
			tsError = _T("Invalid consumers argument: ");
			tsError += tsTemp;
			tsError += _T(".");
			*plptstrErrorDesc = NewTCHAR(tsError.c_str());
			free(ptcBuffer);
			return -1;
		}
	}

	free(ptcBuffer);
	return 0;
}


void FreeTCOData (TCOData *pstructTCOData)
{
	if (!pstructTCOData)
	{
		return;
	}

	free(pstructTCOData->m_lptstrShortDesc);
	free(pstructTCOData->m_lptstrLongDesc);
	free(pstructTCOData->m_lptstrExpectedResult);
	free(pstructTCOData->m_pTraceHandle);
	free(pstructTCOData->m_lptstrInstanceName);
	free(pstructTCOData->m_lptstrLoggerMode);
	free(pstructTCOData->m_lpguidArray);
	if (pstructTCOData->m_pProps)
	{
		free(pstructTCOData->m_pProps->LoggerName);
		free(pstructTCOData->m_pProps->LogFileName);
	}
	free(pstructTCOData->m_pProps);		
	free(pstructTCOData->m_lptstrValidator);

	free(pstructTCOData);
}

void FreeTCOFunctionalData (TCOFunctionalData *pstructTCOFunctionalData)
{
	if (!pstructTCOFunctionalData)
	{
		return;
	}

	int i;
	TCHAR *pTemp;

	for (i = 0; i < pstructTCOFunctionalData->m_nProviders; i++)
	{
		pTemp = pstructTCOFunctionalData->m_lptstrProviderArray[i];
		free (pTemp);
	}
	 
	free (pstructTCOFunctionalData->m_lptstrProviderArray);

	for (i = 0; i < pstructTCOFunctionalData->m_nConsumers; i++)
	{
		pTemp = pstructTCOFunctionalData->m_lptstrConsumerArray[i];
		free (pTemp);
	}
	 
	free (pstructTCOFunctionalData->m_lptstrConsumerArray);

	free(pstructTCOFunctionalData);
}

int ParseExeData
(
	t_string &tsData, 
	int &nExes, 
	LPTSTR *&lptstrArray,
	LPTSTR *plptstrErrorDesc
)
{
	// Embedded " are not allowed in the command line.  Had to draw
	// the line somewhere.
	// Tokenize on "," and " at end of line.
	list <t_string> listExes;

	bool bDone = false;
	
	int nBeg = 0;
	int nFind = tsData.find(_T(","), nBeg);
	
	t_string tsExe;

	while (!bDone)
	{
		if (nFind != t_string::npos)
		{
			tsExe = tsData.substr(nBeg,nFind - nBeg);
			listExes.push_back(tsExe);
			tsExe.erase();
		}
		else
		{
			tsExe = tsData.substr(nBeg,t_string::npos);
			listExes.push_back(tsExe);
			bDone = true;
			tsExe.erase();
		}
		nBeg = nFind + 1;
		nFind = tsData.find(_T(","), nBeg);
	}

	// Allocate the Exe array
	nExes = listExes.size();
	lptstrArray = 
			(TCHAR **) malloc (sizeof(TCHAR *) * nExes);
	RtlZeroMemory
			(lptstrArray, 
			sizeof(sizeof(TCHAR *) * nExes));

	list<t_string>::iterator pListExes;

	int i = 0;

	for (pListExes = listExes.begin(); pListExes != listExes.end() ; ++pListExes)
	{
		tsExe = (*pListExes);
		lptstrArray[i++] = NewTCHAR(tsExe.c_str());
	}

	return ERROR_SUCCESS;
}


int ParseGuids
(
	TCHAR *ptcBuffer, 
	TCOData *pstructTCOData, 
	LPTSTR *plptstrErrorDesc
)
{

	// Is Wnode does not have a GUID put the first one from list in it.
	t_string tsTemp;
	tsTemp = ptcBuffer;

	if (case_insensitive_compare(tsTemp.substr(0,6),_T("guids:")) != 0)
	{
		tsTemp.erase();
		tsTemp = _T("Invalid Guids entry: ");
		tsTemp += ptcBuffer;
		tsTemp += _T(".");
		*plptstrErrorDesc = NewTCHAR(tsTemp.c_str());
		return -1;
	}

	// Count the commas
	int nFind = tsTemp.find(_T(','));

	t_string tsGuid;
	int nBeg = 6;

	if(nBeg == tsTemp.length())
	{
		pstructTCOData->m_nGuids = 0;
		pstructTCOData->m_lpguidArray = NULL;
		return 0;
	}

	// We only have one GUID.
	if (nFind == t_string::npos)
	{
		tsGuid = tsTemp.substr(nBeg,nFind - nBeg);
		// Allocate the GUID array
		pstructTCOData->m_nGuids = 1;
		pstructTCOData->m_lpguidArray = 
			(GUID *) malloc (sizeof(GUID) * pstructTCOData->m_nGuids);
		RtlZeroMemory
			(pstructTCOData->m_lpguidArray , 
			sizeof(sizeof(GUID) * pstructTCOData->m_nGuids));
		// Just one GUID, thank you.
		wGUIDFromString(tsGuid.c_str(), &pstructTCOData->m_lpguidArray[0]);
	
		return 0;
	}

	// We have more than one GUID.
	bool bDone = false;


	list <t_string> listGuids;

	while (!bDone)
	{
		if (nFind != t_string::npos)
		{
			tsGuid = tsTemp.substr(nBeg,nFind - nBeg);
			listGuids.push_back(tsGuid);
			tsGuid.erase();
		}
		else
		{
			tsGuid = tsTemp.substr(nBeg,t_string::npos);
			listGuids.push_back(tsGuid);
			bDone = true;
			tsGuid.erase();
		}
		nBeg = nFind + 1;
		nFind = tsTemp.find(',', nBeg);
	}

	// Allocate the GUID array
	pstructTCOData->m_nGuids = listGuids.size();
	pstructTCOData->m_lpguidArray = 
			(GUID *) malloc (sizeof(GUID) * pstructTCOData->m_nGuids);
	RtlZeroMemory
			(pstructTCOData->m_lpguidArray , 
			sizeof(sizeof(GUID) * pstructTCOData->m_nGuids));

	list<t_string>::iterator pListGuids;

	int i = 0;

	for (pListGuids = listGuids.begin(); pListGuids != listGuids.end() ; ++pListGuids)
	{
		tsGuid = (*pListGuids);
		wGUIDFromString(tsGuid.c_str(), &pstructTCOData->m_lpguidArray[i++]);
	}

	
	return 0;

}