//
// $Id: DataCollector.cpp,v 1.2 2004/07/12 17:52:17 bakerj Exp $
//
//************************** Property of the MITRE Corporation ***************************//
//
// Copyright (c) 2003 - The MITRE Corporation
//
// This file is part of the Query-based Network Assessment project.
//
// The Query-based Network Assessment is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 of the License.
//
// The Query-based Network Assessment is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with the
// Query-based Network Assessment; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
//****************************************************************************************//


#include "DataCollector.h"

//****************************************************************************************//
//								DataCollector Class								  //	
//****************************************************************************************//

DataCollector::DataCollector(XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *oval, 
							XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *data) 
							: DOMOvalDataCollector(oval, data, "windows")
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Init the DataCollector object. Pass the two documents
	//	to the base class. Hardcode the family parameter to windows for the base
	//	class.
	//	oval	- the DOMDocument that represents the oval.xml
	//	data	- the DOMDocument that will contain the data model.
	// -----------------------------------------------------------------------
	
	//	Get the system_characteristics node and add the schema reference
	DOMNode *oval_dataNode = DOMCommon::FindNode(dataDoc, "system_characteristics");
	DOMCommon::AddAttribute((DOMElement*)oval_dataNode, "xsi:schemaLocation", "http://oval.mitre.org/XMLSchema/system_characteristics system-characteristics-schema.xsd http://oval.mitre.org/XMLSchema/system_characteristics#windows windows-system-characteristics-schema.xsd");

}

DataCollector::~DataCollector()
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Clean up after DOM
	// -----------------------------------------------------------------------
}

// ***************************************************************************************	//
//								Public members												//
// ***************************************************************************************	//

