// // System.Web.Configuration.WebConfigurationManager.cs // // Authors: // Lluis Sanchez Gual (lluis@novell.com) // Chris Toshok (toshok@ximian.com) // Marek Habersack // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // // Copyright (C) 2005-2009 Novell, Inc (http://www.novell.com) // using System; using System.IO; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Reflection; using Mono.Web.Util; using System.Xml; using System.Configuration; using System.Configuration.Internal; using _Configuration = System.Configuration.Configuration; using System.Web.Util; using System.Threading; using System.Web.Hosting; namespace System.Web.Configuration { public static class WebConfigurationManager { sealed class ConfigPath { public string Path; public bool InAnotherApp; public ConfigPath (string path, bool inAnotherApp) { this.Path = path; this.InAnotherApp = inAnotherApp; } } const int SAVE_LOCATIONS_CHECK_INTERVAL = 6000; // milliseconds const int SECTION_CACHE_LOCK_TIMEOUT = 200; // milliseconds static readonly char[] pathTrimChars = { '/' }; static readonly object suppressAppReloadLock = new object (); static readonly object saveLocationsCacheLock = new object (); static readonly object getSectionLock = new object (); // See comment for the cacheLock field at top of System.Web.Caching/Cache.cs static readonly ReaderWriterLockSlim sectionCacheLock; static IInternalConfigConfigurationFactory configFactory; static Hashtable configurations = Hashtable.Synchronized (new Hashtable ()); static Hashtable configPaths = Hashtable.Synchronized (new Hashtable ()); static bool suppressAppReload; static Dictionary saveLocationsCache; static Timer saveLocationsTimer; static ArrayList extra_assemblies = null; static internal ArrayList ExtraAssemblies { get { if (extra_assemblies == null) extra_assemblies = new ArrayList(); return extra_assemblies; } } const int DEFAULT_SECTION_CACHE_SIZE = 100; const string CACHE_SIZE_OVERRIDING_KEY = "MONO_ASPNET_WEBCONFIG_CACHESIZE"; static LruCache sectionCache; static WebConfigurationManager () { var section_cache_size = DEFAULT_SECTION_CACHE_SIZE; int section_cache_size_override; bool size_overriden = false; if (int.TryParse (Environment.GetEnvironmentVariable (CACHE_SIZE_OVERRIDING_KEY), out section_cache_size_override)) { section_cache_size = section_cache_size_override; size_overriden = true; Console.WriteLine ("WebConfigurationManager's LRUcache Size overriden to: {0} (via {1})", section_cache_size_override, CACHE_SIZE_OVERRIDING_KEY); } sectionCache = new LruCache (section_cache_size); string eviction_warning = "WebConfigurationManager's LRUcache evictions count reached its max size"; if (!size_overriden) eviction_warning += String.Format ("{0}Cache Size: {1} (overridable via {2})", Environment.NewLine, section_cache_size, CACHE_SIZE_OVERRIDING_KEY); sectionCache.EvictionWarning = eviction_warning; configFactory = ConfigurationManager.ConfigurationFactory; _Configuration.SaveStart += ConfigurationSaveHandler; _Configuration.SaveEnd += ConfigurationSaveHandler; // Part of fix for bug #491531 Type type = Type.GetType ("System.Configuration.CustomizableFileSettingsProvider, System", false); if (type != null) { FieldInfo fi = type.GetField ("webConfigurationFileMapType", BindingFlags.Static | BindingFlags.NonPublic); if (fi != null && fi.FieldType == Type.GetType ("System.Type")) fi.SetValue (null, typeof (ApplicationSettingsConfigurationFileMap)); } sectionCacheLock = new ReaderWriterLockSlim (); } static void ReenableWatcherOnConfigLocation (object state) { string path = state as string; if (String.IsNullOrEmpty (path)) return; DateTime lastWrite; lock (saveLocationsCacheLock) { if (!saveLocationsCache.TryGetValue (path, out lastWrite)) lastWrite = DateTime.MinValue; } DateTime now = DateTime.Now; if (lastWrite == DateTime.MinValue || now.Subtract (lastWrite).TotalMilliseconds >= SAVE_LOCATIONS_CHECK_INTERVAL) { saveLocationsTimer.Dispose (); saveLocationsTimer = null; HttpApplicationFactory.EnableWatcher (VirtualPathUtility.RemoveTrailingSlash (HttpRuntime.AppDomainAppPath), "?eb.?onfig"); } else saveLocationsTimer.Change (SAVE_LOCATIONS_CHECK_INTERVAL, SAVE_LOCATIONS_CHECK_INTERVAL); } static void ConfigurationSaveHandler (_Configuration sender, ConfigurationSaveEventArgs args) { try { sectionCacheLock.EnterWriteLock (); sectionCache.Clear (); } finally { sectionCacheLock.ExitWriteLock (); } lock (suppressAppReloadLock) { string rootConfigPath = WebConfigurationHost.GetWebConfigFileName (HttpRuntime.AppDomainAppPath); if (String.Compare (args.StreamPath, rootConfigPath, StringComparison.OrdinalIgnoreCase) == 0) { SuppressAppReload (args.Start); if (args.Start) { HttpApplicationFactory.DisableWatcher (VirtualPathUtility.RemoveTrailingSlash (HttpRuntime.AppDomainAppPath), "?eb.?onfig"); lock (saveLocationsCacheLock) { if (saveLocationsCache == null) saveLocationsCache = new Dictionary (StringComparer.Ordinal); if (saveLocationsCache.ContainsKey (rootConfigPath)) saveLocationsCache [rootConfigPath] = DateTime.Now; else saveLocationsCache.Add (rootConfigPath, DateTime.Now); if (saveLocationsTimer == null) saveLocationsTimer = new Timer (ReenableWatcherOnConfigLocation, rootConfigPath, SAVE_LOCATIONS_CHECK_INTERVAL, SAVE_LOCATIONS_CHECK_INTERVAL); } } } } } public static _Configuration OpenMachineConfiguration () { return ConfigurationManager.OpenMachineConfiguration (); } [MonoLimitation ("locationSubPath is not handled")] public static _Configuration OpenMachineConfiguration (string locationSubPath) { return OpenMachineConfiguration (); } [MonoLimitation("Mono does not support remote configuration")] public static _Configuration OpenMachineConfiguration (string locationSubPath, string server) { if (server == null) return OpenMachineConfiguration (locationSubPath); throw new NotSupportedException ("Mono doesn't support remote configuration"); } [MonoLimitation("Mono does not support remote configuration")] public static _Configuration OpenMachineConfiguration (string locationSubPath, string server, IntPtr userToken) { if (server == null) return OpenMachineConfiguration (locationSubPath); throw new NotSupportedException ("Mono doesn't support remote configuration"); } [MonoLimitation("Mono does not support remote configuration")] public static _Configuration OpenMachineConfiguration (string locationSubPath, string server, string userName, string password) { if (server == null) return OpenMachineConfiguration (locationSubPath); throw new NotSupportedException ("Mono doesn't support remote configuration"); } public static _Configuration OpenWebConfiguration (string path) { return OpenWebConfiguration (path, null, null, null, null, null); } public static _Configuration OpenWebConfiguration (string path, string site) { return OpenWebConfiguration (path, site, null, null, null, null); } public static _Configuration OpenWebConfiguration (string path, string site, string locationSubPath) { return OpenWebConfiguration (path, site, locationSubPath, null, null, null); } public static _Configuration OpenWebConfiguration (string path, string site, string locationSubPath, string server) { return OpenWebConfiguration (path, site, locationSubPath, server, null, null); } public static _Configuration OpenWebConfiguration (string path, string site, string locationSubPath, string server, IntPtr userToken) { return OpenWebConfiguration (path, site, locationSubPath, server, null, null); } public static _Configuration OpenWebConfiguration (string path, string site, string locationSubPath, string server, string userName, string password) { return OpenWebConfiguration (path, site, locationSubPath, server, null, null, false); } static _Configuration OpenWebConfiguration (string path, string site, string locationSubPath, string server, string userName, string password, bool fweb) { if (String.IsNullOrEmpty (path)) path = "/"; bool inAnotherApp = false; if (!fweb && !String.IsNullOrEmpty (path)) path = FindWebConfig (path, out inAnotherApp); string confKey = path + site + locationSubPath + server + userName + password; _Configuration conf = null; conf = (_Configuration) configurations [confKey]; if (conf == null) { conf = ConfigurationFactory.Create (typeof (WebConfigurationHost), null, path, site, locationSubPath, server, userName, password, inAnotherApp); configurations [confKey] = conf; } return conf; } public static _Configuration OpenMappedWebConfiguration (WebConfigurationFileMap fileMap, string path) { return ConfigurationFactory.Create (typeof(WebConfigurationHost), fileMap, path); } public static _Configuration OpenMappedWebConfiguration (WebConfigurationFileMap fileMap, string path, string site) { return ConfigurationFactory.Create (typeof(WebConfigurationHost), fileMap, path, site); } public static _Configuration OpenMappedWebConfiguration (WebConfigurationFileMap fileMap, string path, string site, string locationSubPath) { return ConfigurationFactory.Create (typeof(WebConfigurationHost), fileMap, path, site, locationSubPath); } public static _Configuration OpenMappedMachineConfiguration (ConfigurationFileMap fileMap) { return ConfigurationFactory.Create (typeof(WebConfigurationHost), fileMap); } public static _Configuration OpenMappedMachineConfiguration (ConfigurationFileMap fileMap, string locationSubPath) { return OpenMappedMachineConfiguration (fileMap); } internal static object SafeGetSection (string sectionName, Type configSectionType) { try { return GetSection (sectionName); } catch (Exception) { if (configSectionType != null) return Activator.CreateInstance (configSectionType); return null; } } internal static object SafeGetSection (string sectionName, string path, Type configSectionType) { try { return GetSection (sectionName, path); } catch (Exception) { if (configSectionType != null) return Activator.CreateInstance (configSectionType); return null; } } public static object GetSection (string sectionName) { HttpContext context = HttpContext.Current; return GetSection (sectionName, GetCurrentPath (context), context); } public static object GetSection (string sectionName, string path) { return GetSection (sectionName, path, HttpContext.Current); } static bool LookUpLocation (string relativePath, ref _Configuration defaultConfiguration) { if (String.IsNullOrEmpty (relativePath)) return false; _Configuration cnew = defaultConfiguration.FindLocationConfiguration (relativePath, defaultConfiguration); if (cnew == defaultConfiguration) return false; defaultConfiguration = cnew; return true; } internal static object GetSection (string sectionName, string path, HttpContext context) { if (String.IsNullOrEmpty (sectionName)) return null; _Configuration c = OpenWebConfiguration (path, null, null, null, null, null, false); string configPath = c.ConfigPath; int baseCacheKey = 0; int cacheKey; bool pathPresent = !String.IsNullOrEmpty (path); string locationPath = null; if (pathPresent) locationPath = "location_" + path; baseCacheKey = sectionName.GetHashCode (); if (configPath != null) baseCacheKey ^= configPath.GetHashCode (); try { sectionCacheLock.EnterWriteLock (); object o; if (pathPresent) { cacheKey = baseCacheKey ^ locationPath.GetHashCode (); if (sectionCache.TryGetValue (cacheKey, out o)) return o; cacheKey = baseCacheKey ^ path.GetHashCode (); if (sectionCache.TryGetValue (cacheKey, out o)) return o; } if (sectionCache.TryGetValue (baseCacheKey, out o)) return o; } finally { sectionCacheLock.ExitWriteLock (); } string cachePath = null; if (pathPresent) { string relPath; if (VirtualPathUtility.IsRooted (path)) { if (path [0] == '~') relPath = path.Length > 1 ? path.Substring (2) : String.Empty; else if (path [0] == '/') relPath = path.Substring (1); else relPath = path; } else relPath = path; HttpRequest req = context != null ? context.Request : null; if (req != null) { string vdir = VirtualPathUtility.GetDirectory (req.PathNoValidation); if (vdir != null) { vdir = vdir.TrimEnd (pathTrimChars); if (String.Compare (c.ConfigPath, vdir, StringComparison.Ordinal) != 0 && LookUpLocation (vdir.Trim (pathTrimChars), ref c)) cachePath = path; } } if (LookUpLocation (relPath, ref c)) cachePath = locationPath; else cachePath = path; } ConfigurationSection section; lock (getSectionLock) { section = c.GetSection (sectionName); } if (section == null) return null; object value = SettingsMappingManager.MapSection (section.GetRuntimeObject ()); if (cachePath != null) cacheKey = baseCacheKey ^ cachePath.GetHashCode (); else cacheKey = baseCacheKey; AddSectionToCache (cacheKey, value); return value; } static string MapPath (HttpRequest req, string virtualPath) { if (req != null) return req.MapPath (virtualPath); string appRoot = HttpRuntime.AppDomainAppVirtualPath; if (!String.IsNullOrEmpty (appRoot) && virtualPath.StartsWith (appRoot, StringComparison.Ordinal)) { if (String.Compare (virtualPath, appRoot, StringComparison.Ordinal) == 0) return HttpRuntime.AppDomainAppPath; return UrlUtils.Combine (HttpRuntime.AppDomainAppPath, virtualPath.Substring (appRoot.Length)); } return null; } static string GetParentDir (string rootPath, string curPath) { int len = curPath.Length - 1; if (len > 0 && curPath [len] == '/') curPath = curPath.Substring (0, len); if (String.Compare (curPath, rootPath, StringComparison.Ordinal) == 0) return null; int idx = curPath.LastIndexOf ('/'); if (idx == -1) return curPath; if (idx == 0) return "/"; return curPath.Substring (0, idx); } internal static string FindWebConfig (string path) { bool dummy; return FindWebConfig (path, out dummy); } internal static string FindWebConfig (string path, out bool inAnotherApp) { inAnotherApp = false; if (String.IsNullOrEmpty (path)) return path; if (HostingEnvironment.VirtualPathProvider != null) { if (HostingEnvironment.VirtualPathProvider.DirectoryExists (path)) path = VirtualPathUtility.AppendTrailingSlash (path); } string rootPath = HttpRuntime.AppDomainAppVirtualPath; ConfigPath curPath; curPath = configPaths [path] as ConfigPath; if (curPath != null) { inAnotherApp = curPath.InAnotherApp; return curPath.Path; } HttpContext ctx = HttpContext.Current; HttpRequest req = ctx != null ? ctx.Request : null; string physPath = req != null ? VirtualPathUtility.AppendTrailingSlash (MapPath (req, path)) : null; string appDomainPath = HttpRuntime.AppDomainAppPath; if (physPath != null && appDomainPath != null && !physPath.StartsWith (appDomainPath, StringComparison.Ordinal)) inAnotherApp = true; string dir; if (inAnotherApp || path [path.Length - 1] == '/') dir = path; else { dir = VirtualPathUtility.GetDirectory (path, false); if (dir == null) return path; } curPath = configPaths [dir] as ConfigPath; if (curPath != null) { inAnotherApp = curPath.InAnotherApp; return curPath.Path; } if (req == null) return path; curPath = new ConfigPath (path, inAnotherApp); while (String.Compare (curPath.Path, rootPath, StringComparison.Ordinal) != 0) { physPath = MapPath (req, curPath.Path); if (physPath == null) { curPath.Path = rootPath; break; } if (WebConfigurationHost.GetWebConfigFileName (physPath) != null) break; curPath.Path = GetParentDir (rootPath, curPath.Path); if (curPath.Path == null || curPath.Path == "~") { curPath.Path = rootPath; break; } } if (String.Compare (curPath.Path, path, StringComparison.Ordinal) != 0) configPaths [path] = curPath; else configPaths [dir] = curPath; return curPath.Path; } static string GetCurrentPath (HttpContext ctx) { HttpRequest req = ctx != null ? ctx.Request : null; return req != null ? req.PathNoValidation : HttpRuntime.AppDomainAppVirtualPath; } internal static bool SuppressAppReload (bool newValue) { bool ret; lock (suppressAppReloadLock) { ret = suppressAppReload; suppressAppReload = newValue; } return ret; } internal static void RemoveConfigurationFromCache (HttpContext ctx) { configurations.Remove (GetCurrentPath (ctx)); } public static object GetWebApplicationSection (string sectionName) { HttpContext ctx = HttpContext.Current; HttpRequest req = ctx != null ? ctx.Request : null; string applicationPath = req != null ? req.ApplicationPath : null; return GetSection (sectionName, String.IsNullOrEmpty (applicationPath) ? String.Empty : applicationPath); } public static NameValueCollection AppSettings { get { return ConfigurationManager.AppSettings; } } public static ConnectionStringSettingsCollection ConnectionStrings { get { return ConfigurationManager.ConnectionStrings; } } internal static IInternalConfigConfigurationFactory ConfigurationFactory { get { return configFactory; } } static void AddSectionToCache (int key, object section) { object cachedSection; bool locked = false; try { if (!sectionCacheLock.TryEnterWriteLock (SECTION_CACHE_LOCK_TIMEOUT)) return; locked = true; if (sectionCache.TryGetValue (key, out cachedSection) && cachedSection != null) return; sectionCache.Add (key, section); } finally { if (locked) { sectionCacheLock.ExitWriteLock (); } } } #region stuff copied from WebConfigurationSettings static internal IConfigurationSystem oldConfig; static Web20DefaultConfig config; //static IInternalConfigSystem configSystem; const BindingFlags privStatic = BindingFlags.NonPublic | BindingFlags.Static; static readonly object lockobj = new object (); internal static void Init () { lock (lockobj) { if (config != null) return; /* deal with the ConfigurationSettings stuff */ { Web20DefaultConfig settings = Web20DefaultConfig.GetInstance (); Type t = typeof (ConfigurationSettings); MethodInfo changeConfig = t.GetMethod ("ChangeConfigurationSystem", privStatic); if (changeConfig == null) throw new ConfigurationException ("Cannot find method CCS"); object [] args = new object [] {settings}; oldConfig = (IConfigurationSystem)changeConfig.Invoke (null, args); config = settings; config.Init (); } /* deal with the ConfigurationManager stuff */ { HttpConfigurationSystem system = new HttpConfigurationSystem (); Type t = typeof (ConfigurationManager); MethodInfo changeConfig = t.GetMethod ("ChangeConfigurationSystem", privStatic); if (changeConfig == null) throw new ConfigurationException ("Cannot find method CCS"); object [] args = new object [] {system}; changeConfig.Invoke (null, args); //configSystem = system; } } } } class Web20DefaultConfig : IConfigurationSystem { static Web20DefaultConfig instance; static Web20DefaultConfig () { instance = new Web20DefaultConfig (); } public static Web20DefaultConfig GetInstance () { return instance; } public object GetConfig (string sectionName) { object o = WebConfigurationManager.GetWebApplicationSection (sectionName); if (o == null || o is IgnoreSection) { /* this can happen when the section * handler doesn't subclass from * ConfigurationSection. let's be * nice and try to load it using the * 1.x style routines in case there's * a 1.x section handler registered * for it. */ object o1 = WebConfigurationManager.oldConfig.GetConfig (sectionName); if (o1 != null) return o1; } return o; } public void Init () { // nothing. We need a context. } } #endregion }