2 // System.Configuration.ConfigurationSettings.cs
5 // Christopher Podurgiel (cpodurgiel@msn.com)
6 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 // Eric Lindvall (eric@5stops.com)
9 // (c) Christopher Podurgiel
10 // (c) 2002 Ximian, Inc. (http://www.ximian.com)
11 // (c) 2003 Novell, Inc. (http://www.novell.com)
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 using System.Collections;
37 using System.Collections.Specialized;
39 using System.Runtime.CompilerServices;
40 using System.Security.Permissions;
43 using System.Xml.XPath;
46 namespace System.Configuration
48 public sealed class ConfigurationSettings
50 static IConfigurationSystem config = DefaultConfig.GetInstance ();
51 static object lockobj = new object ();
52 private ConfigurationSettings ()
56 [Obsolete ("This method is obsolete, it has been replaced by System.Configuration!System.Configuration.ConfigurationManager.GetSection")]
57 public static object GetConfig (string sectionName)
60 return ConfigurationManager.GetSection (sectionName);
62 return config.GetConfig (sectionName);
66 [Obsolete ("This property is obsolete. Please use System.Configuration.ConfigurationManager.AppSettings")]
67 public static NameValueCollection AppSettings
71 object appSettings = ConfigurationManager.GetSection ("appSettings");
73 object appSettings = GetConfig ("appSettings");
75 if (appSettings == null)
76 appSettings = new NameValueCollection ();
77 return (NameValueCollection) appSettings;
81 // Invoked from System.Web, disable warning
82 internal static IConfigurationSystem ChangeConfigurationSystem (IConfigurationSystem newSystem)
84 if (newSystem == null)
85 throw new ArgumentNullException ("newSystem");
88 IConfigurationSystem old = config;
96 // class DefaultConfig: read configuration from machine.config file and application
97 // config file if available.
99 class DefaultConfig : IConfigurationSystem
101 static readonly DefaultConfig instance = new DefaultConfig ();
102 ConfigurationData config;
104 private DefaultConfig ()
108 public static DefaultConfig GetInstance ()
113 [Obsolete ("This method is obsolete. Please use System.Configuration.ConfigurationManager.GetConfig")]
114 public object GetConfig (string sectionName)
117 return config.GetConfig (sectionName);
126 ConfigurationData data = new ConfigurationData ();
127 if (data.LoadString (GetBundledMachineConfig ())) {
130 if (!data.Load (GetMachineConfigPath ()))
131 throw new ConfigurationException ("Cannot find " + GetMachineConfigPath ());
134 string appfile = GetAppConfigPath ();
135 if (appfile == null) {
140 ConfigurationData appData = new ConfigurationData (data);
141 if (appData.Load (appfile))
147 [MethodImplAttribute(MethodImplOptions.InternalCall)]
148 extern private static string get_bundled_machine_config ();
149 internal static string GetBundledMachineConfig ()
151 return get_bundled_machine_config ();
153 [MethodImplAttribute(MethodImplOptions.InternalCall)]
154 extern private static string get_machine_config_path ();
155 internal static string GetMachineConfigPath ()
157 return get_machine_config_path ();
159 private static string GetAppConfigPath ()
161 AppDomainSetup currentInfo = AppDomain.CurrentDomain.SetupInformation;
163 string configFile = currentInfo.ConfigurationFile;
164 if (configFile == null || configFile.Length == 0)
181 public readonly string SectionName;
182 public readonly string TypeName;
183 public readonly bool AllowLocation;
184 public readonly AllowDefinition AllowDefinition;
186 public string FileName;
188 public readonly bool RequirePermission;
190 public SectionData (string sectionName, string typeName,
191 bool allowLocation, AllowDefinition allowDefinition, bool requirePermission)
193 SectionName = sectionName;
195 AllowLocation = allowLocation;
196 AllowDefinition = allowDefinition;
197 RequirePermission = requirePermission;
202 class ConfigurationData
204 ConfigurationData parent;
206 static object removedMark = new object ();
207 static object emptyMark = new object ();
211 static object groupMark = new object ();
215 Hashtable FileCache {
220 cache = new Hashtable ();
225 public ConfigurationData () : this (null)
229 public ConfigurationData (ConfigurationData parent)
231 this.parent = (parent == this) ? null : parent;
232 factories = new Hashtable ();
235 // SECURITY-FIXME: limit this with an imperative assert for reading the specific file
236 [FileIOPermission (SecurityAction.Assert, Unrestricted = true)]
237 public bool Load (string fileName)
240 this.fileName = fileName;
242 || !File.Exists (fileName)
246 XmlTextReader reader = null;
249 FileStream fs = new FileStream (fileName, FileMode.Open, FileAccess.Read);
250 reader = new XmlTextReader (fs);
251 if (InitRead (reader))
252 ReadConfigFile (reader);
253 } catch (ConfigurationException) {
255 } catch (Exception e) {
256 throw new ConfigurationException ("Error reading " + fileName, e);
265 public bool LoadString (string data)
270 XmlTextReader reader = null;
273 TextReader tr = new StringReader (data);
274 reader = new XmlTextReader (tr);
275 if (InitRead (reader))
276 ReadConfigFile (reader);
277 } catch (ConfigurationException) {
279 } catch (Exception e) {
280 throw new ConfigurationException ("Error reading " + fileName, e);
289 object GetHandler (string sectionName)
292 object o = factories [sectionName];
293 if (o == null || o == removedMark) {
295 return parent.GetHandler (sectionName);
300 if (o is IConfigurationSectionHandler)
301 return (IConfigurationSectionHandler) o;
303 o = CreateNewHandler (sectionName, (SectionData) o);
304 factories [sectionName] = o;
309 object CreateNewHandler (string sectionName, SectionData section)
311 Type t = Type.GetType (section.TypeName);
313 throw new ConfigurationException ("Cannot get Type for " + section.TypeName);
316 Type iconfig = typeof (IConfigurationSectionHandler);
317 if (!iconfig.IsAssignableFrom (t))
318 throw new ConfigurationException (sectionName + " does not implement " + iconfig);
321 object o = Activator.CreateInstance (t, true);
323 throw new ConfigurationException ("Cannot get instance for " + t);
328 XmlDocument GetInnerDoc (XmlDocument doc, int i, string [] sectionPath)
330 if (++i >= sectionPath.Length)
333 if (doc.DocumentElement == null)
336 XmlNode node = doc.DocumentElement.FirstChild;
337 while (node != null) {
338 if (node.Name == sectionPath [i]) {
339 ConfigXmlDocument result = new ConfigXmlDocument ();
340 result.Load (new StringReader (node.OuterXml));
341 return GetInnerDoc (result, i, sectionPath);
343 node = node.NextSibling;
349 XmlDocument GetDocumentForSection (string sectionName)
351 ConfigXmlDocument doc = new ConfigXmlDocument ();
355 string [] sectionPath = sectionName.Split ('/');
356 string outerxml = pending [sectionPath [0]] as string;
357 if (outerxml == null)
360 StringReader reader = new StringReader (outerxml);
361 XmlTextReader rd = new XmlTextReader (reader);
363 doc.LoadSingleElement (fileName, rd);
365 return GetInnerDoc (doc, 0, sectionPath);
368 object GetConfigInternal (string sectionName)
370 object handler = GetHandler (sectionName);
371 IConfigurationSectionHandler iconf = handler as IConfigurationSectionHandler;
375 object parentConfig = null;
377 parentConfig = parent.GetConfig (sectionName);
379 XmlDocument doc = GetDocumentForSection (sectionName);
380 if (doc == null || doc.DocumentElement == null)
383 return iconf.Create (parentConfig, fileName, doc.DocumentElement);
386 object GetConfigInternal (string sectionName)
391 public object GetConfig (string sectionName)
395 config = this.FileCache [sectionName];
398 if (config == emptyMark)
405 config = GetConfigInternal (sectionName);
406 this.FileCache [sectionName] = (config == null) ? emptyMark : config;
412 private object LookForFactory (string key)
414 object o = factories [key];
419 return parent.LookForFactory (key);
424 private bool InitRead (XmlTextReader reader)
426 reader.MoveToContent ();
427 if (reader.NodeType != XmlNodeType.Element || reader.Name != "configuration")
428 ThrowException ("Configuration file does not have a valid root element", reader);
430 if (reader.HasAttributes)
431 ThrowException ("Unrecognized attribute in root element", reader);
432 if (reader.IsEmptyElement) {
437 reader.MoveToContent ();
438 return reader.NodeType != XmlNodeType.EndElement;
441 // FIXME: this approach is not always safe and likely to cause bugs.
442 private void MoveToNextElement (XmlTextReader reader)
444 while (reader.Read ()) {
445 XmlNodeType ntype = reader.NodeType;
446 if (ntype == XmlNodeType.Element)
449 if (ntype != XmlNodeType.Whitespace &&
450 ntype != XmlNodeType.Comment &&
451 ntype != XmlNodeType.SignificantWhitespace &&
452 ntype != XmlNodeType.EndElement)
453 ThrowException ("Unrecognized element", reader);
457 private void ReadSection (XmlTextReader reader, string sectionName)
460 string nameValue = null;
461 string typeValue = null;
462 string allowLoc = null, allowDef = null;
463 bool requirePermission = false;
464 string requirePer = null;
465 bool allowLocation = true;
466 AllowDefinition allowDefinition = AllowDefinition.Everywhere;
468 while (reader.MoveToNextAttribute ()) {
469 attName = reader.Name;
473 if (attName == "allowLocation") {
474 if (allowLoc != null)
475 ThrowException ("Duplicated allowLocation attribute.", reader);
477 allowLoc = reader.Value;
478 allowLocation = (allowLoc == "true");
479 if (!allowLocation && allowLoc != "false")
480 ThrowException ("Invalid attribute value", reader);
485 if (attName == "requirePermission") {
486 if (requirePer != null)
487 ThrowException ("Duplicated requirePermission attribute.", reader);
488 requirePer = reader.Value;
489 requirePermission = (requirePer == "true");
490 if (!requirePermission && requirePer != "false")
491 ThrowException ("Invalid attribute value", reader);
495 if (attName == "allowDefinition") {
496 if (allowDef != null)
497 ThrowException ("Duplicated allowDefinition attribute.", reader);
499 allowDef = reader.Value;
501 allowDefinition = (AllowDefinition) Enum.Parse (
502 typeof (AllowDefinition), allowDef);
504 ThrowException ("Invalid attribute value", reader);
510 if (attName == "type") {
511 if (typeValue != null)
512 ThrowException ("Duplicated type attribute.", reader);
513 typeValue = reader.Value;
517 if (attName == "name") {
518 if (nameValue != null)
519 ThrowException ("Duplicated name attribute.", reader);
520 nameValue = reader.Value;
521 if (nameValue == "location")
522 ThrowException ("location is a reserved section name", reader);
526 ThrowException ("Unrecognized attribute.", reader);
529 if (nameValue == null || typeValue == null)
530 ThrowException ("Required attribute missing", reader);
532 if (sectionName != null)
533 nameValue = sectionName + '/' + nameValue;
535 reader.MoveToElement();
536 object o = LookForFactory (nameValue);
537 if (o != null && o != removedMark)
538 ThrowException ("Already have a factory for " + nameValue, reader);
539 SectionData section = new SectionData (nameValue, typeValue, allowLocation,
540 allowDefinition, requirePermission);
541 section.FileName = fileName;
542 factories [nameValue] = section;
544 if (reader.IsEmptyElement)
548 reader.MoveToContent ();
549 if (reader.NodeType != XmlNodeType.EndElement)
550 // sub-section inside a section
551 ReadSections (reader, nameValue);
552 reader.ReadEndElement ();
554 reader.MoveToContent ();
557 private void ReadRemoveSection (XmlTextReader reader, string sectionName)
559 if (!reader.MoveToNextAttribute () || reader.Name != "name")
560 ThrowException ("Unrecognized attribute.", reader);
562 string removeValue = reader.Value;
563 if (removeValue == null || removeValue.Length == 0)
564 ThrowException ("Empty name to remove", reader);
566 reader.MoveToElement ();
568 if (sectionName != null)
569 removeValue = sectionName + '/' + removeValue;
571 object o = LookForFactory (removeValue);
572 if (o != null && o == removedMark)
573 ThrowException ("No factory for " + removeValue, reader);
575 factories [removeValue] = removedMark;
576 MoveToNextElement (reader);
579 private void ReadSectionGroup (XmlTextReader reader, string configSection)
581 if (!reader.MoveToNextAttribute ())
582 ThrowException ("sectionGroup must have a 'name' attribute.", reader);
586 if (reader.Name == "name") {
588 ThrowException ("Duplicate 'name' attribute.", reader);
589 value = reader.Value;
592 if (reader.Name != "type")
593 ThrowException ("Unrecognized attribute.", reader);
594 } while (reader.MoveToNextAttribute ());
597 ThrowException ("No 'name' attribute.", reader);
599 if (value == "location")
600 ThrowException ("location is a reserved section name", reader);
602 if (configSection != null)
603 value = configSection + '/' + value;
605 object o = LookForFactory (value);
606 if (o != null && o != removedMark && o != groupMark)
607 ThrowException ("Already have a factory for " + value, reader);
609 factories [value] = groupMark;
611 if (reader.IsEmptyElement) {
613 reader.MoveToContent ();
616 reader.MoveToContent ();
617 if (reader.NodeType != XmlNodeType.EndElement)
618 ReadSections (reader, value);
619 reader.ReadEndElement ();
620 reader.MoveToContent ();
624 // It stops XmlReader consumption at where it found
625 // surrounding EndElement i.e. EndElement is not consumed here
626 private void ReadSections (XmlTextReader reader, string configSection)
628 int depth = reader.Depth;
629 for (reader.MoveToContent ();
630 reader.Depth == depth;
631 reader.MoveToContent ()) {
632 string name = reader.Name;
633 if (name == "section") {
634 ReadSection (reader, configSection);
638 if (name == "remove") {
639 ReadRemoveSection (reader, configSection);
643 if (name == "clear") {
644 if (reader.HasAttributes)
645 ThrowException ("Unrecognized attribute.", reader);
648 MoveToNextElement (reader);
652 if (name == "sectionGroup") {
653 ReadSectionGroup (reader, configSection);
658 ThrowException ("Unrecognized element: " + reader.Name, reader);
662 void StorePending (string name, XmlTextReader reader)
665 pending = new Hashtable ();
667 pending [name] = reader.ReadOuterXml ();
670 private void ReadConfigFile (XmlTextReader reader)
672 //int depth = reader.Depth;
673 for (reader.MoveToContent ();
674 !reader.EOF && reader.NodeType != XmlNodeType.EndElement;
675 reader.MoveToContent ()) {
676 string name = reader.Name;
677 if (name == "configSections") {
678 if (reader.HasAttributes)
679 ThrowException ("Unrecognized attribute in <configSections>.", reader);
680 if (reader.IsEmptyElement)
684 reader.MoveToContent ();
685 if (reader.NodeType != XmlNodeType.EndElement)
686 ReadSections (reader, null);
687 reader.ReadEndElement ();
689 } else if (name != null && name != "") {
690 StorePending (name, reader);
691 MoveToNextElement (reader);
693 MoveToNextElement (reader);
698 private void ThrowException (string text, XmlTextReader reader)
700 throw new ConfigurationException (text, fileName, reader.LineNumber);