2002-10-25 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[mono.git] / mcs / class / System.Web / System.Web.Util / UrlUtils.cs
1 /**\r
2  * Namespace: System.Web.UI.Util\r
3  * Class:     UrlUtils\r
4  * \r
5  * Author:  Gaurav Vaish\r
6  * Maintainer: gvaish@iitk.ac.in\r
7  * Status:  ??%\r
8  * \r
9  * (C) Gaurav Vaish (2001)\r
10  */\r
11 \r
12 using System;\r
13 using System.Collections;\r
14 using System.Text;\r
15 \r
16 namespace System.Web.Util\r
17 {\r
18         internal class UrlUtils\r
19         {\r
20                 /*\r
21                  * I could not find these functions in the class System.Uri\r
22                  * Besides, an instance of Uri will not be formed until and unless the address is of\r
23                  * the form protocol://[user:pass]host[:port]/[fullpath]\r
24                  * ie, a protocol, and that too without any blanks before,\r
25                  * is a must which may not be the case here.\r
26                  * Important: Escaped URL is assumed here. nothing like .aspx?path=/something\r
27                  * It should be .aspx?path=%2Fsomething\r
28                  */\r
29                 public static string GetProtocol(string url)\r
30                 {\r
31                         //Taking code from Java Class java.net.URL\r
32                         if(url!=null)\r
33                         {\r
34                                 if(url.Length>0)\r
35                                 {\r
36                                         \r
37                                         int i, start = 0, limit;\r
38                                         limit = url.Length;\r
39                                         char c;\r
40                                         bool aRef = false;\r
41                                         while( (limit > 0) && (url[limit-1] <= ' '))\r
42                                         {\r
43                                                 limit --;\r
44                                         }\r
45                                         while( (start < limit) && (url[start] <= ' '))\r
46                                         {\r
47                                                 start++;\r
48                                         }\r
49                                         if(RegionMatches(true, url, start, "url:", 0, 4))\r
50                                         {\r
51                                                 start += 4;\r
52                                         }\r
53                                         if(start < url.Length && url[start]=='#')\r
54                                         {\r
55                                                 aRef = true;\r
56                                         }\r
57                                         for(i = start; !aRef && (i < limit) && ((c=url[i]) != '/'); i++)\r
58                                         {\r
59                                                 if(c==':')\r
60                                                 {\r
61                                                         return url.Substring(start, i - start);\r
62                                                 }\r
63                                         }\r
64                                 }\r
65                         }\r
66                         return String.Empty;\r
67                 }\r
68                 \r
69                 public static bool IsRelativeUrl(string url)\r
70                 {\r
71                         if(url.IndexOf(':') != -1)\r
72                                 return !IsRootUrl(url);\r
73                         return true;\r
74                 }\r
75 \r
76                 public static bool IsRootUrl(string url)\r
77                 {\r
78                         if(url!=null)\r
79                         {\r
80                                 if(url.Length>0)\r
81                                 {\r
82                                         return IsValidProtocol(GetProtocol(url).ToLower());\r
83                                 }\r
84                         }\r
85                         return true;\r
86                 }\r
87                 \r
88                 public static bool IsRooted(string path)\r
89                 {\r
90                         if(path!=null && path.Length > 0)\r
91                         {\r
92                                 return (path[0]=='/' || path[0]=='\\');\r
93                         }\r
94                         return false;\r
95                 }\r
96                 \r
97                 public static void FailIfPhysicalPath(string path)\r
98                 {\r
99                         if(path!= null && path.Length > 0)\r
100                         {\r
101                                 if(path[0]==':' || path.StartsWith(@"\\"))\r
102                                         throw new HttpException(HttpRuntime.FormatResourceString("Physical_path_not_allowed", path));\r
103                         }\r
104                 }\r
105                 \r
106                 public static string Combine(string basePath, string relPath)\r
107                 {\r
108                         FailIfPhysicalPath(relPath);\r
109                         if(IsRootUrl(relPath))\r
110                         {\r
111                                 if(relPath != null && relPath.Length > 0)\r
112                                 {\r
113                                         return Reduce(relPath);\r
114                                 }\r
115                                 return String.Empty;\r
116                         }\r
117                         if(relPath.Length < 3 || relPath[0]!='~' || (relPath[0]!='/' && relPath[0]!='\\'))\r
118                         {\r
119                                 if(basePath==null || basePath.Length==1 || basePath[0]=='/')\r
120                                         basePath = String.Empty;\r
121                                 return Reduce(basePath + "/" + relPath);\r
122                         }\r
123                         string vPath = HttpRuntime.AppDomainAppVirtualPath;\r
124                         if(vPath.Length <= 1)\r
125                                 vPath = String.Empty;\r
126                         return Reduce(vPath + "/" + relPath.Substring(2));\r
127                 }\r
128                 \r
129                 public static bool IsValidProtocol(string protocol)\r
130                 {\r
131                         if(protocol.Length < 1)\r
132                                 return false;\r
133                         char c = protocol[0];\r
134                         if(!Char.IsLetter(c))\r
135                         {\r
136                                 return false;\r
137                         }\r
138                         for(int i=1; i < protocol.Length; i++)\r
139                         {\r
140                                 c = protocol[i];\r
141                                 if(!Char.IsLetterOrDigit(c) && c!='.' && c!='+' && c!='-')\r
142                                 {\r
143                                         return false;\r
144                                 }\r
145                         }\r
146                         return true;\r
147                 }\r
148                 \r
149                 /*\r
150                  * MakeRelative("http://www.foo.com/bar1/bar2/file","http://www.foo.com/bar1")\r
151                  * will return "bar2/file"\r
152                  * while MakeRelative("http://www.foo.com/bar1/...","http://www.anotherfoo.com")\r
153                  * return 'null' and so does the call\r
154                  * MakeRelative("http://www.foo.com/bar1/bar2","http://www.foo.com/bar")\r
155                  */\r
156                 public static string MakeRelative(string fullUrl, string relativeTo)\r
157                 {\r
158                         if(fullUrl==relativeTo)\r
159                         {\r
160                                 return String.Empty;\r
161                         }\r
162                         if(fullUrl.IndexOf(relativeTo)!=0)\r
163                         {\r
164                                 return null;\r
165                         }\r
166                         string leftOver = fullUrl.Substring(relativeTo.Length);\r
167                         if(!fullUrl.EndsWith("/") && !leftOver.StartsWith("/"))\r
168                         {\r
169                                 return null;\r
170                         }\r
171                         if(leftOver.StartsWith("/"))\r
172                         {\r
173                                 leftOver = leftOver.Substring(1);\r
174                         }\r
175                         return leftOver;\r
176                 }\r
177                 \r
178                 /*\r
179                  * Check JavaDocs for java.lang.String#RegionMatches(bool, int, String, int, int)\r
180                  * Could not find anything similar in the System.String class\r
181                  */\r
182                 public static bool RegionMatches(bool ignoreCase, string source, int start, string match, int offset, int len)\r
183                 {\r
184                         if(source!=null || match!=null)\r
185                         {\r
186                                 if(source.Length>0 && match.Length>0)\r
187                                 {\r
188                                         char[] ta = source.ToCharArray();\r
189                                         char[] pa = match.ToCharArray();\r
190                                         if((offset < 0) || (start < 0) || (start > (source.Length - len)) || (offset > (match.Length - len)))\r
191                                         {\r
192                                                 return false;\r
193                                         }\r
194                                         while(len-- > 0)\r
195                                         {\r
196                                                 char c1 = ta[start++];\r
197                                                 char c2 = pa[offset++];\r
198                                                 if(c1==c2)\r
199                                                         continue;\r
200                                                 if(ignoreCase)\r
201                                                 {\r
202                                                         if(Char.ToUpper(c1)==Char.ToUpper(c2))\r
203                                                                 continue;\r
204                                                         // Check for Gregorian Calendar where the above may not hold good\r
205                                                         if(Char.ToLower(c1)==Char.ToLower(c2))\r
206                                                                 continue;\r
207                                                 }\r
208                                                 return false;\r
209                                         }\r
210                                         return true;\r
211                                 }\r
212                         }\r
213                         return false;\r
214                 }\r
215 \r
216                 public static string Reduce(string path)\r
217                 {\r
218                         int len = path.Length;\r
219                         int dotIndex = -1;\r
220                         path = path.Replace('\\','/');\r
221                         while(true)\r
222                         {\r
223                                 dotIndex++;\r
224                                 dotIndex = path.IndexOf('.', dotIndex);\r
225                                 if(dotIndex < 0)\r
226                                 {\r
227                                         return path;\r
228                                 }\r
229                                 if(dotIndex != 0 && path[dotIndex -1]=='/')\r
230                                         continue;\r
231                                 if(dotIndex+1 == len || path[dotIndex+1]=='/')\r
232                                         break;\r
233                                 if(path[dotIndex+1]=='.')\r
234                                         continue;\r
235                                 if(dotIndex+2 == len || path[dotIndex+2]=='/')\r
236                                         break;\r
237                         }\r
238                         ArrayList list = new ArrayList();\r
239                         StringBuilder sb = new StringBuilder();\r
240                         dotIndex = 0;\r
241                         int temp;\r
242                         do\r
243                         {\r
244                                 temp = dotIndex;\r
245                                 dotIndex = path.IndexOf('/', temp + 1);\r
246                                 if(dotIndex < 0)\r
247                                         dotIndex = len;\r
248                                 if( (dotIndex - temp) <= 3 && (dotIndex < 1 || path[dotIndex - 1]== '.') && ( (temp+1) >= len || path[temp+1]=='.') )\r
249                                 {\r
250                                         if(dotIndex - temp == 3)\r
251                                                 continue;\r
252                                         if(list.Count == 0)\r
253                                                 throw new System.Web.HttpException(System.Web.HttpRuntime.FormatResourceString("Cannot_exit_up_top_directory"));\r
254                                         sb.Length = (int) list[list.Count - 1];\r
255                                         list.RemoveRange(list.Count - 1, 1);\r
256                                         continue;\r
257                                 }\r
258                                 list.Add(sb.Length);\r
259                                 sb.Append(path, temp, dotIndex - temp);\r
260                         } while(dotIndex != len);\r
261                         return sb.ToString();\r
262                 }\r
263                 \r
264                 public static string GetDirectory(string url)\r
265                 {\r
266                         if(url==null)\r
267                         {\r
268                                 return null;\r
269                         }\r
270                         if(url.Length==0)\r
271                         {\r
272                                 return String.Empty;\r
273                         }\r
274                         url.Replace('\\','/');\r
275                         string baseDir = url.Substring(0, url.LastIndexOf('/'));\r
276                         if(baseDir.Length==0)\r
277                         {\r
278                                 baseDir = "/";\r
279                         }\r
280                         return baseDir;\r
281                 }\r
282         }\r
283 }\r