//***************************************************************************
//
//  Copyright (c) 1998-2000 Microsoft Corporation
//
//  File:  parsedn.cxx
//
//	Description :
//				Parses CIM paths to objects and returns the requested object
//***************************************************************************

#include "precomp.h"

#define CURRENTSTR (lpszInputString + *pchEaten)

#define SKIPWHITESPACE \
	while (*CURRENTSTR && _istspace( *CURRENTSTR ) ) \
			(*pchEaten)++;

#define WBEMS_STR_OWNER		L"O"
#define	WBEMS_STR_GROUP		L"G"
#define WBEMS_STR_DACL		L"D"
#define WBEMS_STR_SACL		L"S"

static void SecureProxy (bool authnSpecified, enum WbemAuthenticationLevelEnum eAuthLevel,
						 bool impSpecified, enum WbemImpersonationLevelEnum eImpersonLevel,
						 ISWbemServices *pService)
{
	// Secure the proxy using the specified security settings (if any)
	CComPtr<ISWbemSecurity> pSecurity;
	
	if (authnSpecified || impSpecified)
	{
		if (SUCCEEDED(pService->get_Security_(&pSecurity)))
		{
			if (authnSpecified)
				pSecurity->put_AuthenticationLevel (eAuthLevel);

			if (impSpecified)
				pSecurity->put_ImpersonationLevel (eImpersonLevel);
		}
	}
}

static void SecureProxy (bool authnSpecified, enum WbemAuthenticationLevelEnum eAuthLevel,
						 bool impSpecified, enum WbemImpersonationLevelEnum eImpersonLevel,
						 ISWbemObject *pObject)
{
	// Secure the proxy using the specified security settings (if any)
	CComPtr<ISWbemSecurity> pSecurity;
	
	if (authnSpecified || impSpecified)
	{
		if (SUCCEEDED(pObject->get_Security_(&pSecurity)))
		{
			if (authnSpecified)
				pSecurity->put_AuthenticationLevel (eAuthLevel);

			if (impSpecified)
				pSecurity->put_ImpersonationLevel (eImpersonLevel);
		}
	}
}

//***************************************************************************
//
//  CWbemParseDN::CWbemParseDN
//
//  DESCRIPTION:
//
//  Constructor.
//
//***************************************************************************

CWbemParseDN::CWbemParseDN():
			m_cRef(0)
{
	InterlockedIncrement(&g_cObj);
}

//***************************************************************************
//
//  CWbemParseDN::~CWbemParseDN
//
//  DESCRIPTION:
//
//  Destructor.
//  
//***************************************************************************

CWbemParseDN::~CWbemParseDN(void)
{
	InterlockedDecrement(&g_cObj);
}			

//***************************************************************************
// HRESULT CWbemParseDN::QueryInterface
// long CWbemParseDN::AddRef
// long CWbemParseDN::Release
//
// DESCRIPTION:
//
// Standard Com IUNKNOWN functions.
//
//***************************************************************************

STDMETHODIMP CWbemParseDN::QueryInterface (

	IN REFIID riid,
    OUT LPVOID *ppv
)
{
    *ppv=NULL;

    if (IID_IUnknown==riid)
		*ppv = (IUnknown *)this;
	else if (IID_IParseDisplayName==riid)
        *ppv = (IParseDisplayName *)this;

    if (NULL!=*ppv)
    {
        ((LPUNKNOWN)*ppv)->AddRef();
        return NOERROR;
    }

    return E_NOINTERFACE;
}

STDMETHODIMP_(ULONG) CWbemParseDN::AddRef(void)
{
    InterlockedIncrement(&m_cRef);
    return m_cRef;
}

STDMETHODIMP_(ULONG) CWbemParseDN::Release(void)
{
    InterlockedDecrement(&m_cRef);
    if (0L!=m_cRef)
        return m_cRef;
    delete this;
    return 0;
}

