LocalFileSettingsProvider.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Sys / System / Configuration / LocalFileSettingsProvider.cs / 1305376 / LocalFileSettingsProvider.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

using System.Diagnostics.CodeAnalysis; 
 
namespace System.Configuration {
    using System; 
    using System.Collections;
    using System.Collections.Specialized;
    using System.ComponentModel;
    using System.Configuration; 
    using System.Configuration.Provider;
    using System.Diagnostics; 
    using System.Globalization; 
    using System.IO;
    using System.Security; 
    using System.Security.Permissions;
    using System.Xml;
    using System.Xml.Serialization;
    using System.Runtime.Versioning; 

    ///  
    ///     
    ///         This is a provider used to store configuration settings locally for client applications.
    ///     
    /// 
    [
     PermissionSet(SecurityAction.LinkDemand, Name="FullTrust"),
     PermissionSet(SecurityAction.InheritanceDemand, Name="FullTrust") 
    ]
    public class LocalFileSettingsProvider : SettingsProvider, IApplicationSettingsProvider 
    { 
        private string              _appName                    = String.Empty;
        private ClientSettingsStore  _store                     = null; 
        private string              _prevLocalConfigFileName    = null;
        private string              _prevRoamingConfigFileName  = null;
        private XmlEscaper          _escaper                    = null;
 
        /// 
        ///     Abstract SettingsProvider property. 
        ///  
        public override string ApplicationName {
            get { 
                return _appName;
            }
            set {
                _appName = value; 
            }
        } 
 
        private XmlEscaper Escaper {
            get { 
                if (_escaper == null) {
                    _escaper = new XmlEscaper();
                }
 
                return _escaper;
            } 
        } 

        ///  
        ///     We maintain a single instance of the ClientSettingsStore per instance of provider.
        /// 
        private ClientSettingsStore Store {
            get { 
                if (_store == null) {
                    _store = new ClientSettingsStore(); 
                } 

                return _store; 
            }
        }

        ///  
        ///     Abstract ProviderBase method.
        ///  
        public override void Initialize(string name, NameValueCollection values) { 
            if (String.IsNullOrEmpty(name)) {
                name = "LocalFileSettingsProvider"; 
            }

            base.Initialize(name, values);
        } 

        ///  
        ///     Abstract SettingsProvider method 
        /// 
        public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection properties) { 
            SettingsPropertyValueCollection values = new SettingsPropertyValueCollection();
            string sectionName = GetSectionName(context);

            //<--Look for this section in both applicationSettingsGroup and userSettingsGroup--> 
            IDictionary appSettings = Store.ReadSettings(sectionName, false);
            IDictionary userSettings = Store.ReadSettings(sectionName, true); 
            ConnectionStringSettingsCollection connStrings = Store.ReadConnectionStrings(); 

            //<--Now map each SettingProperty to the right StoredSetting and deserialize the value if found.--> 
            foreach (SettingsProperty setting in properties) {
                string settingName = setting.Name;
                SettingsPropertyValue value = new SettingsPropertyValue(setting);
 
                // First look for and handle "special" settings
                SpecialSettingAttribute attr = setting.Attributes[typeof(SpecialSettingAttribute)] as SpecialSettingAttribute; 
                bool isConnString =  (attr != null) ? (attr.SpecialSetting == SpecialSetting.ConnectionString) : false; 

                if (isConnString) { 
                    string connStringName = sectionName + "." + settingName;
                    if (connStrings != null && connStrings[connStringName] != null) {
                        value.PropertyValue = connStrings[connStringName].ConnectionString;
                    } 
                    else if (setting.DefaultValue != null && setting.DefaultValue is string) {
                        value.PropertyValue = setting.DefaultValue; 
                    } 
                    else {
                        //No value found and no default specified 
                        value.PropertyValue = String.Empty;
                    }

                    value.IsDirty = false; //reset IsDirty so that it is correct when SetPropertyValues is called 
                    values.Add(value);
                    continue; 
                } 

                // Not a "special" setting 
                bool isUserSetting = IsUserSetting(setting);

                if (isUserSetting && !ConfigurationManagerInternalFactory.Instance.SupportsUserConfig) {
                    // We encountered a user setting, but the current configuration system does not support 
                    // user settings.
                   throw new ConfigurationErrorsException(SR.GetString(SR.UserSettingsNotSupported)); 
                } 

                IDictionary settings = isUserSetting ? userSettings : appSettings; 

                if (settings.Contains(settingName)) {
                    StoredSetting ss = (StoredSetting) settings[settingName];
                    string valueString = ss.Value.InnerXml; 

                    // We need to un-escape string serialized values 
                    if (ss.SerializeAs == SettingsSerializeAs.String) { 
                        valueString = Escaper.Unescape(valueString);
                    } 

                    value.SerializedValue = valueString;
                }
                else if (setting.DefaultValue != null) { 
                    value.SerializedValue = setting.DefaultValue;
                } 
                else { 
                    //No value found and no default specified
                    value.PropertyValue = null; 
                }

                value.IsDirty = false; //reset IsDirty so that it is correct when SetPropertyValues is called
                values.Add(value); 
            }
 
            return values; 
        }
 
