File: Configuration\HttpConfigurationSystem.cs
Project: ndp\fx\src\xsp\system\Web\System.Web.csproj (System.Web)
//------------------------------------------------------------------------------
// <copyright file="HttpConfigurationSystem.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
 
namespace System.Web.Configuration {
    using System.Configuration.Internal;
    using Microsoft.Win32;
    using System.Collections;
    using System.Collections.Specialized;
    using System.Configuration;
    using System.IO;
    using System.Threading;
    using System.Web;
    using System.Web.Caching;
    using System.Web.Hosting;
    using System.Xml;
    using System.Web.Util;
    using System.Globalization;
    using System.Reflection;
    using System.Security;
    using System.Security.Permissions;
 
    using CultureInfo = System.Globalization.CultureInfo;
    using Debug = System.Web.Util.Debug;
    using UnicodeEncoding = System.Text.UnicodeEncoding;
    using UrlPath = System.Web.Util.UrlPath;
 
    internal class HttpConfigurationSystem : IInternalConfigSystem {
        private const string InternalConfigSettingsFactoryTypeString = "System.Configuration.Internal.InternalConfigSettingsFactory, " + AssemblyRef.SystemConfiguration;
        internal const string ConfigSystemTypeString = "System.Configuration.Internal.ConfigSystem, " + AssemblyRef.SystemConfiguration;
 
#if !PLATFORM_UNIX // File system paths must be lowercased in UNIX
        internal const string MachineConfigSubdirectory = "Config";
#else // !PLATFORM_UNIX
        internal const string MachineConfigSubdirectory = "config";
#endif // !PLATFORM_UNIX
        internal const string MachineConfigFilename = "machine.config";
        internal const string RootWebConfigFilename         = "web.config";
        internal const string WebConfigFileName             = "web.config";
        internal const string InetsrvDirectoryName          = "inetsrv";
        internal const string ApplicationHostConfigFileName = "applicationHost.config";
 
        static private object                           s_initLock;
        static private volatile bool                   s_inited;
 
        // Supports IInternalConfigSystem and the file change dependency delegate
        static private HttpConfigurationSystem          s_httpConfigSystem;
        static private IConfigSystem                    s_configSystem;
 
        static private IConfigMapPath                   s_configMapPath;
        static private WebConfigurationHost             s_configHost;
        static private FileChangeEventHandler           s_fileChangeEventHandler;
        static private string                           s_MsCorLibDirectory;
        static private string                           s_MachineConfigurationDirectory;
        static private string                           s_MachineConfigurationFilePath;
        static private string                           s_RootWebConfigurationFilePath;
 
        static private IInternalConfigRoot              s_configRoot;
        static private IInternalConfigSettingsFactory   s_configSettingsFactory;
        static private bool                             s_initComplete;
 
        static HttpConfigurationSystem() {
            s_initLock = new object();
        }
 
        HttpConfigurationSystem() {}
 