//***************************************************************************
//
//  SCODE CWbemParseDN::ParseDisplayName
//
//  DESCRIPTION:
//
//  Take a CIM object path and return a suitable ISWbem... object 
//
//  PARAMETERS:
//
//	pCtx					The binding context (not used)
//	szDisplayName			The display name to be parsed
//  pchEaten				On return identifies how much of the DN has been 
//							consumed
//  ppmk					On return will address the moniker pointer
//
//  RETURN VALUES:
//
//  E_FAIL					misery
//
//	Other CreateMoniker codes are returned.
//
//***************************************************************************
STDMETHODIMP CWbemParseDN::ParseDisplayName(
	IBindCtx* pCtx, 
	LPOLESTR szDisplayName, 
	ULONG* pchEaten, 
	IMoniker** ppmk)
{
    HRESULT hr = E_FAIL;
    LPUNKNOWN pUnknown = NULL;
	ULONG lTemp = 0;
	
	enum WbemAuthenticationLevelEnum eAuthLevel;
	enum WbemImpersonationLevelEnum eImpersonLevel;
	bool authnSpecified = false;
	bool impSpecified = false;
	BSTR bsAuthority = NULL;
		
	//Check input parameters
	*ppmk = NULL;
    if (NULL != pchEaten)
        *pchEaten = 0;

	if (NULL == szDisplayName)
		return E_FAIL;

	/*
	 *  moniker :  wmiMoniker 
	 *
	 *	wmiMoniker : ["winmgmts:" | "wmi:"] securitySetting ["[" localeSetting "]"] ["!" objectPath]
	 *					| ["winmgmts:" | "wmi:"] "[" localeSetting "]" ["!" objectPath]
	 *					| ["winmgmts:" | "wmi:"] [objectPath]
	 *					| [nativePath]
	 *
	 *	localeSetting    : "locale" <ows> "=" <ows> localeID
	 *
	 *	localeID        : a value of the form "ms_xxxx" where xxxx is a hex LCID value e.g. "ms_0x409".
	 *
	 *	objectPath     : a valid WMI Object Path
	 *
	 *	securitySetting : "{" <ows> authAndImpersonSettings [<ows> "," <ows> privilegeOverrides]
	 *				| "{" <ows> authAndImpersonSettings [<ows> "," <ows> privilegeOverrides] <ows> "}" <ows>
	 *				| "{" <ows> privilegeOverrides <ows> "}" <ows>
	 *				
	 *
	 *	authAndImpersonSettings : 
	 *			authenticationLevel 
	 *			| impersonationLevel
	 *			| authority 
	 *			| authenticationLevel <ows> "," <ows> impersonationLevel [<ows> "," <ows> authority]
	 *			| authenticationLevel <ows> "," <ows> authority [<ows> "," <ows> impersonationLevel]
	 *			| impersonationLevel <ows> "," <ows> authenticationLevel [<ows> "," <ows> authority]
	 *			| impersonationLevel <ows> "," <ows> authority [<ows> "," <ows> authenticationLevel]
	 *			| authority <ows> "," <ows> impersonationLevel [<ows> "," <ows> authenticationLevel]
	 *			| authority <ows> "," <ows> authenticationLevel [<ows> "," <ows> impersonationLevel]
     *               
	 *
	 *	authority : "authority" <ows> "=" <ows> authorityValue
	 *
	 *	authorityValue :    Any valid WMI authority string e.g. "kerberos:mydomain\server" or "ntlmdomain:mydomain".   Note that backslashes need to be escaped in JScript.
	 *
	 *	authenticationLevel : "authenticationLevel" <ows> "=" <ows> authenticationValue 
	 *
	 *	authenticationValue : "default" | "none" | "connect" | "call" | "pkt" | "pktIntegrity" | "pktPrivacy" 
	 *
	 *	impersonationLevel : "impersonationLevel" <ows> "=" <ows> impersonationValue 
	 *
	 *	impersonationValue : "anonymous" | "identify" | "impersonate" | "delegate"
	 *
	 *	privilegeOverrides : "(" <ows> privileges <ows> ")"
	 *
	 *	privileges : privilege [<ows> "," <ows> privileges <ows>]*
	 *
	 *	privilege : ["!"] privilegeName
	 *
	 *	privilegeName : "CreateToken" | "PrimaryToken" | "LockMemory" | "IncreaseQuota" 
	 *						| "MachineAccount" | "Tcb" | "Security" | "TakeOwnership" 
	 *						| "LoadDriver" | "SystemProfile" | "SystemTime" 
	 *						| "ProfileSingleProcess" | "IncreaseBasePriority" 
	 *						| "CreatePagefile" | "CreatePermanent" | "Backup" | "Restore" 
	 *						| "Shutdown" | "Debug" | "Audit" | "SystemEnvironment" | "ChangeNotify" 
	 *						| "RemoteShutdown"
	 *
	 */

	// It had better start with our scheme name
	bool bCheckContext = false;

	if (0 == _wcsnicmp (szDisplayName, WBEMS_PDN_SCHEME, wcslen (WBEMS_PDN_SCHEME)))
	{
		*pchEaten += wcslen (WBEMS_PDN_SCHEME);
		bCheckContext = (pCtx && (wcslen (szDisplayName) == wcslen (WBEMS_PDN_SCHEME)));
	}
	else
		return E_FAIL;

	// One more check - if it was just the scheme and no more check for extra info in the context
	if (bCheckContext)
	{
		IUnknown *pUnk = NULL;

		if (SUCCEEDED (pCtx->GetObjectParam (L"WmiObject", &pUnk)) && pUnk)
		{
			// Is it an IWbemClassObject?
			IWbemClassObject *pIWbemClassObject = NULL;
			// Or is it an IWbemContext?
			IWbemContext *pIWbemContext = NULL;
			// Or is it an IWbemServices?
			IWbemServices *pIWbemServices = NULL;

			if (SUCCEEDED (pUnk->QueryInterface (IID_IWbemClassObject, (void **) &pIWbemClassObject)))
			{
				CSWbemObject *pSWbemObject = new CSWbemObject (NULL, pIWbemClassObject);

				if (!pSWbemObject)
					hr = E_OUTOFMEMORY;
				else
				{
					CComPtr<ISWbemObjectEx> pISWbemObjectEx;
						
					if (SUCCEEDED (pSWbemObject->QueryInterface (IID_ISWbemObjectEx, (void **) &pISWbemObjectEx)))
						hr = CreatePointerMoniker (pISWbemObjectEx, ppmk);
				}

				pIWbemClassObject->Release ();
			} 
			else if (SUCCEEDED (pUnk->QueryInterface (IID_IWbemContext, (void **) &pIWbemContext)))
			{
				CSWbemNamedValueSet *pSWbemNamedValueSet = new CSWbemNamedValueSet (NULL, pIWbemContext);

				if (!pSWbemNamedValueSet)
					hr = E_OUTOFMEMORY;
				else
				{
					CComPtr<ISWbemNamedValueSet> pISWbemNamedValueSet;
						
					if (SUCCEEDED (pSWbemNamedValueSet->QueryInterface (IID_ISWbemNamedValueSet, 
														(PPVOID)&pISWbemNamedValueSet)))
						hr = CreatePointerMoniker (pISWbemNamedValueSet, ppmk);
				}
					
				pIWbemContext->Release ();
			} 
			else if (SUCCEEDED (pUnk->QueryInterface (IID_IWbemServices, (void **) &pIWbemServices)))
			{
				// In this case we must get passed the object path as well
				CComPtr<IUnknown> pUnkPath;

				if (SUCCEEDED (pCtx->GetObjectParam (L"WmiObjectPath", &pUnkPath)) && pUnkPath)
				{
					CComPtr<ISWbemObjectPath> pISWbemObjectPath;
					
					if (SUCCEEDED (pUnkPath->QueryInterface (IID_ISWbemObjectPath, (void **) &pISWbemObjectPath)))
					{
						// Dig the path out to initialize 
						CComBSTR bsNamespace = NULL;

						pISWbemObjectPath->get_Path (&bsNamespace);

						CSWbemServices *pSWbemServices = new CSWbemServices (pIWbemServices, 
														bsNamespace, (BSTR) NULL, NULL, NULL);

						if (!pSWbemServices)
							hr = E_OUTOFMEMORY;
						else
						{
							CComQIPtr<ISWbemServicesEx>
											pISWbemServicesEx (pSWbemServices);
							
							if (pISWbemServicesEx)
								hr = CreatePointerMoniker (pISWbemServicesEx, ppmk);
						}
					}
				}
				pIWbemServices->Release ();
			}

			pUnk->Release ();
		}

		// If this worked return now - o/w revert to regular parsing
		if (SUCCEEDED (hr))
			return hr;
	}
	
	// Check for the optional security info
	CSWbemPrivilegeSet	privilegeSet;

	if (ParseSecurity(szDisplayName + *pchEaten, &lTemp, authnSpecified, &eAuthLevel, 
										impSpecified, &eImpersonLevel, privilegeSet,
										bsAuthority))
		*pchEaten += lTemp;

	// If no impersonation level was specified, get the default from the registry
	if (!impSpecified)
	{
		eImpersonLevel = CSWbemSecurity::GetDefaultImpersonationLevel ();
		impSpecified = true;
	}

	// Create a locator
	CSWbemLocator *pCSWbemLocator = new CSWbemLocator(&privilegeSet);

	if (!pCSWbemLocator)
		hr = E_OUTOFMEMORY;
	else
	{
		CComQIPtr<ISWbemLocator> pISWbemLocator (pCSWbemLocator);

		if (pISWbemLocator)
		{
			// Parse the locale information (if present)
			lTemp = 0;
			BSTR bsLocale = NULL;

			if (ParseLocale (szDisplayName + *pchEaten, &lTemp, bsLocale))
			{
				*pchEaten += lTemp;

				// Skip over the "!" separator if there is one
				if(*(szDisplayName + *pchEaten) != NULL)
					if (0 == _wcsnicmp (szDisplayName + *pchEaten, WBEMS_EXCLAMATION, wcslen (WBEMS_EXCLAMATION)))
						*pchEaten += wcslen (WBEMS_EXCLAMATION);

				// Now ready to parse the path - check if we have the degenerate cases
				if (0 == wcslen (szDisplayName + *pchEaten))
				{		
					// Need to return connection to default namespace on local machine
					CComPtr<ISWbemServices> pISWbemServices;
					if (SUCCEEDED( hr = pISWbemLocator->ConnectServer (NULL, NULL, NULL, NULL,
								bsLocale, bsAuthority, 0, NULL, &pISWbemServices)) )
					{
						SecureProxy (authnSpecified, eAuthLevel, impSpecified, eImpersonLevel, pISWbemServices);
						hr = CreatePointerMoniker(pISWbemServices, ppmk);
					}
				}
				else
				{
					/*
					 * Check the path to see if we are dealing with a class or an instance.
					 * Note that we construct the parser with a flag indicating that relative
					 * namespace paths are OK (not the default behavior).
					 */
					CWbemPathCracker	pathCracker (szDisplayName + *pchEaten);

					if (CWbemPathCracker::WbemPathType::wbemPathTypeError != pathCracker.GetType ())
					{
						CComBSTR bsNamespacePath, bsServerPath;

						if (pathCracker.GetNamespacePath (bsNamespacePath)
							&& pathCracker.GetServer (bsServerPath))
						{
							// Success - begin by connecting to the namespace.
							CComPtr<ISWbemServices> pISWbemServices;
							
							if (SUCCEEDED( hr = pISWbemLocator->ConnectServer (bsServerPath, 
									bsNamespacePath, NULL, NULL, bsLocale, bsAuthority, 0, NULL, &pISWbemServices)) )
							{
								// Secure the proxy using the specified security settings (if any)
								SecureProxy (authnSpecified, eAuthLevel, impSpecified, eImpersonLevel, pISWbemServices);
							
								// Successful connection - now work out if we have a class or instance
								// component. 
								if (pathCracker.IsClassOrInstance())
								{
									CComPtr<ISWbemObject> pISWbemObject;

									// Now get it
									CComBSTR bsRelPath;
									
									if (pathCracker.GetPathText (bsRelPath, true))
									{
										long lFlags = 0; 

										// Note that when we retrieve the object we will retrieve
										// the localized version if a locale was specified in the moniker
										if ((NULL != bsLocale) && (0 < wcslen (bsLocale)))
											lFlags |= wbemFlagUseAmendedQualifiers;

										if (SUCCEEDED( hr = pISWbemServices->Get (bsRelPath,
														lFlags, NULL, &pISWbemObject)) )
											hr = CreatePointerMoniker (pISWbemObject, ppmk);
									}
								}
								else
								{
									// Just a namespace
									hr = CreatePointerMoniker(pISWbemServices, ppmk);				
								}
							}
						}
						else
							hr = WBEM_E_INVALID_SYNTAX;	// Parse failure - abandon ship
					}
					else
						hr = WBEM_E_INVALID_SYNTAX;	// Parse failure - abandon ship
				}
			}
			else
			{
				// Parse failure
				hr = WBEM_E_INVALID_SYNTAX;
			}

			SysFreeString (bsLocale);
		}
	}

	SysFreeString (bsAuthority);

	if (FAILED (hr))
		*pchEaten = 0;
	else
		*pchEaten = wcslen(szDisplayName);

	return hr;
}

