Skip to content

Commit

Permalink
Added WiX detection to MSI Parser. (#220)
Browse files Browse the repository at this point in the history
  • Loading branch information
jedieaston authored Feb 4, 2022
1 parent c29a199 commit 38a111b
Showing 1 changed file with 92 additions and 76 deletions.
168 changes: 92 additions & 76 deletions src/WingetCreateCore/Common/PackageParser.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Microsoft. All rights reserved.
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license.

namespace Microsoft.WingetCreateCore
Expand Down Expand Up @@ -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,
}

/// <summary>
/// Gets or sets the path in the %TEMP% directory where installers are downloaded to.
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -549,60 +549,60 @@ private static string HashAppxFile(IAppxFile signatureFile)
}

return null;
}

/// <summary>
/// Checks if the provided installerTypes are compatible.
/// </summary>
/// <param name="type1">First InstallerType.</param>
/// <param name="type2">Second InstallerType.</param>
/// <returns>A boolean value indicating whether the installerTypes are compatible.</returns>
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;
}
}

}

/// <summary>
/// Checks if the provided installerTypes are compatible.
/// </summary>
/// <param name="type1">First InstallerType.</param>
/// <param name="type2">Second InstallerType.</param>
/// <returns>A boolean value indicating whether the installerTypes are compatible.</returns>
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<Installer> newInstallers)
{
try
Expand Down Expand Up @@ -643,6 +643,20 @@ private static bool ParseExeInstallerType(string path, Installer baseInstaller,
}
}

/// <summary>
/// Checks if a MSI Installer database was generated by WiX, based on common characteristics.
/// </summary>
/// <param name="installer">A MSI Installer database.</param>
/// <returns>A boolean.</returns>
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<Installer> newInstallers)
{
DefaultLocaleManifest defaultLocaleManifest = manifests?.DefaultLocaleManifest;
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 38a111b

Please sign in to comment.