From d77dca43e87e8711b19910a8fd49138f939bf0a4 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Tue, 4 May 2021 22:48:58 -0700 Subject: Move Msmq.wixext into ext --- src/ext/Msmq/CSharp.Build.props | 11 + src/ext/Msmq/Cpp.Build.props | 86 ++ src/ext/Msmq/Directory.Build.props | 29 + src/ext/Msmq/Directory.Build.targets | 48 ++ src/ext/Msmq/Msmq.wixext.sln | 61 ++ src/ext/Msmq/README.md | 2 + src/ext/Msmq/appveyor.cmd | 14 + src/ext/Msmq/appveyor.yml | 40 + src/ext/Msmq/ca/custommsierrors.h | 4 + src/ext/Msmq/ca/dllmain.cpp | 26 + src/ext/Msmq/ca/mqcost.h | 9 + src/ext/Msmq/ca/mqexec.cpp | 192 +++++ src/ext/Msmq/ca/mqqueueexec.cpp | 927 +++++++++++++++++++++ src/ext/Msmq/ca/mqqueueexec.h | 30 + src/ext/Msmq/ca/mqqueuesched.cpp | 582 +++++++++++++ src/ext/Msmq/ca/mqqueuesched.h | 92 ++ src/ext/Msmq/ca/mqsched.cpp | 196 +++++ src/ext/Msmq/ca/mqutilexec.cpp | 380 +++++++++ src/ext/Msmq/ca/mqutilexec.h | 23 + src/ext/Msmq/ca/mqutilsched.cpp | 43 + src/ext/Msmq/ca/mqutilsched.h | 9 + src/ext/Msmq/ca/msmqca.def | 12 + src/ext/Msmq/ca/msmqca.vcxproj | 71 ++ src/ext/Msmq/ca/packages.config | 5 + src/ext/Msmq/ca/precomp.h | 23 + src/ext/Msmq/nuget.config | 17 + .../WixToolsetTest.Msmq/MsmqExtensionFixture.cs | 32 + .../TestData/UsingMessageQueue/Package.en-us.wxl | 11 + .../TestData/UsingMessageQueue/Package.wxs | 15 + .../UsingMessageQueue/PackageComponents.wxs | 12 + .../TestData/UsingMessageQueue/example.txt | 1 + .../WixToolsetTest.Msmq/WixToolsetTest.Msmq.csproj | 41 + src/ext/Msmq/wix.snk | Bin 0 -> 596 bytes src/ext/Msmq/wixext/MsmqCompiler.cs | 528 ++++++++++++ src/ext/Msmq/wixext/MsmqDecompiler.cs | 305 +++++++ src/ext/Msmq/wixext/MsmqErrors.cs | 71 ++ src/ext/Msmq/wixext/MsmqExtensionData.cs | 30 + src/ext/Msmq/wixext/MsmqExtensionFactory.cs | 18 + src/ext/Msmq/wixext/MsmqTableDefinitions.cs | 64 ++ src/ext/Msmq/wixext/MsmqWarnings.cs | 30 + .../wixext/MsmqWindowsInstallerBackendExtension.cs | 13 + .../Symbols/MessageQueueGroupPermissionSymbol.cs | 71 ++ src/ext/Msmq/wixext/Symbols/MessageQueueSymbol.cs | 119 +++ .../Symbols/MessageQueueUserPermissionSymbol.cs | 71 ++ .../Msmq/wixext/Symbols/MsmqSymbolDefinitions.cs | 47 ++ src/ext/Msmq/wixext/WixToolset.Msmq.wixext.csproj | 30 + src/ext/Msmq/wixext/WixToolset.Msmq.wixext.targets | 11 + src/ext/Msmq/wixlib/MsmqExtension.wxs | 29 + src/ext/Msmq/wixlib/caerr.wxi | 96 +++ src/ext/Msmq/wixlib/en-us.wxl | 10 + src/ext/Msmq/wixlib/ja-jp.wxl | 10 + src/ext/Msmq/wixlib/msmq.wixproj | 18 + src/ext/global.json | 5 + 53 files changed, 4620 insertions(+) create mode 100644 src/ext/Msmq/CSharp.Build.props create mode 100644 src/ext/Msmq/Cpp.Build.props create mode 100644 src/ext/Msmq/Directory.Build.props create mode 100644 src/ext/Msmq/Directory.Build.targets create mode 100644 src/ext/Msmq/Msmq.wixext.sln create mode 100644 src/ext/Msmq/README.md create mode 100644 src/ext/Msmq/appveyor.cmd create mode 100644 src/ext/Msmq/appveyor.yml create mode 100644 src/ext/Msmq/ca/custommsierrors.h create mode 100644 src/ext/Msmq/ca/dllmain.cpp create mode 100644 src/ext/Msmq/ca/mqcost.h create mode 100644 src/ext/Msmq/ca/mqexec.cpp create mode 100644 src/ext/Msmq/ca/mqqueueexec.cpp create mode 100644 src/ext/Msmq/ca/mqqueueexec.h create mode 100644 src/ext/Msmq/ca/mqqueuesched.cpp create mode 100644 src/ext/Msmq/ca/mqqueuesched.h create mode 100644 src/ext/Msmq/ca/mqsched.cpp create mode 100644 src/ext/Msmq/ca/mqutilexec.cpp create mode 100644 src/ext/Msmq/ca/mqutilexec.h create mode 100644 src/ext/Msmq/ca/mqutilsched.cpp create mode 100644 src/ext/Msmq/ca/mqutilsched.h create mode 100644 src/ext/Msmq/ca/msmqca.def create mode 100644 src/ext/Msmq/ca/msmqca.vcxproj create mode 100644 src/ext/Msmq/ca/packages.config create mode 100644 src/ext/Msmq/ca/precomp.h create mode 100644 src/ext/Msmq/nuget.config create mode 100644 src/ext/Msmq/test/WixToolsetTest.Msmq/MsmqExtensionFixture.cs create mode 100644 src/ext/Msmq/test/WixToolsetTest.Msmq/TestData/UsingMessageQueue/Package.en-us.wxl create mode 100644 src/ext/Msmq/test/WixToolsetTest.Msmq/TestData/UsingMessageQueue/Package.wxs create mode 100644 src/ext/Msmq/test/WixToolsetTest.Msmq/TestData/UsingMessageQueue/PackageComponents.wxs create mode 100644 src/ext/Msmq/test/WixToolsetTest.Msmq/TestData/UsingMessageQueue/example.txt create mode 100644 src/ext/Msmq/test/WixToolsetTest.Msmq/WixToolsetTest.Msmq.csproj create mode 100644 src/ext/Msmq/wix.snk create mode 100644 src/ext/Msmq/wixext/MsmqCompiler.cs create mode 100644 src/ext/Msmq/wixext/MsmqDecompiler.cs create mode 100644 src/ext/Msmq/wixext/MsmqErrors.cs create mode 100644 src/ext/Msmq/wixext/MsmqExtensionData.cs create mode 100644 src/ext/Msmq/wixext/MsmqExtensionFactory.cs create mode 100644 src/ext/Msmq/wixext/MsmqTableDefinitions.cs create mode 100644 src/ext/Msmq/wixext/MsmqWarnings.cs create mode 100644 src/ext/Msmq/wixext/MsmqWindowsInstallerBackendExtension.cs create mode 100644 src/ext/Msmq/wixext/Symbols/MessageQueueGroupPermissionSymbol.cs create mode 100644 src/ext/Msmq/wixext/Symbols/MessageQueueSymbol.cs create mode 100644 src/ext/Msmq/wixext/Symbols/MessageQueueUserPermissionSymbol.cs create mode 100644 src/ext/Msmq/wixext/Symbols/MsmqSymbolDefinitions.cs create mode 100644 src/ext/Msmq/wixext/WixToolset.Msmq.wixext.csproj create mode 100644 src/ext/Msmq/wixext/WixToolset.Msmq.wixext.targets create mode 100644 src/ext/Msmq/wixlib/MsmqExtension.wxs create mode 100644 src/ext/Msmq/wixlib/caerr.wxi create mode 100644 src/ext/Msmq/wixlib/en-us.wxl create mode 100644 src/ext/Msmq/wixlib/ja-jp.wxl create mode 100644 src/ext/Msmq/wixlib/msmq.wixproj create mode 100644 src/ext/global.json (limited to 'src/ext') diff --git a/src/ext/Msmq/CSharp.Build.props b/src/ext/Msmq/CSharp.Build.props new file mode 100644 index 00000000..b12f4c6e --- /dev/null +++ b/src/ext/Msmq/CSharp.Build.props @@ -0,0 +1,11 @@ + + + + + true + $([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)wix.snk)) + + diff --git a/src/ext/Msmq/Cpp.Build.props b/src/ext/Msmq/Cpp.Build.props new file mode 100644 index 00000000..9b7a1bb5 --- /dev/null +++ b/src/ext/Msmq/Cpp.Build.props @@ -0,0 +1,86 @@ + + + + + + Win32 + $(BaseIntermediateOutputPath)$(Configuration)\$(Platform)\ + $(OutputPath)$(Platform)\ + + + + $([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0')) + + + + + $(DisableSpecificCompilerWarnings) + Level4 + $(ProjectDir)inc;$(MSBuildProjectDirectory);$(IntDir);$(SqlCESdkIncludePath);$(ProjectAdditionalIncludeDirectories);%(AdditionalIncludeDirectories) + WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0501;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) + Use + precomp.h + StdCall + true + false + -YlprecompDefine + /Zc:threadSafeInit- %(AdditionalOptions) + true + + + $(ArmPreprocessorDefinitions);%(PreprocessorDefinitions) + $(ProjectAdditionalResourceIncludeDirectories);%(AdditionalIncludeDirectories) + + + $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ProjectAdditionalLibraryDirectories);%(AdditionalLibraryDirectories) + + + $(ProjectSubSystem) + $(ProjectModuleDefinitionFile) + $(ResourceOnlyDll) + true + $(ProjectAdditionalLinkLibraries);advapi32.lib;comdlg32.lib;user32.lib;oleaut32.lib;gdi32.lib;shell32.lib;ole32.lib;version.lib;%(AdditionalDependencies) + $(OutDir);$(AdditionalMultiTargetLibraryPath);$(ArmLibraryDirectories);$(ProjectAdditionalLinkLibraryDirectories);%(AdditionalLibraryDirectories) + /IGNORE:4099 %(AdditionalOptions) + + + + + + NoExtensions + + + + + CDecl + + + + + OldStyle + true + true + + + + + Disabled + EnableFastChecks + _DEBUG;DEBUG;%(PreprocessorDefinitions) + MultiThreadedDebug + + + + + MinSpace + NDEBUG;%(PreprocessorDefinitions) + true + true + MultiThreaded + + + true + true + + + diff --git a/src/ext/Msmq/Directory.Build.props b/src/ext/Msmq/Directory.Build.props new file mode 100644 index 00000000..f83cc154 --- /dev/null +++ b/src/ext/Msmq/Directory.Build.props @@ -0,0 +1,29 @@ + + + + + + Debug + false + MSB3246 + + $(MSBuildProjectName) + $([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)..\build\)) + $(BaseOutputPath)obj\$(ProjectName)\ + $(BaseOutputPath)$(Configuration)\ + + WiX Toolset Team + WiX Toolset + Copyright (c) .NET Foundation and contributors. All rights reserved. + MS-RL + WiX Toolset + + + + + + + diff --git a/src/ext/Msmq/Directory.Build.targets b/src/ext/Msmq/Directory.Build.targets new file mode 100644 index 00000000..dac7452a --- /dev/null +++ b/src/ext/Msmq/Directory.Build.targets @@ -0,0 +1,48 @@ + + + + + + + true + $(SolutionPath) + $(NCrunchOriginalSolutionPath) + + + + + + + $([System.IO.File]::ReadAllText($(TheSolutionPath))) + $([System.IO.Path]::GetDirectoryName( $(TheSolutionPath) )) + (?<="[PackageName]", ")(.*)(?=", ") + + + + + + %(Identity) + $(SolutionFileContent.Contains('\%(Identity).csproj')) + + + + + $(RegexPattern.Replace('[PackageName]','%(PackageName)') ) + $([System.Text.RegularExpressions.Regex]::Match('$(SolutionFileContent)', '%(Pattern)')) + + + + + + + + + + + diff --git a/src/ext/Msmq/Msmq.wixext.sln b/src/ext/Msmq/Msmq.wixext.sln new file mode 100644 index 00000000..e3ebed6d --- /dev/null +++ b/src/ext/Msmq/Msmq.wixext.sln @@ -0,0 +1,61 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28010.2016 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "msmqca", "src\ca\msmqca.vcxproj", "{CAD56A7E-342B-4324-9DCB-BCEB8F3BC80D}" +EndProject +Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "msmq", "src\wixlib\msmq.wixproj", "{42493058-5FC8-4F85-9884-FF3190E084B6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Msmq.wixext", "src\wixext\WixToolset.Msmq.wixext.csproj", "{B990D81B-9F60-4EEE-B31D-B5D1EAA799EE}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolsetTest.Msmq", "src\test\WixToolsetTest.Msmq\WixToolsetTest.Msmq.csproj", "{B63DA068-338F-473B-9097-FC4E64830A2A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CAD56A7E-342B-4324-9DCB-BCEB8F3BC80D}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {CAD56A7E-342B-4324-9DCB-BCEB8F3BC80D}.Debug|Any CPU.Build.0 = Debug|Win32 + {CAD56A7E-342B-4324-9DCB-BCEB8F3BC80D}.Debug|x86.ActiveCfg = Debug|Win32 + {CAD56A7E-342B-4324-9DCB-BCEB8F3BC80D}.Debug|x86.Build.0 = Debug|Win32 + {CAD56A7E-342B-4324-9DCB-BCEB8F3BC80D}.Release|Any CPU.ActiveCfg = Release|Win32 + {CAD56A7E-342B-4324-9DCB-BCEB8F3BC80D}.Release|Any CPU.Build.0 = Release|Win32 + {CAD56A7E-342B-4324-9DCB-BCEB8F3BC80D}.Release|x86.ActiveCfg = Release|Win32 + {CAD56A7E-342B-4324-9DCB-BCEB8F3BC80D}.Release|x86.Build.0 = Release|Win32 + {42493058-5FC8-4F85-9884-FF3190E084B6}.Debug|Any CPU.ActiveCfg = Debug|x86 + {42493058-5FC8-4F85-9884-FF3190E084B6}.Debug|Any CPU.Build.0 = Debug|x86 + {42493058-5FC8-4F85-9884-FF3190E084B6}.Debug|x86.ActiveCfg = Debug|x86 + {42493058-5FC8-4F85-9884-FF3190E084B6}.Debug|x86.Build.0 = Debug|x86 + {42493058-5FC8-4F85-9884-FF3190E084B6}.Release|Any CPU.ActiveCfg = Release|x86 + {42493058-5FC8-4F85-9884-FF3190E084B6}.Release|Any CPU.Build.0 = Release|x86 + {42493058-5FC8-4F85-9884-FF3190E084B6}.Release|x86.ActiveCfg = Release|x86 + {42493058-5FC8-4F85-9884-FF3190E084B6}.Release|x86.Build.0 = Release|x86 + {B990D81B-9F60-4EEE-B31D-B5D1EAA799EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B990D81B-9F60-4EEE-B31D-B5D1EAA799EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B990D81B-9F60-4EEE-B31D-B5D1EAA799EE}.Debug|x86.ActiveCfg = Debug|Any CPU + {B990D81B-9F60-4EEE-B31D-B5D1EAA799EE}.Debug|x86.Build.0 = Debug|Any CPU + {B990D81B-9F60-4EEE-B31D-B5D1EAA799EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B990D81B-9F60-4EEE-B31D-B5D1EAA799EE}.Release|Any CPU.Build.0 = Release|Any CPU + {B990D81B-9F60-4EEE-B31D-B5D1EAA799EE}.Release|x86.ActiveCfg = Release|Any CPU + {B990D81B-9F60-4EEE-B31D-B5D1EAA799EE}.Release|x86.Build.0 = Release|Any CPU + {B63DA068-338F-473B-9097-FC4E64830A2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B63DA068-338F-473B-9097-FC4E64830A2A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B63DA068-338F-473B-9097-FC4E64830A2A}.Debug|x86.ActiveCfg = Debug|Any CPU + {B63DA068-338F-473B-9097-FC4E64830A2A}.Debug|x86.Build.0 = Debug|Any CPU + {B63DA068-338F-473B-9097-FC4E64830A2A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B63DA068-338F-473B-9097-FC4E64830A2A}.Release|Any CPU.Build.0 = Release|Any CPU + {B63DA068-338F-473B-9097-FC4E64830A2A}.Release|x86.ActiveCfg = Release|Any CPU + {B63DA068-338F-473B-9097-FC4E64830A2A}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {5524C948-C115-4690-9BC4-44E3E963F960} + EndGlobalSection +EndGlobal diff --git a/src/ext/Msmq/README.md b/src/ext/Msmq/README.md new file mode 100644 index 00000000..fa3c277f --- /dev/null +++ b/src/ext/Msmq/README.md @@ -0,0 +1,2 @@ +# Msmq.wixext +WixToolset.Msmq.wixext - MSMQ WiX Toolset Extension diff --git a/src/ext/Msmq/appveyor.cmd b/src/ext/Msmq/appveyor.cmd new file mode 100644 index 00000000..f493b577 --- /dev/null +++ b/src/ext/Msmq/appveyor.cmd @@ -0,0 +1,14 @@ +@setlocal +@pushd %~dp0 + +nuget restore || exit /b + +msbuild -p:Configuration=Release -t:Restore || exit /b + +msbuild -p:Configuration=Release src\test\WixToolsetTest.Msmq\WixToolsetTest.Msmq.csproj || exit /b +dotnet test -c Release --no-build src\test\WixToolsetTest.Msmq || exit /b + +msbuild -p:Configuration=Release -t:Pack src\wixext\WixToolset.Msmq.wixext.csproj || exit /b + +@popd +@endlocal \ No newline at end of file diff --git a/src/ext/Msmq/appveyor.yml b/src/ext/Msmq/appveyor.yml new file mode 100644 index 00000000..7c686b04 --- /dev/null +++ b/src/ext/Msmq/appveyor.yml @@ -0,0 +1,40 @@ +# 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. +# +# Do NOT modify this file. Update the canonical version in Home\repo-template\src\appveyor.yml +# then update all of the repos. + +branches: + only: + - master + - develop + +image: Visual Studio 2019 + +version: 0.0.0.{build} +configuration: Release + +environment: + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + NUGET_XMLDOC_MODE: skip + +build_script: + - appveyor.cmd + +pull_requests: + do_not_increment_build_number: true + +nuget: + disable_publish_on_pr: true + +skip_branch_with_pr: true +skip_tags: true + +artifacts: +- path: build\Release\**\*.nupkg + name: nuget + +notifications: +- provider: Slack + incoming_webhook: + secure: p5xuu+4x2JHfwGDMDe5KcG1k7gZxqYc4jWVwvyNZv5cvkubPD2waJs5yXMAXZNN7Z63/3PWHb7q4KoY/99AjauYa1nZ4c5qYqRPFRBKTHfA= diff --git a/src/ext/Msmq/ca/custommsierrors.h b/src/ext/Msmq/ca/custommsierrors.h new file mode 100644 index 00000000..0c1b23b7 --- /dev/null +++ b/src/ext/Msmq/ca/custommsierrors.h @@ -0,0 +1,4 @@ +#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 msierrMsmqCannotConnect 28101 diff --git a/src/ext/Msmq/ca/dllmain.cpp b/src/ext/Msmq/ca/dllmain.cpp new file mode 100644 index 00000000..35ae6d1c --- /dev/null +++ b/src/ext/Msmq/ca/dllmain.cpp @@ -0,0 +1,26 @@ +// 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 custom actions + +********************************************************************/ +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; +} diff --git a/src/ext/Msmq/ca/mqcost.h b/src/ext/Msmq/ca/mqcost.h new file mode 100644 index 00000000..a40b7437 --- /dev/null +++ b/src/ext/Msmq/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/ext/Msmq/ca/mqexec.cpp b/src/ext/Msmq/ca/mqexec.cpp new file mode 100644 index 00000000..ff7e9b14 --- /dev/null +++ b/src/ext/Msmq/ca/mqexec.cpp @@ -0,0 +1,192 @@ +// 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" + + +/******************************************************************** + 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 = MqiExecInitialize(); + 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 + MqiExecUninitialize(); + + 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 = MqiExecInitialize(); + 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 + MqiExecUninitialize(); + + 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 = MqiExecInitialize(); + 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 + MqiExecUninitialize(); + + 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 = MqiExecInitialize(); + 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 + MqiExecUninitialize(); + + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} diff --git a/src/ext/Msmq/ca/mqqueueexec.cpp b/src/ext/Msmq/ca/mqqueueexec.cpp new file mode 100644 index 00000000..e4304ab8 --- /dev/null +++ b/src/ext/Msmq/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 MqiExecInitialize() +{ + 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 MqiExecUninitialize() +{ + 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/ext/Msmq/ca/mqqueueexec.h b/src/ext/Msmq/ca/mqqueueexec.h new file mode 100644 index 00000000..76bc2023 --- /dev/null +++ b/src/ext/Msmq/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 MqiExecInitialize(); +void MqiExecUninitialize(); +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/ext/Msmq/ca/mqqueuesched.cpp b/src/ext/Msmq/ca/mqqueuesched.cpp new file mode 100644 index 00000000..01777ea4 --- /dev/null +++ b/src/ext/Msmq/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 MqiSchedInitialize() +{ + 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 MqiSchedUninitialize() +{ + 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/ext/Msmq/ca/mqqueuesched.h b/src/ext/Msmq/ca/mqqueuesched.h new file mode 100644 index 00000000..c9381e0a --- /dev/null +++ b/src/ext/Msmq/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 MqiSchedInitialize(); +void MqiSchedUninitialize(); +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/ext/Msmq/ca/mqsched.cpp b/src/ext/Msmq/ca/mqsched.cpp new file mode 100644 index 00000000..4c994901 --- /dev/null +++ b/src/ext/Msmq/ca/mqsched.cpp @@ -0,0 +1,196 @@ +// 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" + + +/******************************************************************** + 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 = MqiSchedInitialize(); + 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 + MqiSchedUninitialize(); + + 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 = MqiSchedInitialize(); + 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 + MqiSchedUninitialize(); + + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} diff --git a/src/ext/Msmq/ca/mqutilexec.cpp b/src/ext/Msmq/ca/mqutilexec.cpp new file mode 100644 index 00000000..a9c56e02 --- /dev/null +++ b/src/ext/Msmq/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/ext/Msmq/ca/mqutilexec.h b/src/ext/Msmq/ca/mqutilexec.h new file mode 100644 index 00000000..d3dc17a1 --- /dev/null +++ b/src/ext/Msmq/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/ext/Msmq/ca/mqutilsched.cpp b/src/ext/Msmq/ca/mqutilsched.cpp new file mode 100644 index 00000000..4353a6d6 --- /dev/null +++ b/src/ext/Msmq/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/ext/Msmq/ca/mqutilsched.h b/src/ext/Msmq/ca/mqutilsched.h new file mode 100644 index 00000000..e172257d --- /dev/null +++ b/src/ext/Msmq/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/ext/Msmq/ca/msmqca.def b/src/ext/Msmq/ca/msmqca.def new file mode 100644 index 00000000..4902858f --- /dev/null +++ b/src/ext/Msmq/ca/msmqca.def @@ -0,0 +1,12 @@ +; 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. + + +LIBRARY "msmqca" + +EXPORTS + MessageQueuingInstall + MessageQueuingUninstall + MessageQueuingExecuteInstall + MessageQueuingRollbackInstall + MessageQueuingExecuteUninstall + MessageQueuingRollbackUninstall diff --git a/src/ext/Msmq/ca/msmqca.vcxproj b/src/ext/Msmq/ca/msmqca.vcxproj new file mode 100644 index 00000000..c4cb3323 --- /dev/null +++ b/src/ext/Msmq/ca/msmqca.vcxproj @@ -0,0 +1,71 @@ + + + + + + + + + + Debug + Win32 + + + Release + Win32 + + + + + {CAD56A7E-342B-4324-9DCB-BCEB8F3BC80D} + DynamicLibrary + msmqca + v142 + Unicode + msmqca.def + WiX Toolset MSMQ CustomAction + + + + + + + msi.lib + + + + + Create + + + + + + + + + + + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + diff --git a/src/ext/Msmq/ca/packages.config b/src/ext/Msmq/ca/packages.config new file mode 100644 index 00000000..9d88f529 --- /dev/null +++ b/src/ext/Msmq/ca/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/ext/Msmq/ca/precomp.h b/src/ext/Msmq/ca/precomp.h new file mode 100644 index 00000000..cbbff6ea --- /dev/null +++ b/src/ext/Msmq/ca/precomp.h @@ -0,0 +1,23 @@ +#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. + + +#include +#include +#include +#include +#include +#include + +#include "wcautil.h" +#include "memutil.h" +#include "strutil.h" +#include "wiutil.h" + +#include "CustomMsiErrors.h" + +#include "mqcost.h" +#include "mqutilsched.h" +#include "mqqueuesched.h" +#include "mqutilexec.h" +#include "mqqueueexec.h" diff --git a/src/ext/Msmq/nuget.config b/src/ext/Msmq/nuget.config new file mode 100644 index 00000000..db7aba29 --- /dev/null +++ b/src/ext/Msmq/nuget.config @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/ext/Msmq/test/WixToolsetTest.Msmq/MsmqExtensionFixture.cs b/src/ext/Msmq/test/WixToolsetTest.Msmq/MsmqExtensionFixture.cs new file mode 100644 index 00000000..057b0a9d --- /dev/null +++ b/src/ext/Msmq/test/WixToolsetTest.Msmq/MsmqExtensionFixture.cs @@ -0,0 +1,32 @@ +// 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 WixToolsetTest.Msmq +{ + using System.Linq; + using WixBuildTools.TestSupport; + using WixToolset.Core.TestPackage; + using WixToolset.Msmq; + using Xunit; + + public class MsmqExtensionFixture + { + [Fact] + public void CanBuildUsingMessageQueue() + { + var folder = TestData.Get(@"TestData\UsingMessageQueue"); + var build = new Builder(folder, typeof(MsmqExtensionFactory), new[] { folder }); + + var results = build.BuildAndQuery(Build, "MessageQueue"); + Assert.Equal(new[] + { + "MessageQueue:TestMQ\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo\t\t\tMQLabel\t\tMQPath\t\t\t\t0", + }, results); + } + + private static void Build(string[] args) + { + var result = WixRunner.Execute(args) + .AssertSuccess(); + } + } +} diff --git a/src/ext/Msmq/test/WixToolsetTest.Msmq/TestData/UsingMessageQueue/Package.en-us.wxl b/src/ext/Msmq/test/WixToolsetTest.Msmq/TestData/UsingMessageQueue/Package.en-us.wxl new file mode 100644 index 00000000..38c12ac1 --- /dev/null +++ b/src/ext/Msmq/test/WixToolsetTest.Msmq/TestData/UsingMessageQueue/Package.en-us.wxl @@ -0,0 +1,11 @@ + + + + + + A newer version of [ProductName] is already installed. + MsiPackage + + diff --git a/src/ext/Msmq/test/WixToolsetTest.Msmq/TestData/UsingMessageQueue/Package.wxs b/src/ext/Msmq/test/WixToolsetTest.Msmq/TestData/UsingMessageQueue/Package.wxs new file mode 100644 index 00000000..bd31e81f --- /dev/null +++ b/src/ext/Msmq/test/WixToolsetTest.Msmq/TestData/UsingMessageQueue/Package.wxs @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/ext/Msmq/test/WixToolsetTest.Msmq/TestData/UsingMessageQueue/PackageComponents.wxs b/src/ext/Msmq/test/WixToolsetTest.Msmq/TestData/UsingMessageQueue/PackageComponents.wxs new file mode 100644 index 00000000..ff9f7d92 --- /dev/null +++ b/src/ext/Msmq/test/WixToolsetTest.Msmq/TestData/UsingMessageQueue/PackageComponents.wxs @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/src/ext/Msmq/test/WixToolsetTest.Msmq/TestData/UsingMessageQueue/example.txt b/src/ext/Msmq/test/WixToolsetTest.Msmq/TestData/UsingMessageQueue/example.txt new file mode 100644 index 00000000..1b4ffe8a --- /dev/null +++ b/src/ext/Msmq/test/WixToolsetTest.Msmq/TestData/UsingMessageQueue/example.txt @@ -0,0 +1 @@ +This is example.txt. \ No newline at end of file diff --git a/src/ext/Msmq/test/WixToolsetTest.Msmq/WixToolsetTest.Msmq.csproj b/src/ext/Msmq/test/WixToolsetTest.Msmq/WixToolsetTest.Msmq.csproj new file mode 100644 index 00000000..7f74e043 --- /dev/null +++ b/src/ext/Msmq/test/WixToolsetTest.Msmq/WixToolsetTest.Msmq.csproj @@ -0,0 +1,41 @@ + + + + + + netcoreapp3.1 + false + + + + NU1701 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ext/Msmq/wix.snk b/src/ext/Msmq/wix.snk new file mode 100644 index 00000000..3908a66a Binary files /dev/null and b/src/ext/Msmq/wix.snk differ diff --git a/src/ext/Msmq/wixext/MsmqCompiler.cs b/src/ext/Msmq/wixext/MsmqCompiler.cs new file mode 100644 index 00000000..cfc4ef65 --- /dev/null +++ b/src/ext/Msmq/wixext/MsmqCompiler.cs @@ -0,0 +1,528 @@ +// 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.Msmq +{ + using System; + using System.Collections.Generic; + using System.Xml.Linq; + using WixToolset.Data; + using WixToolset.Extensibility; + using WixToolset.Msmq.Symbols; + + /// + /// The compiler for the WiX Toolset MSMQ Extension. + /// + public sealed class MsmqCompiler : BaseCompilerExtension + { + public override XNamespace 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(Intermediate intermediate, IntermediateSection section, XElement parentElement, XElement element, IDictionary context) + { + switch (parentElement.Name.LocalName) + { + case "Component": + var componentId = context["ComponentId"]; + var directoryId = context["DirectoryId"]; + + switch (element.Name.LocalName) + { + case "MessageQueue": + this.ParseMessageQueueElement(intermediate, section, element, componentId); + break; + case "MessageQueuePermission": + this.ParseMessageQueuePermissionElement(intermediate, section, element, componentId, null); + break; + default: + this.ParseHelper.UnexpectedElement(parentElement, element); + break; + } + break; + default: + this.ParseHelper.UnexpectedElement(parentElement, element); + break; + } + } + + /// + /// Parses an MSMQ message queue element. + /// + /// Element to parse. + /// Identifier of parent component. + private void ParseMessageQueueElement(Intermediate intermediate, IntermediateSection section, XElement node, string componentId) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(node); + + Identifier id = null; + var basePriority = CompilerConstants.IntegerNotSet; + var journalQuota = CompilerConstants.IntegerNotSet; + string label = null; + string multicastAddress = null; + string pathName = null; + var privLevel = CompilerConstants.IntegerNotSet; + var quota = CompilerConstants.IntegerNotSet; + string serviceTypeGuid = null; + int attributes = 0; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Authenticate": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + attributes |= (int)MqiMessageQueueAttributes.Authenticate; + } + else + { + attributes &= ~(int)MqiMessageQueueAttributes.Authenticate; + } + break; + case "BasePriority": + basePriority = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue); + break; + case "Journal": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + attributes |= (int)MqiMessageQueueAttributes.Journal; + } + else + { + attributes &= ~(int)MqiMessageQueueAttributes.Journal; + } + break; + case "JournalQuota": + journalQuota = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, int.MaxValue); + break; + case "Label": + label = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "MulticastAddress": + multicastAddress = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "PathName": + pathName = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "PrivLevel": + var privLevelAttr = this.ParseHelper.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.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, "MessageQueue", "PrivLevel", privLevelAttr, "none", "body", "optional")); + break; + } + break; + case "Quota": + quota = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, int.MaxValue); + break; + case "Transactional": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + attributes |= (int)MqiMessageQueueAttributes.Transactional; + } + else + { + attributes &= ~(int)MqiMessageQueueAttributes.Transactional; + } + break; + case "ServiceTypeGuid": + serviceTypeGuid = this.TryFormatGuidValue(this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib)); + break; + default: + this.ParseHelper.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, node, attrib); + } + } + + foreach (var child in node.Elements()) + { + if (this.Namespace == child.Name.Namespace) + { + switch (child.Name.LocalName) + { + case "MessageQueuePermission": + this.ParseMessageQueuePermissionElement(intermediate, section, child, componentId, id?.Id); + break; + default: + this.ParseHelper.UnexpectedElement(node, child); + break; + } + } + else + { + this.ParseHelper.ParseExtensionElement(this.Context.Extensions, intermediate, section, node, child); + } + } + + var symbol = section.AddSymbol(new MessageQueueSymbol(sourceLineNumbers, id) + { + ComponentRef = componentId, + Label = label, + MulticastAddress = multicastAddress, + PathName = pathName, + ServiceTypeGuid = serviceTypeGuid, + Attributes = attributes, + }); + + if (CompilerConstants.IntegerNotSet != basePriority) + { + symbol.BasePriority = basePriority; + } + if (CompilerConstants.IntegerNotSet != journalQuota) + { + symbol.JournalQuota = journalQuota; + } + + if (CompilerConstants.IntegerNotSet != privLevel) + { + symbol.PrivLevel = privLevel; + } + if (CompilerConstants.IntegerNotSet != quota) + { + symbol.Quota = quota; + } + + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.CustomAction, "MessageQueuingInstall"); + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.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(Intermediate intermediate, IntermediateSection section, XElement node, string componentId, string messageQueueId) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(node); + + Identifier id = null; + string user = null; + string group = null; + int permissions = 0; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "MessageQueue": + if (null != messageQueueId) + { + this.Messaging.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, node.Parent.Name.LocalName)); + } + messageQueueId = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, MsmqSymbolDefinitions.MessageQueue, messageQueueId); + break; + case "User": + if (null != group) + { + this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "User", "Group")); + } + user = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, "User", user); + break; + case "Group": + if (null != user) + { + this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Group", "User")); + } + group = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, "Group", group); + break; + case "DeleteMessage": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + permissions |= (int)MqiMessageQueuePermission.DeleteMessage; + } + else + { + permissions &= ~(int)MqiMessageQueuePermission.DeleteMessage; + } + break; + case "PeekMessage": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + permissions |= (int)MqiMessageQueuePermission.PeekMessage; + } + else + { + permissions &= ~(int)MqiMessageQueuePermission.PeekMessage; + } + break; + case "WriteMessage": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + permissions |= (int)MqiMessageQueuePermission.WriteMessage; + } + else + { + permissions &= ~(int)MqiMessageQueuePermission.WriteMessage; + } + break; + case "DeleteJournalMessage": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + permissions |= (int)MqiMessageQueuePermission.DeleteJournalMessage; + } + else + { + permissions &= ~(int)MqiMessageQueuePermission.DeleteJournalMessage; + } + break; + case "SetQueueProperties": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + permissions |= (int)MqiMessageQueuePermission.SetQueueProperties; + } + else + { + permissions &= ~(int)MqiMessageQueuePermission.SetQueueProperties; + } + break; + case "GetQueueProperties": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + permissions |= (int)MqiMessageQueuePermission.GetQueueProperties; + } + else + { + permissions &= ~(int)MqiMessageQueuePermission.GetQueueProperties; + } + break; + case "DeleteQueue": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + permissions |= (int)MqiMessageQueuePermission.DeleteQueue; + } + else + { + permissions &= ~(int)MqiMessageQueuePermission.DeleteQueue; + } + break; + case "GetQueuePermissions": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + permissions |= (int)MqiMessageQueuePermission.GetQueuePermissions; + } + else + { + permissions &= ~(int)MqiMessageQueuePermission.GetQueuePermissions; + } + break; + case "ChangeQueuePermissions": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + permissions |= (int)MqiMessageQueuePermission.ChangeQueuePermissions; + } + else + { + permissions &= ~(int)MqiMessageQueuePermission.ChangeQueuePermissions; + } + break; + case "TakeQueueOwnership": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + permissions |= (int)MqiMessageQueuePermission.TakeQueueOwnership; + } + else + { + permissions &= ~(int)MqiMessageQueuePermission.TakeQueueOwnership; + } + break; + case "ReceiveMessage": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + permissions |= (int)MqiMessageQueuePermission.ReceiveMessage; + } + else + { + permissions &= ~(int)MqiMessageQueuePermission.ReceiveMessage; + } + break; + case "ReceiveJournalMessage": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + permissions |= (int)MqiMessageQueuePermission.ReceiveJournalMessage; + } + else + { + permissions &= ~(int)MqiMessageQueuePermission.ReceiveJournalMessage; + } + break; + case "QueueGenericRead": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + permissions |= (int)MqiMessageQueuePermission.QueueGenericRead; + } + else + { + permissions &= ~(int)MqiMessageQueuePermission.QueueGenericRead; + } + break; + case "QueueGenericWrite": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + permissions |= (int)MqiMessageQueuePermission.QueueGenericWrite; + } + else + { + permissions &= ~(int)MqiMessageQueuePermission.QueueGenericWrite; + } + break; + case "QueueGenericExecute": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + permissions |= (int)MqiMessageQueuePermission.QueueGenericExecute; + } + else + { + permissions &= ~(int)MqiMessageQueuePermission.QueueGenericExecute; + } + break; + case "QueueGenericAll": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + permissions |= (int)MqiMessageQueuePermission.QueueGenericAll; + } + else + { + permissions &= ~(int)MqiMessageQueuePermission.QueueGenericAll; + } + break; + default: + this.ParseHelper.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, node, attrib); + } + } + + if (null == id) + { + id = this.ParseHelper.CreateIdentifier("mqp", componentId, messageQueueId, user, group); + } + + if (null == messageQueueId) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "MessageQueue")); + } + if (null == user && null == group) + { + this.Messaging.Write(ErrorMessages.ExpectedAttributes(sourceLineNumbers, node.Name.LocalName, "User", "Group")); + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, node); + + if (null != user) + { + section.AddSymbol(new MessageQueueUserPermissionSymbol(sourceLineNumbers, id) + { + ComponentRef = componentId, + MessageQueueRef = messageQueueId, + UserRef = user, + Permissions = permissions, + }); + } + if (null != group) + { + section.AddSymbol(new MessageQueueGroupPermissionSymbol(sourceLineNumbers, id) + { + ComponentRef = componentId, + MessageQueueRef = messageQueueId, + GroupRef = group, + Permissions = 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) + { + if (!Guid.TryParse(val, out var guid)) + { + return val; + } + return guid.ToString("B").ToUpper(); + } + } +} diff --git a/src/ext/Msmq/wixext/MsmqDecompiler.cs b/src/ext/Msmq/wixext/MsmqDecompiler.cs new file mode 100644 index 00000000..aa8c34b6 --- /dev/null +++ b/src/ext/Msmq/wixext/MsmqDecompiler.cs @@ -0,0 +1,305 @@ +// 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.Msmq +{ +#if TODO_CONSIDER_DECOMPILER + 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; + } + } + } +#endif +} diff --git a/src/ext/Msmq/wixext/MsmqErrors.cs b/src/ext/Msmq/wixext/MsmqErrors.cs new file mode 100644 index 00000000..4342e1cf --- /dev/null +++ b/src/ext/Msmq/wixext/MsmqErrors.cs @@ -0,0 +1,71 @@ +// 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.Data +{ + using System; + using System.Resources; + + public static class MsmqErrors + { + public static Message IllegalAttributeWithoutComponent(SourceLineNumber sourceLineNumbers, string elementName, string attributeName) + { + return Message(sourceLineNumbers, Ids.IllegalAttributeWithoutComponent, "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.", elementName, attributeName); + } + + public static Message IllegalElementWithoutComponent(SourceLineNumber sourceLineNumbers, string elementName) + { + return Message(sourceLineNumbers, Ids.IllegalElementWithoutComponent, "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.", elementName); + } + + public static Message RequiredAttribute(SourceLineNumber sourceLineNumbers, string elementName, string attributeName1, string attributeName2) + { + return Message(sourceLineNumbers, Ids.RequiredAttribute, "A {0} element must have either a {1} attribute or a {2} attribute, or both set.", elementName, attributeName1, attributeName2); + } + + public static Message RequiredAttributeNotUnderComponent(SourceLineNumber sourceLineNumbers, string elementName, string attributeName1, string attributeName2) + { + return Message(sourceLineNumbers, Ids.RequiredAttributeNotUnderComponent, "A {0} element not nested under a component must have either a {1} attribute or a {2} attribute, or both set.", elementName, attributeName1, attributeName2); + } + + public static Message RequiredAttributeUnderComponent(SourceLineNumber sourceLineNumbers, string elementName, string attributeName) + { + return Message(sourceLineNumbers, Ids.RequiredAttributeUnderComponent, "The {0}/@{1} attribute must be provided when {0} element is nested under a component.", elementName, attributeName); + } + + public static Message UnexpectedAttributeWithOtherValue(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string otherAttributeName, string otherValue) + { + return Message(sourceLineNumbers, Ids.UnexpectedAttributeWithOtherValue, "The {0}/@{1} attribute cannot coexist with the {2} attribute's value of '{3}'.", elementName, attributeName, otherAttributeName, otherValue); + } + + public static Message UnexpectedAttributeWithOtherValue(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string value, string otherAttributeName, string otherValue) + { + return Message(sourceLineNumbers, Ids.UnexpectedAttributeWithOtherValue, "The {0}/@{1} attribute's value, '{2}', cannot coexist with the {3} attribute's value of '{4}'.", elementName, attributeName, value, otherAttributeName, otherValue); + } + + public static Message UnexpectedAttributeWithoutOtherValue(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string otherAttributeName, string otherValue) + { + return Message(sourceLineNumbers, Ids.UnexpectedAttributeWithoutOtherValue, "The {0}/@{1} cannot be provided unless the {2} attribute is provided with a value of '{3}'.", elementName, attributeName, otherAttributeName, otherValue); + } + + private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args) + { + return new Message(sourceLineNumber, MessageLevel.Error, (int)id, format, args); + } + + private static Message Message(SourceLineNumber sourceLineNumber, Ids id, ResourceManager resourceManager, string resourceName, params object[] args) + { + return new Message(sourceLineNumber, MessageLevel.Error, (int)id, resourceManager, resourceName, args); + } + + public enum Ids + { + IllegalAttributeWithoutComponent = 6000, + IllegalElementWithoutComponent = 6001, + UnexpectedAttributeWithOtherValue = 6002, + UnexpectedAttributeWithoutOtherValue = 6003, + RequiredAttributeUnderComponent = 6004, + RequiredAttribute = 6005, + RequiredAttributeNotUnderComponent = 6006, + } + } +} diff --git a/src/ext/Msmq/wixext/MsmqExtensionData.cs b/src/ext/Msmq/wixext/MsmqExtensionData.cs new file mode 100644 index 00000000..91485724 --- /dev/null +++ b/src/ext/Msmq/wixext/MsmqExtensionData.cs @@ -0,0 +1,30 @@ +// 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.Msmq +{ + using WixToolset.Data; + using WixToolset.Extensibility; + + /// + /// The WiX Toolset MSMQ Extension. + /// + public sealed class MsmqExtensionData : BaseExtensionData + { + /// + /// Gets the default culture. + /// + /// The default culture. + public override string DefaultCulture => "en-US"; + + public override bool TryGetSymbolDefinitionByName(string name, out IntermediateSymbolDefinition symbolDefinition) + { + symbolDefinition = MsmqSymbolDefinitions.ByName(name); + return symbolDefinition != null; + } + + public override Intermediate GetLibrary(ISymbolDefinitionCreator symbolDefinitions) + { + return Intermediate.Load(typeof(MsmqExtensionData).Assembly, "WixToolset.Msmq.msmq.wixlib", symbolDefinitions); + } + } +} diff --git a/src/ext/Msmq/wixext/MsmqExtensionFactory.cs b/src/ext/Msmq/wixext/MsmqExtensionFactory.cs new file mode 100644 index 00000000..de9f786d --- /dev/null +++ b/src/ext/Msmq/wixext/MsmqExtensionFactory.cs @@ -0,0 +1,18 @@ +// 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.Msmq +{ + using System; + using System.Collections.Generic; + using WixToolset.Extensibility; + + public class MsmqExtensionFactory : BaseExtensionFactory + { + protected override IReadOnlyCollection ExtensionTypes => new[] + { + typeof(MsmqCompiler), + typeof(MsmqExtensionData), + typeof(MsmqWindowsInstallerBackendBinderExtension), + }; + } +} diff --git a/src/ext/Msmq/wixext/MsmqTableDefinitions.cs b/src/ext/Msmq/wixext/MsmqTableDefinitions.cs new file mode 100644 index 00000000..46e2dd10 --- /dev/null +++ b/src/ext/Msmq/wixext/MsmqTableDefinitions.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.Msmq +{ + using WixToolset.Data.WindowsInstaller; + + public static class MsmqTableDefinitions + { + public static readonly TableDefinition MessageQueue = new TableDefinition( + "MessageQueue", + MsmqSymbolDefinitions.MessageQueue, + new[] + { + new ColumnDefinition("MessageQueue", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Component_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, keyTable: "Component", keyColumn: 1, modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("BasePriority", ColumnType.Number, 4, primaryKey: false, nullable: true, ColumnCategory.Unknown), + new ColumnDefinition("JournalQuota", ColumnType.Number, 4, primaryKey: false, nullable: true, ColumnCategory.Unknown), + new ColumnDefinition("Label", ColumnType.Localized, 255, primaryKey: false, nullable: false, ColumnCategory.Formatted, modularizeType: ColumnModularizeType.Property), + new ColumnDefinition("MulticastAddress", ColumnType.String, 255, primaryKey: false, nullable: true, ColumnCategory.Formatted, modularizeType: ColumnModularizeType.Property), + new ColumnDefinition("PathName", ColumnType.String, 255, primaryKey: false, nullable: false, ColumnCategory.Formatted, modularizeType: ColumnModularizeType.Property), + new ColumnDefinition("PrivLevel", ColumnType.Number, 4, primaryKey: false, nullable: true, ColumnCategory.Unknown), + new ColumnDefinition("Quota", ColumnType.Number, 4, primaryKey: false, nullable: true, ColumnCategory.Unknown), + new ColumnDefinition("ServiceTypeGuid", ColumnType.String, 72, primaryKey: false, nullable: true, ColumnCategory.Formatted, modularizeType: ColumnModularizeType.Property), + new ColumnDefinition("Attributes", ColumnType.Number, 4, primaryKey: false, nullable: false, ColumnCategory.Unknown), + }, + symbolIdIsPrimaryKey: true + ); + + public static readonly TableDefinition MessageQueueUserPermission = new TableDefinition( + "MessageQueueUserPermission", + MsmqSymbolDefinitions.MessageQueueUserPermission, + new[] + { + new ColumnDefinition("MessageQueueUserPermission", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Component_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, keyTable: "Component", keyColumn: 1, modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("MessageQueue_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, keyTable: "MessageQueue", keyColumn: 1, modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("User_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Permissions", ColumnType.Number, 4, primaryKey: false, nullable: false, ColumnCategory.Unknown), + }, + symbolIdIsPrimaryKey: true + ); + + public static readonly TableDefinition MessageQueueGroupPermission = new TableDefinition( + "MessageQueueGroupPermission", + MsmqSymbolDefinitions.MessageQueueGroupPermission, + new[] + { + new ColumnDefinition("MessageQueueGroupPermission", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Component_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, keyTable: "Component", keyColumn: 1, modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("MessageQueue_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, keyTable: "MessageQueue", keyColumn: 1, modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Group_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Permissions", ColumnType.Number, 4, primaryKey: false, nullable: false, ColumnCategory.Unknown), + }, + symbolIdIsPrimaryKey: true + ); + + public static readonly TableDefinition[] All = new[] + { + MessageQueue, + MessageQueueUserPermission, + MessageQueueGroupPermission, + }; + } +} diff --git a/src/ext/Msmq/wixext/MsmqWarnings.cs b/src/ext/Msmq/wixext/MsmqWarnings.cs new file mode 100644 index 00000000..41d160e9 --- /dev/null +++ b/src/ext/Msmq/wixext/MsmqWarnings.cs @@ -0,0 +1,30 @@ +// 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.Data +{ + using System; + using System.Resources; + + public static class MsmqWarnings + { + public static Message MissingComponents(SourceLineNumber sourceLineNumbers) + { + return Message(sourceLineNumbers, Ids.MissingComponents, "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."); + } + + private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args) + { + return new Message(sourceLineNumber, MessageLevel.Warning, (int)id, format, args); + } + + private static Message Message(SourceLineNumber sourceLineNumber, Ids id, ResourceManager resourceManager, string resourceName, params object[] args) + { + return new Message(sourceLineNumber, MessageLevel.Warning, (int)id, resourceManager, resourceName, args); + } + + public enum Ids + { + MissingComponents = 6007, + } + } +} diff --git a/src/ext/Msmq/wixext/MsmqWindowsInstallerBackendExtension.cs b/src/ext/Msmq/wixext/MsmqWindowsInstallerBackendExtension.cs new file mode 100644 index 00000000..d317fb60 --- /dev/null +++ b/src/ext/Msmq/wixext/MsmqWindowsInstallerBackendExtension.cs @@ -0,0 +1,13 @@ +// 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.Msmq +{ + using System.Collections.Generic; + using WixToolset.Data.WindowsInstaller; + using WixToolset.Extensibility; + + public class MsmqWindowsInstallerBackendBinderExtension : BaseWindowsInstallerBackendBinderExtension + { + public override IReadOnlyCollection TableDefinitions => MsmqTableDefinitions.All; + } +} diff --git a/src/ext/Msmq/wixext/Symbols/MessageQueueGroupPermissionSymbol.cs b/src/ext/Msmq/wixext/Symbols/MessageQueueGroupPermissionSymbol.cs new file mode 100644 index 00000000..404c061c --- /dev/null +++ b/src/ext/Msmq/wixext/Symbols/MessageQueueGroupPermissionSymbol.cs @@ -0,0 +1,71 @@ +// 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.Msmq +{ + using WixToolset.Data; + using WixToolset.Msmq.Symbols; + + public static partial class MsmqSymbolDefinitions + { + public static readonly IntermediateSymbolDefinition MessageQueueGroupPermission = new IntermediateSymbolDefinition( + MsmqSymbolDefinitionType.MessageQueueGroupPermission.ToString(), + new[] + { + new IntermediateFieldDefinition(nameof(MessageQueueGroupPermissionSymbolFields.ComponentRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(MessageQueueGroupPermissionSymbolFields.MessageQueueRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(MessageQueueGroupPermissionSymbolFields.GroupRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(MessageQueueGroupPermissionSymbolFields.Permissions), IntermediateFieldType.Number), + }, + typeof(MessageQueueGroupPermissionSymbol)); + } +} + +namespace WixToolset.Msmq.Symbols +{ + using WixToolset.Data; + + public enum MessageQueueGroupPermissionSymbolFields + { + ComponentRef, + MessageQueueRef, + GroupRef, + Permissions, + } + + public class MessageQueueGroupPermissionSymbol : IntermediateSymbol + { + public MessageQueueGroupPermissionSymbol() : base(MsmqSymbolDefinitions.MessageQueueGroupPermission, null, null) + { + } + + public MessageQueueGroupPermissionSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(MsmqSymbolDefinitions.MessageQueueGroupPermission, sourceLineNumber, id) + { + } + + public IntermediateField this[MessageQueueGroupPermissionSymbolFields index] => this.Fields[(int)index]; + + public string ComponentRef + { + get => this.Fields[(int)MessageQueueGroupPermissionSymbolFields.ComponentRef].AsString(); + set => this.Set((int)MessageQueueGroupPermissionSymbolFields.ComponentRef, value); + } + + public string MessageQueueRef + { + get => this.Fields[(int)MessageQueueGroupPermissionSymbolFields.MessageQueueRef].AsString(); + set => this.Set((int)MessageQueueGroupPermissionSymbolFields.MessageQueueRef, value); + } + + public string GroupRef + { + get => this.Fields[(int)MessageQueueGroupPermissionSymbolFields.GroupRef].AsString(); + set => this.Set((int)MessageQueueGroupPermissionSymbolFields.GroupRef, value); + } + + public int Permissions + { + get => this.Fields[(int)MessageQueueGroupPermissionSymbolFields.Permissions].AsNumber(); + set => this.Set((int)MessageQueueGroupPermissionSymbolFields.Permissions, value); + } + } +} \ No newline at end of file diff --git a/src/ext/Msmq/wixext/Symbols/MessageQueueSymbol.cs b/src/ext/Msmq/wixext/Symbols/MessageQueueSymbol.cs new file mode 100644 index 00000000..b911f0ea --- /dev/null +++ b/src/ext/Msmq/wixext/Symbols/MessageQueueSymbol.cs @@ -0,0 +1,119 @@ +// 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.Msmq +{ + using WixToolset.Data; + using WixToolset.Msmq.Symbols; + + public static partial class MsmqSymbolDefinitions + { + public static readonly IntermediateSymbolDefinition MessageQueue = new IntermediateSymbolDefinition( + MsmqSymbolDefinitionType.MessageQueue.ToString(), + new[] + { + new IntermediateFieldDefinition(nameof(MessageQueueSymbolFields.ComponentRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(MessageQueueSymbolFields.BasePriority), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(MessageQueueSymbolFields.JournalQuota), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(MessageQueueSymbolFields.Label), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(MessageQueueSymbolFields.MulticastAddress), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(MessageQueueSymbolFields.PathName), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(MessageQueueSymbolFields.PrivLevel), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(MessageQueueSymbolFields.Quota), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(MessageQueueSymbolFields.ServiceTypeGuid), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(MessageQueueSymbolFields.Attributes), IntermediateFieldType.Number), + }, + typeof(MessageQueueSymbol)); + } +} + +namespace WixToolset.Msmq.Symbols +{ + using WixToolset.Data; + + public enum MessageQueueSymbolFields + { + ComponentRef, + BasePriority, + JournalQuota, + Label, + MulticastAddress, + PathName, + PrivLevel, + Quota, + ServiceTypeGuid, + Attributes, + } + + public class MessageQueueSymbol : IntermediateSymbol + { + public MessageQueueSymbol() : base(MsmqSymbolDefinitions.MessageQueue, null, null) + { + } + + public MessageQueueSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(MsmqSymbolDefinitions.MessageQueue, sourceLineNumber, id) + { + } + + public IntermediateField this[MessageQueueSymbolFields index] => this.Fields[(int)index]; + + public string ComponentRef + { + get => this.Fields[(int)MessageQueueSymbolFields.ComponentRef].AsString(); + set => this.Set((int)MessageQueueSymbolFields.ComponentRef, value); + } + + public int? BasePriority + { + get => this.Fields[(int)MessageQueueSymbolFields.BasePriority].AsNullableNumber(); + set => this.Set((int)MessageQueueSymbolFields.BasePriority, value); + } + + public int? JournalQuota + { + get => this.Fields[(int)MessageQueueSymbolFields.JournalQuota].AsNullableNumber(); + set => this.Set((int)MessageQueueSymbolFields.JournalQuota, value); + } + + public string Label + { + get => this.Fields[(int)MessageQueueSymbolFields.Label].AsString(); + set => this.Set((int)MessageQueueSymbolFields.Label, value); + } + + public string MulticastAddress + { + get => this.Fields[(int)MessageQueueSymbolFields.MulticastAddress].AsString(); + set => this.Set((int)MessageQueueSymbolFields.MulticastAddress, value); + } + + public string PathName + { + get => this.Fields[(int)MessageQueueSymbolFields.PathName].AsString(); + set => this.Set((int)MessageQueueSymbolFields.PathName, value); + } + + public int? PrivLevel + { + get => this.Fields[(int)MessageQueueSymbolFields.PrivLevel].AsNullableNumber(); + set => this.Set((int)MessageQueueSymbolFields.PrivLevel, value); + } + + public int? Quota + { + get => this.Fields[(int)MessageQueueSymbolFields.Quota].AsNullableNumber(); + set => this.Set((int)MessageQueueSymbolFields.Quota, value); + } + + public string ServiceTypeGuid + { + get => this.Fields[(int)MessageQueueSymbolFields.ServiceTypeGuid].AsString(); + set => this.Set((int)MessageQueueSymbolFields.ServiceTypeGuid, value); + } + + public int Attributes + { + get => this.Fields[(int)MessageQueueSymbolFields.Attributes].AsNumber(); + set => this.Set((int)MessageQueueSymbolFields.Attributes, value); + } + } +} \ No newline at end of file diff --git a/src/ext/Msmq/wixext/Symbols/MessageQueueUserPermissionSymbol.cs b/src/ext/Msmq/wixext/Symbols/MessageQueueUserPermissionSymbol.cs new file mode 100644 index 00000000..cc783845 --- /dev/null +++ b/src/ext/Msmq/wixext/Symbols/MessageQueueUserPermissionSymbol.cs @@ -0,0 +1,71 @@ +// 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.Msmq +{ + using WixToolset.Data; + using WixToolset.Msmq.Symbols; + + public static partial class MsmqSymbolDefinitions + { + public static readonly IntermediateSymbolDefinition MessageQueueUserPermission = new IntermediateSymbolDefinition( + MsmqSymbolDefinitionType.MessageQueueUserPermission.ToString(), + new[] + { + new IntermediateFieldDefinition(nameof(MessageQueueUserPermissionSymbolFields.ComponentRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(MessageQueueUserPermissionSymbolFields.MessageQueueRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(MessageQueueUserPermissionSymbolFields.UserRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(MessageQueueUserPermissionSymbolFields.Permissions), IntermediateFieldType.Number), + }, + typeof(MessageQueueUserPermissionSymbol)); + } +} + +namespace WixToolset.Msmq.Symbols +{ + using WixToolset.Data; + + public enum MessageQueueUserPermissionSymbolFields + { + ComponentRef, + MessageQueueRef, + UserRef, + Permissions, + } + + public class MessageQueueUserPermissionSymbol : IntermediateSymbol + { + public MessageQueueUserPermissionSymbol() : base(MsmqSymbolDefinitions.MessageQueueUserPermission, null, null) + { + } + + public MessageQueueUserPermissionSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(MsmqSymbolDefinitions.MessageQueueUserPermission, sourceLineNumber, id) + { + } + + public IntermediateField this[MessageQueueUserPermissionSymbolFields index] => this.Fields[(int)index]; + + public string ComponentRef + { + get => this.Fields[(int)MessageQueueUserPermissionSymbolFields.ComponentRef].AsString(); + set => this.Set((int)MessageQueueUserPermissionSymbolFields.ComponentRef, value); + } + + public string MessageQueueRef + { + get => this.Fields[(int)MessageQueueUserPermissionSymbolFields.MessageQueueRef].AsString(); + set => this.Set((int)MessageQueueUserPermissionSymbolFields.MessageQueueRef, value); + } + + public string UserRef + { + get => this.Fields[(int)MessageQueueUserPermissionSymbolFields.UserRef].AsString(); + set => this.Set((int)MessageQueueUserPermissionSymbolFields.UserRef, value); + } + + public int Permissions + { + get => this.Fields[(int)MessageQueueUserPermissionSymbolFields.Permissions].AsNumber(); + set => this.Set((int)MessageQueueUserPermissionSymbolFields.Permissions, value); + } + } +} \ No newline at end of file diff --git a/src/ext/Msmq/wixext/Symbols/MsmqSymbolDefinitions.cs b/src/ext/Msmq/wixext/Symbols/MsmqSymbolDefinitions.cs new file mode 100644 index 00000000..229417fe --- /dev/null +++ b/src/ext/Msmq/wixext/Symbols/MsmqSymbolDefinitions.cs @@ -0,0 +1,47 @@ +// 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.Msmq +{ + using System; + using WixToolset.Data; + + public enum MsmqSymbolDefinitionType + { + MessageQueue, + MessageQueueGroupPermission, + MessageQueueUserPermission, + } + + public static partial class MsmqSymbolDefinitions + { + public static readonly Version Version = new Version("4.0.0"); + + public static IntermediateSymbolDefinition ByName(string name) + { + if (!Enum.TryParse(name, out MsmqSymbolDefinitionType type)) + { + return null; + } + + return ByType(type); + } + + public static IntermediateSymbolDefinition ByType(MsmqSymbolDefinitionType type) + { + switch (type) + { + case MsmqSymbolDefinitionType.MessageQueue: + return MsmqSymbolDefinitions.MessageQueue; + + case MsmqSymbolDefinitionType.MessageQueueGroupPermission: + return MsmqSymbolDefinitions.MessageQueueGroupPermission; + + case MsmqSymbolDefinitionType.MessageQueueUserPermission: + return MsmqSymbolDefinitions.MessageQueueUserPermission; + + default: + throw new ArgumentOutOfRangeException(nameof(type)); + } + } + } +} diff --git a/src/ext/Msmq/wixext/WixToolset.Msmq.wixext.csproj b/src/ext/Msmq/wixext/WixToolset.Msmq.wixext.csproj new file mode 100644 index 00000000..4bd6a3f5 --- /dev/null +++ b/src/ext/Msmq/wixext/WixToolset.Msmq.wixext.csproj @@ -0,0 +1,30 @@ + + + + + + netstandard2.0 + WixToolset.Msmq + WiX Toolset MSMQ Extension + WiX Toolset MSMQ Extension + true + build + + + + + + + + + + + + + + + + + + + diff --git a/src/ext/Msmq/wixext/WixToolset.Msmq.wixext.targets b/src/ext/Msmq/wixext/WixToolset.Msmq.wixext.targets new file mode 100644 index 00000000..5f69fe48 --- /dev/null +++ b/src/ext/Msmq/wixext/WixToolset.Msmq.wixext.targets @@ -0,0 +1,11 @@ + + + + + + $(MSBuildThisFileDirectory)..\tools\WixToolset.Msmq.wixext.dll + + + + + diff --git a/src/ext/Msmq/wixlib/MsmqExtension.wxs b/src/ext/Msmq/wixlib/MsmqExtension.wxs new file mode 100644 index 00000000..86239545 --- /dev/null +++ b/src/ext/Msmq/wixlib/MsmqExtension.wxs @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ext/Msmq/wixlib/caerr.wxi b/src/ext/Msmq/wixlib/caerr.wxi new file mode 100644 index 00000000..ff7ec121 --- /dev/null +++ b/src/ext/Msmq/wixlib/caerr.wxi @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/ext/Msmq/wixlib/en-us.wxl b/src/ext/Msmq/wixlib/en-us.wxl new file mode 100644 index 00000000..ebe08095 --- /dev/null +++ b/src/ext/Msmq/wixlib/en-us.wxl @@ -0,0 +1,10 @@ + + + + + + Configuring message queues + Queue: [1] + Configuring message queues + Queue: [1] + diff --git a/src/ext/Msmq/wixlib/ja-jp.wxl b/src/ext/Msmq/wixlib/ja-jp.wxl new file mode 100644 index 00000000..d56cd7ec --- /dev/null +++ b/src/ext/Msmq/wixlib/ja-jp.wxl @@ -0,0 +1,10 @@ + + + + + + メッセージ キューを構成しています + キュー: [1] + メッセージ キューを構成しています + キュー: [1] + diff --git a/src/ext/Msmq/wixlib/msmq.wixproj b/src/ext/Msmq/wixlib/msmq.wixproj new file mode 100644 index 00000000..ccccbf49 --- /dev/null +++ b/src/ext/Msmq/wixlib/msmq.wixproj @@ -0,0 +1,18 @@ + + + + + Library + true + en-us + + + + + + + + + + + diff --git a/src/ext/global.json b/src/ext/global.json new file mode 100644 index 00000000..23dd3fa6 --- /dev/null +++ b/src/ext/global.json @@ -0,0 +1,5 @@ +{ + "msbuild-sdks": { + "WixToolset.Sdk": "4.0.0-build-0211" + } +} -- cgit v1.2.3-55-g6feb