summaryrefslogtreecommitdiff
path: root/src/mscorlib/src/System/Security/Util/URLString.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/mscorlib/src/System/Security/Util/URLString.cs')
-rw-r--r--src/mscorlib/src/System/Security/Util/URLString.cs1237
1 files changed, 5 insertions, 1232 deletions
diff --git a/src/mscorlib/src/System/Security/Util/URLString.cs b/src/mscorlib/src/System/Security/Util/URLString.cs
index 83f9ce483f..4ec353876a 100644
--- a/src/mscorlib/src/System/Security/Util/URLString.cs
+++ b/src/mscorlib/src/System/Security/Util/URLString.cs
@@ -22,368 +22,8 @@ namespace System.Security.Util {
using System.IO;
using System.Diagnostics.Contracts;
-#if FEATURE_SERIALIZATION
- [Serializable]
-#endif
- internal sealed class URLString : SiteString
+ internal static class URLString
{
- private String m_protocol;
- [OptionalField(VersionAdded = 2)]
- private String m_userpass;
- private SiteString m_siteString;
- private int m_port;
-#if !PLATFORM_UNIX
- private LocalSiteString m_localSite;
-#endif // !PLATFORM_UNIX
- private DirectoryString m_directory;
-
- private const String m_defaultProtocol = "file";
-
- [OptionalField(VersionAdded = 2)]
- private bool m_parseDeferred;
- [OptionalField(VersionAdded = 2)]
- private String m_urlOriginal;
- [OptionalField(VersionAdded = 2)]
- private bool m_parsedOriginal;
-
- [OptionalField(VersionAdded = 3)]
- private bool m_isUncShare;
-
- // legacy field from v1.x, not used in v2 and beyond. Retained purely for serialization compatibility.
- private String m_fullurl;
-
-
- [OnDeserialized]
- public void OnDeserialized(StreamingContext ctx)
- {
-
- if (m_urlOriginal == null)
- {
- // pre-v2 deserialization. Need to fix-up fields here
- m_parseDeferred = false;
- m_parsedOriginal = false; // Dont care what this value is - never used
- m_userpass = "";
- m_urlOriginal = m_fullurl;
- m_fullurl = null;
- }
- }
- [OnSerializing]
- private void OnSerializing(StreamingContext ctx)
- {
-
- if ((ctx.State & ~(StreamingContextStates.Clone|StreamingContextStates.CrossAppDomain)) != 0)
- {
- DoDeferredParse();
- m_fullurl = m_urlOriginal;
- }
- }
- [OnSerialized]
- private void OnSerialized(StreamingContext ctx)
- {
- if ((ctx.State & ~(StreamingContextStates.Clone|StreamingContextStates.CrossAppDomain)) != 0)
- {
- m_fullurl = null;
- }
- }
-
- public URLString()
- {
- m_protocol = "";
- m_userpass = "";
- m_siteString = new SiteString();
- m_port = -1;
-#if !PLATFORM_UNIX
- m_localSite = null;
-#endif // !PLATFORM_UNIX
- m_directory = new DirectoryString();
- m_parseDeferred = false;
- }
-
- private void DoDeferredParse()
- {
- if (m_parseDeferred)
- {
- ParseString(m_urlOriginal, m_parsedOriginal);
- m_parseDeferred = false;
- }
- }
-
- public URLString(string url) : this(url, false, false) {}
- public URLString(string url, bool parsed) : this(url, parsed, false) {}
-
- internal URLString(string url, bool parsed, bool doDeferredParsing)
- {
- m_port = -1;
- m_userpass = "";
- DoFastChecks(url);
- m_urlOriginal = url;
- m_parsedOriginal = parsed;
- m_parseDeferred = true;
- if (doDeferredParsing)
- DoDeferredParse();
- }
-
- // Converts %XX and %uYYYY to the actual characters (I.e. Unesacpes any escape characters present in the URL)
- private String UnescapeURL(String url)
- {
- StringBuilder intermediate = StringBuilderCache.Acquire(url.Length);
- int Rindex = 0; // index into temp that gives the rest of the string to be processed
- int index;
- int braIndex = -1;
- int ketIndex = -1;
- braIndex = url.IndexOf('[',Rindex);
- if (braIndex != -1)
- ketIndex = url.IndexOf(']', braIndex);
-
- do
- {
- index = url.IndexOf( '%', Rindex);
-
- if (index == -1)
- {
- intermediate = intermediate.Append(url, Rindex, (url.Length - Rindex));
- break;
- }
- // if we hit a '%' in the middle of an IPv6 address, dont process that
- if (index > braIndex && index < ketIndex)
- {
- intermediate = intermediate.Append(url, Rindex, (ketIndex - Rindex+1));
- Rindex = ketIndex+1;
- continue;
- }
-
- if (url.Length - index < 2) // Check that there is at least 1 char after the '%'
- throw new ArgumentException( Environment.GetResourceString( "Argument_InvalidUrl" ) );
-
- if (url[index+1] == 'u' || url[index+1] == 'U')
- {
- if (url.Length - index < 6) // example: "%u004d" is 6 chars long
- throw new ArgumentException( Environment.GetResourceString( "Argument_InvalidUrl" ) );
-
- // We have a unicode character specified in hex
-
- try
- {
- char c = (char)(Hex.ConvertHexDigit( url[index+2] ) << 12 |
- Hex.ConvertHexDigit( url[index+3] ) << 8 |
- Hex.ConvertHexDigit( url[index+4] ) << 4 |
- Hex.ConvertHexDigit( url[index+5] ));
- intermediate = intermediate.Append(url, Rindex, index - Rindex);
- intermediate = intermediate.Append(c);
- }
- catch(ArgumentException) // Hex.ConvertHexDigit can throw an "out of range" ArgumentException
- {
- throw new ArgumentException( Environment.GetResourceString( "Argument_InvalidUrl" ) );
- }
-
- Rindex = index + 6 ; //update the 'seen' length
- }
- else
- {
- // we have a hex character.
-
- if (url.Length - index < 3) // example: "%4d" is 3 chars long
- throw new ArgumentException( Environment.GetResourceString( "Argument_InvalidUrl" ) );
-
- try
- {
- char c = (char)(Hex.ConvertHexDigit( url[index+1] ) << 4 | Hex.ConvertHexDigit( url[index+2] ));
-
- intermediate = intermediate.Append(url, Rindex, index - Rindex);
- intermediate = intermediate.Append(c);
- }
- catch(ArgumentException) // Hex.ConvertHexDigit can throw an "out of range" ArgumentException
- {
- throw new ArgumentException( Environment.GetResourceString( "Argument_InvalidUrl" ) );
- }
-
- Rindex = index + 3; // update the 'seen' length
- }
-
- }
- while (true);
- return StringBuilderCache.GetStringAndRelease(intermediate);
- }
-
- // Helper Function for ParseString:
- // Search for the end of the protocol info and grab the actual protocol string
- // ex. http://www.microsoft.com/complus would have a protocol string of http
- private String ParseProtocol(String url)
- {
- String temp;
- int index = url.IndexOf( ':' );
-
- if (index == 0)
- {
- throw new ArgumentException( Environment.GetResourceString( "Argument_InvalidUrl" ) );
- }
- else if (index == -1)
- {
- m_protocol = m_defaultProtocol;
- temp = url;
- }
- else if (url.Length > index + 1)
- {
- if (index == m_defaultProtocol.Length &&
- String.Compare(url, 0, m_defaultProtocol, 0, index, StringComparison.OrdinalIgnoreCase) == 0)
- {
- m_protocol = m_defaultProtocol;
- temp = url.Substring( index + 1 );
-
- // Since an explicit file:// URL could be immediately followed by a host name, we will be
- // conservative and assume that it is on a share rather than a potentally relative local
- // URL.
- m_isUncShare = true;
- }
- else if (url[index+1] != '\\')
- {
-#if !PLATFORM_UNIX
- if (url.Length > index + 2 &&
- url[index+1] == '/' &&
- url[index+2] == '/')
-#else
- if (url.Length > index + 1 &&
- url[index+1] == '/' ) // UNIX style "file:/home/me" is allowed, so account for that
-#endif // !PLATFORM_UNIX
- {
- m_protocol = url.Substring( 0, index );
-
- for (int i = 0; i < m_protocol.Length; ++i)
- {
- char c = m_protocol[i];
-
- if ((c >= 'a' && c <= 'z') ||
- (c >= 'A' && c <= 'Z') ||
- (c >= '0' && c <= '9') ||
- (c == '+') ||
- (c == '.') ||
- (c == '-'))
- {
- continue;
- }
- else
- {
- throw new ArgumentException( Environment.GetResourceString( "Argument_InvalidUrl" ) );
- }
- }
-#if !PLATFORM_UNIX
- temp = url.Substring( index + 3 );
-#else
- // In UNIX, we don't know how many characters we'll have to skip past.
- // Skip past \, /, and :
- //
- for ( int j=index ; j<url.Length ; j++ )
- {
- if ( url[j] != '\\' && url[j] != '/' && url[j] != ':' )
- {
- index = j;
- break;
- }
- }
-
- temp = url.Substring( index );
-#endif // !PLATFORM_UNIX
- }
- else
- {
- throw new ArgumentException( Environment.GetResourceString( "Argument_InvalidUrl" ) );
- }
- }
- else
- {
- m_protocol = m_defaultProtocol;
- temp = url;
- }
- }
- else
- {
- throw new ArgumentException( Environment.GetResourceString( "Argument_InvalidUrl" ) );
- }
-
- return temp;
- }
-
- private String ParsePort(String url)
- {
- String temp = url;
- char[] separators = new char[] { ':', '/' };
- int Rindex = 0;
- int userpassIndex = temp.IndexOf('@');
- if (userpassIndex != -1) {
- if (temp.IndexOf('/',0,userpassIndex) == -1) {
- // this is a user:pass type of string
- m_userpass = temp.Substring(0,userpassIndex);
- Rindex = userpassIndex + 1;
- }
- }
-
- int braIndex = -1;
- int ketIndex = -1;
- int portIndex = -1;
- braIndex = url.IndexOf('[',Rindex);
- if (braIndex != -1)
- ketIndex = url.IndexOf(']', braIndex);
- if (ketIndex != -1)
- {
- // IPv6 address...ignore the IPv6 block when searching for the port
- portIndex = temp.IndexOfAny(separators,ketIndex);
- }
- else
- {
- portIndex = temp.IndexOfAny(separators,Rindex);
- }
-
-
-
- if (portIndex != -1 && temp[portIndex] == ':')
- {
- // make sure it really is a port, and has a number after the :
- if ( temp[portIndex+1] >= '0' && temp[portIndex+1] <= '9' )
- {
- int tempIndex = temp.IndexOf( '/', Rindex);
-
- if (tempIndex == -1)
- {
- m_port = Int32.Parse( temp.Substring(portIndex + 1), CultureInfo.InvariantCulture );
-
- if (m_port < 0)
- throw new ArgumentException( Environment.GetResourceString( "Argument_InvalidUrl" ) );
-
- temp = temp.Substring( Rindex, portIndex - Rindex );
- }
- else if (tempIndex > portIndex)
- {
- m_port = Int32.Parse( temp.Substring(portIndex + 1, tempIndex - portIndex - 1), CultureInfo.InvariantCulture );
- temp = temp.Substring( Rindex, portIndex - Rindex ) + temp.Substring( tempIndex );
- }
- else
- throw new ArgumentException( Environment.GetResourceString( "Argument_InvalidUrl" ) );
- }
- else
- throw new ArgumentException( Environment.GetResourceString( "Argument_InvalidUrl" ) );
- }
- else {
- // Chop of the user/pass portion if any
- temp = temp.Substring(Rindex);
- }
-
- return temp;
- }
-
- // This does three things:
- // 1. It makes the following modifications to the start of the string:
- // a. \\?\ and \\?/ => <empty>
- // b. \\.\ and \\./ => <empty>
- // 2. If isFileUrl is true, converts all slashes to front slashes and strips leading
- // front slashes. See comment by code.
- // 3. Throws a PathTooLongException if the length of the resulting URL is >= MAX_PATH.
- // This is done to prevent security issues due to canonicalization truncations.
- // Remove this method when the Path class supports "\\?\"
- internal static string PreProcessForExtendedPathRemoval(string url, bool isFileUrl)
- {
- return PreProcessForExtendedPathRemoval(checkPathLength: true, url: url, isFileUrl: isFileUrl);
- }
-
internal static string PreProcessForExtendedPathRemoval(bool checkPathLength, string url, bool isFileUrl)
{
bool isUncShare = false;
@@ -417,7 +57,8 @@ namespace System.Security.Util {
}
else
{
- if (isFileUrl) {
+ if (isFileUrl)
+ {
// We need to handle an indefinite number of leading front slashes for file URLs since we could
// get something like:
// file://\\?\
@@ -449,14 +90,14 @@ namespace System.Security.Util {
{
int slashCount = 0;
bool seenFirstBackslash = false;
-
+
while (slashCount < modifiedUrl.Length && (modifiedUrl[slashCount] == '/' || modifiedUrl[slashCount] == '\\'))
{
// Look for sets of consecutive backslashes. We can't just look for these at the start
// of the string, since file:// might come first. Instead, once we see the first \, look
// for a second one following it.
if (!seenFirstBackslash && modifiedUrl[slashCount] == '\\')
- {
+ {
seenFirstBackslash = true;
if (slashCount + 1 < modifiedUrl.Length && modifiedUrl[slashCount + 1] == '\\')
isUncShare = true;
@@ -493,873 +134,5 @@ namespace System.Security.Util {
throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong"));
}
}
-
- // Do any misc massaging of data in the URL
- private String PreProcessURL(String url, bool isFileURL)
- {
-
-#if !PLATFORM_UNIX
- if (isFileURL) {
- // Remove when the Path class supports "\\?\"
- url = PreProcessForExtendedPathRemoval(url, true, ref m_isUncShare);
- }
- else {
- url = url.Replace('\\', '/');
- }
- return url;
-#else
- // Remove superfluous '/'
- // For UNIX, the file path would look something like:
- // file:///home/johndoe/here
- // file:/home/johndoe/here
- // file:../johndoe/here
- // file:~/johndoe/here
- String temp = url;
- int nbSlashes = 0;
- while(nbSlashes<temp.Length && '/'==temp[nbSlashes])
- nbSlashes++;
-
- // if we get a path like file:///directory/name we need to convert
- // this to /directory/name.
- if(nbSlashes > 2)
- temp = temp.Substring(nbSlashes-1, temp.Length - (nbSlashes-1));
- else if (2 == nbSlashes) /* it's a relative path */
- temp = temp.Substring(nbSlashes, temp.Length - nbSlashes);
- return temp;
-#endif // !PLATFORM_UNIX
-
- }
-
- private void ParseFileURL(String url)
- {
-
- String temp = url;
-#if !PLATFORM_UNIX
- int index = temp.IndexOf( '/');
-
- if (index != -1 &&
- ((index == 2 &&
- temp[index-1] != ':' &&
- temp[index-1] != '|') ||
- index != 2) &&
- index != temp.Length - 1)
- {
- // Also, if it is a UNC share, we want m_localSite to
- // be of the form "computername/share", so if the first
- // fileEnd character found is a slash, do some more parsing
- // to find the proper end character.
-
- int tempIndex = temp.IndexOf( '/', index+1);
-
- if (tempIndex != -1)
- index = tempIndex;
- else
- index = -1;
- }
-
- String localSite;
- if (index == -1)
- localSite = temp;
- else
- localSite = temp.Substring(0,index);
-
- if (localSite.Length == 0)
- throw new ArgumentException( Environment.GetResourceString( "Argument_InvalidUrl" ) );
-
- int i;
- bool spacesAllowed;
-
- if (localSite[0] == '\\' && localSite[1] == '\\')
- {
- spacesAllowed = true;
- i = 2;
- }
- else
- {
- i = 0;
- spacesAllowed = false;
- }
-
- bool useSmallCharToUpper = true;
-
- for (; i < localSite.Length; ++i)
- {
- char c = localSite[i];
-
- if ((c >= 'A' && c <= 'Z') ||
- (c >= 'a' && c <= 'z') ||
- (c >= '0' && c <= '9') ||
- (c == '-') || (c == '/') ||
- (c == ':') || (c == '|') ||
- (c == '.') || (c == '*') ||
- (c == '$') || (spacesAllowed && c == ' '))
- {
- continue;
- }
- else
- {
- useSmallCharToUpper = false;
- break;
- }
- }
-
- if (useSmallCharToUpper)
- localSite = String.SmallCharToUpper( localSite );
- else
- localSite = localSite.ToUpper(CultureInfo.InvariantCulture);
-
- m_localSite = new LocalSiteString( localSite );
-
- if (index == -1)
- {
- if (localSite[localSite.Length-1] == '*')
- m_directory = new DirectoryString( "*", false );
- else
- m_directory = new DirectoryString();
- }
- else
- {
- String directoryString = temp.Substring( index + 1 );
- if (directoryString.Length == 0)
- {
- m_directory = new DirectoryString();
- }
- else
- {
- m_directory = new DirectoryString( directoryString, true);
- }
- }
-#else // !PLATFORM_UNIX
- m_directory = new DirectoryString( temp, true);
-#endif // !PLATFORM_UNIX
-
- m_siteString = null;
- return;
- }
-
- private void ParseNonFileURL(String url)
- {
- String temp = url;
- int index = temp.IndexOf('/');
-
- if (index == -1)
- {
-#if !PLATFORM_UNIX
- m_localSite = null; // for drive letter
-#endif // !PLATFORM_UNIX
- m_siteString = new SiteString( temp );
- m_directory = new DirectoryString();
- }
- else
- {
-#if !PLATFORM_UNIX
- String site = temp.Substring( 0, index );
- m_localSite = null;
- m_siteString = new SiteString( site );
-
- String directoryString = temp.Substring( index + 1 );
-
- if (directoryString.Length == 0)
- {
- m_directory = new DirectoryString();
- }
- else
- {
- m_directory = new DirectoryString( directoryString, false );
- }
-#else
- String directoryString = temp.Substring( index + 1 );
- String site = temp.Substring( 0, index );
- m_directory = new DirectoryString( directoryString, false );
- m_siteString = new SiteString( site );
-#endif //!PLATFORM_UNIX
- }
- return;
- }
-
- void DoFastChecks( String url )
- {
- if (url == null)
- {
- throw new ArgumentNullException( nameof(url) );
- }
- Contract.EndContractBlock();
-
- if (url.Length == 0)
- {
- throw new FormatException(Environment.GetResourceString("Format_StringZeroLength"));
- }
- }
-
- // NOTE:
- // 1. We support URLs that follow the common Internet scheme syntax
- // (<scheme>://user:pass@<host>:<port>/<url-path>) and all windows file URLs.
- // 2. In the general case we parse of the site and create a SiteString out of it
- // (which supports our wildcarding scheme). In the case of files we don't support
- // wildcarding and furthermore SiteString doesn't like ':' and '|' which can appear
- // in file urls so we just keep that info in a separate string and set the
- // SiteString to null.
- //
- // ex. http://www.microsoft.com/complus -> m_siteString = "www.microsoft.com" m_localSite = null
- // ex. file:///c:/complus/mscorlib.dll -> m_siteString = null m_localSite = "c:"
- // ex. file:///c|/complus/mscorlib.dll -> m_siteString = null m_localSite = "c:"
- void ParseString( String url, bool parsed )
- {
- // If there are any escaped hex or unicode characters in the url, translate those
- // into the proper character.
-
- if (!parsed)
- {
- url = UnescapeURL(url);
- }
-
- // Identify the protocol and strip the protocol info from the string, if present.
- String temp = ParseProtocol(url);
-
- bool fileProtocol = (String.Compare( m_protocol, "file", StringComparison.OrdinalIgnoreCase) == 0);
-
- // handle any special preocessing...removing extra characters, etc.
- temp = PreProcessURL(temp, fileProtocol);
-
- if (fileProtocol)
- {
- ParseFileURL(temp);
- }
- else
- {
- // Check if there is a port number and parse that out.
- temp = ParsePort(temp);
- ParseNonFileURL(temp);
- // Note: that we allow DNS and Netbios names for non-file protocols (since sitestring will check
- // that the hostname satisfies these two protocols. DNS-only checking can theoretically be added
- // here but that would break all the programs that use '_' (which is fairly common, yet illegal).
- // If this needs to be done at any point, add a call to m_siteString.IsLegalDNSName().
- }
-
-
- }
-
- public String Scheme
- {
- get
- {
- DoDeferredParse();
-
- return m_protocol;
- }
- }
-
- public String Host
- {
- get
- {
- DoDeferredParse();
-
- if (m_siteString != null)
- {
- return m_siteString.ToString();
- }
- else
- {
-#if !PLATFORM_UNIX
- return m_localSite.ToString();
-#else
- return "";
-#endif // !PLATFORM_UNIX
- }
- }
- }
-
- public String Port
- {
- get
- {
- DoDeferredParse();
-
- if (m_port == -1)
- return null;
- else
- return m_port.ToString(CultureInfo.InvariantCulture);
- }
- }
-
- public String Directory
- {
- get
- {
- DoDeferredParse();
-
- return m_directory.ToString();
- }
- }
-
- /// <summary>
- /// Make a best guess at determining if this is URL refers to a file with a relative path. Since
- /// this is a guess to help out users of UrlMembershipCondition who may accidentally supply a
- /// relative URL, we'd rather err on the side of absolute than relative. (We'd rather accept some
- /// meaningless membership conditions rather than reject meaningful ones).
- ///
- /// In order to be a relative file URL, the URL needs to have a protocol of file, and not be on a
- /// UNC share.
- ///
- /// If both of the above are true, then the heuristics we'll use to detect an absolute URL are:
- /// 1. A host name which is:
- /// a. greater than one character and ends in a colon (representing the drive letter) OR
- /// b. ends with a * (so we match any file with the given prefix if any)
- /// 2. Has a directory name (cannot be simply file://c:)
- /// </summary>
- public bool IsRelativeFileUrl
- {
- get
- {
- DoDeferredParse();
-
- if (String.Equals(m_protocol, "file", StringComparison.OrdinalIgnoreCase) && !m_isUncShare)
- {
-#if !PLATFORM_UNIX
- string host = m_localSite != null ? m_localSite.ToString() : null;
- // If the host name ends with the * character, treat this as an absolute URL since the *
- // could represent the rest of the full path.
- if (host.EndsWith('*'))
- return false;
-#endif // !PLATFORM_UNIX
- string directory = m_directory != null ? m_directory.ToString() : null;
-
-#if !PLATFORM_UNIX
- return host == null || host.Length < 2 || !host.EndsWith(':') ||
- String.IsNullOrEmpty(directory);
-#else
- return String.IsNullOrEmpty(directory);
-#endif // !PLATFORM_UNIX
-
- }
-
- // Since this is not a local URL, it cannot be relative
- return false;
- }
- }
-
- public String GetFileName()
- {
- DoDeferredParse();
-
-#if !PLATFORM_UNIX
- if (String.Compare( m_protocol, "file", StringComparison.OrdinalIgnoreCase) != 0)
- return null;
-
- String intermediateDirectory = this.Directory.Replace( '/', '\\' );
-
- String directory = this.Host.Replace( '/', '\\' );
-
- int directorySlashIndex = directory.IndexOf( '\\' );
- if (directorySlashIndex == -1)
- {
- if (directory.Length != 2 ||
- !(directory[1] == ':' || directory[1] == '|'))
- {
- directory = "\\\\" + directory;
- }
- }
- else if (directorySlashIndex != 2 ||
- (directorySlashIndex == 2 && directory[1] != ':' && directory[1] != '|'))
- {
- directory = "\\\\" + directory;
- }
-
- directory += "\\" + intermediateDirectory;
-
- return directory;
-#else
- // In Unix, directory contains the full pathname
- // (this is what we get in Win32)
- if (String.Compare( m_protocol, "file", StringComparison.OrdinalIgnoreCase ) != 0)
- return null;
-
- return this.Directory;
-#endif // !PLATFORM_UNIX
- }
-
-
- public String GetDirectoryName()
- {
- DoDeferredParse();
-
-#if !PLATFORM_UNIX
- if (String.Compare( m_protocol, "file", StringComparison.OrdinalIgnoreCase ) != 0)
- return null;
-
- String intermediateDirectory = this.Directory.Replace( '/', '\\' );
-
- int slashIndex = 0;
- for (int i = intermediateDirectory.Length; i > 0; i--)
- {
- if (intermediateDirectory[i-1] == '\\')
- {
- slashIndex = i;
- break;
- }
- }
-
- String directory = this.Host.Replace( '/', '\\' );
-
- int directorySlashIndex = directory.IndexOf( '\\' );
- if (directorySlashIndex == -1)
- {
- if (directory.Length != 2 ||
- !(directory[1] == ':' || directory[1] == '|'))
- {
- directory = "\\\\" + directory;
- }
- }
- else if (directorySlashIndex > 2 ||
- (directorySlashIndex == 2 && directory[1] != ':' && directory[1] != '|'))
- {
- directory = "\\\\" + directory;
- }
-
- directory += "\\";
-
- if (slashIndex > 0)
- {
- directory += intermediateDirectory.Substring( 0, slashIndex );
- }
-
- return directory;
-#else
- if (String.Compare( m_protocol, "file", StringComparison.OrdinalIgnoreCase) != 0)
- return null;
-
- String directory = this.Directory.ToString();
- int slashIndex = 0;
- for (int i = directory.Length; i > 0; i--)
- {
- if (directory[i-1] == '/')
- {
- slashIndex = i;
- break;
- }
- }
-
- if (slashIndex > 0)
- {
- directory = directory.Substring( 0, slashIndex );
- }
-
- return directory;
-#endif // !PLATFORM_UNIX
- }
-
- public override SiteString Copy()
- {
- return new URLString( m_urlOriginal, m_parsedOriginal );
- }
-
- public override bool IsSubsetOf( SiteString site )
- {
- if (site == null)
- {
- return false;
- }
-
- URLString url = site as URLString;
-
- if (url == null)
- {
- return false;
- }
-
- DoDeferredParse();
- url.DoDeferredParse();
-
- URLString normalUrl1 = this.SpecialNormalizeUrl();
- URLString normalUrl2 = url.SpecialNormalizeUrl();
-
- if (String.Compare( normalUrl1.m_protocol, normalUrl2.m_protocol, StringComparison.OrdinalIgnoreCase) == 0 &&
- normalUrl1.m_directory.IsSubsetOf( normalUrl2.m_directory ))
- {
-#if !PLATFORM_UNIX
- if (normalUrl1.m_localSite != null)
- {
- // We do a little extra processing in here for local files since we allow
- // both <drive_letter>: and <drive_letter>| forms of urls.
-
- return normalUrl1.m_localSite.IsSubsetOf( normalUrl2.m_localSite );
- }
- else
-#endif // !PLATFORM_UNIX
- {
- if (normalUrl1.m_port != normalUrl2.m_port)
- return false;
-
- return normalUrl2.m_siteString != null && normalUrl1.m_siteString.IsSubsetOf( normalUrl2.m_siteString );
- }
- }
- else
- {
- return false;
- }
- }
-
- public override String ToString()
- {
- return m_urlOriginal;
- }
-
- public override bool Equals(Object o)
- {
- DoDeferredParse();
-
- if (o == null || !(o is URLString))
- return false;
- else
- return this.Equals( (URLString)o );
- }
-
- public override int GetHashCode()
- {
- DoDeferredParse();
-
- TextInfo info = CultureInfo.InvariantCulture.TextInfo;
- int accumulator = 0;
-
- if (this.m_protocol != null)
- accumulator = info.GetCaseInsensitiveHashCode( this.m_protocol );
-
-#if !PLATFORM_UNIX
- if (this.m_localSite != null)
- {
- accumulator = accumulator ^ this.m_localSite.GetHashCode();
- }
- else
- {
- accumulator = accumulator ^ this.m_siteString.GetHashCode();
- }
- accumulator = accumulator ^ this.m_directory.GetHashCode();
-#else
- accumulator = accumulator ^ info.GetCaseInsensitiveHashCode(this.m_urlOriginal);
-#endif // !PLATFORM_UNIX
-
-
-
- return accumulator;
- }
-
- public bool Equals( URLString url )
- {
- return CompareUrls( this, url );
- }
-
- public static bool CompareUrls( URLString url1, URLString url2 )
- {
- if (url1 == null && url2 == null)
- return true;
-
- if (url1 == null || url2 == null)
- return false;
-
- url1.DoDeferredParse();
- url2.DoDeferredParse();
-
- URLString normalUrl1 = url1.SpecialNormalizeUrl();
- URLString normalUrl2 = url2.SpecialNormalizeUrl();
-
- // Compare protocol (case insensitive)
-
- if (String.Compare( normalUrl1.m_protocol, normalUrl2.m_protocol, StringComparison.OrdinalIgnoreCase) != 0)
- return false;
-
- // Do special processing for file urls
-
- if (String.Compare( normalUrl1.m_protocol, "file", StringComparison.OrdinalIgnoreCase) == 0)
- {
-#if !PLATFORM_UNIX
- if (!normalUrl1.m_localSite.IsSubsetOf( normalUrl2.m_localSite ) ||
- !normalUrl2.m_localSite.IsSubsetOf( normalUrl1.m_localSite ))
- return false;
-#else
- return url1.IsSubsetOf( url2 ) &&
- url2.IsSubsetOf( url1 );
-#endif // !PLATFORM_UNIX
- }
- else
- {
- if (String.Compare( normalUrl1.m_userpass, normalUrl2.m_userpass, StringComparison.Ordinal) != 0)
- return false;
-
- if (!normalUrl1.m_siteString.IsSubsetOf( normalUrl2.m_siteString ) ||
- !normalUrl2.m_siteString.IsSubsetOf( normalUrl1.m_siteString ))
- return false;
-
- if (url1.m_port != url2.m_port)
- return false;
- }
-
- if (!normalUrl1.m_directory.IsSubsetOf( normalUrl2.m_directory ) ||
- !normalUrl2.m_directory.IsSubsetOf( normalUrl1.m_directory ))
- return false;
-
- return true;
- }
-
- internal String NormalizeUrl()
- {
- DoDeferredParse();
- StringBuilder builtUrl = StringBuilderCache.Acquire();
-
- if (String.Compare( m_protocol, "file", StringComparison.OrdinalIgnoreCase) == 0)
- {
-#if !PLATFORM_UNIX
- builtUrl = builtUrl.AppendFormat("FILE:///{0}/{1}", m_localSite.ToString(), m_directory.ToString());
-#else
- builtUrl = builtUrl.AppendFormat("FILE:///{0}", m_directory.ToString());
-#endif // !PLATFORM_UNIX
- }
- else
- {
- builtUrl = builtUrl.AppendFormat("{0}://{1}{2}", m_protocol, m_userpass, m_siteString.ToString());
-
- if (m_port != -1)
- builtUrl = builtUrl.AppendFormat("{0}",m_port);
-
- builtUrl = builtUrl.AppendFormat("/{0}", m_directory.ToString());
- }
-
- return StringBuilderCache.GetStringAndRelease(builtUrl).ToUpper(CultureInfo.InvariantCulture);
- }
-
-#if !PLATFORM_UNIX
- internal URLString SpecialNormalizeUrl()
- {
- // Under WinXP, file protocol urls can be mapped to
- // drives that aren't actually file protocol underneath
- // due to drive mounting. This code attempts to figure
- // out what a drive is mounted to and create the
- // url is maps to.
-
- DoDeferredParse();
- if (String.Compare( m_protocol, "file", StringComparison.OrdinalIgnoreCase) != 0)
- {
- return this;
- }
- else
- {
- String localSite = m_localSite.ToString();
-
- if (localSite.Length == 2 &&
- (localSite[1] == '|' ||
- localSite[1] == ':'))
- {
- String deviceName = null;
- GetDeviceName(localSite, JitHelpers.GetStringHandleOnStack(ref deviceName));
-
- if (deviceName != null)
- {
- if (deviceName.IndexOf( "://", StringComparison.Ordinal ) != -1)
- {
- URLString u = new URLString( deviceName + "/" + this.m_directory.ToString() );
- u.DoDeferredParse(); // Presumably the caller of SpecialNormalizeUrl wants a fully parsed URL
- return u;
- }
- else
- {
- URLString u = new URLString( "file://" + deviceName + "/" + this.m_directory.ToString() );
- u.DoDeferredParse();// Presumably the caller of SpecialNormalizeUrl wants a fully parsed URL
- return u;
- }
- }
- else
- return this;
- }
- else
- {
- return this;
- }
- }
- }
-
- [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
- [SuppressUnmanagedCodeSecurity]
- private static extern void GetDeviceName( String driveLetter, StringHandleOnStack retDeviceName );
-
-#else
- internal URLString SpecialNormalizeUrl()
- {
- return this;
- }
-#endif // !PLATFORM_UNIX
-
- }
-
-
- [Serializable]
- internal class DirectoryString : SiteString
- {
- private bool m_checkForIllegalChars;
-
- private new static char[] m_separators = { '/' };
-
- // From KB #Q177506, file/folder illegal characters are \ / : * ? " < > |
- protected static char[] m_illegalDirectoryCharacters = { '\\', ':', '*', '?', '"', '<', '>', '|' };
-
- public DirectoryString()
- {
- m_site = "";
- m_separatedSite = new ArrayList();
- }
-
- public DirectoryString( String directory, bool checkForIllegalChars )
- {
- m_site = directory;
- m_checkForIllegalChars = checkForIllegalChars;
- m_separatedSite = CreateSeparatedString(directory);
- }
-
- private ArrayList CreateSeparatedString(String directory)
- {
- if (directory == null || directory.Length == 0)
- {
- throw new ArgumentException(Environment.GetResourceString("Argument_InvalidDirectoryOnUrl"));
- }
- Contract.EndContractBlock();
-
- ArrayList list = new ArrayList();
- String[] separatedArray = directory.Split(m_separators);
-
- for (int index = 0; index < separatedArray.Length; ++index)
- {
- if (separatedArray[index] == null || separatedArray[index].Equals( "" ))
- {
- // this case is fine, we just ignore it the extra separators.
- }
- else if (separatedArray[index].Equals( "*" ))
- {
- if (index != separatedArray.Length-1)
- {
- throw new ArgumentException(Environment.GetResourceString("Argument_InvalidDirectoryOnUrl"));
- }
- list.Add( separatedArray[index] );
- }
- else if (m_checkForIllegalChars && separatedArray[index].IndexOfAny( m_illegalDirectoryCharacters ) != -1)
- {
- throw new ArgumentException(Environment.GetResourceString("Argument_InvalidDirectoryOnUrl"));
- }
- else
- {
- list.Add( separatedArray[index] );
- }
- }
-
- return list;
- }
-
- public virtual bool IsSubsetOf( DirectoryString operand )
- {
- return this.IsSubsetOf( operand, true );
- }
-
- public virtual bool IsSubsetOf( DirectoryString operand, bool ignoreCase )
- {
- if (operand == null)
- {
- return false;
- }
- else if (operand.m_separatedSite.Count == 0)
- {
- return this.m_separatedSite.Count == 0 || this.m_separatedSite.Count > 0 && String.Compare((String)this.m_separatedSite[0], "*", StringComparison.Ordinal) == 0;
- }
- else if (this.m_separatedSite.Count == 0)
- {
- return String.Compare((String)operand.m_separatedSite[0], "*", StringComparison.Ordinal) == 0;
- }
- else
- {
- return base.IsSubsetOf( operand, ignoreCase );
- }
- }
- }
-
-#if !PLATFORM_UNIX
- [Serializable]
- internal class LocalSiteString : SiteString
- {
- private new static char[] m_separators = { '/' };
-
- public LocalSiteString( String site )
- {
- m_site = site.Replace( '|', ':');
-
- if (m_site.Length > 2 && m_site.IndexOf( ':' ) != -1)
- throw new ArgumentException(Environment.GetResourceString("Argument_InvalidDirectoryOnUrl"));
-
- m_separatedSite = CreateSeparatedString(m_site);
- }
-
- private ArrayList CreateSeparatedString(String directory)
- {
- if (directory == null || directory.Length == 0)
- {
- throw new ArgumentException(Environment.GetResourceString("Argument_InvalidDirectoryOnUrl"));
- }
- Contract.EndContractBlock();
-
- ArrayList list = new ArrayList();
- String[] separatedArray = directory.Split(m_separators);
-
- for (int index = 0; index < separatedArray.Length; ++index)
- {
- if (separatedArray[index] == null || separatedArray[index].Equals( "" ))
- {
- if (index < 2 &&
- directory[index] == '/')
- {
- list.Add( "//" );
- }
- else if (index != separatedArray.Length-1)
- {
- throw new ArgumentException(Environment.GetResourceString("Argument_InvalidDirectoryOnUrl"));
- }
- }
- else if (separatedArray[index].Equals( "*" ))
- {
- if (index != separatedArray.Length-1)
- {
- throw new ArgumentException(Environment.GetResourceString("Argument_InvalidDirectoryOnUrl"));
- }
- list.Add( separatedArray[index] );
- }
- else
- {
- list.Add( separatedArray[index] );
- }
- }
-
- return list;
- }
-
- public virtual bool IsSubsetOf( LocalSiteString operand )
- {
- return this.IsSubsetOf( operand, true );
- }
-
- public virtual bool IsSubsetOf( LocalSiteString operand, bool ignoreCase )
- {
- if (operand == null)
- {
- return false;
- }
- else if (operand.m_separatedSite.Count == 0)
- {
- return this.m_separatedSite.Count == 0 || this.m_separatedSite.Count > 0 && String.Compare((String)this.m_separatedSite[0], "*", StringComparison.Ordinal) == 0;
- }
- else if (this.m_separatedSite.Count == 0)
- {
- return String.Compare((String)operand.m_separatedSite[0], "*", StringComparison.Ordinal) == 0;
- }
- else
- {
- return base.IsSubsetOf( operand, ignoreCase );
- }
- }
}
-#endif // !PLATFORM_UNIX
}