fixed 59051
[mono.git] / mcs / class / System.Web / System.Web.Util / UrlUtils.cs
1 /**
2  * Namespace: System.Web.UI.Util
3  * Class:     UrlUtils
4  * 
5  * Author:  Gaurav Vaish
6  * Maintainer: gvaish@iitk.ac.in
7  * Status:  ??%
8  * 
9  * (C) Gaurav Vaish (2001)
10  */
11
12 using System;
13 using System.Collections;
14 using System.Text;
15 using System.Web.SessionState;
16
17 namespace System.Web.Util
18 {
19         internal class UrlUtils
20         {
21                 /*
22                  * I could not find these functions in the class System.Uri
23                  * Besides, an instance of Uri will not be formed until and unless the address is of
24                  * the form protocol://[user:pass]host[:port]/[fullpath]
25                  * ie, a protocol, and that too without any blanks before,
26                  * is a must which may not be the case here.
27                  * Important: Escaped URL is assumed here. nothing like .aspx?path=/something
28                  * It should be .aspx?path=%2Fsomething
29                  */
30                 public static string GetProtocol(string url)
31                 {
32                         //Taking code from Java Class java.net.URL
33                         if(url!=null)
34                         {
35                                 if(url.Length>0)
36                                 {
37                                         
38                                         int i, start = 0, limit;
39                                         limit = url.Length;
40                                         char c;
41                                         bool aRef = false;
42                                         while( (limit > 0) && (url[limit-1] <= ' '))
43                                         {
44                                                 limit --;
45                                         }
46                                         while( (start < limit) && (url[start] <= ' '))
47                                         {
48                                                 start++;
49                                         }
50                                         if(RegionMatches(true, url, start, "url:", 0, 4))
51                                         {
52                                                 start += 4;
53                                         }
54                                         if(start < url.Length && url[start]=='#')
55                                         {
56                                                 aRef = true;
57                                         }
58                                         for(i = start; !aRef && (i < limit) && ((c=url[i]) != '/'); i++)
59                                         {
60                                                 if(c==':')
61                                                 {
62                                                         return url.Substring(start, i - start);
63                                                 }
64                                         }
65                                 }
66                         }
67                         return String.Empty;
68                 }
69                 
70                 public static bool IsRelativeUrl(string url)
71                 {
72                         if (url.IndexOf(':') == -1)
73                                 return !IsRooted(url);
74
75                         return false;
76                 }
77
78                 public static bool IsRootUrl(string url)
79                 {
80                         if(url!=null)
81                         {
82                                 if(url.Length>0)
83                                 {
84                                         return IsValidProtocol(GetProtocol(url).ToLower());
85                                 }
86                         }
87                         return true;
88                 }
89                 
90                 public static bool IsRooted(string path)
91                 {
92                         if(path!=null && path.Length > 0)
93                         {
94                                 return (path[0]=='/' || path[0]=='\\');
95                         }
96                         return true;
97                 }
98                 
99                 public static void FailIfPhysicalPath(string path)
100                 {
101                         if(path!= null && path.Length > 1)
102                         {
103                                 if(path[1]==':' || path.StartsWith(@"\\"))
104                                         throw new HttpException(HttpRuntime.FormatResourceString("Physical_path_not_allowed", path));
105                         }
106                 }
107
108                 public static string Combine (string basePath, string relPath)
109                 {
110                         if (relPath == null)
111                                 throw new ArgumentNullException ("relPath");
112
113                         int rlength = relPath.Length;
114                         if (rlength == 0)
115                                 return "";
116
117                         FailIfPhysicalPath (relPath);
118                         relPath = relPath.Replace ("\\", "/");
119                         if (IsRooted (relPath))
120                                 return Reduce (relPath);
121
122                         char first = relPath [0];
123                         if (rlength < 3 || first == '~' || first == '/' || first == '\\') {
124                                 if (basePath == null || (basePath.Length == 1 && basePath [0] == '/'))
125                                         basePath = String.Empty;
126
127                                 string slash = (first == '/') ? "" : "/";
128                                 if (first == '~') {
129                                         if (rlength == 1) {
130                                                 relPath = "";
131                                         } else if (rlength > 1 && relPath [1] == '/') {
132                                                 relPath = relPath.Substring (2);
133                                                 slash = "/";
134                                         }
135
136                                         return Reduce (HttpRuntime.AppDomainAppVirtualPath + slash + relPath);
137                                 }
138
139                                 return Reduce (basePath + slash + relPath);
140                         }
141
142                         if (basePath == null || basePath == "")
143                                 basePath = HttpRuntime.AppDomainAppVirtualPath;
144
145                         if (basePath.Length <= 1)
146                                 basePath = String.Empty;
147
148                         return Reduce (basePath + "/" + relPath);
149                 }
150
151                 public static bool IsValidProtocol(string protocol)
152                 {
153                         if(protocol.Length < 1)
154                                 return false;
155                         char c = protocol[0];
156                         if(!Char.IsLetter(c))
157                         {
158                                 return false;
159                         }
160                         for(int i=1; i < protocol.Length; i++)
161                         {
162                                 c = protocol[i];
163                                 if(!Char.IsLetterOrDigit(c) && c!='.' && c!='+' && c!='-')
164                                 {
165                                         return false;
166                                 }
167                         }
168                         return true;
169                 }
170                 
171                 /*
172                  * MakeRelative("http://www.foo.com/bar1/bar2/file","http://www.foo.com/bar1")
173                  * will return "bar2/file"
174                  * while MakeRelative("http://www.foo.com/bar1/...","http://www.anotherfoo.com")
175                  * return 'null' and so does the call
176                  * MakeRelative("http://www.foo.com/bar1/bar2","http://www.foo.com/bar")
177                  */
178                 public static string MakeRelative(string fullUrl, string relativeTo)
179                 {
180                         if (fullUrl == relativeTo)
181                                 return String.Empty;
182
183                         if (fullUrl.IndexOf (relativeTo) != 0)
184                                 return null;
185
186                         string leftOver = fullUrl.Substring (relativeTo.Length);
187                         if (leftOver.Length > 0 && leftOver [0] == '/')
188                                 leftOver = leftOver.Substring (1);
189
190                         leftOver = Reduce (leftOver);
191                         if (leftOver.Length > 0 && leftOver [0] == '/')
192                                 leftOver = leftOver.Substring (1);
193
194                         return leftOver;
195                 }
196
197                 /*
198                  * Check JavaDocs for java.lang.String#RegionMatches(bool, int, String, int, int)
199                  * Could not find anything similar in the System.String class
200                  */
201                 public static bool RegionMatches(bool ignoreCase, string source, int start, string match, int offset, int len)
202                 {
203                         if(source!=null || match!=null)
204                         {
205                                 if(source.Length>0 && match.Length>0)
206                                 {
207                                         char[] ta = source.ToCharArray();
208                                         char[] pa = match.ToCharArray();
209                                         if((offset < 0) || (start < 0) || (start > (source.Length - len)) || (offset > (match.Length - len)))
210                                         {
211                                                 return false;
212                                         }
213                                         while(len-- > 0)
214                                         {
215                                                 char c1 = ta[start++];
216                                                 char c2 = pa[offset++];
217                                                 if(c1==c2)
218                                                         continue;
219                                                 if(ignoreCase)
220                                                 {
221                                                         if(Char.ToUpper(c1)==Char.ToUpper(c2))
222                                                                 continue;
223                                                         // Check for Gregorian Calendar where the above may not hold good
224                                                         if(Char.ToLower(c1)==Char.ToLower(c2))
225                                                                 continue;
226                                                 }
227                                                 return false;
228                                         }
229                                         return true;
230                                 }
231                         }
232                         return false;
233                 }
234
235                 public static string Reduce (string path)
236                 {
237                         path = path.Replace ('\\','/');
238
239                         string [] parts = path.Split ('/');
240                         ArrayList result = new ArrayList ();
241                         
242                         int end = parts.Length;
243                         for (int i = 0; i < end; i++) {
244                                 string current = parts [i];
245                                 if (current == "." )
246                                         continue;
247
248                                 if (current == "..") {
249                                         if (result.Count == 0) {
250                                                 if (i == 1) // see bug 52599
251                                                         continue;
252
253                                                 throw new HttpException ("Invalid path.");
254                                         }
255
256                                         result.RemoveAt (result.Count - 1);
257                                         continue;
258                                 }
259
260                                 result.Add (current);
261                         }
262
263                         if (result.Count == 0)
264                                 return "/";
265
266                         return String.Join ("/", (string []) result.ToArray (typeof (string)));
267                 }
268                 
269                 public static string GetDirectory(string url)
270                 {
271                         if(url==null)
272                         {
273                                 return null;
274                         }
275                         if(url.Length==0)
276                         {
277                                 return String.Empty;
278                         }
279                         url = url.Replace('\\','/');
280
281                         string baseDir = "";
282                         int last = url.LastIndexOf ('/');
283                         if (last > 0)
284                                 baseDir = url.Substring(0, url.LastIndexOf('/'));
285
286                         if(baseDir.Length==0)
287                         {
288                                 baseDir = "/";
289                         }
290                         return baseDir;
291                 }
292
293                 static string GetFile (string url)
294                 {
295                         if (url == null)
296                                 return null;
297
298                         if (url.Length == 0)
299                                 return String.Empty;
300
301                         url = url.Replace ('\\', '/');
302
303                         int last = url.LastIndexOf ('/') + 1;
304                         if (last != 0) {
305                                 url = url.Substring (last);
306                         }
307
308                         return url;
309                 }
310
311                 public static string InsertSessionId (string id, string path)
312                 {
313                         string dir = GetDirectory (path);
314                         if (!dir.EndsWith ("/"))
315                                 dir += "/";
316
317                         return Reduce (dir + "(" + id + ")/" + GetFile (path));
318                 }
319
320                 public static string GetSessionId (string path)
321                 {
322                         int len = path.Length;
323                         if ((len < SessionId.IdLength + 2) || (path [len - 1] != ')') ||
324                             (path [len - SessionId.IdLength - 2] != '('))
325                                 return null;
326
327                         return path.Substring (len - SessionId.IdLength - 1, SessionId.IdLength);
328                 }
329
330                 public static string RemoveSessionId (string base_path, string file_path)
331                 {
332                         int len = base_path.Length;
333                         string dir = base_path.Substring (0, len - SessionId.IdLength - 2);
334                         if (!dir.EndsWith ("/"))
335                                 dir += "/";
336
337                         return Reduce (dir + GetFile (file_path));
338                 }
339                 
340                 public static string ResolveVirtualPathFromAppAbsolute (string path)
341                 {
342                         if (path [0] != '~') return path;
343                                 
344                         if (path.Length == 1)
345                                 return HttpRuntime.AppDomainAppVirtualPath;
346                         
347                         if (path [1] == '/' || path [1] == '\\') {
348                                 string appPath = HttpRuntime.AppDomainAppVirtualPath;
349                                 if (appPath.Length > 1)
350                                         return appPath + "/" + path.Substring (2);
351                                 return "/" + path.Substring (2);
352                         }
353                         return path;    
354                 }
355                 
356                 public static string ResolvePhysicalPathFromAppAbsolute (string path)
357                 {
358                         if (path [0] != '~') return path;
359                                 
360                         if (path.Length == 1)
361                                 return HttpRuntime.AppDomainAppPath;
362                         
363                         if (path [1] == '/' || path [1] == '\\') {
364                                 string appPath = HttpRuntime.AppDomainAppPath;
365                                 if (appPath.Length > 1)
366                                         return appPath + "/" + path.Substring (2);
367                                 return "/" + path.Substring (2);
368                         }
369                         return path;    
370                 }
371         }
372 }
373