You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
716 lines
20 KiB
716 lines
20 KiB
|
|
#include "cstore.hxx"
|
|
|
|
|
|
|
|
const WCHAR cwszCRLF[] = L"\r\n";
|
|
|
|
|
|
BOOL GetGpoIdFromClassStorePath(
|
|
WCHAR* wszClassStorePath,
|
|
GUID* pGpoId)
|
|
{
|
|
WCHAR* wszGuidStart;
|
|
WCHAR wszGpoId[MAX_GUIDSTR_LEN + 1];
|
|
|
|
wszGuidStart = wcschr(wszClassStorePath, L'{');
|
|
|
|
if (!wszGuidStart)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
wcsncpy(wszGpoId, wszGuidStart, MAX_GUIDSTR_LEN);
|
|
|
|
wszGpoId[MAX_GUIDSTR_LEN] = L'\0';
|
|
|
|
StringToGuid(wszGpoId, pGpoId);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
HRESULT GetUserSid(PSID *ppUserSid, UINT *pCallType);
|
|
|
|
// set property routines do not do any allocations.
|
|
// get properties have 2 different sets of routines
|
|
// 1. in which there is no allocation taking place
|
|
// and the buffers are freed when the ds data structures
|
|
// are freed.
|
|
// 2. Allocation takes place and these should be used for
|
|
// data that needs to be returned back to the clients.
|
|
|
|
|
|
void FreeAttr(ADS_ATTR_INFO attr)
|
|
{
|
|
CsMemFree(attr.pADsValues);
|
|
}
|
|
|
|
// Note: None of these APIs copies anything into their own buffers.
|
|
// It allocates a buffer for adsvalues though.
|
|
|
|
// packing a property's value into a attribute structure
|
|
// for sending in with a create/modify.
|
|
|
|
void PackStrArrToAttr(ADS_ATTR_INFO *attr, WCHAR *szProperty,
|
|
WCHAR **pszAttr, DWORD num)
|
|
{
|
|
DWORD i;
|
|
|
|
attr->pszAttrName = szProperty;
|
|
attr->dwNumValues = num;
|
|
|
|
if (num)
|
|
attr->dwControlCode = ADS_ATTR_UPDATE;
|
|
else
|
|
attr->dwControlCode = ADS_ATTR_CLEAR;
|
|
|
|
attr->dwADsType = ADSTYPE_DN_STRING;
|
|
attr->pADsValues = (ADSVALUE *)CsMemAlloc(sizeof(ADSVALUE)*num);
|
|
|
|
if (!(attr->pADsValues))
|
|
return;
|
|
|
|
for (i = 0; i < num; i++) {
|
|
attr->pADsValues[i].dwType = ADSTYPE_DN_STRING;
|
|
attr->pADsValues[i].DNString = pszAttr[i];
|
|
}
|
|
}
|
|
|
|
void PackDWArrToAttr(ADS_ATTR_INFO *attr, WCHAR *szProperty, DWORD *pAttr, DWORD num)
|
|
{
|
|
DWORD i;
|
|
|
|
attr->pszAttrName = szProperty;
|
|
attr->dwNumValues = num;
|
|
|
|
if (num)
|
|
attr->dwControlCode = ADS_ATTR_UPDATE;
|
|
else
|
|
attr->dwControlCode = ADS_ATTR_CLEAR;
|
|
|
|
|
|
attr->dwADsType = ADSTYPE_INTEGER;
|
|
attr->pADsValues = (ADSVALUE *)CsMemAlloc(sizeof(ADSVALUE)*num);
|
|
if (!(attr->pADsValues))
|
|
return;
|
|
|
|
for (i = 0; i < num; i++) {
|
|
attr->pADsValues[i].dwType = ADSTYPE_INTEGER;
|
|
attr->pADsValues[i].Integer = pAttr[i];
|
|
}
|
|
}
|
|
|
|
void PackGUIDArrToAttr(ADS_ATTR_INFO *attr, WCHAR *szProperty, GUID *pAttr, DWORD num)
|
|
{
|
|
DWORD i;
|
|
|
|
attr->pszAttrName = szProperty;
|
|
attr->dwNumValues = num;
|
|
|
|
if (num)
|
|
attr->dwControlCode = ADS_ATTR_UPDATE;
|
|
else
|
|
attr->dwControlCode = ADS_ATTR_CLEAR;
|
|
|
|
attr->dwADsType = ADSTYPE_OCTET_STRING;
|
|
attr->pADsValues = (ADSVALUE *)CsMemAlloc(sizeof(ADSVALUE)*num);
|
|
if (!(attr->pADsValues))
|
|
return;
|
|
|
|
for (i = 0; i < num; i++) {
|
|
attr->pADsValues[i].dwType = ADSTYPE_OCTET_STRING;
|
|
attr->pADsValues[i].OctetString.dwLength = sizeof(GUID);
|
|
attr->pADsValues[i].OctetString.lpValue = (unsigned char *)(pAttr+i);
|
|
}
|
|
}
|
|
|
|
void PackBinToAttr(ADS_ATTR_INFO *attr, WCHAR *szProperty, BYTE *pAttr, DWORD sz)
|
|
{
|
|
attr->pszAttrName = szProperty;
|
|
attr->dwNumValues = 1;
|
|
|
|
attr->dwControlCode = ADS_ATTR_UPDATE;
|
|
|
|
attr->dwADsType = ADSTYPE_OCTET_STRING;
|
|
attr->pADsValues = (ADSVALUE *)CsMemAlloc(sizeof(ADSVALUE));
|
|
if (!(attr->pADsValues))
|
|
return;
|
|
|
|
attr->pADsValues[0].dwType = ADSTYPE_OCTET_STRING;
|
|
attr->pADsValues[0].OctetString.dwLength = sz;
|
|
attr->pADsValues[0].OctetString.lpValue = pAttr;
|
|
}
|
|
|
|
void PackStrToAttr(ADS_ATTR_INFO *attr, WCHAR *szProperty, WCHAR *szAttr)
|
|
{
|
|
if (szAttr)
|
|
PackStrArrToAttr(attr, szProperty, &szAttr, 1);
|
|
else
|
|
PackStrArrToAttr(attr, szProperty, &szAttr, 0);
|
|
}
|
|
|
|
void PackDWToAttr(ADS_ATTR_INFO *attr, WCHAR *szProperty, DWORD Attr)
|
|
{
|
|
PackDWArrToAttr(attr, szProperty, &Attr, 1);
|
|
}
|
|
|
|
// passing in a pointer to GUID which is passed down into the LDAP structure.
|
|
|
|
void PackGUIDToAttr(ADS_ATTR_INFO *attr, WCHAR *szProperty, GUID *pAttr)
|
|
{
|
|
PackGUIDArrToAttr(attr, szProperty, pAttr, 1);
|
|
}
|
|
|
|
// returns the attribute corresp. to a given property.
|
|
DWORD GetPropertyFromAttr(ADS_ATTR_INFO *pattr, DWORD cNum, WCHAR *szProperty)
|
|
{
|
|
DWORD i;
|
|
for (i = 0; i < cNum; i++)
|
|
if (_wcsicmp(pattr[i].pszAttrName, szProperty) == 0)
|
|
break;
|
|
return i;
|
|
}
|
|
|
|
|
|
|
|
HRESULT GetCategoryLocaleDesc(LPOLESTR *pdesc, ULONG cdesc, LCID *plcid,
|
|
LPOLESTR szDescription, ULONG ulSize)
|
|
{
|
|
LCID plgid;
|
|
LPOLESTR ptr;
|
|
|
|
szDescription[0] = L'\0';
|
|
if (!cdesc)
|
|
return E_FAIL; // CAT_E_NODESCRIPTION;
|
|
|
|
// Try locale passed in
|
|
if (FindDescription(pdesc, cdesc, plcid, szDescription, 0))
|
|
return S_OK;
|
|
|
|
// Get default sublang local
|
|
plgid = PRIMARYLANGID((WORD)*plcid);
|
|
*plcid = MAKELCID(MAKELANGID(plgid, SUBLANG_DEFAULT), SORT_DEFAULT);
|
|
if (FindDescription(pdesc, cdesc, plcid, szDescription, 0))
|
|
return S_OK;
|
|
|
|
// Get matching lang id
|
|
if (FindDescription(pdesc, cdesc, plcid, szDescription, 1))
|
|
return S_OK;
|
|
|
|
// Get User Default
|
|
*plcid = GetUserDefaultLCID();
|
|
if (FindDescription(pdesc, cdesc, plcid, szDescription, 0))
|
|
return S_OK;
|
|
|
|
// Get System Default
|
|
*plcid = GetUserDefaultLCID();
|
|
if (FindDescription(pdesc, cdesc, plcid, szDescription, 0))
|
|
return S_OK;
|
|
|
|
// Get the first one
|
|
*plcid = wcstoul(pdesc[0], &ptr, 16);
|
|
if (szDescription)
|
|
{
|
|
if ((ptr) && (wcslen(ptr) >= (CAT_DESC_DELIM_LEN+2)))
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = StringCchCopy(szDescription, ulSize, (ptr+CAT_DESC_DELIM_LEN+2));
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
else
|
|
szDescription = L'\0';
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
//-------------------------------------------
|
|
// Returns the description corresp. to a LCID
|
|
// desc: list of descs+lcid
|
|
// cdesc: number of elements.
|
|
// plcid: the lcid in/out
|
|
// szDescription:description returned.
|
|
// GetPrimary: Match only the primary.
|
|
//---------------------------------------
|
|
|
|
ULONG FindDescription(LPOLESTR *desc, ULONG cdesc, LCID *plcid, LPOLESTR szDescription, BOOL GetPrimary)
|
|
{
|
|
ULONG i;
|
|
LCID newlcid;
|
|
LPOLESTR ptr;
|
|
for (i = 0; i < cdesc; i++)
|
|
{
|
|
newlcid = wcstoul(desc[i], &ptr, 16); // to be changed
|
|
// error to be checked.
|
|
if ((newlcid == *plcid) || ((GetPrimary) &&
|
|
(PRIMARYLANGID((WORD)*plcid) == PRIMARYLANGID(LANGIDFROMLCID(newlcid)))))
|
|
{
|
|
if (szDescription)
|
|
{
|
|
if ((ptr) && (wcslen(ptr) >= (CAT_DESC_DELIM_LEN+2)))
|
|
{
|
|
//
|
|
// Copy the description, enforcing the maximum size
|
|
// so we don't overflow the buffer
|
|
//
|
|
wcsncpy(szDescription,
|
|
(ptr+CAT_DESC_DELIM_LEN+2),
|
|
CAT_DESC_MAX_LEN + 1
|
|
);
|
|
|
|
//
|
|
// We must null terminate in case the category
|
|
// was longer than the maximum. We know the buffer
|
|
// is equal in size to the maximum, so we can
|
|
// just add the terminator there. In all other cases,
|
|
// wcsncpy will have written the null terminator
|
|
//
|
|
szDescription[CAT_DESC_MAX_LEN] = L'\0';
|
|
}
|
|
else
|
|
szDescription = L'\0';
|
|
}
|
|
if (GetPrimary)
|
|
*plcid = newlcid;
|
|
return i+1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
DWORD NumDigits10(DWORD Value)
|
|
{
|
|
if (0 == Value) {
|
|
return 1;
|
|
}
|
|
|
|
DWORD ret = 0;
|
|
|
|
for (ret = 0; Value != 0; ret++)
|
|
Value = Value/10;
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
void ReportEventCS(HRESULT ErrorCode, HRESULT ExtendedErrorCode, LPOLESTR szContainerName)
|
|
{
|
|
|
|
WCHAR szErrCode[16];
|
|
HRESULT hr;
|
|
|
|
hr = StringCchPrintf(szErrCode, 16, L"0x%x", ExtendedErrorCode);
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
CEventsBase* pEvents = (CEventsBase*) gpEvents;
|
|
|
|
ASSERT(CS_E_NETWORK_ERROR == ErrorCode);
|
|
|
|
pEvents->Report(
|
|
EVENT_CS_NETWORK_ERROR,
|
|
FALSE,
|
|
2,
|
|
szContainerName,
|
|
szErrCode);
|
|
}
|
|
|
|
// remapping Error Codes returned by LDAP to reasonable class store errors.
|
|
//
|
|
HRESULT RemapErrorCode(HRESULT ErrorCode, LPOLESTR m_szContainerName)
|
|
{
|
|
HRESULT RetCode;
|
|
BOOL fNetError;
|
|
|
|
fNetError = FALSE;
|
|
|
|
if (SUCCEEDED(ErrorCode))
|
|
return S_OK;
|
|
|
|
switch (ErrorCode)
|
|
{
|
|
//
|
|
// All kinds of failures due to ObjectNotFound
|
|
// due to non-existence of object OR
|
|
// non-existent container OR
|
|
// invalid path specification
|
|
// Other than Access Denials
|
|
//
|
|
case HRESULT_FROM_WIN32(ERROR_DS_NO_SUCH_OBJECT):
|
|
case HRESULT_FROM_WIN32(ERROR_DS_NO_RESULTS_RETURNED): // understand what causes this
|
|
case HRESULT_FROM_WIN32(ERROR_DS_NAME_ERROR_NOT_FOUND): // -do-
|
|
|
|
RetCode = CS_E_OBJECT_NOTFOUND; // which object - specific error
|
|
break;
|
|
|
|
case HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS):
|
|
case HRESULT_FROM_WIN32(ERROR_OBJECT_ALREADY_EXISTS):
|
|
case E_ADS_OBJECT_EXISTS:
|
|
RetCode = CS_E_OBJECT_ALREADY_EXISTS;
|
|
break;
|
|
|
|
//
|
|
// The following errors should not be expected normally.
|
|
// Class Store schema mismatched should be handled correctly.
|
|
// Errors below may ONLY occur for corrupted data OR out-of-band changes
|
|
// to a Class Store content.
|
|
|
|
case E_ADS_CANT_CONVERT_DATATYPE:
|
|
case E_ADS_SCHEMA_VIOLATION:
|
|
case HRESULT_FROM_WIN32(ERROR_DS_NO_ATTRIBUTE_OR_VALUE):
|
|
case HRESULT_FROM_WIN32(ERROR_DS_CONSTRAINT_VIOLATION):
|
|
RetCode = CS_E_SCHEMA_MISMATCH;
|
|
break;
|
|
|
|
//
|
|
// Any kinds of Access or Auth Denial
|
|
// return ACCESS_DENIED
|
|
//
|
|
|
|
case HRESULT_FROM_WIN32(ERROR_DS_AUTH_METHOD_NOT_SUPPORTED):
|
|
case HRESULT_FROM_WIN32(ERROR_DS_STRONG_AUTH_REQUIRED):
|
|
case HRESULT_FROM_WIN32(ERROR_DS_CONFIDENTIALITY_REQUIRED):
|
|
case HRESULT_FROM_WIN32(ERROR_INVALID_PASSWORD):
|
|
case HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED):
|
|
case HRESULT_FROM_WIN32(ERROR_DS_AUTH_UNKNOWN):
|
|
|
|
RetCode = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
|
|
break;
|
|
|
|
case E_ADS_BAD_PATHNAME:
|
|
case HRESULT_FROM_WIN32(ERROR_DS_INVALID_ATTRIBUTE_SYNTAX): // this is wrong
|
|
RetCode = CS_E_INVALID_PATH;
|
|
break;
|
|
|
|
//
|
|
// Out of Memory
|
|
//
|
|
|
|
case HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY):
|
|
case HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY):
|
|
|
|
RetCode = E_OUTOFMEMORY;
|
|
break;
|
|
|
|
case HRESULT_FROM_WIN32(ERROR_DS_NAME_ERROR_RESOLVING):
|
|
case HRESULT_FROM_WIN32(ERROR_DS_NAME_ERROR_NOT_UNIQUE):
|
|
case HRESULT_FROM_WIN32(ERROR_DS_NAME_ERROR_NO_MAPPING):
|
|
case HRESULT_FROM_WIN32(ERROR_DS_NAME_ERROR_DOMAIN_ONLY):
|
|
case HRESULT_FROM_WIN32(ERROR_DS_TIMELIMIT_EXCEEDED):
|
|
case HRESULT_FROM_WIN32(ERROR_DS_BUSY):
|
|
case HRESULT_FROM_WIN32(ERROR_DS_UNAVAILABLE):
|
|
case HRESULT_FROM_WIN32(ERROR_DS_UNWILLING_TO_PERFORM):
|
|
case HRESULT_FROM_WIN32(ERROR_TIMEOUT):
|
|
case HRESULT_FROM_WIN32(ERROR_CONNECTION_REFUSED):
|
|
case HRESULT_FROM_WIN32(ERROR_DS_SERVER_DOWN):
|
|
case HRESULT_FROM_WIN32(ERROR_NO_SUCH_DOMAIN):
|
|
RetCode = ErrorCode;
|
|
fNetError = TRUE;
|
|
break;
|
|
|
|
case HRESULT_FROM_WIN32(ERROR_DS_ADMIN_LIMIT_EXCEEDED):
|
|
RetCode = CS_E_ADMIN_LIMIT_EXCEEDED;
|
|
break;
|
|
|
|
default:
|
|
RetCode = ErrorCode;
|
|
}
|
|
|
|
CSDBGPrint((DM_WARNING,
|
|
IDS_CSTORE_REMAP_ERR,
|
|
ErrorCode,
|
|
RetCode));
|
|
|
|
if (RetCode == CS_E_NETWORK_ERROR)
|
|
{
|
|
ReportEventCS(RetCode, ErrorCode, m_szContainerName);
|
|
}
|
|
|
|
return RetCode;
|
|
}
|
|
|
|
// These functions are used to delete a single value from a
|
|
// multivalued property or append to a multivalued property
|
|
|
|
void PackStrArrToAttrEx(ADS_ATTR_INFO *attr, WCHAR *szProperty, WCHAR **pszAttr, DWORD num,
|
|
BOOL APPEND)
|
|
{
|
|
DWORD i;
|
|
|
|
attr->pszAttrName = szProperty;
|
|
attr->dwNumValues = num;
|
|
|
|
if (APPEND)
|
|
attr->dwControlCode = ADS_ATTR_APPEND;
|
|
else
|
|
attr->dwControlCode = ADS_ATTR_DELETE;
|
|
|
|
attr->dwADsType = ADSTYPE_DN_STRING;
|
|
attr->pADsValues = (ADSVALUE *)CsMemAlloc(sizeof(ADSVALUE)*num);
|
|
if (!(attr->pADsValues))
|
|
return;
|
|
|
|
for (i = 0; i < num; i++) {
|
|
attr->pADsValues[i].dwType = ADSTYPE_DN_STRING;
|
|
attr->pADsValues[i].DNString = pszAttr[i];
|
|
}
|
|
}
|
|
|
|
void PackDWArrToAttrEx(ADS_ATTR_INFO *attr, WCHAR *szProperty, DWORD *pAttr, DWORD num,
|
|
BOOL APPEND)
|
|
{
|
|
DWORD i;
|
|
|
|
attr->pszAttrName = szProperty;
|
|
attr->dwNumValues = num;
|
|
|
|
if (APPEND)
|
|
attr->dwControlCode = ADS_ATTR_APPEND;
|
|
else
|
|
attr->dwControlCode = ADS_ATTR_DELETE;
|
|
|
|
attr->dwADsType = ADSTYPE_INTEGER;
|
|
attr->pADsValues = (ADSVALUE *)CsMemAlloc(sizeof(ADSVALUE)*num);
|
|
if (!(attr->pADsValues))
|
|
return;
|
|
|
|
for (i = 0; i < num; i++) {
|
|
attr->pADsValues[i].dwType = ADSTYPE_INTEGER;
|
|
attr->pADsValues[i].Integer = pAttr[i];
|
|
}
|
|
}
|
|
|
|
void PackGUIDArrToAttrEx(ADS_ATTR_INFO *attr, WCHAR *szProperty, GUID *pAttr, DWORD num,
|
|
BOOL APPEND)
|
|
{
|
|
DWORD i;
|
|
|
|
attr->pszAttrName = szProperty;
|
|
attr->dwNumValues = num;
|
|
|
|
if (APPEND)
|
|
attr->dwControlCode = ADS_ATTR_APPEND;
|
|
else
|
|
attr->dwControlCode = ADS_ATTR_DELETE;
|
|
|
|
attr->dwADsType = ADSTYPE_OCTET_STRING;
|
|
attr->pADsValues = (ADSVALUE *)CsMemAlloc(sizeof(ADSVALUE)*num);
|
|
if (!(attr->pADsValues))
|
|
return;
|
|
|
|
for (i = 0; i < num; i++) {
|
|
attr->pADsValues[i].dwType = ADSTYPE_OCTET_STRING;
|
|
attr->pADsValues[i].OctetString.dwLength = sizeof(GUID);
|
|
attr->pADsValues[i].OctetString.lpValue = (unsigned char *)(pAttr+i);
|
|
}
|
|
}
|
|
|
|
WCHAR* AllocGpoPathFromClassStorePath( WCHAR* pszClassStorePath )
|
|
{
|
|
//
|
|
// The class store path looks like CN=ClassStore,CN=[Machine | User],<gpopath>
|
|
// So we will simply look for two "," starting at the beginning of the path.
|
|
// In doing this, we will take care not to access violate due to an incorrect path,
|
|
// Since there is the possiblity that an administrator could find the persisted
|
|
// class store path and mangle it, giving us an incorrect path that would
|
|
// not parse according to the structure above.
|
|
//
|
|
WCHAR* wszGpoPath;
|
|
|
|
//
|
|
// Check for the first ','
|
|
//
|
|
wszGpoPath = wcschr(pszClassStorePath, L',');
|
|
|
|
//
|
|
// If we get NULL here, that means there is no ',' and the path is corrupt
|
|
//
|
|
if ( ! wszGpoPath )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Now check for the second ','
|
|
//
|
|
wszGpoPath = wcschr(wszGpoPath + 1, L',');
|
|
|
|
//
|
|
// Again, if we don't find the second ',',
|
|
// The path is corrupt
|
|
//
|
|
if ( ! wszGpoPath )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Now move one past
|
|
//
|
|
wszGpoPath++;
|
|
|
|
//
|
|
// The caller desires their own copy, so we will allocate space for it
|
|
//
|
|
WCHAR* wszGpoPathResult;
|
|
ULONG ulNoBytes;
|
|
|
|
ulNoBytes = (wcslen( wszGpoPath ) + 1) * sizeof( *wszGpoPathResult );
|
|
wszGpoPathResult = (WCHAR*) CsMemAlloc(ulNoBytes);
|
|
|
|
if ( ! wszGpoPathResult )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Now copy the gpo path
|
|
//
|
|
|
|
HRESULT hr;
|
|
|
|
hr = StringCbCopy( wszGpoPathResult, ulNoBytes, wszGpoPath );
|
|
if (FAILED(hr))
|
|
{
|
|
CsMemFree(wszGpoPathResult);
|
|
wszGpoPathResult = NULL;
|
|
}
|
|
|
|
return wszGpoPathResult;
|
|
}
|
|
|
|
HRESULT
|
|
GetEscapedNameFilter( WCHAR* wszName, WCHAR** ppwszEscapedName )
|
|
{
|
|
DWORD cbLen;
|
|
WCHAR* wszCurrent;
|
|
HRESULT hr;
|
|
ULONG ulCurSizeLeft;
|
|
|
|
//
|
|
// This function escapes package names so that they can be used in
|
|
// an ldap search filter. Names containing certain characters must
|
|
// be escaped since those characters are contained in the vocabulary
|
|
// for the search filter grammar.
|
|
//
|
|
|
|
//
|
|
// The set of characters that must be escaped and the appropriate
|
|
// escape sequences are described in RFC 2254
|
|
//
|
|
|
|
//
|
|
// Determine the maximum size needed for the filter. We include for 3
|
|
// times the length of the name since that is the upper bound on the
|
|
// length of the escaped name
|
|
//
|
|
|
|
//
|
|
// "(PackageAttr=\0" + "<*ppwszEscapedName>" + ")"
|
|
//
|
|
cbLen = sizeof( L"(" PACKAGENAME L"=" ) + ( lstrlen( wszName ) + 1 ) * sizeof( *wszName ) * 3;
|
|
|
|
*ppwszEscapedName = (WCHAR*) CsMemAlloc( cbLen );
|
|
|
|
if ( ! *ppwszEscapedName )
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
//
|
|
// Add in the LHS of the filter expression
|
|
//
|
|
|
|
hr = StringCbCopy( *ppwszEscapedName, cbLen, L"(" PACKAGENAME L"=" );
|
|
if (FAILED(hr))
|
|
{
|
|
CsMemFree(*ppwszEscapedName);
|
|
*ppwszEscapedName = NULL;
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// We will escape the name -- move past the end of the filter expression LHS
|
|
//
|
|
wszCurrent = *ppwszEscapedName + ( sizeof(L"(" PACKAGENAME L"=") - 1 ) / sizeof( WCHAR );
|
|
ulCurSizeLeft = (cbLen - (sizeof(L"(" PACKAGENAME L"=") - 1)) / sizeof(WCHAR);
|
|
|
|
//
|
|
// For each character that needs to be escaped, we will append that character's
|
|
// escape sequence to the string. For characters that do not need to be escaped,
|
|
// we simply simply append the character as-is (i.e. unescaped).
|
|
//
|
|
for ( ; *wszName; wszName++ )
|
|
{
|
|
WCHAR* EscapedChar;
|
|
|
|
//
|
|
// Detect characters that need to be escaped and
|
|
// map each to its escape sequence
|
|
//
|
|
switch ( *wszName )
|
|
{
|
|
case L'*':
|
|
|
|
EscapedChar = L"\\2a";
|
|
break;
|
|
|
|
case L'(':
|
|
|
|
EscapedChar = L"\\28";
|
|
break;
|
|
|
|
case L')':
|
|
|
|
EscapedChar = L"\\29";
|
|
break;
|
|
|
|
case L'\\':
|
|
|
|
EscapedChar = L"\\5c";
|
|
break;
|
|
|
|
default:
|
|
|
|
//
|
|
// This character does not need to be escaped, just append it
|
|
//
|
|
*wszCurrent = *wszName;
|
|
wszCurrent ++;
|
|
ulCurSizeLeft--;
|
|
|
|
continue;
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// We only get here if the character needed to be escaped --
|
|
// we append the string for the escape sequence to the filter string, and
|
|
// move our filter string location to the new end of string
|
|
//
|
|
|
|
hr = StringCchCopy( wszCurrent, ulCurSizeLeft, EscapedChar );
|
|
wszCurrent += 3;
|
|
ulCurSizeLeft-=3;
|
|
}
|
|
|
|
//
|
|
// We need to add the closing parenthesis to the filter expression
|
|
//
|
|
*wszCurrent++ = L')';
|
|
*wszCurrent = L'\0';
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|