//
// $Id: VariableCollector.cpp,v 1.3 2005/03/28 15:59:42 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 "VariableCollector.h"

//****************************************************************************************//
//								VariableCollector Class									  //
//****************************************************************************************//

VariableCollector::VariableCollector(DOMProcessor *myProcessor, string varFileIn, 
									 XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *ovalDoc)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Parse the specified variables file onto a DOMDocument
	//	Set the reference to the oval document
	// -----------------------------------------------------------------------
	
	//	Parse the variables file - validate it against the variables schema
	variableDocument = myProcessor->ParseFile(varFileIn, true);

	//	Set the oval document for this VariableCollector
	if(ovalDoc == NULL)
		throw VariableCollectorException("Error: A NULL oval document has been specified for use with the variable collector.");
	else
		ovalDocument = ovalDoc;

}

VariableCollector::~VariableCollector()
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Delete the variables document.
	// -----------------------------------------------------------------------

	//	Call the destructor
	variableDocument->~DOMDocument();
}

// ***************************************************************************************	//
//								Public members												//
// ***************************************************************************************	//
void VariableCollector::Run()
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Search the oval document for all variable references. When a reference
	//	found get teh value from the variables document. Write the new value to
	//	the oval doument and remove the variable reference.
	//	
	// -----------------------------------------------------------------------
	
	//	Get a refernce to the tests node
	DOMNode *testsNode = DOMCommon::FindNode(ovalDocument, "tests");

	//	get a list of the child nodes
	DOMNodeList *testList = testsNode->getChildNodes();

	//	Loop through all the nodes in testList
	unsigned int index = 0;
	DOMNode *test = NULL;
	int listLength = testList->getLength();
	while(index < listLength)
	{
		test = testList->item(index++);

		if(test != NULL)
		{
			//	only concerned with ELEMENT_NODEs
			if (test->getNodeType() == DOMNode::ELEMENT_NODE)
			{
				//	Get the name of the node
				string name =XmlCommon::GetElementName((DOMElement*)test);
				
				//	Check the test type - ignore compound and unknown tests
				if(name.compare("compound_test") != 0 && name.compare("unknown_test") != 0)
					ProcessTest((DOMElement*)test);
			}
		}
	}
}

// ***************************************************************************************	//
//								Private members												//
// ***************************************************************************************	//
void VariableCollector::GetVariableValue(string varIdIn, string *opOut, string *valOut)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return the value and operator of the variable in the variables 
	//	document with the corresponding variable id
	//
	//	TODO: Determine how to handle xml as part of a variable.
	//	Latest changes to the variables schema also include a type.
	//
	// -----------------------------------------------------------------------

	DOMNode *varRootNode = DOMCommon::FindNode(variableDocument, "variables");
	DOMNode *varNode = DOMCommon::FindNodeByAttribute(varRootNode, "id", varIdIn);
	
	//	Get the operator
	DOMNode *tmp = DOMCommon::FindNode(varNode, "operator");
	opOut->assign(DOMCommon::GetDataNodeValue(tmp));

	//	Get the value
	tmp = DOMCommon::FindNode(varNode, "value");
	valOut->assign(DOMCommon::GetDataNodeValue(tmp));

}
void VariableCollector::ProcessTest(DOMElement *ovalTest)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Search each element in the test for a variable reference.
	//	If one is found get the variable value from the variables document
	//
	//	NOTE: Assuming variable references only occure on
	//	test sub elements. not on their children. This means
	//	that windows file path and file versions can not have
	//	a variable reference. I have hard coded an exception if 
	//	a variable reference is found on either of these.
	//
	// -----------------------------------------------------------------------

	//	get a list of the child nodes
	DOMNodeList *testElementsList = ovalTest->getChildNodes();

	//	Get the test id
	string testId = DOMCommon::GetAttributeByName(ovalTest, "id");

	//	Loop through all the nodes in testList
	unsigned int index = 0;
	DOMNode *testElement = NULL;
	int listLength = testElementsList->getLength();
	while(index < listLength)
	{
		testElement = testElementsList->item(index++);

		if(testElement != NULL)
		{
			//	only concerned with ELEMENT_NODEs
			if (testElement->getNodeType() == DOMNode::ELEMENT_NODE)
			{
				//	Get the name of the node
				string name = XmlCommon::GetElementName((DOMElement*)testElement);
				
				//	Look for a variable reference
				string varId = DOMCommon::GetAttributeByName(testElement, "var_ref");
				if(varId.compare("") != 0)
				{
					//	Get the variable's value and 
					//	set the value in the oval document
					string op = "";
					string val = "";
					GetVariableValue(varId, &op, &val);

					SetValue((DOMElement*)testElement, op, val);

					//	Remove the var_ref attribute on the test element.

				}else
				{
					throw VariableCollectorException("Error: A variable reference is specified with out a value in test:" + testId);
				}
			}
		}
	}

}
void VariableCollector::SetValue(DOMElement *testElm, string opIn, string valIn)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Add the value of the variable to the test element in the oval document
	//
	// -----------------------------------------------------------------------

}


//****************************************************************************************//
//							DOMOvalResultXMLException Class								  //	
//****************************************************************************************//
VariableCollectorException::VariableCollectorException() : Exception()
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Default constructor simply set the severity to ERROR_FATAL. This is 
	//	done with the explicit call to the Exception class default constructor.
	//
	// -----------------------------------------------------------------------

}

VariableCollectorException::VariableCollectorException(string errMsgIn) : Exception(errMsgIn)
{
	// -----------------------------------------------------------------------
	//	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.
	//
	// -----------------------------------------------------------------------

}

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

}

