+ internal 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;
+ }
+ }
+}