VirtualPath.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / whidbey / REDBITS / ndp / fx / src / xsp / System / Web / VirtualPath.cs / 5 / VirtualPath.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

namespace System.Web { 
 
    using System.Globalization;
    using System.Collections; 
    using System.IO;
    using System.Web.Util;
    using System.Web.Hosting;
    using System.Web.Caching; 
    using System.Security.Permissions;
    using Microsoft.Win32; 
 
    [Serializable]
    internal sealed class VirtualPath : IComparable { 

        private static char[] s_illegalVirtualPathChars           = new char[] { ':', '?', '*', '\0' };
        private static char[] s_illegalVirtualPathChars_VerCompat = new char[] { '\0' };
        private static bool   s_VerCompatRegLookedUp              = false; 
        private static object s_VerCompatLock                     = new object();
 
        private string _appRelativeVirtualPath; 
        private string _virtualPath;
 
        // const masks into the BitVector32
        private const int isWithinAppRootComputed           = 0x00000001;
        private const int isWithinAppRoot                   = 0x00000002;
        private const int appRelativeAttempted              = 0x00000004; 

    #pragma warning disable 0649 
        private SimpleBitVector32 flags; 
    #pragma warning restore 0649
 
        internal static VirtualPath RootVirtualPath = VirtualPath.Create("/");

        private VirtualPath() { }
 
        // This is called to set the appropriate virtual path field when we already know
        // that the path is generally well formed. 
        private VirtualPath(string virtualPath) { 
            if (UrlPath.IsAppRelativePath(virtualPath)) {
                _appRelativeVirtualPath = virtualPath; 
            }
            else {
                _virtualPath = virtualPath;
            } 
        }
 
        int IComparable.CompareTo(object obj) { 

            VirtualPath virtualPath = obj as VirtualPath; 

            // Make sure we're compared to another VirtualPath
            if (virtualPath == null)
                throw new ArgumentException(); 

            // Check if it's the same object 
            if (virtualPath == this) 
                return 0;
 
            return StringComparer.InvariantCultureIgnoreCase.Compare(
                this.VirtualPathString, virtualPath.VirtualPathString);
        }
 
        public string VirtualPathString {
            get { 
                if (_virtualPath == null) { 
                    Debug.Assert(_appRelativeVirtualPath != null);
 
                    // This is not valid if we don't know the app path
                    if (HttpRuntime.AppDomainAppVirtualPathObject == null) {
                        throw new HttpException(SR.GetString(SR.VirtualPath_CantMakeAppAbsolute,
                            _appRelativeVirtualPath)); 
                    }
 
                    if (_appRelativeVirtualPath.Length == 1) { 
                        _virtualPath = HttpRuntime.AppDomainAppVirtualPath;
                    } 
                    else {
                        _virtualPath = HttpRuntime.AppDomainAppVirtualPathString +
                            _appRelativeVirtualPath.Substring(2);
                    } 
                }
 
                return _virtualPath; 
            }
        } 

        internal string VirtualPathStringNoTrailingSlash {
            get {
                return UrlPath.RemoveSlashFromPathIfNeeded(VirtualPathString); 
            }
        } 
 
        // Return the virtual path string if we have it, otherwise null
        internal string VirtualPathStringIfAvailable { 
            get {
                return _virtualPath;
            }
        } 

        internal string AppRelativeVirtualPathStringOrNull { 
            get { 
                if (_appRelativeVirtualPath == null) {
                    Debug.Assert(_virtualPath != null); 

                    // If we already tried to get it and couldn't, return null
                    if (flags[appRelativeAttempted])
                        return null; 

                    // This is not valid if we don't know the app path 
                    if (HttpRuntime.AppDomainAppVirtualPathObject == null) { 
                        throw new HttpException(SR.GetString(SR.VirtualPath_CantMakeAppRelative, _virtualPath));
                    } 

                    _appRelativeVirtualPath = UrlPath.MakeVirtualPathAppRelativeOrNull(_virtualPath);

                    // Remember that we've attempted it 
                    flags[appRelativeAttempted] = true;
 
                    // It could be null if it's not under the app root 
                    if (_appRelativeVirtualPath == null)
                        return null; 

                    ValidateState();
                }
 
                return _appRelativeVirtualPath;
            } 
        } 

