aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core/Compiler_Bundle.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixToolset.Core/Compiler_Bundle.cs')
-rw-r--r--src/WixToolset.Core/Compiler_Bundle.cs2727
1 files changed, 2727 insertions, 0 deletions
diff --git a/src/WixToolset.Core/Compiler_Bundle.cs b/src/WixToolset.Core/Compiler_Bundle.cs
new file mode 100644
index 00000000..21028b6f
--- /dev/null
+++ b/src/WixToolset.Core/Compiler_Bundle.cs
@@ -0,0 +1,2727 @@
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
3namespace WixToolset.Core
4{
5 using System;
6 using System.Collections.Generic;
7 using System.Diagnostics;
8 using System.Globalization;
9 using System.IO;
10 using System.Xml.Linq;
11 using WixToolset.Data;
12 using WixToolset.Data.Tuples;
13 using WixToolset.Extensibility;
14
15 /// <summary>
16 /// Compiler of the WiX toolset.
17 /// </summary>
18 internal partial class Compiler : ICompiler
19 {
20 public const string BurnUXContainerId = "WixUXContainer";
21 public const string BurnDefaultAttachedContainerId = "WixAttachedContainer";
22
23 // The following constants must stay in sync with src\burn\engine\core.h
24 private const string BURN_BUNDLE_NAME = "WixBundleName";
25 private const string BURN_BUNDLE_ORIGINAL_SOURCE = "WixBundleOriginalSource";
26 private const string BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER = "WixBundleOriginalSourceFolder";
27 private const string BURN_BUNDLE_LAST_USED_SOURCE = "WixBundleLastUsedSource";
28
29 /// <summary>
30 /// Parses an ApprovedExeForElevation element.
31 /// </summary>
32 /// <param name="node">Element to parse</param>
33 private void ParseApprovedExeForElevation(XElement node)
34 {
35 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
36 Identifier id = null;
37 string key = null;
38 string valueName = null;
39 var win64 = YesNoType.NotSet;
40
41 foreach (var attrib in node.Attributes())
42 {
43 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
44 {
45 switch (attrib.Name.LocalName)
46 {
47 case "Id":
48 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
49 break;
50 case "Key":
51 key = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
52 break;
53 case "Value":
54 valueName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
55 break;
56 case "Win64":
57 win64 = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
58 break;
59 default:
60 this.Core.UnexpectedAttribute(node, attrib);
61 break;
62 }
63 }
64 else
65 {
66 this.Core.ParseExtensionAttribute(node, attrib);
67 }
68 }
69
70 if (null == id)
71 {
72 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
73 }
74
75 if (null == key)
76 {
77 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key"));
78 }
79
80 var attributes = BundleApprovedExeForElevationAttributes.None;
81
82 if (win64 == YesNoType.Yes)
83 {
84 attributes |= BundleApprovedExeForElevationAttributes.Win64;
85 }
86
87 this.Core.ParseForExtensionElements(node);
88
89 if (!this.Core.EncounteredError)
90 {
91 var wixApprovedExeForElevationRow = (WixApprovedExeForElevationTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixApprovedExeForElevation, id);
92 wixApprovedExeForElevationRow.Key = key;
93 wixApprovedExeForElevationRow.Value = valueName;
94 wixApprovedExeForElevationRow.Attributes = (int)attributes;
95 }
96 }
97
98 /// <summary>
99 /// Parses a Bundle element.
100 /// </summary>
101 /// <param name="node">Element to parse</param>
102 private void ParseBundleElement(XElement node)
103 {
104 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
105 string copyright = null;
106 string aboutUrl = null;
107 var compressed = YesNoDefaultType.Default;
108 var disableModify = -1;
109 var disableRemove = YesNoType.NotSet;
110 string helpTelephone = null;
111 string helpUrl = null;
112 string manufacturer = null;
113 string name = null;
114 string tag = null;
115 string updateUrl = null;
116 string upgradeCode = null;
117 string version = null;
118 string condition = null;
119 string parentName = null;
120
121 string fileSystemSafeBundleName = null;
122 string logVariablePrefixAndExtension = null;
123 string iconSourceFile = null;
124 string splashScreenSourceFile = null;
125
126 // Process only standard attributes until the active section is initialized.
127 foreach (var attrib in node.Attributes())
128 {
129 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
130 {
131 switch (attrib.Name.LocalName)
132 {
133 case "AboutUrl":
134 aboutUrl = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
135 break;
136 case "Compressed":
137 compressed = this.Core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib);
138 break;
139 case "Condition":
140 condition = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
141 break;
142 case "Copyright":
143 copyright = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
144 break;
145 case "DisableModify":
146 var value = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
147 switch (value)
148 {
149 case "button":
150 disableModify = 2;
151 break;
152 case "yes":
153 disableModify = 1;
154 break;
155 case "no":
156 disableModify = 0;
157 break;
158 default:
159 this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, value, "button", "yes", "no"));
160 break;
161 }
162 break;
163 case "DisableRemove":
164 disableRemove = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
165 break;
166 case "DisableRepair":
167 this.Core.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName));
168 break;
169 case "HelpTelephone":
170 helpTelephone = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
171 break;
172 case "HelpUrl":
173 helpUrl = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
174 break;
175 case "Manufacturer":
176 manufacturer = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
177 break;
178 case "IconSourceFile":
179 iconSourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
180 break;
181 case "Name":
182 name = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
183 break;
184 case "ParentName":
185 parentName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
186 break;
187 case "SplashScreenSourceFile":
188 splashScreenSourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
189 break;
190 case "Tag":
191 tag = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
192 break;
193 case "UpdateUrl":
194 updateUrl = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
195 break;
196 case "UpgradeCode":
197 upgradeCode = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
198 break;
199 case "Version":
200 version = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib);
201 break;
202 default:
203 this.Core.UnexpectedAttribute(node, attrib);
204 break;
205 }
206 }
207 }
208
209 if (String.IsNullOrEmpty(version))
210 {
211 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version"));
212 }
213 else if (!CompilerCore.IsValidModuleOrBundleVersion(version))
214 {
215 this.Core.Write(WarningMessages.InvalidModuleOrBundleVersion(sourceLineNumbers, "Bundle", version));
216 }
217
218 if (String.IsNullOrEmpty(upgradeCode))
219 {
220 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "UpgradeCode"));
221 }
222
223 if (String.IsNullOrEmpty(copyright))
224 {
225 if (String.IsNullOrEmpty(manufacturer))
226 {
227 copyright = "Copyright (c). All rights reserved.";
228 }
229 else
230 {
231 copyright = String.Format("Copyright (c) {0}. All rights reserved.", manufacturer);
232 }
233 }
234
235 if (String.IsNullOrEmpty(name))
236 {
237 logVariablePrefixAndExtension = String.Concat("WixBundleLog:Setup.log");
238 }
239 else
240 {
241 // Ensure only allowable path characters are in "name" (and change spaces to underscores).
242 fileSystemSafeBundleName = CompilerCore.MakeValidLongFileName(name.Replace(' ', '_'), "_");
243 logVariablePrefixAndExtension = String.Concat("WixBundleLog:", fileSystemSafeBundleName, ".log");
244 }
245
246 this.activeName = String.IsNullOrEmpty(name) ? Common.GenerateGuid() : name;
247 this.Core.CreateActiveSection(this.activeName, SectionType.Bundle, 0, this.Context.CompilationId);
248
249 // Now that the active section is initialized, process only extension attributes.
250 foreach (var attrib in node.Attributes())
251 {
252 if (!String.IsNullOrEmpty(attrib.Name.NamespaceName) && CompilerCore.WixNamespace != attrib.Name.Namespace)
253 {
254 this.Core.ParseExtensionAttribute(node, attrib);
255 }
256 }
257
258 var baSeen = false;
259 var chainSeen = false;
260 var logSeen = false;
261
262 foreach (var child in node.Elements())
263 {
264 if (CompilerCore.WixNamespace == child.Name.Namespace)
265 {
266 switch (child.Name.LocalName)
267 {
268 case "ApprovedExeForElevation":
269 this.ParseApprovedExeForElevation(child);
270 break;
271 case "BootstrapperApplication":
272 if (baSeen)
273 {
274 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
275 this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, "BootstrapperApplication"));
276 }
277 this.ParseBootstrapperApplicationElement(child);
278 baSeen = true;
279 break;
280 case "BootstrapperApplicationRef":
281 this.ParseBootstrapperApplicationRefElement(child);
282 break;
283 case "OptionalUpdateRegistration":
284 this.ParseOptionalUpdateRegistrationElement(child, manufacturer, parentName, name);
285 break;
286 case "Catalog":
287 this.ParseCatalogElement(child);
288 break;
289 case "Chain":
290 if (chainSeen)
291 {
292 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
293 this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, "Chain"));
294 }
295 this.ParseChainElement(child);
296 chainSeen = true;
297 break;
298 case "Container":
299 this.ParseContainerElement(child);
300 break;
301 case "ContainerRef":
302 this.ParseSimpleRefElement(child, "WixBundleContainer");
303 break;
304 case "Log":
305 if (logSeen)
306 {
307 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
308 this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, "Log"));
309 }
310 logVariablePrefixAndExtension = this.ParseLogElement(child, fileSystemSafeBundleName);
311 logSeen = true;
312 break;
313 case "PayloadGroup":
314 this.ParsePayloadGroupElement(child, ComplexReferenceParentType.Layout, "BundleLayoutOnlyPayloads");
315 break;
316 case "PayloadGroupRef":
317 this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Layout, "BundleLayoutOnlyPayloads", ComplexReferenceChildType.Unknown, null);
318 break;
319 case "RelatedBundle":
320 this.ParseRelatedBundleElement(child);
321 break;
322 case "Update":
323 this.ParseUpdateElement(child);
324 break;
325 case "Variable":
326 this.ParseVariableElement(child);
327 break;
328 case "WixVariable":
329 this.ParseWixVariableElement(child);
330 break;
331 default:
332 this.Core.UnexpectedElement(node, child);
333 break;
334 }
335 }
336 else
337 {
338 this.Core.ParseExtensionElement(node, child);
339 }
340 }
341
342 if (!chainSeen)
343 {
344 this.Core.Write(ErrorMessages.ExpectedElement(sourceLineNumbers, node.Name.LocalName, "Chain"));
345 }
346
347 if (!this.Core.EncounteredError)
348 {
349 if (null != upgradeCode)
350 {
351 var tuple = new WixRelatedBundleTuple(sourceLineNumbers)
352 {
353 BundleId = upgradeCode,
354 Action = RelatedBundleActionType.Upgrade,
355 };
356
357 this.Core.AddTuple(tuple);
358 }
359
360 var containerRow = (WixBundleContainerTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleContainer);
361 containerRow.WixBundleContainer = Compiler.BurnDefaultAttachedContainerId;
362 containerRow.Name = "bundle-attached.cab";
363 containerRow.Type = ContainerType.Attached;
364
365 var row = this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundle);
366 row.Set(0, version);
367 row.Set(1, copyright);
368 row.Set(2, name);
369 row.Set(3, aboutUrl);
370 if (-1 != disableModify)
371 {
372 row.Set(4, disableModify);
373 }
374 if (YesNoType.NotSet != disableRemove)
375 {
376 row.Set(5, (YesNoType.Yes == disableRemove) ? 1 : 0);
377 }
378 // row.Set(6] - (deprecated) "disable repair"
379 row.Set(7, helpTelephone);
380 row.Set(8, helpUrl);
381 row.Set(9, manufacturer);
382 row.Set(10, updateUrl);
383 if (YesNoDefaultType.Default != compressed)
384 {
385 row.Set(11, (YesNoDefaultType.Yes == compressed) ? 1 : 0);
386 }
387
388 row.Set(12, logVariablePrefixAndExtension);
389 row.Set(13, iconSourceFile);
390 row.Set(14, splashScreenSourceFile);
391 row.Set(15, condition);
392 row.Set(16, tag);
393 row.Set(17, this.CurrentPlatform.ToString());
394 row.Set(18, parentName);
395 row.Set(19, upgradeCode);
396
397 // Ensure that the bundle stores the well-known persisted values.
398 var bundleNameWellKnownVariable = (WixBundleVariableTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleVariable);
399 bundleNameWellKnownVariable.WixBundleVariable = Compiler.BURN_BUNDLE_NAME;
400 bundleNameWellKnownVariable.Hidden = false;
401 bundleNameWellKnownVariable.Persisted = true;
402
403 var bundleOriginalSourceWellKnownVariable = (WixBundleVariableTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleVariable);
404 bundleOriginalSourceWellKnownVariable.WixBundleVariable = Compiler.BURN_BUNDLE_ORIGINAL_SOURCE;
405 bundleOriginalSourceWellKnownVariable.Hidden = false;
406 bundleOriginalSourceWellKnownVariable.Persisted = true;
407
408 var bundleOriginalSourceFolderWellKnownVariable = (WixBundleVariableTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleVariable);
409 bundleOriginalSourceFolderWellKnownVariable.WixBundleVariable = Compiler.BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER;
410 bundleOriginalSourceFolderWellKnownVariable.Hidden = false;
411 bundleOriginalSourceFolderWellKnownVariable.Persisted = true;
412
413 var bundleLastUsedSourceWellKnownVariable = (WixBundleVariableTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleVariable);
414 bundleLastUsedSourceWellKnownVariable.WixBundleVariable = Compiler.BURN_BUNDLE_LAST_USED_SOURCE;
415 bundleLastUsedSourceWellKnownVariable.Hidden = false;
416 bundleLastUsedSourceWellKnownVariable.Persisted = true;
417 }
418 }
419
420 /// <summary>
421 /// Parse a Container element.
422 /// </summary>
423 /// <param name="node">Element to parse</param>
424 private string ParseLogElement(XElement node, string fileSystemSafeBundleName)
425 {
426 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
427 var disableLog = YesNoType.NotSet;
428 var variable = "WixBundleLog";
429 var logPrefix = fileSystemSafeBundleName ?? "Setup";
430 var logExtension = ".log";
431
432 foreach (var attrib in node.Attributes())
433 {
434 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
435 {
436 switch (attrib.Name.LocalName)
437 {
438 case "Disable":
439 disableLog = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
440 break;
441 case "PathVariable":
442 variable = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
443 break;
444 case "Prefix":
445 logPrefix = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
446 break;
447 case "Extension":
448 logExtension = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
449 break;
450 default:
451 this.Core.UnexpectedAttribute(node, attrib);
452 break;
453 }
454 }
455 else
456 {
457 this.Core.ParseExtensionAttribute(node, attrib);
458 }
459 }
460
461 if (!logExtension.StartsWith(".", StringComparison.Ordinal))
462 {
463 logExtension = String.Concat(".", logExtension);
464 }
465
466 this.Core.ParseForExtensionElements(node);
467
468 return YesNoType.Yes == disableLog ? null : String.Concat(variable, ":", logPrefix, logExtension);
469 }
470
471 /// <summary>
472 /// Parse a Catalog element.
473 /// </summary>
474 /// <param name="node">Element to parse</param>
475 private void ParseCatalogElement(XElement node)
476 {
477 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
478 Identifier id = null;
479 string sourceFile = null;
480
481 foreach (var attrib in node.Attributes())
482 {
483 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
484 {
485 switch (attrib.Name.LocalName)
486 {
487 case "Id":
488 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
489 break;
490 case "SourceFile":
491 sourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
492 break;
493 default:
494 this.Core.UnexpectedAttribute(node, attrib);
495 break;
496 }
497 }
498 }
499
500 if (null == id)
501 {
502 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
503 }
504
505 if (null == sourceFile)
506 {
507 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFile"));
508 }
509
510 this.Core.ParseForExtensionElements(node);
511
512 // Create catalog row
513 if (!this.Core.EncounteredError)
514 {
515 this.CreatePayloadRow(sourceLineNumbers, id, Path.GetFileName(sourceFile), sourceFile, null, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, ComplexReferenceChildType.Unknown, null, YesNoDefaultType.Yes, YesNoType.Yes, null, null, null);
516
517 var wixCatalogRow = (WixBundleCatalogTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleCatalog, id);
518 wixCatalogRow.Payload_ = id.Id;
519 }
520 }
521
522 /// <summary>
523 /// Parse a Container element.
524 /// </summary>
525 /// <param name="node">Element to parse</param>
526 private void ParseContainerElement(XElement node)
527 {
528 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
529 Identifier id = null;
530 string downloadUrl = null;
531 string name = null;
532 var type = ContainerType.Detached;
533
534 foreach (var attrib in node.Attributes())
535 {
536 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
537 {
538 switch (attrib.Name.LocalName)
539 {
540 case "Id":
541 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
542 break;
543 case "DownloadUrl":
544 downloadUrl = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
545 break;
546 case "Name":
547 name = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
548 break;
549 case "Type":
550 var typeString = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
551 if (!Enum.TryParse<ContainerType>(typeString, out type))
552 {
553 this.Core.Write(ErrorMessages.IllegalAttributeValueWithLegalList(sourceLineNumbers, node.Name.LocalName, "Type", typeString, "attached, detached"));
554 }
555 break;
556 default:
557 this.Core.UnexpectedAttribute(node, attrib);
558 break;
559 }
560 }
561 else
562 {
563 this.Core.ParseExtensionAttribute(node, attrib);
564 }
565 }
566
567 if (null == id)
568 {
569 if (!String.IsNullOrEmpty(name))
570 {
571 id = this.Core.CreateIdentifierFromFilename(name);
572 }
573
574 if (null == id)
575 {
576 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
577 id = Identifier.Invalid;
578 }
579 else if (!Common.IsIdentifier(id.Id))
580 {
581 this.Core.Write(ErrorMessages.IllegalIdentifier(sourceLineNumbers, node.Name.LocalName, "Id", id.Id));
582 }
583 }
584 else if (null == name)
585 {
586 name = id.Id;
587 }
588
589 if (!String.IsNullOrEmpty(downloadUrl) && ContainerType.Detached != type)
590 {
591 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "DownloadUrl", "Type", "attached"));
592 }
593
594 foreach (var child in node.Elements())
595 {
596 if (CompilerCore.WixNamespace == child.Name.Namespace)
597 {
598 switch (child.Name.LocalName)
599 {
600 case "PackageGroupRef":
601 this.ParsePackageGroupRefElement(child, ComplexReferenceParentType.Container, id.Id);
602 break;
603 default:
604 this.Core.UnexpectedElement(node, child);
605 break;
606 }
607 }
608 else
609 {
610 this.Core.ParseExtensionElement(node, child);
611 }
612 }
613
614
615 if (!this.Core.EncounteredError)
616 {
617 var row = (WixBundleContainerTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleContainer, id);
618 row.Name = name;
619 row.Type = type;
620 row.DownloadUrl = downloadUrl;
621 }
622 }
623
624 /// <summary>
625 /// Parse the BoostrapperApplication element.
626 /// </summary>
627 /// <param name="node">Element to parse</param>
628 private void ParseBootstrapperApplicationElement(XElement node)
629 {
630 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
631 string id = null;
632 string previousId = null;
633 var previousType = ComplexReferenceChildType.Unknown;
634
635 // The BootstrapperApplication element acts like a Payload element so delegate to the "Payload" attribute parsing code to parse and create a Payload entry.
636 id = this.ParsePayloadElementContent(node, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, previousType, previousId, false);
637 if (null != id)
638 {
639 previousId = id;
640 previousType = ComplexReferenceChildType.Payload;
641 }
642
643 foreach (var child in node.Elements())
644 {
645 if (CompilerCore.WixNamespace == child.Name.Namespace)
646 {
647 switch (child.Name.LocalName)
648 {
649 case "Payload":
650 previousId = this.ParsePayloadElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, previousType, previousId);
651 previousType = ComplexReferenceChildType.Payload;
652 break;
653 case "PayloadGroupRef":
654 previousId = this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, previousType, previousId);
655 previousType = ComplexReferenceChildType.PayloadGroup;
656 break;
657 default:
658 this.Core.UnexpectedElement(node, child);
659 break;
660 }
661 }
662 else
663 {
664 this.Core.ParseExtensionElement(node, child);
665 }
666 }
667
668 if (null == previousId)
669 {
670 // We need *either* <Payload> or <PayloadGroupRef> or even just @SourceFile on the BA...
671 // but we just say there's a missing <Payload>.
672 // TODO: Is there a better message for this?
673 this.Core.Write(ErrorMessages.ExpectedElement(sourceLineNumbers, node.Name.LocalName, "Payload"));
674 }
675
676 // Add the application as an attached container and if an Id was provided add that too.
677 if (!this.Core.EncounteredError)
678 {
679 var containerRow = (WixBundleContainerTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleContainer);
680 containerRow.WixBundleContainer = Compiler.BurnUXContainerId;
681 containerRow.Name = "bundle-ux.cab";
682 containerRow.Type = ContainerType.Attached;
683
684 if (!String.IsNullOrEmpty(id))
685 {
686 var row = this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBootstrapperApplication);
687 row.Set(0, id);
688 }
689 }
690 }
691
692 /// <summary>
693 /// Parse the BoostrapperApplicationRef element.
694 /// </summary>
695 /// <param name="node">Element to parse</param>
696 private void ParseBootstrapperApplicationRefElement(XElement node)
697 {
698 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
699 string id = null;
700 string previousId = null;
701 var previousType = ComplexReferenceChildType.Unknown;
702
703 foreach (var attrib in node.Attributes())
704 {
705 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
706 {
707 switch (attrib.Name.LocalName)
708 {
709 case "Id":
710 id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
711 break;
712 default:
713 this.Core.UnexpectedAttribute(node, attrib);
714 break;
715 }
716 }
717 else
718 {
719 this.Core.ParseExtensionAttribute(node, attrib);
720 }
721 }
722
723 foreach (var child in node.Elements())
724 {
725 if (CompilerCore.WixNamespace == child.Name.Namespace)
726 {
727 switch (child.Name.LocalName)
728 {
729 case "Payload":
730 previousId = this.ParsePayloadElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, previousType, previousId);
731 previousType = ComplexReferenceChildType.Payload;
732 break;
733 case "PayloadGroupRef":
734 previousId = this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, previousType, previousId);
735 previousType = ComplexReferenceChildType.PayloadGroup;
736 break;
737 default:
738 this.Core.UnexpectedElement(node, child);
739 break;
740 }
741 }
742 else
743 {
744 this.Core.ParseExtensionElement(node, child);
745 }
746 }
747
748
749 if (String.IsNullOrEmpty(id))
750 {
751 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
752 }
753 else
754 {
755 this.Core.CreateSimpleReference(sourceLineNumbers, "WixBootstrapperApplication", id);
756 }
757 }
758
759 /// <summary>
760 /// Parse the OptionalUpdateRegistration element.
761 /// </summary>
762 /// <param name="node">The element to parse.</param>
763 /// <param name="defaultManufacturer">The manufacturer.</param>
764 /// <param name="defaultProductFamily">The product family.</param>
765 /// <param name="defaultName">The bundle name.</param>
766 private void ParseOptionalUpdateRegistrationElement(XElement node, string defaultManufacturer, string defaultProductFamily, string defaultName)
767 {
768 const string defaultClassification = "Update";
769
770 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
771 string manufacturer = null;
772 string department = null;
773 string productFamily = null;
774 string name = null;
775 var classification = defaultClassification;
776
777 foreach (var attrib in node.Attributes())
778 {
779 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
780 {
781 switch (attrib.Name.LocalName)
782 {
783 case "Manufacturer":
784 manufacturer = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
785 break;
786 case "Department":
787 department = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
788 break;
789 case "ProductFamily":
790 productFamily = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
791 break;
792 case "Name":
793 name = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
794 break;
795 case "Classification":
796 classification = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
797 break;
798 default:
799 this.Core.UnexpectedAttribute(node, attrib);
800 break;
801 }
802 }
803 else
804 {
805 this.Core.ParseExtensionAttribute(node, attrib);
806 }
807 }
808
809 if (String.IsNullOrEmpty(manufacturer))
810 {
811 if (!String.IsNullOrEmpty(defaultManufacturer))
812 {
813 manufacturer = defaultManufacturer;
814 }
815 else
816 {
817 this.Core.Write(ErrorMessages.ExpectedAttributeInElementOrParent(sourceLineNumbers, node.Name.LocalName, "Manufacturer", node.Parent.Name.LocalName));
818 }
819 }
820
821 if (String.IsNullOrEmpty(productFamily))
822 {
823 if (!String.IsNullOrEmpty(defaultProductFamily))
824 {
825 productFamily = defaultProductFamily;
826 }
827 }
828
829 if (String.IsNullOrEmpty(name))
830 {
831 if (!String.IsNullOrEmpty(defaultName))
832 {
833 name = defaultName;
834 }
835 else
836 {
837 this.Core.Write(ErrorMessages.ExpectedAttributeInElementOrParent(sourceLineNumbers, node.Name.LocalName, "Name", node.Parent.Name.LocalName));
838 }
839 }
840
841 if (String.IsNullOrEmpty(classification))
842 {
843 this.Core.Write(ErrorMessages.IllegalEmptyAttributeValue(sourceLineNumbers, node.Name.LocalName, "Classification", defaultClassification));
844 }
845
846 this.Core.ParseForExtensionElements(node);
847
848 if (!this.Core.EncounteredError)
849 {
850 var row = this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixUpdateRegistration);
851 row.Set(0, manufacturer);
852 row.Set(1, department);
853 row.Set(2, productFamily);
854 row.Set(3, name);
855 row.Set(4, classification);
856 }
857 }
858
859 /// <summary>
860 /// Parse Payload element.
861 /// </summary>
862 /// <param name="node">Element to parse</param>
863 /// <param name="parentType">ComplexReferenceParentType of parent element. (BA or PayloadGroup)</param>
864 /// <param name="parentId">Identifier of parent element.</param>
865 private string ParsePayloadElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
866 {
867 Debug.Assert(ComplexReferenceParentType.PayloadGroup == parentType || ComplexReferenceParentType.Package == parentType || ComplexReferenceParentType.Container == parentType);
868 Debug.Assert(ComplexReferenceChildType.Unknown == previousType || ComplexReferenceChildType.PayloadGroup == previousType || ComplexReferenceChildType.Payload == previousType);
869
870 var id = this.ParsePayloadElementContent(node, parentType, parentId, previousType, previousId, true);
871 var context = new Dictionary<string, string>
872 {
873 ["Id"] = id
874 };
875
876 foreach (var child in node.Elements())
877 {
878 if (CompilerCore.WixNamespace == child.Name.Namespace)
879 {
880 switch (child.Name.LocalName)
881 {
882 default:
883 this.Core.UnexpectedElement(node, child);
884 break;
885 }
886 }
887 else
888 {
889 this.Core.ParseExtensionElement(node, child, context);
890 }
891 }
892
893 return id;
894 }
895
896 /// <summary>
897 /// Parse the attributes of the Payload element.
898 /// </summary>
899 /// <param name="node">Element to parse</param>
900 /// <param name="parentType">ComplexReferenceParentType of parent element.</param>
901 /// <param name="parentId">Identifier of parent element.</param>
902 private string ParsePayloadElementContent(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId, bool required)
903 {
904 Debug.Assert(ComplexReferenceParentType.PayloadGroup == parentType || ComplexReferenceParentType.Package == parentType || ComplexReferenceParentType.Container == parentType);
905
906 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
907 var compressed = YesNoDefaultType.Default;
908 var enableSignatureVerification = YesNoType.No;
909 Identifier id = null;
910 string name = null;
911 string sourceFile = null;
912 string downloadUrl = null;
913 RemotePayload remotePayload = null;
914
915 // This list lets us evaluate extension attributes *after* all core attributes
916 // have been parsed and dealt with, regardless of authoring order.
917 var extensionAttributes = new List<XAttribute>();
918
919 foreach (var attrib in node.Attributes())
920 {
921 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
922 {
923 switch (attrib.Name.LocalName)
924 {
925 case "Id":
926 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
927 break;
928 case "Compressed":
929 compressed = this.Core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib);
930 break;
931 case "Name":
932 name = this.Core.GetAttributeLongFilename(sourceLineNumbers, attrib, false, true);
933 break;
934 case "SourceFile":
935 sourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
936 break;
937 case "DownloadUrl":
938 downloadUrl = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
939 break;
940 case "EnableSignatureVerification":
941 enableSignatureVerification = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
942 break;
943 default:
944 this.Core.UnexpectedAttribute(node, attrib);
945 break;
946 }
947 }
948 else
949 {
950 extensionAttributes.Add(attrib);
951 }
952 }
953
954 if (!required && null == sourceFile)
955 {
956 // Nothing left to do!
957 return null;
958 }
959
960 if (null == id)
961 {
962 id = this.Core.CreateIdentifier("pay", (null != sourceFile) ? sourceFile.ToUpperInvariant() : String.Empty);
963 }
964
965 // Now that the PayloadId is known, we can parse the extension attributes.
966 var context = new Dictionary<string, string>
967 {
968 ["Id"] = id.Id
969 };
970
971 foreach (var extensionAttribute in extensionAttributes)
972 {
973 this.Core.ParseExtensionAttribute(node, extensionAttribute, context);
974 }
975
976 // We only handle the elements we care about. Let caller handle other children.
977 foreach (var child in node.Elements(CompilerCore.WixNamespace + "RemotePayload"))
978 {
979 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
980
981 if (CompilerCore.WixNamespace == node.Name.Namespace && node.Name.LocalName != "ExePackage")
982 {
983 this.Core.Write(ErrorMessages.RemotePayloadUnsupported(childSourceLineNumbers));
984 continue;
985 }
986
987 if (null != remotePayload)
988 {
989 this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, child.Name.LocalName));
990 }
991
992 remotePayload = this.ParseRemotePayloadElement(child);
993 }
994
995 if (null != sourceFile && null != remotePayload)
996 {
997 this.Core.Write(ErrorMessages.UnexpectedElementWithAttribute(sourceLineNumbers, node.Name.LocalName, "RemotePayload", "SourceFile"));
998 }
999 else if (null == sourceFile && null == remotePayload)
1000 {
1001 this.Core.Write(ErrorMessages.ExpectedAttributeOrElement(sourceLineNumbers, node.Name.LocalName, "SourceFile", "RemotePayload"));
1002 }
1003 else if (null == sourceFile)
1004 {
1005 sourceFile = String.Empty;
1006 }
1007
1008 if (null == downloadUrl && null != remotePayload)
1009 {
1010 this.Core.Write(ErrorMessages.ExpectedAttributeWithElement(sourceLineNumbers, node.Name.LocalName, "DownloadUrl", "RemotePayload"));
1011 }
1012
1013 if (Compiler.BurnUXContainerId == parentId)
1014 {
1015 if (compressed == YesNoDefaultType.No)
1016 {
1017 this.Core.Write(WarningMessages.UxPayloadsOnlySupportEmbedding(sourceLineNumbers, sourceFile));
1018 }
1019
1020 compressed = YesNoDefaultType.Yes;
1021 }
1022
1023 this.CreatePayloadRow(sourceLineNumbers, id, name, sourceFile, downloadUrl, parentType, parentId, previousType, previousId, compressed, enableSignatureVerification, null, null, remotePayload);
1024
1025 return id.Id;
1026 }
1027
1028 private RemotePayload ParseRemotePayloadElement(XElement node)
1029 {
1030 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1031 var remotePayload = new RemotePayload();
1032
1033 foreach (var attrib in node.Attributes())
1034 {
1035 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1036 {
1037 switch (attrib.Name.LocalName)
1038 {
1039 case "CertificatePublicKey":
1040 remotePayload.CertificatePublicKey = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1041 break;
1042 case "CertificateThumbprint":
1043 remotePayload.CertificateThumbprint = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1044 break;
1045 case "Description":
1046 remotePayload.Description = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1047 break;
1048 case "Hash":
1049 remotePayload.Hash = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1050 break;
1051 case "ProductName":
1052 remotePayload.ProductName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1053 break;
1054 case "Size":
1055 remotePayload.Size = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue);
1056 break;
1057 case "Version":
1058 remotePayload.Version = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1059 break;
1060 default:
1061 this.Core.UnexpectedAttribute(node, attrib);
1062 break;
1063 }
1064 }
1065 else
1066 {
1067 this.Core.ParseExtensionAttribute(node, attrib);
1068 }
1069 }
1070
1071 if (String.IsNullOrEmpty(remotePayload.ProductName))
1072 {
1073 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ProductName"));
1074 }
1075
1076 if (String.IsNullOrEmpty(remotePayload.Description))
1077 {
1078 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Description"));
1079 }
1080
1081 if (String.IsNullOrEmpty(remotePayload.Hash))
1082 {
1083 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Hash"));
1084 }
1085
1086 if (0 == remotePayload.Size)
1087 {
1088 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Size"));
1089 }
1090
1091 if (String.IsNullOrEmpty(remotePayload.Version))
1092 {
1093 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version"));
1094 }
1095
1096 return remotePayload;
1097 }
1098
1099 /// <summary>
1100 /// Creates the row for a Payload.
1101 /// </summary>
1102 /// <param name="node">Element to parse</param>
1103 /// <param name="parentType">ComplexReferenceParentType of parent element</param>
1104 /// <param name="parentId">Identifier of parent element.</param>
1105 private WixBundlePayloadTuple CreatePayloadRow(SourceLineNumber sourceLineNumbers, Identifier id, string name, string sourceFile, string downloadUrl, ComplexReferenceParentType parentType,
1106 string parentId, ComplexReferenceChildType previousType, string previousId, YesNoDefaultType compressed, YesNoType enableSignatureVerification, string displayName, string description,
1107 RemotePayload remotePayload)
1108 {
1109 WixBundlePayloadTuple row = null;
1110
1111 if (!this.Core.EncounteredError)
1112 {
1113 row = (WixBundlePayloadTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundlePayload, id);
1114 row.Name = String.IsNullOrEmpty(name) ? Path.GetFileName(sourceFile) : name;
1115 row.SourceFile = sourceFile;
1116 row.DownloadUrl = downloadUrl;
1117 row.Compressed = compressed;
1118 row.UnresolvedSourceFile = sourceFile; // duplicate of sourceFile but in a string column so it won't get resolved to a full path during binding.
1119 row.DisplayName = displayName;
1120 row.Description = description;
1121 row.EnableSignatureValidation = (YesNoType.Yes == enableSignatureVerification);
1122
1123 if (null != remotePayload)
1124 {
1125 row.Description = remotePayload.Description;
1126 row.DisplayName = remotePayload.ProductName;
1127 row.Hash = remotePayload.Hash;
1128 row.PublicKey = remotePayload.CertificatePublicKey;
1129 row.Thumbprint = remotePayload.CertificateThumbprint;
1130 row.FileSize = remotePayload.Size;
1131 row.Version = remotePayload.Version;
1132 }
1133
1134 this.CreateGroupAndOrderingRows(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.Payload, id.Id, previousType, previousId);
1135 }
1136
1137 return row;
1138 }
1139
1140 /// <summary>
1141 /// Parse PayloadGroup element.
1142 /// </summary>
1143 /// <param name="node">Element to parse</param>
1144 /// <param name="parentType">Optional ComplexReferenceParentType of parent element. (typically another PayloadGroup)</param>
1145 /// <param name="parentId">Identifier of parent element.</param>
1146 private void ParsePayloadGroupElement(XElement node, ComplexReferenceParentType parentType, string parentId)
1147 {
1148 Debug.Assert(ComplexReferenceParentType.Unknown == parentType || ComplexReferenceParentType.Layout == parentType || ComplexReferenceParentType.PayloadGroup == parentType);
1149
1150 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1151 Identifier id = null;
1152
1153 foreach (var attrib in node.Attributes())
1154 {
1155 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1156 {
1157 switch (attrib.Name.LocalName)
1158 {
1159 case "Id":
1160 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
1161 break;
1162 default:
1163 this.Core.UnexpectedAttribute(node, attrib);
1164 break;
1165 }
1166 }
1167 else
1168 {
1169 this.Core.ParseExtensionAttribute(node, attrib);
1170 }
1171 }
1172
1173 if (null == id)
1174 {
1175 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
1176 id = Identifier.Invalid;
1177 }
1178
1179 var previousType = ComplexReferenceChildType.Unknown;
1180 string previousId = null;
1181 foreach (var child in node.Elements())
1182 {
1183 if (CompilerCore.WixNamespace == child.Name.Namespace)
1184 {
1185 switch (child.Name.LocalName)
1186 {
1187 case "Payload":
1188 previousId = this.ParsePayloadElement(child, ComplexReferenceParentType.PayloadGroup, id.Id, previousType, previousId);
1189 previousType = ComplexReferenceChildType.Payload;
1190 break;
1191 case "PayloadGroupRef":
1192 previousId = this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.PayloadGroup, id.Id, previousType, previousId);
1193 previousType = ComplexReferenceChildType.PayloadGroup;
1194 break;
1195 default:
1196 this.Core.UnexpectedElement(node, child);
1197 break;
1198 }
1199 }
1200 else
1201 {
1202 this.Core.ParseExtensionElement(node, child);
1203 }
1204 }
1205
1206
1207 if (!this.Core.EncounteredError)
1208 {
1209 this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundlePayloadGroup, id);
1210
1211 this.CreateGroupAndOrderingRows(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.PayloadGroup, id.Id, ComplexReferenceChildType.Unknown, null);
1212 }
1213 }
1214
1215 /// <summary>
1216 /// Parses a payload group reference element.
1217 /// </summary>
1218 /// <param name="node">Element to parse.</param>
1219 /// <param name="parentType">ComplexReferenceParentType of parent element (BA or PayloadGroup).</param>
1220 /// <param name="parentId">Identifier of parent element.</param>
1221 private string ParsePayloadGroupRefElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
1222 {
1223 Debug.Assert(ComplexReferenceParentType.Layout == parentType || ComplexReferenceParentType.PayloadGroup == parentType || ComplexReferenceParentType.Package == parentType || ComplexReferenceParentType.Container == parentType);
1224 Debug.Assert(ComplexReferenceChildType.Unknown == previousType || ComplexReferenceChildType.PayloadGroup == previousType || ComplexReferenceChildType.Payload == previousType);
1225
1226 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1227 string id = null;
1228
1229 foreach (var attrib in node.Attributes())
1230 {
1231 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1232 {
1233 switch (attrib.Name.LocalName)
1234 {
1235 case "Id":
1236 id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
1237 this.Core.CreateSimpleReference(sourceLineNumbers, "WixBundlePayloadGroup", id);
1238 break;
1239 default:
1240 this.Core.UnexpectedAttribute(node, attrib);
1241 break;
1242 }
1243 }
1244 else
1245 {
1246 this.Core.ParseExtensionAttribute(node, attrib);
1247 }
1248 }
1249
1250 if (null == id)
1251 {
1252 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
1253 }
1254
1255 this.Core.ParseForExtensionElements(node);
1256
1257 this.CreateGroupAndOrderingRows(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.PayloadGroup, id, previousType, previousId);
1258
1259 return id;
1260 }
1261
1262 /// <summary>
1263 /// Creates group and ordering information.
1264 /// </summary>
1265 /// <param name="sourceLineNumbers">Source line numbers.</param>
1266 /// <param name="parentType">Type of parent group, if known.</param>
1267 /// <param name="parentId">Identifier of parent group, if known.</param>
1268 /// <param name="type">Type of this item.</param>
1269 /// <param name="id">Identifier for this item.</param>
1270 /// <param name="previousType">Type of previous item, if known.</param>
1271 /// <param name="previousId">Identifier of previous item, if known</param>
1272 private void CreateGroupAndOrderingRows(SourceLineNumber sourceLineNumbers,
1273 ComplexReferenceParentType parentType, string parentId,
1274 ComplexReferenceChildType type, string id,
1275 ComplexReferenceChildType previousType, string previousId)
1276 {
1277 if (ComplexReferenceParentType.Unknown != parentType && null != parentId)
1278 {
1279 this.Core.CreateWixGroupRow(sourceLineNumbers, parentType, parentId, type, id);
1280 }
1281
1282 if (ComplexReferenceChildType.Unknown != previousType && null != previousId)
1283 {
1284 this.CreateWixOrderingRow(sourceLineNumbers, type, id, previousType, previousId);
1285 }
1286 }
1287
1288 /// <summary>
1289 /// Parse ExitCode element.
1290 /// </summary>
1291 /// <param name="node">Element to parse</param>
1292 /// <param name="packageId">Id of parent element</param>
1293 private void ParseExitCodeElement(XElement node, string packageId)
1294 {
1295 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1296 var value = CompilerConstants.IntegerNotSet;
1297 var behavior = ExitCodeBehaviorType.NotSet;
1298
1299 foreach (var attrib in node.Attributes())
1300 {
1301 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1302 {
1303 switch (attrib.Name.LocalName)
1304 {
1305 case "Value":
1306 value = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, Int32.MinValue + 2, Int32.MaxValue);
1307 break;
1308 case "Behavior":
1309 var behaviorString = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1310 if (!Enum.TryParse<ExitCodeBehaviorType>(behaviorString, true, out behavior))
1311 {
1312 this.Core.Write(ErrorMessages.IllegalAttributeValueWithLegalList(sourceLineNumbers, node.Name.LocalName, "Behavior", behaviorString, "success, error, scheduleReboot, forceReboot"));
1313 }
1314 break;
1315 default:
1316 this.Core.UnexpectedAttribute(node, attrib);
1317 break;
1318 }
1319 }
1320 else
1321 {
1322 this.Core.ParseExtensionAttribute(node, attrib);
1323 }
1324 }
1325
1326 if (ExitCodeBehaviorType.NotSet == behavior)
1327 {
1328 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Behavior"));
1329 }
1330
1331 this.Core.ParseForExtensionElements(node);
1332
1333 if (!this.Core.EncounteredError)
1334 {
1335 var row = (WixBundlePackageExitCodeTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundlePackageExitCode);
1336 row.ChainPackageId = packageId;
1337 row.Code = value;
1338 row.Behavior = behavior;
1339 }
1340 }
1341
1342 /// <summary>
1343 /// Parse Chain element.
1344 /// </summary>
1345 /// <param name="node">Element to parse</param>
1346 private void ParseChainElement(XElement node)
1347 {
1348 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1349 var attributes = WixChainAttributes.None;
1350
1351 foreach (var attrib in node.Attributes())
1352 {
1353 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1354 {
1355 switch (attrib.Name.LocalName)
1356 {
1357 case "DisableRollback":
1358 if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
1359 {
1360 attributes |= WixChainAttributes.DisableRollback;
1361 }
1362 break;
1363 case "DisableSystemRestore":
1364 if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
1365 {
1366 attributes |= WixChainAttributes.DisableSystemRestore;
1367 }
1368 break;
1369 case "ParallelCache":
1370 if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
1371 {
1372 attributes |= WixChainAttributes.ParallelCache;
1373 }
1374 break;
1375 default:
1376 this.Core.UnexpectedAttribute(node, attrib);
1377 break;
1378 }
1379 }
1380 else
1381 {
1382 this.Core.ParseExtensionAttribute(node, attrib);
1383 }
1384 }
1385
1386 // Ensure there is always a rollback boundary at the beginning of the chain.
1387 this.CreateRollbackBoundary(sourceLineNumbers, new Identifier("WixDefaultBoundary", AccessModifier.Public), YesNoType.Yes, YesNoType.No, ComplexReferenceParentType.PackageGroup, "WixChain", ComplexReferenceChildType.Unknown, null);
1388
1389 var previousId = "WixDefaultBoundary";
1390 var previousType = ComplexReferenceChildType.Package;
1391
1392 foreach (var child in node.Elements())
1393 {
1394 if (CompilerCore.WixNamespace == child.Name.Namespace)
1395 {
1396 switch (child.Name.LocalName)
1397 {
1398 case "MsiPackage":
1399 previousId = this.ParseMsiPackageElement(child, ComplexReferenceParentType.PackageGroup, "WixChain", previousType, previousId);
1400 previousType = ComplexReferenceChildType.Package;
1401 break;
1402 case "MspPackage":
1403 previousId = this.ParseMspPackageElement(child, ComplexReferenceParentType.PackageGroup, "WixChain", previousType, previousId);
1404 previousType = ComplexReferenceChildType.Package;
1405 break;
1406 case "MsuPackage":
1407 previousId = this.ParseMsuPackageElement(child, ComplexReferenceParentType.PackageGroup, "WixChain", previousType, previousId);
1408 previousType = ComplexReferenceChildType.Package;
1409 break;
1410 case "ExePackage":
1411 previousId = this.ParseExePackageElement(child, ComplexReferenceParentType.PackageGroup, "WixChain", previousType, previousId);
1412 previousType = ComplexReferenceChildType.Package;
1413 break;
1414 case "RollbackBoundary":
1415 previousId = this.ParseRollbackBoundaryElement(child, ComplexReferenceParentType.PackageGroup, "WixChain", previousType, previousId);
1416 previousType = ComplexReferenceChildType.Package;
1417 break;
1418 case "PackageGroupRef":
1419 previousId = this.ParsePackageGroupRefElement(child, ComplexReferenceParentType.PackageGroup, "WixChain", previousType, previousId);
1420 previousType = ComplexReferenceChildType.PackageGroup;
1421 break;
1422 default:
1423 this.Core.UnexpectedElement(node, child);
1424 break;
1425 }
1426 }
1427 else
1428 {
1429 this.Core.ParseExtensionElement(node, child);
1430 }
1431 }
1432
1433
1434 if (null == previousId)
1435 {
1436 this.Core.Write(ErrorMessages.ExpectedElement(sourceLineNumbers, node.Name.LocalName, "MsiPackage", "ExePackage", "PackageGroupRef"));
1437 }
1438
1439 if (!this.Core.EncounteredError)
1440 {
1441 var row = (WixChainTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixChain);
1442 row.Attributes = attributes;
1443 }
1444 }
1445
1446 /// <summary>
1447 /// Parse MsiPackage element
1448 /// </summary>
1449 /// <param name="node">Element to parse</param>
1450 /// <param name="parentType">Type of parent group, if known.</param>
1451 /// <param name="parentId">Identifier of parent group, if known.</param>
1452 /// <param name="previousType">Type of previous item, if known.</param>
1453 /// <param name="previousId">Identifier of previous item, if known</param>
1454 /// <returns>Identifier for package element.</returns>
1455 private string ParseMsiPackageElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
1456 {
1457 return this.ParseChainPackage(node, WixBundlePackageType.Msi, parentType, parentId, previousType, previousId);
1458 }
1459
1460 /// <summary>
1461 /// Parse MspPackage element
1462 /// </summary>
1463 /// <param name="node">Element to parse</param>
1464 /// <param name="parentType">Type of parent group, if known.</param>
1465 /// <param name="parentId">Identifier of parent group, if known.</param>
1466 /// <param name="previousType">Type of previous item, if known.</param>
1467 /// <param name="previousId">Identifier of previous item, if known</param>
1468 /// <returns>Identifier for package element.</returns>
1469 private string ParseMspPackageElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
1470 {
1471 return this.ParseChainPackage(node, WixBundlePackageType.Msp, parentType, parentId, previousType, previousId);
1472 }
1473
1474 /// <summary>
1475 /// Parse MsuPackage element
1476 /// </summary>
1477 /// <param name="node">Element to parse</param>
1478 /// <param name="parentType">Type of parent group, if known.</param>
1479 /// <param name="parentId">Identifier of parent group, if known.</param>
1480 /// <param name="previousType">Type of previous item, if known.</param>
1481 /// <param name="previousId">Identifier of previous item, if known</param>
1482 /// <returns>Identifier for package element.</returns>
1483 private string ParseMsuPackageElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
1484 {
1485 return this.ParseChainPackage(node, WixBundlePackageType.Msu, parentType, parentId, previousType, previousId);
1486 }
1487
1488 /// <summary>
1489 /// Parse ExePackage element
1490 /// </summary>
1491 /// <param name="node">Element to parse</param>
1492 /// <param name="parentType">Type of parent group, if known.</param>
1493 /// <param name="parentId">Identifier of parent group, if known.</param>
1494 /// <param name="previousType">Type of previous item, if known.</param>
1495 /// <param name="previousId">Identifier of previous item, if known</param>
1496 /// <returns>Identifier for package element.</returns>
1497 private string ParseExePackageElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
1498 {
1499 return this.ParseChainPackage(node, WixBundlePackageType.Exe, parentType, parentId, previousType, previousId);
1500 }
1501
1502 /// <summary>
1503 /// Parse RollbackBoundary element
1504 /// </summary>
1505 /// <param name="node">Element to parse</param>
1506 /// <param name="parentType">Type of parent group, if known.</param>
1507 /// <param name="parentId">Identifier of parent group, if known.</param>
1508 /// <param name="previousType">Type of previous item, if known.</param>
1509 /// <param name="previousId">Identifier of previous item, if known</param>
1510 /// <returns>Identifier for package element.</returns>
1511 private string ParseRollbackBoundaryElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
1512 {
1513 Debug.Assert(ComplexReferenceParentType.PackageGroup == parentType);
1514 Debug.Assert(ComplexReferenceChildType.Unknown == previousType || ComplexReferenceChildType.PackageGroup == previousType || ComplexReferenceChildType.Package == previousType);
1515
1516 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1517 Identifier id = null;
1518 var vital = YesNoType.Yes;
1519 var transaction = YesNoType.No;
1520
1521 // This list lets us evaluate extension attributes *after* all core attributes
1522 // have been parsed and dealt with, regardless of authoring order.
1523 var extensionAttributes = new List<XAttribute>();
1524
1525 foreach (var attrib in node.Attributes())
1526 {
1527 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1528 {
1529 var allowed = true;
1530 switch (attrib.Name.LocalName)
1531 {
1532 case "Id":
1533 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
1534 break;
1535 case "Vital":
1536 vital = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1537 break;
1538 case "Transaction":
1539 transaction = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1540 break;
1541 default:
1542 allowed = false;
1543 break;
1544 }
1545
1546 if (!allowed)
1547 {
1548 this.Core.UnexpectedAttribute(node, attrib);
1549 }
1550 }
1551 else
1552 {
1553 // Save the extension attributes for later...
1554 extensionAttributes.Add(attrib);
1555 }
1556 }
1557
1558 if (null == id)
1559 {
1560 if (!String.IsNullOrEmpty(previousId))
1561 {
1562 id = this.Core.CreateIdentifier("rba", previousId);
1563 }
1564
1565 if (null == id)
1566 {
1567 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
1568 id = Identifier.Invalid;
1569 }
1570 else if (!Common.IsIdentifier(id.Id))
1571 {
1572 this.Core.Write(ErrorMessages.IllegalIdentifier(sourceLineNumbers, node.Name.LocalName, "Id", id.Id));
1573 }
1574 }
1575
1576 // Now that the rollback identifier is known, we can parse the extension attributes...
1577 var contextValues = new Dictionary<string, string>
1578 {
1579 ["RollbackBoundaryId"] = id.Id
1580 };
1581 foreach (var attribute in extensionAttributes)
1582 {
1583 this.Core.ParseExtensionAttribute(node, attribute, contextValues);
1584 }
1585
1586 this.Core.ParseForExtensionElements(node);
1587
1588 if (!this.Core.EncounteredError)
1589 {
1590 this.CreateRollbackBoundary(sourceLineNumbers, id, vital, transaction, parentType, parentId, previousType, previousId);
1591 }
1592
1593 return id.Id;
1594 }
1595
1596 /// <summary>
1597 /// Parses one of the ChainPackage elements
1598 /// </summary>
1599 /// <param name="node">Element to parse</param>
1600 /// <param name="packageType">Type of package to parse</param>
1601 /// <param name="parentType">Type of parent group, if known.</param>
1602 /// <param name="parentId">Identifier of parent group, if known.</param>
1603 /// <param name="previousType">Type of previous item, if known.</param>
1604 /// <param name="previousId">Identifier of previous item, if known</param>
1605 /// <returns>Identifier for package element.</returns>
1606 /// <remarks>This method contains the shared logic for parsing all of the ChainPackage
1607 /// types, as there is more in common between them than different.</remarks>
1608 private string ParseChainPackage(XElement node, WixBundlePackageType packageType, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
1609 {
1610 Debug.Assert(ComplexReferenceParentType.PackageGroup == parentType);
1611 Debug.Assert(ComplexReferenceChildType.Unknown == previousType || ComplexReferenceChildType.PackageGroup == previousType || ComplexReferenceChildType.Package == previousType);
1612
1613 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1614 Identifier id = null;
1615 string name = null;
1616 string sourceFile = null;
1617 string downloadUrl = null;
1618 string after = null;
1619 string installCondition = null;
1620 var cache = YesNoAlwaysType.Yes; // the default is to cache everything in tradeoff for stability over disk space.
1621 string cacheId = null;
1622 string description = null;
1623 string displayName = null;
1624 var logPathVariable = (packageType == WixBundlePackageType.Msu) ? String.Empty : null;
1625 var rollbackPathVariable = (packageType == WixBundlePackageType.Msu) ? String.Empty : null;
1626 var permanent = YesNoType.NotSet;
1627 var visible = YesNoType.NotSet;
1628 var vital = YesNoType.Yes;
1629 string installCommand = null;
1630 string repairCommand = null;
1631 var repairable = YesNoType.NotSet;
1632 string uninstallCommand = null;
1633 var perMachine = YesNoDefaultType.NotSet;
1634 string detectCondition = null;
1635 string protocol = null;
1636 var installSize = CompilerConstants.IntegerNotSet;
1637 string msuKB = null;
1638 var suppressLooseFilePayloadGeneration = YesNoType.NotSet;
1639 var enableSignatureVerification = YesNoType.No;
1640 var compressed = YesNoDefaultType.Default;
1641 var displayInternalUI = YesNoType.NotSet;
1642 var enableFeatureSelection = YesNoType.NotSet;
1643 var forcePerMachine = YesNoType.NotSet;
1644 RemotePayload remotePayload = null;
1645 var slipstream = YesNoType.NotSet;
1646
1647 var expectedNetFx4Args = new string[] { "/q", "/norestart", "/chainingpackage" };
1648
1649 // This list lets us evaluate extension attributes *after* all core attributes
1650 // have been parsed and dealt with, regardless of authoring order.
1651 var extensionAttributes = new List<XAttribute>();
1652
1653 foreach (var attrib in node.Attributes())
1654 {
1655 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1656 {
1657 var allowed = true;
1658 switch (attrib.Name.LocalName)
1659 {
1660 case "Id":
1661 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
1662 break;
1663 case "Name":
1664 name = this.Core.GetAttributeLongFilename(sourceLineNumbers, attrib, false, true);
1665 if (!this.Core.IsValidLongFilename(name, false, true))
1666 {
1667 this.Core.Write(ErrorMessages.IllegalLongFilename(sourceLineNumbers, node.Name.LocalName, "Name", name));
1668 }
1669 break;
1670 case "SourceFile":
1671 sourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1672 break;
1673 case "DownloadUrl":
1674 downloadUrl = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1675 break;
1676 case "After":
1677 after = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1678 break;
1679 case "InstallCondition":
1680 installCondition = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1681 break;
1682 case "Cache":
1683 cache = this.Core.GetAttributeYesNoAlwaysValue(sourceLineNumbers, attrib);
1684 break;
1685 case "CacheId":
1686 cacheId = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1687 break;
1688 case "Description":
1689 description = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1690 break;
1691 case "DisplayName":
1692 displayName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1693 break;
1694 case "DisplayInternalUI":
1695 displayInternalUI = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1696 allowed = (packageType == WixBundlePackageType.Msi || packageType == WixBundlePackageType.Msp);
1697 break;
1698 case "EnableFeatureSelection":
1699 enableFeatureSelection = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1700 allowed = (packageType == WixBundlePackageType.Msi);
1701 break;
1702 case "ForcePerMachine":
1703 forcePerMachine = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1704 allowed = (packageType == WixBundlePackageType.Msi);
1705 break;
1706 case "LogPathVariable":
1707 logPathVariable = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
1708 break;
1709 case "RollbackLogPathVariable":
1710 rollbackPathVariable = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
1711 break;
1712 case "Permanent":
1713 permanent = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1714 break;
1715 case "Visible":
1716 visible = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1717 allowed = (packageType == WixBundlePackageType.Msi);
1718 break;
1719 case "Vital":
1720 vital = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1721 break;
1722 case "InstallCommand":
1723 installCommand = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1724 allowed = (packageType == WixBundlePackageType.Exe);
1725 break;
1726 case "RepairCommand":
1727 repairCommand = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
1728 repairable = YesNoType.Yes;
1729 allowed = (packageType == WixBundlePackageType.Exe);
1730 break;
1731 case "UninstallCommand":
1732 uninstallCommand = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1733 allowed = (packageType == WixBundlePackageType.Exe);
1734 break;
1735 case "PerMachine":
1736 perMachine = this.Core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib);
1737 allowed = (packageType == WixBundlePackageType.Exe || packageType == WixBundlePackageType.Msp);
1738 break;
1739 case "DetectCondition":
1740 detectCondition = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1741 allowed = (packageType == WixBundlePackageType.Exe || packageType == WixBundlePackageType.Msu);
1742 break;
1743 case "Protocol":
1744 protocol = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1745 allowed = (packageType == WixBundlePackageType.Exe);
1746 break;
1747 case "InstallSize":
1748 installSize = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue);
1749 break;
1750 case "KB":
1751 msuKB = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1752 allowed = (packageType == WixBundlePackageType.Msu);
1753 break;
1754 case "Compressed":
1755 compressed = this.Core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib);
1756 break;
1757 case "SuppressLooseFilePayloadGeneration":
1758 this.Core.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName));
1759 suppressLooseFilePayloadGeneration = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1760 allowed = (packageType == WixBundlePackageType.Msi);
1761 break;
1762 case "EnableSignatureVerification":
1763 enableSignatureVerification = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1764 break;
1765 case "Slipstream":
1766 slipstream = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1767 allowed = (packageType == WixBundlePackageType.Msp);
1768 break;
1769 default:
1770 allowed = false;
1771 break;
1772 }
1773
1774 if (!allowed)
1775 {
1776 this.Core.UnexpectedAttribute(node, attrib);
1777 }
1778 }
1779 else
1780 {
1781 // Save the extension attributes for later...
1782 extensionAttributes.Add(attrib);
1783 }
1784 }
1785
1786 // We need to handle RemotePayload up front because it effects value of sourceFile which is used in Id generation. Id is needed by other child elements.
1787 foreach (var child in node.Elements(CompilerCore.WixNamespace + "RemotePayload"))
1788 {
1789 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
1790
1791 if (CompilerCore.WixNamespace == node.Name.Namespace && node.Name.LocalName != "ExePackage" && node.Name.LocalName != "MsuPackage")
1792 {
1793 this.Core.Write(ErrorMessages.RemotePayloadUnsupported(childSourceLineNumbers));
1794 continue;
1795 }
1796
1797 if (null != remotePayload)
1798 {
1799 this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, child.Name.LocalName));
1800 }
1801
1802 remotePayload = this.ParseRemotePayloadElement(child);
1803 }
1804
1805 if (String.IsNullOrEmpty(sourceFile))
1806 {
1807 if (String.IsNullOrEmpty(name))
1808 {
1809 this.Core.Write(ErrorMessages.ExpectedAttributesWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Name", "SourceFile"));
1810 }
1811 else if (null == remotePayload)
1812 {
1813 sourceFile = Path.Combine("SourceDir", name);
1814 }
1815 }
1816 else if (null != remotePayload)
1817 {
1818 this.Core.Write(ErrorMessages.UnexpectedElementWithAttribute(sourceLineNumbers, node.Name.LocalName, "RemotePayload", "SourceFile"));
1819 }
1820 else if (sourceFile.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal))
1821 {
1822 if (String.IsNullOrEmpty(name))
1823 {
1824 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name", "SourceFile", sourceFile));
1825 }
1826 else
1827 {
1828 sourceFile = Path.Combine(sourceFile, Path.GetFileName(name));
1829 }
1830 }
1831
1832 if (null == downloadUrl && null != remotePayload)
1833 {
1834 this.Core.Write(ErrorMessages.ExpectedAttributeWithElement(sourceLineNumbers, node.Name.LocalName, "DownloadUrl", "RemotePayload"));
1835 }
1836
1837 if (YesNoDefaultType.No != compressed && null != remotePayload)
1838 {
1839 compressed = YesNoDefaultType.No;
1840 this.Core.Write(WarningMessages.RemotePayloadsMustNotAlsoBeCompressed(sourceLineNumbers, node.Name.LocalName));
1841 }
1842
1843 if (null == id)
1844 {
1845 if (!String.IsNullOrEmpty(name))
1846 {
1847 id = this.Core.CreateIdentifierFromFilename(Path.GetFileName(name));
1848 }
1849 else if (!String.IsNullOrEmpty(sourceFile))
1850 {
1851 id = this.Core.CreateIdentifierFromFilename(Path.GetFileName(sourceFile));
1852 }
1853
1854 if (null == id)
1855 {
1856 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
1857 id = Identifier.Invalid;
1858 }
1859 else if (!Common.IsIdentifier(id.Id))
1860 {
1861 this.Core.Write(ErrorMessages.IllegalIdentifier(sourceLineNumbers, node.Name.LocalName, "Id", id.Id));
1862 }
1863 }
1864
1865 if (null == logPathVariable)
1866 {
1867 logPathVariable = String.Concat("WixBundleLog_", id.Id);
1868 }
1869
1870 if (null == rollbackPathVariable)
1871 {
1872 rollbackPathVariable = String.Concat("WixBundleRollbackLog_", id.Id);
1873 }
1874
1875 if (!String.IsNullOrEmpty(protocol) && !protocol.Equals("burn", StringComparison.Ordinal) && !protocol.Equals("netfx4", StringComparison.Ordinal) && !protocol.Equals("none", StringComparison.Ordinal))
1876 {
1877 this.Core.Write(ErrorMessages.IllegalAttributeValueWithLegalList(sourceLineNumbers, node.Name.LocalName, "Protocol", protocol, "none, burn, netfx4"));
1878 }
1879
1880 if (!String.IsNullOrEmpty(protocol) && protocol.Equals("netfx4", StringComparison.Ordinal))
1881 {
1882 foreach (var expectedArgument in expectedNetFx4Args)
1883 {
1884 if (null == installCommand || -1 == installCommand.IndexOf(expectedArgument, StringComparison.OrdinalIgnoreCase))
1885 {
1886 this.Core.Write(WarningMessages.AttributeShouldContain(sourceLineNumbers, node.Name.LocalName, "InstallCommand", installCommand, expectedArgument, "Protocol", "netfx4"));
1887 }
1888
1889 if (null == repairCommand || -1 == repairCommand.IndexOf(expectedArgument, StringComparison.OrdinalIgnoreCase))
1890 {
1891 this.Core.Write(WarningMessages.AttributeShouldContain(sourceLineNumbers, node.Name.LocalName, "RepairCommand", repairCommand, expectedArgument, "Protocol", "netfx4"));
1892 }
1893
1894 if (null == uninstallCommand || -1 == uninstallCommand.IndexOf(expectedArgument, StringComparison.OrdinalIgnoreCase))
1895 {
1896 this.Core.Write(WarningMessages.AttributeShouldContain(sourceLineNumbers, node.Name.LocalName, "UninstallCommand", uninstallCommand, expectedArgument, "Protocol", "netfx4"));
1897 }
1898 }
1899 }
1900
1901 // Only set default scope for EXEs and MSPs if not already set.
1902 if ((WixBundlePackageType.Exe == packageType || WixBundlePackageType.Msp == packageType) && YesNoDefaultType.NotSet == perMachine)
1903 {
1904 perMachine = YesNoDefaultType.Default;
1905 }
1906
1907 // Now that the package ID is known, we can parse the extension attributes...
1908 var contextValues = new Dictionary<string, string>() { { "PackageId", id.Id } };
1909 foreach (var attribute in extensionAttributes)
1910 {
1911 this.Core.ParseExtensionAttribute(node, attribute, contextValues);
1912 }
1913
1914 foreach (var child in node.Elements())
1915 {
1916 if (CompilerCore.WixNamespace == child.Name.Namespace)
1917 {
1918 var allowed = true;
1919 switch (child.Name.LocalName)
1920 {
1921 case "SlipstreamMsp":
1922 allowed = (packageType == WixBundlePackageType.Msi);
1923 if (allowed)
1924 {
1925 this.ParseSlipstreamMspElement(child, id.Id);
1926 }
1927 break;
1928 case "MsiProperty":
1929 allowed = (packageType == WixBundlePackageType.Msi || packageType == WixBundlePackageType.Msp);
1930 if (allowed)
1931 {
1932 this.ParseMsiPropertyElement(child, id.Id);
1933 }
1934 break;
1935 case "Payload":
1936 this.ParsePayloadElement(child, ComplexReferenceParentType.Package, id.Id, ComplexReferenceChildType.Unknown, null);
1937 break;
1938 case "PayloadGroupRef":
1939 this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Package, id.Id, ComplexReferenceChildType.Unknown, null);
1940 break;
1941 case "ExitCode":
1942 allowed = (packageType == WixBundlePackageType.Exe);
1943 if (allowed)
1944 {
1945 this.ParseExitCodeElement(child, id.Id);
1946 }
1947 break;
1948 case "CommandLine":
1949 allowed = (packageType == WixBundlePackageType.Exe);
1950 if (allowed)
1951 {
1952 this.ParseCommandLineElement(child, id.Id);
1953 }
1954 break;
1955 case "RemotePayload":
1956 // Handled previously
1957 break;
1958 default:
1959 allowed = false;
1960 break;
1961 }
1962
1963 if (!allowed)
1964 {
1965 this.Core.UnexpectedElement(node, child);
1966 }
1967 }
1968 else
1969 {
1970 var context = new Dictionary<string, string>() { { "Id", id.Id } };
1971 this.Core.ParseExtensionElement(node, child, context);
1972 }
1973 }
1974
1975 if (!this.Core.EncounteredError)
1976 {
1977 // We create the package contents as a payload with this package as the parent
1978 this.CreatePayloadRow(sourceLineNumbers, id, name, sourceFile, downloadUrl, ComplexReferenceParentType.Package, id.Id,
1979 ComplexReferenceChildType.Unknown, null, compressed, enableSignatureVerification, displayName, description, remotePayload);
1980
1981 var chainItemRow = (WixChainItemTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixChainItem, id);
1982
1983 WixBundlePackageAttributes attributes = 0;
1984 attributes |= (YesNoType.Yes == permanent) ? WixBundlePackageAttributes.Permanent : 0;
1985 attributes |= (YesNoType.Yes == visible) ? WixBundlePackageAttributes.Visible : 0;
1986
1987 var chainPackageRow = (WixBundlePackageTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundlePackage, id);
1988 chainPackageRow.Type = packageType;
1989 chainPackageRow.Payload_ = id.Id;
1990 chainPackageRow.Attributes = attributes;
1991
1992 chainPackageRow.InstallCondition = installCondition;
1993
1994 if (YesNoAlwaysType.NotSet != cache)
1995 {
1996 chainPackageRow.Cache = cache;
1997 }
1998
1999 chainPackageRow.CacheId = cacheId;
2000
2001 if (YesNoType.NotSet != vital)
2002 {
2003 chainPackageRow.Vital = (vital == YesNoType.Yes);
2004 }
2005
2006 if (YesNoDefaultType.NotSet != perMachine)
2007 {
2008 chainPackageRow.PerMachine = perMachine;
2009 }
2010
2011 chainPackageRow.LogPathVariable = logPathVariable;
2012 chainPackageRow.RollbackLogPathVariable = rollbackPathVariable;
2013
2014 if (CompilerConstants.IntegerNotSet != installSize)
2015 {
2016 chainPackageRow.InstallSize = installSize;
2017 }
2018
2019 switch (packageType)
2020 {
2021 case WixBundlePackageType.Exe:
2022 WixBundleExePackageAttributes exeAttributes = 0;
2023 exeAttributes |= (YesNoType.Yes == repairable) ? WixBundleExePackageAttributes.Repairable : 0;
2024
2025 var exeRow = (WixBundleExePackageTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleExePackage, id);
2026 exeRow.Attributes = exeAttributes;
2027 exeRow.DetectCondition = detectCondition;
2028 exeRow.InstallCommand = installCommand;
2029 exeRow.RepairCommand = repairCommand;
2030 exeRow.UninstallCommand = uninstallCommand;
2031 exeRow.ExeProtocol = protocol;
2032 break;
2033
2034 case WixBundlePackageType.Msi:
2035 WixBundleMsiPackageAttributes msiAttributes = 0;
2036 msiAttributes |= (YesNoType.Yes == displayInternalUI) ? WixBundleMsiPackageAttributes.DisplayInternalUI : 0;
2037 msiAttributes |= (YesNoType.Yes == enableFeatureSelection) ? WixBundleMsiPackageAttributes.EnableFeatureSelection : 0;
2038 msiAttributes |= (YesNoType.Yes == forcePerMachine) ? WixBundleMsiPackageAttributes.ForcePerMachine : 0;
2039 msiAttributes |= (YesNoType.Yes == suppressLooseFilePayloadGeneration) ? WixBundleMsiPackageAttributes.SuppressLooseFilePayloadGeneration : 0;
2040
2041 var msiRow = (WixBundleMsiPackageTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleMsiPackage, id);
2042 msiRow.Attributes = msiAttributes;
2043 break;
2044
2045 case WixBundlePackageType.Msp:
2046 WixBundleMspPackageAttributes mspAttributes = 0;
2047 mspAttributes |= (YesNoType.Yes == displayInternalUI) ? WixBundleMspPackageAttributes.DisplayInternalUI : 0;
2048 mspAttributes |= (YesNoType.Yes == slipstream) ? WixBundleMspPackageAttributes.Slipstream : 0;
2049
2050 var mspRow = (WixBundleMspPackageTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleMspPackage, id);
2051 mspRow.Attributes = mspAttributes;
2052 break;
2053
2054 case WixBundlePackageType.Msu:
2055 var msuRow = (WixBundleMsuPackageTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleMsuPackage, id);
2056 msuRow.DetectCondition = detectCondition;
2057 msuRow.MsuKB = msuKB;
2058 break;
2059 }
2060
2061 this.CreateChainPackageMetaRows(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.Package, id.Id, previousType, previousId, after);
2062 }
2063
2064 return id.Id;
2065 }
2066
2067 /// <summary>
2068 /// Parse CommandLine element.
2069 /// </summary>
2070 /// <param name="node">Element to parse</param>
2071 private void ParseCommandLineElement(XElement node, string packageId)
2072 {
2073 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
2074 string installArgument = null;
2075 string uninstallArgument = null;
2076 string repairArgument = null;
2077 string condition = null;
2078
2079 foreach (var attrib in node.Attributes())
2080 {
2081 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
2082 {
2083 switch (attrib.Name.LocalName)
2084 {
2085 case "InstallArgument":
2086 installArgument = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2087 break;
2088 case "UninstallArgument":
2089 uninstallArgument = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2090 break;
2091 case "RepairArgument":
2092 repairArgument = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2093 break;
2094 case "Condition":
2095 condition = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2096 break;
2097 default:
2098 this.Core.UnexpectedAttribute(node, attrib);
2099 break;
2100 }
2101 }
2102 else
2103 {
2104 this.Core.ParseExtensionAttribute(node, attrib);
2105 }
2106 }
2107
2108 if (String.IsNullOrEmpty(condition))
2109 {
2110 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Condition"));
2111 }
2112
2113 this.Core.ParseForExtensionElements(node);
2114
2115 if (!this.Core.EncounteredError)
2116 {
2117 var row = (WixBundlePackageCommandLineTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundlePackageCommandLine);
2118 row.WixBundlePackage_ = packageId;
2119 row.InstallArgument = installArgument;
2120 row.UninstallArgument = uninstallArgument;
2121 row.RepairArgument = repairArgument;
2122 row.Condition = condition;
2123 }
2124 }
2125
2126 /// <summary>
2127 /// Parse PackageGroup element.
2128 /// </summary>
2129 /// <param name="node">Element to parse</param>
2130 private void ParsePackageGroupElement(XElement node)
2131 {
2132 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
2133 Identifier id = null;
2134
2135 foreach (var attrib in node.Attributes())
2136 {
2137 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
2138 {
2139 switch (attrib.Name.LocalName)
2140 {
2141 case "Id":
2142 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
2143 break;
2144 default:
2145 this.Core.UnexpectedAttribute(node, attrib);
2146 break;
2147 }
2148 }
2149 else
2150 {
2151 this.Core.ParseExtensionAttribute(node, attrib);
2152 }
2153 }
2154
2155 if (null == id)
2156 {
2157 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
2158 id = Identifier.Invalid;
2159 }
2160
2161 var previousType = ComplexReferenceChildType.Unknown;
2162 string previousId = null;
2163 foreach (var child in node.Elements())
2164 {
2165 if (CompilerCore.WixNamespace == child.Name.Namespace)
2166 {
2167 switch (child.Name.LocalName)
2168 {
2169 case "MsiPackage":
2170 previousId = this.ParseMsiPackageElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId);
2171 previousType = ComplexReferenceChildType.Package;
2172 break;
2173 case "MspPackage":
2174 previousId = this.ParseMspPackageElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId);
2175 previousType = ComplexReferenceChildType.Package;
2176 break;
2177 case "MsuPackage":
2178 previousId = this.ParseMsuPackageElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId);
2179 previousType = ComplexReferenceChildType.Package;
2180 break;
2181 case "ExePackage":
2182 previousId = this.ParseExePackageElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId);
2183 previousType = ComplexReferenceChildType.Package;
2184 break;
2185 case "RollbackBoundary":
2186 previousId = this.ParseRollbackBoundaryElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId);
2187 previousType = ComplexReferenceChildType.Package;
2188 break;
2189 case "PackageGroupRef":
2190 previousId = this.ParsePackageGroupRefElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId);
2191 previousType = ComplexReferenceChildType.PackageGroup;
2192 break;
2193 default:
2194 this.Core.UnexpectedElement(node, child);
2195 break;
2196 }
2197 }
2198 else
2199 {
2200 this.Core.ParseExtensionElement(node, child);
2201 }
2202 }
2203
2204
2205 if (!this.Core.EncounteredError)
2206 {
2207 this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundlePackageGroup, id);
2208 }
2209 }
2210
2211 /// <summary>
2212 /// Parses a package group reference element.
2213 /// </summary>
2214 /// <param name="node">Element to parse.</param>
2215 /// <param name="parentType">ComplexReferenceParentType of parent element (Unknown or PackageGroup).</param>
2216 /// <param name="parentId">Identifier of parent element.</param>
2217 /// <returns>Identifier for package group element.</rereturns>
2218 private string ParsePackageGroupRefElement(XElement node, ComplexReferenceParentType parentType, string parentId)
2219 {
2220 return this.ParsePackageGroupRefElement(node, parentType, parentId, ComplexReferenceChildType.Unknown, null);
2221 }
2222
2223 /// <summary>
2224 /// Parses a package group reference element.
2225 /// </summary>
2226 /// <param name="node">Element to parse.</param>
2227 /// <param name="parentType">ComplexReferenceParentType of parent element (Unknown or PackageGroup).</param>
2228 /// <param name="parentId">Identifier of parent element.</param>
2229 /// <param name="parentType">ComplexReferenceParentType of previous element (Unknown, Package, or PackageGroup).</param>
2230 /// <param name="parentId">Identifier of parent element.</param>
2231 /// <returns>Identifier for package group element.</rereturns>
2232 private string ParsePackageGroupRefElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
2233 {
2234 Debug.Assert(ComplexReferenceParentType.Unknown == parentType || ComplexReferenceParentType.PackageGroup == parentType || ComplexReferenceParentType.Container == parentType);
2235 Debug.Assert(ComplexReferenceChildType.Unknown == previousType || ComplexReferenceChildType.PackageGroup == previousType || ComplexReferenceChildType.Package == previousType);
2236
2237 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
2238 string id = null;
2239 string after = null;
2240
2241 foreach (var attrib in node.Attributes())
2242 {
2243 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
2244 {
2245 switch (attrib.Name.LocalName)
2246 {
2247 case "Id":
2248 id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
2249 this.Core.CreateSimpleReference(sourceLineNumbers, "WixBundlePackageGroup", id);
2250 break;
2251 case "After":
2252 after = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
2253 break;
2254 default:
2255 this.Core.UnexpectedAttribute(node, attrib);
2256 break;
2257 }
2258 }
2259 else
2260 {
2261 this.Core.ParseExtensionAttribute(node, attrib);
2262
2263 }
2264 }
2265
2266 if (null == id)
2267 {
2268 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
2269 }
2270
2271 if (null != after && ComplexReferenceParentType.Container == parentType)
2272 {
2273 this.Core.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, "After", parentId));
2274 }
2275
2276 this.Core.ParseForExtensionElements(node);
2277
2278 if (ComplexReferenceParentType.Container == parentType)
2279 {
2280 this.Core.CreateWixGroupRow(sourceLineNumbers, ComplexReferenceParentType.Container, parentId, ComplexReferenceChildType.PackageGroup, id);
2281 }
2282 else
2283 {
2284 this.CreateChainPackageMetaRows(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.PackageGroup, id, previousType, previousId, after);
2285 }
2286
2287 return id;
2288 }
2289
2290 /// <summary>
2291 /// Creates rollback boundary.
2292 /// </summary>
2293 /// <param name="sourceLineNumbers">Source line numbers.</param>
2294 /// <param name="id">Identifier for the rollback boundary.</param>
2295 /// <param name="vital">Indicates whether the rollback boundary is vital or not.</param>
2296 /// <param name="parentType">Type of parent group.</param>
2297 /// <param name="parentId">Identifier of parent group.</param>
2298 /// <param name="previousType">Type of previous item, if any.</param>
2299 /// <param name="previousId">Identifier of previous item, if any.</param>
2300 private void CreateRollbackBoundary(SourceLineNumber sourceLineNumbers, Identifier id, YesNoType vital, YesNoType transaction, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
2301 {
2302 var row = (WixChainItemTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixChainItem, id);
2303
2304 var rollbackBoundary = (WixBundleRollbackBoundaryTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleRollbackBoundary, id);
2305
2306 if (YesNoType.NotSet != vital)
2307 {
2308 rollbackBoundary.Vital = (vital == YesNoType.Yes);
2309 }
2310 if (YesNoType.NotSet != transaction)
2311 {
2312 rollbackBoundary.Transaction = (transaction == YesNoType.Yes);
2313 }
2314
2315 this.CreateChainPackageMetaRows(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.Package, id.Id, previousType, previousId, null);
2316 }
2317
2318 /// <summary>
2319 /// Creates group and ordering information for packages
2320 /// </summary>
2321 /// <param name="sourceLineNumbers">Source line numbers.</param>
2322 /// <param name="parentType">Type of parent group, if known.</param>
2323 /// <param name="parentId">Identifier of parent group, if known.</param>
2324 /// <param name="type">Type of this item.</param>
2325 /// <param name="id">Identifier for this item.</param>
2326 /// <param name="previousType">Type of previous item, if known.</param>
2327 /// <param name="previousId">Identifier of previous item, if known</param>
2328 /// <param name="afterId">Identifier of explicit 'After' attribute, if given.</param>
2329 private void CreateChainPackageMetaRows(SourceLineNumber sourceLineNumbers,
2330 ComplexReferenceParentType parentType, string parentId,
2331 ComplexReferenceChildType type, string id,
2332 ComplexReferenceChildType previousType, string previousId, string afterId)
2333 {
2334 // If there's an explicit 'After' attribute, it overrides the inferred previous item.
2335 if (null != afterId)
2336 {
2337 previousType = ComplexReferenceChildType.Package;
2338 previousId = afterId;
2339 }
2340
2341 this.CreateGroupAndOrderingRows(sourceLineNumbers, parentType, parentId, type, id, previousType, previousId);
2342 }
2343
2344 // TODO: Should we define our own enum for this, just to ensure there's no "cross-contamination"?
2345 // TODO: Also, we could potentially include an 'Attributes' field to track things like
2346 // 'before' vs. 'after', and explicit vs. inferred dependencies.
2347 private void CreateWixOrderingRow(SourceLineNumber sourceLineNumbers,
2348 ComplexReferenceChildType itemType, string itemId,
2349 ComplexReferenceChildType dependsOnType, string dependsOnId)
2350 {
2351 if (!this.Core.EncounteredError)
2352 {
2353 var row = this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixOrdering);
2354 row.Set(0, itemType.ToString());
2355 row.Set(1, itemId);
2356 row.Set(2, dependsOnType.ToString());
2357 row.Set(3, dependsOnId);
2358 }
2359 }
2360
2361 /// <summary>
2362 /// Parse MsiProperty element
2363 /// </summary>
2364 /// <param name="node">Element to parse</param>
2365 /// <param name="packageId">Id of parent element</param>
2366 private void ParseMsiPropertyElement(XElement node, string packageId)
2367 {
2368 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
2369 string name = null;
2370 string value = null;
2371 string condition = null;
2372
2373 foreach (var attrib in node.Attributes())
2374 {
2375 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
2376 {
2377 switch (attrib.Name.LocalName)
2378 {
2379 case "Name":
2380 name = this.Core.GetAttributeMsiPropertyNameValue(sourceLineNumbers, attrib);
2381 break;
2382 case "Value":
2383 value = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2384 break;
2385 case "Condition":
2386 condition = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2387 break;
2388 default:
2389 this.Core.UnexpectedAttribute(node, attrib);
2390 break;
2391 }
2392 }
2393 else
2394 {
2395 this.Core.ParseExtensionAttribute(node, attrib);
2396 }
2397 }
2398
2399 if (null == name)
2400 {
2401 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
2402 }
2403
2404 if (null == value)
2405 {
2406 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value"));
2407 }
2408
2409 this.Core.ParseForExtensionElements(node);
2410
2411 if (!this.Core.EncounteredError)
2412 {
2413 var row = (WixBundleMsiPropertyTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleMsiProperty);
2414 row.WixBundlePackage_ = packageId;
2415 row.Name = name;
2416 row.Value = value;
2417
2418 if (!String.IsNullOrEmpty(condition))
2419 {
2420 row.Condition = condition;
2421 }
2422 }
2423 }
2424
2425 /// <summary>
2426 /// Parse SlipstreamMsp element
2427 /// </summary>
2428 /// <param name="node">Element to parse</param>
2429 /// <param name="packageId">Id of parent element</param>
2430 private void ParseSlipstreamMspElement(XElement node, string packageId)
2431 {
2432 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
2433 string id = null;
2434
2435 foreach (var attrib in node.Attributes())
2436 {
2437 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
2438 {
2439 switch (attrib.Name.LocalName)
2440 {
2441 case "Id":
2442 id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
2443 this.Core.CreateSimpleReference(sourceLineNumbers, "WixBundlePackage", id);
2444 break;
2445 default:
2446 this.Core.UnexpectedAttribute(node, attrib);
2447 break;
2448 }
2449 }
2450 else
2451 {
2452 this.Core.ParseExtensionAttribute(node, attrib);
2453 }
2454 }
2455
2456 if (null == id)
2457 {
2458 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
2459 }
2460
2461 this.Core.ParseForExtensionElements(node);
2462
2463 if (!this.Core.EncounteredError)
2464 {
2465 var row = (WixBundleSlipstreamMspTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleSlipstreamMsp);
2466 row.WixBundlePackage_ = packageId;
2467 row.WixBundlePackage_Msp = id;
2468 }
2469 }
2470
2471 /// <summary>
2472 /// Parse RelatedBundle element
2473 /// </summary>
2474 /// <param name="node">Element to parse</param>
2475 private void ParseRelatedBundleElement(XElement node)
2476 {
2477 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
2478 string id = null;
2479 var actionType = RelatedBundleActionType.Detect;
2480
2481 foreach (var attrib in node.Attributes())
2482 {
2483 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
2484 {
2485 switch (attrib.Name.LocalName)
2486 {
2487 case "Id":
2488 id = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
2489 break;
2490 case "Action":
2491 var action = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2492 switch (action)
2493 {
2494 case "Detect":
2495 case "detect":
2496 actionType = RelatedBundleActionType.Detect;
2497 break;
2498 case "Upgrade":
2499 case "upgrade":
2500 actionType = RelatedBundleActionType.Upgrade;
2501 break;
2502 case "Addon":
2503 case "addon":
2504 actionType = RelatedBundleActionType.Addon;
2505 break;
2506 case "Patch":
2507 case "patch":
2508 actionType = RelatedBundleActionType.Patch;
2509 break;
2510 case "":
2511 break;
2512 default:
2513 this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Action", action, "Detect", "Upgrade", "Addon", "Patch"));
2514 break;
2515 }
2516 break;
2517 default:
2518 this.Core.UnexpectedAttribute(node, attrib);
2519 break;
2520 }
2521 }
2522 else
2523 {
2524 this.Core.ParseExtensionAttribute(node, attrib);
2525 }
2526 }
2527
2528 if (null == id)
2529 {
2530 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
2531 }
2532
2533 this.Core.ParseForExtensionElements(node);
2534
2535 if (!this.Core.EncounteredError)
2536 {
2537 var tuple = new WixRelatedBundleTuple(sourceLineNumbers)
2538 {
2539 BundleId = id,
2540 Action = actionType,
2541 };
2542
2543 this.Core.AddTuple(tuple);
2544 //var row = this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixRelatedBundle);
2545 //row.Set(0, id);
2546 //row.Set(1, (int)actionType);
2547 }
2548 }
2549
2550 /// <summary>
2551 /// Parse Update element
2552 /// </summary>
2553 /// <param name="node">Element to parse</param>
2554 private void ParseUpdateElement(XElement node)
2555 {
2556 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
2557 string location = null;
2558
2559 foreach (var attrib in node.Attributes())
2560 {
2561 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
2562 {
2563 switch (attrib.Name.LocalName)
2564 {
2565 case "Location":
2566 location = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2567 break;
2568 default:
2569 this.Core.UnexpectedAttribute(node, attrib);
2570 break;
2571 }
2572 }
2573 else
2574 {
2575 this.Core.ParseExtensionAttribute(node, attrib);
2576 }
2577 }
2578
2579 if (null == location)
2580 {
2581 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Location"));
2582 }
2583
2584 this.Core.ParseForExtensionElements(node);
2585
2586 if (!this.Core.EncounteredError)
2587 {
2588 var row = this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleUpdate);
2589 row.Set(0, location);
2590 }
2591 }
2592
2593 /// <summary>
2594 /// Parse Variable element
2595 /// </summary>
2596 /// <param name="node">Element to parse</param>
2597 private void ParseVariableElement(XElement node)
2598 {
2599 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
2600 var hidden = false;
2601 string name = null;
2602 var persisted = false;
2603 string value = null;
2604 string type = null;
2605
2606 foreach (var attrib in node.Attributes())
2607 {
2608 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
2609 {
2610 switch (attrib.Name.LocalName)
2611 {
2612 case "Hidden":
2613 if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
2614 {
2615 hidden = true;
2616 }
2617 break;
2618 case "Name":
2619 name = this.Core.GetAttributeBundleVariableValue(sourceLineNumbers, attrib);
2620 break;
2621 case "Persisted":
2622 if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
2623 {
2624 persisted = true;
2625 }
2626 break;
2627 case "Value":
2628 value = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
2629 break;
2630 case "Type":
2631 type = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2632 break;
2633 default:
2634 this.Core.UnexpectedAttribute(node, attrib);
2635 break;
2636 }
2637 }
2638 else
2639 {
2640 this.Core.ParseExtensionAttribute(node, attrib);
2641 }
2642 }
2643
2644 if (null == name)
2645 {
2646 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
2647 }
2648 else if (name.StartsWith("Wix", StringComparison.OrdinalIgnoreCase))
2649 {
2650 this.Core.Write(ErrorMessages.ReservedNamespaceViolation(sourceLineNumbers, node.Name.LocalName, "Name", "Wix"));
2651 }
2652
2653 if (null == type && null != value)
2654 {
2655 // Infer the type from the current value...
2656 if (value.StartsWith("v", StringComparison.OrdinalIgnoreCase))
2657 {
2658 // Version constructor does not support simple "v#" syntax so check to see if the value is
2659 // non-negative real quick.
2660 if (Int32.TryParse(value.Substring(1), NumberStyles.None, CultureInfo.InvariantCulture.NumberFormat, out var number))
2661 {
2662 type = "version";
2663 }
2664 else
2665 {
2666 // Sadly, Version doesn't have a TryParse() method until .NET 4, so we have to try/catch to see if it parses.
2667 try
2668 {
2669 var version = new Version(value.Substring(1));
2670 type = "version";
2671 }
2672 catch (Exception)
2673 {
2674 }
2675 }
2676 }
2677
2678 // Not a version, check for numeric.
2679 if (null == type)
2680 {
2681 if (Int64.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture.NumberFormat, out var number))
2682 {
2683 type = "numeric";
2684 }
2685 else
2686 {
2687 type = "string";
2688 }
2689 }
2690 }
2691
2692 if (null == value && null != type)
2693 {
2694 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, "Variable", "Value", "Type"));
2695 }
2696
2697 this.Core.ParseForExtensionElements(node);
2698
2699 if (!this.Core.EncounteredError)
2700 {
2701 var row = (WixBundleVariableTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleVariable);
2702 row.WixBundleVariable = name;
2703 row.Value = value;
2704 row.Type = type;
2705 row.Hidden = hidden;
2706 row.Persisted = persisted;
2707 }
2708 }
2709
2710 private class RemotePayload
2711 {
2712 public string CertificatePublicKey { get; set; }
2713
2714 public string CertificateThumbprint { get; set; }
2715
2716 public string Description { get; set; }
2717
2718 public string Hash { get; set; }
2719
2720 public string ProductName { get; set; }
2721
2722 public int Size { get; set; }
2723
2724 public string Version { get; set; }
2725 }
2726 }
2727}