        /// 
        ///     Abstract SettingsProvider method
        /// 
        public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection values) { 
            string sectionName = GetSectionName(context);
            IDictionary roamingUserSettings = new Hashtable(); 
            IDictionary localUserSettings = new Hashtable(); 

            foreach (SettingsPropertyValue value in values) { 
                SettingsProperty setting = value.Property;
                bool isUserSetting = IsUserSetting(setting);

                if (value.IsDirty) { 
                    if (isUserSetting) {
                        bool isRoaming = IsRoamingSetting(setting); 
                        StoredSetting ss = new StoredSetting(setting.SerializeAs, SerializeToXmlElement(setting, value)); 

                        if (isRoaming) { 
                            roamingUserSettings[setting.Name] = ss;
                        }
                        else {
                            localUserSettings[setting.Name] = ss; 
                        }
 
                        value.IsDirty = false; //reset IsDirty 
                    }
                    else { 
                        // This is an app-scoped or connection string setting that has been written to.
                        // We don't support saving these.
                    }
                } 
            }
 
            // Semi-hack: If there are roamable settings, let's write them before local settings so if a handler 
            // declaration is necessary, it goes in the roaming config file in preference to the local config file.
            if (roamingUserSettings.Count > 0) { 
                Store.WriteSettings(sectionName, true, roamingUserSettings);
            }

            if (localUserSettings.Count > 0) { 
                Store.WriteSettings(sectionName, false, localUserSettings);
            } 
        } 

        ///  
        ///     Implementation of IClientSettingsProvider.Reset. Resets user scoped settings to the values
        ///     in app.exe.config, does nothing for app scoped settings.
        /// 
        public void Reset(SettingsContext context) { 
            string sectionName = GetSectionName(context);
 
            // First revert roaming, then local 
            Store.RevertToParent(sectionName, true);
            Store.RevertToParent(sectionName, false); 
        }

        /// 
        ///    Implementation of IClientSettingsProvider.Upgrade. 
        ///    Tries to locate a previous version of the user.config file. If found, it migrates matching settings.
        ///    If not, it does nothing. 
        ///  
        public void Upgrade(SettingsContext context, SettingsPropertyCollection properties) {
            // Separate the local and roaming settings and upgrade them separately. 

            SettingsPropertyCollection local = new SettingsPropertyCollection();
            SettingsPropertyCollection roaming = new SettingsPropertyCollection();
 
            foreach (SettingsProperty sp in properties) {
                bool isRoaming = IsRoamingSetting(sp); 
 
                if (isRoaming) {
                    roaming.Add(sp); 
                }
                else {
                    local.Add(sp);
                } 
            }
 
            if (roaming.Count > 0) { 
                Upgrade(context, roaming, true);
            } 

            if (local.Count > 0) {
                Upgrade(context, local, false);
            } 
        }
 
        ///  
        ///     Encapsulates the Version constructor so that we can return null when an exception is thrown.
        ///  
        private Version CreateVersion(string name) {
            Version ver = null;

            try { 
                ver = new Version(name);
            } 
            catch (ArgumentException) { 
                ver = null;
            } 
            catch (OverflowException) {
                ver = null;
            }
            catch (FormatException) { 
                ver = null;
            } 
 
            return ver;
        } 

        /// 
        ///    Implementation of IClientSettingsProvider.GetPreviousVersion.
        ///  
        //  Security Note: Like Upgrade, GetPreviousVersion involves finding a previous version user.config file and
        //  reading settings from it. To support this in partial trust, we need to assert file i/o here. We believe 
        //  this to be safe, since the user does not have a way to specify the file or control where we look for it. 
        //  So it is no different than reading from the default user.config file, which we already allow in partial trust.
        //  BTW, the Link/Inheritance demand pair here is just a copy of what's at the class level, and is needed since 
        //  we are overriding security at method level.
        [
         FileIOPermission(SecurityAction.Assert, AllFiles=FileIOPermissionAccess.PathDiscovery | FileIOPermissionAccess.Read),
         PermissionSet(SecurityAction.LinkDemand, Name="FullTrust"), 
         PermissionSet(SecurityAction.InheritanceDemand, Name="FullTrust")
        ] 
        public SettingsPropertyValue GetPreviousVersion(SettingsContext context, SettingsProperty property) { 
            bool isRoaming = IsRoamingSetting(property);
            string prevConfig = GetPreviousConfigFileName(isRoaming); 

            if (!String.IsNullOrEmpty(prevConfig)) {
                SettingsPropertyCollection properties = new SettingsPropertyCollection();
                properties.Add(property); 
                SettingsPropertyValueCollection values = GetSettingValuesFromFile(prevConfig, GetSectionName(context), true, properties);
                return values[property.Name]; 
            } 
            else {
                SettingsPropertyValue value = new SettingsPropertyValue(property); 
                value.PropertyValue = null;
                return value;
            }
        } 

        ///  
        ///     Locates the previous version of user.config, if present. The previous version is determined 
        ///     by walking up one directory level in the *UserConfigPath and searching for the highest version
        ///     number less than the current version. 
        ///     SECURITY NOTE: Config path information is privileged - do not directly pass this on to untrusted callers.
        ///     Note this is meant to be used at installation time to help migrate
        ///     config settings from a previous version of the app.
        ///  
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] 
        private string GetPreviousConfigFileName(bool isRoaming) { 
            if (!ConfigurationManagerInternalFactory.Instance.SupportsUserConfig) {
                throw new ConfigurationErrorsException(SR.GetString(SR.UserSettingsNotSupported)); 
            }

            string prevConfigFile = isRoaming ? _prevRoamingConfigFileName : _prevLocalConfigFileName;
 
            if (String.IsNullOrEmpty(prevConfigFile)) {
                string userConfigPath = isRoaming ? ConfigurationManagerInternalFactory.Instance.ExeRoamingConfigDirectory : ConfigurationManagerInternalFactory.Instance.ExeLocalConfigDirectory; 
                Version curVer = CreateVersion(ConfigurationManagerInternalFactory.Instance.ExeProductVersion); 
                Version prevVer = null;
                DirectoryInfo prevDir = null; 
                string file = null;

                if (curVer == null) {
                    return null; 
                }
 
                DirectoryInfo parentDir = Directory.GetParent(userConfigPath); 

                if (parentDir.Exists) { 
                    foreach (DirectoryInfo dir in parentDir.GetDirectories()) {
                        Version tempVer = CreateVersion(dir.Name);

                        if (tempVer != null && tempVer < curVer) { 
                            if (prevVer == null) {
                                prevVer = tempVer; 
                                prevDir = dir; 
                            }
                            else if (tempVer > prevVer) { 
                                prevVer = tempVer;
                                prevDir = dir;
                            }
                        } 
                    }
 
                    if (prevDir != null) { 
                        file = Path.Combine(prevDir.FullName, ConfigurationManagerInternalFactory.Instance.UserConfigFilename);
                    } 

                    if (File.Exists(file)) {
                        prevConfigFile = file;
                    } 
                }
 
                //Cache for future use. 
                if (isRoaming) {
                    _prevRoamingConfigFileName = prevConfigFile; 
                }
                else {
                    _prevLocalConfigFileName = prevConfigFile;
                } 
            }
 
            return prevConfigFile; 
        }
 
        /// 
        ///     Gleans information from the SettingsContext and determines the name of the config section.
        /// 
        private string GetSectionName(SettingsContext context) { 
            string groupName = (string) context["GroupName"];
            string key = (string) context["SettingsKey"]; 
 
            Debug.Assert(groupName != null, "SettingsContext did not have a GroupName!");
 
            string sectionName = groupName;

            if (!String.IsNullOrEmpty(key)) {
                sectionName = string.Format(CultureInfo.InvariantCulture, "{0}.{1}", sectionName, key); 
            }
 
            return XmlConvert.EncodeLocalName(sectionName); 
        }
 
        /// 
        ///     Retrieves the values of settings from the given config file (as opposed to using
        ///     the configuration for the current context)
        ///  
        private SettingsPropertyValueCollection GetSettingValuesFromFile(string configFileName, string sectionName, bool userScoped, SettingsPropertyCollection properties) {
            SettingsPropertyValueCollection values = new SettingsPropertyValueCollection(); 
            IDictionary settings = ClientSettingsStore.ReadSettingsFromFile(configFileName, sectionName, userScoped); 

            // Map each SettingProperty to the right StoredSetting and deserialize the value if found. 
            foreach (SettingsProperty setting in properties) {
                string settingName = setting.Name;
                SettingsPropertyValue value = new SettingsPropertyValue(setting);
 
                if (settings.Contains(settingName)) {
                    StoredSetting ss = (StoredSetting) settings[settingName]; 
                    string valueString = ss.Value.InnerXml; 

                    // We need to un-escape string serialized values 
                    if (ss.SerializeAs == SettingsSerializeAs.String) {
                        valueString = Escaper.Unescape(valueString);
                    }
 
                    value.SerializedValue = valueString;
                    value.IsDirty = true; 
                    values.Add(value); 
                }
            } 

            return values;
        }
 
        /// 
        ///     Indicates whether a setting is roaming or not. 
        ///  
        private static bool IsRoamingSetting(SettingsProperty setting) {
            // Roaming is not supported in Clickonce deployed apps, since they don't have roaming config files. 
            bool roamingSupported = !ApplicationSettingsBase.IsClickOnceDeployed(AppDomain.CurrentDomain);
            bool isRoaming = false;

            if (roamingSupported) { 
                SettingsManageabilityAttribute manageAttr = setting.Attributes[typeof(SettingsManageabilityAttribute)] as SettingsManageabilityAttribute;
                isRoaming = manageAttr != null && ((manageAttr.Manageability & SettingsManageability.Roaming) == SettingsManageability.Roaming); 
            } 

            return isRoaming; 
        }

        /// 
        ///     This provider needs settings to be marked with either the UserScopedSettingAttribute or the 
        ///     ApplicationScopedSettingAttribute. This method determines whether this setting is user-scoped
        ///     or not. It will throw if none or both of the attributes are present. 
        ///  
        private bool IsUserSetting(SettingsProperty setting) {
            bool isUser = setting.Attributes[typeof(UserScopedSettingAttribute)] is UserScopedSettingAttribute; 
            bool isApp  = setting.Attributes[typeof(ApplicationScopedSettingAttribute)] is ApplicationScopedSettingAttribute;

            if (isUser && isApp) {
                throw new ConfigurationErrorsException(SR.GetString(SR.BothScopeAttributes)); 
            }
            else if (!(isUser || isApp)) { 
                throw new ConfigurationErrorsException(SR.GetString(SR.NoScopeAttributes)); 
            }
 
            return isUser;
        }

        private XmlNode SerializeToXmlElement(SettingsProperty setting, SettingsPropertyValue value) { 
            XmlDocument doc = new XmlDocument();
            XmlElement valueXml = doc.CreateElement("value"); 
 
            string serializedValue = value.SerializedValue as string;
 
            if (serializedValue == null && setting.SerializeAs == SettingsSerializeAs.Binary) {
                // SettingsPropertyValue returns a byte[] in the binary serialization case. We need to
                // encode this - we use base64 since SettingsPropertyValue understands it and we won't have
                // to special case while deserializing. 
                byte[] buf = value.SerializedValue as byte[];
                if (buf != null) { 
                    serializedValue = Convert.ToBase64String(buf); 
                }
            } 

            if (serializedValue == null) {
                serializedValue = String.Empty;
            } 

            // We need to escape string serialized values 
            if (setting.SerializeAs == SettingsSerializeAs.String) { 
                serializedValue = Escaper.Escape(serializedValue);
            } 

            valueXml.InnerXml = serializedValue;

            // Hack to remove the XmlDeclaration that the XmlSerializer adds. 
            XmlNode unwanted = null;
            foreach (XmlNode child in valueXml.ChildNodes) { 
                if (child.NodeType == XmlNodeType.XmlDeclaration) { 
                    unwanted = child;
                    break; 
                }
            }
            if (unwanted != null) {
                valueXml.RemoveChild(unwanted); 
            }
 
            return valueXml; 
        }
 
        /// 
        ///    Private version of upgrade that uses isRoaming to determine which config file to use.
        /// 
        // Security Note: Upgrade involves finding a previous version user.config file and reading settings from it. To 
        // support this in partial trust, we need to assert file i/o here. We believe this to be safe, since the user
        // does not have a way to specify the file or control where we look for it. As such, it is no different than 
        // reading from the default user.config file, which we already allow in partial trust. 
        // The following suppress is okay, since the Link/Inheritance demand pair at the class level are not needed for
        // this method, since it is private. 
        [SuppressMessage("Microsoft.Security", "CA2114:MethodSecurityShouldBeASupersetOfType")]
        [FileIOPermission(SecurityAction.Assert, AllFiles=FileIOPermissionAccess.PathDiscovery | FileIOPermissionAccess.Read)]
        private void Upgrade(SettingsContext context, SettingsPropertyCollection properties, bool isRoaming) {
            string prevConfig = GetPreviousConfigFileName(isRoaming); 

            if (!String.IsNullOrEmpty(prevConfig)) { 
                //Filter the settings properties to exclude those that have a NoSettingsVersionUpgradeAttribute on them. 
                SettingsPropertyCollection upgradeProperties = new SettingsPropertyCollection();
                foreach (SettingsProperty sp in properties) { 
                    if (!(sp.Attributes[typeof(NoSettingsVersionUpgradeAttribute)] is NoSettingsVersionUpgradeAttribute)) {
                        upgradeProperties.Add(sp);
                    }
                } 

                SettingsPropertyValueCollection values = GetSettingValuesFromFile(prevConfig, GetSectionName(context), true, upgradeProperties); 
                SetPropertyValues(context, values); 
            }
        } 

        private class XmlEscaper {
            private XmlDocument doc;
            private XmlElement temp; 

            internal XmlEscaper() { 
                doc = new XmlDocument(); 
                temp = doc.CreateElement("temp");
            } 

            internal string Escape(string xmlString) {
                if (String.IsNullOrEmpty(xmlString)) {
                    return xmlString; 
                }
 
                temp.InnerText = xmlString; 
                return temp.InnerXml;
            } 

            internal string Unescape(string escapedString) {
                if (String.IsNullOrEmpty(escapedString)) {
                    return escapedString; 
                }
 
                temp.InnerXml = escapedString; 
                return temp.InnerText;
            } 
        }
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