//***************************************************************************
//
//  BOOLEAN CWbemParseDN::ParseSecurity
//
//  DESCRIPTION:
//
//  Take an authentication and impersonlation level string as described by the 
//	non-terminal authAndImpersonLevel and parse it into the authentication
// and impersonation levels
//
//  PARAMETERS:
//
//	lpszInputString			The string to be parsed
//  pchEaten				On return identifies how much of the DN has been 
//							consumed
//	authnSpecified			Whether the Moniker specifies a non-default 
//							authn levl
//	lpeAuthLevel			The authentication level parsed. This is one of 
//							enum WbemAuthenticationLevelEnum.
//	impSpecified			Whether the Moniker specifies a non-default imp 
//							level
//	lpeImpersonLevel		The impersonation level parsed. This is one of 
//							enum WbemImpersonationLevelEnum.
//	privilegeSet			On return contains the specified privileges
//	bsAuthority				On return contains the specified authority
//
//  RETURN VALUES:
//
//  TRUE					Parsing was successful. The lpeAuthLevel and 
//							lpeImpersonLevel arguments have valid data.
//	FALSE					Parsing failed.
//
//
//***************************************************************************

bool CWbemParseDN::ParseSecurity (
	LPWSTR lpszInputString, 
	ULONG* pchEaten, 
	bool &authnSpecified,
	enum WbemAuthenticationLevelEnum *lpeAuthLevel,
	bool &impSpecified,
	enum WbemImpersonationLevelEnum *lpeImpersonLevel,
	CSWbemPrivilegeSet	&privilegeSet,
	BSTR &bsAuthority)
{
	bool status = false;

	// Set the default authentication and impersonation levels. 
	*lpeAuthLevel = wbemAuthenticationLevelNone;
	*lpeImpersonLevel = wbemImpersonationLevelImpersonate;

	// Initialize the number of consumed characters
	*pchEaten = 0;

	// Parse the contents

	if (ParseAuthAndImpersonLevel (lpszInputString, pchEaten, authnSpecified, lpeAuthLevel,
					impSpecified, lpeImpersonLevel, privilegeSet, bsAuthority))
		status = true;
	else
		*pchEaten = 0;

	return status;
}

