//
// $Id: FileAttributeData.cpp,v 1.4 2004/07/08 17:25:52 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 "FileAttributeData.h"

//****************************************************************************************//
//								FileAttributeData Class								  //	
//****************************************************************************************//
FileAttributeData::FileAttributeData(DOMNode *test)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Initialize a new FileAttributeData and populate with the node's data
	// -----------------------------------------------------------------------

	string testName;

	//	Validate the test node
	if(test->getNodeType() != DOMNode::ELEMENT_NODE )
	{
		throw ProbeDataException("Error: FileAttributeData() Invalid test node specified.");
	}else
	{
		testName = DOMCommon::ToString(test->getNodeName());
		if(testName.compare("file_test") != 0)
			throw ProbeDataException("Error: FileAttributeData() Invalid test node specified.");
	}

	//	Init the data members
	testId = "";
	path = new TypedData(LITTERAL_TYPE, "");
	exists = false;
	owner = "";
	size = "";
	a_time = "";
	c_time = "";
	development_class = "";
	m_time = "";
	ms_checksum = "";
	md5 = "";
	majVer = "";
	minVer = "";
	buildVer = "";
	priVer = "";
	type = "";
	msg = "";
	nvpPath = NULL;

	//	Call the parse node function
	ParseNode(test);
}

FileAttributeData::FileAttributeData(nvpVector *pathVectorIn)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Initialize a new FileAttributeData as an empty object.
	// -----------------------------------------------------------------------
	testId = "";
	path = new TypedData(LITTERAL_TYPE, "");
	exists = false;
	owner = "";
	size = "";
	a_time = "";
	c_time = "";
	development_class = "";
	m_time = "";
	ms_checksum = "";
	md5 = "";
	majVer = "";
	minVer = "";
	buildVer = "";
	priVer = "";
	type = "";
	msg = "";
	nvpPath = pathVectorIn;
}

FileAttributeData::~FileAttributeData()
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Do nothing for noe\w
	// -----------------------------------------------------------------------

}

// ***************************************************************************************	//
//								Public members												//
// ***************************************************************************************	//
nvpVector* FileAttributeData::GetPathVector()
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return a ptr to the path vector
	//	
	// -----------------------------------------------------------------------

	return nvpPath;
}


void FileAttributeData::Write(XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *dataDocument)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Create a new file_test node and add it to the dataDocumentument.
	//	
	// -----------------------------------------------------------------------

	DOMText *tmpTextNode	= NULL;
	DOMElement *tmpElem		= NULL; 

	//	Create a new test node
	DOMElement *file_test = CreateNewTestNode(dataDocument, "file_test");

	//	Create the path element
	DOMElement*  pathElem = dataDocument->createElement(XMLString::transcode("path"));
	if(!exists)
		DOMCommon::AddAttribute(pathElem, "exists","false");

	//	Add to the path element to the file_test
	file_test->appendChild(pathElem);
	
	//	Always add the path components to the path element
	//	Treat them all as string datatypes
	nvpVector::iterator pathIterator;
	for (pathIterator=nvpPath->begin(); pathIterator!=nvpPath->end(); pathIterator++)
		AddChildNode(dataDocument, pathElem, (*pathIterator)->elName, (*pathIterator)->elValue);

	//	If this is a pattern match add the filepath component to the path element
	//	as a string
	if(path->type == PATTERN_MATCH_TYPE)
		AddChildNode(dataDocument, pathElem, "filepath", path->data);

	
	if(exists)
	{
		//	Add the owner element - as a string
		AddChildNode(dataDocument, file_test, "owner", owner);

		//	Add the size element - as an int
		AddChildNode(dataDocument, file_test, "size", size, INTEGER_DATATYPE);

		//	Add the a_time element - as a string
		//	Look for a trailing <cr> and remove it.
		if(a_time.find_last_of("\n") == a_time.length()-1)
			a_time = a_time.substr(0, a_time.length()-1);
		
		AddChildNode(dataDocument, file_test, "a_time", a_time);

		//	Add the c_time element - as a string
		//	Look for a trailing <cr> and remove it.
		if(c_time.find_last_of("\n") == c_time.length()-1)
			c_time = c_time.substr(0, c_time.length()-1);
		
		AddChildNode(dataDocument, file_test, "c_time", c_time);

		//	Add the m_time element - as a string
		//	Look for a trailing <cr> and remove it.
		if(m_time.find_last_of("\n") == m_time.length()-1)
			m_time = m_time.substr(0, m_time.length()-1);
		
		AddChildNode(dataDocument, file_test, "m_time", m_time);

		//	Add the ms_checksum element - as an integer
		AddChildNode(dataDocument, file_test, "ms_checksum", ms_checksum, INTEGER_DATATYPE);

		//	Add the md5 element - as a string
		AddChildNode(dataDocument, file_test, "md5", md5);


		//	Create the version element and add to the file_test
		DOMElement*  versionElem = dataDocument->createElement(XMLString::transcode("version"));
		//	Specify the version datatype attribute
		DOMCommon::AddAttribute(versionElem, "datatype","version");

		if(majVer.compare("") == 0 && minVer.compare("") == 0 && buildVer.compare("") == 0 && priVer.compare("") == 0)
		{
			DOMCommon::AddAttribute(versionElem, "exists","false");
		}else
		{
			//	Add the major version to the version element
			AddChildNode(dataDocument, versionElem, "major", majVer);

			//	Add the minor version to the version element
			AddChildNode(dataDocument, versionElem, "minor", minVer);

			//	Add the build to the version element
			AddChildNode(dataDocument, versionElem, "build", buildVer);

			//	Add the private version to the version element
			AddChildNode(dataDocument, versionElem, "private", priVer);

		}
		file_test->appendChild(versionElem);

		//	Add the type element - as a string
		AddChildNode(dataDocument, file_test, "type", type);

		// Add the developement class element - as a string
		AddChildNode(dataDocument, file_test, "development_class", development_class);
	}

	//	Add the message element - as a string
	AddChildNode(dataDocument, file_test, "message", msg);
}

