2008-11-20 Gonzalo Paniagua Javier <gonzalo@novell.com>
[mono.git] / mcs / class / System / System.Configuration / ConfigurationSettings.cs
index 9a4c93eb989d3f73e66feef3c2bfd08adf1dc175..199d83fe7bb383121660fd6e0dc5199d262e4321 100644 (file)
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
+#if CONFIGURATION_DEP && !TARGET_JVM
+extern alias PrebuiltSystem;
+using NameValueCollection = PrebuiltSystem.System.Collections.Specialized.NameValueCollection;
+#endif
+
 using System;
 using System.Collections;
 using System.Collections.Specialized;
 using System.IO;
 using System.Runtime.CompilerServices;
+using System.Security.Permissions;
 #if (XML_DEP)
 using System.Xml;
 using System.Xml.XPath;
 #endif
+#if TARGET_JVM
+using vmw.common;
+using vmw.@internal.io;
+#endif
 
 namespace System.Configuration
 {
        public sealed class ConfigurationSettings
        {
-               static IConfigurationSystem config = DefaultConfig.GetInstance ();
+#if !TARGET_JVM
+               static IConfigurationSystem config = DefaultConfig.GetInstance ();
+#else
+               static IConfigurationSystem config {
+                       get {
+                               IConfigurationSystem conf = (IConfigurationSystem) AppDomain.CurrentDomain.GetData ("ConfigurationSettings.Config");
+                               if (conf == null) {
+                                       conf = DefaultConfig.GetInstance ();
+                                       AppDomain.CurrentDomain.SetData ("ConfigurationSettings.Config", conf);
+                               }
+                               return conf;
+                       }
+                       set {
+                               AppDomain.CurrentDomain.SetData ("ConfigurationSettings.Config", value);
+                       }
+               }
+#endif
                static object lockobj = new object ();
                private ConfigurationSettings ()
                {
                }
 
+#if NET_2_0
+               [Obsolete ("This method is obsolete, it has been replaced by System.Configuration!System.Configuration.ConfigurationManager.GetSection")]
+#endif
                public static object GetConfig (string sectionName)
                {
+#if NET_2_0 && CONFIGURATION_DEP
+                       return ConfigurationManager.GetSection (sectionName);
+#else
                        return config.GetConfig (sectionName);
+#endif
                }
 
 #if NET_2_0
@@ -63,16 +96,19 @@ namespace System.Configuration
                public static NameValueCollection AppSettings
                {
                        get {
+#if NET_2_0 && CONFIGURATION_DEP
+                               object appSettings = ConfigurationManager.GetSection ("appSettings");
+#else
                                object appSettings = GetConfig ("appSettings");
+#endif
                                if (appSettings == null)
                                        appSettings = new NameValueCollection ();
-
                                return (NameValueCollection) appSettings;
                        }
                }
 
-               // Invoked from System.Web
-               static IConfigurationSystem ChangeConfigurationSystem (IConfigurationSystem newSystem)
+               // Invoked from System.Web, disable warning
+               internal static IConfigurationSystem ChangeConfigurationSystem (IConfigurationSystem newSystem)
                {
                        if (newSystem == null)
                                throw new ArgumentNullException ("newSystem");
@@ -91,7 +127,23 @@ namespace System.Configuration
        //
        class DefaultConfig : IConfigurationSystem
        {
-               static readonly DefaultConfig instance = new DefaultConfig ();
+#if !TARGET_JVM
+               static readonly DefaultConfig instance = new DefaultConfig ();        
+#else
+               static DefaultConfig instance {
+                       get {
+                               DefaultConfig conf = (DefaultConfig) AppDomain.CurrentDomain.GetData ("DefaultConfig.instance");
+                               if (conf == null) {
+                                       conf = new DefaultConfig ();
+                                       AppDomain.CurrentDomain.SetData ("DefaultConfig.instance", conf);
+                               }
+                               return conf;
+                       }
+                       set {
+                               AppDomain.CurrentDomain.SetData ("DefaultConfig.instance", value);
+                       }
+               }
+#endif
                ConfigurationData config;
                
                private DefaultConfig ()
@@ -119,9 +171,13 @@ namespace System.Configuration
                                        return;
 
                                ConfigurationData data = new ConfigurationData ();
-                               if (!data.Load (GetMachineConfigPath ()))
-                                       throw new ConfigurationException ("Cannot find " + GetMachineConfigPath ());
+                               if (data.LoadString (GetBundledMachineConfig ())) {
+                                       // do nothing
+                               } else {
+                                       if (!data.Load (GetMachineConfigPath ()))
+                                               throw new ConfigurationException ("Cannot find " + GetMachineConfigPath ());
 
+                               }
                                string appfile = GetAppConfigPath ();
                                if (appfile == null) {
                                        config = data;
@@ -135,15 +191,29 @@ namespace System.Configuration
                                        config = data;
                        }
                }
-
+#if TARGET_JVM
+               internal static string GetBundledMachineConfig ()
+               {
+                       return null;
+               }
+               internal static string GetMachineConfigPath ()
+               {
+                       return System.Runtime.InteropServices.RuntimeEnvironment.SystemConfigurationFile;
+               }
+#else
+               [MethodImplAttribute(MethodImplOptions.InternalCall)]
+               extern private static string get_bundled_machine_config ();
+               internal static string GetBundledMachineConfig ()
+               {
+                       return get_bundled_machine_config ();
+               }
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
                extern private static string get_machine_config_path ();
-
                internal static string GetMachineConfigPath ()
                {
                        return get_machine_config_path ();
                }
-
+#endif
                private static string GetAppConfigPath ()
                {
                        AppDomainSetup currentInfo = AppDomain.CurrentDomain.SetupInformation;
@@ -163,7 +233,7 @@ namespace System.Configuration
                MachineOnly,
                MachineToApplication
        }
-       
+
        class SectionData
        {
                public readonly string SectionName;
@@ -171,38 +241,42 @@ namespace System.Configuration
                public readonly bool AllowLocation;
                public readonly AllowDefinition AllowDefinition;
                public string FileName;
+               public readonly bool RequirePermission;
 
                public SectionData (string sectionName, string typeName,
-                                   bool allowLocation, AllowDefinition allowDefinition)
+                           bool allowLocation, AllowDefinition allowDefinition, bool requirePermission)
                {
                        SectionName = sectionName;
                        TypeName = typeName;
                        AllowLocation = allowLocation;
                        AllowDefinition = allowDefinition;
+                       RequirePermission = requirePermission;
                }
        }
-       
+
 
        class ConfigurationData
        {
                ConfigurationData parent;
                Hashtable factories;
+               static object removedMark = new object ();
+               static object emptyMark = new object ();
+#if (XML_DEP)
                Hashtable pending;
                string fileName;
-               static object removedMark = new object ();
                static object groupMark = new object ();
-                static object emptyMark = new object ();
+#endif
                Hashtable cache;
 
-                Hashtable FileCache {
-                        get {
+               Hashtable FileCache {
+                       get {
                                if (cache != null)
                                        return cache;
 
                                cache = new Hashtable ();
-                                return cache;
-                        }
-                }
+                               return cache;
+                       }
+               }
 
                public ConfigurationData () : this (null)
                {
@@ -214,19 +288,39 @@ namespace System.Configuration
                        factories = new Hashtable ();
                }
 
+               // SECURITY-FIXME: limit this with an imperative assert for reading the specific file
+               [FileIOPermission (SecurityAction.Assert, Unrestricted = true)]
                public bool Load (string fileName)
                {
+#if (XML_DEP)
                        this.fileName = fileName;
-                       if (fileName == null || !File.Exists (fileName))
+                       if (fileName == null
+#if !TARGET_JVM
+                               || !File.Exists (fileName)
+#endif
+)
                                return false;
-#if (XML_DEP)
+                       
                        XmlTextReader reader = null;
 
                        try {
+#if !TARGET_JVM
                                FileStream fs = new FileStream (fileName, FileMode.Open, FileAccess.Read);
+#else
+                               Stream fs = (Stream) vmw.common.IOUtils.getStream (fileName);
+
+                               //patch for machine.config
+                               if (fs == null && fileName.EndsWith ("machine.config")) {
+                                       fs = (Stream) IOUtils.getStreamForGHConfigs (fileName);
+                               }
+
+                               if (fs == null) {
+                                       return false;
+                               }
+#endif
                                reader = new XmlTextReader (fs);
-                               InitRead (reader);
-                               ReadConfigFile (reader);
+                               if (InitRead (reader))
+                                       ReadConfigFile (reader);
                        } catch (ConfigurationException) {
                                throw;
                        } catch (Exception e) {
@@ -238,7 +332,31 @@ namespace System.Configuration
 #endif
                        return true;
                }
+               
+               public bool LoadString (string data)
+               {
+                       if (data == null)
+                               return false;
+#if (XML_DEP)
+                       XmlTextReader reader = null;
 
+                       try {
+                               TextReader tr = new StringReader (data);
+                               reader = new XmlTextReader (tr);
+                               if (InitRead (reader))
+                                       ReadConfigFile (reader);
+                       } catch (ConfigurationException) {
+                               throw;
+                       } catch (Exception e) {
+                               throw new ConfigurationException ("Error reading " + fileName, e);
+                       } finally {
+                               if (reader != null)
+                                       reader.Close();
+                       }
+#endif
+                       return true;
+               }
+               
                object GetHandler (string sectionName)
                {
                        lock (factories) {
@@ -317,7 +435,7 @@ namespace System.Configuration
 
                        return GetInnerDoc (doc, 0, sectionPath);
                }
-                
+               
                object GetConfigInternal (string sectionName)
                {
                        object handler = GetHandler (sectionName);
@@ -348,11 +466,11 @@ namespace System.Configuration
                                config = this.FileCache [sectionName];
                        }
 
-                        if (config == emptyMark)
-                                return null;
+                       if (config == emptyMark)
+                               return null;
 
-                        if (config != null)
-                                return config;
+                       if (config != null)
+                               return config;
 
                        lock (this) {
                                config = GetConfigInternal (sectionName);
@@ -360,7 +478,7 @@ namespace System.Configuration
                        }
 
                        return config;
-                }
+               }
 
                private object LookForFactory (string key)
                {
@@ -374,7 +492,7 @@ namespace System.Configuration
                        return null;
                }
 #if (XML_DEP)
-               private void InitRead (XmlTextReader reader)
+               private bool InitRead (XmlTextReader reader)
                {
                        reader.MoveToContent ();
                        if (reader.NodeType != XmlNodeType.Element || reader.Name != "configuration")
@@ -382,10 +500,16 @@ namespace System.Configuration
 
                        if (reader.HasAttributes)
                                ThrowException ("Unrecognized attribute in root element", reader);
-
-                       MoveToNextElement (reader);
+                       if (reader.IsEmptyElement) {
+                               reader.Skip ();
+                               return false;
+                       }
+                       reader.Read ();
+                       reader.MoveToContent ();
+                       return reader.NodeType != XmlNodeType.EndElement;
                }
 
+               // FIXME: this approach is not always safe and likely to cause bugs.
                private void MoveToNextElement (XmlTextReader reader)
                {
                        while (reader.Read ()) {
@@ -407,6 +531,8 @@ namespace System.Configuration
                        string nameValue = null;
                        string typeValue = null;
                        string allowLoc = null, allowDef = null;
+                       bool requirePermission = false;
+                       string requirePer = null;
                        bool allowLocation = true;
                        AllowDefinition allowDefinition = AllowDefinition.Everywhere;
 
@@ -427,6 +553,16 @@ namespace System.Configuration
                                        continue;
                                }
 
+                               if (attName == "requirePermission") {
+                                       if (requirePer != null)
+                                               ThrowException ("Duplicated requirePermission attribute.", reader);
+                                       requirePer = reader.Value;
+                                       requirePermission = (requirePer == "true");
+                                       if (!requirePermission && requirePer != "false")
+                                               ThrowException ("Invalid attribute value", reader);
+                                       continue;
+                               }
+
                                if (attName == "allowDefinition") {
                                        if (allowDef != null)
                                                ThrowException ("Duplicated allowDefinition attribute.", reader);
@@ -471,11 +607,22 @@ namespace System.Configuration
                        object o = LookForFactory (nameValue);
                        if (o != null && o != removedMark)
                                ThrowException ("Already have a factory for " + nameValue, reader);
-                       SectionData section = new SectionData (nameValue, typeValue, allowLocation, allowDefinition);
+                       SectionData section = new SectionData (nameValue, typeValue, allowLocation,
+                               allowDefinition, requirePermission);
                        section.FileName = fileName;
                        factories [nameValue] = section;
 
-                       MoveToNextElement (reader);
+                       if (reader.IsEmptyElement)
+                               reader.Skip ();
+                       else {
+                               reader.Read ();
+                               reader.MoveToContent ();
+                               if (reader.NodeType != XmlNodeType.EndElement)
+                                       // sub-section inside a section
+                                       ReadSections (reader, nameValue);
+                               reader.ReadEndElement ();
+                       }
+                       reader.MoveToContent ();
                }
 
                private void ReadRemoveSection (XmlTextReader reader, string sectionName)
@@ -512,7 +659,7 @@ namespace System.Configuration
                                                ThrowException ("Duplicate 'name' attribute.", reader);
                                        value = reader.Value;
                                }
-                               else 
+                               else
 #if NET_2_0
                                if (reader.Name != "type")
 #endif
@@ -521,10 +668,10 @@ namespace System.Configuration
 
                        if (value == null)
                                ThrowException ("No 'name' attribute.", reader);
-
+                       
                        if (value == "location")
                                ThrowException ("location is a reserved section name", reader);
-                       
+
                        if (configSection != null)
                                value = configSection + '/' + value;
 
@@ -533,14 +680,28 @@ namespace System.Configuration
                                ThrowException ("Already have a factory for " + value, reader);
 
                        factories [value] = groupMark;
-                       MoveToNextElement (reader);
-                       ReadSections (reader, value);
+
+                       if (reader.IsEmptyElement) {
+                               reader.Skip ();
+                               reader.MoveToContent ();
+                       } else {
+                               reader.Read ();
+                               reader.MoveToContent ();
+                               if (reader.NodeType != XmlNodeType.EndElement)
+                                       ReadSections (reader, value);
+                               reader.ReadEndElement ();
+                               reader.MoveToContent ();
+                       }
                }
 
+               // It stops XmlReader consumption at where it found
+               // surrounding EndElement i.e. EndElement is not consumed here
                private void ReadSections (XmlTextReader reader, string configSection)
                {
                        int depth = reader.Depth;
-                       while (reader.Depth == depth) {
+                       for (reader.MoveToContent ();
+                            reader.Depth == depth;
+                            reader.MoveToContent ()) {
                                string name = reader.Name;
                                if (name == "section") {
                                        ReadSection (reader, configSection);
@@ -581,16 +742,23 @@ namespace System.Configuration
 
                private void ReadConfigFile (XmlTextReader reader)
                {
-                       int depth = reader.Depth;
-                       while (!reader.EOF && reader.Depth == depth) {
+                       //int depth = reader.Depth;
+                       for (reader.MoveToContent ();
+                            !reader.EOF && reader.NodeType != XmlNodeType.EndElement;
+                            reader.MoveToContent ()) {
                                string name = reader.Name;
                                if (name == "configSections") {
                                        if (reader.HasAttributes)
                                                ThrowException ("Unrecognized attribute in <configSections>.", reader);
-
-                                       MoveToNextElement (reader);
-                                       if (reader.Depth > depth)
-                                               ReadSections (reader, null);
+                                       if (reader.IsEmptyElement)
+                                               reader.Skip ();
+                                       else {
+                                               reader.Read ();
+                                               reader.MoveToContent ();
+                                               if (reader.NodeType != XmlNodeType.EndElement)
+                                                       ReadSections (reader, null);
+                                               reader.ReadEndElement ();
+                                       }
                                } else if (name != null && name != "") {
                                        StorePending (name, reader);
                                        MoveToNextElement (reader);
@@ -599,7 +767,7 @@ namespace System.Configuration
                                }
                        }
                }
-                               
+               
                private void ThrowException (string text, XmlTextReader reader)
                {
                        throw new ConfigurationException (text, fileName, reader.LineNumber);