//
// $Id: DataCollector.cpp,v 1.2 2004/09/02 15:29:21 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, "redhat")
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Init the DOMOvalDataCollector object. Pass the two documents
	//	to the base class. Hardcode the family parameter to redhat 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#redhat redhat-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 *file_testProbe					= NULL;
	Probe *inetlisteningservers_testProbe	= NULL;
	Probe *process_testProbe				= NULL;
	Probe *rpminfo_testProbe				= NULL;
	Probe *rpmversioncompare_testProbe		= NULL;
	//Probe *shadow_testProbe					= NULL;
	Probe *uname_testProbe					= 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("Collector::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)
			{
				//	get the name of the test
				string name = DOMCommon::ToString(test->getNodeName());
				// Debug
				//cout << "Test name: " << name << endl;
				//string tid = DOMCommon::GetAttributeByName(test, "id");
				//cout <<  "Test id: " << tid << endl;
				if(name.compare("compound_test") != 0 && name.compare("unknown_test") != 0)
				{
					int tmpFamily = DetermineNodeFamily(test);
				
					//	only handle supported tests
					if(name.compare("file_test") == 0 && tmpFamily == REDHAT_OS_FAMILY)
					{
						//	Check the probe
						if(file_testProbe == NULL)
							file_testProbe = new FileProbe();

						//	Run the test
						RunTest(file_testProbe, new FileAttributeData(test));
						
					}else if(name.compare("inetlisteningservers_test") == 0 && tmpFamily == REDHAT_OS_FAMILY)
					{
						if(inetlisteningservers_testProbe == NULL)
							inetlisteningservers_testProbe = new InetListeningServersProbe();

						//	Run the test
						RunTest(inetlisteningservers_testProbe, new InetListeningServersData(test));

					}else if(name.compare("permission_test") == 0 && tmpFamily == REDHAT_OS_FAMILY)
					{
						if(file_testProbe == NULL)
							file_testProbe = new FileProbe();

						//	Run the test
						RunTest(file_testProbe, new FilePermissionData(test));
						

					}else if(name.compare("process_test") == 0 && tmpFamily == REDHAT_OS_FAMILY)
					{
						if(process_testProbe == NULL)
							process_testProbe = new ProcessProbe();

						//	Run the test
						RunTest(process_testProbe, new ProcessData(test));

					}else if(name.compare("rpminfo_test") == 0 && tmpFamily == REDHAT_OS_FAMILY)
					{
						if(rpminfo_testProbe == NULL)
							rpminfo_testProbe = new RPMInfoProbe();

						//	Run the test
						RunTest(rpminfo_testProbe, new RPMInfoData(test));

					}else if(name.compare("rpmversioncompare_test") == 0 && tmpFamily == REDHAT_OS_FAMILY)
					{
						if(rpmversioncompare_testProbe == NULL)
							rpmversioncompare_testProbe = new RPMVersionCompareProbe();

						//	Run the test
						RunTest(rpmversioncompare_testProbe, new RPMVersionCompareData(test));

					}else if(name.compare("shadow_test") == 0 && tmpFamily == REDHAT_OS_FAMILY)
					{
						/*	
						if(file_testProbe == NULL)
							file_testProbe = new FileProbe();

						//	Run the test
						RunTest(file_testProbe, new FileAttributeData(test));
						*/
						cout << "Run Shadow Test " << endl;

					}else if(name.compare("uname_test") == 0 && tmpFamily == REDHAT_OS_FAMILY)
					{
						if(uname_testProbe == NULL)
							uname_testProbe = new UnameProbe();

						//	Run the test
						RunTest(uname_testProbe, new UnameData(test));

					}else if(tmpFamily == REDHAT_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 the probes when finished with them
	delete(file_testProbe);
	delete(inetlisteningservers_testProbe);
	delete(process_testProbe);
	delete(rpminfo_testProbe);
	delete(rpmversioncompare_testProbe);
	delete(uname_testProbe);
}


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

string DataCollector::GetHostName()
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return the Host Name of the current machine as a string
	// -----------------------------------------------------------------------

	string strHostName = "";
	char *chHostName = (char*)malloc(MAXHOSTNAMELENGTH);
	int res = 0;

	res = gethostname(chHostName, MAXHOSTNAMELENGTH);

	if(res != 0)
		throw DOMOvalDataCollectorException("Error: Unable to determine the host name.");


	strHostName = chHostName;


	return strHostName;
}

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

  string ipaddress = "";
  char *chHostName = (char*)malloc(MAXHOSTNAMELENGTH);
  struct hostent *hostData = NULL;
  int res = 0;

  res = gethostname(chHostName, MAXHOSTNAMELENGTH);

  if(res != 0)
    throw DOMOvalDataCollectorException("Error: Unable to determine the host name while getting the ip address.");

  hostData = gethostbyname((const char*)chHostName); 
  if(hostData == NULL)
    throw DOMOvalDataCollectorException("Error: Unable to determine the host information while getting the ip address.");
	
  // Process the hostData structure
  char **tmpListPtr = NULL;
  struct in_addr *addrPtr = NULL;
  switch(hostData->h_addrtype) {
  case AF_INET:
    tmpListPtr = hostData->h_addr_list;
	  	  
    while((addrPtr = (struct in_addr*)*tmpListPtr++) != NULL) {
      if(ipaddress.compare("") != 0)
	ipaddress.append(", ");

      ipaddress.append(inet_ntoa(*addrPtr));
    }
	  
    break;

  default:
    throw DOMOvalDataCollectorException("Error: Unable to determine the host information while getting the ip address.");
    break;
  }
  
  return ipaddress;
}




