//
// $Id: FileAttributeData.cpp,v 1.2 2004/06/22 12:45:18 bakerj Exp $
//
//************************** Property of the MITRE Corporation ***************************//
//
// Copyright (c) 2003 - The MITRE Corporation
//
// This file is part of the OVAL Query Interpreter project.
//
// The OVAL Query Interpreter 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 OVAL Query Interpreter 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
// OVAL Query Interpreter; 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(string filePathIn, DBInterface *dbIn)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Initialize a new FileAttributeData and pase the input file path
	//	into a correctly formatted file path
	// -----------------------------------------------------------------------


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

	db = dbIn;

	//	Call the parse function
	Parse(filePathIn);
}

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

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

}

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

void FileAttributeData::Write(DBInterface *db)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Write the data in this object to the database
	//
	//	TO DO:
	//	Determine how to handle error messages or really any text in the 
	//	msg string
	// -----------------------------------------------------------------------

	//	Only preform the insert if the file was found
	if(this->exists)
	{

		// Prepare the SQL statement.
		string sqlStmt = "";
		sqlStmt.append("INSERT INTO '");
		sqlStmt.append("Windows_FileAttributes");
		sqlStmt.append("' VALUES (");

		//	Filepath
		sqlStmt.append("'");
		sqlStmt.append(Common::FixQuotes(this->path->data));
		sqlStmt.append("'");

		//	Owner
		if(this->owner.compare("") == 0)
		{
			sqlStmt.append(",NULL");

		}else
		{
			sqlStmt.append(",'");
			sqlStmt.append(Common::FixQuotes(this->owner));
			sqlStmt.append("'");
		}

		//	File Size
		if(this->size.compare("") == 0)
		{
			sqlStmt.append(",NULL");

		}else
		{
			sqlStmt.append(",'");
			sqlStmt.append(Common::FixQuotes(this->size));
			sqlStmt.append("'");
		}

		//	Accessed
		if(this->a_time.compare("") == 0)
		{
			sqlStmt.append(",NULL");

		}else
		{
			sqlStmt.append(",'");
			sqlStmt.append(Common::FixQuotes(this->a_time));
			sqlStmt.append("'");
		}

		//	Created
		if(this->c_time.compare("") == 0)
		{
			sqlStmt.append(",NULL");

		}else
		{
			sqlStmt.append(",'");
			sqlStmt.append(Common::FixQuotes(this->c_time));
			sqlStmt.append("'");
		}

		//	Modified
		if(this->m_time.compare("") == 0)
		{
			sqlStmt.append(",NULL");

		}else
		{
			sqlStmt.append(",'");
			sqlStmt.append(Common::FixQuotes(this->m_time));
			sqlStmt.append("'");
		}

		//	Check Sum
		if(this->ms_checksum.compare("") == 0)
		{
			sqlStmt.append(",NULL");

		}else
		{
			sqlStmt.append(",'");
			sqlStmt.append(Common::FixQuotes(this->ms_checksum));
			sqlStmt.append("'");
		}

		//	MD5
		if(this->md5.compare("") == 0)
		{
			sqlStmt.append(",NULL");

		}else
		{
			sqlStmt.append(",'");
			sqlStmt.append(Common::FixQuotes(this->md5));
			sqlStmt.append("'");
		}

		//	Version
		if(this->majVer.compare("") == 0)
		{
			sqlStmt.append(",NULL");

		}else
		{
			sqlStmt.append(",");
			sqlStmt.append(Common::FixQuotes(this->majVer));
		}

		if(this->minVer.compare("") == 0)
		{
			sqlStmt.append(",NULL");

		}else
		{
			sqlStmt.append(",");
			sqlStmt.append(Common::FixQuotes(this->minVer));
		}

		if(this->buildVer.compare("") == 0)
		{
			sqlStmt.append(",NULL");

		}else
		{
			sqlStmt.append(",");
			sqlStmt.append(Common::FixQuotes(this->buildVer));
		}

		if(this->priVer.compare("") == 0)
		{
			sqlStmt.append(",NULL");

		}else
		{
			sqlStmt.append(",");
			sqlStmt.append(Common::FixQuotes(this->priVer));
		}


		//	Type
		if(this->type.compare("") == 0)
		{
			sqlStmt.append(",NULL");

		}else
		{
			sqlStmt.append(",'");
			sqlStmt.append(Common::FixQuotes(this->type));
			sqlStmt.append("'");
		}

		//	development_class
		if(this->development_class.compare("") == 0)
		{
			sqlStmt.append(",NULL");

		}else
		{
			sqlStmt.append(",'");
			sqlStmt.append(Common::FixQuotes(this->development_class));
			sqlStmt.append("'");
		}

		//	Close the statment
		sqlStmt.append(")");

		// Update the database.
		stringVector *svNULL = NULL;
		db->ExecSQL(sqlStmt, svNULL);
	}

	//	Display any messages if running in verbose mode
	if(Log::verboseMode && this->msg.compare("") != 0) 
	{
		string displayMsg = "\nMessage from the Windows File Attribute probe:\n";
		displayMsg.append("----------------------------------------------\n");
		displayMsg.append(this->msg);
		displayMsg.append("\n");
		
		cout << displayMsg << endl;;

		Log::WriteLog(displayMsg + "\n");
	}

}

