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, ProtectedConfigurationSection protectedSection)
76 if (protectedSection == null)
77 throw new ArgumentNullException ("protectedSection");
79 return protectedSection.EncryptSection (encryptedXml, protectionProvider);
82 public virtual void DeleteStream (string streamName)
84 File.Delete (streamName);
87 public virtual string EncryptSection (string clearXml, ProtectedConfigurationProvider protectionProvider, ProtectedConfigurationSection protectedSection)
89 if (protectedSection == null)
90 throw new ArgumentNullException ("protectedSection");
92 return protectedSection.EncryptSection (clearXml, protectionProvider);
95 public virtual string GetConfigPathFromLocationSubPath (string configPath, string locationSubPath)
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);
103 string ret = configPath + "/" + locationSubPath;
104 if (!String.IsNullOrEmpty (ret) && ret [0] == '/')
105 return ret.Substring (1);
110 public virtual Type GetConfigType (string typeName, bool throwOnError)
112 Type type = HttpApplication.LoadType (typeName);
113 if (type == null && throwOnError)
114 throw new ConfigurationErrorsException ("Type not found: '" + typeName + "'");
118 public virtual string GetConfigTypeName (Type t)
120 return t.AssemblyQualifiedName;
123 public virtual void GetRestrictedPermissions (IInternalConfigRecord configRecord, out PermissionSet permissionSet,
124 out bool isHostReady)
126 throw new NotImplementedException ();
129 public virtual string GetStreamName (string configPath)
131 if (configPath == MachinePath) {
133 return System.Runtime.InteropServices.RuntimeEnvironment.SystemConfigurationFile;
135 return map.MachineConfigFilename;
136 } else if (configPath == MachineWebPath) {
142 // check META-INF/web.config exists
143 java.lang.ClassLoader cl = (java.lang.ClassLoader) AppDomain.CurrentDomain.GetData ("GH_ContextClassLoader");
146 java.io.InputStream wcs = cl.getResourceAsStream ("META-INF/web.config");
152 return "/META-INF/web.config";
155 mdir = Path.GetDirectoryName (System.Runtime.InteropServices.RuntimeEnvironment.SystemConfigurationFile);
158 mdir = Path.GetDirectoryName (map.MachineConfigFilename);
160 return GetWebConfigFileName (mdir);
163 string dir = MapPath (configPath);
164 return GetWebConfigFileName (dir);
167 public virtual string GetStreamNameForConfigSource (string streamName, string configSource)
169 throw new NotImplementedException ();
172 public virtual object GetStreamVersion (string streamName)
174 throw new NotImplementedException ();
177 public virtual IDisposable Impersonate ()
179 throw new NotImplementedException ();
182 public virtual void Init (IInternalConfigRoot root, params object[] hostInitParams)
186 public virtual void InitForConfiguration (ref string locationSubPath, out string configPath,
187 out string locationConfigPath, IInternalConfigRoot root,
188 params object[] hostInitConfigurationParams)
190 string fullPath = (string) hostInitConfigurationParams [1];
191 map = (WebConfigurationFileMap) hostInitConfigurationParams [0];
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;
203 if (locationSubPath == null) {
204 configPath = fullPath;
205 if (configPath.Length > 1)
206 configPath = VirtualPathUtility.RemoveTrailingSlash (configPath);
208 configPath = locationSubPath;
210 if (configPath == HttpRuntime.AppDomainAppVirtualPath || configPath == "/")
213 i = configPath.LastIndexOf ("/");
216 locationConfigPath = configPath.Substring (i+1);
219 locationSubPath = "/";
221 locationSubPath = fullPath.Substring (0, i);
223 locationSubPath = MachineWebPath;
224 locationConfigPath = null;
229 public string MapPath (string virtualPath)
231 if (!String.IsNullOrEmpty (virtualPath)) {
232 if (virtualPath.StartsWith (System.Web.Compilation.BuildManager.FAKE_VIRTUAL_PATH_PREFIX, StringComparison.Ordinal))
233 return HttpRuntime.AppDomainAppPath;
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));
251 public string NormalizeVirtualPath (string virtualPath)
253 if (virtualPath == null || virtualPath.Length == 0)
256 virtualPath = virtualPath.Trim ();
258 if (virtualPath [0] == '~' && virtualPath.Length > 2 && virtualPath [1] == '/')
259 virtualPath = virtualPath.Substring (1);
261 if (System.IO.Path.DirectorySeparatorChar != '/')
262 virtualPath = virtualPath.Replace (System.IO.Path.DirectorySeparatorChar, '/');
264 if (UrlUtils.IsRooted (virtualPath)) {
265 virtualPath = UrlUtils.Canonic (virtualPath);
267 if (map.VirtualDirectories.Count > 0) {
268 string root = map.VirtualDirectories [0].VirtualDirectory;
269 virtualPath = UrlUtils.Combine (root, virtualPath);
270 virtualPath = UrlUtils.Canonic (virtualPath);
276 public string MapPathFromMapper (string virtualPath)
278 string path = NormalizeVirtualPath (virtualPath);
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;
286 else if (path [i] == '/') {
287 string pathPart = path.Substring (i + 1).Replace ('/', Path.DirectorySeparatorChar);
288 return Path.Combine (mapping.PhysicalDirectory, pathPart);
292 throw new HttpException ("Invalid virtual directory: " + virtualPath);
295 internal static string GetWebConfigFileName (string dir)
298 DirectoryInfo d = GetCaseSensitiveExistingDirectory (new DirectoryInfo (dir));
302 FileInfo file = (FileInfo) FindByName ("web.config", d.GetFiles ("W*"));
304 file = (FileInfo) FindByName ("web.config", d.GetFiles ("w*"));
307 return file.FullName;
309 AppDomain domain = AppDomain.CurrentDomain;
310 bool hosted = (domain.GetData (ApplicationHost.MonoHostedDataKey) as string) == "yes";
313 foreach (string fn in ApplicationHost.WebConfigFileNames) {
314 string file = Path.Combine (dir, fn);
315 if (File.Exists (file))
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;
325 foreach (string fn in fileNames) {
326 file = Path.Combine (appDir, fn);
327 if (File.Exists (file))
335 static DirectoryInfo GetCaseSensitiveExistingDirectory (DirectoryInfo dir) {
341 DirectoryInfo parent = GetCaseSensitiveExistingDirectory (dir.Parent);
345 return (DirectoryInfo) FindByName (dir.Name, parent.GetDirectories ());
348 static FileSystemInfo FindByName (string name, FileSystemInfo [] infos)
350 for (int i = 0; i < infos.Length; i++) {
351 if (String.Compare (name, infos [i].Name, StringComparison.OrdinalIgnoreCase) == 0)
357 public virtual bool IsAboveApplication (string configPath)
359 throw new NotImplementedException ();
362 public virtual bool IsConfigRecordRequired (string configPath)
364 throw new NotImplementedException ();
367 public virtual bool IsDefinitionAllowed (string configPath, ConfigurationAllowDefinition allowDefinition,
368 ConfigurationAllowExeDefinition allowExeDefinition)
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))
379 if (VirtualPathUtility.IsRooted (configPath))
380 normalized = VirtualPathUtility.Normalize (configPath);
382 normalized = configPath;
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);
394 public virtual bool IsFile (string streamName)
396 throw new NotImplementedException ();
399 public virtual bool IsLocationApplicable (string configPath)
401 throw new NotImplementedException ();
404 public virtual Stream OpenStreamForRead (string streamName)
406 if (!File.Exists (streamName)) {
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");
414 java.io.InputStream inputStream = cl.getResourceAsStream (streamName);
415 return new System.Web.J2EE.J2EEUtils.InputStreamWrapper (inputStream);
419 throw new ConfigurationException ("File '" + streamName + "' not found");
422 return new FileStream (streamName, FileMode.Open, FileAccess.Read);
425 [MonoTODO ("Not implemented")]
426 public virtual Stream OpenStreamForRead (string streamName, bool assertPermissions)
428 throw new NotImplementedException ();
431 public virtual Stream OpenStreamForWrite (string streamName, string templateStreamName, ref object writeContext)
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);
439 [MonoTODO ("Not implemented")]
440 public virtual Stream OpenStreamForWrite (string streamName, string templateStreamName, ref object writeContext,
441 bool assertPermissions)
443 throw new NotImplementedException ();
446 public virtual bool PrefetchAll (string configPath, string streamName)
448 throw new NotImplementedException ();
451 public virtual bool PrefetchSection (string sectionGroupName, string sectionName)
453 throw new NotImplementedException ();
456 [MonoTODO ("Not implemented")]
457 public virtual void RequireCompleteInit (IInternalConfigRecord configRecord)
459 throw new NotImplementedException ();
462 public virtual object StartMonitoringStreamForChanges (string streamName, StreamChangeCallback callback)
464 throw new NotImplementedException ();
467 public virtual void StopMonitoringStreamForChanges (string streamName, StreamChangeCallback callback)
469 throw new NotImplementedException ();
472 public virtual void VerifyDefinitionAllowed (string configPath, ConfigurationAllowDefinition allowDefinition,
473 ConfigurationAllowExeDefinition allowExeDefinition,
474 IConfigErrorInfo errorInfo)
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);
480 public virtual void WriteCompleted (string streamName, bool success, object writeContext)
482 WriteCompleted (streamName, success, writeContext, false);
485 public virtual void WriteCompleted (string streamName, bool success, object writeContext, bool assertPermissions)
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);
498 public virtual bool SupportsChangeNotifications {
499 get { return false; }
502 public virtual bool SupportsLocation {
503 get { return false; }
506 public virtual bool SupportsPath {
507 get { return false; }
510 public virtual bool SupportsRefresh {
511 get { return false; }
514 [MonoTODO("Always returns false")]
515 public virtual bool IsRemote {
516 get { return false; }
519 [MonoTODO ("Not implemented")]
520 public virtual bool IsFullTrustSectionWithoutAptcaAllowed (IInternalConfigRecord configRecord)
522 throw new NotImplementedException ();
525 [MonoTODO ("Not implemented")]
526 public virtual bool IsInitDelayed (IInternalConfigRecord configRecord)
528 throw new NotImplementedException ();
531 [MonoTODO ("Not implemented")]
532 public virtual bool IsSecondaryRoot (string configPath)
534 throw new NotImplementedException ();
537 [MonoTODO ("Not implemented")]
538 public virtual bool IsTrustedConfigPath (string configPath)
540 throw new NotImplementedException ();