        // Return the app relative path if possible. Otherwise, settle for the absolute. 
        public string AppRelativeVirtualPathString {
            get {
                string appRelativeVirtualPath = AppRelativeVirtualPathStringOrNull;
                return (appRelativeVirtualPath != null) ? appRelativeVirtualPath : _virtualPath; 
            }
        } 
 
        // Return the app relative virtual path string if we have it, otherwise null
        internal string AppRelativeVirtualPathStringIfAvailable { 
            get {
                return _appRelativeVirtualPath;
            }
        } 
        // Return the virtual string that's either app relative or not, depending on which
        // one we already have internally.  If we have both, we return absolute 
        internal string VirtualPathStringWhicheverAvailable { 
            get {
                return _virtualPath != null ? _virtualPath : _appRelativeVirtualPath; 
            }
        }

        public string Extension { 
            get {
                return UrlPath.GetExtension(VirtualPathString); 
            } 
        }
 
        public string FileName {
            get {
                return UrlPath.GetFileName(VirtualPathStringNoTrailingSlash);
            } 
        }
 
        // If it's relative, combine it with the app root 
        public VirtualPath CombineWithAppRoot() {
            return HttpRuntime.AppDomainAppVirtualPathObject.Combine(this); 
        }

        public VirtualPath Combine(VirtualPath relativePath) {
            if (relativePath == null) 
                throw new ArgumentNullException("relativePath");
 
            // If it's not relative, return it unchanged 
            if (!relativePath.IsRelative)
                return relativePath; 

            // The base of the combine should never be relative
            FailIfRelativePath();
 
            // Get either _appRelativeVirtualPath or _virtualPath
            string virtualPath = VirtualPathStringWhicheverAvailable; 
 
            // Combine it with the relative
            virtualPath = UrlPath.Combine(virtualPath, relativePath.VirtualPathString); 

            // Set the appropriate virtual path in the new object
            return new VirtualPath(virtualPath);
        } 

        // This simple version of combine should only be used when the relative 
        // path is known to be relative.  It's more efficient, but doesn't do any 
        // sanity checks.
        internal VirtualPath SimpleCombine(string relativePath) { 
            return SimpleCombine(relativePath, false /*addTrailingSlash*/);
        }

        internal VirtualPath SimpleCombineWithDir(string directoryName) { 
            return SimpleCombine(directoryName, true /*addTrailingSlash*/);
        } 
 
        private VirtualPath SimpleCombine(string filename, bool addTrailingSlash) {
 
            // The left part should always be a directory
            Debug.Assert(HasTrailingSlash);

            // The right part should not start or end with a slash 
            Debug.Assert(filename[0] != '/' && !UrlPath.HasTrailingSlash(filename));
 
            // Use either _appRelativeVirtualPath or _virtualPath 
            string virtualPath = VirtualPathStringWhicheverAvailable + filename;
            if (addTrailingSlash) 
                virtualPath += "/";

            // Set the appropriate virtual path in the new object
            VirtualPath combinedVirtualPath = new VirtualPath(virtualPath); 

            // Copy some flags over to avoid having to recalculate them 
            combinedVirtualPath.CopyFlagsFrom(this, isWithinAppRootComputed | isWithinAppRoot | appRelativeAttempted); 

            combinedVirtualPath.ValidateState(); 

            return combinedVirtualPath;
        }
 
        public VirtualPath MakeRelative(VirtualPath toVirtualPath) {
            VirtualPath resultVirtualPath = new VirtualPath(); 
 
            // Neither path can be relative
            FailIfRelativePath(); 
            toVirtualPath.FailIfRelativePath();

            // Set it directly since we know the slashes are already ok
            resultVirtualPath._virtualPath = UrlPath.MakeRelative(this.VirtualPathString, toVirtualPath.VirtualPathString); 

            resultVirtualPath.ValidateState(); 
 
            return resultVirtualPath;
        } 