//***************************************************************************
//
//  BOOLEAN CWbemParseDN::ParseLocale
//
//  DESCRIPTION:
//
//  Take locale setting string as described by the non-terminal localeSetting 
//	and parse it.
//
//  PARAMETERS:
//
//	lpszInputString			The string to be parsed
//  pchEaten				On return identifies how much of the DN has been 
//							consumed
//	bsLocale				Reference to BSTR to hold parsed locale setting
//
//  RETURN VALUES:
//
//  TRUE					Parsing was successful. 
//	FALSE					Parsing failed.
//
//
//***************************************************************************

bool CWbemParseDN::ParseLocale (
	LPWSTR lpszInputString, 
	ULONG* pchEaten, 
	BSTR &bsLocale)
{
	bool status = true;

	// Initialize the number of consumed characters
	*pchEaten = 0;

	// The first character should be '[' - if not we are done
	if (0 == _wcsnicmp (lpszInputString, WBEMS_LEFT_SQBRK, wcslen (WBEMS_LEFT_SQBRK)))
	{
		status = false;

		*pchEaten += wcslen (WBEMS_LEFT_SQBRK);

		// Parse the locale setting
		SKIPWHITESPACE

		// The next string should be "locale"
		if(0 == _wcsnicmp(lpszInputString + *pchEaten, WBEMS_LOCALE, wcslen(WBEMS_LOCALE)))
		{
			*pchEaten += wcslen (WBEMS_LOCALE);

			SKIPWHITESPACE

			// Next should be "="
			if(0 == _wcsnicmp(lpszInputString + *pchEaten, WBEMS_EQUALS, wcslen(WBEMS_EQUALS)))
			{
				*pchEaten += wcslen (WBEMS_EQUALS);

				SKIPWHITESPACE

				// Now we should have a character not equal to "]" (i.e. must specify locale ID string)
				if (0 != _wcsnicmp (lpszInputString + *pchEaten, WBEMS_RIGHT_SQBRK, wcslen (WBEMS_RIGHT_SQBRK)))
				{
					// Consume everything up to the next space or "]"
					LPWSTR cStr = CURRENTSTR;
					ULONG lEaten = 0;	// How many characters we consume
					ULONG lLocale = 0;	// The actual length of the locale ID
					
					while (*(cStr + lEaten))
					{
						if (_istspace(*(cStr + lEaten)))
						{
							lEaten++;

							// Hit white space - now skip until we find the "]"
							SKIPWHITESPACE

							// Now we must have a "]"
							if 	(0 == _wcsnicmp 
									(cStr + lEaten, WBEMS_RIGHT_SQBRK, wcslen (WBEMS_RIGHT_SQBRK)))
							{
								// Success - we are done
								lEaten += wcslen (WBEMS_RIGHT_SQBRK);
							}

							break;
						}
						else if (0 == _wcsnicmp (cStr + lEaten, WBEMS_RIGHT_SQBRK, wcslen (WBEMS_RIGHT_SQBRK)))
						{
							// Hit closing "]" - we are done
							lEaten += wcslen (WBEMS_RIGHT_SQBRK);
							break;
						}
						else	// Consumed a locale character - keep on truckin'
						{
							lLocale++;
							lEaten++;
						}
					}

					// If we terminated correctly, save the locale setting
					if ((lEaten > 1) && (lLocale > 0))
					{
						status = true;

						LPWSTR pLocaleStr = new WCHAR [lLocale + 1];

						if (pLocaleStr)
						{
							wcsncpy (pLocaleStr, lpszInputString + *pchEaten, lLocale);
							pLocaleStr [lLocale] = NULL;
							bsLocale = SysAllocString (pLocaleStr);

							delete [] pLocaleStr;
							*pchEaten += lEaten;
						}
						else
							status = false;
					}
				}
			}
		}
	}

	if (!status)
		*pchEaten = 0;

	return status;
}

//***************************************************************************
//
//  BOOLEAN CWbemParseDN::ParseAuthAndImpersonLevel
//
//  DESCRIPTION:
//
//  Take an authentication/impersonlation/authority level string as described by the 
//	non-terminal authAndImpersonLevel and parse it into the authentication
//	and impersonation levels and the authority string
//
//  PARAMETERS:
//
//	lpszInputString			The string to be parsed
//  pchEaten				On return identifies how much of the DN has been 
//							consumed
//	authnSpecified			Whether the Moniker specifies a non-default 
//							authn levl
//	lpeAuthLevel			The authentication level parsed. This is one of 
//							enum WbemAuthenticationLevelEnum.
//	impSpecified			Whether the Moniker specifies a non-default imp 
//							level
//	lpeImpersonLevel		The impersonation level parsed. This is one of 
//							enum WbemImpersonationLevelEnum.
//	privilegeSet			On return holds the privileges
//	bsAuthority				On retunr holds the authority string (if any)
//
//  RETURN VALUES:
//
//  TRUE					Parsing was successful. The lpeAuthLevel and 
//							lpeImpersonLevel arguments have valid data.
//	FALSE					Parsing failed.
//
//
//***************************************************************************

