2 // System.Web.Configuration.WebConfigurationHost.cs
5 // Lluis Sanchez Gual (lluis@novell.com)
6 // Marek Habersack <mhabersack@novell.com>
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:
16 // The above copyright notice and this permission notice shall be
17 // included in all copies or substantial portions of the Software.
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.
27 // Copyright (C) 2005-2009 Novell, Inc (http://www.novell.com)
33 using System.Collections;
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;
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..
48 * for now, though, just implement it as we do
49 * System.Configuration.InternalConfigurationHost, i.e. the local
52 namespace System.Web.Configuration
54 class WebConfigurationHost: IInternalConfigHost
56 WebConfigurationFileMap map;
57 const string MachinePath = ":machine:";
58 const string MachineWebPath = ":web:";
60 public virtual object CreateConfigurationContext (string configPath, string locationSubPath)
62 return new WebContext (WebApplicationLevel.AtApplication /* XXX */,
64 "" /* application path XXX */,
69 public virtual object CreateDeprecatedConfigContext (string configPath)
71 return new HttpConfigurationContext(configPath);
74 public virtual string DecryptSection (string encryptedXml, ProtectedConfigurationProvider protectionProvider,
75 ProtectedConfigurationSection protectedSection)
77 throw new NotImplementedException ();
80 public virtual void DeleteStream (string streamName)
82 File.Delete (streamName);
85 public virtual string EncryptSection (string encryptedXml, ProtectedConfigurationProvider protectionProvider,
86 ProtectedConfigurationSection protectedSection)
88 throw new NotImplementedException ();
91 public virtual string GetConfigPathFromLocationSubPath (string configPath, string locationSubPath)
93 if (!String.IsNullOrEmpty (locationSubPath) && !String.IsNullOrEmpty (configPath)) {
94 string relConfigPath = configPath.Length == 1 ? null : configPath.Substring (1) + "/";
95 if (relConfigPath != null && locationSubPath.StartsWith (relConfigPath, StringComparison.Ordinal))
96 locationSubPath = locationSubPath.Substring (relConfigPath.Length);
99 string ret = configPath + "/" + locationSubPath;
100 if (!String.IsNullOrEmpty (ret) && ret [0] == '/')
101 return ret.Substring (1);
106 public virtual Type GetConfigType (string typeName, bool throwOnError)
108 Type type = HttpApplication.LoadType (typeName);
109 if (type == null && throwOnError)
110 throw new ConfigurationErrorsException ("Type not found: '" + typeName + "'");
114 public virtual string GetConfigTypeName (Type t)
116 return t.AssemblyQualifiedName;
119 public virtual void GetRestrictedPermissions (IInternalConfigRecord configRecord, out PermissionSet permissionSet,
120 out bool isHostReady)
122 throw new NotImplementedException ();
125 public virtual string GetStreamName (string configPath)
127 if (configPath == MachinePath) {
129 return System.Runtime.InteropServices.RuntimeEnvironment.SystemConfigurationFile;
131 return map.MachineConfigFilename;
132 } else if (configPath == MachineWebPath) {
138 // check META-INF/web.config exists
139 java.lang.ClassLoader cl = (java.lang.ClassLoader) AppDomain.CurrentDomain.GetData ("GH_ContextClassLoader");
142 java.io.InputStream wcs = cl.getResourceAsStream ("META-INF/web.config");
148 return "/META-INF/web.config";
151 mdir = Path.GetDirectoryName (System.Runtime.InteropServices.RuntimeEnvironment.SystemConfigurationFile);
154 mdir = Path.GetDirectoryName (map.MachineConfigFilename);
156 return GetWebConfigFileName (mdir);
159 string dir = MapPath (configPath);
160 return GetWebConfigFileName (dir);
163 public virtual string GetStreamNameForConfigSource (string streamName, string configSource)
165 throw new NotImplementedException ();
168 public virtual object GetStreamVersion (string streamName)
170 throw new NotImplementedException ();
173 public virtual IDisposable Impersonate ()
175 throw new NotImplementedException ();
178 public virtual void Init (IInternalConfigRoot root, params object[] hostInitParams)
182 public virtual void InitForConfiguration (ref string locationSubPath, out string configPath,
183 out string locationConfigPath, IInternalConfigRoot root,
184 params object[] hostInitConfigurationParams)
186 string fullPath = (string) hostInitConfigurationParams [1];
187 map = (WebConfigurationFileMap) hostInitConfigurationParams [0];
189 if (locationSubPath == MachineWebPath) {
190 locationSubPath = MachinePath;
191 configPath = MachineWebPath;
192 locationConfigPath = null;
193 } else if (locationSubPath == MachinePath) {
194 locationSubPath = null;
195 configPath = MachinePath;
196 locationConfigPath = null;
199 if (locationSubPath == null) {
200 configPath = fullPath;
201 if (configPath.Length > 1)
202 configPath = VirtualPathUtility.RemoveTrailingSlash (configPath);
204 configPath = locationSubPath;
206 if (configPath == HttpRuntime.AppDomainAppVirtualPath || configPath == "/")
209 i = configPath.LastIndexOf ("/");
212 locationConfigPath = configPath.Substring (i+1);
215 locationSubPath = "/";
217 locationSubPath = fullPath.Substring (0, i);
219 locationSubPath = MachineWebPath;
220 locationConfigPath = null;
225 public string MapPath (string virtualPath)
227 if (!String.IsNullOrEmpty (virtualPath)) {
228 if (virtualPath.StartsWith (System.Web.Compilation.BuildManager.FAKE_VIRTUAL_PATH_PREFIX, StringComparison.Ordinal))
229 return HttpRuntime.AppDomainAppPath;
233 return MapPathFromMapper (virtualPath);
234 else if (HttpContext.Current != null && HttpContext.Current.Request != null)
235 return HttpContext.Current.Request.MapPath (virtualPath);
236 else if (HttpRuntime.AppDomainAppVirtualPath != null &&
237 virtualPath.StartsWith (HttpRuntime.AppDomainAppVirtualPath)) {
238 if (virtualPath == HttpRuntime.AppDomainAppVirtualPath)
239 return HttpRuntime.AppDomainAppPath;
240 return UrlUtils.Combine (HttpRuntime.AppDomainAppPath,
241 virtualPath.Substring (HttpRuntime.AppDomainAppVirtualPath.Length));
247 public string NormalizeVirtualPath (string virtualPath)
249 if (virtualPath == null || virtualPath.Length == 0)
252 virtualPath = virtualPath.Trim ();
254 if (virtualPath [0] == '~' && virtualPath.Length > 2 && virtualPath [1] == '/')
255 virtualPath = virtualPath.Substring (1);
257 if (System.IO.Path.DirectorySeparatorChar != '/')
258 virtualPath = virtualPath.Replace (System.IO.Path.DirectorySeparatorChar, '/');
260 if (UrlUtils.IsRooted (virtualPath)) {
261 virtualPath = UrlUtils.Canonic (virtualPath);
263 if (map.VirtualDirectories.Count > 0) {
264 string root = map.VirtualDirectories [0].VirtualDirectory;
265 virtualPath = UrlUtils.Combine (root, virtualPath);
266 virtualPath = UrlUtils.Canonic (virtualPath);
272 public string MapPathFromMapper (string virtualPath)
274 string path = NormalizeVirtualPath (virtualPath);
276 foreach (VirtualDirectoryMapping mapping in map.VirtualDirectories) {
277 if (path.StartsWith (mapping.VirtualDirectory)) {
278 int i = mapping.VirtualDirectory.Length;
279 if (path.Length == i) {
280 return mapping.PhysicalDirectory;
282 else if (path [i] == '/') {
283 string pathPart = path.Substring (i + 1).Replace ('/', Path.DirectorySeparatorChar);
284 return Path.Combine (mapping.PhysicalDirectory, pathPart);
288 throw new HttpException ("Invalid virtual directory: " + virtualPath);
291 internal static string GetWebConfigFileName (string dir)
294 DirectoryInfo d = GetCaseSensitiveExistingDirectory (new DirectoryInfo (dir));
298 FileInfo file = (FileInfo) FindByName ("web.config", d.GetFiles ("W*"));
300 file = (FileInfo) FindByName ("web.config", d.GetFiles ("w*"));
303 return file.FullName;
305 AppDomain domain = AppDomain.CurrentDomain;
306 bool hosted = (domain.GetData (ApplicationHost.MonoHostedDataKey) as string) == "yes";
309 foreach (string fn in ApplicationHost.WebConfigFileNames) {
310 string file = Path.Combine (dir, fn);
311 if (File.Exists (file))
315 Assembly asm = Assembly.GetEntryAssembly () ?? Assembly.GetCallingAssembly ();
316 string name = Path.GetFileName (asm.Location);
317 string[] fileNames = new string[] {name + ".config", name + ".Config"};
318 string appDir = domain.BaseDirectory;
321 foreach (string fn in fileNames) {
322 file = Path.Combine (appDir, fn);
323 if (File.Exists (file))
331 static DirectoryInfo GetCaseSensitiveExistingDirectory (DirectoryInfo dir) {
337 DirectoryInfo parent = GetCaseSensitiveExistingDirectory (dir.Parent);
341 return (DirectoryInfo) FindByName (dir.Name, parent.GetDirectories ());
344 static FileSystemInfo FindByName (string name, FileSystemInfo [] infos)
346 for (int i = 0; i < infos.Length; i++) {
347 if (String.Compare (name, infos [i].Name, StringComparison.OrdinalIgnoreCase) == 0)
353 public virtual bool IsAboveApplication (string configPath)
355 throw new NotImplementedException ();
358 public virtual bool IsConfigRecordRequired (string configPath)
360 throw new NotImplementedException ();
363 public virtual bool IsDefinitionAllowed (string configPath, ConfigurationAllowDefinition allowDefinition,
364 ConfigurationAllowExeDefinition allowExeDefinition)
366 switch (allowDefinition) {
367 case ConfigurationAllowDefinition.MachineOnly:
368 return configPath == MachinePath || configPath == MachineWebPath;
369 case ConfigurationAllowDefinition.MachineToWebRoot:
370 case ConfigurationAllowDefinition.MachineToApplication:
371 if (String.IsNullOrEmpty (configPath))
375 if (VirtualPathUtility.IsRooted (configPath))
376 normalized = VirtualPathUtility.Normalize (configPath);
378 normalized = configPath;
380 return (String.Compare (normalized, MachinePath, StringComparison.Ordinal) == 0) ||
381 (String.Compare (normalized, MachineWebPath, StringComparison.Ordinal) == 0) ||
382 (String.Compare (normalized, "/", StringComparison.Ordinal) == 0) ||
383 (String.Compare (normalized, "~", StringComparison.Ordinal) == 0) ||
384 (String.Compare (normalized, HttpRuntime.AppDomainAppVirtualPath) == 0);
390 public virtual bool IsFile (string streamName)
392 throw new NotImplementedException ();
395 public virtual bool IsLocationApplicable (string configPath)
397 throw new NotImplementedException ();
400 public virtual Stream OpenStreamForRead (string streamName)
402 if (!File.Exists (streamName)) {
404 if (streamName != null && (streamName.EndsWith ("machine.config") ||
405 streamName.EndsWith ("web.config"))) {
406 if (streamName.StartsWith ("/"))
407 streamName = streamName.Substring (1);
408 java.lang.ClassLoader cl = (java.lang.ClassLoader) AppDomain.CurrentDomain.GetData ("GH_ContextClassLoader");
410 java.io.InputStream inputStream = cl.getResourceAsStream (streamName);
411 return new System.Web.J2EE.J2EEUtils.InputStreamWrapper (inputStream);
415 throw new ConfigurationException ("File '" + streamName + "' not found");
418 return new FileStream (streamName, FileMode.Open, FileAccess.Read);
421 [MonoTODO ("Not implemented")]
422 public virtual Stream OpenStreamForRead (string streamName, bool assertPermissions)
424 throw new NotImplementedException ();
427 public virtual Stream OpenStreamForWrite (string streamName, string templateStreamName, ref object writeContext)
429 string rootConfigPath = GetWebConfigFileName (HttpRuntime.AppDomainAppPath);
430 if (String.Compare (streamName, rootConfigPath, StringComparison.OrdinalIgnoreCase) == 0)
431 WebConfigurationManager.SuppressAppReload (true);
432 return new FileStream (streamName, FileMode.Create, FileAccess.Write);
435 [MonoTODO ("Not implemented")]
436 public virtual Stream OpenStreamForWrite (string streamName, string templateStreamName, ref object writeContext,
437 bool assertPermissions)
439 throw new NotImplementedException ();
442 public virtual bool PrefetchAll (string configPath, string streamName)
444 throw new NotImplementedException ();
447 public virtual bool PrefetchSection (string sectionGroupName, string sectionName)
449 throw new NotImplementedException ();
452 [MonoTODO ("Not implemented")]
453 public virtual void RequireCompleteInit (IInternalConfigRecord configRecord)
455 throw new NotImplementedException ();
458 public virtual object StartMonitoringStreamForChanges (string streamName, StreamChangeCallback callback)
460 throw new NotImplementedException ();
463 public virtual void StopMonitoringStreamForChanges (string streamName, StreamChangeCallback callback)
465 throw new NotImplementedException ();
468 public virtual void VerifyDefinitionAllowed (string configPath, ConfigurationAllowDefinition allowDefinition,
469 ConfigurationAllowExeDefinition allowExeDefinition,
470 IConfigErrorInfo errorInfo)
472 if (!IsDefinitionAllowed (configPath, allowDefinition, allowExeDefinition))
473 throw new ConfigurationErrorsException ("The section can't be defined in this file (the allowed definition context is '" + allowDefinition + "').", errorInfo.Filename, errorInfo.LineNumber);
476 public virtual void WriteCompleted (string streamName, bool success, object writeContext)
478 WriteCompleted (streamName, success, writeContext, false);
481 public virtual void WriteCompleted (string streamName, bool success, object writeContext, bool assertPermissions)
483 // There are probably other things to be done here, but for the moment we
484 // just mark the completed write as one that should not cause application
485 // reload. Note that it might already be too late for suppression, since the
486 // FileSystemWatcher monitor might have already delivered the
487 // notification. If the stream has been open using OpenStreamForWrite then
488 // we're safe, though.
489 string rootConfigPath = GetWebConfigFileName (HttpRuntime.AppDomainAppPath);
490 if (String.Compare (streamName, rootConfigPath, StringComparison.OrdinalIgnoreCase) == 0)
491 WebConfigurationManager.SuppressAppReload (true);
494 public virtual bool SupportsChangeNotifications {
495 get { return false; }
498 public virtual bool SupportsLocation {
499 get { return false; }
502 public virtual bool SupportsPath {
503 get { return false; }
506 public virtual bool SupportsRefresh {
507 get { return false; }
510 [MonoTODO("Always returns false")]
511 public virtual bool IsRemote {
512 get { return false; }
515 [MonoTODO ("Not implemented")]
516 public virtual bool IsFullTrustSectionWithoutAptcaAllowed (IInternalConfigRecord configRecord)
518 throw new NotImplementedException ();
521 [MonoTODO ("Not implemented")]
522 public virtual bool IsInitDelayed (IInternalConfigRecord configRecord)
524 throw new NotImplementedException ();
527 [MonoTODO ("Not implemented")]
528 public virtual bool IsSecondaryRoot (string configPath)
530 throw new NotImplementedException ();
533 [MonoTODO ("Not implemented")]
534 public virtual bool IsTrustedConfigPath (string configPath)
536 throw new NotImplementedException ();