2004-06-09 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System / System / Uri.cs
index ef60f0f99df4c74c72380af9524e027e451313bf..ddec91998adac4327992ea181c218c6c60e5db64 100755 (executable)
@@ -1,18 +1,23 @@
 //
 // System.Uri
 //
-// Author:
+// Authors:
 //    Lawrence Pit (loz@cable.a2000.nl)
-//
-// Author:
 //    Garrett Rooney (rooneg@electricjellyfish.net)
+//    Ian MacLean (ianm@activestate.com)
+//    Ben Maurer (bmaurer@users.sourceforge.net)
+//    Atsushi Enomoto (atsushi@ximian.com)
 //
 // (C) 2001 Garrett Rooney
+// (C) 2003 Ian MacLean
+// (C) 2003 Ben Maurer
+// (C) 2003 Novell inc.
 //
 
 using System.Net;
 using System.Runtime.Serialization;
 using System.Text;
+using System.Collections;
 
 // See RFC 2396 for more info on URI's.
 
@@ -31,7 +36,11 @@ namespace System
                // o  fragment is empty or starts with # char
                // o  all class variables are in escaped format when they are escapable,
                //    except cachedToString.
-               
+               // o  UNC is supported, as starts with "\\" for windows,
+               //    or "//" with unix.
+
+               private bool isWindowsFilePath = false;
+               private bool isUnixFilePath = false;
                private string scheme = String.Empty;
                private string host = String.Empty;
                private int port = -1;
@@ -39,6 +48,12 @@ namespace System
                private string query = String.Empty;
                private string fragment = String.Empty;
                private string userinfo = String.Empty;
+               private bool is_root_path = false;
+               private bool is_wins_dir = true;
+               private bool isUnc = false;
+               private bool isOpaquePart = false;
+
+               private string [] segments;
                
                private bool userEscaped = false;
                private string cachedAbsoluteUri = null;
@@ -67,9 +82,8 @@ namespace System
 
                protected Uri (SerializationInfo serializationInfo, 
                               StreamingContext streamingContext) :
-                       this (serializationInfo.GetString ("Uri"), true)
+                       this (serializationInfo.GetString ("AbsoluteUri"), true)
                {
-                       // TODO: check whether this is compatible with ms.net
                }
 
                public Uri (string uriString, bool dontEscape) 
@@ -79,11 +93,9 @@ namespace System
                        
                        if (userEscaped) 
                                return;
-                       
+
                        host = EscapeString (host, false, true, false);
                        path = EscapeString (path);
-                       query = EscapeString (query);
-                       fragment = EscapeString (fragment, false, false, true);
                }
 
                public Uri (Uri baseUri, string relativeUri) 
@@ -91,41 +103,155 @@ namespace System
                {                       
                }
 
