//
// $Id: FileAttributeData.cpp,v 1.4 2004/09/02 15:29:57 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
//
//****************************************************************************************//
//
//  file:	FileAttributeData.cpp
//  author: Jon Baker	
//  date:   13 Februauy 2004
//
//	This class will store all data for a Linux files attributes. 
//	The class is a child class of ProbeData and implments the Write() and ParseNode() function 
//****************************************************************************************//

#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. Only ELEMENT_NODE's are allowed.");
	}else
	{
		testName = DOMCommon::ToString(test->getNodeName());
		if(testName.compare("file_test") != 0)
			throw ProbeDataException("Error: FileAttributeData() Invalid test node specified. Received: " + testName);
	}

	//	Init the data members
    testId		= "";
	path		= new TypedData(LITTERAL_TYPE, "");
	exists		= false;
	type		= "";
	group_id	= "";
	user_id		= "";
	a_time		= "";
	c_time		= "";
	m_time		= "";
	md5			= "";
	msg			= "";
	nvpPath		= NULL;

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

FileAttributeData::FileAttributeData()
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Initialize a new FileAttributeData as an empty object.
	// -----------------------------------------------------------------------

    testId		= "";
	path		= new TypedData(LITTERAL_TYPE, "");
	exists		= false;
	type		= "";
	group_id	= "";
	user_id		= "";
	a_time		= "";
	c_time		= "";
	m_time		= "";
	md5			= "";
	msg			= "";
	nvpPath		= NULL;
}

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

}

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

	//	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;

if(nvpPath == NULL)
  cout << " path vector is null" << endl;

        if(nvpPath != NULL)
        {
	  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 path element - as a string
		AddChildNode(dataDocument, file_test, "path", path->data);
		
		//	Add the type element
		AddChildNode(dataDocument, file_test, "type", type, DetermineDataType(type));

		//	Add the group_id element
		AddChildNode(dataDocument, file_test, "group_id", group_id, INTEGER_DATATYPE);

		//	Add the user_id element
		AddChildNode(dataDocument, file_test, "user_id", user_id, INTEGER_DATATYPE);

		//	Add the a_time element
		AddChildNode(dataDocument, file_test, "a_time", a_time, DetermineDataType(a_time));

		//	Add the c_time element
		AddChildNode(dataDocument, file_test, "c_time", c_time, DetermineDataType(c_time));

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

	//	Add the message element
	AddChildNode(dataDocument, file_test, "message", msg);	
}

// ***************************************************************************************	//
//								Private members												//
// ***************************************************************************************	//
void FileAttributeData::ParseNode(DOMNode *test)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Parse the node gathering all data to popular this FileAttributeData
	//	Really just need to gather the Test id.
	// -----------------------------------------------------------------------

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


	///////////////////////////////////////////////////////////////////////////
	//	Gather the data needed to call the probe
	///////////////////////////////////////////////////////////////////////////
	string childName		= "";
	string childOp			= "";
	string childDataType	= "";
	string tmp = "";

	// Create a new path vector
	nvpPath = new nvpVector();

	DOMNode *fileChild;
	DOMNodeList *fileChildList;

	//	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 child's operator
			childOp = DOMCommon::GetAttributeByName(fileChild, "operator");

			//	Get the childs datatype
			childDataType = DOMCommon::GetAttributeByName(fileChild, "datatype");

			
			//	Get the path value	
			if(strncmp(childName.c_str(), "path", 7)==0)
			{
				if(childOp.compare("pattern match") == 0)
					path->type = PATTERN_MATCH_TYPE;
				else
					path->type = LITTERAL_TYPE;

				if(childDataType.compare("component") == 0)
				{
					//	Loop through path children getting the value of each child
					DOMNodeList *tmpChildList = fileChild->getChildNodes();
					unsigned int tmpIndex = 0;
					while(tmpIndex < tmpChildList->getLength())
					{
						DOMNode *tmpChild = tmpChildList->item(tmpIndex);

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

							if(pathComponentType.compare("literal") == 0)
							{
								tmp = DOMCommon::GetDataNodeValue(tmpChild);
								
								//	Preserve the path components
								nvpPath->push_back(new NameValuePair("component", 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
				{
					path->data = DOMCommon::GetDataNodeValue(fileChild);
				}
			}
		}
		index ++;
	}
}
