diff options
35 files changed, 1033 insertions, 247 deletions
diff --git a/src/api/wix/WixToolset.Data/Symbols/WixBundlePayloadSymbol.cs b/src/api/wix/WixToolset.Data/Symbols/WixBundlePayloadSymbol.cs index 82b75285..be581fb3 100644 --- a/src/api/wix/WixToolset.Data/Symbols/WixBundlePayloadSymbol.cs +++ b/src/api/wix/WixToolset.Data/Symbols/WixBundlePayloadSymbol.cs | |||
@@ -26,6 +26,8 @@ namespace WixToolset.Data | |||
26 | new IntermediateFieldDefinition(nameof(WixBundlePayloadSymbolFields.LayoutOnly), IntermediateFieldType.Bool), | 26 | new IntermediateFieldDefinition(nameof(WixBundlePayloadSymbolFields.LayoutOnly), IntermediateFieldType.Bool), |
27 | new IntermediateFieldDefinition(nameof(WixBundlePayloadSymbolFields.Packaging), IntermediateFieldType.Number), | 27 | new IntermediateFieldDefinition(nameof(WixBundlePayloadSymbolFields.Packaging), IntermediateFieldType.Number), |
28 | new IntermediateFieldDefinition(nameof(WixBundlePayloadSymbolFields.ParentPackagePayloadRef), IntermediateFieldType.String), | 28 | new IntermediateFieldDefinition(nameof(WixBundlePayloadSymbolFields.ParentPackagePayloadRef), IntermediateFieldType.String), |
29 | new IntermediateFieldDefinition(nameof(WixBundlePayloadSymbolFields.CertificatePublicKey), IntermediateFieldType.String), | ||
30 | new IntermediateFieldDefinition(nameof(WixBundlePayloadSymbolFields.CertificateThumbprint), IntermediateFieldType.String), | ||
29 | }, | 31 | }, |
30 | typeof(WixBundlePayloadSymbol)); | 32 | typeof(WixBundlePayloadSymbol)); |
31 | } | 33 | } |
@@ -53,6 +55,8 @@ namespace WixToolset.Data.Symbols | |||
53 | LayoutOnly, | 55 | LayoutOnly, |
54 | Packaging, | 56 | Packaging, |
55 | ParentPackagePayloadRef, | 57 | ParentPackagePayloadRef, |
58 | CertificatePublicKey, | ||
59 | CertificateThumbprint, | ||
56 | } | 60 | } |
57 | 61 | ||
58 | public class WixBundlePayloadSymbol : IntermediateSymbol | 62 | public class WixBundlePayloadSymbol : IntermediateSymbol |
@@ -162,5 +166,17 @@ namespace WixToolset.Data.Symbols | |||
162 | get => (string)this.Fields[(int)WixBundlePayloadSymbolFields.ParentPackagePayloadRef]; | 166 | get => (string)this.Fields[(int)WixBundlePayloadSymbolFields.ParentPackagePayloadRef]; |
163 | set => this.Set((int)WixBundlePayloadSymbolFields.ParentPackagePayloadRef, value); | 167 | set => this.Set((int)WixBundlePayloadSymbolFields.ParentPackagePayloadRef, value); |
164 | } | 168 | } |
169 | |||
170 | public string CertificatePublicKey | ||
171 | { | ||
172 | get => (string)this.Fields[(int)WixBundlePayloadSymbolFields.CertificatePublicKey]; | ||
173 | set => this.Set((int)WixBundlePayloadSymbolFields.CertificatePublicKey, value); | ||
174 | } | ||
175 | |||
176 | public string CertificateThumbprint | ||
177 | { | ||
178 | get => (string)this.Fields[(int)WixBundlePayloadSymbolFields.CertificateThumbprint]; | ||
179 | set => this.Set((int)WixBundlePayloadSymbolFields.CertificateThumbprint, value); | ||
180 | } | ||
165 | } | 181 | } |
166 | } | 182 | } |
diff --git a/src/wix/WixToolset.Converters.Symbolizer/WixToolset.Converters.Symbolizer.v3.ncrunchproject b/src/wix/WixToolset.Converters.Symbolizer/WixToolset.Converters.Symbolizer.v3.ncrunchproject new file mode 100644 index 00000000..cd54b69b --- /dev/null +++ b/src/wix/WixToolset.Converters.Symbolizer/WixToolset.Converters.Symbolizer.v3.ncrunchproject | |||
@@ -0,0 +1,7 @@ | |||
1 | <ProjectConfiguration> | ||
2 | <Settings> | ||
3 | <HiddenComponentWarnings> | ||
4 | <Value>LostReference</Value> | ||
5 | </HiddenComponentWarnings> | ||
6 | </Settings> | ||
7 | </ProjectConfiguration> \ No newline at end of file | ||
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs index c8f10a71..af45e736 100644 --- a/src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs +++ b/src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs | |||
@@ -672,7 +672,16 @@ namespace WixToolset.Core.Burn.Bundles | |||
672 | writer.WriteAttributeString("Id", payload.Id.Id); | 672 | writer.WriteAttributeString("Id", payload.Id.Id); |
673 | writer.WriteAttributeString("FilePath", payload.Name); | 673 | writer.WriteAttributeString("FilePath", payload.Name); |
674 | writer.WriteAttributeString("FileSize", payload.FileSize.Value.ToString(CultureInfo.InvariantCulture)); | 674 | writer.WriteAttributeString("FileSize", payload.FileSize.Value.ToString(CultureInfo.InvariantCulture)); |
675 | writer.WriteAttributeString("Hash", payload.Hash); | 675 | |
676 | if (!String.IsNullOrEmpty(payload.CertificatePublicKey) && !String.IsNullOrEmpty(payload.CertificateThumbprint)) | ||
677 | { | ||
678 | writer.WriteAttributeString("CertificateRootPublicKeyIdentifier", payload.CertificatePublicKey); | ||
679 | writer.WriteAttributeString("CertificateRootThumbprint", payload.CertificateThumbprint); | ||
680 | } | ||
681 | else | ||
682 | { | ||
683 | writer.WriteAttributeString("Hash", payload.Hash); | ||
684 | } | ||
676 | 685 | ||
677 | if (payload.LayoutOnly) | 686 | if (payload.LayoutOnly) |
678 | { | 687 | { |
diff --git a/src/wix/WixToolset.Core.Burn/BurnBackendErrors.cs b/src/wix/WixToolset.Core.Burn/BurnBackendErrors.cs index fd6eb093..74dc4cd5 100644 --- a/src/wix/WixToolset.Core.Burn/BurnBackendErrors.cs +++ b/src/wix/WixToolset.Core.Burn/BurnBackendErrors.cs | |||
@@ -56,6 +56,11 @@ namespace WixToolset.Core.Burn | |||
56 | return Message(null, Ids.IncompatibleWixBurnSection, "Unable to read bundle executable '{0}', because this bundle was created with a different version of WiX burn (.wixburn section version '{1}'). You must use the same version of Windows Installer XML in order to read this bundle.", bundleExecutable, bundleVersion); | 56 | return Message(null, Ids.IncompatibleWixBurnSection, "Unable to read bundle executable '{0}', because this bundle was created with a different version of WiX burn (.wixburn section version '{1}'). You must use the same version of Windows Installer XML in order to read this bundle.", bundleExecutable, bundleVersion); |
57 | } | 57 | } |
58 | 58 | ||
59 | public static Message UnsupportedRemotePackagePayload(string extension, string path) | ||
60 | { | ||
61 | return Message(null, Ids.UnsupportedRemotePackagePayload, "The first remote payload must be a supported package type of .exe or .msu. Use the -packageType switch to override the inferred extension: {0} from file: {1}", extension, path); | ||
62 | } | ||
63 | |||
59 | private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args) | 64 | private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args) |
60 | { | 65 | { |
61 | return new Message(sourceLineNumber, MessageLevel.Error, (int)id, format, args); | 66 | return new Message(sourceLineNumber, MessageLevel.Error, (int)id, format, args); |
@@ -73,6 +78,7 @@ namespace WixToolset.Core.Burn | |||
73 | PackageCachePayloadCollision2 = 8007, | 78 | PackageCachePayloadCollision2 = 8007, |
74 | TooManyAttachedContainers = 8008, | 79 | TooManyAttachedContainers = 8008, |
75 | IncompatibleWixBurnSection = 8009, | 80 | IncompatibleWixBurnSection = 8009, |
81 | UnsupportedRemotePackagePayload = 8010, | ||
76 | } // last available is 8499. 8500 is BurnBackendWarnings. | 82 | } // last available is 8499. 8500 is BurnBackendWarnings. |
77 | } | 83 | } |
78 | } | 84 | } |
diff --git a/src/wix/WixToolset.Core.Burn/CommandLine/BurnCommand.cs b/src/wix/WixToolset.Core.Burn/CommandLine/BurnCommand.cs index a50fb7cd..b552678f 100644 --- a/src/wix/WixToolset.Core.Burn/CommandLine/BurnCommand.cs +++ b/src/wix/WixToolset.Core.Burn/CommandLine/BurnCommand.cs | |||
@@ -52,6 +52,10 @@ namespace WixToolset.Core.Burn.CommandLine | |||
52 | case "reattach": | 52 | case "reattach": |
53 | this.Subcommand = new ReattachSubcommand(this.ServiceProvider); | 53 | this.Subcommand = new ReattachSubcommand(this.ServiceProvider); |
54 | return true; | 54 | return true; |
55 | |||
56 | case "remotepayload": | ||
57 | this.Subcommand = new RemotePayloadSubcommand(this.ServiceProvider); | ||
58 | return true; | ||
55 | } | 59 | } |
56 | 60 | ||
57 | return false; | 61 | return false; |
diff --git a/src/wix/WixToolset.Core.Burn/CommandLine/RemotePayloadSubcommand.cs b/src/wix/WixToolset.Core.Burn/CommandLine/RemotePayloadSubcommand.cs new file mode 100644 index 00000000..7b362485 --- /dev/null +++ b/src/wix/WixToolset.Core.Burn/CommandLine/RemotePayloadSubcommand.cs | |||
@@ -0,0 +1,272 @@ | |||
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.Burn.CommandLine | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.IO; | ||
8 | using System.Linq; | ||
9 | using System.Threading; | ||
10 | using System.Threading.Tasks; | ||
11 | using System.Xml.Linq; | ||
12 | using WixToolset.Core.Burn.Interfaces; | ||
13 | using WixToolset.Core.Native; | ||
14 | using WixToolset.Data; | ||
15 | using WixToolset.Data.Symbols; | ||
16 | using WixToolset.Extensibility.Services; | ||
17 | |||
18 | internal class RemotePayloadSubcommand : BurnSubcommandBase | ||
19 | { | ||
20 | private static readonly XName ExePackagePayloadName = "ExePackagePayload"; | ||
21 | private static readonly XName MsuPackagePayloadName = "MsuPackagePayload"; | ||
22 | private static readonly XName PayloadName = "Payload"; | ||
23 | |||
24 | public RemotePayloadSubcommand(IServiceProvider serviceProvider) | ||
25 | { | ||
26 | this.Messaging = serviceProvider.GetService<IMessaging>(); | ||
27 | this.PayloadHarvester = serviceProvider.GetService<IPayloadHarvester>(); | ||
28 | } | ||
29 | |||
30 | private IMessaging Messaging { get; } | ||
31 | |||
32 | private IPayloadHarvester PayloadHarvester { get; } | ||
33 | |||
34 | private List<string> BasePaths { get; } = new List<string>(); | ||
35 | |||
36 | private string DownloadUrl { get; set; } | ||
37 | |||
38 | private List<string> InputPaths { get; } = new List<string>(); | ||
39 | |||
40 | private string OutputPath { get; set; } | ||
41 | |||
42 | private WixBundlePackageType? PackageType { get; set; } | ||
43 | |||
44 | private bool Recurse { get; set; } | ||
45 | |||
46 | private bool UseCertificate { get; set; } | ||
47 | |||
48 | public override Task<int> ExecuteAsync(CancellationToken cancellationToken) | ||
49 | { | ||
50 | var inputPaths = this.ExpandInputPaths(); | ||
51 | if (inputPaths.Count == 0) | ||
52 | { | ||
53 | Console.Error.WriteLine("Path to a remote payload is required"); | ||
54 | return Task.FromResult(-1); | ||
55 | } | ||
56 | |||
57 | // Reverse sort to ensure longest paths are matched first. | ||
58 | this.BasePaths.Sort(); | ||
59 | this.BasePaths.Reverse(); | ||
60 | |||
61 | var elements = this.HarvestRemotePayloads(inputPaths); | ||
62 | |||
63 | if (!this.Messaging.EncounteredError) | ||
64 | { | ||
65 | if (!String.IsNullOrEmpty(this.OutputPath)) | ||
66 | { | ||
67 | var outputFolder = Path.GetDirectoryName(this.OutputPath); | ||
68 | Directory.CreateDirectory(outputFolder); | ||
69 | |||
70 | File.WriteAllLines(this.OutputPath, elements.Select(e => e.ToString())); | ||
71 | } | ||
72 | else | ||
73 | { | ||
74 | foreach (var element in elements) | ||
75 | { | ||
76 | Console.WriteLine(element); | ||
77 | } | ||
78 | } | ||
79 | } | ||
80 | |||
81 | return Task.FromResult(this.Messaging.LastErrorNumber); | ||
82 | } | ||
83 | |||
84 | public override bool TryParseArgument(ICommandLineParser parser, string argument) | ||
85 | { | ||
86 | if (parser.IsSwitch(argument)) | ||
87 | { | ||
88 | var parameter = argument.Substring(1); | ||
89 | switch (parameter.ToLowerInvariant()) | ||
90 | { | ||
91 | case "bp": | ||
92 | case "basepath": | ||
93 | this.BasePaths.Add(parser.GetNextArgumentAsDirectoryOrError(argument)); | ||
94 | return true; | ||
95 | |||
96 | case "du": | ||
97 | case "downloadurl": | ||
98 | this.DownloadUrl = parser.GetNextArgumentOrError(argument); | ||
99 | return true; | ||
100 | |||
101 | case "packagetype": | ||
102 | var packageTypeValue = parser.GetNextArgumentOrError(argument); | ||
103 | if (Enum.TryParse<WixBundlePackageType>(packageTypeValue, ignoreCase: true, out var packageType)) | ||
104 | { | ||
105 | this.PackageType = packageType; | ||
106 | return true; | ||
107 | } | ||
108 | break; | ||
109 | |||
110 | case "o": | ||
111 | case "out": | ||
112 | this.OutputPath = parser.GetNextArgumentAsFilePathOrError(argument); | ||
113 | return true; | ||
114 | |||
115 | case "r": | ||
116 | case "recurse": | ||
117 | this.Recurse = true; | ||
118 | return true; | ||
119 | |||
120 | case "usecertificate": | ||
121 | this.UseCertificate = true; | ||
122 | return true; | ||
123 | } | ||
124 | } | ||
125 | else | ||
126 | { | ||
127 | this.InputPaths.Add(argument); | ||
128 | return true; | ||
129 | } | ||
130 | |||
131 | return false; | ||
132 | } | ||
133 | |||
134 | private IReadOnlyCollection<string> ExpandInputPaths() | ||
135 | { | ||
136 | var result = new List<string>(); | ||
137 | |||
138 | foreach (var inputPath in this.InputPaths) | ||
139 | { | ||
140 | var filename = Path.GetFileName(inputPath); | ||
141 | var folder = Path.GetDirectoryName(inputPath); | ||
142 | |||
143 | if (String.IsNullOrEmpty(folder)) | ||
144 | { | ||
145 | folder = "."; | ||
146 | } | ||
147 | |||
148 | foreach (var path in Directory.EnumerateFiles(folder, filename, this.Recurse ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly)) | ||
149 | { | ||
150 | result.Add(path); | ||
151 | } | ||
152 | } | ||
153 | |||
154 | return result.Distinct(StringComparer.OrdinalIgnoreCase).ToList(); | ||
155 | } | ||
156 | |||
157 | private IEnumerable<XElement> HarvestRemotePayloads(IEnumerable<string> paths) | ||
158 | { | ||
159 | var first = true; | ||
160 | var hashes = new Dictionary<string, CertificateHashes>(); | ||
161 | |||
162 | if (this.UseCertificate) | ||
163 | { | ||
164 | hashes = CertificateHashes.Read(paths).Where(c => !String.IsNullOrEmpty(c.PublicKey) && !String.IsNullOrEmpty(c.Thumbprint) && c.Exception is null).ToDictionary(c => c.Path); | ||
165 | } | ||
166 | |||
167 | foreach (var path in paths) | ||
168 | { | ||
169 | var element = this.CreateRemotePayloadElement(path, first); | ||
170 | first = false; | ||
171 | |||
172 | if (element == null) | ||
173 | { | ||
174 | continue; | ||
175 | } | ||
176 | |||
177 | var payloadSymbol = new WixBundlePayloadSymbol | ||
178 | { | ||
179 | SourceFile = new IntermediateFieldPathValue { Path = path }, | ||
180 | }; | ||
181 | |||
182 | this.PayloadHarvester.HarvestStandardInformation(payloadSymbol); | ||
183 | |||
184 | element.Add(new XAttribute("Name", Path.GetFileName(path))); | ||
185 | |||
186 | if (!String.IsNullOrEmpty(payloadSymbol.DisplayName)) | ||
187 | { | ||
188 | element.Add(new XAttribute("ProductName", payloadSymbol.DisplayName)); | ||
189 | } | ||
190 | |||
191 | if (!String.IsNullOrEmpty(payloadSymbol.Description)) | ||
192 | { | ||
193 | element.Add(new XAttribute("Description", payloadSymbol.Description)); | ||
194 | } | ||
195 | |||
196 | if (!String.IsNullOrEmpty(this.DownloadUrl)) | ||
197 | { | ||
198 | var filename = this.GetRelativeFileName(payloadSymbol.SourceFile.Path); | ||
199 | var formattedUrl = String.Format(this.DownloadUrl, filename); | ||
200 | |||
201 | if (Uri.TryCreate(formattedUrl, UriKind.Absolute, out var url)) | ||
202 | { | ||
203 | element.Add(new XAttribute("DownloadUrl", url.AbsoluteUri)); | ||
204 | } | ||
205 | } | ||
206 | |||
207 | if (hashes.TryGetValue(path, out var certificateHashes)) | ||
208 | { | ||
209 | element.Add(new XAttribute("CertificatePublicKey", certificateHashes.PublicKey)); | ||
210 | element.Add(new XAttribute("CertificateThumbprint", certificateHashes.Thumbprint)); | ||
211 | } | ||
212 | |||
213 | if (!String.IsNullOrEmpty(payloadSymbol.Hash)) | ||
214 | { | ||
215 | element.Add(new XAttribute("Hash", payloadSymbol.Hash)); | ||
216 | } | ||
217 | |||
218 | if (payloadSymbol.FileSize.HasValue) | ||
219 | { | ||
220 | element.Add(new XAttribute("Size", payloadSymbol.FileSize.Value)); | ||
221 | } | ||
222 | |||
223 | if (!String.IsNullOrEmpty(payloadSymbol.Version)) | ||
224 | { | ||
225 | element.Add(new XAttribute("Version", payloadSymbol.Version)); | ||
226 | } | ||
227 | |||
228 | yield return element; | ||
229 | } | ||
230 | } | ||
231 | |||
232 | private XElement CreateRemotePayloadElement(string path, bool firstHarvest) | ||
233 | { | ||
234 | if (firstHarvest) | ||
235 | { | ||
236 | var extension = this.PackageType.HasValue ? this.PackageType.ToString() : Path.GetExtension(path); | ||
237 | |||
238 | switch (extension.ToUpperInvariant()) | ||
239 | { | ||
240 | case "EXE": | ||
241 | case ".EXE": | ||
242 | return new XElement(ExePackagePayloadName); | ||
243 | |||
244 | case "MSU": | ||
245 | case ".MSU": | ||
246 | return new XElement(MsuPackagePayloadName); | ||
247 | |||
248 | default: | ||
249 | this.Messaging.Write(BurnBackendErrors.UnsupportedRemotePackagePayload(extension, path)); | ||
250 | return null; | ||
251 | } | ||
252 | } | ||
253 | else | ||
254 | { | ||
255 | return new XElement(PayloadName); | ||
256 | } | ||
257 | } | ||
258 | |||
259 | private string GetRelativeFileName(string path) | ||
260 | { | ||
261 | foreach (var basePath in this.BasePaths) | ||
262 | { | ||
263 | if (path.StartsWith(basePath, StringComparison.OrdinalIgnoreCase)) | ||
264 | { | ||
265 | return path.Substring(basePath.Length).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); | ||
266 | } | ||
267 | } | ||
268 | |||
269 | return Path.GetFileName(path); | ||
270 | } | ||
271 | } | ||
272 | } | ||
diff --git a/src/wix/WixToolset.Core.Native/CertificateHashes.cs b/src/wix/WixToolset.Core.Native/CertificateHashes.cs new file mode 100644 index 00000000..a024c923 --- /dev/null +++ b/src/wix/WixToolset.Core.Native/CertificateHashes.cs | |||
@@ -0,0 +1,83 @@ | |||
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.Native | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.ComponentModel; | ||
8 | using System.Globalization; | ||
9 | using System.Linq; | ||
10 | |||
11 | /// <summary> | ||
12 | /// Read certificates' public key and thumbprint hashes. | ||
13 | /// </summary> | ||
14 | public sealed class CertificateHashes | ||
15 | { | ||
16 | private static readonly char[] TextLineSplitter = new[] { '\t' }; | ||
17 | |||
18 | private CertificateHashes(string path, string publicKey, string thumbprint, Exception exception) | ||
19 | { | ||
20 | this.Path = path; | ||
21 | this.PublicKey = publicKey; | ||
22 | this.Thumbprint = thumbprint; | ||
23 | this.Exception = exception; | ||
24 | } | ||
25 | |||
26 | /// <summary> | ||
27 | /// Path to the file read. | ||
28 | /// </summary> | ||
29 | public string Path { get; } | ||
30 | |||
31 | /// <summary> | ||
32 | /// Hash of the certificate's public key. | ||
33 | /// </summary> | ||
34 | public string PublicKey { get; } | ||
35 | |||
36 | /// <summary> | ||
37 | /// Hash of the certificate's thumbprint. | ||
38 | /// </summary> | ||
39 | public string Thumbprint { get; } | ||
40 | |||
41 | /// <summary> | ||
42 | /// Exception encountered while trying to read certificate's hash. | ||
43 | /// </summary> | ||
44 | public Exception Exception { get; } | ||
45 | |||
46 | /// <summary> | ||
47 | /// Read the certificate hashes from the provided paths. | ||
48 | /// </summary> | ||
49 | /// <param name="paths">Paths to read for certificates.</param> | ||
50 | /// <returns>Certificate hashes for the provided paths.</returns> | ||
51 | public static IReadOnlyList<CertificateHashes> Read(IEnumerable<string> paths) | ||
52 | { | ||
53 | var result = new List<CertificateHashes>(); | ||
54 | |||
55 | var wixnative = new WixNativeExe("certhashes"); | ||
56 | |||
57 | foreach (var path in paths) | ||
58 | { | ||
59 | wixnative.AddStdinLine(path); | ||
60 | } | ||
61 | |||
62 | try | ||
63 | { | ||
64 | var outputLines = wixnative.Run(); | ||
65 | foreach (var line in outputLines.Where(l => !String.IsNullOrEmpty(l))) | ||
66 | { | ||
67 | var data = line.Split(TextLineSplitter, StringSplitOptions.None); | ||
68 | |||
69 | var error = Int32.Parse(data[3].Substring(2), NumberStyles.HexNumber); | ||
70 | var exception = error != 0 ? new Win32Exception(error) : null; | ||
71 | |||
72 | result.Add(new CertificateHashes(data[0], data[1], data[2], exception)); | ||
73 | } | ||
74 | } | ||
75 | catch (Exception e) | ||
76 | { | ||
77 | result.Add(new CertificateHashes(null, null, null, e)); | ||
78 | } | ||
79 | |||
80 | return result; | ||
81 | } | ||
82 | } | ||
83 | } | ||
diff --git a/src/wix/WixToolset.Core.Native/WixNativeExe.cs b/src/wix/WixToolset.Core.Native/WixNativeExe.cs index 13d7a4ea..f5ff8bf0 100644 --- a/src/wix/WixToolset.Core.Native/WixNativeExe.cs +++ b/src/wix/WixToolset.Core.Native/WixNativeExe.cs | |||
@@ -30,7 +30,7 @@ namespace WixToolset.Core.Native | |||
30 | this.stdinLines.AddRange(lines); | 30 | this.stdinLines.AddRange(lines); |
31 | } | 31 | } |
32 | 32 | ||
33 | public IEnumerable<string> Run() | 33 | public IReadOnlyCollection<string> Run() |
34 | { | 34 | { |
35 | EnsurePathToWixNativeExeSet(); | 35 | EnsurePathToWixNativeExeSet(); |
36 | 36 | ||
diff --git a/src/wix/WixToolset.Core/Compile/CompilerPayload.cs b/src/wix/WixToolset.Core/Compile/CompilerPayload.cs index ea63ee8c..cee9b377 100644 --- a/src/wix/WixToolset.Core/Compile/CompilerPayload.cs +++ b/src/wix/WixToolset.Core/Compile/CompilerPayload.cs | |||
@@ -11,12 +11,31 @@ namespace WixToolset.Core | |||
11 | 11 | ||
12 | internal class CompilerPayload | 12 | internal class CompilerPayload |
13 | { | 13 | { |
14 | public string Version { get; set; } | ||
15 | |||
16 | public CompilerPayload(CompilerCore core, SourceLineNumber sourceLineNumbers, XElement element) | ||
17 | { | ||
18 | this.Core = core; | ||
19 | this.Element = element; | ||
20 | this.SourceLineNumbers = sourceLineNumbers; | ||
21 | } | ||
22 | |||
23 | private CompilerCore Core { get; } | ||
24 | |||
25 | private XElement Element { get; } | ||
26 | |||
27 | private SourceLineNumber SourceLineNumbers { get; } | ||
28 | |||
14 | public YesNoDefaultType Compressed { get; set; } = YesNoDefaultType.Default; | 29 | public YesNoDefaultType Compressed { get; set; } = YesNoDefaultType.Default; |
15 | 30 | ||
16 | public string Description { get; set; } | 31 | public string Description { get; set; } |
17 | 32 | ||
18 | public string DownloadUrl { get; set; } | 33 | public string DownloadUrl { get; set; } |
19 | 34 | ||
35 | public string CertificatePublicKey { get; set; } | ||
36 | |||
37 | public string CertificateThumbprint { get; set; } | ||
38 | |||
20 | public string Hash { get; set; } | 39 | public string Hash { get; set; } |
21 | 40 | ||
22 | public Identifier Id { get; set; } | 41 | public Identifier Id { get; set; } |
@@ -33,24 +52,9 @@ namespace WixToolset.Core | |||
33 | 52 | ||
34 | public string SourceFile { get; set; } | 53 | public string SourceFile { get; set; } |
35 | 54 | ||
36 | public string Version { get; set; } | ||
37 | |||
38 | public CompilerPayload(CompilerCore core, SourceLineNumber sourceLineNumbers, XElement element) | ||
39 | { | ||
40 | this.Core = core; | ||
41 | this.Element = element; | ||
42 | this.SourceLineNumbers = sourceLineNumbers; | ||
43 | } | ||
44 | |||
45 | private CompilerCore Core { get; } | ||
46 | |||
47 | private XElement Element { get; } | ||
48 | |||
49 | private SourceLineNumber SourceLineNumbers { get; } | ||
50 | |||
51 | private void CalculateAndVerifyFields() | 55 | private void CalculateAndVerifyFields() |
52 | { | 56 | { |
53 | var isRemote = this.IsRemoteAllowed && !String.IsNullOrEmpty(this.Hash); | 57 | var isRemote = this.IsRemoteAllowed && (!String.IsNullOrEmpty(this.CertificatePublicKey) || !String.IsNullOrEmpty(this.CertificateThumbprint) || !String.IsNullOrEmpty(this.Hash)); |
54 | 58 | ||
55 | if (String.IsNullOrEmpty(this.SourceFile)) | 59 | if (String.IsNullOrEmpty(this.SourceFile)) |
56 | { | 60 | { |
@@ -81,7 +85,7 @@ namespace WixToolset.Core | |||
81 | } | 85 | } |
82 | else | 86 | else |
83 | { | 87 | { |
84 | this.Core.Write(ErrorMessages.ExpectedAttributes(this.SourceLineNumbers, this.Element.Name.LocalName, "SourceFile", "Hash")); | 88 | this.Core.Write(ErrorMessages.ExpectedAttributes(this.SourceLineNumbers, this.Element.Name.LocalName, "SourceFile", "CertificatePublicKey", "Hash")); |
85 | } | 89 | } |
86 | } | 90 | } |
87 | } | 91 | } |
@@ -93,7 +97,20 @@ namespace WixToolset.Core | |||
93 | { | 97 | { |
94 | if (isRemote) | 98 | if (isRemote) |
95 | { | 99 | { |
96 | this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, "Hash", "SourceFile")); | 100 | if (!String.IsNullOrEmpty(this.Hash)) |
101 | { | ||
102 | this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, "Hash", "SourceFile")); | ||
103 | } | ||
104 | |||
105 | if (!String.IsNullOrEmpty(this.CertificatePublicKey)) | ||
106 | { | ||
107 | this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, "CertificatePublicKey", "SourceFile")); | ||
108 | } | ||
109 | |||
110 | if (!String.IsNullOrEmpty(this.CertificateThumbprint)) | ||
111 | { | ||
112 | this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, "CertificateThumbprint", "SourceFile")); | ||
113 | } | ||
97 | } | 114 | } |
98 | 115 | ||
99 | if (!String.IsNullOrEmpty(this.Description)) | 116 | if (!String.IsNullOrEmpty(this.Description)) |
@@ -120,17 +137,34 @@ namespace WixToolset.Core | |||
120 | { | 137 | { |
121 | if (String.IsNullOrEmpty(this.DownloadUrl)) | 138 | if (String.IsNullOrEmpty(this.DownloadUrl)) |
122 | { | 139 | { |
123 | this.Core.Write(ErrorMessages.ExpectedAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, "DownloadUrl", "Hash")); | 140 | this.Core.Write(ErrorMessages.ExpectedAttributeWithoutOtherAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, "DownloadUrl", "SourceFile")); |
124 | } | 141 | } |
125 | 142 | ||
126 | if (String.IsNullOrEmpty(this.Name)) | 143 | if (String.IsNullOrEmpty(this.Name)) |
127 | { | 144 | { |
128 | this.Core.Write(ErrorMessages.ExpectedAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, "Name", "Hash")); | 145 | this.Core.Write(ErrorMessages.ExpectedAttributeWithoutOtherAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, "Name", "SourceFile")); |
129 | } | 146 | } |
130 | 147 | ||
131 | if (!this.Size.HasValue) | 148 | if (!this.Size.HasValue) |
132 | { | 149 | { |
133 | this.Core.Write(ErrorMessages.ExpectedAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, "Size", "Hash")); | 150 | this.Core.Write(ErrorMessages.ExpectedAttributeWithoutOtherAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, "Size", "SourceFile")); |
151 | } | ||
152 | |||
153 | if (String.IsNullOrEmpty(this.Hash)) | ||
154 | { | ||
155 | this.Core.Write(ErrorMessages.ExpectedAttributeWithoutOtherAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, "Hash", "SourceFile")); | ||
156 | } | ||
157 | |||
158 | if (!String.IsNullOrEmpty(this.CertificatePublicKey) || !String.IsNullOrEmpty(this.CertificateThumbprint)) | ||
159 | { | ||
160 | if (String.IsNullOrEmpty(this.CertificateThumbprint)) | ||
161 | { | ||
162 | this.Core.Write(ErrorMessages.ExpectedAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, "CertificateThumbprint", "CertificatePublicKey")); | ||
163 | } | ||
164 | else if (String.IsNullOrEmpty(this.CertificatePublicKey)) | ||
165 | { | ||
166 | this.Core.Write(ErrorMessages.ExpectedAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, "CertificatePublicKey", "CertificateThumbprint")); | ||
167 | } | ||
134 | } | 168 | } |
135 | 169 | ||
136 | if (YesNoDefaultType.Yes == this.Compressed) | 170 | if (YesNoDefaultType.Yes == this.Compressed) |
@@ -177,6 +211,8 @@ namespace WixToolset.Core | |||
177 | Hash = this.Hash, | 211 | Hash = this.Hash, |
178 | FileSize = this.Size, | 212 | FileSize = this.Size, |
179 | Version = this.Version, | 213 | Version = this.Version, |
214 | CertificatePublicKey = this.CertificatePublicKey, | ||
215 | CertificateThumbprint = this.CertificateThumbprint | ||
180 | }); | 216 | }); |
181 | 217 | ||
182 | this.Core.CreateGroupAndOrderingRows(this.SourceLineNumbers, parentType, parentId, ComplexReferenceChildType.Payload, symbol.Id.Id, ComplexReferenceChildType.Unknown, null); | 218 | this.Core.CreateGroupAndOrderingRows(this.SourceLineNumbers, parentType, parentId, ComplexReferenceChildType.Payload, symbol.Id.Id, ComplexReferenceChildType.Unknown, null); |
@@ -248,6 +284,16 @@ namespace WixToolset.Core | |||
248 | this.DownloadUrl = this.Core.GetAttributeValue(this.SourceLineNumbers, attrib); | 284 | this.DownloadUrl = this.Core.GetAttributeValue(this.SourceLineNumbers, attrib); |
249 | } | 285 | } |
250 | 286 | ||
287 | public void ParseCertificatePublicKey(XAttribute attrib) | ||
288 | { | ||
289 | this.CertificatePublicKey = this.Core.GetAttributeValue(this.SourceLineNumbers, attrib); | ||
290 | } | ||
291 | |||
292 | public void ParseCertificateThumbprint(XAttribute attrib) | ||
293 | { | ||
294 | this.CertificateThumbprint = this.Core.GetAttributeValue(this.SourceLineNumbers, attrib); | ||
295 | } | ||
296 | |||
251 | public void ParseHash(XAttribute attrib) | 297 | public void ParseHash(XAttribute attrib) |
252 | { | 298 | { |
253 | this.Hash = this.Core.GetAttributeValue(this.SourceLineNumbers, attrib); | 299 | this.Hash = this.Core.GetAttributeValue(this.SourceLineNumbers, attrib); |
@@ -286,6 +332,5 @@ namespace WixToolset.Core | |||
286 | { | 332 | { |
287 | this.Version = this.Core.GetAttributeValue(this.SourceLineNumbers, attrib); | 333 | this.Version = this.Core.GetAttributeValue(this.SourceLineNumbers, attrib); |
288 | } | 334 | } |
289 | |||
290 | } | 335 | } |
291 | } | 336 | } |
diff --git a/src/wix/WixToolset.Core/Compiler_Bundle.cs b/src/wix/WixToolset.Core/Compiler_Bundle.cs index c8b78243..3fde990e 100644 --- a/src/wix/WixToolset.Core/Compiler_Bundle.cs +++ b/src/wix/WixToolset.Core/Compiler_Bundle.cs | |||
@@ -2459,6 +2459,20 @@ namespace WixToolset.Core | |||
2459 | case "SourceFile": | 2459 | case "SourceFile": |
2460 | compilerPayload.ParseSourceFile(attrib); | 2460 | compilerPayload.ParseSourceFile(attrib); |
2461 | break; | 2461 | break; |
2462 | case "CertificatePublicKey": | ||
2463 | allowed = compilerPayload.IsRemoteAllowed; | ||
2464 | if (allowed) | ||
2465 | { | ||
2466 | compilerPayload.ParseCertificatePublicKey(attrib); | ||
2467 | } | ||
2468 | break; | ||
2469 | case "CertificateThumbprint": | ||
2470 | allowed = compilerPayload.IsRemoteAllowed; | ||
2471 | if (allowed) | ||
2472 | { | ||
2473 | compilerPayload.ParseCertificateThumbprint(attrib); | ||
2474 | } | ||
2475 | break; | ||
2462 | case "DownloadUrl": | 2476 | case "DownloadUrl": |
2463 | compilerPayload.ParseDownloadUrl(attrib); | 2477 | compilerPayload.ParseDownloadUrl(attrib); |
2464 | break; | 2478 | break; |
diff --git a/src/wix/heat/PayloadHarvester.cs b/src/wix/heat/PayloadHarvester.cs deleted file mode 100644 index d2492512..00000000 --- a/src/wix/heat/PayloadHarvester.cs +++ /dev/null | |||
@@ -1,129 +0,0 @@ | |||
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.Harvesters | ||
4 | { | ||
5 | using System; | ||
6 | using System.IO; | ||
7 | using WixToolset.Core.Burn.Interfaces; | ||
8 | using WixToolset.Data; | ||
9 | using WixToolset.Data.Symbols; | ||
10 | using WixToolset.Harvesters.Data; | ||
11 | using WixToolset.Harvesters.Extensibility; | ||
12 | using Wix = WixToolset.Harvesters.Serialize; | ||
13 | |||
14 | /// <summary> | ||
15 | /// Harvest WiX authoring for a payload from the file system. | ||
16 | /// </summary> | ||
17 | public sealed class PayloadHarvester : BaseHarvesterExtension | ||
18 | { | ||
19 | private bool setUniqueIdentifiers; | ||
20 | private WixBundlePackageType packageType; | ||
21 | |||
22 | private IPayloadHarvester payloadHarvester; | ||
23 | |||
24 | /// <summary> | ||
25 | /// Instantiate a new PayloadHarvester. | ||
26 | /// </summary> | ||
27 | public PayloadHarvester(IPayloadHarvester payloadHarvester, WixBundlePackageType packageType) | ||
28 | { | ||
29 | this.payloadHarvester = payloadHarvester; | ||
30 | |||
31 | this.packageType = packageType; | ||
32 | this.setUniqueIdentifiers = true; | ||
33 | } | ||
34 | |||
35 | /// <summary> | ||
36 | /// Gets of sets the option to set unique identifiers. | ||
37 | /// </summary> | ||
38 | /// <value>The option to set unique identifiers.</value> | ||
39 | public bool SetUniqueIdentifiers | ||
40 | { | ||
41 | get { return this.setUniqueIdentifiers; } | ||
42 | set { this.setUniqueIdentifiers = value; } | ||
43 | } | ||
44 | |||
45 | /// <summary> | ||
46 | /// Harvest a payload. | ||
47 | /// </summary> | ||
48 | /// <param name="argument">The path of the payload.</param> | ||
49 | /// <returns>A harvested payload.</returns> | ||
50 | public override Wix.Fragment[] Harvest(string argument) | ||
51 | { | ||
52 | if (null == argument) | ||
53 | { | ||
54 | throw new ArgumentNullException("argument"); | ||
55 | } | ||
56 | |||
57 | string fullPath = Path.GetFullPath(argument); | ||
58 | |||
59 | var remotePayload = this.HarvestRemotePayload(fullPath); | ||
60 | |||
61 | var fragment = new Wix.Fragment(); | ||
62 | fragment.AddChild(remotePayload); | ||
63 | |||
64 | return new Wix.Fragment[] { fragment }; | ||
65 | } | ||
66 | |||
67 | /// <summary> | ||
68 | /// Harvest a payload. | ||
69 | /// </summary> | ||
70 | /// <param name="path">The path of the payload.</param> | ||
71 | /// <returns>A harvested payload.</returns> | ||
72 | public Wix.RemotePayload HarvestRemotePayload(string path) | ||
73 | { | ||
74 | if (null == path) | ||
75 | { | ||
76 | throw new ArgumentNullException("path"); | ||
77 | } | ||
78 | |||
79 | if (!File.Exists(path)) | ||
80 | { | ||
81 | throw new WixException(HarvesterErrors.FileNotFound(path)); | ||
82 | } | ||
83 | |||
84 | Wix.RemotePayload remotePayload; | ||
85 | |||
86 | switch (this.packageType) | ||
87 | { | ||
88 | case WixBundlePackageType.Exe: | ||
89 | remotePayload = new Wix.ExePackagePayload(); | ||
90 | break; | ||
91 | case WixBundlePackageType.Msu: | ||
92 | remotePayload = new Wix.MsuPackagePayload(); | ||
93 | break; | ||
94 | default: | ||
95 | throw new NotImplementedException(); | ||
96 | } | ||
97 | |||
98 | var payloadSymbol = new WixBundlePayloadSymbol | ||
99 | { | ||
100 | SourceFile = new IntermediateFieldPathValue { Path = path }, | ||
101 | }; | ||
102 | |||
103 | this.payloadHarvester.HarvestStandardInformation(payloadSymbol); | ||
104 | |||
105 | if (payloadSymbol.FileSize.HasValue) | ||
106 | { | ||
107 | remotePayload.Size = payloadSymbol.FileSize.Value; | ||
108 | } | ||
109 | remotePayload.Hash = payloadSymbol.Hash; | ||
110 | |||
111 | if (!String.IsNullOrEmpty(payloadSymbol.Version)) | ||
112 | { | ||
113 | remotePayload.Version = payloadSymbol.Version; | ||
114 | } | ||
115 | |||
116 | if (!String.IsNullOrEmpty(payloadSymbol.Description)) | ||
117 | { | ||
118 | remotePayload.Description = payloadSymbol.Description; | ||
119 | } | ||
120 | |||
121 | if (!String.IsNullOrEmpty(payloadSymbol.DisplayName)) | ||
122 | { | ||
123 | remotePayload.ProductName = payloadSymbol.DisplayName; | ||
124 | } | ||
125 | |||
126 | return remotePayload; | ||
127 | } | ||
128 | } | ||
129 | } | ||
diff --git a/src/wix/heat/UtilHeatExtension.cs b/src/wix/heat/UtilHeatExtension.cs index e5be9cac..5ad5ef8a 100644 --- a/src/wix/heat/UtilHeatExtension.cs +++ b/src/wix/heat/UtilHeatExtension.cs | |||
@@ -87,14 +87,6 @@ namespace WixToolset.Harvesters | |||
87 | harvesterExtension = new FileHarvester(); | 87 | harvesterExtension = new FileHarvester(); |
88 | active = true; | 88 | active = true; |
89 | break; | 89 | break; |
90 | case "exepackagepayload": | ||
91 | harvesterExtension = new PayloadHarvester(this.PayloadHarvester, WixBundlePackageType.Exe); | ||
92 | active = true; | ||
93 | break; | ||
94 | case "msupackagepayload": | ||
95 | harvesterExtension = new PayloadHarvester(this.PayloadHarvester, WixBundlePackageType.Msu); | ||
96 | active = true; | ||
97 | break; | ||
98 | case "perf": | 90 | case "perf": |
99 | harvesterExtension = new PerformanceCategoryHarvester(); | 91 | harvesterExtension = new PerformanceCategoryHarvester(); |
100 | active = true; | 92 | active = true; |
diff --git a/src/wix/test/WixToolsetTest.Converters.Symbolizer/WixToolsetTest.Converters.Symbolizer.v3.ncrunchproject b/src/wix/test/WixToolsetTest.Converters.Symbolizer/WixToolsetTest.Converters.Symbolizer.v3.ncrunchproject index 18ab4f79..b8c570ae 100644 --- a/src/wix/test/WixToolsetTest.Converters.Symbolizer/WixToolsetTest.Converters.Symbolizer.v3.ncrunchproject +++ b/src/wix/test/WixToolsetTest.Converters.Symbolizer/WixToolsetTest.Converters.Symbolizer.v3.ncrunchproject | |||
@@ -1,5 +1,8 @@ | |||
1 | <ProjectConfiguration> | 1 | <ProjectConfiguration> |
2 | <Settings> | 2 | <Settings> |
3 | <HiddenComponentWarnings> | ||
4 | <Value>LostReference</Value> | ||
5 | </HiddenComponentWarnings> | ||
3 | <IgnoredTests> | 6 | <IgnoredTests> |
4 | <NamedTestSelector> | 7 | <NamedTestSelector> |
5 | <TestName>WixToolsetTest.Converters.Symbolizer.ConvertSymbolsFixture.CanLoadWixoutAndConvertToIntermediate</TestName> | 8 | <TestName>WixToolsetTest.Converters.Symbolizer.ConvertSymbolsFixture.CanLoadWixoutAndConvertToIntermediate</TestName> |
diff --git a/src/wix/test/WixToolsetTest.Core.Native/CertificateHashesFixture.cs b/src/wix/test/WixToolsetTest.Core.Native/CertificateHashesFixture.cs new file mode 100644 index 00000000..5783445b --- /dev/null +++ b/src/wix/test/WixToolsetTest.Core.Native/CertificateHashesFixture.cs | |||
@@ -0,0 +1,58 @@ | |||
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.CoreNative | ||
4 | { | ||
5 | using System.ComponentModel; | ||
6 | using System.Linq; | ||
7 | using WixToolset.Core.Native; | ||
8 | using WixToolsetTest.CoreNative.Utility; | ||
9 | using Xunit; | ||
10 | |||
11 | public class CertificateHashesFixture | ||
12 | { | ||
13 | [Fact] | ||
14 | public void CanGetHashesFromSignedFile() | ||
15 | { | ||
16 | var cabFile = TestData.Get(@"TestData\test.cab"); | ||
17 | |||
18 | var hashes = CertificateHashes.Read(new[] { cabFile }); | ||
19 | |||
20 | var hash = hashes.Single(); | ||
21 | Assert.Equal(cabFile, hash.Path); | ||
22 | Assert.Equal("7EC90B3FC3D580EB571210011F1095E149DCC6BB", hash.PublicKey); | ||
23 | Assert.Equal("0B13494DB50BC185A34389BBBAA01EDD1CF56350", hash.Thumbprint); | ||
24 | Assert.Null(hash.Exception); | ||
25 | } | ||
26 | |||
27 | [Fact] | ||
28 | public void CannotGetHashesFromUnsignedFile() | ||
29 | { | ||
30 | var txtFile = TestData.Get(@"TestData\test.txt"); | ||
31 | |||
32 | var hashes = CertificateHashes.Read(new[] { txtFile }); | ||
33 | |||
34 | var hash = hashes.Single(); | ||
35 | Assert.Equal(txtFile, hash.Path); | ||
36 | Assert.Null(hash.Exception); | ||
37 | } | ||
38 | |||
39 | [Fact] | ||
40 | public void CanGetMultipleHashes() | ||
41 | { | ||
42 | var cabFile = TestData.Get(@"TestData\test.cab"); | ||
43 | var txtFile = TestData.Get(@"TestData\test.txt"); | ||
44 | |||
45 | var hashes = CertificateHashes.Read(new[] { cabFile, txtFile }); | ||
46 | |||
47 | Assert.Equal(cabFile, hashes[0].Path); | ||
48 | Assert.Equal("7EC90B3FC3D580EB571210011F1095E149DCC6BB", hashes[0].PublicKey); | ||
49 | Assert.Equal("0B13494DB50BC185A34389BBBAA01EDD1CF56350", hashes[0].Thumbprint); | ||
50 | Assert.Null(hashes[0].Exception); | ||
51 | |||
52 | Assert.Equal(txtFile, hashes[1].Path); | ||
53 | Assert.Empty(hashes[1].PublicKey); | ||
54 | Assert.Empty(hashes[1].Thumbprint); | ||
55 | Assert.Null(hashes[1].Exception); | ||
56 | } | ||
57 | } | ||
58 | } | ||
diff --git a/src/wix/test/WixToolsetTest.Core.Native/TestData/test.cab b/src/wix/test/WixToolsetTest.Core.Native/TestData/test.cab index ca78f632..9700cd64 100644 --- a/src/wix/test/WixToolsetTest.Core.Native/TestData/test.cab +++ b/src/wix/test/WixToolsetTest.Core.Native/TestData/test.cab | |||
Binary files differ | |||
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/PackagePayloadFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/PackagePayloadFixture.cs index 6b2d8bfa..5d0c8561 100644 --- a/src/wix/test/WixToolsetTest.CoreIntegration/PackagePayloadFixture.cs +++ b/src/wix/test/WixToolsetTest.CoreIntegration/PackagePayloadFixture.cs | |||
@@ -57,6 +57,26 @@ namespace WixToolsetTest.CoreIntegration | |||
57 | } | 57 | } |
58 | 58 | ||
59 | [Fact] | 59 | [Fact] |
60 | public void CanSpecifyPackagePayloadWithCertificate() | ||
61 | { | ||
62 | var folder = TestData.Get(@"TestData", "PackagePayload"); | ||
63 | |||
64 | using (var fs = new DisposableFileSystem()) | ||
65 | { | ||
66 | var baseFolder = fs.GetFolder(); | ||
67 | |||
68 | var result = WixRunner.Execute(new[] | ||
69 | { | ||
70 | "build", | ||
71 | Path.Combine(folder, "SpecifiedCertificate.wxs"), | ||
72 | "-o", Path.Combine(baseFolder, "test.wixlib") | ||
73 | }); | ||
74 | |||
75 | result.AssertSuccess(); | ||
76 | } | ||
77 | } | ||
78 | |||
79 | [Fact] | ||
60 | public void ErrorWhenMissingSourceFileAndHash() | 80 | public void ErrorWhenMissingSourceFileAndHash() |
61 | { | 81 | { |
62 | var folder = TestData.Get(@"TestData", "PackagePayload"); | 82 | var folder = TestData.Get(@"TestData", "PackagePayload"); |
@@ -75,7 +95,7 @@ namespace WixToolsetTest.CoreIntegration | |||
75 | Assert.Equal(44, result.ExitCode); | 95 | Assert.Equal(44, result.ExitCode); |
76 | WixAssert.CompareLineByLine(new[] | 96 | WixAssert.CompareLineByLine(new[] |
77 | { | 97 | { |
78 | "The MsuPackagePayload element's SourceFile or Hash attribute was not found; one of these is required.", | 98 | "The MsuPackagePayload element's SourceFile, CertificatePublicKey, or Hash attribute was not found; one of these is required.", |
79 | }, result.Messages.Select(m => m.ToString()).ToArray()); | 99 | }, result.Messages.Select(m => m.ToString()).ToArray()); |
80 | } | 100 | } |
81 | } | 101 | } |
@@ -144,10 +164,10 @@ namespace WixToolsetTest.CoreIntegration | |||
144 | "-o", Path.Combine(baseFolder, "test.wixlib") | 164 | "-o", Path.Combine(baseFolder, "test.wixlib") |
145 | }); | 165 | }); |
146 | 166 | ||
147 | Assert.Equal(10, result.ExitCode); | 167 | Assert.Equal(408, result.ExitCode); |
148 | WixAssert.CompareLineByLine(new[] | 168 | WixAssert.CompareLineByLine(new[] |
149 | { | 169 | { |
150 | "The MsuPackagePayload/@DownloadUrl attribute was not found; it is required when attribute Hash is specified.", | 170 | "The MsuPackagePayload element's DownloadUrl attribute was not found; it is required without attribute SourceFile present.", |
151 | }, result.Messages.Select(m => m.ToString()).ToArray()); | 171 | }, result.Messages.Select(m => m.ToString()).ToArray()); |
152 | } | 172 | } |
153 | } | 173 | } |
@@ -177,6 +197,102 @@ namespace WixToolsetTest.CoreIntegration | |||
177 | } | 197 | } |
178 | 198 | ||
179 | [Fact] | 199 | [Fact] |
200 | public void ErrorWhenSpecifiedSourceFileAndCertificatePublicKey() | ||
201 | { | ||
202 | var folder = TestData.Get(@"TestData", "PackagePayload"); | ||
203 | |||
204 | using (var fs = new DisposableFileSystem()) | ||
205 | { | ||
206 | var baseFolder = fs.GetFolder(); | ||
207 | |||
208 | var result = WixRunner.Execute(new[] | ||
209 | { | ||
210 | "build", | ||
211 | Path.Combine(folder, "SpecifiedSourceFileAndCertificatePublicKey.wxs"), | ||
212 | "-o", Path.Combine(baseFolder, "test.wixlib") | ||
213 | }); | ||
214 | |||
215 | WixAssert.CompareLineByLine(new[] | ||
216 | { | ||
217 | "The ExePackagePayload/@CertificatePublicKey attribute cannot be specified when attribute SourceFile is present.", | ||
218 | }, result.Messages.Select(m => m.ToString()).ToArray()); | ||
219 | Assert.Equal(35, result.ExitCode); | ||
220 | } | ||
221 | } | ||
222 | |||
223 | [Fact] | ||
224 | public void ErrorWhenSpecifiedSourceFileAndCertificateThumprint() | ||
225 | { | ||
226 | var folder = TestData.Get(@"TestData", "PackagePayload"); | ||
227 | |||
228 | using (var fs = new DisposableFileSystem()) | ||
229 | { | ||
230 | var baseFolder = fs.GetFolder(); | ||
231 | |||
232 | var result = WixRunner.Execute(new[] | ||
233 | { | ||
234 | "build", | ||
235 | Path.Combine(folder, "SpecifiedSourceFileAndCertificateThumbprint.wxs"), | ||
236 | "-o", Path.Combine(baseFolder, "test.wixlib") | ||
237 | }); | ||
238 | |||
239 | WixAssert.CompareLineByLine(new[] | ||
240 | { | ||
241 | "The ExePackagePayload/@CertificateThumbprint attribute cannot be specified when attribute SourceFile is present.", | ||
242 | }, result.Messages.Select(m => m.ToString()).ToArray()); | ||
243 | Assert.Equal(35, result.ExitCode); | ||
244 | } | ||
245 | } | ||
246 | |||
247 | [Fact] | ||
248 | public void ErrorWhenSpecifiedCertificateThumbprintWithoutPublicKey() | ||
249 | { | ||
250 | var folder = TestData.Get(@"TestData", "PackagePayload"); | ||
251 | |||
252 | using (var fs = new DisposableFileSystem()) | ||
253 | { | ||
254 | var baseFolder = fs.GetFolder(); | ||
255 | |||
256 | var result = WixRunner.Execute(new[] | ||
257 | { | ||
258 | "build", | ||
259 | Path.Combine(folder, "SpecifiedHashAndCertificatePublicKey.wxs"), | ||
260 | "-o", Path.Combine(baseFolder, "test.wixlib") | ||
261 | }); | ||
262 | |||
263 | WixAssert.CompareLineByLine(new[] | ||
264 | { | ||
265 | "The ExePackagePayload/@CertificatePublicKey attribute was not found; it is required when attribute CertificateThumbprint is specified.", | ||
266 | }, result.Messages.Select(m => m.ToString()).ToArray()); | ||
267 | Assert.Equal(10, result.ExitCode); | ||
268 | } | ||
269 | } | ||
270 | |||
271 | [Fact] | ||
272 | public void ErrorWhenSpecifiedCertificatePublicKeyWithoutThumbprint() | ||
273 | { | ||
274 | var folder = TestData.Get(@"TestData", "PackagePayload"); | ||
275 | |||
276 | using (var fs = new DisposableFileSystem()) | ||
277 | { | ||
278 | var baseFolder = fs.GetFolder(); | ||
279 | |||
280 | var result = WixRunner.Execute(new[] | ||
281 | { | ||
282 | "build", | ||
283 | Path.Combine(folder, "SpecifiedCertificatePublicKeyWithoutThumbprint.wxs"), | ||
284 | "-o", Path.Combine(baseFolder, "test.wixlib") | ||
285 | }); | ||
286 | |||
287 | WixAssert.CompareLineByLine(new[] | ||
288 | { | ||
289 | "The ExePackagePayload/@CertificateThumbprint attribute was not found; it is required when attribute CertificatePublicKey is specified.", | ||
290 | }, result.Messages.Select(m => m.ToString()).ToArray()); | ||
291 | Assert.Equal(10, result.ExitCode); | ||
292 | } | ||
293 | } | ||
294 | |||
295 | [Fact] | ||
180 | public void ErrorWhenWrongPackagePayloadInPayloadGroup() | 296 | public void ErrorWhenWrongPackagePayloadInPayloadGroup() |
181 | { | 297 | { |
182 | var folder = TestData.Get(@"TestData"); | 298 | var folder = TestData.Get(@"TestData"); |
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/RemotePayloadFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/RemotePayloadFixture.cs new file mode 100644 index 00000000..434b3621 --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/RemotePayloadFixture.cs | |||
@@ -0,0 +1,172 @@ | |||
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 System.Linq; | ||
7 | using WixBuildTools.TestSupport; | ||
8 | using WixToolset.Core.TestPackage; | ||
9 | using Xunit; | ||
10 | |||
11 | public class RemotePayloadFixture | ||
12 | { | ||
13 | [Fact] | ||
14 | public void CanGetRemotePayload() | ||
15 | { | ||
16 | var folder = TestData.Get(@"TestData"); | ||
17 | |||
18 | using (var fs = new DisposableFileSystem()) | ||
19 | { | ||
20 | var outputFolder = fs.GetFolder(); | ||
21 | var outFile = Path.Combine(outputFolder, "out.xml"); | ||
22 | |||
23 | var result = WixRunner.Execute(new[] | ||
24 | { | ||
25 | "burn", "remotepayload", | ||
26 | Path.Combine(folder, ".Data", "burn.exe"), | ||
27 | "-downloadurl", "https://www.example.com/files/{0}", | ||
28 | "-o", outFile | ||
29 | }); | ||
30 | |||
31 | result.AssertSuccess(); | ||
32 | |||
33 | var elements = File.ReadAllLines(outFile); | ||
34 | elements = elements.Select(s => s.Replace("\"", "'")).ToArray(); | ||
35 | |||
36 | WixAssert.CompareLineByLine(new[] | ||
37 | { | ||
38 | @"<ExePackagePayload Name='burn.exe' ProductName='Windows Installer XML Toolset' Description='WiX Toolset Bootstrapper' DownloadUrl='https://www.example.com/files/burn.exe' Hash='F6E722518AC3AB7E31C70099368D5770788C179AA23226110DCF07319B1E1964E246A1E8AE72E2CF23E0138AFC281BAFDE45969204405E114EB20C8195DA7E5E' Size='463360' Version='3.14.1703.0' />", | ||
39 | }, elements); | ||
40 | } | ||
41 | } | ||
42 | |||
43 | [Fact] | ||
44 | public void CanGetRemoteMsuPayload() | ||
45 | { | ||
46 | var folder = TestData.Get(@"TestData"); | ||
47 | |||
48 | using (var fs = new DisposableFileSystem()) | ||
49 | { | ||
50 | var outputFolder = fs.GetFolder(); | ||
51 | var outFile = Path.Combine(outputFolder, "out.xml"); | ||
52 | |||
53 | var result = WixRunner.Execute(new[] | ||
54 | { | ||
55 | "burn", "remotepayload", | ||
56 | Path.Combine(folder, ".Data", "Windows8.1-KB2937592-x86.msu"), | ||
57 | "-o", outFile | ||
58 | }); | ||
59 | |||
60 | result.AssertSuccess(); | ||
61 | |||
62 | var elements = File.ReadAllLines(outFile); | ||
63 | elements = elements.Select(s => s.Replace("\"", "'")).ToArray(); | ||
64 | |||
65 | WixAssert.CompareLineByLine(new[] | ||
66 | { | ||
67 | @"<MsuPackagePayload Name='Windows8.1-KB2937592-x86.msu' Hash='904ADEA6AB675ACE16483138BF3F5850FD56ACB6E3A13AFA7263ED49C68CCE6CF84D6AAD6F99AAF175A95EE1A56C787C5AD968019056490B1073E7DBB7B9B7BE' Size='309544' />", | ||
68 | }, elements); | ||
69 | } | ||
70 | } | ||
71 | |||
72 | [Fact] | ||
73 | public void CanGetRemotePayloadWithCertificate() | ||
74 | { | ||
75 | var folder = TestData.Get(@"TestData"); | ||
76 | |||
77 | using (var fs = new DisposableFileSystem()) | ||
78 | { | ||
79 | var outputFolder = fs.GetFolder(); | ||
80 | var outFile = Path.Combine(outputFolder, "out.xml"); | ||
81 | |||
82 | var result = WixRunner.Execute(new[] | ||
83 | { | ||
84 | "burn", "remotepayload", | ||
85 | "-usecertificate", | ||
86 | Path.Combine(folder, ".Data", "burn.exe"), | ||
87 | Path.Combine(folder, ".Data", "signed_cab1.cab"), | ||
88 | "-o", outFile | ||
89 | }); | ||
90 | |||
91 | result.AssertSuccess(); | ||
92 | |||
93 | var elements = File.ReadAllLines(outFile); | ||
94 | elements = elements.Select(s => s.Replace("\"", "'")).ToArray(); | ||
95 | |||
96 | WixAssert.CompareLineByLine(new[] | ||
97 | { | ||
98 | @"<ExePackagePayload Name='burn.exe' ProductName='Windows Installer XML Toolset' Description='WiX Toolset Bootstrapper' Hash='F6E722518AC3AB7E31C70099368D5770788C179AA23226110DCF07319B1E1964E246A1E8AE72E2CF23E0138AFC281BAFDE45969204405E114EB20C8195DA7E5E' Size='463360' Version='3.14.1703.0' />", | ||
99 | @"<Payload Name='signed_cab1.cab' CertificatePublicKey='BBD1B48A37503767C71F455624967D406A5D66C3' CertificateThumbprint='DE13B4CE635E3F63AA2394E66F95C460267BC82F' Hash='D8D3842403710E1F6036A62543224855CADF546853933C2B17BA99D789D4347B36717687C022678A9D3DE749DFC1482DAAB92B997B62BB32A8A6828B9D04C414' Size='1585' />", | ||
100 | }, elements); | ||
101 | } | ||
102 | } | ||
103 | |||
104 | [Fact] | ||
105 | public void CanGetRemotePayloadWithoutCertificate() | ||
106 | { | ||
107 | var folder = TestData.Get(@"TestData"); | ||
108 | |||
109 | using (var fs = new DisposableFileSystem()) | ||
110 | { | ||
111 | var outputFolder = fs.GetFolder(); | ||
112 | var outFile = Path.Combine(outputFolder, "out.xml"); | ||
113 | |||
114 | var result = WixRunner.Execute(new[] | ||
115 | { | ||
116 | "burn", "remotepayload", | ||
117 | Path.Combine(folder, ".Data", "burn.exe"), | ||
118 | Path.Combine(folder, ".Data", "signed_cab1.cab"), | ||
119 | "-o", outFile | ||
120 | }); | ||
121 | |||
122 | result.AssertSuccess(); | ||
123 | |||
124 | var elements = File.ReadAllLines(outFile); | ||
125 | elements = elements.Select(s => s.Replace("\"", "'")).ToArray(); | ||
126 | |||
127 | WixAssert.CompareLineByLine(new[] | ||
128 | { | ||
129 | @"<ExePackagePayload Name='burn.exe' ProductName='Windows Installer XML Toolset' Description='WiX Toolset Bootstrapper' Hash='F6E722518AC3AB7E31C70099368D5770788C179AA23226110DCF07319B1E1964E246A1E8AE72E2CF23E0138AFC281BAFDE45969204405E114EB20C8195DA7E5E' Size='463360' Version='3.14.1703.0' />", | ||
130 | @"<Payload Name='signed_cab1.cab' Hash='D8D3842403710E1F6036A62543224855CADF546853933C2B17BA99D789D4347B36717687C022678A9D3DE749DFC1482DAAB92B997B62BB32A8A6828B9D04C414' Size='1585' />", | ||
131 | }, elements); | ||
132 | } | ||
133 | } | ||
134 | |||
135 | [Fact] | ||
136 | public void CanGetRemotePayloadsRecursive() | ||
137 | { | ||
138 | var folder = TestData.Get(@"TestData"); | ||
139 | |||
140 | using (var fs = new DisposableFileSystem()) | ||
141 | { | ||
142 | var outputFolder = fs.GetFolder(); | ||
143 | var outFile = Path.Combine(outputFolder, "out.xml"); | ||
144 | |||
145 | var result = WixRunner.Execute(new[] | ||
146 | { | ||
147 | "burn", "remotepayload", | ||
148 | "-recurse", | ||
149 | "-du", "https://www.example.com/files/{0}", | ||
150 | Path.Combine(folder, ".Data", "burn.exe"), | ||
151 | Path.Combine(folder, "RemotePayload", "*"), | ||
152 | "-basepath", folder, | ||
153 | "-bp", Path.Combine(folder, ".Data"), | ||
154 | "-o", outFile | ||
155 | }); | ||
156 | |||
157 | result.AssertSuccess(); | ||
158 | |||
159 | var elements = File.ReadAllLines(outFile); | ||
160 | elements = elements.Select(s => s.Replace("\"", "'")).ToArray(); | ||
161 | |||
162 | WixAssert.CompareLineByLine(new[] | ||
163 | { | ||
164 | @"<ExePackagePayload Name='burn.exe' ProductName='Windows Installer XML Toolset' Description='WiX Toolset Bootstrapper' DownloadUrl='https://www.example.com/files/burn.exe' Hash='F6E722518AC3AB7E31C70099368D5770788C179AA23226110DCF07319B1E1964E246A1E8AE72E2CF23E0138AFC281BAFDE45969204405E114EB20C8195DA7E5E' Size='463360' Version='3.14.1703.0' />", | ||
165 | @"<Payload Name='a.dat' DownloadUrl='https://www.example.com/files/RemotePayload/a.dat' Hash='D13926E5CBE5ED8B46133F9199FAF2FF25B25981C67A31AE2BC3F6C20390FACBFADCD89BD22D3445D95B989C8EACFB1E68DB634BECB5C9624865BA453BCE362A' Size='16' />", | ||
166 | @"<Payload Name='b.dat' DownloadUrl='https://www.example.com/files/RemotePayload/subfolder/b.dat' Hash='5F94707BC29ADFE3B9615E6753388707FD0B8F5FD9EEEC2B17E21E72F1635FF7D7A101E7D14F614E111F263CB9AC4D0940BE1247881A7844F226D6C400293D8E' Size='37' />", | ||
167 | @"<Payload Name='c.dat' DownloadUrl='https://www.example.com/files/RemotePayload/subfolder/c.dat' Hash='97D6209A5571E05E4F72F9C6BF0987651FA03E63F971F9B53C2B3D798A666D9864F232D4E2D6442E47D9D72B282309B6EEFF4EE017B43B706FA92A0F5EF74734' Size='42' />", | ||
168 | }, elements); | ||
169 | } | ||
170 | } | ||
171 | } | ||
172 | } | ||
diff --git a/src/wix/test/WixToolsetTest.Heat/TestData/.Data/Windows8.1-KB2937592-x86.msu b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/.Data/Windows8.1-KB2937592-x86.msu index c39f53b0..c39f53b0 100644 --- a/src/wix/test/WixToolsetTest.Heat/TestData/.Data/Windows8.1-KB2937592-x86.msu +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/.Data/Windows8.1-KB2937592-x86.msu | |||
Binary files differ | |||
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/SpecifiedCertificate.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/SpecifiedCertificate.wxs new file mode 100644 index 00000000..b5dec9a2 --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/SpecifiedCertificate.wxs | |||
@@ -0,0 +1,10 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | ||
2 | <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> | ||
3 | <Fragment> | ||
4 | <PackageGroup Id="BundlePackages"> | ||
5 | <ExePackage Id="SpecifiedSourceFileAndHash" Permanent="yes" DetectCondition="none"> | ||
6 | <ExePackagePayload CertificatePublicKey="abcd" CertificateThumbprint="abcd" Hash="1234" DownloadUrl="https://example.com/" Name="fake.exe" Size="100" /> | ||
7 | </ExePackage> | ||
8 | </PackageGroup> | ||
9 | </Fragment> | ||
10 | </Wix> | ||
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/SpecifiedCertificatePublicKeyWithoutThumbprint.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/SpecifiedCertificatePublicKeyWithoutThumbprint.wxs new file mode 100644 index 00000000..aa915a31 --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/SpecifiedCertificatePublicKeyWithoutThumbprint.wxs | |||
@@ -0,0 +1,10 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | ||
2 | <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> | ||
3 | <Fragment> | ||
4 | <PackageGroup Id="BundlePackages"> | ||
5 | <ExePackage Id="SpecifiedSourceFileAndHash" Permanent="yes" DetectCondition="none"> | ||
6 | <ExePackagePayload CertificatePublicKey="abcd" Hash="123" DownloadUrl="https://example.com/" Name="fake.exe" Size="100" /> | ||
7 | </ExePackage> | ||
8 | </PackageGroup> | ||
9 | </Fragment> | ||
10 | </Wix> | ||
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/SpecifiedHashAndCertificatePublicKey.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/SpecifiedHashAndCertificatePublicKey.wxs new file mode 100644 index 00000000..5570017e --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/SpecifiedHashAndCertificatePublicKey.wxs | |||
@@ -0,0 +1,10 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | ||
2 | <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> | ||
3 | <Fragment> | ||
4 | <PackageGroup Id="BundlePackages"> | ||
5 | <ExePackage Id="SpecifiedSourceFileAndHash" Permanent="yes" DetectCondition="none"> | ||
6 | <ExePackagePayload Hash="abcd" CertificateThumbprint="abcd" DownloadUrl="https://example.com/" Name="fake.exe" Size="100" /> | ||
7 | </ExePackage> | ||
8 | </PackageGroup> | ||
9 | </Fragment> | ||
10 | </Wix> | ||
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/SpecifiedSourceFileAndCertificatePublicKey.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/SpecifiedSourceFileAndCertificatePublicKey.wxs new file mode 100644 index 00000000..b79b5596 --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/SpecifiedSourceFileAndCertificatePublicKey.wxs | |||
@@ -0,0 +1,10 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | ||
2 | <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> | ||
3 | <Fragment> | ||
4 | <PackageGroup Id="BundlePackages"> | ||
5 | <ExePackage Id="SpecifiedSourceFileAndHash" Permanent="yes" DetectCondition="none"> | ||
6 | <ExePackagePayload SourceFile="example.exe" CertificatePublicKey="abcd" /> | ||
7 | </ExePackage> | ||
8 | </PackageGroup> | ||
9 | </Fragment> | ||
10 | </Wix> | ||
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/SpecifiedSourceFileAndCertificateThumbprint.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/SpecifiedSourceFileAndCertificateThumbprint.wxs new file mode 100644 index 00000000..ad2ab521 --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/SpecifiedSourceFileAndCertificateThumbprint.wxs | |||
@@ -0,0 +1,10 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | ||
2 | <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> | ||
3 | <Fragment> | ||
4 | <PackageGroup Id="BundlePackages"> | ||
5 | <ExePackage Id="SpecifiedSourceFileAndHash" Permanent="yes" DetectCondition="none"> | ||
6 | <ExePackagePayload SourceFile="example.exe" CertificateThumbprint="abcd" /> | ||
7 | </ExePackage> | ||
8 | </PackageGroup> | ||
9 | </Fragment> | ||
10 | </Wix> | ||
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/a.dat b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/a.dat new file mode 100644 index 00000000..a96b19b3 --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/a.dat | |||
@@ -0,0 +1 @@ | |||
This is a.dat. | |||
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/subfolder/b.dat b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/subfolder/b.dat new file mode 100644 index 00000000..35c4c043 --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/subfolder/b.dat | |||
@@ -0,0 +1 @@ | |||
This is b.dat. A little bit longer. | |||
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/subfolder/c.dat b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/subfolder/c.dat new file mode 100644 index 00000000..937aa91f --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/subfolder/c.dat | |||
@@ -0,0 +1 @@ | |||
This is c.dat. A little bit longer, now! | |||
diff --git a/src/wix/test/WixToolsetTest.Heat/PayloadTests.cs b/src/wix/test/WixToolsetTest.Heat/PayloadTests.cs deleted file mode 100644 index 8072f50d..00000000 --- a/src/wix/test/WixToolsetTest.Heat/PayloadTests.cs +++ /dev/null | |||
@@ -1,66 +0,0 @@ | |||
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.Harvesters | ||
4 | { | ||
5 | using System; | ||
6 | using System.IO; | ||
7 | using WixBuildTools.TestSupport; | ||
8 | using Xunit; | ||
9 | |||
10 | public class PayloadTests | ||
11 | { | ||
12 | [Fact] | ||
13 | public void CanHarvestExePackagePayload() | ||
14 | { | ||
15 | var folder = TestData.Get(@"TestData"); | ||
16 | |||
17 | using (var fs = new DisposableFileSystem()) | ||
18 | { | ||
19 | var baseFolder = fs.GetFolder(); | ||
20 | var outputFilePath = Path.Combine(baseFolder, "test.wxs"); | ||
21 | |||
22 | var result = HeatRunner.Execute(new[] | ||
23 | { | ||
24 | "exepackagepayload", | ||
25 | Path.Combine(folder, ".Data", "burn.exe"), | ||
26 | "-o", outputFilePath, | ||
27 | }); | ||
28 | |||
29 | result.AssertSuccess(); | ||
30 | |||
31 | Assert.True(File.Exists(outputFilePath)); | ||
32 | |||
33 | var expected = File.ReadAllText(Path.Combine(folder, "Payload", "HarvestedExePackagePayload.wxs")).Replace("\r\n", "\n"); | ||
34 | var actual = File.ReadAllText(outputFilePath).Replace("\r\n", "\n"); | ||
35 | Assert.Equal(expected, actual); | ||
36 | } | ||
37 | } | ||
38 | |||
39 | [Fact] | ||
40 | public void CanHarvestMsuPackagePayload() | ||
41 | { | ||
42 | var folder = TestData.Get(@"TestData"); | ||
43 | |||
44 | using (var fs = new DisposableFileSystem()) | ||
45 | { | ||
46 | var baseFolder = fs.GetFolder(); | ||
47 | var outputFilePath = Path.Combine(baseFolder, "test.wxs"); | ||
48 | |||
49 | var result = HeatRunner.Execute(new[] | ||
50 | { | ||
51 | "msupackagepayload", | ||
52 | Path.Combine(folder, ".Data", "Windows8.1-KB2937592-x86.msu"), | ||
53 | "-o", outputFilePath, | ||
54 | }); | ||
55 | |||
56 | result.AssertSuccess(); | ||
57 | |||
58 | Assert.True(File.Exists(outputFilePath)); | ||
59 | |||
60 | var expected = File.ReadAllText(Path.Combine(folder, "Payload", "HarvestedMsuPackagePayload.wxs")).Replace("\r\n", "\n"); | ||
61 | var actual = File.ReadAllText(outputFilePath).Replace("\r\n", "\n"); | ||
62 | Assert.Equal(expected, actual); | ||
63 | } | ||
64 | } | ||
65 | } | ||
66 | } | ||
diff --git a/src/wix/test/WixToolsetTest.Heat/TestData/.Data/burn.exe b/src/wix/test/WixToolsetTest.Heat/TestData/.Data/burn.exe deleted file mode 100644 index 2a4f423f..00000000 --- a/src/wix/test/WixToolsetTest.Heat/TestData/.Data/burn.exe +++ /dev/null | |||
Binary files differ | |||
diff --git a/src/wix/test/WixToolsetTest.Heat/TestData/Payload/HarvestedExePackagePayload.wxs b/src/wix/test/WixToolsetTest.Heat/TestData/Payload/HarvestedExePackagePayload.wxs deleted file mode 100644 index 40100f22..00000000 --- a/src/wix/test/WixToolsetTest.Heat/TestData/Payload/HarvestedExePackagePayload.wxs +++ /dev/null | |||
@@ -1,6 +0,0 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | ||
2 | <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> | ||
3 | <Fragment> | ||
4 | <ExePackagePayload Description="WiX Toolset Bootstrapper" Hash="F6E722518AC3AB7E31C70099368D5770788C179AA23226110DCF07319B1E1964E246A1E8AE72E2CF23E0138AFC281BAFDE45969204405E114EB20C8195DA7E5E" ProductName="Windows Installer XML Toolset" Size="463360" Version="3.14.1703.0" /> | ||
5 | </Fragment> | ||
6 | </Wix> \ No newline at end of file | ||
diff --git a/src/wix/test/WixToolsetTest.Heat/TestData/Payload/HarvestedMsuPackagePayload.wxs b/src/wix/test/WixToolsetTest.Heat/TestData/Payload/HarvestedMsuPackagePayload.wxs deleted file mode 100644 index f203fe27..00000000 --- a/src/wix/test/WixToolsetTest.Heat/TestData/Payload/HarvestedMsuPackagePayload.wxs +++ /dev/null | |||
@@ -1,6 +0,0 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | ||
2 | <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> | ||
3 | <Fragment> | ||
4 | <MsuPackagePayload Hash="904ADEA6AB675ACE16483138BF3F5850FD56ACB6E3A13AFA7263ED49C68CCE6CF84D6AAD6F99AAF175A95EE1A56C787C5AD968019056490B1073E7DBB7B9B7BE" Size="309544" /> | ||
5 | </Fragment> | ||
6 | </Wix> \ No newline at end of file | ||
diff --git a/src/wix/test/WixToolsetTest.Heat/WixToolsetTest.Heat.csproj b/src/wix/test/WixToolsetTest.Heat/WixToolsetTest.Heat.csproj index 2a706aa0..1ba62393 100644 --- a/src/wix/test/WixToolsetTest.Heat/WixToolsetTest.Heat.csproj +++ b/src/wix/test/WixToolsetTest.Heat/WixToolsetTest.Heat.csproj | |||
@@ -9,10 +9,6 @@ | |||
9 | </PropertyGroup> | 9 | </PropertyGroup> |
10 | 10 | ||
11 | <ItemGroup> | 11 | <ItemGroup> |
12 | <Content Include="TestData\**" CopyToOutputDirectory="PreserveNewest" /> | ||
13 | </ItemGroup> | ||
14 | |||
15 | <ItemGroup> | ||
16 | <ProjectReference Include="..\..\heat\heat.csproj" /> | 12 | <ProjectReference Include="..\..\heat\heat.csproj" /> |
17 | <ProjectReference Include="..\..\WixToolset.Core.TestPackage\WixToolset.Core.TestPackage.csproj" /> | 13 | <ProjectReference Include="..\..\WixToolset.Core.TestPackage\WixToolset.Core.TestPackage.csproj" /> |
18 | </ItemGroup> | 14 | </ItemGroup> |
diff --git a/src/wix/wixnative/certhashes.cpp b/src/wix/wixnative/certhashes.cpp new file mode 100644 index 00000000..cbb548ba --- /dev/null +++ b/src/wix/wixnative/certhashes.cpp | |||
@@ -0,0 +1,138 @@ | |||
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 | #include "precomp.h" | ||
4 | |||
5 | #define SHA1_HASH_LEN 20 | ||
6 | |||
7 | static HRESULT GetPublicKeyIdentifierAndThumbprint( | ||
8 | __in_z LPCWSTR wzPath, | ||
9 | __inout_z LPWSTR* psczPublicKeyIdentifier, | ||
10 | __inout_z LPWSTR* psczThumbprint); | ||
11 | |||
12 | static HRESULT GetChainContext( | ||
13 | __in_z LPCWSTR wzPath, | ||
14 | __out PCCERT_CHAIN_CONTEXT* ppChainContext); | ||
15 | |||
16 | |||
17 | HRESULT CertificateHashesCommand( | ||
18 | __in int argc, | ||
19 | __in_ecount(argc) LPWSTR argv[]) | ||
20 | { | ||
21 | Unused(argc); | ||
22 | Unused(argv); | ||
23 | |||
24 | HRESULT hr = S_OK; | ||
25 | |||
26 | LPWSTR sczFilePath = NULL; | ||
27 | LPWSTR sczPublicKeyIdentifier = NULL; | ||
28 | LPWSTR sczThumbprint = NULL; | ||
29 | |||
30 | hr = WixNativeReadStdinPreamble(); | ||
31 | ExitOnFailure(hr, "Failed to read stdin preamble before reading paths to get certificate hashes"); | ||
32 | |||
33 | // Get the hash for each provided file. | ||
34 | for (;;) | ||
35 | { | ||
36 | hr = ConsoleReadW(&sczFilePath); | ||
37 | ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Failed to read file path to signed file from stdin"); | ||
38 | |||
39 | if (!*sczFilePath) | ||
40 | { | ||
41 | break; | ||
42 | } | ||
43 | |||
44 | hr = GetPublicKeyIdentifierAndThumbprint(sczFilePath, &sczPublicKeyIdentifier, &sczThumbprint); | ||
45 | if (FAILED(hr)) | ||
46 | { | ||
47 | // Treat no signature as success without finding certificate hashes. | ||
48 | ConsoleWriteLine(CONSOLE_COLOR_NORMAL, "%ls\t\t\t0x%x", sczFilePath, TRUST_E_NOSIGNATURE == hr ? 0 : hr); | ||
49 | } | ||
50 | else | ||
51 | { | ||
52 | ConsoleWriteLine(CONSOLE_COLOR_NORMAL, "%ls\t%ls\t%ls\t0x%x", sczFilePath, sczPublicKeyIdentifier, sczThumbprint, hr); | ||
53 | } | ||
54 | } | ||
55 | |||
56 | LExit: | ||
57 | ReleaseStr(sczThumbprint); | ||
58 | ReleaseStr(sczPublicKeyIdentifier); | ||
59 | ReleaseStr(sczFilePath); | ||
60 | |||
61 | return hr; | ||
62 | } | ||
63 | |||
64 | static HRESULT GetPublicKeyIdentifierAndThumbprint( | ||
65 | __in_z LPCWSTR wzPath, | ||
66 | __inout_z LPWSTR* psczPublicKeyIdentifier, | ||
67 | __inout_z LPWSTR* psczThumbprint) | ||
68 | { | ||
69 | HRESULT hr = S_OK; | ||
70 | PCCERT_CHAIN_CONTEXT pChainContext = NULL; | ||
71 | PCCERT_CONTEXT pCertContext = NULL; | ||
72 | BYTE rgbPublicKeyIdentifier[SHA1_HASH_LEN] = { }; | ||
73 | DWORD cbPublicKeyIdentifier = sizeof(rgbPublicKeyIdentifier); | ||
74 | BYTE* pbThumbprint = NULL; | ||
75 | DWORD cbThumbprint = 0; | ||
76 | |||
77 | hr = GetChainContext(wzPath, &pChainContext); | ||
78 | ExitOnFailure(hr, "Failed to get chain context for file: %ls", wzPath); | ||
79 | |||
80 | pCertContext = pChainContext->rgpChain[0]->rgpElement[0]->pCertContext; | ||
81 | |||
82 | // Get the certificate's public key identifier and thumbprint. | ||
83 | if (!::CryptHashPublicKeyInfo(NULL, CALG_SHA1, 0, X509_ASN_ENCODING, &pCertContext->pCertInfo->SubjectPublicKeyInfo, rgbPublicKeyIdentifier, &cbPublicKeyIdentifier)) | ||
84 | { | ||
85 | ExitWithLastError(hr, "Failed to get certificate public key identifier from file: %ls", wzPath); | ||
86 | } | ||
87 | |||
88 | hr = CertReadProperty(pCertContext, CERT_SHA1_HASH_PROP_ID, &pbThumbprint, &cbThumbprint); | ||
89 | ExitOnFailure(hr, "Failed to read certificate thumbprint from file: %ls", wzPath); | ||
90 | |||
91 | // Get the public key indentifier and thumbprint in hex. | ||
92 | hr = StrAllocHexEncode(rgbPublicKeyIdentifier, cbPublicKeyIdentifier, psczPublicKeyIdentifier); | ||
93 | ExitOnFailure(hr, "Failed to convert certificate public key to hex for file: %ls", wzPath); | ||
94 | |||
95 | hr = StrAllocHexEncode(pbThumbprint, cbThumbprint, psczThumbprint); | ||
96 | ExitOnFailure(hr, "Failed to convert certificate thumbprint to hex for file: %ls", wzPath); | ||
97 | |||
98 | LExit: | ||
99 | ReleaseMem(pbThumbprint); | ||
100 | return hr; | ||
101 | } | ||
102 | |||
103 | static HRESULT GetChainContext( | ||
104 | __in_z LPCWSTR wzPath, | ||
105 | __out PCCERT_CHAIN_CONTEXT* ppChainContext) | ||
106 | { | ||
107 | HRESULT hr = S_OK; | ||
108 | |||
109 | GUID guidAuthenticode = WINTRUST_ACTION_GENERIC_VERIFY_V2; | ||
110 | WINTRUST_FILE_INFO wfi = { }; | ||
111 | WINTRUST_DATA wtd = { }; | ||
112 | CRYPT_PROVIDER_DATA* pProviderData = NULL; | ||
113 | CRYPT_PROVIDER_SGNR* pSigner = NULL; | ||
114 | |||
115 | wfi.cbStruct = sizeof(wfi); | ||
116 | wfi.pcwszFilePath = wzPath; | ||
117 | |||
118 | wtd.cbStruct = sizeof(wtd); | ||
119 | wtd.dwUnionChoice = WTD_CHOICE_FILE; | ||
120 | wtd.pFile = &wfi; | ||
121 | wtd.dwStateAction = WTD_STATEACTION_VERIFY; | ||
122 | wtd.dwProvFlags = WTD_REVOCATION_CHECK_NONE | WTD_HASH_ONLY_FLAG | WTD_CACHE_ONLY_URL_RETRIEVAL; | ||
123 | wtd.dwUIChoice = WTD_UI_NONE; | ||
124 | |||
125 | hr = ::WinVerifyTrust(static_cast<HWND>(INVALID_HANDLE_VALUE), &guidAuthenticode, &wtd); | ||
126 | ExitOnFailure(hr, "Failed to verify certificate on file: %ls", wzPath); | ||
127 | |||
128 | pProviderData = ::WTHelperProvDataFromStateData(wtd.hWVTStateData); | ||
129 | ExitOnNullWithLastError(pProviderData, hr, "Failed to get provider state from authenticode certificate on file: %ls", wzPath); | ||
130 | |||
131 | pSigner = ::WTHelperGetProvSignerFromChain(pProviderData, 0, FALSE, 0); | ||
132 | ExitOnNullWithLastError(pSigner, hr, "Failed to get signer chain from authenticode certificate on file: %ls", wzPath); | ||
133 | |||
134 | *ppChainContext = pSigner->pChainContext; | ||
135 | |||
136 | LExit: | ||
137 | return hr; | ||
138 | } | ||
diff --git a/src/wix/wixnative/precomp.h b/src/wix/wixnative/precomp.h index 490bbdcf..0a458ca6 100644 --- a/src/wix/wixnative/precomp.h +++ b/src/wix/wixnative/precomp.h | |||
@@ -4,9 +4,12 @@ | |||
4 | #include <windows.h> | 4 | #include <windows.h> |
5 | #include <aclapi.h> | 5 | #include <aclapi.h> |
6 | #include <mergemod.h> | 6 | #include <mergemod.h> |
7 | #include <softpub.h> | ||
7 | #include <strsafe.h> | 8 | #include <strsafe.h> |
9 | #include <wintrust.h> | ||
8 | 10 | ||
9 | #include "dutil.h" | 11 | #include "dutil.h" |
12 | #include "certutil.h" | ||
10 | #include "conutil.h" | 13 | #include "conutil.h" |
11 | #include "memutil.h" | 14 | #include "memutil.h" |
12 | #include "pathutil.h" | 15 | #include "pathutil.h" |
@@ -15,6 +18,7 @@ | |||
15 | #include "cabutil.h" | 18 | #include "cabutil.h" |
16 | 19 | ||
17 | HRESULT WixNativeReadStdinPreamble(); | 20 | HRESULT WixNativeReadStdinPreamble(); |
21 | HRESULT CertificateHashesCommand(__in int argc, __in_ecount(argc) LPWSTR argv[]); | ||
18 | HRESULT SmartCabCommand(__in int argc, __in_ecount(argc) LPWSTR argv[]); | 22 | HRESULT SmartCabCommand(__in int argc, __in_ecount(argc) LPWSTR argv[]); |
19 | HRESULT ResetAclsCommand(__in int argc, __in_ecount(argc) LPWSTR argv[]); | 23 | HRESULT ResetAclsCommand(__in int argc, __in_ecount(argc) LPWSTR argv[]); |
20 | HRESULT EnumCabCommand(__in int argc, __in_ecount(argc) LPWSTR argv[]); | 24 | HRESULT EnumCabCommand(__in int argc, __in_ecount(argc) LPWSTR argv[]); |
diff --git a/src/wix/wixnative/wixnative.cpp b/src/wix/wixnative/wixnative.cpp index d1236da1..8a24d5f1 100644 --- a/src/wix/wixnative/wixnative.cpp +++ b/src/wix/wixnative/wixnative.cpp | |||
@@ -24,6 +24,10 @@ int __cdecl wmain(int argc, LPWSTR argv[]) | |||
24 | { | 24 | { |
25 | hr = EnumCabCommand(argc - 2, argv + 2); | 25 | hr = EnumCabCommand(argc - 2, argv + 2); |
26 | } | 26 | } |
27 | else if (CSTR_EQUAL == ::CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, argv[1], -1, L"certhashes", -1)) | ||
28 | { | ||
29 | hr = CertificateHashesCommand(argc - 2, argv + 2); | ||
30 | } | ||
27 | else if (CSTR_EQUAL == ::CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, argv[1], -1, L"resetacls", -1)) | 31 | else if (CSTR_EQUAL == ::CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, argv[1], -1, L"resetacls", -1)) |
28 | { | 32 | { |
29 | hr = ResetAclsCommand(argc - 2, argv + 2); | 33 | hr = ResetAclsCommand(argc - 2, argv + 2); |
diff --git a/src/wix/wixnative/wixnative.vcxproj b/src/wix/wixnative/wixnative.vcxproj index 1dac36e2..2e90661c 100644 --- a/src/wix/wixnative/wixnative.vcxproj +++ b/src/wix/wixnative/wixnative.vcxproj | |||
@@ -42,7 +42,7 @@ | |||
42 | <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> | 42 | <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> |
43 | 43 | ||
44 | <PropertyGroup> | 44 | <PropertyGroup> |
45 | <ProjectAdditionalLinkLibraries>crypt32.lib;cabinet.lib;msi.lib</ProjectAdditionalLinkLibraries> | 45 | <ProjectAdditionalLinkLibraries>crypt32.lib;cabinet.lib;msi.lib;wintrust.lib</ProjectAdditionalLinkLibraries> |
46 | </PropertyGroup> | 46 | </PropertyGroup> |
47 | 47 | ||
48 | <ItemGroup> | 48 | <ItemGroup> |
@@ -50,6 +50,7 @@ | |||
50 | <ClCompile Include="precomp.cpp"> | 50 | <ClCompile Include="precomp.cpp"> |
51 | <PrecompiledHeader>Create</PrecompiledHeader> | 51 | <PrecompiledHeader>Create</PrecompiledHeader> |
52 | </ClCompile> | 52 | </ClCompile> |
53 | <ClCompile Include="certhashes.cpp" /> | ||
53 | <ClCompile Include="enumcab.cpp" /> | 54 | <ClCompile Include="enumcab.cpp" /> |
54 | <ClCompile Include="extractcab.cpp" /> | 55 | <ClCompile Include="extractcab.cpp" /> |
55 | <ClCompile Include="resetacls.cpp" /> | 56 | <ClCompile Include="resetacls.cpp" /> |