The Labs \ Source Viewer \ SSCLI \ System.Security.Util \ LocalSiteString

  1. // ==++==
  2. //
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. //
  14. // ==--==
  15. // URLString
  16. //
  17. // Implementation of membership condition for zones
  18. //
  19. namespace System.Security.Util
  20. {
  21.    
  22.     using System;
  23.     using System.Collections;
  24.     using System.Runtime.CompilerServices;
  25.     using System.Runtime.Serialization;
  26.     using System.Globalization;
  27.     using System.Text;
  28.     using System.IO;
  29.    
  30.     [Serializable()]
  31.     internal sealed class URLString : SiteString
  32.     {
  33.         private string m_protocol;
  34.         [OptionalField(VersionAdded = 2)]
  35.         private string m_userpass;
  36.         private SiteString m_siteString;
  37.         private int m_port;
  38.         #if !PLATFORM_UNIX
  39.         private LocalSiteString m_localSite;
  40.         #endif // !PLATFORM_UNIX
  41.         private DirectoryString m_directory;
  42.        
  43.         private const string m_defaultProtocol = "file";
  44.        
  45.         [OptionalField(VersionAdded = 2)]
  46.         private bool m_parseDeferred;
  47.         [OptionalField(VersionAdded = 2)]
  48.         private string m_urlOriginal;
  49.         [OptionalField(VersionAdded = 2)]
  50.         private bool m_parsedOriginal;
  51.        
  52.         // legacy field from v1.x, not used in v2 and beyond. Retained purely for serialization compatability.
  53.         private string m_fullurl;
  54.        
  55.        
  56.         [OnDeserialized()]
  57.         public void OnDeserialized(StreamingContext ctx)
  58.         {
  59.            
  60.             if (m_urlOriginal == null) {
  61.                 // pre-v2 deserialization. Need to fix-up fields here
  62.                 m_parseDeferred = false;
  63.                 m_parsedOriginal = false;
  64.                 // Dont care what this value is - never used
  65.                 m_userpass = "";
  66.                 m_urlOriginal = m_fullurl;
  67.                 m_fullurl = null;
  68.             }
  69.         }
  70.         [OnSerializing()]
  71.         private void OnSerializing(StreamingContext ctx)
  72.         {
  73.            
  74.             if ((ctx.State & ~(StreamingContextStates.Clone | StreamingContextStates.CrossAppDomain)) != 0) {
  75.                 DoDeferredParse();
  76.                 m_fullurl = m_urlOriginal;
  77.             }
  78.         }
  79.         [OnSerialized()]
  80.         private void OnSerialized(StreamingContext ctx)
  81.         {
  82.             if ((ctx.State & ~(StreamingContextStates.Clone | StreamingContextStates.CrossAppDomain)) != 0) {
  83.                 m_fullurl = null;
  84.             }
  85.         }
  86.        
  87.         public URLString()
  88.         {
  89.             m_protocol = "";
  90.             m_userpass = "";
  91.             m_siteString = new SiteString();
  92.             m_port = -1;
  93.             #if !PLATFORM_UNIX
  94.             m_localSite = null;
  95.             #endif // !PLATFORM_UNIX
  96.             m_directory = new DirectoryString();
  97.             m_parseDeferred = false;
  98.         }
  99.        
  100.         private void DoDeferredParse()
  101.         {
  102.             if (m_parseDeferred) {
  103.                 ParseString(m_urlOriginal, m_parsedOriginal);
  104.                 m_parseDeferred = false;
  105.             }
  106.         }
  107.        
  108.         public URLString(string url) : this(url, false, false)
  109.         {
  110.         }
  111.         public URLString(string url, bool parsed) : this(url, parsed, false)
  112.         {
  113.         }
  114.        
  115.         internal URLString(string url, bool parsed, bool doDeferredParsing)
  116.         {
  117.             m_port = -1;
  118.             m_userpass = "";
  119.             DoFastChecks(url);
  120.             m_urlOriginal = url;
  121.             m_parsedOriginal = parsed;
  122.             m_parseDeferred = true;
  123.             if (doDeferredParsing)
  124.                 DoDeferredParse();
  125.         }
  126.        
  127.         // Converts %XX and %uYYYY to the actual characters (I.e. Unesacpes any escape characters present in the URL)
  128.         private string UnescapeURL(string url)
  129.         {
  130.             StringBuilder intermediate = new StringBuilder(url.Length);
  131.             int Rindex = 0;
  132.             // index into temp that gives the rest of the string to be processed
  133.             int index;
  134.             int braIndex = -1;
  135.             int ketIndex = -1;
  136.             braIndex = url.IndexOf('[', Rindex);
  137.             if (braIndex != -1)
  138.                 ketIndex = url.IndexOf(']', braIndex);
  139.            
  140.             do {
  141.                 index = url.IndexOf('%', Rindex);
  142.                
  143.                 if (index == -1) {
  144.                     intermediate = intermediate.Append(url, Rindex, (url.Length - Rindex));
  145.                     break;
  146.                 }
  147.                 // if we hit a '%' in the middle of an IPv6 address, dont process that
  148.                 if (index > braIndex && index < ketIndex) {
  149.                     intermediate = intermediate.Append(url, Rindex, (ketIndex - Rindex + 1));
  150.                     Rindex = ketIndex + 1;
  151.                     continue;
  152.                 }
  153.                
  154.                 if (url.Length - index < 2)
  155.                     // Check that there is at least 1 char after the '%'
  156.                     throw new ArgumentException(Environment.GetResourceString("Argument_InvalidUrl"));
  157.                
  158.                 if (url[index + 1] == 'u' || url[index + 1] == 'U') {
  159.                     if (url.Length - index < 6)
  160.                         // example: "%u004d" is 6 chars long
  161.                         throw new ArgumentException(Environment.GetResourceString("Argument_InvalidUrl"));
  162.                    
  163.                     // We have a unicode character specified in hex
  164.                    
  165.                     try {
  166.                         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]));
  167.                         intermediate = intermediate.Append(url, Rindex, index - Rindex);
  168.                         intermediate = intermediate.Append(c);
  169.                     }
  170.                     // Hex.ConvertHexDigit can throw an "out of range" ArgumentException
  171.                     catch (ArgumentException) {
  172.                         throw new ArgumentException(Environment.GetResourceString("Argument_InvalidUrl"));
  173.                     }
  174.                    
  175.                     Rindex = index + 6;
  176.                     //update the 'seen' length
  177.                 }
  178.                 else {
  179.                     // we have a hex character.
  180.                    
  181.                     if (url.Length - index < 3)
  182.                         // example: "%4d" is 3 chars long
  183.                         throw new ArgumentException(Environment.GetResourceString("Argument_InvalidUrl"));
  184.                    
  185.                     try {
  186.                         char c = (char)(Hex.ConvertHexDigit(url[index + 1]) << 4 | Hex.ConvertHexDigit(url[index + 2]));
  187.                        
  188.                         intermediate = intermediate.Append(url, Rindex, index - Rindex);
  189.                         intermediate = intermediate.Append(c);
  190.                     }
  191.                     // Hex.ConvertHexDigit can throw an "out of range" ArgumentException
  192.                     catch (ArgumentException) {
  193.                         throw new ArgumentException(Environment.GetResourceString("Argument_InvalidUrl"));
  194.                     }
  195.                    
  196.                     Rindex = index + 3;
  197.                     // update the 'seen' length
  198.                 }
  199.                
  200.             }
  201.             while (true);
  202.             return intermediate.ToString();
  203.         }
  204.        
  205.         // Helper Function for ParseString:
  206.         // Search for the end of the protocol info and grab the actual protocol string
  207.         // ex. http://www.microsoft.com/complus would have a protocol string of http
  208.         private string ParseProtocol(string url)
  209.         {
  210.             string temp;
  211.             int index = url.IndexOf(':');
  212.            
  213.             if (index == 0) {
  214.                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidUrl"));
  215.             }
  216.             else if (index == -1) {
  217.                 m_protocol = m_defaultProtocol;
  218.                 temp = url;
  219.             }
  220.             else if (url.Length > index + 1) {
  221.                 if (index == m_defaultProtocol.Length && String.Compare(url, 0, m_defaultProtocol, 0, index, StringComparison.OrdinalIgnoreCase) == 0) {
  222.                     m_protocol = m_defaultProtocol;
  223.                     temp = url.Substring(index + 1);
  224.                 }
  225.                 else if (url[index + 1] != '\\') {
  226.                     #if !PLATFORM_UNIX
  227.                     if (url.Length > index + 2 && url[index + 1] == '/' && url[index + 2] == '/')
  228.                         #else
  229.                         // UNIX style "file:/home/me" is allowed, so account for that
  230.                         #endif // !PLATFORM_UNIX
  231.                         if (url.Length > index + 1 && url[index + 1] == '/') {
  232.                             m_protocol = url.Substring(0, index);
  233.                            
  234.                             for (int i = 0; i < m_protocol.Length; ++i) {
  235.                                 char c = m_protocol[i];
  236.                                
  237.                                 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (c == '+') || (c == '.') || (c == '-')) {
  238.                                     continue;
  239.                                 }
  240.                                 else {
  241.                                     throw new ArgumentException(Environment.GetResourceString("Argument_InvalidUrl"));
  242.                                 }
  243.                             }
  244.                             #if !PLATFORM_UNIX
  245.                             temp = url.Substring(index + 3);
  246.                             #else
  247.                             // In UNIX, we don't know how many characters we'll have to skip past.
  248.                             // Skip past \, /, and :
  249.                             //
  250.                             for (int j = index; j < url.Length; j++) {
  251.                                 if (url[j] != '\\' && url[j] != '/' && url[j] != ':') {
  252.                                     index = j;
  253.                                     break;
  254.                                 }
  255.                             }
  256.                            
  257.                             temp = url.Substring(index);
  258.                             #endif // !PLATFORM_UNIX
  259.                         }
  260.                         else {
  261.                             throw new ArgumentException(Environment.GetResourceString("Argument_InvalidUrl"));
  262.                         }
  263.                 }
  264.                 else {
  265.                     m_protocol = m_defaultProtocol;
  266.                     temp = url;
  267.                 }
  268.             }
  269.             else {
  270.                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidUrl"));
  271.             }
  272.            
  273.             return temp;
  274.         }
  275.        
  276.         private string ParsePort(string url)
  277.         {
  278.             string temp = url;
  279.             char[] separators = new char[] {':', '/'};
  280.             int Rindex = 0;
  281.             int userpassIndex = temp.IndexOf('@');
  282.             if (userpassIndex != -1) {
  283.                 if (temp.IndexOf('/', 0, userpassIndex) == -1) {
  284.                     // this is a user:pass type of string
  285.                     m_userpass = temp.Substring(0, userpassIndex);
  286.                     Rindex = userpassIndex + 1;
  287.                 }
  288.             }
  289.            
  290.             int braIndex = -1;
  291.             int ketIndex = -1;
  292.             int portIndex = -1;
  293.             braIndex = url.IndexOf('[', Rindex);
  294.             if (braIndex != -1)
  295.                 ketIndex = url.IndexOf(']', braIndex);
  296.             if (ketIndex != -1) {
  297.                 // IPv6 address...ignore the IPv6 block when searching for the port
  298.                 portIndex = temp.IndexOfAny(separators, ketIndex);
  299.             }
  300.             else {
  301.                 portIndex = temp.IndexOfAny(separators, Rindex);
  302.             }
  303.            
  304.            
  305.            
  306.             if (portIndex != -1 && temp[portIndex] == ':') {
  307.                 // make sure it really is a port, and has a number after the :
  308.                 if (temp[portIndex + 1] >= '0' && temp[portIndex + 1] <= '9') {
  309.                     int tempIndex = temp.IndexOf('/', Rindex);
  310.                    
  311.                     if (tempIndex == -1) {
  312.                         m_port = Int32.Parse(temp.Substring(portIndex + 1), CultureInfo.InvariantCulture);
  313.                        
  314.                         if (m_port < 0)
  315.                             throw new ArgumentException(Environment.GetResourceString("Argument_InvalidUrl"));
  316.                        
  317.                         temp = temp.Substring(Rindex, portIndex - Rindex);
  318.                     }
  319.                     else if (tempIndex > portIndex) {
  320.                         m_port = Int32.Parse(temp.Substring(portIndex + 1, tempIndex - portIndex - 1), CultureInfo.InvariantCulture);
  321.                         temp = temp.Substring(Rindex, portIndex - Rindex) + temp.Substring(tempIndex);
  322.                     }
  323.                     else
  324.                         throw new ArgumentException(Environment.GetResourceString("Argument_InvalidUrl"));
  325.                 }
  326.                 else
  327.                     throw new ArgumentException(Environment.GetResourceString("Argument_InvalidUrl"));
  328.             }
  329.             else {
  330.                 // Chop of the user/pass portion if any
  331.                 temp = temp.Substring(Rindex);
  332.             }
  333.            
  334.             return temp;
  335.         }
  336.        
  337.         // This does three things:
  338.         // 1. It makes the following modifications to the start of the string:
  339.         // a. \\?\ and \\?/ => <empty>
  340.         // b. \\.\ and \\./ => <empty>
  341.         // 2. If isFileUrl is true, converts all slashes to front slashes and strips leading
  342.         // front slashes. See comment by code.
  343.         // 3. Throws a PathTooLongException if the length of the resulting URL is >= MAX_PATH.
  344.         // This is done to prevent security issues due to canonicalization truncations.
  345.         // Remove this method when the Path class supports "\\?\"
  346.         static internal string PreProcessForExtendedPathRemoval(string url, bool isFileUrl)
  347.         {
  348.             // This is the modified URL that we will return
  349.             StringBuilder modifiedUrl = new StringBuilder(url);
  350.            
  351.             // ITEM 1 - remove extended path characters.
  352.             {
  353.                 // Keep track of where we are in both the comparison and altered strings.
  354.                 int curCmpIdx = 0;
  355.                 int curModIdx = 0;
  356.                
  357.                 // If all the '\' have already been converted to '/', just check for //?/ or //./
  358.                 if ((url.Length - curCmpIdx) >= 4 && (String.Compare(url, curCmpIdx, "//?/", 0, 4, StringComparison.OrdinalIgnoreCase) == 0 || String.Compare(url, curCmpIdx, "//./", 0, 4, StringComparison.OrdinalIgnoreCase) == 0)) {
  359.                     modifiedUrl.Remove(curModIdx, 4);
  360.                     curCmpIdx += 4;
  361.                 }
  362.                 else {
  363.                     if (isFileUrl) {
  364.                         // We need to handle an indefinite number of leading front slashes for file URLs since we could
  365.                         // get something like:
  366.                         // file://\\?\
  367.                         // file:/\\?\
  368.                         // file:\\?\
  369.                         // etc...
  370.                         while (url[curCmpIdx] == '/') {
  371.                             curCmpIdx++;
  372.                             curModIdx++;
  373.                         }
  374.                     }
  375.                    
  376.                     // Remove the extended path characters
  377.                     if ((url.Length - curCmpIdx) >= 4 && (String.Compare(url, curCmpIdx, "\\\\?\\", 0, 4, StringComparison.OrdinalIgnoreCase) == 0 || String.Compare(url, curCmpIdx, "\\\\?/", 0, 4, StringComparison.OrdinalIgnoreCase) == 0 || String.Compare(url, curCmpIdx, "\\\\.\\", 0, 4, StringComparison.OrdinalIgnoreCase) == 0 || String.Compare(url, curCmpIdx, "\\\\./", 0, 4, StringComparison.OrdinalIgnoreCase) == 0)) {
  378.                         modifiedUrl.Remove(curModIdx, 4);
  379.                         curCmpIdx += 4;
  380.                     }
  381.                 }
  382.             }
  383.            
  384.             // ITEM 2 - convert all slashes to forward slashes, and strip leading slashes.
  385.             if (isFileUrl) {
  386.                 modifiedUrl.Replace('\\', '/');
  387.                 int slashCount = 0;
  388.                 while (slashCount < modifiedUrl.Length && modifiedUrl[slashCount] == '/') {
  389.                     slashCount++;
  390.                 }
  391.                 modifiedUrl.Remove(0, slashCount);
  392.             }
  393.            
  394.             // ITEM 3 - If the path is greater than or equal (due to terminating NULL in windows) MAX_PATH, we throw.
  395.             if (modifiedUrl.Length >= Path.MAX_PATH) {
  396.                 throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong"));
  397.             }
  398.            
  399.             // Create the result string from the StringBuilder
  400.             return modifiedUrl.ToString();
  401.         }
  402.        
  403.        
  404.         // Do any misc massaging of data in the URL
  405.         private string PreProcessURL(string url, bool isFileURL)
  406.         {
  407.            
  408.             #if !PLATFORM_UNIX
  409.             if (isFileURL) {
  410.                 // Remove when the Path class supports "\\?\"
  411.                 url = PreProcessForExtendedPathRemoval(url, true);
  412.             }
  413.             else {
  414.                 url = url.Replace('\\', '/');
  415.             }
  416.             return url;
  417.             #else
  418.             // Remove superfluous '/'
  419.             // For UNIX, the file path would look something like:
  420.             // file:///home/johndoe/here
  421.             // file:/home/johndoe/here
  422.             // file:../johndoe/here
  423.             // file:~/johndoe/here
  424.             string temp = url;
  425.             int nbSlashes = 0;
  426.             while (nbSlashes < temp.Length && '/' == temp[nbSlashes])
  427.                 nbSlashes++;
  428.            
  429.             // if we get a path like file:///directory/name we need to convert
  430.             // this to /directory/name.
  431.             if (nbSlashes > 2)
  432.                 temp = temp.Substring(nbSlashes - 1, temp.Length - (nbSlashes - 1));
  433.             else if (2 == nbSlashes)
  434.                 /* it's a relative path */                temp = temp.Substring(nbSlashes, temp.Length - nbSlashes);
  435.             return temp;
  436.             #endif // !PLATFORM_UNIX
  437.            
  438.         }
  439.        
  440.         private void ParseFileURL(string url)
  441.         {
  442.            
  443.             string temp = url;
  444.             #if !PLATFORM_UNIX
  445.             int index = temp.IndexOf('/');
  446.            
  447.             if (index != -1 && ((index == 2 && temp[index - 1] != ':' && temp[index - 1] != '|') || index != 2) && index != temp.Length - 1) {
  448.                 // Also, if it is a UNC share, we want m_localSite to
  449.                 // be of the form "computername/share", so if the first
  450.                 // fileEnd character found is a slash, do some more parsing
  451.                 // to find the proper end character.
  452.                
  453.                 int tempIndex = temp.IndexOf('/', index + 1);
  454.                
  455.                 if (tempIndex != -1)
  456.                     index = tempIndex;
  457.                 else
  458.                     index = -1;
  459.             }
  460.            
  461.             string localSite;
  462.             if (index == -1)
  463.                 localSite = temp;
  464.             else
  465.                 localSite = temp.Substring(0, index);
  466.            
  467.             if (localSite.Length == 0)
  468.                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidUrl"));
  469.            
  470.             int i;
  471.             bool spacesAllowed;
  472.            
  473.             if (localSite[0] == '\\' && localSite[1] == '\\') {
  474.                 spacesAllowed = true;
  475.                 i = 2;
  476.             }
  477.             else {
  478.                 i = 0;
  479.                 spacesAllowed = false;
  480.             }
  481.            
  482.             bool useSmallCharToUpper = true;
  483.            
  484.             for (; i < localSite.Length; ++i) {
  485.                 char c = localSite[i];
  486.                
  487.                 if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c == '-') || (c == '/') || (c == ':') || (c == '|') || (c == '.') || (c == '*') || (c == '$') || (spacesAllowed && c == ' ')) {
  488.                     continue;
  489.                 }
  490.                 else {
  491.                     useSmallCharToUpper = false;
  492.                     break;
  493.                 }
  494.             }
  495.            
  496.             if (useSmallCharToUpper)
  497.                 localSite = String.SmallCharToUpper(localSite);
  498.             else
  499.                 localSite = localSite.ToUpper(CultureInfo.InvariantCulture);
  500.            
  501.             m_localSite = new LocalSiteString(localSite);
  502.            
  503.             if (index == -1) {
  504.                 if (localSite[localSite.Length - 1] == '*')
  505.                     m_directory = new DirectoryString("*", false);
  506.                 else
  507.                     m_directory = new DirectoryString();
  508.             }
  509.             else {
  510.                 string directoryString = temp.Substring(index + 1);
  511.                 if (directoryString.Length == 0) {
  512.                     m_directory = new DirectoryString();
  513.                 }
  514.                 else {
  515.                     m_directory = new DirectoryString(directoryString, true);
  516.                 }
  517.             }
  518.             #else // !PLATFORM_UNIX
  519.             m_directory = new DirectoryString(temp, true);
  520.             #endif // !PLATFORM_UNIX
  521.            
  522.             m_siteString = null;
  523.             return;
  524.         }
  525.        
  526.         private void ParseNonFileURL(string url)
  527.         {
  528.             string temp = url;
  529.             int index = temp.IndexOf('/');
  530.            
  531.             if (index == -1) {
  532.                 #if !PLATFORM_UNIX
  533.                 m_localSite = null;
  534.                 // for drive letter
  535.                 #endif // !PLATFORM_UNIX
  536.                 m_siteString = new SiteString(temp);
  537.                 m_directory = new DirectoryString();
  538.             }
  539.             else {
  540.                 #if !PLATFORM_UNIX
  541.                 string site = temp.Substring(0, index);
  542.                 m_localSite = null;
  543.                 m_siteString = new SiteString(site);
  544.                
  545.                 string directoryString = temp.Substring(index + 1);
  546.                
  547.                 if (directoryString.Length == 0) {
  548.                     m_directory = new DirectoryString();
  549.                 }
  550.                 else {
  551.                     m_directory = new DirectoryString(directoryString, false);
  552.                 }
  553.                 #else
  554.                 string directoryString = temp.Substring(index + 1);
  555.                 string site = temp.Substring(0, index);
  556.                 m_directory = new DirectoryString(directoryString, false);
  557.                 m_siteString = new SiteString(site);
  558.                 #endif //!PLATFORM_UNIX
  559.             }
  560.             return;
  561.         }
  562.        
  563.         void DoFastChecks(string url)
  564.         {
  565.             if (url == null) {
  566.                 throw new ArgumentNullException("url");
  567.             }
  568.            
  569.             if (url.Length == 0) {
  570.                 throw new FormatException(Environment.GetResourceString("Format_StringZeroLength"));
  571.             }
  572.         }
  573.        
  574.         // NOTE:
  575.         // 1. We support URLs that follow the common Internet scheme syntax
  576.         // (<scheme>://user:pass@<host>:<port>/<url-path>) and all windows file URLs.
  577.         // 2. In the general case we parse of the site and create a SiteString out of it
  578.         // (which supports our wildcarding scheme). In the case of files we don't support
  579.         // wildcarding and furthermore SiteString doesn't like ':' and '|' which can appear
  580.         // in file urls so we just keep that info in a separate string and set the
  581.         // SiteString to null.
  582.         //
  583.         // ex. http://www.microsoft.com/complus -> m_siteString = "www.microsoft.com" m_localSite = null
  584.         // ex. file:///c:/complus/mscorlib.dll -> m_siteString = null m_localSite = "c:"
  585.         // ex. file:///c|/complus/mscorlib.dll -> m_siteString = null m_localSite = "c:"
  586.         void ParseString(string url, bool parsed)
  587.         {
  588.             // If there are any escaped hex or unicode characters in the url, translate those
  589.             // into the proper character.
  590.            
  591.             if (!parsed) {
  592.                 url = UnescapeURL(url);
  593.             }
  594.            
  595.             // Identify the protocol and strip the protocol info from the string, if present.
  596.             string temp = ParseProtocol(url);
  597.            
  598.             bool fileProtocol = (String.Compare(m_protocol, "file", StringComparison.OrdinalIgnoreCase) == 0);
  599.            
  600.             // handle any special preocessing...removing extra characters, etc.
  601.             temp = PreProcessURL(temp, fileProtocol);
  602.            
  603.             if (fileProtocol) {
  604.                 ParseFileURL(temp);
  605.             }
  606.             else {
  607.                 // Check if there is a port number and parse that out.
  608.                 temp = ParsePort(temp);
  609.                 ParseNonFileURL(temp);
  610.                 // Note: that we allow DNS and Netbios names for non-file protocols (since sitestring will check
  611.                 // that the hostname satisfies these two protocols. DNS-only checking can theoretically be added
  612.                 // here but that would break all the programs that use '_' (which is fairly common, yet illegal).
  613.                 // If this needs to be done at any point, add a call to m_siteString.IsLegalDNSName().
  614.             }
  615.            
  616.            
  617.         }
  618.        
  619.         public string Scheme {
  620.             get {
  621.                 DoDeferredParse();
  622.                
  623.                 return m_protocol;
  624.             }
  625.         }
  626.        
  627.         public string Host {
  628.             get {
  629.                 DoDeferredParse();
  630.                
  631.                 if (m_siteString != null) {
  632.                     return m_siteString.ToString();
  633.                 }
  634.                 else {
  635.                     #if !PLATFORM_UNIX
  636.                     return m_localSite.ToString();
  637.                     #else
  638.                     return ("");
  639.                     #endif // !PLATFORM_UNIX
  640.                 }
  641.             }
  642.         }
  643.        
  644.         public string Port {
  645.             get {
  646.                 DoDeferredParse();
  647.                
  648.                 if (m_port == -1)
  649.                     return null;
  650.                 else
  651.                     return m_port.ToString(CultureInfo.InvariantCulture);
  652.             }
  653.         }
  654.        
  655.         public string Directory {
  656.             get {
  657.                 DoDeferredParse();
  658.                
  659.                 return m_directory.ToString();
  660.             }
  661.         }
  662.        
  663.         public string GetFileName()
  664.         {
  665.             DoDeferredParse();
  666.            
  667.             #if !PLATFORM_UNIX
  668.             if (String.Compare(m_protocol, "file", StringComparison.OrdinalIgnoreCase) != 0)
  669.                 return null;
  670.            
  671.             string intermediateDirectory = this.Directory.Replace('/', '\\');
  672.            
  673.             string directory = this.Host.Replace('/', '\\');
  674.            
  675.             int directorySlashIndex = directory.IndexOf('\\');
  676.             if (directorySlashIndex == -1) {
  677.                 if (directory.Length != 2 || !(directory[1] == ':' || directory[1] == '|')) {
  678.                     directory = "\\\\" + directory;
  679.                 }
  680.             }
  681.             else if (directorySlashIndex > 2 || (directorySlashIndex == 2 && directory[1] != ':' && directory[1] != '|')) {
  682.                 directory = "\\\\" + directory;
  683.             }
  684.            
  685.             directory += "\\" + intermediateDirectory;
  686.            
  687.             return directory;
  688.             #else
  689.             // In Unix, directory contains the full pathname
  690.             // (this is what we get in Win32)
  691.             if (String.Compare(m_protocol, "file", StringComparison.OrdinalIgnoreCase) != 0)
  692.                 return null;
  693.            
  694.             return this.Directory;
  695.             #endif // !PLATFORM_UNIX
  696.         }
  697.        
  698.        
  699.         public string GetDirectoryName()
  700.         {
  701.             DoDeferredParse();
  702.            
  703.             #if !PLATFORM_UNIX
  704.             if (String.Compare(m_protocol, "file", StringComparison.OrdinalIgnoreCase) != 0)
  705.                 return null;
  706.            
  707.             string intermediateDirectory = this.Directory.Replace('/', '\\');
  708.            
  709.             int slashIndex = 0;
  710.             for (int i = intermediateDirectory.Length; i > 0; i--) {
  711.                 if (intermediateDirectory[i - 1] == '\\') {
  712.                     slashIndex = i;
  713.                     break;
  714.                 }
  715.             }
  716.            
  717.             string directory = this.Host.Replace('/', '\\');
  718.            
  719.             int directorySlashIndex = directory.IndexOf('\\');
  720.             if (directorySlashIndex == -1) {
  721.                 if (directory.Length != 2 || !(directory[1] == ':' || directory[1] == '|')) {
  722.                     directory = "\\\\" + directory;
  723.                 }
  724.             }
  725.             else if (directorySlashIndex > 2 || (directorySlashIndex == 2 && directory[1] != ':' && directory[1] != '|')) {
  726.                 directory = "\\\\" + directory;
  727.             }
  728.            
  729.             directory += "\\";
  730.            
  731.             if (slashIndex > 0) {
  732.                 directory += intermediateDirectory.Substring(0, slashIndex);
  733.             }
  734.            
  735.             return directory;
  736.             #else
  737.             if (String.Compare(m_protocol, "file", StringComparison.OrdinalIgnoreCase) != 0)
  738.                 return null;
  739.            
  740.             string directory = this.Directory.ToString();
  741.             int slashIndex = 0;
  742.             for (int i = directory.Length; i > 0; i--) {
  743.                 if (directory[i - 1] == '/') {
  744.                     slashIndex = i;
  745.                     break;
  746.                 }
  747.             }
  748.            
  749.             if (slashIndex > 0) {
  750.                 directory = directory.Substring(0, slashIndex);
  751.             }
  752.            
  753.             return directory;
  754.             #endif // !PLATFORM_UNIX
  755.         }
  756.        
  757.         public override SiteString Copy()
  758.         {
  759.             return new URLString(m_urlOriginal, m_parsedOriginal);
  760.         }
  761.        
  762.         public override bool IsSubsetOf(SiteString site)
  763.         {
  764.             if (site == null) {
  765.                 return false;
  766.             }
  767.            
  768.             URLString url = site as URLString;
  769.            
  770.             if (url == null) {
  771.                 return false;
  772.             }
  773.            
  774.             DoDeferredParse();
  775.             url.DoDeferredParse();
  776.            
  777.             URLString normalUrl1 = this.SpecialNormalizeUrl();
  778.             URLString normalUrl2 = url.SpecialNormalizeUrl();
  779.            
  780.             if (String.Compare(normalUrl1.m_protocol, normalUrl2.m_protocol, StringComparison.OrdinalIgnoreCase) == 0 && normalUrl1.m_directory.IsSubsetOf(normalUrl2.m_directory)) {
  781.                 #if !PLATFORM_UNIX
  782.                 if (normalUrl1.m_localSite != null) {
  783.                     // We do a little extra processing in here for local files since we allow
  784.                     // both <drive_letter>: and <drive_letter>| forms of urls.
  785.                    
  786.                     return normalUrl1.m_localSite.IsSubsetOf(normalUrl2.m_localSite);
  787.                 }
  788.                 #endif // !PLATFORM_UNIX
  789.                 else {
  790.                     if (normalUrl1.m_port != normalUrl2.m_port)
  791.                         return false;
  792.                    
  793.                     return normalUrl2.m_siteString != null && normalUrl1.m_siteString.IsSubsetOf(normalUrl2.m_siteString);
  794.                 }
  795.             }
  796.             else {
  797.                 return false;
  798.             }
  799.         }
  800.        
  801.         public override string ToString()
  802.         {
  803.             return m_urlOriginal;
  804.         }
  805.        
  806.         public override bool Equals(object o)
  807.         {
  808.             DoDeferredParse();
  809.            
  810.             if (o == null || !(o is URLString))
  811.                 return false;
  812.             else
  813.                 return this.Equals((URLString)o);
  814.         }
  815.        
  816.         public override int GetHashCode()
  817.         {
  818.             DoDeferredParse();
  819.            
  820.             TextInfo info = CultureInfo.InvariantCulture.TextInfo;
  821.             int accumulator = 0;
  822.            
  823.             if (this.m_protocol != null)
  824.                 accumulator = info.GetCaseInsensitiveHashCode(this.m_protocol);
  825.            
  826.             #if !PLATFORM_UNIX
  827.             if (this.m_localSite != null) {
  828.                 accumulator = accumulator ^ this.m_localSite.GetHashCode();
  829.             }
  830.             else {
  831.                 accumulator = accumulator ^ this.m_siteString.GetHashCode();
  832.             }
  833.             accumulator = accumulator ^ this.m_directory.GetHashCode();
  834.             #else
  835.             accumulator = accumulator ^ info.GetCaseInsensitiveHashCode(this.m_urlOriginal);
  836.             #endif // !PLATFORM_UNIX
  837.            
  838.            
  839.            
  840.             return accumulator;
  841.         }
  842.        
  843.         public bool Equals(URLString url)
  844.         {
  845.             return CompareUrls(this, url);
  846.         }
  847.        
  848.         public static bool CompareUrls(URLString url1, URLString url2)
  849.         {
  850.             if (url1 == null && url2 == null)
  851.                 return true;
  852.            
  853.             if (url1 == null || url2 == null)
  854.                 return false;
  855.            
  856.             url1.DoDeferredParse();
  857.             url2.DoDeferredParse();
  858.            
  859.             URLString normalUrl1 = url1.SpecialNormalizeUrl();
  860.             URLString normalUrl2 = url2.SpecialNormalizeUrl();
  861.            
  862.             // Compare protocol (case insensitive)
  863.            
  864.             if (String.Compare(normalUrl1.m_protocol, normalUrl2.m_protocol, StringComparison.OrdinalIgnoreCase) != 0)
  865.                 return false;
  866.            
  867.             // Do special processing for file urls
  868.            
  869.             if (String.Compare(normalUrl1.m_protocol, "file", StringComparison.OrdinalIgnoreCase) == 0) {
  870.                 #if !PLATFORM_UNIX
  871.                 if (!normalUrl1.m_localSite.IsSubsetOf(normalUrl2.m_localSite) || !normalUrl2.m_localSite.IsSubsetOf(normalUrl1.m_localSite))
  872.                     return false;
  873.                 #else
  874.                 return url1.IsSubsetOf(url2) && url2.IsSubsetOf(url1);
  875.                 #endif // !PLATFORM_UNIX
  876.             }
  877.             else {
  878.                 if (String.Compare(normalUrl1.m_userpass, normalUrl2.m_userpass, StringComparison.Ordinal) != 0)
  879.                     return false;
  880.                
  881.                 if (!normalUrl1.m_siteString.IsSubsetOf(normalUrl2.m_siteString) || !normalUrl2.m_siteString.IsSubsetOf(normalUrl1.m_siteString))
  882.                     return false;
  883.                
  884.                 if (url1.m_port != url2.m_port)
  885.                     return false;
  886.             }
  887.            
  888.             if (!normalUrl1.m_directory.IsSubsetOf(normalUrl2.m_directory) || !normalUrl2.m_directory.IsSubsetOf(normalUrl1.m_directory))
  889.                 return false;
  890.            
  891.             return true;
  892.         }
  893.        
  894.         internal string NormalizeUrl()
  895.         {
  896.             StringBuilder builtUrl = new StringBuilder();
  897.             DoDeferredParse();
  898.            
  899.             if (String.Compare(m_protocol, "file", StringComparison.OrdinalIgnoreCase) == 0) {
  900.                 #if !PLATFORM_UNIX
  901.                 builtUrl = builtUrl.AppendFormat("FILE:///{0}/{1}", m_localSite.ToString(), m_directory.ToString());
  902.                 #else
  903.                 builtUrl = builtUrl.AppendFormat("FILE:///{0}", m_directory.ToString());
  904.                 #endif // !PLATFORM_UNIX
  905.             }
  906.             else {
  907.                 builtUrl = builtUrl.AppendFormat("{0}://{1}{2}", m_protocol, m_userpass, m_siteString.ToString());
  908.                
  909.                 if (m_port != -1)
  910.                     builtUrl = builtUrl.AppendFormat("{0}", m_port);
  911.                
  912.                 builtUrl = builtUrl.AppendFormat("/{0}", m_directory.ToString());
  913.             }
  914.            
  915.             return builtUrl.ToString().ToUpper(CultureInfo.InvariantCulture);
  916.         }
  917.        
  918.         #if !PLATFORM_UNIX
  919.         internal URLString SpecialNormalizeUrl()
  920.         {
  921.             // Under WinXP, file protocol urls can be mapped to
  922.             // drives that aren't actually file protocol underneath
  923.             // due to drive mounting. This code attempts to figure
  924.             // out what a drive is mounted to and create the
  925.             // url is maps to.
  926.            
  927.             DoDeferredParse();
  928.             if (String.Compare(m_protocol, "file", StringComparison.OrdinalIgnoreCase) != 0) {
  929.                 return this;
  930.             }
  931.             else {
  932.                 string localSite = m_localSite.ToString();
  933.                
  934.                 if (localSite.Length == 2 && (localSite[1] == '|' || localSite[1] == ':')) {
  935.                     string deviceName = _GetDeviceName(localSite);
  936.                    
  937.                     if (deviceName != null) {
  938.                         if (deviceName.IndexOf("://", StringComparison.Ordinal) != -1) {
  939.                             URLString u = new URLString(deviceName + "/" + this.m_directory.ToString());
  940.                             u.DoDeferredParse();
  941.                             // Presumably the caller of SpecialNormalizeUrl wants a fully parsed URL
  942.                             return u;
  943.                         }
  944.                         else {
  945.                             URLString u = new URLString("file://" + deviceName + "/" + this.m_directory.ToString());
  946.                             u.DoDeferredParse();
  947.                             // Presumably the caller of SpecialNormalizeUrl wants a fully parsed URL
  948.                             return u;
  949.                         }
  950.                     }
  951.                     else
  952.                         return this;
  953.                 }
  954.                 else {
  955.                     return this;
  956.                 }
  957.             }
  958.         }
  959.        
  960.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  961.         private static extern string _GetDeviceName(string driveLetter);
  962.        
  963.         #else
  964.         internal URLString SpecialNormalizeUrl()
  965.         {
  966.             return this;
  967.         }
  968.         #endif // !PLATFORM_UNIX
  969.        
  970.     }
  971.    
  972.    
  973.     [Serializable()]
  974.     internal class DirectoryString : SiteString
  975.     {
  976.         private bool m_checkForIllegalChars;
  977.        
  978.         private static new char[] m_separators = {'/'};
  979.        
  980.         protected static char[] m_illegalDirectoryCharacters = {'\\', ':', '*', '?', '"', '<', '>', '|'};
  981.        
  982.         public DirectoryString()
  983.         {
  984.             m_site = "";
  985.             m_separatedSite = new ArrayList();
  986.         }
  987.        
  988.         public DirectoryString(string directory, bool checkForIllegalChars)
  989.         {
  990.             m_site = directory;
  991.             m_checkForIllegalChars = checkForIllegalChars;
  992.             m_separatedSite = CreateSeparatedString(directory);
  993.         }
  994.        
  995.         private ArrayList CreateSeparatedString(string directory)
  996.         {
  997.             ArrayList list = new ArrayList();
  998.            
  999.             if (directory == null || directory.Length == 0) {
  1000.                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidDirectoryOnUrl"));
  1001.             }
  1002.            
  1003.             string[] separatedArray = directory.Split(m_separators);
  1004.            
  1005.             for (int index = 0; index < separatedArray.Length; ++index) {
  1006.                 if (separatedArray[index] == null || separatedArray[index].Equals("")) {
  1007.                     // this case is fine, we just ignore it the extra separators.
  1008.                 }
  1009.                 else if (separatedArray[index].Equals("*")) {
  1010.                     if (index != separatedArray.Length - 1) {
  1011.                         throw new ArgumentException(Environment.GetResourceString("Argument_InvalidDirectoryOnUrl"));
  1012.                     }
  1013.                     list.Add(separatedArray[index]);
  1014.                 }
  1015.                 else if (m_checkForIllegalChars && separatedArray[index].IndexOfAny(m_illegalDirectoryCharacters) != -1) {
  1016.                     throw new ArgumentException(Environment.GetResourceString("Argument_InvalidDirectoryOnUrl"));
  1017.                 }
  1018.                 else {
  1019.                     list.Add(separatedArray[index]);
  1020.                 }
  1021.             }
  1022.            
  1023.             return list;
  1024.         }
  1025.        
  1026.         public virtual bool IsSubsetOf(DirectoryString operand)
  1027.         {
  1028.             return this.IsSubsetOf(operand, true);
  1029.         }
  1030.        
  1031.         public virtual bool IsSubsetOf(DirectoryString operand, bool ignoreCase)
  1032.         {
  1033.             if (operand == null) {
  1034.                 return false;
  1035.             }
  1036.             else if (operand.m_separatedSite.Count == 0) {
  1037.                 return this.m_separatedSite.Count == 0 || this.m_separatedSite.Count > 0 && String.Compare((string)this.m_separatedSite[0], "*", StringComparison.Ordinal) == 0;
  1038.             }
  1039.             else if (this.m_separatedSite.Count == 0) {
  1040.                 return String.Compare((string)operand.m_separatedSite[0], "*", StringComparison.Ordinal) == 0;
  1041.             }
  1042.             else {
  1043.                 return base.IsSubsetOf(operand, ignoreCase);
  1044.             }
  1045.         }
  1046.     }
  1047.    
  1048.     #if !PLATFORM_UNIX
  1049.     [Serializable()]
  1050.     internal class LocalSiteString : SiteString
  1051.     {
  1052.         private static new char[] m_separators = {'/'};
  1053.        
  1054.         public LocalSiteString(string site)
  1055.         {
  1056.             m_site = site.Replace('|', ':');
  1057.            
  1058.             if (m_site.Length > 2 && m_site.IndexOf(':') != -1)
  1059.                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidDirectoryOnUrl"));
  1060.            
  1061.             m_separatedSite = CreateSeparatedString(m_site);
  1062.         }
  1063.        
  1064.         private ArrayList CreateSeparatedString(string directory)
  1065.         {
  1066.             ArrayList list = new ArrayList();
  1067.            
  1068.             if (directory == null || directory.Length == 0) {
  1069.                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidDirectoryOnUrl"));
  1070.             }
  1071.            
  1072.             string[] separatedArray = directory.Split(m_separators);
  1073.            
  1074.             for (int index = 0; index < separatedArray.Length; ++index) {
  1075.                 if (separatedArray[index] == null || separatedArray[index].Equals("")) {
  1076.                     if (index < 2 && directory[index] == '/') {
  1077.                         list.Add('/');
  1078.                     }
  1079.                     else if (index != separatedArray.Length - 1) {
  1080.                         throw new ArgumentException(Environment.GetResourceString("Argument_InvalidDirectoryOnUrl"));
  1081.                     }
  1082.                 }
  1083.                 else if (separatedArray[index].Equals("*")) {
  1084.                     if (index != separatedArray.Length - 1) {
  1085.                         throw new ArgumentException(Environment.GetResourceString("Argument_InvalidDirectoryOnUrl"));
  1086.                     }
  1087.                     list.Add(separatedArray[index]);
  1088.                 }
  1089.                 else {
  1090.                     list.Add(separatedArray[index]);
  1091.                 }
  1092.             }
  1093.            
  1094.             return list;
  1095.         }
  1096.        
  1097.         public virtual bool IsSubsetOf(LocalSiteString operand)
  1098.         {
  1099.             return this.IsSubsetOf(operand, true);
  1100.         }
  1101.        
  1102.         public virtual bool IsSubsetOf(LocalSiteString operand, bool ignoreCase)
  1103.         {
  1104.             if (operand == null) {
  1105.                 return false;
  1106.             }
  1107.             else if (operand.m_separatedSite.Count == 0) {
  1108.                 return this.m_separatedSite.Count == 0 || this.m_separatedSite.Count > 0 && String.Compare((string)this.m_separatedSite[0], "*", StringComparison.Ordinal) == 0;
  1109.             }
  1110.             else if (this.m_separatedSite.Count == 0) {
  1111.                 return String.Compare((string)operand.m_separatedSite[0], "*", StringComparison.Ordinal) == 0;
  1112.             }
  1113.             else {
  1114.                 return base.IsSubsetOf(operand, ignoreCase);
  1115.             }
  1116.         }
  1117.     }
  1118.     #endif // !PLATFORM_UNIX
  1119. }

Developer Fusion