1 //------------------------------------------------------------------------------
2 // <copyright file="MetabaseServerConfig.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
7 namespace System.Web.Configuration {
9 using System.Configuration;
10 using System.Collections;
11 using System.Globalization;
14 using System.Web.Util;
15 using System.Web.Hosting;
16 using System.Web.Caching;
17 using Microsoft.Win32;
19 class MetabaseServerConfig : IServerConfig, IConfigMapPath, IConfigMapPath2 {
20 private const string DEFAULT_SITEID = "1";
21 private const string DEFAULT_ROOTAPPID = "/LM/W3SVC/1/ROOT";
22 private const int MAX_PATH=260;
23 private const int BUFSIZE = MAX_PATH + 1;
24 private const string LMW3SVC_PREFIX = "/LM/W3SVC/";
25 private const string ROOT_SUFFIX="/ROOT";
27 static private MetabaseServerConfig s_instance;
28 static private object s_initLock = new Object();
30 string _defaultSiteName;
31 string _siteIdForCurrentApplication;
33 static internal IServerConfig GetInstance() {
34 if (s_instance == null) {
36 if (s_instance == null) {
37 s_instance = new MetabaseServerConfig();
45 private MetabaseServerConfig() {
46 HttpRuntime.ForceStaticInit(); // force webengine.dll to load
48 // Get the default site information
49 bool found = MBGetSiteNameFromSiteID(DEFAULT_SITEID, out _defaultSiteName);
50 _siteIdForCurrentApplication = HostingEnvironment.SiteID;
51 if (_siteIdForCurrentApplication == null) {
52 _siteIdForCurrentApplication = DEFAULT_SITEID;
56 string IServerConfig.GetSiteNameFromSiteID(string siteID) {
57 if (StringUtil.EqualsIgnoreCase(siteID, DEFAULT_SITEID))
58 return _defaultSiteName;
61 bool found = MBGetSiteNameFromSiteID(siteID, out siteName);
65 // if appHost is null, we use the site ID for the current application
66 string IServerConfig.MapPath(IApplicationHost appHost, VirtualPath path) {
67 string siteID = (appHost == null) ? _siteIdForCurrentApplication : appHost.GetSiteID();
68 return MapPathCaching(siteID, path);
71 string[] IServerConfig.GetVirtualSubdirs(VirtualPath path, bool inApp) {
72 string aboPath = GetAboPath(_siteIdForCurrentApplication, path.VirtualPathString);
73 return MBGetVirtualSubdirs(aboPath, inApp);
76 bool IServerConfig.GetUncUser(IApplicationHost appHost, VirtualPath path, out string username, out string password) {
77 string aboPath = GetAboPath(appHost.GetSiteID(), path.VirtualPathString);
78 return MBGetUncUser(aboPath, out username, out password);
81 long IServerConfig.GetW3WPMemoryLimitInKB() {
82 return (long) MBGetW3WPMemoryLimitInKB();
85 string IConfigMapPath.GetMachineConfigFilename() {
86 return HttpConfigurationSystem.MachineConfigurationFilePath;
89 string IConfigMapPath.GetRootWebConfigFilename() {
90 return HttpConfigurationSystem.RootWebConfigurationFilePath;
93 private void GetPathConfigFilenameWorker(string siteID, VirtualPath path, out string directory, out string baseName) {
94 directory = MapPathCaching(siteID, path);
95 if (directory != null) {
96 baseName = HttpConfigurationSystem.WebConfigFileName;
103 // Based on <siteID, path>, return:
104 // directory - the physical directory of the path (vpath)
105 // baseName - name of the configuration file to look for.
106 // E.g. if siteID="1" and path="/", directory="c:\inetpub\wwwroot" and baseName="web.config"
107 void IConfigMapPath.GetPathConfigFilename(
108 string siteID, string path, out string directory, out string baseName) {
109 GetPathConfigFilenameWorker(siteID, VirtualPath.Create(path), out directory, out baseName);
112 void IConfigMapPath2.GetPathConfigFilename(
113 string siteID, VirtualPath path, out string directory, out string baseName) {
114 GetPathConfigFilenameWorker(siteID, path, out directory, out baseName);
117 void IConfigMapPath.GetDefaultSiteNameAndID(out string siteName, out string siteID) {
118 siteName = _defaultSiteName;
119 siteID = DEFAULT_SITEID;
122 void IConfigMapPath.ResolveSiteArgument(string siteArgument, out string siteName, out string siteID) {
123 if ( String.IsNullOrEmpty(siteArgument) ||
124 StringUtil.EqualsIgnoreCase(siteArgument, DEFAULT_SITEID) ||
125 StringUtil.EqualsIgnoreCase(siteArgument, _defaultSiteName)) {
127 siteName = _defaultSiteName;
128 siteID = DEFAULT_SITEID;
131 siteName = String.Empty;
132 siteID = String.Empty;
135 if (IISMapPath.IsSiteId(siteArgument)) {
136 found = MBGetSiteNameFromSiteID(siteArgument, out siteName);
140 siteID = siteArgument;
143 found = MBGetSiteIDFromSiteName(siteArgument, out siteID);
145 siteName = siteArgument;
148 siteName = siteArgument;
149 siteID = String.Empty;
155 // Map from <siteID, path> to a physical file path
156 string IConfigMapPath.MapPath(string siteID, string vpath) {
157 return MapPathCaching(siteID, VirtualPath.Create(vpath));
160 // IConfigMapPath2 VirtualPath fast path
161 string IConfigMapPath2.MapPath(string siteID, VirtualPath vpath) {
162 return MapPathCaching(siteID, vpath);
165 VirtualPath GetAppPathForPathWorker(string siteID, VirtualPath vpath) {
166 string aboPath = GetAboPath(siteID, vpath.VirtualPathString);
167 string appAboPath = MBGetAppPath(aboPath);
168 if (appAboPath == null)
169 return VirtualPath.RootVirtualPath;
171 string rootAboPath = GetRootAppIDFromSiteID(siteID);
172 if (StringUtil.EqualsIgnoreCase(rootAboPath, appAboPath)) {
173 return VirtualPath.RootVirtualPath;
176 string appPath = appAboPath.Substring(rootAboPath.Length);
177 return VirtualPath.CreateAbsolute(appPath);
180 string IConfigMapPath.GetAppPathForPath(string siteID, string vpath) {
181 VirtualPath resolved = GetAppPathForPathWorker(siteID, VirtualPath.Create(vpath));
182 return resolved.VirtualPathString;
185 // IConfigMapPath2 VirtualPath fast path
186 VirtualPath IConfigMapPath2.GetAppPathForPath(string siteID, VirtualPath vpath) {
187 return GetAppPathForPathWorker(siteID, vpath);
190 private string MatchResult(VirtualPath path, string result) {
191 if (string.IsNullOrEmpty(result)) {
195 result = result.Replace('/', '\\');
197 // ensure extra '\\' in the physical path if the virtual path had extra '/'
198 // and the other way -- no extra '\\' in physical if virtual didn't have it.
199 if (path.HasTrailingSlash) {
200 if (!UrlPath.PathEndsWithExtraSlash(result) && !UrlPath.PathIsDriveRoot(result)) {
201 result = result + "\\";
205 if (UrlPath.PathEndsWithExtraSlash(result) && !UrlPath.PathIsDriveRoot(result)) {
206 result = result.Substring(0, result.Length - 1);
213 private string MapPathCaching(string siteID, VirtualPath path) {
214 // UrlMetaDataSlidingExpiration config setting controls
215 // the duration of all cached items related to url metadata.
216 bool doNotCache = CachedPathData.DoNotCacheUrlMetadata;
217 TimeSpan slidingExpiration = CachedPathData.UrlMetadataSlidingExpiration;
219 // store a single variation of the path
220 VirtualPath originalPath = path;
221 MapPathCacheInfo cacheInfo;
224 cacheInfo = new MapPathCacheInfo();
227 // Check if it's in the cache
228 String cacheKey = CacheInternal.PrefixMapPath + siteID + path.VirtualPathString;
229 cacheInfo = (MapPathCacheInfo)HttpRuntime.Cache.InternalCache.Get(cacheKey);
231 // If not in cache, add it to the cache
232 if (cacheInfo == null) {
233 cacheInfo = new MapPathCacheInfo();
235 // No need to have a lock here. UtcAdd will add the entry if it doesn't exist.
236 // If it does exist, the existing value will be returned (Dev10 Bug 755034).
237 object existingEntry = HttpRuntime.Cache.InternalCache.Add(cacheKey, cacheInfo, new CacheInsertOptions() { SlidingExpiration = slidingExpiration });
238 if (existingEntry != null) {
239 cacheInfo = existingEntry as MapPathCacheInfo;
244 // If not been evaluated, then evaluate it
245 if (!cacheInfo.Evaluated) {
248 if (!cacheInfo.Evaluated && HttpRuntime.IsMapPathRelaxed) {
249 //////////////////////////////////////////////////////////////////
250 // Verify that the parent path is valid. If parent is invalid, then set this to invalid
251 if (path.VirtualPathString.Length > 1) {
252 VirtualPath vParent = path.Parent;
253 if (vParent != null) {
254 string parentPath = vParent.VirtualPathString;
255 if (parentPath.Length > 1 && StringUtil.StringEndsWith(parentPath, '/')) { // Trim the extra trailing / if there is one
256 vParent = VirtualPath.Create(parentPath.Substring(0, parentPath.Length - 1));
259 string parentMapPathResult = MapPathCaching(siteID, vParent);
260 if (parentMapPathResult == HttpRuntime.GetRelaxedMapPathResult(null)) {
261 // parent is invalid!
262 cacheInfo.MapPathResult = parentMapPathResult;
263 cacheInfo.Evaluated = true;
266 cacheInfo.MapPathResult = HttpRuntime.GetRelaxedMapPathResult(null);
267 cacheInfo.Evaluated = true;
273 if (!cacheInfo.Evaluated) {
274 string physicalPath = null;
277 physicalPath = MapPathActual(siteID, path);
279 if (HttpRuntime.IsMapPathRelaxed) {
280 physicalPath = HttpRuntime.GetRelaxedMapPathResult(physicalPath);
283 // Throw if the resulting physical path is not canonical, to prevent potential
284 // security issues (VSWhidbey 418125)
285 if (FileUtil.IsSuspiciousPhysicalPath(physicalPath)) {
286 if (HttpRuntime.IsMapPathRelaxed) {
287 physicalPath = HttpRuntime.GetRelaxedMapPathResult(null);
289 throw new HttpException(SR.GetString(SR.Cannot_map_path, path));
293 } catch (Exception e) {
294 if (HttpRuntime.IsMapPathRelaxed) {
295 physicalPath = HttpRuntime.GetRelaxedMapPathResult(null);
297 cacheInfo.CachedException = e;
298 cacheInfo.Evaluated=true;
303 if ( physicalPath != null ) {
304 // Only cache if we got a good value
305 cacheInfo.MapPathResult = physicalPath;
306 cacheInfo.Evaluated = true;
312 // Throw an exception if required
313 if (cacheInfo.CachedException != null) {
314 throw cacheInfo.CachedException;
317 return MatchResult(originalPath, cacheInfo.MapPathResult);
320 private string MapPathActual(string siteID, VirtualPath path) {
321 string appID = GetRootAppIDFromSiteID(siteID);
322 string physicalPath = MBMapPath(appID, path.VirtualPathString);
326 private string GetRootAppIDFromSiteID(string siteId) {
327 return LMW3SVC_PREFIX + siteId + ROOT_SUFFIX;
330 private string GetAboPath(string siteID, string path) {
331 string rootAppID = GetRootAppIDFromSiteID(siteID);
332 string aboPath = rootAppID + FixupPathSlash(path);
336 private string FixupPathSlash(string path) {
342 if (l == 0 || path[l-1] != '/') {
346 return path.Substring(0, l-1);
350 // Metabase access functions
352 private bool MBGetSiteNameFromSiteID(string siteID, out string siteName) {
353 string appID = GetRootAppIDFromSiteID(siteID);
354 StringBuilder sb = new StringBuilder(BUFSIZE);
355 int r = UnsafeNativeMethods.IsapiAppHostGetSiteName(appID, sb, sb.Capacity);
357 siteName = sb.ToString();
361 siteName = String.Empty;
366 private bool MBGetSiteIDFromSiteName(string siteName, out string siteID) {
367 StringBuilder sb = new StringBuilder(BUFSIZE);
368 int r = UnsafeNativeMethods.IsapiAppHostGetSiteId(siteName, sb, sb.Capacity);
370 siteID = sb.ToString();
374 siteID = String.Empty;
380 private string MBMapPath(string appID, string path) {
381 // keep growing the buffer to support paths longer than MAX_PATH
382 int bufSize = BUFSIZE;
387 sb = new StringBuilder(bufSize);
388 r = UnsafeNativeMethods.IsapiAppHostMapPath(appID, path, sb, sb.Capacity);
389 Debug.Trace("MapPath", "IsapiAppHostMapPath(" + path + ") returns " + r);
392 // insufficient buffer
401 // special case access denied error
402 throw new HostingEnvironmentException(
403 SR.GetString(SR.Cannot_access_mappath_title),
404 SR.GetString(SR.Cannot_access_mappath_details));
409 physicalPath = sb.ToString();
418 private string[] MBGetVirtualSubdirs(string aboPath, bool inApp) {
419 StringBuilder sb = new StringBuilder(BUFSIZE);
421 ArrayList list = new ArrayList();
424 int r = UnsafeNativeMethods.IsapiAppHostGetNextVirtualSubdir(aboPath, inApp, ref index, sb, sb.Capacity);
428 string subdir = sb.ToString();
432 string[] subdirs = new string[list.Count];
433 list.CopyTo(subdirs);
437 private bool MBGetUncUser(string aboPath, out string username, out string password) {
438 StringBuilder usr = new StringBuilder(BUFSIZE);
439 StringBuilder pwd = new StringBuilder(BUFSIZE);
440 int r = UnsafeNativeMethods.IsapiAppHostGetUncUser(aboPath, usr, usr.Capacity, pwd, pwd.Capacity);
442 username = usr.ToString();
443 password = pwd.ToString();
453 private int MBGetW3WPMemoryLimitInKB() {
454 return UnsafeNativeMethods.GetW3WPMemoryLimitInKB();
457 private string MBGetAppPath(string aboPath) {
458 StringBuilder buf = new StringBuilder(aboPath.Length + 1);
459 int r = UnsafeNativeMethods.IsapiAppHostGetAppPath(aboPath, buf, buf.Capacity);
462 appAboPath = buf.ToString();