// 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.Harvesters { using System; using System.DirectoryServices; using System.Globalization; using System.Runtime.InteropServices; using WixToolset.Data; using WixToolset.Harvesters.Data; using WixToolset.Harvesters.Extensibility; using IIs = WixToolset.Harvesters.Serialize.IIs; using Wix = WixToolset.Harvesters.Serialize; /// /// The web site harvester for the WiX Toolset Internet Information Services Extension. /// public sealed class IIsWebSiteHarvester : BaseHarvesterExtension { /// /// Harvest a WiX document. /// /// The argument for harvesting. /// The harvested Fragment. public override Wix.Fragment[] Harvest(string argument) { DirectoryHarvester directoryHarvester = new DirectoryHarvester(); directoryHarvester.Core = this.Core; directoryHarvester.KeepEmptyDirectories = true; IIsWebSiteHarvester iisWebSiteHarvester = new IIsWebSiteHarvester(); iisWebSiteHarvester.Core = this.Core; IIs.WebSite webSite = iisWebSiteHarvester.HarvestWebSite(argument); Wix.Component component = new Wix.Component(); component.AddChild(new Wix.CreateFolder()); component.AddChild(webSite); this.Core.RootDirectory = webSite.Directory; Wix.Directory directory = directoryHarvester.HarvestDirectory(webSite.Directory, true); directory.AddChild(component); Wix.Fragment fragment = new Wix.Fragment(); fragment.AddChild(directory); return new Wix.Fragment[] { fragment }; } /// /// Harvest a web site. /// /// The name of the web site. /// The harvested web site. public IIs.WebSite HarvestWebSite(string name) { try { DirectoryEntry directoryEntry = new DirectoryEntry("IIS://localhost/W3SVC"); foreach (DirectoryEntry childEntry in directoryEntry.Children) { if ("IIsWebServer" == childEntry.SchemaClassName) { if (String.Equals((string)childEntry.Properties["ServerComment"].Value, name, StringComparison.OrdinalIgnoreCase)) { return this.HarvestWebSite(childEntry); } } } } catch (COMException ce) { // 0x8007005 - access denied // If we don't have permission to harvest a website, it's likely because we're on // Vista or higher and aren't an Admin. if ((0x80070005 == unchecked((uint)ce.ErrorCode))) { throw new WixException(HarvesterErrors.InsufficientPermissionHarvestWebSite()); } // 0x80005000 - unknown error else if ((0x80005000 == unchecked((uint)ce.ErrorCode))) { throw new WixException(HarvesterErrors.CannotHarvestWebSite()); } } throw new WixException(HarvesterErrors.WebSiteNotFound(name)); } /// /// Harvest a web site. /// /// The web site directory entry. /// The harvested web site. private IIs.WebSite HarvestWebSite(DirectoryEntry webSiteEntry) { IIs.WebSite webSite = new IIs.WebSite(); foreach (string propertyName in webSiteEntry.Properties.PropertyNames) { PropertyValueCollection property = webSiteEntry.Properties[propertyName]; PropertyValueCollection parentProperty = webSiteEntry.Parent.Properties[propertyName]; if (null == parentProperty.Value || parentProperty.Value.ToString() != property.Value.ToString()) { switch (propertyName) { case "SecureBindings": IIs.WebAddress secureWebAddress = this.HarvestBindings(propertyName, property); if (null != secureWebAddress) { webSite.AddChild(secureWebAddress); } break; case "ServerBindings": IIs.WebAddress webAddress = this.HarvestBindings(propertyName, property); if (null != webAddress) { webSite.AddChild(webAddress); } break; case "ServerComment": webSite.Description = (string)property.Value; break; } } } foreach (DirectoryEntry childEntry in webSiteEntry.Children) { switch (childEntry.SchemaClassName) { case "IIsFilters": string loadOrder = (string)childEntry.Properties["FilterLoadOrder"].Value; if (loadOrder.Length > 0) { string[] filterNames = loadOrder.Split(",".ToCharArray()); for (int i = 0; i < filterNames.Length; i++) { using (DirectoryEntry webFilterEntry = new DirectoryEntry(String.Concat(childEntry.Path, '/', filterNames[i]))) { IIs.WebFilter webFilter = this.HarvestWebFilter(webFilterEntry); webFilter.LoadOrder = (i + 1).ToString(CultureInfo.InvariantCulture); webSite.AddChild(webFilter); } } } break; case "IIsWebDirectory": this.HarvestWebDirectory(childEntry, webSite); break; case "IIsWebVirtualDir": foreach (string propertyName in childEntry.Properties.PropertyNames) { PropertyValueCollection property = childEntry.Properties[propertyName]; switch (propertyName) { case "Path": webSite.Directory = (string)property.Value; break; } } IIs.WebDirProperties webDirProps = this.HarvestWebDirProperties(childEntry); if (null != webDirProps) { webSite.AddChild(webDirProps); } foreach (DirectoryEntry child2Entry in childEntry.Children) { switch (child2Entry.SchemaClassName) { case "IIsWebDirectory": this.HarvestWebDirectory(child2Entry, webSite); break; case "IIsWebVirtualDir": this.HarvestWebVirtualDir(child2Entry, webSite); break; } } break; } } return webSite; } /// /// Harvest bindings. /// /// The property name of the bindings property. /// The bindings property. /// The harvested bindings. private IIs.WebAddress HarvestBindings(string propertyName, PropertyValueCollection bindingsProperty) { if (1 == bindingsProperty.Count) { IIs.WebAddress webAddress = new IIs.WebAddress(); string[] bindings = ((string)bindingsProperty[0]).Split(":".ToCharArray()); if (0 < bindings[0].Length) { webAddress.IP = bindings[0]; } if (0 < bindings[1].Length) { webAddress.Port = bindings[1]; } if (0 < bindings[2].Length) { webAddress.Header = bindings[2]; } if ("SecureBindings" == propertyName) { webAddress.Secure = IIs.YesNoType.yes; } return webAddress; } return null; } /// /// Harvest a web directory. /// /// The web directory directory entry. /// The parent web site. private void HarvestWebDirectory(DirectoryEntry webDirectoryEntry, IIs.WebSite webSite) { foreach (DirectoryEntry childEntry in webDirectoryEntry.Children) { switch (childEntry.SchemaClassName) { case "IIsWebDirectory": this.HarvestWebDirectory(childEntry, webSite); break; case "IIsWebVirtualDir": this.HarvestWebVirtualDir(childEntry, webSite); break; } } IIs.WebDirProperties webDirProperties = this.HarvestWebDirProperties(webDirectoryEntry); if (null != webDirProperties) { IIs.WebDir webDir = new IIs.WebDir(); int indexOfRoot = webDirectoryEntry.Path.IndexOf("ROOT/", StringComparison.OrdinalIgnoreCase); webDir.Path = webDirectoryEntry.Path.Substring(indexOfRoot + 5); webDir.AddChild(webDirProperties); webSite.AddChild(webDir); } } /// /// Harvest a web filter. /// /// The web filter directory entry. /// The harvested web filter. private IIs.WebFilter HarvestWebFilter(DirectoryEntry webFilterEntry) { IIs.WebFilter webFilter = new IIs.WebFilter(); webFilter.Name = webFilterEntry.Name; foreach (string propertyName in webFilterEntry.Properties.PropertyNames) { PropertyValueCollection property = webFilterEntry.Properties[propertyName]; switch (propertyName) { case "FilterDescription": webFilter.Description = (string)property.Value; break; case "FilterFlags": webFilter.Flags = (int)property.Value; break; case "FilterPath": webFilter.Path = (string)property.Value; break; } } return webFilter; } /// /// Harvest a web directory's properties. /// /// The web directory directory entry. /// The harvested web directory's properties. private IIs.WebDirProperties HarvestWebDirProperties(DirectoryEntry directoryEntry) { bool foundProperties = false; IIs.WebDirProperties webDirProperties = new IIs.WebDirProperties(); // Cannot read properties for "iisadmin" site. if (String.Equals("iisadmin", directoryEntry.Name, StringComparison.OrdinalIgnoreCase) && String.Equals("ROOT", directoryEntry.Parent.Name, StringComparison.OrdinalIgnoreCase)) { return null; } foreach (string propertyName in directoryEntry.Properties.PropertyNames) { PropertyValueCollection property = directoryEntry.Properties[propertyName]; PropertyValueCollection parentProperty = directoryEntry.Parent.Properties[propertyName]; if (null == parentProperty.Value || parentProperty.Value.ToString() != property.Value.ToString()) { switch (propertyName) { case "AccessFlags": int access = (int)property.Value; if (0x1 == (access & 0x1)) { webDirProperties.Read = IIs.YesNoType.yes; } if (0x2 == (access & 0x2)) { webDirProperties.Write = IIs.YesNoType.yes; } if (0x4 == (access & 0x4)) { webDirProperties.Execute = IIs.YesNoType.yes; } if (0x200 == (access & 0x200)) { webDirProperties.Script = IIs.YesNoType.yes; } foundProperties = true; break; case "AuthFlags": int authorization = (int)property.Value; if (0x1 == (authorization & 0x1)) { webDirProperties.AnonymousAccess = IIs.YesNoType.yes; } if (0x2 == (authorization & 0x2)) { webDirProperties.BasicAuthentication = IIs.YesNoType.yes; } if (0x4 == (authorization & 0x4)) { webDirProperties.WindowsAuthentication = IIs.YesNoType.yes; } if (0x10 == (authorization & 0x10)) { webDirProperties.DigestAuthentication = IIs.YesNoType.yes; } if (0x40 == (authorization & 0x40)) { webDirProperties.PassportAuthentication = IIs.YesNoType.yes; } foundProperties = true; break; } } } return foundProperties ? webDirProperties : null; } /// /// Harvest a web virtual directory. /// /// The web virtual directory directory entry. /// The parent web site. private void HarvestWebVirtualDir(DirectoryEntry webVirtualDirEntry, IIs.WebSite webSite) { IIs.WebVirtualDir webVirtualDir = new IIs.WebVirtualDir(); foreach (string propertyName in webVirtualDirEntry.Properties.PropertyNames) { PropertyValueCollection property = webVirtualDirEntry.Properties[propertyName]; PropertyValueCollection parentProperty = webVirtualDirEntry.Parent.Properties[propertyName]; if (null == parentProperty.Value || parentProperty.Value.ToString() != property.Value.ToString()) { switch (propertyName) { case "Path": webVirtualDir.Directory = (string)property.Value; break; } } } int indexOfRoot = webVirtualDirEntry.Path.IndexOf("ROOT/", StringComparison.OrdinalIgnoreCase); webVirtualDir.Alias = webVirtualDirEntry.Path.Substring(indexOfRoot + 5); IIs.WebDirProperties webDirProps = this.HarvestWebDirProperties(webVirtualDirEntry); if (webDirProps != null) { webVirtualDir.AddChild(webDirProps); } foreach (DirectoryEntry childEntry in webVirtualDirEntry.Children) { switch (childEntry.SchemaClassName) { case "IIsWebDirectory": this.HarvestWebDirectory(childEntry, webSite); break; case "IIsWebVirtualDir": this.HarvestWebVirtualDir(childEntry, webSite); break; } } webSite.AddChild(webVirtualDir); } } }