diff --git a/src/WingetCreateCore/Common/PackageParser.cs b/src/WingetCreateCore/Common/PackageParser.cs index 1f21cde1..2a60b243 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 @@ -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,20 @@ 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) + { + return + 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"); + } + private static bool ParseMsi(string path, Installer baseInstaller, Manifests manifests, List newInstallers) { DefaultLocaleManifest defaultLocaleManifest = manifests?.DefaultLocaleManifest; @@ -651,8 +665,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 +822,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