Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Net / System / UriExt.cs / 1305376 / UriExt.cs
/*++ Copyright (c) 2003 Microsoft Corporation Module Name: UriExt.cs Abstract: Uri extensibility model Implementation. This file utilizes partial class feature. Uri.cs file contains core System.Uri functionality. Author: Alexei Vopilov Nov 21 2003 Revision History: --*/ namespace System { using System.Collections.Generic; using System.Configuration; using System.Globalization; using System.Net.Configuration; using System.Security.Permissions; using System.Text; using System.Runtime.InteropServices; public partial class Uri { // // All public ctors go through here // private void CreateThis(string uri, bool dontEscape, UriKind uriKind) { // if (!Enum.IsDefined(typeof(UriKind), uriKind)) -- We currently believe that Enum.IsDefined() is too slow to be used here. if ((int)uriKind < (int)UriKind.RelativeOrAbsolute || (int)uriKind > (int)UriKind.Relative) { throw new ArgumentException(SR.GetString(SR.net_uri_InvalidUriKind, uriKind)); } m_String = uri == null? string.Empty: uri; if (dontEscape) m_Flags |= Flags.UserEscaped; ParsingError err = ParseScheme(m_String, ref m_Flags, ref m_Syntax); UriFormatException e; InitializeUri(err, uriKind, out e); if (e != null) throw e; } // private void InitializeUri(ParsingError err, UriKind uriKind, out UriFormatException e) { if (err == ParsingError.None) { if (IsImplicitFile) { // V1 compat VsWhidbey#252282 // A relative Uri wins over implicit UNC path unless the UNC path is of the form "\\something" and uriKind != Absolute if ( #if !PLATFORM_UNIX NotAny(Flags.DosPath) && #endif // !PLATFORM_UNIX uriKind != UriKind.Absolute && (uriKind == UriKind.Relative || (m_String.Length >= 2 && (m_String[0] != '\\' || m_String[1] != '\\')))) { m_Syntax = null; //make it be relative Uri m_Flags &= Flags.UserEscaped; // the only flag that makes sense for a relative uri e = null; return; // Otheriwse an absolute file Uri wins when it's of the form "\\something" } // // VsWhidbey#423805 and V1 compat issue // We should support relative Uris of the form c:\bla or c:/bla // #if !PLATFORM_UNIX else if (uriKind == UriKind.Relative && InFact(Flags.DosPath)) { m_Syntax = null; //make it be relative Uri m_Flags &= Flags.UserEscaped; // the only flag that makes sense for a relative uri e = null; return; // Otheriwse an absolute file Uri wins when it's of the form "c:\something" } #endif // !PLATFORM_UNIX } } else if (err > ParsingError.LastRelativeUriOkErrIndex) { //This is a fatal error based solely on scheme name parsing m_String = null; // make it be invalid Uri e = GetException(err); return; } System.Net.GlobalLog.Assert(err == ParsingError.None || (object)m_Syntax == null, "Uri::.ctor|ParseScheme has found an error:{0}, but m_Syntax is not null.", err); // // // bool hasUnicode = false; // Is there unicode .. if ((!s_ConfigInitialized) && CheckForConfigLoad(m_String)){ InitializeUriConfig(); } m_iriParsing = (s_IriParsing && ((m_Syntax == null) || m_Syntax.InFact(UriSyntaxFlags.AllowIriParsing))); if (m_iriParsing && CheckForUnicode(m_String)){ m_Flags |= Flags.HasUnicode; hasUnicode = true; // switch internal strings m_originalUnicodeString = m_String; // original string location changed } if (m_Syntax != null) { if (m_Syntax.IsSimple) { if ((err = PrivateParseMinimal()) != ParsingError.None) { if (uriKind != UriKind.Absolute && err <= ParsingError.LastRelativeUriOkErrIndex) { // RFC 3986 Section 5.4.2 - http:(relativeUri) may be considered a valid relative Uri. m_Syntax = null; // convert to relative uri e = null; m_Flags &= Flags.UserEscaped; // the only flag that makes sense for a relative uri } else e = GetException(err); } else if (uriKind == UriKind.Relative) { // Here we know that we can create an absolute Uri, but the user has requested only a relative one e = GetException(ParsingError.CannotCreateRelative); } else e = null; // will return from here if (m_iriParsing && hasUnicode){ // In this scenario we need to parse the whole string EnsureParseRemaining(); } } else { // offer custom parser to create a parsing context m_Syntax = m_Syntax.InternalOnNewUri(); // incase they won't call us m_Flags |= Flags.UserDrivenParsing; // Ask a registered type to validate this uri m_Syntax.InternalValidate(this, out e); if (e != null) { // Can we still take it as a relative Uri? if (uriKind != UriKind.Absolute && err != ParsingError.None && err <= ParsingError.LastRelativeUriOkErrIndex) { m_Syntax = null; // convert it to relative e = null; m_Flags &= Flags.UserEscaped; // the only flag that makes sense for a relative uri } } else // e == null { if (err != ParsingError.None || InFact(Flags.ErrorOrParsingRecursion)) { // User parser took over on an invalid Uri SetUserDrivenParsing(); } else if (uriKind == UriKind.Relative) { // Here we know that custom parser can create an absolute Uri, but the user has requested only a relative one e = GetException(ParsingError.CannotCreateRelative); } if (m_iriParsing && hasUnicode){ // In this scenario we need to parse the whole string EnsureParseRemaining(); } } // will return from here } } // If we encountered any parsing errors that indicate this may be a relative Uri, // and we'll allow relative Uri's, then create one. else if (err != ParsingError.None && uriKind != UriKind.Absolute && err <= ParsingError.LastRelativeUriOkErrIndex) { e = null; m_Flags &= (Flags.UserEscaped | Flags.HasUnicode); // the only flags that makes sense for a relative uri if (m_iriParsing && hasUnicode) { // Iri'ze and then normalize relative uris m_String = EscapeUnescapeIri(m_originalUnicodeString, 0, m_originalUnicodeString.Length, (UriComponents)0); try { m_String = m_String.Normalize(NormalizationForm.FormC); } catch (ArgumentException) { e = GetException(ParsingError.BadFormat); } } } else { m_String = null; // make it be invalid Uri e = GetException(err); } } // // http://www.ietf.org/rfc/rfc3986.txt // // 3.3. Path // In addition, a URI reference (Section 4.1) may be a relative-path reference, in which case the first // path segment cannot contain a colon (":") character. // // 4.2. Relative Reference // A path segment that contains a colon character (e.g., "this:that") cannot be used as the first segment // of a relative-path reference, as it would be mistaken for a scheme name. Such a segment must be // preceded by a dot-segment (e.g., "./this:that") to make a relative-path reference. // // 5.4.2. Abnormal Examples // http:(relativeUri) may be considered a valid relative Uri. // // Returns true if a colon is found in the first path segment, false otherwise // private static bool CheckForColonInFirstPathSegment(String uriString) { // Check for anything that may terminate the first regular path segment // or an illegal colon char[] pathDelims = new char[] { ':', '\\', '/', '?', '#' }; int index = uriString.IndexOfAny(pathDelims); return (index >= 0 && uriString[index] == ':'); } // // Checks if there are any unicode or escaped chars or ace to determine whether to load // config // private unsafe bool CheckForConfigLoad(String data) { bool initConfig = false; int length = data.Length; fixed (char* temp = data){ for (int i = 0; i < length; ++i){ if ((temp[i] > '\x7f') || (temp[i] == '%') || ((temp[i] == 'x') && ((i + 3) < length) && (temp[i + 1] == 'n') && (temp[i + 2] == '-') && (temp[i + 3] == '-'))) { // Unicode or maybe ace initConfig = true; break; } } } return initConfig; } // // Unescapes entire string and checks if it has unicode chars // private unsafe bool CheckForUnicode(String data) { bool hasUnicode = false; char[] chars = new char[data.Length]; int count = 0; chars = UnescapeString(data, 0, data.Length, chars, ref count, c_DummyChar, c_DummyChar, c_DummyChar, UnescapeMode.Unescape | UnescapeMode.UnescapeAll, null, false, false); String tempStr = new string(chars, 0, count); int length = tempStr.Length; fixed (char* tempPtr = tempStr){ for (int i = 0; i < length; ++i){ if (tempPtr[i] > '\x7f'){ // Unicode hasUnicode = true; break; } } } return hasUnicode; } // // Check if the char (potentially surrogate) at given offset is in the iri range // Takes in isQuery because because iri restrictions for query are different // internal static bool CheckIriUnicodeRange(string uri, int offset, ref bool surrogatePair, bool isQuery) { char invalidLowSurr = '\uFFFF'; return CheckIriUnicodeRange(uri[offset],(offset + 1 < uri.Length) ? uri[offset + 1] : invalidLowSurr, ref surrogatePair, isQuery); } // // Checks if provided non surrogate char lies in iri range // internal static bool CheckIriUnicodeRange(char unicode, bool isQuery) { if ((unicode >= '\u00A0' && unicode <= '\uD7FF') || (unicode >= '\uF900' && unicode <= '\uFDCF') || (unicode >= '\uFDF0' && unicode <= '\uFFEF') || (isQuery && unicode >= '\uE000' && unicode <= '\uF8FF')){ return true; }else{ return false; } } // // Check if the highSurr is in the iri range or if highSurr and lowSurr are a surr pair then // it checks if the combined char is in the range // Takes in isQuery because because iri restrictions for query are different // internal static bool CheckIriUnicodeRange(char highSurr, char lowSurr, ref bool surrogatePair, bool isQuery) { bool inRange = false; surrogatePair = false; if (CheckIriUnicodeRange(highSurr, isQuery)){ inRange = true; } else if (Char.IsHighSurrogate(highSurr)){ if (Char.IsSurrogatePair(highSurr, lowSurr)){ surrogatePair = true; char[] chars = new char[2] { highSurr, lowSurr }; string surrPair = new string(chars); if (((surrPair.CompareTo("\U00010000") >= 0) && (surrPair.CompareTo("\U0001FFFD") <= 0)) || ((surrPair.CompareTo("\U00020000") >= 0) && (surrPair.CompareTo("\U0002FFFD") <= 0)) || ((surrPair.CompareTo("\U00030000") >= 0) && (surrPair.CompareTo("\U0003FFFD") <= 0)) || ((surrPair.CompareTo("\U00040000") >= 0) && (surrPair.CompareTo("\U0004FFFD") <= 0)) || ((surrPair.CompareTo("\U00050000") >= 0) && (surrPair.CompareTo("\U0005FFFD") <= 0)) || ((surrPair.CompareTo("\U00060000") >= 0) && (surrPair.CompareTo("\U0006FFFD") <= 0)) || ((surrPair.CompareTo("\U00070000") >= 0) && (surrPair.CompareTo("\U0007FFFD") <= 0)) || ((surrPair.CompareTo("\U00080000") >= 0) && (surrPair.CompareTo("\U0008FFFD") <= 0)) || ((surrPair.CompareTo("\U00090000") >= 0) && (surrPair.CompareTo("\U0009FFFD") <= 0)) || ((surrPair.CompareTo("\U000A0000") >= 0) && (surrPair.CompareTo("\U000AFFFD") <= 0)) || ((surrPair.CompareTo("\U000B0000") >= 0) && (surrPair.CompareTo("\U000BFFFD") <= 0)) || ((surrPair.CompareTo("\U000C0000") >= 0) && (surrPair.CompareTo("\U000CFFFD") <= 0)) || ((surrPair.CompareTo("\U000D0000") >= 0) && (surrPair.CompareTo("\U000DFFFD") <= 0)) || ((surrPair.CompareTo("\U000E0000") >= 0) && (surrPair.CompareTo("\U000EFFFD") <= 0)) || (isQuery && (((surrPair.CompareTo("\U000F0000") >= 0) && (surrPair.CompareTo("\U000FFFFD") <= 0)) || ((surrPair.CompareTo("\U00100000") >= 0) && (surrPair.CompareTo("\U0010FFFD") <= 0))))) inRange = true; } } return inRange; } // // // Returns true if the string represents a valid argument to the Uri ctor // If uriKind != AbsoluteUri then certain parsing erros are ignored but Uri usage is limited // public static bool TryCreate(string uriString, UriKind uriKind, out Uri result) { if ((object)uriString == null) { result = null; return false; } UriFormatException e = null; result = CreateHelper(uriString, false, uriKind, ref e); return (object) e == null && result != null; } // public static bool TryCreate(Uri baseUri, string relativeUri, out Uri result) { Uri relativeLink; if (TryCreate(relativeUri, UriKind.RelativeOrAbsolute, out relativeLink)) { if (!relativeLink.IsAbsoluteUri) return TryCreate(baseUri, relativeLink, out result); result = relativeLink; return true; } result = null; return false; } // public static bool TryCreate(Uri baseUri, Uri relativeUri, out Uri result) { result = null; //Consider: Work out the baseUri==null case if ((object)baseUri == null || (object)relativeUri == null) return false; if (baseUri.IsNotAbsoluteUri) return false; UriFormatException e; string newUriString = null; bool dontEscape; if (baseUri.Syntax.IsSimple) { dontEscape = relativeUri.UserEscaped; result = ResolveHelper(baseUri, relativeUri, ref newUriString, ref dontEscape, out e); } else { dontEscape = false; newUriString = baseUri.Syntax.InternalResolve(baseUri, relativeUri, out e); } if (e != null) return false; if ((object) result == null) result = CreateHelper(newUriString, dontEscape, UriKind.Absolute, ref e); return (object) e == null && result != null && result.IsAbsoluteUri; } // // public bool IsBaseOf(Uri uri) { if ((object)uri == null) throw new ArgumentNullException("uri"); if (!IsAbsoluteUri) return false; if (Syntax.IsSimple) return IsBaseOfHelper(uri); return Syntax.InternalIsBaseOf(this, uri); } // // public string GetComponents(UriComponents components, UriFormat format) { if (((components & UriComponents.SerializationInfoString) != 0) && components != UriComponents.SerializationInfoString) throw new ArgumentOutOfRangeException("UriComponents.SerializationInfoString"); if ((format & ~UriFormat.SafeUnescaped) != 0) throw new ArgumentOutOfRangeException("format"); if (IsNotAbsoluteUri) { if (components == UriComponents.SerializationInfoString) return GetRelativeSerializationString(format); else throw new InvalidOperationException(SR.GetString(SR.net_uri_NotAbsolute)); } if (Syntax.IsSimple) return GetComponentsHelper(components, format); return Syntax.InternalGetComponents(this, components, format); } // public bool IsWellFormedOriginalString() { if (IsNotAbsoluteUri || Syntax.IsSimple) return InternalIsWellFormedOriginalString(); return Syntax.InternalIsWellFormedOriginalString(this); } // Consider: (perf) Making it to not create a Uri internally public static bool IsWellFormedUriString(string uriString, UriKind uriKind) { Uri result; if (!Uri.TryCreate(uriString, uriKind, out result)) return false; return result.IsWellFormedOriginalString(); } // // // This is for languages that do not support == != operators overloading // // Note that Uri.Equals will get an optimized path but is limited to true/fasle result only // public static int Compare(Uri uri1, Uri uri2, UriComponents partsToCompare, UriFormat compareFormat, StringComparison comparisonType) { if ((object) uri1 == null) { if (uri2 == null) return 0; // Equal return -1; // null < non-null } if ((object) uri2 == null) return 1; // non-null > null // a relative uri is always less than an absolute one if (!uri1.IsAbsoluteUri || !uri2.IsAbsoluteUri) return uri1.IsAbsoluteUri? 1: uri2.IsAbsoluteUri? -1: string.Compare(uri1.OriginalString, uri2.OriginalString, comparisonType); return string.Compare( uri1.GetParts(partsToCompare, compareFormat), uri2.GetParts(partsToCompare, compareFormat), comparisonType ); } // // // public static string UnescapeDataString(string stringToUnescape) { if ((object) stringToUnescape == null) throw new ArgumentNullException("stringToUnescape"); if (stringToUnescape.Length == 0) return string.Empty; unsafe { fixed (char* pStr = stringToUnescape) { int position; for (position = 0; position < stringToUnescape.Length; ++position) if (pStr[position] == '%') break; if (position == stringToUnescape.Length) return stringToUnescape; position = 0; char[] dest = new char[stringToUnescape.Length]; dest = UnescapeString(stringToUnescape, 0, stringToUnescape.Length, dest, ref position, c_DummyChar, c_DummyChar, c_DummyChar, UnescapeMode.Unescape | UnescapeMode.UnescapeAllOrThrow, null, false, true); return new string(dest, 0, position); } } } // // Where stringToEscape is intented to be a completely unescaped URI string. // This method will escape any character that is not a reserved or unreserved character, including percent signs. // Note that EscapeUriString will also do not escape a '#' sign. // public static string EscapeUriString(string stringToEscape) { if ((object)stringToEscape == null) throw new ArgumentNullException("stringToUnescape"); if (stringToEscape.Length == 0) return string.Empty; int position = 0; char[] dest = EscapeString(stringToEscape, 0, stringToEscape.Length, null, ref position, true, c_DummyChar, c_DummyChar, c_DummyChar); if ((object) dest == null) return stringToEscape; return new string(dest, 0, position); } // // Where stringToEscape is intended to be URI data, but not an entire URI. // This method will escape any character that is not an unreserved character, including percent signs. // public static string EscapeDataString(string stringToEscape) { if ((object) stringToEscape == null) throw new ArgumentNullException("stringToUnescape"); if (stringToEscape.Length == 0) return string.Empty; int position = 0; char[] dest = EscapeString(stringToEscape, 0, stringToEscape.Length, null, ref position, false, c_DummyChar, c_DummyChar, c_DummyChar); if (dest == null) return stringToEscape; return new string(dest, 0, position); } // - forceX characters are always escaped if found // - rsvd character will remain unescaped // // start - starting offset from input // end - the exclusive ending offset in input // destPos - starting offset in dest for output, on return this will be an exclusive "end" in the output. // // In case "dest" has lack of space it will be reallocated by preserving the _whole_ content up to current destPos // // Returns null if nothing has to be escaped AND passed dest was null, otherwise the resulting array with the updated destPos // const short c_MaxAsciiCharsReallocate = 40; const short c_MaxUnicodeCharsReallocate = 40; const short c_MaxUTF_8BytesPerUnicodeChar = 4; const short c_EncodedCharsPerByte = 3; private unsafe static char[] EscapeString(string input, int start, int end, char[] dest, ref int destPos, bool isUriString, char force1, char force2, char rsvd) { if (end - start >= c_MaxUriBufferSize) throw GetException(ParsingError.SizeLimit); int i = start; int prevInputPos = start; byte *bytes = stackalloc byte[c_MaxUnicodeCharsReallocate*c_MaxUTF_8BytesPerUnicodeChar]; // 40*4=160 fixed (char* pStr = input) { for(; i < end; ++i) { char ch = pStr[i]; // a Unicode ? if (ch > '\x7F') { short maxSize = (short)Math.Min(end - i, (int)c_MaxUnicodeCharsReallocate-1); short count = 1; for (; count < maxSize && pStr[i + count] > '\x7f'; ++count) ; // Is the last a high surrogate? if (pStr[i + count-1] >= 0xD800 && pStr[i + count-1] <= 0xDBFF) { // Should be a rare case where the app tries to feed an invalid Unicode surrogates pair if (count == 1 || count == end - i) throw new UriFormatException(SR.GetString(SR.net_uri_BadString)); // need to grab one more char as a Surrogate except when it's a bogus input ++count; } dest = EnsureDestinationSize(pStr, dest, i, (short)(count*c_MaxUTF_8BytesPerUnicodeChar*c_EncodedCharsPerByte), c_MaxUnicodeCharsReallocate*c_MaxUTF_8BytesPerUnicodeChar*c_EncodedCharsPerByte, ref destPos, prevInputPos); short numberOfBytes = (short)Encoding.UTF8.GetBytes(pStr+i, count, bytes, c_MaxUnicodeCharsReallocate*c_MaxUTF_8BytesPerUnicodeChar); // This is the only exception that built in UriParser can throw after a Uri ctor. // Should not happen unless the app tries to feed an invalid Unicode String if (numberOfBytes == 0) throw new UriFormatException(SR.GetString(SR.net_uri_BadString)); i += (count-1); for (count = 0 ; count < numberOfBytes; ++count) EscapeAsciiChar((char)bytes[count], dest, ref destPos); prevInputPos = i+1; } else if (ch == '%' && rsvd == '%') { // Means we don't reEncode '%' but check for the possible escaped sequence dest = EnsureDestinationSize(pStr, dest, i, c_EncodedCharsPerByte, c_MaxAsciiCharsReallocate*c_EncodedCharsPerByte, ref destPos, prevInputPos); if(i + 2 < end && EscapedAscii(pStr[i+1], pStr[i+2]) != c_DummyChar) { // leave it escaped dest[destPos++] = '%'; dest[destPos++] = pStr[i+1]; dest[destPos++] = pStr[i+2]; i += 2; } else { EscapeAsciiChar('%', dest, ref destPos); } prevInputPos = i+1; } else if (ch == force1 || ch == force2) { dest = EnsureDestinationSize(pStr, dest, i, c_EncodedCharsPerByte, c_MaxAsciiCharsReallocate*c_EncodedCharsPerByte, ref destPos, prevInputPos); EscapeAsciiChar(ch, dest, ref destPos); prevInputPos = i+1; } else if (ch != rsvd && (isUriString? IsNotReservedNotUnreservedNotHash(ch): IsNotUnreserved(ch))) { dest = EnsureDestinationSize(pStr, dest, i, c_EncodedCharsPerByte, c_MaxAsciiCharsReallocate*c_EncodedCharsPerByte, ref destPos, prevInputPos); EscapeAsciiChar(ch, dest, ref destPos); prevInputPos = i+1; } } if (prevInputPos != i) { // need to fill up the dest array ? if (prevInputPos != start || dest != null) dest = EnsureDestinationSize(pStr, dest, i, 0, 0, ref destPos, prevInputPos); } } return dest; } // // ensure destination array has enough space and contains all the needed input stuff // private unsafe static char[] EnsureDestinationSize(char *pStr, char[] dest, int currentInputPos, short charsToAdd, short minReallocateChars, ref int destPos, int prevInputPos) { if ((object) dest == null || dest.Length < destPos + (currentInputPos-prevInputPos) + charsToAdd) { // allocating or reallocating array by ensuring enough space based on maxCharsToAdd. char[] newresult = new char[destPos + (currentInputPos-prevInputPos) + minReallocateChars]; if ((object) dest != null && destPos != 0) Buffer.BlockCopy(dest, 0, newresult, 0, destPos<<1); dest = newresult; } // ensuring we copied everything form the input string left before last escaping while (prevInputPos != currentInputPos) dest[destPos++] = pStr[prevInputPos++]; return dest; } // // mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" // reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," // excluded = control | space | delims | unwise // delims = "<" | ">" | "#" | "%" | <"> // unwise = "{" | "}" | "|" | "\" | "^" | "[" | "]" | "`" // private static unsafe bool IsNotReservedNotUnreservedNotHash(char c) { if (c > 'z' && c != '~') { return true; } else if (c > 'Z' && c < 'a' && c != '_') { return true; } else if (c < '!') { return true; } else if (c == '>' || c == '<' || c == '%' || c == '"' || c == '`') { return true; } return false; } // private static unsafe bool IsNotUnreserved(char c) { if (c > 'z' && c != '~') { return true; } else if ((c > '9' && c < 'A') || (c > 'Z' && c < 'a' && c != '_')) { return true; } else if (c < '\'' && c != '!') { return true; } else if (c == '+' || c == ',' || c == '/') { return true; } return false; } // // This method will assume that any good Escaped Sequence will be unescaped in the output // - Assumes Dest.Length - detPosition >= end-start // - UnescapeLevel controls various modes of opearion // - Any "bad" escape sequence will remain as is or '%' will be escaped. // - destPosition tells the starting index in dest for placing the result. // On return destPosition tells the last character + 1 postion in the "dest" array. // - The control chars and chars passed in rsdvX parameters may be re-escaped depending on UnescapeLevel // - It is a RARE case when Unescape actually needs escaping some characteres mentioned above. // For this reason it returns a char[] that is usually the same ref as the input "dest" value. // [Flags] private enum UnescapeMode { CopyOnly = 0x0, // used for V1.0 ToString() compatibility mode only Escape = 0x1, // Only used by ImplicitFile, the string is already fully unescaped Unescape = 0x2, // Only used as V1.0 UserEscaped compatibility mode EscapeUnescape = Unescape|Escape, // does both escaping control+reserved and unescaping of safe characters V1ToStringFlag = 0x4, // Only used as V1.0 ToString() compatibility mode, assumes DontEscape level also UnescapeAll = 0x8, // just unescape everything, leave bad escaped sequences as is (should throw but can't due to v1.0 compat) UnescapeAllOrThrow = 0x10|UnescapeAll, // just unescape everything plus throw on bad escaped sequences } private unsafe static char[] UnescapeString( string input, int start, int end, char[] dest, ref int destPosition, char rsvd1, char rsvd2, char rsvd3, UnescapeMode unescapeMode, UriParser syntax, bool isQuery, bool readOnlyConfig) { fixed (char *pStr = input) { return UnescapeString( pStr, start, end, dest, ref destPosition, rsvd1, rsvd2, rsvd3, unescapeMode, syntax, isQuery, readOnlyConfig); } } private unsafe static char[] UnescapeString(char *pStr, int start, int end, char[] dest, ref int destPosition, char rsvd1, char rsvd2, char rsvd3, UnescapeMode unescapeMode, UriParser syntax, bool isQuery, bool readOnlyConfig) { byte [] bytes = null; byte escapedReallocations = 0; bool escapeReserved = false; int next = start; bool iriParsing = s_IriParsing && ((readOnlyConfig) || (!readOnlyConfig && IriParsingStatic(syntax))) && ((unescapeMode & UnescapeMode.EscapeUnescape) == UnescapeMode.EscapeUnescape); while (true) { // we may need to re-pin dest[] fixed (char* pDest = dest) { if ((unescapeMode & UnescapeMode.EscapeUnescape) == UnescapeMode.CopyOnly) { while (start < end) pDest[destPosition++] = pStr[start++]; return dest; } while (true) { char ch = (char)0; for (;next < end; ++next) { if ((ch = pStr[next]) == '%') { if ((unescapeMode & UnescapeMode.Unescape) == 0) { // re-escape, don't check anything else escapeReserved = true; } else if (next+2 < end) { ch = EscapedAscii(pStr[next+1], pStr[next+2]); // Unescape a good sequence if full unescape is requested if (unescapeMode >= UnescapeMode.UnescapeAll) { if (ch == c_DummyChar) { if (unescapeMode >= UnescapeMode.UnescapeAllOrThrow) { // Should be a rare case where the app tries to feed an invalid escaped sequence throw new UriFormatException(SR.GetString(SR.net_uri_BadString)); } continue; } } // re-escape % from an invalid sequence else if (ch == c_DummyChar) { if ((unescapeMode & UnescapeMode.Escape) != 0) escapeReserved = true; else continue; // we should throw instead but since v1.0 woudl just print '%' } // Do not unescape '%' itself unless full unescape is requested else if (ch == '%') { next += 2; continue; } // Do not unescape a reserved char unless full unescape is requested else if (ch == rsvd1 || ch == rsvd2 || ch == rsvd3) { next += 2; continue; } // Do not unescape a dangerous char unless it's V1ToStringFlags mode else if ((unescapeMode & UnescapeMode.V1ToStringFlag) == 0 && IsNotSafeForUnescape(ch)) { next += 2; continue; } else if (iriParsing && ((ch <='\x9F' && IsNotSafeForUnescape(ch)) || (ch >'\x9F' &&!CheckIriUnicodeRange(ch, isQuery)))) { // check if unenscaping gives a char ouside iri range // if it does then keep it escaped next += 2; continue; } // unescape escaped char or escape % break; } else if (unescapeMode >= UnescapeMode.UnescapeAll) { if (unescapeMode >= UnescapeMode.UnescapeAllOrThrow) { // Should be a rare case where the app tries to feed an invalid escaped sequence throw new UriFormatException(SR.GetString(SR.net_uri_BadString)); } // keep a '%' as part of a bogus sequence (we should throw but this is how 1.0 has shipped) continue; } else { escapeReserved = true; } // escape (escapeReserved==ture) or otheriwse unescape the sequence break; } else if ((unescapeMode & (UnescapeMode.Unescape | UnescapeMode.UnescapeAll)) == (UnescapeMode.Unescape | UnescapeMode.UnescapeAll)) { continue; } else if ((unescapeMode & UnescapeMode.Escape) != 0) { // Could actually escape some of the characters if (ch == rsvd1 || ch == rsvd2 || ch == rsvd3) { // found an unescaped reserved character -> escape it escapeReserved = true; break; } else if ((unescapeMode & UnescapeMode.V1ToStringFlag) == 0 && (ch <= '\x1F' || (ch >= '\x7F' && ch <= '\x9F'))) { // found an unescaped reserved character -> escape it escapeReserved = true; break; } } } //copy off previous characters from input while (start < next) pDest[destPosition++] = pStr[start++]; if (next != end) { //VsWhidbey#87423 if (escapeReserved) { //escape that char // Since this should be _really_ rare case, reallocate with constant size increase of 30 rsvd-type characters. if (escapedReallocations == 0) { escapedReallocations = 30; char[] newDest = new char[dest.Length + escapedReallocations*3]; fixed (char *pNewDest = newDest) { for (int i = 0; i < destPosition; ++i) pNewDest[i] = pDest[i]; } dest = newDest; // re-pin new dest[] array goto dest_fixed_loop_break; } else { --escapedReallocations; EscapeAsciiChar(pStr[next], dest, ref destPosition); escapeReserved = false; start = ++next; continue; } } // unescaping either one Ascii or possibly multiple Unicode if (ch <= '\x7F') { //ASCII dest[destPosition++] = ch; next+=3; start = next; continue; } // Unicode int byteCount = 1; // lazy initialization of max size, will reuse the array for next sequences if ((object) bytes == null) bytes = new byte[end - next]; bytes[0] = (byte)ch; next+=3; while (next < end) { // Check on exit criterion if ((ch = pStr[next]) != '%' || next+2 >= end) break; // already made sure we have 3 characters in str ch = EscapedAscii(pStr[next+1], pStr[next+2]); //invalid hex sequence ? if (ch == c_DummyChar) break; // character is not part of a UTF-8 sequence ? else if (ch < '\x80') break; else { //a UTF-8 sequence bytes[byteCount++] = (byte)ch; next += 3; } } Encoding noFallbackCharUTF8 = Encoding.GetEncoding("utf-8", new EncoderReplacementFallback(""), new DecoderReplacementFallback("")); char[] unescapedChars = new char[bytes.Length]; int charCount = noFallbackCharUTF8.GetChars(bytes, 0, byteCount, unescapedChars, 0); if (charCount != 0) { start = next; // match exact bytes // Do not unescape chars not allowed by Iri // need to check for invalid utf sequences that may not have given any chars MatchUTF8Sequence(pDest, dest, ref destPosition, unescapedChars, charCount, bytes, isQuery, iriParsing); } else { // the encoded, high-ANSI characters are not UTF-8 encoded // [....] codereview: a bug? // If we expect UTF-8 then we should discard (not copy) anything else. // The reason is that >0x7F Ansi and UTF-8 cannot be supported at the same time if (unescapeMode >= UnescapeMode.UnescapeAllOrThrow) { // Should be a rare case where the app tries to feed an invalid escaped sequence throw new UriFormatException(SR.GetString(SR.net_uri_BadString)); } next = start + 3; start = next; dest[destPosition++]= (char)bytes[0]; } } if (next == end) goto done; } dest_fixed_loop_break: ; } } done: return dest; } // // Need to check for invalid utf sequences that may not have given any chars. // We got the unescaped chars, we then reencode them and match off the bytes // to get the invalid sequence bytes that we just copy off // private static unsafe void MatchUTF8Sequence(char* pDest, char[] dest, ref int destOffset, char[] unescapedChars, int charCount, byte[] bytes, bool isQuery, bool iriParsing) { int count = 0; fixed (char* unescapedCharsPtr = unescapedChars){ for (int j = 0; j < charCount; ++j){ bool isHighSurr = Char.IsHighSurrogate(unescapedCharsPtr[j]); byte[] encodedBytes = Encoding.UTF8.GetBytes(unescapedChars, j, isHighSurr ? 2 : 1); int encodedBytesLength = encodedBytes.Length; // we have to keep unicode chars outside Iri range escaped bool inIriRange = false; if (iriParsing){ if (!isHighSurr) inIriRange = CheckIriUnicodeRange(unescapedChars[j], isQuery); else{ bool surrPair = false; inIriRange = CheckIriUnicodeRange(unescapedChars[j], unescapedChars[j + 1], ref surrPair, isQuery); } } while (true){ while (bytes[count] != encodedBytes[0]){ EscapeAsciiChar((char)bytes[count++], dest, ref destOffset); } // check if all bytes match bool allBytesMatch = true; int k = 0; for (; k < encodedBytesLength; ++k){ if (bytes[count + k] != encodedBytes[k]){ allBytesMatch = false; break; } } if (allBytesMatch){ count += encodedBytesLength; if (iriParsing){ if (!inIriRange){ // need to keep chars not allowed as escaped for (int l = 0; l < encodedBytes.Length; ++l){ EscapeAsciiChar((char)encodedBytes[l], dest, ref destOffset); } } else if (!IsBidiControlCharacter(unescapedCharsPtr[j])){ //copy chars pDest[destOffset++] = unescapedCharsPtr[j]; } if (isHighSurr) pDest[destOffset++] = unescapedCharsPtr[j + 1]; } else{ //copy chars pDest[destOffset++] = unescapedCharsPtr[j]; if (isHighSurr) pDest[destOffset++] = unescapedCharsPtr[j + 1]; } break; // break out of while (true) since we've matched this char bytes } else{ // copy bytes till place where bytes dont match for (int l = 0; l < k; ++l){ EscapeAsciiChar((char)bytes[count++], dest, ref destOffset); } } } if (isHighSurr) j++; } } } // for rfc 3987 internal bool CheckIsReserved(char ch) { char[] rsvd = new char[]{':', '/', '?', '#', '[', ']', '@'}; //gen-delims for (int i = 0; i < rsvd.Length; ++i){ if (rsvd[i] == ch) return true; } return false; } // // Check reserved chars according to rfc 3987 in a sepecific component // internal bool CheckIsReserved(char ch, UriComponents component) { if ((component != UriComponents.Scheme) || (component != UriComponents.UserInfo) || (component != UriComponents.Host) || (component != UriComponents.Port) || (component != UriComponents.Path) || (component != UriComponents.Query) || (component != UriComponents.Fragment) ) return (component == (UriComponents)0)? CheckIsReserved(ch): false; else { switch(component) { // Reserved chars according to rfc 3987 case UriComponents.UserInfo: if( ch == '/' || ch == '?' || ch == '#' || ch == '[' || ch == ']' || ch == '@' ) return true; break; case UriComponents.Host: if( ch == ':' || ch == '/' || ch == '?' || ch == '#' || ch == '[' || ch == ']' || ch == '@' ) return true; break; case UriComponents.Path: if( ch == '/' || ch == '?' || ch == '#' || ch == '[' || ch == ']' ) return true; break; case UriComponents.Query: if(ch == '#' || ch == '[' || ch == ']') return true; break; case UriComponents.Fragment: if(ch == '#' || ch == '[' || ch == ']') return true; break; default: break; } return false; } } // // Cleans up the specified component according to Iri rules // a) Chars allowed by iri in a component are unescaped if found escaped // b) Bidi chars are stripped // // should be called only if IRI parsing is switched on internal unsafe string EscapeUnescapeIri(string input, int start, int end, UriComponents component) { fixed (char *pInput = input) { return EscapeUnescapeIri(pInput, start, end, component); } } // // See above explanation // internal unsafe string EscapeUnescapeIri(char* pInput, int start, int end, UriComponents component) { char [] dest = new char[ end - start ]; byte[] bytes = null; // Pin the array to do pointer accesses GCHandle destHandle = GCHandle.Alloc(dest, GCHandleType.Pinned); char* pDest = (char*)destHandle.AddrOfPinnedObject(); byte escapedReallocations = 0; int next = start; int destOffset = 0; char ch; bool escape = false; bool surrogatePair = false; bool isUnicode = false; for (;next < end; ++next) { escape = false; surrogatePair = false; isUnicode = false; if ((ch = pInput[next]) == '%'){ if (next + 2 < end){ ch = EscapedAscii(pInput[next + 1], pInput[next + 2]); // Do not unescape a reserved char if (ch == c_DummyChar || ch == '%' || CheckIsReserved(ch, component) || IsNotSafeForUnescape(ch)){ // keep as is pDest[destOffset++] = pInput[next++]; pDest[destOffset++] = pInput[next++]; pDest[destOffset++] = pInput[next]; continue; } else if (ch <= '\x7F'){ //ASCII pDest[destOffset++] = ch; next += 2; continue; }else{ // possibly utf8 encoded sequence of unicode // check if safe to unescape according to Iri rules int startSeq = next; int byteCount = 1; // lazy initialization of max size, will reuse the array for next sequences if ((object)bytes == null) bytes = new byte[end - next]; bytes[0] = (byte)ch; next += 3; while (next < end) { // Check on exit criterion if ((ch = pInput[next]) != '%' || next + 2 >= end) break; // already made sure we have 3 characters in str ch = EscapedAscii(pInput[next + 1], pInput[next + 2]); //invalid hex sequence ? if (ch == c_DummyChar) break; // character is not part of a UTF-8 sequence ? else if (ch < '\x80') break; else { //a UTF-8 sequence bytes[byteCount++] = (byte)ch; next += 3; } } next--; // for loop will increment Encoding noFallbackCharUTF8 = Encoding.GetEncoding("utf-8", new EncoderReplacementFallback(""), new DecoderReplacementFallback("")); char[] unescapedChars = new char[bytes.Length]; int charCount = noFallbackCharUTF8.GetChars(bytes, 0, byteCount, unescapedChars, 0); if (charCount != 0){ // need to check for invalid utf sequences that may not have given any chars // check if unicode value is allowed MatchUTF8Sequence( pDest, dest, ref destOffset, unescapedChars, charCount, bytes, component == UriComponents.Query, true); } else { // copy escaped sequence as is for (int i = startSeq; i <= next; ++i) pDest[destOffset++] = pInput[i]; } } }else{ pDest[destOffset++] = pInput[next]; } } else if (ch > '\x7f'){ // unicode char ch2; if ((Char.IsHighSurrogate(ch)) && (next + 1 < end)){ ch2 = pInput[next + 1]; escape = !CheckIriUnicodeRange(ch, ch2, ref surrogatePair, component == UriComponents.Query); if (!escape){ // copy the two chars pDest[destOffset++] = pInput[next++]; pDest[destOffset++] = pInput[next]; }else{ isUnicode = true; } }else{ if(CheckIriUnicodeRange(ch, component == UriComponents.Query)){ if (!IsBidiControlCharacter(ch)){ // copy it pDest[destOffset++] = pInput[next]; } }else{ // escape it escape = true; isUnicode = true; } } }else{ // just copy the character pDest[destOffset++] = pInput[next]; } if (escape){ if (escapedReallocations == 0){ // may need more memory since we didnt anticipate escaping escapedReallocations = 30; char[] newDest = new char[dest.Length + escapedReallocations * 3]; fixed (char* pNewDest = newDest){ for (int i = 0; i < destOffset; ++i) pNewDest[i] = pDest[i]; } if (destHandle.IsAllocated) destHandle.Free(); dest = newDest; // re-pin new dest[] array destHandle = GCHandle.Alloc(dest, GCHandleType.Pinned); pDest = (char*)destHandle.AddrOfPinnedObject(); }else{ if (isUnicode){ if (surrogatePair) escapedReallocations -= 4; else escapedReallocations -= 3; } else --escapedReallocations; } byte[] encodedBytes = new byte[4]; fixed (byte* pEncodedBytes = encodedBytes){ int encodedBytesCount = Encoding.UTF8.GetBytes(pInput + next, surrogatePair ? 2 : 1, pEncodedBytes, 4); for (int count = 0; count < encodedBytesCount; ++count) EscapeAsciiChar((char)encodedBytes[count], dest, ref destOffset); } } } if (destHandle.IsAllocated) destHandle.Free(); return new string(dest, 0 , destOffset ); } // // Do not unescape these in safe mode: // 1) reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," // 2) excluded = control | "#" | "%" | "\" // // That will still give plenty characters unescaped by SafeUnesced mode such as // 1) Unicode characters // 2) Unreserved = alphanum | "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" // 3) DelimitersAndUnwise = "<" | ">" | <"> | "{" | "}" | "|" | "^" | "[" | "]" | "`" static bool IsNotSafeForUnescape(char ch) { if (ch <= '\x1F' || (ch >= '\x7F' && ch <= '\x9F')) return true; else if ((ch >= ';' && ch <= '@' && (ch | '\x2') != '>') || (ch >= '#' && ch <= '&') || ch == '+' || ch == ',' || ch == '/' || ch == '\\') return true; return false; } // // Internal stuff // // Returns false if OriginalString value // (1) is not correctly escaped as per URI spec excluding intl UNC name case // (2) or is an absolute Uri that represents implicit file Uri "c:\dir\file" // (3) or is an absolute Uri that misses a slash before path "file://c:/dir/file" // (4) or contains unescaped backslashes even if they will be treated // as forward slashes like http:\\host/path\file or file:\\\c:\path // internal unsafe bool InternalIsWellFormedOriginalString() { if (UserDrivenParsing) throw new InvalidOperationException(SR.GetString(SR.net_uri_UserDrivenParsing, this.GetType().FullName)); fixed (char* str = m_String) { ushort idx = 0; // // For a relative Uri we only care about escaping and backslashes // if (!IsAbsoluteUri) return (CheckCanonical(str, ref idx, (ushort)m_String.Length, c_EOL) & (Check.BackslashInPath | Check.EscapedCanonical)) == Check.EscapedCanonical; // // (2) or is an absolute Uri that represents implicit file Uri "c:\dir\file" // if (IsImplicitFile) return false; //This will get all the offsets, a Host name will be checked separatelly below EnsureParseRemaining(); Flags nonCanonical = (m_Flags & (Flags.E_CannotDisplayCanonical | Flags.IriCanonical)); // User, Path, Query or Fragment may have some non escaped characters if (((nonCanonical & Flags.E_CannotDisplayCanonical & (Flags.E_UserNotCanonical | Flags.E_PathNotCanonical | Flags.E_QueryNotCanonical | Flags.E_FragmentNotCanonical)) != Flags.Zero) && (!m_iriParsing || (m_iriParsing && (((nonCanonical & Flags.E_UserNotCanonical) == 0) || ((nonCanonical & Flags.UserIriCanonical) == 0)) && (((nonCanonical & Flags.E_PathNotCanonical) == 0) || ((nonCanonical & Flags.PathIriCanonical) == 0)) && (((nonCanonical & Flags.E_QueryNotCanonical) == 0) || ((nonCanonical & Flags.QueryIriCanonical) == 0)) && (((nonCanonical & Flags.E_FragmentNotCanonical) == 0) || ((nonCanonical & Flags.FragmentIriCanonical) == 0))))) return false; // checking on scheme:\\ or file://// if (InFact(Flags.AuthorityFound)) { idx = (ushort)(m_Info.Offset.Scheme + m_Syntax.SchemeName.Length + 2); if (idx >= m_Info.Offset.User || m_String[idx - 1] == '\\' || m_String[idx] == '\\') return false; #if !PLATFORM_UNIX if (InFact(Flags.UncPath | Flags.DosPath)) { while (++idx < m_Info.Offset.User && (m_String[idx] == '/' || m_String[idx] == '\\')) return false; } #endif // !PLATFORM_UNIX } // (3) or is an absolute Uri that misses a slash before path "file://c:/dir/file" // Note that for this check to be more general we assert that if Path is non empty and if it requires a first slash // (which looks absent) then the method has to fail. // Today it's only possible for a Dos like path, i.e. file://c:/bla would fail below check. if (InFact(Flags.FirstSlashAbsent) && m_Info.Offset.Query > m_Info.Offset.Path) return false; // (4) or contains unescaped backslashes even if they will be treated // as forward slashes like http:\\host/path\file or file:\\\c:\path // Note we do not check for Flags.ShouldBeCompressed i.e. allow // /./ and alike as valid if (InFact(Flags.BackslashInPath)) return false; // Capturing a rare case like file:///c|/dir if (IsDosPath && m_String[m_Info.Offset.Path + SecuredPathIndex - 1] == '|') return false; // // May need some real CPU processing to anwser the request // // // Check escaping for authority // if ((m_Flags & Flags.CanonicalDnsHost) == 0) { idx = m_Info.Offset.User; if (!(m_iriParsing && (HostType == Flags.IPv6HostType))){ Check result = CheckCanonical(str, ref idx, (ushort)m_Info.Offset.Path, '/'); if (((result & (Check.ReservedFound | Check.BackslashInPath | Check.EscapedCanonical)) != Check.EscapedCanonical) && (!m_iriParsing || (m_iriParsing && ((result & (Check.DisplayCanonical | Check.FoundNonAscii | Check.NotIriCanonical)) != (Check.DisplayCanonical | Check.FoundNonAscii))))) return false; } } // Want to ensure there are slashes after the scheme if ((m_Flags & (Flags.SchemeNotCanonical | Flags.AuthorityFound)) == (Flags.SchemeNotCanonical | Flags.AuthorityFound)) { idx = (ushort)m_Syntax.SchemeName.Length; while (str[idx++] != ':') ; if (idx + 1 >= m_String.Length || str[idx] != '/' || str[idx + 1] != '/') return false; } } // // May be scheme, host, port or path need some canonicalization but still the uri string is found to be a "well formed" one // return true; } // Should never be used except by the below method private Uri(Flags flags, UriParser uriParser, string uri) { m_Flags = flags; m_Syntax = uriParser; m_String = uri; } // // a Uri.TryCreate() method goes through here. // internal static Uri CreateHelper(string uriString, bool dontEscape, UriKind uriKind, ref UriFormatException e) { // if (!Enum.IsDefined(typeof(UriKind), uriKind)) -- We currently believe that Enum.IsDefined() is too slow to be used here. if ((int)uriKind < (int)UriKind.RelativeOrAbsolute || (int)uriKind > (int)UriKind.Relative){ throw new ArgumentException(SR.GetString(SR.net_uri_InvalidUriKind, uriKind)); } UriParser syntax = null; Flags flags = Flags.Zero; ParsingError err = ParseScheme(uriString, ref flags, ref syntax); if (dontEscape) flags |= Flags.UserEscaped; // We won't use User factory for these errors if (err != ParsingError.None) { // If it looks as a relative Uri, custom factory is ignored if (uriKind != UriKind.Absolute && err <= ParsingError.LastRelativeUriOkErrIndex) return new Uri((flags & Flags.UserEscaped), null, uriString); return null; } // Cannot be relative Uri if came here Uri result = new Uri(flags, syntax, uriString); // Validate instance using ether built in or a user Parser try { result.InitializeUri(err, uriKind, out e); if (e == null) return result; return null; } catch (UriFormatException ee) { System.Net.GlobalLog.Assert(!syntax.IsSimple, "A UriPraser threw on InitializeAndValidate."); e = ee; // A precaution since custom Parser should never throw in this case. return null; } } // // Resolves into either baseUri or relativeUri according to conditions OR if not possible it uses newUriString // to return combined URI strings from both Uris // otherwise if e != null on output the operation has failed // internal static Uri ResolveHelper(Uri baseUri, Uri relativeUri, ref string newUriString, ref bool userEscaped, out UriFormatException e) { System.Net.GlobalLog.Assert(!baseUri.IsNotAbsoluteUri && !baseUri.UserDrivenParsing, "Uri::ResolveHelper()|baseUri is not Absolute or is controlled by User Parser."); e = null; string relativeStr = string.Empty; if ((object)relativeUri != null) { if (relativeUri.IsAbsoluteUri) return relativeUri; relativeStr = relativeUri.OriginalString; userEscaped = relativeUri.UserEscaped; } else relativeStr = string.Empty; // Here we can assert that passed "relativeUri" is indeed a relative one if (relativeStr.Length > 0 && (IsLWS(relativeStr[0]) || IsLWS(relativeStr[relativeStr.Length - 1]))) relativeStr = relativeStr.Trim(_WSchars); if (relativeStr.Length == 0) { newUriString = baseUri.GetParts(UriComponents.AbsoluteUri, baseUri.UserEscaped ? UriFormat.UriEscaped : UriFormat.SafeUnescaped); return null; } // Check for a simple fragment in relative part if (relativeStr[0] == '#' && !baseUri.IsImplicitFile && baseUri.Syntax.InFact(UriSyntaxFlags.MayHaveFragment)) { newUriString = baseUri.GetParts(UriComponents.AbsoluteUri & ~UriComponents.Fragment, UriFormat.UriEscaped) + relativeStr; return null; } // Check for a simple query in relative part if (relativeStr[0] == '?' && !baseUri.IsImplicitFile && baseUri.Syntax.InFact(UriSyntaxFlags.MayHaveQuery)) { newUriString = baseUri.GetParts(UriComponents.AbsoluteUri & ~UriComponents.Query & ~UriComponents.Fragment, UriFormat.UriEscaped) + relativeStr; return null; } // Check on the DOS path in the relative Uri (a special case) if (relativeStr.Length >= 3 && (relativeStr[1] == ':' || relativeStr[1] == '|') && IsAsciiLetter(relativeStr[0]) && (relativeStr[2] == '\\' || relativeStr[2] == '/')) { if (baseUri.IsImplicitFile) { // It could have file:/// prepended to the result but we want to keep it as *Implicit* File Uri newUriString = relativeStr; return null; } else if (baseUri.Syntax.InFact(UriSyntaxFlags.AllowDOSPath)) { // The scheme is not changed just the path gets replaced string prefix; if (baseUri.InFact(Flags.AuthorityFound)) prefix = baseUri.Syntax.InFact(UriSyntaxFlags.PathIsRooted) ? ":///" : "://"; else prefix = baseUri.Syntax.InFact(UriSyntaxFlags.PathIsRooted) ? ":/" : ":"; newUriString = baseUri.Scheme + prefix + relativeStr; return null; } // If we are here then input like "http://host/path/" + "C:\x" will produce the result http://host/path/c:/x } ParsingError err = GetCombinedString(baseUri, relativeStr, userEscaped, ref newUriString); if (err != ParsingError.None) { e = GetException(err); return null; } if ((object)newUriString == (object)baseUri.m_String) return baseUri; return null; } private unsafe string GetRelativeSerializationString(UriFormat format) { if (format == UriFormat.UriEscaped) { if (m_String.Length == 0) return string.Empty; int position = 0; char[] dest = EscapeString(m_String, 0, m_String.Length, null, ref position, true, c_DummyChar, c_DummyChar, '%'); if ((object)dest == null) return m_String; return new string(dest, 0, position); } else if (format == UriFormat.Unescaped) return UnescapeDataString(m_String); else if (format == UriFormat.SafeUnescaped) { if (m_String.Length == 0) return string.Empty; char[] dest = new char[m_String.Length]; int position = 0; dest = UnescapeString(m_String, 0, m_String.Length, dest, ref position, c_DummyChar, c_DummyChar, c_DummyChar, UnescapeMode.EscapeUnescape, null, false, true); return new string(dest, 0, position); } else throw new ArgumentOutOfRangeException("format"); } // // UriParser helpers methods // internal string GetComponentsHelper(UriComponents uriComponents, UriFormat uriFormat) { if (uriComponents == UriComponents.Scheme) return m_Syntax.SchemeName; // A serialzation info is "almost" the same as AbsoluteUri except for IPv6 + ScopeID hostname case if ((uriComponents & UriComponents.SerializationInfoString) != 0) uriComponents |= UriComponents.AbsoluteUri; //This will get all the offsets, HostString will be created below if needed EnsureParseRemaining(); //Check to see if we need the host/authotity string if ((uriComponents & UriComponents.Host) != 0) EnsureHostString(true); //This, single Port request is always processed here if (uriComponents == UriComponents.Port || uriComponents == UriComponents.StrongPort) { if (((m_Flags & Flags.NotDefaultPort) != 0) || (uriComponents == UriComponents.StrongPort && m_Syntax.DefaultPort != UriParser.NoDefaultPort)) { // recreate string from the port value return m_Info.Offset.PortValue.ToString(CultureInfo.InvariantCulture); } return string.Empty; } if ((uriComponents & UriComponents.StrongPort) != 0) { // Down the path we rely on Port to be ON for StrongPort uriComponents |= UriComponents.Port; } //This request sometime is faster to process here if (uriComponents == UriComponents.Host && (uriFormat == UriFormat.UriEscaped || (( m_Flags & (Flags.HostNotCanonical | Flags.E_HostNotCanonical)) == 0))) { EnsureHostString(false); return m_Info.Host; } switch (uriFormat) { case UriFormat.UriEscaped: return GetEscapedParts(uriComponents); case V1ToStringUnescape: case UriFormat.SafeUnescaped: case UriFormat.Unescaped: return GetUnescapedParts(uriComponents, uriFormat); default: throw new ArgumentOutOfRangeException("uriFormat"); } } // // // internal bool IsBaseOfHelper(Uri uriLink) { //TO if (!IsAbsoluteUri || UserDrivenParsing) return false; if (!uriLink.IsAbsoluteUri) { //a relative uri could have quite tricky form, it's better to fix it now. string newUriString = null; UriFormatException e; bool dontEscape = false; uriLink = ResolveHelper(this, uriLink, ref newUriString, ref dontEscape, out e); if (e != null) return false; if ((object)uriLink == null) uriLink = CreateHelper(newUriString, dontEscape, UriKind.Absolute, ref e); if (e != null) return false; } if (Syntax.SchemeName != uriLink.Syntax.SchemeName) return false; // Canonicalize and test for substring match up to the last path slash string me = GetParts(UriComponents.AbsoluteUri & ~UriComponents.Fragment, UriFormat.SafeUnescaped); string she = uriLink.GetParts(UriComponents.AbsoluteUri & ~UriComponents.Fragment, UriFormat.SafeUnescaped); unsafe { fixed (char* pMe = me) { fixed (char* pShe = she) { return TestForSubPath(pMe, (ushort)me.Length, pShe, (ushort)she.Length, IsUncOrDosPath || uriLink.IsUncOrDosPath); } } } } // // Only a ctor time call // private void CreateThisFromUri(Uri otherUri) { // Clone the other guy but develop own UriInfo member m_Info = null; m_Flags = otherUri.m_Flags; if (InFact(Flags.MinimalUriInfoSet)) { m_Flags &= ~(Flags.MinimalUriInfoSet | Flags.AllUriInfoSet | Flags.IndexMask); // Port / Path offset int portIndex = otherUri.m_Info.Offset.Path; if (InFact(Flags.NotDefaultPort)) { // Find the start of the port. Account for non-canonical ports like :00123 while (otherUri.m_String[portIndex] != ':' && portIndex > otherUri.m_Info.Offset.Host) { portIndex--; } if (otherUri.m_String[portIndex] != ':') { // Something wrong with the NotDefaultPort flag. Reset to path index System.Net.GlobalLog.Assert("Uri failed to locate custom port at index: " + portIndex); portIndex = otherUri.m_Info.Offset.Path; } } m_Flags |= (Flags)portIndex; // Port or path } m_Syntax = otherUri.m_Syntax; m_String = otherUri.m_String; m_iriParsing = otherUri.m_iriParsing; if (otherUri.OriginalStringSwitched){ m_originalUnicodeString = otherUri.m_originalUnicodeString; } if (otherUri.AllowIdn && (otherUri.InFact(Flags.IdnHost) || otherUri.InFact(Flags.UnicodeHost))){ m_DnsSafeHost = otherUri.m_DnsSafeHost; } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- CdpEqualityComparer.cs
- KeyValueConfigurationCollection.cs
- DataSourceView.cs
- ObjectStateEntryDbDataRecord.cs
- GlobalizationAssembly.cs
- BaseServiceProvider.cs
- SqlDependencyListener.cs
- IconBitmapDecoder.cs
- SerializerWriterEventHandlers.cs
- ThicknessKeyFrameCollection.cs
- ApplicationTrust.cs
- ListItemCollection.cs
- SafeHandle.cs
- ValidatedControlConverter.cs
- _CookieModule.cs
- IPCCacheManager.cs
- GcSettings.cs
- RawUIStateInputReport.cs
- QfeChecker.cs
- ProxySimple.cs
- Hyperlink.cs
- StatusCommandUI.cs
- xdrvalidator.cs
- CodeEventReferenceExpression.cs
- LocalValueEnumerator.cs
- XmlResolver.cs
- XmlChildEnumerator.cs
- ConsoleTraceListener.cs
- XamlDesignerSerializationManager.cs
- SerializationInfoEnumerator.cs
- IndentedTextWriter.cs
- ConsumerConnectionPointCollection.cs
- GPRECTF.cs
- DiscreteKeyFrames.cs
- Triplet.cs
- DataGridViewRowConverter.cs
- DynamicDiscoSearcher.cs
- PerformanceCounterPermissionEntryCollection.cs
- WindowsImpersonationContext.cs
- HttpCapabilitiesSectionHandler.cs
- ObjectDisposedException.cs
- ToolStripSeparatorRenderEventArgs.cs
- Internal.cs
- AffineTransform3D.cs
- CodeCompileUnit.cs
- ObjectToken.cs
- mactripleDES.cs
- EntitySetBaseCollection.cs
- XmlValidatingReaderImpl.cs
- CompositeControl.cs
- AsyncParams.cs
- RotateTransform.cs
- FixedDocumentPaginator.cs
- Menu.cs
- uribuilder.cs
- UnsafeNativeMethods.cs
- ErrorHandler.cs
- Soap.cs
- HttpHeaderCollection.cs
- RowUpdatingEventArgs.cs
- DrawListViewSubItemEventArgs.cs
- FixedSOMTable.cs
- LinearKeyFrames.cs
- DesignerResources.cs
- TextPointerBase.cs
- XmlPreloadedResolver.cs
- X500Name.cs
- AuthenticationModuleElement.cs
- WeakEventTable.cs
- SqlMethodTransformer.cs
- DBConcurrencyException.cs
- MaskInputRejectedEventArgs.cs
- XmlKeywords.cs
- QuotedStringWriteStateInfo.cs
- WebPartDeleteVerb.cs
- ReadOnlyPermissionSet.cs
- ClientApiGenerator.cs
- DSASignatureDeformatter.cs
- FrameworkElementFactory.cs
- GAC.cs
- DatagridviewDisplayedBandsData.cs
- FlowLayout.cs
- NativeMethods.cs
- AsyncContentLoadedEventArgs.cs
- GregorianCalendar.cs
- OleDbStruct.cs
- odbcmetadatafactory.cs
- WindowProviderWrapper.cs
- VectorCollectionConverter.cs
- CreateUserErrorEventArgs.cs
- ADConnectionHelper.cs
- SQLBytes.cs
- StylusTouchDevice.cs
- ProcessHostMapPath.cs
- SafeFileMappingHandle.cs
- WebServiceClientProxyGenerator.cs
- KeyEvent.cs
- ArglessEventHandlerProxy.cs
- Int64KeyFrameCollection.cs
- EventLogPropertySelector.cs