//
// $Id: MetabaseKeyProbe.cpp,v 1.10 2005/04/05 17:08:00 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 "MetabaseKeyProbe.h"

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

MetabaseKeyProbe::MetabaseKeyProbe()
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Do nothing for now
	//
	// -----------------------------------------------------------------------
}

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

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

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

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

	return instance;	
}

pdVector MetabaseKeyProbe::Run(ProbeData *probeDataIn)
{
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//
	//  Run the Metabase probe. Return a vector of MetabaseKeyData. Supporting pattern 
	//	match on key, and id
	//
	//
	//	TO DO:
	//	Name is not currently supported. We don't know how to get it.
	// -----------------------------------------------------------------------

	MetabaseKeyData *mkIn = (MetabaseKeyData*)probeDataIn;
	
	pdVector returnVector;

	// Initialize the COM library for use by the calling thread.
	//
	// NOTE: Define _WIN32_DCOM (in your project settings pre-processor defines) inorder to
	// use CoInitializeEx() The API is specific to DCOM release of the OS is surrounded by
	// conditional compilation statements that require you to #define to access the
	// definitions.

	CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

	// Create a single uninitialized object of the MSAdminBase class.
	
	HRESULT hRes;
	IMSAdminBase* pIABase;

	hRes = CoCreateInstance(CLSID_MSAdminBase,		// Class identifier (CLSID) of the object
						    NULL,					// Pointer to controlling IUnknown
							CLSCTX_ALL,				// Context for running executable code
							IID_IMSAdminBase,		// Reference to the identifier of the interface
							(void**)&pIABase);		// Address of output variable

	if (hRes != ERROR_SUCCESS) {

		if (hRes == REGDB_E_CLASSNOTREG) {
			// This error is occures when the Metabase doesn't exist on the computer. (ie when
			// IIS isn't installed)

			string errorMessage = "";
			errorMessage.append("The metabase could not be found, most likely because IIS ");
			errorMessage.append("isn't installed.");

			MetabaseKeyData *tmp = new MetabaseKeyData();
			tmp->key->object = mkIn->key->object;
			tmp->identifier->object = mkIn->identifier->object;
			tmp->SetMessage(errorMessage);
			tmp->SetStatus(doesNotExist);
			tmp->isPatternMatchObject = mkIn->isPatternMatchObject;
			returnVector.push_back(tmp);
			tmp = NULL;


		} else {
			string errorMessage = "";
			errorMessage.append("(MetabaseKeyProbe) Failed to create instance of AdminBase ");
			errorMessage.append("object inorder to gather data from the metabase.");
			
			MetabaseKeyData *tmp = new MetabaseKeyData();
			tmp->key->object = mkIn->key->object;
			tmp->identifier->object = mkIn->identifier->object;
			tmp->SetMessage(errorMessage);
			tmp->SetStatus(error);
			tmp->isPatternMatchObject = mkIn->isPatternMatchObject;
			returnVector.push_back(tmp);
			tmp = NULL;
		}

	} else {

		/////////////////////////////////////////////////////////////////
		//	The metabase exists and the COM library was successfully
		//	initialized
		/////////////////////////////////////////////////////////////////

		sVector keys;

		try {

			//	Look for pattern match types
			if(mkIn->key->type != pattern_match && mkIn->identifier->type != pattern_match) {
				//	Only literal types
				returnVector.push_back(GetMetaData(mkIn->key->object, mkIn->identifier->object, pIABase));

			} else {

				// Two flags to track whether a match was found for a pattern
				bool foundMatchingId = false;
				bool foundMatchingKey = false;
				

				//	Get the matching keys or use provided key
				if(mkIn->key->type == pattern_match) {

					try {

						myMatcher->Reset();

						GetMatchingKeys("", mkIn->key->object , &keys, pIABase);

						if(keys.size() > 0)
							foundMatchingKey = true;

					} catch(REGEXException ex) {

						if(ex.GetSeverity() == ERROR_WARN) {

							string pcreMsg = "";
							pcreMsg.append("Metabase Keys Probe Warning - while searching for matching keys:\n");
							pcreMsg.append("----------------------------------------------------------------\n");
							pcreMsg.append(ex.GetErrorMessage());
							if(Log::verboseMode) {
								cout << pcreMsg << "\n"<< endl;
								Log::WriteLog(pcreMsg + "\n\n");
							}
							
						} else {
							throw;
						}
					}

				} else {
					//	Ensure key exists
					if(ValidateKey(mkIn->key->object, pIABase)) {
						keys.push_back(mkIn->key->object);
						foundMatchingKey = true;
					} else {
						MetabaseKeyData *tmp = new MetabaseKeyData();
						tmp->key->object = mkIn->key->object;
						tmp->identifier->object = mkIn->identifier->object;
						tmp->identifier->type = mkIn->identifier->type;
						tmp->SetMessage("The key'" + mkIn->key->object + "' was not found.");
						tmp->SetStatus(doesNotExist);
						tmp->isPatternMatchObject = mkIn->isPatternMatchObject;
						returnVector.push_back(tmp);
						tmp = NULL;
					}					
				}

				//	For each key found
				sVector::iterator key;
				for (key=keys.begin(); key!=keys.end(); key++)
				{
					//	Get the matching identifiers or use provided identifier
					if(mkIn->identifier->type == pattern_match) {
						//	Reset the pattern matching utility
						myMatcher->Reset();

						GetMatchingIdentifiers((*key), mkIn->identifier->object, &returnVector, pIABase);
						
						if(returnVector.size() > 0)
							foundMatchingId = true;	
					
					} else {

						MetabaseKeyData *tmp = GetMetaData((*key), mkIn->identifier->object, pIABase);
						if(tmp->GetStatus() != doesNotExist) {
							returnVector.push_back(tmp);
						} else {
							delete tmp;
						}
					}
				}

				// Now create an object to indicate the results of the pattern match.
				// Need to pass on that the pattern match matched something or did not
				//	match any items
				MetabaseKeyData *tmp = new MetabaseKeyData();
				tmp->key->object = mkIn->key->object;
				tmp->key->type = mkIn->key->type;
				tmp->identifier->object = mkIn->identifier->object;
				tmp->identifier->type = mkIn->identifier->type;
				if(!foundMatchingId || !foundMatchingKey) {
					tmp->SetMessage("The specified pattern did not match any items on the system");
					tmp->SetStatus(doesNotExist);
				} else {
					tmp->SetMessage("The specified pattern matched at least one item on the system.");
					tmp->SetStatus(exists);
				}
				tmp->isPatternMatchObject = true;
				returnVector.push_back(tmp);
				tmp = NULL;
			}

		}catch(Exception ex) {

			MetabaseKeyData *tmp = new MetabaseKeyData();
			tmp->key->object = mkIn->key->object;
			tmp->key->type = mkIn->key->type;
			tmp->identifier->object = mkIn->identifier->object;
			tmp->identifier->type = mkIn->identifier->type;
			tmp->isPatternMatchObject = mkIn->isPatternMatchObject;
			tmp->SetMessage(ex.GetErrorMessage());
			
			//	Set status
			if(ex.GetSeverity() == ERROR_NOTICE) {
				tmp->SetStatus(doesNotExist);
			} else {
				tmp->SetStatus(error);
			}

			returnVector.push_back(tmp);
			tmp = NULL;

		}catch(...) {

			MetabaseKeyData *tmp = new MetabaseKeyData();
			tmp->key->object = mkIn->key->object;
			tmp->key->type = mkIn->key->type;
			tmp->identifier->object = mkIn->identifier->object;
			tmp->identifier->type = mkIn->identifier->type;
			tmp->isPatternMatchObject = mkIn->isPatternMatchObject;
			tmp->SetMessage("Error: Unknown exception has occured.");
			tmp->SetStatus(error);
			returnVector.push_back(tmp);
			tmp = NULL;
		}
	}

	// Close the COM library on the current thread
	CoUninitialize();

	return (returnVector);
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  Private Members  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
void MetabaseKeyProbe::GetMatchingIdentifiers(string strKeyIn, string pattern, pdVector *returnVectorIn, IMSAdminBase *pIABase)
{
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//
	//  Given a key in the Metabase, this function will get the data for each
	//  identifier associated with it that matches the specified pattern. The data
	//	is then stored in MetabaseKeyData object and pushed onto the return vector.
	//
	// -----------------------------------------------------------------------

	HRESULT hRes;
	DWORD numEntries;
	DWORD dataSetNumber;
	string workingKey				= strKeyIn;
	MetabaseKeyData *tmpWinMBKey	= NULL;

	/////////////////////////////////////////////////////////////////
	//	Prepare the working key
	//	Reverse the slashes so they are all facing ->
	//	Ensure that there is a leading slash
	/////////////////////////////////////////////////////////////////
	workingKey = Common::SwitchChar(workingKey, "\\", "/");
	if(workingKey.find("/") != 0)
		workingKey.insert(0, "/");


	LPWSTR pbKey;
	size_t size = mbstowcs(NULL, workingKey.c_str(), workingKey.length()) + 1;
	pbKey = new wchar_t[size];
	size = mbstowcs(pbKey, workingKey.c_str(), size) + 1;
	if (size == -1) {
		string errorMessage = "";
		errorMessage.append("There was an error converting to the key '");
		errorMessage.append(strKeyIn);
		errorMessage.append("to UNICODE.");
		throw MetabaseKeysProbeException(errorMessage);
	}

	/////////////////////////////////////////////////////////////////
	//	Get all the data for the key
	/////////////////////////////////////////////////////////////////

	// Initialize the buffer to store the list of identifiers from the metabase with the
	// given path.  We set up a zero length buffer to start with because we do not know
	// how big it has to be.  The first call to GetAllData will therefore fail, but will
	// return the necessary buffer size which we will use to redimension the buffer.
	DWORD dwReqIdentBufLen = 0;
	DWORD dwIdentBufLen = 0; 
	unsigned char* pbIdentBuffer = new unsigned char[0];


	// Try to retrieve the different identifiers associated with the specified key in the
	// metabase.  Check to see if GetAllData failed due to an insufficient buffer.
	// NOTE: This is going to fail the first time because we have dwIdentBufLen=0 the first
	// time through.  When it fails, it returns the required buffer size as dwReqIdentBufLen
	// which we will use to reset dwIdentBufLen.
	while (true) {

		hRes = pIABase->GetAllData(METADATA_MASTER_ROOT_HANDLE,		// metabase handle
	                               pbKey,							// path to the key, relative to handle
	                               METADATA_NO_ATTRIBUTES,			// flags used to get the data
	                               ALL_METADATA,					// user type of data
	                               ALL_METADATA,					// data type of data
	                               &numEntries,						// the number of entries copied to pbBuffer
	                               &dataSetNumber,					// points to a number associated with this data set
							       dwIdentBufLen,					// the size, in wchars, of buffer
							       pbIdentBuffer,					// the buffer that receives the data
							       &dwReqIdentBufLen);				// if the method fails, receives the required buffer size
		
		if(hRes == ERROR_SUCCESS) {

			DWORD i;

			try {

				// Walk through the list of identifiers and data.  pbIdentBuffer should contain
				// an array of METADATA_GETALL_RECORD

				METADATA_GETALL_RECORD mdGetAllRecord;
				unsigned int offset = 0;
				unsigned int dataOffset = 0;
				for (i=0; i<numEntries; i++) {

					offset = (i * sizeof(METADATA_GETALL_RECORD));
					//	Get the current Record
					memcpy(&mdGetAllRecord, (pbIdentBuffer + offset), sizeof(METADATA_GETALL_RECORD));

					//	Create a new MetabaseKeyData object to store the results in. 
					tmpWinMBKey = new MetabaseKeyData();

					//	Get the identifier
					tmpWinMBKey->identifier->object = GetIdentifier(&mdGetAllRecord);

					//	Id the identifier matches the pattern add it to the result vector
					if(myMatcher->IsMatch(pattern.c_str(), tmpWinMBKey->identifier->object.c_str())) {

						//	Get the remaining data
						tmpWinMBKey->key->object = strKeyIn;
						tmpWinMBKey->data_type= GetDataType(&mdGetAllRecord);
						tmpWinMBKey->user_type= GetUserType(&mdGetAllRecord);
						tmpWinMBKey->data = GetData(&mdGetAllRecord, pbIdentBuffer);

						// Add the name to the structure.
						// TODO - Can not figure out where/how to get the name that corresponds to the ID.
						tmpWinMBKey->name->status = notCollected;
						tmpWinMBKey->SetMessage(tmpWinMBKey->GetMessage() + "Currently unable to retrieve name information for metabase keys.");

						//	Add the data to the return vector
						returnVectorIn->push_back(tmpWinMBKey);
					}
				}

				//	Break out of the while true loop
				break;

			}catch(REGEXException ex) {

				if(ex.GetSeverity() == ERROR_WARN)
				{
					string pcreMsg = "";
					pcreMsg.append("Metabase Keys Probe Warning - while searching for matching identifiers:\n");
					pcreMsg.append("-----------------------------------------------------------------------\n");
					pcreMsg.append(ex.GetErrorMessage());
					if(Log::verboseMode) {
						cout << pcreMsg << "\n"<< endl;
						Log::WriteLog(pcreMsg + "\n\n");
					}
				} else {
					tmpWinMBKey->key->object = strKeyIn;
					tmpWinMBKey->SetStatus(error);
					tmpWinMBKey->SetMessage(ex.GetErrorMessage());
					returnVectorIn->push_back(tmpWinMBKey);				
				}

				//	If pattern fails once no need to continue
				break;
			}

		} else if (hRes == RETURNCODETOHRESULT(ERROR_INSUFFICIENT_BUFFER)) {

			// Setup the buffer to handle the overflow.
			delete [] (pbIdentBuffer); 
			pbIdentBuffer = new unsigned char[dwReqIdentBufLen]; 
			dwIdentBufLen = dwReqIdentBufLen;

		//	An invalid parameter was specified.
		} else if(hRes == RETURNCODETOHRESULT(ERROR_INVALID_PARAMETER)) {

			string errorMessage = "";
			errorMessage.append("(MetabaseKeyProbe) Invalid parameter specified while attempting to get all data for key: '");
			errorMessage.append(strKeyIn);
			errorMessage.append("' pattern: '");
			errorMessage.append(pattern);
			errorMessage.append("'.");

			throw MetabaseKeysProbeException(errorMessage);

		//	Finished enumerating data for the key
		} else if(hRes == RETURNCODETOHRESULT(ERROR_ACCESS_DENIED)) {

			string errorMessage = "";
			errorMessage.append("(MetabaseKeyProbe) Error while attmpting to get all data. Access is denied to  key: '");
			errorMessage.append(strKeyIn);
			errorMessage.append("' pattern: '");
			errorMessage.append(pattern);
			errorMessage.append("'.");

			throw MetabaseKeysProbeException(errorMessage);

		//	The path not found error should not occure.
		//	At this point only paths that have been found in the metabase
		//	are being processed. 
		} else if(hRes == RETURNCODETOHRESULT(ERROR_PATH_NOT_FOUND)) {

			string errorMessage = "";
			errorMessage.append("(MetabaseKeyProbe) Unable to find the path: '");
			errorMessage.append(strKeyIn);
			errorMessage.append("' while attempting to get all its data. Pattern used: '");
			errorMessage.append(pattern);
			errorMessage.append("'.");

			throw MetabaseKeysProbeException(errorMessage);

		//	All expected return values have been handled. This case should never occure
		} else {

			char strErrorCode[33];
			_itoa(HRESULTTOWIN32(hRes), strErrorCode, 10);

			string errorMessage = "";
			errorMessage.append("(MetabaseKeyProbe) An unknown exception has occured while getting data for the key: '");
			errorMessage.append(strKeyIn);
			errorMessage.append("' pattern: '");
			errorMessage.append(pattern);
			errorMessage.append("' - Error code: ");
			errorMessage.append(strErrorCode);
			errorMessage.append("");

			throw MetabaseKeysProbeException(errorMessage);
		}
	}

	//	Delete the indentifier buffer
	delete [] (pbIdentBuffer);

	/////////////////////////////////////////////////////////////////
	//	If no identifiers matched the specified pattern 
	//	create a probe data object and add it to the return vector
	/////////////////////////////////////////////////////////////////
	//if(returnVectorIn->size() == 0) {
	//	cout << "No identifiers found." << endl;
		
	//	tmpWinMBKey = new MetabaseKeyData();
	//	tmpWinMBKey->key->object = strKeyIn;
	//	tmpWinMBKey->identifier->object = pattern;
	//	tmpWinMBKey->identifier->type = pattern_match;
	//	tmpWinMBKey->SetStatus(doesNotExist);
	//	tmpWinMBKey->SetMessage("(MetabaseKeyProbe) Unable to find a matching identifier.");
	//	returnVectorIn->push_back(tmpWinMBKey);
	//}
}

void MetabaseKeyProbe::GetMatchingKeys(string keyIn, string pattern, sVector *keys, IMSAdminBase *pIABase)
{
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//
	//  Search the metabase for keys under the input key that match the pattern. 
	//	Add all matches to the keys vector. For every sub key found make recursive
	//	call. stop when all subkeys have been searched
	//
	// -----------------------------------------------------------------------

	LONG res;													//	result from enumerating the subkeys
	LPWSTR name			= new wchar_t[METADATA_MAX_NAME_LEN];//	buffer to store the subkey name
	LPWSTR pszMDPath	= new wchar_t[METADATA_MAX_NAME_LEN];//
	DWORD dwIndex		= 0;									//	index of subkey to enumerate
	string workingKey	= "";									//	The name of the keyIn and the subkey concatenated
	string tmpKey		= "";
	string errorMessage = "";									//	
	char *strName		= (char *)malloc(METADATA_MAX_NAME_LEN);//


	//for (dwIndex = 0, res = ERROR_SUCCESS; res == ERROR_SUCCESS; dwIndex++)
	do
	{
		//	Reset the buffers
		ZeroMemory(name, METADATA_MAX_NAME_LEN);
		ZeroMemory(pszMDPath, METADATA_MAX_NAME_LEN);
		ZeroMemory(strName, METADATA_MAX_NAME_LEN);

		//	Get the working key as a string
		workingKey = keyIn;

		//	Convert the key from a string to a wchar_t*.
		//	Must add 1 to size to include the NULL at the end of the string
		size_t size = mbstowcs(NULL, workingKey.c_str(), workingKey.length()) + 1;
		//pszMDPath = new wchar_t[size];
		size = mbstowcs(pszMDPath, workingKey.c_str(), size) + 1;
		if (size == -1)
		{
			string errorMessage = "";
			errorMessage.append("There was an error converting the key '");
			errorMessage.append(workingKey);
			errorMessage.append("to UNICODE.");
			throw MetabaseKeysProbeException(errorMessage);
		}

		res = pIABase->EnumKeys(METADATA_MASTER_ROOT_HANDLE,	//	metabase handle.
								pszMDPath,						//	path to the key.
								name,							//	receives the name of the subkey.
								dwIndex);						//	index of the subkey.


		//	Check results
		if(res == ERROR_SUCCESS)
		{
			//	Convert the new name to a string
			size = wcstombs(strName, name, METADATA_MAX_NAME_LEN);

			//	Add the subkey to the working key
			workingKey.append("/");
			workingKey.append(strName);

			//	If a match add the new key to the keys vector
			tmpKey = Common::SwitchChar(workingKey, "/", "\\");
			if(tmpKey.find("\\") == 0)
				tmpKey.erase(0, 1);
	
			if(myMatcher->IsMatch(pattern.c_str(), tmpKey.c_str()))
				keys->push_back(tmpKey);

			//	Make recursive call
			GetMatchingKeys(workingKey, pattern, keys, pIABase);
		}

		//	Increment index
		dwIndex++;

	}while(res == ERROR_SUCCESS);

	delete [] (pszMDPath);
	delete [] (name);
	delete [] (strName);

}


MetabaseKeyData* MetabaseKeyProbe::GetMetaData(string strKeyIn, string strIdentifierIn, IMSAdminBase *pIABase)
{
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//
	//  Given a key and an identifier in the Metabase, this function will first check that
	//	the key exists. Then it will get the data for the key and identifier. 
	//	The data is then stored in MetabaseKeyData object and passed back to the
	//  calling function.
	//
	// -----------------------------------------------------------------------

	HRESULT hRes;
	string workingKey = strKeyIn;

	//	Initialize the MetabaseKeyData object used for output.
	MetabaseKeyData* tmp = new MetabaseKeyData();
	tmp->key->object = strKeyIn;
	tmp->identifier->object = strIdentifierIn;


	/////////////////////////////////////////////////////////////////
	//	Check that the key exists. If it does not exist set the
	//	set the object status to doesNotExist
	/////////////////////////////////////////////////////////////////
	if(!ValidateKey(strKeyIn, pIABase)) {

		tmp->SetStatus(doesNotExist);
		tmp->SetMessage("The key'" + strKeyIn + "' was not found.");

	} else {

		/////////////////////////////////////////////////////////////////
		//	Prepare the working key
		//	Reverse the slashes so they are all facing ->
		//	Ensure that there is a leading slash
		/////////////////////////////////////////////////////////////////
		workingKey = Common::SwitchChar(workingKey, "\\", "/");
		if(workingKey.find("/") != 0)
			workingKey.insert(0, "/");

		//	Convert the key from a string to a wchar_t*.
		//	Must add 1 to size to include the NULL at the end of the string
		LPWSTR pbKey;
		size_t size = mbstowcs(NULL, workingKey.c_str(), workingKey.length()) + 1;
		pbKey = new wchar_t[size];
		size = mbstowcs(pbKey, workingKey.c_str(), size) + 1;
		if (size == -1) {

			string errorMessage = "";
			errorMessage.append("There was an error converting the key '");
			errorMessage.append(workingKey);
			errorMessage.append("to UNICODE.");
			throw MetabaseKeysProbeException(errorMessage, ERROR_FATAL);
		}

		/////////////////////////////////////////////////////////////////
		//	Check that the identifier exists and get the data
		/////////////////////////////////////////////////////////////////

		// Initialize the buffer to store the data from the metabase.  We set up a zero length
		// buffer to start with because we do not know how big it has to be.  The first call
		// to GetData will therefore fail, but will return the necessary buffer size which we
		// will use to redimension the buffer.

		DWORD dwReqDataBufLen = 0;

		// Initialize a METADATA_RECORD structure that will store the information about the
		// key we want to look at.

		METADATA_RECORD mdRec;

		mdRec.dwMDAttributes = METADATA_NO_ATTRIBUTES;
		mdRec.dwMDDataLen = 0;
		mdRec.dwMDDataType = ALL_METADATA;
		mdRec.dwMDIdentifier = atoi(strIdentifierIn.c_str());
		mdRec.dwMDUserType = ALL_METADATA;
		mdRec.pbMDData = new BYTE[0];

		// Use the METADATA_RECORD to retrieve the data for the specified key.  Remember that
		// this call to GetData() should fail due to an insufficient buffer.
		hRes = pIABase->GetData(METADATA_MASTER_ROOT_HANDLE,		// metabase handle
								pbKey,								// path to the key, relative to handle
								&mdRec,								// pointer to a structure that receives the data
								&dwReqDataBufLen);					// the required data length in the case of an overflow.

		//	Setup the buffer to handle the overflow and call GetData() again.
		if (hRes == RETURNCODETOHRESULT(ERROR_INSUFFICIENT_BUFFER)) {

			delete [] (mdRec.pbMDData); 
			mdRec.pbMDData = new BYTE[dwReqDataBufLen]; 
			mdRec.dwMDDataLen = dwReqDataBufLen;

			hRes = pIABase->GetData(METADATA_MASTER_ROOT_HANDLE,	// metabase handle
									pbKey,							// path to the key, relative to handle
									&mdRec,							// pointer to a structure that receives the data
									&dwReqDataBufLen);				// the required data length in the case of an overflow.
		}

		if(hRes == ERROR_PATH_NOT_FOUND || hRes == MD_ERROR_DATA_NOT_FOUND) {

			tmp->SetMessage("(MetabaseKeyProbe) The identifier: '" + strIdentifierIn + "' could not be found under key: '" + strKeyIn + "'.");
			tmp->SetStatus(doesNotExist);

		} else if (hRes != ERROR_SUCCESS) {

			tmp->SetMessage("(MetabaseKeyProbe) Failed to get data for identifier: '" + strIdentifierIn + "' under key: '" + strKeyIn + "'.");
			tmp->SetStatus(error);

		} else {

			//	We now have the data we want.  Fill out the MetabaseKeyData object
			RetrieveInfo(&tmp, strKeyIn, &mdRec);
		}

		// We are all done so delete the data buffer.
		delete [] (mdRec.pbMDData);
	}

	return tmp;
}

void MetabaseKeyProbe::GetMetaKeys(pdVector *returnVectorIn, string strIdentifierIn, IMSAdminBase *pIABase)
{
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//
	//  Given an identifier in the Metabase, this function will get the data for each
	//  key associated with it.  The data is then stored in a MetabaseKeyData object
	//  and pushed onto the return vector.
	//
	// -----------------------------------------------------------------------

	HRESULT hRes;

	// Initialize the buffer to store the list of paths from the metabase with the given
	// identifier.  We set up a zero length buffer to start with because we do not know
	// how big it has to be.  The first call to GetDataPaths will therefore fail, but will
	// return the necessary buffer size which we will use to redimension the buffer.
	//
	// NOTE: We assign pbTempPathBuffer to point to the beginning of pbPathBuffer.  That
	// way, when we are finished walking down the list of paths in pbPathBuffer, we can
	// still find the start of the buffer to delete it.

	DWORD dwReqPathBufLen = 0;
	DWORD dwPathBufLen = 0; 
	LPWSTR pbPathBuffer = new wchar_t[0];
	LPWSTR pbTempPathBuffer = pbPathBuffer;

	// Try to retrieve the different paths where the specified identifier is found in the
	// metabase.  Check to see if GetDataPaths failed due to an insufficient buffer.
	// NOTE: This is going to fail the first time because we have dwPathBufLen=0 the first
	// time through.  When it fails, it returns the required buffer size as dwReqPathBufLen
	// which we will use to reset dwPathBufLen.

	while (true)
	{
		// TODO - GetDataPaths() fails when not run as Administrator.
		// All the info I have found says the only the Administrator has permission to work
		// with the metabase.  I got the impression that this was just writing to the
		// metabase (metabase.bin) but since all I am doing is reading from it and it is
		// failing, I now assume that it is access in general.  I can not find a way around
		// this.

		hRes = pIABase->GetDataPaths(METADATA_MASTER_ROOT_HANDLE,		// metabase handle
									 L"",								// path to the key, relative to handle
									 atoi(strIdentifierIn.c_str()),		// identifier of the data
									 ALL_METADATA,						// type of data
									 dwPathBufLen,						// the size, in wchars, of buffer
									 pbPathBuffer,						// the buffer that receives the data
									 &dwReqPathBufLen);					// if the method fails, receives the required buffer size
		
		if(hRes == ERROR_SUCCESS)
		{
			// Walk through the list of paths.  pbPathBuffer should contain a list of paths
			// seperated by null characters.  The end of the list is siginfied by a two
			// null characters.

			wchar_t* tmpStr = pbPathBuffer;
			wint_t tmpChar = NULL;

			while (wcslen(pbPathBuffer) != 0)
			{
				// Convert the UNICODE path to a char path.

				char* szChar = NULL;
				size_t size = 0;
				string strPath = "";

				if((size = wcstombs(0, pbPathBuffer, 0)) != -1)
				{
					szChar = new char[size + 1];
					szChar[size] = NULL;

					wcstombs(szChar, pbPathBuffer, size);

					strPath = Common::SwitchChar(szChar+1, "/", "\\");
					strPath = strPath.erase(strPath.length()-1, 1);
				}

				if (size == -1)
				{
					MetabaseKeyData* tmp = new MetabaseKeyData();
					tmp->identifier->object.append(strIdentifierIn);
					tmp->SetMessage(tmp->GetMessage() + "There was an error converting from UNICODE key.");
					(*returnVectorIn).push_back(tmp);
				}
				else
				{
					(*returnVectorIn).push_back(GetMetaData(strPath,
			                                                strIdentifierIn,
											                pIABase));
				}

				// Advance the path buffer to the next path in the list.

				tmpStr = wcschr(pbPathBuffer, tmpChar);
				pbPathBuffer = tmpStr + 1;
			}

			break;
		}
		else if (hRes == RETURNCODETOHRESULT(ERROR_INSUFFICIENT_BUFFER))
		{
			// Setup the buffer to handle the overflow.

			delete [] (pbPathBuffer); 
			pbPathBuffer = new wchar_t[dwReqPathBufLen]; 
			dwPathBufLen = dwReqPathBufLen;

			// Reset pbTempPathBuffer to point to the beginning of the new pbPathBuffer.

			pbTempPathBuffer = pbPathBuffer;

			continue;
		}
		//else if (hRes == RETURNCODETOHRESULT(ERROR_INVALID_PARAMETER))
		//{
		//	break;
		//}
		//else if (hRes == RETURNCODETOHRESULT(ERROR_NOT_ENOUGH_MEMORY))
		//{
		//	break;
		//}
		else if (hRes == RETURNCODETOHRESULT(ERROR_ACCESS_DENIED))
		{
			string errorMessage = "";

			errorMessage.append("(MetabaseKeyProbe) Invalid access permissions to get ");
			errorMessage.append("data for identifier '");
			errorMessage.append(strIdentifierIn);
			errorMessage.append("'.");

			throw MetabaseKeysProbeException(errorMessage);
		}
		else if (hRes == RETURNCODETOHRESULT(ERROR_PATH_NOT_FOUND))
		{
			// The specified identifier was not found in the metabase.  Break out of
			// this loop but do not throw an error.

			MetabaseKeyData* tmp = new MetabaseKeyData();
					
			tmp->identifier->object.append(strIdentifierIn);
			tmp->SetMessage(tmp->GetMessage() + "The identifier could not be found.");

			(*returnVectorIn).push_back(tmp);
		}
		else
		{
			char strErrorCode[33];
			_itoa(HRESULTTOWIN32(hRes), strErrorCode, 10);

			string errorMessage = "";

			errorMessage.append("(MetabaseKeyProbe) Failed to get data for identifier '");
			errorMessage.append(strIdentifierIn);
			errorMessage.append("' - error # ");
			errorMessage.append(strErrorCode);
			errorMessage.append("");

			throw MetabaseKeysProbeException(errorMessage);
		}
	}

	// Delete the path buffer using our pointer to the beginning of the buffer held in
	// pbTempPathBuffer.

	delete [] (pbTempPathBuffer);
}

void MetabaseKeyProbe::RetrieveInfo(MetabaseKeyData** tmp, string pathIn, METADATA_RECORD* metaRecordIn)
{
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//
	//  Get the information from the METADATA_RECORD
	//
	//	TO DO
	//	Figure out how to get the name...
	// -----------------------------------------------------------------------

	DWORD i;

	// Add the path to the structure.
	(*tmp)->key->object = Common::SwitchChar(pathIn, "/", "\\");
	(*tmp)->key->type = literal;
	(*tmp)->SetStatus(exists);


	//	Add the identifier to the structure.
	char strIdentifier[10];
	ZeroMemory(strIdentifier, sizeof(strIdentifier));
	_snprintf(strIdentifier, sizeof(strIdentifier)-1, "%d", (*metaRecordIn).dwMDIdentifier);
	strIdentifier[sizeof(strIdentifier)-1] = '\0';

	(*tmp)->identifier->object = strIdentifier;
	(*tmp)->identifier->type = literal;


	// Add the name to the structure.
	//
	// TODO - Can not figure out where/how to get the name that corresponds to the ID.
	(*tmp)->name->value = "";
	(*tmp)->name->status = notCollected;
	(*tmp)->name->dataType = stringType;
	(*tmp)->SetMessage((*tmp)->GetMessage() + "Currently unable to retrieve name information for metabase keys.");


	//	Get the user type
	switch ((*metaRecordIn).dwMDUserType)
	{
		case ASP_MD_UT_APP:
			(*tmp)->user_type->value = "ASP_MD_UT_APP";
			(*tmp)->user_type->dataType = stringType;
			(*tmp)->user_type->status = exists;
			break;
		case IIS_MD_UT_FILE:
			(*tmp)->user_type->value = "IIS_MD_UT_FILE";
			(*tmp)->user_type->dataType = stringType;
			(*tmp)->user_type->status = exists;
			break;
		case IIS_MD_UT_SERVER:
			(*tmp)->user_type->value = "IIS_MD_UT_SERVER";
			(*tmp)->user_type->dataType = stringType;
			(*tmp)->user_type->status = exists;
			break;
		case IIS_MD_UT_WAM:
			(*tmp)->user_type->value = "IIS_MD_UT_WAM";
			(*tmp)->user_type->dataType = stringType;
			(*tmp)->user_type->status = exists;
			break;
		default:
			(*tmp)->user_type->value = "";
			(*tmp)->user_type->dataType = stringType;
			(*tmp)->user_type->status = doesNotExist;
	}

	//	Get the data
	switch ((*metaRecordIn).dwMDDataType)
	{
		case BINARY_METADATA:
		{
			(*tmp)->data_type->value = "BINARY_METADATA";
			(*tmp)->data_type->dataType = stringType;
			(*tmp)->data_type->status = exists;
			(*tmp)->data->dataType = binaryType;
			(*tmp)->data->status = exists;

			// The buffer must be three bytes long, two bytes for each hex charater in the
			// binary data, and one byte for the terminating NULL character.

			char binaryBuf[3];

			// Loop through each hex character.  By zero-ing out the binaryBuf before
			// _snprintf(), we are making sure the buffer is NULL terminated.

			for (i=0; i<(*metaRecordIn).dwMDDataLen; i++)
			{
				ZeroMemory(binaryBuf, 3);
				_snprintf(binaryBuf, 2, "%02x", (*metaRecordIn).pbMDData[i]);
				(*tmp)->data->value.append(binaryBuf);
				(*tmp)->data->value.append(" ");
			}

			break;
		}

		case DWORD_METADATA:
		{
			(*tmp)->data_type->value = "DWORD_METADATA";
			(*tmp)->data_type->dataType = stringType;
			(*tmp)->data_type->status = exists;
			(*tmp)->data->dataType = integerType;
			(*tmp)->data->status = exists;

			// The dwordBuf is 12 bytes since the max DWORD (2,147,483,647) is 10 characters
			// long.  Also add a byte for a possible negative sign and a byte for the
			// terminating NULL character.

			char dwordBuf[12];

			ZeroMemory(dwordBuf, sizeof(dwordBuf));
			_snprintf(dwordBuf, sizeof(dwordBuf)-1, "%d", *(DWORD *)(*metaRecordIn).pbMDData);
			dwordBuf[sizeof(dwordBuf)-1] = '\0';

			(*tmp)->data->value.append(dwordBuf);
			
			break;
		}

		case EXPANDSZ_METADATA:
		{
			(*tmp)->data_type->value = "EXPANDSZ_METADATA";
			(*tmp)->data_type->dataType = stringType;
			(*tmp)->data_type->status = exists;
			(*tmp)->data->dataType = stringType;
			(*tmp)->data->status = exists;

			// The buffer must be three bytes long, two bytes for each wide charater in the
			// string data, and one byte for the terminating NULL character.

			char expandszBuf[3];

			// Loop through each wide character.  Make sure the buffer is NULL terminated.
			// The loop goes to (dwMDDataLen - 2) since we can skip the last character.  It
			// is a terminating NULL charater and will be automatically replaced during the
			// append method.

			// NOTE: If dwMDDataLen < 2 then (dwMDDataLen-2) becomes a very high DWORD.
			// This is because there are no negative numbers for DWORDS.  Make sure we guard
			// against this by checking value of dwMDDataLen.

			if ((*metaRecordIn).dwMDDataLen >= 2)
			{
				for (i=0; i<((*metaRecordIn).dwMDDataLen-2); i=i+2)
				{
					ZeroMemory(expandszBuf, sizeof(expandszBuf));

					// NOTE: When %C is used with printf functions, it specifies a wide
					// character.  When %C is used with wprintf functions, it specifies a
					// single-byte character.

					_snprintf(expandszBuf, sizeof(expandszBuf)-1, "%C", (*metaRecordIn).pbMDData[i]);
					expandszBuf[sizeof(expandszBuf)-1] = '\0';
					(*tmp)->data->value.append(expandszBuf);
				}
			}

			break;
		}

		case MULTISZ_METADATA:
		{
			(*tmp)->data_type->value = "MULTISZ_METADATA";
			(*tmp)->data_type->dataType = stringType;
			(*tmp)->data_type->status = exists;
			(*tmp)->data->dataType = stringType;
			(*tmp)->data->status = exists;

			// The buffer must be three bytes long, two bytes for each wide charater in the
			// string data, and one byte for the terminating NULL character.

			char multiszBuf[3];

			// Loop through each wide character.  Make sure the buffer is NULL terminated.
			// MULTISZ data is an array of null-terminated strings, terminated by two null
			// characters.  Therefore, the loop goes to (dwMDDataLen - 4) since we can skip
			// the last two characters.  This keeps an extra bar from beeing added to the
			// end of the valueString.  A terminating NULL charater and will be
			// automatically replaced during the append method.

			// NOTE: If dwMDDataLen < 4 then (dwMDDataLen-4) becomes a very high DWORD.
			// This is because there are no negative numbers for DWORDS.  Make sure we guard
			// against this by checking value of dwMDDataLen.
			if ((*metaRecordIn).dwMDDataLen >= 4)
			{
				for (i=0; i<((*metaRecordIn).dwMDDataLen-4); i=i+2)
				{
					ZeroMemory(multiszBuf, sizeof(multiszBuf));
					_snprintf(multiszBuf, sizeof(multiszBuf)-1, "%C", (*metaRecordIn).pbMDData[i]);
					multiszBuf[sizeof(multiszBuf)-1] = '\0';
					if (multiszBuf[0] == '\0') (*tmp)->data->value.append("|");
					else (*tmp)->data->value.append(multiszBuf);
				}
			}

			break;
		}

		case STRING_METADATA:
		{
			(*tmp)->data_type->value = "STRING_METADATA";
			(*tmp)->data_type->dataType = stringType;
			(*tmp)->data_type->status = exists;
			(*tmp)->data->dataType = stringType;
			(*tmp)->data->status = exists;

			char strBuf[3];

			// NOTE: If dwMDDataLen < 2 then (dwMDDataLen-2) becomes a very high DWORD.
			// This is because there are no negative numbers for DWORDS.  Make sure we guard
			// against this by checking value of dwMDDataLen.

			if ((*metaRecordIn).dwMDDataLen >= 2)
			{
				for (i=0; i<((*metaRecordIn).dwMDDataLen-2); i=i+2)
				{
					ZeroMemory(strBuf, sizeof(strBuf));
					_snprintf(strBuf, sizeof(strBuf)-1, "%C", (*metaRecordIn).pbMDData[i]);
					strBuf[sizeof(strBuf)-1] = '\0';
					(*tmp)->data->value.append(strBuf);
				}
			}

			break;
		}

		default:
		{
			(*tmp)->data_type->value = "";
			(*tmp)->data_type->status = doesNotExist;
			(*tmp)->data_type->dataType = stringType;
			(*tmp)->data->value = "";
			(*tmp)->data->value = doesNotExist;
			(*tmp)->data_type->value = stringType;
		}
	}
}

DataElement* MetabaseKeyProbe::GetData(METADATA_GETALL_RECORD *metaRecordIn, unsigned char* pbIdentBuffer)
{
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//
	//  Return the data of the METADATA_GETALL_RECORD as a string 
	//	The data for a METADATA_GETALL_RECORD is stored after the array of records 
	//	that is returned by the call to GetAllData(). Each record in the array has an
	//	offset to its data in the buffer and a length for its data.
	//
	// -----------------------------------------------------------------------
	DataElement *elm = new DataElement();
	DWORD i;

	//	Create a buffer the size of the data input
	char *dataBuff = (char*)malloc((*metaRecordIn).dwMDDataLen);
	ZeroMemory(dataBuff, sizeof(dataBuff));

	memcpy(dataBuff, (pbIdentBuffer + (*metaRecordIn).dwMDDataOffset), (*metaRecordIn).dwMDDataLen);

	//	Get the data
	switch ((*metaRecordIn).dwMDDataType)
	{
		case BINARY_METADATA:
		{
			elm->dataType = binaryType;
			// The buffer must be three bytes long, two bytes for each hex charater in the
			// binary data, and one byte for the terminating NULL character.

			char binaryBuf[3];

			// Loop through each hex character.  By zero-ing out the binaryBuf before
			// _snprintf(), we are making sure the buffer is NULL terminated.

			for (i=0; i<(*metaRecordIn).dwMDDataLen; i++)
			{
				ZeroMemory(binaryBuf, 3);
				_snprintf(binaryBuf, 2, "%02x", dataBuff[i]);
				elm->value.append(binaryBuf);
				elm->value.append(" ");
			}

			break;
		}

		case DWORD_METADATA:
		{

			elm->dataType = integerType;
			// The dwordBuf is 12 bytes since the max DWORD (2,147,483,647) is 10 characters
			// long.  Also add a byte for a possible negative sign and a byte for the
			// terminating NULL character.

			char dwordBuf[12];

			ZeroMemory(dwordBuf, sizeof(dwordBuf));
			_snprintf(dwordBuf, sizeof(dwordBuf)-1, "%d", *(DWORD *)dataBuff);
			dwordBuf[sizeof(dwordBuf)-1] = '\0';

			elm->value.append(dwordBuf);
			
			break;
		}

		case EXPANDSZ_METADATA:
		{
			elm->dataType = stringType;

			// The buffer must be three bytes long, two bytes for each wide charater in the
			// string data, and one byte for the terminating NULL character.

			char expandszBuf[3];

			// Loop through each wide character.  Make sure the buffer is NULL terminated.
			// The loop goes to (dwMDDataLen - 2) since we can skip the last character.  It
			// is a terminating NULL charater and will be automatically replaced during the
			// append method.

			// NOTE: If dwMDDataLen < 2 then (dwMDDataLen-2) becomes a very high DWORD.
			// This is because there are no negative numbers for DWORDS.  Make sure we guard
			// against this by checking value of dwMDDataLen.

			if ((*metaRecordIn).dwMDDataLen >= 2)
			{
				for (i=0; i<((*metaRecordIn).dwMDDataLen-2); i=i+2)
				{
					ZeroMemory(expandszBuf, sizeof(expandszBuf));

					// NOTE: When %C is used with printf functions, it specifies a wide
					// character.  When %C is used with wprintf functions, it specifies a
					// single-byte character.

					_snprintf(expandszBuf, sizeof(expandszBuf)-1, "%C", dataBuff[i]);
					expandszBuf[sizeof(expandszBuf)-1] = '\0';
					elm->value.append(expandszBuf);
				}
			}

			break;
		}

		case MULTISZ_METADATA:
		{
			elm->dataType = stringType;

			// The buffer must be three bytes long, two bytes for each wide charater in the
			// string data, and one byte for the terminating NULL character.

			char multiszBuf[3];

			// Loop through each wide character.  Make sure the buffer is NULL terminated.
			// MULTISZ data is an array of null-terminated strings, terminated by two null
			// characters.  Therefore, the loop goes to (dwMDDataLen - 4) since we can skip
			// the last two characters.  This keeps an extra bar from beeing added to the
			// end of the valueString.  A terminating NULL charater and will be
			// automatically replaced during the append method.

			// NOTE: If dwMDDataLen < 4 then (dwMDDataLen-4) becomes a very high DWORD.
			// This is because there are no negative numbers for DWORDS.  Make sure we guard
			// against this by checking value of dwMDDataLen.
			if ((*metaRecordIn).dwMDDataLen >= 4)
			{
				for (i=0; i<((*metaRecordIn).dwMDDataLen-4); i=i+2)
				{
					ZeroMemory(multiszBuf, sizeof(multiszBuf));
					_snprintf(multiszBuf, sizeof(multiszBuf)-1, "%C", dataBuff[i]);
					multiszBuf[sizeof(multiszBuf)-1] = '\0';
					if (multiszBuf[0] == '\0') 
						elm->value.append("|");
					else 
						elm->value.append(multiszBuf);
				}
			}

			break;
		}

		case STRING_METADATA:
		{
			elm->dataType = stringType;

			char strBuf[3];

			// NOTE: If dwMDDataLen < 2 then (dwMDDataLen-2) becomes a very high DWORD.
			// This is because there are no negative numbers for DWORDS.  Make sure we guard
			// against this by checking value of dwMDDataLen.

			if ((*metaRecordIn).dwMDDataLen >= 2)
			{
				for (i=0; i<((*metaRecordIn).dwMDDataLen-2); i=i+2)
				{
					ZeroMemory(strBuf, sizeof(strBuf));
					_snprintf(strBuf, sizeof(strBuf)-1, "%C", dataBuff[i]);
					strBuf[sizeof(strBuf)-1] = '\0';
					elm->value.append(strBuf);
				}
			}

			break;
		}

		default:
		{
			elm->value = "";
			elm->status = doesNotExist;
		}
	}

	//	Release the memory allocated to the data buffer
	ZeroMemory(dataBuff, sizeof(dataBuff));


	return elm;
}

DataElement* MetabaseKeyProbe::GetDataType(METADATA_GETALL_RECORD *metaRecordIn)
{
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//
	//  Return the data_type of the METADATA_GETALL_RECORD as a string 
	//
	// -----------------------------------------------------------------------
	DataElement *elm = new DataElement();

	switch ((*metaRecordIn).dwMDDataType)
	{
		case BINARY_METADATA:
			elm->value = "BINARY_METADATA";
			break;
		case DWORD_METADATA:
			elm->value = "DWORD_METADATA";
			break;
		case EXPANDSZ_METADATA:
			elm->value = "EXPANDSZ_METADATA";
			break;
		case MULTISZ_METADATA:
			elm->value = "MULTISZ_METADATA";
			break;
		case STRING_METADATA:
			elm->value = "STRING_METADATA";
			break;
		default:
			elm->value = "";
			elm->status = doesNotExist;
	}

	return elm;
}

string MetabaseKeyProbe::GetIdentifier(METADATA_GETALL_RECORD *metaRecordIn)
{
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//
	//  Return the identifier of the METADATA_GETALL_RECORD as a string 
	//
	// -----------------------------------------------------------------------

	//	Convert the DWORD identifier to a string
	char strIdentifier[10];
	ZeroMemory(strIdentifier, sizeof(strIdentifier));
	_snprintf(strIdentifier, sizeof(strIdentifier)-1, "%d", (*metaRecordIn).dwMDIdentifier);
	strIdentifier[sizeof(strIdentifier)-1] = '\0';

	return strIdentifier;
}

DataElement* MetabaseKeyProbe::GetUserType(METADATA_GETALL_RECORD *metaRecordIn)
{
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//
	//  Return the user_type of the METADATA_GETALL_RECORD as a string 
	//
	// -----------------------------------------------------------------------

	DataElement *elm = new DataElement(); 
	
	switch ((*metaRecordIn).dwMDUserType)
	{
		case ASP_MD_UT_APP:
			elm->value = "ASP_MD_UT_APP";
			break;
		case IIS_MD_UT_FILE:
			elm->value = "IIS_MD_UT_FILE";
			break;
		case IIS_MD_UT_SERVER:
			elm->value = "IIS_MD_UT_SERVER";
			break;
		case IIS_MD_UT_WAM:
			elm->value = "IIS_MD_UT_WAM";
			break;
		default:
			elm->value = "";
			elm->status = doesNotExist;
	}

	return elm;
}

bool MetabaseKeyProbe::ValidateKey(string strKeyIn, IMSAdminBase *pIABase)
{
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//
	//  Return the true if the key exists. The function attemps to open the key. 
	//	If the key can be opened it exists..Must ensure that the key is closed 
	//	before returning.
	//
	// -----------------------------------------------------------------------

	HRESULT hRes;
	string workingKey	= strKeyIn;
	bool keyExists		= false;

	/////////////////////////////////////////////////////////////////
	//	Prepare the working key
	//	Reverse the slashes so they are all facing ->
	//	Ensure that there is a leading slash
	/////////////////////////////////////////////////////////////////
	workingKey = Common::SwitchChar(workingKey, "\\", "/");
	if(workingKey.find("/") != 0)
		workingKey.insert(0, "/");

	//	Convert the key from a string to a wchar_t*.
	//	Must add 1 to size to include the NULL at the end of the string
	LPWSTR pbKey;
	size_t size = mbstowcs(NULL, workingKey.c_str(), workingKey.length()) + 1;
	pbKey = new wchar_t[size];
	size = mbstowcs(pbKey, workingKey.c_str(), size) + 1;
	if (size == -1)
	{
		string errorMessage = "";
		errorMessage.append("There was an error converting the key '");
		errorMessage.append(workingKey);
		errorMessage.append("to UNICODE.");
		throw MetabaseKeysProbeException(errorMessage);
	}

	/////////////////////////////////////////////////////////////////
	//	Check that the key exists. Store it if it does
	/////////////////////////////////////////////////////////////////
	METADATA_HANDLE tmpMetaDataHandle;
	hRes = pIABase->OpenKey(METADATA_MASTER_ROOT_HANDLE,	//	handle to the metabase
							pbKey,							//	key to open
							METADATA_PERMISSION_READ,		//
							2000,							//
							&tmpMetaDataHandle);			//

	if(hRes == ERROR_SUCCESS)
	{
		//	Add the key to the return structure
		keyExists = true;

	}else if(hRes == ERROR_PATH_NOT_FOUND)
	{
		//	Do nothing the key doesn't exist		

	}else if(hRes == ERROR_PATH_BUSY)
	{
		//	Write message and return
		string errMsg = "(MetabaseKeyProbe) The path to the key: '" + strKeyIn + "' is busy and con not be opened.";
	
		//	Close the key
		hRes = pIABase->CloseKey(tmpMetaDataHandle);

		throw MetabaseKeysProbeException(errMsg);

	}else if(hRes == ERROR_INVALID_PARAMETER)
	{
		//	Write message and return
		string errMsg = "(MetabaseKeyProbe) An invalid parameter was specified while opening the key: '" + strKeyIn + "'.";

		//	Close the key
		hRes = pIABase->CloseKey(tmpMetaDataHandle);

		throw MetabaseKeysProbeException(errMsg);
		

	}else
	{
		char errorCodeBuffer[20];
		_ltoa(hRes, errorCodeBuffer, 20);

		//	Write message and return
		string errMsg = "(MetabaseKeyProbe) An unknown error occured while opening the key: '" + strKeyIn + "'.";
		errMsg.append("\nError code - ");
		errMsg.append(errorCodeBuffer);

		//	Close the key
		hRes = pIABase->CloseKey(tmpMetaDataHandle);

		throw MetabaseKeysProbeException(errMsg);
	}

	//	Close the key
	hRes = pIABase->CloseKey(tmpMetaDataHandle);

	return keyExists;
}


//****************************************************************************************//
//						MetabaseKeysProbeException Class								  //	
//****************************************************************************************//
MetabaseKeysProbeException::MetabaseKeysProbeException(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.
	//
	// -----------------------------------------------------------------------

}

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

}