The Labs \ Source Viewer \ SSCLI \ System.Net \ CookieVariant

  1. //------------------------------------------------------------------------------
  2. // <copyright file="cookie.cs" company="Microsoft">
  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. // </copyright>
  14. //------------------------------------------------------------------------------
  15. namespace System.Net
  16. {
  17.     using System.Collections;
  18.     using System.Globalization;
  19.     using System.Threading;
  20.    
  21.     internal enum CookieVariant
  22.     {
  23.         Unknown,
  24.         Plain,
  25.         Rfc2109,
  26.         Rfc2965,
  27.         Default = Rfc2109
  28.     }
  29.    
  30.     //
  31.     // Cookie class
  32.     //
  33.     // Adheres to RFC 2965
  34.     //
  35.     // Currently, only client-side cookies. The cookie classes know how to
  36.     // parse a set-cookie format string, but not a cookie format string
  37.     // (e.g. "Cookie: $Version=1; name=value; $Path=/foo; $Secure")
  38.     //
  39.    
  40.     /// <devdoc>
  41.     /// <para>[To be supplied.]</para>
  42.     /// </devdoc>
  43.     [Serializable()]
  44.     public sealed class Cookie
  45.     {
  46.        
  47.         internal const int MaxSupportedVersion = 1;
  48.         internal const string CommentAttributeName = "Comment";
  49.         internal const string CommentUrlAttributeName = "CommentURL";
  50.         internal const string DiscardAttributeName = "Discard";
  51.         internal const string DomainAttributeName = "Domain";
  52.         internal const string ExpiresAttributeName = "Expires";
  53.         internal const string MaxAgeAttributeName = "Max-Age";
  54.         internal const string PathAttributeName = "Path";
  55.         internal const string PortAttributeName = "Port";
  56.         internal const string SecureAttributeName = "Secure";
  57.         internal const string VersionAttributeName = "Version";
  58.         internal const string HttpOnlyAttributeName = "HttpOnly";
  59.        
  60.         internal const string SeparatorLiteral = "; ";
  61.         internal const string EqualsLiteral = "=";
  62.         internal const string QuotesLiteral = "\"";
  63.         internal const string SpecialAttributeLiteral = "$";
  64.        
  65.         static internal readonly char[] PortSplitDelimiters = new char[] {' ', ',', '"'};
  66.         static internal readonly char[] Reserved2Name = new char[] {' ', '\t', '\r', '\n', '=', ';', ','};
  67.         static internal readonly char[] Reserved2Value = new char[] {';', ','};
  68.         private static Comparer staticComparer = new Comparer();
  69.        
  70.         // fields
  71.        
  72.         string m_comment = string.Empty;
  73.         Uri m_commentUri = null;
  74.         CookieVariant m_cookieVariant = CookieVariant.Plain;
  75.         bool m_discard = false;
  76.         string m_domain = string.Empty;
  77.         bool m_domain_implicit = true;
  78.         DateTime m_expires = DateTime.MinValue;
  79.         string m_name = string.Empty;
  80.         string m_path = string.Empty;
  81.         bool m_path_implicit = true;
  82.         string m_port = string.Empty;
  83.         bool m_port_implicit = true;
  84.         int[] m_port_list = null;
  85.         bool m_secure = false;
  86.         [System.Runtime.Serialization.OptionalField()]
  87.         bool m_httpOnly = false;
  88.         DateTime m_timeStamp = DateTime.Now;
  89.         string m_value = string.Empty;
  90.         int m_version = 0;
  91.        
  92.         string m_domainKey = string.Empty;
  93.         internal bool IsQuotedVersion = false;
  94.         internal bool IsQuotedDomain = false;
  95.        
  96.        
  97.         // constructors
  98.        
  99.         /// <devdoc>
  100.         /// <para>[To be supplied.]</para>
  101.         /// </devdoc>
  102.         public Cookie()
  103.         {
  104.         }
  105.        
  106.        
  107.         //public Cookie(string cookie) {
  108.         // if ((cookie == null) || (cookie == String.Empty)) {
  109.         // throw new ArgumentException("cookie");
  110.         // }
  111.         // Parse(cookie.Trim());
  112.         // Validate();
  113.         //}
  114.        
  115.         /// <devdoc>
  116.         /// <para>[To be supplied.]</para>
  117.         /// </devdoc>
  118.         public Cookie(string name, string value)
  119.         {
  120.             Name = name;
  121.             m_value = value;
  122.         }
  123.        
  124.         /// <devdoc>
  125.         /// <para>[To be supplied.]</para>
  126.         /// </devdoc>
  127.         public Cookie(string name, string value, string path) : this(name, value)
  128.         {
  129.             Path = path;
  130.         }
  131.        
  132.         /// <devdoc>
  133.         /// <para>[To be supplied.]</para>
  134.         /// </devdoc>
  135.         public Cookie(string name, string value, string path, string domain) : this(name, value, path)
  136.         {
  137.             Domain = domain;
  138.         }
  139.        
  140.         // properties
  141.        
  142.         /// <devdoc>
  143.         /// <para>[To be supplied.]</para>
  144.         /// </devdoc>
  145.         public string Comment {
  146.             get { return m_comment; }
  147.             set {
  148.                 if (value == null) {
  149.                     value = string.Empty;
  150.                 }
  151.                 m_comment = value;
  152.             }
  153.         }
  154.        
  155.         /// <devdoc>
  156.         /// <para>[To be supplied.]</para>
  157.         /// </devdoc>
  158.         public Uri CommentUri {
  159.             get { return m_commentUri; }
  160.             set { m_commentUri = value; }
  161.         }
  162.        
  163.        
  164.         public bool HttpOnly {
  165.             get { return m_httpOnly; }
  166.             set { m_httpOnly = value; }
  167.         }
  168.        
  169.        
  170.         /// <devdoc>
  171.         /// <para>[To be supplied.]</para>
  172.         /// </devdoc>
  173.         public bool Discard {
  174.             get { return m_discard; }
  175.             set { m_discard = value; }
  176.         }
  177.        
  178.         /// <devdoc>
  179.         /// <para>[To be supplied.]</para>
  180.         /// </devdoc>
  181.         public string Domain {
  182.             get { return m_domain; }
  183.             set {
  184.                 m_domain = (value == null ? String.Empty : value);
  185.                 m_domain_implicit = false;
  186.                 m_domainKey = string.Empty;
  187.                 //this will get it value when adding into the Container.
  188.             }
  189.         }
  190.        
  191.         private string _Domain {
  192.             get { return (Plain || m_domain_implicit || (m_domain.Length == 0)) ? string.Empty : (SpecialAttributeLiteral + DomainAttributeName + EqualsLiteral + (IsQuotedDomain ? "\"" : string.Empty) + m_domain + (IsQuotedDomain ? "\"" : string.Empty)); }
  193.         }
  194.        
  195.         /// <devdoc>
  196.         /// <para>[To be supplied.]</para>
  197.         /// </devdoc>
  198.         public bool Expired {
  199.             get { return (m_expires <= DateTime.Now) && (m_expires != DateTime.MinValue); }
  200.             set {
  201.                 if (value == true) {
  202.                     m_expires = DateTime.Now;
  203.                 }
  204.             }
  205.         }
  206.        
  207.         /// <devdoc>
  208.         /// <para>[To be supplied.]</para>
  209.         /// </devdoc>
  210.         public DateTime Expires {
  211.             get { return m_expires; }
  212.             set { m_expires = value; }
  213.         }
  214.        
  215.         /// <devdoc>
  216.         /// <para>[To be supplied.]</para>
  217.         /// </devdoc>
  218.         public string Name {
  219.             get { return m_name; }
  220.             set {
  221.                 if (ValidationHelper.IsBlankString(value) || !InternalSetName(value)) {
  222.                     throw new CookieException(SR.GetString(SR.net_cookie_attribute, "Name", value == null ? "<null>" : value));
  223.                 }
  224.             }
  225.         }
  226.        
  227.         internal bool InternalSetName(string value)
  228.         {
  229.             if (ValidationHelper.IsBlankString(value) || value[0] == '$' || value.IndexOfAny(Reserved2Name) != -1) {
  230.                 m_name = string.Empty;
  231.                 return false;
  232.             }
  233.             m_name = value;
  234.             return true;
  235.         }
  236.        
  237.         /// <devdoc>
  238.         /// <para>[To be supplied.]</para>
  239.         /// </devdoc>
  240.         public string Path {
  241.             get { return m_path; }
  242.             set {
  243.                 m_path = (value == null ? String.Empty : value);
  244.                 m_path_implicit = false;
  245.             }
  246.         }
  247.        
  248.         private string _Path {
  249.             get { return (Plain || m_path_implicit || (m_path.Length == 0)) ? string.Empty : (SpecialAttributeLiteral + PathAttributeName + EqualsLiteral + m_path); }
  250.         }
  251.        
  252.         internal bool Plain {
  253.             get { return Variant == CookieVariant.Plain; }
  254.         }
  255.        
  256.        
  257.         //
  258.         // According to spec we must assume default values for attributes but still
  259.         // keep in mind that we must not include them into the requests.
  260.         // We also check the validiy of all attributes based on the version and variant (read RFC)
  261.         //
  262.         // To work properly this function must be called after cookie construction with
  263.         // default (response) URI AND set_default == true
  264.         //
  265.         // Afterwards, the function can be called many times with other URIs and
  266.         // set_default == false to check whether this cookie matches given uri
  267.         //
  268.        
  269.         internal bool VerifySetDefaults(CookieVariant variant, Uri uri, bool isLocalDomain, string localDomain, bool set_default, bool isThrow)
  270.         {
  271.            
  272.             string host = uri.Host;
  273.             int port = uri.Port;
  274.             string path = uri.AbsolutePath;
  275.             bool valid = true;
  276.            
  277.             if (set_default) {
  278.                 // Set Variant. If version is zero => reset cookie to Version0 style
  279.                 if (Version == 0) {
  280.                     variant = CookieVariant.Plain;
  281.                 }
  282.                 else if (Version == 1 && variant == CookieVariant.Unknown) {
  283.                     //since we don't expose Variant to an app, set it to Default
  284.                     variant = CookieVariant.Default;
  285.                 }
  286.                 m_cookieVariant = variant;
  287.             }
  288.            
  289.             //Check the name
  290.             if (m_name == null || m_name.Length == 0 || m_name[0] == '$' || m_name.IndexOfAny(Reserved2Name) != -1) {
  291.                 if (isThrow) {
  292.                     throw new CookieException(SR.GetString(SR.net_cookie_attribute, "Name", m_name == null ? "<null>" : m_name));
  293.                 }
  294.                 return false;
  295.             }
  296.            
  297.             //Check the value
  298.             if (m_value == null || (!(m_value.Length > 2 && m_value[0] == '"' && m_value[m_value.Length - 1] == '"') && m_value.IndexOfAny(Reserved2Value) != -1)) {
  299.                 if (isThrow) {
  300.                     throw new CookieException(SR.GetString(SR.net_cookie_attribute, "Value", m_value == null ? "<null>" : m_value));
  301.                 }
  302.                 return false;
  303.             }
  304.            
  305.             //Check Comment syntax
  306.             if (Comment != null && !(Comment.Length > 2 && Comment[0] == '"' && Comment[Comment.Length - 1] == '"') && (Comment.IndexOfAny(Reserved2Value) != -1)) {
  307.                 if (isThrow)
  308.                     throw new CookieException(SR.GetString(SR.net_cookie_attribute, CommentAttributeName, Comment));
  309.                 return false;
  310.             }
  311.            
  312.             //Check Path syntax
  313.             if (Path != null && !(Path.Length > 2 && Path[0] == '"' && Path[Path.Length - 1] == '"') && (Path.IndexOfAny(Reserved2Value) != -1)) {
  314.                 if (isThrow) {
  315.                     throw new CookieException(SR.GetString(SR.net_cookie_attribute, PathAttributeName, Path));
  316.                 }
  317.                 return false;
  318.             }
  319.            
  320.             //Check/set domain
  321.             // if domain is implicit => assume a) uri is valid, b) just set domain to uri hostname
  322.             if (set_default && m_domain_implicit == true) {
  323.                 m_domain = host;
  324.             }
  325.             else {
  326.                 if (!m_domain_implicit) {
  327.                     // Forwarding note: If Uri.Host is of IP address form then the only supported case
  328.                     // is for IMPLICIT domain property of a cookie.
  329.                     // The below code (explicit cookie.Domain value) will try to parse Uri.Host IP string
  330.                     // as a fqdn and reject the cookie
  331.                    
  332.                     //aliasing since we might need the KeyValue (but not the original one)
  333.                     string domain = m_domain;
  334.                    
  335.                     //Syntax check for Domain charset plus empty string
  336.                     if (!DomainCharsTest(domain)) {
  337.                         if (isThrow) {
  338.                             throw new CookieException(SR.GetString(SR.net_cookie_attribute, DomainAttributeName, domain == null ? "<null>" : domain));
  339.                         }
  340.                         return false;
  341.                     }
  342.                    
  343.                     //domain must start with '.' if set explicitly
  344.                     if (domain[0] != '.') {
  345.                         if (!(variant == CookieVariant.Rfc2965 || variant == CookieVariant.Plain)) {
  346.                             if (isThrow) {
  347.                                 throw new CookieException(SR.GetString(SR.net_cookie_attribute, DomainAttributeName, m_domain));
  348.                             }
  349.                             return false;
  350.                         }
  351.                         domain = '.' + domain;
  352.                     }
  353.                    
  354.                     int host_dot = host.IndexOf('.');
  355.                     bool is_local = false;
  356.                    
  357.                     // First quick check is for pushing a cookie into the local domain
  358.                     if (isLocalDomain && (string.Compare(localDomain, domain, StringComparison.OrdinalIgnoreCase) == 0)) {
  359.                         valid = true;
  360.                     }
  361.                     else if (domain.Length < 4 || (!(is_local = (string.Compare(domain, ".local", StringComparison.OrdinalIgnoreCase)) == 0) && domain.IndexOf('.', 1, domain.Length - 2) == -1)) {
  362.                         // explicit domains must contain 'inside' dots or be ".local"
  363.                         valid = false;
  364.                     }
  365.                     else if (is_local && isLocalDomain) {
  366.                         // valid ".local" found for the local domain Uri
  367.                         valid = true;
  368.                     }
  369.                     else if (is_local && !isLocalDomain) {
  370.                         // ".local" cannot happen on non-local domain
  371.                         valid = false;
  372.                     }
  373.                     else if (variant == CookieVariant.Plain) {
  374.                         // We distiguish between Version0 cookie and other versions on domain issue
  375.                         // According to Version0 spec a domain must be just a substring of the hostname
  376.                        
  377.                         if (!(host.Length + 1 == domain.Length && string.Compare(host, 0, domain, 1, host.Length, StringComparison.OrdinalIgnoreCase) == 0)) {
  378.                             if (host.Length <= domain.Length || string.Compare(host, host.Length - domain.Length, domain, 0, domain.Length, StringComparison.OrdinalIgnoreCase) != 0) {
  379.                                 valid = false;
  380.                             }
  381.                         }
  382.                     }
  383.                     else if (!is_local && (host_dot == -1 || domain.Length != host.Length - host_dot || string.Compare(host, host_dot, domain, 0, domain.Length, StringComparison.OrdinalIgnoreCase) != 0)) {
  384.                         //starting the first dot, the host must match the domain
  385.                         valid = false;
  386.                     }
  387.                    
  388.                     if (valid) {
  389.                         if (is_local) {
  390.                             m_domainKey = localDomain.ToLower(CultureInfo.InvariantCulture);
  391.                         }
  392.                         else {
  393.                             m_domainKey = domain.ToLower(CultureInfo.InvariantCulture);
  394.                         }
  395.                     }
  396.                    
  397.                 }
  398.                 else {
  399.                     // for implicitly set domain AND at the set_default==false time
  400.                     // we simply got to match uri.Host against m_domain
  401.                     if (string.Compare(host, m_domain, StringComparison.OrdinalIgnoreCase) != 0) {
  402.                         valid = false;
  403.                     }
  404.                    
  405.                 }
  406.                 if (!valid) {
  407.                     if (isThrow) {
  408.                         throw new CookieException(SR.GetString(SR.net_cookie_attribute, DomainAttributeName, m_domain));
  409.                     }
  410.                     return false;
  411.                 }
  412.             }
  413.            
  414.            
  415.             //Check/Set Path
  416.            
  417.             if (set_default && m_path_implicit == true) {
  418.                 //assuming that uri path is always valid and contains at least one '/'
  419.                 switch (m_cookieVariant) {
  420.                     case CookieVariant.Plain:
  421.                         m_path = path;
  422.                         break;
  423.                     case CookieVariant.Rfc2109:
  424.                         m_path = path.Substring(0, path.LastIndexOf('/'));
  425.                         //may be empty
  426.                         break;
  427.                     case CookieVariant.Rfc2965:
  428.                     default:
  429.                        
  430.                         //hope future versions will have same 'Path' semantic?
  431.                         m_path = path.Substring(0, path.LastIndexOf('/') + 1);
  432.                         break;
  433.                    
  434.                 }
  435.             }
  436.             else {
  437.                 //check current path (implicit/explicit) against given uri
  438.                 if (!path.StartsWith(CookieParser.CheckQuoted(m_path))) {
  439.                     if (isThrow) {
  440.                         throw new CookieException(SR.GetString(SR.net_cookie_attribute, PathAttributeName, m_path));
  441.                     }
  442.                     return false;
  443.                 }
  444.             }
  445.            
  446.             // set the default port if Port attribute was present but had no value
  447.             if (set_default && (m_port_implicit == false && m_port.Length == 0)) {
  448.                 m_port_list = new int[1] {port};
  449.             }
  450.            
  451.             if (m_port_implicit == false) {
  452.                 // Port must match agaist the one from the uri
  453.                 valid = false;
  454.                 foreach (int p in m_port_list) {
  455.                     if (p == port) {
  456.                         valid = true;
  457.                         break;
  458.                     }
  459.                 }
  460.                 if (!valid) {
  461.                     if (isThrow) {
  462.                         throw new CookieException(SR.GetString(SR.net_cookie_attribute, PortAttributeName, m_port));
  463.                     }
  464.                     return false;
  465.                 }
  466.             }
  467.             return true;
  468.         }
  469.        
  470.         //Very primitive test to make sure that the name does not have illegal characters
  471.         // As per RFC 952 (relaxed on first char could be a digit and string can have '_')
  472.         private static bool DomainCharsTest(string name)
  473.         {
  474.             if (name == null || name.Length == 0) {
  475.                 return false;
  476.             }
  477.             for (int i = 0; i < name.Length; ++i) {
  478.                 char ch = name[i];
  479.                 if (!((ch >= '0' && ch <= '9') || (ch == '.' || ch == '-') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch == '_')))
  480.                     return false;
  481.             }
  482.             return true;
  483.         }
  484.        
  485.         /// <devdoc>
  486.         /// <para>[To be supplied.]</para>
  487.         /// </devdoc>
  488.         public string Port {
  489.             get { return m_port; }
  490.             set {
  491.                 m_port_implicit = false;
  492.                 if ((value == null || value.Length == 0)) {
  493.                     //"Port" is present but has no value.
  494.                     m_port = string.Empty;
  495.                 }
  496.                 else {
  497.                     m_port = value;
  498.                     // Parse port list
  499.                     if (value[0] != '"' || value[value.Length - 1] != '"') {
  500.                         throw new CookieException(SR.GetString(SR.net_cookie_attribute, PortAttributeName, m_port));
  501.                     }
  502.                     string[] ports = value.Split(PortSplitDelimiters);
  503.                     m_port_list = new int[ports.Length];
  504.                     for (int i = 0; i < ports.Length; ++i) {
  505.                         m_port_list[i] = -1;
  506.                         if (ports[i].Length == 0) {
  507.                             //ignore spaces this way, and leave port=-1 in the slot
  508.                             continue;
  509.                         }
  510.                         if (!Int32.TryParse(ports[i], out m_port_list[i]))
  511.                             throw new CookieException(SR.GetString(SR.net_cookie_attribute, PortAttributeName, m_port));
  512.                     }
  513.                     m_version = MaxSupportedVersion;
  514.                     m_cookieVariant = CookieVariant.Rfc2965;
  515.                 }
  516.             }
  517.         }
  518.        
  519.        
  520.         internal int[] PortList {
  521. //It must be null if Port Attribute was ommitted in the response
  522.             get { return m_port_list; }
  523.         }
  524.        
  525.         private string _Port {
  526.             get { return m_port_implicit ? string.Empty : (SpecialAttributeLiteral + PortAttributeName + ((m_port.Length == 0) ? string.Empty : (EqualsLiteral + m_port))); }
  527.         }
  528.        
  529.         /// <devdoc>
  530.         /// <para>[To be supplied.]</para>
  531.         /// </devdoc>
  532.         public bool Secure {
  533.             get { return m_secure; }
  534.             set { m_secure = value; }
  535.         }
  536.        
  537.         /// <devdoc>
  538.         /// <para>[To be supplied.]</para>
  539.         /// </devdoc>
  540.         public DateTime TimeStamp {
  541.             get { return m_timeStamp; }
  542.         }
  543.        
  544.         /// <devdoc>
  545.         /// <para>[To be supplied.]</para>
  546.         /// </devdoc>
  547.         public string Value {
  548.             get { return m_value; }
  549.             set { m_value = (value == null ? String.Empty : value); }
  550.         }
  551.        
  552.         internal CookieVariant Variant {
  553.             get { return m_cookieVariant; }
  554.             set {
  555.                 // only set by HttpListenerRequest::Cookies_get()
  556.                 GlobalLog.Assert(value == CookieVariant.Rfc2965, "Cookie#{0}::set_Variant()|value:{1}", ValidationHelper.HashString(this), value);
  557.                 m_cookieVariant = value;
  558.             }
  559.         }
  560.        
  561.         // m_domainKey member is set internally in VerifySetDefaults()
  562.         // If it is not set then verification function was not called
  563.         // and this should never happen
  564.         internal string DomainKey {
  565.             get { return m_domain_implicit ? Domain : m_domainKey; }
  566.         }
  567.        
  568.        
  569.         //public Version Version {
  570.         /// <devdoc>
  571.         /// <para>[To be supplied.]</para>
  572.         /// </devdoc>
  573.         public int Version {
  574.             get { return m_version; }
  575.             set {
  576.                 if (value < 0) {
  577.                     throw new ArgumentOutOfRangeException("value");
  578.                 }
  579.                 m_version = value;
  580.                 if (value > 0 && m_cookieVariant < CookieVariant.Rfc2109) {
  581.                     m_cookieVariant = CookieVariant.Rfc2109;
  582.                 }
  583.             }
  584.         }
  585.        
  586.         private string _Version {
  587.             get { return (Version == 0) ? string.Empty : (SpecialAttributeLiteral + VersionAttributeName + EqualsLiteral + (IsQuotedVersion ? "\"" : string.Empty) + m_version.ToString(NumberFormatInfo.InvariantInfo) + (IsQuotedVersion ? "\"" : string.Empty)); }
  588.         }
  589.        
  590.         // methods
  591.        
  592.        
  593.         static internal IComparer GetComparer()
  594.         {
  595.             //the class don't have any members, it's safe reuse the instance
  596.             return staticComparer;
  597.         }
  598.        
  599.        
  600.         /// <devdoc>
  601.         /// <para>[To be supplied.]</para>
  602.         /// </devdoc>
  603.         public override bool Equals(object comparand)
  604.         {
  605.             if (!(comparand is Cookie)) {
  606.                 return false;
  607.             }
  608.            
  609.             Cookie other = (Cookie)comparand;
  610.            
  611.             return (string.Compare(Name, other.Name, StringComparison.OrdinalIgnoreCase) == 0) && (string.Compare(Value, other.Value, StringComparison.Ordinal) == 0) && (string.Compare(Path, other.Path, StringComparison.Ordinal) == 0) && (string.Compare(Domain, other.Domain, StringComparison.OrdinalIgnoreCase) == 0) && (Version == other.Version);
  612.         }
  613.        
  614.         /// <devdoc>
  615.         /// <para>[To be supplied.]</para>
  616.         /// </devdoc>
  617.         public override int GetHashCode()
  618.         {
  619.             //
  620.             //string hashString = Name + "=" + Value + ";" + Path + "; " + Domain + "; " + Version;
  621.             //int hash = 0;
  622.             //
  623.             //foreach (char ch in hashString) {
  624.             // hash = unchecked(hash << 1 ^ (int)ch);
  625.             //}
  626.             //return hash;
  627.             return (Name + "=" + Value + ";" + Path + "; " + Domain + "; " + Version).GetHashCode();
  628.         }
  629.        
  630.         /// <devdoc>
  631.         /// <para>[To be supplied.]</para>
  632.         /// </devdoc>
  633.         public override string ToString()
  634.         {
  635.            
  636.             string domain = _Domain;
  637.             string path = _Path;
  638.             string port = _Port;
  639.             string version = _Version;
  640.            
  641.             string result = ((version.Length == 0) ? string.Empty : (version + SeparatorLiteral)) + Name + EqualsLiteral + Value + ((path.Length == 0) ? string.Empty : (SeparatorLiteral + path)) + ((domain.Length == 0) ? string.Empty : (SeparatorLiteral + domain)) + ((port.Length == 0) ? string.Empty : (SeparatorLiteral + port));
  642.             if (result == "=") {
  643.                 return string.Empty;
  644.             }
  645.             return result;
  646.         }
  647.        
  648.         internal string ToServerString()
  649.         {
  650.             string result = Name + EqualsLiteral + Value;
  651.             if (m_comment != null && m_comment.Length > 0) {
  652.                 result += SeparatorLiteral + CommentAttributeName + EqualsLiteral + m_comment;
  653.             }
  654.             if (m_commentUri != null) {
  655.                 result += SeparatorLiteral + CommentUrlAttributeName + EqualsLiteral + QuotesLiteral + m_commentUri.ToString() + QuotesLiteral;
  656.             }
  657.             if (m_discard) {
  658.                 result += SeparatorLiteral + DiscardAttributeName;
  659.             }
  660.             if (!Plain && !m_domain_implicit && m_domain != null && m_domain.Length > 0) {
  661.                 result += SeparatorLiteral + DomainAttributeName + EqualsLiteral + m_domain;
  662.             }
  663.             int seconds = (Expires - DateTime.UtcNow).Seconds;
  664.             if (seconds > 0) {
  665.                 result += SeparatorLiteral + MaxAgeAttributeName + EqualsLiteral + seconds.ToString(NumberFormatInfo.InvariantInfo);
  666.             }
  667.             if (!Plain && !m_path_implicit && m_path != null && m_path.Length > 0) {
  668.                 result += SeparatorLiteral + PathAttributeName + EqualsLiteral + m_path;
  669.             }
  670.             if (!Plain && !m_port_implicit && m_port != null && m_port.Length > 0) {
  671.                 // QuotesLiteral are included in m_port
  672.                 result += SeparatorLiteral + PortAttributeName + EqualsLiteral + m_port;
  673.             }
  674.             if (m_version > 0) {
  675.                 result += SeparatorLiteral + VersionAttributeName + EqualsLiteral + m_version.ToString(NumberFormatInfo.InvariantInfo);
  676.             }
  677.             return result == EqualsLiteral ? null : result;
  678.         }
  679.        
  680.         //private void Validate() {
  681.         // if ((m_name == String.Empty) && (m_value == String.Empty)) {
  682.         // throw new CookieException();
  683.         // }
  684.         // if ((m_name != String.Empty) && (m_name[0] == '$')) {
  685.         // throw new CookieException();
  686.         // }
  687.         //}
  688.        
  689.         #if DEBUG
  690.         /// <devdoc>
  691.         /// <para>[To be supplied.]</para>
  692.         /// </devdoc>
  693.         internal void Dump()
  694.         {
  695.             GlobalLog.Print("Cookie: " + ToString() + "->\n" + "\tComment = " + Comment + "\n" + "\tCommentUri = " + CommentUri + "\n" + "\tDiscard = " + Discard + "\n" + "\tDomain = " + Domain + "\n" + "\tExpired = " + Expired + "\n" + "\tExpires = " + Expires + "\n" + "\tName = " + Name + "\n" + "\tPath = " + Path + "\n" + "\tPort = " + Port + "\n" + "\tSecure = " + Secure + "\n" + "\tTimeStamp = " + TimeStamp + "\n" + "\tValue = " + Value + "\n" + "\tVariant = " + Variant + "\n" + "\tVersion = " + Version + "\n" + "\tHttpOnly = " + HttpOnly + "\n");
  696.         }
  697.         #endif
  698.     }
  699.    
  700.     internal enum CookieToken
  701.     {
  702.        
  703.         // state types
  704.        
  705.         Nothing,
  706.         NameValuePair,
  707.         // X=Y
  708.         Attribute,
  709.         // X
  710.         EndToken,
  711.         // ';'
  712.         EndCookie,
  713.         // ','
  714.         End,
  715.         // EOLN
  716.         Equals,
  717.        
  718.         // value types
  719.        
  720.         Comment,
  721.         CommentUrl,
  722.         CookieName,
  723.         Discard,
  724.         Domain,
  725.         Expires,
  726.         MaxAge,
  727.         Path,
  728.         Port,
  729.         Secure,
  730.         HttpOnly,
  731.         Unknown,
  732.         Version
  733.     }
  734.    
  735.     //
  736.     // CookieTokenizer
  737.     //
  738.     // Used to split a single or multi-cookie (header) string into individual
  739.     // tokens
  740.     //
  741.    
  742.     internal class CookieTokenizer
  743.     {
  744.        
  745.         // fields
  746.        
  747.         bool m_eofCookie;
  748.         int m_index;
  749.         int m_length;
  750.         string m_name;
  751.         bool m_quoted;
  752.         int m_start;
  753.         CookieToken m_token;
  754.         int m_tokenLength;
  755.         string m_tokenStream;
  756.         string m_value;
  757.        
  758.         // constructors
  759.        
  760.         internal CookieTokenizer(string tokenStream)
  761.         {
  762.             m_length = tokenStream.Length;
  763.             m_tokenStream = tokenStream;
  764.         }
  765.        
  766.         // properties
  767.        
  768.         internal bool EndOfCookie {
  769.             get { return m_eofCookie; }
  770.             set { m_eofCookie = value; }
  771.         }
  772.        
  773.         internal bool Eof {
  774.             get { return m_index >= m_length; }
  775.         }
  776.        
  777.         internal string Name {
  778.             get { return m_name; }
  779.             set { m_name = value; }
  780.         }
  781.        
  782.         internal bool Quoted {
  783.             get { return m_quoted; }
  784.             set { m_quoted = value; }
  785.         }
  786.        
  787.         internal CookieToken Token {
  788.             get { return m_token; }
  789.             set { m_token = value; }
  790.         }
  791.        
  792.         internal string Value {
  793.             get { return m_value; }
  794.             set { m_value = value; }
  795.         }
  796.        
  797.         // methods
  798.        
  799.         //
  800.         // Extract
  801.         //
  802.         // extract the current token
  803.         //
  804.        
  805.         internal string Extract()
  806.         {
  807.            
  808.             string tokenString = string.Empty;
  809.            
  810.             if (m_tokenLength != 0) {
  811.                 tokenString = m_tokenStream.Substring(m_start, m_tokenLength);
  812.                 if (!Quoted) {
  813.                     tokenString = tokenString.Trim();
  814.                 }
  815.             }
  816.             return tokenString;
  817.         }
  818.        
  819.         //
  820.         // FindNext
  821.         //
  822.         // Find the start and length of the next token. The token is terminated
  823.         // by one of:
  824.         //
  825.         // - end-of-line
  826.         // - end-of-cookie: unquoted comma separates multiple cookies
  827.         // - end-of-token: unquoted semi-colon
  828.         // - end-of-name: unquoted equals
  829.         //
  830.         // Inputs:
  831.         // <argument> ignoreComma
  832.         // true if parsing doesn't stop at a comma. This is only true when
  833.         // we know we're parsing an original cookie that has an expires=
  834.         // attribute, because the format of the time/date used in expires
  835.         // is:
  836.         // Wdy, dd-mmm-yyyy HH:MM:SS GMT
  837.         //
  838.         // <argument> ignoreEquals
  839.         // true if parsing doesn't stop at an equals sign. The LHS of the
  840.         // first equals sign is an attribute name. The next token may
  841.         // include one or more equals signs. E.g.,
  842.         //
  843.         // SESSIONID=ID=MSNx45&q=33
  844.         //
  845.         // Outputs:
  846.         // <member> m_index
  847.         // incremented to the last position in m_tokenStream contained by
  848.         // the current token
  849.         //
  850.         // <member> m_start
  851.         // incremented to the start of the current token
  852.         //
  853.         // <member> m_tokenLength
  854.         // set to the length of the current token
  855.         //
  856.         // Assumes:
  857.         // Nothing
  858.         //
  859.         // Returns:
  860.         // type of CookieToken found:
  861.         //
  862.         // End - end of the cookie string
  863.         // EndCookie - end of current cookie in (potentially) a
  864.         // multi-cookie string
  865.         // EndToken - end of name=value pair, or end of an attribute
  866.         // Equals - end of name=
  867.         //
  868.         // Throws:
  869.         // Nothing
  870.         //
  871.        
  872.         internal CookieToken FindNext(bool ignoreComma, bool ignoreEquals)
  873.         {
  874.             m_tokenLength = 0;
  875.             m_start = m_index;
  876.             while ((m_index < m_length) && Char.IsWhiteSpace(m_tokenStream[m_index])) {
  877.                 ++m_index;
  878.                 ++m_start;
  879.             }
  880.            
  881.             CookieToken token = CookieToken.End;
  882.             int increment = 1;
  883.            
  884.             if (!Eof) {
  885.                 if (m_tokenStream[m_index] == '"') {
  886.                     Quoted = true;
  887.                     ++m_index;
  888.                     bool quoteOn = false;
  889.                     while (m_index < m_length) {
  890.                         char currChar = m_tokenStream[m_index];
  891.                         if (!quoteOn && currChar == '"')
  892.                             break;
  893.                         if (quoteOn)
  894.                             quoteOn = false;
  895.                         else if (currChar == '\\')
  896.                             quoteOn = true;
  897.                         ++m_index;
  898.                     }
  899.                     if (m_index < m_length) {
  900.                         ++m_index;
  901.                     }
  902.                     m_tokenLength = m_index - m_start;
  903.                     increment = 0;
  904.                     // if we are here, reset ignoreComma
  905.                     // In effect, we ignore everything after quoted string till next delimiter
  906.                     ignoreComma = false;
  907.                 }
  908.                 while ((m_index < m_length) && (m_tokenStream[m_index] != ';') && (ignoreEquals || (m_tokenStream[m_index] != '=')) && (ignoreComma || (m_tokenStream[m_index] != ','))) {
  909.                    
  910.                     // Fixing 2 things:
  911.                     // 1) ignore day of week in cookie string
  912.                     // 2) revert ignoreComma once meet it, so won't miss the next cookie)
  913.                     if (m_tokenStream[m_index] == ',') {
  914.                         m_start = m_index + 1;
  915.                         m_tokenLength = -1;
  916.                         ignoreComma = false;
  917.                     }
  918.                     ++m_index;
  919.                     m_tokenLength += increment;
  920.                    
  921.                 }
  922.                 if (!Eof) {
  923.                     switch (m_tokenStream[m_index]) {
  924.                         case ';':
  925.                             token = CookieToken.EndToken;
  926.                             break;
  927.                         case '=':
  928.                            
  929.                             token = CookieToken.Equals;
  930.                             break;
  931.                         default:
  932.                            
  933.                             token = CookieToken.EndCookie;
  934.                             break;
  935.                     }
  936.                     ++m_index;
  937.                 }
  938.             }
  939.             return token;
  940.         }
  941.        
  942.         //
  943.         // Next
  944.         //
  945.         // Get the next cookie name/value or attribute
  946.         //
  947.         // Cookies come in the following formats:
  948.         //
  949.         // 1. Version0
  950.         // Set-Cookie: [<name>][=][<value>]
  951.         // [; expires=<date>]
  952.         // [; path=<path>]
  953.         // [; domain=<domain>]
  954.         // [; secure]
  955.         // Cookie: <name>=<value>
  956.         //
  957.         // Notes: <name> and/or <value> may be blank
  958.         // <date> is the RFC 822/1123 date format that
  959.         // incorporates commas, e.g.
  960.         // "Wednesday, 09-Nov-99 23:12:40 GMT"
  961.         //
  962.         // 2. RFC 2109
  963.         // Set-Cookie: 1#{
  964.         // <name>=<value>
  965.         // [; comment=<comment>]
  966.         // [; domain=<domain>]
  967.         // [; max-age=<seconds>]
  968.         // [; path=<path>]
  969.         // [; secure]
  970.         // ; Version=<version>
  971.         // }
  972.         // Cookie: $Version=<version>
  973.         // 1#{
  974.         // ; <name>=<value>
  975.         // [; path=<path>]
  976.         // [; domain=<domain>]
  977.         // }
  978.         //
  979.         // 3. RFC 2965
  980.         // Set-Cookie2: 1#{
  981.         // <name>=<value>
  982.         // [; comment=<comment>]
  983.         // [; commentURL=<comment>]
  984.         // [; discard]
  985.         // [; domain=<domain>]
  986.         // [; max-age=<seconds>]
  987.         // [; path=<path>]
  988.         // [; ports=<portlist>]
  989.         // [; secure]
  990.         // ; Version=<version>
  991.         // }
  992.         // Cookie: $Version=<version>
  993.         // 1#{
  994.         // ; <name>=<value>
  995.         // [; path=<path>]
  996.         // [; domain=<domain>]
  997.         // [; port="<port>"]
  998.         // }
  999.         // [Cookie2: $Version=<version>]
  1000.         //
  1001.         // Inputs:
  1002.         // <argument> first
  1003.         // true if this is the first name/attribute that we have looked for
  1004.         // in the cookie stream
  1005.         //
  1006.         // Outputs:
  1007.         //
  1008.         // Assumes:
  1009.         // Nothing
  1010.         //
  1011.         // Returns:
  1012.         // type of CookieToken found:
  1013.         //
  1014.         // - Attribute
  1015.         // - token was single-value. May be empty. Caller should check
  1016.         // Eof or EndCookie to determine if any more action needs to
  1017.         // be taken
  1018.         //
  1019.         // - NameValuePair
  1020.         // - Name and Value are meaningful. Either may be empty
  1021.         //
  1022.         // Throws:
  1023.         // Nothing
  1024.         //
  1025.        
  1026.         internal CookieToken Next(bool first, bool parseResponseCookies)
  1027.         {
  1028.            
  1029.             Reset();
  1030.            
  1031.             CookieToken terminator = FindNext(false, false);
  1032.             if (terminator == CookieToken.EndCookie) {
  1033.                 EndOfCookie = true;
  1034.             }
  1035.            
  1036.             if ((terminator == CookieToken.End) || (terminator == CookieToken.EndCookie)) {
  1037.                 if ((Name = Extract()).Length != 0) {
  1038.                     Token = TokenFromName(parseResponseCookies);
  1039.                     return CookieToken.Attribute;
  1040.                 }
  1041.                 return terminator;
  1042.             }
  1043.             Name = Extract();
  1044.             if (first) {
  1045.                 Token = CookieToken.CookieName;
  1046.             }
  1047.             else {
  1048.                 Token = TokenFromName(parseResponseCookies);
  1049.             }
  1050.             if (terminator == CookieToken.Equals) {
  1051.                 terminator = FindNext(!first && (Token == CookieToken.Expires), true);
  1052.                 if (terminator == CookieToken.EndCookie) {
  1053.                     EndOfCookie = true;
  1054.                 }
  1055.                 Value = Extract();
  1056.                 return CookieToken.NameValuePair;
  1057.             }
  1058.             else {
  1059.                 return CookieToken.Attribute;
  1060.             }
  1061.         }
  1062.        
  1063.         //
  1064.         // Reset
  1065.         //
  1066.         // set up this tokenizer for finding the next name/value pair or
  1067.         // attribute, or end-of-[token, cookie, or line]
  1068.         //
  1069.        
  1070.         internal void Reset()
  1071.         {
  1072.             m_eofCookie = false;
  1073.             m_name = string.Empty;
  1074.             m_quoted = false;
  1075.             m_start = m_index;
  1076.             m_token = CookieToken.Nothing;
  1077.             m_tokenLength = 0;
  1078.             m_value = string.Empty;
  1079.         }
  1080.        
  1081.         private struct RecognizedAttribute
  1082.         {
  1083.            
  1084.             string m_name;
  1085.             CookieToken m_token;
  1086.            
  1087.             internal RecognizedAttribute(string name, CookieToken token)
  1088.             {
  1089.                 m_name = name;
  1090.                 m_token = token;
  1091.             }
  1092.            
  1093.             internal CookieToken Token {
  1094.                 get { return m_token; }
  1095.             }
  1096.            
  1097.             internal bool IsEqualTo(string value)
  1098.             {
  1099.                 return string.Compare(m_name, value, StringComparison.OrdinalIgnoreCase) == 0;
  1100.             }
  1101.         }
  1102.        
  1103.         //
  1104.         // recognized attributes in order of expected commonality
  1105.         //
  1106.        
  1107.         static RecognizedAttribute[] RecognizedAttributes = {new RecognizedAttribute(Cookie.PathAttributeName, CookieToken.Path), new RecognizedAttribute(Cookie.MaxAgeAttributeName, CookieToken.MaxAge), new RecognizedAttribute(Cookie.ExpiresAttributeName, CookieToken.Expires), new RecognizedAttribute(Cookie.VersionAttributeName, CookieToken.Version), new RecognizedAttribute(Cookie.DomainAttributeName, CookieToken.Domain), new RecognizedAttribute(Cookie.SecureAttributeName, CookieToken.Secure), new RecognizedAttribute(Cookie.DiscardAttributeName, CookieToken.Discard), new RecognizedAttribute(Cookie.PortAttributeName, CookieToken.Port), new RecognizedAttribute(Cookie.CommentAttributeName, CookieToken.Comment), new RecognizedAttribute(Cookie.CommentUrlAttributeName, CookieToken.CommentUrl),
  1108.         new RecognizedAttribute(Cookie.HttpOnlyAttributeName, CookieToken.HttpOnly)};
  1109.        
  1110.         static RecognizedAttribute[] RecognizedServerAttributes = {new RecognizedAttribute('$' + Cookie.PathAttributeName, CookieToken.Path), new RecognizedAttribute('$' + Cookie.VersionAttributeName, CookieToken.Version), new RecognizedAttribute('$' + Cookie.DomainAttributeName, CookieToken.Domain), new RecognizedAttribute('$' + Cookie.PortAttributeName, CookieToken.Port), new RecognizedAttribute('$' + Cookie.HttpOnlyAttributeName, CookieToken.HttpOnly)};
  1111.        
  1112.         internal CookieToken TokenFromName(bool parseResponseCookies)
  1113.         {
  1114.             if (!parseResponseCookies) {
  1115.                 for (int i = 0; i < RecognizedServerAttributes.Length; ++i) {
  1116.                     if (RecognizedServerAttributes[i].IsEqualTo(Name)) {
  1117.                         return RecognizedServerAttributes[i].Token;
  1118.                     }
  1119.                 }
  1120.             }
  1121.             else {
  1122.                 for (int i = 0; i < RecognizedAttributes.Length; ++i) {
  1123.                     if (RecognizedAttributes[i].IsEqualTo(Name)) {
  1124.                         return RecognizedAttributes[i].Token;
  1125.                     }
  1126.                 }
  1127.             }
  1128.             return CookieToken.Unknown;
  1129.         }
  1130.     }
  1131.    
  1132.     //
  1133.     // CookieParser
  1134.     //
  1135.     // Takes a cookie header, makes cookies
  1136.     //
  1137.    
  1138.     internal class CookieParser
  1139.     {
  1140.        
  1141.         // fields
  1142.        
  1143.         CookieTokenizer m_tokenizer;
  1144.         Cookie m_savedCookie;
  1145.        
  1146.         // constructors
  1147.        
  1148.         internal CookieParser(string cookieString)
  1149.         {
  1150.             m_tokenizer = new CookieTokenizer(cookieString);
  1151.         }
  1152.        
  1153.         // properties
  1154.        
  1155.         // methods
  1156.        
  1157.         //
  1158.         // Get
  1159.         //
  1160.         // Gets the next cookie
  1161.         //
  1162.         // Inputs:
  1163.         // Nothing
  1164.         //
  1165.         // Outputs:
  1166.         // Nothing
  1167.         //
  1168.         // Assumes:
  1169.         // Nothing
  1170.         //
  1171.         // Returns:
  1172.         // new cookie object, or null if there's no more
  1173.         //
  1174.         // Throws:
  1175.         // Nothing
  1176.         //
  1177.        
  1178.         internal Cookie Get()
  1179.         {
  1180.            
  1181.             Cookie cookie = null;
  1182.            
  1183.             // only first ocurence of an attribute value must be counted
  1184.             bool commentSet = false;
  1185.             bool commentUriSet = false;
  1186.             bool domainSet = false;
  1187.             bool expiresSet = false;
  1188.             bool pathSet = false;
  1189.             bool portSet = false;
  1190.             //special case as it may have no value in header
  1191.             bool versionSet = false;
  1192.             bool secureSet = false;
  1193.             bool discardSet = false;
  1194.            
  1195.             do {
  1196.                 CookieToken token = m_tokenizer.Next(cookie == null, true);
  1197.                 if (cookie == null && (token == CookieToken.NameValuePair || token == CookieToken.Attribute)) {
  1198.                     cookie = new Cookie();
  1199.                     if (cookie.InternalSetName(m_tokenizer.Name) == false) {
  1200.                         //will be rejected
  1201.                         cookie.InternalSetName(string.Empty);
  1202.                     }
  1203.                     cookie.Value = m_tokenizer.Value;
  1204.                 }
  1205.                 else {
  1206.                     switch (token) {
  1207.                         case CookieToken.NameValuePair:
  1208.                             switch (m_tokenizer.Token) {
  1209.                                 case CookieToken.Comment:
  1210.                                     if (!commentSet) {
  1211.                                         commentSet = true;
  1212.                                         cookie.Comment = m_tokenizer.Value;
  1213.                                     }
  1214.                                     break;
  1215.                                 case CookieToken.CommentUrl:
  1216.                                    
  1217.                                     if (!commentUriSet) {
  1218.                                         commentUriSet = true;
  1219.                                         Uri parsed;
  1220.                                         if (Uri.TryCreate(CheckQuoted(m_tokenizer.Value), UriKind.Absolute, out parsed)) {
  1221.                                             cookie.CommentUri = parsed;
  1222.                                         }
  1223.                                     }
  1224.                                     break;
  1225.                                 case CookieToken.Domain:
  1226.                                    
  1227.                                     if (!domainSet) {
  1228.                                         domainSet = true;
  1229.                                         cookie.Domain = CheckQuoted(m_tokenizer.Value);
  1230.                                         cookie.IsQuotedDomain = m_tokenizer.Quoted;
  1231.                                     }
  1232.                                     break;
  1233.                                 case CookieToken.Expires:
  1234.                                    
  1235.                                     if (!expiresSet) {
  1236.                                         expiresSet = true;
  1237.                                         // ParseCookieDate() does many formats for the date.
  1238.                                         // Also note that the parser will eat day of the week
  1239.                                         // plus comma and leading spaces
  1240.                                         DateTime expires;
  1241.                                         if (HttpDateParse.ParseCookieDate(CheckQuoted(m_tokenizer.Value), out expires)) {
  1242.                                             cookie.Expires = expires;
  1243.                                         }
  1244.                                         else {
  1245.                                             //this cookie will be rejected
  1246.                                             cookie.InternalSetName(string.Empty);
  1247.                                         }
  1248.                                     }
  1249.                                     break;
  1250.                                 case CookieToken.MaxAge:
  1251.                                    
  1252.                                     if (!expiresSet) {
  1253.                                         expiresSet = true;
  1254.                                         int parsed;
  1255.                                         if (int.TryParse(CheckQuoted(m_tokenizer.Value), out parsed)) {
  1256.                                             cookie.Expires = DateTime.Now.AddSeconds((double)parsed);
  1257.                                         }
  1258.                                         else {
  1259.                                             //this cookie will be rejected
  1260.                                             cookie.InternalSetName(string.Empty);
  1261.                                         }
  1262.                                     }
  1263.                                     break;
  1264.                                 case CookieToken.Path:
  1265.                                    
  1266.                                     if (!pathSet) {
  1267.                                         pathSet = true;
  1268.                                         cookie.Path = m_tokenizer.Value;
  1269.                                     }
  1270.                                     break;
  1271.                                 case CookieToken.Port:
  1272.                                    
  1273.                                     if (!portSet) {
  1274.                                         portSet = true;
  1275.                                         try {
  1276.                                             cookie.Port = m_tokenizer.Value;
  1277.                                         }
  1278.                                         catch {
  1279.                                             //this cookie will be rejected
  1280.                                             cookie.InternalSetName(string.Empty);
  1281.                                         }
  1282.                                     }
  1283.                                     break;
  1284.                                 case CookieToken.Version:
  1285.                                    
  1286.                                     if (!versionSet) {
  1287.                                         versionSet = true;
  1288.                                         int parsed;
  1289.                                         if (int.TryParse(CheckQuoted(m_tokenizer.Value), out parsed)) {
  1290.                                             cookie.Version = parsed;
  1291.                                             cookie.IsQuotedVersion = m_tokenizer.Quoted;
  1292.                                         }
  1293.                                         else {
  1294.                                             //this cookie will be rejected
  1295.                                             cookie.InternalSetName(string.Empty);
  1296.                                         }
  1297.                                     }
  1298.                                     break;
  1299.                             }
  1300.                             break;
  1301.                         case CookieToken.Attribute:
  1302.                            
  1303.                             switch (m_tokenizer.Token) {
  1304.                                 case CookieToken.Discard:
  1305.                                     if (!discardSet) {
  1306.                                         discardSet = true;
  1307.                                         cookie.Discard = true;
  1308.                                     }
  1309.                                     break;
  1310.                                 case CookieToken.Secure:
  1311.                                    
  1312.                                     if (!secureSet) {
  1313.                                         secureSet = true;
  1314.                                         cookie.Secure = true;
  1315.                                     }
  1316.                                     break;
  1317.                                 case CookieToken.HttpOnly:
  1318.                                    
  1319.                                     cookie.HttpOnly = true;
  1320.                                     break;
  1321.                                 case CookieToken.Port:
  1322.                                    
  1323.                                     if (!portSet) {
  1324.                                         portSet = true;
  1325.                                         cookie.Port = string.Empty;
  1326.                                     }
  1327.                                     break;
  1328.                             }
  1329.                             break;
  1330.                     }
  1331.                 }
  1332.             }
  1333.             while (!m_tokenizer.Eof && !m_tokenizer.EndOfCookie);
  1334.             return cookie;
  1335.         }
  1336.        
  1337.         // twin parsing method, different enough that it's better to split it into
  1338.         // a different method
  1339.         internal Cookie GetServer()
  1340.         {
  1341.            
  1342.             Cookie cookie = m_savedCookie;
  1343.             m_savedCookie = null;
  1344.            
  1345.             // only first ocurence of an attribute value must be counted
  1346.             bool domainSet = false;
  1347.             bool pathSet = false;
  1348.             bool portSet = false;
  1349.             //special case as it may have no value in header
  1350.             do {
  1351.                 bool first = cookie == null || cookie.Name == null || cookie.Name.Length == 0;
  1352.                 CookieToken token = m_tokenizer.Next(first, false);
  1353.                
  1354.                 if (first && (token == CookieToken.NameValuePair || token == CookieToken.Attribute)) {
  1355.                     if (cookie == null) {
  1356.                         cookie = new Cookie();
  1357.                     }
  1358.                     if (cookie.InternalSetName(m_tokenizer.Name) == false) {
  1359.                         //will be rejected
  1360.                         cookie.InternalSetName(string.Empty);
  1361.                     }
  1362.                     cookie.Value = m_tokenizer.Value;
  1363.                 }
  1364.                 else {
  1365.                     switch (token) {
  1366.                         case CookieToken.NameValuePair:
  1367.                             switch (m_tokenizer.Token) {
  1368.                                 case CookieToken.Domain:
  1369.                                     if (!domainSet) {
  1370.                                         domainSet = true;
  1371.                                         cookie.Domain = CheckQuoted(m_tokenizer.Value);
  1372.                                         cookie.IsQuotedDomain = m_tokenizer.Quoted;
  1373.                                     }
  1374.                                     break;
  1375.                                 case CookieToken.Path:
  1376.                                    
  1377.                                     if (!pathSet) {
  1378.                                         pathSet = true;
  1379.                                         cookie.Path = m_tokenizer.Value;
  1380.                                     }
  1381.                                     break;
  1382.                                 case CookieToken.Port:
  1383.                                    
  1384.                                     if (!portSet) {
  1385.                                         portSet = true;
  1386.                                         try {
  1387.                                             cookie.Port = m_tokenizer.Value;
  1388.                                         }
  1389.                                         catch (CookieException) {
  1390.                                             //this cookie will be rejected
  1391.                                             cookie.InternalSetName(string.Empty);
  1392.                                         }
  1393.                                     }
  1394.                                     break;
  1395.                                 case CookieToken.Version:
  1396.                                    
  1397.                                     // this is a new cookie, this token is for the next cookie.
  1398.                                     m_savedCookie = new Cookie();
  1399.                                     int parsed;
  1400.                                     if (int.TryParse(m_tokenizer.Value, out parsed)) {
  1401.                                         m_savedCookie.Version = parsed;
  1402.                                     }
  1403.                                     return cookie;
  1404.                                 case CookieToken.Unknown:
  1405.                                    
  1406.                                     // this is a new cookie, the token is for the next cookie.
  1407.                                     m_savedCookie = new Cookie();
  1408.                                     if (m_savedCookie.InternalSetName(m_tokenizer.Name) == false) {
  1409.                                         //will be rejected
  1410.                                         m_savedCookie.InternalSetName(string.Empty);
  1411.                                     }
  1412.                                     m_savedCookie.Value = m_tokenizer.Value;
  1413.                                     return cookie;
  1414.                                
  1415.                             }
  1416.                             break;
  1417.                         case CookieToken.Attribute:
  1418.                            
  1419.                             switch (m_tokenizer.Token) {
  1420.                                 case CookieToken.Port:
  1421.                                     if (!portSet) {
  1422.                                         portSet = true;
  1423.                                         cookie.Port = string.Empty;
  1424.                                     }
  1425.                                     break;
  1426.                             }
  1427.                             break;
  1428.                     }
  1429.                 }
  1430.             }
  1431.             while (!m_tokenizer.Eof && !m_tokenizer.EndOfCookie);
  1432.             return cookie;
  1433.         }
  1434.        
  1435.         static internal string CheckQuoted(string value)
  1436.         {
  1437.             if (value.Length < 2 || value[0] != '"' || value[value.Length - 1] != '"')
  1438.                 return value;
  1439.            
  1440.             return value.Length == 2 ? string.Empty : value.Substring(1, value.Length - 2);
  1441.         }
  1442.     }
  1443.    
  1444.    
  1445.     internal class Comparer : IComparer
  1446.     {
  1447.        
  1448.         int IComparer.Compare(object ol, object or)
  1449.         {
  1450.            
  1451.             Cookie left = (Cookie)ol;
  1452.             Cookie right = (Cookie)or;
  1453.            
  1454.             int result;
  1455.            
  1456.             if ((result = string.Compare(left.Name, right.Name, StringComparison.OrdinalIgnoreCase)) != 0) {
  1457.                 return result;
  1458.             }
  1459.            
  1460.             if ((result = string.Compare(left.Domain, right.Domain, StringComparison.OrdinalIgnoreCase)) != 0) {
  1461.                 return result;
  1462.             }
  1463.            
  1464.             //
  1465.             //NB: The only path is case sensitive as per spec.
  1466.             // However, on Win Platform that may break some lazy applications.
  1467.             //
  1468.             if ((result = string.Compare(left.Path, right.Path, StringComparison.Ordinal)) != 0) {
  1469.                 return result;
  1470.             }
  1471.             // They are equal here even if variants are still different.
  1472.             return 0;
  1473.         }
  1474.     }
  1475. }

Developer Fusion