-               [MonoTODO]
                public Uri (Uri baseUri, string relativeUri, bool dontEscape) 
                {
-                       userEscaped = dontEscape;
+                       if (baseUri == null)
+                               throw new NullReferenceException ("baseUri");
+
                        // See RFC 2396 Par 5.2 and Appendix C
-                       throw new NotImplementedException ();
+
+                       userEscaped = dontEscape;
+
+                       if (relativeUri == null)
+                               throw new NullReferenceException ("relativeUri");
+
+                       int pos = relativeUri.IndexOf (':');
+                       if (pos != -1) {
+
+                               int pos2 = relativeUri.IndexOfAny (new char [] {'/', '\\'});
+
+                               if (pos2 > pos) {
+                                       // equivalent to new Uri (relativeUri, dontEscape)
+                                       Parse (relativeUri);
+
+                                       if (userEscaped) 
+                                               return;
+
+                                       host = EscapeString (host, false, true, false);
+                                       path = EscapeString (path);
+                                       return;
+                               }
+                       }
+
+                       this.scheme = baseUri.scheme;
+                       this.host = baseUri.host;
+                       this.port = baseUri.port;
+                       this.userinfo = baseUri.userinfo;
+                       this.isUnc = baseUri.isUnc;
+                       this.isWindowsFilePath = baseUri.isWindowsFilePath;
+                       this.isUnixFilePath = baseUri.isUnixFilePath;
+                       this.isOpaquePart = baseUri.isOpaquePart;
+
+                       if (relativeUri == String.Empty) {
+                               this.path = baseUri.path;
+                               this.query = baseUri.query;
+                               this.fragment = baseUri.fragment;
+                               return;
+                       }
+                       
+                       // 8 fragment
+                       pos = relativeUri.IndexOf ('#');
+                       if (pos != -1) {
+                               fragment = relativeUri.Substring (pos);
+                               relativeUri = relativeUri.Substring (0, pos);
+                       }
+
+                       // 6 query
+                       pos = relativeUri.IndexOf ('?');
+                       if (pos != -1) {
+                               query = relativeUri.Substring (pos);
+                               if (!userEscaped)
+                                       query = EscapeString (query);
+                               relativeUri = relativeUri.Substring (0, pos);
+                       }
+
+                       if (relativeUri.Length > 0 && relativeUri [0] == '/') {
+                               path = relativeUri;
+                               if (!userEscaped)
+                                       path = EscapeString (path);
+                               return;
+                       }
+                       
+                       // par 5.2 step 6 a)
+                       path = baseUri.path;
+                       if (relativeUri.Length > 0 || query.Length > 0) {
+                               pos = path.LastIndexOf ('/');
+                               if (pos >= 0) 
+                                       path = path.Substring (0, pos + 1);
+                       }
+
+                       if(relativeUri.Length == 0)
+                               return;
+       
+                       // 6 b)
+                       path += relativeUri;
+
+                       // 6 c)
+                       int startIndex = 0;
+                       while (true) {
+                               pos = path.IndexOf ("./", startIndex);
+                               if (pos == -1)
+                                       break;
+                               if (pos == 0)
+                                       path = path.Remove (0, 2);
+                               else if (path [pos - 1] != '.')
+                                       path = path.Remove (pos, 2);
+                               else
+                                       startIndex = pos + 1;
+                       }
+                       
+                       // 6 d)
+                       if (path.Length > 1 && 
+                           path [path.Length - 1] == '.' &&
+                           path [path.Length - 2] == '/')
+                               path = path.Remove (path.Length - 1, 1);
+                       
+                       // 6 e)
+                       startIndex = 0;
+                       while (true) {
+                               pos = path.IndexOf ("/../", startIndex);
+                               if (pos == -1)
+                                       break;
+                               if (pos == 0) {
+                                       startIndex = 3;
+                                       continue;
+                               }
+                               int pos2 = path.LastIndexOf ('/', pos - 1);
+                               if (pos2 == -1) {
+                                       startIndex = pos + 1;
+                               } else {
+                                       if (path.Substring (pos2 + 1, pos - pos2 - 1) != "..")
+                                               path = path.Remove (pos2 + 1, pos - pos2 + 3);
+                                       else
+                                               startIndex = pos + 1;
+                               }
+                       }
+                       
+                       // 6 f)
+                       if (path.Length > 3 && path.EndsWith ("/..")) {
+                               pos = path.LastIndexOf ('/', path.Length - 4);
+                               if (pos != -1)
+                                       if (path.Substring (pos + 1, path.Length - pos - 4) != "..")
+                                               path = path.Remove (pos + 1, path.Length - pos - 1);
+                       }
+                       
+                       if (!userEscaped)
+                               path = EscapeString (path);
                }               
                
                // Properties