bool CWbemParseDN::ParseAuthAndImpersonLevel (
	LPWSTR lpszInputString, 
	ULONG* pchEaten, 
	bool &authnSpecified,
	enum WbemAuthenticationLevelEnum *lpeAuthLevel,
	bool &impSpecified,
	enum WbemImpersonationLevelEnum *lpeImpersonLevel,
	CSWbemPrivilegeSet &privilegeSet,
	BSTR &bsAuthority)
{
	// The first character should be '{'
	if (0 != _wcsnicmp (lpszInputString, WBEMS_LEFT_CURLY, wcslen (WBEMS_LEFT_CURLY)))
		return FALSE;
	else
		*pchEaten += wcslen (WBEMS_LEFT_CURLY);

	bool	authoritySpecified = false;
	bool	privilegeSpecified = false;
	bool	done = false;
	bool	error = false;

	while (!done)
	{
		bool parsingAuthenticationLevel = false;	// Which token are we parsing?
		bool parsingPrivilegeSet = false;
		bool parsingAuthority = false;

		SKIPWHITESPACE
		
		// The next string should be one of "authenticationLevel", "impersonationLevel",
		// "authority", the privilege collection start marker "(", or the security
		// descriptor start marker "<"
		if(0 == _wcsnicmp(lpszInputString + *pchEaten, WBEMS_AUTH_LEVEL, wcslen(WBEMS_AUTH_LEVEL)))
		{
			// Error if we have already parsed this or have parsed privilege set
			if (authnSpecified || privilegeSpecified)
			{
				error = true;
				break;
			}
			else
			{
				parsingAuthenticationLevel = true;
				*pchEaten += wcslen (WBEMS_AUTH_LEVEL);
			}
		}
		else if (0 == _wcsnicmp(lpszInputString + *pchEaten, WBEMS_IMPERSON_LEVEL, wcslen(WBEMS_IMPERSON_LEVEL)))
		{
			// Error if we have already parsed this or have parsed privilege set
			if (impSpecified || privilegeSpecified)
			{
				error = true;
				break;
			}
			else
				*pchEaten += wcslen (WBEMS_IMPERSON_LEVEL) ;
		}
		else if (0 == _wcsnicmp(lpszInputString + *pchEaten, WBEMS_AUTHORITY, wcslen(WBEMS_AUTHORITY)))
		{
			// Error if we have already parsed this or have parsed privilege set
			if (authoritySpecified || privilegeSpecified)
			{
				error = true;
				break;
			}
			else
			{
				parsingAuthority = true;
				*pchEaten += wcslen (WBEMS_AUTHORITY) ;
			}
		}
		else if (0 == _wcsnicmp(lpszInputString + *pchEaten, WBEMS_LEFT_PAREN, wcslen(WBEMS_LEFT_PAREN)))
		{
			// Error if we have already done this
			if (privilegeSpecified)
			{
				error = true;
				break;
			}
			else
			{
				parsingPrivilegeSet = true;
				*pchEaten += wcslen (WBEMS_LEFT_PAREN);
			}
		}
		else
		{
			// Unrecognized token or NULL
			error = true;
			break;
		}

		// Getting here means we have something to parse
		SKIPWHITESPACE

		if (parsingPrivilegeSet)
		{
			ULONG chEaten = 0;

			if (ParsePrivilegeSet (lpszInputString + *pchEaten, &chEaten, privilegeSet))
			{
				privilegeSpecified = true;
				*pchEaten += chEaten;

				// If the next token is "}" we are done
				if(0 == _wcsnicmp(lpszInputString + *pchEaten, WBEMS_RIGHT_CURLY, wcslen(WBEMS_RIGHT_CURLY)))
				{
					*pchEaten += wcslen (WBEMS_RIGHT_CURLY);
					done = true;
				}
			}
			else
			{
				error = true;
				break;
			}
		}
		else
		{
			// Parsing authentication, impersonation or authority. The next character should be '='
			if(0 != _wcsnicmp(lpszInputString + *pchEaten, WBEMS_EQUALS, wcslen(WBEMS_EQUALS)))
			{
				error = true;
				break;
			}
			else
			{
				*pchEaten += wcslen (WBEMS_EQUALS);
				SKIPWHITESPACE

				if (parsingAuthenticationLevel)
				{
					if (!ParseAuthenticationLevel (lpszInputString, pchEaten, lpeAuthLevel))
					{
						error = true;
						break;
					}
					else
						authnSpecified = true;
				}
				else if (parsingAuthority)
				{
					// Get the authority string
					if (!ParseAuthority (lpszInputString, pchEaten, bsAuthority))
					{
						error = true;
						break;
					}
					else
						authoritySpecified = true;
				}
				else
				{
					// Must be parsing impersonation level
					
					if (!ParseImpersonationLevel (lpszInputString, pchEaten, lpeImpersonLevel))
					{
						error = true;
						break;
					}
					else
						impSpecified = true;
				}

				SKIPWHITESPACE
					
				// The next token should be "}" or ","
				if(0 == _wcsnicmp(lpszInputString + *pchEaten, WBEMS_RIGHT_CURLY, wcslen(WBEMS_RIGHT_CURLY)))
				{
					*pchEaten += wcslen (WBEMS_RIGHT_CURLY);
					done = true;
				}
				else if(0 == _wcsnicmp(lpszInputString + *pchEaten, WBEMS_COMMA, wcslen(WBEMS_COMMA)))
				{
					// If we have parsed all expected tokens this is an error
					if (authnSpecified && impSpecified && authoritySpecified && privilegeSpecified)
					{
						error = true;
						break;
					}
					else
					{
						*pchEaten += wcslen (WBEMS_COMMA);
						// Loop round again for the next token
					}
				}
				else
				{
					// Unrecognized token
					error = true;
					break;
				}
			}
		}
	}

	if (error)
	{
		impSpecified = authnSpecified = false;
		*pchEaten = 0;
		return false;
	}

	return true;		// success
}

//***************************************************************************
//
//  BOOLEAN CWbemParseDN::ParseImpersonationLevel
//
//  DESCRIPTION:
//
//  Parse the string specification of an impersonation level into a
//	symbolic constant value.
//
//  PARAMETERS:
//
//	lpszInputString			The string to be parsed
//  pchEaten				On return identifies how much of the DN has been 
//							consumed
//	lpeImpersonLevel		The impersonation level parsed. This is one of 
//							enum WbemImpersonationLevelEnum.
//
//  RETURN VALUES:
//
//  TRUE					Parsing was successful. The lpeImpersonLevel 
//							argument has valid data.
//	FALSE					Parsing failed.
//
//
//***************************************************************************

bool CWbemParseDN::ParseImpersonationLevel (
	LPWSTR lpszInputString, 
	ULONG* pchEaten, 
	enum WbemImpersonationLevelEnum *lpeImpersonLevel
)
{
	bool status = true;	
	
	if(0 == _wcsnicmp(lpszInputString + *pchEaten, WBEMS_IMPERSON_ANON, wcslen(WBEMS_IMPERSON_ANON)))
	{
		*lpeImpersonLevel = wbemImpersonationLevelAnonymous;
		*pchEaten += wcslen (WBEMS_IMPERSON_ANON);
	}
	else if(0 == _wcsnicmp(lpszInputString + *pchEaten, WBEMS_IMPERSON_IDENTIFY, wcslen(WBEMS_IMPERSON_IDENTIFY)))
	{
		*lpeImpersonLevel = wbemImpersonationLevelIdentify;
		*pchEaten += wcslen (WBEMS_IMPERSON_IDENTIFY);
	}
	else if(0 == _wcsnicmp(lpszInputString + *pchEaten, WBEMS_IMPERSON_IMPERSON, wcslen(WBEMS_IMPERSON_IMPERSON)))
	{
		*lpeImpersonLevel = wbemImpersonationLevelImpersonate;
		*pchEaten += wcslen (WBEMS_IMPERSON_IMPERSON);
	}
	else if(0 == _wcsnicmp(lpszInputString + *pchEaten, WBEMS_IMPERSON_DELEGATE, wcslen(WBEMS_IMPERSON_DELEGATE)))
	{
		*lpeImpersonLevel = wbemImpersonationLevelDelegate;
		*pchEaten += wcslen (WBEMS_IMPERSON_DELEGATE);
	}
	else
		status = false;

	return status;
}

