diff options
Diffstat (limited to 'src/ext/Http/wixext/HttpCompiler.cs')
-rw-r--r-- | src/ext/Http/wixext/HttpCompiler.cs | 383 |
1 files changed, 383 insertions, 0 deletions
diff --git a/src/ext/Http/wixext/HttpCompiler.cs b/src/ext/Http/wixext/HttpCompiler.cs new file mode 100644 index 00000000..6c572470 --- /dev/null +++ b/src/ext/Http/wixext/HttpCompiler.cs | |||
@@ -0,0 +1,383 @@ | |||
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.Http | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.Xml.Linq; | ||
8 | using WixToolset.Data; | ||
9 | using WixToolset.Extensibility; | ||
10 | using WixToolset.Extensibility.Data; | ||
11 | using WixToolset.Http.Symbols; | ||
12 | |||
13 | /// <summary> | ||
14 | /// The compiler for the WiX Toolset Http Extension. | ||
15 | /// </summary> | ||
16 | public sealed class HttpCompiler : BaseCompilerExtension | ||
17 | { | ||
18 | public override XNamespace Namespace => "http://wixtoolset.org/schemas/v4/wxs/http"; | ||
19 | |||
20 | /// <summary> | ||
21 | /// Processes an element for the Compiler. | ||
22 | /// </summary> | ||
23 | /// <param name="sourceLineNumbers">Source line number for the parent element.</param> | ||
24 | /// <param name="parentElement">Parent element of element to process.</param> | ||
25 | /// <param name="element">Element to process.</param> | ||
26 | /// <param name="contextValues">Extra information about the context in which this element is being parsed.</param> | ||
27 | public override void ParseElement(Intermediate intermediate, IntermediateSection section, XElement parentElement, XElement element, IDictionary<string, string> context) | ||
28 | { | ||
29 | switch (parentElement.Name.LocalName) | ||
30 | { | ||
31 | case "ServiceInstall": | ||
32 | var serviceInstallName = context["ServiceInstallName"]; | ||
33 | var serviceUser = String.IsNullOrEmpty(serviceInstallName) ? null : String.Concat("NT SERVICE\\", serviceInstallName); | ||
34 | var serviceComponentId = context["ServiceInstallComponentId"]; | ||
35 | |||
36 | switch (element.Name.LocalName) | ||
37 | { | ||
38 | case "UrlReservation": | ||
39 | this.ParseUrlReservationElement(intermediate, section, element, serviceComponentId, serviceUser); | ||
40 | break; | ||
41 | default: | ||
42 | this.ParseHelper.UnexpectedElement(parentElement, element); | ||
43 | break; | ||
44 | } | ||
45 | break; | ||
46 | case "Component": | ||
47 | string componentId = context["ComponentId"]; | ||
48 | |||
49 | switch (element.Name.LocalName) | ||
50 | { | ||
51 | case "SniSslCertificate": | ||
52 | this.ParseSniSslCertificateElement(intermediate, section, element, componentId); | ||
53 | break; | ||
54 | |||
55 | case "UrlReservation": | ||
56 | this.ParseUrlReservationElement(intermediate, section, element, componentId, null); | ||
57 | break; | ||
58 | default: | ||
59 | this.ParseHelper.UnexpectedElement(parentElement, element); | ||
60 | break; | ||
61 | } | ||
62 | break; | ||
63 | default: | ||
64 | this.ParseHelper.UnexpectedElement(parentElement, element); | ||
65 | break; | ||
66 | } | ||
67 | } | ||
68 | |||
69 | /// <summary> | ||
70 | /// Parses a SniSsl element. | ||
71 | /// </summary> | ||
72 | /// <param name="node">The element to parse.</param> | ||
73 | /// <param name="componentId">Identifier of the component that owns this SNI SSL Certificate.</param> | ||
74 | private void ParseSniSslCertificateElement(Intermediate intermediate, IntermediateSection section, XElement node, string componentId) | ||
75 | { | ||
76 | var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(node); | ||
77 | Identifier id = null; | ||
78 | string host = null; | ||
79 | string port = null; | ||
80 | string appId = null; | ||
81 | string store = null; | ||
82 | string thumbprint = null; | ||
83 | var handleExisting = HandleExisting.Replace; | ||
84 | string handleExistingValue = null; | ||
85 | |||
86 | foreach (var attrib in node.Attributes()) | ||
87 | { | ||
88 | if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) | ||
89 | { | ||
90 | switch (attrib.Name.LocalName) | ||
91 | { | ||
92 | case "Id": | ||
93 | id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); | ||
94 | break; | ||
95 | case "AppId": | ||
96 | appId = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); | ||
97 | break; | ||
98 | case "HandleExisting": | ||
99 | handleExistingValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); | ||
100 | switch (handleExistingValue) | ||
101 | { | ||
102 | case "replace": | ||
103 | handleExisting = HandleExisting.Replace; | ||
104 | break; | ||
105 | case "ignore": | ||
106 | handleExisting = HandleExisting.Ignore; | ||
107 | break; | ||
108 | case "fail": | ||
109 | handleExisting = HandleExisting.Fail; | ||
110 | break; | ||
111 | default: | ||
112 | this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "HandleExisting", handleExistingValue, "replace", "ignore", "fail")); | ||
113 | break; | ||
114 | } | ||
115 | break; | ||
116 | case "Host": | ||
117 | host = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); | ||
118 | break; | ||
119 | case "Port": | ||
120 | port = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); | ||
121 | break; | ||
122 | case "Store": | ||
123 | store = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); | ||
124 | break; | ||
125 | case "Thumbprint": | ||
126 | thumbprint = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); | ||
127 | break; | ||
128 | default: | ||
129 | this.ParseHelper.UnexpectedAttribute(node, attrib); | ||
130 | break; | ||
131 | } | ||
132 | } | ||
133 | else | ||
134 | { | ||
135 | this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, node, attrib); | ||
136 | } | ||
137 | } | ||
138 | |||
139 | // Need the element ID for child element processing, so generate now if not authored. | ||
140 | if (null == id) | ||
141 | { | ||
142 | id = this.ParseHelper.CreateIdentifier("ssl", componentId, host, port); | ||
143 | } | ||
144 | |||
145 | // Required attributes. | ||
146 | if (null == host) | ||
147 | { | ||
148 | this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Host")); | ||
149 | } | ||
150 | |||
151 | if (null == port) | ||
152 | { | ||
153 | this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Port")); | ||
154 | } | ||
155 | |||
156 | if (null == thumbprint) | ||
157 | { | ||
158 | this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Thumbprint")); | ||
159 | } | ||
160 | |||
161 | // Parse unknown children. | ||
162 | this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, node); | ||
163 | |||
164 | if (!this.Messaging.EncounteredError) | ||
165 | { | ||
166 | section.AddSymbol(new WixHttpSniSslCertSymbol(sourceLineNumbers, id) | ||
167 | { | ||
168 | Host = host, | ||
169 | Port = port, | ||
170 | Thumbprint = thumbprint, | ||
171 | AppId = appId, | ||
172 | Store = store, | ||
173 | HandleExisting = handleExisting, | ||
174 | ComponentRef = componentId, | ||
175 | }); | ||
176 | |||
177 | this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4SchedHttpSniSslCertsInstall", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); | ||
178 | this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4SchedHttpSniSslCertsUninstall", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); | ||
179 | } | ||
180 | } | ||
181 | |||
182 | /// <summary> | ||
183 | /// Parses a UrlReservation element. | ||
184 | /// </summary> | ||
185 | /// <param name="node">The element to parse.</param> | ||
186 | /// <param name="componentId">Identifier of the component that owns this URL reservation.</param> | ||
187 | /// <param name="securityPrincipal">The security principal of the parent element (null if nested under Component).</param> | ||
188 | private void ParseUrlReservationElement(Intermediate intermediate, IntermediateSection section, XElement node, string componentId, string securityPrincipal) | ||
189 | { | ||
190 | var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(node); | ||
191 | Identifier id = null; | ||
192 | var handleExisting = HandleExisting.Replace; | ||
193 | string sddl = null; | ||
194 | string url = null; | ||
195 | var foundACE = false; | ||
196 | |||
197 | foreach (var attrib in node.Attributes()) | ||
198 | { | ||
199 | if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) | ||
200 | { | ||
201 | switch (attrib.Name.LocalName) | ||
202 | { | ||
203 | case "Id": | ||
204 | id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); | ||
205 | break; | ||
206 | case "HandleExisting": | ||
207 | var handleExistingValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); | ||
208 | switch (handleExistingValue) | ||
209 | { | ||
210 | case "replace": | ||
211 | handleExisting = HandleExisting.Replace; | ||
212 | break; | ||
213 | case "ignore": | ||
214 | handleExisting = HandleExisting.Ignore; | ||
215 | break; | ||
216 | case "fail": | ||
217 | handleExisting = HandleExisting.Fail; | ||
218 | break; | ||
219 | default: | ||
220 | this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "HandleExisting", handleExistingValue, "replace", "ignore", "fail")); | ||
221 | break; | ||
222 | } | ||
223 | break; | ||
224 | case "Sddl": | ||
225 | sddl = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); | ||
226 | break; | ||
227 | case "Url": | ||
228 | url = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); | ||
229 | break; | ||
230 | default: | ||
231 | this.ParseHelper.UnexpectedAttribute(node, attrib); | ||
232 | break; | ||
233 | } | ||
234 | } | ||
235 | else | ||
236 | { | ||
237 | this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, node, attrib); | ||
238 | } | ||
239 | } | ||
240 | |||
241 | // Need the element ID for child element processing, so generate now if not authored. | ||
242 | if (null == id) | ||
243 | { | ||
244 | id = this.ParseHelper.CreateIdentifier("url", componentId, securityPrincipal, url); | ||
245 | } | ||
246 | |||
247 | // Parse UrlAce children. | ||
248 | foreach (var child in node.Elements()) | ||
249 | { | ||
250 | if (this.Namespace == child.Name.Namespace) | ||
251 | { | ||
252 | switch (child.Name.LocalName) | ||
253 | { | ||
254 | case "UrlAce": | ||
255 | if (null != sddl) | ||
256 | { | ||
257 | this.Messaging.Write(ErrorMessages.IllegalParentAttributeWhenNested(sourceLineNumbers, "UrlReservation", "Sddl", "UrlAce")); | ||
258 | } | ||
259 | else | ||
260 | { | ||
261 | foundACE = true; | ||
262 | this.ParseUrlAceElement(intermediate, section, child, id.Id, securityPrincipal); | ||
263 | } | ||
264 | break; | ||
265 | default: | ||
266 | this.ParseHelper.UnexpectedElement(node, child); | ||
267 | break; | ||
268 | } | ||
269 | } | ||
270 | else | ||
271 | { | ||
272 | this.ParseHelper.ParseExtensionElement(this.Context.Extensions, intermediate, section, node, child); | ||
273 | } | ||
274 | } | ||
275 | |||
276 | // Url is required. | ||
277 | if (null == url) | ||
278 | { | ||
279 | this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Url")); | ||
280 | } | ||
281 | |||
282 | // Security is required. | ||
283 | if (null == sddl && !foundACE) | ||
284 | { | ||
285 | this.Messaging.Write(HttpErrors.NoSecuritySpecified(sourceLineNumbers)); | ||
286 | } | ||
287 | |||
288 | if (!this.Messaging.EncounteredError) | ||
289 | { | ||
290 | section.AddSymbol(new WixHttpUrlReservationSymbol(sourceLineNumbers, id) | ||
291 | { | ||
292 | HandleExisting = handleExisting, | ||
293 | Sddl = sddl, | ||
294 | Url = url, | ||
295 | ComponentRef = componentId, | ||
296 | }); | ||
297 | |||
298 | this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4SchedHttpUrlReservationsInstall", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); | ||
299 | this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4SchedHttpUrlReservationsUninstall", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); | ||
300 | } | ||
301 | } | ||
302 | |||
303 | /// <summary> | ||
304 | /// Parses a UrlAce element. | ||
305 | /// </summary> | ||
306 | /// <param name="node">The element to parse.</param> | ||
307 | /// <param name="urlReservationId">The URL reservation ID.</param> | ||
308 | /// <param name="defaultSecurityPrincipal">The default security principal.</param> | ||
309 | private void ParseUrlAceElement(Intermediate intermediate, IntermediateSection section, XElement node, string urlReservationId, string defaultSecurityPrincipal) | ||
310 | { | ||
311 | var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(node); | ||
312 | Identifier id = null; | ||
313 | var securityPrincipal = defaultSecurityPrincipal; | ||
314 | var rights = HttpConstants.GENERIC_ALL; | ||
315 | string rightsValue = null; | ||
316 | |||
317 | foreach (var attrib in node.Attributes()) | ||
318 | { | ||
319 | if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) | ||
320 | { | ||
321 | switch (attrib.Name.LocalName) | ||
322 | { | ||
323 | case "Id": | ||
324 | id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); | ||
325 | break; | ||
326 | case "SecurityPrincipal": | ||
327 | securityPrincipal = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); | ||
328 | break; | ||
329 | case "Rights": | ||
330 | rightsValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); | ||
331 | switch (rightsValue) | ||
332 | { | ||
333 | case "all": | ||
334 | rights = HttpConstants.GENERIC_ALL; | ||
335 | break; | ||
336 | case "delegate": | ||
337 | rights = HttpConstants.GENERIC_WRITE; | ||
338 | break; | ||
339 | case "register": | ||
340 | rights = HttpConstants.GENERIC_EXECUTE; | ||
341 | break; | ||
342 | default: | ||
343 | this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Rights", rightsValue, "all", "delegate", "register")); | ||
344 | break; | ||
345 | } | ||
346 | break; | ||
347 | default: | ||
348 | this.ParseHelper.UnexpectedAttribute(node, attrib); | ||
349 | break; | ||
350 | } | ||
351 | } | ||
352 | else | ||
353 | { | ||
354 | this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, node, attrib); | ||
355 | } | ||
356 | } | ||
357 | |||
358 | // Generate Id now if not authored. | ||
359 | if (null == id) | ||
360 | { | ||
361 | id = this.ParseHelper.CreateIdentifier("ace", urlReservationId, securityPrincipal, rightsValue); | ||
362 | } | ||
363 | |||
364 | this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, node); | ||
365 | |||
366 | // SecurityPrincipal is required. | ||
367 | if (null == securityPrincipal) | ||
368 | { | ||
369 | this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "SecurityPrincipal")); | ||
370 | } | ||
371 | |||
372 | if (!this.Messaging.EncounteredError) | ||
373 | { | ||
374 | section.AddSymbol(new WixHttpUrlAceSymbol(sourceLineNumbers, id) | ||
375 | { | ||
376 | WixHttpUrlReservationRef = urlReservationId, | ||
377 | SecurityPrincipal = securityPrincipal, | ||
378 | Rights = rights, | ||
379 | }); | ||
380 | } | ||
381 | } | ||
382 | } | ||
383 | } | ||