-               
+
                public string AbsolutePath { 
                        get { return path; } 
                }
 
                public string AbsoluteUri { 
-                       get { 
-                               if (cachedAbsoluteUri == null) {                        
-                                       StringBuilder s = new StringBuilder ();
-                                       s.Append (scheme);
-                                       s.Append (GetSchemeDelimiter (scheme));
-                                       if (path.Length > 1 && path [1] == ':' && "file".Equals (scheme)) 
-                                               s.Append ('/');  // win32 file
-                                       if (userinfo.Length > 0) 
-                                               s.Append (userinfo).Append ('@');
-                                       s.Append (host);
-                                       int defaultPort = GetDefaultPort (scheme);
-                                       if ((port != -1) && (port != defaultPort))
-                                               s.Append (':').Append (port);                    
-                                       s.Append (path);
-                                       s.Append (query);
-                                       s.Append (fragment);
-
-                                       cachedAbsoluteUri = s.ToString ();
+                       get {
+                               if (cachedAbsoluteUri == null) {
+//                                     cachedAbsoluteUri = GetLeftPart (UriPartial.Path) + query + fragment;
+                                       string qf = IsFile ? query + fragment : EscapeString (query + fragment);
+                                       cachedAbsoluteUri = GetLeftPart (UriPartial.Path) + qf;
                                }
-                               
                                return cachedAbsoluteUri;
                        } 
                }
@@ -146,7 +272,14 @@ namespace System
                }
 
                public UriHostNameType HostNameType { 
-                       get { return CheckHostName (host); } 
+                       get {
+                               UriHostNameType ret = CheckHostName (host);
+                               if (ret != UriHostNameType.Unknown)
+                                       return ret;
+
+                               // looks it always returns Basic...
+                               return UriHostNameType.Basic; //.Unknown;
+                       } 
                }
 
                public bool IsDefaultPort { 
@@ -166,7 +299,8 @@ namespace System
                                        return true;
                                        
                                try {
-                                       return IPAddress.IsLoopback (IPAddress.Parse (host));
+                                       if (IPAddress.Loopback.Equals (IPAddress.Parse (host)))
+                                               return true;
                                } catch (FormatException) {}
 
                                try {
@@ -177,23 +311,37 @@ namespace System
                        } 
                }
 
-               public bool IsUnc { 
-                       get { return (scheme == Uri.UriSchemeFile); } 
+               public bool IsUnc {
+                       // rule: This should be true only if
+                       //   - uri string starts from "\\", or
+                       //   - uri string starts from "//" (Samba way)
+                       get { return isUnc; } 
                }
 
                public string LocalPath { 
-                       get { 
-                               if (!IsUnc)
-                                       return path;                            
-                               
+                       get {
+                               if (!IsFile)
+                                       return path;
+
+                               bool windows = (path.Length > 3 && path [1] == ':' &&
+                                               (path [2] == '\\' || path [2] == '/'));
+
+                               if (!IsUnc) {
+                                       string p = Unescape (path);
+                                       if (System.IO.Path.DirectorySeparatorChar == '\\' || windows)
+                                               return p.Replace ('/', '\\');
+                                       else
+                                               return p;
+                               }
+
                                // support *nix and W32 styles
                                if (path.Length > 1 && path [1] == ':')
-                                       return path.Replace ('/', '\\');
-                                       
+                                       return Unescape (path.Replace ('/', '\\'));
+
                                if (System.IO.Path.DirectorySeparatorChar == '\\')
-                                       return "\\\\" + host + path.Replace ('/', '\\');
+                                       return "\\\\" + Unescape (host + path.Replace ('/', '\\'));
                                else 
-                                       return "/" + host + path;
+                                       return (is_root_path? "/": "") + (is_wins_dir? "/": "") + Unescape (host + path);
                        } 
                }
 
@@ -215,15 +363,39 @@ namespace System
 
                public string [] Segments { 
                        get { 
-                               string p = path.EndsWith ("/") 
-                                        ? path.Remove (path.Length - 1, 1)
-                                        : path;
-                               string [] segments = p.Split ('/');
-                               int len = segments.Length - 1;
-                               for (int i = 0; i < len; i++) 
-                                       segments [i] += '/';
-                               if (path.EndsWith ("/"))
-                                       segments [len] += '/';
+                               if (segments != null)
+                                       return segments;
+
+                               if (path == "") {
+                                       segments = new string [0];
+                                       return segments;
+                               }
+
+                               string [] parts = path.Split ('/');
+                               segments = parts;
+                               bool endSlash = path.EndsWith ("/");
+                               if (parts.Length > 0 && endSlash) {
+                                       string [] newParts = new string [parts.Length - 1];
+                                       Array.Copy (parts, 0, newParts, 0, parts.Length - 1);
+                                       parts = newParts;
+                               }
+
+                               int i = 0;
+                               if (IsFile && path.Length > 1 && path [1] == ':') {
+                                       string [] newParts = new string [parts.Length + 1];
+                                       Array.Copy (parts, 1, newParts, 2, parts.Length - 1);
+                                       parts = newParts;
+                                       parts [0] = path.Substring (0, 2);
+                                       parts [1] = "";
+                                       i++;
+                               }
+                               
+                               int end = parts.Length;
+                               for (; i < end; i++) 
+                                       if (i != end - 1 || endSlash)
+                                               parts [i] += '/';
+
+                               segments = parts;
                                return segments;
                        } 
                }
@@ -300,6 +472,11 @@ namespace System
                        return true;
                }
 
+               [MonoTODO ("Find out what this should do")]
+               protected virtual void Canonicalize ()
+               {
+               }
+
                public static bool CheckSchemeName (string schemeName) 
                {
                        if (schemeName == null || schemeName.Length == 0)
@@ -311,13 +488,18 @@ namespace System
                        int len = schemeName.Length;
                        for (int i = 1; i < len; i++) {
                                char c = schemeName [i];
-                               if (!Char.IsLetterOrDigit (c) || c != ',' || c != '+' || c != '-')
+                               if (!Char.IsLetterOrDigit (c) && c != '.' && c != '+' && c != '-')
                                        return false;
                        }
                        
                        return true;
                }
-               
+
+               [MonoTODO ("Find out what this should do")]
+               protected virtual void CheckSecurity ()
+               {
+               }
+
                public override bool Equals (object comparant) 
                {
                        if (comparant == null) 
@@ -331,12 +513,12 @@ namespace System
                                uri = new Uri (s);
                        }
                        
-                       return ((this.scheme == uri.scheme) &&
-                               (this.userinfo == uri.userinfo) &&
-                               (this.host == uri.host) &&
-                               (this.port == uri.port) &&
-                               (this.path == uri.path) &&
-                               (this.query == uri.query));
+                       return ((this.scheme.ToLower () == uri.scheme.ToLower ()) &&
+                               (this.userinfo.ToLower () == uri.userinfo.ToLower ()) &&
+                               (this.host.ToLower () == uri.host.ToLower ()) &&
+                               (this.port == uri.port) &&
+                               (this.path == uri.path) &&
+                               (this.query.ToLower () == uri.query.ToLower ()));
                }               
                
                public override int GetHashCode () 
@@ -353,20 +535,42 @@ namespace System
                
                public string GetLeftPart (UriPartial part) 
                {
-                       switch (part) {
+                       int defaultPort;
+                       switch (part) {                         
                        case UriPartial.Scheme : 
-                               return scheme + GetSchemeDelimiter (scheme); 
+                               return scheme + GetOpaqueWiseSchemeDelimiter ();
                        case UriPartial.Authority :
                                if (host == String.Empty ||
                                    scheme == Uri.UriSchemeMailto ||
                                    scheme == Uri.UriSchemeNews)
                                        return String.Empty;
-                               else
-                                       return scheme + GetSchemeDelimiter (scheme) + host;
-                       case UriPartial.Path :
-                               return scheme + GetSchemeDelimiter (scheme) + userinfo +
-                                      (userinfo.Length > 0 ? "@" : String.Empty) + 
-                                      host + path;
+                                       
+                               StringBuilder s = new StringBuilder ();
+                               s.Append (scheme);
+                               s.Append (GetOpaqueWiseSchemeDelimiter ());
+                               if (path.Length > 1 && path [1] == ':' && (Uri.UriSchemeFile == scheme)) 
+                                       s.Append ('/');  // win32 file
+                               if (userinfo.Length > 0) 
+                                       s.Append (userinfo).Append ('@');
+                               s.Append (host);
+                               defaultPort = GetDefaultPort (scheme);
+                               if ((port != -1) && (port != defaultPort))
+                                       s.Append (':').Append (port);                    
+                               return s.ToString ();                           
+                       case UriPartial.Path :                  
+                               StringBuilder sb = new StringBuilder ();
+                               sb.Append (scheme);
+                               sb.Append (GetOpaqueWiseSchemeDelimiter ());
+                               if (path.Length > 1 && path [1] == ':' && (Uri.UriSchemeFile == scheme)) 
+                                       sb.Append ('/');  // win32 file
+                               if (userinfo.Length > 0) 
+                                       sb.Append (userinfo).Append ('@');
+                               sb.Append (host);
+                               defaultPort = GetDefaultPort (scheme);
+                               if ((port != -1) && (port != defaultPort))
+                                       sb.Append (':').Append (port);                   
+                               sb.Append (path);
+                               return sb.ToString ();
                        }
                        return null;
                }
@@ -433,32 +637,56 @@ namespace System
                                IsHexDigit (pattern [index]));
                }
 
