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 return ApplicationHost.FindWebConfig (dir);
323 Assembly asm = Assembly.GetEntryAssembly () ?? Assembly.GetCallingAssembly ();
324 string name = Path.GetFileName (asm.Location);
325 string[] fileNames = new string[] {name + ".config", name + ".Config"};
326 string appDir = domain.BaseDirectory;
329 foreach (string fn in fileNames) {
330 file = Path.Combine (appDir, fn);
331 if (File.Exists (file))
339 static DirectoryInfo GetCaseSensitiveExistingDirectory (DirectoryInfo dir) {
345 DirectoryInfo parent = GetCaseSensitiveExistingDirectory (dir.Parent);
349 return (DirectoryInfo) FindByName (dir.Name, parent.GetDirectories ());
352 static FileSystemInfo FindByName (string name, FileSystemInfo [] infos)
354 for (int i = 0; i < infos.Length; i++) {
355 if (String.Compare (name, infos [i].Name, StringComparison.OrdinalIgnoreCase) == 0)
361 public virtual bool IsAboveApplication (string configPath)
363 return !configPath.Contains (HttpRuntime.AppDomainAppPath);
366 public virtual bool IsConfigRecordRequired (string configPath)
368 throw new NotImplementedException ();
371 public virtual bool IsDefinitionAllowed (string configPath, ConfigurationAllowDefinition allowDefinition,
372 ConfigurationAllowExeDefinition allowExeDefinition)
374 switch (allowDefinition) {
375 case ConfigurationAllowDefinition.MachineOnly:
376 return configPath == MachinePath || configPath == MachineWebPath;
377 case ConfigurationAllowDefinition.MachineToWebRoot:
378 case ConfigurationAllowDefinition.MachineToApplication:
379 if (String.IsNullOrEmpty (configPath))
383 if (VirtualPathUtility.IsRooted (configPath))
384 normalized = VirtualPathUtility.Normalize (configPath);
386 normalized = configPath;
388 if ((String.Compare (normalized, MachinePath, StringComparison.Ordinal) == 0) ||
389 (String.Compare (normalized, MachineWebPath, StringComparison.Ordinal) == 0))
392 if ((String.Compare (normalized, appVirtualPath) != 0))
393 return IsApplication (normalized);
401 [MonoTODO("Should return false in case strPath points to the root of an application.")]
402 internal bool IsApplication(string strPath)
407 public virtual bool IsFile (string streamName)
409 throw new NotImplementedException ();
412 public virtual bool IsLocationApplicable (string configPath)
414 throw new NotImplementedException ();
417 public virtual Stream OpenStreamForRead (string streamName)
419 if (!File.Exists (streamName)) {
421 if (streamName != null && (streamName.EndsWith ("machine.config") ||
422 streamName.EndsWith ("web.config"))) {
423 if (streamName.StartsWith ("/"))
424 streamName = streamName.Substring (1);
425 java.lang.ClassLoader cl = (java.lang.ClassLoader) AppDomain.CurrentDomain.GetData ("GH_ContextClassLoader");
427 java.io.InputStream inputStream = cl.getResourceAsStream (streamName);
428 return new System.Web.J2EE.J2EEUtils.InputStreamWrapper (inputStream);
435 return new FileStream (streamName, FileMode.Open, FileAccess.Read);
438 [MonoTODO ("Not implemented")]
439 public virtual Stream OpenStreamForRead (string streamName, bool assertPermissions)
441 throw new NotImplementedException ();
444 public virtual Stream OpenStreamForWrite (string streamName, string templateStreamName, ref object writeContext)
446 if (!IsAboveApplication (streamName))
447 WebConfigurationManager.SuppressAppReload (true);
449 return new FileStream (streamName, FileMode.Create, FileAccess.Write);
452 [MonoTODO ("Not implemented")]
453 public virtual Stream OpenStreamForWrite (string streamName, string templateStreamName, ref object writeContext,
454 bool assertPermissions)
456 throw new NotImplementedException ();
459 public virtual bool PrefetchAll (string configPath, string streamName)
461 throw new NotImplementedException ();
464 public virtual bool PrefetchSection (string sectionGroupName, string sectionName)
466 throw new NotImplementedException ();
469 [MonoTODO ("Not implemented")]
470 public virtual void RequireCompleteInit (IInternalConfigRecord configRecord)
472 throw new NotImplementedException ();
475 public virtual object StartMonitoringStreamForChanges (string streamName, StreamChangeCallback callback)
477 throw new NotImplementedException ();
480 public virtual void StopMonitoringStreamForChanges (string streamName, StreamChangeCallback callback)
482 throw new NotImplementedException ();
485 public virtual void VerifyDefinitionAllowed (string configPath, ConfigurationAllowDefinition allowDefinition,
486 ConfigurationAllowExeDefinition allowExeDefinition,
487 IConfigErrorInfo errorInfo)
489 if (!IsDefinitionAllowed (configPath, allowDefinition, allowExeDefinition))
490 throw new ConfigurationErrorsException ("The section can't be defined in this file (the allowed definition context is '" + allowDefinition + "').", errorInfo.Filename, errorInfo.LineNumber);
493 public virtual void WriteCompleted (string streamName, bool success, object writeContext)
495 WriteCompleted (streamName, success, writeContext, false);
498 public virtual void WriteCompleted (string streamName, bool success, object writeContext, bool assertPermissions)
500 // There are probably other things to be done here, but for the moment we
501 // just mark the completed write as one that should not cause application
502 // reload. Note that it might already be too late for suppression, since the
503 // FileSystemWatcher monitor might have already delivered the
504 // notification. If the stream has been open using OpenStreamForWrite then
505 // we're safe, though.
507 if (!IsAboveApplication (streamName))
508 WebConfigurationManager.SuppressAppReload (true);
511 public virtual bool SupportsChangeNotifications {
512 get { return false; }
515 public virtual bool SupportsLocation {
516 get { return false; }
519 public virtual bool SupportsPath {
520 get { return false; }
523 public virtual bool SupportsRefresh {
524 get { return false; }
527 [MonoTODO("Always returns false")]
528 public virtual bool IsRemote {
529 get { return false; }
532 [MonoTODO ("Not implemented")]
533 public virtual bool IsFullTrustSectionWithoutAptcaAllowed (IInternalConfigRecord configRecord)
535 throw new NotImplementedException ();
538 [MonoTODO ("Not implemented")]
539 public virtual bool IsInitDelayed (IInternalConfigRecord configRecord)
541 throw new NotImplementedException ();
544 [MonoTODO ("Not implemented")]
545 public virtual bool IsSecondaryRoot (string configPath)
547 throw new NotImplementedException ();
550 [MonoTODO ("Not implemented")]
551 public virtual bool IsTrustedConfigPath (string configPath)
553 throw new NotImplementedException ();