NT4/private/windows/rover/filesync/core/oleutil.c
2020-09-30 17:12:29 +02:00

725 lines
19 KiB
C

/*
* oleutil.c - OLE utility functions module.
*/
/* Headers
**********/
#include "project.h"
#pragma hdrstop
#include "oleutil.h"
/* Macros
*********/
/* macro for translating an HRESULT to a TWINRESULT */
#define HRESULTToTWINRESULT(hr, TR) case hr: tr = TR; break
/* Constants
************/
/* maximum allowed registry lengths */
#define MAX_REG_KEY_LEN MAX_PATH_LEN
#define MAX_REG_VALUE_LEN MAX_PATH_LEN
/* subkeys and associated lengths */
#define CLSID_SUBKEY TEXT("CLSID")
/* CLSID subkey length in bytes, including null terminator */
#define CLSID_SUBKEY_LEN (5 + 1)
#define IN_PROC_SERVER_SUBKEY TEXT("InProcServer32")
/* InProcServer32 subkey length in bytes, including null terminator */
#define IN_PROC_SERVER_SUBKEY_LEN (14 + 1)
#define LOCAL_SERVER_SUBKEY TEXT("LocalServer32")
/* LocalServer32 subkey length in bytes, including null terminator */
#define LOCAL_SERVER_SUBKEY_LEN (13 + 1)
#define RECONCILER_SUBKEY TEXT("Roles\\Reconciler")
/* InProcServer32 subkey length in bytes, including null terminator */
#define RECONCILER_SUBKEY_LEN (5 + 1 + 10 + 1)
#define NOTIFY_SUBKEY TEXT("Roles\\NotifyReplica")
/* InProcServer32 subkey length in bytes, including null terminator */
#define NOTIFY_SUBKEY_LEN (5 + 1 + 13 + 1)
#define COPY_SUBKEY TEXT("SingleChangeHook")
/* copy subkey length in bytes, including null terminator */
#define COPY_SUBKEY_LEN (16 + 1)
/*
* length of GUID subkey string in bytes, including null terminator
*
* "{12345678-1234-1234-1234-123456789012}"
*/
#define GUID_SUBKEY_LEN (1 + 8 + 1 + 4 + 1 + 4 + 1 + 4 + 1 + 12 + 1 + 1)
/*
* class ID key length in bytes, including null terminator
*
* "CLSID\{12345678-1234-1234-1234-123456789012}"
*/
#define CLSID_REG_KEY_LEN (CLSID_SUBKEY_LEN + GUID_SUBKEY_LEN)
/*
* InProcServer32 key length in bytes, including null terminator
*
* "CLSID\{12345678-1234-1234-1234-123456789012}\InProcServer32"
*/
#define IN_PROC_SERVER_REG_KEY_LEN (CLSID_REG_KEY_LEN + IN_PROC_SERVER_SUBKEY_LEN)
/*
* LocalServer32 key length in bytes, including null terminator
*
* "CLSID\{12345678-1234-1234-1234-123456789012}\LocalServer32"
*/
#define LOCAL_SERVER_REG_KEY_LEN (CLSID_REG_KEY_LEN + LOCAL_SERVER_SUBKEY_LEN)
/*
* reconciler key length in bytes, including null terminator
*
* "CLSID\{12345678-1234-1234-1234-123456789012}\Roles\Reconciler"
*/
#define RECONCILER_REG_KEY_LEN (CLSID_REG_KEY_LEN + RECONCILER_SUBKEY_LEN)
/*
* notify replica key length in bytes, including null terminator
*
* "CLSID\{12345678-1234-1234-1234-123456789012}\Roles\NotifyReplica"
*/
#define NOTIFY_REG_KEY_LEN (CLSID_REG_KEY_LEN + NOTIFY_SUBKEY_LEN)
/*
* copy key length in bytes, including null terminator
*
* "CLSID\{12345678-1234-1234-1234-123456789012}\SingleChangeHook"
*/
#define COPY_REG_KEY_LEN (CLSID_REG_KEY_LEN + COPY_SUBKEY_LEN)
/* Macros
*********/
/*
* Determine whether or not an integer value is within a given inclusive range.
*/
#define IsWithin(test, first, last) ((UINT)((test) - (first)) <= (UINT)((last) - (first)))
/***************************** Private Functions *****************************/
/* Module Prototypes
********************/
PRIVATE_CODE void MakeClsIDSubKey(PCGUID, LPCTSTR, LPTSTR);
PRIVATE_CODE BOOL HexStringToDWORD(LPCTSTR *, PDWORD, UINT, TCHAR);
PRIVATE_CODE BOOL StringToGUID(LPCTSTR, PGUID);
PRIVATE_CODE HRESULT GetClassID(LPCTSTR, LPCTSTR, PCLSID);
/*
** MakeClsIDSubKey()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PRIVATE_CODE void MakeClsIDSubKey(PCGUID pcguid, LPCTSTR pcszSubKey,
LPTSTR pszRegKeyBuf)
{
ASSERT(IS_VALID_STRUCT_PTR(pcguid, CGUID));
ASSERT(IS_VALID_STRING_PTR(pcszSubKey, CSTR));
ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszRegKeyBuf, STR, CLSID_REG_KEY_LEN + lstrlen(pcszSubKey) + 1));
/* (- 1) for null terminator. */
EVAL(wsprintf(pszRegKeyBuf,
TEXT("CLSID\\{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\\%s"),
pcguid->Data1,
pcguid->Data2,
pcguid->Data3,
pcguid->Data4[0],
pcguid->Data4[1],
pcguid->Data4[2],
pcguid->Data4[3],
pcguid->Data4[4],
pcguid->Data4[5],
pcguid->Data4[6],
pcguid->Data4[7],
pcszSubKey)
== CLSID_REG_KEY_LEN + lstrlen(pcszSubKey));
return;
}
/*
** HexStringToDWORD()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PRIVATE_CODE BOOL HexStringToDWORD(LPCTSTR *ppcsz, PDWORD pdwValue, UINT ucDigits,
TCHAR chDelimiter)
{
BOOL bResult = TRUE;
UINT u;
/* chDelimiter may be any value. */
ASSERT(IS_VALID_WRITE_PTR(ppcsz, LPCTSTR));
ASSERT(IS_VALID_STRING_PTR(*ppcsz, CSTR));
ASSERT(IS_VALID_WRITE_PTR(pdwValue, DWORD));
ASSERT(ucDigits <= 8);
*pdwValue = 0;
for (u = 0; u < ucDigits; u++)
{
TCHAR ch = (*ppcsz)[u];
if (IsWithin(ch, TEXT('0'), TEXT('9')))
*pdwValue = (*pdwValue << 4) + ch - TEXT('0');
else if (IsWithin((ch |= (TEXT('a') - TEXT('A'))), TEXT('a'), TEXT('f')))
*pdwValue = (*pdwValue << 4) + ch - TEXT('a') + 10;
else
{
WARNING_OUT((TEXT("HexStringToDWORD(): Found unrecognized hex digit %c."),
ch));
bResult = FALSE;
break;
}
}
if (bResult)
{
if (chDelimiter)
{
bResult = ((*ppcsz)[u++] == chDelimiter);
if (! bResult)
WARNING_OUT((TEXT("HexStringToDWORD(): Character %c does not match required delimiter %c."),
(*ppcsz)[u],
chDelimiter));
}
*ppcsz += u;
}
ASSERT(IS_VALID_STRING_PTR(*ppcsz, CSTR));
return(bResult);
}
/*
** StringToGUID()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PRIVATE_CODE BOOL StringToGUID(LPCTSTR pcszGUID, PGUID pguid)
{
BOOL bResult = FALSE;
DWORD dwValue;
LPCTSTR pcszNext = pcszGUID;
ASSERT(IS_VALID_STRING_PTR(pcszGUID, CSTR));
ASSERT(IS_VALID_WRITE_PTR(pguid, GUID));
if (*pcszNext++ == TEXT('{') &&
HexStringToDWORD(&pcszNext, &(pguid->Data1), sizeof(DWORD) * 2, TEXT('-')) &&
HexStringToDWORD(&pcszNext, &dwValue, sizeof(WORD) * 2, TEXT('-')))
{
pguid->Data2 = (WORD)dwValue;
if (HexStringToDWORD(&pcszNext, &dwValue, sizeof(WORD) * 2, TEXT('-')))
{
UINT u;
#pragma data_seg(DATA_SEG_READ_ONLY)
static const TCHAR SrgcchDelimiters[] = { 0, TEXT('-'), 0, 0, 0, 0, 0, TEXT('}') };
#pragma data_seg()
pguid->Data3 = (WORD)dwValue;
bResult = TRUE;
for (u = 0; u < ARRAY_ELEMENTS(pguid->Data4); u++)
{
if (HexStringToDWORD(&pcszNext, &dwValue, sizeof(BYTE) * 2,
SrgcchDelimiters[u]))
pguid->Data4[u] = (BYTE)dwValue;
else
{
bResult = FALSE;
break;
}
}
if (bResult)
{
ASSERT(u == ARRAY_ELEMENTS(pguid->Data4));
if (*pcszNext)
{
bResult = FALSE;
WARNING_OUT((TEXT("StringToGUID(): Found %c instead of }."),
*pcszNext));
}
}
else
WARNING_OUT((TEXT("StringToGUID(): Bad GUID string %s."),
pcszGUID));
}
}
ASSERT(! bResult ||
IS_VALID_STRUCT_PTR(pguid, CGUID));
return(bResult);
}
/*
** GetClassID()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PRIVATE_CODE HRESULT GetClassID(LPCTSTR pcszPath, LPCTSTR pcszSubKey, PCLSID pclsid)
{
HRESULT hr;
CLSID clsidFile;
ASSERT(IS_VALID_STRING_PTR(pcszPath, CSTR));
ASSERT(IS_VALID_STRING_PTR(pcszSubKey, CSTR));
ASSERT(IS_VALID_WRITE_PTR(pclsid, CLSID));
/* Verify string length constants. */
/* (+ 1) for each null terminator. */
ASSERT(lstrlen(CLSID_SUBKEY) + 1 == CLSID_SUBKEY_LEN);
ASSERT(lstrlen(IN_PROC_SERVER_SUBKEY) + 1 == IN_PROC_SERVER_SUBKEY_LEN);
ASSERT(lstrlen(LOCAL_SERVER_SUBKEY) + 1 == LOCAL_SERVER_SUBKEY_LEN);
ASSERT(lstrlen(RECONCILER_SUBKEY) + 1 == RECONCILER_SUBKEY_LEN);
ASSERT(lstrlen(NOTIFY_SUBKEY) + 1 == NOTIFY_SUBKEY_LEN);
ASSERT(lstrlen(COPY_SUBKEY) + 1 == COPY_SUBKEY_LEN);
ASSERT(lstrlen( TEXT("{12345678-1234-1234-1234-123456789012}")) + 1 == GUID_SUBKEY_LEN);
ASSERT(lstrlen(TEXT("CLSID\\{12345678-1234-1234-1234-123456789012}")) + 1 == CLSID_REG_KEY_LEN);
ASSERT(lstrlen(TEXT("CLSID\\{12345678-1234-1234-1234-123456789012}\\InProcServer32")) + 1 == IN_PROC_SERVER_REG_KEY_LEN);
ASSERT(lstrlen(TEXT("CLSID\\{12345678-1234-1234-1234-123456789012}\\LocalServer32")) + 1 == LOCAL_SERVER_REG_KEY_LEN);
ASSERT(lstrlen(TEXT("CLSID\\{12345678-1234-1234-1234-123456789012}\\Roles\\Reconciler")) + 1 == RECONCILER_REG_KEY_LEN);
ASSERT(lstrlen(TEXT("CLSID\\{12345678-1234-1234-1234-123456789012}\\Roles\\NotifyReplica")) + 1 == NOTIFY_REG_KEY_LEN);
ASSERT(lstrlen(TEXT("CLSID\\{12345678-1234-1234-1234-123456789012}\\SingleChangeHook")) + 1 == COPY_REG_KEY_LEN);
hr = GetClassFileByExtension(pcszPath, &clsidFile);
if (SUCCEEDED(hr))
{
TCHAR rgchRecRegKey[MAX_REG_KEY_LEN];
TCHAR rgchRecGUID[GUID_SUBKEY_LEN];
DWORD dwcbLen = sizeof(rgchRecGUID);
hr = REGDB_E_CLASSNOTREG;
MakeClsIDSubKey(&clsidFile, pcszSubKey, rgchRecRegKey);
if (GetDefaultRegKeyValue(HKEY_CLASSES_ROOT, rgchRecRegKey, rgchRecGUID,
&dwcbLen) == ERROR_SUCCESS)
{
/* (+ 1) for null terminator. */
ASSERT((DWORD)(lstrlen(rgchRecGUID) + 1) * sizeof(TCHAR) == dwcbLen);
ASSERT(dwcbLen * sizeof(TCHAR) <= sizeof(rgchRecGUID));
if (StringToGUID(rgchRecGUID, pclsid))
{
hr = S_OK;
#ifdef DEBUG
{
TCHAR rgchInProcServerKey[IN_PROC_SERVER_REG_KEY_LEN];
TCHAR rgchInProcServerValue[MAX_REG_VALUE_LEN];
DWORD dwcbInProcLen = sizeof(rgchInProcServerValue);
/* Display the path to the registered reconciler. */
wsprintf(rgchInProcServerKey, TEXT("%s\\%s\\%s"), CLSID_SUBKEY,
rgchRecGUID, IN_PROC_SERVER_SUBKEY);
if (GetDefaultRegKeyValue(HKEY_CLASSES_ROOT,
rgchInProcServerKey,
rgchInProcServerValue, &dwcbInProcLen)
== ERROR_SUCCESS)
TRACE_OUT((TEXT("GetClassID(): Found registered %s reconciler %s for file %s."),
IN_PROC_SERVER_SUBKEY,
rgchInProcServerValue,
pcszPath));
else
{
dwcbInProcLen = sizeof(rgchInProcServerValue);
wsprintf(rgchInProcServerKey, TEXT("%s\\%s\\%s"), CLSID_SUBKEY,
rgchRecGUID, LOCAL_SERVER_SUBKEY);
if (GetDefaultRegKeyValue(HKEY_CLASSES_ROOT,
rgchInProcServerKey,
rgchInProcServerValue,
&dwcbInProcLen)
== ERROR_SUCCESS)
TRACE_OUT((TEXT("GetClassID(): Found registered %s reconciler %s for file %s."),
LOCAL_SERVER_SUBKEY,
rgchInProcServerValue,
pcszPath));
else
WARNING_OUT((TEXT("GetClassID(): Unregistered class ID %s listed as reconciler in %s for file %s."),
rgchRecGUID,
rgchRecRegKey,
pcszPath));
}
}
#endif
}
else
WARNING_OUT((TEXT("GetClassID(): Bad reconciler class ID %s specified for key %s."),
rgchRecGUID,
rgchRecRegKey));
}
else
TRACE_OUT((TEXT("GetClassID(): No reconciler registered for file %s."),
pcszPath));
}
else
TRACE_OUT((TEXT("GetClassID(): No class ID registered for file %s."),
pcszPath));
ASSERT(FAILED(hr) ||
IS_VALID_STRUCT_PTR(pclsid, CCLSID));
return(hr);
}
/****************************** Public Functions *****************************/
/*
** GetClassFileByExtension()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE HRESULT GetClassFileByExtension(LPCTSTR pcszFile, PCLSID pclsid)
{
HRESULT hr = MK_E_INVALIDEXTENSION;
LPCTSTR pcszExtension;
ASSERT(IS_VALID_STRING_PTR(pcszFile, CSTR));
ASSERT(IS_VALID_WRITE_PTR(pclsid, CLSID));
pcszExtension = ExtractExtension(pcszFile);
if (*pcszExtension)
{
TCHAR rgchFileType[MAX_REG_VALUE_LEN];
DWORD dwcbBufLen = sizeof(rgchFileType);
ASSERT(*pcszExtension == PERIOD);
if (GetDefaultRegKeyValue(HKEY_CLASSES_ROOT, pcszExtension, rgchFileType,
&dwcbBufLen) == ERROR_SUCCESS)
{
/* dwcbBufLen includes rgchFileType's null terminator. */
if (dwcbBufLen + sizeof(CLSID_SUBKEY) <= sizeof(rgchFileType))
{
TCHAR rgchFileClsID[GUID_SUBKEY_LEN];
DWORD dwcbFileClsIDBufLen = sizeof(rgchFileClsID);
CatPath(rgchFileType, CLSID_SUBKEY);
if (GetDefaultRegKeyValue(HKEY_CLASSES_ROOT, rgchFileType,
rgchFileClsID, &dwcbFileClsIDBufLen)
== ERROR_SUCCESS)
{
if (StringToGUID(rgchFileClsID, pclsid))
{
hr = S_OK;
TRACE_OUT((TEXT("GetClassFileByExtension(): Retrieved class ID %s for file %s."),
rgchFileClsID,
pcszFile));
}
else
WARNING_OUT((TEXT("GetClassFileByExtension(): Invalid class ID \"%s\" in %s\\%s. No class ID will be used."),
rgchFileClsID,
rgchFileType,
CLSID_SUBKEY));
}
else
TRACE_OUT((TEXT("GetClassFileByExtension(): No %s subkey for file type key \"%s\". No class ID will be used."),
CLSID_SUBKEY,
rgchFileType));
}
else
WARNING_OUT((TEXT("GetClassFileByExtension(): File type \"%s\" for extension %s of file %s is too long. No class ID will be used."),
rgchFileType,
pcszExtension,
pcszFile));
}
else
TRACE_OUT((TEXT("GetClassFileByExtension(): No file type for extension %s of file %s. No class ID will be used."),
pcszExtension,
pcszFile));
}
else
TRACE_OUT((TEXT("GetClassFileByExtension(): File %s has no extension. No class ID will be used."),
pcszFile));
ASSERT(FAILED(hr) ||
IS_VALID_STRUCT_PTR(pclsid, CCLSID));
return(hr);
}
/*
** GetReconcilerClassID()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE HRESULT GetReconcilerClassID(LPCTSTR pcszPath, PCLSID pclsid)
{
ASSERT(IS_VALID_STRING_PTR(pcszPath, CSTR));
ASSERT(IS_VALID_WRITE_PTR(pclsid, CLSID));
return(GetClassID(pcszPath, RECONCILER_SUBKEY, pclsid));
}
/*
** GetCopyHandlerClassID()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE HRESULT GetCopyHandlerClassID(LPCTSTR pcszPath, PCLSID pclsid)
{
HRESULT hr;
ASSERT(IS_VALID_STRING_PTR(pcszPath, CSTR));
ASSERT(IS_VALID_WRITE_PTR(pclsid, CLSID));
hr = GetReconcilerClassID(pcszPath, pclsid);
if (SUCCEEDED(hr))
{
TCHAR rgchCopyRegKey[COPY_REG_KEY_LEN];
MakeClsIDSubKey(pclsid, COPY_SUBKEY, rgchCopyRegKey);
if (RegKeyExists(HKEY_CLASSES_ROOT, rgchCopyRegKey))
hr = S_OK;
else
hr = REGDB_E_CLASSNOTREG;
}
return(hr);
}
/*
** GetReplicaNotificationClassID()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE HRESULT GetReplicaNotificationClassID(LPCTSTR pcszPath, PCLSID pclsid)
{
ASSERT(IS_VALID_STRING_PTR(pcszPath, CSTR));
ASSERT(IS_VALID_WRITE_PTR(pclsid, CLSID));
return(GetClassID(pcszPath, NOTIFY_SUBKEY, pclsid));
}
/*
** CompareGUIDs()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE COMPARISONRESULT CompareGUIDs(PCGUID pcguid1, PCGUID pcguid2)
{
ASSERT(IS_VALID_STRUCT_PTR(pcguid1, CGUID));
ASSERT(IS_VALID_STRUCT_PTR(pcguid2, CGUID));
return(MyMemComp(pcguid1, pcguid2, sizeof(*pcguid1)));
}
/*
** TranslateHRESULTToTWINRESULT()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE TWINRESULT TranslateHRESULTToTWINRESULT(HRESULT hr)
{
TWINRESULT tr;
switch (hr)
{
HRESULTToTWINRESULT(S_OK, TR_SUCCESS);
HRESULTToTWINRESULT(REC_S_NOTCOMPLETE, TR_MERGE_INCOMPLETE);
HRESULTToTWINRESULT(REC_S_NOTCOMPLETEBUTPROPAGATE, TR_MERGE_INCOMPLETE);
HRESULTToTWINRESULT(E_ABORT, TR_ABORT);
HRESULTToTWINRESULT(E_OUTOFMEMORY, TR_OUT_OF_MEMORY);
HRESULTToTWINRESULT(E_FAIL, TR_RH_LOAD_FAILED);
HRESULTToTWINRESULT(REC_E_ABORTED, TR_ABORT);
HRESULTToTWINRESULT(REC_E_TOODIFFERENT, TR_TOO_DIFFERENT);
default:
if (SUCCEEDED(hr))
{
tr = TR_SUCCESS;
WARNING_OUT((TEXT("TranslateHRESULTToTWINRESULT(): Translating unlisted success HRESULT %s into TWINRESULT %s."),
GetHRESULTString(hr),
GetTWINRESULTString(tr)));
}
else
{
tr = TR_RH_LOAD_FAILED;
WARNING_OUT((TEXT("TranslateHRESULTToTWINRESULT(): Translating unlisted failure HRESULT %s into TWINRESULT %s."),
GetHRESULTString(hr),
GetTWINRESULTString(tr)));
}
break;
}
return(tr);
}
#if defined(DEBUG) || defined(VSTF)
/*
** IsValidPCINotifyReplica()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE BOOL IsValidPCINotifyReplica(PCINotifyReplica pcinr)
{
return(IS_VALID_READ_PTR(pcinr, CINotifyReplica) &&
IS_VALID_READ_PTR(pcinr->lpVtbl, sizeof(*(pcinr->lpVtbl))) &&
IS_VALID_STRUCT_PTR((PCIUnknown)pcinr, CIUnknown) &&
IS_VALID_CODE_PTR(pcinr->lpVtbl->YouAreAReplica, YouAreAReplica));
}
/*
** IsValidPCIReconcileInitiator()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE BOOL IsValidPCIReconcileInitiator(PCIReconcileInitiator pciri)
{
return(IS_VALID_READ_PTR(pciri, CIReconcileInitiator) &&
IS_VALID_READ_PTR(pciri->lpVtbl, sizeof(*(pciri->lpVtbl))) &&
IS_VALID_STRUCT_PTR((PCIUnknown)pciri, CIUnknown) &&
IS_VALID_CODE_PTR(pciri->lpVtbl->SetAbortCallback, SetAbortCallback) &&
IS_VALID_CODE_PTR(pciri->lpVtbl->SetProgressFeedback, SetProgressFeedback));
}
#endif