using System.Diagnostics.CodeAnalysis; 
 
namespace System.Configuration {
    using System; 
    using System.Collections;
    using System.Collections.Specialized;
    using System.ComponentModel;
    using System.Configuration; 
    using System.Configuration.Provider;
    using System.Diagnostics; 
    using System.Globalization; 
    using System.IO;
    using System.Security; 
    using System.Security.Permissions;
    using System.Xml;
    using System.Xml.Serialization;
    using System.Runtime.Versioning; 

    ///  
    ///     
    ///         This is a provider used to store configuration settings locally for client applications.
    ///     
    /// 
    [
     PermissionSet(SecurityAction.LinkDemand, Name="FullTrust"),
     PermissionSet(SecurityAction.InheritanceDemand, Name="FullTrust") 
    ]
    public class LocalFileSettingsProvider : SettingsProvider, IApplicationSettingsProvider 
    { 
        private string              _appName                    = String.Empty;
        private ClientSettingsStore  _store                     = null; 
        private string              _prevLocalConfigFileName    = null;
        private string              _prevRoamingConfigFileName  = null;
        private XmlEscaper          _escaper                    = null;
 
        /// 
        ///     Abstract SettingsProvider property. 
        ///  
        public override string ApplicationName {
            get { 
                return _appName;
            }
            set {
                _appName = value; 
            }
        } 
 
