2 // System.Configuration.WebConfigurationSettings.cs
5 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 // (c) 2003,2004 Novell, Inc. (http://www.novell.com)
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using System.Configuration;
33 using System.Collections;
35 using System.Reflection;
36 using System.Runtime.Remoting;
37 using System.Web.Util;
40 using vmw.@internal.io;
42 using System.Web.J2EE;
45 namespace System.Web.Configuration
47 class WebConfigurationSettings
50 static private IConfigurationSystem oldConfig {
52 return (IConfigurationSystem)AppDomain.CurrentDomain.GetData("WebConfigurationSettings.oldConfig");
55 AppDomain.CurrentDomain.SetData("WebConfigurationSettings.oldConfig", value);
59 static private WebDefaultConfig config {
61 return (WebDefaultConfig)AppDomain.CurrentDomain.GetData("WebConfigurationSettings.config");
64 AppDomain.CurrentDomain.SetData("WebConfigurationSettings.config", value);
68 static IConfigurationSystem oldConfig;
69 static WebDefaultConfig config;
71 static string machineConfigPath;
72 const BindingFlags privStatic = BindingFlags.NonPublic | BindingFlags.Static;
73 static readonly object lockobj = new object ();
75 private WebConfigurationSettings ()
79 public static void Init ()
85 WebDefaultConfig settings = WebDefaultConfig.GetInstance ();
86 Type t = typeof (ConfigurationSettings);
87 MethodInfo changeConfig = t.GetMethod ("ChangeConfigurationSystem",
90 if (changeConfig == null)
91 throw new ConfigurationException ("Cannot find method CCS");
93 object [] args = new object [] {settings};
94 oldConfig = (IConfigurationSystem) changeConfig.Invoke (null, args);
99 public static void Init (HttpContext context)
102 config.Init (context);
105 public static object GetConfig (string sectionName)
107 return config.GetConfig (sectionName);
110 public static object GetConfig (string sectionName, HttpContext context)
112 return config.GetConfig (sectionName, context);
115 public static string MachineConfigPath {
118 if (machineConfigPath != null)
119 return machineConfigPath;
124 Type t = oldConfig.GetType ();
125 MethodInfo getMC = t.GetMethod ("GetMachineConfigPath",
129 throw new ConfigurationException ("Cannot find method GMC");
131 machineConfigPath = (string) getMC.Invoke (null, null);
132 return machineConfigPath;
139 // class WebDefaultConfig: read configuration from machine.config file and application
140 // config file if available.
142 class WebDefaultConfig : IConfigurationSystem
145 static private WebDefaultConfig instance {
147 WebDefaultConfig val = (WebDefaultConfig)AppDomain.CurrentDomain.GetData("WebDefaultConfig.instance");
149 val = new WebDefaultConfig();
150 AppDomain.CurrentDomain.SetData("WebDefaultConfig.instance", val);
155 AppDomain.CurrentDomain.SetData("WebDefaultConfig.instance", value);
159 static WebDefaultConfig instance;
161 Hashtable fileToConfig;
162 HttpContext firstContext;
165 static WebDefaultConfig ()
167 instance = new WebDefaultConfig ();
170 private WebDefaultConfig ()
172 fileToConfig = new Hashtable ();
175 public static WebDefaultConfig GetInstance ()
180 public object GetConfig (string sectionName)
182 HttpContext current = HttpContext.Current;
184 current = firstContext;
185 return GetConfig (sectionName, current);
188 public object GetConfig (string sectionName, HttpContext context)
193 ConfigurationData config = GetConfigFromFileName (context.Request.CurrentExecutionFilePath, context);
197 return config.GetConfig (sectionName, context);
200 ConfigurationData GetConfigFromFileName (string filepath, HttpContext context)
203 return (ConfigurationData) fileToConfig [WebConfigurationSettings.MachineConfigPath];
205 string dir = UrlUtils.GetDirectory (filepath);
206 if (HttpRuntime.AppDomainAppVirtualPath.Length > dir.Length)
207 return (ConfigurationData) fileToConfig [WebConfigurationSettings.MachineConfigPath];
209 ConfigurationData data = (ConfigurationData) fileToConfig [dir];
213 string realpath = null;
215 realpath = context.Request.MapPath (dir);
217 realpath = context.Request.MapPath (HttpRuntime.AppDomainAppVirtualPath);
219 string lower = Path.Combine (realpath, "web.config");
220 bool isLower = File.Exists (lower);
221 string wcfile = null;
223 string upper = Path.Combine (realpath, "Web.config");
224 bool isUpper = File.Exists (upper);
231 string tempDir = dir;
232 if (tempDir == HttpRuntime.AppDomainAppVirtualPath ||
233 tempDir + "/" == HttpRuntime.AppDomainAppVirtualPath) {
235 realpath = HttpRuntime.AppDomainAppPath;
237 ConfigurationData parent = GetConfigFromFileName (tempDir, context);
238 if (wcfile == null) {
239 data = new ConfigurationData (parent, null, realpath);
241 fileToConfig [dir] = data;
245 data = new ConfigurationData (parent, wcfile);
247 data.LoadFromFile (wcfile);
248 fileToConfig [dir] = data;
249 RemotingConfiguration.Configure (wcfile);
257 // nothing. We need a context.
260 public void Init (HttpContext context)
269 firstContext = context;
270 ConfigurationData data = new ConfigurationData ();
271 if (!data.LoadFromFile (WebConfigurationSettings.MachineConfigPath))
272 throw new ConfigurationException ("Cannot find " + WebConfigurationSettings.MachineConfigPath);
274 fileToConfig [WebConfigurationSettings.MachineConfigPath] = data;
280 class FileWatcherCache
282 Hashtable cacheTable;
285 #if !TARGET_JVM // no file watcher support yet in Grasshopper
286 FileSystemWatcher watcher;
288 ConfigurationData data;
290 public FileWatcherCache (ConfigurationData data)
293 cacheTable = new Hashtable ();
294 this.path = Path.GetDirectoryName (data.FileName);
295 this.filename = Path.GetFileName (data.FileName);
296 if (!Directory.Exists (path))
300 watcher = new FileSystemWatcher (this.path, this.filename);
301 FileSystemEventHandler handler = new FileSystemEventHandler (SetChanged);
302 watcher.Created += handler;
303 watcher.Changed += handler;
304 watcher.Deleted += handler;
305 watcher.EnableRaisingEvents = true;
310 void SetChanged (object o, FileSystemEventArgs args)
315 if (args.ChangeType == WatcherChangeTypes.Created)
316 RemotingConfiguration.Configure (args.FullPath);
318 if (args.ChangeType != WatcherChangeTypes.Deleted)
319 data.LoadFromFile (args.FullPath);
324 public object this [string key] {
327 return cacheTable [key];
332 cacheTable [key] = value;
340 watcher.EnableRaisingEvents = false;
354 public readonly string SectionName;
355 public readonly string TypeName;
356 public readonly bool AllowLocation;
357 public readonly AllowDefinition AllowDefinition;
358 public string FileName;
360 public SectionData (string sectionName, string typeName,
361 bool allowLocation, AllowDefinition allowDefinition)
363 SectionName = sectionName;
365 AllowLocation = allowLocation;
366 AllowDefinition = allowDefinition;
370 class ConfigurationData
372 ConfigurationData parent;
378 static object removedMark = new object ();
379 static object groupMark = new object ();
380 static object emptyMark = new object ();
381 FileWatcherCache fileCache;
382 static char [] forbiddenPathChars = new char [] {
383 ';', '?', ':', '@', '&', '=', '+',
384 '$', ',','\\', '*', '\"', '<', '>'
387 static string forbiddenStr = "';', '?', ':', '@', '&', '=', '+', '$', ',', '\\', '*', '\"', '<', '>'";
389 internal FileWatcherCache FileCache {
392 if (fileCache != null)
395 fileCache = new FileWatcherCache (this);
402 internal string FileName {
403 get { return fileName; }
406 internal ConfigurationData Parent {
407 get { return parent; }
410 internal string DirName {
411 get { return dirname; }
412 set { dirname = value; }
415 internal void Reset ()
421 if (locations != null)
425 public ConfigurationData () : this (null, null)
429 public ConfigurationData (ConfigurationData parent, string filename)
431 this.parent = (parent == this) ? null : parent;
432 this.fileName = filename;
433 factories = new Hashtable ();
436 public ConfigurationData (ConfigurationData parent, string filename, string realdir)
438 this.parent = (parent == this) ? null : parent;
439 if (filename == null) {
440 this.fileName = Path.Combine (realdir, "*.config");
442 this.fileName = filename;
444 factories = new Hashtable ();
447 public bool LoadFromFile (string fileName)
449 this.fileName = fileName;
451 if (fileName == null || !File.Exists (fileName)) {
453 if (fileName != null && fileName.EndsWith("machine.config"))
455 if (fileName.StartsWith("/"))
456 fileName = fileName.Substring(1);
457 java.lang.ClassLoader cl = (java.lang.ClassLoader)AppDomain.CurrentDomain.GetData("GH_ContextClassLoader");
460 java.io.InputStream inputStream = cl.getResourceAsStream(fileName);
461 fs = (Stream)IOUtils.getStream(inputStream);
468 XmlTextReader reader = null;
472 fs = new FileStream (fileName, FileMode.Open, FileAccess.Read);
473 reader = new XmlTextReader (fs);
475 ReadConfig (reader, false);
476 } catch (ConfigurationException) {
478 } catch (Exception e) {
479 throw new ConfigurationException ("Error reading " + fileName, e);
488 public void LoadFromReader (XmlTextReader reader, string fakeFileName, bool isLocation)
490 fileName = fakeFileName;
491 MoveToNextElement (reader);
492 ReadConfig (reader, isLocation);
495 object GetHandler (string sectionName)
498 object o = factories [sectionName];
499 if (o == null || o == removedMark) {
501 return parent.GetHandler (sectionName);
506 if (o is IConfigurationSectionHandler)
507 return (IConfigurationSectionHandler) o;
509 o = CreateNewHandler (sectionName, (SectionData) o);
510 factories [sectionName] = o;
515 object CreateNewHandler (string sectionName, SectionData section)
517 Type t = Type.GetType (section.TypeName);
519 throw new ConfigurationException ("Cannot get Type for " + section.TypeName);
521 Type iconfig = typeof (IConfigurationSectionHandler);
522 if (!iconfig.IsAssignableFrom (t))
523 throw new ConfigurationException (sectionName + " does not implement " + iconfig);
525 object o = Activator.CreateInstance (t, true);
527 throw new ConfigurationException ("Cannot get instance for " + t);
532 XmlDocument GetInnerDoc (XmlDocument doc, int i, string [] sectionPath)
534 if (++i >= sectionPath.Length)
537 if (doc.DocumentElement == null)
540 XmlNode node = doc.DocumentElement.FirstChild;
541 while (node != null) {
542 if (node.Name == sectionPath [i]) {
543 ConfigXmlDocument result = new ConfigXmlDocument ();
544 result.Load (new StringReader (node.OuterXml));
545 return GetInnerDoc (result, i, sectionPath);
547 node = node.NextSibling;
553 XmlDocument GetDocumentForSection (string sectionName)
555 ConfigXmlDocument doc = new ConfigXmlDocument ();
559 string [] sectionPath = sectionName.Split ('/');
560 string outerxml = pending [sectionPath [0]] as string;
561 if (outerxml == null)
564 StringReader reader = new StringReader (outerxml);
565 XmlTextReader rd = new XmlTextReader (reader);
567 doc.LoadSingleElement (fileName, rd);
569 return GetInnerDoc (doc, 0, sectionPath);
572 object GetConfigInternal (string sectionName, HttpContext context, bool useLoc)
574 object handler = GetHandler (sectionName);
575 IConfigurationSectionHandler iconf = handler as IConfigurationSectionHandler;
579 object parentConfig = null;
580 if (parent != null) {
582 parentConfig = parent.GetConfig (sectionName, context);
584 parentConfig = parent.GetConfigOptLocation (sectionName, context, false);
587 XmlDocument doc = GetDocumentForSection (sectionName);
588 if (doc == null || doc.DocumentElement == null)
591 return iconf.Create (parentConfig, fileName, doc.DocumentElement);
594 string MakeRelative (string fullUrl, string relativeTo)
596 if (fullUrl == relativeTo)
599 if (fullUrl.IndexOf (relativeTo) != 0)
602 string leftOver = fullUrl.Substring (relativeTo.Length);
603 if (leftOver.Length > 0 && leftOver [0] == '/')
604 leftOver = leftOver.Substring (1);
606 leftOver = UrlUtils.Canonic (leftOver);
607 if (leftOver.Length > 0 && leftOver [0] == '/')
608 leftOver = leftOver.Substring (1);
614 public object GetConfig (string sectionName, HttpContext context)
616 if (locations != null && dirname != null) {
617 string reduced = MakeRelative (context.Request.CurrentExecutionFilePath, dirname);
618 string [] parts = reduced.Split ('/');
619 Location location = null;
621 string target = null;
622 for (int i = 0; i < parts.Length; i++) {
626 target = target + "/" + parts [i];
628 if (locations.ContainsKey (target)) {
629 location = locations [target] as Location;
630 } else if (locations.ContainsKey (target + "/*")) {
631 location = locations [target + "/*"] as Location;
635 if (location == null) {
636 location = locations ["*"] as Location;
639 if (location != null && location.Config != null) {
640 object o = location.Config.GetConfigOptLocation (sectionName, context, false);
647 return GetConfigOptLocation (sectionName, context, true);
650 object GetConfigOptLocation (string sectionName, HttpContext context, bool useLoc)
652 object config = this.FileCache [sectionName];
653 if (config == emptyMark)
660 config = GetConfigInternal (sectionName, context, useLoc);
661 this.FileCache [sectionName] = (config == null) ? emptyMark : config;
667 private object LookForFactory (string key)
669 object o = factories [key];
674 return parent.LookForFactory (key);
679 private void InitRead (XmlTextReader reader)
681 reader.MoveToContent ();
682 if (reader.NodeType != XmlNodeType.Element || reader.Name != "configuration")
683 ThrowException ("Configuration file does not have a valid root element", reader);
685 if (reader.HasAttributes)
686 ThrowException ("Unrecognized attribute in root element", reader);
688 MoveToNextElement (reader);
691 internal void MoveToNextElement (XmlTextReader reader)
693 while (reader.Read ()) {
694 XmlNodeType ntype = reader.NodeType;
695 if (ntype == XmlNodeType.Element)
698 if (ntype != XmlNodeType.Whitespace &&
699 ntype != XmlNodeType.Comment &&
700 ntype != XmlNodeType.SignificantWhitespace &&
701 ntype != XmlNodeType.EndElement)
702 ThrowException ("Unrecognized element", reader);
706 private void ReadSection (XmlTextReader reader, string sectionName)
709 string nameValue = null;
710 string typeValue = null;
711 string allowLoc = null, allowDef = null;
712 bool allowLocation = true;
713 AllowDefinition allowDefinition = AllowDefinition.Everywhere;
715 while (reader.MoveToNextAttribute ()) {
716 attName = reader.Name;
720 if (attName == "allowLocation") {
721 if (allowLoc != null)
722 ThrowException ("Duplicated allowLocation attribute.", reader);
724 allowLoc = reader.Value;
725 allowLocation = (allowLoc == "true");
726 if (!allowLocation && allowLoc != "false")
727 ThrowException ("Invalid attribute value", reader);
732 if (attName == "allowDefinition") {
733 if (allowDef != null)
734 ThrowException ("Duplicated allowDefinition attribute.", reader);
736 allowDef = reader.Value;
738 allowDefinition = (AllowDefinition) Enum.Parse (
739 typeof (AllowDefinition), allowDef);
741 ThrowException ("Invalid attribute value", reader);
747 if (attName == "type") {
748 if (typeValue != null)
749 ThrowException ("Duplicated type attribute.", reader);
750 typeValue = reader.Value;
754 if (attName == "name") {
755 if (nameValue != null)
756 ThrowException ("Duplicated name attribute.", reader);
758 nameValue = reader.Value;
759 if (nameValue == "location")
760 ThrowException ("location is a reserved section name", reader);
764 ThrowException ("Unrecognized attribute.", reader);
767 if (nameValue == null || typeValue == null)
768 ThrowException ("Required attribute missing", reader);
770 if (sectionName != null)
771 nameValue = sectionName + '/' + nameValue;
773 reader.MoveToElement();
774 object o = LookForFactory (nameValue);
775 if (o != null && o != removedMark)
776 ThrowException ("Already have a factory for " + nameValue, reader);
778 SectionData section = new SectionData (nameValue, typeValue, allowLocation, allowDefinition);
779 section.FileName = fileName;
780 factories [nameValue] = section;
781 MoveToNextElement (reader);
784 private void ReadRemoveSection (XmlTextReader reader, string sectionName)
786 if (!reader.MoveToNextAttribute () || reader.Name != "name")
787 ThrowException ("Unrecognized attribute.", reader);
789 string removeValue = reader.Value;
790 if (removeValue == null || removeValue.Length == 0)
791 ThrowException ("Empty name to remove", reader);
793 reader.MoveToElement ();
795 if (sectionName != null)
796 removeValue = sectionName + '/' + removeValue;
798 object o = LookForFactory (removeValue);
799 if (o != null && o == removedMark)
800 ThrowException ("No factory for " + removeValue, reader);
802 factories [removeValue] = removedMark;
803 MoveToNextElement (reader);
806 private void ReadSectionGroup (XmlTextReader reader, string configSection)
808 if (!reader.MoveToNextAttribute ())
809 ThrowException ("sectionGroup must have a 'name' attribute.", reader);
814 if (reader.Name == "name") {
816 ThrowException ("Duplicate 'name' attribute.", reader);
817 value = reader.Value;
819 else if (reader.Name != "type")
820 ThrowException ("Unrecognized attribute.", reader);
821 } while (reader.MoveToNextAttribute ());
823 if (reader.Name != "name")
824 ThrowException ("Unrecognized attribute.", reader);
826 if (reader.MoveToNextAttribute ())
827 ThrowException ("Unrecognized attribute.", reader);
829 value = reader.Value;
832 ThrowException ("No 'name' attribute.", reader);
834 if (value == "location")
835 ThrowException ("location is a reserved section name", reader);
837 if (configSection != null)
838 value = configSection + '/' + value;
840 object o = LookForFactory (value);
841 if (o != null && o != removedMark && o != groupMark)
842 ThrowException ("Already have a factory for " + value, reader);
844 factories [value] = groupMark;
845 MoveToNextElement (reader);
846 ReadSections (reader, value);
849 private void ReadSections (XmlTextReader reader, string configSection)
851 int depth = reader.Depth;
852 while (reader.Depth == depth) {
853 string name = reader.Name;
854 if (name == "section") {
855 ReadSection (reader, configSection);
859 if (name == "remove") {
860 ReadRemoveSection (reader, configSection);
864 if (name == "clear") {
865 if (reader.HasAttributes)
866 ThrowException ("Unrecognized attribute.", reader);
869 MoveToNextElement (reader);
873 if (name == "sectionGroup") {
874 ReadSectionGroup (reader, configSection);
878 ThrowException ("Unrecognized element: " + reader.Name, reader);
882 void StoreLocation (string name, XmlTextReader reader)
885 bool haveAllow = false;
886 bool allowOverride = true;
889 while (reader.MoveToNextAttribute ()) {
894 ThrowException ("Duplicate path attribute", reader);
897 if (path.StartsWith ("."))
898 ThrowException ("Path cannot begin with '.'", reader);
900 if (path.IndexOfAny (forbiddenPathChars) != -1)
901 ThrowException ("Path cannot contain " + forbiddenStr, reader);
906 if (att == "allowOverride") {
908 ThrowException ("Duplicate allowOverride attribute", reader);
911 allowOverride = (reader.Value == "true");
912 if (!allowOverride && reader.Value != "false")
913 ThrowException ("allowOverride must be either true or false", reader);
917 ThrowException ("Unrecognized attribute.", reader);
921 return; // empty location tag
923 Location loc = new Location (this, path, allowOverride);
924 if (locations == null)
925 locations = new Hashtable ();
926 else if (locations.ContainsKey (loc.Path))
927 ThrowException ("Duplicated location path: " + loc.Path, reader);
929 reader.MoveToElement ();
930 loc.LoadFromString (reader.ReadOuterXml ());
931 locations [loc.Path] = loc;
932 if (!loc.AllowOverride) {
933 XmlTextReader inner = loc.GetReader ();
935 MoveToNextElement (inner);
936 ReadConfig (loc.GetReader (), true);
943 void StorePending (string name, XmlTextReader reader)
946 pending = new Hashtable ();
948 if (pending.ContainsKey (name))
949 ThrowException ("Sections can only appear once: " + name, reader);
951 pending [name] = reader.ReadOuterXml ();
954 void ReadConfig (XmlTextReader reader, bool isLocation)
956 int depth = reader.Depth;
957 while (!reader.EOF && reader.Depth == depth) {
958 string name = reader.Name;
960 if (name == "configSections") {
962 ThrowException ("<configSections> inside <location>", reader);
964 if (reader.HasAttributes)
965 ThrowException ("Unrecognized attribute in <configSections>.", reader);
967 MoveToNextElement (reader);
968 if (reader.Depth > depth)
969 ReadSections (reader, null);
970 } else if (name == "location") {
972 ThrowException ("<location> inside <location>", reader);
974 StoreLocation (name, reader);
975 MoveToNextElement (reader);
976 } else if (name != null && name != ""){
977 StorePending (name, reader);
978 MoveToNextElement (reader);
980 MoveToNextElement (reader);
985 void ThrowException (string text, XmlTextReader reader)
987 throw new ConfigurationException (text, fileName, reader.LineNumber);
995 ConfigurationData parent;
996 ConfigurationData thisOne;
999 public Location (ConfigurationData parent, string path, bool allowOverride)
1001 this.parent = parent;
1002 this.allowOverride = allowOverride;
1003 this.path = (path == null || path == "") ? "*" : path;
1006 public bool AllowOverride {
1007 get { return (path != "*" || allowOverride); }
1010 public string Path {
1011 get { return path; }
1014 public string XmlStr {
1015 set { xmlstr = value; }
1018 public void LoadFromString (string str)
1021 throw new ArgumentNullException ("str");
1023 if (thisOne != null)
1024 throw new InvalidOperationException ();
1026 this.xmlstr = str.Trim ();
1030 XmlTextReader reader = GetReader ();
1031 thisOne = new ConfigurationData (parent, parent.FileName);
1032 thisOne.LoadFromReader (reader, parent.FileName, true);
1035 public XmlTextReader GetReader ()
1040 XmlTextReader reader = new XmlTextReader (new StringReader (xmlstr));
1041 reader.ReadStartElement ("location");
1045 public ConfigurationData Config {
1046 get { return thisOne; }