-               [MonoTODO]
                public string MakeRelative (Uri toUri) 
                {
-                       throw new NotImplementedException ();   
+                       if ((this.Scheme != toUri.Scheme) ||
+                           (this.Authority != toUri.Authority))
+                               return toUri.ToString ();
+                               
+                       if (this.path == toUri.path)
+                               return String.Empty;
+                               
+                       string [] segments = this.Segments;
+                       string [] segments2 = toUri.Segments;
+                       
+                       int k = 0;
+                       int max = Math.Min (segments.Length, segments2.Length);
+                       for (; k < max; k++)
+                               if (segments [k] != segments2 [k]) 
+                                       break;
+                       
+                       string result = String.Empty;
+                       for (int i = k + 1; i < segments.Length; i++)
+                               result += "../";
+                       for (int i = k; i < segments2.Length; i++)
+                               result += segments2 [i];
+                       
+                       return result;
                }
 
                public override string ToString () 
                {
                        if (cachedToString != null) 
                                return cachedToString;
-                               
                        cachedToString = Unescape (AbsoluteUri);
-                       
+
                        return cachedToString;
                }
 
-               public void GetObjectData (SerializationInfo info, 
+               void ISerializable.GetObjectData (SerializationInfo info, 
                                          StreamingContext context)
                {
-                       // TODO: check whether this is compatible with ms.net
-                       info.AddValue ("Uri", this.AbsoluteUri);
+                       info.AddValue ("AbsoluteUri", this.AbsoluteUri);
                }
 
 
                // Internal Methods             
 
+               protected virtual void Escape ()
+               {
+                       path = EscapeString (path);
+               }
+
                protected static string EscapeString (string str) 
                {
                        return EscapeString (str, false, true, true);
@@ -469,10 +697,11 @@ namespace System
                        if (str == null)
                                return String.Empty;
                        
+                       byte [] data = Encoding.UTF8.GetBytes (str.ToCharArray ());
                        StringBuilder s = new StringBuilder ();
-                       int len = str.Length;   
+                       int len = data.Length;  
                        for (int i = 0; i < len; i++) {
-                               char c = str [i];
+                               char c = (char) data [i];
                                // reserved    = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ","
                                // mark        = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
                                // control     = <US-ASCII coded characters 00-1F and 7F hexadecimal>
@@ -480,20 +709,40 @@ namespace System
                                // delims      = "<" | ">" | "#" | "%" | <">
                                // unwise      = "{" | "}" | "|" | "\" | "^" | "[" | "]" | "`"
 
+                               // check for escape code already placed in str, 
+                               // i.e. for encoding that follows the pattern 
+                // "%hexhex" in a string, where "hex" is a digit from 0-9 
+                               // or a letter from A-F (case-insensitive).
+                               if('%'.Equals(c) && IsHexEncoding(str,i))
+                               {
+                                       // if ,yes , copy it as is
+                                       s.Append(c);
+                                       s.Append(str[++i]);
+                                       s.Append(str[++i]);
+                                       continue;
+                               }
+
                                if ((c <= 0x20) || (c >= 0x7f) || 
                                    ("<>%\"{}|\\^`".IndexOf (c) != -1) ||
                                    (escapeHex && (c == '#')) ||
                                    (escapeBrackets && (c == '[' || c == ']')) ||
                                    (escapeReserved && (";/?:@&=+$,".IndexOf (c) != -1))) {
+                                       // FIXME: EscapeString should allow wide characters (> 255). HexEscape rejects it.
                                        s.Append (HexEscape (c));
                                        continue;
                                }
+                               
                                        
                                s.Append (c);
                        }
                        
                        return s.ToString ();
-               }               
+               }
+
+               [MonoTODO ("Find out what this should do!")]
+               protected virtual void Parse ()
+               {
+               }       
                
                protected virtual string Unescape (string str) 
                {
@@ -507,7 +756,7 @@ namespace System
                                        s.Append (HexUnescape (str, ref i));
                                        i--;
                                } else
