main
cs 354 lines 14.8 KB
Raw
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
4 {
5 using System;
6 using System.IO;
7 using System.Xml.Linq;
8 using WixToolset.Data;
9 using WixToolset.Data.Burn;
10 using WixToolset.Data.Symbols;
11
12 internal class CompilerPayload
13 {
14 public CompilerPayload(CompilerCore core, SourceLineNumber sourceLineNumbers, XElement element)
15 {
16 this.Core = core;
17 this.Element = element;
18 this.SourceLineNumbers = sourceLineNumbers;
19 }
20
21 public CompilerCore Core { get; }
22
23 public XElement Element { get; }
24
25 public SourceLineNumber SourceLineNumbers { get; }
26
27 public YesNoDefaultType Compressed { get; set; } = YesNoDefaultType.Default;
28
29 public string Description { get; set; }
30
31 public string DownloadUrl { get; set; }
32
33 public string CertificatePublicKey { get; set; }
34
35 public string CertificateThumbprint { get; set; }
36
37 public string Hash { get; set; }
38
39 public Identifier Id { get; set; }
40
41 public bool IsRemoteAllowed { get; set; }
42
43 public bool IsRequired { get; set; } = true;
44
45 public string Name { get; set; }
46
47 public string ProductName { get; set; }
48
49 public long? Size { get; set; }
50
51 public string SourceFile { get; set; }
52
53 public string Version { get; set; }
54
55 private void CalculateAndVerifyFields()
56 {
57 var isRemote = this.IsRemoteAllowed && (!String.IsNullOrEmpty(this.CertificatePublicKey) || !String.IsNullOrEmpty(this.CertificateThumbprint) || !String.IsNullOrEmpty(this.Hash));
58
59 if (String.IsNullOrEmpty(this.SourceFile))
60 {
61 if (!String.IsNullOrEmpty(this.Name) && !isRemote)
62 {
63 this.SourceFile = Path.Combine("SourceDir", this.Name);
64 }
65 }
66 else if (this.SourceFile.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal))
67 {
68 if (String.IsNullOrEmpty(this.Name))
69 {
70 this.Core.Write(ErrorMessages.ExpectedAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, "Name", "SourceFile", this.SourceFile));
71 }
72 else
73 {
74 this.SourceFile = Path.Combine(this.SourceFile, Path.GetFileName(this.Name));
75 }
76 }
77
78 if (String.IsNullOrEmpty(this.SourceFile) && !isRemote)
79 {
80 if (this.IsRequired)
81 {
82 if (!this.IsRemoteAllowed)
83 {
84 this.Core.Write(ErrorMessages.ExpectedAttributes(this.SourceLineNumbers, this.Element.Name.LocalName, "Name", "SourceFile"));
85 }
86 else
87 {
88 this.Core.Write(ErrorMessages.ExpectedAttributes(this.SourceLineNumbers, this.Element.Name.LocalName, "SourceFile", "CertificatePublicKey", "Hash"));
89 }
90 }
91 }
92 else if (this.IsRemoteAllowed)
93 {
94 var isLocal = !String.IsNullOrEmpty(this.SourceFile);
95
96 if (isLocal)
97 {
98 if (isRemote)
99 {
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 }
114 }
115
116 if (!String.IsNullOrEmpty(this.Description))
117 {
118 this.Core.Write(ErrorMessages.IllegalAttributeWithoutOtherAttributes(this.SourceLineNumbers, this.Element.Name.LocalName, "Description", "Hash", "CertificatePublicKey"));
119 }
120
121 if (!String.IsNullOrEmpty(this.ProductName))
122 {
123 this.Core.Write(ErrorMessages.IllegalAttributeWithoutOtherAttributes(this.SourceLineNumbers, this.Element.Name.LocalName, "ProductName", "Hash", "CertificatePublicKey"));
124 }
125
126 if (this.Size.HasValue)
127 {
128 this.Core.Write(ErrorMessages.IllegalAttributeWithoutOtherAttributes(this.SourceLineNumbers, this.Element.Name.LocalName, "Size", "Hash", "CertificatePublicKey"));
129 }
130
131 if (!String.IsNullOrEmpty(this.Version))
132 {
133 this.Core.Write(ErrorMessages.IllegalAttributeWithoutOtherAttributes(this.SourceLineNumbers, this.Element.Name.LocalName, "Version", "Hash", "CertificatePublicKey"));
134 }
135 }
136 else
137 {
138 if (String.IsNullOrEmpty(this.DownloadUrl))
139 {
140 this.Core.Write(ErrorMessages.ExpectedAttributeWithoutOtherAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, "DownloadUrl", "SourceFile"));
141 }
142
143 if (String.IsNullOrEmpty(this.Name))
144 {
145 this.Core.Write(ErrorMessages.ExpectedAttributeWithoutOtherAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, "Name", "SourceFile"));
146 }
147
148 // If remote payload is being verified by a certificate.
149 if (!String.IsNullOrEmpty(this.CertificatePublicKey) || !String.IsNullOrEmpty(this.CertificateThumbprint))
150 {
151 var oneOfCertificateAttributeNames = !String.IsNullOrEmpty(this.CertificatePublicKey) ? "CertificatePublicKey" : "CertificateThumbprint";
152
153 if (String.IsNullOrEmpty(this.CertificateThumbprint))
154 {
155 this.Core.Write(ErrorMessages.ExpectedAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, "CertificateThumbprint", "CertificatePublicKey"));
156 }
157 else if (String.IsNullOrEmpty(this.CertificatePublicKey))
158 {
159 this.Core.Write(ErrorMessages.ExpectedAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, "CertificatePublicKey", "CertificateThumbprint"));
160 }
161
162 if (!String.IsNullOrEmpty(this.Hash))
163 {
164 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, "Hash", oneOfCertificateAttributeNames));
165 }
166 }
167 else // payload is being verified by hash.
168 {
169 if (String.IsNullOrEmpty(this.Hash))
170 {
171 this.Core.Write(ErrorMessages.ExpectedAttributeWithoutOtherAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, "Hash", "SourceFile"));
172 }
173
174 if (!this.Size.HasValue)
175 {
176 this.Core.Write(ErrorMessages.ExpectedAttributeWithoutOtherAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, "Size", "SourceFile"));
177 }
178 }
179
180 if (YesNoDefaultType.Yes == this.Compressed)
181 {
182 this.Core.Write(WarningMessages.RemotePayloadsMustNotAlsoBeCompressed(this.SourceLineNumbers, this.Element.Name.LocalName));
183 }
184
185 this.Compressed = YesNoDefaultType.No;
186 }
187 }
188 }
189
190 public WixBundlePayloadSymbol CreatePayloadSymbol(ComplexReferenceParentType parentType, string parentId)
191 {
192 WixBundlePayloadSymbol symbol = null;
193
194 if (parentType == ComplexReferenceParentType.Container && parentId == BurnConstants.BurnUXContainerName)
195 {
196 if (this.Compressed == YesNoDefaultType.No)
197 {
198 this.Core.Write(WarningMessages.UxPayloadsOnlySupportEmbedding(this.SourceLineNumbers, this.SourceFile));
199 }
200
201 if (!String.IsNullOrEmpty(this.DownloadUrl))
202 {
203 this.Core.Write(WarningMessages.DownloadUrlNotSupportedForBAPayloads(this.SourceLineNumbers, this.Id.Id));
204 }
205
206 this.Compressed = YesNoDefaultType.Yes;
207 this.DownloadUrl = null;
208 }
209
210 if (String.IsNullOrEmpty(this.Name) && String.IsNullOrEmpty(this.SourceFile))
211 {
212 this.Core.Write(ErrorMessages.ExpectedAttributes(this.SourceLineNumbers, this.Element.Name.LocalName, "Name", "SourceFile"));
213 }
214
215 if (!this.Core.EncounteredError)
216 {
217 symbol = this.Core.AddSymbol(new WixBundlePayloadSymbol(this.SourceLineNumbers, this.Id)
218 {
219 Name = String.IsNullOrEmpty(this.Name) ? Path.GetFileName(this.SourceFile) : this.Name,
220 SourceFile = new IntermediateFieldPathValue { Path = this.SourceFile },
221 DownloadUrl = this.DownloadUrl,
222 Compressed = (this.Compressed == YesNoDefaultType.Yes) ? true : (this.Compressed == YesNoDefaultType.No) ? (bool?)false : null,
223 UnresolvedSourceFile = this.SourceFile, // duplicate of sourceFile but in a string column so it won't get resolved to a full path during binding.
224 DisplayName = this.ProductName,
225 Description = this.Description,
226 Hash = this.Hash,
227 FileSize = this.Size,
228 Version = this.Version,
229 CertificatePublicKey = this.CertificatePublicKey,
230 CertificateThumbprint = this.CertificateThumbprint
231 });
232
233 this.Core.CreateGroupAndOrderingRows(this.SourceLineNumbers, parentType, parentId, ComplexReferenceChildType.Payload, symbol.Id.Id, ComplexReferenceChildType.Unknown, null);
234 }
235
236 return symbol;
237 }
238
239 public void FinishCompilingPackage()
240 {
241 this.CalculateAndVerifyFields();
242 this.GenerateIdFromFilename();
243
244 if (this.Id == null)
245 {
246 this.Core.Write(ErrorMessages.ExpectedAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, "Id"));
247 this.Id = Identifier.Invalid;
248 }
249 }
250
251 public void FinishCompilingPackagePayload()
252 {
253 this.CalculateAndVerifyFields();
254 this.GenerateIdFromFilename();
255 }
256
257 public void FinishCompilingPayload(string parentId)
258 {
259 this.CalculateAndVerifyFields();
260 this.GenerateIdFromPrefix("pay", parentId);
261 }
262
263 private void GenerateIdFromFilename()
264 {
265 if (this.Id == null)
266 {
267 if (!String.IsNullOrEmpty(this.Name))
268 {
269 this.Id = this.Core.CreateIdentifierFromFilename(Path.GetFileName(this.Name));
270 }
271 else if (!String.IsNullOrEmpty(this.SourceFile))
272 {
273 this.Id = this.Core.CreateIdentifierFromFilename(Path.GetFileName(this.SourceFile));
274 }
275 else // if Name and SourceFile were not specified an error was already reported.
276 {
277 this.Id = Identifier.Invalid;
278 }
279 }
280 }
281
282 private void GenerateIdFromPrefix(string prefix, string parentId)
283 {
284 if (this.Id == null)
285 {
286 this.Id = this.Core.CreateIdentifier(prefix, parentId, this.SourceFile?.ToUpperInvariant() ?? this.Name?.ToUpperInvariant());
287 }
288 }
289
290 public void ParseCompressed(XAttribute attrib)
291 {
292 this.Compressed = this.Core.GetAttributeYesNoDefaultValue(this.SourceLineNumbers, attrib);
293 }
294
295 public void ParseDescription(XAttribute attrib)
296 {
297 this.Description = this.Core.GetAttributeValue(this.SourceLineNumbers, attrib);
298 }
299
300 public void ParseDownloadUrl(XAttribute attrib)
301 {
302 this.DownloadUrl = this.Core.GetAttributeValue(this.SourceLineNumbers, attrib);
303 }
304
305 public void ParseCertificatePublicKey(XAttribute attrib)
306 {
307 this.CertificatePublicKey = this.Core.GetAttributeValue(this.SourceLineNumbers, attrib);
308 }
309
310 public void ParseCertificateThumbprint(XAttribute attrib)
311 {
312 this.CertificateThumbprint = this.Core.GetAttributeValue(this.SourceLineNumbers, attrib);
313 }
314
315 public void ParseHash(XAttribute attrib)
316 {
317 this.Hash = this.Core.GetAttributeValue(this.SourceLineNumbers, attrib);
318 }
319
320 public void ParseId(XAttribute attrib)
321 {
322 this.Id = this.Core.GetAttributeIdentifier(this.SourceLineNumbers, attrib);
323 }
324
325 public void ParseName(XAttribute attrib)
326 {
327 this.Name = this.Core.GetAttributeLongFilename(this.SourceLineNumbers, attrib, false, true);
328 if (!this.Core.IsValidLongFilename(this.Name, false, true))
329 {
330 this.Core.Write(ErrorMessages.IllegalLongFilename(this.SourceLineNumbers, this.Element.Name.LocalName, "Name", this.Name));
331 }
332 }
333
334 public void ParseProductName(XAttribute attrib)
335 {
336 this.ProductName = this.Core.GetAttributeValue(this.SourceLineNumbers, attrib);
337 }
338
339 public void ParseSize(XAttribute attrib)
340 {
341 this.Size = this.Core.GetAttributeLongValue(this.SourceLineNumbers, attrib, 1, Int64.MaxValue);
342 }
343
344 public void ParseSourceFile(XAttribute attrib)
345 {
346 this.SourceFile = this.Core.GetAttributeValue(this.SourceLineNumbers, attrib);
347 }
348
349 public void ParseVersion(XAttribute attrib)
350 {
351 this.Version = this.Core.GetAttributeValue(this.SourceLineNumbers, attrib);
352 }
353 }
354 }