//***************************************************************************
//
//  BOOLEAN CWbemParseDN::ParseAuthenticationLevel
//
//  DESCRIPTION:
//
//  Parse the string specification of an authentication level into a
//	symbolic constant value.
//
//  PARAMETERS:
//
//	lpszInputString			The string to be parsed
//  pchEaten				On return identifies how much of the DN has been 
//							consumed
//	lpeAuthLevel			The authentication level parsed. This is one of 
//							enum WbemAuthenticationLevelEnum.
//
//  RETURN VALUES:
//
//  TRUE					Parsing was successful. The lpeAuthLevel 
//							argument has valid data.
//	FALSE					Parsing failed.
//
//
//***************************************************************************

bool CWbemParseDN::ParseAuthenticationLevel (
	LPWSTR lpszInputString, 
	ULONG* pchEaten, 
	enum WbemAuthenticationLevelEnum *lpeAuthLevel
)
{
	bool status = true;	
	
	if(0 == _wcsnicmp(lpszInputString + *pchEaten, WBEMS_AUTH_DEFAULT, wcslen(WBEMS_AUTH_DEFAULT)))
	{
		*lpeAuthLevel = wbemAuthenticationLevelDefault;
		*pchEaten += wcslen (WBEMS_AUTH_DEFAULT);
	}
	else if(0 == _wcsnicmp(lpszInputString + *pchEaten, WBEMS_AUTH_NONE, wcslen(WBEMS_AUTH_NONE)))
	{
		*lpeAuthLevel = wbemAuthenticationLevelNone;
		*pchEaten += wcslen (WBEMS_AUTH_NONE);
	}
	else if(0 == _wcsnicmp(lpszInputString + *pchEaten, WBEMS_AUTH_CONNECT, wcslen(WBEMS_AUTH_CONNECT)))
	{
		*lpeAuthLevel = wbemAuthenticationLevelConnect;
		*pchEaten += wcslen (WBEMS_AUTH_CONNECT);
	}
	else if(0 == _wcsnicmp(lpszInputString + *pchEaten, WBEMS_AUTH_CALL, wcslen(WBEMS_AUTH_CALL)))
	{
		*lpeAuthLevel = wbemAuthenticationLevelCall;
		*pchEaten += wcslen (WBEMS_AUTH_CALL);
	}
	else if(0 == _wcsnicmp(lpszInputString + *pchEaten, WBEMS_AUTH_PKT_INT, wcslen(WBEMS_AUTH_PKT_INT)))
	{
		*lpeAuthLevel = wbemAuthenticationLevelPktIntegrity;
		*pchEaten += wcslen (WBEMS_AUTH_PKT_INT);
	}
	else if(0 == _wcsnicmp(lpszInputString + *pchEaten, WBEMS_AUTH_PKT_PRIV, wcslen(WBEMS_AUTH_PKT_PRIV)))
	{
		*lpeAuthLevel = wbemAuthenticationLevelPktPrivacy;
		*pchEaten += wcslen (WBEMS_AUTH_PKT_PRIV);
	}
	else if(0 == _wcsnicmp(lpszInputString + *pchEaten, WBEMS_AUTH_PKT, wcslen(WBEMS_AUTH_PKT)))
	{
		*lpeAuthLevel = wbemAuthenticationLevelPkt;
		*pchEaten += wcslen (WBEMS_AUTH_PKT);
	}
	else
		status = false;
	
	return status;
}

//***************************************************************************
//
//  BOOLEAN CWbemParseDN::ParseAuthority
//
//  DESCRIPTION:
//
//  Take authority setting string as described by the non-terminal localeSetting 
//	and parse it.
//
//  PARAMETERS:
//
//	lpszInputString			The string to be parsed
//  pchEaten				On return identifies how much of the DN has been 
//							consumed
//	bsAuthority				Reference to BSTR to hold parsed authority string
//
//  RETURN VALUES:
//
//  TRUE					Parsing was successful. 
//	FALSE					Parsing failed.
//
//
//***************************************************************************

bool CWbemParseDN::ParseAuthority (
	LPWSTR lpszInputString, 
	ULONG* pchEaten, 
	BSTR &bsAuthority)
{
	bool status = false;

	// Now we should have a character not equal to "," or "}" (i.e. must specify authority string)
	if ((0 != _wcsnicmp (lpszInputString + *pchEaten, WBEMS_COMMA, wcslen (WBEMS_COMMA))) &&
		(0 != _wcsnicmp (lpszInputString + *pchEaten, WBEMS_RIGHT_CURLY, wcslen (WBEMS_RIGHT_CURLY))))
	{
		// Consume everything up to the next space, "," or "]"
		LPWSTR cStr = CURRENTSTR;
		ULONG lEaten = 0;		// Number of characters consumed
		ULONG lAuthority = 0;	// Actual length of the authority string
		
		while (*(cStr + lEaten))
		{
			if (_istspace(*(cStr + lEaten)))
			{
				// Hit white space - stop now
				break;
			}
			else if ((0 == _wcsnicmp (cStr + lEaten, WBEMS_RIGHT_CURLY, wcslen (WBEMS_RIGHT_CURLY))) ||
					 (0 == _wcsnicmp (cStr + lEaten, WBEMS_COMMA, wcslen (WBEMS_COMMA))))
			{
				// Hit closing "}" or "," - we are done; unpop the "}" or "," as that will be handled
				// in the calling function
				break;
			}
			else	// Keep on truckin'
			{
				lAuthority++;
				lEaten++;
			}
		}

		// If we terminated correctly, save the locale setting
		if ((lEaten > 1) && (lAuthority > 0))
		{
			status = true;

			LPWSTR pAuthorityStr = new WCHAR [lAuthority + 1];

			if (pAuthorityStr)
			{
				wcsncpy (pAuthorityStr, lpszInputString + *pchEaten, lAuthority);
				pAuthorityStr [lAuthority] = NULL;
				bsAuthority = SysAllocString (pAuthorityStr);

				delete [] pAuthorityStr;
				*pchEaten += lEaten;
			}
			else
				status = false;
		}
	}
	
	if (!status)
		*pchEaten = 0;

	return status;
}