// ***************************************************************************************	//
//								Private members												//
// ***************************************************************************************	//
string FileAttributeData::GetEnvironmentVariable(string envVarIn)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return the value of the environment variable specified. If it is not 
	//	found return NULL.
	//
	// -----------------------------------------------------------------------

	string value;
	value = getenv(envVarIn.c_str());

	//	Check the value 
	if(value.compare("") == 0)
	{
		string errMsg = "Message: Unable to find the value of: " + envVarIn + "\n";
		throw ProbeDataException(errMsg);
	}

	return (value);
}

void FileAttributeData::Parse(string filePathInput)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Parse a file path as it is stored in the database into a properly
	//  formatted file path for use with the probe
	//	Database format:
	//	type|value:type|value
	// -----------------------------------------------------------------------
	string tmpPathValue		= "";
	string tmpTypeAndValue	= "";
	string tmpPathType		= "";
	string errMsg			= "";
	unsigned int colon		= string::npos;
	unsigned int bar		= string::npos;
	bool isPatternMatch		= false;
	char* token				= NULL;
	char chColon[]			= ":";
	REGEX myMatcher;;

	///////////////////////////////////////////////////////////////////////////
	//	tokenize the input filepath
	///////////////////////////////////////////////////////////////////////////
	char* theString = (char*)malloc(filePathInput.length());
	theString = strcpy(theString, filePathInput.c_str());
	token = strtok(theString, chColon);

	while(token != NULL)
	{
		//	Get the first token
		tmpTypeAndValue = token;

		//	Break the string into type and value
		bar = tmpTypeAndValue.find("|"),
		tmpPathType = tmpTypeAndValue.substr(0, bar++);
		tmpPathValue = tmpTypeAndValue.substr(bar, tmpTypeAndValue.length());
 
		//	Handle the various types
		if(tmpPathType.compare("REG") == 0)
		{
			//	Get the value
			try
			{
				int lastSlash = tmpPathValue.find_last_of("\\");
				string tmpKey = tmpPathValue.substr(0, lastSlash);
				string tmpName = tmpPathValue.substr(lastSlash+1, tmpPathValue.length()-lastSlash);

				tmpPathValue = RegistryKeysProbe::GetRegKeyValue(tmpPathValue);

				//	This value must be inserted into the database
				string sqlStmt = "";
				sqlStmt.append("INSERT INTO ");
				sqlStmt.append("Windows_RegistryKeys");
				sqlStmt.append(" VALUES ('");
				//	Key
				sqlStmt.append(Common::FixQuotes(tmpKey));
				sqlStmt.append("','");
				//	Name
				sqlStmt.append(Common::FixQuotes(tmpName));
				// Type
				sqlStmt.append("',NULL,'");
				//	Value
				sqlStmt.append(Common::FixQuotes(tmpPathValue));
				sqlStmt.append("')");

				db->ExecSQL(sqlStmt);


				//	Escape pattern match chars if a pattern match
				if(isPatternMatch)
					tmpPathValue = myMatcher.EscapeRegexChars(tmpPathValue);

			}catch(Exception ex)
			{
				if(Log::verboseMode)
				{ 
					errMsg = "Message from Windows File Attribute probe:\n";
					errMsg.append("------------------------------------------\n");
					errMsg.append(ex.GetErrorMessage());							
					Log::WriteLog(errMsg);
					cout << errMsg << endl;		
				}

				//	Reset tmpPathValue
				tmpPathValue = "";
			}


		}else if(tmpPathType.compare("LIT") == 0)
		{
				//do nothing for now
				
		}else if(tmpPathType.compare("PAT") == 0)
		{
			if(!isPatternMatch)
			{
				//	Need to escape any regex chars that we not escaped in previous strings
				//	This handles registry keys that may not have been escaped because the 
				//	isPatternmatch flag had not been set
				path->data = myMatcher.EscapeRegexChars(path->data);

				isPatternMatch = true;
			}
				
		}else if(tmpPathType.compare("ENV") == 0)
		{
			//	Get the environment variable
			try
			{
				tmpPathValue = GetEnvironmentVariable(tmpPathValue);

				//	Escape pattern match chars if a patern match
				if(isPatternMatch)
					tmpPathValue = myMatcher.EscapeRegexChars(tmpPathValue);
			
			}catch(Exception ex)
			{
				if(Log::verboseMode)
				{
					errMsg = "Message from Windows File Attribute probe:\n";
					errMsg.append("------------------------------------------\n");
					errMsg.append(ex.GetErrorMessage());							
					Log::WriteLog(errMsg);
					cout << errMsg << endl;			
				}
			}
		}

		//	remove trailing \'s						
		if(tmpPathValue.find_last_of("\\") == tmpPathValue.length()-1 && tmpPathValue.compare("") != 0)
			tmpPathValue = tmpPathValue.substr(0, tmpPathValue.length()-1);
		
		//	concat - handle path delimiters
		if(path->data.length() > 0)
		{
			if(tmpPathValue.find("\\") != 0)
				path->data.append("\\");
		}
		
		//	If a pattern match set type to pattern match
		if(isPatternMatch)
			path->type = PATTERN_MATCH_TYPE;

		//	Build the file path
		path->data.append(tmpPathValue);

		//	Get the next token
		token = strtok(NULL, chColon);
	}
}