-                                       s.Append (c);                                   
+                                       s.Append (c);
                        }
                        return s.ToString ();
                }
@@ -542,32 +791,71 @@ namespace System
                                        break;
                        }
 
+                       if (pos == len)
+                               throw new UriFormatException ("The format of the URI could not be determined.");
+
+                       if (c == '/') {
+                               is_root_path = true;
+                       }
+
+                       if ((uriString.Length >= 2) &&
+                                       ((uriString [0] == '/') && (uriString [1] == '/')) ||
+                                       ((uriString [0] == '\\') || (uriString [1] == '\\'))) {
+                               is_wins_dir = true;
+                               is_root_path = true;
+                       }
+
                        // 2 scheme
-                       if (c == ':') {                         
+                       if (c == ':') {
                                if (pos == 1) {
                                        // a windows filepath
-                                       scheme = "file";
+                                       if (uriString.Length < 3 || (uriString [2] != '\\' && uriString [2] != '/'))
+                                               throw new UriFormatException ("Invalid URI: The format of the URI could not be determined.");
+                                       isWindowsFilePath = true;
+                                       scheme = Uri.UriSchemeFile;
                                        path = uriString.Replace ('\\', '/');
                                        return;
                                }
-                                       
+
                                scheme = uriString.Substring (0, pos).ToLower ();
                                uriString = uriString.Remove (0, pos + 1);
-                       } else 
-                               scheme = "file";                        
-                                               
+                       } else if ((c == '/') && (pos == 0)) {
+                               scheme = Uri.UriSchemeFile;
+                               if (uriString.Length > 1 && uriString [1] != '/')
+                                       // unix bare filepath
+                                       isUnixFilePath = true;
+                               else
+                                       // unix UNC (kind of)
+                                       isUnc = true;
+                       } else {
+                               if (uriString [0] != '\\' && uriString [0] != '/' && !uriString.StartsWith ("file://"))
+                                       throw new UriFormatException ("Invalid URI: The format of the URI could not be determined.");
+                               scheme = Uri.UriSchemeFile;
+                               if (uriString.StartsWith ("\\\\")) {
+                                       isUnc = true;
+                                       isWindowsFilePath = true;
+                               }
+                               if (uriString.Length > 8 && uriString [8] != '/')
+                                       isUnc = true;
+                       }
+
                        // 3
                        if ((uriString.Length >= 2) && 
-                           ((uriString [0] == '/') || (uriString [0] == '\\')) &&
-                           ((uriString [1] == '/') || (uriString [1] == '\\'))) 
-                               if ("file".Equals (scheme)) 
-                                       uriString = uriString.TrimStart (new char [] {'/', '\\'});                              
+                           ((uriString [0] == '/') && (uriString [1] == '/')) ||
+                           ((uriString [0] == '\\') || (uriString [1] == '\\')))  {
+                               if (scheme == Uri.UriSchemeFile)
+                                       uriString = uriString.TrimStart (new char [] {'/', '\\'});
                                else
                                        uriString = uriString.Remove (0, 2);
-                               
+                       } else if (!IsPredefinedScheme (scheme)) {
+                               path = uriString;
+                               isOpaquePart = true;
+                               return;
+                       }
+
                        // 8 fragment
                        pos = uriString.IndexOf ('#');