// ***************************************************************************************	//
//								Private members												//
// ***************************************************************************************	//
void FileAttributeData::ParseNode(DOMNode *test)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Parse a test node. Set all data elements for this FileAttributeData
	//	object.
	//	
	// -----------------------------------------------------------------------
	string childName			= "";
	string tmpPathComponent		= "";
	string tmp					= "";
	string pathComponentType	= "";
	string pathOperator			= "";
	string pathDatatype			= "";
	string errMsg				= "";

	nvpPath						= new nvpVector();

	DOMNode *fileChild			= NULL;
	DOMNode *tmpChild			= NULL;
	DOMNodeList *tmpChildList	= NULL;
	DOMNodeList *fileChildList	= NULL;

	REGEX myMatcher;

	unsigned int tmpIndex;

	///////////////////////////////////////////////////////////////////////////
	//	Gather file test data from XML
	///////////////////////////////////////////////////////////////////////////

	//	Get the test id
	testId = DOMCommon::GetAttributeByName(test, "id");
	if(testId.compare("") ==0)
		throw ProbeDataException("Error: Unable to find the 'id' attribute for a windows file test.");	


	//	get a list of the child nodes and their values
	fileChildList = test->getChildNodes();
	unsigned int index = 0;
	while(index < fileChildList->getLength())
	{
		fileChild = fileChildList->item(index);

		//	only concerned with ELEMENT_NODEs
		if (fileChild->getNodeType() == DOMNode::ELEMENT_NODE)
		{
			//	get the name of the child
			childName = DOMCommon::ToString(fileChild->getNodeName());
			
			//	Get the key value	
			if(strncmp(childName.c_str(), "path", 4)==0)
			{
				//	Get the operatior attribute on the path element
				pathOperator = DOMCommon::GetAttributeByName(fileChild, "operator");

				//	If a pattern match set type to pattern match
				if(pathOperator.compare("pattern match") == 0)
					path->type = PATTERN_MATCH_TYPE;
				else
					path->type = LITTERAL_TYPE;

				//	Get the datatype attribute on the path element
				pathDatatype = DOMCommon::GetAttributeByName(fileChild, "datatype");

				//	If the path datatype is component process the components
				if(pathDatatype.compare("component") == 0 )
				{
					//	Loop through path children getting the value of each child
					tmpChildList = fileChild->getChildNodes();
					tmpIndex = 0;
					while(tmpIndex < tmpChildList->getLength())
					{
						tmpChild = tmpChildList->item(tmpIndex);

						//	only concerned with ELEMENT_NODEs
						if (tmpChild->getNodeType() == DOMNode::ELEMENT_NODE)
						{
							//	Get the type of path component
							pathComponentType = DOMCommon::GetAttributeByName(tmpChild, "type");
							
							if(pathComponentType.compare("") == 0)
								throw ProbeDataException("Error: Type attribute missing from a file test key element.");

							if(strncmp(pathComponentType.c_str(), "registry_value", 14) == 0)
							{
								tmp = DOMCommon::GetDataNodeValue(tmpChild);
															
								//	Preserve the path components
								nvpPath->push_back(new NameValuePair("component", tmp));

								//	Get the value
								try
								{
									tmp = RegistryKeysProbe::GetRegKeyValue(tmp);
								}catch(Exception ex)
								{

								/*	
									It does not seem appropriate for probes to out put error messages when they
									are unable to find data they need taht is expected behavior.

									if(Log::verboseMode)
									{ 
										errMsg = "\n     File test: ";
										errMsg.append(testId);
										errMsg.append("\n--------------------------------------------------------------\n");
										errMsg.append(ex.GetErrorMessage());							
										errMsg.append("\n--------------------------------------------------------------\n");
										Log::WriteLog(errMsg);
										cout << errMsg << endl;		
									}
								*/
									errMsg = ex.GetErrorMessage();

									//	Reset the value of tmp 
									tmp = "";
								}

								//	Escape pattern match chars if a pattern match
								if(pathOperator.compare("pattern match") == 0)
									tmp = myMatcher.EscapeRegexChars(tmp);

							}else if(strncmp(pathComponentType.c_str(), "literal", 7) == 0)
							{
								tmp = DOMCommon::GetDataNodeValue(tmpChild);
								
								//	Preserve the path components
								nvpPath->push_back(new NameValuePair("component", tmp));

							
							}else if(strncmp(pathComponentType.c_str(), "environment_variable", 20) == 0)
							{
								tmp = DOMCommon::GetDataNodeValue(tmpChild);
								
								//	Preserve the path components
								nvpPath->push_back(new NameValuePair("component", tmp));

								//	Get the environment variable
								try
								{
									tmp = Common::GetEnviromentVariable(tmp);
								}catch(Exception ex)
								{
								/*	It does not seem appropriate for probes to out put error messages when they
									are unable to find data they need taht is expected behavior.
									if(Log::verboseMode)
									{
										Log::WriteLog(ex.GetErrorMessage());
										cout << ex.GetErrorMessage() << endl;		
									}
								*/
									errMsg = ex.GetErrorMessage();
								}

								//	Escape pattern match chars if a patern match
								if(pathOperator.compare("pattern match") == 0)
									tmp = myMatcher.EscapeRegexChars(tmp);
							}

							//	remove trailing \'s						
							if(tmp.find_last_of("\\") == tmp.length()-1 && tmp.compare("") != 0)
								tmp = tmp.substr(0, tmp.length()-1);
							
							//	concat - handle path delimiters
							if(path->data.length() > 0)
							{
								if(tmp.find("\\") != 0)
									path->data.append("\\");
							}

							//	Build the file path
							path->data.append(tmp);
						}
						tmpIndex++;
					}
				
				//	Path datatype is not component
				//
				//	NOTE: We would like to be able to do this, but it is currently not possible 
				//	with the oval schema this set of code should never get run if the xml used
				//	is valid.
				}else 
				{				
					//	Get the value of the path element
					path->data = DOMCommon::GetDataNodeValue(fileChild);

				}
				
				//	Preserve the pattern for output
				if(pathOperator.compare("pattern match") == 0)
				{
					nvpPath->push_back(new NameValuePair("pattern", path->data));

				//	Preserve path for output
				}else
				{
					nvpPath->push_back(new NameValuePair("filepath", path->data));
				}
			}
		}

		index ++;
	}

	//	Append any error messages to the msg field
	msg.append(errMsg);
}
