//
// $Id: AnalysisEngine.cpp,v 1.11 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 "AnalysisEngine.h"

//****************************************************************************************//
//								AnalysisEngine Class									  //
//****************************************************************************************//

AnalysisEngine::AnalysisEngine(XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *oval, 
									   XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *data,
									   XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *mapping,
									   XmlProcessor *processorIn, bool useConfig, string analysisFamily)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Init the AnalysisEngine
	//	Throw an exception if there is an error.
	// -----------------------------------------------------------------------

    try {

        XMLPlatformUtils::Initialize();

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

		throw AnalysisEngineException(errMsg);
    }

	//	Set the oval file
	if(oval == NULL)
		throw AnalysisEngineException("Error: A NULL oval document has been selected for analysis.");
	else
		ovalDoc = oval;

	//	Set the data file
	if(data == NULL)
		throw AnalysisEngineException("Error: A NULL data document has been selected for analysis.");
	else
		dataDoc = data; 

	//	Set the data file
	if(mapping == NULL)
		throw AnalysisEngineException("Error: A NULL mapping document has been selected for analysis.");
	else
		mappingDoc = mapping; 

	//	Create a new OvalResultXml object
	myResultXml = new OvalResultXml(processorIn, analysisFamily, ovalDoc, dataDoc);

	// Initialize all the analyzers
	Analyzer::Init(myResultXml, dataDoc, ovalDoc, mappingDoc, useConfig, analysisFamily);
}

AnalysisEngine::~AnalysisEngine()
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Clean up after the AnalysisEngine
	// -----------------------------------------------------------------------

	XMLPlatformUtils::Terminate();

	delete myResultXml;
}

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

XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* AnalysisEngine::Run()
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Run the analysis
	// -----------------------------------------------------------------------

	//	Get the host name for display
	string hostname = XmlCommon::GetDataNodeValue(XmlCommon::FindNode(dataDoc, "primary_host_name"));
	if(hostname.compare("") == 0)
		hostname = "NOT SPECIFIED";

	//	Display a message with the host name and the ip address
	cout << "    Performing analysis on: " << hostname << "\n" << endl;
	Log::WriteLog("    Performing analysis on: " + hostname + "\n\n");

	//	Start the evaluation
	EvaluateDefinitions();

	//	Get the result document and return it.
	return (myResultXml->GetResultFile());
}

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

void AnalysisEngine::EvaluateDefinitions()
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Loop through all definitions in the oval document.
	//	Pass each definition node to the a DefinitionAnalyzer.
	//	Call EvaluateDefinition for each definition in the family being analyzed
	//	Call the print routine to print the results.
	//
	// -----------------------------------------------------------------------

	DOMElement *definitions;
	DOMElement *def;
	DOMNodeList *defList;
	string defFamily;
	string defClass;
	string strDefinition;
	Result result;
	sVector notVuln;
	sVector vuln;
	sVector unknown;

	//	Get a reference to the Analyzer factory
	AnalyzerFactory *analyzerFactory = AnalyzerFactory::Instance();

	//	get a ptr to the definitions node in the oval document.
	definitions = XmlCommon::FindNodeNS(ovalDoc, "definitions");

	//	get a list of the child nodes
	defList = definitions->getChildNodes();

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

		DOMNode *tmpNode = defList->item(index++);

		if(tmpNode != NULL) {

			//	only concerned with ELEMENT_NODEs
			if (tmpNode->getNodeType() == DOMNode::ELEMENT_NODE) {
				def = (DOMElement*)tmpNode;
				
				//	Get the family of the definition
				defFamily = GetDefinitionFamily(def);

				//	Get the family of the definition
				defClass = XmlCommon::GetAttributeByName(def, "class");

				//	Gather information from the definition
				strDefinition = DefinitionToString(def);

				// Get a DefinitionnAnalyzer from the factory
				Analyzer *definitionAnalyzer = analyzerFactory->GetAnalyzer(def);

				//	Evaluate the definition
				result = definitionAnalyzer->Run(def);
				
				// Store the result
				if(result == false_result)
					notVuln.push_back(strDefinition);
				else if(result == true_result)
					vuln.push_back(strDefinition);
				else
					unknown.push_back(strDefinition);
					
			}
		}
	}

	///////////////////////////////////////////////////////////////////////////
	//	Call the print routine
	///////////////////////////////////////////////////////////////////////////
	Print(&vuln, &notVuln, &unknown);
}

