From 0afd76e4c5d46f237591d860e7d445e267522187 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Mon, 30 Mar 2020 19:46:56 +1000 Subject: Add DetectSHA2Support "search". --- nuget.config | 2 + src/be/UtilBundleExtension.cpp | 87 ++++++ src/be/UtilBundleExtension.h | 16 + src/be/detectsha2support.cpp | 58 ++++ src/be/detectsha2support.h | 8 + src/be/packages.config | 1 + src/be/precomp.h | 23 +- src/be/utilbe.cpp | 28 +- src/be/utilbe.vcxproj | 20 +- src/be/utilsearch.cpp | 137 ++++++++ src/be/utilsearch.h | 52 +++ .../TestData/BundleWithSearches/Bundle.wxs | 5 + .../WixToolsetTest.Util/UtilExtensionFixture.cs | 55 ++-- src/wixext/Tuples/UtilTupleDefinitions.cs | 10 + src/wixext/Tuples/WixDetectSHA2SupportTuple.cs | 33 ++ src/wixext/UtilBinder.cs | 347 --------------------- src/wixext/UtilCompiler.cs | 198 ++++++------ src/wixext/UtilConstants.cs | 2 + src/wixext/UtilErrors.cs | 6 - src/wixext/util.xsd | 24 ++ 20 files changed, 611 insertions(+), 501 deletions(-) create mode 100644 src/be/UtilBundleExtension.cpp create mode 100644 src/be/UtilBundleExtension.h create mode 100644 src/be/detectsha2support.cpp create mode 100644 src/be/detectsha2support.h create mode 100644 src/be/utilsearch.cpp create mode 100644 src/be/utilsearch.h create mode 100644 src/wixext/Tuples/WixDetectSHA2SupportTuple.cs delete mode 100644 src/wixext/UtilBinder.cs diff --git a/nuget.config b/nuget.config index aaee3228..80101d94 100644 --- a/nuget.config +++ b/nuget.config @@ -2,6 +2,8 @@ + + diff --git a/src/be/UtilBundleExtension.cpp b/src/be/UtilBundleExtension.cpp new file mode 100644 index 00000000..2ac842a5 --- /dev/null +++ b/src/be/UtilBundleExtension.cpp @@ -0,0 +1,87 @@ +// 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" +#include "BextBaseBundleExtension.h" + +class CWixUtilBundleExtension : public CBextBaseBundleExtension +{ +public: // IBundleExtension + virtual STDMETHODIMP Search( + __in LPCWSTR wzId, + __in LPCWSTR wzVariable + ) + { + HRESULT hr = S_OK; + + hr = UtilSearchExecute(&m_searches, wzId, wzVariable, m_pEngine); + + return hr; + } + +public: //CBextBaseBundleExtension + virtual STDMETHODIMP Initialize( + __in const BUNDLE_EXTENSION_CREATE_ARGS* pCreateArgs + ) + { + HRESULT hr = S_OK; + IXMLDOMDocument* pixdManifest = NULL; + IXMLDOMNode* pixnBundleExtension = NULL; + + hr = CBextBaseBundleExtension::Initialize(pCreateArgs); + ExitOnFailure(hr, "CBextBaseBundleExtension initialization failed."); + + hr = XmlLoadDocumentFromFile(m_sczBundleExtensionDataPath, &pixdManifest); + ExitOnFailure(hr, "Failed to load bundle extension manifest from path: %ls", m_sczBundleExtensionDataPath); + + hr = BextGetBundleExtensionDataNode(pixdManifest, UTIL_BUNDLE_EXTENSION_ID, &pixnBundleExtension); + ExitOnFailure(hr, "Failed to get BundleExtension '%ls'", UTIL_BUNDLE_EXTENSION_ID); + + hr = UtilSearchParseFromXml(&m_searches, pixnBundleExtension); + ExitOnFailure(hr, "Failed to parse searches from bundle extension manifest."); + + LExit: + ReleaseObject(pixnBundleExtension); + ReleaseObject(pixdManifest); + + return hr; + } + +public: + CWixUtilBundleExtension( + __in IBundleExtensionEngine* pEngine + ) : CBextBaseBundleExtension(pEngine) + { + m_searches = { }; + } + + ~CWixUtilBundleExtension() + { + UtilSearchUninitialize(&m_searches); + } + +private: + UTIL_SEARCHES m_searches; +}; + +HRESULT UtilBundleExtensionCreate( + __in IBundleExtensionEngine* pEngine, + __in const BUNDLE_EXTENSION_CREATE_ARGS* pArgs, + __out IBundleExtension** ppBundleExtension + ) +{ + HRESULT hr = S_OK; + CWixUtilBundleExtension* pExtension = NULL; + + pExtension = new CWixUtilBundleExtension(pEngine); + ExitOnNull(pExtension, hr, E_OUTOFMEMORY, "Failed to create new CWixUtilBundleExtension."); + + hr = pExtension->Initialize(pArgs); + ExitOnFailure(hr, "CWixUtilBundleExtension initialization failed"); + + *ppBundleExtension = pExtension; + pExtension = NULL; + +LExit: + ReleaseObject(pExtension); + return hr; +} diff --git a/src/be/UtilBundleExtension.h b/src/be/UtilBundleExtension.h new file mode 100644 index 00000000..16c5b346 --- /dev/null +++ b/src/be/UtilBundleExtension.h @@ -0,0 +1,16 @@ +#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. + + +// constants + +#define UTIL_BUNDLE_EXTENSION_ID L"WixUtilBundleExtension" + + +// function declarations + +HRESULT UtilBundleExtensionCreate( + __in IBundleExtensionEngine* pEngine, + __in const BUNDLE_EXTENSION_CREATE_ARGS* pArgs, + __out IBundleExtension** ppBundleExtension + ); diff --git a/src/be/detectsha2support.cpp b/src/be/detectsha2support.cpp new file mode 100644 index 00000000..f1f3637e --- /dev/null +++ b/src/be/detectsha2support.cpp @@ -0,0 +1,58 @@ +// 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" + +// https://gist.github.com/navossoc/7572c7d82243e9f818989e2765e7793a +HRESULT DetectSHA2Support( + __out BOOL* pfSupported + ) +{ + HRESULT hr = S_OK; + HMODULE hModule = NULL; + FARPROC pfn = NULL; + DWORD er = ERROR_SUCCESS; + + hr = LoadSystemLibrary(L"wintrust.dll", &hModule); + ExitOnFailure(hr, "Failed to load wintrust.dll"); + + pfn = ::GetProcAddress(hModule, "CryptCATAdminAcquireContext2"); + if (pfn) + { + *pfSupported = TRUE; + ExitFunction1(hr = S_OK); + } + + er = ::GetLastError(); + if (er == ERROR_PROC_NOT_FOUND) + { + *pfSupported = FALSE; + ExitFunction1(hr = S_OK); + } + + hr = HRESULT_FROM_WIN32(er); + ExitOnFailure(hr, "Failed to probe for CryptCATAdminAcquireContext2 in wintrust.dll"); + +LExit: + ::FreeLibrary(hModule); + + return hr; +} + +HRESULT UtilPerformDetectSHA2Support( + __in LPCWSTR wzVariable, + __in UTIL_SEARCH* /*pSearch*/, + __in IBundleExtensionEngine* pEngine + ) +{ + HRESULT hr = S_OK; + BOOL fSupported = FALSE; + + hr = DetectSHA2Support(&fSupported); + ExitOnFailure(hr, "DetectSHA2Support failed."); + + hr = pEngine->SetVariableNumeric(wzVariable, fSupported ? 1 : 0); + ExitOnFailure(hr, "Failed to set variable '%ls'", wzVariable); + +LExit: + return hr; +} diff --git a/src/be/detectsha2support.h b/src/be/detectsha2support.h new file mode 100644 index 00000000..7f1f6031 --- /dev/null +++ b/src/be/detectsha2support.h @@ -0,0 +1,8 @@ +#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 UtilPerformDetectSHA2Support( + __in LPCWSTR wzVariable, + __in UTIL_SEARCH* pSearch, + __in IBundleExtensionEngine* pEngine + ); \ No newline at end of file diff --git a/src/be/packages.config b/src/be/packages.config index f05546c5..ca23641f 100644 --- a/src/be/packages.config +++ b/src/be/packages.config @@ -1,5 +1,6 @@  + \ No newline at end of file diff --git a/src/be/precomp.h b/src/be/precomp.h index 9dc3c468..a4ab7abf 100644 --- a/src/be/precomp.h +++ b/src/be/precomp.h @@ -17,9 +17,20 @@ #define MAXUINT USHRT_MAX -#include "dutil.h" -#include "memutil.h" - -#include "BootstrapperEngine.h" -#include "BundleExtensionEngine.h" -#include "BundleExtension.h" +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "utilsearch.h" +#include "detectsha2support.h" +#include "UtilBundleExtension.h" diff --git a/src/be/utilbe.cpp b/src/be/utilbe.cpp index 370669dd..d9816dc7 100644 --- a/src/be/utilbe.cpp +++ b/src/be/utilbe.cpp @@ -1,19 +1,41 @@ // 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" +#include "BextBaseBundleExtensionProc.h" + +static IBundleExtension* vpBundleExtension = NULL; // function definitions extern "C" HRESULT WINAPI BundleExtensionCreate( - __in const BUNDLE_EXTENSION_CREATE_ARGS* /*pArgs*/, - __inout BUNDLE_EXTENSION_CREATE_RESULTS* /*pResults*/ + __in const BUNDLE_EXTENSION_CREATE_ARGS* pArgs, + __inout BUNDLE_EXTENSION_CREATE_RESULTS* pResults ) { - HRESULT hr = S_OK; + HRESULT hr = S_OK; + IBundleExtensionEngine* pEngine = NULL; + + hr = XmlInitialize(); + ExitOnFailure(hr, "Failed to initialize XML."); + + hr = BextInitializeFromCreateArgs(pArgs, &pEngine); + ExitOnFailure(hr, "Failed to initialize bext"); + + hr = UtilBundleExtensionCreate(pEngine, pArgs, &vpBundleExtension); + BextExitOnFailure(hr, "Failed to create WixUtilBundleExtension"); + + pResults->pfnBundleExtensionProc = BextBaseBundleExtensionProc; + pResults->pvBundleExtensionProcContext = vpBundleExtension; + +LExit: + ReleaseObject(pEngine); return hr; } extern "C" void WINAPI BundleExtensionDestroy() { + BextUninitialize(); + ReleaseNullObject(vpBundleExtension); + XmlUninitialize(); } \ No newline at end of file diff --git a/src/be/utilbe.vcxproj b/src/be/utilbe.vcxproj index 963eac7f..e64d113e 100644 --- a/src/be/utilbe.vcxproj +++ b/src/be/utilbe.vcxproj @@ -1,10 +1,9 @@ - + - Debug @@ -15,7 +14,6 @@ Win32 - {630C1EE7-2517-4A8C-83E3-DA1150308B58} DynamicLibrary @@ -25,37 +23,37 @@ utilbe.def WiX Toolset Util BundleExtension - - 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}. + - + \ No newline at end of file diff --git a/src/be/utilsearch.cpp b/src/be/utilsearch.cpp new file mode 100644 index 00000000..4e9d86a1 --- /dev/null +++ b/src/be/utilsearch.cpp @@ -0,0 +1,137 @@ +// 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" + + +STDMETHODIMP UtilSearchParseFromXml( + __in UTIL_SEARCHES* pSearches, + __in IXMLDOMNode* pixnBundleExtension + ) +{ + HRESULT hr = S_OK; + IXMLDOMNodeList* pixnNodes = NULL; + IXMLDOMNode* pixnNode = NULL; + DWORD cNodes = 0; + BSTR bstrNodeName = NULL; + + // Select Util search nodes. + hr = XmlSelectNodes(pixnBundleExtension, L"WixDetectSHA2Support", &pixnNodes); + ExitOnFailure(hr, "Failed to select Util search nodes."); + + // Get Util search node count. + hr = pixnNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get Util search node count."); + + if (!cNodes) + { + ExitFunction(); + } + + // Allocate memory for searches. + pSearches->rgSearches = (UTIL_SEARCH*)MemAlloc(sizeof(UTIL_SEARCH) * cNodes, TRUE); + ExitOnNull(pSearches->rgSearches, hr, E_OUTOFMEMORY, "Failed to allocate memory for search structs."); + + pSearches->cSearches = cNodes; + + // Parse search elements. + for (DWORD i = 0; i < cNodes; ++i) + { + UTIL_SEARCH* pSearch = &pSearches->rgSearches[i]; + + hr = XmlNextElement(pixnNodes, &pixnNode, &bstrNodeName); + ExitOnFailure(hr, "Failed to get next node."); + + // @Id + hr = XmlGetAttributeEx(pixnNode, L"Id", &pSearch->sczId); + ExitOnFailure(hr, "Failed to get @Id."); + + // Read type specific attributes. + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"WixDetectSHA2Support", -1)) + { + pSearch->Type = UTIL_SEARCH_TYPE_DETECT_SHA2_SUPPORT; + } + else + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Unexpected element name: %ls", bstrNodeName); + } + + // prepare next iteration + ReleaseNullObject(pixnNode); + ReleaseNullBSTR(bstrNodeName); + } + +LExit: + ReleaseBSTR(bstrNodeName); + ReleaseObject(pixnNode); + ReleaseObject(pixnNodes); + + return hr; +} + +void UtilSearchUninitialize( + __in UTIL_SEARCHES* pSearches + ) +{ + if (pSearches->rgSearches) + { + for (DWORD i = 0; i < pSearches->cSearches; ++i) + { + UTIL_SEARCH* pSearch = &pSearches->rgSearches[i]; + + ReleaseStr(pSearch->sczId); + } + MemFree(pSearches->rgSearches); + } +} + +STDMETHODIMP UtilSearchExecute( + __in UTIL_SEARCHES* pSearches, + __in LPCWSTR wzSearchId, + __in LPCWSTR wzVariable, + __in IBundleExtensionEngine* pEngine + ) +{ + HRESULT hr = S_OK; + UTIL_SEARCH* pSearch = NULL; + + hr = UtilSearchFindById(pSearches, wzSearchId, &pSearch); + ExitOnFailure(hr, "Search id '%ls' is unknown to the util extension."); + + switch (pSearch->Type) + { + case UTIL_SEARCH_TYPE_DETECT_SHA2_SUPPORT: + hr = UtilPerformDetectSHA2Support(wzVariable, pSearch, pEngine); + break; + default: + hr = E_UNEXPECTED; + } + +LExit: + return hr; +} + +STDMETHODIMP UtilSearchFindById( + __in UTIL_SEARCHES* pSearches, + __in LPCWSTR wzId, + __out UTIL_SEARCH** ppSearch + ) +{ + HRESULT hr = S_OK; + + for (DWORD i = 0; i < pSearches->cSearches; ++i) + { + UTIL_SEARCH* pSearch = &pSearches->rgSearches[i]; + + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pSearch->sczId, -1, wzId, -1)) + { + *ppSearch = pSearch; + ExitFunction1(hr = S_OK); + } + } + + hr = E_NOTFOUND; + +LExit: + return hr; +} diff --git a/src/be/utilsearch.h b/src/be/utilsearch.h new file mode 100644 index 00000000..1e0ca96d --- /dev/null +++ b/src/be/utilsearch.h @@ -0,0 +1,52 @@ +#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. + + +// constants + +enum UTIL_SEARCH_TYPE +{ + UTIL_SEARCH_TYPE_NONE, + UTIL_SEARCH_TYPE_DETECT_SHA2_SUPPORT, +}; + + +// structs + +typedef struct _UTIL_SEARCH +{ + LPWSTR sczId; + + UTIL_SEARCH_TYPE Type; +} UTIL_SEARCH; + +typedef struct _UTIL_SEARCHES +{ + UTIL_SEARCH* rgSearches; + DWORD cSearches; +} UTIL_SEARCHES; + + +// function declarations + +STDMETHODIMP UtilSearchParseFromXml( + __in UTIL_SEARCHES* pSearches, + __in IXMLDOMNode* pixnBundleExtension + ); + +void UtilSearchUninitialize( + __in UTIL_SEARCHES* pSearches + ); + +STDMETHODIMP UtilSearchExecute( + __in UTIL_SEARCHES* pSearches, + __in LPCWSTR wzSearchId, + __in LPCWSTR wzVariable, + __in IBundleExtensionEngine* pEngine + ); + +STDMETHODIMP UtilSearchFindById( + __in UTIL_SEARCHES* pSearches, + __in LPCWSTR wzId, + __out UTIL_SEARCH** ppSearch + ); diff --git a/src/test/WixToolsetTest.Util/TestData/BundleWithSearches/Bundle.wxs b/src/test/WixToolsetTest.Util/TestData/BundleWithSearches/Bundle.wxs index cafd4255..82646b37 100644 --- a/src/test/WixToolsetTest.Util/TestData/BundleWithSearches/Bundle.wxs +++ b/src/test/WixToolsetTest.Util/TestData/BundleWithSearches/Bundle.wxs @@ -7,6 +7,7 @@ + @@ -32,4 +33,8 @@ + + + + diff --git a/src/test/WixToolsetTest.Util/UtilExtensionFixture.cs b/src/test/WixToolsetTest.Util/UtilExtensionFixture.cs index 8f33eff5..c5b370ac 100644 --- a/src/test/WixToolsetTest.Util/UtilExtensionFixture.cs +++ b/src/test/WixToolsetTest.Util/UtilExtensionFixture.cs @@ -146,6 +146,9 @@ namespace WixToolsetTest.Util { var baseFolder = fs.GetFolder(); var intermediateFolder = Path.Combine(baseFolder, "obj"); + var bundlePath = Path.Combine(baseFolder, @"bin\test.exe"); + var baFolderPath = Path.Combine(baseFolder, "ba"); + var extractFolderPath = Path.Combine(baseFolder, "extract"); var result = WixRunner.Execute(new[] { @@ -156,45 +159,35 @@ namespace WixToolsetTest.Util "-bindpath", Path.Combine(folder, "data"), "-intermediateFolder", intermediateFolder, "-burnStub", burnStubPath, - "-o", Path.Combine(baseFolder, @"bin\test.exe") + "-o", bundlePath }); result.AssertSuccess(); - Assert.True(File.Exists(Path.Combine(baseFolder, @"bin\test.exe"))); + Assert.True(File.Exists(bundlePath)); #if TODO Assert.True(File.Exists(Path.Combine(baseFolder, @"bin\test.wixpdb"))); #endif - var intermediate = Intermediate.Load(Path.Combine(intermediateFolder, @"test.wir")); - var section = intermediate.Sections.Single(); - - var searchTuples = section.Tuples.OfType().OrderBy(t => t.Id.Id).ToList(); - Assert.Equal(3, searchTuples.Count); - Assert.Equal("FileSearchId", searchTuples[0].Id.Id); - Assert.Equal("FileSearchVariable", searchTuples[0].Variable); - Assert.Equal("ProductSearchId", searchTuples[1].Id.Id); - Assert.Equal("ProductSearchVariable", searchTuples[1].Variable); - Assert.Equal("1 & 2 < 3", searchTuples[1].Condition); - Assert.Equal("RegistrySearchId", searchTuples[2].Id.Id); - Assert.Equal("RegistrySearchVariable", searchTuples[2].Variable); - - var fileSearchTuple = section.Tuples.OfType().Single(); - Assert.Equal("FileSearchId", fileSearchTuple.Id.Id); - Assert.Equal(@"%windir%\System32\mscoree.dll", fileSearchTuple.Path); - Assert.Equal(WixFileSearchAttributes.Default | WixFileSearchAttributes.WantExists, fileSearchTuple.Attributes); - - var productSearchTuple = section.Tuples.OfType().Single(); - Assert.Equal("ProductSearchId", productSearchTuple.Id.Id); - Assert.Equal("{738D02BF-E231-4370-8209-E9FD4E1BE2A1}", productSearchTuple.Guid); - Assert.Equal(WixProductSearchAttributes.Version | WixProductSearchAttributes.UpgradeCode, productSearchTuple.Attributes); - - var registrySearchTuple = section.Tuples.OfType().Single(); - Assert.Equal("RegistrySearchId", registrySearchTuple.Id.Id); - Assert.Equal(RegistryRootType.LocalMachine, registrySearchTuple.Root); - Assert.Equal(@"SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full", registrySearchTuple.Key); - Assert.Equal("Release", registrySearchTuple.Value); - Assert.Equal(WixRegistrySearchAttributes.WantValue | WixRegistrySearchAttributes.Raw, registrySearchTuple.Attributes); + var extractResult = BundleExtractor.ExtractBAContainer(null, bundlePath, baFolderPath, extractFolderPath); + extractResult.AssertSuccess(); + + var bundleExtensionDatas = extractResult.SelectBundleExtensionDataNodes("/be:BundleExtensionData/be:BundleExtension[@Id='WixUtilBundleExtension']"); + Assert.Equal(1, bundleExtensionDatas.Count); + Assert.Equal("" + + "" + + "", bundleExtensionDatas[0].GetTestXml()); + + var utilSearches = extractResult.SelectManifestNodes("/burn:BurnManifest/*[self::burn:ExtensionSearch or self::burn:FileSearch or self::burn:MsiProductSearch or self::burn:RegistrySearch]"); + Assert.Equal(4, utilSearches.Count); + Assert.Equal("", utilSearches[0].GetTestXml()); + Assert.Equal("", utilSearches[1].GetTestXml()); + Assert.Equal("", utilSearches[2].GetTestXml()); + Assert.Equal("", utilSearches[3].GetTestXml()); } } diff --git a/src/wixext/Tuples/UtilTupleDefinitions.cs b/src/wixext/Tuples/UtilTupleDefinitions.cs index 00c98337..ece64502 100644 --- a/src/wixext/Tuples/UtilTupleDefinitions.cs +++ b/src/wixext/Tuples/UtilTupleDefinitions.cs @@ -4,6 +4,7 @@ namespace WixToolset.Util { using System; using WixToolset.Data; + using WixToolset.Data.Burn; public enum UtilTupleDefinitionType { @@ -19,6 +20,7 @@ namespace WixToolset.Util User, UserGroup, WixCloseApplication, + WixDetectSHA2Support, WixFormatFiles, WixInternetShortcut, WixRemoveFolderEx, @@ -82,6 +84,9 @@ namespace WixToolset.Util case UtilTupleDefinitionType.WixCloseApplication: return UtilTupleDefinitions.WixCloseApplication; + case UtilTupleDefinitionType.WixDetectSHA2Support: + return UtilTupleDefinitions.WixDetectSHA2Support; + case UtilTupleDefinitionType.WixFormatFiles: return UtilTupleDefinitions.WixFormatFiles; @@ -107,5 +112,10 @@ namespace WixToolset.Util throw new ArgumentOutOfRangeException(nameof(type)); } } + + static UtilTupleDefinitions() + { + WixDetectSHA2Support.AddTag(BurnConstants.BundleExtensionSearchTupleDefinitionTag); + } } } diff --git a/src/wixext/Tuples/WixDetectSHA2SupportTuple.cs b/src/wixext/Tuples/WixDetectSHA2SupportTuple.cs new file mode 100644 index 00000000..38b76ff2 --- /dev/null +++ b/src/wixext/Tuples/WixDetectSHA2SupportTuple.cs @@ -0,0 +1,33 @@ +// 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.Util +{ + using WixToolset.Data; + using WixToolset.Util.Tuples; + + public static partial class UtilTupleDefinitions + { + public static readonly IntermediateTupleDefinition WixDetectSHA2Support = new IntermediateTupleDefinition( + UtilTupleDefinitionType.WixDetectSHA2Support.ToString(), + new IntermediateFieldDefinition[0], + typeof(WixDetectSHA2SupportTuple)); + } +} + +namespace WixToolset.Util.Tuples +{ + using WixToolset.Data; + + public class WixDetectSHA2SupportTuple : IntermediateTuple + { + public WixDetectSHA2SupportTuple() : base(UtilTupleDefinitions.WixDetectSHA2Support, null, null) + { + } + + public WixDetectSHA2SupportTuple(SourceLineNumber sourceLineNumber, Identifier id = null) : base(UtilTupleDefinitions.WixDetectSHA2Support, sourceLineNumber, id) + { + } + + public IntermediateField this[GroupTupleFields index] => this.Fields[(int)index]; + } +} diff --git a/src/wixext/UtilBinder.cs b/src/wixext/UtilBinder.cs deleted file mode 100644 index ef80a876..00000000 --- a/src/wixext/UtilBinder.cs +++ /dev/null @@ -1,347 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Extensions -{ -#if TODO_BRINGBACK_FOR_BUNDLES - using System; - using System.Collections.Generic; - using System.Globalization; - using WixToolset.Data; - using WixToolset.Extensibility; - - /// - /// The binder for the WiX Toolset Utility Extension. - /// - public sealed class UtilBinder : BinderExtension - { - // TODO: When WixSearch is supported in Product, etc, we may need to call - // ReorderWixSearch() from each of those initializers. - - // TODO: A general-purpose "reorder this table given these constraints" - // mechanism may end up being helpful. This could be declaratively stated - // in the table definitions, or exposed from the core Wix.dll and called - // as-needed by any extensions. - - /// - /// Called before bundle binding occurs. - /// - public override void Initialize(Output bundle) - { - if (OutputType.Bundle == bundle.Type) - { - this.ReorderWixSearch(bundle); - } - } - - /// - /// Reorders Any WixSearch items. - /// - /// Output containing the tables to process. - private void ReorderWixSearch(Output output) - { - Table wixSearchTable = output.Tables["WixSearch"]; - if (null == wixSearchTable || wixSearchTable.Rows.Count == 0) - { - // nothing to do! - return; - } - - RowDictionary rowDictionary = new RowDictionary(); - foreach (Row row in wixSearchTable.Rows) - { - rowDictionary.AddRow(row); - } - - Constraints constraints = new Constraints(); - Table wixSearchRelationTable = output.Tables["WixSearchRelation"]; - if (null != wixSearchRelationTable && wixSearchRelationTable.Rows.Count > 0) - { - // add relational info to our data... - foreach (Row row in wixSearchRelationTable.Rows) - { - constraints.AddConstraint((string)row[0], (string)row[1]); - } - } - - this.FindCircularReference(constraints); - - if (this.Core.EncounteredError) - { - return; - } - - this.FlattenDependentReferences(constraints); - - // Reorder by topographical sort (http://en.wikipedia.org/wiki/Topological_sorting) - // We use a variation of Kahn (1962) algorithm as described in - // Wikipedia, with the additional criteria that start nodes are sorted - // lexicographically at each step to ensure a deterministic ordering - // based on 'after' dependencies and ID. - TopologicalSort sorter = new TopologicalSort(); - List sortedIds = sorter.Sort(rowDictionary.Keys, constraints); - - // Now, re-write the table with the searches in order... - wixSearchTable.Rows.Clear(); - foreach (string id in sortedIds) - { - wixSearchTable.Rows.Add(rowDictionary[id]); - } - } - - /// - /// A dictionary of Row items, indexed by their first column. - /// - private class RowDictionary : Dictionary - { - public void AddRow(Row row) - { - this.Add((string)row[0], row); - } - - // TODO: Hide other Add methods? - } - - /// - /// A dictionary of constraints, mapping an id to a list of ids. - /// - private class Constraints : Dictionary> - { - public void AddConstraint(string id, string afterId) - { - if (!this.ContainsKey(id)) - { - this.Add(id, new List()); - } - - // TODO: Show warning if a constraint is seen twice? - if (!this[id].Contains(afterId)) - { - this[id].Add(afterId); - } - } - - // TODO: Hide other Add methods? - } - - /// - /// Finds circular references in the constraints. - /// - /// Constraints to check. - /// This is not particularly performant, but it works. - private void FindCircularReference(Constraints constraints) - { - foreach (string id in constraints.Keys) - { - List seenIds = new List(); - string chain = null; - if (FindCircularReference(constraints, id, id, seenIds, out chain)) - { - // We will show a separate message for every ID that's in - // the loop. We could bail after the first one, but then - // we wouldn't catch disjoint loops in a single run. - this.Core.OnMessage(UtilErrors.CircularSearchReference(chain)); - } - } - } - - /// - /// Recursive function that finds circular references in the constraints. - /// - /// Constraints to check. - /// The identifier currently being looking for. (Fixed across a given run.) - /// The idenifier curently being tested. - /// A list of identifiers seen, to ensure each identifier is only expanded once. - /// If a circular reference is found, will contain the chain of references. - /// True if a circular reference is found, false otherwise. - private bool FindCircularReference(Constraints constraints, string checkId, string currentId, List seenIds, out string chain) - { - chain = null; - List afterList = null; - if (constraints.TryGetValue(currentId, out afterList)) - { - foreach (string afterId in afterList) - { - if (afterId == checkId) - { - chain = String.Format(CultureInfo.InvariantCulture, "{0} -> {1}", currentId, afterId); - return true; - } - - if (!seenIds.Contains(afterId)) - { - seenIds.Add(afterId); - if (FindCircularReference(constraints, checkId, afterId, seenIds, out chain)) - { - chain = String.Format(CultureInfo.InvariantCulture, "{0} -> {1}", currentId, chain); - return true; - } - } - } - } - - return false; - } - - /// - /// Flattens any dependency chains to simplify reordering. - /// - /// - private void FlattenDependentReferences(Constraints constraints) - { - foreach (string id in constraints.Keys) - { - List flattenedIds = new List(); - AddDependentReferences(constraints, id, flattenedIds); - List constraintList = constraints[id]; - foreach (string flattenedId in flattenedIds) - { - if (!constraintList.Contains(flattenedId)) - { - constraintList.Add(flattenedId); - } - } - } - } - - /// - /// Adds dependent references to a list. - /// - /// - /// - /// - private void AddDependentReferences(Constraints constraints, string currentId, List seenIds) - { - List afterList = null; - if (constraints.TryGetValue(currentId, out afterList)) - { - foreach (string afterId in afterList) - { - if (!seenIds.Contains(afterId)) - { - seenIds.Add(afterId); - AddDependentReferences(constraints, afterId, seenIds); - } - } - } - } - - /// - /// Reorder by topological sort - /// - /// - /// We use a variation of Kahn (1962) algorithm as described in - /// Wikipedia (http://en.wikipedia.org/wiki/Topological_sorting), with - /// the additional criteria that start nodes are sorted lexicographically - /// at each step to ensure a deterministic ordering based on 'after' - /// dependencies and ID. - /// - private class TopologicalSort - { - private List startIds = new List(); - private Constraints constraints; - - /// - /// Reorder by topological sort - /// - /// The complete list of IDs. - /// Constraints to use. - /// The topologically sorted list of IDs. - internal List Sort(IEnumerable allIds, Constraints constraints) - { - this.startIds.Clear(); - this.CopyConstraints(constraints); - - this.FindInitialStartIds(allIds); - - // We always create a new sortedId list, because we return it - // to the caller and don't know what its lifetime may be. - List sortedIds = new List(); - - while (this.startIds.Count > 0) - { - this.SortStartIds(); - - string currentId = this.startIds[0]; - sortedIds.Add(currentId); - this.startIds.RemoveAt(0); - - this.ResolveConstraint(currentId); - } - - return sortedIds; - } - - /// - /// Copies a Constraints set (to prevent modifying the incoming data). - /// - /// Constraints to copy. - private void CopyConstraints(Constraints constraints) - { - this.constraints = new Constraints(); - foreach (string id in constraints.Keys) - { - foreach (string afterId in constraints[id]) - { - this.constraints.AddConstraint(id, afterId); - } - } - } - - /// - /// Finds initial start IDs. (Those with no constraints.) - /// - /// The complete list of IDs. - private void FindInitialStartIds(IEnumerable allIds) - { - foreach (string id in allIds) - { - if (!this.constraints.ContainsKey(id)) - { - this.startIds.Add(id); - } - } - } - - /// - /// Sorts start IDs. - /// - private void SortStartIds() - { - this.startIds.Sort(); - } - - /// - /// Removes the resolved constraint and updates the list of startIds - /// with any now-valid (all constraints resolved) IDs. - /// - /// The ID to resolve from the set of constraints. - private void ResolveConstraint(string resolvedId) - { - List newStartIds = new List(); - - foreach (string id in constraints.Keys) - { - if (this.constraints[id].Contains(resolvedId)) - { - this.constraints[id].Remove(resolvedId); - - // If we just removed the last constraint for this - // ID, it is now a valid start ID. - if (0 == this.constraints[id].Count) - { - newStartIds.Add(id); - } - } - } - - foreach (string id in newStartIds) - { - this.constraints.Remove(id); - } - - this.startIds.AddRange(newStartIds); - } - } - } -#endif -} diff --git a/src/wixext/UtilCompiler.cs b/src/wixext/UtilCompiler.cs index 6b359591..6e3c2531 100644 --- a/src/wixext/UtilCompiler.cs +++ b/src/wixext/UtilCompiler.cs @@ -230,6 +230,8 @@ namespace WixToolset.Util break; case "ComponentSearch": case "ComponentSearchRef": + case "DetectSHA2Support": + case "DetectSHA2SupportRef": case "DirectorySearch": case "DirectorySearchRef": case "FileSearch": @@ -251,6 +253,12 @@ namespace WixToolset.Util case "ComponentSearchRef": this.ParseComponentSearchRefElement(intermediate, section, element); break; + case "DetectSHA2Support": + this.ParseDetectSHA2SupportElement(intermediate, section, element); + break; + case "DetectSHA2SupportRef": + this.ParseDetectSHA2SupportRefElement(intermediate, section, element); + break; case "DirectorySearch": this.ParseDirectorySearchElement(intermediate, section, element); break; @@ -422,11 +430,6 @@ namespace WixToolset.Util } } - if (null == variable) - { - this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Variable")); - } - if (null == guid) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Guid")); @@ -439,16 +442,10 @@ namespace WixToolset.Util this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + this.ParseHelper.CreateWixSearchTuple(section, sourceLineNumbers, element.Name.LocalName, id, variable, condition, after, null); + if (!this.Messaging.EncounteredError) { - this.CreateWixSearchRow(section, sourceLineNumbers, id, variable, condition); - if (after != null) - { - this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, "WixSearch", after); - // TODO: We're currently defaulting to "always run after", which we will need to change... - this.CreateWixSearchRelationRow(section, sourceLineNumbers, id, after, 2); - } - WixComponentSearchAttributes attributes = WixComponentSearchAttributes.KeyPath; switch (result) { @@ -505,6 +502,89 @@ namespace WixToolset.Util this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); } + /// + /// Parses a DetectSHA2Support element. + /// + /// Element to parse. + private void ParseDetectSHA2SupportElement(Intermediate intermediate, IntermediateSection section, XElement element) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + string variable = null; + string condition = null; + string after = null; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + case "Variable": + case "Condition": + case "After": + this.ParseCommonSearchAttributes(sourceLineNumbers, attrib, ref id, ref variable, ref condition, ref after); + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (id == null) + { + id = this.ParseHelper.CreateIdentifier("wds2s", variable, condition, after); + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + + this.ParseHelper.CreateWixSearchTuple(section, sourceLineNumbers, element.Name.LocalName, id, variable, condition, after, UtilConstants.UtilBundleExtensionId); + + if (!this.Messaging.EncounteredError) + { + section.Tuples.Add(new WixDetectSHA2SupportTuple(sourceLineNumbers, id)); + } + } + + /// + /// Parses a DetectSHA2SupportRef element + /// + /// Element to parse. + private void ParseDetectSHA2SupportRefElement(Intermediate intermediate, IntermediateSection section, XElement element) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + string refId = null; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + refId = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, "WixDetectSHA2Support", refId); + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + } + /// /// Parses an event source element. /// @@ -868,11 +948,6 @@ namespace WixToolset.Util } } - if (null == variable) - { - this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Variable")); - } - if (null == path) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Path")); @@ -885,16 +960,10 @@ namespace WixToolset.Util this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + this.ParseHelper.CreateWixSearchTuple(section, sourceLineNumbers, element.Name.LocalName, id, variable, condition, after, null); + if (!this.Messaging.EncounteredError) { - this.CreateWixSearchRow(section, sourceLineNumbers, id, variable, condition); - if (after != null) - { - this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, "WixSearch", after); - // TODO: We're currently defaulting to "always run after", which we will need to change... - this.CreateWixSearchRelationRow(section, sourceLineNumbers, id, after, 2); - } - WixFileSearchAttributes attributes = WixFileSearchAttributes.IsDirectory; switch (result) { @@ -990,11 +1059,6 @@ namespace WixToolset.Util } } - if (null == variable) - { - this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Variable")); - } - if (null == path) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Path")); @@ -1007,16 +1071,10 @@ namespace WixToolset.Util this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, node); + this.ParseHelper.CreateWixSearchTuple(section, sourceLineNumbers, node.Name.LocalName, id, variable, condition, after, null); + if (!this.Messaging.EncounteredError) { - this.CreateWixSearchRow(section, sourceLineNumbers, id, variable, condition); - if (after != null) - { - this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, "WixSearch", after); - // TODO: We're currently defaulting to "always run after", which we will need to change... - this.CreateWixSearchRelationRow(section, sourceLineNumbers, id, after, 2); - } - WixFileSearchAttributes attributes = WixFileSearchAttributes.Default; switch (result) { @@ -1048,38 +1106,6 @@ namespace WixToolset.Util }); } - /// - /// Creates a row in the WixSearch table. - /// - /// Source line number for the parent element. - /// Identifier of the search. - /// The Burn variable to store the result into. - /// A condition to test before evaluating the search. - private void CreateWixSearchRow(IntermediateSection section, SourceLineNumber sourceLineNumbers, Identifier id, string variable, string condition) - { - section.Tuples.Add(new WixSearchTuple(sourceLineNumbers, id) - { - Variable = variable, - Condition = condition, - }); - } - - /// - /// - /// - /// Source line number for the parent element. - /// Identifier of the search (key into the WixSearch table) - /// Identifier of the search that comes before (key into the WixSearch table) - /// Further details about the relation between id and parentId. - private void CreateWixSearchRelationRow(IntermediateSection section, SourceLineNumber sourceLineNumbers, Identifier id, string parentId, int attributes) - { - section.Tuples.Add(new WixSearchRelationTuple(sourceLineNumbers, id) - { - ParentSearchRef = parentId, - Attributes = attributes, - }); - } - /// /// Parses a file share element. /// @@ -2523,11 +2549,6 @@ namespace WixToolset.Util } } - if (null == variable) - { - this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Variable")); - } - if (null == upgradeCode && null == productCode) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "ProductCode", "UpgradeCode", true)); @@ -2545,16 +2566,10 @@ namespace WixToolset.Util this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + this.ParseHelper.CreateWixSearchTuple(section, sourceLineNumbers, element.Name.LocalName, id, variable, condition, after, null); + if (!this.Messaging.EncounteredError) { - this.CreateWixSearchRow(section, sourceLineNumbers, id, variable, condition); - if (after != null) - { - this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, "WixSearch", after); - // TODO: We're currently defaulting to "always run after", which we will need to change... - this.CreateWixSearchRelationRow(section, sourceLineNumbers, id, after, 2); - } - WixProductSearchAttributes attributes = WixProductSearchAttributes.Version; switch (result) { @@ -2662,11 +2677,6 @@ namespace WixToolset.Util } } - if (null == variable) - { - this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Variable")); - } - if (!root.HasValue) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Root")); @@ -2726,16 +2736,10 @@ namespace WixToolset.Util this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + this.ParseHelper.CreateWixSearchTuple(section, sourceLineNumbers, element.Name.LocalName, id, variable, condition, after, null); + if (!this.Messaging.EncounteredError) { - this.CreateWixSearchRow(section, sourceLineNumbers, id, variable, condition); - if (after != null) - { - this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, "WixSearch", after); - // TODO: We're currently defaulting to "always run after", which we will need to change... - this.CreateWixSearchRelationRow(section, sourceLineNumbers, id, after, 2); - } - section.Tuples.Add(new WixRegistrySearchTuple(sourceLineNumbers, id) { Root = (RegistryRootType)root, diff --git a/src/wixext/UtilConstants.cs b/src/wixext/UtilConstants.cs index 28ff368f..d9ad460f 100644 --- a/src/wixext/UtilConstants.cs +++ b/src/wixext/UtilConstants.cs @@ -13,5 +13,7 @@ namespace WixToolset.Util internal static readonly string[] RegistryPermissions = { "Read", "Write", "CreateSubkeys", "EnumerateSubkeys", "Notify", "CreateLink" }; internal static readonly string[] ServicePermissions = { "ServiceQueryConfig", "ServiceChangeConfig", "ServiceQueryStatus", "ServiceEnumerateDependents", "ServiceStart", "ServiceStop", "ServicePauseContinue", "ServiceInterrogate", "ServiceUserDefinedControl" }; internal static readonly string[] StandardPermissions = { "Delete", "ReadPermission", "ChangePermission", "TakeOwnership", "Synchronize" }; + + internal const string UtilBundleExtensionId = "WixUtilBundleExtension"; } } diff --git a/src/wixext/UtilErrors.cs b/src/wixext/UtilErrors.cs index 988b8321..c04f0449 100644 --- a/src/wixext/UtilErrors.cs +++ b/src/wixext/UtilErrors.cs @@ -13,11 +13,6 @@ namespace WixToolset.Util return Message(null, Ids.ArgumentRequiresValue, "The argument '{0}' does not have a value specified and it is required.", argument); } - public static Message CircularSearchReference(string chain) - { - return Message(null, Ids.CircularSearchReference, "A circular reference of search ordering constraints was detected: {0}. Search ordering references must form a directed acyclic graph.", chain); - } - public static Message DirectoryNotFound(string directory) { return Message(null, Ids.DirectoryNotFound, "The directory '{0}' could not be found.", directory); @@ -102,7 +97,6 @@ namespace WixToolset.Util FileNotFound = 5059, PerformanceCategoryNotFound = 5060, UnsupportedPerformanceCounterType = 5061, - CircularSearchReference = 5062, InvalidRegistryObject = 5063, } } diff --git a/src/wixext/util.xsd b/src/wixext/util.xsd index fb8d8032..a8c3d208 100644 --- a/src/wixext/util.xsd +++ b/src/wixext/util.xsd @@ -172,6 +172,30 @@ + + + Detects support for SHA2. + + + + + + + + + + + + References a DetectSHA2Support. + + + + + + + + + Describes a directory search. -- cgit v1.2.3-55-g6feb