2008-11-18 Marek Habersack <mhabersack@novell.com>
[mono.git] / mcs / class / System.Web / System.Web.Util / UrlUtils.cs
index 179158ec1ab20f1b8cf8f6c4fd2784b1cadbc4aa..b55e245c7c5c9c1479ab933f532e763dcd8b9e9f 100644 (file)
@@ -30,6 +30,7 @@
 //
 
 using System.Web.SessionState;
+using System.Text;
 namespace System.Web.Util {
        
        internal class UrlUtils {
@@ -56,15 +57,23 @@ namespace System.Web.Util {
 
                internal static string GetSessionId (string path)
                {
+                       if (path == null)
+                               return null;
+
                        string appvpath = HttpRuntime.AppDomainAppVirtualPath;
-                       if (path.Length <= appvpath.Length)
+                       int appvpathlen = appvpath.Length;
+
+                       if (path.Length <= appvpathlen)
                                return null;
 
-                       path = path.Substring (appvpath.Length);
-                       if (path.Length == 0 || path [0] != '/')
+                       path = path.Substring (appvpathlen);
+                       
+                       int len = path.Length;
+                       if (len == 0 || path [0] != '/') {
                                path = '/' + path;
+                               len++;
+                       }                       
 
-                       int len = path.Length;
                        if ((len < SessionId.IdLength + 3) || (path [1] != '(') ||
                            (path [SessionId.IdLength + 2] != ')'))
                                return null;
@@ -72,6 +81,14 @@ namespace System.Web.Util {
                        return path.Substring (2, SessionId.IdLength);
                }
 
+               internal static bool HasSessionId (string path)
+               {
+                       if (path == null || path.Length < 5)
+                               return false;
+
+                       return (StrUtils.StartsWith (path, "/(") && path.IndexOf (")/") > 2);
+               }
+
                internal static string RemoveSessionId (string base_path, string file_path)
                {
                        // Caller did a GetSessionId first
@@ -129,7 +146,7 @@ namespace System.Web.Util {
                                return Canonic (basePath + slash + relPath);
                        }
 
-                       if (basePath == null || basePath == "" || basePath [0] == '~')
+                       if (basePath == null || basePath.Length == 0 || basePath [0] == '~')
                                basePath = HttpRuntime.AppDomainAppVirtualPath;
 
                        if (basePath.Length <= 1)
@@ -142,6 +159,8 @@ namespace System.Web.Util {
                
                internal static string Canonic (string path)
                {
+                       bool isRooted = IsRooted(path);
+                       bool endsWithSlash = path.EndsWith("/");
                        string [] parts = path.Split (path_sep);
                        int end = parts.Length;
                        
@@ -149,28 +168,41 @@ namespace System.Web.Util {
                        
                        for (int i = 0; i < end; i++) {
                                string current = parts [i];
+
+                               if (current.Length == 0)
+                                       continue;
+
                                if (current == "." )
                                        continue;
 
                                if (current == "..") {
-                                       if (dest == 0) {
-                                               if (i == 1) // see bug 52599
-                                                       continue;
-
-                                               throw new HttpException ("Invalid path.");
-                                       }
-
                                        dest --;
                                        continue;
                                }
+                               if (dest < 0)
+                                       if (!isRooted)
+                                               throw new HttpException ("Invalid path.");
+                                       else
+                                               dest = 0;
 
                                parts [dest++] = current;
                        }
+                       if (dest < 0)
+                               throw new HttpException ("Invalid path.");
 
                        if (dest == 0)
                                return "/";
 
-                       return String.Join ("/", parts, 0, dest);
+                       string str = String.Join ("/", parts, 0, dest);
+#if NET_2_0
+                       str = RemoveDoubleSlashes (str);
+#endif
+                       if (isRooted)
+                               str = "/" + str;
+                       if (endsWithSlash)
+                               str = str + "/";
+
+                       return str;
                }
                
                internal static string GetDirectory (string url)
@@ -179,6 +211,8 @@ namespace System.Web.Util {
                        int last = url.LastIndexOf ('/');
 
                        if (last > 0) {
+                               if (last < url.Length)
+                                       last++;
 #if NET_2_0
                                return RemoveDoubleSlashes (url.Substring (0, last));
 #else
@@ -193,13 +227,33 @@ namespace System.Web.Util {
                internal static string RemoveDoubleSlashes (string input)
                {
                        // MS VirtualPathUtility removes duplicate '/'
-                       string str = input;
-                       string x;
-                       while ((x = str.Replace ("//", "/")) != str) {
-                               str = x;
+
+                       int index = -1;
+                       for (int i = 1; i < input.Length; i++)
+                               if (input [i] == '/' && input [i - 1] == '/') {
+                                       index = i - 1;
+                                       break;
+                               }
+
+                       if (index == -1) // common case optimization
+                               return input;
+
+                       StringBuilder sb = new StringBuilder (input.Length);
+                       sb.Append (input, 0, index);
+
+                       for (int i = index; i < input.Length; i++) {
+                               if (input [i] == '/') {
+                                       int next = i + 1;
+                                       if (next < input.Length && input [next] == '/')
+                                               continue;
+                                       sb.Append ('/');
+                               }
+                               else {
+                                       sb.Append (input [i]);
+                               }
                        }
 
-                       return str;
+                       return sb.ToString ();
                }
 #endif
 
@@ -213,12 +267,12 @@ namespace System.Web.Util {
                                return url.Substring (last+1);
                        }
 
-                       throw new Exception (String.Format ("GetFile: `{0}' does not contain a /", url));
+                       throw new ArgumentException (String.Format ("GetFile: `{0}' does not contain a /", url));
                }
                
                internal static bool IsRooted (string path)
                {
-                       if (path == null || path == "")
+                       if (path == null || path.Length == 0)
                                return true;
 
                        char c = path [0];