1 // 2 // System.Web.UrlUtils.cs 3 // 4 // Authors: 5 // Gonzalo Paniagua (gonzalo@ximian.com) 6 // Jackson Harper (jackson@ximian.com) 7 // 8 9 // 10 // Copyright (C) 2005 Novell, Inc (http://www.novell.com) 11 // 12 // Permission is hereby granted, free of charge, to any person obtaining 13 // a copy of this software and associated documentation files (the 14 // "Software"), to deal in the Software without restriction, including 15 // without limitation the rights to use, copy, modify, merge, publish, 16 // distribute, sublicense, and/or sell copies of the Software, and to 17 // permit persons to whom the Software is furnished to do so, subject to 18 // the following conditions: 19 // 20 // The above copyright notice and this permission notice shall be 21 // included in all copies or substantial portions of the Software. 22 // 23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 // 31 32 using System.Web.SessionState; 33 using System.Text; 34 namespace System.Web.Util { 35 36 #if VISUAL_STUDIO 37 public 38 #else 39 internal 40 #endif 41 class UrlUtils { 42 43 // appRoot + SessionID + vpath InsertSessionId(string id, string path)44 public static string InsertSessionId (string id, string path) 45 { 46 string dir = GetDirectory (path); 47 if (!dir.EndsWith ("/")) 48 dir += "/"; 49 50 string appvpath = HttpRuntime.AppDomainAppVirtualPath; 51 if (!appvpath.EndsWith ("/")) 52 appvpath += "/"; 53 54 if (path.StartsWith (appvpath)) 55 path = path.Substring (appvpath.Length); 56 57 if (path.StartsWith("/")) 58 path = path.Length > 1 ? path.Substring (1) : ""; 59 60 return Canonic (appvpath + "(" + id + ")/" + path); 61 } 62 GetSessionId(string path)63 public static string GetSessionId (string path) 64 { 65 #if TARGET_DOTNET 66 return null; 67 #else 68 if (path == null) 69 return null; 70 71 string appvpath = HttpRuntime.AppDomainAppVirtualPath; 72 int appvpathlen = appvpath.Length; 73 74 if (path.Length <= appvpathlen) 75 return null; 76 77 path = path.Substring (appvpathlen); 78 79 int len = path.Length; 80 if (len == 0 || path [0] != '/') { 81 path = '/' + path; 82 len++; 83 } 84 85 if ((len < SessionId.IdLength + 3) || (path [1] != '(') || 86 (path [SessionId.IdLength + 2] != ')')) 87 return null; 88 89 return path.Substring (2, SessionId.IdLength); 90 #endif 91 } 92 HasSessionId(string path)93 public static bool HasSessionId (string path) 94 { 95 if (path == null || path.Length < 5) 96 return false; 97 98 return (StrUtils.StartsWith (path, "/(") && path.IndexOf (")/") > 2); 99 } 100 RemoveSessionId(string base_path, string file_path)101 public static string RemoveSessionId (string base_path, string file_path) 102 { 103 // Caller did a GetSessionId first 104 int idx = base_path.IndexOf ("/("); 105 string dir = base_path.Substring (0, idx + 1); 106 if (!dir.EndsWith ("/")) 107 dir += "/"; 108 109 idx = base_path.IndexOf (")/"); 110 if (idx != -1 && base_path.Length > idx + 2) { 111 string dir2 = base_path.Substring (idx + 2); 112 if (!dir2.EndsWith ("/")) 113 dir2 += "/"; 114 115 dir += dir2; 116 } 117 118 return Canonic (dir + GetFile (file_path)); 119 } 120 Combine(string basePath, string relPath)121 public static string Combine (string basePath, string relPath) 122 { 123 if (relPath == null) 124 throw new ArgumentNullException ("relPath"); 125 126 int rlength = relPath.Length; 127 if (rlength == 0) 128 return ""; 129 130 relPath = relPath.Replace ('\\', '/'); 131 if (IsRooted (relPath)) 132 return Canonic (relPath); 133 134 char first = relPath [0]; 135 if (rlength < 3 || first == '~' || first == '/' || first == '\\') { 136 if (basePath == null || (basePath.Length == 1 && basePath [0] == '/')) 137 basePath = String.Empty; 138 139 string slash = (first == '/') ? "" : "/"; 140 if (first == '~') { 141 if (rlength == 1) { 142 relPath = ""; 143 } else if (rlength > 1 && relPath [1] == '/') { 144 relPath = relPath.Substring (2); 145 slash = "/"; 146 } 147 148 string appvpath = HttpRuntime.AppDomainAppVirtualPath; 149 if (appvpath.EndsWith ("/")) 150 slash = ""; 151 152 return Canonic (appvpath + slash + relPath); 153 } 154 155 return Canonic (basePath + slash + relPath); 156 } 157 158 if (basePath == null || basePath.Length == 0 || basePath [0] == '~') 159 basePath = HttpRuntime.AppDomainAppVirtualPath; 160 161 if (basePath.Length <= 1) 162 basePath = String.Empty; 163 164 return Canonic (basePath + "/" + relPath); 165 } 166 167 static char [] path_sep = {'\\', '/'}; 168 Canonic(string path)169 public static string Canonic (string path) 170 { 171 bool isRooted = IsRooted(path); 172 bool endsWithSlash = path.EndsWith("/"); 173 string [] parts = path.Split (path_sep); 174 int end = parts.Length; 175 176 int dest = 0; 177 178 for (int i = 0; i < end; i++) { 179 string current = parts [i]; 180 181 if (current.Length == 0) 182 continue; 183 184 if (current == "." ) 185 continue; 186 187 if (current == "..") { 188 dest --; 189 continue; 190 } 191 if (dest < 0) 192 if (!isRooted) 193 throw new HttpException ("Invalid path."); 194 else 195 dest = 0; 196 197 parts [dest++] = current; 198 } 199 if (dest < 0) 200 throw new HttpException ("Invalid path."); 201 202 if (dest == 0) 203 return "/"; 204 205 string str = String.Join ("/", parts, 0, dest); 206 str = RemoveDoubleSlashes (str); 207 if (isRooted) 208 str = "/" + str; 209 if (endsWithSlash) 210 str = str + "/"; 211 212 return str; 213 } 214 GetDirectory(string url)215 public static string GetDirectory (string url) 216 { 217 url = url.Replace('\\','/'); 218 int last = url.LastIndexOf ('/'); 219 220 if (last > 0) { 221 if (last < url.Length) 222 last++; 223 return RemoveDoubleSlashes (url.Substring (0, last)); 224 } 225 226 return "/"; 227 } 228 RemoveDoubleSlashes(string input)229 public static string RemoveDoubleSlashes (string input) 230 { 231 // MS VirtualPathUtility removes duplicate '/' 232 233 int index = -1; 234 for (int i = 1; i < input.Length; i++) 235 if (input [i] == '/' && input [i - 1] == '/') { 236 index = i - 1; 237 break; 238 } 239 240 if (index == -1) // common case optimization 241 return input; 242 243 StringBuilder sb = new StringBuilder (input.Length); 244 sb.Append (input, 0, index); 245 246 for (int i = index; i < input.Length; i++) { 247 if (input [i] == '/') { 248 int next = i + 1; 249 if (next < input.Length && input [next] == '/') 250 continue; 251 sb.Append ('/'); 252 } 253 else { 254 sb.Append (input [i]); 255 } 256 } 257 258 return sb.ToString (); 259 } 260 GetFile(string url)261 public static string GetFile (string url) 262 { 263 url = url.Replace('\\','/'); 264 int last = url.LastIndexOf ('/'); 265 if (last >= 0) { 266 if (url.Length == 1) // Empty file name instead of ArgumentOutOfRange 267 return ""; 268 return url.Substring (last+1); 269 } 270 271 throw new ArgumentException (String.Format ("GetFile: `{0}' does not contain a /", url)); 272 } 273 IsRooted(string path)274 public static bool IsRooted (string path) 275 { 276 if (path == null || path.Length == 0) 277 return true; 278 279 char c = path [0]; 280 if (c == '/' || c == '\\') 281 return true; 282 283 return false; 284 } 285 IsRelativeUrl(string path)286 public static bool IsRelativeUrl (string path) 287 { 288 return (path [0] != '/' && path.IndexOf (':') == -1); 289 } 290 ResolveVirtualPathFromAppAbsolute(string path)291 public static string ResolveVirtualPathFromAppAbsolute (string path) 292 { 293 if (path [0] != '~') return path; 294 295 if (path.Length == 1) 296 return HttpRuntime.AppDomainAppVirtualPath; 297 298 if (path [1] == '/' || path [1] == '\\') { 299 string appPath = HttpRuntime.AppDomainAppVirtualPath; 300 if (appPath.Length > 1) 301 return appPath + "/" + path.Substring (2); 302 return "/" + path.Substring (2); 303 } 304 return path; 305 } 306 ResolvePhysicalPathFromAppAbsolute(string path)307 public static string ResolvePhysicalPathFromAppAbsolute (string path) 308 { 309 if (path [0] != '~') return path; 310 311 if (path.Length == 1) 312 return HttpRuntime.AppDomainAppPath; 313 314 if (path [1] == '/' || path [1] == '\\') { 315 string appPath = HttpRuntime.AppDomainAppPath; 316 if (appPath.Length > 1) 317 return appPath + "/" + path.Substring (2); 318 return "/" + path.Substring (2); 319 } 320 return path; 321 } 322 } 323 } 324