        public string MapPath() {
            return HostingEnvironment.MapPath(this);
        } 

        internal string MapPathInternal() { 
            return HostingEnvironment.MapPathInternal(this); 
        }
 
        internal string MapPathInternal(bool permitNull) {
            return HostingEnvironment.MapPathInternal(this, permitNull);
        }
 
        internal string MapPathInternal(VirtualPath baseVirtualDir, bool allowCrossAppMapping) {
            return HostingEnvironment.MapPathInternal(this, baseVirtualDir, allowCrossAppMapping); 
        } 

        ///////////// VirtualPathProvider wrapper methods ///////////// 

        public string GetFileHash(IEnumerable virtualPathDependencies) {
            return HostingEnvironment.VirtualPathProvider.GetFileHash(this, virtualPathDependencies);
        } 

        public CacheDependency GetCacheDependency(IEnumerable virtualPathDependencies, DateTime utcStart) { 
            return HostingEnvironment.VirtualPathProvider.GetCacheDependency( 
                this, virtualPathDependencies, utcStart);
        } 

        public bool FileExists() {
            return HostingEnvironment.VirtualPathProvider.FileExists(this);
        } 

        public bool DirectoryExists() { 
            return HostingEnvironment.VirtualPathProvider.DirectoryExists(this); 
        }
 
        public VirtualFile GetFile() {
            return HostingEnvironment.VirtualPathProvider.GetFile(this);
        }
 
        public VirtualDirectory GetDirectory() {
            Debug.Assert(this.HasTrailingSlash); 
            return HostingEnvironment.VirtualPathProvider.GetDirectory(this); 
        }
 
        public string GetCacheKey() {
            return HostingEnvironment.VirtualPathProvider.GetCacheKey(this);
        }
 
        public Stream OpenFile() {
            return VirtualPathProvider.OpenFile(this); 
        } 

        ///////////// end of VirtualPathProvider methods ///////////// 


        internal bool HasTrailingSlash {
            get { 
                if (_virtualPath != null) {
                    return UrlPath.HasTrailingSlash(_virtualPath); 
                } 
                else {
                    return UrlPath.HasTrailingSlash(_appRelativeVirtualPath); 
                }
            }
        }
 
        public bool IsWithinAppRoot {
            get { 
                // If we don't already know it, compute it and cache it 
                if (!flags[isWithinAppRootComputed]) {
                    if (HttpRuntime.AppDomainIdInternal == null) { 
                        Debug.Assert(false);
                        return true;    // app domain not initialized
                    }
 
                    if (flags[appRelativeAttempted]) {
                        // If we already tried to get the app relative path, we can tell whether 
                        // it's in the app root by checking whether it's not null 
                        flags[isWithinAppRoot] = (_appRelativeVirtualPath != null);
                    } 
                    else {
                        flags[isWithinAppRoot] = UrlPath.IsEqualOrSubpath(HttpRuntime.AppDomainAppVirtualPathString,
                            VirtualPathString);
                    } 

                    flags[isWithinAppRootComputed] = true; 
                } 

                return flags[isWithinAppRoot]; 
            }
        }

        internal void FailIfNotWithinAppRoot() { 
            if (!this.IsWithinAppRoot) {
                throw new ArgumentException(SR.GetString(SR.Cross_app_not_allowed, this.VirtualPathString)); 
            } 
        }
 
        internal void FailIfRelativePath() {
            if (this.IsRelative) {
                throw new ArgumentException(SR.GetString(SR.VirtualPath_AllowRelativePath, _virtualPath));
            } 
        }
 
        public bool IsRelative { 
            get {
                // Note that we don't need to check for "~/", since _virtualPath never contains 
                // app relative paths (_appRelativeVirtualPath does)
                return _virtualPath != null && _virtualPath[0] != '/';
            }
        } 

        public bool IsRoot { 
            get { 
                return _virtualPath == "/";
            } 
        }

