//
// $Id: AbsFileFinder.cpp,v 1.8 2006/08/18 14:01:45 bakerj Exp $
//
//****************************************************************************************//
// Copyright (c) 2006, 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 "AbsFileFinder.h"

AbsFileFinder::AbsFileFinder() {
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//
	//	Note that this is a protected constructor
	// -----------------------------------------------------------------------

	fileMatcher = new REGEX();
}

AbsFileFinder::~AbsFileFinder() {
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//
	// -----------------------------------------------------------------------

	delete fileMatcher;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  Public Members  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
StringPairVector* AbsFileFinder::SearchFiles(ObjectEntity* path, ObjectEntity* fileName, BehaviorVector* behaviors) {
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//
	//	Return a set of file items that match the specified path and filename
	// -----------------------------------------------------------------------

	StringPairVector* filePaths = new StringPairVector();

	// check datatypes - only allow string
	if(path->GetDatatype() != OvalEnum::STRING_TYPE) {
		throw ProbeException("Error: invalid data type specified on path. Found: " + OvalEnum::DatatypeToString(path->GetDatatype()));
	}
	if(fileName->GetDatatype() != OvalEnum::STRING_TYPE) {
		throw ProbeException("Error: invalid data type specified on fileName. Found: " + OvalEnum::DatatypeToString(fileName->GetDatatype()));
	}

	// check operation - only allow  equals, not equals and pattern match
	if(path->GetOperation() != OvalEnum::EQUALS_OPERATION 
		&& path->GetOperation() != OvalEnum::PATTERN_MATCH_OPERATION 
		&& path->GetOperation() != OvalEnum::NOT_EQUAL_OPERATION) {
		throw ProbeException("Error: invalid operation specified on path. Found: " + OvalEnum::OperationToString(path->GetOperation()));
	}
	if(fileName->GetOperation() != OvalEnum::EQUALS_OPERATION 
		&& fileName->GetOperation() != OvalEnum::PATTERN_MATCH_OPERATION 
		&& fileName->GetOperation() != OvalEnum::NOT_EQUAL_OPERATION) {
		throw ProbeException("Error: invalid operation specified on fileName. Found: " + OvalEnum::OperationToString(fileName->GetOperation()));
	}

	// get all the paths
	StringVector* paths = this->GetPaths(path);
	StringVector::iterator pathIt;
	for(pathIt = paths->begin(); pathIt != paths->end(); pathIt++) {
		
		// get all files if the file is not nil
		if(fileName->GetNil()) {

			StringPair* filePath = new StringPair((*pathIt), "");
            filePaths->push_back(filePath);

		} else {

			StringVector* fileNames = this->GetFileNames((*pathIt), fileName, behaviors);
			StringVector::iterator fileNameIt;
			for(fileNameIt = fileNames->begin(); fileNameIt != fileNames->end(); fileNameIt++) {

				StringPair* filePath = new StringPair((*pathIt), (*fileNameIt));
				filePaths->push_back(filePath);

			}
			delete fileNames;
		}
	}
	delete paths;

	return filePaths;
}


StringVector* AbsFileFinder::GetPaths(ObjectEntity* path) {
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//
	//	Search the file system for paths that match the provided ObjectEntity.
	//  Return a vector of matching file paths.  
	// -----------------------------------------------------------------------

	StringVector* paths = new StringVector();

	// does this path use variables?
	if(path->GetVarRef() == NULL) {
		
		// proceed based on operation
		if(path->GetOperation() == OvalEnum::EQUALS_OPERATION) {
			if(this->PathExists(path->GetValue())) {
				paths->push_back(path->GetValue());
			}

		} else if(path->GetOperation() == OvalEnum::NOT_EQUAL_OPERATION) {
			// turn the provided path value into a negative pattern match
			// then get all that match the pattern
			this->FindPaths(path->GetValue(), paths, false);

		} else if(path->GetOperation() == OvalEnum::PATTERN_MATCH_OPERATION) {
			this->FindPaths(path->GetValue(), paths);
		}		

	} else {

		StringVector* allPaths = new StringVector();

		if(path->GetOperation() == OvalEnum::EQUALS_OPERATION) {
			// in the case of equals simply loop through all the 
			// variable values and add them to the set of all paths
			// if they exist on the system
			VariableValueVector::iterator iterator;
			for(iterator = path->GetVarRef()->GetValues()->begin(); iterator != path->GetVarRef()->GetValues()->end(); iterator++) {				
				if(this->PathExists((*iterator)->GetValue())) {
					paths->push_back((*iterator)->GetValue());
				}
			}

		} else {
			// for not equals and pattern match fetch all paths that match
			// any of the variable values. Then analyze each path found on 
			// the system against the variable values 			

			// loop through all variable values and call findFiles
			VariableValueVector* values = path->GetVariableValues();
			VariableValueVector::iterator iterator;
			for(iterator = values->begin(); iterator != values->end(); iterator++) {
				if(path->GetOperation() == OvalEnum::NOT_EQUAL_OPERATION) {
					this->FindPaths((*iterator)->GetValue(), allPaths, false);
				} else {
					this->FindPaths((*iterator)->GetValue(), allPaths, true);
				}
			}
		}

		// only keep paths that match operation and value and var check
		ItemEntity* tmp = new ItemEntity("path","", OvalEnum::STRING_TYPE, true, OvalEnum::EXISTS_STATUS);
		StringVector::iterator it;
		for(it = allPaths->begin(); it != allPaths->end(); it++) {
			tmp->SetValue((*it));
			if(path->Analyze(tmp) == OvalEnum::TRUE_RESULT) {
				paths->push_back((*it));
			}
		}
		delete tmp;
		delete allPaths;
	}

	return paths;
}

StringVector* AbsFileFinder::GetFileNames(string path, ObjectEntity* fileName, BehaviorVector* behaviors) {
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//
	//	Search the file system for files under the specified path that match the 
	//	provided ObjectEntity.
	//  Return a vector of matching file paths.  
	// -----------------------------------------------------------------------

	StringVector* fileNames = new StringVector();

	// does this fileName use variables?
	if(fileName->GetVarRef() == NULL) {
		
		// proceed based on operation
		if(fileName->GetOperation() == OvalEnum::EQUALS_OPERATION) {
			if(this->FileNameExists(path, fileName->GetValue())) {
				fileNames->push_back(fileName->GetValue());
			}

		} else if(fileName->GetOperation() == OvalEnum::NOT_EQUAL_OPERATION) {
			// turn the provided fileName value into a negative pattern match
			// then get all that match the pattern
			this->GetFilesForPattern(path, fileName->GetValue(), behaviors, fileNames, false);

		} else if(fileName->GetOperation() == OvalEnum::PATTERN_MATCH_OPERATION) {
			this->GetFilesForPattern(path, fileName->GetValue(), behaviors, fileNames);
		}		

	} else {

		StringVector* allFileNames = new StringVector();

		if(fileName->GetOperation() == OvalEnum::EQUALS_OPERATION) {
			// in the case of equals simply loop through all the 
			// variable values and add them to the set of all file names
			// if they exist on the system
			VariableValueVector::iterator iterator;
			for(iterator = fileName->GetVarRef()->GetValues()->begin(); iterator != fileName->GetVarRef()->GetValues()->end(); iterator++) {
				
				if(this->FileNameExists(path, (*iterator)->GetValue())) {
					fileNames->push_back((*iterator)->GetValue());
				}
			}

		} else {
	
			// for not equals and pattern match fetch all files that
			// match any of the variable values.		

			// loop through all variable values and call GetFilesForPattern
			VariableValueVector* values = fileName->GetVariableValues();
			VariableValueVector::iterator iterator;
			for(iterator = values->begin(); iterator != values->end(); iterator++) {
				if(fileName->GetOperation() == OvalEnum::NOT_EQUAL_OPERATION) {
					this->GetFilesForPattern(path, (*iterator)->GetValue(), behaviors, allFileNames, false);
				} else {
					this->GetFilesForPattern(path, (*iterator)->GetValue(), behaviors, allFileNames, true);
				}
			}
		}

		// only keep files that match operation and value and var check
		ItemEntity* tmp = new ItemEntity("filename","", OvalEnum::STRING_TYPE, true, OvalEnum::EXISTS_STATUS);
		StringVector::iterator it;
		for(it = allFileNames->begin(); it != allFileNames->end(); it++) {
			tmp->SetValue((*it));
			if(fileName->Analyze(tmp) == OvalEnum::TRUE_RESULT) {
				fileNames->push_back((*it));
			}
		}
		delete tmp;
		delete allFileNames;
	}

	return fileNames;
}

bool AbsFileFinder::IsMatch(string pattern, string value, bool isRegex) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return true if the specified value matches the provided pattern 
	//	based on the type of match specified. Match types are either 
	//	regex or not equal
	//
	// -----------------------------------------------------------------------

	bool match = false;

	if(isRegex) {
		if(this->fileMatcher->IsMatch(pattern.c_str(), value.c_str())) {
			match = true;
		}
	} else {	
		if(value.compare(pattern) != 0) {
			match = true;
		}
	}

	return match;
}

//****************************************************************************************//
//								FileFinderException Class								  //	
//****************************************************************************************//
FileFinderException::FileFinderException(string errMsgIn, int severity, Exception* ex) : Exception(errMsgIn, severity, ex) {
	// -----------------------------------------------------------------------
	//	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.
	//
	// -----------------------------------------------------------------------

}

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

}