        //
        // Set this configuration system to the default for requests ConfigurationManager.GetSection
        //
        static internal void EnsureInit(IConfigMapPath configMapPath, bool listenToFileChanges, bool initComplete) {
            if (!s_inited) {
                lock (s_initLock) {
                    if (!s_inited) {
                        s_initComplete = initComplete;
 
                        // Use the IIS map path if one is not explicitly provided
                        if (configMapPath == null) {
                            configMapPath = IISMapPath.GetInstance();
                        }
 
                        s_configMapPath = configMapPath;
 
                        Type typeConfigSystem = Type.GetType(ConfigSystemTypeString, true);
                        s_configSystem = (IConfigSystem) Activator.CreateInstance(typeConfigSystem, true);
                        s_configSystem.Init(
                                typeof(WebConfigurationHost),               // The config host we'll create and use
                                // The remaining parameters are passed to the config host:
                                true,                                       // Use the supplied configMapPath
                                s_configMapPath,                            // the configMapPath to use
                                null,                                       // ConfigurationFileMap
                                HostingEnvironment.ApplicationVirtualPath,  // app path
                                HostingEnvironment.SiteNameNoDemand,        // app site name
                                HostingEnvironment.SiteID);                 // app site ID
 
                        s_configRoot = s_configSystem.Root;
                        s_configHost = (WebConfigurationHost) s_configSystem.Host;
 
                        // Register for config changed notifications
                        HttpConfigurationSystem configSystem = new HttpConfigurationSystem();
 
                        if (listenToFileChanges) {
                            s_configRoot.ConfigChanged += new InternalConfigEventHandler(configSystem.OnConfigurationChanged);
                        }
 
                        // Set the configSystem into the ConfigurationManager class.
                        // Please note that factory.SetConfigurationSystem will end up calling
                        // ConfigurationManager.SetConfigurationSystem, which is an internal static method
                        // in System.Configuration.dll.  If we want to call that here, we have to use
                        // reflection and that's what we want to avoid.
                        Type typeFactory = Type.GetType(InternalConfigSettingsFactoryTypeString, true);
                        s_configSettingsFactory = (IInternalConfigSettingsFactory) Activator.CreateInstance(typeFactory, true);
                        s_configSettingsFactory.SetConfigurationSystem(configSystem, initComplete);
 
                        // The system has been successfully set, so mark that we should use it.
                        s_httpConfigSystem = configSystem;
 
                        // Mark as having completed initialization after s_httpConfigSystem has been set.
                        // s_inited is coordinated with s_httpConfigSystem in UseHttpConfigurationSystem.
                        s_inited = true;
                    }
                }
            }
 
            Debug.Assert(s_httpConfigSystem != null, "s_httpConfigSystem != null - The appdomain is using the client configuration system.");
        }
 
        static internal void CompleteInit() {
            Debug.Assert(!s_initComplete, "!s_initComplete");
            s_configSettingsFactory.CompleteInit();
            s_configSettingsFactory = null;
        }
 
        // Return true if the HttpConfigurationSystem is being used
        // by ConfigurationManager.
        static internal bool UseHttpConfigurationSystem {
            get {
                if (!s_inited) {
                    lock (s_initLock) {
                        if (!s_inited) {
                            //
                            // If we ask whether the HttpConfigurationSystem is in use, and it has not
                            // been initialized, then this caller is going to end up using the client
                            // configuration system. So prevent initialization of the HttpConfigurationSystem
                            // by setting s_inited = true.
                            //
                            s_inited = true;
                            Debug.Assert(s_httpConfigSystem == null, "s_httpConfigSystem == null");
                        }
                    }
                }
 
                return s_httpConfigSystem != null;
            }
        }
 
        // Return true if the HttpConfigurationSystem is already loaded
        // by ConfigurationManager.
        static internal bool IsSet {
            get {
                return s_httpConfigSystem != null;
            }
        }
 
 
        //
        // Return the config object for the current context.
        // If the HttpContext is not available, get the config object
        // for the web application path.
        //
        object IInternalConfigSystem.GetSection(string configKey) {
            return HttpConfigurationSystem.GetSection(configKey);
        }
 
        // Does not support refresh - the appdomain will restart if
        // a file changes.
        void IInternalConfigSystem.RefreshConfig(string sectionName) {}
 
        // Supports user config
        bool IInternalConfigSystem.SupportsUserConfig {
            get {
                return false;
            }
        }
 
        // GetSection
        //
        // Get the Config for the current context
        //
        static internal object GetSection(string sectionName) {
            HttpContext context = HttpContext.Current;
            if (context != null) {
                return context.GetSection(sectionName);
            }
            else {
                // If there is no context, then lets get the config for
                // the application we are hosted in.
                return GetApplicationSection(sectionName);
            }
        }
 
        // GetSection
        //
        // Get the Config for a specific path
        //
        static internal object GetSection(string sectionName, VirtualPath path) {
            Debug.Assert(UseHttpConfigurationSystem, "UseHttpConfigurationSystem");
 
            CachedPathData pathData;
 
            pathData = CachedPathData.GetVirtualPathData(path, true);
 
            return pathData.ConfigRecord.GetSection(sectionName);
        }
 
        static internal object GetSection(string sectionName, string path) {
            return GetSection(sectionName, VirtualPath.CreateNonRelativeAllowNull(path));
        }
 