        public VirtualPath Parent {
            get { 
                // Getting the parent doesn't make much sense on relative paths
                FailIfRelativePath(); 
 
                // "/" doesn't have a parent, so return null
                if (IsRoot) 
                    return null;

                // Get either _appRelativeVirtualPath or _virtualPath
                string virtualPath = VirtualPathStringWhicheverAvailable; 

                // Get rid of the ending slash, otherwise we end up with Parent("/app/sub/") == "/app/sub/" 
                virtualPath = UrlPath.RemoveSlashFromPathIfNeeded(virtualPath); 

                // But if it's just "~", use the absolute path instead to get the parent 
                if (virtualPath == "~")
                    virtualPath = VirtualPathStringNoTrailingSlash;

                int index = virtualPath.LastIndexOf('/'); 
                Debug.Assert(index >= 0);
 
                // e.g. the parent of "/blah" is "/" 
                if (index == 0)
                    return RootVirtualPath; 

                //

                // Get the parent 
                virtualPath = virtualPath.Substring(0, index + 1);
 
                // Set the appropriate virtual path in the new object 
                return new VirtualPath(virtualPath);
            } 
        }

        internal static VirtualPath Combine(VirtualPath v1, VirtualPath v2) {
 
            // If the first is null, use the app root instead
            if (v1 == null) { 
                v1 = HttpRuntime.AppDomainAppVirtualPathObject; 
            }
 
            // If the first is still null, return the second, unless it's relative
            if (v1 == null) {
                v2.FailIfRelativePath();
                return v2; 
            }
 
            return v1.Combine(v2); 
        }
 
        public static bool operator == (VirtualPath v1, VirtualPath v2) {
            return VirtualPath.Equals(v1, v2);
        }
 
        public static bool operator != (VirtualPath v1, VirtualPath v2) {
            return !VirtualPath.Equals(v1, v2); 
        } 

        public static bool Equals(VirtualPath v1, VirtualPath v2) { 

            // Check if it's the same object
            if ((Object)v1 == (Object)v2) {
                return true; 
            }
 
            if ((Object)v1 == null || (Object)v2 == null) { 
                return false;
            } 

            return EqualsHelper(v1, v2);
        }
 
        public override bool Equals(object value) {
 
            if (value == null) 
                return false;
 
            VirtualPath virtualPath = value as VirtualPath;
            if ((object)virtualPath == null) {
                Debug.Assert(false);
                return false; 
            }
 
            return EqualsHelper(virtualPath, this); 
        }
 
        private static bool EqualsHelper(VirtualPath v1, VirtualPath v2) {
            return StringComparer.InvariantCultureIgnoreCase.Compare(
                v1.VirtualPathString, v2.VirtualPathString) == 0;
        } 

        public override int GetHashCode() { 
            return StringComparer.InvariantCultureIgnoreCase.GetHashCode(VirtualPathString); 
        }
 
        public override String ToString() {
            // If we only have the app relative path, and we don't know the app root, return
            // the app relative path instead of accessing VirtualPathString, which would throw
            if (_virtualPath == null && HttpRuntime.AppDomainAppVirtualPathObject == null) { 
                Debug.Assert(_appRelativeVirtualPath != null);
                return _appRelativeVirtualPath; 
            } 

            return VirtualPathString; 
        }

        // Copy a set of flags from another VirtualPath object
        private void CopyFlagsFrom(VirtualPath virtualPath, int mask) { 
            flags.IntegerValue |= virtualPath.flags.IntegerValue & mask;
        } 
 
        internal static string GetVirtualPathString(VirtualPath virtualPath) {
            return virtualPath == null ? null : virtualPath.VirtualPathString; 
        }

        internal static string GetVirtualPathStringNoTrailingSlash(VirtualPath virtualPath) {
            return virtualPath == null ? null : virtualPath.VirtualPathStringNoTrailingSlash; 
        }
 
        internal static string GetAppRelativeVirtualPathString(VirtualPath virtualPath) { 
            return virtualPath == null ? null : virtualPath.AppRelativeVirtualPathString;
        } 

        // Same as GetAppRelativeVirtualPathString, but returns "" instead of null
        internal static string GetAppRelativeVirtualPathStringOrEmpty(VirtualPath virtualPath) {
            return virtualPath == null ? String.Empty : virtualPath.AppRelativeVirtualPathString; 
        }
 
