//
// $Id: FileFinder.cpp,v 1.7 2005/04/12 13:32:11 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 "FileFinder.h"


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

}

FileFinder::~FileFinder()
{
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//
	//	Do nothing for now
	// -----------------------------------------------------------------------

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  Private Members  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
sVector FileFinder::FindFiles(string regex)
{
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//
	//	Search the file system for files whose path matches the provided regex.
	//  Return a vector of matching file paths.  
	//	Attempt to break off a constant portion of the path to spead up searching
	// -----------------------------------------------------------------------
	sVector paths;
	string fileName = "";

	string patternOut= "";
	string constPortion= "";
	this->fileMatcher->GetConstantPortion(regex, "/", &patternOut, &constPortion);

	// Found a constant porion
	if(patternOut.compare("") != 0) {

		//	Call search function
		GetFilePathsForPattern(constPortion, regex, &paths);

	//	No constant portion.
	} else if(constPortion.compare("") == 0) { 
		
		//	Call search function
		try  {

			GetFilePathsForPattern("/", regex, &paths);

		} catch(REGEXException ex) {
			if(ex.GetSeverity() == ERROR_WARN) {
				string pcreMsg = "";
				pcreMsg.append("File Finder Warning:\n");
				pcreMsg.append("------------------------------\n");
				pcreMsg.append(ex.GetErrorMessage());
				if(Log::verboseMode) {
					cout << pcreMsg << "\n" << endl;
					Log::WriteLog(pcreMsg + "\n\n");
				}
			}
		}

	} else if(patternOut.compare("") == 0) {

		//	There are no pattern matching chars treat this as a normal path 
		paths.push_back(constPortion);
	}

	return paths;
}

void FileFinder::GetFilePathsForPattern(string dirIn, string pattern, sVector *pathVector)
{
	//------------------------------------------------------------------------------------//
	//
	//  ABSTRACT
	//
	//  Traverse the file system looking for matching file paths. Each matching path is 
	//	pushed onto the pathVector vector. 
	//
	//	fullpath may be a file or a directory. 
	//		- if it is a file check if it matches the pattern
	//		- if a directory call self recursively for each name in the directory
	//	
	//------------------------------------------------------------------------------------//

	struct stat statbuf;
	struct dirent *dirp;
	DIR *dp;
	string tmp = "";

	//	Call stat 
	if(lstat(dirIn.c_str(), &statbuf) < 0) {
		//	stat error
		//	not sure I want to report this now
		return;
	}

	//	If not a directory check if a match and return
	if(S_ISDIR(statbuf.st_mode) == 0) {
		if(fileMatcher->IsMatch(pattern.c_str(), dirIn.c_str()))
		  pathVector->push_back(dirIn);

		return;
	}
	
	///////////////////////////////////////////////////////
	//	It is a directory
	///////////////////////////////////////////////////////

	//	Append a '/'
	if(dirIn.at(dirIn.length()-1) != '/')
	  dirIn.append("/");

	//	Open the directory
	dp = opendir(dirIn.c_str());
	if(dp == NULL) {
		//	Error opening directory
		//	not sure this error matters
		// cout << "Failed to open the directory" << endl;
		return;
	}

	//	Loop through all names in the directory
	while((dirp = readdir(dp)) != NULL) {
		//	Ignore dot and dot-dot
		if(strcmp(dirp->d_name, ".") == 0 || strcmp(dirp->d_name, "..") == 0)
			continue;

		//	append the name after the "/"
		tmp = dirIn;
		tmp.append(dirp->d_name);

		// Nake recursive call
		GetFilePathsForPattern(tmp, pattern, pathVector);
	}


	//	Close the directory
	if(closedir(dp) < 0) {
		//	Error closing the directory
		//	not sure this error matters
		// cout << "Failed to close the directory" << endl;
		return;
	}
}