        private XmlEscaper Escaper {
            get { 
                if (_escaper == null) {
                    _escaper = new XmlEscaper();
                }
 
                return _escaper;
            } 
        } 

        ///  
        ///     We maintain a single instance of the ClientSettingsStore per instance of provider.
        /// 
        private ClientSettingsStore Store {
            get { 
                if (_store == null) {
                    _store = new ClientSettingsStore(); 
                } 

                return _store; 
            }
        }

        ///  
        ///     Abstract ProviderBase method.
        ///  
        public override void Initialize(string name, NameValueCollection values) { 
            if (String.IsNullOrEmpty(name)) {
                name = "LocalFileSettingsProvider"; 
            }

            base.Initialize(name, values);
        } 

        ///  
        ///     Abstract SettingsProvider method 
        /// 
        public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection properties) { 
            SettingsPropertyValueCollection values = new SettingsPropertyValueCollection();
            string sectionName = GetSectionName(context);

            //<--Look for this section in both applicationSettingsGroup and userSettingsGroup--> 
            IDictionary appSettings = Store.ReadSettings(sectionName, false);
            IDictionary userSettings = Store.ReadSettings(sectionName, true); 
            ConnectionStringSettingsCollection connStrings = Store.ReadConnectionStrings(); 

            //<--Now map each SettingProperty to the right StoredSetting and deserialize the value if found.--> 
            foreach (SettingsProperty setting in properties) {
                string settingName = setting.Name;
                SettingsPropertyValue value = new SettingsPropertyValue(setting);
 
                // First look for and handle "special" settings
                SpecialSettingAttribute attr = setting.Attributes[typeof(SpecialSettingAttribute)] as SpecialSettingAttribute; 
                bool isConnString =  (attr != null) ? (attr.SpecialSetting == SpecialSetting.ConnectionString) : false; 

                if (isConnString) { 
                    string connStringName = sectionName + "." + settingName;
                    if (connStrings != null && connStrings[connStringName] != null) {
                        value.PropertyValue = connStrings[connStringName].ConnectionString;
                    } 
                    else if (setting.DefaultValue != null && setting.DefaultValue is string) {
                        value.PropertyValue = setting.DefaultValue; 
                    } 
                    else {
                        //No value found and no default specified 
                        value.PropertyValue = String.Empty;
                    }

                    value.IsDirty = false; //reset IsDirty so that it is correct when SetPropertyValues is called 
                    values.Add(value);
                    continue; 
                } 

                // Not a "special" setting 
                bool isUserSetting = IsUserSetting(setting);

                if (isUserSetting && !ConfigurationManagerInternalFactory.Instance.SupportsUserConfig) {
                    // We encountered a user setting, but the current configuration system does not support 
                    // user settings.
                   throw new ConfigurationErrorsException(SR.GetString(SR.UserSettingsNotSupported)); 
                } 

                IDictionary settings = isUserSetting ? userSettings : appSettings; 

                if (settings.Contains(settingName)) {
                    StoredSetting ss = (StoredSetting) settings[settingName];
                    string valueString = ss.Value.InnerXml; 

                    // We need to un-escape string serialized values 
                    if (ss.SerializeAs == SettingsSerializeAs.String) { 
                        valueString = Escaper.Unescape(valueString);
                    } 

                    value.SerializedValue = valueString;
                }
                else if (setting.DefaultValue != null) { 
                    value.SerializedValue = setting.DefaultValue;
                } 
                else { 
                    //No value found and no default specified
                    value.PropertyValue = null; 
                }

                value.IsDirty = false; //reset IsDirty so that it is correct when SetPropertyValues is called
                values.Add(value); 
            }
 
            return values; 
        }
 