//***************************************************************************
//
//  BOOLEAN CWbemParseDN::ParsePrivilegeSet
//
//  DESCRIPTION:
//
//  Parse the string specification of privilege settings.
//
//  PARAMETERS:
//
//	lpszInputString			The string to be parsed
//  pchEaten				On return identifies how much of the DN has been 
//							consumed
//	privilegeSet			The container into which the parsed privileges
//							are stored.
//
//  RETURN VALUES:
//
//  TRUE					Parsing was successful. 
//	FALSE					Parsing failed.
//
//
//***************************************************************************

bool CWbemParseDN::ParsePrivilegeSet (
	LPWSTR lpszInputString,
	ULONG *pchEaten, 
	CSWbemPrivilegeSet &privilegeSet
)
{
	// We have consumed the initial "(".  Now we are looking for
	// a list of privileges, followed by a final ")"

	bool status = true;
	ULONG chEaten = *pchEaten;		// In case we need to roll back
	bool done = false;
	bool firstPrivilege = true;

	SKIPWHITESPACE

	while (!done)
	{
		VARIANT_BOOL bEnabled = VARIANT_TRUE;

		// If not the first privilege we are expecting a ","
		if (!firstPrivilege)
		{
			if (0 == _wcsnicmp(lpszInputString + *pchEaten, WBEMS_COMMA, wcslen(WBEMS_COMMA)))
			{
				*pchEaten += wcslen (WBEMS_COMMA);
				SKIPWHITESPACE
			}
			else
			{
				status = false;
				break;
			}
		}

		// Next token may be a "!" to indicate a disabled privilege
		if (0 == _wcsnicmp(lpszInputString + *pchEaten, WBEMS_EXCLAMATION, wcslen(WBEMS_EXCLAMATION)))
		{
			*pchEaten += wcslen (WBEMS_EXCLAMATION);
			bEnabled = VARIANT_FALSE;
			SKIPWHITESPACE
		}

		// Next token must be a valid privilege moniker name
		WbemPrivilegeEnum	iPrivilege;

		if (CSWbemPrivilege::GetIdFromMonikerName (lpszInputString + *pchEaten, iPrivilege))
		{
			ISWbemPrivilege *pDummy = NULL;

			if (SUCCEEDED (privilegeSet.Add (iPrivilege, bEnabled, &pDummy)))
			{
				*pchEaten += wcslen (CSWbemPrivilege::GetMonikerNameFromId (iPrivilege));
				pDummy->Release ();
			}
			else
			{
				status = false;
				break;
			}
		}
		else
		{
			// Didn't recognize the privilege name
			status = false;
			break;
		}
		
		SKIPWHITESPACE

		// Finally if we meet a ")" we are truly done with no error
		if (0 == _wcsnicmp(lpszInputString + *pchEaten, WBEMS_RIGHT_PAREN, wcslen(WBEMS_RIGHT_PAREN)))
		{
			*pchEaten += wcslen (WBEMS_RIGHT_PAREN);
			done = true;
			SKIPWHITESPACE
		}

		firstPrivilege = false;
		SKIPWHITESPACE
	}

	if (!status)
	{
		// Misery - blow away any privileges we might have accrued
		*pchEaten = chEaten;
		privilegeSet.DeleteAll ();
	}

	return status;
}

//***************************************************************************
//
//  BOOLEAN CWbemParseDN::GetSecurityString
//
//  DESCRIPTION:
//
//  Take an authentication and impersonlation level and convert it into 
//	a security specifier string.
//
//  PARAMETERS:
//
//	authnSpecified		Whether a nondefault authn levl is specified.
//	authnLevel			The authentication level.
//	impSpecified		Whether a non-default imp level is specified.
//	impLevel			The impersonation level.
//	privilegeSet		Privileges
//	bsAuthority			Authority
//	
//
//  RETURN VALUES:
//		the newly created string (which the caller must free) or NULL
//
//***************************************************************************

