Merge pull request #3715 from kumpera/fix-44707
[mono.git] / mcs / class / referencesource / System / net / System / UriExt.cs
1 /*++
2 Copyright (c) 2003 Microsoft Corporation
3
4 Module Name:
5
6     UriExt.cs
7
8 Abstract:
9
10     Uri extensibility model Implementation.
11     This file utilizes partial class feature.
12     Uri.cs file contains core System.Uri functionality.
13
14 Author:
15     Alexei Vopilov    Nov 21 2003
16
17 Revision History:
18
19 --*/
20 #if MONO
21 #undef PLATFORM_UNIX
22 #endif
23
24 namespace System {
25     using System.Globalization;
26     using System.Text;
27     using System.Runtime.InteropServices;
28     using System.Diagnostics;
29
30     public partial class Uri {
31         //
32         // All public ctors go through here
33         //
34         private void CreateThis(string uri, bool dontEscape, UriKind uriKind)
35         {
36             // if (!Enum.IsDefined(typeof(UriKind), uriKind)) -- We currently believe that Enum.IsDefined() is too slow 
37             // to be used here.
38             if ((int)uriKind < (int)UriKind.RelativeOrAbsolute || (int)uriKind > (int)UriKind.Relative) {
39 #if MONO
40                 if (uriKind != DotNetRelativeOrAbsolute)
41 #endif
42                 throw new ArgumentException(SR.GetString(SR.net_uri_InvalidUriKind, uriKind));
43             }
44
45             m_String = uri == null? string.Empty: uri;
46
47             if (dontEscape)
48                 m_Flags |= Flags.UserEscaped;
49
50             ParsingError err = ParseScheme(m_String, ref m_Flags, ref m_Syntax);
51             UriFormatException e;
52
53             InitializeUri(err, uriKind, out e);
54             if (e != null)
55                 throw e;
56         }
57         //
58         private void InitializeUri(ParsingError err, UriKind uriKind, out UriFormatException e)
59         {
60             if (err == ParsingError.None)
61             {
62                 if (IsImplicitFile)
63                 {
64 #if MONO
65                     if (uriKind == UriKind.RelativeOrAbsolute && m_String.Length > 0 && m_String[0] == '/' && !useDotNetRelativeOrAbsolute) {
66                         // For original Mono behaviour where / is considered to be absolute path
67                     }
68                     else
69 #endif
70                     // V1 compat VsWhidbey#252282
71                     // A relative Uri wins over implicit UNC path unless the UNC path is of the form "\\something" and 
72                     // uriKind != Absolute
73                     if (
74 #if !PLATFORM_UNIX
75                         NotAny(Flags.DosPath) &&
76 #endif // !PLATFORM_UNIX
77                         uriKind != UriKind.Absolute &&
78                        (uriKind == UriKind.Relative || (m_String.Length >= 2 && (m_String[0] != '\\' || m_String[1] != '\\'))))
79
80                     {
81                         m_Syntax = null; //make it be relative Uri
82                         m_Flags &= Flags.UserEscaped; // the only flag that makes sense for a relative uri
83                         e = null;
84                         return;
85                         // Otheriwse an absolute file Uri wins when it's of the form "\\something"
86                     }
87                     //
88                     // VsWhidbey#423805 and V1 compat issue
89                     // We should support relative Uris of the form c:\bla or c:/bla
90                     //
91 #if !PLATFORM_UNIX
92                     else if (uriKind == UriKind.Relative && InFact(Flags.DosPath))
93                     {
94                         m_Syntax = null; //make it be relative Uri
95                         m_Flags &= Flags.UserEscaped; // the only flag that makes sense for a relative uri
96                         e = null;
97                         return;
98                         // Otheriwse an absolute file Uri wins when it's of the form "c:\something"
99                     }
100 #endif // !PLATFORM_UNIX
101                 }
102             }
103             else if (err > ParsingError.LastRelativeUriOkErrIndex)
104             {
105                 //This is a fatal error based solely on scheme name parsing
106                 m_String = null; // make it be invalid Uri
107                 e = GetException(err);
108                 return;
109             }
110
111             //
112             //
113             //
114             bool hasUnicode = false;
115
116             // Is there unicode ..
117             if ((!s_ConfigInitialized) && CheckForConfigLoad(m_String)){
118                 InitializeUriConfig();
119             }
120
121             m_iriParsing = (s_IriParsing && ((m_Syntax == null) || m_Syntax.InFact(UriSyntaxFlags.AllowIriParsing)));
122             
123             if (m_iriParsing && 
124                 (CheckForUnicode(m_String) || CheckForEscapedUnreserved(m_String))) {
125                 m_Flags |= Flags.HasUnicode;
126                 hasUnicode = true;
127                 // switch internal strings
128                 m_originalUnicodeString = m_String; // original string location changed
129             }
130
131             if (m_Syntax != null)
132             {
133                 if (m_Syntax.IsSimple)
134                 {
135                     if ((err = PrivateParseMinimal()) != ParsingError.None)
136                     {
137                         if (uriKind != UriKind.Absolute && err <= ParsingError.LastRelativeUriOkErrIndex)
138                         {
139                             // RFC 3986 Section 5.4.2 - http:(relativeUri) may be considered a valid relative Uri.
140                             m_Syntax = null; // convert to relative uri
141                             e = null;
142                             m_Flags &= Flags.UserEscaped; // the only flag that makes sense for a relative uri
143                         }
144                         else
145                             e = GetException(err);
146                     }
147                     else if (uriKind == UriKind.Relative)
148                     {
149                         // Here we know that we can create an absolute Uri, but the user has requested only a relative one
150                         e = GetException(ParsingError.CannotCreateRelative);
151                     }
152                     else
153                         e = null;
154                     // will return from here
155
156                     if (m_iriParsing && hasUnicode){
157                         // In this scenario we need to parse the whole string 
158                         EnsureParseRemaining();
159                     }
160                 }
161                 else
162                 {
163                     // offer custom parser to create a parsing context
164                     m_Syntax = m_Syntax.InternalOnNewUri();
165
166                     // incase they won't call us
167                     m_Flags |= Flags.UserDrivenParsing;
168
169                     // Ask a registered type to validate this uri
170                     m_Syntax.InternalValidate(this, out e);
171
172                     if (e != null)
173                     {
174                         // Can we still take it as a relative Uri?
175                         if (uriKind != UriKind.Absolute && err != ParsingError.None 
176                             && err <= ParsingError.LastRelativeUriOkErrIndex)
177                         {
178                             m_Syntax = null; // convert it to relative
179                             e = null;
180                             m_Flags &= Flags.UserEscaped; // the only flag that makes sense for a relative uri
181                         }
182                     }
183                     else // e == null
184                     {
185                         if (err != ParsingError.None || InFact(Flags.ErrorOrParsingRecursion))
186                         {
187                             // User parser took over on an invalid Uri
188                             SetUserDrivenParsing();
189                         }
190                         else if (uriKind == UriKind.Relative)
191                         {
192                             // Here we know that custom parser can create an absolute Uri, but the user has requested only a 
193                             // relative one
194                             e = GetException(ParsingError.CannotCreateRelative);
195                         }
196
197                         if (m_iriParsing && hasUnicode){
198                             // In this scenario we need to parse the whole string 
199                             EnsureParseRemaining();
200                         }
201                         
202                     }
203                     // will return from here
204                 }
205             }
206             // If we encountered any parsing errors that indicate this may be a relative Uri, 
207             // and we'll allow relative Uri's, then create one.
208             else if (err != ParsingError.None && uriKind != UriKind.Absolute 
209                 && err <= ParsingError.LastRelativeUriOkErrIndex)
210             {
211                 e = null;
212                 m_Flags &= (Flags.UserEscaped | Flags.HasUnicode); // the only flags that makes sense for a relative uri
213                 if (m_iriParsing && hasUnicode)
214                 {
215                     // Iri'ze and then normalize relative uris
216                     m_String = EscapeUnescapeIri(m_originalUnicodeString, 0, m_originalUnicodeString.Length,
217                                                 (UriComponents)0);
218                     try
219                     {
220                         if (UriParser.ShouldUseLegacyV2Quirks)
221                             m_String = m_String.Normalize(NormalizationForm.FormC);
222                     }
223                     catch (ArgumentException)
224                     {
225                         e = GetException(ParsingError.BadFormat);
226                     }
227                 }
228             }
229             else
230             {
231                m_String = null; // make it be invalid Uri
232                e = GetException(err);
233             }
234         }
235         
236         //
237         // Checks if there are any unicode or escaped chars or ace to determine whether to load
238         // config
239         //
240         private unsafe bool CheckForConfigLoad(String data)
241         {
242             bool initConfig = false;
243             int length = data.Length;
244
245             fixed (char* temp = data){
246                 for (int i = 0; i < length; ++i){
247
248                     if ((temp[i] > '\x7f') || (temp[i] == '%') ||
249                         ((temp[i] == 'x') && ((i + 3) < length) && (temp[i + 1] == 'n') && (temp[i + 2] == '-') && (temp[i + 3] == '-')))
250                     {
251
252                         // Unicode or maybe ace 
253                         initConfig = true;
254                         break;
255                     }
256                 }
257
258
259             }
260
261             return initConfig;
262         }
263
264         //
265         // Unescapes entire string and checks if it has unicode chars
266         //
267         private bool CheckForUnicode(String data)
268         {
269             bool hasUnicode = false;
270             char[] chars = new char[data.Length];
271             int count = 0;
272
273             chars = UriHelper.UnescapeString(data, 0, data.Length, chars, ref count, c_DummyChar, c_DummyChar, 
274                 c_DummyChar, UnescapeMode.Unescape | UnescapeMode.UnescapeAll, null, false);
275
276             for (int i = 0; i < count; ++i){
277                 if (chars[i] > '\x7f'){
278                     // Unicode 
279                     hasUnicode = true;
280                     break;
281                 }
282             }
283             return hasUnicode;
284         }
285
286         // Does this string have any %6A sequences that are 3986 Unreserved characters?  These should be un-escaped.
287         private unsafe bool CheckForEscapedUnreserved(String data)
288         {
289             fixed (char* tempPtr = data)
290             {
291                 for (int i = 0; i < data.Length - 2; ++i)
292                 {
293                     if (tempPtr[i] == '%' && IsHexDigit(tempPtr[i + 1]) && IsHexDigit(tempPtr[i + 2])
294                         && tempPtr[i + 1] >= '0' && tempPtr[i + 1] <= '7') // max 0x7F
295                     {
296                         char ch = UriHelper.EscapedAscii(tempPtr[i + 1], tempPtr[i + 2]);
297                         if (ch != c_DummyChar && UriHelper.Is3986Unreserved(ch))
298                         {
299                             return true;
300                         }
301                     }
302                 }
303             }
304             return false;
305         }
306         
307         //
308         //
309         //  Returns true if the string represents a valid argument to the Uri ctor
310         //  If uriKind != AbsoluteUri then certain parsing erros are ignored but Uri usage is limited
311         //
312         public static bool TryCreate(string uriString, UriKind uriKind, out Uri result)
313         {
314             if ((object)uriString == null)
315             {
316                 result = null;
317                 return false;
318             }
319             UriFormatException e = null;
320             result = CreateHelper(uriString, false, uriKind, ref e);
321             return (object) e == null && result != null;
322         }
323         //
324         public static bool TryCreate(Uri baseUri, string relativeUri, out Uri result)
325         {
326             Uri relativeLink;
327             const UriKind kind =
328 #if MONO
329                 DotNetRelativeOrAbsolute;
330 #else
331                 RelativeOrAbsolute;
332 #endif
333             if (TryCreate(relativeUri, kind, out relativeLink))
334             {
335                 if (!relativeLink.IsAbsoluteUri)
336                     return TryCreate(baseUri, relativeLink, out result);
337
338                 result = relativeLink;
339                 return true;
340             }
341             result = null;
342             return false;
343         }
344         //
345         public static bool TryCreate(Uri baseUri, Uri relativeUri, out Uri result)
346         {
347             result = null;
348
349             //Consider: Work out the baseUri==null case
350             if ((object)baseUri == null || (object)relativeUri == null)
351                 return false;
352
353             if (baseUri.IsNotAbsoluteUri)
354                 return false;
355
356             UriFormatException e;
357             string newUriString = null;
358
359             bool dontEscape;
360             if (baseUri.Syntax.IsSimple)
361             {
362                 dontEscape = relativeUri.UserEscaped;
363                 result = ResolveHelper(baseUri, relativeUri, ref newUriString, ref dontEscape, out e);
364             }
365             else
366             {
367                 dontEscape = false;
368                 newUriString = baseUri.Syntax.InternalResolve(baseUri, relativeUri, out e);
369             }
370
371             if (e != null)
372                 return false;
373
374             if ((object) result == null)
375                 result = CreateHelper(newUriString, dontEscape, UriKind.Absolute, ref e);
376
377             return (object) e == null && result != null && result.IsAbsoluteUri;
378         }
379         //
380         //
381         public string GetComponents(UriComponents components, UriFormat format)
382         {
383             if (((components & UriComponents.SerializationInfoString) != 0) && components != UriComponents.SerializationInfoString)
384                 throw new ArgumentOutOfRangeException("components", components, SR.GetString(SR.net_uri_NotJustSerialization));
385
386             if ((format & ~UriFormat.SafeUnescaped) != 0)
387                 throw new ArgumentOutOfRangeException("format");
388
389             if (IsNotAbsoluteUri)
390             {
391                 if (components == UriComponents.SerializationInfoString)
392                     return GetRelativeSerializationString(format);
393                 else
394                     throw new InvalidOperationException(SR.GetString(SR.net_uri_NotAbsolute));
395             }
396
397             if (Syntax.IsSimple)
398                 return GetComponentsHelper(components, format);
399
400             return Syntax.InternalGetComponents(this, components, format);
401         }
402         //
403         //
404         // This is for languages that do not support == != operators overloading
405         //
406         // Note that Uri.Equals will get an optimized path but is limited to true/fasle result only
407         //
408         public static int Compare(Uri uri1, Uri uri2, UriComponents partsToCompare, UriFormat compareFormat, 
409             StringComparison comparisonType)
410         {
411
412             if ((object) uri1 == null)
413             {
414                 if (uri2 == null)
415                     return 0; // Equal
416                 return -1;    // null < non-null
417             }
418             if ((object) uri2 == null)
419                 return 1;     // non-null > null
420
421             // a relative uri is always less than an absolute one
422             if (!uri1.IsAbsoluteUri || !uri2.IsAbsoluteUri)
423                 return uri1.IsAbsoluteUri? 1: uri2.IsAbsoluteUri? -1: string.Compare(uri1.OriginalString, 
424                     uri2.OriginalString, comparisonType);
425
426             return string.Compare(
427                                     uri1.GetParts(partsToCompare, compareFormat),
428                                     uri2.GetParts(partsToCompare, compareFormat),
429                                     comparisonType
430                                   );
431         }
432         
433         public bool IsWellFormedOriginalString()
434         {
435             if (IsNotAbsoluteUri || Syntax.IsSimple)
436                 return InternalIsWellFormedOriginalString();
437
438             return Syntax.InternalIsWellFormedOriginalString(this);
439         }
440
441         // Consider: (perf) Making it to not create a Uri internally
442         public static bool IsWellFormedUriString(string uriString, UriKind uriKind)
443         {
444             Uri result;
445
446 #if MONO
447             if (uriKind == UriKind.RelativeOrAbsolute)
448                 uriKind = DotNetRelativeOrAbsolute;
449 #endif
450
451             if (!Uri.TryCreate(uriString, uriKind, out result))
452                 return false;
453
454             return result.IsWellFormedOriginalString();
455         }
456
457         //
458         // Internal stuff
459         //
460
461         // Returns false if OriginalString value
462         // (1) is not correctly escaped as per URI spec excluding intl UNC name case
463         // (2) or is an absolute Uri that represents implicit file Uri "c:\dir\file"
464         // (3) or is an absolute Uri that misses a slash before path "file://c:/dir/file"
465         // (4) or contains unescaped backslashes even if they will be treated
466         //     as forward slashes like http:\\host/path\file or file:\\\c:\path
467         //
468         internal unsafe bool InternalIsWellFormedOriginalString()
469         {
470             if (UserDrivenParsing)
471                 throw new InvalidOperationException(SR.GetString(SR.net_uri_UserDrivenParsing, this.GetType().FullName));
472
473             fixed (char* str = m_String)
474             {
475                 ushort idx = 0;
476                 //
477                 // For a relative Uri we only care about escaping and backslashes
478                 //
479                 if (!IsAbsoluteUri)
480                 {
481                     // my:scheme/path?query is not well formed because the colon is ambiguous
482                     if (!UriParser.ShouldUseLegacyV2Quirks && CheckForColonInFirstPathSegment(m_String))
483                     {
484                         return false;
485                     }
486                     return (CheckCanonical(str, ref idx, (ushort)m_String.Length, c_EOL) 
487                             & (Check.BackslashInPath | Check.EscapedCanonical)) == Check.EscapedCanonical;
488                 }
489
490                 //
491                 // (2) or is an absolute Uri that represents implicit file Uri "c:\dir\file"
492                 //
493                 if (IsImplicitFile)
494                     return false;
495
496                 //This will get all the offsets, a Host name will be checked separatelly below
497                 EnsureParseRemaining();
498
499                 Flags nonCanonical = (m_Flags & (Flags.E_CannotDisplayCanonical | Flags.IriCanonical));
500                 // User, Path, Query or Fragment may have some non escaped characters
501                 if (((nonCanonical & Flags.E_CannotDisplayCanonical & (Flags.E_UserNotCanonical | Flags.E_PathNotCanonical |
502                                         Flags.E_QueryNotCanonical | Flags.E_FragmentNotCanonical)) != Flags.Zero) &&
503                     (!m_iriParsing || (m_iriParsing &&
504                     (((nonCanonical & Flags.E_UserNotCanonical) == 0) || ((nonCanonical & Flags.UserIriCanonical) == 0)) &&
505                     (((nonCanonical & Flags.E_PathNotCanonical) == 0) || ((nonCanonical & Flags.PathIriCanonical) == 0)) &&
506                     (((nonCanonical & Flags.E_QueryNotCanonical) == 0) || ((nonCanonical & Flags.QueryIriCanonical) == 0)) &&
507                     (((nonCanonical & Flags.E_FragmentNotCanonical) == 0) || ((nonCanonical & Flags.FragmentIriCanonical) == 0)))))
508                 {
509                     return false;
510                 }
511
512                 // checking on scheme:\\ or file:////
513                 if (InFact(Flags.AuthorityFound))
514                 {
515                     idx = (ushort)(m_Info.Offset.Scheme + m_Syntax.SchemeName.Length + 2);
516                     if (idx >= m_Info.Offset.User || m_String[idx - 1] == '\\' || m_String[idx] == '\\')
517                         return false;
518
519 #if !PLATFORM_UNIX
520                     if (InFact(Flags.UncPath | Flags.DosPath))
521                     {
522                         while (++idx < m_Info.Offset.User && (m_String[idx] == '/' || m_String[idx] == '\\'))
523                             return false;
524                     }
525 #endif // !PLATFORM_UNIX
526                 }
527
528
529                 // (3) or is an absolute Uri that misses a slash before path "file://c:/dir/file"
530                 // Note that for this check to be more general we assert that if Path is non empty and if it requires a first slash
531                 // (which looks absent) then the method has to fail.
532                 // Today it's only possible for a Dos like path, i.e. file://c:/bla would fail below check.
533                 if (InFact(Flags.FirstSlashAbsent) && m_Info.Offset.Query > m_Info.Offset.Path)
534                     return false;
535
536                 // (4) or contains unescaped backslashes even if they will be treated
537                 //     as forward slashes like http:\\host/path\file or file:\\\c:\path
538                 // Note we do not check for Flags.ShouldBeCompressed i.e. allow // /./ and alike as valid
539                 if (InFact(Flags.BackslashInPath))
540                     return false;
541
542                 // Capturing a rare case like file:///c|/dir
543                 if (IsDosPath && m_String[m_Info.Offset.Path + SecuredPathIndex - 1] == '|')
544                     return false;
545
546                 //
547                 // May need some real CPU processing to anwser the request
548                 //
549                 //
550                 // Check escaping for authority
551                 //
552                 // IPv6 hosts cannot be properly validated by CheckCannonical
553                 if ((m_Flags & Flags.CanonicalDnsHost) == 0 && HostType != Flags.IPv6HostType)
554                 {
555                     idx = m_Info.Offset.User;
556                     Check result = CheckCanonical(str, ref idx, (ushort)m_Info.Offset.Path, '/');
557                     if (((result & (Check.ReservedFound | Check.BackslashInPath | Check.EscapedCanonical)) 
558                         != Check.EscapedCanonical) 
559                         && (!m_iriParsing || (m_iriParsing 
560                             && ((result & (Check.DisplayCanonical | Check.FoundNonAscii | Check.NotIriCanonical))
561                                 != (Check.DisplayCanonical | Check.FoundNonAscii)))))
562                     {
563                         return false;
564                     }
565                 }
566
567                 // Want to ensure there are slashes after the scheme
568                 if ((m_Flags & (Flags.SchemeNotCanonical | Flags.AuthorityFound)) 
569                     == (Flags.SchemeNotCanonical | Flags.AuthorityFound))
570                 {
571                     idx = (ushort)m_Syntax.SchemeName.Length;
572                     while (str[idx++] != ':') ;
573                     if (idx + 1 >= m_String.Length || str[idx] != '/' || str[idx + 1] != '/')
574                         return false;
575                 }
576             }
577             //
578             // May be scheme, host, port or path need some canonicalization but still the uri string is found to be a 
579             // "well formed" one
580             //
581             return true;
582         }
583
584         //
585         //
586         //
587         public static string UnescapeDataString(string stringToUnescape)
588         {
589             if ((object) stringToUnescape == null)
590                 throw new ArgumentNullException("stringToUnescape");
591
592             if (stringToUnescape.Length == 0)
593                 return string.Empty;
594
595             unsafe {
596                 fixed (char* pStr = stringToUnescape)
597                 {
598                     int position;
599                     for (position = 0; position < stringToUnescape.Length; ++position)
600                         if (pStr[position] == '%')
601                             break;
602
603                     if (position == stringToUnescape.Length)
604                         return stringToUnescape;
605
606                     UnescapeMode unescapeMode = UnescapeMode.Unescape | UnescapeMode.UnescapeAll;                    
607                     position = 0;
608                     char[] dest = new char[stringToUnescape.Length];
609                     dest = UriHelper.UnescapeString(stringToUnescape, 0, stringToUnescape.Length, dest, ref position, 
610                         c_DummyChar, c_DummyChar, c_DummyChar, unescapeMode, null, false);
611                     return new string(dest, 0, position);
612                 }
613             }
614         }
615         //
616         // Where stringToEscape is intented to be a completely unescaped URI string.
617         // This method will escape any character that is not a reserved or unreserved character, including percent signs.
618         // Note that EscapeUriString will also do not escape a '#' sign.
619         //
620         public static string EscapeUriString(string stringToEscape)
621         {
622             if ((object)stringToEscape == null)
623                 throw new ArgumentNullException("stringToEscape");
624
625             if (stringToEscape.Length == 0)
626                 return string.Empty;
627
628             int position = 0;
629             char[] dest = UriHelper.EscapeString(stringToEscape, 0, stringToEscape.Length, null, ref position, true, 
630                 c_DummyChar, c_DummyChar, c_DummyChar);
631             if ((object) dest == null)
632                 return stringToEscape;
633             return new string(dest, 0, position);
634         }
635         //
636         // Where stringToEscape is intended to be URI data, but not an entire URI.
637         // This method will escape any character that is not an unreserved character, including percent signs.
638         //
639         public static string EscapeDataString(string stringToEscape)
640         {
641             if ((object) stringToEscape == null)
642                 throw new ArgumentNullException("stringToEscape");
643
644             if (stringToEscape.Length == 0)
645                 return string.Empty;
646
647             int position = 0;
648             char[] dest = UriHelper.EscapeString(stringToEscape, 0, stringToEscape.Length, null, ref position, false, 
649                 c_DummyChar, c_DummyChar, c_DummyChar);
650             if (dest == null)
651                 return stringToEscape;
652             return new string(dest, 0, position);
653         }
654
655         //
656         // Cleans up the specified component according to Iri rules
657         // a) Chars allowed by iri in a component are unescaped if found escaped
658         // b) Bidi chars are stripped
659         //
660         // should be called only if IRI parsing is switched on 
661         internal unsafe string EscapeUnescapeIri(string input, int start, int end, UriComponents component)
662         {
663             fixed (char *pInput = input)
664             {
665                 return IriHelper.EscapeUnescapeIri(pInput, start, end, component);
666             }
667         }
668         
669         // Should never be used except by the below method
670         private Uri(Flags flags, UriParser uriParser, string uri)
671         {
672             m_Flags = flags;
673             m_Syntax = uriParser;
674             m_String = uri;
675         }
676         //
677         // a Uri.TryCreate() method goes through here.
678         //
679         internal static Uri CreateHelper(string uriString, bool dontEscape, UriKind uriKind, ref UriFormatException e)
680         {
681             // if (!Enum.IsDefined(typeof(UriKind), uriKind)) -- We currently believe that Enum.IsDefined() is too slow 
682             // to be used here.
683             if ((int)uriKind < (int)UriKind.RelativeOrAbsolute || (int)uriKind > (int)UriKind.Relative){
684 #if MONO
685                 if (uriKind != DotNetRelativeOrAbsolute)
686 #endif               
687                 throw new ArgumentException(SR.GetString(SR.net_uri_InvalidUriKind, uriKind));
688             }
689
690             UriParser syntax = null;
691             Flags flags = Flags.Zero;
692             ParsingError err = ParseScheme(uriString, ref flags, ref syntax);
693
694             if (dontEscape)
695                 flags |= Flags.UserEscaped;
696
697             // We won't use User factory for these errors
698             if (err != ParsingError.None)
699             {
700                 // If it looks as a relative Uri, custom factory is ignored
701                 if (uriKind != UriKind.Absolute && err <= ParsingError.LastRelativeUriOkErrIndex)
702                     return new Uri((flags & Flags.UserEscaped), null, uriString);
703
704                 return null;
705             }
706
707             // Cannot be relative Uri if came here
708             Uri result = new Uri(flags, syntax, uriString);
709
710             // Validate instance using ether built in or a user Parser
711             try
712             {
713                 result.InitializeUri(err, uriKind, out e);
714
715                 if (e == null)
716                     return result;
717
718                 return null;
719             }
720             catch (UriFormatException ee)
721             {
722                 Debug.Assert(!syntax.IsSimple, "A UriPraser threw on InitializeAndValidate.");
723                 e = ee;
724                 // A precaution since custom Parser should never throw in this case.
725                 return null;
726             }
727         }
728         //
729         // Resolves into either baseUri or relativeUri according to conditions OR if not possible it uses newUriString 
730         // to  return combined URI strings from both Uris 
731         // otherwise if e != null on output the operation has failed
732         //
733
734         internal static Uri ResolveHelper(Uri baseUri, Uri relativeUri, ref string newUriString, ref bool userEscaped, 
735             out UriFormatException e)
736         {
737             Debug.Assert(!baseUri.IsNotAbsoluteUri && !baseUri.UserDrivenParsing, "Uri::ResolveHelper()|baseUri is not Absolute or is controlled by User Parser.");
738
739             e = null;
740             string relativeStr = string.Empty;
741
742             if ((object)relativeUri != null)
743             {
744                 if (relativeUri.IsAbsoluteUri) {
745 #if MONO
746                 // Hack for Mono specific handling of /paths which are absolute on unix but
747                 // we want to allow concatination of such paths to match .net
748                 if (!(!IsWindowsFileSystem && relativeUri.OriginalString [0] == '/' && relativeUri.IsImplicitFile))
749 #endif
750
751                     return relativeUri;
752                 }
753
754                 relativeStr = relativeUri.OriginalString;
755                 userEscaped = relativeUri.UserEscaped;
756             }
757             else
758                 relativeStr = string.Empty;
759
760             // Here we can assert that passed "relativeUri" is indeed a relative one
761
762             if (relativeStr.Length > 0 && (IsLWS(relativeStr[0]) || IsLWS(relativeStr[relativeStr.Length - 1])))
763                 relativeStr = relativeStr.Trim(_WSchars);
764
765             if (relativeStr.Length == 0)
766             {
767                 newUriString = baseUri.GetParts(UriComponents.AbsoluteUri, 
768                     baseUri.UserEscaped ? UriFormat.UriEscaped : UriFormat.SafeUnescaped);
769                 return null;
770             }
771
772             // Check for a simple fragment in relative part
773             if (relativeStr[0] == '#' && !baseUri.IsImplicitFile && baseUri.Syntax.InFact(UriSyntaxFlags.MayHaveFragment))
774             {
775                 newUriString = baseUri.GetParts(UriComponents.AbsoluteUri & ~UriComponents.Fragment, 
776                     UriFormat.UriEscaped) + relativeStr;
777                 return null;
778             }
779             
780             // Check for a simple query in relative part
781             if (relativeStr[0] == '?' && !baseUri.IsImplicitFile && baseUri.Syntax.InFact(UriSyntaxFlags.MayHaveQuery))
782             {
783                 newUriString = baseUri.GetParts(UriComponents.AbsoluteUri & ~UriComponents.Query & ~UriComponents.Fragment, 
784                     UriFormat.UriEscaped) + relativeStr;
785                 return null;
786             }
787             
788             // Check on the DOS path in the relative Uri (a special case)
789             if (relativeStr.Length >= 3
790                 && (relativeStr[1] == ':' || relativeStr[1] == '|')
791                 && IsAsciiLetter(relativeStr[0])
792                 && (relativeStr[2] == '\\' || relativeStr[2] == '/'))
793             {
794
795                 if (baseUri.IsImplicitFile)
796                 {
797                     // It could have file:/// prepended to the result but we want to keep it as *Implicit* File Uri
798                     newUriString = relativeStr;
799                     return null;
800                 }
801                 else if (baseUri.Syntax.InFact(UriSyntaxFlags.AllowDOSPath))
802                 {
803                     // The scheme is not changed just the path gets replaced
804                     string prefix;
805                     if (baseUri.InFact(Flags.AuthorityFound))
806                         prefix = baseUri.Syntax.InFact(UriSyntaxFlags.PathIsRooted) ? ":///" : "://";
807                     else
808                         prefix = baseUri.Syntax.InFact(UriSyntaxFlags.PathIsRooted) ? ":/" : ":";
809
810                     newUriString = baseUri.Scheme + prefix + relativeStr;
811                     return null;
812                 }
813                 // If we are here then input like "http://host/path/" + "C:\x" will produce the result  http://host/path/c:/x
814             }
815
816
817             ParsingError err = GetCombinedString(baseUri, relativeStr, userEscaped, ref newUriString);
818
819             if (err != ParsingError.None)
820             {
821                 e = GetException(err);
822                 return null;
823             }
824
825             if ((object)newUriString == (object)baseUri.m_String)
826                 return baseUri;
827
828             return null;
829         }
830
831         private unsafe string GetRelativeSerializationString(UriFormat format)
832         {
833             if (format == UriFormat.UriEscaped)
834             {
835                 if (m_String.Length == 0)
836                     return string.Empty;
837                 int position = 0;
838                 char[] dest = UriHelper.EscapeString(m_String, 0, m_String.Length, null, ref position, true, 
839                     c_DummyChar, c_DummyChar, '%');
840                 if ((object)dest == null)
841                     return m_String;
842                 return new string(dest, 0, position);
843             }
844
845             else if (format == UriFormat.Unescaped)
846                 return UnescapeDataString(m_String);
847
848             else if (format == UriFormat.SafeUnescaped)
849             {
850                 if (m_String.Length == 0)
851                     return string.Empty;
852
853                 char[] dest = new char[m_String.Length];
854                 int position = 0;
855                 dest = UriHelper.UnescapeString(m_String, 0, m_String.Length, dest, ref position, c_DummyChar, 
856                     c_DummyChar, c_DummyChar, UnescapeMode.EscapeUnescape, null, false);
857                 return new string(dest, 0, position);
858             }
859             else
860                 throw new ArgumentOutOfRangeException("format");
861
862         }
863
864         //
865         // UriParser helpers methods
866         //
867         internal string GetComponentsHelper(UriComponents uriComponents, UriFormat uriFormat)
868         {
869             if (uriComponents == UriComponents.Scheme)
870                 return m_Syntax.SchemeName;
871
872             // A serialzation info is "almost" the same as AbsoluteUri except for IPv6 + ScopeID hostname case
873             if ((uriComponents & UriComponents.SerializationInfoString) != 0)
874                 uriComponents |= UriComponents.AbsoluteUri;
875
876             //This will get all the offsets, HostString will be created below if needed
877             EnsureParseRemaining();
878
879             if ((uriComponents & UriComponents.NormalizedHost) != 0)
880             {
881                 // Down the path we rely on Host to be ON for NormalizedHost
882                 uriComponents |= UriComponents.Host;
883             }
884
885             //Check to see if we need the host/authotity string
886             if ((uriComponents & UriComponents.Host) != 0)
887                 EnsureHostString(true);
888
889             //This, single Port request is always processed here
890             if (uriComponents == UriComponents.Port || uriComponents == UriComponents.StrongPort)
891             {
892                 if (((m_Flags & Flags.NotDefaultPort) != 0) || (uriComponents == UriComponents.StrongPort 
893                     && m_Syntax.DefaultPort != UriParser.NoDefaultPort))
894                 {
895                     // recreate string from the port value
896                     return m_Info.Offset.PortValue.ToString(CultureInfo.InvariantCulture);
897                 }
898                 return string.Empty;
899             }
900
901             if ((uriComponents & UriComponents.StrongPort) != 0)
902             {
903                 // Down the path we rely on Port to be ON for StrongPort
904                 uriComponents |= UriComponents.Port;
905             }
906
907             //This request sometime is faster to process here
908             if (uriComponents == UriComponents.Host && (uriFormat == UriFormat.UriEscaped 
909                 || (( m_Flags & (Flags.HostNotCanonical | Flags.E_HostNotCanonical)) == 0)))
910             {
911                 EnsureHostString(false);
912                 return m_Info.Host;
913             }
914
915             switch (uriFormat)
916             {
917                 case UriFormat.UriEscaped:
918                     return GetEscapedParts(uriComponents);
919
920                 case V1ToStringUnescape:
921                 case UriFormat.SafeUnescaped:
922                 case UriFormat.Unescaped:
923                     return GetUnescapedParts(uriComponents, uriFormat);
924
925                 default:
926                     throw new ArgumentOutOfRangeException("uriFormat");
927             }
928         }
929         //
930         //
931         public bool IsBaseOf(Uri uri)
932         {
933             if ((object)uri == null)
934                 throw new ArgumentNullException("uri");
935
936             if (!IsAbsoluteUri)
937                 return false;
938
939             if (Syntax.IsSimple)
940                 return IsBaseOfHelper(uri);
941
942             return Syntax.InternalIsBaseOf(this, uri);
943         }
944         //
945         //
946         //
947         internal bool IsBaseOfHelper(Uri uriLink)
948         {
949             //TO 
950             if (!IsAbsoluteUri || UserDrivenParsing)
951                 return false;
952
953             if (!uriLink.IsAbsoluteUri)
954             {
955                 //a relative uri could have quite tricky form, it's better to fix it now.
956                 string newUriString = null;
957                 UriFormatException e;
958                 bool dontEscape = false;
959
960                 uriLink = ResolveHelper(this, uriLink, ref newUriString, ref dontEscape, out e);
961                 if (e != null)
962                     return false;
963
964                 if ((object)uriLink == null)
965                     uriLink = CreateHelper(newUriString, dontEscape, UriKind.Absolute, ref e);
966
967                 if (e != null)
968                     return false;
969             }
970
971             if (Syntax.SchemeName != uriLink.Syntax.SchemeName)
972                 return false;
973
974             // Canonicalize and test for substring match up to the last path slash
975             string me = GetParts(UriComponents.AbsoluteUri & ~UriComponents.Fragment, UriFormat.SafeUnescaped);
976             string she = uriLink.GetParts(UriComponents.AbsoluteUri & ~UriComponents.Fragment, UriFormat.SafeUnescaped);
977
978             unsafe
979             {
980                 fixed (char* pMe = me)
981                 {
982                     fixed (char* pShe = she)
983                     {
984                         return UriHelper.TestForSubPath(pMe, (ushort)me.Length, pShe, (ushort)she.Length, 
985                             IsUncOrDosPath || uriLink.IsUncOrDosPath);
986                     }
987                 }
988             }
989         }
990         //
991         // Only a ctor time call
992         //
993         private void CreateThisFromUri(Uri otherUri)
994         {
995             // Clone the other guy but develop own UriInfo member
996             m_Info = null;
997
998             m_Flags = otherUri.m_Flags;
999             if (InFact(Flags.MinimalUriInfoSet))
1000             {
1001                 m_Flags &= ~(Flags.MinimalUriInfoSet | Flags.AllUriInfoSet | Flags.IndexMask);
1002                 // Port / Path offset
1003                 int portIndex = otherUri.m_Info.Offset.Path;
1004                 if (InFact(Flags.NotDefaultPort))
1005                 {
1006                     // Find the start of the port.  Account for non-canonical ports like :00123
1007                     while (otherUri.m_String[portIndex] != ':' && portIndex > otherUri.m_Info.Offset.Host)
1008                     {
1009                         portIndex--;
1010                     }
1011                     if (otherUri.m_String[portIndex] != ':')
1012                     {
1013                         // Something wrong with the NotDefaultPort flag.  Reset to path index
1014                         Debug.Assert(false, "Uri failed to locate custom port at index: " + portIndex);
1015                         portIndex = otherUri.m_Info.Offset.Path;
1016                     }
1017                 }
1018                 m_Flags |= (Flags)portIndex; // Port or path
1019             }
1020
1021             m_Syntax = otherUri.m_Syntax;
1022             m_String = otherUri.m_String;
1023             m_iriParsing = otherUri.m_iriParsing;
1024             if (otherUri.OriginalStringSwitched){
1025                 m_originalUnicodeString = otherUri.m_originalUnicodeString;
1026             }
1027             if (otherUri.AllowIdn && (otherUri.InFact(Flags.IdnHost) || otherUri.InFact(Flags.UnicodeHost))){
1028                 m_DnsSafeHost = otherUri.m_DnsSafeHost;
1029             }
1030         }
1031     }
1032 }