main
cs 3,804 lines 172 KB
Raw
1 // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 namespace WixToolset.Core
4 {
5 using System;
6 using System.Collections.Generic;
7 using System.Diagnostics;
8 using System.Globalization;
9 using System.Linq;
10 using System.Xml.Linq;
11 using WixToolset.Data;
12 using WixToolset.Data.Burn;
13 using WixToolset.Data.Symbols;
14 using WixToolset.Extensibility.Data;
15
16 /// <summary>
17 /// Compiler of the WiX toolset.
18 /// </summary>
19 internal partial class Compiler : ICompiler
20 {
21 private static readonly Identifier BurnUXContainerId = new Identifier(AccessModifier.Section, BurnConstants.BurnUXContainerName);
22 private static readonly Identifier BurnDefaultAttachedContainerId = new Identifier(AccessModifier.Section, BurnConstants.BurnDefaultAttachedContainerName);
23 private static readonly Identifier BundleLayoutOnlyPayloads = new Identifier(AccessModifier.Section, BurnConstants.BundleLayoutOnlyPayloadsName);
24
25 /// <summary>
26 /// Parses an ApprovedExeForElevation element.
27 /// </summary>
28 /// <param name="node">Element to parse</param>
29 private void ParseApprovedExeForElevation(XElement node)
30 {
31 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
32 Identifier id = null;
33 string key = null;
34 string valueName = null;
35 var win64 = this.Context.IsCurrentPlatform64Bit;
36
37 foreach (var attrib in node.Attributes())
38 {
39 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
40 {
41 switch (attrib.Name.LocalName)
42 {
43 case "Id":
44 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
45 break;
46 case "Bitness":
47 var bitnessValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
48 switch (bitnessValue)
49 {
50 case "always32":
51 win64 = false;
52 break;
53 case "always64":
54 win64 = true;
55 break;
56 case "default":
57 case "":
58 break;
59 default:
60 this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, bitnessValue, "default", "always32", "always64"));
61 break;
62 }
63 break;
64 case "Key":
65 key = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
66 break;
67 case "Value":
68 valueName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
69 break;
70 default:
71 this.Core.UnexpectedAttribute(node, attrib);
72 break;
73 }
74 }
75 else
76 {
77 this.Core.ParseExtensionAttribute(node, attrib);
78 }
79 }
80
81 if (null == id)
82 {
83 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
84 }
85
86 if (null == key)
87 {
88 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key"));
89 }
90
91 var attributes = WixApprovedExeForElevationAttributes.None;
92
93 if (win64)
94 {
95 attributes |= WixApprovedExeForElevationAttributes.Win64;
96 }
97
98 this.Core.ParseForExtensionElements(node);
99
100 if (!this.Core.EncounteredError)
101 {
102 this.Core.AddSymbol(new WixApprovedExeForElevationSymbol(sourceLineNumbers, id)
103 {
104 Key = key,
105 ValueName = valueName,
106 Attributes = attributes,
107 });
108 }
109 }
110
111 /// <summary>
112 /// Parses a Bundle element.
113 /// </summary>
114 /// <param name="node">Element to parse</param>
115 private void ParseBundleElement(XElement node)
116 {
117 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
118 string copyright = null;
119 string aboutUrl = null;
120 var modifyType = WixBundleModifyType.Allowed;
121 var compressed = YesNoDefaultType.Default;
122 WixBundleAttributes attributes = 0;
123 string helpTelephone = null;
124 string helpUrl = null;
125 string inProgressName = null;
126 string manufacturer = null;
127 string name = null;
128 string tag = null;
129 string updateUrl = null;
130 string upgradeCode = null;
131 string version = null;
132 string condition = null;
133 string parentName = null;
134
135 string fileSystemSafeBundleName = null;
136 string logVariablePrefixAndExtension;
137 string iconSourceFile = null;
138 string splashScreenSourceFile = null;
139
140 // Process only standard attributes until the active section is initialized.
141 foreach (var attrib in node.Attributes())
142 {
143 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
144 {
145 switch (attrib.Name.LocalName)
146 {
147 case "AboutUrl":
148 aboutUrl = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
149 break;
150 case "Compressed":
151 compressed = this.Core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib);
152 break;
153 case "Condition":
154 condition = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
155 break;
156 case "Copyright":
157 copyright = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
158 break;
159 case "DisableModify":
160 var value = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
161 switch (value)
162 {
163 case "button":
164 modifyType = WixBundleModifyType.SingleChangeUninstallButton;
165 break;
166 case "yes":
167 modifyType = WixBundleModifyType.Disabled;
168 break;
169 case "no":
170 modifyType = WixBundleModifyType.Allowed;
171 break;
172 default:
173 this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, value, "button", "yes", "no"));
174 break;
175 }
176 break;
177 case "DisableRemove":
178 if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
179 {
180 attributes |= WixBundleAttributes.DisableRemove;
181 }
182 break;
183 case "HelpTelephone":
184 helpTelephone = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
185 break;
186 case "HelpUrl":
187 helpUrl = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
188 break;
189 case "Manufacturer":
190 manufacturer = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
191 break;
192 case "IconSourceFile":
193 iconSourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
194 break;
195 case "InProgressName":
196 inProgressName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
197 break;
198 case "Name":
199 name = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
200 break;
201 case "ParentName":
202 parentName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
203 break;
204 case "ProviderKey":
205 // This can't be processed until we create the section.
206 break;
207 case "SplashScreenSourceFile":
208 splashScreenSourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
209 break;
210 case "Tag":
211 tag = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
212 break;
213 case "UpdateUrl":
214 updateUrl = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
215 break;
216 case "UpgradeCode":
217 upgradeCode = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
218 break;
219 case "Version":
220 version = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib);
221 break;
222 default:
223 this.Core.UnexpectedAttribute(node, attrib);
224 break;
225 }
226 }
227 }
228
229 if (String.IsNullOrEmpty(version))
230 {
231 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version"));
232 }
233
234 if (String.IsNullOrEmpty(upgradeCode))
235 {
236 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "UpgradeCode"));
237 }
238
239 if (String.IsNullOrEmpty(copyright))
240 {
241 if (String.IsNullOrEmpty(manufacturer))
242 {
243 copyright = "Copyright (c). All rights reserved.";
244 }
245 else
246 {
247 copyright = String.Format("Copyright (c) {0}. All rights reserved.", manufacturer);
248 }
249 }
250
251 if (String.IsNullOrEmpty(name) && !String.IsNullOrEmpty(inProgressName))
252 {
253 name = inProgressName;
254 inProgressName = null;
255 }
256
257 if (String.IsNullOrEmpty(name))
258 {
259 logVariablePrefixAndExtension = String.Concat("WixBundleLog:Setup:log");
260 }
261 else
262 {
263 // Ensure only allowable path characters are in "name" (and change spaces to underscores).
264 fileSystemSafeBundleName = CompilerCore.MakeValidLongFileName(name.Replace(' ', '_'), '_');
265 logVariablePrefixAndExtension = String.Concat("WixBundleLog:", fileSystemSafeBundleName, ":log");
266 }
267
268 this.activeName = String.IsNullOrEmpty(name) ? Common.GenerateGuid() : name;
269 this.Core.CreateActiveSection(this.activeName, SectionType.Bundle, this.Context.CompilationId);
270
271 // Now that the active section is initialized, process only extension attributes and the special ProviderKey attribute.
272 foreach (var attrib in node.Attributes())
273 {
274 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
275 {
276 switch (attrib.Name.LocalName)
277 {
278 case "ProviderKey":
279 this.ParseBundleProviderKeyAttribute(sourceLineNumbers, node, attrib);
280 break;
281 // Unknown attributes were reported earlier.
282 }
283 }
284 else
285 {
286 this.Core.ParseExtensionAttribute(node, attrib);
287 }
288 }
289
290 var chainSeen = false;
291 var logSeen = false;
292 var updateSeen = false;
293
294 foreach (var child in node.Elements())
295 {
296 if (CompilerCore.WixNamespace == child.Name.Namespace)
297 {
298 switch (child.Name.LocalName)
299 {
300 case "ApprovedExeForElevation":
301 this.ParseApprovedExeForElevation(child);
302 break;
303 case "BootstrapperApplication":
304 this.ParseBootstrapperApplicationElement(child);
305 break;
306 case "BootstrapperApplicationRef":
307 this.ParseBootstrapperApplicationRefElement(child);
308 break;
309 case "BundleCustomData":
310 this.ParseBundleCustomDataElement(child);
311 break;
312 case "BundleCustomDataRef":
313 this.ParseBundleCustomDataRefElement(child);
314 break;
315 case "BundleExtension": // kept for backward compatibility
316 case "BootstrapperExtension":
317 this.ParseBootstrapperExtensionElement(child);
318 break;
319 case "BundleExtensionRef": // kept for backward compatibility
320 case "BootstrapperExtensionRef":
321 this.ParseSimpleRefElement(child, SymbolDefinitions.WixBootstrapperExtension);
322 break;
323 case "OptionalUpdateRegistration":
324 this.ParseOptionalUpdateRegistrationElement(child, manufacturer, parentName, name);
325 break;
326 case "Chain":
327 if (chainSeen)
328 {
329 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
330 this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, "Chain"));
331 }
332 this.ParseChainElement(child);
333 chainSeen = true;
334 break;
335 case "Container":
336 this.ParseContainerElement(child);
337 break;
338 case "ContainerRef":
339 this.ParseSimpleRefElement(child, SymbolDefinitions.WixBundleContainer);
340 break;
341 case "Log":
342 if (logSeen)
343 {
344 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
345 this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, "Log"));
346 }
347 logVariablePrefixAndExtension = this.ParseLogElement(child, fileSystemSafeBundleName);
348 logSeen = true;
349 break;
350 case "PayloadGroup":
351 this.ParsePayloadGroupElement(child, ComplexReferenceParentType.Layout, Compiler.BundleLayoutOnlyPayloads);
352 break;
353 case "PayloadGroupRef":
354 this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Layout, Compiler.BundleLayoutOnlyPayloads);
355 break;
356 case "RelatedBundle":
357 this.ParseRelatedBundleElement(child);
358 break;
359 case "Requires":
360 this.ParseRequiresElement(child, null);
361 break;
362 case "SetVariable":
363 this.ParseSetVariableElement(child);
364 break;
365 case "SetVariableRef":
366 this.ParseSimpleRefElement(child, SymbolDefinitions.WixSetVariable);
367 break;
368 case "SoftwareTag":
369 this.ParseBundleTagElement(child);
370 break;
371 case "Update":
372 if (updateSeen)
373 {
374 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
375 this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, "Update"));
376 }
377 this.ParseUpdateElement(child);
378 updateSeen = true;
379 break;
380 case "Variable":
381 this.ParseVariableElement(child);
382 break;
383 case "WixVariable":
384 this.ParseWixVariableElement(child);
385 break;
386 default:
387 this.Core.UnexpectedElement(node, child);
388 break;
389 }
390 }
391 else
392 {
393 this.Core.ParseExtensionElement(node, child);
394 }
395 }
396
397 if (!chainSeen)
398 {
399 this.Core.Write(ErrorMessages.ExpectedElement(sourceLineNumbers, node.Name.LocalName, "Chain"));
400 }
401
402 if (!this.Core.EncounteredError)
403 {
404 var symbol = this.Core.AddSymbol(new WixBundleSymbol(sourceLineNumbers)
405 {
406 UpgradeCode = upgradeCode,
407 Version = version,
408 Copyright = copyright,
409 DisableModify = modifyType,
410 InProgressName = inProgressName,
411 Name = name,
412 Manufacturer = manufacturer,
413 Attributes = attributes,
414 AboutUrl = aboutUrl,
415 HelpUrl = helpUrl,
416 HelpTelephone = helpTelephone,
417 UpdateUrl = updateUrl,
418 Compressed = YesNoDefaultType.Yes == compressed ? true : YesNoDefaultType.No == compressed ? (bool?)false : null,
419 IconSourceFile = new IntermediateFieldPathValue { Path = iconSourceFile },
420 SplashScreenSourceFile = new IntermediateFieldPathValue { Path = splashScreenSourceFile },
421 Condition = condition,
422 Tag = tag,
423 Platform = this.CurrentPlatform,
424 ParentName = parentName,
425 });
426
427 if (!String.IsNullOrEmpty(logVariablePrefixAndExtension))
428 {
429 var split = logVariablePrefixAndExtension.Split(':');
430 symbol.LogPathVariable = split[0];
431 symbol.LogPrefix = split[1];
432 symbol.LogExtension = split[2];
433 }
434
435 if (null != upgradeCode)
436 {
437 this.Core.AddSymbol(new WixRelatedBundleSymbol(sourceLineNumbers)
438 {
439 BundleId = upgradeCode,
440 Action = RelatedBundleActionType.Upgrade,
441 });
442 }
443
444 this.Core.AddSymbol(new WixBundleContainerSymbol(sourceLineNumbers, Compiler.BurnUXContainerId)
445 {
446 Name = "bundle-ux.cab",
447 Type = ContainerType.Attached
448 });
449
450 this.Core.AddSymbol(new WixBundleContainerSymbol(sourceLineNumbers, Compiler.BurnDefaultAttachedContainerId)
451 {
452 Name = "bundle-attached.cab",
453 Type = ContainerType.Attached,
454 });
455 }
456 }
457
458 /// <summary>
459 /// Parse a Container element.
460 /// </summary>
461 /// <param name="node">Element to parse</param>
462 /// <param name="fileSystemSafeBundleName"></param>
463 private string ParseLogElement(XElement node, string fileSystemSafeBundleName)
464 {
465 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
466 var disableLog = YesNoType.NotSet;
467 var variable = "WixBundleLog";
468 var logPrefix = fileSystemSafeBundleName ?? "Setup";
469 var logExtension = ".log";
470
471 foreach (var attrib in node.Attributes())
472 {
473 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
474 {
475 switch (attrib.Name.LocalName)
476 {
477 case "Disable":
478 disableLog = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
479 break;
480 case "PathVariable":
481 variable = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
482 break;
483 case "Prefix":
484 logPrefix = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
485 break;
486 case "Extension":
487 logExtension = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
488 break;
489 default:
490 this.Core.UnexpectedAttribute(node, attrib);
491 break;
492 }
493 }
494 else
495 {
496 this.Core.ParseExtensionAttribute(node, attrib);
497 }
498 }
499
500 if (!logExtension.StartsWith(".", StringComparison.Ordinal))
501 {
502 logExtension = String.Concat(".", logExtension);
503 }
504
505 this.Core.ParseForExtensionElements(node);
506
507 return YesNoType.Yes == disableLog ? null : String.Join(":", variable, logPrefix, logExtension);
508 }
509
510 /// <summary>
511 /// Parse a Container element.
512 /// </summary>
513 /// <param name="node">Element to parse</param>
514 private void ParseContainerElement(XElement node)
515 {
516 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
517 Identifier id = null;
518 string downloadUrl = null;
519 string name = null;
520 var type = ContainerType.Detached;
521
522 foreach (var attrib in node.Attributes())
523 {
524 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
525 {
526 switch (attrib.Name.LocalName)
527 {
528 case "Id":
529 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
530 if (id?.Id == BurnConstants.BurnUXContainerName || id?.Id == BurnConstants.BurnDefaultAttachedContainerName)
531 {
532 this.Messaging.Write(CompilerErrors.ReservedValue(sourceLineNumbers, node.Name.LocalName, "Id", id.Id));
533 }
534 break;
535 case "DownloadUrl":
536 downloadUrl = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
537 break;
538 case "Name":
539 name = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
540 break;
541 case "Type":
542 var typeString = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
543 switch (typeString)
544 {
545 case "attached":
546 type = ContainerType.Attached;
547 break;
548 case "detached":
549 type = ContainerType.Detached;
550 break;
551 default:
552 this.Core.Write(ErrorMessages.IllegalAttributeValueWithLegalList(sourceLineNumbers, node.Name.LocalName, "Type", typeString, "attached, detached"));
553 break;
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 this.Core.AddSymbol(new WixBundleContainerSymbol(sourceLineNumbers, id)
618 {
619 Name = name,
620 Type = type,
621 DownloadUrl = downloadUrl
622 });
623 }
624 }
625
626 /// <summary>
627 /// Parse the BootstrapperApplication element.
628 /// </summary>
629 /// <param name="node">Element to parse</param>
630 private void ParseBootstrapperApplicationElement(XElement node)
631 {
632 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
633 var compilerPayload = new CompilerPayload(this.Core, sourceLineNumbers, node);
634 XElement exePayloadRefNode = null;
635 Identifier exePayloadRefId = null;
636 bool? secondary = null;
637
638 foreach (var attrib in node.Attributes())
639 {
640 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
641 {
642 switch (attrib.Name.LocalName)
643 {
644 case "Id":
645 compilerPayload.ParseId(attrib);
646 break;
647
648 case "Name":
649 compilerPayload.ParseName(attrib);
650 break;
651
652 case "Secondary":
653 secondary = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib) == YesNoType.Yes;
654 break;
655
656 case "SourceFile":
657 compilerPayload.ParseSourceFile(attrib);
658 break;
659
660 default:
661 this.Core.UnexpectedAttribute(node, attrib);
662 break;
663 }
664 }
665 }
666
667 foreach (var child in node.Elements())
668 {
669 if (CompilerCore.WixNamespace == child.Name.Namespace)
670 {
671 switch (child.Name.LocalName)
672 {
673 case "BootstrapperApplicationDll":
674 if (exePayloadRefId == null)
675 {
676 exePayloadRefNode = node;
677 exePayloadRefId = this.ParseBootstrapperApplicationDllElement(child, compilerPayload.Id);
678 }
679 else
680 {
681 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
682 var exePayloadSourceLineNumbers = Preprocessor.GetSourceLineNumbers(exePayloadRefNode);
683 this.Messaging.Write(CompilerErrors.AlreadyDefinedBootstrapperApplicationSource(childSourceLineNumbers, exePayloadSourceLineNumbers, exePayloadRefNode.Name.LocalName));
684 }
685 break;
686
687 case "Payload":
688 this.ParsePayloadElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, isRemoteAllowed: false);
689 break;
690
691 case "PayloadGroupRef":
692 this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId);
693 break;
694
695 default:
696 this.Core.UnexpectedElement(node, child);
697 break;
698 }
699 }
700 else
701 {
702 var context = new Dictionary<string, string>() { { "Id", compilerPayload.Id?.Id }, { "Name", compilerPayload.Name }, { "Secondary", secondary?.ToString() }, { "Source", compilerPayload.SourceFile }, };
703 var possibleKeyPath = this.Core.ParsePossibleKeyPathExtensionElement(node, child, context);
704 if (possibleKeyPath?.Type == PossibleKeyPathType.File)
705 {
706 if (exePayloadRefNode == null)
707 {
708 exePayloadRefNode = node;
709 exePayloadRefId = possibleKeyPath.Id;
710 secondary = possibleKeyPath.Explicit;
711 }
712 else
713 {
714 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
715 var exePayloadSourceLineNumbers = Preprocessor.GetSourceLineNumbers(exePayloadRefNode);
716 this.Messaging.Write(CompilerErrors.AlreadyDefinedBootstrapperApplicationSource(childSourceLineNumbers, exePayloadSourceLineNumbers, exePayloadRefNode.Name.LocalName));
717 }
718 }
719 }
720 }
721
722 if (compilerPayload.Id == null)
723 {
724 compilerPayload.Id = exePayloadRefId ?? this.Core.CreateIdentifier("ba", compilerPayload.Name, compilerPayload.SourceFile);
725 }
726
727 if (String.IsNullOrEmpty(compilerPayload.SourceFile) && String.IsNullOrEmpty(compilerPayload.Name))
728 {
729 if (exePayloadRefId == null)
730 {
731 compilerPayload.FinishCompilingPayload(Compiler.BurnUXContainerId.Id);
732 }
733 }
734 else if (exePayloadRefId != null)
735 {
736 var exePayloadSourceLineNumbers = Preprocessor.GetSourceLineNumbers(exePayloadRefNode);
737
738 this.Messaging.Write(CompilerErrors.AlreadyDefinedBootstrapperApplicationSource(exePayloadSourceLineNumbers, sourceLineNumbers, node.Name.LocalName));
739 }
740 else
741 {
742 compilerPayload.FinishCompilingPayload(Compiler.BurnUXContainerId.Id);
743
744 var exePayload = compilerPayload.CreatePayloadSymbol(ComplexReferenceParentType.Container, Compiler.BurnUXContainerId.Id);
745
746 exePayloadRefId = exePayload?.Id;
747 }
748
749 if (!this.Core.EncounteredError)
750 {
751 this.Core.AddSymbol(new WixBootstrapperApplicationSymbol(sourceLineNumbers, compilerPayload.Id)
752 {
753 ExePayloadRef = exePayloadRefId?.Id,
754 Secondary = secondary
755 });
756 }
757 }
758
759 /// <summary>
760 /// Parse the deprecated BootstrapperApplicationDll element.
761 /// </summary>
762 /// <param name="node">Element to parse</param>
763 /// <param name="defaultId">Default bootstrapper application identifieir</param>
764 private Identifier ParseBootstrapperApplicationDllElement(XElement node, Identifier defaultId)
765 {
766 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
767 var compilerPayload = new CompilerPayload(this.Core, sourceLineNumbers, node)
768 {
769 Id = defaultId
770 };
771
772 this.Core.Write(WarningMessages.DeprecatedElement(sourceLineNumbers, node.Name.LocalName));
773
774 // This list lets us evaluate extension attributes *after* all core attributes
775 // have been parsed and dealt with, regardless of authoring order.
776 var extensionAttributes = new List<XAttribute>();
777
778 foreach (var attrib in node.Attributes())
779 {
780 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
781 {
782 switch (attrib.Name.LocalName)
783 {
784 case "Id":
785 compilerPayload.ParseId(attrib);
786 break;
787 case "Name":
788 compilerPayload.ParseName(attrib);
789 break;
790 case "SourceFile":
791 compilerPayload.ParseSourceFile(attrib);
792 break;
793 case "DpiAwareness":
794 // Ignore for backwards compatibility.
795 break;
796 default:
797 this.Core.UnexpectedAttribute(node, attrib);
798 break;
799 }
800 }
801 else
802 {
803 extensionAttributes.Add(attrib);
804 }
805 }
806
807 compilerPayload.FinishCompilingPayload(Compiler.BurnUXContainerId.Id);
808
809 // Now that the Id is known, we can parse the extension attributes.
810 var context = new Dictionary<string, string>
811 {
812 ["Id"] = compilerPayload.Id.Id,
813 };
814
815 foreach (var extensionAttribute in extensionAttributes)
816 {
817 this.Core.ParseExtensionAttribute(node, extensionAttribute, context);
818 }
819
820 this.Core.ParseForExtensionElements(node);
821
822 if (!this.Core.EncounteredError)
823 {
824 compilerPayload.CreatePayloadSymbol(ComplexReferenceParentType.Container, Compiler.BurnUXContainerId.Id);
825 }
826
827 return compilerPayload.Id;
828 }
829
830 /// <summary>
831 /// Parse the BootstrapperApplicationRef element.
832 /// </summary>
833 /// <param name="node">Element to parse</param>
834 private void ParseBootstrapperApplicationRefElement(XElement node)
835 {
836 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
837 string id = null;
838
839 foreach (var attrib in node.Attributes())
840 {
841 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
842 {
843 switch (attrib.Name.LocalName)
844 {
845 case "Id":
846 id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
847 break;
848 default:
849 this.Core.UnexpectedAttribute(node, attrib);
850 break;
851 }
852 }
853 else
854 {
855 this.Core.ParseExtensionAttribute(node, attrib);
856 }
857 }
858
859 foreach (var child in node.Elements())
860 {
861 if (CompilerCore.WixNamespace == child.Name.Namespace)
862 {
863 switch (child.Name.LocalName)
864 {
865 case "Payload":
866 this.ParsePayloadElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, isRemoteAllowed: false);
867 break;
868 case "PayloadGroupRef":
869 this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId);
870 break;
871 default:
872 this.Core.UnexpectedElement(node, child);
873 break;
874 }
875 }
876 else
877 {
878 this.Core.ParseExtensionElement(node, child);
879 }
880 }
881
882 if (String.IsNullOrEmpty(id))
883 {
884 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
885 }
886 else
887 {
888 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.WixBootstrapperApplication, id);
889 }
890 }
891
892 /// <summary>
893 /// Parses a BundleCustomData element.
894 /// </summary>
895 /// <param name="node">Element to parse.</param>
896 private void ParseBundleCustomDataElement(XElement node)
897 {
898 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
899 string customDataId = null;
900 WixBundleCustomDataType? customDataType = null;
901 string extensionId = null;
902 var attributeDefinitions = new List<WixBundleCustomDataAttributeSymbol>();
903 var foundAttributeDefinitions = false;
904
905 foreach (var attrib in node.Attributes())
906 {
907 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
908 {
909 switch (attrib.Name.LocalName)
910 {
911 case "Id":
912 customDataId = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
913 break;
914 case "Type":
915 var typeValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
916 switch (typeValue)
917 {
918 case "bootstrapperApplication":
919 customDataType = WixBundleCustomDataType.BootstrapperApplication;
920 break;
921 case "bundleExtension": // kept for backward compatibility
922 case "bootstrapperExtension":
923 customDataType = WixBundleCustomDataType.BootstrapperExtension;
924 break;
925 default:
926 this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Type", typeValue, "bootstrapperApplication", "bootstrapperExtension"));
927 customDataType = WixBundleCustomDataType.Unknown; // set a value to prevent expected attribute error below.
928 break;
929 }
930 break;
931 case "ExtensionId":
932 extensionId = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
933 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.WixBootstrapperExtension, extensionId);
934 break;
935 default:
936 this.Core.UnexpectedAttribute(node, attrib);
937 break;
938 }
939 }
940 else
941 {
942 this.Core.ParseExtensionAttribute(node, attrib);
943 }
944 }
945
946 if (null == customDataId)
947 {
948 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
949 }
950
951 var hasExtensionId = null != extensionId;
952 if (!customDataType.HasValue)
953 {
954 customDataType = hasExtensionId ? WixBundleCustomDataType.BootstrapperExtension : WixBundleCustomDataType.BootstrapperApplication;
955 }
956
957 if (!customDataType.HasValue)
958 {
959 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Type"));
960 }
961 else if (hasExtensionId)
962 {
963 if (customDataType.Value == WixBundleCustomDataType.BootstrapperApplication)
964 {
965 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "ExtensonId", "Type", "bootstrapperApplication"));
966 }
967 }
968 else if (customDataType.Value == WixBundleCustomDataType.BootstrapperExtension)
969 {
970 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ExtensionId", "Type", "bootstrapperExtension"));
971 }
972
973 foreach (var child in node.Elements())
974 {
975 if (CompilerCore.WixNamespace == child.Name.Namespace)
976 {
977 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
978 switch (child.Name.LocalName)
979 {
980 case "BundleAttributeDefinition":
981 foundAttributeDefinitions = true;
982
983 var attributeDefinition = this.ParseBundleAttributeDefinitionElement(child, childSourceLineNumbers, customDataId);
984 if (attributeDefinition != null)
985 {
986 attributeDefinitions.Add(attributeDefinition);
987 }
988 break;
989 case "BundleElement":
990 this.ParseBundleElementElement(child, childSourceLineNumbers, customDataId);
991 break;
992 default:
993 this.Core.UnexpectedElement(node, child);
994 break;
995 }
996 }
997 else
998 {
999 this.Core.ParseExtensionElement(node, child);
1000 }
1001 }
1002
1003 if (attributeDefinitions.Count > 0)
1004 {
1005 if (!this.Core.EncounteredError)
1006 {
1007 var attributeNames = String.Join(new string(WixBundleCustomDataSymbol.AttributeNamesSeparator, 1), attributeDefinitions.Select(c => c.Name));
1008
1009 this.Core.AddSymbol(new WixBundleCustomDataSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, customDataId))
1010 {
1011 AttributeNames = attributeNames,
1012 Type = customDataType.Value,
1013 BootstrapperExtensionRef = extensionId,
1014 });
1015 }
1016 }
1017 else if (!foundAttributeDefinitions)
1018 {
1019 this.Core.Write(ErrorMessages.ExpectedElement(sourceLineNumbers, node.Name.LocalName, "BundleAttributeDefinition"));
1020 }
1021 }
1022
1023 /// <summary>
1024 /// Parses a BundleCustomDataRef element.
1025 /// </summary>
1026 /// <param name="node">Element to parse.</param>
1027 private void ParseBundleCustomDataRefElement(XElement node)
1028 {
1029 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1030 string customDataId = null;
1031
1032 foreach (var attrib in node.Attributes())
1033 {
1034 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1035 {
1036 switch (attrib.Name.LocalName)
1037 {
1038 case "Id":
1039 customDataId = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
1040 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.WixBundleCustomData, customDataId);
1041 break;
1042 default:
1043 this.Core.UnexpectedAttribute(node, attrib);
1044 break;
1045 }
1046 }
1047 else
1048 {
1049 this.Core.ParseExtensionAttribute(node, attrib);
1050 }
1051 }
1052
1053 if (null == customDataId)
1054 {
1055 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
1056 }
1057
1058 foreach (var child in node.Elements())
1059 {
1060 if (CompilerCore.WixNamespace == child.Name.Namespace)
1061 {
1062 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
1063 switch (child.Name.LocalName)
1064 {
1065 case "BundleElement":
1066 this.ParseBundleElementElement(child, childSourceLineNumbers, customDataId);
1067 break;
1068 default:
1069 this.Core.UnexpectedElement(node, child);
1070 break;
1071 }
1072 }
1073 else
1074 {
1075 this.Core.ParseExtensionElement(node, child);
1076 }
1077 }
1078 }
1079
1080 /// <summary>
1081 /// Parses a BundleAttributeDefinition element.
1082 /// </summary>
1083 /// <param name="node">Element to parse.</param>
1084 /// <param name="sourceLineNumbers">Element's SourceLineNumbers.</param>
1085 /// <param name="customDataId">BundleCustomData Id.</param>
1086 private WixBundleCustomDataAttributeSymbol ParseBundleAttributeDefinitionElement(XElement node, SourceLineNumber sourceLineNumbers, string customDataId)
1087 {
1088 string attributeName = null;
1089
1090 foreach (var attrib in node.Attributes())
1091 {
1092 switch (attrib.Name.LocalName)
1093 {
1094 case "Id":
1095 attributeName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1096 break;
1097 default:
1098 this.Core.UnexpectedAttribute(node, attrib);
1099 break;
1100 }
1101 }
1102
1103 if (null == attributeName)
1104 {
1105 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
1106 }
1107
1108 this.Core.ParseForExtensionElements(node);
1109
1110 if (this.Core.EncounteredError)
1111 {
1112 return null;
1113 }
1114
1115 var customDataAttribute = this.Core.AddSymbol(new WixBundleCustomDataAttributeSymbol(sourceLineNumbers, new Identifier(AccessModifier.Section, customDataId, attributeName))
1116 {
1117 CustomDataRef = customDataId,
1118 Name = attributeName,
1119 });
1120 return customDataAttribute;
1121 }
1122
1123 /// <summary>
1124 /// Parses a BundleElement element.
1125 /// </summary>
1126 /// <param name="node">Element to parse.</param>
1127 /// <param name="sourceLineNumbers">Element's SourceLineNumbers.</param>
1128 /// <param name="customDataId">BundleCustomData Id.</param>
1129 private void ParseBundleElementElement(XElement node, SourceLineNumber sourceLineNumbers, string customDataId)
1130 {
1131 var elementId = Guid.NewGuid().ToString("N").ToUpperInvariant();
1132
1133 foreach (var attrib in node.Attributes())
1134 {
1135 this.Core.ParseExtensionAttribute(node, attrib);
1136 }
1137
1138 foreach (var child in node.Elements())
1139 {
1140 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
1141 switch (child.Name.LocalName)
1142 {
1143 case "BundleAttribute":
1144 string attributeName = null;
1145 string value = null;
1146 foreach (var attrib in child.Attributes())
1147 {
1148 switch (attrib.Name.LocalName)
1149 {
1150 case "Id":
1151 attributeName = this.Core.GetAttributeValue(childSourceLineNumbers, attrib);
1152 break;
1153 case "Value":
1154 value = this.Core.GetAttributeValue(childSourceLineNumbers, attrib);
1155 break;
1156 default:
1157 this.Core.ParseExtensionAttribute(child, attrib);
1158 break;
1159 }
1160 }
1161
1162 if (null == attributeName)
1163 {
1164 this.Core.Write(ErrorMessages.ExpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Id"));
1165 }
1166
1167 if (!this.Core.EncounteredError)
1168 {
1169 this.Core.AddSymbol(new WixBundleCustomDataCellSymbol(childSourceLineNumbers, new Identifier(AccessModifier.Section, customDataId, elementId, attributeName))
1170 {
1171 ElementId = elementId,
1172 AttributeRef = attributeName,
1173 CustomDataRef = customDataId,
1174 Value = value,
1175 });
1176 }
1177 break;
1178 default:
1179 this.Core.UnexpectedElement(node, child);
1180 break;
1181 }
1182 }
1183
1184 if (!this.Core.EncounteredError)
1185 {
1186 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.WixBundleCustomData, customDataId);
1187 }
1188 }
1189
1190 /// <summary>
1191 /// Parse the BootstrapperExtension element.
1192 /// </summary>
1193 /// <param name="node">Element to parse</param>
1194 private void ParseBootstrapperExtensionElement(XElement node)
1195 {
1196 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1197 var compilerPayload = new CompilerPayload(this.Core, sourceLineNumbers, node);
1198
1199 // This list lets us evaluate extension attributes *after* all core attributes
1200 // have been parsed and dealt with, regardless of authoring order.
1201 var extensionAttributes = new List<XAttribute>();
1202
1203 foreach (var attrib in node.Attributes())
1204 {
1205 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1206 {
1207 switch (attrib.Name.LocalName)
1208 {
1209 case "Id":
1210 compilerPayload.ParseId(attrib);
1211 break;
1212 case "Name":
1213 compilerPayload.ParseName(attrib);
1214 break;
1215 case "SourceFile":
1216 compilerPayload.ParseSourceFile(attrib);
1217 break;
1218 default:
1219 this.Core.UnexpectedAttribute(node, attrib);
1220 break;
1221 }
1222 }
1223 else
1224 {
1225 extensionAttributes.Add(attrib);
1226 }
1227 }
1228
1229 compilerPayload.FinishCompilingPayload(Compiler.BurnUXContainerId.Id);
1230
1231 // Now that the Id is known, we can parse the extension attributes.
1232 var context = new Dictionary<string, string>
1233 {
1234 ["Id"] = compilerPayload.Id.Id,
1235 };
1236
1237 foreach (var extensionAttribute in extensionAttributes)
1238 {
1239 this.Core.ParseExtensionAttribute(node, extensionAttribute, context);
1240 }
1241
1242 compilerPayload.CreatePayloadSymbol(ComplexReferenceParentType.Container, Compiler.BurnUXContainerId.Id);
1243
1244 foreach (var child in node.Elements())
1245 {
1246 if (CompilerCore.WixNamespace == child.Name.Namespace)
1247 {
1248 switch (child.Name.LocalName)
1249 {
1250 case "Payload":
1251 this.ParsePayloadElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, isRemoteAllowed: false);
1252 break;
1253 case "PayloadGroupRef":
1254 this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId);
1255 break;
1256 default:
1257 this.Core.UnexpectedElement(node, child);
1258 break;
1259 }
1260 }
1261 else
1262 {
1263 this.Core.ParseExtensionElement(node, child);
1264 }
1265 }
1266
1267 // Add the BootstrapperExtension.
1268 if (!this.Core.EncounteredError)
1269 {
1270 this.Core.AddSymbol(new WixBootstrapperExtensionSymbol(sourceLineNumbers, compilerPayload.Id)
1271 {
1272 PayloadRef = compilerPayload.Id.Id,
1273 });
1274 }
1275 }
1276
1277 /// <summary>
1278 /// Parse the OptionalUpdateRegistration element.
1279 /// </summary>
1280 /// <param name="node">The element to parse.</param>
1281 /// <param name="defaultManufacturer">The manufacturer.</param>
1282 /// <param name="defaultProductFamily">The product family.</param>
1283 /// <param name="defaultName">The bundle name.</param>
1284 private void ParseOptionalUpdateRegistrationElement(XElement node, string defaultManufacturer, string defaultProductFamily, string defaultName)
1285 {
1286 const string defaultClassification = "Update";
1287
1288 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1289 string manufacturer = null;
1290 string department = null;
1291 string productFamily = null;
1292 string name = null;
1293 var classification = defaultClassification;
1294
1295 foreach (var attrib in node.Attributes())
1296 {
1297 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1298 {
1299 switch (attrib.Name.LocalName)
1300 {
1301 case "Manufacturer":
1302 manufacturer = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1303 break;
1304 case "Department":
1305 department = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1306 break;
1307 case "ProductFamily":
1308 productFamily = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1309 break;
1310 case "Name":
1311 name = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1312 break;
1313 case "Classification":
1314 classification = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1315 break;
1316 default:
1317 this.Core.UnexpectedAttribute(node, attrib);
1318 break;
1319 }
1320 }
1321 else
1322 {
1323 this.Core.ParseExtensionAttribute(node, attrib);
1324 }
1325 }
1326
1327 if (String.IsNullOrEmpty(manufacturer))
1328 {
1329 if (!String.IsNullOrEmpty(defaultManufacturer))
1330 {
1331 manufacturer = defaultManufacturer;
1332 }
1333 else
1334 {
1335 this.Core.Write(ErrorMessages.ExpectedAttributeInElementOrParent(sourceLineNumbers, node.Name.LocalName, "Manufacturer", node.Parent.Name.LocalName));
1336 }
1337 }
1338
1339 if (String.IsNullOrEmpty(productFamily))
1340 {
1341 if (!String.IsNullOrEmpty(defaultProductFamily))
1342 {
1343 productFamily = defaultProductFamily;
1344 }
1345 }
1346
1347 if (String.IsNullOrEmpty(name))
1348 {
1349 if (!String.IsNullOrEmpty(defaultName))
1350 {
1351 name = defaultName;
1352 }
1353 else
1354 {
1355 this.Core.Write(ErrorMessages.ExpectedAttributeInElementOrParent(sourceLineNumbers, node.Name.LocalName, "Name", node.Parent.Name.LocalName));
1356 }
1357 }
1358
1359 if (String.IsNullOrEmpty(classification))
1360 {
1361 this.Core.Write(ErrorMessages.IllegalEmptyAttributeValue(sourceLineNumbers, node.Name.LocalName, "Classification", defaultClassification));
1362 }
1363
1364 this.Core.ParseForExtensionElements(node);
1365
1366 if (!this.Core.EncounteredError)
1367 {
1368 this.Core.AddSymbol(new WixUpdateRegistrationSymbol(sourceLineNumbers)
1369 {
1370 Manufacturer = manufacturer,
1371 Department = department,
1372 ProductFamily = productFamily,
1373 Name = name,
1374 Classification = classification
1375 });
1376 }
1377 }
1378
1379 /// <summary>
1380 /// Parse Payload element.
1381 /// </summary>
1382 /// <param name="node">Element to parse</param>
1383 /// <param name="parentType">ComplexReferenceParentType of parent element. (BA or PayloadGroup)</param>
1384 /// <param name="parentId">Identifier of parent element.</param>
1385 /// <param name="isRemoteAllowed">Indicates if the Payload element can be remote or not.</param>
1386 private Identifier ParsePayloadElement(XElement node, ComplexReferenceParentType parentType, Identifier parentId, bool isRemoteAllowed)
1387 {
1388 Debug.Assert(ComplexReferenceParentType.PayloadGroup == parentType || ComplexReferenceParentType.Package == parentType || ComplexReferenceParentType.Container == parentType);
1389
1390 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1391 var compilerPayload = new CompilerPayload(this.Core, sourceLineNumbers, node)
1392 {
1393 IsRemoteAllowed = isRemoteAllowed
1394 };
1395
1396 // This list lets us evaluate extension attributes *after* all core attributes
1397 // have been parsed and dealt with, regardless of authoring order.
1398 var extensionAttributes = new List<XAttribute>();
1399
1400 foreach (var attrib in node.Attributes())
1401 {
1402 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1403 {
1404 var allowed = true;
1405 switch (attrib.Name.LocalName)
1406 {
1407 case "Id":
1408 compilerPayload.ParseId(attrib);
1409 break;
1410 case "CertificatePublicKey":
1411 compilerPayload.ParseCertificatePublicKey(attrib);
1412 break;
1413 case "CertificateThumbprint":
1414 compilerPayload.ParseCertificateThumbprint(attrib);
1415 break;
1416 case "Compressed":
1417 compilerPayload.ParseCompressed(attrib);
1418 break;
1419 case "Hash":
1420 compilerPayload.ParseHash(attrib);
1421 break;
1422 case "Name":
1423 compilerPayload.ParseName(attrib);
1424 break;
1425 case "Size":
1426 compilerPayload.ParseSize(attrib);
1427 break;
1428 case "SourceFile":
1429 compilerPayload.ParseSourceFile(attrib);
1430 break;
1431 case "DownloadUrl":
1432 compilerPayload.ParseDownloadUrl(attrib);
1433 break;
1434 default:
1435 allowed = false;
1436 break;
1437 }
1438
1439 if (!allowed)
1440 {
1441 this.Core.UnexpectedAttribute(node, attrib);
1442 }
1443 }
1444 else
1445 {
1446 extensionAttributes.Add(attrib);
1447 }
1448 }
1449
1450 compilerPayload.FinishCompilingPayload(parentId?.Id);
1451
1452 // Now that the PayloadId is known, we can parse the extension attributes.
1453 var context = new Dictionary<string, string>
1454 {
1455 ["Id"] = compilerPayload.Id.Id,
1456 };
1457
1458 foreach (var extensionAttribute in extensionAttributes)
1459 {
1460 this.Core.ParseExtensionAttribute(node, extensionAttribute, context);
1461 }
1462
1463 foreach (var child in node.Elements())
1464 {
1465 if (CompilerCore.WixNamespace == child.Name.Namespace)
1466 {
1467 switch (child.Name.LocalName)
1468 {
1469 default:
1470 this.Core.UnexpectedElement(node, child);
1471 break;
1472 }
1473 }
1474 else
1475 {
1476 this.Core.ParseExtensionElement(node, child, context);
1477 }
1478 }
1479
1480 compilerPayload.CreatePayloadSymbol(parentType, parentId?.Id);
1481
1482 return compilerPayload.Id;
1483 }
1484
1485 /// <summary>
1486 /// Parse PayloadGroup element.
1487 /// </summary>
1488 /// <param name="node">Element to parse</param>
1489 /// <param name="parentType">Optional ComplexReferenceParentType of parent element. (typically another PayloadGroup)</param>
1490 /// <param name="parentId">Identifier of parent element.</param>
1491 private void ParsePayloadGroupElement(XElement node, ComplexReferenceParentType parentType, Identifier parentId)
1492 {
1493 Debug.Assert(ComplexReferenceParentType.Unknown == parentType || ComplexReferenceParentType.Layout == parentType || ComplexReferenceParentType.PayloadGroup == parentType);
1494
1495 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1496 Identifier id = null;
1497
1498 foreach (var attrib in node.Attributes())
1499 {
1500 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1501 {
1502 switch (attrib.Name.LocalName)
1503 {
1504 case "Id":
1505 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
1506 break;
1507 default:
1508 this.Core.UnexpectedAttribute(node, attrib);
1509 break;
1510 }
1511 }
1512 else
1513 {
1514 this.Core.ParseExtensionAttribute(node, attrib);
1515 }
1516 }
1517
1518 if (null == id)
1519 {
1520 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
1521 id = Identifier.Invalid;
1522 }
1523
1524 foreach (var child in node.Elements())
1525 {
1526 if (CompilerCore.WixNamespace == child.Name.Namespace)
1527 {
1528 WixBundlePackageType? packageType = null;
1529 switch (child.Name.LocalName)
1530 {
1531 case "BundlePackagePayload":
1532 packageType = WixBundlePackageType.Bundle;
1533 break;
1534 case "ExePackagePayload":
1535 packageType = WixBundlePackageType.Exe;
1536 break;
1537 case "MsiPackagePayload":
1538 packageType = WixBundlePackageType.Msi;
1539 break;
1540 case "MspPackagePayload":
1541 packageType = WixBundlePackageType.Msp;
1542 break;
1543 case "MsuPackagePayload":
1544 packageType = WixBundlePackageType.Msu;
1545 break;
1546 case "Payload":
1547 this.ParsePayloadElement(child, ComplexReferenceParentType.PayloadGroup, id, isRemoteAllowed: true);
1548 break;
1549 case "PayloadGroupRef":
1550 this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.PayloadGroup, id);
1551 break;
1552 default:
1553 this.Core.UnexpectedElement(node, child);
1554 break;
1555 }
1556
1557 if (packageType.HasValue)
1558 {
1559 var compilerPackagePayload = this.ParsePackagePayloadElement(null, child, packageType.Value, null);
1560 compilerPackagePayload.CreatePackagePayloadSymbol(ComplexReferenceParentType.PayloadGroup, id?.Id);
1561 }
1562 }
1563 else
1564 {
1565 this.Core.ParseExtensionElement(node, child);
1566 }
1567 }
1568
1569
1570 if (!this.Core.EncounteredError)
1571 {
1572 this.Core.AddSymbol(new WixBundlePayloadGroupSymbol(sourceLineNumbers, id));
1573
1574 this.Core.CreateGroupAndOrderingRows(sourceLineNumbers, parentType, parentId?.Id, ComplexReferenceChildType.PayloadGroup, id.Id, ComplexReferenceChildType.Unknown, null);
1575 }
1576 }
1577
1578 /// <summary>
1579 /// Parses a payload group reference element.
1580 /// </summary>
1581 /// <param name="node">Element to parse.</param>
1582 /// <param name="parentType">ComplexReferenceParentType of parent element (BA or PayloadGroup).</param>
1583 /// <param name="parentId">Identifier of parent element.</param>
1584 private Identifier ParsePayloadGroupRefElement(XElement node, ComplexReferenceParentType parentType, Identifier parentId)
1585 {
1586 Debug.Assert(ComplexReferenceParentType.Layout == parentType || ComplexReferenceParentType.PayloadGroup == parentType || ComplexReferenceParentType.Package == parentType || ComplexReferenceParentType.Container == parentType);
1587
1588 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1589 Identifier id = null;
1590
1591 foreach (var attrib in node.Attributes())
1592 {
1593 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1594 {
1595 switch (attrib.Name.LocalName)
1596 {
1597 case "Id":
1598 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
1599 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.WixBundlePayloadGroup, id.Id);
1600 break;
1601 default:
1602 this.Core.UnexpectedAttribute(node, attrib);
1603 break;
1604 }
1605 }
1606 else
1607 {
1608 this.Core.ParseExtensionAttribute(node, attrib);
1609 }
1610 }
1611
1612 if (null == id)
1613 {
1614 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
1615 }
1616
1617 this.Core.ParseForExtensionElements(node);
1618
1619 this.Core.CreateGroupAndOrderingRows(sourceLineNumbers, parentType, parentId?.Id, ComplexReferenceChildType.PayloadGroup, id?.Id, ComplexReferenceChildType.Unknown, null);
1620
1621 return id;
1622 }
1623
1624 /// <summary>
1625 /// Parse ExitCode element.
1626 /// </summary>
1627 /// <param name="node">Element to parse</param>
1628 /// <param name="packageId">Id of parent element</param>
1629 private void ParseExitCodeElement(XElement node, string packageId)
1630 {
1631 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1632 int? value = null;
1633 var behavior = ExitCodeBehaviorType.NotSet;
1634
1635 foreach (var attrib in node.Attributes())
1636 {
1637 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1638 {
1639 switch (attrib.Name.LocalName)
1640 {
1641 case "Value":
1642 value = this.Core.GetAttributeRawIntegerValue(sourceLineNumbers, attrib);
1643 break;
1644 case "Behavior":
1645 var behaviorString = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1646 switch (behaviorString)
1647 {
1648 case "error":
1649 behavior = ExitCodeBehaviorType.Error;
1650 break;
1651 case "errorForceReboot":
1652 behavior = ExitCodeBehaviorType.ErrorForceReboot;
1653 break;
1654 case "errorScheduleReboot":
1655 behavior = ExitCodeBehaviorType.ErrorScheduleReboot;
1656 break;
1657 case "forceReboot":
1658 behavior = ExitCodeBehaviorType.ForceReboot;
1659 break;
1660 case "scheduleReboot":
1661 behavior = ExitCodeBehaviorType.ScheduleReboot;
1662 break;
1663 case "success":
1664 behavior = ExitCodeBehaviorType.Success;
1665 break;
1666 default:
1667 this.Core.Write(ErrorMessages.IllegalAttributeValueWithLegalList(sourceLineNumbers, node.Name.LocalName, "Behavior", behaviorString, "success, error, scheduleReboot, forceReboot, errorScheduleReboot, errorForceReboot"));
1668 behavior = ExitCodeBehaviorType.Success; // set value to avoid ExpectedAttribute below.
1669 break;
1670 }
1671 break;
1672 default:
1673 this.Core.UnexpectedAttribute(node, attrib);
1674 break;
1675 }
1676 }
1677 else
1678 {
1679 this.Core.ParseExtensionAttribute(node, attrib);
1680 }
1681 }
1682
1683 if (ExitCodeBehaviorType.NotSet == behavior)
1684 {
1685 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Behavior"));
1686 }
1687
1688 this.Core.ParseForExtensionElements(node);
1689
1690 if (!this.Core.EncounteredError)
1691 {
1692 this.Core.AddSymbol(new WixBundlePackageExitCodeSymbol(sourceLineNumbers)
1693 {
1694 ChainPackageId = packageId,
1695 Code = value,
1696 Behavior = behavior
1697 });
1698 }
1699 }
1700
1701 /// <summary>
1702 /// Parse Chain element.
1703 /// </summary>
1704 /// <param name="node">Element to parse</param>
1705 private void ParseChainElement(XElement node)
1706 {
1707 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1708 var attributes = WixChainAttributes.None;
1709
1710 foreach (var attrib in node.Attributes())
1711 {
1712 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1713 {
1714 switch (attrib.Name.LocalName)
1715 {
1716 case "DisableRollback":
1717 if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
1718 {
1719 attributes |= WixChainAttributes.DisableRollback;
1720 }
1721 break;
1722 case "DisableSystemRestore":
1723 if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
1724 {
1725 attributes |= WixChainAttributes.DisableSystemRestore;
1726 }
1727 break;
1728 case "ParallelCache":
1729 if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
1730 {
1731 attributes |= WixChainAttributes.ParallelCache;
1732 }
1733 break;
1734 default:
1735 this.Core.UnexpectedAttribute(node, attrib);
1736 break;
1737 }
1738 }
1739 else
1740 {
1741 this.Core.ParseExtensionAttribute(node, attrib);
1742 }
1743 }
1744
1745 string previousId = null;
1746 var previousType = ComplexReferenceChildType.Unknown;
1747
1748 foreach (var child in node.Elements())
1749 {
1750 if (CompilerCore.WixNamespace == child.Name.Namespace)
1751 {
1752 switch (child.Name.LocalName)
1753 {
1754 case "MsiPackage":
1755 previousId = this.ParseMsiPackageElement(child, ComplexReferenceParentType.PackageGroup, BurnConstants.BundleChainPackageGroupId, previousType, previousId);
1756 previousType = ComplexReferenceChildType.Package;
1757 break;
1758 case "MspPackage":
1759 previousId = this.ParseMspPackageElement(child, ComplexReferenceParentType.PackageGroup, BurnConstants.BundleChainPackageGroupId, previousType, previousId);
1760 previousType = ComplexReferenceChildType.Package;
1761 break;
1762 case "MsuPackage":
1763 previousId = this.ParseMsuPackageElement(child, ComplexReferenceParentType.PackageGroup, BurnConstants.BundleChainPackageGroupId, previousType, previousId);
1764 previousType = ComplexReferenceChildType.Package;
1765 break;
1766 case "ExePackage":
1767 previousId = this.ParseExePackageElement(child, ComplexReferenceParentType.PackageGroup, BurnConstants.BundleChainPackageGroupId, previousType, previousId);
1768 previousType = ComplexReferenceChildType.Package;
1769 break;
1770 case "BundlePackage":
1771 previousId = this.ParseBundlePackageElement(child, ComplexReferenceParentType.PackageGroup, BurnConstants.BundleChainPackageGroupId, previousType, previousId);
1772 previousType = ComplexReferenceChildType.Package;
1773 break;
1774 case "RollbackBoundary":
1775 previousId = this.ParseRollbackBoundaryElement(child, ComplexReferenceParentType.PackageGroup, BurnConstants.BundleChainPackageGroupId, previousType, previousId);
1776 previousType = ComplexReferenceChildType.Package;
1777 break;
1778 case "PackageGroupRef":
1779 previousId = this.ParsePackageGroupRefElement(child, ComplexReferenceParentType.PackageGroup, BurnConstants.BundleChainPackageGroupId, previousType, previousId);
1780 previousType = ComplexReferenceChildType.PackageGroup;
1781 break;
1782 default:
1783 this.Core.UnexpectedElement(node, child);
1784 break;
1785 }
1786 }
1787 else
1788 {
1789 this.Core.ParseExtensionElement(node, child);
1790 }
1791 }
1792
1793
1794 if (null == previousId)
1795 {
1796 this.Core.Write(ErrorMessages.ExpectedElement(sourceLineNumbers, node.Name.LocalName, "MsiPackage", "ExePackage", "PackageGroupRef"));
1797 }
1798
1799 if (!this.Core.EncounteredError)
1800 {
1801 this.Core.AddSymbol(new WixChainSymbol(sourceLineNumbers)
1802 {
1803 Attributes = attributes
1804 });
1805 }
1806 }
1807
1808 /// <summary>
1809 /// Parse MsiPackage element
1810 /// </summary>
1811 /// <param name="node">Element to parse</param>
1812 /// <param name="parentType">Type of parent group, if known.</param>
1813 /// <param name="parentId">Identifier of parent group, if known.</param>
1814 /// <param name="previousType">Type of previous item, if known.</param>
1815 /// <param name="previousId">Identifier of previous item, if known</param>
1816 /// <returns>Identifier for package element.</returns>
1817 private string ParseMsiPackageElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
1818 {
1819 return this.ParseChainPackage(node, WixBundlePackageType.Msi, parentType, parentId, previousType, previousId);
1820 }
1821
1822 /// <summary>
1823 /// Parse MspPackage element
1824 /// </summary>
1825 /// <param name="node">Element to parse</param>
1826 /// <param name="parentType">Type of parent group, if known.</param>
1827 /// <param name="parentId">Identifier of parent group, if known.</param>
1828 /// <param name="previousType">Type of previous item, if known.</param>
1829 /// <param name="previousId">Identifier of previous item, if known</param>
1830 /// <returns>Identifier for package element.</returns>
1831 private string ParseMspPackageElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
1832 {
1833 return this.ParseChainPackage(node, WixBundlePackageType.Msp, parentType, parentId, previousType, previousId);
1834 }
1835
1836 /// <summary>
1837 /// Parse MsuPackage element
1838 /// </summary>
1839 /// <param name="node">Element to parse</param>
1840 /// <param name="parentType">Type of parent group, if known.</param>
1841 /// <param name="parentId">Identifier of parent group, if known.</param>
1842 /// <param name="previousType">Type of previous item, if known.</param>
1843 /// <param name="previousId">Identifier of previous item, if known</param>
1844 /// <returns>Identifier for package element.</returns>
1845 private string ParseMsuPackageElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
1846 {
1847 return this.ParseChainPackage(node, WixBundlePackageType.Msu, parentType, parentId, previousType, previousId);
1848 }
1849
1850 /// <summary>
1851 /// Parse ExePackage element
1852 /// </summary>
1853 /// <param name="node">Element to parse</param>
1854 /// <param name="parentType">Type of parent group, if known.</param>
1855 /// <param name="parentId">Identifier of parent group, if known.</param>
1856 /// <param name="previousType">Type of previous item, if known.</param>
1857 /// <param name="previousId">Identifier of previous item, if known</param>
1858 /// <returns>Identifier for package element.</returns>
1859 private string ParseExePackageElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
1860 {
1861 return this.ParseChainPackage(node, WixBundlePackageType.Exe, parentType, parentId, previousType, previousId);
1862 }
1863
1864 /// <summary>
1865 /// Parse BundlePackage element
1866 /// </summary>
1867 /// <param name="node">Element to parse</param>
1868 /// <param name="parentType">Type of parent group, if known.</param>
1869 /// <param name="parentId">Identifier of parent group, if known.</param>
1870 /// <param name="previousType">Type of previous item, if known.</param>
1871 /// <param name="previousId">Identifier of previous item, if known</param>
1872 /// <returns>Identifier for package element.</returns>
1873 private string ParseBundlePackageElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
1874 {
1875 return this.ParseChainPackage(node, WixBundlePackageType.Bundle, parentType, parentId, previousType, previousId);
1876 }
1877
1878 /// <summary>
1879 /// Parse RollbackBoundary element
1880 /// </summary>
1881 /// <param name="node">Element to parse</param>
1882 /// <param name="parentType">Type of parent group, if known.</param>
1883 /// <param name="parentId">Identifier of parent group, if known.</param>
1884 /// <param name="previousType">Type of previous item, if known.</param>
1885 /// <param name="previousId">Identifier of previous item, if known</param>
1886 /// <returns>Identifier for package element.</returns>
1887 private string ParseRollbackBoundaryElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
1888 {
1889 Debug.Assert(ComplexReferenceParentType.PackageGroup == parentType);
1890 Debug.Assert(ComplexReferenceChildType.Unknown == previousType || ComplexReferenceChildType.PackageGroup == previousType || ComplexReferenceChildType.Package == previousType);
1891
1892 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1893 Identifier id = null;
1894 var vital = YesNoType.Yes;
1895 var transaction = YesNoType.No;
1896 string logPathVariable = null;
1897
1898 // This list lets us evaluate extension attributes *after* all core attributes
1899 // have been parsed and dealt with, regardless of authoring order.
1900 var extensionAttributes = new List<XAttribute>();
1901
1902 foreach (var attrib in node.Attributes())
1903 {
1904 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1905 {
1906 var allowed = true;
1907 switch (attrib.Name.LocalName)
1908 {
1909 case "Id":
1910 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
1911 if (id?.Id == BurnConstants.BundleDefaultBoundaryId)
1912 {
1913 this.Messaging.Write(CompilerErrors.ReservedValue(sourceLineNumbers, node.Name.LocalName, "Id", id.Id));
1914 }
1915 break;
1916 case "Vital":
1917 vital = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1918 break;
1919 case "Transaction":
1920 transaction = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1921 break;
1922 case "LogPathVariable":
1923 logPathVariable = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
1924 break;
1925 default:
1926 allowed = false;
1927 break;
1928 }
1929
1930 if (!allowed)
1931 {
1932 this.Core.UnexpectedAttribute(node, attrib);
1933 }
1934 }
1935 else
1936 {
1937 // Save the extension attributes for later...
1938 extensionAttributes.Add(attrib);
1939 }
1940 }
1941
1942 if (null == id)
1943 {
1944 if (!String.IsNullOrEmpty(previousId))
1945 {
1946 id = this.Core.CreateIdentifier("rba", previousId);
1947 }
1948
1949 if (null == id)
1950 {
1951 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
1952 id = Identifier.Invalid;
1953 }
1954 else if (!Common.IsIdentifier(id.Id))
1955 {
1956 this.Core.Write(ErrorMessages.IllegalIdentifier(sourceLineNumbers, node.Name.LocalName, "Id", id.Id));
1957 }
1958 }
1959
1960 // Now that the rollback identifier is known, we can parse the extension attributes...
1961 var contextValues = new Dictionary<string, string>
1962 {
1963 ["RollbackBoundaryId"] = id.Id
1964 };
1965 foreach (var attribute in extensionAttributes)
1966 {
1967 this.Core.ParseExtensionAttribute(node, attribute, contextValues);
1968 }
1969
1970 this.Core.ParseForExtensionElements(node);
1971
1972 if (transaction == YesNoType.Yes)
1973 {
1974 if (logPathVariable == null)
1975 {
1976 logPathVariable = String.Concat("WixBundleLog_", id.Id);
1977 }
1978 }
1979 else if (logPathVariable != null)
1980 {
1981 this.Core.Write(ErrorMessages.IllegalAttributeValueWithoutOtherAttribute(sourceLineNumbers, node.Name.LocalName, "LogPathVariable", logPathVariable, "Transaction"));
1982 }
1983
1984 if (!this.Core.EncounteredError)
1985 {
1986 this.CreateRollbackBoundary(sourceLineNumbers, id, vital, transaction, logPathVariable, parentType, parentId, previousType, previousId);
1987 }
1988
1989 return id.Id;
1990 }
1991
1992 /// <summary>
1993 /// Parses one of the ChainPackage elements
1994 /// </summary>
1995 /// <param name="node">Element to parse</param>
1996 /// <param name="packageType">Type of package to parse</param>
1997 /// <param name="parentType">Type of parent group, if known.</param>
1998 /// <param name="parentId">Identifier of parent group, if known.</param>
1999 /// <param name="previousType">Type of previous item, if known.</param>
2000 /// <param name="previousId">Identifier of previous item, if known</param>
2001 /// <returns>Identifier for package element.</returns>
2002 /// <remarks>This method contains the shared logic for parsing all of the ChainPackage
2003 /// types, as there is more in common between them than different.</remarks>
2004 private string ParseChainPackage(XElement node, WixBundlePackageType packageType, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
2005 {
2006 Debug.Assert(ComplexReferenceParentType.PackageGroup == parentType);
2007 Debug.Assert(ComplexReferenceChildType.Unknown == previousType || ComplexReferenceChildType.PackageGroup == previousType || ComplexReferenceChildType.Package == previousType);
2008
2009 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
2010 var compilerPayload = new CompilerPayload(this.Core, sourceLineNumbers, node)
2011 {
2012 IsRequired = false,
2013 };
2014 string after = null;
2015 string installCondition = null;
2016 string repairCondition = null;
2017 var cache = BundleCacheType.Keep; // the default is to cache everything in tradeoff for stability over disk space.
2018 string cacheId = null;
2019 string description = null;
2020 string displayName = null;
2021 var logPathVariable = (packageType == WixBundlePackageType.Msu) ? String.Empty : null;
2022 var rollbackPathVariable = (packageType == WixBundlePackageType.Msu) ? String.Empty : null;
2023 var permanent = YesNoType.NotSet;
2024 var visible = YesNoType.NotSet;
2025 var vital = YesNoType.Yes;
2026 string installArguments = null;
2027 string repairArguments = null;
2028 string uninstallArguments = null;
2029 var perMachine = YesNoDefaultType.NotSet;
2030 string detectCondition = null;
2031 string protocol = null;
2032 long? installSize = null;
2033 var enableFeatureSelection = YesNoType.NotSet;
2034 var forcePerMachine = YesNoType.NotSet;
2035 CompilerPackagePayload childCompilerPackagePayload = null;
2036 var bundle = YesNoType.NotSet;
2037 var slipstream = YesNoType.NotSet;
2038 var hasPayloadInfo = false;
2039 var exeDetectionType = WixBundleExePackageDetectionType.None;
2040 string arpId = null;
2041 string arpDisplayVersion = null;
2042 var arpWin64 = YesNoType.NotSet;
2043 var arpUseUninstallString = YesNoType.NotSet;
2044
2045 var expectedNetFx4Args = new string[] { "/q", "/norestart" };
2046
2047 // This list lets us evaluate extension attributes *after* all core attributes
2048 // have been parsed and dealt with, regardless of authoring order.
2049 var extensionAttributes = new List<XAttribute>();
2050
2051 foreach (var attrib in node.Attributes())
2052 {
2053 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
2054 {
2055 var allowed = true;
2056 switch (attrib.Name.LocalName)
2057 {
2058 case "Id":
2059 compilerPayload.ParseId(attrib);
2060 break;
2061 case "Name":
2062 compilerPayload.ParseName(attrib);
2063 hasPayloadInfo = true;
2064 break;
2065 case "SourceFile":
2066 compilerPayload.ParseSourceFile(attrib);
2067 hasPayloadInfo = true;
2068 break;
2069 case "DownloadUrl":
2070 compilerPayload.ParseDownloadUrl(attrib);
2071 hasPayloadInfo = true;
2072 break;
2073 case "After":
2074 after = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2075 break;
2076 case "InstallCondition":
2077 installCondition = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2078 break;
2079 case "RepairCondition":
2080 repairCondition = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2081 allowed = (packageType != WixBundlePackageType.Msu);
2082 break;
2083 case "Cache":
2084 var value = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2085 switch (value)
2086 {
2087 case "force":
2088 cache = BundleCacheType.Force;
2089 break;
2090 case "keep":
2091 cache = BundleCacheType.Keep;
2092 break;
2093 case "remove":
2094 cache = BundleCacheType.Remove;
2095 break;
2096 case "":
2097 break;
2098 default:
2099 this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, value, "force", "keep", "remove"));
2100 break;
2101 }
2102 break;
2103 case "CacheId":
2104 cacheId = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2105 break;
2106 case "Description":
2107 description = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2108 break;
2109 case "DisplayName":
2110 displayName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2111 break;
2112 case "EnableFeatureSelection":
2113 enableFeatureSelection = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
2114 allowed = (packageType == WixBundlePackageType.Msi);
2115 break;
2116 case "ForcePerMachine":
2117 forcePerMachine = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
2118 allowed = (packageType == WixBundlePackageType.Msi);
2119 break;
2120 case "LogPathVariable":
2121 logPathVariable = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
2122 break;
2123 case "RollbackLogPathVariable":
2124 rollbackPathVariable = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
2125 break;
2126 case "Permanent":
2127 permanent = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
2128 allowed = (packageType != WixBundlePackageType.Msu);
2129 break;
2130 case "Visible":
2131 visible = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
2132 allowed = (packageType == WixBundlePackageType.Bundle || packageType == WixBundlePackageType.Msi);
2133 break;
2134 case "Vital":
2135 vital = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
2136 break;
2137 case "Bundle":
2138 bundle = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
2139 allowed = (packageType == WixBundlePackageType.Exe);
2140 break;
2141 case "InstallArguments":
2142 installArguments = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2143 allowed = (packageType == WixBundlePackageType.Bundle || packageType == WixBundlePackageType.Exe);
2144 break;
2145 case "RepairArguments":
2146 repairArguments = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
2147 allowed = (packageType == WixBundlePackageType.Bundle || packageType == WixBundlePackageType.Exe);
2148 break;
2149 case "UninstallArguments":
2150 uninstallArguments = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
2151 allowed = (packageType == WixBundlePackageType.Bundle || packageType == WixBundlePackageType.Exe);
2152 break;
2153 case "PerMachine":
2154 perMachine = this.Core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib);
2155 allowed = (packageType == WixBundlePackageType.Exe || packageType == WixBundlePackageType.Msp);
2156 break;
2157 case "DetectCondition":
2158 detectCondition = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
2159 allowed = (packageType == WixBundlePackageType.Exe || packageType == WixBundlePackageType.Msu);
2160 exeDetectionType = WixBundleExePackageDetectionType.Condition;
2161 break;
2162 case "Protocol":
2163 protocol = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2164 allowed = (packageType == WixBundlePackageType.Exe);
2165 break;
2166 case "InstallSize":
2167 installSize = this.Core.GetAttributeLongValue(sourceLineNumbers, attrib, 0, Int64.MaxValue);
2168 break;
2169 case "Compressed":
2170 compilerPayload.ParseCompressed(attrib);
2171 hasPayloadInfo = true;
2172 break;
2173 case "Slipstream":
2174 slipstream = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
2175 allowed = (packageType == WixBundlePackageType.Msp);
2176 break;
2177 default:
2178 allowed = false;
2179 break;
2180 }
2181
2182 if (!allowed)
2183 {
2184 this.Core.UnexpectedAttribute(node, attrib);
2185 }
2186 }
2187 else
2188 {
2189 // Save the extension attributes for later...
2190 extensionAttributes.Add(attrib);
2191 }
2192 }
2193
2194 // We need to handle the package payload up front because it affects Id generation. Id is needed by other child elements.
2195 var packagePayloadElementName = packageType + "PackagePayload";
2196 foreach (var child in node.Elements(CompilerCore.WixNamespace + packagePayloadElementName))
2197 {
2198 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
2199
2200 if (childCompilerPackagePayload != null)
2201 {
2202 this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, child.Name.LocalName));
2203 }
2204 else if (hasPayloadInfo)
2205 {
2206 this.Core.Write(ErrorMessages.UnexpectedElementWithAttribute(sourceLineNumbers, node.Name.LocalName, child.Name.LocalName, "SourceFile", "Name", "DownloadUrl", "Compressed"));
2207 }
2208
2209 childCompilerPackagePayload = this.ParsePackagePayloadElement(childSourceLineNumbers, child, packageType, compilerPayload.Id);
2210 }
2211
2212 if (compilerPayload.Id == null && childCompilerPackagePayload != null)
2213 {
2214 compilerPayload.Id = childCompilerPackagePayload.CompilerPayload.Id;
2215 }
2216
2217 compilerPayload.FinishCompilingPackage();
2218 var id = compilerPayload.Id;
2219
2220 // Now that the package ID is known, we can parse the extension attributes...
2221 var contextValues = new Dictionary<string, string>() { { "PackageId", id.Id } };
2222 foreach (var attribute in extensionAttributes)
2223 {
2224 this.Core.ParseExtensionAttribute(node, attribute, contextValues);
2225 }
2226
2227 foreach (var child in node.Elements())
2228 {
2229 if (CompilerCore.WixNamespace == child.Name.Namespace)
2230 {
2231 var allowed = true;
2232 switch (child.Name.LocalName)
2233 {
2234 case "ArpEntry":
2235 allowed = packageType == WixBundlePackageType.Exe;
2236 if (allowed)
2237 {
2238 if (exeDetectionType != WixBundleExePackageDetectionType.None)
2239 {
2240 this.Core.Write(ErrorMessages.UnexpectedElementWithAttribute(sourceLineNumbers, node.Name.LocalName, child.Name.LocalName, "DetectCondition"));
2241 }
2242 if (null != uninstallArguments)
2243 {
2244 this.Core.Write(ErrorMessages.UnexpectedElementWithAttribute(sourceLineNumbers, node.Name.LocalName, child.Name.LocalName, "UninstallArguments"));
2245 }
2246
2247 exeDetectionType = WixBundleExePackageDetectionType.Arp;
2248 this.ParseExePackageArpEntryElement(child, out arpId, out arpDisplayVersion, out arpWin64, out uninstallArguments, out arpUseUninstallString);
2249 }
2250 break;
2251 case "SlipstreamMsp":
2252 allowed = (packageType == WixBundlePackageType.Msi);
2253 if (allowed)
2254 {
2255 this.ParseSlipstreamMspElement(child, id.Id);
2256 }
2257 break;
2258 case "MsiProperty":
2259 allowed = (packageType == WixBundlePackageType.Msi || packageType == WixBundlePackageType.Msp);
2260 if (allowed)
2261 {
2262 this.ParseMsiPropertyElement(child, id.Id);
2263 }
2264 break;
2265 case "Payload":
2266 this.ParsePayloadElement(child, ComplexReferenceParentType.Package, id, isRemoteAllowed: true);
2267 break;
2268 case "PayloadGroupRef":
2269 this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Package, id);
2270 break;
2271 case "Provides":
2272 this.ParseProvidesElement(child, packageType, id.Id, out _);
2273 break;
2274 case "ExitCode":
2275 allowed = (packageType == WixBundlePackageType.Bundle || packageType == WixBundlePackageType.Exe);
2276 if (allowed)
2277 {
2278 this.ParseExitCodeElement(child, id.Id);
2279 }
2280 break;
2281 case "CommandLine":
2282 allowed = (packageType == WixBundlePackageType.Bundle || packageType == WixBundlePackageType.Exe);
2283 if (allowed)
2284 {
2285 this.ParseCommandLineElement(child, id.Id);
2286 }
2287 break;
2288 case "BundlePackagePayload":
2289 case "ExePackagePayload":
2290 case "MsiPackagePayload":
2291 case "MspPackagePayload":
2292 case "MsuPackagePayload":
2293 allowed = packagePayloadElementName == child.Name.LocalName;
2294 // Handled previously
2295 break;
2296 default:
2297 allowed = false;
2298 break;
2299 }
2300
2301 if (!allowed)
2302 {
2303 this.Core.UnexpectedElement(node, child);
2304 }
2305 }
2306 else
2307 {
2308 var context = new Dictionary<string, string>() { { "Id", id.Id } };
2309 this.Core.ParseExtensionElement(node, child, context);
2310 }
2311 }
2312
2313 if (packageType == WixBundlePackageType.Exe && exeDetectionType == WixBundleExePackageDetectionType.None && uninstallArguments != null)
2314 {
2315 exeDetectionType = WixBundleExePackageDetectionType.Condition;
2316 }
2317
2318 if (id.Id == BurnConstants.BundleDefaultBoundaryId)
2319 {
2320 this.Messaging.Write(CompilerErrors.ReservedValue(sourceLineNumbers, node.Name.LocalName, "Id", id.Id));
2321 }
2322
2323 if (null == logPathVariable)
2324 {
2325 logPathVariable = String.Concat("WixBundleLog_", id.Id);
2326 }
2327
2328 if (null == rollbackPathVariable)
2329 {
2330 rollbackPathVariable = String.Concat("WixBundleRollbackLog_", id.Id);
2331 }
2332
2333 if (packageType == WixBundlePackageType.Bundle || packageType == WixBundlePackageType.Msi)
2334 {
2335 if (permanent == YesNoType.Yes && visible == YesNoType.NotSet)
2336 {
2337 visible = YesNoType.Yes;
2338 }
2339 }
2340 else if (packageType == WixBundlePackageType.Exe)
2341 {
2342 // Set default scope for EXEs and MSPs if not already set.
2343 if (perMachine == YesNoDefaultType.NotSet)
2344 {
2345 perMachine = YesNoDefaultType.Default;
2346 }
2347
2348 if (exeDetectionType == WixBundleExePackageDetectionType.Arp)
2349 {
2350 // Missing attributes are reported when parsing the element.
2351 }
2352 else if (exeDetectionType == WixBundleExePackageDetectionType.Condition)
2353 {
2354 if (String.IsNullOrEmpty(detectCondition))
2355 {
2356 if (permanent == YesNoType.No)
2357 {
2358 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "DetectCondition", "Permanent", "no"));
2359 }
2360 else if (permanent == YesNoType.NotSet)
2361 {
2362 this.Core.Write(ErrorMessages.ExpectedAttributeWithoutOtherAttribute(sourceLineNumbers, node.Name.LocalName, "DetectCondition", "Permanent"));
2363 }
2364 else if (repairArguments != null)
2365 {
2366 this.Core.Write(ErrorMessages.ExpectedAttributeWithValueWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "DetectCondition", "RepairArguments"));
2367 }
2368 else if (uninstallArguments != null)
2369 {
2370 this.Core.Write(ErrorMessages.ExpectedAttributeWithValueWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "DetectCondition", "UninstallArguments"));
2371 }
2372 else
2373 {
2374 Debug.Assert(detectCondition == String.Empty);
2375 exeDetectionType = WixBundleExePackageDetectionType.None;
2376 }
2377 }
2378
2379 if (uninstallArguments == null)
2380 {
2381 if (permanent == YesNoType.No)
2382 {
2383 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "UninstallArguments", "Permanent", "no"));
2384 }
2385 else if (permanent == YesNoType.NotSet)
2386 {
2387 this.Core.Write(ErrorMessages.ExpectedAttributeWithoutOtherAttribute(sourceLineNumbers, node.Name.LocalName, "UninstallArguments", "Permanent"));
2388 }
2389 }
2390 }
2391 else if (exeDetectionType == WixBundleExePackageDetectionType.None)
2392 {
2393 if (permanent == YesNoType.No)
2394 {
2395 this.Core.Write(ErrorMessages.ExpectedAttributeOrElementWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "DetectCondition", "ArpEntry", "Permanent", "no"));
2396 }
2397 else if (permanent == YesNoType.NotSet)
2398 {
2399 this.Core.Write(ErrorMessages.ExpectedAttributeOrElementWithoutOtherAttribute(sourceLineNumbers, node.Name.LocalName, "DetectCondition", "ArpEntry", "Permanent"));
2400 }
2401 else if (repairArguments != null)
2402 {
2403 this.Core.Write(ErrorMessages.ExpectedAttributeOrElementWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "DetectCondition", "ArpEntry", "RepairArguments"));
2404 }
2405 else
2406 {
2407 this.Core.Write(WarningMessages.ExePackageDetectInformationRecommended(sourceLineNumbers));
2408 }
2409 }
2410
2411 if (repairArguments == null && repairCondition != null)
2412 {
2413 this.Core.Write(ErrorMessages.ExpectedAttributeWithValueWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "RepairArguments", "RepairCondition"));
2414 }
2415
2416 // Validate the protocol if provided.
2417 if (!String.IsNullOrEmpty(protocol))
2418 {
2419 if (protocol.Equals("netfx4", StringComparison.Ordinal))
2420 {
2421 foreach (var expectedArgument in expectedNetFx4Args)
2422 {
2423 if (null == installArguments || -1 == installArguments.IndexOf(expectedArgument, StringComparison.OrdinalIgnoreCase))
2424 {
2425 this.Core.Write(WarningMessages.AttributeShouldContain(sourceLineNumbers, node.Name.LocalName, "InstallArguments", installArguments, expectedArgument, "Protocol", "netfx4"));
2426 }
2427
2428 if (!String.IsNullOrEmpty(repairArguments) && -1 == repairArguments.IndexOf(expectedArgument, StringComparison.OrdinalIgnoreCase))
2429 {
2430 this.Core.Write(WarningMessages.AttributeShouldContain(sourceLineNumbers, node.Name.LocalName, "RepairArguments", repairArguments, expectedArgument, "Protocol", "netfx4"));
2431 }
2432
2433 if (!String.IsNullOrEmpty(uninstallArguments) && -1 == uninstallArguments.IndexOf(expectedArgument, StringComparison.OrdinalIgnoreCase))
2434 {
2435 this.Core.Write(WarningMessages.AttributeShouldContain(sourceLineNumbers, node.Name.LocalName, "UninstallArguments", uninstallArguments, expectedArgument, "Protocol", "netfx4"));
2436 }
2437 }
2438
2439 if (bundle == YesNoType.Yes)
2440 {
2441 this.Core.Write(ErrorMessages.IllegalAttributeValueWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Protocol", protocol, "Bundle", "yes"));
2442 }
2443 }
2444 else if (!protocol.Equals("burn", StringComparison.Ordinal) && !protocol.Equals("none", StringComparison.Ordinal))
2445 {
2446 this.Core.Write(ErrorMessages.IllegalAttributeValueWithLegalList(sourceLineNumbers, node.Name.LocalName, "Protocol", protocol, "none, burn, netfx4"));
2447 }
2448 }
2449 else if (bundle == YesNoType.Yes)
2450 {
2451 protocol = "burn";
2452 }
2453 }
2454 else if (packageType == WixBundlePackageType.Msp)
2455 {
2456 // Set default scope for EXEs and MSPs if not already set.
2457 if (perMachine == YesNoDefaultType.NotSet)
2458 {
2459 perMachine = YesNoDefaultType.Default;
2460 }
2461 }
2462 else if (packageType == WixBundlePackageType.Msu)
2463 {
2464 // Detect condition is recommended for Msu packages.
2465 if (String.IsNullOrEmpty(detectCondition))
2466 {
2467 this.Core.Write(WarningMessages.DetectConditionRecommended(sourceLineNumbers, node.Name.LocalName));
2468 }
2469 }
2470
2471 if (!this.Core.EncounteredError)
2472 {
2473 var compilerPackagePayload = childCompilerPackagePayload ?? (hasPayloadInfo ? new CompilerPackagePayload(compilerPayload, packageType) : null);
2474 compilerPackagePayload?.CreatePackagePayloadSymbol(ComplexReferenceParentType.Package, id.Id);
2475
2476 this.Core.AddSymbol(new WixChainItemSymbol(sourceLineNumbers, id));
2477
2478 WixBundlePackageAttributes attributes = 0;
2479 attributes |= (YesNoType.Yes == permanent) ? WixBundlePackageAttributes.Permanent : 0;
2480 attributes |= (YesNoType.Yes == visible) ? WixBundlePackageAttributes.Visible : 0;
2481
2482 var chainPackageSymbol = this.Core.AddSymbol(new WixBundlePackageSymbol(sourceLineNumbers, id)
2483 {
2484 Type = packageType,
2485 Attributes = attributes,
2486 InstallCondition = installCondition,
2487 RepairCondition = repairCondition,
2488 Cache = cache,
2489 CacheId = cacheId,
2490 Description = description,
2491 DisplayName = displayName,
2492 LogPathVariable = logPathVariable,
2493 RollbackLogPathVariable = rollbackPathVariable,
2494 Vital = vital == YesNoType.Yes,
2495 });
2496
2497 if (perMachine == YesNoDefaultType.Yes)
2498 {
2499 chainPackageSymbol.PerMachine = true;
2500 }
2501 else if (perMachine == YesNoDefaultType.No)
2502 {
2503 chainPackageSymbol.PerMachine = false;
2504 }
2505
2506 if (installSize.HasValue)
2507 {
2508 chainPackageSymbol.InstallSize = installSize;
2509 }
2510
2511 switch (packageType)
2512 {
2513 case WixBundlePackageType.Bundle:
2514 WixBundleBundlePackageAttributes bundleAttributes = 0;
2515
2516 this.Core.AddSymbol(new WixBundleBundlePackageSymbol(sourceLineNumbers, id)
2517 {
2518 Attributes = bundleAttributes,
2519 InstallCommand = installArguments,
2520 RepairCommand = repairArguments,
2521 UninstallCommand = uninstallArguments,
2522 });
2523 break;
2524
2525 case WixBundlePackageType.Exe:
2526 WixBundleExePackageAttributes exeAttributes = 0;
2527 exeAttributes |= (YesNoType.Yes == bundle) ? WixBundleExePackageAttributes.Bundle : 0;
2528 exeAttributes |= (YesNoType.Yes == arpWin64) ? WixBundleExePackageAttributes.ArpWin64 : 0;
2529 exeAttributes |= (YesNoType.Yes == arpUseUninstallString) ? WixBundleExePackageAttributes.ArpUseUninstallString : 0;
2530
2531 this.Core.AddSymbol(new WixBundleExePackageSymbol(sourceLineNumbers, id)
2532 {
2533 Attributes = exeAttributes,
2534 DetectCondition = detectCondition,
2535 InstallCommand = installArguments,
2536 RepairCommand = repairArguments,
2537 UninstallCommand = uninstallArguments,
2538 ExeProtocol = protocol,
2539 DetectionType = exeDetectionType,
2540 ArpId = arpId,
2541 ArpDisplayVersion = arpDisplayVersion,
2542 });
2543 break;
2544
2545 case WixBundlePackageType.Msi:
2546 WixBundleMsiPackageAttributes msiAttributes = 0;
2547 msiAttributes |= (YesNoType.Yes == enableFeatureSelection) ? WixBundleMsiPackageAttributes.EnableFeatureSelection : 0;
2548 msiAttributes |= (YesNoType.Yes == forcePerMachine) ? WixBundleMsiPackageAttributes.ForcePerMachine : 0;
2549
2550 this.Core.AddSymbol(new WixBundleMsiPackageSymbol(sourceLineNumbers, id)
2551 {
2552 Attributes = msiAttributes
2553 });
2554 break;
2555
2556 case WixBundlePackageType.Msp:
2557 WixBundleMspPackageAttributes mspAttributes = 0;
2558 mspAttributes |= (YesNoType.Yes == slipstream) ? WixBundleMspPackageAttributes.Slipstream : 0;
2559
2560 this.Core.AddSymbol(new WixBundleMspPackageSymbol(sourceLineNumbers, id)
2561 {
2562 Attributes = mspAttributes
2563 });
2564 break;
2565
2566 case WixBundlePackageType.Msu:
2567 this.Core.AddSymbol(new WixBundleMsuPackageSymbol(sourceLineNumbers, id)
2568 {
2569 DetectCondition = detectCondition
2570 });
2571 break;
2572 }
2573
2574 this.CreateChainPackageMetaRows(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.Package, id.Id, previousType, previousId, after);
2575 this.Core.CreateGroupAndOrderingRows(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.ContainerPackage, id.Id, ComplexReferenceChildType.Unknown, null);
2576 }
2577
2578 return id.Id;
2579 }
2580
2581 private CompilerPackagePayload ParsePackagePayloadElement(SourceLineNumber sourceLineNumbers, XElement node, WixBundlePackageType packageType, Identifier defaultId)
2582 {
2583 sourceLineNumbers = sourceLineNumbers ?? Preprocessor.GetSourceLineNumbers(node);
2584 var compilerPayload = new CompilerPayload(this.Core, sourceLineNumbers, node)
2585 {
2586 Id = defaultId,
2587 IsRemoteAllowed = packageType == WixBundlePackageType.Bundle || packageType == WixBundlePackageType.Exe || packageType == WixBundlePackageType.Msu,
2588 };
2589 var compilerPackagePayload = new CompilerPackagePayload(compilerPayload, packageType);
2590
2591 // This list lets us evaluate extension attributes *after* all core attributes
2592 // have been parsed and dealt with, regardless of authoring order.
2593 var extensionAttributes = new List<XAttribute>();
2594
2595 foreach (var attrib in node.Attributes())
2596 {
2597 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
2598 {
2599 var allowed = true;
2600 switch (attrib.Name.LocalName)
2601 {
2602 case "Id":
2603 compilerPayload.ParseId(attrib);
2604 break;
2605 case "Compressed":
2606 compilerPayload.ParseCompressed(attrib);
2607 break;
2608 case "Name":
2609 compilerPayload.ParseName(attrib);
2610 break;
2611 case "SourceFile":
2612 compilerPayload.ParseSourceFile(attrib);
2613 break;
2614 case "CertificatePublicKey":
2615 allowed = compilerPayload.IsRemoteAllowed;
2616 if (allowed)
2617 {
2618 compilerPayload.ParseCertificatePublicKey(attrib);
2619 }
2620 break;
2621 case "CertificateThumbprint":
2622 allowed = compilerPayload.IsRemoteAllowed;
2623 if (allowed)
2624 {
2625 compilerPayload.ParseCertificateThumbprint(attrib);
2626 }
2627 break;
2628 case "DownloadUrl":
2629 compilerPayload.ParseDownloadUrl(attrib);
2630 break;
2631 case "Description":
2632 allowed = compilerPayload.IsRemoteAllowed;
2633 if (allowed)
2634 {
2635 compilerPayload.ParseDescription(attrib);
2636 }
2637 break;
2638 case "Hash":
2639 allowed = compilerPayload.IsRemoteAllowed;
2640 if (allowed)
2641 {
2642 compilerPayload.ParseHash(attrib);
2643 }
2644 break;
2645 case "PayloadGeneration":
2646 allowed = compilerPackagePayload.ParsePayloadGeneration(attrib);
2647 break;
2648 case "ProductName":
2649 allowed = compilerPayload.IsRemoteAllowed;
2650 if (allowed)
2651 {
2652 compilerPayload.ParseProductName(attrib);
2653 }
2654 break;
2655 case "Size":
2656 allowed = compilerPayload.IsRemoteAllowed;
2657 if (allowed)
2658 {
2659 compilerPayload.ParseSize(attrib);
2660 }
2661 break;
2662 case "Version":
2663 allowed = compilerPayload.IsRemoteAllowed;
2664 if (allowed)
2665 {
2666 compilerPayload.ParseVersion(attrib);
2667 }
2668 break;
2669 default:
2670 allowed = false;
2671 break;
2672 }
2673
2674 if (!allowed)
2675 {
2676 this.Core.UnexpectedAttribute(node, attrib);
2677 }
2678 }
2679 else
2680 {
2681 extensionAttributes.Add(attrib);
2682 }
2683 }
2684
2685 compilerPayload.FinishCompilingPackagePayload();
2686
2687 // Now that the PayloadId is known, we can parse the extension attributes.
2688 var context = new Dictionary<string, string>
2689 {
2690 ["Id"] = compilerPayload.Id.Id,
2691 };
2692
2693 foreach (var extensionAttribute in extensionAttributes)
2694 {
2695 this.Core.ParseExtensionAttribute(node, extensionAttribute, context);
2696 }
2697
2698 var remoteBundleSeen = false;
2699
2700 foreach (var child in node.Elements())
2701 {
2702 if (CompilerCore.WixNamespace == child.Name.Namespace)
2703 {
2704 bool allowed;
2705 switch (child.Name.LocalName)
2706 {
2707 case "RemoteBundle":
2708 allowed = packageType == WixBundlePackageType.Bundle;
2709
2710 if (allowed)
2711 {
2712 if (compilerPackagePayload.PayloadGenerationType.HasValue)
2713 {
2714 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
2715 this.Core.Write(ErrorMessages.UnexpectedElementWithAttribute(childSourceLineNumbers, node.Name.LocalName, "RemoteBundle", "PayloadGeneration"));
2716 }
2717
2718 if (remoteBundleSeen)
2719 {
2720 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
2721 this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, "RemoteBundle"));
2722 }
2723
2724 this.ParseRemoteBundleElement(child, compilerPayload.Id.Id);
2725 remoteBundleSeen = true;
2726 }
2727
2728 break;
2729 default:
2730 allowed = false;
2731 break;
2732 }
2733
2734 if (!allowed)
2735 {
2736 this.Core.UnexpectedElement(node, child);
2737 }
2738 }
2739 else
2740 {
2741 this.Core.ParseExtensionElement(node, child, context);
2742 }
2743 }
2744
2745 var isLocal = !String.IsNullOrEmpty(compilerPayload.SourceFile);
2746 if (packageType == WixBundlePackageType.Bundle && !isLocal && !remoteBundleSeen)
2747 {
2748 this.Core.Write(ErrorMessages.ExpectedElement(sourceLineNumbers, node.Name.LocalName, "RemoteBundle"));
2749 }
2750
2751 return compilerPackagePayload;
2752 }
2753
2754 private void ParseRemoteBundleElement(XElement node, string packagePayloadId)
2755 {
2756 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
2757 string bundleId = null;
2758 string displayName = null;
2759 string engineVersion = null;
2760 long? installSize = null;
2761 string manifestNamespace = null;
2762 var perMachine = YesNoType.NotSet;
2763 var protocolVersion = -1;
2764 string providerKey = null;
2765 string upgradeCode = null;
2766 string version = null;
2767 var win64 = YesNoType.NotSet;
2768
2769 foreach (var attrib in node.Attributes())
2770 {
2771 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
2772 {
2773 switch (attrib.Name.LocalName)
2774 {
2775 case "BundleId":
2776 bundleId = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib);
2777 break;
2778 case "DisplayName":
2779 displayName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2780 break;
2781 case "EngineVersion":
2782 engineVersion = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib);
2783 break;
2784 case "InstallSize":
2785 installSize = this.Core.GetAttributeLongValue(sourceLineNumbers, attrib, 0, Int64.MaxValue);
2786 break;
2787 case "ManifestNamespace":
2788 manifestNamespace = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2789 break;
2790 case "PerMachine":
2791 perMachine = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
2792 break;
2793 case "ProtocolVersion":
2794 protocolVersion = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue);
2795 break;
2796 case "ProviderKey":
2797 providerKey = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2798 break;
2799 case "UpgradeCode":
2800 upgradeCode = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib);
2801 break;
2802 case "Version":
2803 version = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib);
2804 break;
2805 case "Win64":
2806 win64 = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
2807 break;
2808 default:
2809 this.Core.UnexpectedAttribute(node, attrib);
2810 break;
2811 }
2812 }
2813 else
2814 {
2815 this.Core.ParseExtensionAttribute(node, attrib);
2816 }
2817 }
2818
2819 if (String.IsNullOrEmpty(bundleId))
2820 {
2821 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "BundleId"));
2822 }
2823
2824 if (String.IsNullOrEmpty(manifestNamespace))
2825 {
2826 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ManifestNamespace"));
2827 }
2828
2829 if (perMachine == YesNoType.NotSet)
2830 {
2831 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "PerMachine"));
2832 }
2833
2834 if (protocolVersion == -1)
2835 {
2836 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ProtocolVersion"));
2837 }
2838
2839 if (String.IsNullOrEmpty(providerKey))
2840 {
2841 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ProviderKey"));
2842 }
2843
2844 if (String.IsNullOrEmpty(upgradeCode))
2845 {
2846 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "UpgradeCode"));
2847 }
2848
2849 if (String.IsNullOrEmpty(version))
2850 {
2851 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version"));
2852 }
2853
2854 if (win64 == YesNoType.NotSet)
2855 {
2856 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Win64"));
2857 }
2858
2859 if (!this.Messaging.EncounteredError)
2860 {
2861 WixBundleHarvestedBundlePackageAttributes bundleAttributes = 0;
2862 bundleAttributes |= (YesNoType.Yes == perMachine) ? WixBundleHarvestedBundlePackageAttributes.PerMachine : 0;
2863 bundleAttributes |= (YesNoType.Yes == win64) ? WixBundleHarvestedBundlePackageAttributes.Win64 : 0;
2864
2865 var symbol = this.Core.AddSymbol(new WixBundleHarvestedBundlePackageSymbol(sourceLineNumbers, new Identifier(AccessModifier.Section, packagePayloadId))
2866 {
2867 Attributes = bundleAttributes,
2868 BundleId = bundleId,
2869 DisplayName = displayName,
2870 EngineVersion = engineVersion,
2871 ManifestNamespace = manifestNamespace,
2872 ProtocolVersion = protocolVersion,
2873 Version = version,
2874 });
2875
2876 if (installSize.HasValue)
2877 {
2878 symbol.InstallSize = installSize.Value;
2879 }
2880
2881 this.Core.AddSymbol(new WixBundlePackageRelatedBundleSymbol(sourceLineNumbers)
2882 {
2883 PackagePayloadRef = packagePayloadId,
2884 BundleId = upgradeCode,
2885 Action = RelatedBundleActionType.Upgrade,
2886 });
2887
2888 var depId = this.Core.CreateIdentifier("dep", packagePayloadId, providerKey);
2889 this.Core.AddSymbol(new WixBundleHarvestedDependencyProviderSymbol(sourceLineNumbers, depId)
2890 {
2891 PackagePayloadRef = packagePayloadId,
2892 ProviderKey = providerKey,
2893 Version = version,
2894 });
2895 }
2896
2897 var context = new Dictionary<string, string>
2898 {
2899 ["Id"] = packagePayloadId,
2900 };
2901
2902 foreach (var child in node.Elements())
2903 {
2904 if (CompilerCore.WixNamespace == child.Name.Namespace)
2905 {
2906 switch (child.Name.LocalName)
2907 {
2908 case "RemoteRelatedBundle":
2909 this.ParseRemoteRelatedBundleElement(child, packagePayloadId);
2910 break;
2911 default:
2912 this.Core.UnexpectedElement(node, child);
2913 break;
2914 }
2915 }
2916 else
2917 {
2918 this.Core.ParseExtensionElement(node, child, context);
2919 }
2920 }
2921 }
2922
2923 private void ParseRemoteRelatedBundleElement(XElement node, string payloadId)
2924 {
2925 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
2926 string id = null;
2927 RelatedBundleActionType? actionType = null;
2928
2929 foreach (var attrib in node.Attributes())
2930 {
2931 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
2932 {
2933 switch (attrib.Name.LocalName)
2934 {
2935 case "Id":
2936 id = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
2937 break;
2938 case "Action":
2939 var action = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2940 switch (action)
2941 {
2942 case "Detect":
2943 case "detect":
2944 actionType = RelatedBundleActionType.Detect;
2945 break;
2946 case "Upgrade":
2947 case "upgrade":
2948 actionType = RelatedBundleActionType.Upgrade;
2949 break;
2950 case "Addon":
2951 case "addon":
2952 actionType = RelatedBundleActionType.Addon;
2953 break;
2954 case "Patch":
2955 case "patch":
2956 actionType = RelatedBundleActionType.Patch;
2957 break;
2958 case "":
2959 break;
2960 default:
2961 this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Action", action, "Detect", "Upgrade", "Addon", "Patch"));
2962 break;
2963 }
2964 break;
2965 default:
2966 this.Core.UnexpectedAttribute(node, attrib);
2967 break;
2968 }
2969 }
2970 else
2971 {
2972 this.Core.ParseExtensionAttribute(node, attrib);
2973 }
2974 }
2975
2976 if (null == id)
2977 {
2978 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
2979 }
2980
2981 if (!actionType.HasValue)
2982 {
2983 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Action"));
2984 }
2985
2986 var context = new Dictionary<string, string>
2987 {
2988 ["PayloadId"] = payloadId,
2989 };
2990
2991 this.Core.ParseForExtensionElements(node, context);
2992
2993 if (!this.Core.EncounteredError)
2994 {
2995 this.Core.AddSymbol(new WixBundlePackageRelatedBundleSymbol(sourceLineNumbers)
2996 {
2997 PackagePayloadRef = payloadId,
2998 BundleId = id,
2999 Action = actionType.Value,
3000 });
3001 }
3002 }
3003
3004 private void ParseExePackageArpEntryElement(XElement node, out string id, out string version, out YesNoType win64, out string uninstallArguments, out YesNoType arpUseUninstallString)
3005 {
3006 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
3007 id = null;
3008 version = null;
3009 win64 = YesNoType.NotSet;
3010 arpUseUninstallString = YesNoType.NotSet;
3011 uninstallArguments = null;
3012
3013 foreach (var attrib in node.Attributes())
3014 {
3015 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
3016 {
3017 switch (attrib.Name.LocalName)
3018 {
3019 case "Id":
3020 id = this.Core.GetAttributeLongFilename(sourceLineNumbers, attrib);
3021 break;
3022 case "Version":
3023 version = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3024 break;
3025 case "AdditionalUninstallArguments":
3026 uninstallArguments = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3027 break;
3028 case "UseUninstallString":
3029 arpUseUninstallString = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
3030 break;
3031 case "Win64":
3032 win64 = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
3033 break;
3034 default:
3035 this.Core.UnexpectedAttribute(node, attrib);
3036 break;
3037 }
3038 }
3039 else
3040 {
3041 this.Core.ParseExtensionAttribute(node, attrib);
3042 }
3043 }
3044
3045 this.Core.ParseForExtensionElements(node);
3046
3047 if (String.IsNullOrEmpty(id))
3048 {
3049 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
3050 }
3051
3052 if (String.IsNullOrEmpty(version))
3053 {
3054 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version"));
3055 }
3056
3057 if (win64 == YesNoType.NotSet)
3058 {
3059 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Win64"));
3060 }
3061 }
3062
3063 /// <summary>
3064 /// Parse CommandLine element.
3065 /// </summary>
3066 /// <param name="node">Element to parse</param>
3067 /// <param name="packageId">Parent packageId</param>
3068 private void ParseCommandLineElement(XElement node, string packageId)
3069 {
3070 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
3071 string installArgument = null;
3072 string uninstallArgument = null;
3073 string repairArgument = null;
3074 string condition = null;
3075
3076 foreach (var attrib in node.Attributes())
3077 {
3078 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
3079 {
3080 switch (attrib.Name.LocalName)
3081 {
3082 case "InstallArgument":
3083 installArgument = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3084 break;
3085 case "UninstallArgument":
3086 uninstallArgument = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3087 break;
3088 case "RepairArgument":
3089 repairArgument = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3090 break;
3091 case "Condition":
3092 condition = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3093 break;
3094 default:
3095 this.Core.UnexpectedAttribute(node, attrib);
3096 break;
3097 }
3098 }
3099 else
3100 {
3101 this.Core.ParseExtensionAttribute(node, attrib);
3102 }
3103 }
3104
3105 if (String.IsNullOrEmpty(condition))
3106 {
3107 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Condition"));
3108 }
3109
3110 this.Core.ParseForExtensionElements(node);
3111
3112 if (!this.Core.EncounteredError)
3113 {
3114 this.Core.AddSymbol(new WixBundlePackageCommandLineSymbol(sourceLineNumbers)
3115 {
3116 WixBundlePackageRef = packageId,
3117 InstallArgument = installArgument,
3118 UninstallArgument = uninstallArgument,
3119 RepairArgument = repairArgument,
3120 Condition = condition
3121 });
3122 }
3123 }
3124
3125 /// <summary>
3126 /// Parse PackageGroup element.
3127 /// </summary>
3128 /// <param name="node">Element to parse</param>
3129 private void ParsePackageGroupElement(XElement node)
3130 {
3131 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
3132 Identifier id = null;
3133
3134 foreach (var attrib in node.Attributes())
3135 {
3136 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
3137 {
3138 switch (attrib.Name.LocalName)
3139 {
3140 case "Id":
3141 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
3142 if (id?.Id == BurnConstants.BundleChainPackageGroupId)
3143 {
3144 this.Messaging.Write(CompilerErrors.ReservedValue(sourceLineNumbers, node.Name.LocalName, "Id", id.Id));
3145 }
3146 break;
3147 default:
3148 this.Core.UnexpectedAttribute(node, attrib);
3149 break;
3150 }
3151 }
3152 else
3153 {
3154 this.Core.ParseExtensionAttribute(node, attrib);
3155 }
3156 }
3157
3158 if (null == id)
3159 {
3160 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
3161 id = Identifier.Invalid;
3162 }
3163
3164 var previousType = ComplexReferenceChildType.Unknown;
3165 string previousId = null;
3166 foreach (var child in node.Elements())
3167 {
3168 if (CompilerCore.WixNamespace == child.Name.Namespace)
3169 {
3170 switch (child.Name.LocalName)
3171 {
3172 case "MsiPackage":
3173 previousId = this.ParseMsiPackageElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId);
3174 previousType = ComplexReferenceChildType.Package;
3175 break;
3176 case "MspPackage":
3177 previousId = this.ParseMspPackageElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId);
3178 previousType = ComplexReferenceChildType.Package;
3179 break;
3180 case "MsuPackage":
3181 previousId = this.ParseMsuPackageElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId);
3182 previousType = ComplexReferenceChildType.Package;
3183 break;
3184 case "ExePackage":
3185 previousId = this.ParseExePackageElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId);
3186 previousType = ComplexReferenceChildType.Package;
3187 break;
3188 case "BundlePackage":
3189 previousId = this.ParseBundlePackageElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId);
3190 previousType = ComplexReferenceChildType.Package;
3191 break;
3192 case "RollbackBoundary":
3193 previousId = this.ParseRollbackBoundaryElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId);
3194 previousType = ComplexReferenceChildType.Package;
3195 break;
3196 case "PackageGroupRef":
3197 previousId = this.ParsePackageGroupRefElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId);
3198 previousType = ComplexReferenceChildType.PackageGroup;
3199 break;
3200 default:
3201 this.Core.UnexpectedElement(node, child);
3202 break;
3203 }
3204 }
3205 else
3206 {
3207 this.Core.ParseExtensionElement(node, child);
3208 }
3209 }
3210
3211
3212 if (!this.Core.EncounteredError)
3213 {
3214 this.Core.AddSymbol(new WixBundlePackageGroupSymbol(sourceLineNumbers, id));
3215 }
3216 }
3217
3218 /// <summary>
3219 /// Parses a package group reference element.
3220 /// </summary>
3221 /// <param name="node">Element to parse.</param>
3222 /// <param name="parentType">ComplexReferenceParentType of parent element (Unknown or PackageGroup).</param>
3223 /// <param name="parentId">Identifier of parent element.</param>
3224 /// <returns>Identifier for package group element.</returns>
3225 private string ParsePackageGroupRefElement(XElement node, ComplexReferenceParentType parentType, string parentId)
3226 {
3227 return this.ParsePackageGroupRefElement(node, parentType, parentId, ComplexReferenceChildType.Unknown, null);
3228 }
3229
3230 /// <summary>
3231 /// Parses a package group reference element.
3232 /// </summary>
3233 /// <param name="node">Element to parse.</param>
3234 /// <param name="parentType">ComplexReferenceParentType of parent element (Unknown or PackageGroup).</param>
3235 /// <param name="parentId">Identifier of parent element.</param>
3236 /// <param name="previousType"></param>
3237 /// <param name="previousId"></param>
3238 /// <returns>Identifier for package group element.</returns>
3239 private string ParsePackageGroupRefElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
3240 {
3241 Debug.Assert(ComplexReferenceParentType.Unknown == parentType || ComplexReferenceParentType.PackageGroup == parentType || ComplexReferenceParentType.Container == parentType);
3242 Debug.Assert(ComplexReferenceChildType.Unknown == previousType || ComplexReferenceChildType.PackageGroup == previousType || ComplexReferenceChildType.Package == previousType);
3243
3244 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
3245 string id = null;
3246 string after = null;
3247
3248 foreach (var attrib in node.Attributes())
3249 {
3250 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
3251 {
3252 switch (attrib.Name.LocalName)
3253 {
3254 case "Id":
3255 id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
3256 if (id == BurnConstants.BundleChainPackageGroupId)
3257 {
3258 this.Messaging.Write(CompilerErrors.ReservedValue(sourceLineNumbers, node.Name.LocalName, "Id", id));
3259 }
3260 else
3261 {
3262 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.WixBundlePackageGroup, id);
3263 }
3264 break;
3265 case "After":
3266 after = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
3267 break;
3268 default:
3269 this.Core.UnexpectedAttribute(node, attrib);
3270 break;
3271 }
3272 }
3273 else
3274 {
3275 this.Core.ParseExtensionAttribute(node, attrib);
3276
3277 }
3278 }
3279
3280 if (null == id)
3281 {
3282 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
3283 }
3284
3285 if (null != after && ComplexReferenceParentType.Container == parentType)
3286 {
3287 this.Core.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, "After", parentId));
3288 }
3289
3290 this.Core.ParseForExtensionElements(node);
3291
3292 if (ComplexReferenceParentType.Container == parentType)
3293 {
3294 this.Core.CreateWixGroupRow(sourceLineNumbers, ComplexReferenceParentType.Container, parentId, ComplexReferenceChildType.PackageGroup, id);
3295 }
3296 else
3297 {
3298 this.CreateChainPackageMetaRows(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.PackageGroup, id, previousType, previousId, after);
3299 }
3300
3301 return id;
3302 }
3303
3304 /// <summary>
3305 /// Creates rollback boundary.
3306 /// </summary>
3307 /// <param name="sourceLineNumbers">Source line numbers.</param>
3308 /// <param name="id">Identifier for the rollback boundary.</param>
3309 /// <param name="vital">Indicates whether the rollback boundary is vital or not.</param>
3310 /// <param name="transaction">Indicates whether the rollback boundary will use an MSI transaction.</param>
3311 /// <param name="logPathVariable">The variable for the path of the MSI transaction log.</param>
3312 /// <param name="parentType">Type of parent group.</param>
3313 /// <param name="parentId">Identifier of parent group.</param>
3314 /// <param name="previousType">Type of previous item, if any.</param>
3315 /// <param name="previousId">Identifier of previous item, if any.</param>
3316 private void CreateRollbackBoundary(SourceLineNumber sourceLineNumbers, Identifier id, YesNoType vital, YesNoType transaction, string logPathVariable, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
3317 {
3318 this.Core.AddSymbol(new WixChainItemSymbol(sourceLineNumbers, id));
3319
3320 var rollbackBoundary = this.Core.AddSymbol(new WixBundleRollbackBoundarySymbol(sourceLineNumbers, id)
3321 {
3322 Transaction = transaction == YesNoType.Yes,
3323 Vital = vital == YesNoType.Yes,
3324 });
3325
3326 if (logPathVariable != null)
3327 {
3328 rollbackBoundary.LogPathVariable = logPathVariable;
3329 }
3330
3331 this.CreateChainPackageMetaRows(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.Package, id.Id, previousType, previousId, null);
3332 }
3333
3334 /// <summary>
3335 /// Creates group and ordering information for packages
3336 /// </summary>
3337 /// <param name="sourceLineNumbers">Source line numbers.</param>
3338 /// <param name="parentType">Type of parent group, if known.</param>
3339 /// <param name="parentId">Identifier of parent group, if known.</param>
3340 /// <param name="type">Type of this item.</param>
3341 /// <param name="id">Identifier for this item.</param>
3342 /// <param name="previousType">Type of previous item, if known.</param>
3343 /// <param name="previousId">Identifier of previous item, if known</param>
3344 /// <param name="afterId">Identifier of explicit 'After' attribute, if given.</param>
3345 private void CreateChainPackageMetaRows(SourceLineNumber sourceLineNumbers,
3346 ComplexReferenceParentType parentType, string parentId,
3347 ComplexReferenceChildType type, string id,
3348 ComplexReferenceChildType previousType, string previousId, string afterId)
3349 {
3350 // If there's an explicit 'After' attribute, it overrides the inferred previous item.
3351 if (null != afterId)
3352 {
3353 previousType = ComplexReferenceChildType.Package;
3354 previousId = afterId;
3355 }
3356
3357 this.Core.CreateGroupAndOrderingRows(sourceLineNumbers, parentType, parentId, type, id, previousType, previousId);
3358 }
3359
3360 /// <summary>
3361 /// Parse MsiProperty element
3362 /// </summary>
3363 /// <param name="node">Element to parse</param>
3364 /// <param name="packageId">Id of parent element</param>
3365 private void ParseMsiPropertyElement(XElement node, string packageId)
3366 {
3367 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
3368 string name = null;
3369 string value = null;
3370 string condition = null;
3371
3372 foreach (var attrib in node.Attributes())
3373 {
3374 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
3375 {
3376 switch (attrib.Name.LocalName)
3377 {
3378 case "Name":
3379 name = this.Core.GetAttributeMsiPropertyNameValue(sourceLineNumbers, attrib);
3380 break;
3381 case "Value":
3382 value = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
3383 break;
3384 case "Condition":
3385 condition = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3386 break;
3387 default:
3388 this.Core.UnexpectedAttribute(node, attrib);
3389 break;
3390 }
3391 }
3392 else
3393 {
3394 this.Core.ParseExtensionAttribute(node, attrib);
3395 }
3396 }
3397
3398 if (null == name)
3399 {
3400 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
3401 }
3402
3403 if (null == value)
3404 {
3405 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value"));
3406 }
3407
3408 this.Core.ParseForExtensionElements(node);
3409
3410 if (!this.Core.EncounteredError)
3411 {
3412 var symbol = this.Core.AddSymbol(new WixBundleMsiPropertySymbol(sourceLineNumbers, new Identifier(AccessModifier.Section, packageId, name))
3413 {
3414 PackageRef = packageId,
3415 Name = name,
3416 Value = value
3417 });
3418
3419 if (!String.IsNullOrEmpty(condition))
3420 {
3421 symbol.Condition = condition;
3422 }
3423 }
3424 }
3425
3426 /// <summary>
3427 /// Parse SlipstreamMsp element
3428 /// </summary>
3429 /// <param name="node">Element to parse</param>
3430 /// <param name="packageId">Id of parent element</param>
3431 private void ParseSlipstreamMspElement(XElement node, string packageId)
3432 {
3433 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
3434 string id = null;
3435
3436 foreach (var attrib in node.Attributes())
3437 {
3438 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
3439 {
3440 switch (attrib.Name.LocalName)
3441 {
3442 case "Id":
3443 id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
3444 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.WixBundlePackage, id);
3445 break;
3446 default:
3447 this.Core.UnexpectedAttribute(node, attrib);
3448 break;
3449 }
3450 }
3451 else
3452 {
3453 this.Core.ParseExtensionAttribute(node, attrib);
3454 }
3455 }
3456
3457 if (null == id)
3458 {
3459 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
3460 }
3461
3462 this.Core.ParseForExtensionElements(node);
3463
3464 if (!this.Core.EncounteredError)
3465 {
3466 this.Core.AddSymbol(new WixBundleSlipstreamMspSymbol(sourceLineNumbers, new Identifier(AccessModifier.Section, packageId, id))
3467 {
3468 TargetPackageRef = packageId,
3469 MspPackageRef = id
3470 });
3471 }
3472 }
3473
3474 /// <summary>
3475 /// Parse RelatedBundle element
3476 /// </summary>
3477 /// <param name="node">Element to parse</param>
3478 private void ParseRelatedBundleElement(XElement node)
3479 {
3480 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
3481 string id = null;
3482 var actionType = RelatedBundleActionType.Detect;
3483
3484 foreach (var attrib in node.Attributes())
3485 {
3486 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
3487 {
3488 switch (attrib.Name.LocalName)
3489 {
3490 case "Id":
3491 id = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
3492 break;
3493 case "Action":
3494 var action = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3495 switch (action)
3496 {
3497 case "Detect":
3498 case "detect":
3499 actionType = RelatedBundleActionType.Detect;
3500 break;
3501 case "Upgrade":
3502 case "upgrade":
3503 actionType = RelatedBundleActionType.Upgrade;
3504 break;
3505 case "Addon":
3506 case "addon":
3507 actionType = RelatedBundleActionType.Addon;
3508 break;
3509 case "Patch":
3510 case "patch":
3511 actionType = RelatedBundleActionType.Patch;
3512 break;
3513 case "":
3514 break;
3515 default:
3516 this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Action", action, "Detect", "Upgrade", "Addon", "Patch"));
3517 break;
3518 }
3519 break;
3520 default:
3521 this.Core.UnexpectedAttribute(node, attrib);
3522 break;
3523 }
3524 }
3525 else
3526 {
3527 this.Core.ParseExtensionAttribute(node, attrib);
3528 }
3529 }
3530
3531 if (null == id)
3532 {
3533 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
3534 }
3535
3536 this.Core.ParseForExtensionElements(node);
3537
3538 if (!this.Core.EncounteredError)
3539 {
3540 this.Core.AddSymbol(new WixRelatedBundleSymbol(sourceLineNumbers)
3541 {
3542 BundleId = id,
3543 Action = actionType,
3544 });
3545 }
3546 }
3547
3548 /// <summary>
3549 /// Parse Update element
3550 /// </summary>
3551 /// <param name="node">Element to parse</param>
3552 private void ParseUpdateElement(XElement node)
3553 {
3554 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
3555 string location = null;
3556
3557 foreach (var attrib in node.Attributes())
3558 {
3559 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
3560 {
3561 switch (attrib.Name.LocalName)
3562 {
3563 case "Location":
3564 location = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3565 break;
3566 default:
3567 this.Core.UnexpectedAttribute(node, attrib);
3568 break;
3569 }
3570 }
3571 else
3572 {
3573 this.Core.ParseExtensionAttribute(node, attrib);
3574 }
3575 }
3576
3577 if (null == location)
3578 {
3579 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Location"));
3580 }
3581
3582 this.Core.ParseForExtensionElements(node);
3583
3584 if (!this.Core.EncounteredError)
3585 {
3586 this.Core.AddSymbol(new WixBundleUpdateSymbol(sourceLineNumbers)
3587 {
3588 Location = location
3589 });
3590 }
3591 }
3592
3593 /// <summary>
3594 /// Parse SetVariable element
3595 /// </summary>
3596 /// <param name="node">Element to parse</param>
3597 private void ParseSetVariableElement(XElement node)
3598 {
3599 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
3600 Identifier id = null;
3601 string variable = null;
3602 string condition = null;
3603 string after = null;
3604 string value = null;
3605 string typeValue = null;
3606
3607 foreach (var attrib in node.Attributes())
3608 {
3609 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
3610 {
3611 switch (attrib.Name.LocalName)
3612 {
3613 case "Id":
3614 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
3615 break;
3616 case "Variable":
3617 variable = this.Core.GetAttributeBundleVariableNameValue(sourceLineNumbers, attrib);
3618 break;
3619 case "Condition":
3620 condition = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3621 break;
3622 case "After":
3623 after = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3624 break;
3625 case "Value":
3626 value = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3627 break;
3628 case "Type":
3629 typeValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3630 break;
3631
3632 default:
3633 this.Core.UnexpectedAttribute(node, attrib);
3634 break;
3635 }
3636 }
3637 else
3638 {
3639 this.Core.ParseExtensionAttribute(node, attrib, null);
3640 }
3641 }
3642
3643 var type = this.ValidateVariableTypeWithValue(sourceLineNumbers, node, typeValue, value);
3644
3645 this.Core.ParseForExtensionElements(node);
3646
3647 if (id == null)
3648 {
3649 id = this.Core.CreateIdentifier("sbv", variable, condition, after, value, type.ToString());
3650 }
3651
3652 if (!this.Messaging.EncounteredError)
3653 {
3654 this.Core.CreateWixSearchSymbol(sourceLineNumbers, node.Name.LocalName, id, variable, condition, after);
3655
3656 this.Core.AddSymbol(new WixSetVariableSymbol(sourceLineNumbers, id)
3657 {
3658 Value = value,
3659 Type = type,
3660 });
3661 }
3662 }
3663
3664 /// <summary>
3665 /// Parse Variable element
3666 /// </summary>
3667 /// <param name="node">Element to parse</param>
3668 private void ParseVariableElement(XElement node)
3669 {
3670 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
3671 var hidden = false;
3672 Identifier name = null;
3673 var persisted = false;
3674 string value = null;
3675 string typeValue = null;
3676
3677 foreach (var attrib in node.Attributes())
3678 {
3679 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
3680 {
3681 switch (attrib.Name.LocalName)
3682 {
3683 case "Hidden":
3684 if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
3685 {
3686 hidden = true;
3687 }
3688 break;
3689 case "Name":
3690 name = this.Core.GetAttributeBundleVariableNameIdentifier(sourceLineNumbers, attrib);
3691 break;
3692 case "Persisted":
3693 if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
3694 {
3695 persisted = true;
3696 }
3697 break;
3698 case "Value":
3699 value = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
3700 break;
3701 case "Type":
3702 typeValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3703 break;
3704 default:
3705 this.Core.UnexpectedAttribute(node, attrib);
3706 break;
3707 }
3708 }
3709 else
3710 {
3711 this.Core.ParseExtensionAttribute(node, attrib);
3712 }
3713 }
3714
3715 if (null == name)
3716 {
3717 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
3718 }
3719
3720 if (hidden && persisted)
3721 {
3722 this.Core.Write(ErrorMessages.IllegalAttributeValueWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Hidden", "yes", "Persisted"));
3723 }
3724
3725 var type = this.ValidateVariableTypeWithValue(sourceLineNumbers, node, typeValue, value);
3726
3727 this.Core.ParseForExtensionElements(node);
3728
3729 if (!this.Core.EncounteredError)
3730 {
3731 this.Core.AddSymbol(new WixBundleVariableSymbol(sourceLineNumbers, name)
3732 {
3733 Value = value,
3734 Type = type,
3735 Hidden = hidden,
3736 Persisted = persisted
3737 });
3738 }
3739 }
3740
3741 private WixBundleVariableType ValidateVariableTypeWithValue(SourceLineNumber sourceLineNumbers, XElement node, string typeValue, string value)
3742 {
3743 WixBundleVariableType type;
3744 switch (typeValue)
3745 {
3746 case "formatted":
3747 type = WixBundleVariableType.Formatted;
3748 break;
3749 case "numeric":
3750 type = WixBundleVariableType.Numeric;
3751 break;
3752 case "string":
3753 type = WixBundleVariableType.String;
3754 break;
3755 case "version":
3756 type = WixBundleVariableType.Version;
3757 break;
3758 case null:
3759 type = WixBundleVariableType.Unknown;
3760 break;
3761 default:
3762 this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Type", typeValue, "formatted", "numeric", "string", "version"));
3763 return WixBundleVariableType.Unknown;
3764 }
3765
3766 if (type != WixBundleVariableType.Unknown)
3767 {
3768 if (value == null)
3769 {
3770 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, "Variable", "Value", "Type"));
3771 }
3772
3773 return type;
3774 }
3775 else if (value == null)
3776 {
3777 return type;
3778 }
3779
3780 // Infer the type from the current value...
3781 if (value.StartsWith("v", StringComparison.OrdinalIgnoreCase))
3782 {
3783 // Version constructor does not support simple "v#" syntax so check to see if the value is
3784 // non-negative real quick.
3785 if (Int32.TryParse(value.Substring(1), NumberStyles.None, CultureInfo.InvariantCulture.NumberFormat, out var _))
3786 {
3787 return WixBundleVariableType.Version;
3788 }
3789 else if (Version.TryParse(value.Substring(1), out var _))
3790 {
3791 return WixBundleVariableType.Version;
3792 }
3793 }
3794
3795 // Not a version, check for numeric.
3796 if (Int64.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture.NumberFormat, out var _))
3797 {
3798 return WixBundleVariableType.Numeric;
3799 }
3800
3801 return WixBundleVariableType.String;
3802 }
3803 }
3804 }