void DataCollector::Run()
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Find the tests node and loop through all of its children.
	//	If a test belongs to the family that is being tested run that test.
	//	
	//	To run a test call DOMOvalDataCollector::RUNTest()
	// -----------------------------------------------------------------------

	DOMNode *testsNode;
	DOMNode *test;
	DOMNodeList *ovalTestsChildren;

	//	Store a ptr to each type of probe init to NULL
	//	These probes will only be initialized if needed
	Probe *activeDirectoryProbe	= NULL;
	Probe *fileAttributeProbe	= NULL;
	Probe *metabaseProbe		= NULL;
	Probe *registryProbe		= NULL;

	//	Add the host name, ip address, and time stamp
	string hostName = "";
	try
	{
		hostName = GetHostName();

	}catch(Exception ex)
	{	
		if(Log::verboseMode)
		{
			cout << "DataCollector::Run() " << ex.GetErrorMessage() << endl;
			Log::WriteLog("DataCollector::Run() " + ex.GetErrorMessage() + "\n");
		}
		hostName = "UNABLE TO DETERMINE";
	}
	string ip = "";
	try
	{
		ip = GetIPAddress();

	}catch(Exception ex)
	{	
		if(Log::verboseMode)
		{
			cout << "DataCollector::Run() " << ex.GetErrorMessage() << endl;
			Log::WriteLog("DataCollector::Run() " + ex.GetErrorMessage() + "\n");
		}
		ip = "UNABLE TO DETERMINE";
	}
	
	WriteHostName(hostName);
	WriteIPAddress(ip);
	WriteTimeStamp();

	//	get a ptr to the tests node in the oval document.
	testsNode = DOMCommon::FindNode(ovalDoc, "tests");
	//	get a list of the child nodes
	ovalTestsChildren = testsNode->getChildNodes();

	//	Loop through all the nodes in ovalTestList
	unsigned int index = 0;
	while(index < ovalTestsChildren->getLength())
	{
		test = ovalTestsChildren->item(index);

		//	Handle exceptions on a test by test basis
		try
		{
			//	only concerned with ELEMENT_NODEs
			if (test->getNodeType() == DOMNode::ELEMENT_NODE)
			{
			//	string testId = DOMCommon::GetAttributeByName(test, "id");
			//	cout << "Test Id: " << testId << endl; 

				//	get the name of the test
				string name = DOMCommon::ToString(test->getNodeName());
				if(strncmp(name.c_str(), "compound_test", 13) != 0 && strncmp(name.c_str(), "unknown_test", 12) != 0)
				{
					int tmpFamily = DetermineNodeFamily(test);
				
					//	only handle supported tests
					if(strncmp(name.c_str(), "activedirectory_test", 20)==0
							&& tmpFamily == WINDOWS_OS_FAMILY)
					{
						//	Check the probe
						if(activeDirectoryProbe == NULL)
							activeDirectoryProbe = new ActiveDirectoryProbe();

						//	Run the test
						RunTest(activeDirectoryProbe, new ActiveDirectoryData(test));

					}else if(strncmp(name.c_str(), "file_test", 9)==0
							&& tmpFamily == WINDOWS_OS_FAMILY)
					{
						//	Check the probe
						if(fileAttributeProbe == NULL)
							fileAttributeProbe = new FileAttributesProbe();

						//	Run the test
						RunTest(fileAttributeProbe, new FileAttributeData(test));
						
					}else if(strncmp(name.c_str(), "metabase_test", 13)==0
							&& tmpFamily == WINDOWS_OS_FAMILY)
					{
						//	Check the probe
						if(metabaseProbe == NULL)
							metabaseProbe = new MetabaseKeysProbe();

						//	Run the test
						RunTest(metabaseProbe, new MetabaseKeyData(test));

					}else if(strncmp(name.c_str(), "registry_test", 13)==0 
							&& tmpFamily == WINDOWS_OS_FAMILY)
					{
						//	Check the probe
						if(registryProbe == NULL)
							registryProbe = new RegistryKeysProbe();

						//	Run the test
						RunTest(registryProbe, new RegistryKeyData(test));

					}else if(tmpFamily == WINDOWS_OS_FAMILY)
					{
						//	This is not really an error, we just don't handle this test yet
						throw DOMOvalDataCollectorException("Message: Not a supported test: " + name + " id= " + DOMCommon::GetAttributeByName(test, "id") + "\n");
					}
				}
			}

		}catch(Exception ex)
		{
			if(Log::verboseMode)
			{
				Log::WriteLog(ex.GetErrorMessage());
				cout << ex.GetErrorMessage() << endl;		
			}
		}catch(...)
		{
			string errMsg = "Fatal error: An unknown exception occured in 'DataCollector::Run()'\n";
			Log::WriteLog(errMsg);
			cout << errMsg << endl;
		}

		index ++;
	}

	delete(activeDirectoryProbe);
	delete(fileAttributeProbe);
	delete(metabaseProbe);
	delete(registryProbe);
}

// ***************************************************************************************	//
//								Private members												//
// ***************************************************************************************	//




string DataCollector::GetHostName()
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Get interface information about the system. 
	//		- interface name
	//		- mac address
	//		- ip address
	//		- host name
	//	
	//	Data is stored in a vecctor of interface objects.
	// -----------------------------------------------------------------------

