//
// $Id: FileProbe.cpp,v 1.11 2005/04/11 20:47:28 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 "FileProbe.h"

//****************************************************************************************//
//								FileProbe Class											  //	
//****************************************************************************************//
FileProbe *FileProbe::instance = NULL;

FileProbe::FileProbe()
{
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//
	//	Note that this is a private constructor
	// -----------------------------------------------------------------------

}

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

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  Public Members  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
Probe* FileProbe::Instance()
{

	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//
	//	Ensure that the FileProbe is a singleton.
	// -----------------------------------------------------------------------

	// Use lazy initialization
	if(instance == NULL) 
		instance = new FileProbe();

	return instance;	
}

pdVector FileProbe::Run(ProbeData *probeDataIn)
{
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//
	//	Get all the files on the system that match the pattern. 
	//
	// -----------------------------------------------------------------------

	FileData *fileDataIn = (FileData*)probeDataIn;
	FileData *fileDataOut;
	pdVector returnVector;
	sVector pathVector;
	sVector drives;
	
	try {
		if(fileDataIn->path->object.compare("") == 0)
			throw new FileProbeException("Error: No path was specified.", ERROR_FATAL);

		//	Reset the pattern mathing object
		myMatcher->Reset();

		if(fileDataIn->path->type == pattern_match) {
			//	Call pattern matching code
			FileFinder fileFinder;
			pathVector = fileFinder.Search(fileDataIn->path->object);

			//	If a pattern match add a record indicating if the pattern was successfull
			if(fileDataIn->path->type == pattern_match) {
				fileDataOut = new FileData();
				fileDataOut->path->object = fileDataIn->path->object;
				fileDataOut->path->type = fileDataIn->path->type;
				fileDataOut->isPatternMatchObject = fileDataIn->isPatternMatchObject;
				if(pathVector.size() == 0) {
					fileDataOut->SetStatus(doesNotExist);
				} else {
					fileDataOut->SetStatus(exists);
				}
				returnVector.push_back(fileDataOut);
				fileDataOut = NULL;
			}

		} else {

			pathVector.push_back(fileDataIn->path->object);
		}

		//	Loop through all paths found and get the data
		sVector::iterator path;
		for (path=pathVector.begin(); path!=pathVector.end(); path++) {

			fileDataOut = GetFileAttributes((*path));
			returnVector.push_back(fileDataOut);
			fileDataOut = NULL;
		}

	} catch(Exception ex) {
		fileDataOut = new FileData();
		fileDataOut->SetMessage(ex.GetErrorMessage());
		fileDataOut->path->object = fileDataIn->path->object;
		fileDataOut->path->type = fileDataIn->path->type;
		fileDataOut->isPatternMatchObject = fileDataIn->isPatternMatchObject;
		fileDataOut->SetStatus(error);
		returnVector.push_back(fileDataOut);

	} catch(...) {
		fileDataOut = new FileData();
		fileDataOut->SetMessage("Error: FileProbe() an unknown exception has occured.");
		fileDataOut->path->object = fileDataIn->path->object;
		fileDataOut->path->type = fileDataIn->path->type;
		fileDataOut->isPatternMatchObject = fileDataIn->isPatternMatchObject;
		fileDataOut->SetStatus(error);
		returnVector.push_back(fileDataOut);
	}

	return (returnVector);
}

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