        // Default Create method 
        public static VirtualPath Create(string virtualPath) {
            return Create(virtualPath, VirtualPathOptions.AllowAllPath); 
        }

        public static VirtualPath CreateTrailingSlash(string virtualPath) {
            return Create(virtualPath, VirtualPathOptions.AllowAllPath | VirtualPathOptions.EnsureTrailingSlash); 
        }
 
        public static VirtualPath CreateAllowNull(string virtualPath) { 
            return Create(virtualPath, VirtualPathOptions.AllowAllPath | VirtualPathOptions.AllowNull);
        } 

        public static VirtualPath CreateAbsolute(string virtualPath) {
            return Create(virtualPath, VirtualPathOptions.AllowAbsolutePath);
        } 

        public static VirtualPath CreateNonRelative(string virtualPath) { 
            return Create(virtualPath, VirtualPathOptions.AllowAbsolutePath | VirtualPathOptions.AllowAppRelativePath); 
        }
 
        public static VirtualPath CreateAbsoluteTrailingSlash(string virtualPath) {
            return Create(virtualPath, VirtualPathOptions.AllowAbsolutePath | VirtualPathOptions.EnsureTrailingSlash);
        }
 
        public static VirtualPath CreateNonRelativeTrailingSlash(string virtualPath) {
            return Create(virtualPath, VirtualPathOptions.AllowAbsolutePath | VirtualPathOptions.AllowAppRelativePath | 
                VirtualPathOptions.EnsureTrailingSlash); 
        }
 
        public static VirtualPath CreateAbsoluteAllowNull(string virtualPath) {
            return Create(virtualPath, VirtualPathOptions.AllowAbsolutePath | VirtualPathOptions.AllowNull);
        }
 
        public static VirtualPath CreateNonRelativeAllowNull(string virtualPath) {
            return Create(virtualPath, VirtualPathOptions.AllowAbsolutePath | VirtualPathOptions.AllowAppRelativePath | VirtualPathOptions.AllowNull); 
        } 

        public static VirtualPath CreateNonRelativeTrailingSlashAllowNull(string virtualPath) { 
            return Create(virtualPath, VirtualPathOptions.AllowAbsolutePath | VirtualPathOptions.AllowAppRelativePath |
                VirtualPathOptions.AllowNull | VirtualPathOptions.EnsureTrailingSlash);
        }
 