-                       if (pos != -1) {
+                       if (!IsUnc && pos != -1) {
                                fragment = uriString.Substring (pos);
                                uriString = uriString.Substring (0, pos);
                        }
@@ -583,7 +871,8 @@ namespace System
                        pos = uriString.IndexOfAny (new char[] {'/', '\\'});
                        if (pos == -1) {
                                if ((scheme != Uri.UriSchemeMailto) &&
-                                   (scheme != Uri.UriSchemeNews))
+                                   (scheme != Uri.UriSchemeNews) &&
+                                       (scheme != Uri.UriSchemeFile))
                                        path = "/";
                        } else {
                                path = uriString.Substring (pos).Replace ('\\', '/');
@@ -604,8 +893,7 @@ namespace System
                                string portStr = uriString.Remove (0, pos + 1);
                                if (portStr.Length > 1 && portStr [portStr.Length - 1] != ']') {
                                        try {
-                                               port = Int32.Parse (portStr);
-                                               new System.Net.IPEndPoint (0, port);  // test validity port
+                                               port = (int) UInt32.Parse (portStr);
                                                uriString = uriString.Substring (0, pos);
                                        } catch (Exception) {
                                                throw new UriFormatException ("Invalid URI: invalid port number");
@@ -618,14 +906,70 @@ namespace System
                        
                        // 4 authority
                        host = uriString;
-                       if (host.Length > 1 && host [0] == '[' && host [host.Length - 1] == ']') 
+                       if (host.Length > 1 && host [0] == '[' && host [host.Length - 1] == ']') {
                                try {
                                        host = "[" + IPv6Address.Parse (host).ToString () + "]";
                                } catch (Exception) {
                                        throw new UriFormatException ("Invalid URI: The hostname could not be parsed");
                                }
+                       }
+
+                       if (host.Length == 2 && host [1] == ':') {
+                               // windows filepath
+                               path = host + path;
+                               host = String.Empty;
+                       } else if (isUnixFilePath) {
+                               uriString = "//" + uriString;
+                               host = String.Empty;
+                       } else if (host.Length == 0) {
+                               throw new UriFormatException ("Invalid URI: The hostname could not be parsed");
+                       } else if (scheme == UriSchemeFile) {
+                               isUnc = true;
+                       }
+
+                       if ((scheme != Uri.UriSchemeMailto) &&
+                                       (scheme != Uri.UriSchemeNews) &&
+                                       (scheme != Uri.UriSchemeFile))
+                       path = Reduce (path);
                }
 
+               private static string Reduce (string path)
+               {
+                       path = path.Replace ('\\','/');
+                       string [] parts = path.Split ('/');
+                       ArrayList result = new ArrayList ();
+
+                       int end = parts.Length;
+                       for (int i = 0; i < end; i++) {
+                               string current = parts [i];
+                               if (current == "" || current == "." )
+                                       continue;
+
+                               if (current == "..") {
+                                       if (result.Count == 0) {
+                                               if (i == 1) // see bug 52599
+                                                       continue;
+                                               throw new Exception ("Invalid path.");
+                                       }
+
+                                       result.RemoveAt (result.Count - 1);
+                                       continue;
+                               }
+
+                               result.Add (current);
+                       }
+
+                       if (result.Count == 0)
+                               return "/";
+
+                       result.Insert (0, "");
+
+                       string res = String.Join ("/", (string []) result.ToArray (typeof (string)));
+                       if (path.EndsWith ("/"))
+                               res += '/';
+                               
+                       return res;
+               }
                                
                private struct UriScheme 
                {
@@ -643,8 +987,8 @@ namespace System
 
                static UriScheme [] schemes = new UriScheme [] {
                        new UriScheme (UriSchemeHttp, SchemeDelimiter, 80),
-                       new UriScheme (UriSchemeHttps, SchemeDelimiter, 223),
-                       new UriScheme (UriSchemeFtp, SchemeDelimiter, 23),
+                       new UriScheme (UriSchemeHttps, SchemeDelimiter, 443),
+                       new UriScheme (UriSchemeFtp, SchemeDelimiter, 21),
                        new UriScheme (UriSchemeFile, SchemeDelimiter, -1),
                        new UriScheme (UriSchemeMailto, ":", 25),
                        new UriScheme (UriSchemeNews, ":", -1),
@@ -657,7 +1001,7 @@ namespace System
                        for (int i = 0; i < schemes.Length; i++) 
                                if (schemes [i].scheme == scheme)
                                        return schemes [i].delimiter;
-                       return String.Empty;
+                       return Uri.SchemeDelimiter;
                }
                
                internal static int GetDefaultPort (string scheme)
@@ -666,6 +1010,76 @@ namespace System
                                if (schemes [i].scheme == scheme)
                                        return schemes [i].defaultPort;
                        return -1;                      
-               }                               
+               }
+
+               private string GetOpaqueWiseSchemeDelimiter ()
+               {
+                       if (isOpaquePart)
+                               return ":";
+                       else
+                               return GetSchemeDelimiter (scheme);
+               }
+
+               protected virtual bool IsBadFileSystemCharacter (char ch)
+               {
+                       // It does not always overlap with InvalidPathChars.
+                       int chInt = (int) ch;
+                       if (chInt < 32 || (chInt < 64 && chInt > 57))
+                               return true;
+                       switch (chInt) {
+                       case 0:
+                       case 34: // "
+                       case 38: // &
+                       case 42: // *
+                       case 44: // ,
+                       case 47: // /
+                       case 92: // \
+                       case 94: // ^
+                       case 124: // |
+                               return true;
+                       }
+
+                       return false;
+               }
+
+               
+               protected static bool IsExcludedCharacter (char ch)
+               {
+                       if (ch <= 32 || ch >= 127)
+                               return true;
+                       
+                       if (ch == '"' || ch == '#' || ch == '%' || ch == '<' ||
+                           ch == '>' || ch == '[' || ch == '\\' || ch == ']' ||
+                           ch == '^' || ch == '`' || ch == '{' || ch == '|' ||
+                           ch == '}')
+                               return true;
+                       return false;
+               }
+
+               private static bool IsPredefinedScheme (string scheme)
+               {
+                       switch (scheme) {
+                       case "http":
+                       case "https":
+                       case "file":
+                       case "ftp":
+                       case "nntp":
+                       case "gopher":
+                       case "mailto":
+                       case "news":
+                               return true;
+                       default:
+                               return false;
+                       }
+               }
+
+               protected virtual bool IsReservedCharacter (char ch)
+               {
+                       if (ch == '$' || ch == '&' || ch == '+' || ch == ',' ||
+                           ch == '/' || ch == ':' || ch == ';' || ch == '=' ||
+                           ch == '@')
+                               return true;
+                       return false;
+               }
        }
 }