wchar_t *CWbemParseDN::GetSecurityString (
	bool authnSpecified, 
	enum WbemAuthenticationLevelEnum authnLevel, 
	bool impSpecified, 
	enum WbemImpersonationLevelEnum impLevel,
	CSWbemPrivilegeSet &privilegeSet,
	BSTR &bsAuthority
)
{
	wchar_t *pResult = NULL;
	long lPrivilegeCount = 0;
	privilegeSet.get_Count (&lPrivilegeCount);
	ULONG lNumDisabled = privilegeSet.GetNumberOfDisabledElements ();
	PrivilegeMap privMap = privilegeSet.GetPrivilegeMap ();
	bool authoritySpecified = ((NULL != bsAuthority) && (0 < wcslen (bsAuthority)));

	// Degenerate case - no security info
	if (!authnSpecified && !impSpecified && (0 == lPrivilegeCount)
		&& !authoritySpecified)
		return NULL;

	// Must have at least these 2 tokens
	size_t len = wcslen (WBEMS_LEFT_CURLY) + wcslen (WBEMS_RIGHT_CURLY);
	
	if (authnSpecified)
	{
		len += wcslen(WBEMS_AUTH_LEVEL) + wcslen (WBEMS_EQUALS);

		switch (authnLevel)
		{
			case wbemAuthenticationLevelDefault:
				len += wcslen (WBEMS_AUTH_DEFAULT);
				break;

			case wbemAuthenticationLevelNone:
				len += wcslen (WBEMS_AUTH_NONE);
				break;

			case wbemAuthenticationLevelConnect:
				len += wcslen (WBEMS_AUTH_CONNECT);
				break;

			case wbemAuthenticationLevelCall:
				len += wcslen (WBEMS_AUTH_CALL);
				break;

			case wbemAuthenticationLevelPkt:
				len += wcslen (WBEMS_AUTH_PKT);
				break;

			case wbemAuthenticationLevelPktIntegrity:
				len += wcslen (WBEMS_AUTH_PKT_INT);
				break;

			case wbemAuthenticationLevelPktPrivacy:
				len += wcslen (WBEMS_AUTH_PKT_PRIV);
				break;

			default:
				return NULL;	// Bad level
		}

		if (impSpecified || authoritySpecified)
			len += wcslen (WBEMS_COMMA);
	}

	if (impSpecified)
	{
		len += wcslen(WBEMS_IMPERSON_LEVEL) + wcslen (WBEMS_EQUALS);

		switch (impLevel)
		{
			case wbemImpersonationLevelAnonymous:
				len += wcslen (WBEMS_IMPERSON_ANON);
				break;

			case wbemImpersonationLevelIdentify:
				len += wcslen (WBEMS_IMPERSON_IDENTIFY);
				break;

			case wbemImpersonationLevelImpersonate:
				len += wcslen (WBEMS_IMPERSON_IMPERSON);
				break;

			case wbemImpersonationLevelDelegate:
				len += wcslen (WBEMS_IMPERSON_DELEGATE);
				break;

			default:
				return NULL;	// Bad level
		}

		if (authoritySpecified)
			len += wcslen (WBEMS_COMMA);
	}

	if (authoritySpecified)
		len += wcslen(WBEMS_AUTHORITY) + wcslen (WBEMS_EQUALS) + wcslen (bsAuthority);

	if (0 < lPrivilegeCount)
	{
		// If imp, authn or authority also specified, we need another separator
		if (authnSpecified || impSpecified || authoritySpecified)
			len += wcslen (WBEMS_COMMA);

		// Need these boundary tokens
		len += wcslen (WBEMS_LEFT_PAREN) + wcslen (WBEMS_RIGHT_PAREN);

		// Need a separator between each privilege
		if (1 < lPrivilegeCount)
			len += (lPrivilegeCount - 1) * wcslen (WBEMS_COMMA);

		// Need to specify false values with "!"
		if (lNumDisabled)
			len += lNumDisabled * wcslen (WBEMS_EXCLAMATION);

		// Now add the privilege strings
		PrivilegeMap::iterator next = privMap.begin ();

		while (next != privMap.end ())
		{
			OLECHAR *sMonikerName = CSWbemPrivilege::GetMonikerNameFromId ((*next).first);
			
			if (sMonikerName)
				len += wcslen (sMonikerName);

			next++;
		}
	}

	pResult = new wchar_t [len + 1];

	if (pResult)
	{
		// Now build the string
		wcscpy (pResult, WBEMS_LEFT_CURLY);
		
		if (authnSpecified)
		{
			wcscat (pResult, WBEMS_AUTH_LEVEL);
			wcscat (pResult, WBEMS_EQUALS);

			switch (authnLevel)
			{
				case wbemAuthenticationLevelDefault:
					wcscat (pResult, WBEMS_AUTH_DEFAULT);
					break;

				case wbemAuthenticationLevelNone:
					wcscat (pResult, WBEMS_AUTH_NONE);
					break;

				case wbemAuthenticationLevelConnect:
					wcscat (pResult, WBEMS_AUTH_CONNECT);
					break;

				case wbemAuthenticationLevelCall:
					wcscat (pResult, WBEMS_AUTH_CALL);
					break;

				case wbemAuthenticationLevelPkt:
					wcscat (pResult, WBEMS_AUTH_PKT);
					break;

				case wbemAuthenticationLevelPktIntegrity:
					wcscat (pResult, WBEMS_AUTH_PKT_INT);
					break;

				case wbemAuthenticationLevelPktPrivacy:
					wcscat (pResult, WBEMS_AUTH_PKT_PRIV);
					break;
			}

			if (impSpecified || authoritySpecified || (0 < lPrivilegeCount))
				wcscat (pResult, WBEMS_COMMA);
		}

		if (impSpecified)
		{
			wcscat (pResult, WBEMS_IMPERSON_LEVEL);
			wcscat (pResult, WBEMS_EQUALS);

			switch (impLevel)
			{
				case wbemImpersonationLevelAnonymous:
					wcscat (pResult, WBEMS_IMPERSON_ANON);
					break;

				case wbemImpersonationLevelIdentify:
					wcscat (pResult, WBEMS_IMPERSON_IDENTIFY);
					break;

				case wbemImpersonationLevelImpersonate:
					wcscat (pResult, WBEMS_IMPERSON_IMPERSON);
					break;

				case wbemImpersonationLevelDelegate:
					wcscat (pResult, WBEMS_IMPERSON_DELEGATE);
					break;

				default:
					return NULL;	// Bad level
			}

			if (authoritySpecified || (0 < lPrivilegeCount))
				wcscat (pResult, WBEMS_COMMA);
		}

		if (authoritySpecified)
		{
			wcscat (pResult, WBEMS_AUTHORITY);
			wcscat (pResult, WBEMS_EQUALS);
			wcscat (pResult, bsAuthority);

			if ((0 < lPrivilegeCount))
				wcscat (pResult, WBEMS_COMMA);
		}

		if (lPrivilegeCount)
		{
			wcscat (pResult, WBEMS_LEFT_PAREN);
			
			// Now add the privilege strings
			PrivilegeMap::iterator next = privMap.begin ();
			bool firstPrivilege = true;

			while (next != privMap.end ())
			{
				if (!firstPrivilege)
					wcscat (pResult, WBEMS_COMMA);

				firstPrivilege = false;
			
				CSWbemPrivilege *pPrivilege = (*next).second;
				VARIANT_BOOL bValue;
				if (SUCCEEDED (pPrivilege->get_IsEnabled (&bValue)) &&
							(VARIANT_FALSE == bValue))
					wcscat (pResult, WBEMS_EXCLAMATION);

				OLECHAR *sMonikerName = CSWbemPrivilege::GetMonikerNameFromId ((*next).first);
				wcscat (pResult, sMonikerName);

				next++;
			}

			wcscat (pResult, WBEMS_RIGHT_PAREN);
		}

		wcscat (pResult, WBEMS_RIGHT_CURLY);
		
		pResult [len] = NULL;
	}

	return pResult;
}


//***************************************************************************
//
//  BOOLEAN CWbemParseDN::GetLocaleString
//
//  DESCRIPTION:
//
//  Take a locale value and convert it into a locale specifier string.
//
//  PARAMETERS:
//
//	bsLocale		The value (if any)
//
//  RETURN VALUES:
//		the newly created string (which the caller must free) or NULL
//
//***************************************************************************

wchar_t *CWbemParseDN::GetLocaleString (
	BSTR bsLocale
)
{
	wchar_t *pResult = NULL;
	
	// Degenerate case - no locale info
	if (!bsLocale || (0 == wcslen (bsLocale)))
		return NULL;

	// Calculate length of string
	size_t len = wcslen (WBEMS_LEFT_SQBRK) + wcslen (WBEMS_LOCALE) +
			wcslen (WBEMS_EQUALS) + wcslen (bsLocale) + wcslen (WBEMS_RIGHT_SQBRK);
	
	pResult = new wchar_t [len + 1];

	if (pResult)
	{
		// Now build the string
		wcscpy (pResult, WBEMS_LEFT_SQBRK);
		wcscat (pResult, WBEMS_LOCALE);
		wcscat (pResult, WBEMS_EQUALS);
		wcscat (pResult, bsLocale);
		wcscat (pResult, WBEMS_RIGHT_SQBRK);
			
		pResult [len] = NULL;
	}

	return pResult;
}


#undef CURRENTSTR
#undef SKIPWHITESPACE