File: net\System\Net\Configuration\UriSection.cs
Project: ndp\fx\src\System.csproj (System)
//------------------------------------------------------------------------------
// <copyright file="UriSection.cs" company="Microsoft Corporation">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
 
 
namespace System.Configuration
{
    using System.Diagnostics.CodeAnalysis;
    using System.Threading;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Security.Permissions;
    using System.Globalization;
    using System.Runtime.InteropServices;
    using System.IO;
    
    /// <summary>
    /// Summary description for UriSection.
    /// </summary>
    public sealed class UriSection : ConfigurationSection
    {
        private static readonly ConfigurationPropertyCollection properties = new ConfigurationPropertyCollection();
 
        private static readonly ConfigurationProperty idn = new ConfigurationProperty(CommonConfigurationStrings.Idn, 
            typeof(IdnElement), null, ConfigurationPropertyOptions.None);
 
        private static readonly ConfigurationProperty iriParsing = new ConfigurationProperty(
            CommonConfigurationStrings.IriParsing, typeof(IriParsingElement), null, ConfigurationPropertyOptions.None);
 
        private static readonly ConfigurationProperty schemeSettings =
            new ConfigurationProperty(CommonConfigurationStrings.SchemeSettings,
            typeof(SchemeSettingElementCollection), null, ConfigurationPropertyOptions.None);
 
        static UriSection()
        {
            properties.Add(idn);
            properties.Add(iriParsing);
            properties.Add(schemeSettings);
        }
 
        [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Idn", Justification = "changing this would be a breaking change because the API has been present since v3.5")]
        [ConfigurationProperty(CommonConfigurationStrings.Idn)]
        public IdnElement Idn{
            get {
                return (IdnElement)this[idn]; 
            }
        }
 
        [ConfigurationProperty(CommonConfigurationStrings.IriParsing)]
        public IriParsingElement IriParsing
        {
            get {
                return (IriParsingElement)this[iriParsing];
            }
        }
 
        [ConfigurationProperty(CommonConfigurationStrings.SchemeSettings)]
        public SchemeSettingElementCollection SchemeSettings
        {
            get {
                return (SchemeSettingElementCollection)this[schemeSettings];
            }
        }
        
        protected override ConfigurationPropertyCollection Properties 
        {
            get {
                return properties;
            }
        }
    }
 
    internal sealed class UriSectionInternal
    {
        private static readonly object classSyncObject = new object();
        private UriIdnScope idnScope;
        private bool iriParsing;
        private Dictionary<string, SchemeSettingInternal> schemeSettings;
 
        private UriSectionInternal()
        {
            this.schemeSettings = new Dictionary<string, SchemeSettingInternal>();
        }
 
        private UriSectionInternal(UriSection section)
            : this()
        {
            this.idnScope = section.Idn.Enabled;
            this.iriParsing = section.IriParsing.Enabled;
 
            if (section.SchemeSettings != null) {
                SchemeSettingInternal schemeSetting;
                foreach (SchemeSettingElement element in section.SchemeSettings)
                {
                    schemeSetting = new SchemeSettingInternal(element.Name, element.GenericUriParserOptions);
                    this.schemeSettings.Add(schemeSetting.Name, schemeSetting);
    	        }
            }
        }
 
        private UriSectionInternal(UriIdnScope idnScope, bool iriParsing,
            IEnumerable<SchemeSettingInternal> schemeSettings)
            : this()
        {
            this.idnScope = idnScope;
            this.iriParsing = iriParsing;
 
            if (schemeSettings != null) {
                foreach (SchemeSettingInternal schemeSetting in schemeSettings) {
                    this.schemeSettings.Add(schemeSetting.Name, schemeSetting);
                }
            }
        }
 
        internal UriIdnScope IdnScope
        {
            get { return idnScope; }
        }
 
        internal bool IriParsing
        {
            get { return iriParsing; }
        }
 
        internal SchemeSettingInternal GetSchemeSetting(string scheme)
        {
            SchemeSettingInternal result;
            if (schemeSettings.TryGetValue(scheme.ToLowerInvariant(), out result)) {
                return result;
            }
            else {
                return null;
            }
        }
 