string AnalysisEngine::GetDefinitionFamily(DOMElement *def)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return the family that the provided definition node corresponds to
	// -----------------------------------------------------------------------

	//	Get the node name
	string name = XmlCommon::GetElementName(def);
	string strFamily = "";
	DOMElement *tmpNode = NULL;

	if(name.compare("definition") == 0) { 

		//	Get the affected family
		tmpNode = XmlCommon::FindNodeNS(def, "affected");
		if(tmpNode != NULL)
			strFamily = XmlCommon::GetAttributeByName(tmpNode, "family");

	} else { 

		throw AnalysisEngineException("Error: GetDefinitionFamily() can not process a \"" + name + "\" node.");	
	}

	return strFamily;
}

void AnalysisEngine::Print(sVector *vuln, sVector *notVuln, sVector *unknown)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Print the results of the analysis
	//
	// -----------------------------------------------------------------------

	///////////////////////////////////////////////////////////////////////////
	//	Display a header indicating that the definitions are being processed
	///////////////////////////////////////////////////////////////////////////
	cout << " ** OVAL definition results.\n" << endl;
	Log::WriteLog(" ** OVAL definition results.\n\n");

	///////////////////////////////////////////////////////////////////////////
	//	Print the vulnerable results
	///////////////////////////////////////////////////////////////////////////
	if(vuln->size() == 0) {
		cout << "    VULNERABILITIES FOUND:" << endl;
		cout << "    OVAL Id     Reference Source     Reference Id" << endl;
		cout << "    ---------------------------------------------" << endl;
		cout << "    None." << endl;
		cout << "    ---------------------------------------------\n" << endl;
		Log::WriteLog("    VULNERABILITIES FOUND:\n");
		Log::WriteLog("    OVAL Id     Reference Source     Reference Id\n");
		Log::WriteLog("    ---------------------------------------------\n");
		Log::WriteLog("    None\n");
		Log::WriteLog("    ---------------------------------------------\n\n");
	
	} else {
		cout << "    VULNERABILITIES FOUND:" << endl;
		cout << "    OOVAL Id    Reference Source     Reference Id" << endl;
		cout << "    ---------------------------------------------" << endl;
		Log::WriteLog("    VULNERABILITIES FOUND:\n");
		Log::WriteLog("    OVAL Id     Reference Source     Reference Id\n");
		Log::WriteLog("    ---------------------------------------------\n");

		sVector::iterator vulnIterator;
		for (vulnIterator=vuln->begin(); vulnIterator!=vuln->end(); vulnIterator++) {
			cout << (*vulnIterator) << endl;
			Log::WriteLog((*vulnIterator) + "\n");
		}

		cout << "    ---------------------------------------------\n" << endl;
		Log::WriteLog("    ---------------------------------------------\n\n");
	}

	///////////////////////////////////////////////////////////////////////////
	//	Print the not vulnerable results
	///////////////////////////////////////////////////////////////////////////
	if(notVuln->size() == 0) {

		cout << "    VULNERABILITIES NOT FOUND:" << endl;
		cout << "    OVAL Id     Reference Source     Reference Id" << endl;
		cout << "    ---------------------------------------------" << endl;
		cout << "    None." << endl;
		cout << "    ---------------------------------------------\n" << endl;
		Log::WriteLog("    VULNERABILITIES NOT FOUND:\n");
		Log::WriteLog("    OVAL Id     Reference Source     Reference Id\n");
		Log::WriteLog("    ---------------------------------------------\n");
		Log::WriteLog("    None\n");
		Log::WriteLog("    ---------------------------------------------\n\n");
	
	} else {
		cout << "    VULNERABILITIES NOT FOUND:" << endl;
		cout << "    OOVAL Id    Reference Source     Reference Id" << endl;
		cout << "    ---------------------------------------------" << endl;
		Log::WriteLog("    VULNERABILITIES NOT FOUND:\n");
		Log::WriteLog("    OVAL Id     Reference Source     Reference Id\n");
		Log::WriteLog("    ---------------------------------------------\n");

		sVector::iterator notvulnIterator;
		for (notvulnIterator=notVuln->begin(); notvulnIterator!=notVuln->end(); notvulnIterator++) {
			cout << (*notvulnIterator) << endl;
			Log::WriteLog((*notvulnIterator) + "\n");
		}
	
		cout << "    ---------------------------------------------\n" << endl;
		Log::WriteLog("    ---------------------------------------------\n\n");
	}
	

	///////////////////////////////////////////////////////////////////////////
	//	Print the unknown results
	///////////////////////////////////////////////////////////////////////////
	if(unknown->size() == 0) {
		cout << "    UNABLE TO DETERMINE RESULT:" << endl;
		cout << "    OVAL Id     Reference Source     Reference Id" << endl;
		cout << "    ---------------------------------------------" << endl;
		cout << "    None." << endl;
		cout << "    ---------------------------------------------\n" << endl;
		Log::WriteLog("    UNABLE TO DETERMINE RESULT:\n");
		Log::WriteLog("    OVAL Id     Reference Source     Reference Id\n");
		Log::WriteLog("    ---------------------------------------------\n");
		Log::WriteLog("    None\n");
		Log::WriteLog("    ---------------------------------------------\n\n");
	
	} else {
		cout << "    UNABLE TO DETERMINE RESULT:" << endl;
		cout << "    OVAL Id     Reference Source     Reference Id" << endl;
		cout << "    ---------------------------------------------" << endl;
		Log::WriteLog("    UNABLE TO DETERMINE RESULT:\n");
		Log::WriteLog("    OVAL Id     Reference Source     Reference Id\n");
		Log::WriteLog("    ---------------------------------------------\n");

		sVector::iterator unknownIterator;
		for (unknownIterator=unknown->begin(); unknownIterator!=unknown->end(); unknownIterator++) {
			cout << (*unknownIterator) << endl;
			Log::WriteLog((*unknownIterator) + "\n");
		}

		cout << "    ---------------------------------------------\n" << endl;
		Log::WriteLog("    ---------------------------------------------\n\n");
	}

	///////////////////////////////////////////////////////////////////////////
	//	Display a footer  indicating that the definitions have completed
	///////////////////////////////////////////////////////////////////////////
	cout << "\n ** finished evaluating OVAL  definitions.\n" << endl;	
	Log::WriteLog("\n ** finished evaluating OVAL  definitions.\n\n");
}