/*	This code has been romoved because late in the release of the oval schema for 
	version 4 an error in the schem was discovered that makes this test meaningless.
	A future version of the oval schem will correct this error.

void FileProbe::GetFileEffectiveRights(FileData *fileData)
{
	//------------------------------------------------------------------------------------//
	//
	//  ABSTRACT
	//
	//  
	//
	//------------------------------------------------------------------------------------//

	// Retrieve a copy of the security descriptor for the specified file.  If an error
	// occured, exit this app.
	DWORD res;
	PACL pdacl;
	PSECURITY_DESCRIPTOR pSD;

	res = GetNamedSecurityInfo(const_cast<char*>(fileData->path->object.c_str()),	// object name
							   SE_FILE_OBJECT,						// object type
							   DACL_SECURITY_INFORMATION,			// information type
							   NULL,								// owner SID
							   NULL,								// primary group SID
							   &pdacl,								// DACL
							   NULL,								// SACL
							   &pSD);								// Security Descriptor

	if (res != ERROR_SUCCESS) {
		if (res == ERROR_FILE_NOT_FOUND) {

			string errorMessage = "";
			errorMessage.append("\nERROR: The file '");
			errorMessage.append(fileData->path->object);
			errorMessage.append("' does not exist.");
				
			fileData->SetMessage(errorMessage);
			fileData->SetStatus(doesNotExist);

		} else {

			string errorMessage = "";
			errorMessage.append("\nERROR: Unable to retrieve a copy ");
			errorMessage.append("of the security descriptor for '");
			errorMessage.append(fileData->path->object);
			errorMessage.append("'.");
			
			fileData->SetMessage(errorMessage);
			fileData->SetStatus(error);
		}

		return;
	}

	// Check to see if a valid DACL and security descriptor were returned.  If either is
	// invalid, then exit the app.
    if ((IsValidSecurityDescriptor(pSD) == 0) || (IsValidAcl(pdacl) == 0)) {

		LocalFree(pSD);
		string errorMessage = "";
		errorMessage.append("\nERROR: Invalid DACL or security ");
		errorMessage.append("descriptor.  Cannot get effective rights for '");
		errorMessage.append(fileData->path->object);
		errorMessage.append("'.");
		
		fileData->SetMessage(errorMessage);
		fileData->SetStatus(error);
		return;
	}

	// Get the access control list.
	ACL_SIZE_INFORMATION acl_size_info;
	DWORD acl_size = sizeof(ACL_SIZE_INFORMATION);

	if (GetAclInformation(pdacl,						// access-control list
						  (LPVOID)&acl_size_info,		// ACL information
						  acl_size,						// size of ACL information
						  AclSizeInformation) != 0)		// info class
	{
		LPTSTR owner_name = NULL;
		LPTSTR domain_name = NULL;

		// Loop through each ACE found in the ACL.
		DWORD ace_number;
		for (ace_number = 0; ace_number < acl_size_info.AceCount; ace_number++) {
			
			// Get the ACE.
			ACCESS_ALLOWED_ACE* ace;

            if (GetAce(pdacl, ace_number, (LPVOID *) & ace) == 0) {

				string errorMessage = "";
				errorMessage.append("\nERROR: Unable to get next ");
				errorMessage.append("ACE for '");
				errorMessage.append(fileData->path->object);
				errorMessage.append("'.");

				fileData->SetMessage(errorMessage);
				fileData->SetStatus(error);
				continue;
            }

			// Get the SID associated with this ACE.
			PSID psid = (PSID) & (ace->SidStart);

			// Check to make sure the SID is valid.
            if (!(IsValidSid(psid))) {

				string errorMessage = "";
				errorMessage.append("\nERROR: Invalid SID for '");
				errorMessage.append(fileData->path->object);
				errorMessage.append("'.");

				fileData->SetMessage(errorMessage);	
				fileData->SetStatus(error);
				continue;
            }

			// Call LookupAccountSid with owner_name_size = 0 and domain_name_size =0.
			// This will cause LookupAccountSid to fail and return with the required
			// buffer sizes.
			SID_NAME_USE sid_type;

			DWORD owner_name_size = 0;
			DWORD domain_name_size = 0;

			LookupAccountSid(NULL,					// name of local or remote computer
							 psid,					// security identifier
							 owner_name,			// account name buffer
							 &owner_name_size,		// size of account name buffer
							 domain_name,			// domain name
							 &domain_name_size,		// size of domain name buffer
							 &sid_type);			// SID type

            owner_name_size++;
            owner_name = (LPTSTR)realloc(owner_name, owner_name_size * sizeof(TCHAR));

            if (owner_name == NULL) {

				string errorMessage = "";
				errorMessage.append("\nERROR: Could not allocate space.  ");
				errorMessage.append("Cannot get effective rights for this ACE for '");
				errorMessage.append(fileData->path->object);
				errorMessage.append("'.");

				fileData->SetMessage(errorMessage);
				fileData->SetStatus(error);
				continue;
            }

            domain_name_size++;
            domain_name = (LPTSTR)realloc(domain_name, domain_name_size * sizeof(TCHAR));

            if (domain_name == NULL) {
				string errorMessage = "";
				errorMessage.append("\nERROR: Could not allocate space.  ");
				errorMessage.append("Cannot get effective rights for this ACE for '");
				errorMessage.append(fileData->path->object);
				errorMessage.append("'");

				fileData->SetMessage(errorMessage);
				fileData->SetStatus(error);
				continue;
            }
			
			// Call LookupAccountSid again to retrieve the name of the account and the
			// name of the first domain on which this SID is found.
    		if (LookupAccountSid(NULL,					// name of local or remote computer
								 psid,					// security identifier
								 owner_name,			// account name buffer
								 &owner_name_size,		// size of account name buffer
								 domain_name,			// domain name
								 &domain_name_size,		// size of domain name buffer
								 &sid_type) == 0)		// SID type
			{
				string errorMessage = "";
				errorMessage.append("\nERROR: Unable to get the name ");
				errorMessage.append("of the account for this SID for '");
				errorMessage.append(fileData->path->object);
				errorMessage.append("'.");
				
				fileData->SetMessage(errorMessage);
				fileData->SetStatus(error);
				continue;
            }

			// Convert access mask to binary.
			int j;
			char mask[33];

			for (j = 0; j < 32; j++)
			{
				if (ace->Mask & (1 << j))
					mask[31 - j] = '1';
				else
					mask[31 - j] = '0';
			}
			mask[32] = 0;

			fileData->trustee_name->value = owner_name;
			fileData->trustee_name->dataType = stringType;
			fileData->trustee_domain->value = domain_name;
			fileData->trustee_domain->dataType = stringType;
			
			// Produce textual SID.
			LPTSTR sid_string;
			Common::GetTextualSid(psid, &sid_string);
			fileData->trustee_sid->value = sid_string;
			free(sid_string);
			fileData->trustee_sid->dataType = stringType;

		//??fileData->acl_type->value = owner_name;
			fileData->acl_type->status = notCollected;

			fileData->standard_delete->value = mask[16];
			fileData->standard_delete->dataType = booleanType;

			fileData->standard_read_control->value = mask[17];
			fileData->standard_read_control->dataType = booleanType;

			fileData->standard_write_dac->value = mask[18];
			fileData->standard_write_dac->dataType = booleanType;

			fileData->standard_write_owner->value = mask[19];
			fileData->standard_write_owner->dataType = booleanType;

			fileData->standard_synchronize->value = mask[20];
			fileData->standard_synchronize->dataType = booleanType;

			fileData->access_system_security->value = mask[24];
			fileData->access_system_security->dataType = booleanType;

			fileData->generic_read->value = mask[31];
			fileData->generic_read->dataType = booleanType;

			fileData->generic_write->value = mask[30];
			fileData->generic_write->dataType = booleanType;

			fileData->generic_execute->value = mask[29];
			fileData->generic_execute->dataType = booleanType;

			fileData->generic_all->value = mask[28];
			fileData->generic_all->dataType = booleanType;

			fileData->file_read_data->value = mask[0];
			fileData->file_read_data->dataType = booleanType;

			fileData->file_write_data->value = mask[1];
			fileData->file_write_data->dataType = booleanType;

			fileData->file_append_data->value = mask[2];
			fileData->file_append_data->dataType = booleanType;

			fileData->file_read_ea->value = mask[3];
			fileData->file_read_ea->dataType = booleanType;

			fileData->file_write_ea->value = mask[4];
			fileData->file_write_ea->dataType = booleanType;

			fileData->file_execute->value = mask[5];
			fileData->file_execute->dataType = booleanType;

			fileData->file_delete_child->value = mask[6];
			fileData->file_delete_child->dataType = booleanType;

			fileData->file_read_attributes->value = mask[7];
			fileData->file_read_attributes->dataType = booleanType;

			fileData->file_write_attributes->value = mask[8];
			fileData->file_write_attributes->dataType = booleanType;
		}

		free(domain_name);
		free(owner_name);
		LocalFree(pSD);

	} else {
		LocalFree(pSD);
		string errorMessage = "";
		errorMessage.append("\nERROR: Unable to retrieve access-control ");
		errorMessage.append("list.  Cannot get effective rights for '");
		errorMessage.append(fileData->path->object);
		errorMessage.append("'.");
		
		fileData->SetMessage(errorMessage);
		fileData->SetStatus(error);
		return;
	}
}
*/

