//
// $Id: FileAttributesProbe.cpp,v 1.3 2004/08/25 16:07:29 bakerj Exp $
//
//************************** Property of the MITRE Corporation ***************************//
//
// Copyright (c) 2003 - The MITRE Corporation
//
// This file is part of the OVAL Definition Interpreter.
//
// The OVAL Definition 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 Definition 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
// Definition Interpreter; if not, write to the Free Software Foundation, Inc., 59 Temple
// Place, Suite 330, Boston, MA 02111-1307 USA
//
//****************************************************************************************//

#include "FileAttributesProbe.h"

//****************************************************************************************//
//								FileAttributesProbe Class								  //	
//****************************************************************************************//
FileAttributesProbe::FileAttributesProbe()
{
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//
	//	Do nothing for now
	// -----------------------------------------------------------------------

}

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

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  Public Members  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//

pdVector FileAttributesProbe::Run(ProbeData *probeDataIn)
{
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//
	//	If a path is specified in fattIn->path just get the data for the file.
	//	If a pattern is specified in fattIn->pattern get all the files on the ssytem that
	//	match the pattern. 
	//	If niether is specified throw an exception.
	//
	//	OPTIMIZATION:
	//	Once a match is found get the data for the file. Currently all files that match 
	//	are found then the data is collected for each file. 
	//	
	//	NOTE:
	//	The Search attribute is not handled
	// -----------------------------------------------------------------------

	FileAttributeData *fattIn = (FileAttributeData*)probeDataIn;
	FileAttributeData *fattOut;
	pdVector returnVector;
	sVector pathVector;
	sVector drives;


	//	If a pattern is set call pattern matching code
	if(fattIn->path->type == PATTERN_MATCH_TYPE)
	{		
		try
		{
			//	Reset the pattern mathing object
			myMatcher->Reset();

			//	Call pattern matching code
			pathVector = PathPatternMatch(fattIn->path->data);

			//	Loop through all paths found and get the data
			sVector::iterator path;
			for (path=pathVector.begin(); path!=pathVector.end(); path++)
			{
				fattOut = GetFileAttributes((*path));
				fattOut->path->type = PATTERN_MATCH_TYPE;
				returnVector.push_back(fattOut);
			}

			//	Ensure that each test has at least one result FileAttribute
			if(returnVector.size() == 0)
			{
				fattOut = new FileAttributeData();
				fattOut->msg = "No files found that match the specified pattern.";
				returnVector.push_back(fattOut);
			}

		}catch(Exception ex)
		{
			fattOut = new FileAttributeData();
			fattOut->msg = ex.GetErrorMessage();
			returnVector.push_back(fattOut);

		}catch(...)
		{
			fattOut = new FileAttributeData();
			fattOut->msg = "Error: FileAttributesProbe() an unknown exception has occured.";
			returnVector.push_back(fattOut);
		}


	//	If litteral Get file attributes and data
	}else if (fattIn->path->type == LITTERAL_TYPE)
	{
		fattOut = GetFileAttributes(fattIn->path->data);
		returnVector.push_back(fattOut);

	//	Must be either litteral or pattern match type
	}else
	{
		throw FileAttributesProbeException("Error: Either a path or a pattern must be specified for the File Attributes Probe to run.");
	}

	//	Before returning ensure that all tests have the testId set
	pdVector::iterator pdIterator;
	for (pdIterator=returnVector.begin(); pdIterator!=returnVector.end(); pdIterator++)
	{
		(*pdIterator)->SetTestId(fattIn->GetTestId());
	}

	return (returnVector);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  Private Members  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//

sVector FileAttributesProbe::GetDrives()
{
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//
	//	Get all fixed drives on the system. Return them in a string vector
	// -----------------------------------------------------------------------
	sVector drives;	
	unsigned int index	= 0;
	string tmp			= "";
	string drive		= "";
	string errMsg		= "";
	DWORD nBufferLength = 0;
	DWORD dwResult		= 0;
	LPTSTR lpBuffer		= new char[0];

	//	Get the required buffer size
	dwResult = GetLogicalDriveStrings(	nBufferLength,	// size of buffer
										lpBuffer);		// drive strings buffer

	if(dwResult > nBufferLength)
	{
		//	Call the function again with the correct buffer size
		delete [] (lpBuffer); 
		lpBuffer = new char[dwResult]; 
		nBufferLength = dwResult;
		dwResult = GetLogicalDriveStrings(	nBufferLength,	// size of buffer
											lpBuffer);		// drive strings buffer
	
	}else if(dwResult == 0)
	{
		//	Error check GetLastError 
		char strErrorCode[33];
		_itoa(GetLastError(), strErrorCode, 10);
		errMsg.append("Error: Unable to enumerate the drives on the system. Error code: ");
		errMsg.append(strErrorCode);
		errMsg.append("\n");

	}else 
	{
		//	Unknown Error
		errMsg.append("Error: Unable to enumerate the drives on the system. (Unknown error)\n");
	}

	
	if(dwResult == 0)
	{
		//	Error check GetLastError 
		char strErrorCode[33];
		_itoa(GetLastError(), strErrorCode, 10);
		errMsg.append("Error: Unable to enumerate the drives on the system. Error code: ");
		errMsg.append(strErrorCode);
		errMsg.append("\n");

	//	Process the list of drives
	}else
	{
		while(index < dwResult)
		{
			tmp = lpBuffer[index];
			index += 4;
			drive.append(tmp);
			drive.append(":\\");
			
			//	Only fixed drives
			if(GetDriveType(drive.c_str()) == DRIVE_FIXED)
				drives.push_back(drive);

			drive = "";			
		}	
	}

	return drives;
}

FileAttributeData* FileAttributesProbe::GetFileAttributes(string fileIn)
{
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//
	//	Get all attributes for the file specified in fileIn..Return them in a 
	//	FileAttribute
	// -----------------------------------------------------------------------

	HANDLE hFile;
	DWORD res;
	char buf[512];
	string errorMessage = "";

	FileAttributeData *fattOut = new FileAttributeData();


	try
	{
		//////////////////////////////////////////////////////
		/////////////////////  FilePath  /////////////////////
		//////////////////////////////////////////////////////

		fattOut->path->data = fileIn;

		//////////////////////////////////////////////////////
		//////////////////////  Owner  ///////////////////////
		//////////////////////////////////////////////////////

		// Get the handle of the object.
		//
		// SMC-AUDIT: ISSUE: should probably verify that this is a regular file before opening,
		// instead of a virtual memory file!
		//
		// ARB:

		try
		{
			hFile = CreateFile(fileIn.c_str(),				// file name
							   GENERIC_READ,				// access mode
							   FILE_SHARE_READ,				// share mode
							   NULL,						// SD
							   OPEN_EXISTING,				// how to create
							   FILE_ATTRIBUTE_NORMAL,		// file attributes
							   NULL);						// handle to template file

			if (hFile == INVALID_HANDLE_VALUE)
			{
				DWORD errorNum = GetLastError();

				if(errorNum == ERROR_FILE_NOT_FOUND)
				{
					errorMessage.append("(FileAttributesProbe) The file '");
					errorMessage.append(fileIn);
					errorMessage.append("' could not be found.");
					throw FileAttributesProbeException(errorMessage);
				}
				else if(errorNum == ERROR_PATH_NOT_FOUND)
				{
					errorMessage.append("(FileAttributesProbe) The path '");
					errorMessage.append(fileIn);
					errorMessage.append("' does not exist.");
					throw FileAttributesProbeException(errorMessage);
				}
				else
				{
					char errorCodeBuffer[33];
					_ltoa(errorNum, errorCodeBuffer, 10);

					errorMessage.append("(FileAttributesProbe) Unable to open a handle to the file '");
					errorMessage.append(fileIn);
					errorMessage.append("'.  Error Code - ");
					errorMessage.append(errorCodeBuffer);
					throw FileAttributesProbeException(errorMessage);
				}
			}

			//	Set the path since the file was open successfully. 
			fattOut->path->data = fileIn;
			fattOut->exists = true;

			PSID owner_sid;
			PSECURITY_DESCRIPTOR p_sd;

			// Get the owner SID of the file.

			res = GetSecurityInfo(hFile,						// handle to object
								  SE_FILE_OBJECT,				// object type
								  OWNER_SECURITY_INFORMATION,	// information type
								  &owner_sid,					// owner SID
								  NULL,							// primary group SID
								  NULL,							// DACL
								  NULL,							// SACL
								  &p_sd);						// SD

			if (res != ERROR_SUCCESS)
			{
				string errorMessage = "";
				errorMessage.append("(FileAttributesProbe) Unable to get the security descriptor for the file '");
				errorMessage.append(fileIn);
				errorMessage.append("'.\n");
				throw FileAttributesProbeException(errorMessage);
			}

			// First call to LookupAccountSid to get the buffer sizes.

			LPTSTR aname = NULL;
			LPTSTR dname = NULL;
			DWORD dwaname = 0;
			DWORD dwdname = 0;
			SID_NAME_USE eUse;

			res = LookupAccountSid(NULL,					// name of local or remote computer
								   owner_sid,				// security identifier
								   aname,					// account name buffer
								   (LPDWORD)&dwaname,		// size of account name buffer
								   dname,					// domain name
								   (LPDWORD)&dwdname,		// size of domain name buffer
								   &eUse);					// SID type

			// Reallocate memory for the buffers.

			aname = (LPTSTR) malloc(dwaname);
			if (aname == NULL)
			{
				string errorMessage = "";
				errorMessage.append("(FileAttributesProbe) Could not allocate space for the file '");
				errorMessage.append(fileIn);
				errorMessage.append("'.");
				throw FileAttributesProbeException(errorMessage);
			}

			dname = (LPTSTR) malloc(dwdname);
			if (dname == NULL)
			{
				free(aname);

				string errorMessage = "";
				errorMessage.append("(FileAttributesProbe) Could not allocate space for the file '");
				errorMessage.append(fileIn);
				errorMessage.append("'.");
				throw FileAttributesProbeException(errorMessage);
			}

			// Second call to LookupAccountSid to get the account name.

			res = LookupAccountSid(NULL,					// name of local or remote computer
								   owner_sid,				// security identifier
								   aname,					// account name buffer
								   (LPDWORD)&dwaname,		// size of account name buffer
								   dname,					// domain name
								   (LPDWORD)&dwdname,		// size of domain name buffer
								   &eUse);					// SID type

			if (res == FALSE)
			{
				free(aname);
				free(dname);

				string errorMessage = "";
				errorMessage.append("(FileAttributesProbe) Unable to get the name of the account ");
				errorMessage.append("for this SID for the file '");
				errorMessage.append(fileIn);
				errorMessage.append("'.");
				throw FileAttributesProbeException(errorMessage);
			}
			
			//	Set owner 
			fattOut->owner = aname;

			free(aname);
			free(dname);

		}catch(FileAttributesProbeException ex)
		{	
			fattOut->msg.append("Message: " + ex.GetErrorMessage());

		}catch(...)
		{	
			string errMsg = "";
			errMsg.append("(FileAttributesProbe) Unknown error attempting to get the owner of the file '");
			errMsg.append(fileIn);
			errMsg.append("' ");

			fattOut->msg.append("Message: " + errorMessage);
			fattOut->msg.append("Error Message: " + errMsg);
		}

		//////////////////////////////////////////////////////
		/////////////////////  FileSize  /////////////////////
		//////////////////////////////////////////////////////

		struct _stat statusBuffer;
		int result;
 
		// Get status information associated with the file.

		result = _stat(fileIn.c_str(), &statusBuffer);
		if (result < 0)
		{
			string errorMessage = "";
			errorMessage.append("(FileAttributesProbe) Unable to get status information ");
			errorMessage.append("associated with the file '");
			errorMessage.append(fileIn);
			errorMessage.append("'.\n");
			//throw FileAttributesProbeException(errorMessage);
		}

		// Add file size to SQL.

		ZeroMemory(buf, sizeof(buf));
		_snprintf(buf, sizeof(buf)-1, "%ld", statusBuffer.st_size);
		buf[sizeof(buf)-1] = '\0';
		
		//	Set size
		fattOut->size = buf;

		//////////////////////////////////////////////////////
		/////////////////////  Accessed  /////////////////////
		//////////////////////////////////////////////////////

		fattOut->a_time = Common::ToString((long)statusBuffer.st_atime);

		//////////////////////////////////////////////////////
		/////////////////////  Created  /////////////////////
		//////////////////////////////////////////////////////

		fattOut->c_time = Common::ToString((long)statusBuffer.st_ctime);

		//////////////////////////////////////////////////////
		/////////////////////  Modified  /////////////////////
		//////////////////////////////////////////////////////

		fattOut->m_time = Common::ToString((long)statusBuffer.st_mtime);

		//////////////////////////////////////////////////////
		////////////////////  MSChecksum  ////////////////////
		//////////////////////////////////////////////////////

		DWORD headersum;  
		DWORD checksum;

		res = MapFileAndCheckSum(const_cast<char*>(fileIn.c_str()), &headersum, &checksum);
		if (res != CHECKSUM_SUCCESS)
		{
			errorMessage.append("(FileAttributesProbe) Unable to get ms_checksum information for the file '");
			errorMessage.append(fileIn);
			errorMessage.append("' \n");
			//fattOut->ms_checksum = "(error)";
		}
		else
		{
			ZeroMemory(buf, sizeof(buf));
			_snprintf(buf, sizeof(buf)-1, "%d", checksum);
			buf[sizeof(buf)-1] = '\0';

			fattOut->ms_checksum = buf;
		}

		//////////////////////////////////////////////////////
		////////////////////////  MD5  ///////////////////////
		//////////////////////////////////////////////////////

		FILE* fp = NULL;
		fp = fopen(fileIn.c_str(), "r");
		if (fp == NULL)
		{
			errorMessage.append("(FileAttributesProbe) Unable to get MD5 information for the file '");
			errorMessage.append(fileIn);
			errorMessage.append("' \n");
			//fattOut->md5 = "(error)";
		}
		else
		{
			// Create the md5 hash.  This constructor creates a new md5 object, updates the hash,
			// finalizes the hash, and closes the FILE object.
			
			MD5 context(fp);

			ZeroMemory(buf, sizeof(buf));
			_snprintf(buf, sizeof(buf)-1, "%s", context.hex_digest());
			buf[sizeof(buf)-1] = '\0';

			fattOut->md5 = buf;
		}

		//////////////////////////////////////////////////////
		////////////////////  FileVersion  ///////////////////
		//////////////////////////////////////////////////////

		char ver1[16];
		char ver2[16];
		char ver3[16];
		char ver4[16];

		ZeroMemory(ver1, sizeof(ver1));
		ZeroMemory(ver2, sizeof(ver2));
		ZeroMemory(ver3, sizeof(ver3));
		ZeroMemory(ver4, sizeof(ver4));

		DWORD junk;
		DWORD versionsize;
		LPVOID versionbuf;

		// Get the required size of the version info buffer.

		versionsize = GetFileVersionInfoSize(fileIn.c_str(), &junk);
		if (versionsize > 0)
		{
			versionbuf = (LPVOID)malloc(versionsize);
			if (GetFileVersionInfo(fileIn.c_str(), 0, versionsize, versionbuf) == TRUE)
			{
				VS_FIXEDFILEINFO* pFFI = NULL;
				UINT vdatalen;

				if (VerQueryValue(versionbuf, "\\", (void**)&pFFI, &vdatalen) == FALSE)
				{
					errorMessage.append("(FileAttributesProbe) Unable to get version information for the file '");
					errorMessage.append(fileIn);
					errorMessage.append("' \n");

					_snprintf(ver1, sizeof(ver1)-1, "");
					_snprintf(ver2, sizeof(ver2)-1, "");
					_snprintf(ver3, sizeof(ver3)-1, "");
					_snprintf(ver4, sizeof(ver4)-1, "");
				}
				else if (pFFI == NULL)
				{
					errorMessage.append("(FileAttributesProbe) No version information available for the file '");
					errorMessage.append(fileIn);
					errorMessage.append("' \n");

					_snprintf(ver1, sizeof(ver1)-1, "");
					_snprintf(ver2, sizeof(ver2)-1, "");
					_snprintf(ver3, sizeof(ver3)-1, "");
					_snprintf(ver4, sizeof(ver4)-1, "");
				}
				else
				{
					_snprintf(ver1, sizeof(ver1)-1, "%d", (HIWORD(pFFI->dwFileVersionMS)));
					_snprintf(ver2, sizeof(ver2)-1, "%d", (LOWORD(pFFI->dwFileVersionMS)));
					_snprintf(ver3, sizeof(ver3)-1, "%d", (HIWORD(pFFI->dwFileVersionLS)));
					_snprintf(ver4, sizeof(ver4)-1, "%d", (LOWORD(pFFI->dwFileVersionLS)));
				}

				//////////////////////////////////////////////////////
				///////////////  DevelopementClass  //////////////////
				//////////////////////////////////////////////////////

				//	Get the language-code page and construct the string for file version request
				DWORD *lpTransArray;
				TCHAR szSubblock[80];
				TCHAR szSubblockHeader[25];
				int retVal = VerQueryValue(versionbuf,  
							  TEXT("\\VarFileInfo\\Translation"),
							  (LPVOID*)&lpTransArray,
							  &vdatalen);

				if(retVal != 0) 
				{	
					//	Convert the code page info into a zero-terminated
					//	string specifying which version-information value to retrieve
					_stprintf(szSubblockHeader, TEXT("\\StringFileInfo\\%04X%04X"), LOWORD(lpTransArray[0]), HIWORD(lpTransArray[0]));					
					_stprintf(szSubblock, TEXT("%s\\%s"), szSubblockHeader, TEXT("FileVersion"));

					//	Get the file's developement class
					LPTSTR lpszValue;
					retVal = VerQueryValue(versionbuf,
											szSubblock, 
											(LPVOID *)&lpszValue, 
											&vdatalen);
								  
					if(retVal != 0) 
					{
						//	Check to see if the version string has a developement path string in it
						string verStr = lpszValue;
						REGEX verMatcher;
						if(verMatcher.IsMatch(".+\\([^\\)].+\\)", verStr.c_str()))
						{
							//	Parse the version string
							verStr = verStr.substr(verStr.find("(") + 1);
							fattOut->development_class = verStr.substr(0, verStr.find("."));
						}

					}else if(vdatalen == 0)
					{
						errorMessage.append("(FileAttributesProbe) No value is available for the specified version-information name, \"szSubblock,\" for file: ");
						errorMessage.append(fileIn);
						errorMessage.append("' \n");

					}else if(retVal == 0)
					{
						errorMessage.append("(FileAttributesProbe) Either specified name, \"szSubblock,\" does not exist or the specified resource is not valid for file: ");
						errorMessage.append(fileIn);
						errorMessage.append("' \n");
						cout << "If the specified name does not exist or the specified resource is not valid, the return value is zero." << endl;
					}

				}else if(vdatalen == 0)
				{
					errorMessage.append("(FileAttributesProbe) No value is available for the specified version-information name, \"szSubblock,\" for file: ");
					errorMessage.append(fileIn);
					errorMessage.append("' \n");
				}else if(retVal == 0)
				{
					errorMessage.append("(FileAttributesProbe) Either specified name, \"szSubblock,\" does not exist or the specified resource is not valid for file: ");
					errorMessage.append(fileIn);
					errorMessage.append("' \n");
					cout << "If the specified name does not exist or the specified resource is not valid, the return value is zero." << endl;

				}

			}
			else
			{
				errorMessage.append("(FileAttributesProbe) Unable to get version information for the file '");
				errorMessage.append(fileIn);
				errorMessage.append("' \n");

				_snprintf(ver1, sizeof(ver1)-1, "");
				_snprintf(ver2, sizeof(ver2)-1, "");
				_snprintf(ver3, sizeof(ver3)-1, "");
				_snprintf(ver4, sizeof(ver4)-1, "");
			}

			free(versionbuf);
		}
		else
		{
			errorMessage.append("(FileAttributesProbe) No version information available for the file '");
			errorMessage.append(fileIn);
			errorMessage.append("' \n");

			_snprintf(ver1, sizeof(ver1)-1, "");
			_snprintf(ver2, sizeof(ver2)-1, "");
			_snprintf(ver3, sizeof(ver3)-1, "");
			_snprintf(ver4, sizeof(ver4)-1, "");
		}

		ver1[sizeof(ver1)-1] = '\0';
		ver2[sizeof(ver2)-1] = '\0';
		ver3[sizeof(ver3)-1] = '\0';
		ver4[sizeof(ver4)-1] = '\0';

		fattOut->majVer		= ver1;
		fattOut->minVer		= ver2;
		fattOut->buildVer	= ver3;
		fattOut->priVer		= ver4;

		//////////////////////////////////////////////////////
		/////////////////////  FileType  /////////////////////
		//////////////////////////////////////////////////////

		res = GetFileType(hFile);

		BOOL gfaRes;
		WIN32_FILE_ATTRIBUTE_DATA lpFileInformation;

		switch (res)
		{
			case FILE_TYPE_DISK:

				gfaRes = GetFileAttributesEx(fileIn.c_str(),				// file or directory name
											 GetFileExInfoStandard,			// attribute class
											 (LPVOID)&lpFileInformation);	// attribute information 

				if (lpFileInformation.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)
						fattOut->type = "FILE_ATTRIBUTE_DIRECTORY";
				else
						fattOut->type = "FILE_TYPE_DISK";

				break;

			case FILE_TYPE_CHAR:

				fattOut->type = "FILE_TYPE_CHAR";
				break;

			case FILE_TYPE_PIPE:

				fattOut->type = "FILE_TYPE_PIPE";
				break;

			case FILE_TYPE_UNKNOWN:

				errorMessage.append("(FileAttributesProbe) No file type information available for the file '");
				errorMessage.append(fileIn);
				errorMessage.append("' \n");
				
				break;

			default:

				errorMessage.append("(FileAttributesProbe) Unable to get file type information for the file '");
				errorMessage.append(fileIn);
				errorMessage.append("' \n");
				
				break;
		}

		//////////////////////////////////////////////////////
		//////////////////////////////////////////////////////
	}
	catch(FileAttributesProbeException ex)
	{	
		fattOut->msg.append("Message: " + ex.GetErrorMessage());

	}catch(...)
	{	
		string errMsg = "";
		errMsg.append("(FileAttributesProbe) Unknown error attempting to get file attribute information for the file '");
		errMsg.append(fileIn);
		errMsg.append("' \n");

		fattOut->msg.append("Message: " + errorMessage);
		fattOut->msg.append("Error Message: " + errMsg);
	}


	return (fattOut);
}

void FileAttributesProbe::GetFilePathsForPattern(string dirIn, string pattern, sVector *pathVector)
{
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//
	//  This function gets all file paths that match a given pattern.
	//	It will not attempt to match any file or directory that starts with a period or
	//	is named "System Volume Information". This is to avoid doing anything to the "." 
	//	and ".." files or accessing restricted direcoties.  
	//	This does call itself recursively as it must search all sub directories of dirIn.
	//	If a match is found the path is pushed on to a vector of strings.
	//
	// -----------------------------------------------------------------------

	try
	{
		//	Stop is a Null Dir
		if ((dirIn.empty() == true) || (dirIn == ""))
			return;

		// Verify that the path that was passed into this function ends with a slash.  If
		// it doesn't, then add one.
		if (dirIn[dirIn.length()-1] != '\\')
			dirIn.append(1, '\\');
		
		// Append a '*' to the end of the path to signify that we want to find all files
		// in given directory.
		string findDir;
		findDir = dirIn + "*";

		// Find the first file in the directory.  If this fails, then there is no reason
		// to continue.
		WIN32_FIND_DATA FindFileData;
		HANDLE hFind;

		hFind = FindFirstFile(findDir.c_str(), &FindFileData);
		if (hFind == INVALID_HANDLE_VALUE)
		{
			string errorMessage = "";

			errorMessage.append("Error: Unable to get a valid handle in GetFilePathsForPattern().\n\tDirectory: ");
			errorMessage.append(dirIn);
			errorMessage.append("\n\tPattern: ");
			errorMessage.append(pattern);
			throw FileAttributesProbeException(errorMessage);
		}

		//	Loop through each file in the directory.  
		//	If a sub-directory is found, make a recursive call to GetFilePathsForPattern to search its contents.
		//	If a file is found get the file path and check it against the pattern

		do
		{
			if ((strncmp(FindFileData.cFileName, ".", 1) == 0) ||
				(strncmp(FindFileData.cFileName, "..", 2) == 0) ||
				(strncmp(FindFileData.cFileName, "System Volume Information", 25) == 0))
			{
				// do nothing
			}else if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
			{
				string dirToSearch = dirIn;					
				dirToSearch.append(FindFileData.cFileName);

				GetFilePathsForPattern(dirToSearch, pattern, pathVector);
			
			}else
			{
				string filePath = dirIn;					
				filePath.append(FindFileData.cFileName);
	
				//	Check pattern
				if(myMatcher->IsMatch(pattern.c_str(), filePath.c_str()))
					pathVector->push_back(filePath);
			}
		} while (FindNextFile(hFind, &FindFileData));

		//	Close the handle to the file search object.
		if(!FindClose(hFind))
		{
			string errorMessage = "";
			errorMessage.append("Error: ");
			errorMessage.append("Unable to close search handle while trying to search for matching file paths. \n\tDirectory: ");
			errorMessage.append(dirIn);
			errorMessage.append("\n\tPattern: ");
			errorMessage.append(pattern);

			throw FileAttributesProbeException(errorMessage);	
		}



	//	Just need to ensure that all exceptions have a nice message. 
	//	So rethrow the exceptions I created catch the others and format them.
	}catch(Exception ex)
	{
		throw;

	}catch(...)
	{
		string errorMessage = "";
		errorMessage.append("Error: ");
		errorMessage.append("An unspecified error was encountered while trying to search for matching paths. \n\tDirectory: ");
		errorMessage.append(dirIn);
		errorMessage.append("\n\tPattern: ");
		errorMessage.append(pattern);

		throw FileAttributesProbeException(errorMessage);
	}
}

sVector FileAttributesProbe::PathPatternMatch(string pattern)
{
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//	Return a vector of paths that match pattern.
	//	Attempt to break off a constant portion of the path. 
	//
	// -----------------------------------------------------------------------

	sVector paths;
	sVector drives;
	string fileName = "";

	string patternOut= "";
	string constPortion= "";
	this->myMatcher->GetConstantPortion(pattern, "\\", &patternOut, &constPortion);
	// Remove extra slashes
	constPortion = myMatcher->RemoveExtraSlashes(constPortion);


	if(patternOut.compare("") != 0)
	{
		//	Call search function
		GetFilePathsForPattern(constPortion, pattern, &paths);

	}else if(constPortion.compare("") == 0)
	{
		//	No constant portion. 
		
		//	Must start searching for matches on each drive.
		drives = GetDrives();

		sVector::iterator drive;
		for (drive=drives.begin(); drive!=drives.end(); drive++)
		{
			//	Call search function
			try 
			{
				GetFilePathsForPattern((*drive), pattern, &paths);

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

	}else if(patternOut.compare("") == 0)
	{
		//	There are no pattern matching chars treat this as a normal path 
		paths.push_back(constPortion);
	}

	return paths;
}


//****************************************************************************************//
//							FileAttributesProbeException Class							  //	
//****************************************************************************************//
FileAttributesProbeException::FileAttributesProbeException() : Exception()
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Default constructor simply set the severity to ERROR_FATAL. This is 
	//	done with the explicit call to the Exception class default constructor.
	//
	// -----------------------------------------------------------------------

}

FileAttributesProbeException::FileAttributesProbeException(string errMsgIn) : Exception(errMsgIn)
{
	// -----------------------------------------------------------------------
	//	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.
	//
	// -----------------------------------------------------------------------

}

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

}