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 | |
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
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 | } | ||