summaryrefslogtreecommitdiff
path: root/src/ext/Http/wixext/HttpCompiler.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/ext/Http/wixext/HttpCompiler.cs')
-rw-r--r--src/ext/Http/wixext/HttpCompiler.cs383
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
3namespace 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}