string AnalysisEngine::DefinitionToString(DOMElement *definition)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Create a string representation of a definition element
	//
	// -----------------------------------------------------------------------

	string strDefinition = "";

	//	Ensure that the element provided is a definition
	string name = XmlCommon::GetElementName(definition);
	if(name.compare("definition") != 0)
		throw AnalysisEngineException("Error: Called ToString() with a node other than a definition node. Node name: " + name + ".");

	string definitionId = XmlCommon::GetAttributeByName(definition, "id");
	definitionId = Common::PadString(definitionId, 11);

	//	Get the reference source
	DOMElement *reference = XmlCommon::FindNodeNS(definition, "reference");
	string strReferenceId = "";
	string strReferenceSource = "";
	if(reference == NULL) {
		strReferenceId = "Not specified";
		strReferenceSource = "Not specified";
	} else {
		strReferenceId = XmlCommon::GetDataNodeValue(reference);
		strReferenceSource = XmlCommon::GetAttributeByName(reference, "source");
	}
	strReferenceSource = Common::PadString(strReferenceSource, 20);
	
	strDefinition.append("    " + definitionId + " " + strReferenceSource + " " + strReferenceId);

	return strDefinition;
}

//****************************************************************************************//
//							AnalysisEngineException Class							  //	
//****************************************************************************************//
AnalysisEngineException::AnalysisEngineException(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
	//
	// -----------------------------------------------------------------------

}

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