        public static VirtualPath Create(string virtualPath, VirtualPathOptions options) {
 
            // Trim it first, so that blank strings (e.g. "  ") get treated as empty 
            if (virtualPath != null)
                virtualPath = virtualPath.Trim(); 

            // If it's empty, check whether we allow it
            if (String.IsNullOrEmpty(virtualPath)) {
                if ((options & VirtualPathOptions.AllowNull) != 0) 
                    return null;
 
                throw new ArgumentNullException("virtualPath"); 
            }
 
            // Check for invalid characters
            if (ContainsIllegalVirtualPathChars(virtualPath)) {
                throw new HttpException(SR.GetString(SR.Invalid_vpath, virtualPath));
            } 

            // Flip blackslashes, and remove duplicate slashes 
            string fixedUpVirtualPath = UrlPath.FixVirtualPathSlashes(virtualPath); 

            // If we're supposed to fail on malformed path, check whether FixVirtualPathSlashes 
            // had to do anything.
            if ((options & VirtualPathOptions.FailIfMalformed) != 0 &&
                !Object.ReferenceEquals(virtualPath, fixedUpVirtualPath)) {
                throw new HttpException(SR.GetString(SR.Invalid_vpath, virtualPath)); 
            }
            virtualPath = fixedUpVirtualPath; 
 
            // Make sure it ends with a trailing slash if requested
            if ((options & VirtualPathOptions.EnsureTrailingSlash) != 0) 
                virtualPath = UrlPath.AppendSlashToPathIfNeeded(virtualPath);

            VirtualPath virtualPathObject = new VirtualPath();
 
            if (UrlPath.IsAppRelativePath(virtualPath)) {
 
                virtualPath = UrlPath.ReduceVirtualPath(virtualPath); 

                if (virtualPath[0] == UrlPath.appRelativeCharacter) { 
                    if ((options & VirtualPathOptions.AllowAppRelativePath) == 0) {
                        throw new ArgumentException(SR.GetString(SR.VirtualPath_AllowAppRelativePath, virtualPath));
                    }
 
                    virtualPathObject._appRelativeVirtualPath = virtualPath;
                } 
                else { 
                    // It's possible for the path to become absolute after calling Reduce,
                    // even though it started with "~/".  e.g. if the app is "/app" and the path is 
                    // "~/../hello.aspx", it becomes "/hello.aspx", which is absolute

                    if ((options & VirtualPathOptions.AllowAbsolutePath) == 0) {
                        throw new ArgumentException(SR.GetString(SR.VirtualPath_AllowAbsolutePath, virtualPath)); 
                    }
 
                    virtualPathObject._virtualPath = virtualPath; 
                }
            } 
            else {
                if (virtualPath[0] != '/') {
                    if ((options & VirtualPathOptions.AllowRelativePath) == 0) {
                        throw new ArgumentException(SR.GetString(SR.VirtualPath_AllowRelativePath, virtualPath)); 
                    }
 
                    // Don't Reduce relative paths, since the Reduce method is broken (e.g. "../foo.aspx" --> "/foo.aspx!") 
                    //
                    virtualPathObject._virtualPath = virtualPath; 
                }
                else {
                    if ((options & VirtualPathOptions.AllowAbsolutePath) == 0) {
                        throw new ArgumentException(SR.GetString(SR.VirtualPath_AllowAbsolutePath, virtualPath)); 
                    }
 
                    virtualPathObject._virtualPath = UrlPath.ReduceVirtualPath(virtualPath); 
                }
            } 

            virtualPathObject.ValidateState();

            return virtualPathObject; 
        }
 
        // Debug only method to check that the object is in a consistent state 
        [System.Diagnostics.Conditional("DBG")]
        private void ValidateState() { 
#if DBG
            Debug.Assert(_virtualPath != null || _appRelativeVirtualPath != null);

            if (_virtualPath != null) { 
                CheckValidVirtualPath(_virtualPath);
            } 
 
            if (_appRelativeVirtualPath != null) {
                Debug.Assert(UrlPath.IsAppRelativePath(_appRelativeVirtualPath)); 
                CheckValidVirtualPath(_appRelativeVirtualPath);
            }
#endif
        } 

#if DBG 
        private static void CheckValidVirtualPath(string virtualPath) { 
            Debug.Assert(!ContainsIllegalVirtualPathChars(virtualPath));
            Debug.Assert(virtualPath.IndexOf('\\') < 0); 
        }
#endif

        private static bool ContainsIllegalVirtualPathChars(string virtualPath) { 
            if (!s_VerCompatRegLookedUp)
                LookUpRegForVerCompat(); 
            return virtualPath.IndexOfAny(s_illegalVirtualPathChars) >= 0; 
        }
 
        [RegistryPermission(SecurityAction.Assert, Unrestricted=true)]
        private static void LookUpRegForVerCompat() {
            lock(s_VerCompatLock) {
                if (s_VerCompatRegLookedUp) 
                    return;
                try { 
                    object o = Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ASP.NET", "VerificationCompatibility", 0); 
                    if (o != null && (o is int || o is uint) && ((int)o) == 1)
                        s_illegalVirtualPathChars = s_illegalVirtualPathChars_VerCompat; 
                    s_VerCompatRegLookedUp = true;
                } catch { // ignore exceptions
                }
            } 
        }
    } 
 
    [Flags]
    internal enum VirtualPathOptions { 
        AllowNull               = 0x00000001,
        EnsureTrailingSlash     = 0x00000002,
        AllowAbsolutePath       = 0x00000004,
        AllowAppRelativePath    = 0x00000008, 
        AllowRelativePath       = 0x00000010,
        FailIfMalformed         = 0x00000020, 
 
        AllowAllPath = AllowAbsolutePath | AllowAppRelativePath | AllowRelativePath,
    } 
}
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK