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 string appVirtualPath;
62 public virtual object CreateConfigurationContext (string configPath, string locationSubPath)
64 return new WebContext (WebApplicationLevel.AtApplication /* XXX */,
66 "" /* application path XXX */,
71 public virtual object CreateDeprecatedConfigContext (string configPath)
73 return new HttpConfigurationContext(configPath);
76 public virtual string DecryptSection (string encryptedXml, ProtectedConfigurationProvider protectionProvider, ProtectedConfigurationSection protectedSection)
78 if (protectedSection == null)
79 throw new ArgumentNullException ("protectedSection");
81 return protectedSection.EncryptSection (encryptedXml, protectionProvider);
84 public virtual void DeleteStream (string streamName)
86 File.Delete (streamName);
89 public virtual string EncryptSection (string clearXml, ProtectedConfigurationProvider protectionProvider, ProtectedConfigurationSection protectedSection)
91 if (protectedSection == null)
92 throw new ArgumentNullException ("protectedSection");
94 return protectedSection.EncryptSection (clearXml, protectionProvider);
97 public virtual string GetConfigPathFromLocationSubPath (string configPath, string locationSubPath)
99 if (!String.IsNullOrEmpty (locationSubPath) && !String.IsNullOrEmpty (configPath)) {
100 string relConfigPath = configPath.Length == 1 ? null : configPath.Substring (1) + "/";
101 if (relConfigPath != null && locationSubPath.StartsWith (relConfigPath, StringComparison.Ordinal))
102 locationSubPath = locationSubPath.Substring (relConfigPath.Length);
105 string ret = configPath + "/" + locationSubPath;
106 if (!String.IsNullOrEmpty (ret) && ret [0] == '/')
107 return ret.Substring (1);
112 public virtual Type GetConfigType (string typeName, bool throwOnError)
114 Type type = HttpApplication.LoadType (typeName);
115 if (type == null && throwOnError)
116 throw new ConfigurationErrorsException ("Type not found: '" + typeName + "'");
120 public virtual string GetConfigTypeName (Type t)
122 return t.AssemblyQualifiedName;
125 public virtual void GetRestrictedPermissions (IInternalConfigRecord configRecord, out PermissionSet permissionSet,
126 out bool isHostReady)
128 throw new NotImplementedException ();
131 public virtual string GetStreamName (string configPath)
133 if (configPath == MachinePath) {
135 return System.Runtime.InteropServices.RuntimeEnvironment.SystemConfigurationFile;
137 return map.MachineConfigFilename;
138 } else if (configPath == MachineWebPath) {
144 // check META-INF/web.config exists
145 java.lang.ClassLoader cl = (java.lang.ClassLoader) AppDomain.CurrentDomain.GetData ("GH_ContextClassLoader");
148 java.io.InputStream wcs = cl.getResourceAsStream ("META-INF/web.config");
154 return "/META-INF/web.config";
157 mdir = Path.GetDirectoryName (System.Runtime.InteropServices.RuntimeEnvironment.SystemConfigurationFile);
160 mdir = Path.GetDirectoryName (map.MachineConfigFilename);
162 return GetWebConfigFileName (mdir);
165 string dir = MapPath (configPath);
166 return GetWebConfigFileName (dir);
169 public virtual string GetStreamNameForConfigSource (string streamName, string configSource)
171 throw new NotImplementedException ();
174 public virtual object GetStreamVersion (string streamName)
176 throw new NotImplementedException ();
179 public virtual IDisposable Impersonate ()
181 throw new NotImplementedException ();
184 public virtual void Init (IInternalConfigRoot root, params object[] hostInitParams)
188 public virtual void InitForConfiguration (ref string locationSubPath, out string configPath,
189 out string locationConfigPath, IInternalConfigRoot root,
190 params object[] hostInitConfigurationParams)
192 string fullPath = (string) hostInitConfigurationParams [1];
193 map = (WebConfigurationFileMap) hostInitConfigurationParams [0];
194 bool inAnotherApp = (bool) hostInitConfigurationParams [7];
197 appVirtualPath = fullPath;
199 appVirtualPath = HttpRuntime.AppDomainAppVirtualPath;
201 if (locationSubPath == MachineWebPath) {
202 locationSubPath = MachinePath;
203 configPath = MachineWebPath;
204 locationConfigPath = null;
205 } else if (locationSubPath == MachinePath) {
206 locationSubPath = null;
207 configPath = MachinePath;
208 locationConfigPath = null;
211 if (locationSubPath == null) {
212 configPath = fullPath;
213 if (configPath.Length > 1)
214 configPath = VirtualPathUtility.RemoveTrailingSlash (configPath);
216 configPath = locationSubPath;
218 if (configPath == HttpRuntime.AppDomainAppVirtualPath || configPath == "/")
221 i = configPath.LastIndexOf ("/");
224 locationConfigPath = configPath.Substring (i+1);
227 locationSubPath = "/";
229 locationSubPath = fullPath.Substring (0, i);
231 locationSubPath = MachineWebPath;
232 locationConfigPath = null;
237 public string MapPath (string virtualPath)
239 if (!String.IsNullOrEmpty (virtualPath)) {
240 if (virtualPath.StartsWith (System.Web.Compilation.BuildManager.FAKE_VIRTUAL_PATH_PREFIX, StringComparison.Ordinal))
241 return HttpRuntime.AppDomainAppPath;
245 return MapPathFromMapper (virtualPath);
246 else if (HttpContext.Current != null && HttpContext.Current.Request != null)
247 return HttpContext.Current.Request.MapPath (virtualPath);
248 else if (HttpRuntime.AppDomainAppVirtualPath != null &&
249 virtualPath.StartsWith (HttpRuntime.AppDomainAppVirtualPath)) {
250 if (virtualPath == HttpRuntime.AppDomainAppVirtualPath)
251 return HttpRuntime.AppDomainAppPath;
252 return UrlUtils.Combine (HttpRuntime.AppDomainAppPath,
253 virtualPath.Substring (HttpRuntime.AppDomainAppVirtualPath.Length));
259 public string NormalizeVirtualPath (string virtualPath)
261 if (virtualPath == null || virtualPath.Length == 0)
264 virtualPath = virtualPath.Trim ();
266 if (virtualPath [0] == '~' && virtualPath.Length > 2 && virtualPath [1] == '/')
267 virtualPath = virtualPath.Substring (1);
269 if (System.IO.Path.DirectorySeparatorChar != '/')
270 virtualPath = virtualPath.Replace (System.IO.Path.DirectorySeparatorChar, '/');
272 if (UrlUtils.IsRooted (virtualPath)) {
273 virtualPath = UrlUtils.Canonic (virtualPath);
275 if (map.VirtualDirectories.Count > 0) {
276 string root = map.VirtualDirectories [0].VirtualDirectory;
277 virtualPath = UrlUtils.Combine (root, virtualPath);
278 virtualPath = UrlUtils.Canonic (virtualPath);
284 public string MapPathFromMapper (string virtualPath)
286 string path = NormalizeVirtualPath (virtualPath);
288 foreach (VirtualDirectoryMapping mapping in map.VirtualDirectories) {
289 if (path.StartsWith (mapping.VirtualDirectory)) {
290 int i = mapping.VirtualDirectory.Length;
291 if (path.Length == i) {
292 return mapping.PhysicalDirectory;
294 else if (path [i] == '/') {
295 string pathPart = path.Substring (i + 1).Replace ('/', Path.DirectorySeparatorChar);
296 return Path.Combine (mapping.PhysicalDirectory, pathPart);
300 throw new HttpException ("Invalid virtual directory: " + virtualPath);
303 internal static string GetWebConfigFileName (string dir)
306 DirectoryInfo d = GetCaseSensitiveExistingDirectory (new DirectoryInfo (dir));
310 FileInfo file = (FileInfo) FindByName ("web.config", d.GetFiles ("W*"));
312 file = (FileInfo) FindByName ("web.config", d.GetFiles ("w*"));
315 return file.FullName;
317 AppDomain domain = AppDomain.CurrentDomain;
318 bool hosted = (domain.GetData (ApplicationHost.MonoHostedDataKey) as string) == "yes";
321 foreach (string fn in ApplicationHost.WebConfigFileNames) {
322 string file = Path.Combine (dir, fn);
323 if (File.Exists (file))
327 Assembly asm = Assembly.GetEntryAssembly () ?? Assembly.GetCallingAssembly ();
328 string name = Path.GetFileName (asm.Location);
329 string[] fileNames = new string[] {name + ".config", name + ".Config"};
330 string appDir = domain.BaseDirectory;
333 foreach (string fn in fileNames) {
334 file = Path.Combine (appDir, fn);
335 if (File.Exists (file))
343 static DirectoryInfo GetCaseSensitiveExistingDirectory (DirectoryInfo dir) {
349 DirectoryInfo parent = GetCaseSensitiveExistingDirectory (dir.Parent);
353 return (DirectoryInfo) FindByName (dir.Name, parent.GetDirectories ());
356 static FileSystemInfo FindByName (string name, FileSystemInfo [] infos)
358 for (int i = 0; i < infos.Length; i++) {
359 if (String.Compare (name, infos [i].Name, StringComparison.OrdinalIgnoreCase) == 0)
365 public virtual bool IsAboveApplication (string configPath)
367 throw new NotImplementedException ();
370 public virtual bool IsConfigRecordRequired (string configPath)
372 throw new NotImplementedException ();
375 public virtual bool IsDefinitionAllowed (string configPath, ConfigurationAllowDefinition allowDefinition,
376 ConfigurationAllowExeDefinition allowExeDefinition)
378 switch (allowDefinition) {
379 case ConfigurationAllowDefinition.MachineOnly:
380 return configPath == MachinePath || configPath == MachineWebPath;
381 case ConfigurationAllowDefinition.MachineToWebRoot:
382 case ConfigurationAllowDefinition.MachineToApplication:
383 if (String.IsNullOrEmpty (configPath))
387 if (VirtualPathUtility.IsRooted (configPath))
388 normalized = VirtualPathUtility.Normalize (configPath);
390 normalized = configPath;
392 return (String.Compare (normalized, MachinePath, StringComparison.Ordinal) == 0) ||
393 (String.Compare (normalized, MachineWebPath, StringComparison.Ordinal) == 0) ||
394 (String.Compare (normalized, "/", StringComparison.Ordinal) == 0) ||
395 (String.Compare (normalized, "~", StringComparison.Ordinal) == 0) ||
396 (String.Compare (normalized, appVirtualPath) == 0);
402 public virtual bool IsFile (string streamName)
404 throw new NotImplementedException ();
407 public virtual bool IsLocationApplicable (string configPath)
409 throw new NotImplementedException ();
412 public virtual Stream OpenStreamForRead (string streamName)
414 if (!File.Exists (streamName)) {
416 if (streamName != null && (streamName.EndsWith ("machine.config") ||
417 streamName.EndsWith ("web.config"))) {
418 if (streamName.StartsWith ("/"))
419 streamName = streamName.Substring (1);
420 java.lang.ClassLoader cl = (java.lang.ClassLoader) AppDomain.CurrentDomain.GetData ("GH_ContextClassLoader");
422 java.io.InputStream inputStream = cl.getResourceAsStream (streamName);
423 return new System.Web.J2EE.J2EEUtils.InputStreamWrapper (inputStream);
430 return new FileStream (streamName, FileMode.Open, FileAccess.Read);
433 [MonoTODO ("Not implemented")]
434 public virtual Stream OpenStreamForRead (string streamName, bool assertPermissions)
436 throw new NotImplementedException ();
439 public virtual Stream OpenStreamForWrite (string streamName, string templateStreamName, ref object writeContext)
441 string rootConfigPath = GetWebConfigFileName (HttpRuntime.AppDomainAppPath);
442 if (String.Compare (streamName, rootConfigPath, StringComparison.OrdinalIgnoreCase) == 0)
443 WebConfigurationManager.SuppressAppReload (true);
444 return new FileStream (streamName, FileMode.Create, FileAccess.Write);
447 [MonoTODO ("Not implemented")]
448 public virtual Stream OpenStreamForWrite (string streamName, string templateStreamName, ref object writeContext,
449 bool assertPermissions)
451 throw new NotImplementedException ();
454 public virtual bool PrefetchAll (string configPath, string streamName)
456 throw new NotImplementedException ();
459 public virtual bool PrefetchSection (string sectionGroupName, string sectionName)
461 throw new NotImplementedException ();
464 [MonoTODO ("Not implemented")]
465 public virtual void RequireCompleteInit (IInternalConfigRecord configRecord)
467 throw new NotImplementedException ();
470 public virtual object StartMonitoringStreamForChanges (string streamName, StreamChangeCallback callback)
472 throw new NotImplementedException ();
475 public virtual void StopMonitoringStreamForChanges (string streamName, StreamChangeCallback callback)
477 throw new NotImplementedException ();
480 public virtual void VerifyDefinitionAllowed (string configPath, ConfigurationAllowDefinition allowDefinition,
481 ConfigurationAllowExeDefinition allowExeDefinition,
482 IConfigErrorInfo errorInfo)
484 if (!IsDefinitionAllowed (configPath, allowDefinition, allowExeDefinition))
485 throw new ConfigurationErrorsException ("The section can't be defined in this file (the allowed definition context is '" + allowDefinition + "').", errorInfo.Filename, errorInfo.LineNumber);
488 public virtual void WriteCompleted (string streamName, bool success, object writeContext)
490 WriteCompleted (streamName, success, writeContext, false);
493 public virtual void WriteCompleted (string streamName, bool success, object writeContext, bool assertPermissions)
495 // There are probably other things to be done here, but for the moment we
496 // just mark the completed write as one that should not cause application
497 // reload. Note that it might already be too late for suppression, since the
498 // FileSystemWatcher monitor might have already delivered the
499 // notification. If the stream has been open using OpenStreamForWrite then
500 // we're safe, though.
501 string rootConfigPath = GetWebConfigFileName (HttpRuntime.AppDomainAppPath);
502 if (String.Compare (streamName, rootConfigPath, StringComparison.OrdinalIgnoreCase) == 0)
503 WebConfigurationManager.SuppressAppReload (true);
506 public virtual bool SupportsChangeNotifications {
507 get { return false; }
510 public virtual bool SupportsLocation {
511 get { return false; }
514 public virtual bool SupportsPath {
515 get { return false; }
518 public virtual bool SupportsRefresh {
519 get { return false; }
522 [MonoTODO("Always returns false")]
523 public virtual bool IsRemote {
524 get { return false; }
527 [MonoTODO ("Not implemented")]
528 public virtual bool IsFullTrustSectionWithoutAptcaAllowed (IInternalConfigRecord configRecord)
530 throw new NotImplementedException ();
533 [MonoTODO ("Not implemented")]
534 public virtual bool IsInitDelayed (IInternalConfigRecord configRecord)
536 throw new NotImplementedException ();
539 [MonoTODO ("Not implemented")]
540 public virtual bool IsSecondaryRoot (string configPath)
542 throw new NotImplementedException ();
545 [MonoTODO ("Not implemented")]
546 public virtual bool IsTrustedConfigPath (string configPath)
548 throw new NotImplementedException ();