[Cleanup] Removed TARGET_JVM
[mono.git] / mcs / class / System / System.Configuration / ConfigurationSettings.cs
1 //
2 // System.Configuration.ConfigurationSettings.cs
3 //
4 // Author:
5 //   Christopher Podurgiel (cpodurgiel@msn.com)
6 //   Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 //   Eric Lindvall (eric@5stops.com)
8 //
9 // (c) Christopher Podurgiel
10 // (c) 2002 Ximian, Inc. (http://www.ximian.com)
11 // (c) 2003 Novell, Inc. (http://www.novell.com)
12 //
13
14 //
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:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
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.
33 //
34
35 #if CONFIGURATION_DEP
36 extern alias PrebuiltSystem;
37 using NameValueCollection = PrebuiltSystem.System.Collections.Specialized.NameValueCollection;
38 #endif
39
40 using System;
41 using System.Collections;
42 using System.Collections.Specialized;
43 using System.IO;
44 using System.Runtime.CompilerServices;
45 using System.Security.Permissions;
46 #if (XML_DEP)
47 using System.Xml;
48 using System.Xml.XPath;
49 #endif
50
51 namespace System.Configuration
52 {
53         public sealed class ConfigurationSettings
54         {
55                 static IConfigurationSystem config = DefaultConfig.GetInstance ();
56                 static object lockobj = new object ();
57                 private ConfigurationSettings ()
58                 {
59                 }
60
61                 [Obsolete ("This method is obsolete, it has been replaced by System.Configuration!System.Configuration.ConfigurationManager.GetSection")]
62                 public static object GetConfig (string sectionName)
63                 {
64 #if CONFIGURATION_DEP
65                         return ConfigurationManager.GetSection (sectionName);
66 #else
67                         return config.GetConfig (sectionName);
68 #endif
69                 }
70
71                 [Obsolete ("This property is obsolete.  Please use System.Configuration.ConfigurationManager.AppSettings")]
72                 public static NameValueCollection AppSettings
73                 {
74                         get {
75 #if CONFIGURATION_DEP
76                                 object appSettings = ConfigurationManager.GetSection ("appSettings");
77 #else
78                                 object appSettings = GetConfig ("appSettings");
79 #endif
80                                 if (appSettings == null)
81                                         appSettings = new NameValueCollection ();
82                                 return (NameValueCollection) appSettings;
83                         }
84                 }
85
86                 // Invoked from System.Web, disable warning
87                 internal static IConfigurationSystem ChangeConfigurationSystem (IConfigurationSystem newSystem)
88                 {
89                         if (newSystem == null)
90                                 throw new ArgumentNullException ("newSystem");
91
92                         lock (lockobj) {
93                                 IConfigurationSystem old = config;
94                                 config = newSystem;
95                                 return old;
96                         }
97                 }
98         }
99
100         //
101         // class DefaultConfig: read configuration from machine.config file and application
102         // config file if available.
103         //
104         class DefaultConfig : IConfigurationSystem
105         {
106                 static readonly DefaultConfig instance = new DefaultConfig ();        
107                 ConfigurationData config;
108                 
109                 private DefaultConfig ()
110                 {
111                 }
112
113                 public static DefaultConfig GetInstance ()
114                 {
115                         return instance;
116                 }
117
118                 [Obsolete ("This method is obsolete.  Please use System.Configuration.ConfigurationManager.GetConfig")]
119                 public object GetConfig (string sectionName)
120                 {
121                         Init ();
122                         return config.GetConfig (sectionName);
123                 }
124
125                 public void Init ()
126                 {
127                         lock (this) {
128                                 if (config != null)
129                                         return;
130
131                                 ConfigurationData data = new ConfigurationData ();
132                                 if (data.LoadString (GetBundledMachineConfig ())) {
133                                         // do nothing
134                                 } else {
135                                         if (!data.Load (GetMachineConfigPath ()))
136                                                 throw new ConfigurationException ("Cannot find " + GetMachineConfigPath ());
137
138                                 }
139                                 string appfile = GetAppConfigPath ();
140                                 if (appfile == null) {
141                                         config = data;
142                                         return;
143                                 }
144
145                                 ConfigurationData appData = new ConfigurationData (data);
146                                 if (appData.Load (appfile))
147                                         config = appData;
148                                 else
149                                         config = data;
150                         }
151                 }
152                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
153                 extern private static string get_bundled_machine_config ();
154                 internal static string GetBundledMachineConfig ()
155                 {
156                         return get_bundled_machine_config ();
157                 }
158                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
159                 extern private static string get_machine_config_path ();
160                 internal static string GetMachineConfigPath ()
161                 {
162                         return get_machine_config_path ();
163                 }
164                 private static string GetAppConfigPath ()
165                 {
166                         AppDomainSetup currentInfo = AppDomain.CurrentDomain.SetupInformation;
167
168                         string configFile = currentInfo.ConfigurationFile;
169                         if (configFile == null || configFile.Length == 0)
170                                 return null;
171
172                         return configFile;
173
174                 }
175         }
176
177         enum AllowDefinition
178         {
179                 Everywhere,
180                 MachineOnly,
181                 MachineToApplication
182         }
183
184         class SectionData
185         {
186                 public readonly string SectionName;
187                 public readonly string TypeName;
188                 public readonly bool AllowLocation;
189                 public readonly AllowDefinition AllowDefinition;
190 #if XML_DEP
191                 public string FileName;
192 #endif
193                 public readonly bool RequirePermission;
194
195                 public SectionData (string sectionName, string typeName,
196                             bool allowLocation, AllowDefinition allowDefinition, bool requirePermission)
197                 {
198                         SectionName = sectionName;
199                         TypeName = typeName;
200                         AllowLocation = allowLocation;
201                         AllowDefinition = allowDefinition;
202                         RequirePermission = requirePermission;
203                 }
204         }
205
206
207         class ConfigurationData
208         {
209                 ConfigurationData parent;
210                 Hashtable factories;
211                 static object removedMark = new object ();
212                 static object emptyMark = new object ();
213 #if (XML_DEP)
214                 Hashtable pending;
215                 string fileName;
216                 static object groupMark = new object ();
217 #endif
218                 Hashtable cache;
219
220                 Hashtable FileCache {
221                         get {
222                                 if (cache != null)
223                                         return cache;
224
225                                 cache = new Hashtable ();
226                                 return cache;
227                         }
228                 }
229
230                 public ConfigurationData () : this (null)
231                 {
232                 }
233
234                 public ConfigurationData (ConfigurationData parent)
235                 {
236                         this.parent = (parent == this) ? null : parent;
237                         factories = new Hashtable ();
238                 }
239
240                 // SECURITY-FIXME: limit this with an imperative assert for reading the specific file
241                 [FileIOPermission (SecurityAction.Assert, Unrestricted = true)]
242                 public bool Load (string fileName)
243                 {
244 #if (XML_DEP)
245                         this.fileName = fileName;
246                         if (fileName == null
247                                 || !File.Exists (fileName)
248 )
249                                 return false;
250                         
251                         XmlTextReader reader = null;
252
253                         try {
254                                 FileStream fs = new FileStream (fileName, FileMode.Open, FileAccess.Read);
255                                 reader = new XmlTextReader (fs);
256                                 if (InitRead (reader))
257                                         ReadConfigFile (reader);
258                         } catch (ConfigurationException) {
259                                 throw;
260                         } catch (Exception e) {
261                                 throw new ConfigurationException ("Error reading " + fileName, e);
262                         } finally {
263                                 if (reader != null)
264                                         reader.Close();
265                         }
266 #endif
267                         return true;
268                 }
269                 
270                 public bool LoadString (string data)
271                 {
272                         if (data == null)
273                                 return false;
274 #if (XML_DEP)
275                         XmlTextReader reader = null;
276
277                         try {
278                                 TextReader tr = new StringReader (data);
279                                 reader = new XmlTextReader (tr);
280                                 if (InitRead (reader))
281                                         ReadConfigFile (reader);
282                         } catch (ConfigurationException) {
283                                 throw;
284                         } catch (Exception e) {
285                                 throw new ConfigurationException ("Error reading " + fileName, e);
286                         } finally {
287                                 if (reader != null)
288                                         reader.Close();
289                         }
290 #endif
291                         return true;
292                 }
293                 
294                 object GetHandler (string sectionName)
295                 {
296                         lock (factories) {
297                                 object o = factories [sectionName];
298                                 if (o == null || o == removedMark) {
299                                         if (parent != null)
300                                                 return parent.GetHandler (sectionName);
301
302                                         return null;
303                                 }
304
305                                 if (o is IConfigurationSectionHandler)
306                                         return (IConfigurationSectionHandler) o;
307
308                                 o = CreateNewHandler (sectionName, (SectionData) o);
309                                 factories [sectionName] = o;
310                                 return o;
311                         }
312                 }
313
314                 object CreateNewHandler (string sectionName, SectionData section)
315                 {
316                         Type t = Type.GetType (section.TypeName);
317                         if (t == null)
318                                 throw new ConfigurationException ("Cannot get Type for " + section.TypeName);
319
320 #if false
321                         Type iconfig = typeof (IConfigurationSectionHandler);
322                         if (!iconfig.IsAssignableFrom (t))
323                                 throw new ConfigurationException (sectionName + " does not implement " + iconfig);
324 #endif
325                         
326                         object o = Activator.CreateInstance (t, true);
327                         if (o == null)
328                                 throw new ConfigurationException ("Cannot get instance for " + t);
329
330                         return o;
331                 }
332 #if (XML_DEP)
333                 XmlDocument GetInnerDoc (XmlDocument doc, int i, string [] sectionPath)
334                 {
335                         if (++i >= sectionPath.Length)
336                                 return doc;
337
338                         if (doc.DocumentElement == null)
339                                 return null;
340
341                         XmlNode node = doc.DocumentElement.FirstChild;
342                         while (node != null) {
343                                 if (node.Name == sectionPath [i]) {
344                                         ConfigXmlDocument result = new ConfigXmlDocument ();
345                                         result.Load (new StringReader (node.OuterXml));
346                                         return GetInnerDoc (result, i, sectionPath);
347                                 }
348                                 node = node.NextSibling;
349                         }
350
351                         return null;
352                 }
353
354                 XmlDocument GetDocumentForSection (string sectionName)
355                 {
356                         ConfigXmlDocument doc = new ConfigXmlDocument ();
357                         if (pending == null)
358                                 return doc;
359
360                         string [] sectionPath = sectionName.Split ('/');
361                         string outerxml = pending [sectionPath [0]] as string;
362                         if (outerxml == null)
363                                 return doc;
364
365                         StringReader reader = new StringReader (outerxml);
366                         XmlTextReader rd = new XmlTextReader (reader);
367                         rd.MoveToContent ();
368                         doc.LoadSingleElement (fileName, rd);
369
370                         return GetInnerDoc (doc, 0, sectionPath);
371                 }
372                 
373                 object GetConfigInternal (string sectionName)
374                 {
375                         object handler = GetHandler (sectionName);
376                         IConfigurationSectionHandler iconf = handler as IConfigurationSectionHandler;
377                         if (iconf == null)
378                                 return handler;
379
380                         object parentConfig = null;
381                         if (parent != null)
382                                 parentConfig = parent.GetConfig (sectionName);
383
384                         XmlDocument doc = GetDocumentForSection (sectionName);
385                         if (doc == null || doc.DocumentElement == null)
386                                 return parentConfig;
387                         
388                         return iconf.Create (parentConfig, fileName, doc.DocumentElement);
389                 }
390 #else
391                 object GetConfigInternal (string sectionName)
392                 {
393                     return null;
394                 }
395 #endif
396                 public object GetConfig (string sectionName)
397                 {
398                         object config;
399                         lock (this) {
400                                 config = this.FileCache [sectionName];
401                         }
402
403                         if (config == emptyMark)
404                                 return null;
405
406                         if (config != null)
407                                 return config;
408
409                         lock (this) {
410                                 config = GetConfigInternal (sectionName);
411                                 this.FileCache [sectionName] = (config == null) ? emptyMark : config;
412                         }
413
414                         return config;
415                 }
416
417                 private object LookForFactory (string key)
418                 {
419                         object o = factories [key];
420                         if (o != null)
421                                 return o;
422
423                         if (parent != null)
424                                 return parent.LookForFactory (key);
425
426                         return null;
427                 }
428 #if (XML_DEP)
429                 private bool InitRead (XmlTextReader reader)
430                 {
431                         reader.MoveToContent ();
432                         if (reader.NodeType != XmlNodeType.Element || reader.Name != "configuration")
433                                 ThrowException ("Configuration file does not have a valid root element", reader);
434
435                         if (reader.HasAttributes)
436                                 ThrowException ("Unrecognized attribute in root element", reader);
437                         if (reader.IsEmptyElement) {
438                                 reader.Skip ();
439                                 return false;
440                         }
441                         reader.Read ();
442                         reader.MoveToContent ();
443                         return reader.NodeType != XmlNodeType.EndElement;
444                 }
445
446                 // FIXME: this approach is not always safe and likely to cause bugs.
447                 private void MoveToNextElement (XmlTextReader reader)
448                 {
449                         while (reader.Read ()) {
450                                 XmlNodeType ntype = reader.NodeType;
451                                 if (ntype == XmlNodeType.Element)
452                                         return;
453
454                                 if (ntype != XmlNodeType.Whitespace &&
455                                     ntype != XmlNodeType.Comment &&
456                                     ntype != XmlNodeType.SignificantWhitespace &&
457                                     ntype != XmlNodeType.EndElement)
458                                         ThrowException ("Unrecognized element", reader);
459                         }
460                 }
461
462                 private void ReadSection (XmlTextReader reader, string sectionName)
463                 {
464                         string attName;
465                         string nameValue = null;
466                         string typeValue = null;
467                         string allowLoc = null, allowDef = null;
468                         bool requirePermission = false;
469                         string requirePer = null;
470                         bool allowLocation = true;
471                         AllowDefinition allowDefinition = AllowDefinition.Everywhere;
472
473                         while (reader.MoveToNextAttribute ()) {
474                                 attName = reader.Name;
475                                 if (attName == null)
476                                         continue;
477
478                                 if (attName == "allowLocation") {
479                                         if (allowLoc != null)
480                                                 ThrowException ("Duplicated allowLocation attribute.", reader);
481
482                                         allowLoc = reader.Value;
483                                         allowLocation = (allowLoc == "true");
484                                         if (!allowLocation && allowLoc != "false")
485                                                 ThrowException ("Invalid attribute value", reader);
486
487                                         continue;
488                                 }
489
490                                 if (attName == "requirePermission") {
491                                         if (requirePer != null)
492                                                 ThrowException ("Duplicated requirePermission attribute.", reader);
493                                         requirePer = reader.Value;
494                                         requirePermission = (requirePer == "true");
495                                         if (!requirePermission && requirePer != "false")
496                                                 ThrowException ("Invalid attribute value", reader);
497                                         continue;
498                                 }
499
500                                 if (attName == "allowDefinition") {
501                                         if (allowDef != null)
502                                                 ThrowException ("Duplicated allowDefinition attribute.", reader);
503
504                                         allowDef = reader.Value;
505                                         try {
506                                                 allowDefinition = (AllowDefinition) Enum.Parse (
507                                                                    typeof (AllowDefinition), allowDef);
508                                         } catch {
509                                                 ThrowException ("Invalid attribute value", reader);
510                                         }
511
512                                         continue;
513                                 }
514
515                                 if (attName == "type")  {
516                                         if (typeValue != null)
517                                                 ThrowException ("Duplicated type attribute.", reader);
518                                         typeValue = reader.Value;
519                                         continue;
520                                 }
521                                 
522                                 if (attName == "name")  {
523                                         if (nameValue != null)
524                                                 ThrowException ("Duplicated name attribute.", reader);
525                                         nameValue = reader.Value;
526                                         if (nameValue == "location")
527                                                 ThrowException ("location is a reserved section name", reader);
528                                         continue;
529                                 }
530
531                                 ThrowException ("Unrecognized attribute.", reader);
532                         }
533
534                         if (nameValue == null || typeValue == null)
535                                 ThrowException ("Required attribute missing", reader);
536
537                         if (sectionName != null)
538                                 nameValue = sectionName + '/' + nameValue;
539
540                         reader.MoveToElement();
541                         object o = LookForFactory (nameValue);
542                         if (o != null && o != removedMark)
543                                 ThrowException ("Already have a factory for " + nameValue, reader);
544                         SectionData section = new SectionData (nameValue, typeValue, allowLocation,
545                                 allowDefinition, requirePermission);
546                         section.FileName = fileName;
547                         factories [nameValue] = section;
548
549                         if (reader.IsEmptyElement)
550                                 reader.Skip ();
551                         else {
552                                 reader.Read ();
553                                 reader.MoveToContent ();
554                                 if (reader.NodeType != XmlNodeType.EndElement)
555                                         // sub-section inside a section
556                                         ReadSections (reader, nameValue);
557                                 reader.ReadEndElement ();
558                         }
559                         reader.MoveToContent ();
560                 }
561
562                 private void ReadRemoveSection (XmlTextReader reader, string sectionName)
563                 {
564                         if (!reader.MoveToNextAttribute () || reader.Name != "name")
565                                 ThrowException ("Unrecognized attribute.", reader);
566
567                         string removeValue = reader.Value;
568                         if (removeValue == null || removeValue.Length == 0)
569                                 ThrowException ("Empty name to remove", reader);
570
571                         reader.MoveToElement ();
572
573                         if (sectionName != null)
574                                 removeValue = sectionName + '/' + removeValue;
575
576                         object o = LookForFactory (removeValue);
577                         if (o != null && o == removedMark)
578                                 ThrowException ("No factory for " + removeValue, reader);
579
580                         factories [removeValue] = removedMark;
581                         MoveToNextElement (reader);
582                 }
583
584                 private void ReadSectionGroup (XmlTextReader reader, string configSection)
585                 {
586                         if (!reader.MoveToNextAttribute ())
587                                 ThrowException ("sectionGroup must have a 'name' attribute.", reader);
588
589                         string value = null;
590                         do {
591                                 if (reader.Name == "name") {
592                                         if (value != null)
593                                                 ThrowException ("Duplicate 'name' attribute.", reader);
594                                         value = reader.Value;
595                                 }
596                                 else
597                                 if (reader.Name != "type")
598                                         ThrowException ("Unrecognized attribute.", reader);
599                         } while (reader.MoveToNextAttribute ());
600
601                         if (value == null)
602                                 ThrowException ("No 'name' attribute.", reader);
603                         
604                         if (value == "location")
605                                 ThrowException ("location is a reserved section name", reader);
606
607                         if (configSection != null)
608                                 value = configSection + '/' + value;
609
610                         object o = LookForFactory (value);
611                         if (o != null && o != removedMark && o != groupMark)
612                                 ThrowException ("Already have a factory for " + value, reader);
613
614                         factories [value] = groupMark;
615
616                         if (reader.IsEmptyElement) {
617                                 reader.Skip ();
618                                 reader.MoveToContent ();
619                         } else {
620                                 reader.Read ();
621                                 reader.MoveToContent ();
622                                 if (reader.NodeType != XmlNodeType.EndElement)
623                                         ReadSections (reader, value);
624                                 reader.ReadEndElement ();
625                                 reader.MoveToContent ();
626                         }
627                 }
628
629                 // It stops XmlReader consumption at where it found
630                 // surrounding EndElement i.e. EndElement is not consumed here
631                 private void ReadSections (XmlTextReader reader, string configSection)
632                 {
633                         int depth = reader.Depth;
634                         for (reader.MoveToContent ();
635                              reader.Depth == depth;
636                              reader.MoveToContent ()) {
637                                 string name = reader.Name;
638                                 if (name == "section") {
639                                         ReadSection (reader, configSection);
640                                         continue;
641                                 } 
642                                 
643                                 if (name == "remove") {
644                                         ReadRemoveSection (reader, configSection);
645                                         continue;
646                                 }
647
648                                 if (name == "clear") {
649                                         if (reader.HasAttributes)
650                                                 ThrowException ("Unrecognized attribute.", reader);
651
652                                         factories.Clear ();
653                                         MoveToNextElement (reader);
654                                         continue;
655                                 }
656
657                                 if (name == "sectionGroup") {
658                                         ReadSectionGroup (reader, configSection);
659                                         continue;
660                                 }
661                                 
662
663                                 ThrowException ("Unrecognized element: " + reader.Name, reader);
664                         }
665                 }
666
667                 void StorePending (string name, XmlTextReader reader)
668                 {
669                         if (pending == null)
670                                 pending = new Hashtable ();
671
672                         pending [name] = reader.ReadOuterXml ();
673                 }
674
675                 private void ReadConfigFile (XmlTextReader reader)
676                 {
677                         //int depth = reader.Depth;
678                         for (reader.MoveToContent ();
679                              !reader.EOF && reader.NodeType != XmlNodeType.EndElement;
680                              reader.MoveToContent ()) {
681                                 string name = reader.Name;
682                                 if (name == "configSections") {
683                                         if (reader.HasAttributes)
684                                                 ThrowException ("Unrecognized attribute in <configSections>.", reader);
685                                         if (reader.IsEmptyElement)
686                                                 reader.Skip ();
687                                         else {
688                                                 reader.Read ();
689                                                 reader.MoveToContent ();
690                                                 if (reader.NodeType != XmlNodeType.EndElement)
691                                                         ReadSections (reader, null);
692                                                 reader.ReadEndElement ();
693                                         }
694                                 } else if (name != null && name != "") {
695                                         StorePending (name, reader);
696                                         MoveToNextElement (reader);
697                                 } else {
698                                         MoveToNextElement (reader);
699                                 }
700                         }
701                 }
702                 
703                 private void ThrowException (string text, XmlTextReader reader)
704                 {
705                         throw new ConfigurationException (text, fileName, reader.LineNumber);
706                 }
707 #endif
708         }
709 }
710
711