        // This method originally just used System.Configuration to get the new-to-Orcas Uri config section.  
        // Unfortunately that created a circular dependency on System.Config that ultimately could cause
        // ConfigurationExceptions that didn't used to happen in Whidbey and thus break existing deployed 
        // applications.
        //
        // Now this method will determine if it is running in a client application or a web scenario (ASP.NET).   
        // If in a web scenario this code must still use System.Configuration to read config as the web scenario 
        // has a hierarchy of config files that only System.Configuration can practically discover and parse.  
        // In a client scenario this code will now use System.Xml.XmlReader to parse machine and app config files.
        //
        // The default output of this method if it encounters invalid or non-existent Uri config is the original 
        // Whidbey settings (no IDN support, no IRI support, and escaped dots and slashes are unescaped).
        //
        [SuppressMessage("Microsoft.Security","CA2106:SecureAsserts", Justification="Must Assert unrestricted FileIOPermission to get the app config path")]
        internal static UriSectionInternal GetSection()
        {
            lock (classSyncObject) {
 
                string appConfigFilePath = null;
 
                // Must Assert unrestricted FileIOPermission to get the app config path.
                new FileIOPermission(PermissionState.Unrestricted).Assert();
                try {
                    appConfigFilePath = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
                }
                finally {
                    FileIOPermission.RevertAssert();
                }
 
                if (IsWebConfig(appConfigFilePath)) {
                    // This is a web scenario. It is safe and *necessary* to use System.Configuration.
                    return LoadUsingSystemConfiguration();
                }
                else {
                    // This is a client application scenario. It is not safe to use System.Config for app 
                    // compat reasons. 
                    return LoadUsingCustomParser(appConfigFilePath);
                }
            }
        }
 
        private static UriSectionInternal LoadUsingSystemConfiguration()
        {
            try {
                UriSection section = PrivilegedConfigurationManager.GetSection(
                    CommonConfigurationStrings.UriSectionName) as UriSection;
 
                if (section == null) {
                    return null;
                }
 
                return new UriSectionInternal(section);
            }
            catch (ConfigurationException) {
                // Simply ---- any ConfigurationException.
                // Throwing it would potentially break applications.
                // Uri did not read config in previous releases.
                return null;
            }
        }
 
        [SuppressMessage("Microsoft.Security","CA2106:SecureAsserts", Justification="Must Assert unrestricted FileIOPermission to get the machine config path")]
        private static UriSectionInternal LoadUsingCustomParser(string appConfigFilePath)
        {
            // Already have the application config file path in scope.
            // Get the path of the machine config file.
            string runtimeDir = null;
 
            // Must Assert unrestricted FileIOPermission to get the machine config path.
            new FileIOPermission(PermissionState.Unrestricted).Assert();
            try {
                runtimeDir = RuntimeEnvironment.GetRuntimeDirectory();
            }
            finally {
                FileIOPermission.RevertAssert();
            }
            string machineConfigFilePath = Path.Combine(Path.Combine(runtimeDir, "Config"), "machine.config");
 
            UriSectionData machineSettings = UriSectionReader.Read(machineConfigFilePath);
            // pass machineSettings to ctor: appSettings will use the values of machineSettings as init values.
            UriSectionData appSettings = UriSectionReader.Read(appConfigFilePath, machineSettings);
 
            UriSectionData resultSectionData = null;
            if (appSettings != null) {
                resultSectionData = appSettings;
            }
            else if (machineSettings != null) {
                resultSectionData = machineSettings;
            }
 
            if (resultSectionData != null) {
                UriIdnScope idnScope = resultSectionData.IdnScope ?? IdnElement.EnabledDefaultValue;
                bool iriParsing = resultSectionData.IriParsing ?? IriParsingElement.EnabledDefaultValue;
                IEnumerable<SchemeSettingInternal> schemeSettings = 
                    resultSectionData.SchemeSettings.Values as IEnumerable<SchemeSettingInternal>;
    
                return new UriSectionInternal(idnScope, iriParsing, schemeSettings);
            }
 
            return null;
        }
 
        private static bool IsWebConfig(string appConfigFile)
        {
            // Determine if we are in a Web config scenario.
 
            // Existence of string object associated with .appVPath tells
            // us that this is an ASP.Net web scenario.
            string appVPath = AppDomain.CurrentDomain.GetData(".appVPath") as string;
            if (appVPath != null) {
                return true;
            }
 
            // If application config file path not null
            // and begins with http:// or https://
            // then this is the No-Touch web deployment scenario.
            if (appConfigFile != null) {
                if (appConfigFile.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
                    appConfigFile.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) {
 
                    return true;
                }
            }
            return false;
        }
    }
}