/*	string interfaceName;
	string macAddress;
	string hostName;
	string ipAddress;
	char myBuffer[1024];
	
	//	Allocate information for up to 16 NICs
	IP_ADAPTER_INFO AdapterInfo[16];        

	//	Save memory size of buffer
	DWORD dwBufLen = sizeof(AdapterInfo);  

	DWORD dwStatus = GetAdaptersInfo(	// Call GetAdapterInfo
	AdapterInfo,						// [out] buffer to receive data
	&dwBufLen);							// [in] size of receive data buffer
	assert(dwStatus == ERROR_SUCCESS);	// Verify return value is 
										// valid, no buffer overflow

	PIP_ADAPTER_INFO pAdapterInfo = AdapterInfo;	// Contains pointer to
													// current adapter info
	do {
		//	Get the interface name
		interfaceName = pAdapterInfo->AdapterName;
		
		//	Reset the buffer
		memset(myBuffer, 0, sizeof(myBuffer));

		//	Get the MAC address
		sprintf(myBuffer,"%02X-%02X-%02X-%02X-%02X-%02X", 
				pAdapterInfo->Address[0], pAdapterInfo->Address[1], pAdapterInfo->Address[2],
				pAdapterInfo->Address[3], pAdapterInfo->Address[4], pAdapterInfo->Address[5]);

		macAddress = myBuffer;

		//	Get the hostname
		//hostName = gethostbyipaddress(

		//	Get the ip address
	//	ipAddress = inet_ntoa((*pAdapterInfo->CurrentIpAddress).IpAddress);
		

		//	Advance to the next adapter
		pAdapterInfo = pAdapterInfo->Next;  										 
	}
	while(pAdapterInfo);                    // Terminate if last adapter


	return macAddress;*/

	
	string hostname = "";
	FIXED_INFO *FixedInfo;
	ULONG ulOutBufLen;
	DWORD res;

	FixedInfo = (FIXED_INFO *) GlobalAlloc( GPTR, sizeof( FIXED_INFO ) );
	ulOutBufLen = sizeof( FIXED_INFO );

	//	Make first call to get the required buffer size
	res = GetNetworkParams( FixedInfo, &ulOutBufLen);
	if(res == ERROR_BUFFER_OVERFLOW)
	{
		GlobalFree( FixedInfo );
		FixedInfo = (FIXED_INFO*)GlobalAlloc(GPTR, ulOutBufLen);
	}else
	{
		throw DOMOvalDataCollectorException("Error: Unable to determine required memory while attempting to get the host name.");
	}
	
	// Check allocated memory
    if (FixedInfo == NULL)
        throw DOMOvalDataCollectorException("Error: Unable to allocate memory while attempting to get the host name.");


	//	Make second call to get the data
	res = GetNetworkParams( FixedInfo, &ulOutBufLen);
	if (res != ERROR_SUCCESS) 
	{
		throw DOMOvalDataCollectorException("Error: Unable to get host name from system.");

	}else
	{
		hostname = FixedInfo->HostName;
		if(strcmp(FixedInfo->DomainName, "") != 0)
		{
			hostname.append(".");
			hostname.append(FixedInfo->DomainName);
		}
	}


	return hostname;
}

string DataCollector::GetIPAddress()
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return the ip address of the current machine as a string
	// -----------------------------------------------------------------------

	string ipaddress = "";

    ULONG AdapterInfoSize = 0;
	PIP_ADAPTER_INFO pAdapterInfo;
	DWORD res;

	//	Make first call to get the required buffer size
	res = GetAdaptersInfo(NULL, &AdapterInfoSize);

	if (res == ERROR_BUFFER_OVERFLOW)
    {
		pAdapterInfo = (PIP_ADAPTER_INFO) GlobalAlloc(GPTR, AdapterInfoSize);
	}else
	{
		throw DOMOvalDataCollectorException("Error: Unable to determine required memory while attempting to get the ip address.");
	}


    // Check allocated memory
    if (pAdapterInfo == NULL)
        throw DOMOvalDataCollectorException("Error: Unable to allocate memory while attempting to get the ip address.");

    //	Make second call to get the data
	res = GetAdaptersInfo(pAdapterInfo, &AdapterInfoSize);
	if (res != ERROR_SUCCESS)
	{
		throw DOMOvalDataCollectorException("Error: Unable to get ip address from system.");

	}else
	{

		PIP_ADAPTER_INFO pAdapt = pAdapterInfo;

		while (pAdapt)
		{

			PIP_ADDR_STRING pAddrStr = &(pAdapt->IpAddressList);
			while(pAddrStr)
			{
				if(ipaddress.compare("") != 0)
					ipaddress.append(", ");
				
				ipaddress = pAddrStr->IpAddress.String;

				pAddrStr = pAddrStr->Next;
			}

			pAdapt = pAdapt->Next;
		}
	}


	return ipaddress;
}





