@joebigelow / wix / commits / db22d99a

Enhance bundle backend validation.

Sean Hall committed Mar 15, 2022 at 18:09 UTC db22d99a4d603caab18fd42cb40881431c353912
26 files changed +926 -278
src/api/wix/WixToolset.Data/ErrorMessages.cs
+6
@@ -1184,6 +1184,11 @@ namespace WixToolset.Data
1184 return Message(null, Ids.InvalidCommandLineFileName, "Invalid file name specified on the command line: '{0}'. Error message: '{1}'", fileName, error);
1185 }
1186
1187 + public static Message InvalidBundleCondition(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string condition)
1188 + {
1189 + return Message(sourceLineNumbers, Ids.InvalidBundleCondition, "The {0}/@{1} attribute's value '{2}' is not a valid bundle condition.", elementName, attributeName, condition);
1190 + }
1191 +
1192 public static Message InvalidDateTimeFormat(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string value)
1193 {
1194 return Message(sourceLineNumbers, Ids.InvalidDateTimeFormat, "The {0}/@{1} attribute's value '{2}' is not a valid date/time value. A date/time value should follow the format YYYY-MM-DDTHH:mm:ss.", elementName, attributeName, value);
@@ -2686,6 +2691,7 @@ namespace WixToolset.Data
2691 MultiplePackagePayloads3 = 406,
2692 MissingPackagePayload = 407,
2693 ExpectedAttributeWithoutOtherAttributes = 408,
2694 + InvalidBundleCondition = 409,
2695 }
2696 }
2697 }
src/api/wix/WixToolset.Data/WarningMessages.cs
+6
@@ -593,6 +593,11 @@ namespace WixToolset.Data
593 return Message(null, Ids.UnableToResetAcls, "Unable to reset acls on destination files. Exception detail: {0}", error);
594 }
595
596 + public static Message UnavailableBundleConditionVariable(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string variable, string illegalValueList)
597 + {
598 + return Message(sourceLineNumbers, Ids.UnavailableBundleConditionVariable, "{0}/@{1} contains the built-in Variable '{2}', which is not available when it is evaluated. (Unavailable Variables are: {3}.). Rewrite the condition to avoid Variables that are never valid during its evaluation.", elementName, attributeName, variable, illegalValueList);
599 + }
600 +
601 public static Message UnclearShortcut(SourceLineNumber sourceLineNumbers, string shortcutId, string fileId, string componentId)
602 {
603 return Message(sourceLineNumbers, Ids.UnclearShortcut, "Because it is an advertised shortcut, the target of shortcut '{0}' will be the keypath of component '{2}' rather than parent file '{1}'. To eliminate this warning, you can (1) make the Shortcut element a child of the File element that is the keypath of component '{2}', (2) make file '{1}' the keypath of component '{2}', or (3) remove the @Advertise attribute so the shortcut is a non-advertised shortcut.", shortcutId, fileId, componentId);
@@ -804,6 +809,7 @@ namespace WixToolset.Data
809 DetectConditionRecommended = 1153,
810 CollidingModularizationTypes = 1156,
811 InvalidEnvironmentVariable = 1157,
812 + UnavailableBundleConditionVariable = 1159,
813 }
814 }
815 }
src/api/wix/WixToolset.Extensibility/Data/BundleConditionPhase.cs new
+35
@@ -0,0 +1,35 @@
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.Extensibility.Data
4 +{
5 + /// <summary>
6 + /// The Burn execution phase during which a Condition will be evaluated.
7 + /// </summary>
8 + public enum BundleConditionPhase
9 + {
10 + /// <summary>
11 + /// Condition is evaluated by the engine before loading the BootstrapperApplication (Bundle/@Condition).
12 + /// </summary>
13 + Startup,
14 +
15 + /// <summary>
16 + /// Condition is evaluated during Detect (ExePackage/@DetectCondition).
17 + /// </summary>
18 + Detect,
19 +
20 + /// <summary>
21 + /// Condition is evaluated during Plan (ExePackage/@InstallCondition).
22 + /// </summary>
23 + Plan,
24 +
25 + /// <summary>
26 + /// Condition is evaluated during Apply (MsiProperty/@Condition).
27 + /// </summary>
28 + Execute,
29 +
30 + /// <summary>
31 + /// Condition is evaluated after Apply.
32 + /// </summary>
33 + Shutdown,
34 + }
35 +}
src/api/wix/WixToolset.Extensibility/Services/IBackendHelper.cs
-11
@@ -73,17 +73,6 @@ namespace WixToolset.Extensibility.Services
73 /// <returns>The generated identifier.</returns>
74 string GenerateIdentifier(string prefix, params string[] args);
75
76 - /// <summary>
77 - /// Validates path is relative and canonicalizes it.
78 - /// For example, "a\..\c\.\d.exe" => "c\d.exe".
79 - /// </summary>
80 - /// <param name="sourceLineNumbers"></param>
81 - /// <param name="elementName"></param>
82 - /// <param name="attributeName"></param>
83 - /// <param name="relativePath"></param>
84 - /// <returns>The original value if not relative, otherwise the canonicalized relative path.</returns>
85 - string GetCanonicalRelativePath(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string relativePath);
86 -
76 /// <summary>
77 /// Gets a valid code page from the given web name or integer value.
78 /// </summary>
src/api/wix/WixToolset.Extensibility/Services/IBundleValidator.cs new
+56
@@ -0,0 +1,56 @@
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.Extensibility.Services
4 +{
5 + using WixToolset.Data;
6 + using WixToolset.Extensibility.Data;
7 +
8 + /// <summary>
9 + /// Interface provided to help with bundle validation.
10 + /// </summary>
11 + public interface IBundleValidator
12 + {
13 +
14 + /// <summary>
15 + /// Validates path is relative and canonicalizes it.
16 + /// For example, "a\..\c\.\d.exe" => "c\d.exe".
17 + /// </summary>
18 + /// <param name="sourceLineNumbers"></param>
19 + /// <param name="elementName"></param>
20 + /// <param name="attributeName"></param>
21 + /// <param name="relativePath"></param>
22 + /// <returns>The original value if not relative, otherwise the canonicalized relative path.</returns>
23 + string GetCanonicalRelativePath(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string relativePath);
24 +
25 + /// <summary>
26 + /// Validates an MsiProperty name value and displays an error for an illegal value.
27 + /// </summary>
28 + /// <param name="sourceLineNumbers"></param>
29 + /// <param name="elementName"></param>
30 + /// <param name="attributeName"></param>
31 + /// <param name="propertyName"></param>
32 + /// <returns>Whether the name is valid.</returns>
33 + bool ValidateBundleMsiPropertyName(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string propertyName);
34 +
35 + /// <summary>
36 + /// Validates a Bundle variable name and displays an error for an illegal value.
37 + /// </summary>
38 + /// <param name="sourceLineNumbers"></param>
39 + /// <param name="elementName"></param>
40 + /// <param name="attributeName"></param>
41 + /// <param name="variableName"></param>
42 + /// <returns>Whether the name is valid.</returns>
43 + bool ValidateBundleVariableName(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string variableName);
44 +
45 + /// <summary>
46 + /// Validates a bundle condition and displays an error for an illegal value.
47 + /// </summary>
48 + /// <param name="sourceLineNumbers"></param>
49 + /// <param name="elementName"></param>
50 + /// <param name="attributeName"></param>
51 + /// <param name="condition"></param>
52 + /// <param name="phase"></param>
53 + /// <returns>Whether the condition is valid.</returns>
54 + bool ValidateBundleCondition(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string condition, BundleConditionPhase phase);
55 + }
56 +}
src/api/wix/WixToolset.Extensibility/Services/IBurnBackendHelper.cs
+1 -1
@@ -7,7 +7,7 @@ namespace WixToolset.Extensibility.Services
7 /// <summary>
8 /// Interface provided to help Burn backend extensions.
9 /// </summary>
10 - public interface IBurnBackendHelper : IBackendHelper
10 + public interface IBurnBackendHelper : IBackendHelper, IBundleValidator
11 {
12 /// <summary>
13 /// Adds the given XML to the BootstrapperApplicationData manifest.
src/api/wix/WixToolset.Extensibility/Services/IParseHelper.cs
+1 -12
@@ -13,7 +13,7 @@ namespace WixToolset.Extensibility.Services
13 /// <summary>
14 /// Interface provided to help compiler extensions parse.
15 /// </summary>
16 - public interface IParseHelper
16 + public interface IParseHelper : IBundleValidator
17 {
18 /// <summary>
19 /// Creates a version 3 name-based UUID.
@@ -322,17 +322,6 @@ namespace WixToolset.Extensibility.Services
322 /// <returns>The attribute's YesNoType value.</returns>
323 YesNoDefaultType GetAttributeYesNoDefaultValue(SourceLineNumber sourceLineNumbers, XAttribute attribute);
324
325 - /// <summary>
326 - /// Validates path is relative and canonicalizes it.
327 - /// For example, "a\..\c\.\d.exe" => "c\d.exe".
328 - /// </summary>
329 - /// <param name="sourceLineNumbers"></param>
330 - /// <param name="elementName"></param>
331 - /// <param name="attributeName"></param>
332 - /// <param name="relativePath"></param>
333 - /// <returns>The original value if not relative, otherwise the canonicalized relative path.</returns>
334 - string GetCanonicalRelativePath(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string relativePath);
335 -
325 /// <summary>
326 /// Gets a source line number for an element.
327 /// </summary>
src/burn/test/BurnUnitTest/VariableTest.cpp
+1
@@ -381,6 +381,7 @@ namespace Bootstrapper
381 Assert::True(EvaluateConditionHelper(&variables, L"(PROP3 = \"NOT\" OR PROP1 = \"VAL1\") AND PROP2 = \"VAL2\""));
382 Assert::True(EvaluateConditionHelper(&variables, L"PROP3 = \"NOT\" OR (PROP1 = \"VAL1\" AND PROP2 = \"VAL2\")"));
383
384 + Assert::True(EvaluateFailureConditionHelper(&variables, L""));
385 Assert::True(EvaluateFailureConditionHelper(&variables, L"="));
386 Assert::True(EvaluateFailureConditionHelper(&variables, L"(PROP1"));
387 Assert::True(EvaluateFailureConditionHelper(&variables, L"(PROP1 = \""));
src/ext/Bal/test/WixToolsetTest.Bal/BalExtensionFixture.cs
+18 -4
@@ -2,6 +2,7 @@
2
3 namespace WixToolsetTest.Bal
4 {
5 + using System;
6 using System.IO;
7 using System.Linq;
8 using System.Xml;
@@ -138,20 +139,33 @@ namespace WixToolsetTest.Bal
139 {
140 var baseFolder = fs.GetFolder();
141 var bundleFile = Path.Combine(baseFolder, "bin", "test.exe");
141 - var bundleSourceFolder = TestData.Get(@"TestData\Overridable");
142 + var bundleSourceFolder = TestData.Get(@"TestData");
143 var intermediateFolder = Path.Combine(baseFolder, "obj");
144 var baFolderPath = Path.Combine(baseFolder, "ba");
145 var extractFolderPath = Path.Combine(baseFolder, "extract");
146
146 - var compileResult = WixRunner.Execute(new[]
147 + var result = WixRunner.Execute(new[]
148 {
149 "build",
149 - Path.Combine(bundleSourceFolder, "WrongCaseBundle.wxs"),
150 + Path.Combine(bundleSourceFolder, "Overridable", "WrongCaseBundle.wxs"),
151 + "-loc", Path.Combine(bundleSourceFolder, "Overridable", "WrongCaseBundle.wxl"),
152 + "-bindpath", Path.Combine(bundleSourceFolder, "WixStdBa", "Data"),
153 "-ext", TestData.Get(@"WixToolset.Bal.wixext.dll"),
154 "-intermediateFolder", intermediateFolder,
155 "-o", bundleFile,
156 });
154 - Assert.Equal((int)BalErrors.Ids.NonUpperCaseOverridableVariable, compileResult.ExitCode);
157 +
158 + Assert.InRange(result.ExitCode, 2, Int32.MaxValue);
159 +
160 + var messages = result.Messages.Select(m => m.ToString()).ToList();
161 + messages.Sort();
162 +
163 + WixAssert.CompareLineByLine(new[]
164 + {
165 + "bal:Condition/@Condition contains the built-in Variable 'WixBundleAction', which is not available when it is evaluated. (Unavailable Variables are: 'WixBundleAction'.). Rewrite the condition to avoid Variables that are never valid during its evaluation.",
166 + "Overridable variable 'Test1' must be 'TEST1' with Bundle/@CommandLineVariables value 'upperCase'.",
167 + "The *Package/@bal:DisplayInternalUICondition attribute's value '=' is not a valid bundle condition.",
168 + }, messages.ToArray());
169 }
170 }
171 }
src/ext/Bal/test/WixToolsetTest.Bal/TestData/Overridable/WrongCaseBundle.wxl new
+4
@@ -0,0 +1,4 @@
1 +<WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="en-US">
2 + <String Id="NonsenseDetectCondition">WixBundleAction = 4</String>
3 + <String Id="NonsensePlanCondition">=</String>
4 +</WixLocalization>
src/ext/Bal/test/WixToolsetTest.Bal/TestData/Overridable/WrongCaseBundle.wxs
+2
@@ -8,6 +8,8 @@
8 <Variable Name="Test1" bal:Overridable="yes" />
9 <Chain>
10 <ExePackage Permanent="yes" DetectCondition="none" SourceFile="runtimes\win-x86\native\wixnative.exe" />
11 + <MsiPackage SourceFile="test.msi" bal:DisplayInternalUICondition="!(loc.NonsensePlanCondition)" />
12 </Chain>
13 + <bal:Condition Condition="!(loc.NonsenseDetectCondition)" Message="Unsupported" />
14 </Bundle>
15 </Wix>
src/ext/Bal/wixext/BalBurnBackendExtension.cs
+22 -1
@@ -10,6 +10,7 @@ namespace WixToolset.Bal
10 using WixToolset.Data.Burn;
11 using WixToolset.Data.Symbols;
12 using WixToolset.Extensibility;
13 + using WixToolset.Extensibility.Data;
14
15 public class BalBurnBackendExtension : BaseBurnBackendBinderExtension
16 {
@@ -31,6 +32,8 @@ namespace WixToolset.Bal
32 {
33 base.SymbolsFinalized(section);
34
35 + this.VerifyBalConditions(section);
36 + this.VerifyBalPackageInfos(section);
37 this.VerifyOverridableVariables(section);
38
39 var baSymbol = section.Symbols.OfType<WixBootstrapperApplicationDllSymbol>().SingleOrDefault();
@@ -100,7 +103,7 @@ namespace WixToolset.Bal
103 {
104 foreach (var payloadPropertiesSymbol in payloadPropertiesSymbols)
105 {
103 - if (string.Equals(payloadPropertiesSymbol.Name, "bafunctions.dll", StringComparison.OrdinalIgnoreCase) &&
106 + if (String.Equals(payloadPropertiesSymbol.Name, "bafunctions.dll", StringComparison.OrdinalIgnoreCase) &&
107 BurnConstants.BurnUXContainerName == payloadPropertiesSymbol.ContainerRef)
108 {
109 this.Messaging.Write(BalWarnings.UnmarkedBAFunctionsDLL(payloadPropertiesSymbol.SourceLineNumbers));
@@ -120,6 +123,24 @@ namespace WixToolset.Bal
123 }
124 }
125
126 + private void VerifyBalConditions(IntermediateSection section)
127 + {
128 + var balConditionSymbols = section.Symbols.OfType<WixBalConditionSymbol>().ToList();
129 + foreach (var balConditionSymbol in balConditionSymbols)
130 + {
131 + this.BackendHelper.ValidateBundleCondition(balConditionSymbol.SourceLineNumbers, "bal:Condition", "Condition", balConditionSymbol.Condition, BundleConditionPhase.Detect);
132 + }
133 + }
134 +
135 + private void VerifyBalPackageInfos(IntermediateSection section)
136 + {
137 + var balPackageInfoSymbols = section.Symbols.OfType<WixBalPackageInfoSymbol>().ToList();
138 + foreach (var balPackageInfoSymbol in balPackageInfoSymbols)
139 + {
140 + this.BackendHelper.ValidateBundleCondition(balPackageInfoSymbol.SourceLineNumbers, "*Package", "bal:DisplayInternalUICondition", balPackageInfoSymbol.DisplayInternalUICondition, BundleConditionPhase.Plan);
141 + }
142 + }
143 +
144 private void VerifyOverridableVariables(IntermediateSection section)
145 {
146 var bundleSymbol = section.Symbols.OfType<WixBundleSymbol>().Single();
src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
+13 -2
@@ -150,7 +150,7 @@ namespace WixToolset.Core.Burn
150 // Process the explicitly authored payloads.
151 ISet<string> processedPayloads;
152 {
153 - var command = new ProcessPayloadsCommand(this.BackendHelper, this.PayloadHarvester, payloadSymbols.Values, bundleSymbol.DefaultPackagingType, layoutDirectory);
153 + var command = new ProcessPayloadsCommand(this.InternalBurnBackendHelper, this.PayloadHarvester, payloadSymbols.Values, bundleSymbol.DefaultPackagingType, layoutDirectory);
154 command.Execute();
155
156 fileTransfers.AddRange(command.FileTransfers);
@@ -228,7 +228,7 @@ namespace WixToolset.Core.Burn
228 {
229 var toProcess = payloadSymbols.Values.Where(r => !processedPayloads.Contains(r.Id.Id)).ToList();
230
231 - var command = new ProcessPayloadsCommand(this.BackendHelper, this.PayloadHarvester, toProcess, bundleSymbol.DefaultPackagingType, layoutDirectory);
231 + var command = new ProcessPayloadsCommand(this.InternalBurnBackendHelper, this.PayloadHarvester, toProcess, bundleSymbol.DefaultPackagingType, layoutDirectory);
232 command.Execute();
233
234 fileTransfers.AddRange(command.FileTransfers);
@@ -381,6 +381,17 @@ namespace WixToolset.Core.Burn
381 return;
382 }
383
384 + // Now that extensions can't change anything else, verify everything is still valid.
385 + {
386 + var command = new PerformBundleBackendValidationCommand(this.Messaging, this.InternalBurnBackendHelper, section, facades);
387 + command.Execute();
388 + }
389 +
390 + if (this.Messaging.EncounteredError)
391 + {
392 + return;
393 + }
394 +
395 // Generate data for all manifests.
396 {
397 var command = new GenerateManifestDataFromIRCommand(this.Messaging, section, this.BackendExtensions, this.InternalBurnBackendHelper, extensionSearchSymbolsById);
src/wix/WixToolset.Core.Burn/Bundles/PerformBundleBackendValidationCommand.cs new
+156
@@ -0,0 +1,156 @@
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 +
3 +namespace WixToolset.Core.Burn.Bundles
4 +{
5 + using System;
6 + using System.Collections.Generic;
7 + using WixToolset.Data;
8 + using WixToolset.Data.Symbols;
9 + using WixToolset.Extensibility.Data;
10 + using WixToolset.Extensibility.Services;
11 +
12 + internal class PerformBundleBackendValidationCommand
13 + {
14 + public PerformBundleBackendValidationCommand(IMessaging messaging, IBurnBackendHelper burnBackendHelper, IntermediateSection section, IDictionary<string, PackageFacade> packageFacadesById)
15 + {
16 + this.Messaging = messaging;
17 + this.BackendHelper = burnBackendHelper;
18 + this.Section = section;
19 + this.PackageFacadesById = packageFacadesById;
20 + }
21 +
22 + public IMessaging Messaging { get; }
23 +
24 + public IBurnBackendHelper BackendHelper { get; }
25 +
26 + public IntermediateSection Section { get; }
27 +
28 + public IDictionary<string, PackageFacade> PackageFacadesById { get; }
29 +
30 + public void Execute()
31 + {
32 + foreach (var symbol in this.Section.Symbols)
33 + {
34 + if (symbol is WixBundleSymbol wixBundleSymbol)
35 + {
36 + this.ValidateBundle(wixBundleSymbol);
37 + }
38 + else if (symbol is WixBundleMsiPropertySymbol wixBundleMsiPropertySymbol)
39 + {
40 + this.ValidateMsiProperty(wixBundleMsiPropertySymbol);
41 + }
42 + else if (symbol is WixBundleVariableSymbol wixBundleVariableSymbol)
43 + {
44 + this.ValidateVariable(wixBundleVariableSymbol);
45 + }
46 + else if (symbol is WixBundlePackageCommandLineSymbol wixBundlePackageCommandLineSymbol)
47 + {
48 + this.ValidatePackageCommandLine(wixBundlePackageCommandLineSymbol);
49 + }
50 + else if (symbol is WixSearchSymbol wixSearchSymbol)
51 + {
52 + this.ValidateSearch(wixSearchSymbol);
53 + }
54 + }
55 +
56 + foreach (var packageFacade in this.PackageFacadesById.Values)
57 + {
58 + if (packageFacade.SpecificPackageSymbol is WixBundleExePackageSymbol wixBundleExePackageSymbol)
59 + {
60 + this.ValidateExePackage(wixBundleExePackageSymbol, packageFacade.PackageSymbol);
61 + }
62 + else if (packageFacade.SpecificPackageSymbol is WixBundleMsiPackageSymbol wixBundleMsiPackageSymbol)
63 + {
64 + this.ValidateMsiPackage(wixBundleMsiPackageSymbol, packageFacade.PackageSymbol);
65 + }
66 + else if (packageFacade.SpecificPackageSymbol is WixBundleMspPackageSymbol wixBundleMspPackageSymbol)
67 + {
68 + this.ValidateMspPackage(wixBundleMspPackageSymbol, packageFacade.PackageSymbol);
69 + }
70 + else if (packageFacade.SpecificPackageSymbol is WixBundleMsuPackageSymbol wixBundleMsuPackageSymbol)
71 + {
72 + this.ValidateMsuPackage(wixBundleMsuPackageSymbol, packageFacade.PackageSymbol);
73 + }
74 + }
75 + }
76 +
77 + private void ValidateBundle(WixBundleSymbol symbol)
78 + {
79 + if (symbol.Condition != null)
80 + {
81 + this.BackendHelper.ValidateBundleCondition(symbol.SourceLineNumbers, "Bundle", "Condition", symbol.Condition, BundleConditionPhase.Startup);
82 + }
83 + }
84 +
85 + private void ValidateChainPackage(WixBundlePackageSymbol symbol, string elementName)
86 + {
87 + if (!String.IsNullOrEmpty(symbol.InstallCondition))
88 + {
89 + this.BackendHelper.ValidateBundleCondition(symbol.SourceLineNumbers, elementName, "InstallCondition", symbol.InstallCondition, BundleConditionPhase.Plan);
90 + }
91 + }
92 +
93 + private void ValidateExePackage(WixBundleExePackageSymbol symbol, WixBundlePackageSymbol packageSymbol)
94 + {
95 + this.ValidateChainPackage(packageSymbol, "ExePackage");
96 +
97 + if (!packageSymbol.Permanent)
98 + {
99 + this.BackendHelper.ValidateBundleCondition(symbol.SourceLineNumbers, "ExePackage", "DetectCondition", symbol.DetectCondition, BundleConditionPhase.Detect);
100 + }
101 + }
102 +
103 + private void ValidateMsiPackage(WixBundleMsiPackageSymbol symbol, WixBundlePackageSymbol packageSymbol)
104 + {
105 + this.ValidateChainPackage(packageSymbol, "MsiPackage");
106 + }
107 +
108 + private void ValidateMsiProperty(WixBundleMsiPropertySymbol symbol)
109 + {
110 + this.BackendHelper.ValidateBundleMsiPropertyName(symbol.SourceLineNumbers, "MsiProperty", "Name", symbol.Name);
111 +
112 + if (symbol.Condition != null)
113 + {
114 + this.BackendHelper.ValidateBundleCondition(symbol.SourceLineNumbers, "MsiProperty", "Condition", symbol.Condition, BundleConditionPhase.Execute);
115 + }
116 + }
117 +
118 + private void ValidateMspPackage(WixBundleMspPackageSymbol symbol, WixBundlePackageSymbol packageSymbol)
119 + {
120 + this.ValidateChainPackage(packageSymbol, "MspPackage");
121 + }
122 +
123 + private void ValidateMsuPackage(WixBundleMsuPackageSymbol symbol, WixBundlePackageSymbol packageSymbol)
124 + {
125 + this.ValidateChainPackage(packageSymbol, "MsuPackage");
126 +
127 + if (!packageSymbol.Permanent)
128 + {
129 + this.BackendHelper.ValidateBundleCondition(symbol.SourceLineNumbers, "MsuPackage", "DetectCondition", symbol.DetectCondition, BundleConditionPhase.Detect);
130 + }
131 + }
132 +
133 + private void ValidatePackageCommandLine(WixBundlePackageCommandLineSymbol symbol)
134 + {
135 + if (symbol.Condition != null)
136 + {
137 + this.BackendHelper.ValidateBundleCondition(symbol.SourceLineNumbers, "CommandLine", "Condition", symbol.Condition, BundleConditionPhase.Execute);
138 + }
139 + }
140 +
141 + private void ValidateSearch(WixSearchSymbol symbol)
142 + {
143 + this.BackendHelper.ValidateBundleVariableName(symbol.SourceLineNumbers, "*Search", "Variable", symbol.Variable);
144 +
145 + if (symbol.Condition != null)
146 + {
147 + this.BackendHelper.ValidateBundleCondition(symbol.SourceLineNumbers, "*Search", "Condition", symbol.Condition, BundleConditionPhase.Detect);
148 + }
149 + }
150 +
151 + private void ValidateVariable(WixBundleVariableSymbol symbol)
152 + {
153 + this.BackendHelper.ValidateBundleVariableName(symbol.SourceLineNumbers, "Variable", "Name", symbol.Id.Id);
154 + }
155 + }
156 +}
src/wix/WixToolset.Core.Burn/Bundles/ProcessPayloadsCommand.cs
+2 -2
@@ -15,7 +15,7 @@ namespace WixToolset.Core.Burn.Bundles
15
16 internal class ProcessPayloadsCommand
17 {
18 - public ProcessPayloadsCommand(IBackendHelper backendHelper, IPayloadHarvester payloadHarvester, IEnumerable<WixBundlePayloadSymbol> payloads, PackagingType defaultPackaging, string layoutDirectory)
18 + public ProcessPayloadsCommand(IBurnBackendHelper backendHelper, IPayloadHarvester payloadHarvester, IEnumerable<WixBundlePayloadSymbol> payloads, PackagingType defaultPackaging, string layoutDirectory)
19 {
20 this.BackendHelper = backendHelper;
21 this.PayloadHarvester = payloadHarvester;
@@ -28,7 +28,7 @@ namespace WixToolset.Core.Burn.Bundles
28
29 public IEnumerable<ITrackedFile> TrackedFiles { get; private set; }
30
31 - private IBackendHelper BackendHelper { get; }
31 + private IBurnBackendHelper BackendHelper { get; }
32
33 private IPayloadHarvester PayloadHarvester { get; }
34
src/wix/WixToolset.Core.Burn/ExtensibilityServices/BurnBackendHelper.cs
+100 -21
@@ -20,6 +20,7 @@ namespace WixToolset.Core.Burn.ExtensibilityServices
20 public static readonly XmlWriterSettings WriterSettings = new XmlWriterSettings { ConformanceLevel = ConformanceLevel.Fragment };
21
22 private readonly IBackendHelper backendHelper;
23 + private readonly IBundleValidator bundleValidator;
24
25 private ManifestData BootstrapperApplicationManifestData { get; } = new ManifestData();
26
@@ -28,49 +29,105 @@ namespace WixToolset.Core.Burn.ExtensibilityServices
29 public BurnBackendHelper(IServiceProvider serviceProvider)
30 {
31 this.backendHelper = serviceProvider.GetService<IBackendHelper>();
32 + this.bundleValidator = serviceProvider.GetService<IBundleValidator>();
33 }
34
35 #region IBackendHelper interfaces
36
35 - public IFileFacade CreateFileFacade(FileSymbol file, AssemblySymbol assembly) => this.backendHelper.CreateFileFacade(file, assembly);
36 -
37 - public IFileFacade CreateFileFacade(FileRow fileRow) => this.backendHelper.CreateFileFacade(fileRow);
37 + public IFileFacade CreateFileFacade(FileSymbol file, AssemblySymbol assembly)
38 + {
39 + return this.backendHelper.CreateFileFacade(file, assembly);
40 + }
41
39 - public IFileFacade CreateFileFacadeFromMergeModule(FileSymbol fileSymbol) => this.backendHelper.CreateFileFacadeFromMergeModule(fileSymbol);
42 + public IFileFacade CreateFileFacade(FileRow fileRow)
43 + {
44 + return this.backendHelper.CreateFileFacade(fileRow);
45 + }
46
41 - public IFileTransfer CreateFileTransfer(string source, string destination, bool move, SourceLineNumber sourceLineNumbers = null) => this.backendHelper.CreateFileTransfer(source, destination, move, sourceLineNumbers);
47 + public IFileFacade CreateFileFacadeFromMergeModule(FileSymbol fileSymbol)
48 + {
49 + return this.backendHelper.CreateFileFacadeFromMergeModule(fileSymbol);
50 + }
51
43 - public string CreateGuid() => this.backendHelper.CreateGuid();
52 + public IFileTransfer CreateFileTransfer(string source, string destination, bool move, SourceLineNumber sourceLineNumbers = null)
53 + {
54 + return this.backendHelper.CreateFileTransfer(source, destination, move, sourceLineNumbers);
55 + }
56
45 - public string CreateGuid(Guid namespaceGuid, string value) => this.backendHelper.CreateGuid(namespaceGuid, value);
57 + public string CreateGuid()
58 + {
59 + return this.backendHelper.CreateGuid();
60 + }
61
47 - public IResolvedDirectory CreateResolvedDirectory(string directoryParent, string name) => this.backendHelper.CreateResolvedDirectory(directoryParent, name);
62 + public string CreateGuid(Guid namespaceGuid, string value)
63 + {
64 + return this.backendHelper.CreateGuid(namespaceGuid, value);
65 + }
66
49 - public IReadOnlyList<ITrackedFile> ExtractEmbeddedFiles(IEnumerable<IExpectedExtractFile> embeddedFiles) => this.backendHelper.ExtractEmbeddedFiles(embeddedFiles);
67 + public IResolvedDirectory CreateResolvedDirectory(string directoryParent, string name)
68 + {
69 + return this.backendHelper.CreateResolvedDirectory(directoryParent, name);
70 + }
71
51 - public string GenerateIdentifier(string prefix, params string[] args) => this.backendHelper.GenerateIdentifier(prefix, args);
72 + public IReadOnlyList<ITrackedFile> ExtractEmbeddedFiles(IEnumerable<IExpectedExtractFile> embeddedFiles)
73 + {
74 + return this.backendHelper.ExtractEmbeddedFiles(embeddedFiles);
75 + }
76
53 - public string GetCanonicalRelativePath(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string relativePath) => this.backendHelper.GetCanonicalRelativePath(sourceLineNumbers, elementName, attributeName, relativePath);
77 + public string GenerateIdentifier(string prefix, params string[] args)
78 + {
79 + return this.backendHelper.GenerateIdentifier(prefix, args);
80 + }
81
55 - public int GetValidCodePage(string value, bool allowNoChange, bool onlyAnsi = false, SourceLineNumber sourceLineNumbers = null) => this.backendHelper.GetValidCodePage(value, allowNoChange, onlyAnsi, sourceLineNumbers);
82 + public int GetValidCodePage(string value, bool allowNoChange, bool onlyAnsi = false, SourceLineNumber sourceLineNumbers = null)
83 + {
84 + return this.backendHelper.GetValidCodePage(value, allowNoChange, onlyAnsi, sourceLineNumbers);
85 + }
86
57 - public string GetMsiFileName(string value, bool source, bool longName) => this.backendHelper.GetMsiFileName(value, source, longName);
87 + public string GetMsiFileName(string value, bool source, bool longName)
88 + {
89 + return this.backendHelper.GetMsiFileName(value, source, longName);
90 + }
91
59 - public bool IsValidBinderVariable(string variable) => this.backendHelper.IsValidBinderVariable(variable);
92 + public bool IsValidBinderVariable(string variable)
93 + {
94 + return this.backendHelper.IsValidBinderVariable(variable);
95 + }
96
61 - public bool IsValidFourPartVersion(string version) => this.backendHelper.IsValidFourPartVersion(version);
97 + public bool IsValidFourPartVersion(string version)
98 + {
99 + return this.backendHelper.IsValidFourPartVersion(version);
100 + }
101
63 - public bool IsValidIdentifier(string id) => this.backendHelper.IsValidIdentifier(id);
102 + public bool IsValidIdentifier(string id)
103 + {
104 + return this.backendHelper.IsValidIdentifier(id);
105 + }
106
65 - public bool IsValidLongFilename(string filename, bool allowWildcards, bool allowRelative) => this.backendHelper.IsValidLongFilename(filename, allowWildcards, allowRelative);
107 + public bool IsValidLongFilename(string filename, bool allowWildcards, bool allowRelative)
108 + {
109 + return this.backendHelper.IsValidLongFilename(filename, allowWildcards, allowRelative);
110 + }
111
67 - public bool IsValidShortFilename(string filename, bool allowWildcards) => this.backendHelper.IsValidShortFilename(filename, allowWildcards);
112 + public bool IsValidShortFilename(string filename, bool allowWildcards)
113 + {
114 + return this.backendHelper.IsValidShortFilename(filename, allowWildcards);
115 + }
116
69 - public void ResolveDelayedFields(IEnumerable<IDelayedField> delayedFields, Dictionary<string, string> variableCache) => this.backendHelper.ResolveDelayedFields(delayedFields, variableCache);
117 + public void ResolveDelayedFields(IEnumerable<IDelayedField> delayedFields, Dictionary<string, string> variableCache)
118 + {
119 + this.backendHelper.ResolveDelayedFields(delayedFields, variableCache);
120 + }
121
71 - public string[] SplitMsiFileName(string value) => this.backendHelper.SplitMsiFileName(value);
122 + public string[] SplitMsiFileName(string value)
123 + {
124 + return this.backendHelper.SplitMsiFileName(value);
125 + }
126
73 - public ITrackedFile TrackFile(string path, TrackedFileType type, SourceLineNumber sourceLineNumbers = null) => this.backendHelper.TrackFile(path, type, sourceLineNumbers);
127 + public ITrackedFile TrackFile(string path, TrackedFileType type, SourceLineNumber sourceLineNumbers = null)
128 + {
129 + return this.backendHelper.TrackFile(path, type, sourceLineNumbers);
130 + }
131
132 #endregion
133
@@ -100,6 +157,28 @@ namespace WixToolset.Core.Burn.ExtensibilityServices
157
158 #endregion
159
160 + #region IBundleValidator
161 + public string GetCanonicalRelativePath(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string relativePath)
162 + {
163 + return this.bundleValidator.GetCanonicalRelativePath(sourceLineNumbers, elementName, attributeName, relativePath);
164 + }
165 +
166 + public bool ValidateBundleMsiPropertyName(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string propertyName)
167 + {
168 + return this.bundleValidator.ValidateBundleMsiPropertyName(sourceLineNumbers, elementName, attributeName, propertyName);
169 + }
170 +
171 + public bool ValidateBundleVariableName(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string variableName)
172 + {
173 + return this.bundleValidator.ValidateBundleVariableName(sourceLineNumbers, elementName, attributeName, variableName);
174 + }
175 +
176 + public bool ValidateBundleCondition(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string condition, BundleConditionPhase phase)
177 + {
178 + return this.bundleValidator.ValidateBundleCondition(sourceLineNumbers, elementName, attributeName, condition, phase);
179 + }
180 + #endregion
181 +
182 #region IInternalBurnBackendHelper interfaces
183
184 public void WriteBootstrapperApplicationData(XmlWriter writer)
src/wix/WixToolset.Core.WindowsInstaller/ExtensibilityServices/WindowsInstallerBackendHelper.cs
+76 -21
@@ -23,45 +23,100 @@ namespace WixToolset.Core.WindowsInstaller.ExtensibilityServices
23
24 #region IBackendHelper interfaces
25
26 - public IFileFacade CreateFileFacade(FileSymbol file, AssemblySymbol assembly) => this.backendHelper.CreateFileFacade(file, assembly);
27 -
28 - public IFileFacade CreateFileFacade(FileRow fileRow) => this.backendHelper.CreateFileFacade(fileRow);
26 + public IFileFacade CreateFileFacade(FileSymbol file, AssemblySymbol assembly)
27 + {
28 + return this.backendHelper.CreateFileFacade(file, assembly);
29 + }
30
30 - public IFileFacade CreateFileFacadeFromMergeModule(FileSymbol fileSymbol) => this.backendHelper.CreateFileFacadeFromMergeModule(fileSymbol);
31 + public IFileFacade CreateFileFacade(FileRow fileRow)
32 + {
33 + return this.backendHelper.CreateFileFacade(fileRow);
34 + }
35
32 - public IFileTransfer CreateFileTransfer(string source, string destination, bool move, SourceLineNumber sourceLineNumbers = null) => this.backendHelper.CreateFileTransfer(source, destination, move, sourceLineNumbers);
36 + public IFileFacade CreateFileFacadeFromMergeModule(FileSymbol fileSymbol)
37 + {
38 + return this.backendHelper.CreateFileFacadeFromMergeModule(fileSymbol);
39 + }
40
34 - public string CreateGuid() => this.backendHelper.CreateGuid();
41 + public IFileTransfer CreateFileTransfer(string source, string destination, bool move, SourceLineNumber sourceLineNumbers = null)
42 + {
43 + return this.backendHelper.CreateFileTransfer(source, destination, move, sourceLineNumbers);
44 + }
45
36 - public string CreateGuid(Guid namespaceGuid, string value) => this.backendHelper.CreateGuid(namespaceGuid, value);
46 + public string CreateGuid()
47 + {
48 + return this.backendHelper.CreateGuid();
49 + }
50
38 - public IResolvedDirectory CreateResolvedDirectory(string directoryParent, string name) => this.backendHelper.CreateResolvedDirectory(directoryParent, name);
51 + public string CreateGuid(Guid namespaceGuid, string value)
52 + {
53 + return this.backendHelper.CreateGuid(namespaceGuid, value);
54 + }
55
40 - public IReadOnlyList<ITrackedFile> ExtractEmbeddedFiles(IEnumerable<IExpectedExtractFile> embeddedFiles) => this.backendHelper.ExtractEmbeddedFiles(embeddedFiles);
56 + public IResolvedDirectory CreateResolvedDirectory(string directoryParent, string name)
57 + {
58 + return this.backendHelper.CreateResolvedDirectory(directoryParent, name);
59 + }
60
42 - public string GenerateIdentifier(string prefix, params string[] args) => this.backendHelper.GenerateIdentifier(prefix, args);
61 + public IReadOnlyList<ITrackedFile> ExtractEmbeddedFiles(IEnumerable<IExpectedExtractFile> embeddedFiles)
62 + {
63 + return this.backendHelper.ExtractEmbeddedFiles(embeddedFiles);
64 + }
65
44 - public string GetCanonicalRelativePath(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string relativePath) => this.backendHelper.GetCanonicalRelativePath(sourceLineNumbers, elementName, attributeName, relativePath);
66 + public string GenerateIdentifier(string prefix, params string[] args)
67 + {
68 + return this.backendHelper.GenerateIdentifier(prefix, args);
69 + }
70
46 - public int GetValidCodePage(string value, bool allowNoChange, bool onlyAnsi = false, SourceLineNumber sourceLineNumbers = null) => this.backendHelper.GetValidCodePage(value, allowNoChange, onlyAnsi, sourceLineNumbers);
71 + public int GetValidCodePage(string value, bool allowNoChange, bool onlyAnsi = false, SourceLineNumber sourceLineNumbers = null)
72 + {
73 + return this.backendHelper.GetValidCodePage(value, allowNoChange, onlyAnsi, sourceLineNumbers);
74 + }
75
48 - public string GetMsiFileName(string value, bool source, bool longName) => this.backendHelper.GetMsiFileName(value, source, longName);
76 + public string GetMsiFileName(string value, bool source, bool longName)
77 + {
78 + return this.backendHelper.GetMsiFileName(value, source, longName);
79 + }
80
50 - public bool IsValidBinderVariable(string variable) => this.backendHelper.IsValidBinderVariable(variable);
81 + public bool IsValidBinderVariable(string variable)
82 + {
83 + return this.backendHelper.IsValidBinderVariable(variable);
84 + }
85
52 - public bool IsValidFourPartVersion(string version) => this.backendHelper.IsValidFourPartVersion(version);
86 + public bool IsValidFourPartVersion(string version)
87 + {
88 + return this.backendHelper.IsValidFourPartVersion(version);
89 + }
90
54 - public bool IsValidIdentifier(string id) => this.backendHelper.IsValidIdentifier(id);
91 + public bool IsValidIdentifier(string id)
92 + {
93 + return this.backendHelper.IsValidIdentifier(id);
94 + }
95
56 - public bool IsValidLongFilename(string filename, bool allowWildcards, bool allowRelative) => this.backendHelper.IsValidLongFilename(filename, allowWildcards, allowRelative);
96 + public bool IsValidLongFilename(string filename, bool allowWildcards, bool allowRelative)
97 + {
98 + return this.backendHelper.IsValidLongFilename(filename, allowWildcards, allowRelative);
99 + }
100
58 - public bool IsValidShortFilename(string filename, bool allowWildcards) => this.backendHelper.IsValidShortFilename(filename, allowWildcards);
101 + public bool IsValidShortFilename(string filename, bool allowWildcards)
102 + {
103 + return this.backendHelper.IsValidShortFilename(filename, allowWildcards);
104 + }
105
60 - public void ResolveDelayedFields(IEnumerable<IDelayedField> delayedFields, Dictionary<string, string> variableCache) => this.backendHelper.ResolveDelayedFields(delayedFields, variableCache);
106 + public void ResolveDelayedFields(IEnumerable<IDelayedField> delayedFields, Dictionary<string, string> variableCache)
107 + {
108 + this.backendHelper.ResolveDelayedFields(delayedFields, variableCache);
109 + }
110
62 - public string[] SplitMsiFileName(string value) => this.backendHelper.SplitMsiFileName(value);
111 + public string[] SplitMsiFileName(string value)
112 + {
113 + return this.backendHelper.SplitMsiFileName(value);
114 + }
115
64 - public ITrackedFile TrackFile(string path, TrackedFileType type, SourceLineNumber sourceLineNumbers = null) => this.backendHelper.TrackFile(path, type, sourceLineNumbers);
116 + public ITrackedFile TrackFile(string path, TrackedFileType type, SourceLineNumber sourceLineNumbers = null)
117 + {
118 + return this.backendHelper.TrackFile(path, type, sourceLineNumbers);
119 + }
120
121 #endregion
122
src/wix/WixToolset.Core/Common.cs
-21
@@ -27,27 +27,6 @@ namespace WixToolset.Core
27 internal static readonly char[] IllegalRelativeLongFilenameCharacters = new[] { '?', '*', '|', '>', '<', ':', '\"' }; // like illegal, but we allow '\' and '/'
28 internal static readonly char[] IllegalWildcardLongFilenameCharacters = new[] { '\\', '/', '|', '>', '<', ':', '\"' }; // like illegal: but we allow '*' and '?'
29
30 - public static string GetCanonicalRelativePath(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string relativePath, IMessaging messageHandler)
31 - {
32 - const string root = @"C:\";
33 - if (!Path.IsPathRooted(relativePath))
34 - {
35 - var normalizedPath = Path.GetFullPath(root + relativePath);
36 - if (normalizedPath.StartsWith(root))
37 - {
38 - var canonicalizedPath = normalizedPath.Substring(root.Length);
39 - if (canonicalizedPath != relativePath)
40 - {
41 - messageHandler.Write(WarningMessages.PathCanonicalized(sourceLineNumbers, elementName, attributeName, relativePath, canonicalizedPath));
42 - }
43 - return canonicalizedPath;
44 - }
45 - }
46 -
47 - messageHandler.Write(ErrorMessages.PayloadMustBeRelativeToCache(sourceLineNumbers, elementName, attributeName, relativePath));
48 - return relativePath;
49 - }
50 -
30 /// <summary>
31 /// Gets a valid code page from the given web name or integer value.
32 /// </summary>
src/wix/WixToolset.Core/CompilerCore.cs
+2 -171
@@ -17,24 +17,6 @@ namespace WixToolset.Core
17 using WixToolset.Extensibility.Data;
18 using WixToolset.Extensibility.Services;
19
20 - internal enum ValueListKind
21 - {
22 - /// <summary>
23 - /// A list of values with nothing before the final value.
24 - /// </summary>
25 - None,
26 -
27 - /// <summary>
28 - /// A list of values with 'and' before the final value.
29 - /// </summary>
30 - And,
31 -
32 - /// <summary>
33 - /// A list of values with 'or' before the final value.
34 - /// </summary>
35 - Or
36 - }
37 -
20 /// <summary>
21 /// Core class for the compiler.
22 /// </summary>
@@ -43,80 +25,6 @@ namespace WixToolset.Core
25 internal static readonly XNamespace W3SchemaPrefix = "http://www.w3.org/";
26 internal static readonly XNamespace WixNamespace = "http://wixtoolset.org/schemas/v4/wxs";
27
46 - // Built-in variables (from burn\engine\variable.cpp, "vrgBuiltInVariables", around line 113)
47 - private static readonly List<string> BuiltinBundleVariables = new List<string>(
48 - new string[] {
49 - "AdminToolsFolder",
50 - "AppDataFolder",
51 - "CommonAppDataFolder",
52 - "CommonFiles64Folder",
53 - "CommonFilesFolder",
54 - "CompatibilityMode",
55 - "Date",
56 - "DesktopFolder",
57 - "FavoritesFolder",
58 - "FontsFolder",
59 - "InstallerName",
60 - "InstallerVersion",
61 - "LocalAppDataFolder",
62 - "LogonUser",
63 - "MyPicturesFolder",
64 - "NativeMachine",
65 - "NTProductType",
66 - "NTSuiteBackOffice",
67 - "NTSuiteDataCenter",
68 - "NTSuiteEnterprise",
69 - "NTSuitePersonal",
70 - "NTSuiteSmallBusiness",
71 - "NTSuiteSmallBusinessRestricted",
72 - "NTSuiteWebServer",
73 - "PersonalFolder",
74 - "Privileged",
75 - "ProgramFiles64Folder",
76 - "ProgramFiles6432Folder",
77 - "ProgramFilesFolder",
78 - "ProgramMenuFolder",
79 - "RebootPending",
80 - "SendToFolder",
81 - "ServicePackLevel",
82 - "StartMenuFolder",
83 - "StartupFolder",
84 - "System64Folder",
85 - "SystemFolder",
86 - "TempFolder",
87 - "TemplateFolder",
88 - "TerminalServer",
89 - "UserLanguageID",
90 - "UserUILanguageID",
91 - "VersionMsi",
92 - "VersionNT",
93 - "VersionNT64",
94 - "WindowsFolder",
95 - "WindowsVolume",
96 - "WixBundleAction",
97 - "WixBundleCommandLineAction",
98 - "WixBundleForcedRestartPackage",
99 - "WixBundleElevated",
100 - "WixBundleInstalled",
101 - "WixBundleProviderKey",
102 - "WixBundleTag",
103 - "WixBundleVersion",
104 - });
105 -
106 - private static readonly List<string> DisallowedMsiProperties = new List<string>(
107 - new string[] {
108 - "ACTION",
109 - "ADDLOCAL",
110 - "ADDSOURCE",
111 - "ADDDEFAULT",
112 - "ADVERTISE",
113 - "ALLUSERS",
114 - "REBOOT",
115 - "REINSTALL",
116 - "REINSTALLMODE",
117 - "REMOVE"
118 - });
119 -
28 private readonly Dictionary<XNamespace, ICompilerExtension> extensions;
29 private readonly IParseHelper parseHelper;
30 private readonly Intermediate intermediate;
@@ -857,11 +765,7 @@ namespace WixToolset.Core
765
766 if (!String.IsNullOrEmpty(value))
767 {
860 - if (CompilerCore.BuiltinBundleVariables.Contains(value))
861 - {
862 - string illegalValues = CompilerCore.CreateValueList(ValueListKind.Or, CompilerCore.BuiltinBundleVariables);
863 - this.Write(ErrorMessages.IllegalAttributeValueWithIllegalList(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, illegalValues));
864 - }
768 + this.parseHelper.ValidateBundleVariableName(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value);
769 }
770
771 return value;
@@ -879,11 +783,7 @@ namespace WixToolset.Core
783
784 if (0 < value.Length)
785 {
882 - if (CompilerCore.DisallowedMsiProperties.Contains(value))
883 - {
884 - string illegalValues = CompilerCore.CreateValueList(ValueListKind.Or, CompilerCore.DisallowedMsiProperties);
885 - this.Write(ErrorMessages.DisallowedMsiProperty(sourceLineNumbers, value, illegalValues));
886 - }
786 + this.parseHelper.ValidateBundleMsiPropertyName(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value);
787 }
788
789 return value;
@@ -1095,74 +995,5 @@ namespace WixToolset.Core
995 {
996 return this.parseHelper.ScheduleActionSymbol(this.ActiveSection, sourceLineNumbers, access, sequence, actionName, condition, beforeAction, afterAction, overridable);
997 }
1098 -
1099 - private static string CreateValueList(ValueListKind kind, IEnumerable<string> values)
1100 - {
1101 - // Ideally, we could denote the list kind (and the list itself) directly in the
1102 - // message XML, and detect and expand in the MessageHandler.GenerateMessageString()
1103 - // method. Doing so would make vararg-style messages much easier, but impacts
1104 - // every single message we format. For now, callers just have to know when a
1105 - // message takes a list of values in a single string argument, the caller will
1106 - // have to do the expansion themselves. (And, unfortunately, hard-code the knowledge
1107 - // that the list is an 'and' or 'or' list.)
1108 -
1109 - // For a localizable solution, we need to be able to get the list format string
1110 - // from resources. We aren't currently localized right now, so the values are
1111 - // just hard-coded.
1112 - const string valueFormat = "'{0}'";
1113 - const string valueSeparator = ", ";
1114 - string terminalTerm = String.Empty;
1115 -
1116 - switch (kind)
1117 - {
1118 - case ValueListKind.None:
1119 - terminalTerm = "";
1120 - break;
1121 - case ValueListKind.And:
1122 - terminalTerm = "and ";
1123 - break;
1124 - case ValueListKind.Or:
1125 - terminalTerm = "or ";
1126 - break;
1127 - }
1128 -
1129 - StringBuilder list = new StringBuilder();
1130 -
1131 - // This weird construction helps us determine when we're adding the last value
1132 - // to the list. Instead of adding them as we encounter them, we cache the current
1133 - // value and append the *previous* one.
1134 - string previousValue = null;
1135 - bool haveValues = false;
1136 - foreach (string value in values)
1137 - {
1138 - if (null != previousValue)
1139 - {
1140 - if (haveValues)
1141 - {
1142 - list.Append(valueSeparator);
1143 - }
1144 - list.AppendFormat(valueFormat, previousValue);
1145 - haveValues = true;
1146 - }
1147 -
1148 - previousValue = value;
1149 - }
1150 -
1151 - // If we have no previous value, that means that the list contained no values, and
1152 - // something has gone very wrong.
1153 - Debug.Assert(null != previousValue);
1154 - if (null != previousValue)
1155 - {
1156 - if (haveValues)
1157 - {
1158 - list.Append(valueSeparator);
1159 - list.Append(terminalTerm);
1160 - }
1161 - list.AppendFormat(valueFormat, previousValue);
1162 - haveValues = true;
1163 - }
1164 -
1165 - return list.ToString();
1166 - }
998 }
999 }
src/wix/WixToolset.Core/ExtensibilityServices/BackendHelper.cs
-5
@@ -64,11 +64,6 @@ namespace WixToolset.Core.ExtensibilityServices
64 return Common.GenerateIdentifier(prefix, args);
65 }
66
67 - public string GetCanonicalRelativePath(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string relativePath)
68 - {
69 - return Common.GetCanonicalRelativePath(sourceLineNumbers, elementName, attributeName, relativePath, this.Messaging);
70 - }
71 -
67 public int GetValidCodePage(string value, bool allowNoChange = false, bool onlyAnsi = false, SourceLineNumber sourceLineNumbers = null)
68 {
69 return Common.GetValidCodePage(value, allowNoChange, onlyAnsi, sourceLineNumbers);
src/wix/WixToolset.Core/ExtensibilityServices/BundleValidator.cs new
+326
@@ -0,0 +1,326 @@
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.ExtensibilityServices
4 +{
5 + using System;
6 + using System.Collections.Generic;
7 + using System.Diagnostics;
8 + using System.IO;
9 + using System.Text;
10 + using WixToolset.Data;
11 + using WixToolset.Extensibility.Data;
12 + using WixToolset.Extensibility.Services;
13 +
14 + internal class BundleValidator : IBundleValidator
15 + {
16 + public BundleValidator(IServiceProvider serviceProvider)
17 + {
18 + this.Messaging = serviceProvider.GetService<IMessaging>();
19 + }
20 +
21 + protected IMessaging Messaging { get; }
22 +
23 + private enum ValueListKind
24 + {
25 + /// <summary>
26 + /// A list of values with nothing before the final value.
27 + /// </summary>
28 + None,
29 +
30 + /// <summary>
31 + /// A list of values with 'and' before the final value.
32 + /// </summary>
33 + And,
34 +
35 + /// <summary>
36 + /// A list of values with 'or' before the final value.
37 + /// </summary>
38 + Or,
39 + }
40 +
41 + // Built-in variables (from burn\engine\variable.cpp, "vrgBuiltInVariables", around line 207)
42 + private static readonly List<string> BuiltinBundleVariables = new List<string>(
43 + new string[] {
44 + "AdminToolsFolder",
45 + "AppDataFolder",
46 + "CommonAppDataFolder",
47 + "CommonFiles64Folder",
48 + "CommonFilesFolder",
49 + "CompatibilityMode",
50 + "Date",
51 + "DesktopFolder",
52 + "FavoritesFolder",
53 + "FontsFolder",
54 + "InstallerName",
55 + "InstallerVersion",
56 + "LocalAppDataFolder",
57 + "LogonUser",
58 + "MyPicturesFolder",
59 + "NativeMachine",
60 + "NTProductType",
61 + "NTSuiteBackOffice",
62 + "NTSuiteDataCenter",
63 + "NTSuiteEnterprise",
64 + "NTSuitePersonal",
65 + "NTSuiteSmallBusiness",
66 + "NTSuiteSmallBusinessRestricted",
67 + "NTSuiteWebServer",
68 + "PersonalFolder",
69 + "Privileged",
70 + "ProgramFiles64Folder",
71 + "ProgramFiles6432Folder",
72 + "ProgramFilesFolder",
73 + "ProgramMenuFolder",
74 + "RebootPending",
75 + "SendToFolder",
76 + "ServicePackLevel",
77 + "StartMenuFolder",
78 + "StartupFolder",
79 + "System64Folder",
80 + "SystemFolder",
81 + "TempFolder",
82 + "TemplateFolder",
83 + "TerminalServer",
84 + "UserLanguageID",
85 + "UserUILanguageID",
86 + "VersionMsi",
87 + "VersionNT",
88 + "VersionNT64",
89 + "WindowsFolder",
90 + "WindowsVolume",
91 + "WixBundleAction",
92 + "WixBundleCommandLineAction",
93 + "WixBundleForcedRestartPackage",
94 + "WixBundleElevated",
95 + "WixBundleInstalled",
96 + "WixBundleProviderKey",
97 + "WixBundleTag",
98 + "WixBundleVersion",
99 + });
100 +
101 + private static readonly List<string> DisallowedMsiProperties = new List<string>(
102 + new string[] {
103 + "ACTION",
104 + "ADDLOCAL",
105 + "ADDSOURCE",
106 + "ADDDEFAULT",
107 + "ADVERTISE",
108 + "ALLUSERS",
109 + "REBOOT",
110 + "REINSTALL",
111 + "REINSTALLMODE",
112 + "REMOVE"
113 + });
114 +
115 + private static readonly List<string> UnavailableStartupVariables = new List<string>(
116 + new string[] {
117 + "RebootPending",
118 + "WixBundleAction",
119 + "WixBundleInstalled",
120 + });
121 +
122 + private static readonly List<string> UnavailableDetectVariables = new List<string>(
123 + new string[] {
124 + "WixBundleAction",
125 + });
126 +
127 + public string GetCanonicalRelativePath(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string relativePath)
128 + {
129 + const string root = @"C:\";
130 + if (!Path.IsPathRooted(relativePath))
131 + {
132 + var normalizedPath = Path.GetFullPath(root + relativePath);
133 + if (normalizedPath.StartsWith(root))
134 + {
135 + var canonicalizedPath = normalizedPath.Substring(root.Length);
136 + if (canonicalizedPath != relativePath)
137 + {
138 + this.Messaging.Write(WarningMessages.PathCanonicalized(sourceLineNumbers, elementName, attributeName, relativePath, canonicalizedPath));
139 + }
140 + return canonicalizedPath;
141 + }
142 + }
143 +
144 + this.Messaging.Write(ErrorMessages.PayloadMustBeRelativeToCache(sourceLineNumbers, elementName, attributeName, relativePath));
145 + return relativePath;
146 + }
147 +
148 + public bool ValidateBundleVariableName(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string variableName)
149 + {
150 + if (String.IsNullOrEmpty(variableName))
151 + {
152 + this.Messaging.Write(ErrorMessages.IllegalEmptyAttributeValue(sourceLineNumbers, elementName, attributeName));
153 +
154 + return false;
155 + }
156 + else if (BuiltinBundleVariables.Contains(variableName))
157 + {
158 + var illegalValues = CreateValueList(ValueListKind.Or, BuiltinBundleVariables);
159 + this.Messaging.Write(ErrorMessages.IllegalAttributeValueWithIllegalList(sourceLineNumbers, elementName, attributeName, variableName, illegalValues));
160 +
161 + return false;
162 + }
163 + else
164 + {
165 + return true;
166 + }
167 + }
168 +
169 + public bool ValidateBundleMsiPropertyName(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string propertyName)
170 + {
171 + if (String.IsNullOrEmpty(propertyName))
172 + {
173 + this.Messaging.Write(ErrorMessages.IllegalEmptyAttributeValue(sourceLineNumbers, elementName, attributeName));
174 +
175 + return false;
176 + }
177 + else if (DisallowedMsiProperties.Contains(propertyName))
178 + {
179 + var illegalValues = CreateValueList(ValueListKind.Or, DisallowedMsiProperties);
180 + this.Messaging.Write(ErrorMessages.DisallowedMsiProperty(sourceLineNumbers, propertyName, illegalValues));
181 +
182 + return false;
183 + }
184 + else
185 + {
186 + return true;
187 + }
188 + }
189 +
190 + public bool ValidateBundleCondition(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string condition, BundleConditionPhase phase)
191 + {
192 + if (!this.TryParseCondition(sourceLineNumbers, elementName, attributeName, condition))
193 + {
194 + return false;
195 + }
196 +
197 +
198 + // TODO: These lists are incomplete.
199 + List<string> unavailableVariables = null;
200 + switch (phase)
201 + {
202 + case BundleConditionPhase.Startup:
203 + unavailableVariables = UnavailableStartupVariables;
204 + break;
205 + case BundleConditionPhase.Detect:
206 + unavailableVariables = UnavailableDetectVariables;
207 + break;
208 + }
209 +
210 + if (unavailableVariables != null)
211 + {
212 + return this.ValidateBundleConditionUnavailableVariables(sourceLineNumbers, elementName, attributeName, condition, unavailableVariables);
213 + }
214 + else
215 + {
216 + return true;
217 + }
218 + }
219 +
220 + private bool ValidateBundleConditionUnavailableVariables(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string condition, List<string> unavailableVariables)
221 + {
222 + foreach (var variableName in unavailableVariables)
223 + {
224 + //TODO: use the results of parsing to validate that the restricted variables are actually used as variables
225 + if (condition.Contains(variableName))
226 + {
227 + var illegalValues = CreateValueList(ValueListKind.Or, unavailableVariables);
228 + this.Messaging.Write(WarningMessages.UnavailableBundleConditionVariable(sourceLineNumbers, elementName, attributeName, variableName, illegalValues));
229 +
230 + return false;
231 + }
232 + }
233 +
234 + return true;
235 + }
236 +
237 + private bool TryParseCondition(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string condition)
238 + {
239 + if (String.IsNullOrEmpty(condition))
240 + {
241 + this.Messaging.Write(ErrorMessages.IllegalEmptyAttributeValue(sourceLineNumbers, elementName, attributeName));
242 +
243 + return false;
244 + }
245 + //TODO: Actually parse the condition to definitively tell which Variables are referenced.
246 + else if (condition.Trim() == "=")
247 + {
248 + this.Messaging.Write(ErrorMessages.InvalidBundleCondition(sourceLineNumbers, elementName, attributeName, condition));
249 + return false;
250 + }
251 + else
252 + {
253 + return true;
254 + }
255 + }
256 +
257 + private static string CreateValueList(ValueListKind kind, IEnumerable<string> values)
258 + {
259 + // Ideally, we could denote the list kind (and the list itself) directly in the
260 + // message XML, and detect and expand in the MessageHandler.GenerateMessageString()
261 + // method. Doing so would make vararg-style messages much easier, but impacts
262 + // every single message we format. For now, callers just have to know when a
263 + // message takes a list of values in a single string argument, the caller will
264 + // have to do the expansion themselves. (And, unfortunately, hard-code the knowledge
265 + // that the list is an 'and' or 'or' list.)
266 +
267 + // For a localizable solution, we need to be able to get the list format string
268 + // from resources. We aren't currently localized right now, so the values are
269 + // just hard-coded.
270 + const string valueFormat = "'{0}'";
271 + const string valueSeparator = ", ";
272 + var terminalTerm = String.Empty;
273 +
274 + switch (kind)
275 + {
276 + case ValueListKind.None:
277 + terminalTerm = "";
278 + break;
279 + case ValueListKind.And:
280 + terminalTerm = "and ";
281 + break;
282 + case ValueListKind.Or:
283 + terminalTerm = "or ";
284 + break;
285 + }
286 +
287 + var list = new StringBuilder();
288 +
289 + // This weird construction helps us determine when we're adding the last value
290 + // to the list. Instead of adding them as we encounter them, we cache the current
291 + // value and append the *previous* one.
292 + string previousValue = null;
293 + var haveValues = false;
294 + foreach (var value in values)
295 + {
296 + if (null != previousValue)
297 + {
298 + if (haveValues)
299 + {
300 + list.Append(valueSeparator);
301 + }
302 + list.AppendFormat(valueFormat, previousValue);
303 + haveValues = true;
304 + }
305 +
306 + previousValue = value;
307 + }
308 +
309 + // If we have no previous value, that means that the list contained no values, and
310 + // something has gone very wrong.
311 + Debug.Assert(null != previousValue);
312 + if (null != previousValue)
313 + {
314 + if (haveValues)
315 + {
316 + list.Append(valueSeparator);
317 + list.Append(terminalTerm);
318 + }
319 + list.AppendFormat(valueFormat, previousValue);
320 + //haveValues = true;
321 + }
322 +
323 + return list.ToString();
324 + }
325 + }
326 +}
src/wix/WixToolset.Core/ExtensibilityServices/ParseHelper.cs
+29 -6
@@ -22,11 +22,14 @@ namespace WixToolset.Core.ExtensibilityServices
22 {
23 this.ServiceProvider = serviceProvider;
24
25 + this.BundleValidator = serviceProvider.GetService<IBundleValidator>();
26 this.Messaging = serviceProvider.GetService<IMessaging>();
27 }
28
29 private IServiceProvider ServiceProvider { get; }
30
31 + private IBundleValidator BundleValidator { get; }
32 +
33 private IMessaging Messaging { get; }
34
35 private ISymbolDefinitionCreator Creator { get; set; }
@@ -239,11 +242,14 @@ namespace WixToolset.Core.ExtensibilityServices
242
243 public void CreateWixSearchSymbol(IntermediateSection section, SourceLineNumber sourceLineNumbers, string elementName, Identifier id, string variable, string condition, string after, string bundleExtensionId)
244 {
242 - // TODO: verify variable is not a standard bundle variable
245 if (variable == null)
246 {
247 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, elementName, "Variable"));
248 }
249 + else
250 + {
251 + this.BundleValidator.ValidateBundleVariableName(sourceLineNumbers, elementName, "Variable", variable);
252 + }
253
254 section.AddSymbol(new WixSearchSymbol(sourceLineNumbers, id)
255 {
@@ -623,11 +629,6 @@ namespace WixToolset.Core.ExtensibilityServices
629 }
630 }
631
626 - public string GetCanonicalRelativePath(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string relativePath)
627 - {
628 - return Common.GetCanonicalRelativePath(sourceLineNumbers, elementName, attributeName, relativePath, this.Messaging);
629 - }
630 -
632 public SourceLineNumber GetSourceLineNumbers(XElement element)
633 {
634 return Preprocessor.GetSourceLineNumbers(element);
@@ -858,5 +859,27 @@ namespace WixToolset.Core.ExtensibilityServices
859
860 return extension != null;
861 }
862 +
863 + #region IBundleValidator
864 + public string GetCanonicalRelativePath(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string relativePath)
865 + {
866 + return this.BundleValidator.GetCanonicalRelativePath(sourceLineNumbers, elementName, attributeName, relativePath);
867 + }
868 +
869 + public bool ValidateBundleMsiPropertyName(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string propertyName)
870 + {
871 + return this.BundleValidator.ValidateBundleMsiPropertyName(sourceLineNumbers, elementName, attributeName, propertyName);
872 + }
873 +
874 + public bool ValidateBundleVariableName(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string variableName)
875 + {
876 + return this.BundleValidator.ValidateBundleVariableName(sourceLineNumbers, elementName, attributeName, variableName);
877 + }
878 +
879 + public bool ValidateBundleCondition(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string condition, BundleConditionPhase phase)
880 + {
881 + return this.BundleValidator.ValidateBundleCondition(sourceLineNumbers, elementName, attributeName, condition, phase);
882 + }
883 + #endregion
884 }
885 }
src/wix/WixToolset.Core/WixToolsetServiceProvider.cs
+1
@@ -20,6 +20,7 @@ namespace WixToolset.Core
20 // Singletons.
21 this.AddService((provider, singletons) => AddSingleton<IExtensionManager>(singletons, new ExtensionManager(provider)));
22 this.AddService((provider, singletons) => AddSingleton<IMessaging>(singletons, new Messaging()));
23 + this.AddService((provider, singletons) => AddSingleton<IBundleValidator>(singletons, new BundleValidator(provider)));
24 this.AddService((provider, singletons) => AddSingleton<ISymbolDefinitionCreator>(singletons, new SymbolDefinitionCreator(provider)));
25 this.AddService((provider, singletons) => AddSingleton<IParseHelper>(singletons, new ParseHelper(provider)));
26 this.AddService((provider, singletons) => AddSingleton<IPreprocessHelper>(singletons, new PreprocessHelper(provider)));
src/wix/test/WixToolsetTest.CoreIntegration/BadInputFixture.cs
+43
@@ -4,6 +4,7 @@ namespace WixToolsetTest.CoreIntegration
4 {
5 using System;
6 using System.IO;
7 + using System.Linq;
8 using WixBuildTools.TestSupport;
9 using WixToolset.Core.TestPackage;
10 using Xunit;
@@ -129,5 +130,47 @@ namespace WixToolsetTest.CoreIntegration
130 Assert.Equal(193, result.ExitCode);
131 }
132 }
133 +
134 + [Fact]
135 + public void GuardsAgainstVariousBundleValuesFromLoc()
136 + {
137 + var folder = TestData.Get(@"TestData");
138 +
139 + using (var fs = new DisposableFileSystem())
140 + {
141 + var baseFolder = fs.GetFolder();
142 + var intermediateFolder = Path.Combine(baseFolder, "obj");
143 +
144 + var result = WixRunner.Execute(new[]
145 + {
146 + "build",
147 + Path.Combine(folder, "BundleWithInvalid", "BundleWithInvalidLocValues.wxs"),
148 + "-loc", Path.Combine(folder, "BundleWithInvalid", "BundleWithInvalidLocValues.wxl"),
149 + "-bindpath", Path.Combine(folder, ".Data"),
150 + "-bindpath", Path.Combine(folder, "DecompileSingleFileCompressed"),
151 + "-bindpath", Path.Combine(folder, "SimpleBundle", "data"),
152 + "-intermediateFolder", intermediateFolder,
153 + "-o", Path.Combine(baseFolder, @"bin\test.exe")
154 + });
155 +
156 + Assert.InRange(result.ExitCode, 2, Int32.MaxValue);
157 +
158 + var messages = result.Messages.Select(m => m.ToString()).ToList();
159 + messages.Sort();
160 +
161 + WixAssert.CompareLineByLine(new[]
162 + {
163 + "*Search/@Condition contains the built-in Variable 'WixBundleAction', which is not available when it is evaluated. (Unavailable Variables are: 'WixBundleAction'.). Rewrite the condition to avoid Variables that are never valid during its evaluation.",
164 + "Bundle/@Condition contains the built-in Variable 'WixBundleInstalled', which is not available when it is evaluated. (Unavailable Variables are: 'RebootPending', 'WixBundleAction', or 'WixBundleInstalled'.). Rewrite the condition to avoid Variables that are never valid during its evaluation.",
165 + "ExePackage/@DetectCondition contains the built-in Variable 'WixBundleAction', which is not available when it is evaluated. (Unavailable Variables are: 'WixBundleAction'.). Rewrite the condition to avoid Variables that are never valid during its evaluation.",
166 + "The *Search/@Variable attribute's value, 'WixBundleInstalled', is one of the illegal options: 'AdminToolsFolder', 'AppDataFolder', 'CommonAppDataFolder', 'CommonFiles64Folder', 'CommonFilesFolder', 'CompatibilityMode', 'Date', 'DesktopFolder', 'FavoritesFolder', 'FontsFolder', 'InstallerName', 'InstallerVersion', 'LocalAppDataFolder', 'LogonUser', 'MyPicturesFolder', 'NativeMachine', 'NTProductType', 'NTSuiteBackOffice', 'NTSuiteDataCenter', 'NTSuiteEnterprise', 'NTSuitePersonal', 'NTSuiteSmallBusiness', 'NTSuiteSmallBusinessRestricted', 'NTSuiteWebServer', 'PersonalFolder', 'Privileged', 'ProgramFiles64Folder', 'ProgramFiles6432Folder', 'ProgramFilesFolder', 'ProgramMenuFolder', 'RebootPending', 'SendToFolder', 'ServicePackLevel', 'StartMenuFolder', 'StartupFolder', 'System64Folder', 'SystemFolder', 'TempFolder', 'TemplateFolder', 'TerminalServer', 'UserLanguageID', 'UserUILanguageID', 'VersionMsi', 'VersionNT', 'VersionNT64', 'WindowsFolder', 'WindowsVolume', 'WixBundleAction', 'WixBundleCommandLineAction', 'WixBundleForcedRestartPackage', 'WixBundleElevated', 'WixBundleInstalled', 'WixBundleProviderKey', 'WixBundleTag', or 'WixBundleVersion'.",
167 + "The CommandLine/@Condition attribute's value '=' is not a valid bundle condition.",
168 + "The MsiPackage/@InstallCondition attribute's value '=' is not a valid bundle condition.",
169 + "The MsiProperty/@Condition attribute's value '=' is not a valid bundle condition.",
170 + //"The Variable/@Name attribute's value, 'WixBundleInstalled', is one of the illegal options: 'AdminToolsFolder', 'AppDataFolder', 'CommonAppDataFolder', 'CommonFiles64Folder', 'CommonFilesFolder', 'CompatibilityMode', 'Date', 'DesktopFolder', 'FavoritesFolder', 'FontsFolder', 'InstallerName', 'InstallerVersion', 'LocalAppDataFolder', 'LogonUser', 'MyPicturesFolder', 'NativeMachine', 'NTProductType', 'NTSuiteBackOffice', 'NTSuiteDataCenter', 'NTSuiteEnterprise', 'NTSuitePersonal', 'NTSuiteSmallBusiness', 'NTSuiteSmallBusinessRestricted', 'NTSuiteWebServer', 'PersonalFolder', 'Privileged', 'ProgramFiles64Folder', 'ProgramFiles6432Folder', 'ProgramFilesFolder', 'ProgramMenuFolder', 'RebootPending', 'SendToFolder', 'ServicePackLevel', 'StartMenuFolder', 'StartupFolder', 'System64Folder', 'SystemFolder', 'TempFolder', 'TemplateFolder', 'TerminalServer', 'UserLanguageID', 'UserUILanguageID', 'VersionMsi', 'VersionNT', 'VersionNT64', 'WindowsFolder', 'WindowsVolume', 'WixBundleAction', 'WixBundleCommandLineAction', 'WixBundleForcedRestartPackage', 'WixBundleElevated', 'WixBundleInstalled', 'WixBundleProviderKey', 'WixBundleTag', or 'WixBundleVersion'.",
171 + "The 'REINSTALLMODE' MsiProperty is controlled by the bootstrapper and cannot be authored. (Illegal properties are: 'ACTION', 'ADDLOCAL', 'ADDSOURCE', 'ADDDEFAULT', 'ADVERTISE', 'ALLUSERS', 'REBOOT', 'REINSTALL', 'REINSTALLMODE', or 'REMOVE'.) Remove the MsiProperty element.",
172 + }, messages.ToArray());
173 + }
174 + }
175 }
176 }
src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundleWithInvalid/BundleWithInvalidLocValues.wxl new
+8
@@ -0,0 +1,8 @@
1 +<WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="en-US">
2 + <String Id="BuiltinMsiPropertyName">REINSTALLMODE</String>
3 + <String Id="BuiltinBurnVariableName">WixBundleInstalled</String>
4 + <String Id="NonsenseDetectCondition">WixBundleAction = 4</String>
5 + <String Id="NonsenseExecuteCondition">=</String>
6 + <String Id="NonsenseGlobalCondition">WixBundleInstalled &lt;&gt; 1</String>
7 + <String Id="NonsensePlanCondition">=</String>
8 +</WixLocalization>
src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundleWithInvalid/BundleWithInvalidLocValues.wxs new
+18
@@ -0,0 +1,18 @@
1 +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 + <Bundle Name="BundleWithInvalidUpgradeCode" Condition="!(loc.NonsenseGlobalCondition)"
3 + Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="{F2A56B65-2105-44C8-A532-A93A8C169D07}">
4 + <BootstrapperApplication>
5 + <BootstrapperApplicationDll SourceFile="fakeba.dll" />
6 + </BootstrapperApplication>
7 + <Chain>
8 + <MsiPackage SourceFile="example.msi" InstallCondition="!(loc.NonsensePlanCondition)">
9 + <MsiProperty Condition="!(loc.NonsenseExecuteCondition)" Name="!(loc.BuiltinMsiPropertyName)" Value="1" />
10 + </MsiPackage>
11 + <ExePackage DetectCondition="!(loc.NonsenseDetectCondition)" UninstallArguments="-uninstall" SourceFile="burn.exe">
12 + <CommandLine Condition="!(loc.NonsenseExecuteCondition)" />
13 + </ExePackage>
14 + </Chain>
15 + <Variable Name="!(loc.BuiltinBurnVariableName)" Value="1" />
16 + <SetVariable Id="Builtin" Condition="!(loc.NonsenseDetectCondition)" Variable="!(loc.BuiltinBurnVariableName)" Value="1" />
17 + </Bundle>
18 +</Wix>