Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Web / Configuration / ProcessHostMapPath.cs
blobc5de8cbbb46e26a48c573928e43aebfaf4bd4f72
1 //------------------------------------------------------------------------------
2 // <copyright file="ProcessHostMapPath.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6 namespace System.Web.Configuration {
7 using System;
8 using System.Collections;
9 using System.Configuration;
10 using System.Diagnostics.CodeAnalysis;
11 using System.Globalization;
12 using System.IO;
13 using System.Runtime.InteropServices;
14 using System.Text;
15 using System.Web.Caching;
16 using System.Web.Hosting;
17 using System.Web.UI;
18 using System.Web.Util;
19 using System.Xml;
22 // Uses IIS 7 native config
24 internal sealed class ProcessHostMapPath : IConfigMapPath, IConfigMapPath2 {
25 IProcessHostSupportFunctions _functions;
26 internal static string _DefaultPhysicalPathOnMapPathFailure = null;
28 static ProcessHostMapPath() {
29 HttpRuntime.ForceStaticInit();
32 [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "We carefully control this method's caller.")]
33 internal ProcessHostMapPath(IProcessHostSupportFunctions functions) {
34 // if the functions are null and we're
35 // not in a worker process, init the mgdeng config system
36 if (null == functions) {
37 ProcessHostConfigUtils.InitStandaloneConfig();
40 // we need to explicit create a COM proxy in this app domain
41 // so we don't go back to the default domain or have lifetime issues
42 if (null != functions) {
43 _functions = Misc.CreateLocalSupportFunctions(functions);
46 // proactive set the config functions for
47 // webengine in case this is a TCP init path
48 if (null != _functions ) {
49 IntPtr configSystem = _functions.GetNativeConfigurationSystem();
50 Debug.Assert(IntPtr.Zero != configSystem, "null != configSystem");
52 if (IntPtr.Zero != configSystem) {
53 // won't fail if valid pointer
54 // no cleanup needed, we don't own instance
55 UnsafeIISMethods.MgdSetNativeConfiguration(configSystem);
60 string IConfigMapPath.GetMachineConfigFilename() {
61 return HttpConfigurationSystem.MachineConfigurationFilePath;
64 string IConfigMapPath.GetRootWebConfigFilename() {
65 string rootWeb = null;
67 if (null != _functions) {
68 rootWeb = _functions.GetRootWebConfigFilename();
71 if (String.IsNullOrEmpty(rootWeb)) {
72 rootWeb = HttpConfigurationSystem.RootWebConfigurationFilePath;
74 Debug.Trace("MapPath", "ProcHostMP.GetRootWebConfigFilename = " +
75 rootWeb);
77 Debug.Assert(!String.IsNullOrEmpty(rootWeb), "rootWeb != null or empty");
79 return rootWeb;
82 private void GetPathConfigFilenameWorker(string siteID, VirtualPath path, out string directory, out string baseName) {
83 directory = MapPathCaching(siteID, path);
84 if (directory != null) {
85 baseName = HttpConfigurationSystem.WebConfigFileName;
87 else {
88 baseName = null;
91 Debug.Trace("MapPath", "ProcHostMP.GetPathConfigFilename(" + siteID + ", " + path + ")\n" +
92 " result = " + directory + " and " + baseName + "\n");
95 void IConfigMapPath.GetPathConfigFilename(
96 string siteID, string path, out string directory, out string baseName) {
97 GetPathConfigFilenameWorker(siteID, VirtualPath.Create(path), out directory, out baseName);
100 // IConfigMapPath2 VirtualPath variant
101 void IConfigMapPath2.GetPathConfigFilename(
102 string siteID,
103 VirtualPath path,
104 out string directory,
105 out string baseName) {
106 GetPathConfigFilenameWorker(siteID, path, out directory, out baseName);
110 void IConfigMapPath.GetDefaultSiteNameAndID(out string siteName, out string siteID) {
111 Debug.Trace("MapPath", "ProcHostMP.GetDefaultSiteNameAndID\n");
112 siteID = ProcessHostConfigUtils.DEFAULT_SITE_ID_STRING;
113 siteName = ProcessHostConfigUtils.GetSiteNameFromId(ProcessHostConfigUtils.DEFAULT_SITE_ID_UINT);
117 void IConfigMapPath.ResolveSiteArgument(string siteArgument, out string siteName, out string siteID) {
118 Debug.Trace("MapPath", "ProcHostMP.ResolveSiteArgument(" + siteArgument + ")\n");
121 if ( String.IsNullOrEmpty(siteArgument) ||
122 StringUtil.EqualsIgnoreCase(siteArgument, ProcessHostConfigUtils.DEFAULT_SITE_ID_STRING) ||
123 StringUtil.EqualsIgnoreCase(siteArgument, ProcessHostConfigUtils.GetSiteNameFromId(ProcessHostConfigUtils.DEFAULT_SITE_ID_UINT))) {
125 siteName = ProcessHostConfigUtils.GetSiteNameFromId(ProcessHostConfigUtils.DEFAULT_SITE_ID_UINT);
126 siteID = ProcessHostConfigUtils.DEFAULT_SITE_ID_STRING;
128 else {
129 siteName = String.Empty;
130 siteID = String.Empty;
132 string resolvedName = null;
133 if (IISMapPath.IsSiteId(siteArgument)) {
134 uint id;
136 if (UInt32.TryParse(siteArgument, out id)) {
137 resolvedName = ProcessHostConfigUtils.GetSiteNameFromId(id);
140 // try to resolve the string
141 else {
142 uint id = UnsafeIISMethods.MgdResolveSiteName(IntPtr.Zero, siteArgument);
143 if (id != 0) {
144 siteID = id.ToString(CultureInfo.InvariantCulture);
145 siteName = siteArgument;
146 return;
150 if (!String.IsNullOrEmpty(resolvedName)) {
151 siteName = resolvedName;
152 siteID = siteArgument;
154 else {
155 siteName = siteArgument;
156 siteID = String.Empty;
160 Debug.Assert(!String.IsNullOrEmpty(siteName), "!String.IsNullOrEmpty(siteName), siteArg=" + siteArgument);
163 private string MapPathWorker(string siteID, VirtualPath path) {
164 Debug.Trace("MapPath", "ProcHostMP.MapPath(" + siteID + ", " + path + ")\n");
165 return MapPathCaching(siteID, path);
168 // IConfigMapPath2 variant with VirtualPath
169 string IConfigMapPath2.MapPath(string siteID, VirtualPath path) {
170 return MapPathWorker(siteID, path);
173 string IConfigMapPath.MapPath(string siteID, string path) {
174 return MapPathWorker(siteID, VirtualPath.Create(path));
177 string IConfigMapPath.GetAppPathForPath(string siteID, string path) {
178 VirtualPath resolved = GetAppPathForPathWorker(siteID, VirtualPath.Create(path));
179 return resolved.VirtualPathString;
182 // IConfigMapPath2 variant with VirtualPath
183 VirtualPath IConfigMapPath2.GetAppPathForPath(string siteID, VirtualPath path) {
184 return GetAppPathForPathWorker(siteID, path);
187 VirtualPath GetAppPathForPathWorker(string siteID, VirtualPath path) {
188 Debug.Trace("MapPath", "ProcHostMP.GetAppPathForPath(" + siteID + ", " + path.VirtualPathString + ")\n");
190 uint siteValue = 0;
191 if (!UInt32.TryParse(siteID, out siteValue)) {
192 return VirtualPath.RootVirtualPath;
195 IntPtr pBstr = IntPtr.Zero;
196 int cBstr = 0;
197 string appPath;
198 try {
199 int result = UnsafeIISMethods.MgdGetAppPathForPath(IntPtr.Zero, siteValue, path.VirtualPathString, out pBstr, out cBstr);
200 appPath = (result == 0 && cBstr > 0) ? StringUtil.StringFromWCharPtr(pBstr, cBstr) : null;
202 finally {
203 if (pBstr != IntPtr.Zero) {
204 Marshal.FreeBSTR(pBstr);
208 return (appPath != null) ? VirtualPath.Create(appPath) : VirtualPath.RootVirtualPath;
211 private string MapPathCaching(string siteID, VirtualPath path) {
212 // UrlMetaDataSlidingExpiration config setting controls
213 // the duration of all cached items related to url metadata.
214 bool doNotCache = CachedPathData.DoNotCacheUrlMetadata;
215 TimeSpan slidingExpiration = CachedPathData.UrlMetadataSlidingExpiration;
217 // store a single variation of the path
218 VirtualPath originalPath = path;
219 MapPathCacheInfo cacheInfo;
221 if (doNotCache) {
222 cacheInfo = new MapPathCacheInfo();
224 else {
225 // Check if it's in the cache
226 String cacheKey = CacheInternal.PrefixMapPath + siteID + path.VirtualPathString;
227 cacheInfo = (MapPathCacheInfo)HttpRuntime.Cache.InternalCache.Get(cacheKey);
229 // If not in cache, add it to the cache
230 if (cacheInfo == null) {
231 cacheInfo = new MapPathCacheInfo();
232 // Add to the cache.
233 // No need to have a lock here. UtcAdd will add the entry if it doesn't exist.
234 // If it does exist, the existing value will be returned (Dev10 Bug 755034).
235 object existingEntry = HttpRuntime.Cache.InternalCache.Add(cacheKey, cacheInfo, new CacheInsertOptions() { SlidingExpiration = slidingExpiration });
236 if (existingEntry != null) {
237 cacheInfo = existingEntry as MapPathCacheInfo;
242 // If not been evaluated, then evaluate it
243 if (!cacheInfo.Evaluated) {
244 lock(cacheInfo) {
245 if (!cacheInfo.Evaluated && HttpRuntime.IsMapPathRelaxed) {
246 //////////////////////////////////////////////////////////////////
247 // Verify that the parent path is valid. If parent is invalid, then set this to invalid
248 if (path.VirtualPathString.Length > 1) {
249 VirtualPath vParent = path.Parent;
250 if (vParent != null) {
251 string parentPath = vParent.VirtualPathString;
252 if (parentPath.Length > 1 && StringUtil.StringEndsWith(parentPath, '/')) { // Trim the extra trailing / if there is one
253 vParent = VirtualPath.Create(parentPath.Substring(0, parentPath.Length - 1));
255 try {
256 string parentMapPathResult = MapPathCaching(siteID, vParent);
257 if (parentMapPathResult == HttpRuntime.GetRelaxedMapPathResult(null)) {
258 // parent is invalid!
259 cacheInfo.MapPathResult = parentMapPathResult;
260 cacheInfo.Evaluated = true;
262 } catch {
263 cacheInfo.MapPathResult = HttpRuntime.GetRelaxedMapPathResult(null);
264 cacheInfo.Evaluated = true;
270 if (!cacheInfo.Evaluated) {
271 try {
272 string physicalPath = null;
273 uint siteIDValue;
274 if (UInt32.TryParse(siteID, out siteIDValue)) {
275 string siteName = ProcessHostConfigUtils.GetSiteNameFromId(siteIDValue);
276 physicalPath = ProcessHostConfigUtils.MapPathActual(siteName, path);
278 if (physicalPath != null && physicalPath.Length == 2 && physicalPath[1] == ':')
279 physicalPath += "\\";
280 // Throw if the resulting physical path is not canonical, to prevent potential
281 // security issues (VSWhidbey 418125)
283 if (HttpRuntime.IsMapPathRelaxed) {
284 physicalPath = HttpRuntime.GetRelaxedMapPathResult(physicalPath);
287 if (FileUtil.IsSuspiciousPhysicalPath(physicalPath)) {
288 if (HttpRuntime.IsMapPathRelaxed) {
289 physicalPath = HttpRuntime.GetRelaxedMapPathResult(null);
290 } else {
291 throw new HttpException(SR.GetString(SR.Cannot_map_path, path));
295 cacheInfo.MapPathResult = physicalPath;
296 } catch (Exception e) {
297 if (HttpRuntime.IsMapPathRelaxed) {
298 cacheInfo.MapPathResult = HttpRuntime.GetRelaxedMapPathResult(null);
299 } else {
300 cacheInfo.CachedException = e;
301 cacheInfo.Evaluated=true;
302 throw;
306 cacheInfo.Evaluated = true;
311 // Throw an exception if required
312 if (cacheInfo.CachedException != null) {
313 throw cacheInfo.CachedException;
316 return MatchResult(originalPath, cacheInfo.MapPathResult);
319 private string MatchResult(VirtualPath path, string result) {
320 if (string.IsNullOrEmpty(result)) {
321 return result;
324 result = result.Replace('/', '\\');
326 // ensure extra '\\' in the physical path if the virtual path had extra '/'
327 // and the other way -- no extra '\\' in physical if virtual didn't have it.
328 if (path.HasTrailingSlash) {
329 if (!UrlPath.PathEndsWithExtraSlash(result)) {
330 result = result + "\\";
333 else {
334 if (UrlPath.PathEndsWithExtraSlash(result)) {
335 result = result.Substring(0, result.Length - 1);
339 return result;