        // GetAppSection
        //
        // Get the Config for a specific path
        //
        static internal object GetApplicationSection(string sectionName) {
            Debug.Assert(UseHttpConfigurationSystem, "UseHttpConfigurationSystem");
 
            CachedPathData pathData;
 
            pathData = CachedPathData.GetApplicationPathData();
 
            return pathData.ConfigRecord.GetSection(sectionName);
        }
 
 
        //
        // Return the unique configuration record for a config path.
        // Used by CachedPathData to retreive config records.
        //
        static internal IInternalConfigRecord GetUniqueConfigRecord(string configPath) {
            if (!UseHttpConfigurationSystem)
                return null;
 
            IInternalConfigRecord configRecord = s_configRoot.GetUniqueConfigRecord(configPath);
            return configRecord;
        }
 
        static internal void AddFileDependency(String file) {
            if (String.IsNullOrEmpty(file))
                return;
#if !FEATURE_PAL // No file change notification in Coriolis
            if (UseHttpConfigurationSystem) {
                if (s_fileChangeEventHandler == null) {
                    s_fileChangeEventHandler = new FileChangeEventHandler(s_httpConfigSystem.OnConfigFileChanged);
                }
 
                HttpRuntime.FileChangesMonitor.StartMonitoringFile(file, s_fileChangeEventHandler);
            }
#endif // !FEATURE_PAL
        }
 
        internal void OnConfigurationChanged(Object sender, InternalConfigEventArgs e) {
            HttpRuntime.OnConfigChange(message: null);
        }
 
        internal void OnConfigFileChanged(Object sender, FileChangeEvent e) {
            string message = FileChangesMonitor.GenerateErrorMessage(e.Action, e.FileName);
            HttpRuntime.OnConfigChange(message);
        }
 
        static internal String MsCorLibDirectory {
            [FileIOPermissionAttribute(SecurityAction.Assert, AllFiles=FileIOPermissionAccess.PathDiscovery)]
            get {
                if (s_MsCorLibDirectory == null) {
                    s_MsCorLibDirectory = System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory();
                }
 
                return s_MsCorLibDirectory;
            }
        }
 
        static internal string MachineConfigurationDirectory {
            get {
                if (s_MachineConfigurationDirectory == null) {
#if !FEATURE_PAL
                    s_MachineConfigurationDirectory = Path.Combine(MsCorLibDirectory, MachineConfigSubdirectory);
#else // !FEATURE_PAL
                    System.UInt32 length = 0;
 
                    // Get the required size
                    if (!UnsafeNativeMethods.GetMachineConfigurationDirectory(null, ref length)) {
                        throw new System.ComponentModel.Win32Exception();
                    }
 
                    // Now, create the string and call again
                    System.Text.StringBuilder sb = new System.Text.StringBuilder((int)length);
 
                    if (!UnsafeNativeMethods.GetMachineConfigurationDirectory(sb, ref length)) {
                        throw new System.ComponentModel.Win32Exception();
                    }
 
                    s_MachineConfigurationDirectory = sb.ToString();
#endif // !FEATURE_PAL
                }
 
                return s_MachineConfigurationDirectory;
            }
        }
 
        static internal string MachineConfigurationFilePath {
            get {
                if (s_MachineConfigurationFilePath == null) {
                    s_MachineConfigurationFilePath = Path.Combine(MachineConfigurationDirectory, MachineConfigFilename);
                }
 
                return s_MachineConfigurationFilePath;
            }
        }
 
        static internal string RootWebConfigurationFilePath {
            get {
                if (s_RootWebConfigurationFilePath == null) {
                    s_RootWebConfigurationFilePath = Path.Combine(MachineConfigurationDirectory, RootWebConfigFilename);
                }
 
                return s_RootWebConfigurationFilePath;
            }
            // for IIS 7, support setting this file path to support the hostable
            // web core and admin scenarios
            set {
                s_RootWebConfigurationFilePath = value;
                if (null == s_RootWebConfigurationFilePath) {
                    s_RootWebConfigurationFilePath = Path.Combine(MachineConfigurationDirectory, RootWebConfigFilename);
                }
            }
        }
    }
}