From 7c8e34de56b3348c5a421cd0cced183e1394c5c7 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Tue, 4 May 2021 22:48:12 -0700 Subject: Move Iis.wixext into ext --- src/ext/Iis/wixext/IIsCompiler.cs | 2620 +++++++++++++++++++++++++++++++++++++ 1 file changed, 2620 insertions(+) create mode 100644 src/ext/Iis/wixext/IIsCompiler.cs (limited to 'src/ext/Iis/wixext/IIsCompiler.cs') diff --git a/src/ext/Iis/wixext/IIsCompiler.cs b/src/ext/Iis/wixext/IIsCompiler.cs new file mode 100644 index 00000000..cb573ad1 --- /dev/null +++ b/src/ext/Iis/wixext/IIsCompiler.cs @@ -0,0 +1,2620 @@ +// 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. + +namespace WixToolset.Iis +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using System.Xml.Linq; + using WixToolset.Data; + using WixToolset.Extensibility; + using WixToolset.Extensibility.Data; + using WixToolset.Iis.Symbols; + + /// + /// The compiler for the WiX Toolset Internet Information Services Extension. + /// + public sealed class IIsCompiler : BaseCompilerExtension + { + public override XNamespace Namespace => "http://wixtoolset.org/schemas/v4/wxs/iis"; + + /// + /// Types of objects that custom HTTP Headers can be applied to. + /// + /// Note that this must be kept in sync with the eHttpHeaderParentType in scahttpheader.h. + private enum HttpHeaderParentType + { + /// Custom HTTP Header is to be applied to a Web Virtual Directory. + WebVirtualDir = 1, + /// Custom HTTP Header is to be applied to a Web Site. + WebSite = 2, + } + + /// + /// Types of objects that MimeMaps can be applied to. + /// + /// Note that this must be kept in sync with the eMimeMapParentType in scamimemap.h. + private enum MimeMapParentType + { + /// MimeMap is to be applied to a Web Virtual Directory. + WebVirtualDir = 1, + WebSite = 2, + } + + /// + /// Types of objects that custom WebErrors can be applied to. + /// + /// Note that this must be kept in sync with the eWebErrorParentType in scaweberror.h. + private enum WebErrorParentType + { + /// Custom WebError is to be applied to a Web Virtual Directory. + WebVirtualDir = 1, + + /// Custom WebError is to be applied to a Web Site. + WebSite = 2, + } + + /// + /// Processes an element for the Compiler. + /// + /// Source line number for the parent element. + /// Parent element of element to process. + /// Element to process. + /// Extra information about the context in which this element is being parsed. + public override void ParseElement(Intermediate intermediate, IntermediateSection section, XElement parentElement, XElement element, IDictionary context) + { + switch (parentElement.Name.LocalName) + { + case "Component": + var componentId = context["ComponentId"]; + var directoryId = context["DirectoryId"]; + + switch (element.Name.LocalName) + { + case "Certificate": + this.ParseCertificateElement(intermediate, section, element, componentId); + break; + case "WebAppPool": + this.ParseWebAppPoolElement(intermediate, section, element, componentId); + break; + case "WebDir": + this.ParseWebDirElement(intermediate, section, element, componentId, null); + break; + case "WebFilter": + this.ParseWebFilterElement(intermediate, section, element, componentId, null); + break; + case "WebProperty": + this.ParseWebPropertyElement(intermediate, section, element, componentId); + break; + case "WebServiceExtension": + this.ParseWebServiceExtensionElement(intermediate, section, element, componentId); + break; + case "WebSite": + this.ParseWebSiteElement(intermediate, section, element, componentId); + break; + case "WebVirtualDir": + this.ParseWebVirtualDirElement(intermediate, section, element, componentId, null, null); + break; + default: + this.ParseHelper.UnexpectedElement(parentElement, element); + break; + } + break; + case "Fragment": + case "Module": + case "Package": + switch (element.Name.LocalName) + { + case "WebApplication": + this.ParseWebApplicationElement(intermediate, section, element); + break; + case "WebAppPool": + this.ParseWebAppPoolElement(intermediate, section, element, null); + break; + case "WebDirProperties": + this.ParseWebDirPropertiesElement(intermediate, section, element, null); + break; + case "WebLog": + this.ParseWebLogElement(intermediate, section, element); + break; + case "WebSite": + this.ParseWebSiteElement(intermediate, section, element, null); + break; + default: + this.ParseHelper.UnexpectedElement(parentElement, element); + break; + } + break; + default: + this.ParseHelper.UnexpectedElement(parentElement, element); + break; + } + } + + /// + /// Parses a certificate element. + /// + /// Element to parse. + /// Identifier for parent component. + private void ParseCertificateElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + int attributes = 8; // SCA_CERT_ATTRIBUTE_VITAL + string binaryRef = null; + string certificatePath = null; + string name = null; + string pfxPassword = null; + int storeLocation = 0; + string storeName = null; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "BinaryRef": + attributes |= 2; // SCA_CERT_ATTRIBUTE_BINARYDATA + binaryRef = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.Binary, binaryRef); + break; + case "CertificatePath": + certificatePath = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Name": + name = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Overwrite": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + attributes |= 4; // SCA_CERT_ATTRIBUTE_OVERWRITE + } + else + { + attributes &= ~4; // SCA_CERT_ATTRIBUTE_OVERWRITE + } + break; + case "PFXPassword": + pfxPassword = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Request": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + attributes |= 1; // SCA_CERT_ATTRIBUTE_REQUEST + } + else + { + attributes &= ~1; // SCA_CERT_ATTRIBUTE_REQUEST + } + break; + case "StoreLocation": + var storeLocationValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + if (0 < storeLocationValue.Length) + { + switch (storeLocationValue) + { + case "currentUser": + storeLocation = 1; // SCA_CERTSYSTEMSTORE_CURRENTUSER + break; + case "localMachine": + storeLocation = 2; // SCA_CERTSYSTEMSTORE_LOCALMACHINE + break; + default: + storeLocation = -1; + this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "StoreLocation", storeLocationValue, "currentUser", "localMachine")); + break; + } + } + break; + case "StoreName": + var storeNameValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + if (0 < storeNameValue.Length) + { + switch (storeNameValue) + { + case "ca": + storeName = "CA"; + break; + case "my": + case "personal": + storeName = "MY"; + break; + case "request": + storeName = "REQUEST"; + break; + case "root": + storeName = "Root"; + break; + case "otherPeople": + storeName = "AddressBook"; + break; + case "trustedPeople": + storeName = "TrustedPeople"; + break; + case "trustedPublisher": + storeName = "TrustedPublisher"; + break; + default: + this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "StoreName", storeNameValue, "ca", "my", "request", "root", "otherPeople", "trustedPeople", "trustedPublisher")); + break; + } + } + break; + case "Vital": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + attributes |= 8; // SCA_CERT_ATTRIBUTE_VITAL + } + else + { + attributes &= ~8; // SCA_CERT_ATTRIBUTE_VITAL + } + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + + if (null == id) + { + id = this.ParseHelper.CreateIdentifier("crt", componentId, binaryRef, certificatePath); + } + + if (null == name) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Name")); + } + + if (0 == storeLocation) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "StoreLocation")); + } + + if (null == storeName) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "StoreName")); + } + + if (null != binaryRef && null != certificatePath) + { + this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, element.Name.LocalName, "BinaryRef", "CertificatePath", certificatePath)); + } + else if (null == binaryRef && null == certificatePath) + { + this.Messaging.Write(ErrorMessages.ExpectedAttributes(sourceLineNumbers, element.Name.LocalName, "BinaryRef", "CertificatePath")); + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + + // Reference InstallCertificates and UninstallCertificates since nothing will happen without them + this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4InstallCertificates", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); + this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4UninstallCertificates", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); + this.ParseHelper.EnsureTable(section, sourceLineNumbers, IisTableDefinitions.CertificateHash); // Certificate CustomActions require the CertificateHash table + + if (!this.Messaging.EncounteredError) + { + section.AddSymbol(new CertificateSymbol(sourceLineNumbers, id) + { + ComponentRef = componentId, + Name = name, + StoreLocation = storeLocation, + StoreName = storeName, + Attributes = attributes, + BinaryRef = binaryRef, + CertificatePath = certificatePath, + PFXPassword = pfxPassword, + }); + } + } + + /// + /// Parses a CertificateRef extension element. + /// + /// Element to parse. + /// Identifier for parent web site. + private void ParseCertificateRefElement(Intermediate intermediate, IntermediateSection section, XElement element, string webId) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, IisSymbolDefinitions.Certificate, id.Id); + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (null == id) + { + id = this.ParseHelper.CreateIdentifier("wsc", webId); + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + + if (!this.Messaging.EncounteredError) + { + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, IisSymbolDefinitions.Certificate, id.Id); + + section.AddSymbol(new IIsWebSiteCertificatesSymbol(sourceLineNumbers) + { + WebRef = webId, + CertificateRef = id.Id, + }); + } + } + + /// + /// Parses a mime map element. + /// + /// Element to parse. + /// Identifier for parent symbol. + /// Type that parentId refers to. + private void ParseMimeMapElement(Intermediate intermediate, IntermediateSection section, XElement element, string parentId, MimeMapParentType parentType) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + string extension = null; + string type = null; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Extension": + extension = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Type": + type = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (null == id) + { + id = this.ParseHelper.CreateIdentifier("imm", parentId, type, extension); + } + + if (null == extension) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Extension")); + } + else if (0 < extension.Length) + { + if (!extension.StartsWith(".", StringComparison.Ordinal)) + { + this.Messaging.Write(IIsErrors.MimeMapExtensionMissingPeriod(sourceLineNumbers, element.Name.LocalName, "Extension", extension)); + } + } + + if (null == type) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Type")); + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + + if (!this.Messaging.EncounteredError) + { + section.AddSymbol(new IIsMimeMapSymbol(sourceLineNumbers, id) + { + ParentType = (int)parentType, + ParentValue = parentId, + MimeType = type, + Extension = extension, + }); + } + } + + /// + /// Parses a recycle time element. + /// + /// Element to parse. + /// Recycle time value. + private string ParseRecycleTimeElement(Intermediate intermediate, IntermediateSection section, XElement element) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + string value = null; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Value": + value = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (null == value) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Value")); + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + + return value; + } + + /// + /// Parses a web address element. + /// + /// Element to parse. + /// Identifier of parent web site. + /// Identifier for web address. + private string ParseWebAddressElement(Intermediate intermediate, IntermediateSection section, XElement element, string parentWeb) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + string header = null; + string ip = null; + string port = null; + var secure = false; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Header": + header = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "IP": + ip = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Port": + port = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Secure": + secure = YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (null == id) + { + id = this.ParseHelper.CreateIdentifier("iwa", parentWeb, ip, port); + } + + if (null == port) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Port")); + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + + if (!this.Messaging.EncounteredError) + { + section.AddSymbol(new IIsWebAddressSymbol(sourceLineNumbers, id) + { + WebRef = parentWeb, + IP = ip, + Port = port, + Header = header, + Secure = secure ? 1 : 0, + }); + } + + return id?.Id; + } + + /// + /// Parses a web application element. + /// + /// Element to parse. + /// Identifier for web application. + private string ParseWebApplicationElement(Intermediate intermediate, IntermediateSection section, XElement element) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + var allowSessions = YesNoDefaultType.Default; + string appPool = null; + var buffer = YesNoDefaultType.Default; + var clientDebugging = YesNoDefaultType.Default; + string defaultScript = null; + int isolation = 0; + string name = null; + var parentPaths = YesNoDefaultType.Default; + var scriptTimeout = CompilerConstants.IntegerNotSet; + var sessionTimeout = CompilerConstants.IntegerNotSet; + var serverDebugging = YesNoDefaultType.Default; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "AllowSessions": + allowSessions = this.ParseHelper.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib); + break; + case "Buffer": + buffer = this.ParseHelper.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib); + break; + case "ClientDebugging": + clientDebugging = this.ParseHelper.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib); + break; + case "DefaultScript": + defaultScript = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + if (0 < defaultScript.Length) + { + switch (defaultScript) + { + case "JScript": + case "VBScript": + // these are valid values + break; + default: + this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName, defaultScript, "JScript", "VBScript")); + break; + } + } + break; + case "Isolation": + string isolationValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + if (0 < isolationValue.Length) + { + switch (isolationValue) + { + case "low": + isolation = 0; + break; + case "medium": + isolation = 2; + break; + case "high": + isolation = 1; + break; + default: + this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName, isolationValue, "low", "medium", "high")); + break; + } + } + break; + case "Name": + name = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "ParentPaths": + parentPaths = this.ParseHelper.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib); + break; + case "ScriptTimeout": + scriptTimeout = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue); + break; + case "ServerDebugging": + serverDebugging = this.ParseHelper.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib); + break; + case "SessionTimeout": + sessionTimeout = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue); + break; + case "WebAppPool": + appPool = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, IisSymbolDefinitions.IIsAppPool, appPool); + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (null == id) + { + id = this.ParseHelper.CreateIdentifier("wap", name, appPool); + } + + if (null == name) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Name")); + } + else if (-1 != name.IndexOf("\\", StringComparison.Ordinal)) + { + this.Messaging.Write(IIsErrors.IllegalCharacterInAttributeValue(sourceLineNumbers, element.Name.LocalName, "Name", name, '\\')); + } + + foreach (var child in element.Elements()) + { + if (this.Namespace == child.Name.Namespace) + { + switch (child.Name.LocalName) + { + case "WebApplicationExtension": + this.ParseWebApplicationExtensionElement(intermediate, section, child, id?.Id); + break; + default: + this.ParseHelper.UnexpectedElement(element, child); + break; + } + } + else + { + this.ParseHelper.ParseExtensionElement(this.Context.Extensions, intermediate, section, element, child); + } + } + + if (!this.Messaging.EncounteredError) + { + var symbol = section.AddSymbol(new IIsWebApplicationSymbol(sourceLineNumbers, id) + { + Name = name, + Isolation = isolation, + DefaultScript = defaultScript, + AppPoolRef = appPool, + }); + + if (YesNoDefaultType.Default != allowSessions) + { + symbol.AllowSessions = YesNoDefaultType.Yes == allowSessions ? 1 : 0; + } + + if (CompilerConstants.IntegerNotSet != sessionTimeout) + { + symbol.SessionTimeout = sessionTimeout; + } + + if (YesNoDefaultType.Default != buffer) + { + symbol.Buffer = YesNoDefaultType.Yes == buffer ? 1 : 0; + } + + if (YesNoDefaultType.Default != parentPaths) + { + symbol.ParentPaths = YesNoDefaultType.Yes == parentPaths ? 1 : 0; + } + + if (CompilerConstants.IntegerNotSet != scriptTimeout) + { + symbol.ScriptTimeout = scriptTimeout; + } + + if (YesNoDefaultType.Default != serverDebugging) + { + symbol.ServerDebugging = YesNoDefaultType.Yes == serverDebugging ? 1 : 0; + } + + if (YesNoDefaultType.Default != clientDebugging) + { + symbol.ClientDebugging = YesNoDefaultType.Yes == clientDebugging ? 1 : 0; + } + } + + return id?.Id; + } + + /// + /// Parses a web application extension element. + /// + /// Element to parse. + /// Identifier for parent web application. + private void ParseWebApplicationExtensionElement(Intermediate intermediate, IntermediateSection section, XElement element, string application) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + int attributes = 0; + string executable = null; + string extension = null; + string verbs = null; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "CheckPath": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + attributes |= 4; + } + else + { + attributes &= ~4; + } + break; + case "Executable": + executable = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Extension": + extension = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Script": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + attributes |= 1; + } + else + { + attributes &= ~1; + } + break; + case "Verbs": + verbs = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + + if (!this.Messaging.EncounteredError) + { + var symbol = section.AddSymbol(new IIsWebApplicationExtensionSymbol(sourceLineNumbers) + { + ApplicationRef = application, + Extension = extension, + Verbs = verbs, + Executable = executable, + }); + + if (0 < attributes) + { + symbol.Attributes = attributes; + } + } + } + + /// + /// Parses web application pool element. + /// + /// Element to parse. + /// Optional identifier of parent component. + private void ParseWebAppPoolElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + int attributes = 0; + var cpuAction = CompilerConstants.IntegerNotSet; + string cpuMon = null; + var idleTimeout = CompilerConstants.IntegerNotSet; + int maxCpuUsage = 0; + var maxWorkerProcs = CompilerConstants.IntegerNotSet; + string managedRuntimeVersion = null; + string managedPipelineMode = null; + string name = null; + var privateMemory = CompilerConstants.IntegerNotSet; + var queueLimit = CompilerConstants.IntegerNotSet; + var recycleMinutes = CompilerConstants.IntegerNotSet; + var recycleRequests = CompilerConstants.IntegerNotSet; + string recycleTimes = null; + var refreshCpu = CompilerConstants.IntegerNotSet; + string user = null; + var virtualMemory = CompilerConstants.IntegerNotSet; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "CpuAction": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + var cpuActionValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + if (0 < cpuActionValue.Length) + { + switch (cpuActionValue) + { + case "shutdown": + cpuAction = 1; + break; + case "none": + cpuAction = 0; + break; + default: + this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName, cpuActionValue, "shutdown", "none")); + break; + } + } + break; + case "Identity": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + var identityValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + if (0 < identityValue.Length) + { + switch (identityValue) + { + case "networkService": + attributes |= 1; + break; + case "localService": + attributes |= 2; + break; + case "localSystem": + attributes |= 4; + break; + case "other": + attributes |= 8; + break; + case "applicationPoolIdentity": + attributes |= 0x10; + break; + default: + this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName, identityValue, "networkService", "localService", "localSystem", "other", "applicationPoolIdentity")); + break; + } + } + break; + case "IdleTimeout": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + idleTimeout = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue); + break; + case "ManagedPipelineMode": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + managedPipelineMode = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + + + if (!String.IsNullOrEmpty(managedPipelineMode)) + { + switch (managedPipelineMode) + { + // In 3.5 we allowed lower case values (per camel case enum style), we now use formatted fields, + // so the value needs to match exactly what we pass in to IIS which uses pascal case. + case "classic": + managedPipelineMode = "Classic"; + break; + case "integrated": + managedPipelineMode = "Integrated"; + break; + case "Classic": + break; + case "Integrated": + break; + default: + if (!this.ParseHelper.ContainsProperty(managedPipelineMode)) + { + this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName, managedPipelineMode, "Classic", "Integrated")); + } + break; + } + } + + break; + case "ManagedRuntimeVersion": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + managedRuntimeVersion = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "MaxCpuUsage": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + maxCpuUsage = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, 100); + break; + case "MaxWorkerProcesses": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + maxWorkerProcs = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue); + break; + case "Name": + name = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "PrivateMemory": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + privateMemory = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, 4294967); + break; + case "QueueLimit": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + queueLimit = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue); + break; + case "RecycleMinutes": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + recycleMinutes = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue); + break; + case "RecycleRequests": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + recycleRequests = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue); + break; + case "RefreshCpu": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + refreshCpu = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, int.MaxValue); + break; + case "User": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + user = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, "User", user); + break; + case "VirtualMemory": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + virtualMemory = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, 4294967); + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (null == id) + { + id = this.ParseHelper.CreateIdentifier("iap", name, componentId, user); + } + + if (null == name) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Name")); + } + + if (null == user && 8 == (attributes & 0x1F)) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "User", "Identity", "other")); + } + + if (null != user && 8 != (attributes & 0x1F)) + { + this.Messaging.Write(ErrorMessages.IllegalAttributeValueWithoutOtherAttribute(sourceLineNumbers, element.Name.LocalName, "User", user, "Identity", "other")); + } + + cpuMon = maxCpuUsage.ToString(CultureInfo.InvariantCulture.NumberFormat); + if (CompilerConstants.IntegerNotSet != refreshCpu) + { + cpuMon = String.Concat(cpuMon, ",", refreshCpu.ToString(CultureInfo.InvariantCulture.NumberFormat)); + if (CompilerConstants.IntegerNotSet != cpuAction) + { + cpuMon = String.Concat(cpuMon, ",", cpuAction.ToString(CultureInfo.InvariantCulture.NumberFormat)); + } + } + + foreach (var child in element.Elements()) + { + if (this.Namespace == child.Name.Namespace) + { + switch (child.Name.LocalName) + { + case "RecycleTime": + if (null == componentId) + { + var childSourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(child); + this.Messaging.Write(IIsErrors.IllegalElementWithoutComponent(childSourceLineNumbers, element.Name.LocalName)); + } + + if (null == recycleTimes) + { + recycleTimes = this.ParseRecycleTimeElement(intermediate, section, child); + } + else + { + recycleTimes = String.Concat(recycleTimes, ",", this.ParseRecycleTimeElement(intermediate, section, child)); + } + break; + default: + this.ParseHelper.UnexpectedElement(element, child); + break; + } + } + else + { + this.ParseHelper.ParseExtensionElement(this.Context.Extensions, intermediate, section, element, child); + } + } + + if (null != componentId) + { + // Reference ConfigureIIs since nothing will happen without it + this.AddReferenceToConfigureIIs(section, sourceLineNumbers); + } + + if (!this.Messaging.EncounteredError) + { + var symbol = section.AddSymbol(new IIsAppPoolSymbol(sourceLineNumbers, id) + { + Name = name, + ComponentRef = componentId, + Attributes = attributes, + UserRef = user, + RecycleTimes = recycleTimes, + CPUMon = cpuMon, + ManagedRuntimeVersion = managedRuntimeVersion, + ManagedPipelineMode = managedPipelineMode, + }); + + if (CompilerConstants.IntegerNotSet != recycleMinutes) + { + symbol.RecycleMinutes = recycleMinutes; + } + + if (CompilerConstants.IntegerNotSet != recycleRequests) + { + symbol.RecycleRequests = recycleRequests; + } + + if (CompilerConstants.IntegerNotSet != idleTimeout) + { + symbol.IdleTimeout = idleTimeout; + } + + if (CompilerConstants.IntegerNotSet != queueLimit) + { + symbol.QueueLimit = queueLimit; + } + + if (CompilerConstants.IntegerNotSet != maxWorkerProcs) + { + symbol.MaxProc = maxWorkerProcs; + } + + if (CompilerConstants.IntegerNotSet != virtualMemory) + { + symbol.VirtualMemory = virtualMemory; + } + + if (CompilerConstants.IntegerNotSet != privateMemory) + { + symbol.PrivateMemory = privateMemory; + } + } + } + + /// + /// Parses a web directory element. + /// + /// Element to parse. + /// Identifier for parent component. + /// Optional identifier for parent web site. + private void ParseWebDirElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId, string parentWeb) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + string dirProperties = null; + string path = null; + string application = null; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "DirProperties": + dirProperties = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + break; + case "Path": + path = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "WebApplication": + application = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "WebSite": + if (null != parentWeb) + { + this.Messaging.Write(IIsErrors.WebSiteAttributeUnderWebSite(sourceLineNumbers, element.Name.LocalName)); + } + + parentWeb = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, IisSymbolDefinitions.IIsWebSite, parentWeb); + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (null == id) + { + id = this.ParseHelper.CreateIdentifier("iwd", componentId, parentWeb, dirProperties, application); + } + + if (null == path) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Path")); + } + + if (null == parentWeb) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "WebSite")); + } + + foreach (var child in element.Elements()) + { + if (this.Namespace == child.Name.Namespace) + { + var childSourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(child); + switch (child.Name.LocalName) + { + case "WebApplication": + if (null != application) + { + this.Messaging.Write(IIsErrors.WebApplicationAlreadySpecified(childSourceLineNumbers, element.Name.LocalName)); + } + + application = this.ParseWebApplicationElement(intermediate, section, child); + break; + case "WebDirProperties": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalElementWithoutComponent(childSourceLineNumbers, child.Name.LocalName)); + } + + string childWebDirProperties = this.ParseWebDirPropertiesElement(intermediate, section, child, componentId); + if (null == dirProperties) + { + dirProperties = childWebDirProperties; + } + else + { + this.Messaging.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, child.Name.LocalName, "DirProperties", child.Name.LocalName)); + } + break; + default: + this.ParseHelper.UnexpectedElement(element, child); + break; + } + } + else + { + this.ParseHelper.ParseExtensionElement(this.Context.Extensions, intermediate, section, element, child); + } + } + + if (null == dirProperties) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "DirProperties")); + } + + if (null != application) + { + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, IisSymbolDefinitions.IIsWebApplication, application); + } + + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, IisSymbolDefinitions.IIsWebDirProperties, dirProperties); + + // Reference ConfigureIIs since nothing will happen without it + this.AddReferenceToConfigureIIs(section, sourceLineNumbers); + + if (!this.Messaging.EncounteredError) + { + section.AddSymbol(new IIsWebDirSymbol(sourceLineNumbers, id) + { + ComponentRef = componentId, + WebRef = parentWeb, + Path = path, + DirPropertiesRef = dirProperties, + ApplicationRef = application, + }); + } + } + + /// + /// Parses a web directory properties element. + /// + /// Element to parse. + /// The identifier for this WebDirProperties. + private string ParseWebDirPropertiesElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + int access = 0; + var accessSet = false; + int accessSSLFlags = 0; + var accessSSLFlagsSet = false; + string anonymousUser = null; + var aspDetailedError = YesNoType.NotSet; + string authenticationProviders = null; + int authorization = 0; + var authorizationSet = false; + string cacheControlCustom = null; + var cacheControlMaxAge = CompilerConstants.LongNotSet; + string defaultDocuments = null; + string httpExpires = null; + var iisControlledPassword = false; + var index = YesNoType.NotSet; + var logVisits = YesNoType.NotSet; + var notCustomError = YesNoType.NotSet; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "AnonymousUser": + anonymousUser = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, "User", anonymousUser); + break; + case "AspDetailedError": + aspDetailedError = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "AuthenticationProviders": + authenticationProviders = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "CacheControlCustom": + cacheControlCustom = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "CacheControlMaxAge": + cacheControlMaxAge = this.ParseHelper.GetAttributeLongValue(sourceLineNumbers, attrib, 0, uint.MaxValue); // 4294967295 (uint.MaxValue) represents unlimited + break; + case "ClearCustomError": + notCustomError = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "DefaultDocuments": + defaultDocuments = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "HttpExpires": + httpExpires = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "IIsControlledPassword": + iisControlledPassword = YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "Index": + index = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "LogVisits": + logVisits = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + + // Access attributes + case "Execute": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + access |= 4; + } + else + { + access &= ~4; + } + accessSet = true; + break; + case "Read": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + access |= 1; + } + else + { + access &= ~1; + } + accessSet = true; + break; + case "Script": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + access |= 512; + } + else + { + access &= ~512; + } + accessSet = true; + break; + case "Write": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + access |= 2; + } + else + { + access &= ~2; + } + accessSet = true; + break; + + // AccessSSL Attributes + case "AccessSSL": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + accessSSLFlags |= 8; + } + else + { + accessSSLFlags &= ~8; + } + accessSSLFlagsSet = true; + break; + case "AccessSSL128": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + accessSSLFlags |= 256; + } + else + { + accessSSLFlags &= ~256; + } + accessSSLFlagsSet = true; + break; + case "AccessSSLMapCert": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + accessSSLFlags |= 128; + } + else + { + accessSSLFlags &= ~128; + } + accessSSLFlagsSet = true; + break; + case "AccessSSLNegotiateCert": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + accessSSLFlags |= 32; + } + else + { + accessSSLFlags &= ~32; + } + accessSSLFlagsSet = true; + break; + case "AccessSSLRequireCert": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + accessSSLFlags |= 64; + } + else + { + accessSSLFlags &= ~64; + } + accessSSLFlagsSet = true; + break; + + // Authorization attributes + case "AnonymousAccess": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + authorization |= 1; + } + else + { + authorization &= ~1; + } + authorizationSet = true; + break; + case "BasicAuthentication": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + authorization |= 2; + } + else + { + authorization &= ~2; + } + authorizationSet = true; + break; + case "DigestAuthentication": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + authorization |= 16; + } + else + { + authorization &= ~16; + } + authorizationSet = true; + break; + case "PassportAuthentication": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + authorization |= 64; + } + else + { + authorization &= ~64; + } + authorizationSet = true; + break; + case "WindowsAuthentication": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + authorization |= 4; + } + else + { + authorization &= ~4; + } + authorizationSet = true; + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (null == id) + { + if (null == componentId) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Id")); + id = Identifier.Invalid; + } + else + { + id = this.ParseHelper.CreateIdentifier("wdp", componentId); + } + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + + if (!this.Messaging.EncounteredError) + { + var symbol = section.AddSymbol(new IIsWebDirPropertiesSymbol(sourceLineNumbers, id) + { + AnonymousUserRef = anonymousUser, + IIsControlledPassword = iisControlledPassword ? 1 : 0, + DefaultDoc = defaultDocuments, + HttpExpires = httpExpires, + CacheControlCustom = cacheControlCustom, + }); + + if (accessSet) + { + symbol.Access = access; + } + + if (authorizationSet) + { + symbol.Authorization = authorization; + } + + if (YesNoType.NotSet != logVisits) + { + symbol.LogVisits = YesNoType.Yes == logVisits ? 1 : 0; + } + + if (YesNoType.NotSet != index) + { + symbol.Index = YesNoType.Yes == index ? 1 : 0; + } + + if (YesNoType.NotSet != aspDetailedError) + { + symbol.AspDetailedError = YesNoType.Yes == aspDetailedError ? 1 : 0; + } + + if (CompilerConstants.LongNotSet != cacheControlMaxAge) + { + symbol.CacheControlMaxAge = unchecked((int)cacheControlMaxAge); + } + + if (YesNoType.NotSet != notCustomError) + { + symbol.NoCustomError = YesNoType.Yes == notCustomError ? 1 : 0; + } + + if (accessSSLFlagsSet) + { + symbol.AccessSSLFlags = accessSSLFlags; + } + + if (null != authenticationProviders) + { + symbol.AuthenticationProviders = authenticationProviders; + } + } + + return id?.Id; + } + + /// + /// Parses a web error element. + /// + /// Element to parse. + /// Type of the parent. + /// Id of the parent. + private void ParseWebErrorElement(Intermediate intermediate, IntermediateSection section, XElement element, WebErrorParentType parentType, string parent) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + var errorCode = CompilerConstants.IntegerNotSet; + string file = null; + string url = null; + var subCode = CompilerConstants.IntegerNotSet; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "ErrorCode": + errorCode = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 400, 599); + break; + case "File": + file = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "SubCode": + subCode = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, int.MaxValue); + break; + case "URL": + url = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (CompilerConstants.IntegerNotSet == errorCode) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "ErrorCode")); + errorCode = CompilerConstants.IllegalInteger; + } + + if (CompilerConstants.IntegerNotSet == subCode) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "SubCode")); + subCode = CompilerConstants.IllegalInteger; + } + + if (String.IsNullOrEmpty(file) && String.IsNullOrEmpty(url)) + { + this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, element.Name.LocalName, "File", "URL")); + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + + // Reference ConfigureIIs since nothing will happen without it + this.AddReferenceToConfigureIIs(section, sourceLineNumbers); + + if (!this.Messaging.EncounteredError) + { + section.AddSymbol(new IIsWebErrorSymbol(sourceLineNumbers) + { + ErrorCode = errorCode, + SubCode = subCode, + ParentType = (int)parentType, + ParentValue = parent, + File = file, + URL = url, + }); + } + } + + /// + /// Parses a web filter element. + /// + /// Element to parse. + /// Identifier of parent component. + /// Optional identifier of parent web site. + private void ParseWebFilterElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId, string parentWeb) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + string description = null; + int flags = 0; + var loadOrder = CompilerConstants.IntegerNotSet; + string name = null; + string path = null; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Description": + description = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Flags": + flags = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, int.MaxValue); + break; + case "LoadOrder": + string loadOrderValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + if (0 < loadOrderValue.Length) + { + switch (loadOrderValue) + { + case "first": + loadOrder = 0; + break; + case "last": + loadOrder = -1; + break; + default: + loadOrder = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, short.MaxValue); + break; + } + } + break; + case "Name": + name = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Path": + path = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "WebSite": + if (null != parentWeb) + { + this.Messaging.Write(IIsErrors.WebSiteAttributeUnderWebSite(sourceLineNumbers, element.Name.LocalName)); + } + + parentWeb = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, IisSymbolDefinitions.IIsWebSite, parentWeb); + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (null == id) + { + id = this.ParseHelper.CreateIdentifier("ifl", name, componentId, path, parentWeb); + } + + if (null == name) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Name")); + } + + if (null == path) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Path")); + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + + // Reference ConfigureIIs since nothing will happen without it + this.AddReferenceToConfigureIIs(section, sourceLineNumbers); + + if (!this.Messaging.EncounteredError) + { + var symbol = section.AddSymbol(new IIsFilterSymbol(sourceLineNumbers, id) + { + Name = name, + ComponentRef = componentId, + Path = path, + WebRef = parentWeb, + Description = description, + Flags = flags, + }); + + if (CompilerConstants.IntegerNotSet != loadOrder) + { + symbol.LoadOrder = loadOrder; + } + } + } + + /// + /// Parses web log element. + /// + /// Node to be parsed. + private void ParseWebLogElement(Intermediate intermediate, IntermediateSection section, XElement element) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + string type = null; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Type": + var typeValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + if (0 < typeValue.Length) + { + switch (typeValue) + { + case "IIS": + type = "Microsoft IIS Log File Format"; + break; + case "NCSA": + type = "NCSA Common Log File Format"; + break; + case "none": + type = "none"; + break; + case "ODBC": + type = "ODBC Logging"; + break; + case "W3C": + type = "W3C Extended Log File Format"; + break; + default: + this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "Type", typeValue, "IIS", "NCSA", "none", "ODBC", "W3C")); + break; + } + } + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (null == id) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Id")); + } + + if (null == type) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Type")); + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + + if (!this.Messaging.EncounteredError) + { + section.AddSymbol(new IIsWebLogSymbol(sourceLineNumbers, id) + { + Format = type, + }); + } + } + + /// + /// Parses a web property element. + /// + /// Element to parse. + /// Identifier for parent component. + private void ParseWebPropertyElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + string value = null; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Value": + value = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + switch (id?.Id) + { + case "ETagChangeNumber": + case "MaxGlobalBandwidth": + // Must specify a value for these + if (null == value) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Value", "Id", id.Id)); + } + break; + case "IIs5IsolationMode": + case "LogInUTF8": + // Can't specify a value for these + if (null != value) + { + this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, element.Name.LocalName, "Value", "Id", id.Id)); + } + break; + default: + this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "Id", id?.Id, "ETagChangeNumber", "IIs5IsolationMode", "LogInUTF8", "MaxGlobalBandwidth")); + break; + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + + // Reference ConfigureIIs since nothing will happen without it + this.AddReferenceToConfigureIIs(section, sourceLineNumbers); + + if (!this.Messaging.EncounteredError) + { + section.AddSymbol(new IIsPropertySymbol(sourceLineNumbers, id) + { + ComponentRef = componentId, + Attributes = 0, + Value = value, + }); + } + } + + /// + /// Parses a web service extension element. + /// + /// Element to parse. + /// Identifier for parent component. + private void ParseWebServiceExtensionElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + int attributes = 0; + string description = null; + string file = null; + string group = null; + + foreach (XAttribute attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Allow": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + attributes |= 1; + } + else + { + attributes &= ~1; + } + break; + case "Description": + description = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "File": + file = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Group": + group = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "UIDeletable": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + attributes |= 2; + } + else + { + attributes &= ~2; + } + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (null == id) + { + id = this.ParseHelper.CreateIdentifier("iwe", componentId, file); + } + + if (null == file) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "File")); + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + + // Reference ConfigureIIs since nothing will happen without it + this.AddReferenceToConfigureIIs(section, sourceLineNumbers); + + if (!this.Messaging.EncounteredError) + { + section.AddSymbol(new IIsWebServiceExtensionSymbol(sourceLineNumbers, id) + { + ComponentRef = componentId, + File = file, + Description = description, + Group = group, + Attributes = attributes, + }); + } + } + + /// + /// Parses a web site element. + /// + /// Element to parse. + /// Optional identifier of parent component. + private void ParseWebSiteElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + string application = null; + int attributes = 0; + var connectionTimeout = CompilerConstants.IntegerNotSet; + string description = null; + string directory = null; + string dirProperties = null; + string keyAddress = null; + string log = null; + string siteId = null; + var sequence = CompilerConstants.IntegerNotSet; + var state = CompilerConstants.IntegerNotSet; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "AutoStart": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + state = 2; + } + else if (state != 1) + { + state = 0; + } + break; + case "ConfigureIfExists": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + attributes &= ~2; + } + else + { + attributes |= 2; + } + break; + case "ConnectionTimeout": + connectionTimeout = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue); + break; + case "Description": + description = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Directory": + directory = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.Directory, directory); + break; + case "DirProperties": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + dirProperties = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "SiteId": + siteId = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + if ("*" == siteId) + { + siteId = "-1"; + } + break; + case "Sequence": + sequence = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, short.MaxValue); + break; + case "StartOnInstall": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + // when state is set to 2 it implies 1, so don't set it to 1 + if (2 != state && YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + state = 1; + } + else if (2 != state) + { + state = 0; + } + break; + case "WebApplication": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + application = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "WebLog": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); + } + + log = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, IisSymbolDefinitions.IIsWebLog, log); + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (null == id) + { + id = this.ParseHelper.CreateIdentifier("iws", description, componentId, siteId, application); + } + + if (null == description) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Description")); + } + + if (null == directory && null != componentId) + { + this.Messaging.Write(IIsErrors.RequiredAttributeUnderComponent(sourceLineNumbers, element.Name.LocalName, "Directory")); + } + + foreach (var child in element.Elements()) + { + if (this.Namespace == child.Name.Namespace) + { + var childSourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(child); + switch (child.Name.LocalName) + { + case "CertificateRef": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalElementWithoutComponent(childSourceLineNumbers, child.Name.LocalName)); + } + + this.ParseCertificateRefElement(intermediate, section, child, id?.Id); + break; + case "HttpHeader": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalElementWithoutComponent(childSourceLineNumbers, child.Name.LocalName)); + } + + this.ParseHttpHeaderElement(intermediate, section, child, HttpHeaderParentType.WebSite, id?.Id); + break; + case "WebAddress": + string address = this.ParseWebAddressElement(intermediate, section, child, id?.Id); + if (null == keyAddress) + { + keyAddress = address; + } + break; + case "WebApplication": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalElementWithoutComponent(childSourceLineNumbers, child.Name.LocalName)); + } + + if (null != application) + { + this.Messaging.Write(IIsErrors.WebApplicationAlreadySpecified(childSourceLineNumbers, element.Name.LocalName)); + } + + application = this.ParseWebApplicationElement(intermediate, section, child); + break; + case "WebDir": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalElementWithoutComponent(childSourceLineNumbers, child.Name.LocalName)); + } + + this.ParseWebDirElement(intermediate, section, child, componentId, id?.Id); + break; + case "WebDirProperties": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalElementWithoutComponent(childSourceLineNumbers, child.Name.LocalName)); + } + + string childWebDirProperties = this.ParseWebDirPropertiesElement(intermediate, section, child, componentId); + if (null == dirProperties) + { + dirProperties = childWebDirProperties; + } + else + { + this.Messaging.Write(ErrorMessages.IllegalParentAttributeWhenNested(sourceLineNumbers, "WebSite", "DirProperties", child.Name.LocalName)); + } + break; + case "WebError": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalElementWithoutComponent(childSourceLineNumbers, child.Name.LocalName)); + } + + this.ParseWebErrorElement(intermediate, section, child, WebErrorParentType.WebSite, id?.Id); + break; + case "WebFilter": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalElementWithoutComponent(childSourceLineNumbers, child.Name.LocalName)); + } + + this.ParseWebFilterElement(intermediate, section, child, componentId, id?.Id); + break; + case "WebVirtualDir": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalElementWithoutComponent(childSourceLineNumbers, child.Name.LocalName)); + } + + this.ParseWebVirtualDirElement(intermediate, section, child, componentId, id?.Id, null); + break; + case "MimeMap": + this.ParseMimeMapElement(intermediate, section, child, id?.Id, MimeMapParentType.WebSite); + break; + default: + this.ParseHelper.UnexpectedElement(element, child); + break; + } + } + else + { + this.ParseHelper.ParseExtensionElement(this.Context.Extensions, intermediate, section, element, child); + } + } + + + if (null == keyAddress) + { + this.Messaging.Write(ErrorMessages.ExpectedElement(sourceLineNumbers, element.Name.LocalName, "WebAddress")); + } + + if (null != application) + { + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, IisSymbolDefinitions.IIsWebApplication, application); + } + + if (null != dirProperties) + { + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, IisSymbolDefinitions.IIsWebDirProperties, dirProperties); + } + + if (null != componentId) + { + // Reference ConfigureIIs since nothing will happen without it + this.AddReferenceToConfigureIIs(section, sourceLineNumbers); + } + + if (!this.Messaging.EncounteredError) + { + var symbol = section.AddSymbol(new IIsWebSiteSymbol(sourceLineNumbers, id) + { + ComponentRef = componentId, + Description = description, + DirectoryRef = directory, + KeyAddressRef = keyAddress, + DirPropertiesRef = dirProperties, + ApplicationRef = application, + LogRef = log, + WebsiteId = siteId, + }); + + if (CompilerConstants.IntegerNotSet != connectionTimeout) + { + symbol.ConnectionTimeout = connectionTimeout; + } + + if (CompilerConstants.IntegerNotSet != state) + { + symbol.State = state; + } + + if (0 != attributes) + { + symbol.Attributes = attributes; + } + + if (CompilerConstants.IntegerNotSet != sequence) + { + symbol.Sequence = sequence; + } + } + } + + /// + /// Parses a HTTP Header element. + /// + /// Element to parse. + /// Type of the parent. + /// Id of the parent. + private void ParseHttpHeaderElement(Intermediate intermediate, IntermediateSection section, XElement element, HttpHeaderParentType parentType, string parent) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + string headerName = null; + string headerValue = null; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Name": + headerName = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Value": + headerValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (null == headerName) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Name")); + } + else if (null == id) + { + id = this.ParseHelper.CreateIdentifierFromFilename(headerName); + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + + // Reference ConfigureIIs since nothing will happen without it + this.AddReferenceToConfigureIIs(section, sourceLineNumbers); + + if (!this.Messaging.EncounteredError) + { + section.AddSymbol(new IIsHttpHeaderSymbol(sourceLineNumbers, id) + { + HttpHeader = id.Id, + ParentType = (int)parentType, + ParentValue = parent, + Name = headerName, + Value = headerValue, + Attributes = 0, + }); + } + } + + /// + /// Parses a virtual directory element. + /// + /// Element to parse. + /// Identifier of parent component. + /// Identifier of parent web site. + /// Alias of the parent web site. + private void ParseWebVirtualDirElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId, string parentWeb, string parentAlias) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + string alias = null; + string application = null; + string directory = null; + string dirProperties = null; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Alias": + alias = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Directory": + directory = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.Directory, directory); + break; + case "DirProperties": + dirProperties = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "WebApplication": + application = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "WebSite": + if (null != parentWeb) + { + this.Messaging.Write(IIsErrors.WebSiteAttributeUnderWebSite(sourceLineNumbers, element.Name.LocalName)); + } + + parentWeb = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, IisSymbolDefinitions.IIsWebSite, parentWeb); + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (null == id) + { + id = this.ParseHelper.CreateIdentifier("wvd", alias, directory, dirProperties, application, parentWeb); + } + + if (null == alias) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Alias")); + } + else if (-1 != alias.IndexOf("\\", StringComparison.Ordinal)) + { + this.Messaging.Write(IIsErrors.IllegalCharacterInAttributeValue(sourceLineNumbers, element.Name.LocalName, "Alias", alias, '\\')); + } + + if (null == directory) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Directory")); + } + + if (null == parentWeb) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "WebSite")); + } + + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalElementWithoutComponent(sourceLineNumbers, element.Name.LocalName)); + } + + if (null != parentAlias) + { + alias = String.Concat(parentAlias, "/", alias); + } + + foreach (var child in element.Elements()) + { + if (this.Namespace == child.Name.Namespace) + { + var childSourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(child); + switch (child.Name.LocalName) + { + case "WebApplication": + if (null != application) + { + this.Messaging.Write(IIsErrors.WebApplicationAlreadySpecified(childSourceLineNumbers, element.Name.LocalName)); + } + + application = this.ParseWebApplicationElement(intermediate, section, child); + break; + case "WebDirProperties": + if (null == componentId) + { + this.Messaging.Write(IIsErrors.IllegalElementWithoutComponent(childSourceLineNumbers, child.Name.LocalName)); + } + + string childWebDirProperties = this.ParseWebDirPropertiesElement(intermediate, section, child, componentId); + if (null == dirProperties) + { + dirProperties = childWebDirProperties; + } + else + { + this.Messaging.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, child.Name.LocalName, "DirProperties", child.Name.LocalName)); + } + break; + + case "WebError": + this.ParseWebErrorElement(intermediate, section, child, WebErrorParentType.WebVirtualDir, id?.Id); + break; + case "WebVirtualDir": + this.ParseWebVirtualDirElement(intermediate, section, child, componentId, parentWeb, alias); + break; + case "HttpHeader": + this.ParseHttpHeaderElement(intermediate, section, child, HttpHeaderParentType.WebVirtualDir, id?.Id); + break; + case "MimeMap": + this.ParseMimeMapElement(intermediate, section, child, id?.Id, MimeMapParentType.WebVirtualDir); + break; + default: + this.ParseHelper.UnexpectedElement(element, child); + break; + } + } + else + { + this.ParseHelper.ParseExtensionElement(this.Context.Extensions, intermediate, section, element, child); + } + } + + if (null != dirProperties) + { + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, IisSymbolDefinitions.IIsWebDirProperties, dirProperties); + } + + if (null != application) + { + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, IisSymbolDefinitions.IIsWebApplication, application); + } + + // Reference ConfigureIIs since nothing will happen without it + this.AddReferenceToConfigureIIs(section, sourceLineNumbers); + + if (!this.Messaging.EncounteredError) + { + section.AddSymbol(new IIsWebVirtualDirSymbol(sourceLineNumbers, id) + { + ComponentRef = componentId, + WebRef = parentWeb, + Alias = alias, + DirectoryRef = directory, + DirPropertiesRef = dirProperties, + ApplicationRef = application, + }); + } + } + + private void AddReferenceToConfigureIIs(IntermediateSection section, SourceLineNumber sourceLineNumbers) + { + this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4ConfigureIIs", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); + } + } +} -- cgit v1.2.3-55-g6feb