FileData* FileProbe::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 = "";

	FileData *fattOut = new FileData();


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

		fattOut->path->object = 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:

		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("(FileProbe) The file '");
				errorMessage.append(fileIn);
				errorMessage.append("' could not be found.");
				
				fattOut->SetMessage(errorMessage);
				fattOut->SetStatus(doesNotExist);
				return fattOut;

			} else if(errorNum == ERROR_PATH_NOT_FOUND) {

				errorMessage.append("(FileProbe) The path '");
				errorMessage.append(fileIn);
				errorMessage.append("' does not exist.");

				fattOut->SetMessage(errorMessage);
				fattOut->SetStatus(doesNotExist);
				return fattOut;

			} else {
				char errorCodeBuffer[33];
				_ltoa(errorNum, errorCodeBuffer, 10);

				errorMessage.append("(FileProbe) Unable to open a handle to the file '");
				errorMessage.append(fileIn);
				errorMessage.append("'.  Error Code - ");
				errorMessage.append(errorCodeBuffer);
				
				fattOut->SetMessage(errorMessage);
				fattOut->SetStatus(error);
				return fattOut;
			}
		}

		//	Found the file 
		//	Set the status to exists
		fattOut->SetStatus(exists);

		try {

			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("(FileProbe) Unable to get the security descriptor for the file '");
				errorMessage.append(fileIn);
				errorMessage.append("'.\n");
				throw FileProbeException(errorMessage, ERROR_NOTICE);
			}

			// 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("(FileProbe) Could not allocate space for the file '");
				errorMessage.append(fileIn);
				errorMessage.append("'.");
				throw FileProbeException(errorMessage);
			}

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

				free(aname);

				string errorMessage = "";
				errorMessage.append("(FileProbe) Could not allocate space for the file '");
				errorMessage.append(fileIn);
				errorMessage.append("'.");
				throw FileProbeException(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("(FileProbe) Unable to get the name of the account ");
				errorMessage.append("for this SID for the file '");
				errorMessage.append(fileIn);
				errorMessage.append("'.");
				throw FileProbeException(errorMessage);
			}
			
			//	Set owner 
			fattOut->owner->value = aname;
			fattOut->owner->dataType = stringType;
			fattOut->owner->status = exists;


			free(aname);
			free(dname);

		} catch(FileProbeException ex) {	
			fattOut->SetMessage("Message: " + ex.GetErrorMessage());
			fattOut->owner->value = "";
			fattOut->owner->dataType = stringType;
			fattOut->owner->status = error;

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

			fattOut->SetMessage("Message: " + errorMessage);
			fattOut->SetMessage("Error Message: " + errMsg);
			fattOut->owner->value = "";
			fattOut->owner->dataType = stringType;
			fattOut->owner->status = error;
		}

		//////////////////////////////////////////////////////
		/////////////////////  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("(FileProbe) Unable to get status information ");
			errorMessage.append("associated with the file '");
			errorMessage.append(fileIn);
			errorMessage.append("'.\n");

			fattOut->size->status = error;
			fattOut->a_time->status = error;
			fattOut->c_time->status = error;
			fattOut->m_time->status = error;

		} else {

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

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

			fattOut->a_time->value = Common::ToString((long)statusBuffer.st_atime);
			fattOut->a_time->dataType = stringType;
			// Strip trailing \n if found
			if(fattOut->a_time->value.find_last_of("\n") == fattOut->a_time->value.length()-1)
				fattOut->a_time->value = fattOut->a_time->value.substr(0, fattOut->a_time->value.length()-1);

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

			fattOut->c_time->value = Common::ToString((long)statusBuffer.st_ctime);
			fattOut->c_time->dataType = stringType;
			// Strip trailing \n if found
			if(fattOut->c_time->value.find_last_of("\n") == fattOut->c_time->value.length()-1)
				fattOut->c_time->value = fattOut->c_time->value.substr(0, fattOut->c_time->value.length()-1);

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

			fattOut->m_time->value = Common::ToString((long)statusBuffer.st_mtime);
			fattOut->m_time->dataType = stringType;
			// Strip trailing \n if found
			if(fattOut->m_time->value.find_last_of("\n") == fattOut->m_time->value.length()-1)
				fattOut->m_time->value = fattOut->m_time->value.substr(0, fattOut->m_time->value.length()-1);
		}

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

		DWORD headersum;  
		DWORD checksum;

		res = MapFileAndCheckSum(const_cast<char*>(fileIn.c_str()), &headersum, &checksum);
		if (res != CHECKSUM_SUCCESS) {
			errorMessage.append("(FileProbe) Unable to get ms_checksum information for the file '");
			errorMessage.append(fileIn);
			errorMessage.append("' \n");
			fattOut->ms_checksum->status = error;

		} else {
			ZeroMemory(buf, sizeof(buf));
			_snprintf(buf, sizeof(buf)-1, "%d", checksum);
			buf[sizeof(buf)-1] = '\0';
			fattOut->ms_checksum->value = buf;
			fattOut->ms_checksum->dataType = integerType;

		}

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

		FILE* fp = NULL;
		fp = fopen(fileIn.c_str(), "r");
		if (fp == NULL) {
			errorMessage.append("(FileProbe) Unable to get MD5 information for the file '");
			errorMessage.append(fileIn);
			errorMessage.append("' \n");
			fattOut->md5->status = 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->value = buf;
			fattOut->md5->dataType = stringType;

		}

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

				//	Get The major, minor, private and build numbers for the file
				if (VerQueryValue(versionbuf, "\\", (void**)&pFFI, &vdatalen) == FALSE) {

					errorMessage.append("(FileProbe) Unable to get version information for the file '");
					errorMessage.append(fileIn);
					errorMessage.append("' \n");

					fattOut->versionStatus = error;
				
				} else if (pFFI == NULL) {

					errorMessage.append("(FileProbe) No version information available for the file '");
					errorMessage.append(fileIn);
					errorMessage.append("' \n");

					fattOut->versionStatus = doesNotExist;

				} 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)));

					fattOut->majVer->value = ver1;
					fattOut->minVer->value = ver2;
					fattOut->buildVer->value = ver3;
					fattOut->priVer->value = ver4;
					fattOut->versionStatus = exists;	
				}

				//////////////////////////////////////////////////////
				///////////////  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->value = verStr.substr(0, verStr.find("."));
						}

					} else if(vdatalen == 0) {

						errorMessage.append("(FileProbe) No value is available for the specified version-information name, \"szSubblock,\" for file: ");
						errorMessage.append(fileIn);
						errorMessage.append("' \n");
						fattOut->development_class->status = doesNotExist;

					} else if(retVal == 0) {

						errorMessage.append("(FileProbe) Either specified name, \"szSubblock,\" does not exist or the specified resource is not valid for file: ");
						errorMessage.append(fileIn);
						errorMessage.append("' \n");
						fattOut->development_class->status = doesNotExist;
					}

				} else if(vdatalen == 0) {

					errorMessage.append("(FileProbe) No value is available for the specified version-information name, \"szSubblock,\" for file: ");
					errorMessage.append(fileIn);
					errorMessage.append("' \n");
					fattOut->development_class->status = doesNotExist;

				} else if(retVal == 0) {

					errorMessage.append("(FileProbe) Either specified name, \"szSubblock,\" does not exist or the specified resource is not valid for file: ");
					errorMessage.append(fileIn);
					errorMessage.append("' \n");
					fattOut->development_class->status = doesNotExist;
				}

			} else {

				errorMessage.append("(FileProbe) Unable to get version information for the file '");
				errorMessage.append(fileIn);
				errorMessage.append("' \n");
				fattOut->versionStatus = error;
			}

			free(versionbuf);

		} else {

			errorMessage.append("(FileProbe) No version information available for the file '");
			errorMessage.append(fileIn);
			errorMessage.append("' \n");
			fattOut->versionStatus = doesNotExist;
		}

		//////////////////////////////////////////////////////
		/////////////////////  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->value = "FILE_ATTRIBUTE_DIRECTORY";
				else
						fattOut->type->value = "FILE_TYPE_DISK";

				break;

			case FILE_TYPE_CHAR:

				fattOut->type->value = "FILE_TYPE_CHAR";
				break;

			case FILE_TYPE_PIPE:

				fattOut->type->value = "FILE_TYPE_PIPE";
				break;

			case FILE_TYPE_UNKNOWN:

				errorMessage.append("(FileProbe) No file type information available for the file '");
				errorMessage.append(fileIn);
				errorMessage.append("' \n");
				fattOut->type->status = error;
				
				break;

			default:

				errorMessage.append("(FileProbe) Unable to get file type information for the file '");
				errorMessage.append(fileIn);
				errorMessage.append("' \n");
				fattOut->type->status = error;
				
				break;
		}

		//////////////////////////////////////////////////////
		//////////////////////////////////////////////////////
	} catch(FileProbeException ex) {	
	
		fattOut->path->object = fileIn;
		fattOut->SetStatus(error);
		fattOut->SetMessage("Message: " + ex.GetErrorMessage());

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

		fattOut->path->object = fileIn;
		fattOut->SetStatus(error);
		fattOut->SetMessage("Message: " + errorMessage + " Error Message: " + errMsg);
	}

	// Now get the effective rights
	//GetFileEffectiveRights(fattOut);

	return (fattOut);
}

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

}

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

}

