2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[mono.git] / mcs / class / System.Web / System.Web.Configuration_2.0 / WebConfigurationManager.cs
1 //
2 // System.Web.Configuration.WebConfigurationManager.cs
3 //
4 // Authors:
5 //      Lluis Sanchez Gual (lluis@novell.com)
6 //      Chris Toshok (toshok@ximian.com)
7 //
8 // Permission is hereby granted, free of charge, to any person obtaining
9 // a copy of this software and associated documentation files (the
10 // "Software"), to deal in the Software without restriction, including
11 // without limitation the rights to use, copy, modify, merge, publish,
12 // distribute, sublicense, and/or sell copies of the Software, and to
13 // permit persons to whom the Software is furnished to do so, subject to
14 // the following conditions:
15 // 
16 // The above copyright notice and this permission notice shall be
17 // included in all copies or substantial portions of the Software.
18 // 
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 //
27 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
28 //
29
30 #if NET_2_0
31
32 using System;
33 using System.IO;
34 using System.Collections;
35 using System.Collections.Specialized;
36 using System.Reflection;
37 #if MONOWEB_DEP
38 using Mono.Web.Util;
39 #endif
40 using System.Xml;
41 using System.Configuration;
42 using System.Configuration.Internal;
43 using _Configuration = System.Configuration.Configuration;
44 using System.Web.Util;
45
46 namespace System.Web.Configuration {
47
48         public static class WebConfigurationManager
49         {
50 #if !TARGET_J2EE
51                 static IInternalConfigConfigurationFactory configFactory;
52                 static Hashtable configurations = Hashtable.Synchronized (new Hashtable ());
53                 static Hashtable sectionCache = new Hashtable ();
54                 static Hashtable configPaths = Hashtable.Synchronized (new Hashtable ());
55 #else
56                 const string AppSettingsKey = "WebConfigurationManager.AppSettings";
57                 static internal IInternalConfigConfigurationFactory configFactory
58                 {
59                         get{
60                                 IInternalConfigConfigurationFactory factory = (IInternalConfigConfigurationFactory)AppDomain.CurrentDomain.GetData("WebConfigurationManager.configFactory");
61                                 if (factory == null){
62                                         lock (AppDomain.CurrentDomain){
63                                                 object initialized = AppDomain.CurrentDomain.GetData("WebConfigurationManager.configFactory.initialized");
64                                                 if (initialized == null){
65                                                         PropertyInfo prop = typeof(ConfigurationManager).GetProperty("ConfigurationFactory", BindingFlags.Static | BindingFlags.NonPublic);
66                                                         if (prop != null){
67                                                                 factory = prop.GetValue(null, null) as IInternalConfigConfigurationFactory;
68                                                                 configFactory = factory;
69                                                         }
70                                                 }
71                                         }
72                                 }
73                                 return factory != null ? factory : configFactory;
74                         }
75                         set{
76                                 AppDomain.CurrentDomain.SetData("WebConfigurationManager.configFactory", value);
77                                 AppDomain.CurrentDomain.SetData("WebConfigurationManager.configFactory.initialized", true);
78                         }
79                 }
80
81                 static internal Hashtable configurations
82                 {
83                         get{
84                                 Hashtable table = (Hashtable)AppDomain.CurrentDomain.GetData("WebConfigurationManager.configurations");
85                                 if (table == null){
86                                         lock (AppDomain.CurrentDomain){
87                                                 object initialized = AppDomain.CurrentDomain.GetData("WebConfigurationManager.configurations.initialized");
88                                                 if (initialized == null){
89                                                         table = Hashtable.Synchronized (new Hashtable (StringComparer.OrdinalIgnoreCase));
90                                                         configurations = table;
91                                                 }
92                                         }
93                                 }
94                                 return table != null ? table : configurations;
95
96                         }
97                         set{
98                                 AppDomain.CurrentDomain.SetData("WebConfigurationManager.configurations", value);
99                                 AppDomain.CurrentDomain.SetData("WebConfigurationManager.configurations.initialized", true);
100                         }
101                 }
102
103                 static Hashtable sectionCache
104                 {
105                         get
106                         {
107                                 Hashtable sectionCache = (Hashtable) AppDomain.CurrentDomain.GetData ("sectionCache");
108                                 if (sectionCache == null) {
109                                         sectionCache = new Hashtable (StringComparer.OrdinalIgnoreCase);
110                                         AppDomain.CurrentDomain.SetData ("sectionCache", sectionCache);
111                                 }
112                                 return sectionCache;
113                         }
114                         set
115                         {
116                                 AppDomain.CurrentDomain.SetData ("sectionCache", value);
117                         }
118                 }
119
120                 static internal Hashtable configPaths
121                 {
122                         get{
123                                 Hashtable table = (Hashtable)AppDomain.CurrentDomain.GetData("WebConfigurationManager.configPaths");
124                                 if (table == null){
125                                         lock (AppDomain.CurrentDomain){
126                                                 object initialized = AppDomain.CurrentDomain.GetData("WebConfigurationManager.configPaths.initialized");
127                                                 if (initialized == null){
128                                                         table = Hashtable.Synchronized (new Hashtable (StringComparer.OrdinalIgnoreCase));
129                                                         configPaths = table;
130                                                 }
131                                         }
132                                 }
133                                 return table != null ? table : configPaths;
134
135                         }
136                         set{
137                                 AppDomain.CurrentDomain.SetData("WebConfigurationManager.configPaths", value);
138                                 AppDomain.CurrentDomain.SetData("WebConfigurationManager.configPaths.initialized", true);
139                         }
140                 }
141 #endif
142
143                 static ArrayList extra_assemblies = null;
144                 static internal ArrayList ExtraAssemblies {
145                         get {
146                                 if (extra_assemblies == null)
147                                         extra_assemblies = new ArrayList();
148                                 return extra_assemblies;
149                         }
150                 }
151
152                 static bool hasConfigErrors = false;
153                 static object hasConfigErrorsLock = new object ();
154                 static internal bool HasConfigErrors {
155                         get {
156                                 lock (hasConfigErrorsLock) {
157                                         return hasConfigErrors;
158                                 }
159                         }
160                 }
161                 
162                 static WebConfigurationManager ()
163                 {
164                         PropertyInfo prop = typeof(ConfigurationManager).GetProperty ("ConfigurationFactory", BindingFlags.Static | BindingFlags.NonPublic);
165                         if (prop != null)
166                                 configFactory = prop.GetValue (null, null) as IInternalConfigConfigurationFactory;
167
168                         // Part of fix for bug #491531
169                         Type type = Type.GetType ("System.Configuration.CustomizableFileSettingsProvider, System", false);
170                         if (type != null) {
171                                 FieldInfo fi = type.GetField ("webConfigurationFileMapType", BindingFlags.Static | BindingFlags.NonPublic);
172                                 if (fi != null && fi.FieldType == Type.GetType ("System.Type"))
173                                         fi.SetValue (null, typeof (ApplicationSettingsConfigurationFileMap));
174                         }
175                 }
176
177                 public static _Configuration OpenMachineConfiguration ()
178                 {
179                         return ConfigurationManager.OpenMachineConfiguration ();
180                 }
181                 
182                 [MonoLimitation ("locationSubPath is not handled")]
183                 public static _Configuration OpenMachineConfiguration (string locationSubPath)
184                 {
185                         return OpenMachineConfiguration ();
186                 }
187
188                 [MonoLimitation("Mono does not support remote configuration")]
189                 public static _Configuration OpenMachineConfiguration (string locationSubPath,
190                                                                        string server)
191                 {
192                         if (server == null)
193                                 return OpenMachineConfiguration (locationSubPath);
194
195                         throw new NotSupportedException ("Mono doesn't support remote configuration");
196                 }
197
198                 [MonoLimitation("Mono does not support remote configuration")]
199                 public static _Configuration OpenMachineConfiguration (string locationSubPath,
200                                                                        string server,
201                                                                        IntPtr userToken)
202                 {
203                         if (server == null)
204                                 return OpenMachineConfiguration (locationSubPath);
205                         throw new NotSupportedException ("Mono doesn't support remote configuration");
206                 }
207
208                 [MonoLimitation("Mono does not support remote configuration")]
209                 public static _Configuration OpenMachineConfiguration (string locationSubPath,
210                                                                        string server,
211                                                                        string userName,
212                                                                        string password)
213                 {
214                         if (server == null)
215                                 return OpenMachineConfiguration (locationSubPath);
216                         throw new NotSupportedException ("Mono doesn't support remote configuration");
217                 }
218
219                 public static _Configuration OpenWebConfiguration (string path)
220                 {
221                         return OpenWebConfiguration (path, null, null, null, null, null);
222                 }
223                 
224                 public static _Configuration OpenWebConfiguration (string path, string site)
225                 {
226                         return OpenWebConfiguration (path, site, null, null, null, null);
227                 }
228                 
229                 public static _Configuration OpenWebConfiguration (string path, string site, string locationSubPath)
230                 {
231                         return OpenWebConfiguration (path, site, locationSubPath, null, null, null);
232                 }
233
234                 public static _Configuration OpenWebConfiguration (string path, string site, string locationSubPath, string server)
235                 {
236                         return OpenWebConfiguration (path, site, locationSubPath, server, null, null);
237                 }
238
239                 public static _Configuration OpenWebConfiguration (string path, string site, string locationSubPath, string server, IntPtr userToken)
240                 {
241                         return OpenWebConfiguration (path, site, locationSubPath, server, null, null);
242                 }
243                 
244                 public static _Configuration OpenWebConfiguration (string path, string site, string locationSubPath, string server, string userName, string password)
245                 {
246                         return OpenWebConfiguration (path, site, locationSubPath, server, null, null, false);
247                 }
248
249                 static _Configuration OpenWebConfiguration (string path, string site, string locationSubPath, string server, string userName, string password, bool fweb)
250                 {
251                         if (String.IsNullOrEmpty (path))
252                                 path = "/";
253
254                         if (!fweb && !String.IsNullOrEmpty (path))
255                                 path = FindWebConfig (path);
256
257                         string confKey = path + site + locationSubPath + server + userName + password;
258                         _Configuration conf = null;
259                         conf = (_Configuration) configurations [confKey];
260                         if (conf == null) {
261                                 try {
262                                         conf = ConfigurationFactory.Create (typeof (WebConfigurationHost), null, path, site, locationSubPath, server, userName, password);
263                                         configurations [confKey] = conf;
264                                 } catch (Exception ex) {
265                                         lock (hasConfigErrorsLock) {
266                                                 hasConfigErrors = true;
267                                         }
268                                         throw ex;
269                                 }
270                         }
271                         return conf;
272                 }
273
274                 public static _Configuration OpenMappedWebConfiguration (WebConfigurationFileMap fileMap, string path)
275                 {
276                         return ConfigurationFactory.Create (typeof(WebConfigurationHost), fileMap, path);
277                 }
278                 
279                 public static _Configuration OpenMappedWebConfiguration (WebConfigurationFileMap fileMap, string path, string site)
280                 {
281                         return ConfigurationFactory.Create (typeof(WebConfigurationHost), fileMap, path, site);
282                 }
283                 
284                 public static _Configuration OpenMappedWebConfiguration (WebConfigurationFileMap fileMap, string path, string site, string locationSubPath)
285                 {
286                         return ConfigurationFactory.Create (typeof(WebConfigurationHost), fileMap, path, site, locationSubPath);
287                 }
288                 
289                 public static _Configuration OpenMappedMachineConfiguration (ConfigurationFileMap fileMap)
290                 {
291                         return ConfigurationFactory.Create (typeof(WebConfigurationHost), fileMap);
292                 }
293
294                 public static _Configuration OpenMappedMachineConfiguration (ConfigurationFileMap fileMap,
295                                                                              string locationSubPath)
296                 {
297                         return OpenMappedMachineConfiguration (fileMap);
298                 }
299
300                 internal static object SafeGetSection (string sectionName, Type configSectionType)
301                 {
302                         try {
303                                 return GetSection (sectionName);
304                         } catch (Exception) {
305                                 if (configSectionType != null)
306                                         return Activator.CreateInstance (configSectionType);
307                                 return null;
308                         }
309                 }
310                 
311                 internal static object SafeGetSection (string sectionName, string path, Type configSectionType)
312                 {
313                         try {
314                                 return GetSection (sectionName, path);
315                         } catch (Exception) {
316                                 if (configSectionType != null)
317                                         return Activator.CreateInstance (configSectionType);
318                                 return null;
319                         }
320                 }
321                 
322                 public static object GetSection (string sectionName)
323                 {
324                         HttpContext context = HttpContext.Current;
325                         return GetSection (sectionName, GetCurrentPath (context), context);
326                 }
327
328                 public static object GetSection (string sectionName, string path)
329                 {
330                         return GetSection (sectionName, path, HttpContext.Current);
331                 }
332
333                 internal static object GetSection (string sectionName, string path, HttpContext context)
334                 {
335                         string config_vdir = FindWebConfig (path);
336                         if (String.IsNullOrEmpty (config_vdir))
337                                 config_vdir = "/";
338
339                         object cachedSection = sectionCache [GetSectionCacheKey (sectionName, config_vdir)];
340                         if (cachedSection != null)
341                                 return cachedSection;
342
343                         HttpRequest req = context != null ? context.Request : null;
344                         _Configuration c = OpenWebConfiguration (config_vdir, //path, /* path */
345                                                                  null, /* site */
346                                                                  req != null ? VirtualPathUtility.GetDirectory (req.Path) : null, /* locationSubPath */
347                                                                  null, /* server */
348                                                                  null, /* userName */
349                                                                  null, /* password */
350                                                                  true  /* path from FindWebConfig */);
351                         ConfigurationSection section = c.GetSection (sectionName);
352                         if (section == null)
353                                 return null;
354
355 #if TARGET_J2EE
356                         object value = get_runtime_object.Invoke (section, new object [0]);
357                         if (String.CompareOrdinal ("appSettings", sectionName) == 0) {
358                                 NameValueCollection collection;
359                                 collection = new KeyValueMergedCollection (HttpContext.Current, (NameValueCollection) value);
360                                 value = collection;
361                         }
362
363                         AddSectionToCache (GetSectionCacheKey (sectionName, config_vdir), value);
364                         return value;
365 #else
366 #if MONOWEB_DEP
367                         object value = SettingsMappingManager.MapSection (get_runtime_object.Invoke (section, new object [0]));
368 #else
369                         object value = null;
370 #endif
371                         AddSectionToCache (GetSectionCacheKey (sectionName, config_vdir), value);
372                         return value;
373 #endif
374                 }
375                 
376                 static string MapPath (HttpRequest req, string virtualPath)
377                 {
378                         if (req != null)
379                                 return req.MapPath (virtualPath);
380
381                         string appRoot = HttpRuntime.AppDomainAppVirtualPath;
382                         if (!String.IsNullOrEmpty (appRoot) && virtualPath.StartsWith (appRoot, StringComparison.Ordinal)) {
383                                 if (String.Compare (virtualPath, appRoot, StringComparison.Ordinal) == 0)
384                                         return HttpRuntime.AppDomainAppPath;
385                                 return UrlUtils.Combine (HttpRuntime.AppDomainAppPath, virtualPath.Substring (appRoot.Length));
386                         }
387                         
388                         return null;
389                 }
390
391                 static string GetParentDir (string rootPath, string curPath)
392                 {
393                         int len = curPath.Length - 1;
394                         if (len > 0 && curPath [len] == '/')
395                                 curPath = curPath.Substring (0, len);
396
397                         if (String.Compare (curPath, rootPath, StringComparison.Ordinal) == 0)
398                                 return null;
399                         
400                         int idx = curPath.LastIndexOf ('/');
401                         if (idx == -1)
402                                 return curPath;
403
404                         if (idx == 0)
405                                 return "/";
406                         
407                         return curPath.Substring (0, idx);
408                 }
409                 
410                 internal static string FindWebConfig (string path)
411                 {
412                         if (String.IsNullOrEmpty (path))
413                                 return path;
414
415                         string dir;
416                         if (path [path.Length - 1] == '/')
417                                 dir = path;
418                         else {
419                                 dir = VirtualPathUtility.GetDirectory (path, false);
420                                 if (dir == null)
421                                         return path;
422                         }
423                         
424                         string curPath = configPaths [dir] as string;
425                         if (curPath != null)
426                                 return curPath;
427                         
428                         HttpContext ctx = HttpContext.Current;
429                         HttpRequest req = ctx != null ? ctx.Request : null;
430                         if (req == null)
431                                 return path;
432
433                         curPath = path;
434                         string rootPath = HttpRuntime.AppDomainAppVirtualPath;
435                         string physPath;
436
437                         while (String.Compare (curPath, rootPath, StringComparison.Ordinal) != 0) {
438                                 physPath = MapPath (req, curPath);
439                                 if (physPath == null) {
440                                         curPath = rootPath;
441                                         break;
442                                 }
443
444                                 if (WebConfigurationHost.GetWebConfigFileName (physPath) != null)
445                                         break;
446                                 
447                                 curPath = GetParentDir (rootPath, curPath);
448                                 if (curPath == null) {
449                                         curPath = rootPath;
450                                         break;
451                                 }
452                         }
453
454                         configPaths [dir] = curPath;
455                         return curPath;
456                 }
457                 
458                 static string GetCurrentPath (HttpContext ctx)
459                 {
460                         HttpRequest req = ctx != null ? ctx.Request : null;
461                         return req != null ? req.Path : HttpRuntime.AppDomainAppVirtualPath;
462                 }
463
464                 internal static void RemoveConfigurationFromCache (HttpContext ctx)
465                 {
466                         configurations.Remove (GetCurrentPath (ctx));
467                 }
468
469 #if TARGET_J2EE || MONOWEB_DEP
470                 readonly static MethodInfo get_runtime_object = typeof (ConfigurationSection).GetMethod ("GetRuntimeObject", BindingFlags.NonPublic | BindingFlags.Instance);
471 #endif          
472
473                 public static object GetWebApplicationSection (string sectionName)
474                 {
475                         HttpContext ctx = HttpContext.Current;
476                         HttpRequest req = ctx != null ? ctx.Request : null;
477                         string applicationPath = req != null ? req.ApplicationPath : null;
478                         return GetSection (sectionName, String.IsNullOrEmpty (applicationPath) ? String.Empty : applicationPath);
479                 }
480
481                 public static NameValueCollection AppSettings {
482                         get { return ConfigurationManager.AppSettings; }
483                 }
484
485                 public static ConnectionStringSettingsCollection ConnectionStrings {
486                         get { return ConfigurationManager.ConnectionStrings; }
487                 }
488
489                 internal static IInternalConfigConfigurationFactory ConfigurationFactory {
490                         get { return configFactory; }
491                 }
492
493                 static void AddSectionToCache (int key, object section)
494                 {
495                         if (sectionCache [key] != null)
496                                 return;
497
498                         Hashtable tmpTable = (Hashtable) sectionCache.Clone ();
499                         if (tmpTable.Contains (key))
500                                 return;
501
502                         tmpTable.Add (key, section);
503                         sectionCache = tmpTable;
504                 }
505
506                 static int GetSectionCacheKey (string sectionName, string path)
507                 {
508                         return (sectionName != null ? sectionName.GetHashCode () : 0) ^ ((path != null ? path.GetHashCode () : 0) + 37);
509                 }
510
511                 
512 #region stuff copied from WebConfigurationSettings
513 #if TARGET_J2EE
514                 static internal IConfigurationSystem oldConfig {
515                         get {
516                                 return (IConfigurationSystem)AppDomain.CurrentDomain.GetData("WebConfigurationManager.oldConfig");
517                         }
518                         set {
519                                 AppDomain.CurrentDomain.SetData("WebConfigurationManager.oldConfig", value);
520                         }
521                 }
522
523                 static Web20DefaultConfig config {
524                         get {
525                                 return (Web20DefaultConfig) AppDomain.CurrentDomain.GetData ("Web20DefaultConfig.config");
526                         }
527                         set {
528                                 AppDomain.CurrentDomain.SetData ("Web20DefaultConfig.config", value);
529                         }
530                 }
531
532                 static IInternalConfigSystem configSystem {
533                         get {
534                                 return (IInternalConfigSystem) AppDomain.CurrentDomain.GetData ("IInternalConfigSystem.configSystem");
535                         }
536                         set {
537                                 AppDomain.CurrentDomain.SetData ("IInternalConfigSystem.configSystem", value);
538                         }
539                 }
540 #else
541                 static internal IConfigurationSystem oldConfig;
542                 static Web20DefaultConfig config;
543                 //static IInternalConfigSystem configSystem;
544 #endif
545                 const BindingFlags privStatic = BindingFlags.NonPublic | BindingFlags.Static;
546                 static readonly object lockobj = new object ();
547
548                 internal static void Init ()
549                 {
550                         lock (lockobj) {
551                                 if (config != null)
552                                         return;
553
554                                 /* deal with the ConfigurationSettings stuff */
555                                 {
556                                         Web20DefaultConfig settings = Web20DefaultConfig.GetInstance ();
557                                         Type t = typeof (ConfigurationSettings);
558                                         MethodInfo changeConfig = t.GetMethod ("ChangeConfigurationSystem",
559                                                                                privStatic);
560
561                                         if (changeConfig == null)
562                                                 throw new ConfigurationException ("Cannot find method CCS");
563
564                                         object [] args = new object [] {settings};
565                                         oldConfig = (IConfigurationSystem)changeConfig.Invoke (null, args);
566                                         config = settings;
567
568                                         config.Init ();
569                                 }
570
571                                 /* deal with the ConfigurationManager stuff */
572                                 {
573                                         HttpConfigurationSystem system = new HttpConfigurationSystem ();
574                                         Type t = typeof (ConfigurationManager);
575                                         MethodInfo changeConfig = t.GetMethod ("ChangeConfigurationSystem",
576                                                                                privStatic);
577
578                                         if (changeConfig == null)
579                                                 throw new ConfigurationException ("Cannot find method CCS");
580
581                                         object [] args = new object [] {system};
582                                         changeConfig.Invoke (null, args);
583                                         //configSystem = system;
584                                 }
585                         }
586                 }
587         }
588
589         class Web20DefaultConfig : IConfigurationSystem
590         {
591 #if TARGET_J2EE
592                 static Web20DefaultConfig instance {
593                         get {
594                                 Web20DefaultConfig val = (Web20DefaultConfig)AppDomain.CurrentDomain.GetData("Web20DefaultConfig.instance");
595                                 if (val == null) {
596                                         val = new Web20DefaultConfig();
597                                         AppDomain.CurrentDomain.SetData("Web20DefaultConfig.instance", val);
598                                 }
599                                 return val;
600                         }
601                         set {
602                                 AppDomain.CurrentDomain.SetData("Web20DefaultConfig.instance", value);
603                         }
604                 }
605 #else
606                 static Web20DefaultConfig instance;
607 #endif
608
609                 static Web20DefaultConfig ()
610                 {
611                         instance = new Web20DefaultConfig ();
612                 }
613
614                 public static Web20DefaultConfig GetInstance ()
615                 {
616                         return instance;
617                 }
618
619                 public object GetConfig (string sectionName)
620                 {
621                         object o = WebConfigurationManager.GetWebApplicationSection (sectionName);
622
623                         if (o == null || o is IgnoreSection) {
624                                 /* this can happen when the section
625                                  * handler doesn't subclass from
626                                  * ConfigurationSection.  let's be
627                                  * nice and try to load it using the
628                                  * 1.x style routines in case there's
629                                  * a 1.x section handler registered
630                                  * for it.
631                                  */
632                                 object o1 = WebConfigurationManager.oldConfig.GetConfig (sectionName);
633                                 if (o1 != null)
634                                         return o1;
635                         }
636
637                         return o;
638                 }
639
640                 public void Init ()
641                 {
642                         // nothing. We need a context.
643                 }
644         }
645
646 #endregion
647 }
648
649 #endif