2010-02-12 Marek Habersack <mhabersack@novell.com>
[mono.git] / mcs / class / System.Web / System.Web.Configuration_2.0 / WebConfigurationHost.cs
1 //
2 // System.Web.Configuration.WebConfigurationHost.cs
3 //
4 // Authors:
5 //  Lluis Sanchez Gual (lluis@novell.com)
6 //  Marek Habersack <mhabersack@novell.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-2009 Novell, Inc (http://www.novell.com)
28 //
29
30 #if NET_2_0
31
32 using System;
33 using System.Collections;
34 using System.IO;
35 using System.Security;
36 using System.Configuration;
37 using System.Configuration.Internal;
38 using System.Web.Hosting;
39 using System.Web.Util;
40 using System.Reflection;
41
42 /*
43  * this class needs to be rewritten to support usage of the
44  * IRemoteWebConfigurationHostServer interface.  Once that's done, we
45  * need an implementation of that interface that talks (through a web
46  * service?) to a remote site..
47  *
48  * for now, though, just implement it as we do
49  * System.Configuration.InternalConfigurationHost, i.e. the local
50  * case.
51  */
52 namespace System.Web.Configuration
53 {
54         class WebConfigurationHost: IInternalConfigHost
55         {
56                 WebConfigurationFileMap map;
57                 const string MachinePath = ":machine:";
58                 const string MachineWebPath = ":web:";
59                 
60                 public virtual object CreateConfigurationContext (string configPath, string locationSubPath)
61                 {
62                         return new WebContext (WebApplicationLevel.AtApplication /* XXX */,
63                                                "" /* site XXX */,
64                                                "" /* application path XXX */,
65                                                configPath,
66                                                locationSubPath);
67                 }
68                 
69                 public virtual object CreateDeprecatedConfigContext (string configPath)
70                 {
71                         return new HttpConfigurationContext(configPath);
72                 }
73                 
74                 public virtual string DecryptSection (string encryptedXml, ProtectedConfigurationProvider protectionProvider, ProtectedConfigurationSection protectedSection)
75                 {
76                         if (protectedSection == null)
77                                 throw new ArgumentNullException ("protectedSection");
78
79                         return protectedSection.EncryptSection (encryptedXml, protectionProvider);
80                 }
81                 
82                 public virtual void DeleteStream (string streamName)
83                 {
84                         File.Delete (streamName);
85                 }
86                 
87                 public virtual string EncryptSection (string clearXml, ProtectedConfigurationProvider protectionProvider, ProtectedConfigurationSection protectedSection)
88                 {
89                         if (protectedSection == null)
90                                 throw new ArgumentNullException ("protectedSection");
91
92                         return protectedSection.EncryptSection (clearXml, protectionProvider);
93                 }
94                 
95                 public virtual string GetConfigPathFromLocationSubPath (string configPath, string locationSubPath)
96                 {
97                         if (!String.IsNullOrEmpty (locationSubPath) && !String.IsNullOrEmpty (configPath)) {
98                                 string relConfigPath = configPath.Length == 1 ? null : configPath.Substring (1) + "/";
99                                 if (relConfigPath != null && locationSubPath.StartsWith (relConfigPath, StringComparison.Ordinal))
100                                         locationSubPath = locationSubPath.Substring (relConfigPath.Length);
101                         }
102                         
103                         string ret = configPath + "/" + locationSubPath;
104                         if (!String.IsNullOrEmpty (ret) && ret [0] == '/')
105                                 return ret.Substring (1);
106                         
107                         return ret;
108                 }
109                 
110                 public virtual Type GetConfigType (string typeName, bool throwOnError)
111                 {
112                         Type type = HttpApplication.LoadType (typeName);
113                         if (type == null && throwOnError)
114                                 throw new ConfigurationErrorsException ("Type not found: '" + typeName + "'");
115                         return type;
116                 }
117                 
118                 public virtual string GetConfigTypeName (Type t)
119                 {
120                         return t.AssemblyQualifiedName;
121                 }
122                 
123                 public virtual void GetRestrictedPermissions (IInternalConfigRecord configRecord, out PermissionSet permissionSet,
124                                                               out bool isHostReady)
125                 {
126                         throw new NotImplementedException ();
127                 }
128                 
129                 public virtual string GetStreamName (string configPath)
130                 {
131                         if (configPath == MachinePath) {
132                                 if (map == null)
133                                         return System.Runtime.InteropServices.RuntimeEnvironment.SystemConfigurationFile;
134                                 else
135                                         return map.MachineConfigFilename;
136                         } else if (configPath == MachineWebPath) {
137                                 string mdir;
138
139                                 if (map == null)
140 #if TARGET_J2EE
141                                 {
142                                         // check META-INF/web.config exists
143                                         java.lang.ClassLoader cl = (java.lang.ClassLoader) AppDomain.CurrentDomain.GetData ("GH_ContextClassLoader");
144                                         if (cl == null)
145                                                 return null;
146                                         java.io.InputStream wcs = cl.getResourceAsStream ("META-INF/web.config");
147                                         if (wcs == null)
148                                                 return null;
149
150                                         wcs.close ();
151
152                                         return "/META-INF/web.config";
153                                 }
154 #else
155                                         mdir = Path.GetDirectoryName (System.Runtime.InteropServices.RuntimeEnvironment.SystemConfigurationFile);
156 #endif
157                                 else
158                                         mdir = Path.GetDirectoryName (map.MachineConfigFilename);
159
160                                 return GetWebConfigFileName (mdir);
161                         }
162                         
163                         string dir = MapPath (configPath);
164                         return GetWebConfigFileName (dir);
165                 }
166                 
167                 public virtual string GetStreamNameForConfigSource (string streamName, string configSource)
168                 {
169                         throw new NotImplementedException ();
170                 }
171                 
172                 public virtual object GetStreamVersion (string streamName)
173                 {
174                         throw new NotImplementedException ();
175                 }
176                 
177                 public virtual IDisposable Impersonate ()
178                 {
179                         throw new NotImplementedException ();
180                 }
181                 
182                 public virtual void Init (IInternalConfigRoot root, params object[] hostInitParams)
183                 {
184                 }
185                 
186                 public virtual void InitForConfiguration (ref string locationSubPath, out string configPath,
187                                                           out string locationConfigPath, IInternalConfigRoot root,
188                                                           params object[] hostInitConfigurationParams)
189                 {
190                         string fullPath = (string) hostInitConfigurationParams [1];
191                         map = (WebConfigurationFileMap) hostInitConfigurationParams [0];
192
193                         if (locationSubPath == MachineWebPath) {
194                                 locationSubPath = MachinePath;
195                                 configPath = MachineWebPath;
196                                 locationConfigPath = null;
197                         } else if (locationSubPath == MachinePath) {
198                                 locationSubPath = null;
199                                 configPath = MachinePath;
200                                 locationConfigPath = null;
201                         } else {
202                                 int i;
203                                 if (locationSubPath == null) {
204                                         configPath = fullPath;
205                                         if (configPath.Length > 1)
206                                                 configPath = VirtualPathUtility.RemoveTrailingSlash (configPath);
207                                 } else
208                                         configPath = locationSubPath;
209                                 
210                                 if (configPath == HttpRuntime.AppDomainAppVirtualPath || configPath == "/")
211                                         i = -1;
212                                 else
213                                         i = configPath.LastIndexOf ("/");
214
215                                 if (i != -1) {
216                                         locationConfigPath = configPath.Substring (i+1);
217                                         
218                                         if (i == 0)
219                                                 locationSubPath = "/";
220                                         else
221                                                 locationSubPath = fullPath.Substring (0, i);
222                                 } else {
223                                         locationSubPath = MachineWebPath;
224                                         locationConfigPath = null;
225                                 }
226                         }
227                 }
228                 
229                 public string MapPath (string virtualPath)
230                 {
231                         if (!String.IsNullOrEmpty (virtualPath)) {
232                                 if (virtualPath.StartsWith (System.Web.Compilation.BuildManager.FAKE_VIRTUAL_PATH_PREFIX, StringComparison.Ordinal))
233                                         return HttpRuntime.AppDomainAppPath;
234                         }
235                         
236                         if (map != null)
237                                 return MapPathFromMapper (virtualPath);
238                         else if (HttpContext.Current != null && HttpContext.Current.Request != null)
239                                 return HttpContext.Current.Request.MapPath (virtualPath);
240                         else if (HttpRuntime.AppDomainAppVirtualPath != null &&
241                                  virtualPath.StartsWith (HttpRuntime.AppDomainAppVirtualPath)) {
242                                 if (virtualPath == HttpRuntime.AppDomainAppVirtualPath)
243                                         return HttpRuntime.AppDomainAppPath;
244                                 return UrlUtils.Combine (HttpRuntime.AppDomainAppPath,
245                                                          virtualPath.Substring (HttpRuntime.AppDomainAppVirtualPath.Length));
246                         }
247                         
248                         return virtualPath;
249                 }
250                 
251                 public string NormalizeVirtualPath (string virtualPath)
252                 {
253                         if (virtualPath == null || virtualPath.Length == 0)
254                                 virtualPath = ".";
255                         else
256                                 virtualPath = virtualPath.Trim ();
257
258                         if (virtualPath [0] == '~' && virtualPath.Length > 2 && virtualPath [1] == '/')
259                                 virtualPath = virtualPath.Substring (1);
260                                 
261                         if (System.IO.Path.DirectorySeparatorChar != '/')
262                                 virtualPath = virtualPath.Replace (System.IO.Path.DirectorySeparatorChar, '/');
263
264                         if (UrlUtils.IsRooted (virtualPath)) {
265                                 virtualPath = UrlUtils.Canonic (virtualPath);
266                         } else {
267                                 if (map.VirtualDirectories.Count > 0) {
268                                         string root = map.VirtualDirectories [0].VirtualDirectory;
269                                         virtualPath = UrlUtils.Combine (root, virtualPath);
270                                         virtualPath = UrlUtils.Canonic (virtualPath);
271                                 }
272                         }
273                         return virtualPath;
274                 }
275
276                 public string MapPathFromMapper (string virtualPath)
277                 {
278                         string path = NormalizeVirtualPath (virtualPath);
279                         
280                         foreach (VirtualDirectoryMapping mapping in map.VirtualDirectories) {
281                                 if (path.StartsWith (mapping.VirtualDirectory)) {
282                                         int i = mapping.VirtualDirectory.Length;
283                                         if (path.Length == i) {
284                                                 return mapping.PhysicalDirectory;
285                                         }
286                                         else if (path [i] == '/') {
287                                                 string pathPart = path.Substring (i + 1).Replace ('/', Path.DirectorySeparatorChar);
288                                                 return Path.Combine (mapping.PhysicalDirectory, pathPart);
289                                         }
290                                 }
291                         }
292                         throw new HttpException ("Invalid virtual directory: " + virtualPath);
293                 }
294
295                 internal static string GetWebConfigFileName (string dir)
296                 {
297 #if TARGET_J2EE
298                         DirectoryInfo d = GetCaseSensitiveExistingDirectory (new DirectoryInfo (dir));
299                         if (d == null)
300                                 return null;
301
302                         FileInfo file = (FileInfo) FindByName ("web.config", d.GetFiles ("W*"));
303                         if (file == null)
304                                 file = (FileInfo) FindByName ("web.config", d.GetFiles ("w*"));
305
306                         if (file != null)
307                                 return file.FullName;
308 #else
309                         AppDomain domain = AppDomain.CurrentDomain;
310                         bool hosted = (domain.GetData (ApplicationHost.MonoHostedDataKey) as string) == "yes";
311
312                         if (hosted) {
313                                 foreach (string fn in ApplicationHost.WebConfigFileNames) {
314                                         string file = Path.Combine (dir, fn);
315                                         if (File.Exists (file))
316                                                 return file;
317                                 }
318                         } else {
319                                 Assembly asm = Assembly.GetEntryAssembly () ?? Assembly.GetCallingAssembly ();
320                                 string name = Path.GetFileName (asm.Location);
321                                 string[] fileNames = new string[] {name + ".config", name + ".Config"};
322                                 string appDir = domain.BaseDirectory;
323                                 string file;
324
325                                 foreach (string fn in fileNames) {
326                                         file = Path.Combine (appDir, fn);
327                                         if (File.Exists (file))
328                                                 return file;
329                                 }
330                         }
331 #endif                  
332                         return null;
333                 }
334 #if TARGET_J2EE
335                 static DirectoryInfo GetCaseSensitiveExistingDirectory (DirectoryInfo dir) {
336                         if (dir == null)
337                                 return null;
338                         if (dir.Exists)
339                                 return dir;
340
341                         DirectoryInfo parent = GetCaseSensitiveExistingDirectory (dir.Parent);
342                         if (parent == null)
343                                 return null;
344
345                         return (DirectoryInfo) FindByName (dir.Name, parent.GetDirectories ());
346                 }
347                 
348                 static FileSystemInfo FindByName (string name, FileSystemInfo [] infos)
349                 {
350                         for (int i = 0; i < infos.Length; i++) {
351                                 if (String.Compare (name, infos [i].Name, StringComparison.OrdinalIgnoreCase) == 0)
352                                         return infos [i];
353                         }
354                         return null;
355                 }
356 #endif
357                 public virtual bool IsAboveApplication (string configPath)
358                 {
359                         throw new NotImplementedException ();
360                 }
361                 
362                 public virtual bool IsConfigRecordRequired (string configPath)
363                 {
364                         throw new NotImplementedException ();
365                 }
366                 
367                 public virtual bool IsDefinitionAllowed (string configPath, ConfigurationAllowDefinition allowDefinition,
368                                                          ConfigurationAllowExeDefinition allowExeDefinition)
369                 {
370                         switch (allowDefinition) {
371                                 case ConfigurationAllowDefinition.MachineOnly:
372                                         return configPath == MachinePath || configPath == MachineWebPath;
373                                 case ConfigurationAllowDefinition.MachineToWebRoot:
374                                 case ConfigurationAllowDefinition.MachineToApplication:
375                                         if (String.IsNullOrEmpty (configPath))
376                                                 return true;
377                                         string normalized;
378
379                                         if (VirtualPathUtility.IsRooted (configPath))
380                                                 normalized = VirtualPathUtility.Normalize (configPath);
381                                         else
382                                                 normalized = configPath;
383                                         
384                                         return (String.Compare (normalized, MachinePath, StringComparison.Ordinal) == 0) ||
385                                                 (String.Compare (normalized, MachineWebPath, StringComparison.Ordinal) == 0) ||
386                                                 (String.Compare (normalized, "/", StringComparison.Ordinal) == 0) ||
387                                                 (String.Compare (normalized, "~", StringComparison.Ordinal) == 0) ||
388                                                 (String.Compare (normalized, HttpRuntime.AppDomainAppVirtualPath) == 0);
389                                 default:
390                                         return true;
391                         }
392                 }
393                 
394                 public virtual bool IsFile (string streamName)
395                 {
396                         throw new NotImplementedException ();
397                 }
398                 
399                 public virtual bool IsLocationApplicable (string configPath)
400                 {
401                         throw new NotImplementedException ();
402                 }
403                 
404                 public virtual Stream OpenStreamForRead (string streamName)
405                 {
406                         if (!File.Exists (streamName)) {
407 #if TARGET_J2EE
408                                 if (streamName != null && (streamName.EndsWith ("machine.config") ||
409                                                            streamName.EndsWith ("web.config"))) {
410                                         if (streamName.StartsWith ("/"))
411                                                 streamName = streamName.Substring (1);
412                                         java.lang.ClassLoader cl = (java.lang.ClassLoader) AppDomain.CurrentDomain.GetData ("GH_ContextClassLoader");
413                                         if (cl != null) {
414                                                 java.io.InputStream inputStream = cl.getResourceAsStream (streamName);
415                                                 return new System.Web.J2EE.J2EEUtils.InputStreamWrapper (inputStream);
416                                         }
417                                 }
418 #endif
419                                 throw new ConfigurationException ("File '" + streamName + "' not found");
420                         }
421                                 
422                         return new FileStream (streamName, FileMode.Open, FileAccess.Read);
423                 }
424
425                 [MonoTODO ("Not implemented")]
426                 public virtual Stream OpenStreamForRead (string streamName, bool assertPermissions)
427                 {
428                         throw new NotImplementedException ();
429                 }
430
431                 public virtual Stream OpenStreamForWrite (string streamName, string templateStreamName, ref object writeContext)
432                 {
433                         string rootConfigPath = GetWebConfigFileName (HttpRuntime.AppDomainAppPath);
434                         if (String.Compare (streamName, rootConfigPath, StringComparison.OrdinalIgnoreCase) == 0)
435                                 WebConfigurationManager.SuppressAppReload (true);
436                         return new FileStream (streamName, FileMode.Create, FileAccess.Write);
437                 }
438
439                 [MonoTODO ("Not implemented")]
440                 public virtual Stream OpenStreamForWrite (string streamName, string templateStreamName, ref object writeContext,
441                                                           bool assertPermissions)
442                 {
443                         throw new NotImplementedException ();
444                 }
445                 
446                 public virtual bool PrefetchAll (string configPath, string streamName)
447                 {
448                         throw new NotImplementedException ();
449                 }
450                 
451                 public virtual bool PrefetchSection (string sectionGroupName, string sectionName)
452                 {
453                         throw new NotImplementedException ();
454                 }
455
456                 [MonoTODO ("Not implemented")]
457                 public virtual void RequireCompleteInit (IInternalConfigRecord configRecord)
458                 {
459                         throw new NotImplementedException ();
460                 }
461
462                 public virtual object StartMonitoringStreamForChanges (string streamName, StreamChangeCallback callback)
463                 {                       
464                         throw new NotImplementedException ();
465                 }
466                 
467                 public virtual void StopMonitoringStreamForChanges (string streamName, StreamChangeCallback callback)
468                 {
469                         throw new NotImplementedException ();
470                 }
471                 
472                 public virtual void VerifyDefinitionAllowed (string configPath, ConfigurationAllowDefinition allowDefinition,
473                                                              ConfigurationAllowExeDefinition allowExeDefinition,
474                                                              IConfigErrorInfo errorInfo)
475                 {
476                         if (!IsDefinitionAllowed (configPath, allowDefinition, allowExeDefinition))
477                                 throw new ConfigurationErrorsException ("The section can't be defined in this file (the allowed definition context is '" + allowDefinition + "').", errorInfo.Filename, errorInfo.LineNumber);
478                 }
479                 
480                 public virtual void WriteCompleted (string streamName, bool success, object writeContext)
481                 {
482                         WriteCompleted (streamName, success, writeContext, false);
483                 }               
484
485                 public virtual void WriteCompleted (string streamName, bool success, object writeContext, bool assertPermissions)
486                 {
487                         // There are probably other things to be done here, but for the moment we
488                         // just mark the completed write as one that should not cause application
489                         // reload. Note that it might already be too late for suppression, since the
490                         // FileSystemWatcher monitor might have already delivered the
491                         // notification. If the stream has been open using OpenStreamForWrite then
492                         // we're safe, though.
493                         string rootConfigPath = GetWebConfigFileName (HttpRuntime.AppDomainAppPath);
494                         if (String.Compare (streamName, rootConfigPath, StringComparison.OrdinalIgnoreCase) == 0)
495                                 WebConfigurationManager.SuppressAppReload (true);
496                 }
497
498                 public virtual bool SupportsChangeNotifications {
499                         get { return false; }
500                 }
501                 
502                 public virtual bool SupportsLocation {
503                         get { return false; }
504                 }
505                 
506                 public virtual bool SupportsPath {
507                         get { return false; }
508                 }
509                 
510                 public virtual bool SupportsRefresh {
511                         get { return false; }
512                 }
513
514                 [MonoTODO("Always returns false")]
515                 public virtual bool IsRemote {
516                         get { return false; }
517                 }
518
519                 [MonoTODO ("Not implemented")]
520                 public virtual bool IsFullTrustSectionWithoutAptcaAllowed (IInternalConfigRecord configRecord)
521                 {
522                         throw new NotImplementedException ();
523                 }
524
525                 [MonoTODO ("Not implemented")]
526                 public virtual bool IsInitDelayed (IInternalConfigRecord configRecord)
527                 {
528                         throw new NotImplementedException ();
529                 }
530
531                 [MonoTODO ("Not implemented")]
532                 public virtual bool IsSecondaryRoot (string configPath)
533                 {
534                         throw new NotImplementedException ();
535                 }
536
537                 [MonoTODO ("Not implemented")]
538                 public virtual bool IsTrustedConfigPath (string configPath)
539                 {
540                         throw new NotImplementedException ();
541                 }
542         }
543 }
544
545 #endif