From 3bcafe0abdbf04d7f462f89f2ae0e85f3309bbcc Mon Sep 17 00:00:00 2001 From: Easton Pillay Date: Thu, 27 Jan 2022 09:56:20 -0600 Subject: [PATCH 1/3] Added WiX detection to MSI Parser. --- src/WingetCreateCore/Common/PackageParser.cs | 183 +++++++++++-------- 1 file changed, 108 insertions(+), 75 deletions(-) diff --git a/src/WingetCreateCore/Common/PackageParser.cs b/src/WingetCreateCore/Common/PackageParser.cs index 1f21cde1..e41ac690 100644 --- a/src/WingetCreateCore/Common/PackageParser.cs +++ b/src/WingetCreateCore/Common/PackageParser.cs @@ -54,14 +54,14 @@ private enum MachineType X86 = 0x014c, X64 = 0x8664, } - - private enum CompatibilitySet - { - None, - Exe, - Msi, - Msix, - } + + private enum CompatibilitySet + { + None, + Exe, + Msi, + Msix, + } /// /// Gets or sets the path in the %TEMP% directory where installers are downloaded to. @@ -315,14 +315,14 @@ private static Installer FindInstallerMatch( // If we can find an exact match by comparing the installerType and the override architecture, then return match. // Otherwise, continue and try matching based on arch detected from url and binary detection, as the user could be trying overwrite with a new architecture. var installerTypeMatches = existingInstallers.Where( - i => (i.InstallerType ?? installerManifest.InstallerType) == newInstaller.InstallerType); - - // If there are no exact installerType matches, check if there is a compatible installerType that can be matched. - if (!installerTypeMatches.Any()) - { + i => (i.InstallerType ?? installerManifest.InstallerType) == newInstaller.InstallerType); + + // If there are no exact installerType matches, check if there is a compatible installerType that can be matched. + if (!installerTypeMatches.Any()) + { installerTypeMatches = existingInstallers.Where( - i => IsCompatibleInstallerType(i.InstallerType ?? installerManifest.InstallerType, newInstaller.InstallerType)); - } + i => IsCompatibleInstallerType(i.InstallerType ?? installerManifest.InstallerType, newInstaller.InstallerType)); + } var overrideArchMatches = installerTypeMatches.Where(i => i.Architecture == installerMetadata.OverrideArchitecture); if (overrideArchMatches.Count() == 1) @@ -374,9 +374,9 @@ private static void UpdateInstallerMetadata(Installer existingInstaller, Install existingInstaller.Architecture = newInstaller.Architecture; existingInstaller.InstallerUrl = newInstaller.InstallerUrl; existingInstaller.InstallerSha256 = newInstaller.InstallerSha256; - existingInstaller.SignatureSha256 = newInstaller.SignatureSha256; - - // If the newInstaller field value is null, we default to using the existingInstaller field value. + existingInstaller.SignatureSha256 = newInstaller.SignatureSha256; + + // If the newInstaller field value is null, we default to using the existingInstaller field value. existingInstaller.ProductCode = newInstaller.ProductCode ?? existingInstaller.ProductCode; existingInstaller.MinimumOSVersion = newInstaller.MinimumOSVersion ?? existingInstaller.MinimumOSVersion; existingInstaller.PackageFamilyName = newInstaller.PackageFamilyName ?? existingInstaller.PackageFamilyName; @@ -549,60 +549,60 @@ private static string HashAppxFile(IAppxFile signatureFile) } return null; - } - - /// - /// Checks if the provided installerTypes are compatible. - /// - /// First InstallerType. - /// Second InstallerType. - /// A boolean value indicating whether the installerTypes are compatible. - private static bool IsCompatibleInstallerType(InstallerType? type1, InstallerType? type2) - { - if (!type1.HasValue || !type2.HasValue) - { - return false; - } - - InstallerType installerType1 = type1.Value; - InstallerType installerType2 = type2.Value; - - if (installerType1 == installerType2) - { - return true; - } - - CompatibilitySet set1 = GetCompatibilitySet(installerType1); - CompatibilitySet set2 = GetCompatibilitySet(installerType2); - - if (set1 == CompatibilitySet.None || set2 == CompatibilitySet.None) - { - return false; - } - - return set1 == set2; - } - - private static CompatibilitySet GetCompatibilitySet(InstallerType type) - { - switch (type) - { - case InstallerType.Inno: - case InstallerType.Nullsoft: - case InstallerType.Exe: - case InstallerType.Burn: - return CompatibilitySet.Exe; - case InstallerType.Wix: - case InstallerType.Msi: - return CompatibilitySet.Msi; - case InstallerType.Msix: - case InstallerType.Appx: - return CompatibilitySet.Msix; - default: - return CompatibilitySet.None; - } - } - + } + + /// + /// Checks if the provided installerTypes are compatible. + /// + /// First InstallerType. + /// Second InstallerType. + /// A boolean value indicating whether the installerTypes are compatible. + private static bool IsCompatibleInstallerType(InstallerType? type1, InstallerType? type2) + { + if (!type1.HasValue || !type2.HasValue) + { + return false; + } + + InstallerType installerType1 = type1.Value; + InstallerType installerType2 = type2.Value; + + if (installerType1 == installerType2) + { + return true; + } + + CompatibilitySet set1 = GetCompatibilitySet(installerType1); + CompatibilitySet set2 = GetCompatibilitySet(installerType2); + + if (set1 == CompatibilitySet.None || set2 == CompatibilitySet.None) + { + return false; + } + + return set1 == set2; + } + + private static CompatibilitySet GetCompatibilitySet(InstallerType type) + { + switch (type) + { + case InstallerType.Inno: + case InstallerType.Nullsoft: + case InstallerType.Exe: + case InstallerType.Burn: + return CompatibilitySet.Exe; + case InstallerType.Wix: + case InstallerType.Msi: + return CompatibilitySet.Msi; + case InstallerType.Msix: + case InstallerType.Appx: + return CompatibilitySet.Msix; + default: + return CompatibilitySet.None; + } + } + private static bool ParseExeInstallerType(string path, Installer baseInstaller, List newInstallers) { try @@ -643,6 +643,37 @@ private static bool ParseExeInstallerType(string path, Installer baseInstaller, } } + /// + /// Checks if a MSI Installer database was generated by WiX, based on common characteristics. + /// + /// A MSI Installer database. + /// A boolean. + private static bool IsWix(QDatabase installer) + { + // Do any of the WiX properties exist? + foreach (var property in installer.Properties) + { + if (property.Property.ToLower().Contains("wix")) + { + return true; + } + } + + // Do any of the WiX tables exist? + foreach (var table in installer.Tables) + { + if (table.Name.ToLower().Contains("wix")) + { + return true; + } + } + + // Does the CreatingApp mention WiX? + return + installer.SummaryInfo.CreatingApp.ToLower().Contains("wix") || + installer.SummaryInfo.CreatingApp.ToLower().Contains("windows installer xml"); + } + private static bool ParseMsi(string path, Installer baseInstaller, Manifests manifests, List newInstallers) { DefaultLocaleManifest defaultLocaleManifest = manifests?.DefaultLocaleManifest; @@ -651,8 +682,10 @@ private static bool ParseMsi(string path, Installer baseInstaller, Manifests man { using (var database = new QDatabase(path, Deployment.WindowsInstaller.DatabaseOpenMode.ReadOnly)) { - baseInstaller.InstallerType = InstallerType.Msi; - + baseInstaller.InstallerType = IsWix(database) + ? InstallerType.Wix + : InstallerType.Msi; + var properties = database.Properties.ToList(); if (defaultLocaleManifest != null) @@ -806,7 +839,7 @@ private static AppxMetadata GetAppxMetadataAndSetInstallerProperties(string path signatureSha256 = HashAppxFile(signatureFile); } - baseInstaller.SignatureSha256 = signatureSha256; + baseInstaller.SignatureSha256 = signatureSha256; baseInstaller.InstallerType = InstallerType.Msix; // Add installer nodes for MSIX installers From 16d43186f8d95c4434a3e81ab3dff8d9ed3b7fe2 Mon Sep 17 00:00:00 2001 From: Easton Pillay Date: Thu, 27 Jan 2022 11:39:41 -0500 Subject: [PATCH 2/3] Applied changes from @Trenly's review. --- src/WingetCreateCore/Common/PackageParser.cs | 23 +++----------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/src/WingetCreateCore/Common/PackageParser.cs b/src/WingetCreateCore/Common/PackageParser.cs index e41ac690..9a48546d 100644 --- a/src/WingetCreateCore/Common/PackageParser.cs +++ b/src/WingetCreateCore/Common/PackageParser.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. namespace Microsoft.WingetCreateCore @@ -650,26 +650,9 @@ private static bool ParseExeInstallerType(string path, Installer baseInstaller, /// A boolean. private static bool IsWix(QDatabase installer) { - // Do any of the WiX properties exist? - foreach (var property in installer.Properties) - { - if (property.Property.ToLower().Contains("wix")) - { - return true; - } - } - - // Do any of the WiX tables exist? - foreach (var table in installer.Tables) - { - if (table.Name.ToLower().Contains("wix")) - { - return true; - } - } - - // Does the CreatingApp mention WiX? return + Array.Exists(installer.Tables.ToArray(), table => table.Name.ToLower().Contains("wix")) || + Array.Exists(installer.Properties.ToArray(), property => property.Property.ToLower().Contains("wix") || property.Value.ToLower().Contains("wix")) || installer.SummaryInfo.CreatingApp.ToLower().Contains("wix") || installer.SummaryInfo.CreatingApp.ToLower().Contains("windows installer xml"); } From a7b44c6db48773f310896ab07af703803a0eba98 Mon Sep 17 00:00:00 2001 From: Easton Pillay Date: Thu, 27 Jan 2022 12:51:24 -0500 Subject: [PATCH 3/3] Did it without an Array conversion. --- src/WingetCreateCore/Common/PackageParser.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WingetCreateCore/Common/PackageParser.cs b/src/WingetCreateCore/Common/PackageParser.cs index 9a48546d..2a60b243 100644 --- a/src/WingetCreateCore/Common/PackageParser.cs +++ b/src/WingetCreateCore/Common/PackageParser.cs @@ -651,8 +651,8 @@ private static bool ParseExeInstallerType(string path, Installer baseInstaller, private static bool IsWix(QDatabase installer) { return - Array.Exists(installer.Tables.ToArray(), table => table.Name.ToLower().Contains("wix")) || - Array.Exists(installer.Properties.ToArray(), property => property.Property.ToLower().Contains("wix") || property.Value.ToLower().Contains("wix")) || + installer.Tables.AsEnumerable().Any(table => table.Name.ToLower().Contains("wix")) || + installer.Properties.AsEnumerable().Any(property => property.Property.ToLower().Contains("wix") || property.Value.ToLower().Contains("wix")) || installer.SummaryInfo.CreatingApp.ToLower().Contains("wix") || installer.SummaryInfo.CreatingApp.ToLower().Contains("windows installer xml"); }