| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500 |
- //
- // System.Web.VirtualPathUtility.cs
- //
- // Author:
- // Chris Toshok ([email protected])
- // Gonzalo Paniagua Javier ([email protected])
- //
- //
- // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
- //
- // Permission is hereby granted, free of charge, to any person obtaining
- // a copy of this software and associated documentation files (the
- // "Software"), to deal in the Software without restriction, including
- // without limitation the rights to use, copy, modify, merge, publish,
- // distribute, sublicense, and/or sell copies of the Software, and to
- // permit persons to whom the Software is furnished to do so, subject to
- // the following conditions:
- //
- // The above copyright notice and this permission notice shall be
- // included in all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- //
- using System.Collections.Specialized;
- using System.Web.Configuration;
- using System.Web.Util;
- using System.Text;
- using Microsoft.Win32;
- namespace System.Web {
- public static class VirtualPathUtility
- {
- static bool monoSettingsVerifyCompatibility;
- static bool runningOnWindows;
-
- static VirtualPathUtility ()
- {
- try {
- runningOnWindows = HttpRuntime.RunningOnWindows;
- var monoSettings = WebConfigurationManager.GetWebApplicationSection ("system.web/monoSettings") as MonoSettingsSection;
- if (monoSettings != null)
- monoSettingsVerifyCompatibility = monoSettings.VerificationCompatibility != 1;
- } catch {
- // ignore
- }
- }
-
- public static string AppendTrailingSlash (string virtualPath)
- {
- if (virtualPath == null)
- return virtualPath;
- int length = virtualPath.Length;
- if (length == 0 || virtualPath [length - 1] == '/')
- return virtualPath;
- return virtualPath + "/";
- }
- public static string Combine (string basePath, string relativePath)
- {
- basePath = Normalize (basePath);
- if (IsRooted (relativePath))
- return Normalize (relativePath);
- int basePathLen = basePath.Length;
- if (basePath [basePathLen - 1] != '/') {
- if (basePathLen > 1) {
- int lastSlash = basePath.LastIndexOf ('/');
- if (lastSlash >= 0)
- basePath = basePath.Substring (0, lastSlash + 1);
- } else { // "~" only
- basePath += "/";
- }
- }
- return Normalize (basePath + relativePath);
- }
- public static string GetDirectory (string virtualPath)
- {
- return GetDirectory (virtualPath, true);
- }
- internal static string GetDirectory (string virtualPath, bool normalize)
- {
- if (normalize)
- virtualPath = Normalize (virtualPath);
- if (IsAppRelative (virtualPath) && virtualPath.Length < 3) { // "~" or "~/"
- virtualPath = ToAbsolute (virtualPath);
- }
-
- if (virtualPath.Length == 1 && virtualPath [0] == '/') { // "/"
- return null;
- }
- int last = virtualPath.LastIndexOf ('/', virtualPath.Length - 2, virtualPath.Length - 2);
- if (last > 0)
- return virtualPath.Substring (0, last + 1);
- else
- return "/";
- }
- public static string GetExtension (string virtualPath)
- {
- if (StrUtils.IsNullOrEmpty (virtualPath))
- throw new ArgumentNullException ("virtualPath");
- virtualPath = Canonize (virtualPath);
- int dot = virtualPath.LastIndexOf ('.');
- if (dot == -1 || dot == virtualPath.Length - 1 || dot < virtualPath.LastIndexOf ('/'))
- return String.Empty;
- return virtualPath.Substring (dot);
- }
- public static string GetFileName (string virtualPath)
- {
- virtualPath = Normalize (virtualPath);
-
- if (IsAppRelative (virtualPath) && virtualPath.Length < 3) { // "~" or "~/"
- virtualPath = ToAbsolute (virtualPath);
- }
- if (virtualPath.Length == 1 && virtualPath [0] == '/') { // "/"
- return String.Empty;
- }
- virtualPath = RemoveTrailingSlash (virtualPath);
- int last = virtualPath.LastIndexOf ('/');
- return virtualPath.Substring (last + 1);
- }
- internal static bool IsRooted (string virtualPath)
- {
- return IsAbsolute (virtualPath) || IsAppRelative (virtualPath);
- }
- public static bool IsAbsolute (string virtualPath)
- {
- if (StrUtils.IsNullOrEmpty (virtualPath))
- throw new ArgumentNullException ("virtualPath");
- return (virtualPath [0] == '/' || virtualPath [0] == '\\');
- }
- public static bool IsAppRelative (string virtualPath)
- {
- if (StrUtils.IsNullOrEmpty (virtualPath))
- throw new ArgumentNullException ("virtualPath");
- if (virtualPath.Length == 1 && virtualPath [0] == '~')
- return true;
- if (virtualPath [0] == '~' && (virtualPath [1] == '/' || virtualPath [1] == '\\'))
- return true;
- return false;
- }
- // MSDN: If the fromPath and toPath parameters are not rooted; that is,
- // they do not equal the root operator (the tilde [~]), do not start with a tilde (~),
- // such as a tilde and a slash mark (~/) or a tilde and a double backslash (~//),
- // or do not start with a slash mark (/), an ArgumentException exception is thrown.
- public static string MakeRelative (string fromPath, string toPath)
- {
- if (fromPath == null || toPath == null)
- throw new NullReferenceException (); // yeah!
- if (toPath == "")
- return toPath;
- toPath = ToAbsoluteInternal (toPath);
- fromPath = ToAbsoluteInternal (fromPath);
- if (String.CompareOrdinal (fromPath, toPath) == 0 && fromPath [fromPath.Length - 1] == '/')
- return "./";
- string [] toPath_parts = toPath.Split ('/');
- string [] fromPath_parts = fromPath.Split ('/');
- int dest = 1;
- while (toPath_parts [dest] == fromPath_parts [dest]) {
- if (toPath_parts.Length == (dest + 1) || fromPath_parts.Length == (dest + 1)) {
- break;
- }
- dest++;
- }
- StringBuilder res = new StringBuilder();
- for (int i = 1; i < fromPath_parts.Length - dest; i++) {
- res.Append ("../");
- }
- for (int i = dest; i < toPath_parts.Length; i++) {
- res.Append (toPath_parts [i]);
- if (i < toPath_parts.Length - 1)
- res.Append ('/');
- }
- return res.ToString ();
- }
- static string ToAbsoluteInternal (string virtualPath)
- {
- if (IsAppRelative (virtualPath))
- return ToAbsolute (virtualPath, HttpRuntime.AppDomainAppVirtualPath);
- else if (IsAbsolute (virtualPath))
- return Normalize (virtualPath);
- throw new ArgumentOutOfRangeException ("Specified argument was out of the range of valid values.");
- }
- public static string RemoveTrailingSlash (string virtualPath)
- {
- if (virtualPath == null || virtualPath == "")
- return null;
- int last = virtualPath.Length - 1;
- if (last == 0 || virtualPath [last] != '/')
- return virtualPath;
- return virtualPath.Substring (0, last);
- }
- public static string ToAbsolute (string virtualPath)
- {
- return ToAbsolute (virtualPath, true);
- }
- internal static string ToAbsolute (string virtualPath, bool normalize)
- {
- if (IsAbsolute (virtualPath)) {
- if (normalize)
- return Normalize (virtualPath);
- else
- return virtualPath;
- }
- string apppath = HttpRuntime.AppDomainAppVirtualPath;
- if (apppath == null)
- throw new HttpException ("The path to the application is not known");
- if (virtualPath.Length == 1 && virtualPath [0] == '~')
- return apppath;
- return ToAbsolute (virtualPath,apppath);
- }
- // If virtualPath is:
- // Absolute, the ToAbsolute method returns the virtual path with no changes.
- // Application relative, the ToAbsolute method adds applicationPath to the beginning of the virtual path.
- // Not rooted, the ToAbsolute method raises an ArgumentOutOfRangeException exception.
- public static string ToAbsolute (string virtualPath, string applicationPath)
- {
- return ToAbsolute (virtualPath, applicationPath, true);
- }
- internal static string ToAbsolute (string virtualPath, string applicationPath, bool normalize)
- {
- if (StrUtils.IsNullOrEmpty (applicationPath))
- throw new ArgumentNullException ("applicationPath");
- if (StrUtils.IsNullOrEmpty (virtualPath))
- throw new ArgumentNullException ("virtualPath");
- if (IsAppRelative(virtualPath)) {
- if (applicationPath [0] != '/')
- throw new ArgumentException ("appPath is not rooted", "applicationPath");
-
- string path = applicationPath + (virtualPath.Length == 1 ? "/" : virtualPath.Substring (1));
- if (normalize)
- return Normalize (path);
- else
- return path;
- }
- if (virtualPath [0] != '/')
- throw new ArgumentException (String.Format ("Relative path not allowed: '{0}'", virtualPath));
- if (normalize)
- return Normalize (virtualPath);
- else
- return virtualPath;
- }
- public static string ToAppRelative (string virtualPath)
- {
- string apppath = HttpRuntime.AppDomainAppVirtualPath;
- if (apppath == null)
- throw new HttpException ("The path to the application is not known");
- return ToAppRelative (virtualPath, apppath);
- }
- public static string ToAppRelative (string virtualPath, string applicationPath)
- {
- virtualPath = Normalize (virtualPath);
- if (IsAppRelative (virtualPath))
- return virtualPath;
- if (!IsAbsolute (applicationPath))
- throw new ArgumentException ("appPath is not absolute", "applicationPath");
-
- applicationPath = Normalize (applicationPath);
- if (applicationPath.Length == 1)
- return "~" + virtualPath;
- int appPath_lenght = applicationPath.Length;
- if (String.CompareOrdinal (virtualPath, applicationPath) == 0)
- return "~/";
- if (String.CompareOrdinal (virtualPath, 0, applicationPath, 0, appPath_lenght) == 0)
- return "~" + virtualPath.Substring (appPath_lenght);
- return virtualPath;
- }
- static char [] path_sep = { '/' };
- static string Normalize (string path)
- {
- if (!IsRooted (path))
- throw new ArgumentException (String.Format ("The relative virtual path '{0}' is not allowed here.", path));
- if (path.Length == 1) // '/' or '~'
- return path;
- path = Canonize (path);
- int dotPos = path.IndexOf ('.');
- while (dotPos >= 0) {
- if (++dotPos == path.Length)
- break;
- char nextChar = path [dotPos];
- if ((nextChar == '/') || (nextChar == '.'))
- break;
- dotPos = path.IndexOf ('.', dotPos);
- }
- if (dotPos < 0)
- return path;
- bool starts_with_tilda = false;
- bool ends_with_slash = false;
- string [] apppath_parts= null;
- if (path [0] == '~') {
- if (path.Length == 2) // "~/"
- return "~/";
- starts_with_tilda = true;
- path = path.Substring (1);
- }
- else if (path.Length == 1) { // "/"
- return "/";
- }
- if (path [path.Length - 1] == '/')
- ends_with_slash = true;
- string [] parts = StrUtils.SplitRemoveEmptyEntries (path, path_sep);
- int end = parts.Length;
- int dest = 0;
- for (int i = 0; i < end; i++) {
- string current = parts [i];
- if (current == ".")
- continue;
- if (current == "..") {
- dest--;
- if(dest >= 0)
- continue;
- if (starts_with_tilda) {
- if (apppath_parts == null) {
- string apppath = HttpRuntime.AppDomainAppVirtualPath;
- apppath_parts = StrUtils.SplitRemoveEmptyEntries (apppath, path_sep);
- }
- if ((apppath_parts.Length + dest) >= 0)
- continue;
- }
-
- throw new HttpException ("Cannot use a leading .. to exit above the top directory.");
- }
- if (dest >= 0)
- parts [dest] = current;
- else
- apppath_parts [apppath_parts.Length + dest] = current;
-
- dest++;
- }
- StringBuilder str = new StringBuilder();
- if (apppath_parts != null) {
- starts_with_tilda = false;
- int count = apppath_parts.Length;
- if (dest < 0)
- count += dest;
- for (int i = 0; i < count; i++) {
- str.Append ('/');
- str.Append (apppath_parts [i]);
- }
- }
- else if (starts_with_tilda) {
- str.Append ('~');
- }
- for (int i = 0; i < dest; i++) {
- str.Append ('/');
- str.Append (parts [i]);
- }
- if (str.Length > 0) {
- if (ends_with_slash)
- str.Append ('/');
- }
- else {
- return "/";
- }
- return str.ToString ();
- }
- internal static string Canonize (string path)
- {
- int index = -1;
- for (int i=0; i < path.Length; i++) {
- if ((path [i] == '\\') || (path [i] == '/' && (i + 1) < path.Length && (path [i + 1] == '/' || path [i + 1] == '\\'))) {
- index = i;
- break;
- }
- }
- if (index < 0)
- return path;
- StringBuilder sb = new StringBuilder (path.Length);
- sb.Append (path, 0, index);
- for (int i = index; i < path.Length; i++) {
- if (path [i] == '\\' || path [i] == '/') {
- int next = i + 1;
- if (next < path.Length && (path [next] == '\\' || path [next] == '/'))
- continue;
- sb.Append ('/');
- }
- else {
- sb.Append (path [i]);
- }
- }
- return sb.ToString ();
- }
- // See: http://support.microsoft.com/kb/932552
- // See: https://bugzilla.novell.com/show_bug.cgi?id=509163
- static readonly char[] invalidVirtualPathChars = {':', '*'};
- static readonly string aspNetVerificationKey = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\ASP.NET";
- internal static bool IsValidVirtualPath (string path)
- {
- if (path == null)
- return false;
- bool doValidate = true;
- if (runningOnWindows) {
- try {
- object v = Registry.GetValue (aspNetVerificationKey, "VerificationCompatibility", null);
- if (v != null && v is int)
- doValidate = (int)v != 1;
- } catch {
- // ignore
- }
- }
- if (doValidate)
- doValidate = monoSettingsVerifyCompatibility;
- if (!doValidate)
- return true;
- return path.IndexOfAny (invalidVirtualPathChars) == -1;
- }
- }
- }
|