From a12b13c36d1da707cf541595cf5f38338f37cc68 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sat, 19 Jan 2019 19:19:15 -0600 Subject: Import code from old v4 repo --- src/ca/mqcost.h | 9 + src/ca/mqexec.cpp | 214 +++++++++ src/ca/mqqueueexec.cpp | 927 +++++++++++++++++++++++++++++++++++++ src/ca/mqqueueexec.h | 30 ++ src/ca/mqqueuesched.cpp | 582 +++++++++++++++++++++++ src/ca/mqqueuesched.h | 92 ++++ src/ca/mqsched.cpp | 219 +++++++++ src/ca/mqutilexec.cpp | 380 +++++++++++++++ src/ca/mqutilexec.h | 23 + src/ca/mqutilsched.cpp | 43 ++ src/ca/mqutilsched.h | 9 + src/wixext/MsmqCompiler.cs | 536 +++++++++++++++++++++ src/wixext/MsmqDecompiler.cs | 303 ++++++++++++ src/wixext/MsmqExtensionData.cs | 64 +++ src/wixext/WixMsmqExtension.csproj | 49 ++ src/wixext/messages.xml | 77 +++ src/wixext/msmq.xsd | 121 +++++ src/wixext/tables.xml | 54 +++ src/wixlib/MsmqExtension.wixproj | 24 + src/wixlib/MsmqExtension.wxs | 31 ++ src/wixlib/en-us.wxl | 10 + src/wixlib/ja-jp.wxl | 10 + 22 files changed, 3807 insertions(+) create mode 100644 src/ca/mqcost.h create mode 100644 src/ca/mqexec.cpp create mode 100644 src/ca/mqqueueexec.cpp create mode 100644 src/ca/mqqueueexec.h create mode 100644 src/ca/mqqueuesched.cpp create mode 100644 src/ca/mqqueuesched.h create mode 100644 src/ca/mqsched.cpp create mode 100644 src/ca/mqutilexec.cpp create mode 100644 src/ca/mqutilexec.h create mode 100644 src/ca/mqutilsched.cpp create mode 100644 src/ca/mqutilsched.h create mode 100644 src/wixext/MsmqCompiler.cs create mode 100644 src/wixext/MsmqDecompiler.cs create mode 100644 src/wixext/MsmqExtensionData.cs create mode 100644 src/wixext/WixMsmqExtension.csproj create mode 100644 src/wixext/messages.xml create mode 100644 src/wixext/msmq.xsd create mode 100644 src/wixext/tables.xml create mode 100644 src/wixlib/MsmqExtension.wixproj create mode 100644 src/wixlib/MsmqExtension.wxs create mode 100644 src/wixlib/en-us.wxl create mode 100644 src/wixlib/ja-jp.wxl (limited to 'src') diff --git a/src/ca/mqcost.h b/src/ca/mqcost.h new file mode 100644 index 00000000..a40b7437 --- /dev/null +++ b/src/ca/mqcost.h @@ -0,0 +1,9 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#define COST_MESSAGE_QUEUE_CREATE 10000 +#define COST_MESSAGE_QUEUE_DELETE 10000 + +#define COST_MESSAGE_QUEUE_PERMISSION_ADD 10000 +#define COST_MESSAGE_QUEUE_PERMISSION_REMOVE 10000 diff --git a/src/ca/mqexec.cpp b/src/ca/mqexec.cpp new file mode 100644 index 00000000..bac54f31 --- /dev/null +++ b/src/ca/mqexec.cpp @@ -0,0 +1,214 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +/******************************************************************** + DllMain - standard entry point for all WiX CustomActions + +********************************************************************/ +extern "C" BOOL WINAPI DllMain( + IN HINSTANCE hInst, + IN ULONG ulReason, + IN LPVOID) +{ + switch(ulReason) + { + case DLL_PROCESS_ATTACH: + WcaGlobalInitialize(hInst); + break; + + case DLL_PROCESS_DETACH: + WcaGlobalFinalize(); + break; + } + + return TRUE; +} + +/******************************************************************** + MessageQueuingExecuteInstall - CUSTOM ACTION ENTRY POINT + + Input: deferred CustomActionData - MessageQueuingExecuteInstall +********************************************************************/ +extern "C" UINT __stdcall MessageQueuingExecuteInstall(MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + LPWSTR pwzCustomActionData = NULL; + LPWSTR pwzData = NULL; + + // initialize + hr = WcaInitialize(hInstall, "MessageQueuingExecuteInstall"); + ExitOnFailure(hr, "Failed to initialize MessageQueuingExecuteInstall"); + + hr = MqiInitialize(); + ExitOnFailure(hr, "Failed to initialize"); + + // get custom action data + hr = WcaGetProperty(L"CustomActionData", &pwzCustomActionData); + ExitOnFailure(hr, "Failed to get CustomActionData"); + pwzData = pwzCustomActionData; + + // create message queues + hr = MqiCreateMessageQueues(&pwzData); + ExitOnFailure(hr, "Failed to create message queues"); + if (S_FALSE == hr) ExitFunction(); + + // add message queue permissions + hr = MqiAddMessageQueuePermissions(&pwzData); + ExitOnFailure(hr, "Failed to add message queue permissions"); + if (S_FALSE == hr) ExitFunction(); + + hr = S_OK; + +LExit: + // clean up + ReleaseStr(pwzCustomActionData); + + // uninitialize + MqiUninitialize(); + + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + +/******************************************************************** + MessageQueuingRollbackInstall - CUSTOM ACTION ENTRY POINT + + Input: deferred CustomActionData - MessageQueuingRollbackInstall +********************************************************************/ +extern "C" UINT __stdcall MessageQueuingRollbackInstall(MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + LPWSTR pwzCustomActionData = NULL; + LPWSTR pwzData = NULL; + + // initialize + hr = WcaInitialize(hInstall, "MessageQueuingRollbackInstall"); + ExitOnFailure(hr, "Failed to initialize MessageQueuingRollbackInstall"); + + hr = MqiInitialize(); + ExitOnFailure(hr, "Failed to initialize"); + + // get custom action data + hr = WcaGetProperty(L"CustomActionData", &pwzCustomActionData); + ExitOnFailure(hr, "Failed to get CustomActionData"); + pwzData = pwzCustomActionData; + + // add message queue permissions + hr = MqiRollbackAddMessageQueuePermissions(&pwzData); + ExitOnFailure(hr, "Failed to rollback add message queue permissions"); + + // create message queues + hr = MqiRollbackCreateMessageQueues(&pwzData); + ExitOnFailure(hr, "Failed to rollback create message queues"); + + hr = S_OK; + +LExit: + // clean up + ReleaseStr(pwzCustomActionData); + + // uninitialize + MqiUninitialize(); + + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + +/******************************************************************** + MessageQueuingExecuteUninstall - CUSTOM ACTION ENTRY POINT + + Input: deferred CustomActionData - MessageQueuingExecuteUninstall +********************************************************************/ +extern "C" UINT __stdcall MessageQueuingExecuteUninstall(MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + LPWSTR pwzCustomActionData = NULL; + LPWSTR pwzData = NULL; + + // initialize + hr = WcaInitialize(hInstall, "MessageQueuingExecuteUninstall"); + ExitOnFailure(hr, "Failed to initialize MessageQueuingExecuteUninstall"); + + hr = MqiInitialize(); + ExitOnFailure(hr, "Failed to initialize"); + + // get custom action data + hr = WcaGetProperty(L"CustomActionData", &pwzCustomActionData); + ExitOnFailure(hr, "Failed to get CustomActionData"); + pwzData = pwzCustomActionData; + + // remove message queue permissions + hr = MqiRemoveMessageQueuePermissions(&pwzData); + ExitOnFailure(hr, "Failed to remove message queue permissions"); + if (S_FALSE == hr) ExitFunction(); + + // delete message queues + hr = MqiDeleteMessageQueues(&pwzData); + ExitOnFailure(hr, "Failed to delete message queues"); + if (S_FALSE == hr) ExitFunction(); + + hr = S_OK; + +LExit: + // clean up + ReleaseStr(pwzCustomActionData); + + // uninitialize + MqiUninitialize(); + + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + +/******************************************************************** + MessageQueuingRollbackUninstall - CUSTOM ACTION ENTRY POINT + + Input: deferred CustomActionData - MessageQueuingRollbackUninstall +********************************************************************/ +extern "C" UINT __stdcall MessageQueuingRollbackUninstall(MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + LPWSTR pwzCustomActionData = NULL; + LPWSTR pwzData = NULL; + + // initialize + hr = WcaInitialize(hInstall, "MessageQueuingRollbackUninstall"); + ExitOnFailure(hr, "Failed to initialize MessageQueuingRollbackUninstall"); + + hr = MqiInitialize(); + ExitOnFailure(hr, "Failed to initialize"); + + // get custom action data + hr = WcaGetProperty(L"CustomActionData", &pwzCustomActionData); + ExitOnFailure(hr, "Failed to get CustomActionData"); + pwzData = pwzCustomActionData; + + // delete message queues + hr = MqiRollbackDeleteMessageQueues(&pwzData); + ExitOnFailure(hr, "Failed to delete message queues"); + + // remove message queue permissions + hr = MqiRollbackRemoveMessageQueuePermissions(&pwzData); + ExitOnFailure(hr, "Failed to remove message queue permissions"); + + hr = S_OK; + +LExit: + // clean up + ReleaseStr(pwzCustomActionData); + + // uninitialize + MqiUninitialize(); + + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} diff --git a/src/ca/mqqueueexec.cpp b/src/ca/mqqueueexec.cpp new file mode 100644 index 00000000..f5b99da7 --- /dev/null +++ b/src/ca/mqqueueexec.cpp @@ -0,0 +1,927 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// private typedefs + +typedef HRESULT (__stdcall *MQCreateQueueFunc)(PSECURITY_DESCRIPTOR, MQQUEUEPROPS*, LPWSTR, LPDWORD); +typedef HRESULT (__stdcall *MQDeleteQueueFunc)(LPCWSTR); +typedef HRESULT (__stdcall *MQPathNameToFormatNameFunc)(LPCWSTR, LPWSTR, LPDWORD); +typedef HRESULT (__stdcall *MQGetQueueSecurityFunc)(LPCWSTR, SECURITY_INFORMATION, PSECURITY_DESCRIPTOR, DWORD, LPDWORD); +typedef HRESULT (__stdcall *MQSetQueueSecurityFunc)(LPCWSTR, SECURITY_INFORMATION, PSECURITY_DESCRIPTOR); + + +// private enums + +enum eMessageQueueAttributes +{ + mqaAuthenticate = (1 << 0), + mqaJournal = (1 << 1), + mqaTransactional = (1 << 2) +}; + +enum eMessageQueuePrivacyLevel +{ + mqplNone = 0, + mqplOptional = 1, + mqplBody = 2 +}; + +enum eMessageQueuePermission +{ + mqpDeleteMessage = (1 << 0), + mqpPeekMessage = (1 << 1), + mqpWriteMessage = (1 << 2), + mqpDeleteJournalMessage = (1 << 3), + mqpSetQueueProperties = (1 << 4), + mqpGetQueueProperties = (1 << 5), + mqpDeleteQueue = (1 << 6), + mqpGetQueuePermissions = (1 << 7), + mqpChangeQueuePermissions = (1 << 8), + mqpTakeQueueOwnership = (1 << 9), + mqpReceiveMessage = (1 << 10), + mqpReceiveJournalMessage = (1 << 11), + mqpQueueGenericRead = (1 << 12), + mqpQueueGenericWrite = (1 << 13), + mqpQueueGenericExecute = (1 << 14), + mqpQueueGenericAll = (1 << 15) +}; + + +// private structs + +struct MQI_MESSAGE_QUEUE_ATTRIBUTES +{ + LPWSTR pwzKey; + int iBasePriority; + int iJournalQuota; + LPWSTR pwzLabel; + LPWSTR pwzMulticastAddress; + LPWSTR pwzPathName; + int iPrivLevel; + int iQuota; + LPWSTR pwzServiceTypeGuid; + int iAttributes; +}; + +struct MQI_MESSAGE_QUEUE_PERMISSION_ATTRIBUTES +{ + LPWSTR pwzKey; + LPWSTR pwzPathName; + LPWSTR pwzDomain; + LPWSTR pwzName; + int iPermissions; +}; + + +// prototypes for private helper functions + +static HRESULT ReadMessageQueueAttributes( + LPWSTR* ppwzData, + MQI_MESSAGE_QUEUE_ATTRIBUTES* pAttrs + ); +static void FreeMessageQueueAttributes( + MQI_MESSAGE_QUEUE_ATTRIBUTES* pAttrs + ); +static HRESULT ReadMessageQueuePermissionAttributes( + LPWSTR* ppwzData, + MQI_MESSAGE_QUEUE_PERMISSION_ATTRIBUTES* pAttrs + ); +static void FreeMessageQueuePermissionAttributes( + MQI_MESSAGE_QUEUE_PERMISSION_ATTRIBUTES* pAttrs + ); +static HRESULT CreateMessageQueue( + MQI_MESSAGE_QUEUE_ATTRIBUTES* pAttrs + ); +static HRESULT DeleteMessageQueue( + MQI_MESSAGE_QUEUE_ATTRIBUTES* pAttrs + ); +static HRESULT SetMessageQueuePermissions( + MQI_MESSAGE_QUEUE_PERMISSION_ATTRIBUTES* pAttrs, + BOOL fRevoke + ); +static void SetAccessPermissions( + int iPermissions, + LPDWORD pgrfAccessPermissions + ); + + +// private variables + +static HMODULE ghMQRT; +static MQCreateQueueFunc gpfnMQCreateQueue; +static MQDeleteQueueFunc gpfnMQDeleteQueue; +static MQPathNameToFormatNameFunc gpfnMQPathNameToFormatName; +static MQGetQueueSecurityFunc gpfnMQGetQueueSecurity; +static MQSetQueueSecurityFunc gpfnMQSetQueueSecurity; + + +// function definitions + +HRESULT MqiInitialize() +{ + HRESULT hr = S_OK; + + // load mqrt.dll + ghMQRT = ::LoadLibraryW(L"mqrt.dll"); + ExitOnNull(ghMQRT, hr, E_FAIL, "Failed to load mqrt.dll"); + + // get MQCreateQueue function address + gpfnMQCreateQueue = (MQCreateQueueFunc)::GetProcAddress(ghMQRT, "MQCreateQueue"); + ExitOnNull(gpfnMQCreateQueue, hr, HRESULT_FROM_WIN32(::GetLastError()), "Failed get address for MQCreateQueue() function"); + + // get MQDeleteQueue function address + gpfnMQDeleteQueue = (MQDeleteQueueFunc)::GetProcAddress(ghMQRT, "MQDeleteQueue"); + ExitOnNull(gpfnMQDeleteQueue, hr, HRESULT_FROM_WIN32(::GetLastError()), "Failed get address for MQDeleteQueue() function"); + + // get MQPathNameToFormatName function address + gpfnMQPathNameToFormatName = (MQPathNameToFormatNameFunc)::GetProcAddress(ghMQRT, "MQPathNameToFormatName"); + ExitOnNull(gpfnMQPathNameToFormatName, hr, HRESULT_FROM_WIN32(::GetLastError()), "Failed get address for MQPathNameToFormatName() function"); + + // get MQGetQueueSecurity function address + gpfnMQGetQueueSecurity = (MQGetQueueSecurityFunc)::GetProcAddress(ghMQRT, "MQGetQueueSecurity"); + ExitOnNull(gpfnMQGetQueueSecurity, hr, HRESULT_FROM_WIN32(::GetLastError()), "Failed get address for MQGetQueueSecurity() function"); + + // get MQSetQueueSecurity function address + gpfnMQSetQueueSecurity = (MQSetQueueSecurityFunc)::GetProcAddress(ghMQRT, "MQSetQueueSecurity"); + ExitOnNull(gpfnMQSetQueueSecurity, hr, HRESULT_FROM_WIN32(::GetLastError()), "Failed get address for MQSetQueueSecurity() function"); + + hr = S_OK; + +LExit: + return hr; +} + +void MqiUninitialize() +{ + if (ghMQRT) + ::FreeLibrary(ghMQRT); +} + +HRESULT MqiCreateMessageQueues( + LPWSTR* ppwzData + ) +{ + HRESULT hr = S_OK; + + int iCnt = 0; + + MQI_MESSAGE_QUEUE_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // ger count + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + for (int i = 0; i < iCnt; i++) + { + // read attributes from CustomActionData + hr = ReadMessageQueueAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read attributes"); + + // progress message + hr = PcaActionDataMessage(1, attrs.pwzPathName); + ExitOnFailure(hr, "Failed to send progress messages, key: %S", attrs.pwzKey); + + // create message queue + hr = CreateMessageQueue(&attrs); + ExitOnFailure(hr, "Failed to create message queue, key: %S", attrs.pwzKey); + + // progress tics + hr = WcaProgressMessage(COST_MESSAGE_QUEUE_CREATE, FALSE); + ExitOnFailure(hr, "Failed to update progress"); + } + + hr = S_OK; + +LExit: + // clean up + FreeMessageQueueAttributes(&attrs); + + return hr; +} + +HRESULT MqiRollbackCreateMessageQueues( + LPWSTR* ppwzData + ) +{ + HRESULT hr = S_OK; + + int iCnt = 0; + + MQI_MESSAGE_QUEUE_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // ger count + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + for (int i = 0; i < iCnt; i++) + { + // read attributes from CustomActionData + hr = ReadMessageQueueAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read attributes"); + + // create message queue + hr = DeleteMessageQueue(&attrs); + if (FAILED(hr)) + WcaLog(LOGMSG_STANDARD, "Failed to delete message queue, hr: 0x%x, key: %S", hr, attrs.pwzKey); + } + + hr = S_OK; + +LExit: + // clean up + FreeMessageQueueAttributes(&attrs); + + return hr; +} + +HRESULT MqiDeleteMessageQueues( + LPWSTR* ppwzData + ) +{ + HRESULT hr = S_OK; + + int iCnt = 0; + + MQI_MESSAGE_QUEUE_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // ger count + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + for (int i = 0; i < iCnt; i++) + { + // read attributes from CustomActionData + hr = ReadMessageQueueAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read attributes"); + + // progress message + hr = PcaActionDataMessage(1, attrs.pwzPathName); + ExitOnFailure(hr, "Failed to send progress messages, key: %S", attrs.pwzKey); + + // create message queue + hr = DeleteMessageQueue(&attrs); + if (FAILED(hr)) + { + WcaLog(LOGMSG_STANDARD, "Failed to delete queue, hr: 0x%x, key: %S", hr, attrs.pwzKey); + continue; + } + + // progress tics + hr = WcaProgressMessage(COST_MESSAGE_QUEUE_DELETE, FALSE); + ExitOnFailure(hr, "Failed to update progress"); + } + + hr = S_OK; + +LExit: + // clean up + FreeMessageQueueAttributes(&attrs); + + return hr; +} + +HRESULT MqiRollbackDeleteMessageQueues( + LPWSTR* ppwzData + ) +{ + HRESULT hr = S_OK; + + int iCnt = 0; + + MQI_MESSAGE_QUEUE_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // ger count + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + for (int i = 0; i < iCnt; i++) + { + // read attributes from CustomActionData + hr = ReadMessageQueueAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read attributes"); + + // create message queue + hr = CreateMessageQueue(&attrs); + if (FAILED(hr)) + WcaLog(LOGMSG_STANDARD, "Failed to create message queue, hr: 0x%x, key: %S", hr, attrs.pwzKey); + } + + hr = S_OK; + +LExit: + // clean up + FreeMessageQueueAttributes(&attrs); + + return hr; +} + +HRESULT MqiAddMessageQueuePermissions( + LPWSTR* ppwzData + ) +{ + HRESULT hr = S_OK; + + int iCnt = 0; + + MQI_MESSAGE_QUEUE_PERMISSION_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // ger count + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + for (int i = 0; i < iCnt; i++) + { + // read attributes from CustomActionData + hr = ReadMessageQueuePermissionAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read attributes"); + + // progress message + hr = PcaActionDataMessage(1, attrs.pwzPathName); + ExitOnFailure(hr, "Failed to send progress messages"); + + // add message queue permission + hr = SetMessageQueuePermissions(&attrs, FALSE); + ExitOnFailure(hr, "Failed to add message queue permission"); + + // progress tics + hr = WcaProgressMessage(COST_MESSAGE_QUEUE_PERMISSION_ADD, FALSE); + ExitOnFailure(hr, "Failed to update progress"); + } + + hr = S_OK; + +LExit: + // clean up + FreeMessageQueuePermissionAttributes(&attrs); + + return hr; +} + +HRESULT MqiRollbackAddMessageQueuePermissions( + LPWSTR* ppwzData + ) +{ + HRESULT hr = S_OK; + + int iCnt = 0; + + MQI_MESSAGE_QUEUE_PERMISSION_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // ger count + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + for (int i = 0; i < iCnt; i++) + { + // read attributes from CustomActionData + hr = ReadMessageQueuePermissionAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read attributes"); + + // add message queue permission + hr = SetMessageQueuePermissions(&attrs, TRUE); + if (FAILED(hr)) + WcaLog(LOGMSG_STANDARD, "Failed to rollback add message queue permission, hr: 0x%x, key: %S", hr, attrs.pwzKey); + } + + hr = S_OK; + +LExit: + // clean up + FreeMessageQueuePermissionAttributes(&attrs); + + return hr; +} + +HRESULT MqiRemoveMessageQueuePermissions( + LPWSTR* ppwzData + ) +{ + HRESULT hr = S_OK; + + int iCnt = 0; + + MQI_MESSAGE_QUEUE_PERMISSION_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // ger count + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + for (int i = 0; i < iCnt; i++) + { + // read attributes from CustomActionData + hr = ReadMessageQueuePermissionAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read attributes"); + + // progress message + hr = PcaActionDataMessage(1, attrs.pwzPathName); + ExitOnFailure(hr, "Failed to send progress messages"); + + // add message queue permission + hr = SetMessageQueuePermissions(&attrs, TRUE); + ExitOnFailure(hr, "Failed to remove message queue permission"); + + // progress tics + hr = WcaProgressMessage(COST_MESSAGE_QUEUE_PERMISSION_ADD, FALSE); + ExitOnFailure(hr, "Failed to update progress"); + } + + hr = S_OK; + +LExit: + // clean up + FreeMessageQueuePermissionAttributes(&attrs); + + return hr; +} + +HRESULT MqiRollbackRemoveMessageQueuePermissions( + LPWSTR* ppwzData + ) +{ + HRESULT hr = S_OK; + + int iCnt = 0; + + MQI_MESSAGE_QUEUE_PERMISSION_ATTRIBUTES attrs; + ::ZeroMemory(&attrs, sizeof(attrs)); + + // ger count + hr = WcaReadIntegerFromCaData(ppwzData, &iCnt); + ExitOnFailure(hr, "Failed to read count"); + + for (int i = 0; i < iCnt; i++) + { + // read attributes from CustomActionData + hr = ReadMessageQueuePermissionAttributes(ppwzData, &attrs); + ExitOnFailure(hr, "Failed to read attributes"); + + // add message queue permission + hr = SetMessageQueuePermissions(&attrs, FALSE); + if (FAILED(hr)) + WcaLog(LOGMSG_STANDARD, "Failed to rollback remove message queue permission, hr: 0x%x, key: %S", hr, attrs.pwzKey); + } + + hr = S_OK; + +LExit: + // clean up + FreeMessageQueuePermissionAttributes(&attrs); + + return hr; +} + + +// helper function definitions + +static HRESULT ReadMessageQueueAttributes( + LPWSTR* ppwzData, + MQI_MESSAGE_QUEUE_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + + // read message queue information from custom action data + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzKey); + ExitOnFailure(hr, "Failed to read key from custom action data"); + hr = WcaReadIntegerFromCaData(ppwzData, &pAttrs->iBasePriority); + ExitOnFailure(hr, "Failed to read base priority from custom action data"); + hr = WcaReadIntegerFromCaData(ppwzData, &pAttrs->iJournalQuota); + ExitOnFailure(hr, "Failed to read journal quota from custom action data"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzLabel); + ExitOnFailure(hr, "Failed to read label from custom action data"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzMulticastAddress); + ExitOnFailure(hr, "Failed to read multicast address from custom action data"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzPathName); + ExitOnFailure(hr, "Failed to read path name from custom action data"); + hr = WcaReadIntegerFromCaData(ppwzData, &pAttrs->iPrivLevel); + ExitOnFailure(hr, "Failed to read privacy level from custom action data"); + hr = WcaReadIntegerFromCaData(ppwzData, &pAttrs->iQuota); + ExitOnFailure(hr, "Failed to read quota from custom action data"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzServiceTypeGuid); + ExitOnFailure(hr, "Failed to read service type guid from custom action data"); + hr = WcaReadIntegerFromCaData(ppwzData, &pAttrs->iAttributes); + ExitOnFailure(hr, "Failed to read attributes from custom action data"); + + hr = S_OK; + +LExit: + return hr; +} + +static void FreeMessageQueueAttributes( + MQI_MESSAGE_QUEUE_ATTRIBUTES* pAttrs + ) +{ + ReleaseStr(pAttrs->pwzKey); + ReleaseStr(pAttrs->pwzLabel); + ReleaseStr(pAttrs->pwzMulticastAddress); + ReleaseStr(pAttrs->pwzPathName); + ReleaseStr(pAttrs->pwzServiceTypeGuid); +} + +static HRESULT ReadMessageQueuePermissionAttributes( + LPWSTR* ppwzData, + MQI_MESSAGE_QUEUE_PERMISSION_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + + // read message queue permission information from custom action data + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzKey); + ExitOnFailure(hr, "Failed to read key from custom action data"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzPathName); + ExitOnFailure(hr, "Failed to read path name from custom action data"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzDomain); + ExitOnFailure(hr, "Failed to read domain from custom action data"); + hr = WcaReadStringFromCaData(ppwzData, &pAttrs->pwzName); + ExitOnFailure(hr, "Failed to read name from custom action data"); + hr = WcaReadIntegerFromCaData(ppwzData, &pAttrs->iPermissions); + ExitOnFailure(hr, "Failed to read permissions from custom action data"); + + hr = S_OK; + +LExit: + return hr; +} + +static void FreeMessageQueuePermissionAttributes( + MQI_MESSAGE_QUEUE_PERMISSION_ATTRIBUTES* pAttrs + ) +{ + ReleaseStr(pAttrs->pwzKey); + ReleaseStr(pAttrs->pwzPathName); + ReleaseStr(pAttrs->pwzDomain); + ReleaseStr(pAttrs->pwzName); +} + +static HRESULT CreateMessageQueue( + MQI_MESSAGE_QUEUE_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + + SECURITY_DESCRIPTOR sd; + PSID pOwner = NULL; + DWORD cbDacl = 0; + PACL pDacl = NULL; + QUEUEPROPID aPropID[11]; + MQPROPVARIANT aPropVar[11]; + MQQUEUEPROPS props; + + GUID guidType; + + DWORD dwFormatNameLength = 0; + + ::ZeroMemory(&sd, sizeof(sd)); + ::ZeroMemory(aPropID, sizeof(aPropID)); + ::ZeroMemory(aPropVar, sizeof(aPropVar)); + ::ZeroMemory(&props, sizeof(props)); + ::ZeroMemory(&guidType, sizeof(guidType)); + + // initialize security descriptor + if (!::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) + ExitOnFailure(hr = HRESULT_FROM_WIN32(::GetLastError()), "Failed to initialize security descriptor"); + + // set security descriptor owner + hr = PcaAccountNameToSid(L"\\Administrators", &pOwner); + ExitOnFailure(hr, "Failed to get sid for account name"); + + if (!::SetSecurityDescriptorOwner(&sd, pOwner, FALSE)) + ExitOnFailure(hr = HRESULT_FROM_WIN32(::GetLastError()), "Failed to set security descriptor owner"); + + // set security descriptor DACL + cbDacl = sizeof(ACL) + (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)) + ::GetLengthSid(pOwner); + pDacl = (PACL)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, cbDacl); + ExitOnNull(pDacl, hr, E_OUTOFMEMORY, "Failed to allocate buffer for DACL"); + + if (!::InitializeAcl(pDacl, cbDacl, ACL_REVISION)) + ExitOnFailure(hr = HRESULT_FROM_WIN32(::GetLastError()), "Failed to initialize DACL"); + + if (!::AddAccessAllowedAce(pDacl, ACL_REVISION, MQSEC_QUEUE_GENERIC_ALL, pOwner)) + ExitOnFailure(hr = HRESULT_FROM_WIN32(::GetLastError()), "Failed to add ACE to DACL"); + + if (!::SetSecurityDescriptorDacl(&sd, TRUE, pDacl, FALSE)) + ExitOnFailure(hr = HRESULT_FROM_WIN32(::GetLastError()), "Failed to set security descriptor DACL"); + + // set property values + props.aPropID = aPropID; + props.aPropVar = aPropVar; + + aPropID[0] = PROPID_Q_LABEL; + aPropVar[0].vt = VT_LPWSTR; + aPropVar[0].pwszVal = pAttrs->pwzLabel; + + aPropID[1] = PROPID_Q_PATHNAME; + aPropVar[1].vt = VT_LPWSTR; + aPropVar[1].pwszVal = pAttrs->pwzPathName; + + aPropID[2] = PROPID_Q_AUTHENTICATE; + aPropVar[2].vt = VT_UI1; + aPropVar[2].bVal = mqaAuthenticate == (pAttrs->iAttributes & mqaAuthenticate); + + aPropID[3] = PROPID_Q_JOURNAL; + aPropVar[3].vt = VT_UI1; + aPropVar[3].bVal = mqaJournal == (pAttrs->iAttributes & mqaJournal); + + aPropID[4] = PROPID_Q_TRANSACTION; + aPropVar[4].vt = VT_UI1; + aPropVar[4].bVal = mqaTransactional == (pAttrs->iAttributes & mqaTransactional); + + props.cProp = 5; + + if (MSI_NULL_INTEGER != pAttrs->iBasePriority) + { + aPropID[props.cProp] = PROPID_Q_BASEPRIORITY; + aPropVar[props.cProp].vt = VT_I2; + aPropVar[props.cProp].iVal = (SHORT)pAttrs->iBasePriority; + props.cProp++; + } + + if (MSI_NULL_INTEGER != pAttrs->iJournalQuota) + { + aPropID[props.cProp] = PROPID_Q_JOURNAL_QUOTA; + aPropVar[props.cProp].vt = VT_UI4; + aPropVar[props.cProp].ulVal = (ULONG)pAttrs->iJournalQuota; + props.cProp++; + } + + if (*pAttrs->pwzMulticastAddress) + { + aPropID[props.cProp] = PROPID_Q_MULTICAST_ADDRESS; + aPropVar[props.cProp].vt = VT_LPWSTR; + aPropVar[props.cProp].pwszVal = pAttrs->pwzMulticastAddress; + props.cProp++; + } + + if (MSI_NULL_INTEGER != pAttrs->iPrivLevel) + { + aPropID[props.cProp] = PROPID_Q_PRIV_LEVEL; + aPropVar[props.cProp].vt = VT_UI4; + switch (pAttrs->iPrivLevel) + { + case mqplNone: + aPropVar[props.cProp].ulVal = MQ_PRIV_LEVEL_NONE; + break; + case mqplBody: + aPropVar[props.cProp].ulVal = MQ_PRIV_LEVEL_BODY; + break; + case mqplOptional: + aPropVar[props.cProp].ulVal = MQ_PRIV_LEVEL_OPTIONAL; + break; + } + props.cProp++; + } + + if (MSI_NULL_INTEGER != pAttrs->iQuota) + { + aPropID[props.cProp] = PROPID_Q_QUOTA; + aPropVar[props.cProp].vt = VT_UI4; + aPropVar[props.cProp].ulVal = (ULONG)pAttrs->iQuota; + props.cProp++; + } + + if (*pAttrs->pwzServiceTypeGuid) + { + // parse guid string + hr = PcaGuidFromString(pAttrs->pwzServiceTypeGuid, &guidType); + ExitOnFailure(hr, "Failed to parse service type GUID string"); + + aPropID[props.cProp] = PROPID_Q_TYPE; + aPropVar[props.cProp].vt = VT_CLSID; + aPropVar[props.cProp].puuid = &guidType; + props.cProp++; + } + + // create message queue + hr = gpfnMQCreateQueue(&sd, &props, NULL, &dwFormatNameLength); + ExitOnFailure(hr, "Failed to create message queue"); + + // log + WcaLog(LOGMSG_VERBOSE, "Message queue created, key: %S, PathName: '%S'", pAttrs->pwzKey, pAttrs->pwzPathName); + + hr = S_OK; + +LExit: + // clean up + if (pOwner) + ::HeapFree(::GetProcessHeap(), 0, pOwner); + if (pDacl) + ::HeapFree(::GetProcessHeap(), 0, pDacl); + + return hr; +} + +static HRESULT DeleteMessageQueue( + MQI_MESSAGE_QUEUE_ATTRIBUTES* pAttrs + ) +{ + HRESULT hr = S_OK; + + LPWSTR pwzFormatName = NULL; + DWORD dwCount = 128; + + // get format name + hr = StrAlloc(&pwzFormatName, dwCount); + ExitOnFailure(hr, "Failed to allocate format name string"); + do { + hr = gpfnMQPathNameToFormatName(pAttrs->pwzPathName, pwzFormatName, &dwCount); + switch (hr) + { + case MQ_ERROR_QUEUE_NOT_FOUND: + ExitFunction1(hr = S_OK); // nothing to delete + case MQ_ERROR_FORMATNAME_BUFFER_TOO_SMALL: + hr = StrAlloc(&pwzFormatName, dwCount); + ExitOnFailure(hr, "Failed to reallocate format name string"); + hr = S_FALSE; // retry + break; + default: + ExitOnFailure(hr, "Failed to get format name"); + hr = S_OK; + } + } while (S_FALSE == hr); + + // delete queue + hr = gpfnMQDeleteQueue(pwzFormatName); + ExitOnFailure(hr, "Failed to delete queue"); + + // log + WcaLog(LOGMSG_VERBOSE, "Message queue deleted, key: %S, PathName: '%S'", pAttrs->pwzKey, pAttrs->pwzPathName); + + hr = S_OK; + +LExit: + // clean up + ReleaseStr(pwzFormatName); + + return hr; +} + +static HRESULT SetMessageQueuePermissions( + MQI_MESSAGE_QUEUE_PERMISSION_ATTRIBUTES* pAttrs, + BOOL fRevoke + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + DWORD dw = 0; + + LPWSTR pwzAccount = NULL; + LPWSTR pwzFormatName = NULL; + + PSECURITY_DESCRIPTOR psd = NULL; + PSECURITY_DESCRIPTOR ptsd = NULL; + + PACL pAclExisting = NULL; + PACL pAclNew = NULL; + BOOL fDaclPresent = FALSE; + BOOL fDaclDefaulted = FALSE; + + PSID psid = NULL; + + EXPLICIT_ACCESSW ea; + SECURITY_DESCRIPTOR sdNew; + + ::ZeroMemory(&ea, sizeof(ea)); + ::ZeroMemory(&sdNew, sizeof(sdNew)); + + // get format name + dw = 128; + hr = StrAlloc(&pwzFormatName, dw); + ExitOnFailure(hr, "Failed to allocate format name string"); + do { + hr = gpfnMQPathNameToFormatName(pAttrs->pwzPathName, pwzFormatName, &dw); + if (MQ_ERROR_FORMATNAME_BUFFER_TOO_SMALL == hr) + { + hr = StrAlloc(&pwzFormatName, dw); + ExitOnFailure(hr, "Failed to reallocate format name string"); + hr = S_FALSE; // retry + } + else + { + ExitOnFailure(hr, "Failed to get format name"); + hr = S_OK; + } + } while (S_FALSE == hr); + + // get queue security information + dw = 256; + psd = (PSECURITY_DESCRIPTOR)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, dw); + ExitOnNull(psd, hr, E_OUTOFMEMORY, "Failed to allocate buffer for security descriptor"); + do { + hr = gpfnMQGetQueueSecurity(pwzFormatName, DACL_SECURITY_INFORMATION, psd, dw, &dw); + if (MQ_ERROR_SECURITY_DESCRIPTOR_TOO_SMALL == hr) + { + ptsd = (PSECURITY_DESCRIPTOR)::HeapReAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, psd, dw); + ExitOnNull(ptsd, hr, E_OUTOFMEMORY, "Failed to reallocate buffer for security descriptor"); + psd = ptsd; + hr = S_FALSE; // retry + } + else + { + ExitOnFailure(hr, "Failed to get queue security information"); + hr = S_OK; + } + } while (S_FALSE == hr); + + // get dacl + if (!::GetSecurityDescriptorDacl(psd, &fDaclPresent, &pAclExisting, &fDaclDefaulted)) + ExitOnFailure(hr = HRESULT_FROM_WIN32(::GetLastError()), "Failed to get DACL for security descriptor"); + if (!fDaclPresent || !pAclExisting) + ExitOnFailure(hr = E_ACCESSDENIED, "Failed to get DACL for security descriptor, access denied"); + + // build account name string + hr = PcaBuildAccountName(pAttrs->pwzDomain, pAttrs->pwzName, &pwzAccount); + ExitOnFailure(hr, "Failed to build account name string"); + + // get sid for account name + hr = PcaAccountNameToSid(pwzAccount, &psid); + ExitOnFailure(hr, "Failed to get SID for account name"); + + // set acl entry + SetAccessPermissions(pAttrs->iPermissions, &ea.grfAccessPermissions); + ea.grfAccessMode = fRevoke ? REVOKE_ACCESS : SET_ACCESS; + ea.grfInheritance = NO_INHERITANCE; + ::BuildTrusteeWithSidW(&ea.Trustee, psid); + + er = ::SetEntriesInAclW(1, &ea, pAclExisting, &pAclNew); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to set ACL entry"); + + // create new security descriptor + if (!::InitializeSecurityDescriptor(&sdNew, SECURITY_DESCRIPTOR_REVISION)) + ExitOnFailure(hr = HRESULT_FROM_WIN32(::GetLastError()), "Failed to initialize security descriptor"); + + if (!::SetSecurityDescriptorDacl(&sdNew, TRUE, pAclNew, FALSE)) + ExitOnFailure(hr = HRESULT_FROM_WIN32(::GetLastError()), "Failed to set DACL for security descriptor"); + + // set queue security information + hr = gpfnMQSetQueueSecurity(pwzFormatName, DACL_SECURITY_INFORMATION, &sdNew); + ExitOnFailure(hr, "Failed to set queue security information"); + + // log + WcaLog(LOGMSG_VERBOSE, "Permission set for message queue, key: %S, PathName: '%S'", pAttrs->pwzKey, pAttrs->pwzPathName); + + hr = S_OK; + +LExit: + // clean up + ReleaseStr(pwzFormatName); + ReleaseStr(pwzAccount); + + if (psd) + ::HeapFree(::GetProcessHeap(), 0, psd); + if (psid) + ::HeapFree(::GetProcessHeap(), 0, psid); + if (pAclNew) + ::LocalFree(pAclNew); + + return hr; +} + +static void SetAccessPermissions( + int iPermissions, + LPDWORD pgrfAccessPermissions + ) +{ + if (iPermissions & mqpDeleteMessage) + *pgrfAccessPermissions |= MQSEC_DELETE_MESSAGE; + if (iPermissions & mqpPeekMessage) + *pgrfAccessPermissions |= MQSEC_PEEK_MESSAGE; + if (iPermissions & mqpWriteMessage) + *pgrfAccessPermissions |= MQSEC_WRITE_MESSAGE; + if (iPermissions & mqpDeleteJournalMessage) + *pgrfAccessPermissions |= MQSEC_DELETE_JOURNAL_MESSAGE; + if (iPermissions & mqpSetQueueProperties) + *pgrfAccessPermissions |= MQSEC_SET_QUEUE_PROPERTIES; + if (iPermissions & mqpGetQueueProperties) + *pgrfAccessPermissions |= MQSEC_GET_QUEUE_PROPERTIES; + if (iPermissions & mqpDeleteQueue) + *pgrfAccessPermissions |= MQSEC_DELETE_QUEUE; + if (iPermissions & mqpGetQueuePermissions) + *pgrfAccessPermissions |= MQSEC_GET_QUEUE_PERMISSIONS; + if (iPermissions & mqpChangeQueuePermissions) + *pgrfAccessPermissions |= MQSEC_CHANGE_QUEUE_PERMISSIONS; + if (iPermissions & mqpTakeQueueOwnership) + *pgrfAccessPermissions |= MQSEC_TAKE_QUEUE_OWNERSHIP; + if (iPermissions & mqpReceiveMessage) + *pgrfAccessPermissions |= MQSEC_RECEIVE_MESSAGE; + if (iPermissions & mqpReceiveJournalMessage) + *pgrfAccessPermissions |= MQSEC_RECEIVE_JOURNAL_MESSAGE; + if (iPermissions & mqpQueueGenericRead) + *pgrfAccessPermissions |= MQSEC_QUEUE_GENERIC_READ; + if (iPermissions & mqpQueueGenericWrite) + *pgrfAccessPermissions |= MQSEC_QUEUE_GENERIC_WRITE; + if (iPermissions & mqpQueueGenericExecute) + *pgrfAccessPermissions |= MQSEC_QUEUE_GENERIC_EXECUTE; + if (iPermissions & mqpQueueGenericAll) + *pgrfAccessPermissions |= MQSEC_QUEUE_GENERIC_ALL; +} diff --git a/src/ca/mqqueueexec.h b/src/ca/mqqueueexec.h new file mode 100644 index 00000000..37ceea50 --- /dev/null +++ b/src/ca/mqqueueexec.h @@ -0,0 +1,30 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +HRESULT MqiInitialize(); +void MqiUninitialize(); +HRESULT MqiCreateMessageQueues( + LPWSTR* ppwzData + ); +HRESULT MqiRollbackCreateMessageQueues( + LPWSTR* ppwzData + ); +HRESULT MqiDeleteMessageQueues( + LPWSTR* ppwzData + ); +HRESULT MqiRollbackDeleteMessageQueues( + LPWSTR* ppwzData + ); +HRESULT MqiAddMessageQueuePermissions( + LPWSTR* ppwzData + ); +HRESULT MqiRollbackAddMessageQueuePermissions( + LPWSTR* ppwzData + ); +HRESULT MqiRemoveMessageQueuePermissions( + LPWSTR* ppwzData + ); +HRESULT MqiRollbackRemoveMessageQueuePermissions( + LPWSTR* ppwzData + ); diff --git a/src/ca/mqqueuesched.cpp b/src/ca/mqqueuesched.cpp new file mode 100644 index 00000000..4f40a4aa --- /dev/null +++ b/src/ca/mqqueuesched.cpp @@ -0,0 +1,582 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// sql queries + +LPCWSTR vcsMessageQueueQuery = + L"SELECT `MessageQueue`, `Component_`, `BasePriority`, `JournalQuota`, `Label`, `MulticastAddress`, `PathName`, `PrivLevel`, `Quota`, `ServiceTypeGuid`, `Attributes` FROM `MessageQueue`"; +enum eMessageQueueQuery { mqqMessageQueue = 1, mqqComponent, mqqBasePriority, mqqJournalQuota, mqqLabel, mqqMulticastAddress, mqqPathName, mqqPrivLevel, mqqQuota, mqqServiceTypeGuid, mqqAttributes }; + +LPCWSTR vcsMessageQueueUserPermissionQuery = + L"SELECT `MessageQueueUserPermission`, `MessageQueue_`, `MessageQueueUserPermission`.`Component_`, `Domain`, `Name`, `Permissions` FROM `MessageQueueUserPermission`, `User` WHERE `User_` = `User`"; +LPCWSTR vcsMessageQueueGroupPermissionQuery = + L"SELECT `MessageQueueGroupPermission`, `MessageQueue_`, `MessageQueueGroupPermission`.`Component_`, `Domain`, `Name`, `Permissions` FROM `MessageQueueGroupPermission`, `Group` WHERE `Group_` = `Group`"; +enum eMessageQueuePermissionQuery { mqpqMessageQueuePermission = 1, mqpqMessageQueue, mqpqComponent, mqpqDomain, mqpqName, mqpqPermissions }; + + +// prototypes for private helper functions + +static HRESULT MqiMessageQueueFindByKey( + MQI_MESSAGE_QUEUE_LIST* pList, + LPCWSTR pwzKey, + MQI_MESSAGE_QUEUE** ppItm + ); +static HRESULT AddMessageQueueToActionData( + MQI_MESSAGE_QUEUE* pItm, + LPWSTR* ppwzActionData + ); +static HRESULT MessageQueueTrusteePermissionsRead( + LPCWSTR pwzQuery, + MQI_MESSAGE_QUEUE_LIST* pMessageQueueList, + MQI_MESSAGE_QUEUE_PERMISSION_LIST* pList + ); +static HRESULT AddMessageQueuePermissionToActionData( + MQI_MESSAGE_QUEUE_PERMISSION* pItm, + LPWSTR* ppwzActionData + ); + + +// private typedefs + +typedef HRESULT (__stdcall *MQPathNameToFormatNameFunc)(LPCWSTR, LPWSTR, LPDWORD); + + +// private variables + +static HMODULE ghMQRT; +static MQPathNameToFormatNameFunc gpfnMQPathNameToFormatName; + + +// function definitions + +HRESULT MqiInitialize() +{ + HRESULT hr = S_OK; + + // load mqrt.dll + ghMQRT = ::LoadLibraryW(L"mqrt.dll"); + if (!ghMQRT) + { + ExitFunction1(hr = S_FALSE); + } + + // get MQPathNameToFormatName function address + gpfnMQPathNameToFormatName = (MQPathNameToFormatNameFunc)::GetProcAddress(ghMQRT, "MQPathNameToFormatName"); + ExitOnNullWithLastError(gpfnMQPathNameToFormatName, hr, "Failed get address for MQPathNameToFormatName() function"); + + hr = S_OK; + +LExit: + return hr; +} + +void MqiUninitialize() +{ + if (ghMQRT) + { + ::FreeLibrary(ghMQRT); + } +} + +HRESULT MqiMessageQueueRead( + MQI_MESSAGE_QUEUE_LIST* pList + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + PMSIHANDLE hView, hRec; + + MQI_MESSAGE_QUEUE* pItm = NULL; + LPWSTR pwzData = NULL; + + // loop through all partitions + hr = WcaOpenExecuteView(vcsMessageQueueQuery, &hView); + ExitOnFailure(hr, "Failed to execute view on MessageQueue table"); + + while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) + { + // create entry + pItm = (MQI_MESSAGE_QUEUE*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MQI_MESSAGE_QUEUE)); + if (!pItm) + ExitFunction1(hr = E_OUTOFMEMORY); + + // get key + hr = WcaGetRecordString(hRec, mqqMessageQueue, &pwzData); + ExitOnFailure(hr, "Failed to get key"); + StringCchCopyW(pItm->wzKey, countof(pItm->wzKey), pwzData); + + // get component install state + hr = WcaGetRecordString(hRec, mqqComponent, &pwzData); + ExitOnFailure(hr, "Failed to get component"); + + // get component install state + er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &pItm->isInstalled, &pItm->isAction); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to get component state"); + + // get base priority + hr = WcaGetRecordInteger(hRec, mqqBasePriority, &pItm->iBasePriority); + ExitOnFailure(hr, "Failed to get base priority"); + + // get journal quota + hr = WcaGetRecordInteger(hRec, mqqJournalQuota, &pItm->iJournalQuota); + ExitOnFailure(hr, "Failed to get journal quota"); + + // get label + hr = WcaGetRecordFormattedString(hRec, mqqLabel, &pwzData); + ExitOnFailure(hr, "Failed to get label"); + StringCchCopyW(pItm->wzLabel, countof(pItm->wzLabel), pwzData); + + // get multicast address + hr = WcaGetRecordFormattedString(hRec, mqqMulticastAddress, &pwzData); + ExitOnFailure(hr, "Failed to get multicast address"); + StringCchCopyW(pItm->wzMulticastAddress, countof(pItm->wzMulticastAddress), pwzData); + + // get path name + hr = WcaGetRecordFormattedString(hRec, mqqPathName, &pwzData); + ExitOnFailure(hr, "Failed to get path name"); + StringCchCopyW(pItm->wzPathName, countof(pItm->wzPathName), pwzData); + + // get privacy level + hr = WcaGetRecordInteger(hRec, mqqPrivLevel, &pItm->iPrivLevel); + ExitOnFailure(hr, "Failed to get privacy level"); + + // get quota + hr = WcaGetRecordInteger(hRec, mqqQuota, &pItm->iQuota); + ExitOnFailure(hr, "Failed to get quota"); + + // get service type guid + hr = WcaGetRecordFormattedString(hRec, mqqServiceTypeGuid, &pwzData); + ExitOnFailure(hr, "Failed to get service type guid"); + StringCchCopyW(pItm->wzServiceTypeGuid, countof(pItm->wzServiceTypeGuid), pwzData); + + // get attributes + hr = WcaGetRecordInteger(hRec, mqqAttributes, &pItm->iAttributes); + ExitOnFailure(hr, "Failed to get attributes"); + + // increment counters + if (WcaIsInstalling(pItm->isInstalled, pItm->isAction)) + pList->iInstallCount++; + if (WcaIsUninstalling(pItm->isInstalled, pItm->isAction)) + pList->iUninstallCount++; + + // add entry + pItm->pNext = pList->pFirst; + pList->pFirst = pItm; + pItm = NULL; + } + + if (E_NOMOREITEMS == hr) + hr = S_OK; + +LExit: + // clean up + if (pItm) + ::HeapFree(::GetProcessHeap(), 0, pItm); + + ReleaseStr(pwzData); + + return hr; +} + +HRESULT MqiMessageQueueVerify( + MQI_MESSAGE_QUEUE_LIST* pList + ) +{ + HRESULT hr = S_OK; + LPWSTR pwzFormatName = NULL; + DWORD dwCount = 128; + + for (MQI_MESSAGE_QUEUE* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // queues that are being installed only + if (!WcaIsInstalling(pItm->isInstalled, pItm->isAction)) + continue; + + // get format name + hr = StrAlloc(&pwzFormatName, dwCount); + ExitOnFailure(hr, "Failed to allocate format name string"); + do { + hr = gpfnMQPathNameToFormatName(pItm->wzPathName, pwzFormatName, &dwCount); + switch (hr) + { + case MQ_ERROR_QUEUE_NOT_FOUND: + break; // break + case MQ_ERROR_FORMATNAME_BUFFER_TOO_SMALL: + hr = StrAlloc(&pwzFormatName, dwCount); + ExitOnFailure(hr, "Failed to reallocate format name string"); + hr = S_FALSE; // retry + break; + default: + ExitOnFailure(hr, "Failed to get format name"); + hr = S_OK; + } + } while (S_FALSE == hr); + + if (MQ_ERROR_QUEUE_NOT_FOUND == hr) + { + continue; + } + pItm->fExists = TRUE; + pList->iInstallCount--; + + // clean up + ReleaseNullStr(pwzFormatName); + } + + hr = S_OK; + +LExit: + ReleaseStr(pwzFormatName); + return hr; +} + +HRESULT MqiMessageQueueInstall( + MQI_MESSAGE_QUEUE_LIST* pList, + BOOL fRollback, + LPWSTR* ppwzActionData + ) +{ + HRESULT hr = S_OK; + + // add count to action data + hr = WcaWriteIntegerToCaData(pList->iInstallCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add count to custom action data"); + + for (MQI_MESSAGE_QUEUE* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // queues that are being installed only + if (!WcaIsInstalling(pItm->isInstalled, pItm->isAction)) + continue; + + // if the queue exists we should not try to create it + if (pItm->fExists && !fRollback) + { + continue; + } + + // add message queue to action data + hr = AddMessageQueueToActionData(pItm, ppwzActionData); + ExitOnFailure(hr, "Failed to add message queue to action data"); + } + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT MqiMessageQueueUninstall( + MQI_MESSAGE_QUEUE_LIST* pList, + BOOL fRollback, + LPWSTR* ppwzActionData + ) +{ + HRESULT hr = S_OK; + + // add count to action data + hr = WcaWriteIntegerToCaData(pList->iUninstallCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add count to custom action data"); + + for (MQI_MESSAGE_QUEUE* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // queues that are being uninstalled only + if (!WcaIsUninstalling(pItm->isInstalled, pItm->isAction)) + continue; + + // if we did not create the queue we should not try to delete it + if (pItm->fExists && fRollback) + { + continue; + } + + // add message queue to action data + hr = AddMessageQueueToActionData(pItm, ppwzActionData); + ExitOnFailure(hr, "Failed to add message queue to action data"); + } + + hr = S_OK; + +LExit: + return hr; +} + +void MqiMessageQueueFreeList( + MQI_MESSAGE_QUEUE_LIST* pList + ) +{ + MQI_MESSAGE_QUEUE* pItm = pList->pFirst; + while (pItm) + { + MQI_MESSAGE_QUEUE* pDelete = pItm; + pItm = pItm->pNext; + ::HeapFree(::GetProcessHeap(), 0, pDelete); + } +} + +HRESULT MqiMessageQueuePermissionRead( + MQI_MESSAGE_QUEUE_LIST* pMessageQueueList, + MQI_MESSAGE_QUEUE_PERMISSION_LIST* pList + ) +{ + HRESULT hr = S_OK; + + // read message queue user permissions + if (S_OK == WcaTableExists(L"MessageQueueUserPermission")) + { + hr = MessageQueueTrusteePermissionsRead(vcsMessageQueueUserPermissionQuery, pMessageQueueList, pList); + ExitOnFailure(hr, "Failed to read message queue user permissions"); + } + + // read message queue group permissions + if (S_OK == WcaTableExists(L"MessageQueueGroupPermission")) + { + hr = MessageQueueTrusteePermissionsRead(vcsMessageQueueGroupPermissionQuery, pMessageQueueList, pList); + ExitOnFailure(hr, "Failed to read message queue group permissions"); + } + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT MqiMessageQueuePermissionInstall( + MQI_MESSAGE_QUEUE_PERMISSION_LIST* pList, + LPWSTR* ppwzActionData + ) +{ + HRESULT hr = S_OK; + + // add count to action data + hr = WcaWriteIntegerToCaData(pList->iInstallCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add count to custom action data"); + + for (MQI_MESSAGE_QUEUE_PERMISSION* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // queue permissions that are being installed only + if (!WcaIsInstalling(pItm->isInstalled, pItm->isAction)) + continue; + + // add message queue permission to action data + hr = AddMessageQueuePermissionToActionData(pItm, ppwzActionData); + ExitOnFailure(hr, "Failed to add message queue permission to action data"); + } + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT MqiMessageQueuePermissionUninstall( + MQI_MESSAGE_QUEUE_PERMISSION_LIST* pList, + LPWSTR* ppwzActionData + ) +{ + HRESULT hr = S_OK; + + // add count to action data + hr = WcaWriteIntegerToCaData(pList->iUninstallCount, ppwzActionData); + ExitOnFailure(hr, "Failed to add count to custom action data"); + + for (MQI_MESSAGE_QUEUE_PERMISSION* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + // queue permissions that are being uninstalled only + if (!WcaIsUninstalling(pItm->isInstalled, pItm->isAction)) + continue; + + // add message queue permission to action data + hr = AddMessageQueuePermissionToActionData(pItm, ppwzActionData); + ExitOnFailure(hr, "Failed to add message queue permission to action data"); + } + + hr = S_OK; + +LExit: + return hr; +} + +void MqiMessageQueuePermissionFreeList( + MQI_MESSAGE_QUEUE_PERMISSION_LIST* pList + ) +{ + MQI_MESSAGE_QUEUE_PERMISSION* pItm = pList->pFirst; + while (pItm) + { + MQI_MESSAGE_QUEUE_PERMISSION* pDelete = pItm; + pItm = pItm->pNext; + ::HeapFree(::GetProcessHeap(), 0, pDelete); + } +} + + +// helper function definitions + +static HRESULT MqiMessageQueueFindByKey( + MQI_MESSAGE_QUEUE_LIST* pList, + LPCWSTR pwzKey, + MQI_MESSAGE_QUEUE** ppItm + ) +{ + for (MQI_MESSAGE_QUEUE* pItm = pList->pFirst; pItm; pItm = pItm->pNext) + { + if (0 == lstrcmpW(pItm->wzKey, pwzKey)) + { + *ppItm = pItm; + return S_OK; + } + } + + return S_FALSE; +} + +static HRESULT AddMessageQueueToActionData( + MQI_MESSAGE_QUEUE* pItm, + LPWSTR* ppwzActionData + ) +{ + HRESULT hr = S_OK; + + // add message queue information to custom action data + hr = WcaWriteStringToCaData(pItm->wzKey, ppwzActionData); + ExitOnFailure(hr, "Failed to add key to custom action data"); + hr = WcaWriteIntegerToCaData(pItm->iBasePriority, ppwzActionData); + ExitOnFailure(hr, "Failed to add base priority to custom action data"); + hr = WcaWriteIntegerToCaData(pItm->iJournalQuota, ppwzActionData); + ExitOnFailure(hr, "Failed to add journal quota to custom action data"); + hr = WcaWriteStringToCaData(pItm->wzLabel, ppwzActionData); + ExitOnFailure(hr, "Failed to add label to custom action data"); + hr = WcaWriteStringToCaData(pItm->wzMulticastAddress, ppwzActionData); + ExitOnFailure(hr, "Failed to add multicast address to custom action data"); + hr = WcaWriteStringToCaData(pItm->wzPathName, ppwzActionData); + ExitOnFailure(hr, "Failed to add path name to custom action data"); + hr = WcaWriteIntegerToCaData(pItm->iPrivLevel, ppwzActionData); + ExitOnFailure(hr, "Failed to add privacy level to custom action data"); + hr = WcaWriteIntegerToCaData(pItm->iQuota, ppwzActionData); + ExitOnFailure(hr, "Failed to add quota to custom action data"); + hr = WcaWriteStringToCaData(pItm->wzServiceTypeGuid, ppwzActionData); + ExitOnFailure(hr, "Failed to add service type guid to custom action data"); + hr = WcaWriteIntegerToCaData(pItm->iAttributes, ppwzActionData); + ExitOnFailure(hr, "Failed to add attributes to custom action data"); + + hr = S_OK; + +LExit: + return hr; +} + +static HRESULT MessageQueueTrusteePermissionsRead( + LPCWSTR pwzQuery, + MQI_MESSAGE_QUEUE_LIST* pMessageQueueList, + MQI_MESSAGE_QUEUE_PERMISSION_LIST* pList + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + PMSIHANDLE hView, hRec; + + LPWSTR pwzData = NULL; + + MQI_MESSAGE_QUEUE_PERMISSION* pItm = NULL; + + // loop through all application roles + hr = WcaOpenExecuteView(pwzQuery, &hView); + ExitOnFailure(hr, "Failed to execute view on table"); + + while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) + { + // create entry + pItm = (MQI_MESSAGE_QUEUE_PERMISSION*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MQI_MESSAGE_QUEUE_PERMISSION)); + if (!pItm) + ExitFunction1(hr = E_OUTOFMEMORY); + + // get key + hr = WcaGetRecordString(hRec, mqpqMessageQueuePermission, &pwzData); + ExitOnFailure(hr, "Failed to get key"); + StringCchCopyW(pItm->wzKey, countof(pItm->wzKey), pwzData); + + // get component + hr = WcaGetRecordString(hRec, mqpqComponent, &pwzData); + ExitOnFailure(hr, "Failed to get component"); + + // get component install state + er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &pItm->isInstalled, &pItm->isAction); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to get component state"); + + // get message queue + hr = WcaGetRecordString(hRec, mqpqMessageQueue, &pwzData); + ExitOnFailure(hr, "Failed to get application role"); + + hr = MqiMessageQueueFindByKey(pMessageQueueList, pwzData, &pItm->pMessageQueue); + if (S_FALSE == hr) + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + ExitOnFailure(hr, "Failed to find message queue, key: %S", pwzData); + + // get user domain + hr = WcaGetRecordFormattedString(hRec, mqpqDomain, &pwzData); + ExitOnFailure(hr, "Failed to get domain"); + StringCchCopyW(pItm->wzDomain, countof(pItm->wzDomain), pwzData); + + // get user name + hr = WcaGetRecordFormattedString(hRec, mqpqName, &pwzData); + ExitOnFailure(hr, "Failed to get name"); + StringCchCopyW(pItm->wzName, countof(pItm->wzName), pwzData); + + // get permissions + hr = WcaGetRecordInteger(hRec, mqpqPermissions, &pItm->iPermissions); + ExitOnFailure(hr, "Failed to get permissions"); + + // set references & increment counters + if (WcaIsInstalling(pItm->isInstalled, pItm->isAction)) + pList->iInstallCount++; + if (WcaIsUninstalling(pItm->isInstalled, pItm->isAction)) + pList->iUninstallCount++; + + // add entry + if (pList->pFirst) + pItm->pNext = pList->pFirst; + pList->pFirst = pItm; + pItm = NULL; + } + + if (E_NOMOREITEMS == hr) + hr = S_OK; + +LExit: + // clean up + ReleaseStr(pwzData); + + if (pItm) + ::HeapFree(::GetProcessHeap(), 0, pItm); + + return hr; +} + +static HRESULT AddMessageQueuePermissionToActionData( + MQI_MESSAGE_QUEUE_PERMISSION* pItm, + LPWSTR* ppwzActionData + ) +{ + HRESULT hr = S_OK; + + // add message queue information to custom action data + hr = WcaWriteStringToCaData(pItm->wzKey, ppwzActionData); + ExitOnFailure(hr, "Failed to add key to custom action data"); + hr = WcaWriteStringToCaData(pItm->pMessageQueue->wzPathName, ppwzActionData); + ExitOnFailure(hr, "Failed to add path name to custom action data"); + hr = WcaWriteStringToCaData(pItm->wzDomain, ppwzActionData); + ExitOnFailure(hr, "Failed to add domain to custom action data"); + hr = WcaWriteStringToCaData(pItm->wzName, ppwzActionData); + ExitOnFailure(hr, "Failed to add name to custom action data"); + hr = WcaWriteIntegerToCaData(pItm->iPermissions, ppwzActionData); + ExitOnFailure(hr, "Failed to add permissions to custom action data"); + + hr = S_OK; + +LExit: + return hr; +} diff --git a/src/ca/mqqueuesched.h b/src/ca/mqqueuesched.h new file mode 100644 index 00000000..b063ca28 --- /dev/null +++ b/src/ca/mqqueuesched.h @@ -0,0 +1,92 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +struct MQI_MESSAGE_QUEUE +{ + WCHAR wzKey[MAX_DARWIN_KEY + 1]; + int iBasePriority; + int iJournalQuota; + WCHAR wzLabel[MAX_DARWIN_COLUMN + 1]; + WCHAR wzMulticastAddress[MAX_DARWIN_COLUMN + 1]; + WCHAR wzPathName[MAX_DARWIN_COLUMN + 1]; + int iPrivLevel; + int iQuota; + WCHAR wzServiceTypeGuid[MAX_DARWIN_COLUMN + 1]; + int iAttributes; + + INSTALLSTATE isInstalled, isAction; + BOOL fExists; + + MQI_MESSAGE_QUEUE* pNext; +}; + +struct MQI_MESSAGE_QUEUE_LIST +{ + MQI_MESSAGE_QUEUE* pFirst; + + int iInstallCount; + int iUninstallCount; +}; + +struct MQI_MESSAGE_QUEUE_PERMISSION +{ + WCHAR wzKey[MAX_DARWIN_KEY + 1]; + WCHAR wzDomain[MAX_DARWIN_COLUMN + 1]; + WCHAR wzName[MAX_DARWIN_COLUMN + 1]; + int iPermissions; + + MQI_MESSAGE_QUEUE* pMessageQueue; + + INSTALLSTATE isInstalled, isAction; + + MQI_MESSAGE_QUEUE_PERMISSION* pNext; +}; + +struct MQI_MESSAGE_QUEUE_PERMISSION_LIST +{ + MQI_MESSAGE_QUEUE_PERMISSION* pFirst; + + int iInstallCount; + int iUninstallCount; +}; + + +// function prototypes + +HRESULT MqiInitialize(); +void MqiUninitialize(); +HRESULT MqiMessageQueueRead( + MQI_MESSAGE_QUEUE_LIST* pList + ); +HRESULT MqiMessageQueueVerify( + MQI_MESSAGE_QUEUE_LIST* pList + ); +HRESULT MqiMessageQueueInstall( + MQI_MESSAGE_QUEUE_LIST* pList, + BOOL fRollback, + LPWSTR* ppwzActionData + ); +HRESULT MqiMessageQueueUninstall( + MQI_MESSAGE_QUEUE_LIST* pList, + BOOL fRollback, + LPWSTR* ppwzActionData + ); +void MqiMessageQueueFreeList( + MQI_MESSAGE_QUEUE_LIST* pList + ); +HRESULT MqiMessageQueuePermissionRead( + MQI_MESSAGE_QUEUE_LIST* pMessageQueueList, + MQI_MESSAGE_QUEUE_PERMISSION_LIST* pList + ); +HRESULT MqiMessageQueuePermissionInstall( + MQI_MESSAGE_QUEUE_PERMISSION_LIST* pList, + LPWSTR* ppwzActionData + ); +HRESULT MqiMessageQueuePermissionUninstall( + MQI_MESSAGE_QUEUE_PERMISSION_LIST* pList, + LPWSTR* ppwzActionData + ); +void MqiMessageQueuePermissionFreeList( + MQI_MESSAGE_QUEUE_PERMISSION_LIST* pList + ); diff --git a/src/ca/mqsched.cpp b/src/ca/mqsched.cpp new file mode 100644 index 00000000..cefce853 --- /dev/null +++ b/src/ca/mqsched.cpp @@ -0,0 +1,219 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +/******************************************************************** + DllMain - standard entry point for all WiX CustomActions + +********************************************************************/ +extern "C" BOOL WINAPI DllMain( + IN HINSTANCE hInst, + IN ULONG ulReason, + IN LPVOID) +{ + switch(ulReason) + { + case DLL_PROCESS_ATTACH: + WcaGlobalInitialize(hInst); + break; + + case DLL_PROCESS_DETACH: + WcaGlobalFinalize(); + break; + } + + return TRUE; +} + +/******************************************************************** + MessageQueuingInstall - CUSTOM ACTION ENTRY POINT for installing MSMQ message queues + +********************************************************************/ +extern "C" UINT __stdcall MessageQueuingInstall(MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + MQI_MESSAGE_QUEUE_LIST lstMessageQueues; + MQI_MESSAGE_QUEUE_PERMISSION_LIST lstMessageQueuePermissions; + + int iCost = 0; + LPWSTR pwzRollbackActionData = NULL; + LPWSTR pwzExecuteActionData = NULL; + + ::ZeroMemory(&lstMessageQueues, sizeof(lstMessageQueues)); + ::ZeroMemory(&lstMessageQueuePermissions, sizeof(lstMessageQueuePermissions)); + + // initialize + hr = WcaInitialize(hInstall, "MessageQueuingInstall"); + ExitOnFailure(hr, "Failed to initialize"); + + do + { + hr = MqiInitialize(); + if (S_FALSE == hr) + { + WcaLog(LOGMSG_STANDARD, "Failed to load mqrt.dll."); + er = WcaErrorMessage(msierrMsmqCannotConnect, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0); + switch (er) + { + case IDABORT: + ExitFunction1(hr = E_FAIL); // bail with error + case IDRETRY: + break; // retry + case IDIGNORE: __fallthrough; + default: + ExitFunction1(hr = S_OK); // pretend everything is okay and bail + } + } + ExitOnFailure(hr, "Failed to initialize MSMQ."); + } while (S_FALSE == hr); + + // read message queues + hr = MqiMessageQueueRead(&lstMessageQueues); + ExitOnFailure(hr, "Failed to read MessageQueue table"); + + // read message queue permissions + hr = MqiMessageQueuePermissionRead(&lstMessageQueues, &lstMessageQueuePermissions); + ExitOnFailure(hr, "Failed to read message queue permissions"); + + // verify message queue elementes + hr = MqiMessageQueueVerify(&lstMessageQueues); + ExitOnFailure(hr, "Failed to verify message queue elements."); + + if (lstMessageQueues.iInstallCount || lstMessageQueuePermissions.iInstallCount) + { + // schedule rollback action + hr = MqiMessageQueuePermissionInstall(&lstMessageQueuePermissions, &pwzRollbackActionData); + ExitOnFailure(hr, "Failed to add message queue permissions to rollback action data"); + + hr = MqiMessageQueueInstall(&lstMessageQueues, TRUE, &pwzRollbackActionData); + ExitOnFailure(hr, "Failed to add message queues to rollback action data"); + + hr = WcaDoDeferredAction(L"MessageQueuingRollbackInstall", pwzRollbackActionData, 0); + ExitOnFailure(hr, "Failed to schedule MessageQueuingRollbackInstall"); + + // schedule execute action + hr = MqiMessageQueueInstall(&lstMessageQueues, FALSE, &pwzExecuteActionData); + ExitOnFailure(hr, "Failed to add message queues to execute action data"); + iCost += lstMessageQueues.iInstallCount * COST_MESSAGE_QUEUE_CREATE; + + hr = MqiMessageQueuePermissionInstall(&lstMessageQueuePermissions, &pwzExecuteActionData); + ExitOnFailure(hr, "Failed to add message queue permissions to execute action data"); + iCost += lstMessageQueues.iInstallCount * COST_MESSAGE_QUEUE_PERMISSION_ADD; + + hr = WcaDoDeferredAction(L"MessageQueuingExecuteInstall", pwzExecuteActionData, iCost); + ExitOnFailure(hr, "Failed to schedule MessageQueuingExecuteInstall"); + } + + hr = S_OK; + +LExit: + // clean up + MqiMessageQueueFreeList(&lstMessageQueues); + MqiMessageQueuePermissionFreeList(&lstMessageQueuePermissions); + + ReleaseStr(pwzRollbackActionData); + ReleaseStr(pwzExecuteActionData); + + // uninitialize + MqiUninitialize(); + + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + + +/******************************************************************** + MessageQueuingUninstall - CUSTOM ACTION ENTRY POINT for uninstalling MSMQ message queues + +********************************************************************/ +extern "C" UINT __stdcall MessageQueuingUninstall(MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + MQI_MESSAGE_QUEUE_LIST lstMessageQueues; + MQI_MESSAGE_QUEUE_PERMISSION_LIST lstMessageQueuePermissions; + + int iCost = 0; + LPWSTR pwzRollbackActionData = NULL; + LPWSTR pwzExecuteActionData = NULL; + + ::ZeroMemory(&lstMessageQueues, sizeof(lstMessageQueues)); + ::ZeroMemory(&lstMessageQueuePermissions, sizeof(lstMessageQueuePermissions)); + + // initialize + hr = WcaInitialize(hInstall, "MessageQueuingUninstall"); + ExitOnFailure(hr, "Failed to initialize"); + + do + { + hr = MqiInitialize(); + if (S_FALSE == hr) + { + WcaLog(LOGMSG_STANDARD, "Failed to load mqrt.dll."); + er = WcaErrorMessage(msierrMsmqCannotConnect, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0); + switch (er) + { + case IDABORT: + ExitFunction1(hr = E_FAIL); // bail with error + case IDRETRY: + break; // retry + case IDIGNORE: __fallthrough; + default: + ExitFunction1(hr = S_OK); // pretend everything is okay and bail + } + } + ExitOnFailure(hr, "Failed to initialize MSMQ."); + } while (S_FALSE == hr); + + // read message queues + hr = MqiMessageQueueRead(&lstMessageQueues); + ExitOnFailure(hr, "Failed to read MessageQueue table"); + + // read message queue permissions + hr = MqiMessageQueuePermissionRead(&lstMessageQueues, &lstMessageQueuePermissions); + ExitOnFailure(hr, "Failed to read message queue permissions"); + + if (lstMessageQueues.iUninstallCount || lstMessageQueuePermissions.iUninstallCount) + { + // schedule rollback action + hr = MqiMessageQueueUninstall(&lstMessageQueues, TRUE, &pwzRollbackActionData); + ExitOnFailure(hr, "Failed to add message queues to rollback action data"); + + hr = MqiMessageQueuePermissionUninstall(&lstMessageQueuePermissions, &pwzRollbackActionData); + ExitOnFailure(hr, "Failed to add message queue permissions to rollback action data"); + + hr = WcaDoDeferredAction(L"MessageQueuingRollbackUninstall", pwzRollbackActionData, 0); + ExitOnFailure(hr, "Failed to schedule MessageQueuingRollbackUninstall"); + + // schedule execute action + hr = MqiMessageQueuePermissionUninstall(&lstMessageQueuePermissions, &pwzExecuteActionData); + ExitOnFailure(hr, "Failed to add message queue permissions to execute action data"); + + hr = MqiMessageQueueUninstall(&lstMessageQueues, FALSE, &pwzExecuteActionData); + ExitOnFailure(hr, "Failed to add message queues to execute action data"); + iCost += lstMessageQueues.iUninstallCount * COST_MESSAGE_QUEUE_DELETE; + + hr = WcaDoDeferredAction(L"MessageQueuingExecuteUninstall", pwzExecuteActionData, iCost); + ExitOnFailure(hr, "Failed to schedule MessageQueuingExecuteUninstall"); + } + + hr = S_OK; + +LExit: + // clean up + MqiMessageQueueFreeList(&lstMessageQueues); + MqiMessageQueuePermissionFreeList(&lstMessageQueuePermissions); + + ReleaseStr(pwzRollbackActionData); + ReleaseStr(pwzExecuteActionData); + + // uninitialize + MqiUninitialize(); + + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} diff --git a/src/ca/mqutilexec.cpp b/src/ca/mqutilexec.cpp new file mode 100644 index 00000000..a9c56e02 --- /dev/null +++ b/src/ca/mqutilexec.cpp @@ -0,0 +1,380 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// private structs + +struct PCA_WELLKNOWN_SID +{ + LPCWSTR pwzName; + SID_IDENTIFIER_AUTHORITY iaIdentifierAuthority; + BYTE nSubAuthorityCount; + DWORD dwSubAuthority[8]; +}; + + +// well known SIDs + +PCA_WELLKNOWN_SID wsWellKnownSids[] = { + {L"\\Everyone", SECURITY_WORLD_SID_AUTHORITY, 1, {SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0}}, + {L"\\Administrators", SECURITY_NT_AUTHORITY, 2, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0}}, + {L"\\LocalSystem", SECURITY_NT_AUTHORITY, 1, {SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0}}, + {L"\\LocalService", SECURITY_NT_AUTHORITY, 1, {SECURITY_LOCAL_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0}}, + {L"\\NetworkService", SECURITY_NT_AUTHORITY, 1, {SECURITY_NETWORK_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0}}, + {L"\\AuthenticatedUser", SECURITY_NT_AUTHORITY, 1, {SECURITY_AUTHENTICATED_USER_RID, 0, 0, 0, 0, 0, 0, 0}}, + {L"\\Guests", SECURITY_NT_AUTHORITY, 2, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS, 0, 0, 0, 0, 0, 0}}, + {L"\\Users", SECURITY_NT_AUTHORITY, 2, {SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_USERS, 0, 0, 0, 0, 0, 0}}, + {L"\\CREATOR OWNER", SECURITY_NT_AUTHORITY, 1, {SECURITY_CREATOR_OWNER_RID, 0, 0, 0, 0, 0, 0, 0}}, + {NULL, SECURITY_NULL_SID_AUTHORITY, 0, {0, 0, 0, 0, 0, 0, 0, 0}} +}; + + +// prototypes for private helper functions + +static HRESULT CreateSidFromDomainRidPair( + PSID pDomainSid, + DWORD dwRid, + PSID* ppSid + ); +static HRESULT InitLsaUnicodeString( + PLSA_UNICODE_STRING plusStr, + LPCWSTR pwzStr, + DWORD dwLen + ); +static void FreeLsaUnicodeString( + PLSA_UNICODE_STRING plusStr + ); + + +// function definitions + +HRESULT PcaActionDataMessage( + DWORD cArgs, + ... + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + + PMSIHANDLE hRec; + va_list args; + + // record + hRec = ::MsiCreateRecord(cArgs); + ExitOnNull(hRec, hr, E_OUTOFMEMORY, "Failed to create record"); + + va_start(args, cArgs); + for (DWORD i = 1; i <= cArgs; i++) + { + LPCWSTR pwzArg = va_arg(args, WCHAR*); + if (pwzArg && *pwzArg) + { + er = ::MsiRecordSetStringW(hRec, i, pwzArg); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to set record field string"); + } + } + va_end(args); + + // message + er = WcaProcessMessage(INSTALLMESSAGE_ACTIONDATA, hRec); + if (0 == er || IDOK == er || IDYES == er) + { + hr = S_OK; + } + else if (ERROR_INSTALL_USEREXIT == er || IDABORT == er || IDCANCEL == er) + { + WcaSetReturnValue(ERROR_INSTALL_USEREXIT); // note that the user said exit + hr = S_FALSE; + } + else + hr = E_UNEXPECTED; + +LExit: + return hr; +} + +HRESULT PcaAccountNameToSid( + LPCWSTR pwzAccountName, + PSID* ppSid + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + NTSTATUS st = 0; + + PSID pSid = NULL; + LSA_OBJECT_ATTRIBUTES loaAttributes; + LSA_HANDLE lsahPolicy = NULL; + LSA_UNICODE_STRING lusName; + PLSA_REFERENCED_DOMAIN_LIST plrdsDomains = NULL; + PLSA_TRANSLATED_SID pltsSid = NULL; + + ::ZeroMemory(&loaAttributes, sizeof(loaAttributes)); + ::ZeroMemory(&lusName, sizeof(lusName)); + + // identify well known SIDs + for (PCA_WELLKNOWN_SID* pWS = wsWellKnownSids; pWS->pwzName; pWS++) + { + if (0 == lstrcmpiW(pwzAccountName, pWS->pwzName)) + { + // allocate SID buffer + pSid = (PSID)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, ::GetSidLengthRequired(pWS->nSubAuthorityCount)); + ExitOnNull(pSid, hr, E_OUTOFMEMORY, "Failed to allocate buffer for SID"); + + // initialize SID + ::InitializeSid(pSid, &pWS->iaIdentifierAuthority, pWS->nSubAuthorityCount); + + // copy sub autorities + for (DWORD i = 0; i < pWS->nSubAuthorityCount; i++) + *::GetSidSubAuthority(pSid, i) = pWS->dwSubAuthority[i]; + + break; + } + } + + // lookup name + if (!pSid) + { + // open policy handle + st = ::LsaOpenPolicy(NULL, &loaAttributes, POLICY_ALL_ACCESS, &lsahPolicy); + er = ::LsaNtStatusToWinError(st); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to open policy handle"); + + // create account name lsa unicode string + hr = InitLsaUnicodeString(&lusName, pwzAccountName, wcslen(pwzAccountName)); + ExitOnFailure(hr, "Failed to initialize account name string"); + + // lookup name + st = ::LsaLookupNames(lsahPolicy, 1, &lusName, &plrdsDomains, &pltsSid); + er = ::LsaNtStatusToWinError(st); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to lookup account names"); + + if (SidTypeDomain == pltsSid->Use) + ExitOnFailure(hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), "Domain SIDs not supported"); + + // convert sid + hr = CreateSidFromDomainRidPair(plrdsDomains->Domains[pltsSid->DomainIndex].Sid, pltsSid->RelativeId, &pSid); + ExitOnFailure(hr, "Failed to convert SID"); + } + + *ppSid = pSid; + pSid = NULL; + + hr = S_OK; + +LExit: + // clean up + if (pSid) + ::HeapFree(::GetProcessHeap(), 0, pSid); + if (lsahPolicy) + ::LsaClose(lsahPolicy); + if (plrdsDomains) + ::LsaFreeMemory(plrdsDomains); + if (pltsSid) + ::LsaFreeMemory(pltsSid); + FreeLsaUnicodeString(&lusName); + + return hr; +} + +HRESULT PcaSidToAccountName( + PSID pSid, + LPWSTR* ppwzAccountName + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + NTSTATUS st = 0; + + LSA_OBJECT_ATTRIBUTES loaAttributes; + LSA_HANDLE lsahPolicy = NULL; + PLSA_REFERENCED_DOMAIN_LIST plrdsDomains = NULL; + PLSA_TRANSLATED_NAME pltnName = NULL; + + LPWSTR pwzDomain = NULL; + LPWSTR pwzName = NULL; + + ::ZeroMemory(&loaAttributes, sizeof(loaAttributes)); + + // open policy handle + st = ::LsaOpenPolicy(NULL, &loaAttributes, POLICY_ALL_ACCESS, &lsahPolicy); + er = ::LsaNtStatusToWinError(st); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to open policy handle"); + + // lookup SID + st = ::LsaLookupSids(lsahPolicy, 1, &pSid, &plrdsDomains, &pltnName); + er = ::LsaNtStatusToWinError(st); + ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to lookup SID"); + + if (SidTypeDomain == pltnName->Use) + ExitOnFailure(hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), "Domain SIDs not supported"); + + // format account name string + if (SidTypeWellKnownGroup != pltnName->Use) + { + PLSA_UNICODE_STRING plusDomain = &plrdsDomains->Domains[pltnName->DomainIndex].Name; + hr = StrAllocString(&pwzDomain, plusDomain->Buffer, plusDomain->Length / sizeof(WCHAR)); + ExitOnFailure(hr, "Failed to allocate name string"); + } + + hr = StrAllocString(&pwzName, pltnName->Name.Buffer, pltnName->Name.Length / sizeof(WCHAR)); + ExitOnFailure(hr, "Failed to allocate domain string"); + + hr = StrAllocFormatted(ppwzAccountName, L"%s\\%s", pwzDomain ? pwzDomain : L"", pwzName); + ExitOnFailure(hr, "Failed to format account name string"); + + hr = S_OK; + +LExit: + // clean up + if (lsahPolicy) + ::LsaClose(lsahPolicy); + if (plrdsDomains) + ::LsaFreeMemory(plrdsDomains); + if (pltnName) + ::LsaFreeMemory(pltnName); + + ReleaseStr(pwzDomain); + ReleaseStr(pwzName); + + return hr; +} + +HRESULT PcaBuildAccountName( + LPCWSTR pwzDomain, + LPCWSTR pwzName, + LPWSTR* ppwzAccount + ) +{ + HRESULT hr = S_OK; + + WCHAR wzComputerName[MAX_COMPUTERNAME_LENGTH + 1]; + ::ZeroMemory(wzComputerName, sizeof(wzComputerName)); + + // if domain is '.', get computer name + if (0 == lstrcmpW(pwzDomain, L".")) + { + DWORD dwSize = countof(wzComputerName); + if (!::GetComputerNameW(wzComputerName, &dwSize)) + ExitOnFailure(hr = HRESULT_FROM_WIN32(::GetLastError()), "Failed to get computer name"); + } + + // build account name + hr = StrAllocFormatted(ppwzAccount, L"%s\\%s", *wzComputerName ? wzComputerName : pwzDomain, pwzName); + ExitOnFailure(hr, "Failed to build domain user name"); + + hr = S_OK; + +LExit: + return hr; +} + +HRESULT PcaGuidFromString( + LPCWSTR pwzGuid, + LPGUID pGuid + ) +{ + HRESULT hr = S_OK; + + int cch = 0; + + WCHAR wz[39]; + ::ZeroMemory(wz, sizeof(wz)); + + cch = lstrlenW(pwzGuid); + + if (38 == cch && L'{' == pwzGuid[0] && L'}' == pwzGuid[37]) + StringCchCopyW(wz, countof(wz), pwzGuid); + else if (36 == cch) + StringCchPrintfW(wz, countof(wz), L"{%s}", pwzGuid); + else + ExitFunction1(hr = E_INVALIDARG); + + hr = ::CLSIDFromString(wz, pGuid); + +LExit: + return hr; +} + + +// helper function definitions + +static HRESULT CreateSidFromDomainRidPair( + PSID pDomainSid, + DWORD dwRid, + PSID* ppSid + ) +{ + HRESULT hr = S_OK; + + PSID pSid = NULL; + + // get domain SID sub authority count + UCHAR ucSubAuthorityCount = *::GetSidSubAuthorityCount(pDomainSid); + + // allocate SID buffer + DWORD dwLengthRequired = ::GetSidLengthRequired(ucSubAuthorityCount + (UCHAR)1); + if (*ppSid) + { + SIZE_T ccb = ::HeapSize(::GetProcessHeap(), 0, *ppSid); + if (-1 == ccb) + ExitOnFailure(hr = E_FAIL, "Failed to get size of SID buffer"); + + if (ccb < dwLengthRequired) + { + pSid = (PSID)::HeapReAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, *ppSid, dwLengthRequired); + ExitOnNull(pSid, hr, E_OUTOFMEMORY, "Failed to reallocate buffer for SID, len: %d", dwLengthRequired); + *ppSid = pSid; + } + } + else + { + *ppSid = (PSID)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, dwLengthRequired); + ExitOnNull(*ppSid, hr, E_OUTOFMEMORY, "Failed to allocate buffer for SID, len: %d", dwLengthRequired); + } + + ::InitializeSid(*ppSid, ::GetSidIdentifierAuthority(pDomainSid), ucSubAuthorityCount + (UCHAR)1); + + // copy sub autorities + DWORD i = 0; + for (; i < ucSubAuthorityCount; i++) + *::GetSidSubAuthority(*ppSid, i) = *::GetSidSubAuthority(pDomainSid, i); + *::GetSidSubAuthority(*ppSid, i) = dwRid; + + hr = S_OK; + +LExit: + return hr; +} + +static HRESULT InitLsaUnicodeString( + PLSA_UNICODE_STRING plusStr, + LPCWSTR pwzStr, + DWORD dwLen + ) +{ + HRESULT hr = S_OK; + + plusStr->Length = (USHORT)dwLen * sizeof(WCHAR); + plusStr->MaximumLength = (USHORT)(dwLen + 1) * sizeof(WCHAR); + + plusStr->Buffer = (WCHAR*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WCHAR) * (dwLen + 1)); + ExitOnNull(plusStr->Buffer, hr, E_OUTOFMEMORY, "Failed to allocate account name string"); + + hr = StringCchCopyW(plusStr->Buffer, dwLen + 1, pwzStr); + ExitOnFailure(hr, "Failed to copy buffer"); + + hr = S_OK; + +LExit: + return hr; +} + +static void FreeLsaUnicodeString( + PLSA_UNICODE_STRING plusStr + ) +{ + if (plusStr->Buffer) + ::HeapFree(::GetProcessHeap(), 0, plusStr->Buffer); +} diff --git a/src/ca/mqutilexec.h b/src/ca/mqutilexec.h new file mode 100644 index 00000000..d3dc17a1 --- /dev/null +++ b/src/ca/mqutilexec.h @@ -0,0 +1,23 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +HRESULT PcaActionDataMessage( + DWORD cArgs, + ... + ); +HRESULT PcaAccountNameToSid( + LPCWSTR pwzAccountName, + PSID* ppSid + ); +HRESULT PcaSidToAccountName( + PSID pSid, + LPWSTR* ppwzAccountName + ); +HRESULT PcaBuildAccountName( + LPCWSTR pwzDomain, + LPCWSTR pwzName, + LPWSTR* ppwzAccount + ); +HRESULT PcaGuidFromString( + LPCWSTR pwzGuid, + GUID* pGuid + ); diff --git a/src/ca/mqutilsched.cpp b/src/ca/mqutilsched.cpp new file mode 100644 index 00000000..4353a6d6 --- /dev/null +++ b/src/ca/mqutilsched.cpp @@ -0,0 +1,43 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// function definitions + +HRESULT PcaGuidToRegFormat( + LPWSTR pwzGuid, + LPWSTR pwzDest, + SIZE_T cchDest + ) +{ + HRESULT hr = S_OK; + + GUID guid = GUID_NULL; + int cch = 0; + + WCHAR wz[39]; + ::ZeroMemory(wz, sizeof(wz)); + + cch = lstrlenW(pwzGuid); + + if (38 == cch && L'{' == pwzGuid[0] && L'}' == pwzGuid[37]) + StringCchCopyW(wz, countof(wz), pwzGuid); + else if (36 == cch) + StringCchPrintfW(wz, countof(wz), L"{%s}", pwzGuid); + else + ExitFunction1(hr = E_INVALIDARG); + + // convert string to guid + hr = ::CLSIDFromString(wz, &guid); + ExitOnFailure(hr, "Failed to parse guid string"); + + // convert guid to string + if (0 == ::StringFromGUID2(guid, pwzDest, cchDest)) + ExitOnFailure(hr = E_FAIL, "Failed to convert guid to string"); + + hr = S_OK; + +LExit: + return hr; +} diff --git a/src/ca/mqutilsched.h b/src/ca/mqutilsched.h new file mode 100644 index 00000000..e172257d --- /dev/null +++ b/src/ca/mqutilsched.h @@ -0,0 +1,9 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +HRESULT PcaGuidToRegFormat( + LPWSTR pwzGuid, + LPWSTR pwzDest, + SIZE_T cchDest + ); diff --git a/src/wixext/MsmqCompiler.cs b/src/wixext/MsmqCompiler.cs new file mode 100644 index 00000000..b40e2dc1 --- /dev/null +++ b/src/wixext/MsmqCompiler.cs @@ -0,0 +1,536 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolset.Extensions +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Globalization; + using System.Reflection; + using System.Xml; + using System.Xml.Linq; + using System.Xml.Schema; + using WixToolset.Data; + using WixToolset.Extensibility; + + /// + /// The compiler for the WiX Toolset Internet Information Services Extension. + /// + public sealed class MsmqCompiler : CompilerExtension + { + /// + /// Instantiate a new MsmqCompiler. + /// + public MsmqCompiler() + { + this.Namespace = "http://wixtoolset.org/schemas/v4/wxs/msmq"; + } + + /// + /// + /// + public enum MqiMessageQueueAttributes + { + Authenticate = (1 << 0), + Journal = (1 << 1), + Transactional = (1 << 2) + } + + /// + /// + /// + public enum MqiMessageQueuePrivacyLevel + { + None = 0, + Optional = 1, + Body = 2 + } + + /// + /// + /// + public enum MqiMessageQueuePermission + { + DeleteMessage = (1 << 0), + PeekMessage = (1 << 1), + WriteMessage = (1 << 2), + DeleteJournalMessage = (1 << 3), + SetQueueProperties = (1 << 4), + GetQueueProperties = (1 << 5), + DeleteQueue = (1 << 6), + GetQueuePermissions = (1 << 7), + ChangeQueuePermissions = (1 << 8), + TakeQueueOwnership = (1 << 9), + ReceiveMessage = (1 << 10), + ReceiveJournalMessage = (1 << 11), + QueueGenericRead = (1 << 12), + QueueGenericWrite = (1 << 13), + QueueGenericExecute = (1 << 14), + QueueGenericAll = (1 << 15) + } + + /// + /// Processes an element for the Compiler. + /// + /// Parent element of element to process. + /// Element to process. + /// Extra information about the context in which this element is being parsed. + public override void ParseElement(XElement parentElement, XElement element, IDictionary context) + { + switch (parentElement.Name.LocalName) + { + case "Component": + string componentId = context["ComponentId"]; + string directoryId = context["DirectoryId"]; + + switch (element.Name.LocalName) + { + case "MessageQueue": + this.ParseMessageQueueElement(element, componentId); + break; + case "MessageQueuePermission": + this.ParseMessageQueuePermissionElement(element, componentId, null); + break; + default: + this.Core.UnexpectedElement(parentElement, element); + break; + } + break; + default: + this.Core.UnexpectedElement(parentElement, element); + break; + } + } + + /// + /// Parses an MSMQ message queue element. + /// + /// Element to parse. + /// Identifier of parent component. + private void ParseMessageQueueElement(XElement node, string componentId) + { + SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + + string id = null; + int basePriority = CompilerConstants.IntegerNotSet; + int journalQuota = CompilerConstants.IntegerNotSet; + string label = null; + string multicastAddress = null; + string pathName = null; + int privLevel = CompilerConstants.IntegerNotSet; + int quota = CompilerConstants.IntegerNotSet; + string serviceTypeGuid = null; + int attributes = 0; + + foreach (XAttribute attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + break; + case "Authenticate": + if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + attributes |= (int)MqiMessageQueueAttributes.Authenticate; + } + else + { + attributes &= ~(int)MqiMessageQueueAttributes.Authenticate; + } + break; + case "BasePriority": + basePriority = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue); + break; + case "Journal": + if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + attributes |= (int)MqiMessageQueueAttributes.Journal; + } + else + { + attributes &= ~(int)MqiMessageQueueAttributes.Journal; + } + break; + case "JournalQuota": + journalQuota = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, int.MaxValue); + break; + case "Label": + label = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "MulticastAddress": + multicastAddress = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "PathName": + pathName = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "PrivLevel": + string privLevelAttr = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + switch (privLevelAttr) + { + case "none": + privLevel = (int)MqiMessageQueuePrivacyLevel.None; + break; + case "optional": + privLevel = (int)MqiMessageQueuePrivacyLevel.Optional; + break; + case "body": + privLevel = (int)MqiMessageQueuePrivacyLevel.Body; + break; + default: + this.Core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, "MessageQueue", "PrivLevel", privLevelAttr, "none", "body", "optional")); + break; + } + break; + case "Quota": + quota = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, int.MaxValue); + break; + case "Transactional": + if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + attributes |= (int)MqiMessageQueueAttributes.Transactional; + } + else + { + attributes &= ~(int)MqiMessageQueueAttributes.Transactional; + } + break; + case "ServiceTypeGuid": + serviceTypeGuid = TryFormatGuidValue(this.Core.GetAttributeValue(sourceLineNumbers, attrib)); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + foreach (XElement child in node.Elements()) + { + if (this.Namespace == child.Name.Namespace) + { + switch (child.Name.LocalName) + { + case "MessageQueuePermission": + this.ParseMessageQueuePermissionElement(child, componentId, id); + break; + default: + this.Core.UnexpectedElement(node, child); + break; + } + } + else + { + this.Core.ParseExtensionElement(node, child); + } + } + + Row row = this.Core.CreateRow(sourceLineNumbers, "MessageQueue"); + row[0] = id; + row[1] = componentId; + if (CompilerConstants.IntegerNotSet != basePriority) + { + row[2] = basePriority; + } + if (CompilerConstants.IntegerNotSet != journalQuota) + { + row[3] = journalQuota; + } + row[4] = label; + row[5] = multicastAddress; + row[6] = pathName; + if (CompilerConstants.IntegerNotSet != privLevel) + { + row[7] = privLevel; + } + if (CompilerConstants.IntegerNotSet != quota) + { + row[8] = quota; + } + row[9] = serviceTypeGuid; + row[10] = attributes; + + this.Core.CreateSimpleReference(sourceLineNumbers, "CustomAction", "MessageQueuingInstall"); + this.Core.CreateSimpleReference(sourceLineNumbers, "CustomAction", "MessageQueuingUninstall"); + } + + /// + /// Parses an MSMQ message queue permission element. + /// + /// Element to parse. + /// Identifier of parent component. + /// Optional identifier of parent message queue. + private void ParseMessageQueuePermissionElement(XElement node, string componentId, string messageQueueId) + { + SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + + string id = null; + string user = null; + string group = null; + int permissions = 0; + + foreach (XAttribute attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + break; + case "MessageQueue": + if (null != messageQueueId) + { + this.Core.OnMessage(WixErrors.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, node.Parent.Name.LocalName)); + } + messageQueueId = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + this.Core.CreateSimpleReference(sourceLineNumbers, "MessageQueue", messageQueueId); + break; + case "User": + if (null != group) + { + this.Core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "User", "Group")); + } + user = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + this.Core.CreateSimpleReference(sourceLineNumbers, "User", user); + break; + case "Group": + if (null != user) + { + this.Core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Group", "User")); + } + group = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + this.Core.CreateSimpleReference(sourceLineNumbers, "Group", group); + break; + case "DeleteMessage": + if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + permissions |= (int)MqiMessageQueuePermission.DeleteMessage; + } + else + { + permissions &= ~(int)MqiMessageQueuePermission.DeleteMessage; + } + break; + case "PeekMessage": + if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + permissions |= (int)MqiMessageQueuePermission.PeekMessage; + } + else + { + permissions &= ~(int)MqiMessageQueuePermission.PeekMessage; + } + break; + case "WriteMessage": + if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + permissions |= (int)MqiMessageQueuePermission.WriteMessage; + } + else + { + permissions &= ~(int)MqiMessageQueuePermission.WriteMessage; + } + break; + case "DeleteJournalMessage": + if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + permissions |= (int)MqiMessageQueuePermission.DeleteJournalMessage; + } + else + { + permissions &= ~(int)MqiMessageQueuePermission.DeleteJournalMessage; + } + break; + case "SetQueueProperties": + if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + permissions |= (int)MqiMessageQueuePermission.SetQueueProperties; + } + else + { + permissions &= ~(int)MqiMessageQueuePermission.SetQueueProperties; + } + break; + case "GetQueueProperties": + if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + permissions |= (int)MqiMessageQueuePermission.GetQueueProperties; + } + else + { + permissions &= ~(int)MqiMessageQueuePermission.GetQueueProperties; + } + break; + case "DeleteQueue": + if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + permissions |= (int)MqiMessageQueuePermission.DeleteQueue; + } + else + { + permissions &= ~(int)MqiMessageQueuePermission.DeleteQueue; + } + break; + case "GetQueuePermissions": + if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + permissions |= (int)MqiMessageQueuePermission.GetQueuePermissions; + } + else + { + permissions &= ~(int)MqiMessageQueuePermission.GetQueuePermissions; + } + break; + case "ChangeQueuePermissions": + if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + permissions |= (int)MqiMessageQueuePermission.ChangeQueuePermissions; + } + else + { + permissions &= ~(int)MqiMessageQueuePermission.ChangeQueuePermissions; + } + break; + case "TakeQueueOwnership": + if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + permissions |= (int)MqiMessageQueuePermission.TakeQueueOwnership; + } + else + { + permissions &= ~(int)MqiMessageQueuePermission.TakeQueueOwnership; + } + break; + case "ReceiveMessage": + if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + permissions |= (int)MqiMessageQueuePermission.ReceiveMessage; + } + else + { + permissions &= ~(int)MqiMessageQueuePermission.ReceiveMessage; + } + break; + case "ReceiveJournalMessage": + if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + permissions |= (int)MqiMessageQueuePermission.ReceiveJournalMessage; + } + else + { + permissions &= ~(int)MqiMessageQueuePermission.ReceiveJournalMessage; + } + break; + case "QueueGenericRead": + if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + permissions |= (int)MqiMessageQueuePermission.QueueGenericRead; + } + else + { + permissions &= ~(int)MqiMessageQueuePermission.QueueGenericRead; + } + break; + case "QueueGenericWrite": + if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + permissions |= (int)MqiMessageQueuePermission.QueueGenericWrite; + } + else + { + permissions &= ~(int)MqiMessageQueuePermission.QueueGenericWrite; + } + break; + case "QueueGenericExecute": + if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + permissions |= (int)MqiMessageQueuePermission.QueueGenericExecute; + } + else + { + permissions &= ~(int)MqiMessageQueuePermission.QueueGenericExecute; + } + break; + case "QueueGenericAll": + if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + permissions |= (int)MqiMessageQueuePermission.QueueGenericAll; + } + else + { + permissions &= ~(int)MqiMessageQueuePermission.QueueGenericAll; + } + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + if (null == messageQueueId) + { + this.Core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "MessageQueue")); + } + if (null == user && null == group) + { + this.Core.OnMessage(WixErrors.ExpectedAttributes(sourceLineNumbers, node.Name.LocalName, "User", "Group")); + } + + this.Core.ParseForExtensionElements(node); + + if (null != user) + { + Row row = this.Core.CreateRow(sourceLineNumbers, "MessageQueueUserPermission"); + row[0] = id; + row[1] = componentId; + row[2] = messageQueueId; + row[3] = user; + row[4] = permissions; + } + if (null != group) + { + Row row = this.Core.CreateRow(sourceLineNumbers, "MessageQueueGroupPermission"); + row[0] = id; + row[1] = componentId; + row[2] = messageQueueId; + row[3] = group; + row[4] = permissions; + } + } + + /// + /// Attempts to parse the input value as a GUID, and in case the value is a valid + /// GUID returnes it in the format "{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}". + /// + /// + /// + string TryFormatGuidValue(string val) + { + try + { + Guid guid = new Guid(val); + return guid.ToString("B").ToUpper(); + } + catch (FormatException) + { + return val; + } + catch (OverflowException) + { + return val; + } + } + } +} diff --git a/src/wixext/MsmqDecompiler.cs b/src/wixext/MsmqDecompiler.cs new file mode 100644 index 00000000..396fc49a --- /dev/null +++ b/src/wixext/MsmqDecompiler.cs @@ -0,0 +1,303 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolset.Extensions +{ + using System; + using System.Collections; + using System.Globalization; + using WixToolset.Data; + using WixToolset.Extensibility; + using Msmq = WixToolset.Extensions.Serialize.Msmq; + using Wix = WixToolset.Data.Serialize; + + /// + /// The decompiler for the WiX Toolset MSMQ Extension. + /// + public sealed class MsmqDecompiler : DecompilerExtension + { + /// + /// Creates a decompiler for MSMQ Extension. + /// + public MsmqDecompiler() + { + this.TableDefinitions = MsmqExtensionData.GetExtensionTableDefinitions(); + } + + /// + /// Get the extensions library to be removed. + /// + /// Table definitions for library. + /// Library to remove from decompiled output. + public override Library GetLibraryToRemove(TableDefinitionCollection tableDefinitions) + { + return MsmqExtensionData.GetExtensionLibrary(tableDefinitions); + } + + /// + /// Decompiles an extension table. + /// + /// The table to decompile. + public override void DecompileTable(Table table) + { + switch (table.Name) + { + case "MessageQueue": + this.DecompileMessageQueueTable(table); + break; + case "MessageQueueUserPermission": + this.DecompileMessageQueueUserPermissionTable(table); + break; + case "MessageQueueGroupPermission": + this.DecompileMessageQueueGroupPermissionTable(table); + break; + default: + base.DecompileTable(table); + break; + } + } + + /// + /// Decompile the MessageQueue table. + /// + /// The table to decompile. + private void DecompileMessageQueueTable(Table table) + { + foreach (Row row in table.Rows) + { + Msmq.MessageQueue queue = new Msmq.MessageQueue(); + + queue.Id = (string)row[0]; + + if (null != row[2]) + { + queue.BasePriority = (int)row[2]; + } + + if (null != row[3]) + { + queue.JournalQuota = (int)row[3]; + } + + queue.Label = (string)row[4]; + + if (null != row[5]) + { + queue.MulticastAddress = (string)row[5]; + } + + queue.PathName = (string)row[6]; + + if (null != row[7]) + { + switch ((MsmqCompiler.MqiMessageQueuePrivacyLevel)row[7]) + { + case MsmqCompiler.MqiMessageQueuePrivacyLevel.None: + queue.PrivLevel = Msmq.MessageQueue.PrivLevelType.none; + break; + case MsmqCompiler.MqiMessageQueuePrivacyLevel.Optional: + queue.PrivLevel = Msmq.MessageQueue.PrivLevelType.optional; + break; + case MsmqCompiler.MqiMessageQueuePrivacyLevel.Body: + queue.PrivLevel = Msmq.MessageQueue.PrivLevelType.body; + break; + default: + break; + } + } + + if (null != row[8]) + { + queue.Quota = (int)row[8]; + } + + if (null != row[9]) + { + queue.ServiceTypeGuid = (string)row[9]; + } + + int attributes = (int)row[10]; + + if (0 != (attributes & (int)MsmqCompiler.MqiMessageQueueAttributes.Authenticate)) + { + queue.Authenticate = Msmq.YesNoType.yes; + } + + if (0 != (attributes & (int)MsmqCompiler.MqiMessageQueueAttributes.Journal)) + { + queue.Journal = Msmq.YesNoType.yes; + } + + if (0 != (attributes & (int)MsmqCompiler.MqiMessageQueueAttributes.Transactional)) + { + queue.Transactional = Msmq.YesNoType.yes; + } + + Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[1]); + if (null != component) + { + component.AddChild(queue); + } + else + { + this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[1], "Component")); + } + } + } + + /// + /// Decompile the MessageQueueUserPermission table. + /// + /// The table to decompile. + private void DecompileMessageQueueUserPermissionTable(Table table) + { + foreach (Row row in table.Rows) + { + Msmq.MessageQueuePermission queuePermission = new Msmq.MessageQueuePermission(); + + queuePermission.Id = (string)row[0]; + + if (null != row[2]) + { + queuePermission.MessageQueue = (string)row[2]; + } + + queuePermission.User = (string)row[3]; + + DecompileMessageQueuePermissionAttributes(row, queuePermission); + + Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[1]); + if (null != component) + { + component.AddChild(queuePermission); + } + else + { + this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[1], "Component")); + } + } + } + + /// + /// Decompile the MessageQueueGroupPermission table. + /// + /// The table to decompile. + private void DecompileMessageQueueGroupPermissionTable(Table table) + { + foreach (Row row in table.Rows) + { + Msmq.MessageQueuePermission queuePermission = new Msmq.MessageQueuePermission(); + + queuePermission.Id = (string)row[0]; + + if (null != row[2]) + { + queuePermission.MessageQueue = (string)row[2]; + } + + queuePermission.Group = (string)row[3]; + + DecompileMessageQueuePermissionAttributes(row, queuePermission); + + Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[1]); + if (null != component) + { + component.AddChild(queuePermission); + } + else + { + this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[1], "Component")); + } + } + } + + /// + /// Decompile row attributes for the MessageQueueUserPermission and MessageQueueGroupPermission tables. + /// + /// The row to decompile. + /// Target element. + private void DecompileMessageQueuePermissionAttributes(Row row, Msmq.MessageQueuePermission queuePermission) + { + int attributes = (int)row[4]; + + if (0 != (attributes & (int)MsmqCompiler.MqiMessageQueuePermission.DeleteMessage)) + { + queuePermission.DeleteMessage = Msmq.YesNoType.yes; + } + + if (0 != (attributes & (int)MsmqCompiler.MqiMessageQueuePermission.PeekMessage)) + { + queuePermission.PeekMessage = Msmq.YesNoType.yes; + } + + if (0 != (attributes & (int)MsmqCompiler.MqiMessageQueuePermission.WriteMessage)) + { + queuePermission.WriteMessage = Msmq.YesNoType.yes; + } + + if (0 != (attributes & (int)MsmqCompiler.MqiMessageQueuePermission.DeleteJournalMessage)) + { + queuePermission.DeleteJournalMessage = Msmq.YesNoType.yes; + } + + if (0 != (attributes & (int)MsmqCompiler.MqiMessageQueuePermission.SetQueueProperties)) + { + queuePermission.SetQueueProperties = Msmq.YesNoType.yes; + } + + if (0 != (attributes & (int)MsmqCompiler.MqiMessageQueuePermission.GetQueueProperties)) + { + queuePermission.GetQueueProperties = Msmq.YesNoType.yes; + } + + if (0 != (attributes & (int)MsmqCompiler.MqiMessageQueuePermission.DeleteQueue)) + { + queuePermission.DeleteQueue = Msmq.YesNoType.yes; + } + + if (0 != (attributes & (int)MsmqCompiler.MqiMessageQueuePermission.GetQueuePermissions)) + { + queuePermission.GetQueuePermissions = Msmq.YesNoType.yes; + } + + if (0 != (attributes & (int)MsmqCompiler.MqiMessageQueuePermission.ChangeQueuePermissions)) + { + queuePermission.ChangeQueuePermissions = Msmq.YesNoType.yes; + } + + if (0 != (attributes & (int)MsmqCompiler.MqiMessageQueuePermission.TakeQueueOwnership)) + { + queuePermission.TakeQueueOwnership = Msmq.YesNoType.yes; + } + + if (0 != (attributes & (int)MsmqCompiler.MqiMessageQueuePermission.ReceiveMessage)) + { + queuePermission.ReceiveMessage = Msmq.YesNoType.yes; + } + + if (0 != (attributes & (int)MsmqCompiler.MqiMessageQueuePermission.ReceiveJournalMessage)) + { + queuePermission.ReceiveJournalMessage = Msmq.YesNoType.yes; + } + + if (0 != (attributes & (int)MsmqCompiler.MqiMessageQueuePermission.QueueGenericRead)) + { + queuePermission.QueueGenericRead = Msmq.YesNoType.yes; + } + + if (0 != (attributes & (int)MsmqCompiler.MqiMessageQueuePermission.QueueGenericWrite)) + { + queuePermission.QueueGenericWrite = Msmq.YesNoType.yes; + } + + if (0 != (attributes & (int)MsmqCompiler.MqiMessageQueuePermission.QueueGenericExecute)) + { + queuePermission.QueueGenericExecute = Msmq.YesNoType.yes; + } + + if (0 != (attributes & (int)MsmqCompiler.MqiMessageQueuePermission.QueueGenericAll)) + { + queuePermission.QueueGenericAll = Msmq.YesNoType.yes; + } + } + } +} diff --git a/src/wixext/MsmqExtensionData.cs b/src/wixext/MsmqExtensionData.cs new file mode 100644 index 00000000..81d53ce7 --- /dev/null +++ b/src/wixext/MsmqExtensionData.cs @@ -0,0 +1,64 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolset.Extensions +{ + using System; + using System.Reflection; + using WixToolset.Data; + using WixToolset.Extensibility; + + /// + /// The WiX Toolset MSMQ Extension. + /// + public sealed class MsmqExtensionData : ExtensionData + { + /// + /// Gets the default culture. + /// + /// The default culture. + public override string DefaultCulture + { + get { return "en-us"; } + } + + /// + /// Gets the optional table definitions for this extension. + /// + /// The optional table definitions for this extension. + public override TableDefinitionCollection TableDefinitions + { + get + { + return MsmqExtensionData.GetExtensionTableDefinitions(); + } + } + + /// + /// Gets the library associated with this extension. + /// + /// The table definitions to use while loading the library. + /// The loaded library. + public override Library GetLibrary(TableDefinitionCollection tableDefinitions) + { + return MsmqExtensionData.GetExtensionLibrary(tableDefinitions); + } + + /// + /// Internal mechanism to access the extension's table definitions. + /// + /// Extension's table definitions. + internal static TableDefinitionCollection GetExtensionTableDefinitions() + { + return ExtensionData.LoadTableDefinitionHelper(Assembly.GetExecutingAssembly(), "WixToolset.Extensions.Data.tables.xml"); + } + + /// + /// Internal mechanism to access the extension's library. + /// + /// Extension's library. + internal static Library GetExtensionLibrary(TableDefinitionCollection tableDefinitions) + { + return ExtensionData.LoadLibraryHelper(Assembly.GetExecutingAssembly(), "WixToolset.Extensions.Data.msmq.wixlib", tableDefinitions); + } + } +} diff --git a/src/wixext/WixMsmqExtension.csproj b/src/wixext/WixMsmqExtension.csproj new file mode 100644 index 00000000..e37d62a3 --- /dev/null +++ b/src/wixext/WixMsmqExtension.csproj @@ -0,0 +1,49 @@ + + + + + + + {B990D81B-9F60-4EEE-B31D-B5D1EAA799EE} + WixMsmqExtension + Library + WixToolset.Extensions + + + + + + + + $(RootNamespace).Data.tables.xml + + + $(RootNamespace).Xsd.msmq.xsd + + + WixToolset.Data.Serialize + WixToolset.Extensions.Serialize.Msmq + + + msmq.xsd + PreserveNewest + + + Data\msmq.wixlib + + + + + + + + + + + + false + + + + + diff --git a/src/wixext/messages.xml b/src/wixext/messages.xml new file mode 100644 index 00000000..5ab6417f --- /dev/null +++ b/src/wixext/messages.xml @@ -0,0 +1,77 @@ + + + + + + + + + The {0}/@{1} attribute cannot be specified unless the element has a component as an ancestor. A {0} that does not have a component ancestor is not installed. + + + + + + + The {0} element cannot be specified unless the element has a component as an ancestor. A {0} that does not have a component ancestor is not installed. + + + + + + The {0}/@{1} attribute cannot coexist with the {2} attribute's value of '{3}'. + + + + + + + The {0}/@{1} attribute's value, '{2}', cannot coexist with the {3} attribute's value of '{4}'. + + + + + + + + + + The {0}/@{1} cannot be provided unless the {2} attribute is provided with a value of '{3}'. + + + + + + + + + The {0}/@{1} attribute must be provided when {0} element is nested under a component. + + + + + + + A {0} element must have either a {1} attribute or a {2} attribute, or both set. + + + + + + + + A {0} element not nested under a component must have either a {1} attribute or a {2} attribute, or both set. + + + + + + + + + The MsmqAssembly element has a Type attribute with a value of 'native', but the element does not contain any MsmqComponent elements. All components contained in a native assembly must be listed, or they will not be correctly removed during uninstall. + + + + + diff --git a/src/wixext/msmq.xsd b/src/wixext/msmq.xsd new file mode 100644 index 00000000..26348330 --- /dev/null +++ b/src/wixext/msmq.xsd @@ -0,0 +1,121 @@ + + + + + + + + The source code schema for the WiX Toolset MSMQ Extension. + + + + + + + + + + + + + + + + + + + + + + + Default: No. + + + + + + Default: No. + + + + + + + + + + + + + + + + + + + Default: No. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Values of this type will either be "yes" or "no". + + + + + + + + diff --git a/src/wixext/tables.xml b/src/wixext/tables.xml new file mode 100644 index 00000000..5fdd1fad --- /dev/null +++ b/src/wixext/tables.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/wixlib/MsmqExtension.wixproj b/src/wixlib/MsmqExtension.wixproj new file mode 100644 index 00000000..6dcdfbc7 --- /dev/null +++ b/src/wixlib/MsmqExtension.wixproj @@ -0,0 +1,24 @@ + + + + + + + {42493058-5FC8-4F85-9884-FF3190E084B6} + msmq + Library + true + + + + + + + + + + + + + + diff --git a/src/wixlib/MsmqExtension.wxs b/src/wixlib/MsmqExtension.wxs new file mode 100644 index 00000000..b8622580 --- /dev/null +++ b/src/wixlib/MsmqExtension.wxs @@ -0,0 +1,31 @@ + + + + + + + + + + !(loc.MessageQueuingExecuteInstall) + !(loc.MessageQueuingExecuteUninstall) + + + + + + + + + + + VersionNT >= 500 + VersionNT >= 500 + + + + + + + + diff --git a/src/wixlib/en-us.wxl b/src/wixlib/en-us.wxl new file mode 100644 index 00000000..ebe08095 --- /dev/null +++ b/src/wixlib/en-us.wxl @@ -0,0 +1,10 @@ + + + + + + Configuring message queues + Queue: [1] + Configuring message queues + Queue: [1] + diff --git a/src/wixlib/ja-jp.wxl b/src/wixlib/ja-jp.wxl new file mode 100644 index 00000000..d56cd7ec --- /dev/null +++ b/src/wixlib/ja-jp.wxl @@ -0,0 +1,10 @@ + + + + + + メッセージ キューを構成しています + キュー: [1] + メッセージ キューを構成しています + キュー: [1] + -- cgit v1.2.3-55-g6feb