diff options
| author | Rob Mensching <rob@firegiant.com> | 2022-01-08 07:44:33 -0800 |
|---|---|---|
| committer | Rob Mensching <rob@firegiant.com> | 2022-01-10 17:08:43 -0800 |
| commit | 4182d6d594301b88d4780d0222a3d1448b524fde (patch) | |
| tree | 3dd0aeb1e8346b4cae625022f6b501122998378f /src | |
| parent | cec50194881e99d1f3ef1c8a2537e2eb0c124d75 (diff) | |
| download | wix-4182d6d594301b88d4780d0222a3d1448b524fde.tar.gz wix-4182d6d594301b88d4780d0222a3d1448b524fde.tar.bz2 wix-4182d6d594301b88d4780d0222a3d1448b524fde.zip | |
Introduce "msi inscribe" command and use it in wix.signing.targets
Diffstat (limited to 'src')
10 files changed, 376 insertions, 31 deletions
diff --git a/src/wix/WixToolset.BuildTasks/InscribeMsiWithCabinetSignatures.cs b/src/wix/WixToolset.BuildTasks/InscribeMsiWithCabinetSignatures.cs new file mode 100644 index 00000000..9f78caa5 --- /dev/null +++ b/src/wix/WixToolset.BuildTasks/InscribeMsiWithCabinetSignatures.cs | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | // 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. | ||
| 2 | |||
| 3 | namespace WixToolset.BuildTasks | ||
| 4 | { | ||
| 5 | using Microsoft.Build.Framework; | ||
| 6 | |||
| 7 | /// <summary> | ||
| 8 | /// An MSBuild task to run WiX to update cabinet signatures in a MSI. | ||
| 9 | /// </summary> | ||
| 10 | public sealed partial class InscribeMsiWithCabinetSignatures : WixExeBaseTask | ||
| 11 | { | ||
| 12 | [Required] | ||
| 13 | public ITaskItem DatabaseFile { get; set; } | ||
| 14 | |||
| 15 | [Required] | ||
| 16 | public ITaskItem IntermediateDirectory { get; set; } | ||
| 17 | |||
| 18 | public ITaskItem OutputFile { get; set; } | ||
| 19 | |||
| 20 | protected override void BuildCommandLine(WixCommandLineBuilder commandLineBuilder) | ||
| 21 | { | ||
| 22 | commandLineBuilder.AppendTextUnquoted("msi inscribe"); | ||
| 23 | |||
| 24 | commandLineBuilder.AppendFileNameIfNotNull(this.DatabaseFile); | ||
| 25 | commandLineBuilder.AppendSwitchIfNotNull("-out ", this.OutputFile); | ||
| 26 | commandLineBuilder.AppendSwitchIfNotNull("-intermediatefolder ", this.IntermediateDirectory); | ||
| 27 | |||
| 28 | base.BuildCommandLine(commandLineBuilder); | ||
| 29 | |||
| 30 | commandLineBuilder.AppendTextIfNotWhitespace(this.AdditionalOptions); | ||
| 31 | } | ||
| 32 | } | ||
| 33 | } | ||
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs index 57f2f753..80fcb8e1 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs | |||
| @@ -12,20 +12,25 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe | |||
| 12 | using WixToolset.Core.WindowsInstaller.Bind; | 12 | using WixToolset.Core.WindowsInstaller.Bind; |
| 13 | using WixToolset.Data; | 13 | using WixToolset.Data; |
| 14 | using WixToolset.Data.WindowsInstaller; | 14 | using WixToolset.Data.WindowsInstaller; |
| 15 | using WixToolset.Extensibility.Data; | ||
| 16 | using WixToolset.Extensibility.Services; | 15 | using WixToolset.Extensibility.Services; |
| 17 | 16 | ||
| 18 | internal class InscribeMsiPackageCommand | 17 | internal class InscribeMsiPackageCommand |
| 19 | { | 18 | { |
| 20 | public InscribeMsiPackageCommand(IInscribeContext context) | 19 | public InscribeMsiPackageCommand(IServiceProvider serviceProvider, string inputPath, string intermediateFolder, string outputPath) |
| 21 | { | 20 | { |
| 22 | this.Context = context; | 21 | this.Messaging = serviceProvider.GetService<IMessaging>(); |
| 23 | this.Messaging = context.ServiceProvider.GetService<IMessaging>(); | 22 | this.WindowsInstallerBackendHelper = serviceProvider.GetService<IWindowsInstallerBackendHelper>(); |
| 24 | this.WindowsInstallerBackendHelper = context.ServiceProvider.GetService<IWindowsInstallerBackendHelper>(); | ||
| 25 | this.TableDefinitions = new TableDefinitionCollection(WindowsInstallerTableDefinitions.All); | 23 | this.TableDefinitions = new TableDefinitionCollection(WindowsInstallerTableDefinitions.All); |
| 24 | this.InputPath = inputPath; | ||
| 25 | this.IntermediateFolder = intermediateFolder; | ||
| 26 | this.OutputPath = outputPath; | ||
| 26 | } | 27 | } |
| 27 | 28 | ||
| 28 | private IInscribeContext Context { get; } | 29 | private string InputPath { get; } |
| 30 | |||
| 31 | private string IntermediateFolder { get; } | ||
| 32 | |||
| 33 | private string OutputPath { get; } | ||
| 29 | 34 | ||
| 30 | private IMessaging Messaging { get; } | 35 | private IMessaging Messaging { get; } |
| 31 | 36 | ||
| @@ -39,14 +44,22 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe | |||
| 39 | var foundUnsignedExternals = false; | 44 | var foundUnsignedExternals = false; |
| 40 | var shouldCommit = false; | 45 | var shouldCommit = false; |
| 41 | 46 | ||
| 42 | var attributes = File.GetAttributes(this.Context.InputFilePath); | 47 | var databasePath = this.OutputPath; |
| 48 | |||
| 49 | if (!String.Equals(this.InputPath, this.OutputPath, StringComparison.OrdinalIgnoreCase)) | ||
| 50 | { | ||
| 51 | Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath)); | ||
| 52 | File.Copy(this.InputPath, this.OutputPath, true); | ||
| 53 | } | ||
| 54 | |||
| 55 | var attributes = File.GetAttributes(databasePath); | ||
| 43 | if (FileAttributes.ReadOnly == (attributes & FileAttributes.ReadOnly)) | 56 | if (FileAttributes.ReadOnly == (attributes & FileAttributes.ReadOnly)) |
| 44 | { | 57 | { |
| 45 | this.Messaging.Write(ErrorMessages.ReadOnlyOutputFile(this.Context.InputFilePath)); | 58 | this.Messaging.Write(ErrorMessages.ReadOnlyOutputFile(databasePath)); |
| 46 | return shouldCommit; | 59 | return shouldCommit; |
| 47 | } | 60 | } |
| 48 | 61 | ||
| 49 | using (var database = new Database(this.Context.InputFilePath, OpenDatabase.Transact)) | 62 | using (var database = new Database(databasePath, OpenDatabase.Transact)) |
| 50 | { | 63 | { |
| 51 | // Just use the English codepage, because the tables we're importing only have binary streams / MSI identifiers / other non-localizable content | 64 | // Just use the English codepage, because the tables we're importing only have binary streams / MSI identifiers / other non-localizable content |
| 52 | var codepage = 1252; | 65 | var codepage = 1252; |
| @@ -65,8 +78,7 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe | |||
| 65 | { | 78 | { |
| 66 | foreach (var digitalSignatureRecord in digitalSignatureView.Records) | 79 | foreach (var digitalSignatureRecord in digitalSignatureView.Records) |
| 67 | { | 80 | { |
| 68 | Row digitalSignatureRow = null; | 81 | var digitalSignatureRow = digitalSignatureTable.CreateRow(null); |
| 69 | digitalSignatureRow = digitalSignatureTable.CreateRow(null); | ||
| 70 | 82 | ||
| 71 | var table = digitalSignatureRecord.GetString(0); | 83 | var table = digitalSignatureRecord.GetString(0); |
| 72 | var signObject = digitalSignatureRecord.GetString(1); | 84 | var signObject = digitalSignatureRecord.GetString(1); |
| @@ -78,7 +90,7 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe | |||
| 78 | if (false == digitalSignatureRecord.IsNull(3)) | 90 | if (false == digitalSignatureRecord.IsNull(3)) |
| 79 | { | 91 | { |
| 80 | // Export to a file, because the MSI API's require us to provide a file path on disk | 92 | // Export to a file, because the MSI API's require us to provide a file path on disk |
| 81 | var hashPath = Path.Combine(this.Context.IntermediateFolder, "MsiDigitalSignature"); | 93 | var hashPath = Path.Combine(this.IntermediateFolder, "MsiDigitalSignature"); |
| 82 | var hashFileName = String.Concat(table, ".", signObject, ".bin"); | 94 | var hashFileName = String.Concat(table, ".", signObject, ".bin"); |
| 83 | 95 | ||
| 84 | Directory.CreateDirectory(hashPath); | 96 | Directory.CreateDirectory(hashPath); |
| @@ -111,7 +123,7 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe | |||
| 111 | var certificateId = digitalCertificateRecord.GetString(1); // get the identifier of the certificate | 123 | var certificateId = digitalCertificateRecord.GetString(1); // get the identifier of the certificate |
| 112 | 124 | ||
| 113 | // Export to a file, because the MSI API's require us to provide a file path on disk | 125 | // Export to a file, because the MSI API's require us to provide a file path on disk |
| 114 | var certPath = Path.Combine(this.Context.IntermediateFolder, "MsiDigitalCertificate"); | 126 | var certPath = Path.Combine(this.IntermediateFolder, "MsiDigitalCertificate"); |
| 115 | Directory.CreateDirectory(certPath); | 127 | Directory.CreateDirectory(certPath); |
| 116 | certPath = Path.Combine(certPath, String.Concat(certificateId, ".cer")); | 128 | certPath = Path.Combine(certPath, String.Concat(certificateId, ".cer")); |
| 117 | 129 | ||
| @@ -147,7 +159,6 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe | |||
| 147 | foreach (var mediaRecord in mediaView.Records) | 159 | foreach (var mediaRecord in mediaView.Records) |
| 148 | { | 160 | { |
| 149 | X509Certificate2 cert2 = null; | 161 | X509Certificate2 cert2 = null; |
| 150 | Row digitalSignatureRow = null; | ||
| 151 | 162 | ||
| 152 | var cabName = mediaRecord.GetString(4); // get the name of the cab | 163 | var cabName = mediaRecord.GetString(4); // get the name of the cab |
| 153 | // If there is no cabinet or it's an internal cab, skip it. | 164 | // If there is no cabinet or it's an internal cab, skip it. |
| @@ -157,7 +168,7 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe | |||
| 157 | } | 168 | } |
| 158 | 169 | ||
| 159 | var cabId = mediaRecord.GetString(1); // get the ID of the cab | 170 | var cabId = mediaRecord.GetString(1); // get the ID of the cab |
| 160 | var cabPath = Path.Combine(Path.GetDirectoryName(this.Context.InputFilePath), cabName); | 171 | var cabPath = Path.Combine(Path.GetDirectoryName(this.InputPath), cabName); |
| 161 | 172 | ||
| 162 | // If the cabs aren't there, throw an error but continue to catch the other errors | 173 | // If the cabs aren't there, throw an error but continue to catch the other errors |
| 163 | if (!File.Exists(cabPath)) | 174 | if (!File.Exists(cabPath)) |
| @@ -207,7 +218,7 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe | |||
| 207 | digitalCertificateRow[0] = certificateGeneratedId; | 218 | digitalCertificateRow[0] = certificateGeneratedId; |
| 208 | 219 | ||
| 209 | // Export to a file, because the MSI API's require us to provide a file path on disk | 220 | // Export to a file, because the MSI API's require us to provide a file path on disk |
| 210 | var certPath = Path.Combine(this.Context.IntermediateFolder, "MsiDigitalCertificate"); | 221 | var certPath = Path.Combine(this.IntermediateFolder, "MsiDigitalCertificate"); |
| 211 | Directory.CreateDirectory(certPath); | 222 | Directory.CreateDirectory(certPath); |
| 212 | certPath = Path.Combine(certPath, String.Concat(cert2.Thumbprint, ".cer")); | 223 | certPath = Path.Combine(certPath, String.Concat(cert2.Thumbprint, ".cer")); |
| 213 | File.Delete(certPath); | 224 | File.Delete(certPath); |
| @@ -224,7 +235,7 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe | |||
| 224 | certificates.Add(cert2.Thumbprint, certificateGeneratedId); | 235 | certificates.Add(cert2.Thumbprint, certificateGeneratedId); |
| 225 | } | 236 | } |
| 226 | 237 | ||
| 227 | digitalSignatureRow = digitalSignatureTable.CreateRow(null); | 238 | var digitalSignatureRow = digitalSignatureTable.CreateRow(null); |
| 228 | 239 | ||
| 229 | digitalSignatureRow[0] = "Media"; | 240 | digitalSignatureRow[0] = "Media"; |
| 230 | digitalSignatureRow[1] = cabId; | 241 | digitalSignatureRow[1] = cabId; |
| @@ -234,7 +245,7 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe | |||
| 234 | 245 | ||
| 235 | if (digitalCertificateTable.Rows.Count > 0) | 246 | if (digitalCertificateTable.Rows.Count > 0) |
| 236 | { | 247 | { |
| 237 | var command = new CreateIdtFileCommand(this.Messaging, digitalCertificateTable, codepage, this.Context.IntermediateFolder, true); | 248 | var command = new CreateIdtFileCommand(this.Messaging, digitalCertificateTable, codepage, this.IntermediateFolder, true); |
| 238 | command.Execute(); | 249 | command.Execute(); |
| 239 | 250 | ||
| 240 | database.Import(command.IdtPath); | 251 | database.Import(command.IdtPath); |
| @@ -243,7 +254,7 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe | |||
| 243 | 254 | ||
| 244 | if (digitalSignatureTable.Rows.Count > 0) | 255 | if (digitalSignatureTable.Rows.Count > 0) |
| 245 | { | 256 | { |
| 246 | var command = new CreateIdtFileCommand(this.Messaging, digitalSignatureTable, codepage, this.Context.IntermediateFolder, true); | 257 | var command = new CreateIdtFileCommand(this.Messaging, digitalSignatureTable, codepage, this.IntermediateFolder, true); |
| 247 | command.Execute(); | 258 | command.Execute(); |
| 248 | 259 | ||
| 249 | database.Import(command.IdtPath); | 260 | database.Import(command.IdtPath); |
| @@ -257,7 +268,7 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe | |||
| 257 | // If we did find external cabs but not all of them were signed, give a warning | 268 | // If we did find external cabs but not all of them were signed, give a warning |
| 258 | if (foundUnsignedExternals) | 269 | if (foundUnsignedExternals) |
| 259 | { | 270 | { |
| 260 | this.Messaging.Write(WarningMessages.ExternalCabsAreNotSigned(this.Context.InputFilePath)); | 271 | this.Messaging.Write(WarningMessages.ExternalCabsAreNotSigned(this.InputPath)); |
| 261 | } | 272 | } |
| 262 | 273 | ||
| 263 | if (shouldCommit) | 274 | if (shouldCommit) |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/InscribeSubcommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/InscribeSubcommand.cs new file mode 100644 index 00000000..d44fb0bd --- /dev/null +++ b/src/wix/WixToolset.Core.WindowsInstaller/InscribeSubcommand.cs | |||
| @@ -0,0 +1,80 @@ | |||
| 1 | // 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. | ||
| 2 | |||
| 3 | namespace WixToolset.Core.WindowsInstaller | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.IO; | ||
| 7 | using System.Threading; | ||
| 8 | using System.Threading.Tasks; | ||
| 9 | using WixToolset.Core.WindowsInstaller.Inscribe; | ||
| 10 | using WixToolset.Extensibility.Services; | ||
| 11 | |||
| 12 | internal class InscribeSubcommand : WindowsInstallerSubcommandBase | ||
| 13 | { | ||
| 14 | public InscribeSubcommand(IServiceProvider serviceProvider) | ||
| 15 | { | ||
| 16 | this.ServiceProvider = serviceProvider; | ||
| 17 | this.Messaging = serviceProvider.GetService<IMessaging>(); | ||
| 18 | } | ||
| 19 | |||
| 20 | private IServiceProvider ServiceProvider { get; } | ||
| 21 | |||
| 22 | private IMessaging Messaging { get; } | ||
| 23 | |||
| 24 | private string InputPath { get; set; } | ||
| 25 | |||
| 26 | private string IntermediateFolder { get; set; } | ||
| 27 | |||
| 28 | private string OutputPath { get; set; } | ||
| 29 | |||
| 30 | public override Task<int> ExecuteAsync(CancellationToken cancellationToken) | ||
| 31 | { | ||
| 32 | if (String.IsNullOrEmpty(this.InputPath)) | ||
| 33 | { | ||
| 34 | Console.Error.WriteLine("Input MSI database is required"); | ||
| 35 | return Task.FromResult(-1); | ||
| 36 | } | ||
| 37 | |||
| 38 | if (String.IsNullOrEmpty(this.IntermediateFolder)) | ||
| 39 | { | ||
| 40 | this.IntermediateFolder = Path.GetTempPath(); | ||
| 41 | } | ||
| 42 | |||
| 43 | if (String.IsNullOrEmpty(this.OutputPath)) | ||
| 44 | { | ||
| 45 | this.OutputPath = this.InputPath; | ||
| 46 | } | ||
| 47 | |||
| 48 | var command = new InscribeMsiPackageCommand(this.ServiceProvider, this.InputPath, this.IntermediateFolder, this.OutputPath); | ||
| 49 | command.Execute(); | ||
| 50 | |||
| 51 | return Task.FromResult(this.Messaging.LastErrorNumber); | ||
| 52 | } | ||
| 53 | |||
| 54 | public override bool TryParseArgument(ICommandLineParser parser, string argument) | ||
| 55 | { | ||
| 56 | if (parser.IsSwitch(argument)) | ||
| 57 | { | ||
| 58 | var parameter = argument.Substring(1); | ||
| 59 | switch (parameter.ToLowerInvariant()) | ||
| 60 | { | ||
| 61 | case "intermediatefolder": | ||
| 62 | this.IntermediateFolder = parser.GetNextArgumentAsDirectoryOrError(argument); | ||
| 63 | return true; | ||
| 64 | |||
| 65 | case "o": | ||
| 66 | case "out": | ||
| 67 | this.OutputPath = parser.GetNextArgumentAsFilePathOrError(argument); | ||
| 68 | return true; | ||
| 69 | } | ||
| 70 | } | ||
| 71 | else if (String.IsNullOrEmpty(this.InputPath)) | ||
| 72 | { | ||
| 73 | this.InputPath = argument; | ||
| 74 | return true; | ||
| 75 | } | ||
| 76 | |||
| 77 | return false; | ||
| 78 | } | ||
| 79 | } | ||
| 80 | } | ||
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/MsiBackend.cs b/src/wix/WixToolset.Core.WindowsInstaller/MsiBackend.cs index 3bd58c25..ab8def5f 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/MsiBackend.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/MsiBackend.cs | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | 2 | ||
| 3 | namespace WixToolset.Core.WindowsInstaller | 3 | namespace WixToolset.Core.WindowsInstaller |
| 4 | { | 4 | { |
| 5 | using System; | ||
| 5 | using WixToolset.Core.WindowsInstaller.Bind; | 6 | using WixToolset.Core.WindowsInstaller.Bind; |
| 6 | using WixToolset.Core.WindowsInstaller.Decompile; | 7 | using WixToolset.Core.WindowsInstaller.Decompile; |
| 7 | using WixToolset.Core.WindowsInstaller.Inscribe; | 8 | using WixToolset.Core.WindowsInstaller.Inscribe; |
| @@ -72,8 +73,9 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 72 | 73 | ||
| 73 | public bool Inscribe(IInscribeContext context) | 74 | public bool Inscribe(IInscribeContext context) |
| 74 | { | 75 | { |
| 75 | var command = new InscribeMsiPackageCommand(context); | 76 | //var command = new InscribeMsiPackageCommand(context); |
| 76 | return command.Execute(); | 77 | //return command.Execute(); |
| 78 | throw new NotImplementedException(); | ||
| 77 | } | 79 | } |
| 78 | 80 | ||
| 79 | public Intermediate Unbind(IUnbindContext context) | 81 | public Intermediate Unbind(IUnbindContext context) |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerCommand.cs new file mode 100644 index 00000000..cedc65b2 --- /dev/null +++ b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerCommand.cs | |||
| @@ -0,0 +1,73 @@ | |||
| 1 | // 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. | ||
| 2 | |||
| 3 | namespace WixToolset.Core.WindowsInstaller | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Threading; | ||
| 7 | using System.Threading.Tasks; | ||
| 8 | using WixToolset.Extensibility.Data; | ||
| 9 | using WixToolset.Extensibility.Services; | ||
| 10 | |||
| 11 | /// <summary> | ||
| 12 | /// Windows Installer specialized command. | ||
| 13 | /// </summary> | ||
| 14 | internal class WindowsInstallerCommand : ICommandLineCommand | ||
| 15 | { | ||
| 16 | public WindowsInstallerCommand(IServiceProvider serviceProvider) | ||
| 17 | { | ||
| 18 | this.ServiceProvider = serviceProvider; | ||
| 19 | } | ||
| 20 | |||
| 21 | public bool ShowHelp { get; set; } | ||
| 22 | |||
| 23 | public bool ShowLogo { get; set; } | ||
| 24 | |||
| 25 | public bool StopParsing { get; set; } | ||
| 26 | |||
| 27 | private IServiceProvider ServiceProvider { get; } | ||
| 28 | |||
| 29 | private WindowsInstallerSubcommandBase Subcommand { get; set; } | ||
| 30 | |||
| 31 | public Task<int> ExecuteAsync(CancellationToken cancellationToken) | ||
| 32 | { | ||
| 33 | if (this.ShowHelp || this.Subcommand is null) | ||
| 34 | { | ||
| 35 | DisplayHelp(); | ||
| 36 | return Task.FromResult(1); | ||
| 37 | } | ||
| 38 | |||
| 39 | return this.Subcommand.ExecuteAsync(cancellationToken); | ||
| 40 | } | ||
| 41 | |||
| 42 | public bool TryParseArgument(ICommandLineParser parser, string argument) | ||
| 43 | { | ||
| 44 | if (this.Subcommand is null) | ||
| 45 | { | ||
| 46 | switch (argument.ToLowerInvariant()) | ||
| 47 | { | ||
| 48 | case "inscribe": | ||
| 49 | this.Subcommand = new InscribeSubcommand(this.ServiceProvider); | ||
| 50 | return true; | ||
| 51 | } | ||
| 52 | |||
| 53 | return false; | ||
| 54 | } | ||
| 55 | |||
| 56 | return this.Subcommand.TryParseArgument(parser, argument); | ||
| 57 | } | ||
| 58 | |||
| 59 | private static void DisplayHelp() | ||
| 60 | { | ||
| 61 | Console.WriteLine(); | ||
| 62 | Console.WriteLine("Usage: wix msi inscribe input.msi [-intermedidateFolder folder] [-out output.msi]"); | ||
| 63 | Console.WriteLine(); | ||
| 64 | Console.WriteLine("Options:"); | ||
| 65 | Console.WriteLine(" -h|--help Show command line help."); | ||
| 66 | Console.WriteLine(" --nologo Suppress displaying the logo information."); | ||
| 67 | Console.WriteLine(); | ||
| 68 | Console.WriteLine("Commands:"); | ||
| 69 | Console.WriteLine(); | ||
| 70 | Console.WriteLine(" inscribe Updates MSI database with cabinet signature information."); | ||
| 71 | } | ||
| 72 | } | ||
| 73 | } | ||
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerExtensionCommandLine.cs b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerExtensionCommandLine.cs new file mode 100644 index 00000000..4c516d0d --- /dev/null +++ b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerExtensionCommandLine.cs | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | // 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. | ||
| 2 | |||
| 3 | namespace WixToolset.Core.WindowsInstaller | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Collections.Generic; | ||
| 7 | using WixToolset.Extensibility; | ||
| 8 | using WixToolset.Extensibility.Data; | ||
| 9 | using WixToolset.Extensibility.Services; | ||
| 10 | |||
| 11 | /// <summary> | ||
| 12 | /// Parses the "msi" command-line command. See <c>WindowsInstallerCommand</c> | ||
| 13 | /// for the bulk of the command-line processing. | ||
| 14 | /// </summary> | ||
| 15 | internal class WindowsInstallerExtensionCommandLine : BaseExtensionCommandLine | ||
| 16 | { | ||
| 17 | public WindowsInstallerExtensionCommandLine(IServiceProvider serviceProvider) | ||
| 18 | { | ||
| 19 | this.ServiceProvider = serviceProvider; | ||
| 20 | } | ||
| 21 | |||
| 22 | private IServiceProvider ServiceProvider { get; } | ||
| 23 | |||
| 24 | public override IReadOnlyCollection<ExtensionCommandLineSwitch> CommandLineSwitches => new ExtensionCommandLineSwitch[] | ||
| 25 | { | ||
| 26 | new ExtensionCommandLineSwitch { Switch = "msi", Description = "Windows Installer specialized operations." }, | ||
| 27 | }; | ||
| 28 | |||
| 29 | public override bool TryParseCommand(ICommandLineParser parser, string argument, out ICommandLineCommand command) | ||
| 30 | { | ||
| 31 | command = null; | ||
| 32 | |||
| 33 | if ("msi".Equals(argument, StringComparison.OrdinalIgnoreCase)) | ||
| 34 | { | ||
| 35 | command = new WindowsInstallerCommand(this.ServiceProvider); | ||
| 36 | } | ||
| 37 | |||
| 38 | return command != null; | ||
| 39 | } | ||
| 40 | } | ||
| 41 | } | ||
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerExtensionFactory.cs b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerExtensionFactory.cs index 7b12fc8c..0eb4df5e 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerExtensionFactory.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerExtensionFactory.cs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // 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. | 1 | // 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. |
| 2 | 2 | ||
| 3 | namespace WixToolset.Core.WindowsInstaller | 3 | namespace WixToolset.Core.WindowsInstaller |
| 4 | { | 4 | { |
| @@ -7,11 +7,22 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 7 | 7 | ||
| 8 | internal class WindowsInstallerExtensionFactory : IExtensionFactory | 8 | internal class WindowsInstallerExtensionFactory : IExtensionFactory |
| 9 | { | 9 | { |
| 10 | public WindowsInstallerExtensionFactory(IServiceProvider serviceProvider) | ||
| 11 | { | ||
| 12 | this.ServiceProvider = serviceProvider; | ||
| 13 | } | ||
| 14 | |||
| 15 | private IServiceProvider ServiceProvider { get; } | ||
| 16 | |||
| 10 | public bool TryCreateExtension(Type extensionType, out object extension) | 17 | public bool TryCreateExtension(Type extensionType, out object extension) |
| 11 | { | 18 | { |
| 12 | extension = null; | 19 | extension = null; |
| 13 | 20 | ||
| 14 | if (extensionType == typeof(IBackendFactory)) | 21 | if (extensionType == typeof(IExtensionCommandLine)) |
| 22 | { | ||
| 23 | extension = new WindowsInstallerExtensionCommandLine(this.ServiceProvider); | ||
| 24 | } | ||
| 25 | else if (extensionType == typeof(IBackendFactory)) | ||
| 15 | { | 26 | { |
| 16 | extension = new WindowsInstallerBackendFactory(); | 27 | extension = new WindowsInstallerBackendFactory(); |
| 17 | } | 28 | } |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerSubcommandBase.cs b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerSubcommandBase.cs new file mode 100644 index 00000000..c5156a1d --- /dev/null +++ b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerSubcommandBase.cs | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | // 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. | ||
| 2 | |||
| 3 | namespace WixToolset.Core.WindowsInstaller | ||
| 4 | { | ||
| 5 | using System.Threading; | ||
| 6 | using System.Threading.Tasks; | ||
| 7 | using WixToolset.Extensibility.Services; | ||
| 8 | |||
| 9 | internal abstract class WindowsInstallerSubcommandBase | ||
| 10 | { | ||
| 11 | public abstract bool TryParseArgument(ICommandLineParser parser, string argument); | ||
| 12 | |||
| 13 | public abstract Task<int> ExecuteAsync(CancellationToken cancellationToken); | ||
| 14 | } | ||
| 15 | } | ||
diff --git a/src/wix/WixToolset.Sdk/tools/wix.signing.targets b/src/wix/WixToolset.Sdk/tools/wix.signing.targets index 5a4ea98d..3df05d51 100644 --- a/src/wix/WixToolset.Sdk/tools/wix.signing.targets +++ b/src/wix/WixToolset.Sdk/tools/wix.signing.targets | |||
| @@ -11,6 +11,10 @@ | |||
| 11 | <UsingTask TaskName="GetCabList" AssemblyFile="$(WixTasksPath)" /> | 11 | <UsingTask TaskName="GetCabList" AssemblyFile="$(WixTasksPath)" /> |
| 12 | <UsingTask TaskName="GetLooseFileList" AssemblyFile="$(WixTasksPath)" /> | 12 | <UsingTask TaskName="GetLooseFileList" AssemblyFile="$(WixTasksPath)" /> |
| 13 | 13 | ||
| 14 | <UsingTask TaskName="InscribeMsiWithCabinetSignatures" Condition=" '$(WixTasksPath64)' == '' " AssemblyFile="$(WixTasksPath)" /> | ||
| 15 | <UsingTask TaskName="InscribeMsiWithCabinetSignatures" Condition=" '$(WixTasksPath64)' != '' " AssemblyFile="$(WixTasksPath)" Architecture="x86" /> | ||
| 16 | <UsingTask TaskName="InscribeMsiWithCabinetSignatures" Condition=" '$(WixTasksPath64)' != '' " AssemblyFile="$(WixTasksPath64)" Architecture="x64" /> | ||
| 17 | |||
| 14 | <!-- Default Inscribe properties. --> | 18 | <!-- Default Inscribe properties. --> |
| 15 | <PropertyGroup> | 19 | <PropertyGroup> |
| 16 | <InscribeNoLogo Condition=" '$(InscribeNoLogo)' == '' ">$(NoLogo)</InscribeNoLogo> | 20 | <InscribeNoLogo Condition=" '$(InscribeNoLogo)' == '' ">$(NoLogo)</InscribeNoLogo> |
| @@ -21,6 +25,10 @@ | |||
| 21 | <InscribeVerboseOutput Condition=" '$(InscribeVerboseOutput)' == '' ">$(VerboseOutput)</InscribeVerboseOutput> | 25 | <InscribeVerboseOutput Condition=" '$(InscribeVerboseOutput)' == '' ">$(VerboseOutput)</InscribeVerboseOutput> |
| 22 | </PropertyGroup> | 26 | </PropertyGroup> |
| 23 | 27 | ||
| 28 | <PropertyGroup> | ||
| 29 | <SignOutputCabs Condition=" '$(SignOutputCabs)'=='' ">$(SignOutput)</SignOutputCabs> | ||
| 30 | </PropertyGroup> | ||
| 31 | |||
| 24 | <!-- | 32 | <!-- |
| 25 | ================================================================================================== | 33 | ================================================================================================== |
| 26 | Signing | 34 | Signing |
| @@ -58,7 +66,7 @@ | |||
| 58 | <Target | 66 | <Target |
| 59 | Name="Signing" | 67 | Name="Signing" |
| 60 | DependsOnTargets="$(SigningDependsOn)" | 68 | DependsOnTargets="$(SigningDependsOn)" |
| 61 | Inputs="$(TargetPath)" | 69 | Inputs="@(_WixBuildOutputFile)" |
| 62 | Outputs="$(SignedFilePath)" | 70 | Outputs="$(SignedFilePath)" |
| 63 | Condition=" '$(SignOutput)' == 'true' "> | 71 | Condition=" '$(SignOutput)' == 'true' "> |
| 64 | 72 | ||
| @@ -76,7 +84,7 @@ | |||
| 76 | <Target | 84 | <Target |
| 77 | Name="CalculateSignTargetFiles"> | 85 | Name="CalculateSignTargetFiles"> |
| 78 | <ItemGroup> | 86 | <ItemGroup> |
| 79 | <SignTargetPath Include="$(TargetPath)" /> | 87 | <SignTargetPath Include="@(_WixBuildOutputFile)" /> |
| 80 | </ItemGroup> | 88 | </ItemGroup> |
| 81 | </Target> | 89 | </Target> |
| 82 | 90 | ||
| @@ -98,7 +106,8 @@ | |||
| 98 | <Target | 106 | <Target |
| 99 | Name="GetCabsToSign" | 107 | Name="GetCabsToSign" |
| 100 | Inputs="@(SignTargetPath)" | 108 | Inputs="@(SignTargetPath)" |
| 101 | Outputs="$(SignedFilePath)"> | 109 | Outputs="$(SignedFilePath)" |
| 110 | Condition=" '$(SignOutputCabs)' == 'true' "> | ||
| 102 | <GetCabList Database="%(SignTargetPath.FullPath)"> | 111 | <GetCabList Database="%(SignTargetPath.FullPath)"> |
| 103 | <Output TaskParameter="CabList" ItemName="SignCabs" /> | 112 | <Output TaskParameter="CabList" ItemName="SignCabs" /> |
| 104 | <Output TaskParameter="CabList" ItemName="FileWrites" /> | 113 | <Output TaskParameter="CabList" ItemName="FileWrites" /> |
| @@ -174,18 +183,22 @@ | |||
| 174 | Outputs="$(SignedFilePath)" | 183 | Outputs="$(SignedFilePath)" |
| 175 | Condition=" '@(SignCabs)' != '' "> | 184 | Condition=" '@(SignCabs)' != '' "> |
| 176 | 185 | ||
| 177 | <Insignia | 186 | <InscribeMsiWithCabinetSignatures |
| 178 | DatabaseFile="%(SignTargetPath.FullPath)" | 187 | DatabaseFile="%(SignTargetPath.FullPath)" |
| 179 | OutputFile="%(SignTargetPath.FullPath)" | 188 | OutputFile="%(SignTargetPath.FullPath)" |
| 180 | ToolPath="$(WixToolPath)" | 189 | IntermediateDirectory="%(SignTargetPath.RootDir)%(SignTargetPath.Directory)" |
| 190 | |||
| 181 | NoLogo="$(InscribeNoLogo)" | 191 | NoLogo="$(InscribeNoLogo)" |
| 182 | RunAsSeparateProcess="$(RunWixToolsOutOfProc)" | ||
| 183 | SuppressAllWarnings="$(InscribeSuppressAllWarnings)" | 192 | SuppressAllWarnings="$(InscribeSuppressAllWarnings)" |
| 184 | SuppressSpecificWarnings="$(InscribeSuppressSpecificWarnings)" | 193 | SuppressSpecificWarnings="$(InscribeSuppressSpecificWarnings)" |
| 185 | TreatWarningsAsErrors="$(InscribeTreatWarningsAsErrors)" | 194 | TreatWarningsAsErrors="$(InscribeTreatWarningsAsErrors)" |
| 186 | TreatSpecificWarningsAsErrors="$(InscribeTreatSpecificWarningsAsErrors)" | 195 | TreatSpecificWarningsAsErrors="$(InscribeTreatSpecificWarningsAsErrors)" |
| 187 | VerboseOutput="$(InscribeVerboseOutput)" | 196 | VerboseOutput="$(InscribeVerboseOutput)" |
| 188 | AdditionalOptions="$(InscribeAdditionalOptions)" /> | 197 | AdditionalOptions="$(InscribeAdditionalOptions)" |
| 198 | |||
| 199 | RunAsSeparateProcess="$(RunWixToolsOutOfProc)" | ||
| 200 | ToolExe="$(WixToolExe)" | ||
| 201 | ToolPath="$(WixToolDir)" /> | ||
| 189 | </Target> | 202 | </Target> |
| 190 | 203 | ||
| 191 | <!-- | 204 | <!-- |
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/SigningFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/SigningFixture.cs new file mode 100644 index 00000000..79896172 --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/SigningFixture.cs | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | // 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. | ||
| 2 | |||
| 3 | namespace WixToolsetTest.CoreIntegration | ||
| 4 | { | ||
| 5 | using System.IO; | ||
| 6 | using WixBuildTools.TestSupport; | ||
| 7 | using WixToolset.Core.TestPackage; | ||
| 8 | using Xunit; | ||
| 9 | |||
| 10 | public class SigningFixture | ||
| 11 | { | ||
| 12 | [Fact] | ||
| 13 | public void CanInscribeMsiWithSignedCabinet() | ||
| 14 | { | ||
| 15 | var folder = TestData.Get(@"TestData\SingleFileCompressed"); | ||
| 16 | var signedFolder = TestData.Get(@"TestData\.Data"); | ||
| 17 | |||
| 18 | using (var fs = new DisposableFileSystem()) | ||
| 19 | { | ||
| 20 | var baseFolder = fs.GetFolder(); | ||
| 21 | var intermediateFolder = Path.Combine(baseFolder, "obj"); | ||
| 22 | var outputMsi = Path.Combine(intermediateFolder, @"bin\test.msi"); | ||
| 23 | var signedMsi = Path.Combine(baseFolder, @"signed.msi"); | ||
| 24 | |||
| 25 | var result = WixRunner.Execute(new[] | ||
| 26 | { | ||
| 27 | "build", | ||
| 28 | Path.Combine(folder, "Package.wxs"), | ||
| 29 | Path.Combine(folder, "PackageComponents.wxs"), | ||
| 30 | "-loc", Path.Combine(folder, "Package.en-us.wxl"), | ||
| 31 | "-bindpath", Path.Combine(folder, "data"), | ||
| 32 | "-intermediateFolder", intermediateFolder, | ||
| 33 | "-o", outputMsi | ||
| 34 | }); | ||
| 35 | |||
| 36 | result.AssertSuccess(); | ||
| 37 | |||
| 38 | var beforeRows = Query.QueryDatabase(outputMsi, new[] { "MsiDigitalSignature", "MsiDigitalCertificate" }); | ||
| 39 | Assert.Empty(beforeRows); | ||
| 40 | |||
| 41 | // Swap in a pre-signed cabinet since signing during the unit test | ||
| 42 | // is a challenge. The cabinet contents almost definitely don't | ||
| 43 | // match but that's okay for these testing purposes. | ||
| 44 | File.Copy(Path.Combine(signedFolder, "signed_cab1.cab"), Path.Combine(Path.GetDirectoryName(outputMsi), "example.cab"), true); | ||
| 45 | |||
| 46 | result = WixRunner.Execute(new[] | ||
| 47 | { | ||
| 48 | "msi", | ||
| 49 | "inscribe", | ||
| 50 | outputMsi, | ||
| 51 | "-o", signedMsi, | ||
| 52 | "-intermediateFolder", intermediateFolder, | ||
| 53 | }); | ||
| 54 | |||
| 55 | result.AssertSuccess(); | ||
| 56 | |||
| 57 | var rows = Query.QueryDatabase(signedMsi, new[] { "MsiDigitalSignature", "MsiDigitalCertificate" }); | ||
| 58 | WixAssert.CompareLineByLine(new[] | ||
| 59 | { | ||
| 60 | "MsiDigitalCertificate:cer8xpsawK5TG4sIx4em8F.i7ocIKU\t[Binary data]", | ||
| 61 | "MsiDigitalSignature:Media\t1\tcer8xpsawK5TG4sIx4em8F.i7ocIKU\t" | ||
| 62 | }, rows); | ||
| 63 | } | ||
| 64 | } | ||
| 65 | } | ||
| 66 | } | ||
