main
cs 382 lines 17.8 KB
Raw
1 // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 namespace WixToolset.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
85 foreach (var attrib in node.Attributes())
86 {
87 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
88 {
89 switch (attrib.Name.LocalName)
90 {
91 case "Id":
92 id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib);
93 break;
94 case "AppId":
95 appId = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
96 break;
97 case "HandleExisting":
98 var handleExistingValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
99 switch (handleExistingValue)
100 {
101 case "replace":
102 handleExisting = HandleExisting.Replace;
103 break;
104 case "ignore":
105 handleExisting = HandleExisting.Ignore;
106 break;
107 case "fail":
108 handleExisting = HandleExisting.Fail;
109 break;
110 default:
111 this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "HandleExisting", handleExistingValue, "replace", "ignore", "fail"));
112 break;
113 }
114 break;
115 case "Host":
116 host = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
117 break;
118 case "Port":
119 port = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
120 break;
121 case "Store":
122 store = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
123 break;
124 case "Thumbprint":
125 thumbprint = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
126 break;
127 default:
128 this.ParseHelper.UnexpectedAttribute(node, attrib);
129 break;
130 }
131 }
132 else
133 {
134 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, node, attrib);
135 }
136 }
137
138 // Need the element ID for child element processing, so generate now if not authored.
139 if (null == id)
140 {
141 id = this.ParseHelper.CreateIdentifier("ssl", componentId, host, port);
142 }
143
144 // Required attributes.
145 if (null == host)
146 {
147 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Host"));
148 }
149
150 if (null == port)
151 {
152 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Port"));
153 }
154
155 if (null == thumbprint)
156 {
157 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Thumbprint"));
158 }
159
160 // Parse unknown children.
161 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, node);
162
163 if (!this.Messaging.EncounteredError)
164 {
165 section.AddSymbol(new WixHttpSniSslCertSymbol(sourceLineNumbers, id)
166 {
167 Host = host,
168 Port = port,
169 Thumbprint = thumbprint,
170 AppId = appId,
171 Store = store,
172 HandleExisting = handleExisting,
173 ComponentRef = componentId,
174 });
175
176 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4SchedHttpSniSslCertsInstall", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64);
177 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4SchedHttpSniSslCertsUninstall", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64);
178 }
179 }
180
181 /// <summary>
182 /// Parses a UrlReservation element.
183 /// </summary>
184 /// <param name="node">The element to parse.</param>
185 /// <param name="componentId">Identifier of the component that owns this URL reservation.</param>
186 /// <param name="securityPrincipal">The security principal of the parent element (null if nested under Component).</param>
187 private void ParseUrlReservationElement(Intermediate intermediate, IntermediateSection section, XElement node, string componentId, string securityPrincipal)
188 {
189 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(node);
190 Identifier id = null;
191 var handleExisting = HandleExisting.Replace;
192 string sddl = null;
193 string url = null;
194 var foundACE = false;
195
196 foreach (var attrib in node.Attributes())
197 {
198 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
199 {
200 switch (attrib.Name.LocalName)
201 {
202 case "Id":
203 id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib);
204 break;
205 case "HandleExisting":
206 var handleExistingValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
207 switch (handleExistingValue)
208 {
209 case "replace":
210 handleExisting = HandleExisting.Replace;
211 break;
212 case "ignore":
213 handleExisting = HandleExisting.Ignore;
214 break;
215 case "fail":
216 handleExisting = HandleExisting.Fail;
217 break;
218 default:
219 this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "HandleExisting", handleExistingValue, "replace", "ignore", "fail"));
220 break;
221 }
222 break;
223 case "Sddl":
224 sddl = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
225 break;
226 case "Url":
227 url = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
228 break;
229 default:
230 this.ParseHelper.UnexpectedAttribute(node, attrib);
231 break;
232 }
233 }
234 else
235 {
236 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, node, attrib);
237 }
238 }
239
240 // Need the element ID for child element processing, so generate now if not authored.
241 if (null == id)
242 {
243 id = this.ParseHelper.CreateIdentifier("url", componentId, securityPrincipal, url);
244 }
245
246 // Parse UrlAce children.
247 foreach (var child in node.Elements())
248 {
249 if (this.Namespace == child.Name.Namespace)
250 {
251 switch (child.Name.LocalName)
252 {
253 case "UrlAce":
254 if (null != sddl)
255 {
256 this.Messaging.Write(ErrorMessages.IllegalParentAttributeWhenNested(sourceLineNumbers, "UrlReservation", "Sddl", "UrlAce"));
257 }
258 else
259 {
260 foundACE = true;
261 this.ParseUrlAceElement(intermediate, section, child, id.Id, securityPrincipal);
262 }
263 break;
264 default:
265 this.ParseHelper.UnexpectedElement(node, child);
266 break;
267 }
268 }
269 else
270 {
271 this.ParseHelper.ParseExtensionElement(this.Context.Extensions, intermediate, section, node, child);
272 }
273 }
274
275 // Url is required.
276 if (null == url)
277 {
278 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Url"));
279 }
280
281 // Security is required.
282 if (null == sddl && !foundACE)
283 {
284 this.Messaging.Write(HttpErrors.NoSecuritySpecified(sourceLineNumbers));
285 }
286
287 if (!this.Messaging.EncounteredError)
288 {
289 section.AddSymbol(new WixHttpUrlReservationSymbol(sourceLineNumbers, id)
290 {
291 HandleExisting = handleExisting,
292 Sddl = sddl,
293 Url = url,
294 ComponentRef = componentId,
295 });
296
297 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4SchedHttpUrlReservationsInstall", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64);
298 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4SchedHttpUrlReservationsUninstall", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64);
299 }
300 }
301
302 /// <summary>
303 /// Parses a UrlAce element.
304 /// </summary>
305 /// <param name="node">The element to parse.</param>
306 /// <param name="urlReservationId">The URL reservation ID.</param>
307 /// <param name="defaultSecurityPrincipal">The default security principal.</param>
308 private void ParseUrlAceElement(Intermediate intermediate, IntermediateSection section, XElement node, string urlReservationId, string defaultSecurityPrincipal)
309 {
310 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(node);
311 Identifier id = null;
312 var securityPrincipal = defaultSecurityPrincipal;
313 var rights = HttpConstants.GENERIC_ALL;
314 string rightsValue = null;
315
316 foreach (var attrib in node.Attributes())
317 {
318 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
319 {
320 switch (attrib.Name.LocalName)
321 {
322 case "Id":
323 id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib);
324 break;
325 case "SecurityPrincipal":
326 securityPrincipal = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
327 break;
328 case "Rights":
329 rightsValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
330 switch (rightsValue)
331 {
332 case "all":
333 rights = HttpConstants.GENERIC_ALL;
334 break;
335 case "delegate":
336 rights = HttpConstants.GENERIC_WRITE;
337 break;
338 case "register":
339 rights = HttpConstants.GENERIC_EXECUTE;
340 break;
341 default:
342 this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Rights", rightsValue, "all", "delegate", "register"));
343 break;
344 }
345 break;
346 default:
347 this.ParseHelper.UnexpectedAttribute(node, attrib);
348 break;
349 }
350 }
351 else
352 {
353 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, node, attrib);
354 }
355 }
356
357 // Generate Id now if not authored.
358 if (null == id)
359 {
360 id = this.ParseHelper.CreateIdentifier("ace", urlReservationId, securityPrincipal, rightsValue);
361 }
362
363 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, node);
364
365 // SecurityPrincipal is required.
366 if (null == securityPrincipal)
367 {
368 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "SecurityPrincipal"));
369 }
370
371 if (!this.Messaging.EncounteredError)
372 {
373 section.AddSymbol(new WixHttpUrlAceSymbol(sourceLineNumbers, id)
374 {
375 WixHttpUrlReservationRef = urlReservationId,
376 SecurityPrincipal = securityPrincipal,
377 Rights = rights,
378 });
379 }
380 }
381 }
382 }