//
// $Id: OvalDataCollector.cpp,v 1.10 2005/08/05 20:19:15 bakerj Exp $
//
//****************************************************************************************//
// Copyright (c) 2005, The MITRE Corporation
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice, this list
//       of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright notice, this 
//       list of conditions and the following disclaimer in the documentation and/or other
//       materials provided with the distribution.
//     * Neither the name of The MITRE Corporation nor the names of its contributors may be
//       used to endorse or promote products derived from this software without specific 
//       prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
// SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
// OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
// TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//****************************************************************************************//


#include "OvalDataCollector.h"

//****************************************************************************************//
//								OvalDataCollector Class								  //	
//****************************************************************************************//
OvalDataCollector::OvalDataCollector(XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *oval,
								XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *data,
								string strFamily)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Init the OvalDataCollector object
	//	oval	- the DOMDocument that represents the oval.xml
	//	data	- the DOMDocument that will contain the data model.
	//	family	- a flag indicating which family to gather data for.
	//
	// -----------------------------------------------------------------------

	string errMsg;

    try 
	{
        XMLPlatformUtils::Initialize();
    }
    catch (const XMLException& toCatch) 
	{
        errMsg.append("Error:  An error occured durring initialization of the xml utilities:\n");
        errMsg.append(XmlCommon::ToString(toCatch.getMessage()));
		errMsg.append("\n");

		throw OvalDataCollectorException(errMsg);
    }

	//	validate inputs
	if(oval != NULL)
		ovalDoc = oval;
	else
		throw OvalDataCollectorException("Error: The oval Document is NULL. Unable to initialize the data collector.");

	if(data != NULL)
		dataDoc = data;
	else
		throw OvalDataCollectorException("Error: The data Document is NULL. Unable to initialize the data collector.");


	//	Get the system_characteristics node
	DOMNode *scNode = XmlCommon::FindNode(dataDoc, "system_characteristics");
	
	//	Set dataDoc Family on the oval node
	XmlCommon::AddAttribute((DOMElement*)scNode, "family", strFamily);

	//	Add the namespace info to the oval_data node
	XmlCommon::AddAttribute((DOMElement*)scNode, "xmlns", "http://oval.mitre.org/XMLSchema/system_characteristics");
	XmlCommon::AddAttribute((DOMElement*)scNode, "xmlns:system_characteristics", "http://oval.mitre.org/XMLSchema/system_characteristics");
	XmlCommon::AddAttribute((DOMElement*)scNode, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
	XmlCommon::AddAttribute((DOMElement*)scNode, "xsi:schemaLocation", "http://oval.mitre.org/XMLSchema/system_characteristics system-characteristics-schema.xsd");


	// Add Generator Info
	WriteGenerator();

	//	Create the system_info element and add it as a child of the sc element
	DOMElement *sysInfoElm = XmlCommon::CreateElement(data, "system_info"); 
	scNode->appendChild(sysInfoElm);

	//	Create the system_data element and add it as a child of the sc element
	DOMElement *sysDataElm = XmlCommon::CreateElement(data, "system_data"); 
	scNode->appendChild(sysDataElm);

	// Finally set the data document reference for all probe data objects
	ProbeData::SetDataDoc(data);
}

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

	XMLPlatformUtils::Terminate();
}


// ***************************************************************************************	//
//								Protected members											//
// ***************************************************************************************	//

int OvalDataCollector::DetermineNodeFamily(DOMElement* node)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return the family of the node provided based on the xmlns associated 
	//	with the node. Throw an error if the the node does not have a xmlns
	//	attribute.
	//	
	// -----------------------------------------------------------------------
	
	int family = ERROR_OS_FAMILY;
	string attVal;
	string error;

	if(node != NULL)
	{
		//	get the xmlns attribute for the node
		attVal = XmlCommon::GetAttributeByName(node, "xmlns");
		if(attVal.compare("") != 0)
		{
			//	convert the attribute value to a family flag
			if(strstr(attVal.c_str(), "redhat") != NULL)
			{
				family = REDHAT_OS_FAMILY;
			}else if(strstr(attVal.c_str(), "solaris") != NULL)
			{
				family = SOLARIS_OS_FAMILY;
			}else if(strstr(attVal.c_str(),"windows") != NULL)
			{
				family = WINDOWS_OS_FAMILY;
			}else
			{
				family = UNSUPPORTED_OS_FAMILY;
			}

		}else
		{
			attVal = XmlCommon::GetAttributeByName(node, "id");
			throw XmlCommonException("Error: Unable to locate the xmlns attribute for the provided node: " + attVal);
		}

	}else
	{
		throw XmlCommonException("Error: Invalid node passed to 'XmlCommon::DetermineNodeFamily' function. The node can't be NULL");
	}

	return(family);
}

void OvalDataCollector::RunTest(Probe *probe, ProbeData *probeDataIn)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Run the probe with the provided probe data. 
	//	Call the write method on each ProbeData object in the
	//	result vector.
	// -----------------------------------------------------------------------

	pdVector resultVector;

	//	Run the probe
       	resultVector = probe->Run(probeDataIn);	
	
	///////////////////////////////////////////////////////////////////////////
	//	Write the result Data nodes and delete data items in result vector
	///////////////////////////////////////////////////////////////////////////
	ProbeData *tmp	= NULL;
	while(resultVector.size() !=0)
	{
	  	tmp = resultVector[resultVector.size()-1];
	       	tmp->Write();
	  	resultVector.pop_back();
	  	delete tmp;
	  	tmp = NULL;
	}
}

void OvalDataCollector::WriteGenerator()
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Write the generator element. Note that the values are all hard coded
	//	except for the time stamp value.
	//
	// -----------------------------------------------------------------------

	string time = Common::GetTimeStamp();

	DOMElement *scNode = XmlCommon::FindNode(dataDoc, "system_characteristics");
	DOMElement *generatorElm = XmlCommon::AddChildElement(dataDoc, scNode, "generator");
	XmlCommon::AddChildElement(dataDoc, generatorElm, "product_name", "OVAL Definition Interpreter");
	XmlCommon::AddChildElement(dataDoc, generatorElm, "product_version", Version::GetVersion());
	XmlCommon::AddChildElement(dataDoc, generatorElm, "schema_version", "4");
	XmlCommon::AddChildElement(dataDoc, generatorElm, "timestamp", time);
}

void OvalDataCollector::WriteSystemInfo(Probe *sysInfoProbe)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Add the system_info element to the dataDoc. This done by running
	//	the provided platform specific system info probe and then calling 
	//	the write method on the platform independant probe data object.
	// -----------------------------------------------------------------------

	RunTest(sysInfoProbe, NULL);

}

//****************************************************************************************//
//						OvalDataCollectorException Class								  //	
//****************************************************************************************//
OvalDataCollectorException::OvalDataCollectorException(string errMsgIn, int severity) : Exception(errMsgIn, severity)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Set the error message and then set the severity to ERROR_FATAL. This is 
	//	done with the explicit call to the Exception class constructor that 
	//	takes a single string param.
	//
	// -----------------------------------------------------------------------

}

OvalDataCollectorException::~OvalDataCollectorException()
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Do nothing for now
	//
	// -----------------------------------------------------------------------

}