        /// 
        ///     Abstract SettingsProvider method
        /// 
        public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection values) { 
            string sectionName = GetSectionName(context);
            IDictionary roamingUserSettings = new Hashtable(); 
            IDictionary localUserSettings = new Hashtable(); 

            foreach (SettingsPropertyValue value in values) { 
                SettingsProperty setting = value.Property;
                bool isUserSetting = IsUserSetting(setting);

                if (value.IsDirty) { 
                    if (isUserSetting) {
                        bool isRoaming = IsRoamingSetting(setting); 
                        StoredSetting ss = new StoredSetting(setting.SerializeAs, SerializeToXmlElement(setting, value)); 

                        if (isRoaming) { 
                            roamingUserSettings[setting.Name] = ss;
                        }
                        else {
                            localUserSettings[setting.Name] = ss; 
                        }
 
                        value.IsDirty = false; //reset IsDirty 
                    }
                    else { 
                        // This is an app-scoped or connection string setting that has been written to.
                        // We don't support saving these.
                    }
                } 
            }
 
            // Semi-hack: If there are roamable settings, let's write them before local settings so if a handler 
            // declaration is necessary, it goes in the roaming config file in preference to the local config file.
            if (roamingUserSettings.Count > 0) { 
                Store.WriteSettings(sectionName, true, roamingUserSettings);
            }

            if (localUserSettings.Count > 0) { 
                Store.WriteSettings(sectionName, false, localUserSettings);
            } 
        } 

        ///  
        ///     Implementation of IClientSettingsProvider.Reset. Resets user scoped settings to the values
        ///     in app.exe.config, does nothing for app scoped settings.
        /// 
        public void Reset(SettingsContext context) { 
            string sectionName = GetSectionName(context);
 
            // First revert roaming, then local 
            Store.RevertToParent(sectionName, true);
            Store.RevertToParent(sectionName, false); 
        }

        /// 
        ///    Implementation of IClientSettingsProvider.Upgrade. 
        ///    Tries to locate a previous version of the user.config file. If found, it migrates matching settings.
        ///    If not, it does nothing. 
        ///  
        public void Upgrade(SettingsContext context, SettingsPropertyCollection properties) {
            // Separate the local and roaming settings and upgrade them separately. 

            SettingsPropertyCollection local = new SettingsPropertyCollection();
            SettingsPropertyCollection roaming = new SettingsPropertyCollection();
 
            foreach (SettingsProperty sp in properties) {
                bool isRoaming = IsRoamingSetting(sp); 
 
                if (isRoaming) {
                    roaming.Add(sp); 
                }
                else {
                    local.Add(sp);
                } 
            }
 
            if (roaming.Count > 0) { 
                Upgrade(context, roaming, true);
            } 

            if (local.Count > 0) {
                Upgrade(context, local, false);
            } 
        }
 
        ///  
        ///     Encapsulates the Version constructor so that we can return null when an exception is thrown.
        ///  
        private Version CreateVersion(string name) {
            Version ver = null;

            try { 
                ver = new Version(name);
            } 
            catch (ArgumentException) { 
                ver = null;
            } 
            catch (OverflowException) {
                ver = null;
            }
            catch (FormatException) { 
                ver = null;
            } 
 
            return ver;
        } 

        /// 
        ///    Implementation of IClientSettingsProvider.GetPreviousVersion.
        ///  
        //  Security Note: Like Upgrade, GetPreviousVersion involves finding a previous version user.config file and
        //  reading settings from it. To support this in partial trust, we need to assert file i/o here. We believe 
        //  this to be safe, since the user does not have a way to specify the file or control where we look for it. 
        //  So it is no different than reading from the default user.config file, which we already allow in partial trust.
        //  BTW, the Link/Inheritance demand pair here is just a copy of what's at the class level, and is needed since 
        //  we are overriding security at method level.
        [
         FileIOPermission(SecurityAction.Assert, AllFiles=FileIOPermissionAccess.PathDiscovery | FileIOPermissionAccess.Read),
         PermissionSet(SecurityAction.LinkDemand, Name="FullTrust"), 
         PermissionSet(SecurityAction.InheritanceDemand, Name="FullTrust")
        ] 
        public SettingsPropertyValue GetPreviousVersion(SettingsContext context, SettingsProperty property) { 
            bool isRoaming = IsRoamingSetting(property);
            string prevConfig = GetPreviousConfigFileName(isRoaming); 

            if (!String.IsNullOrEmpty(prevConfig)) {
                SettingsPropertyCollection properties = new SettingsPropertyCollection();
                properties.Add(property); 
                SettingsPropertyValueCollection values = GetSettingValuesFromFile(prevConfig, GetSectionName(context), true, properties);
                return values[property.Name]; 
            } 
            else {
                SettingsPropertyValue value = new SettingsPropertyValue(property); 
                value.PropertyValue = null;
                return value;
            }
        } 

        ///  
        ///     Locates the previous version of user.config, if present. The previous version is determined 
        ///     by walking up one directory level in the *UserConfigPath and searching for the highest version
        ///     number less than the current version. 
        ///     SECURITY NOTE: Config path information is privileged - do not directly pass this on to untrusted callers.
        ///     Note this is meant to be used at installation time to help migrate
        ///     config settings from a previous version of the app.
        ///  
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] 
        private string GetPreviousConfigFileName(bool isRoaming) { 
            if (!ConfigurationManagerInternalFactory.Instance.SupportsUserConfig) {
                throw new ConfigurationErrorsException(SR.GetString(SR.UserSettingsNotSupported)); 
            }

            string prevConfigFile = isRoaming ? _prevRoamingConfigFileName : _prevLocalConfigFileName;
 
            if (String.IsNullOrEmpty(prevConfigFile)) {
                string userConfigPath = isRoaming ? ConfigurationManagerInternalFactory.Instance.ExeRoamingConfigDirectory : ConfigurationManagerInternalFactory.Instance.ExeLocalConfigDirectory; 
                Version curVer = CreateVersion(ConfigurationManagerInternalFactory.Instance.ExeProductVersion); 
                Version prevVer = null;
                DirectoryInfo prevDir = null; 
                string file = null;

                if (curVer == null) {
                    return null; 
                }
 
                DirectoryInfo parentDir = Directory.GetParent(userConfigPath); 

                if (parentDir.Exists) { 
                    foreach (DirectoryInfo dir in parentDir.GetDirectories()) {
                        Version tempVer = CreateVersion(dir.Name);

                        if (tempVer != null && tempVer < curVer) { 
                            if (prevVer == null) {
                                prevVer = tempVer; 
                                prevDir = dir; 
                            }
                            else if (tempVer > prevVer) { 
                                prevVer = tempVer;
                                prevDir = dir;
                            }
                        } 
                    }
 
                    if (prevDir != null) { 
                        file = Path.Combine(prevDir.FullName, ConfigurationManagerInternalFactory.Instance.UserConfigFilename);
                    } 

                    if (File.Exists(file)) {
                        prevConfigFile = file;
                    } 
                }
 
                //Cache for future use. 
                if (isRoaming) {
                    _prevRoamingConfigFileName = prevConfigFile; 
                }
                else {
                    _prevLocalConfigFileName = prevConfigFile;
                } 
            }
 
            return prevConfigFile; 
        }
 
        /// 
        ///     Gleans information from the SettingsContext and determines the name of the config section.
        /// 
        private string GetSectionName(SettingsContext context) { 
            string groupName = (string) context["GroupName"];
            string key = (string) context["SettingsKey"]; 
 
            Debug.Assert(groupName != null, "SettingsContext did not have a GroupName!");
 
            string sectionName = groupName;

            if (!String.IsNullOrEmpty(key)) {
                sectionName = string.Format(CultureInfo.InvariantCulture, "{0}.{1}", sectionName, key); 
            }
 
            return XmlConvert.EncodeLocalName(sectionName); 
        }
 
        /// 
        ///     Retrieves the values of settings from the given config file (as opposed to using
        ///     the configuration for the current context)
        ///  
        private SettingsPropertyValueCollection GetSettingValuesFromFile(string configFileName, string sectionName, bool userScoped, SettingsPropertyCollection properties) {
            SettingsPropertyValueCollection values = new SettingsPropertyValueCollection(); 
            IDictionary settings = ClientSettingsStore.ReadSettingsFromFile(configFileName, sectionName, userScoped); 

            // Map each SettingProperty to the right StoredSetting and deserialize the value if found. 
            foreach (SettingsProperty setting in properties) {
                string settingName = setting.Name;
                SettingsPropertyValue value = new SettingsPropertyValue(setting);
 
                if (settings.Contains(settingName)) {
                    StoredSetting ss = (StoredSetting) settings[settingName]; 
                    string valueString = ss.Value.InnerXml; 

                    // We need to un-escape string serialized values 
                    if (ss.SerializeAs == SettingsSerializeAs.String) {
                        valueString = Escaper.Unescape(valueString);
                    }
 
                    value.SerializedValue = valueString;
                    value.IsDirty = true; 
                    values.Add(value); 
                }
            } 

            return values;
        }
 
        /// 
        ///     Indicates whether a setting is roaming or not. 
        ///  
        private static bool IsRoamingSetting(SettingsProperty setting) {
            // Roaming is not supported in Clickonce deployed apps, since they don't have roaming config files. 
            bool roamingSupported = !ApplicationSettingsBase.IsClickOnceDeployed(AppDomain.CurrentDomain);
            bool isRoaming = false;

            if (roamingSupported) { 
                SettingsManageabilityAttribute manageAttr = setting.Attributes[typeof(SettingsManageabilityAttribute)] as SettingsManageabilityAttribute;
                isRoaming = manageAttr != null && ((manageAttr.Manageability & SettingsManageability.Roaming) == SettingsManageability.Roaming); 
            } 

            return isRoaming; 
        }

        /// 
        ///     This provider needs settings to be marked with either the UserScopedSettingAttribute or the 
        ///     ApplicationScopedSettingAttribute. This method determines whether this setting is user-scoped
        ///     or not. It will throw if none or both of the attributes are present. 
        ///  
        private bool IsUserSetting(SettingsProperty setting) {
            bool isUser = setting.Attributes[typeof(UserScopedSettingAttribute)] is UserScopedSettingAttribute; 
            bool isApp  = setting.Attributes[typeof(ApplicationScopedSettingAttribute)] is ApplicationScopedSettingAttribute;

            if (isUser && isApp) {
                throw new ConfigurationErrorsException(SR.GetString(SR.BothScopeAttributes)); 
            }
            else if (!(isUser || isApp)) { 
                throw new ConfigurationErrorsException(SR.GetString(SR.NoScopeAttributes)); 
            }
 
            return isUser;
        }

        private XmlNode SerializeToXmlElement(SettingsProperty setting, SettingsPropertyValue value) { 
            XmlDocument doc = new XmlDocument();
            XmlElement valueXml = doc.CreateElement("value"); 
 
            string serializedValue = value.SerializedValue as string;
 
            if (serializedValue == null && setting.SerializeAs == SettingsSerializeAs.Binary) {
                // SettingsPropertyValue returns a byte[] in the binary serialization case. We need to
                // encode this - we use base64 since SettingsPropertyValue understands it and we won't have
                // to special case while deserializing. 
                byte[] buf = value.SerializedValue as byte[];
                if (buf != null) { 
                    serializedValue = Convert.ToBase64String(buf); 
                }
            } 

            if (serializedValue == null) {
                serializedValue = String.Empty;
            } 

            // We need to escape string serialized values 
            if (setting.SerializeAs == SettingsSerializeAs.String) { 
                serializedValue = Escaper.Escape(serializedValue);
            } 

            valueXml.InnerXml = serializedValue;

            // Hack to remove the XmlDeclaration that the XmlSerializer adds. 
            XmlNode unwanted = null;
            foreach (XmlNode child in valueXml.ChildNodes) { 
                if (child.NodeType == XmlNodeType.XmlDeclaration) { 
                    unwanted = child;
                    break; 
                }
            }
            if (unwanted != null) {
                valueXml.RemoveChild(unwanted); 
            }
 
            return valueXml; 
        }
 
        /// 
        ///    Private version of upgrade that uses isRoaming to determine which config file to use.
        /// 
        // Security Note: Upgrade involves finding a previous version user.config file and reading settings from it. To 
        // support this in partial trust, we need to assert file i/o here. We believe this to be safe, since the user
        // does not have a way to specify the file or control where we look for it. As such, it is no different than 
        // reading from the default user.config file, which we already allow in partial trust. 
        // The following suppress is okay, since the Link/Inheritance demand pair at the class level are not needed for
        // this method, since it is private. 
        [SuppressMessage("Microsoft.Security", "CA2114:MethodSecurityShouldBeASupersetOfType")]
        [FileIOPermission(SecurityAction.Assert, AllFiles=FileIOPermissionAccess.PathDiscovery | FileIOPermissionAccess.Read)]
        private void Upgrade(SettingsContext context, SettingsPropertyCollection properties, bool isRoaming) {
            string prevConfig = GetPreviousConfigFileName(isRoaming); 

            if (!String.IsNullOrEmpty(prevConfig)) { 
                //Filter the settings properties to exclude those that have a NoSettingsVersionUpgradeAttribute on them. 
                SettingsPropertyCollection upgradeProperties = new SettingsPropertyCollection();
                foreach (SettingsProperty sp in properties) { 
                    if (!(sp.Attributes[typeof(NoSettingsVersionUpgradeAttribute)] is NoSettingsVersionUpgradeAttribute)) {
                        upgradeProperties.Add(sp);
                    }
                } 

                SettingsPropertyValueCollection values = GetSettingValuesFromFile(prevConfig, GetSectionName(context), true, upgradeProperties); 
                SetPropertyValues(context, values); 
            }
        } 

        private class XmlEscaper {
            private XmlDocument doc;
            private XmlElement temp; 

            internal XmlEscaper() { 
                doc = new XmlDocument(); 
                temp = doc.CreateElement("temp");
            } 

            internal string Escape(string xmlString) {
                if (String.IsNullOrEmpty(xmlString)) {
                    return xmlString; 
                }
 
                temp.InnerText = xmlString; 
                return temp.InnerXml;
            } 

            internal string Unescape(string escapedString) {
                if (String.IsNullOrEmpty(escapedString)) {
                    return escapedString; 
                }
 
                temp.InnerXml = escapedString; 
                return temp.InnerText;
            } 
        }
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK