| // | 
| // Cookie.cs | 
| //    Copied from System.Net.Cookie | 
| // | 
| // Authors: | 
| //    Lawrence Pit (loz@cable.a2000.nl) | 
| //    Gonzalo Paniagua Javier (gonzalo@ximian.com) | 
| //    Daniel Nauck (dna@mono-project.de) | 
| //    Sebastien Pouliot (sebastien@ximian.com) | 
| // | 
| // Copyright (C) 2004,2009 Novell, Inc (http://www.novell.com) | 
| // | 
| // Permission is hereby granted, free of charge, to any person obtaining | 
| // a copy of this software and associated documentation files (the | 
| // "Software"), to deal in the Software without restriction, including | 
| // without limitation the rights to use, copy, modify, merge, publish, | 
| // distribute, sublicense, and/or sell copies of the Software, and to | 
| // permit persons to whom the Software is furnished to do so, subject to | 
| // the following conditions: | 
| //  | 
| // The above copyright notice and this permission notice shall be | 
| // included in all copies or substantial portions of the Software. | 
| //  | 
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | 
| // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | 
| // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | 
| // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | 
| // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | 
| // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | 
| // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | 
| // | 
|   | 
| using System; | 
| using System.Text; | 
| using System.Globalization; | 
| using System.Collections; | 
| using System.Net; | 
|   | 
| namespace WebSocketSharp.Net { | 
|   | 
|     // Supported cookie formats are: | 
|     // Netscape: http://home.netscape.com/newsref/std/cookie_spec.html | 
|     // RFC 2109: http://www.ietf.org/rfc/rfc2109.txt | 
|     // RFC 2965: http://www.ietf.org/rfc/rfc2965.txt | 
|     [Serializable] | 
|     public sealed class Cookie  | 
|     { | 
|         string comment; | 
|         Uri commentUri; | 
|         bool discard; | 
|         string domain; | 
|         DateTime expires; | 
|         bool httpOnly; | 
|         string name; | 
|         string path; | 
|         string port; | 
|         int [] ports; | 
|         bool secure; | 
|         DateTime timestamp; | 
|         string val; | 
|         int version; | 
|          | 
|         static char [] reservedCharsName = new char [] {' ', '=', ';', ',', '\n', '\r', '\t'}; | 
|         static char [] portSeparators = new char [] {'"', ','}; | 
|                 static string tspecials = "()<>@,;:\\\"/[]?={} \t";   // from RFC 2965, 2068 | 
|   | 
|         public Cookie () | 
|         { | 
|             expires = DateTime.MinValue; | 
|             timestamp = DateTime.Now; | 
|             domain = String.Empty; | 
|             name = String.Empty; | 
|             val = String.Empty; | 
|             comment = String.Empty; | 
|             port = String.Empty; | 
|         } | 
|   | 
|         public Cookie (string name, string value) | 
|             : this () | 
|         { | 
|             Name = name; | 
|             Value = value; | 
|         } | 
|   | 
|         public Cookie (string name, string value, string path)  | 
|             : this (name, value)  | 
|         { | 
|             Path = path; | 
|         } | 
|   | 
|         public Cookie (string name, string value, string path, string domain) | 
|             : this (name, value, path) | 
|         { | 
|             Domain = domain; | 
|         } | 
|   | 
|         public string Comment { | 
|             get { return comment; } | 
|             set { comment = value == null ? String.Empty : value; } | 
|         } | 
|   | 
|         public Uri CommentUri { | 
|             get { return commentUri; } | 
|             set { commentUri = value; } | 
|         } | 
|   | 
|         public bool Discard { | 
|             get { return discard; } | 
|             set { discard = value; } | 
|         } | 
|   | 
|         public string Domain { | 
|             get { return domain; } | 
|             set { | 
|                 if (String.IsNullOrEmpty (value)) { | 
|                     domain = String.Empty; | 
|                     ExactDomain = true; | 
|                 } else { | 
|                     domain = value; | 
|                     ExactDomain = (value [0] != '.'); | 
|                 } | 
|             } | 
|         } | 
|   | 
|         internal bool ExactDomain { get; set; } | 
|   | 
|         public bool Expired { | 
|             get {  | 
|                 return expires <= DateTime.Now &&  | 
|                        expires != DateTime.MinValue; | 
|             } | 
|             set {   | 
|                 if (value) | 
|                     expires = DateTime.Now; | 
|             } | 
|         } | 
|   | 
|         public DateTime Expires { | 
|             get { return expires; } | 
|             set { expires = value; } | 
|         } | 
|   | 
|         public bool HttpOnly { | 
|             get { return httpOnly; } | 
|             set { httpOnly = value; } | 
|         } | 
|   | 
|         public string Name { | 
|             get { return name; } | 
|             set {  | 
|                 if (String.IsNullOrEmpty (value)) | 
|                     throw new CookieException ("Name cannot be empty"); | 
|                  | 
|                 if (value [0] == '$' || value.IndexOfAny (reservedCharsName) != -1) { | 
|                     // see CookieTest, according to MS implementation | 
|                     // the name value changes even though it's incorrect | 
|                     name = String.Empty; | 
|                     throw new CookieException ("Name contains invalid characters"); | 
|                 } | 
|                      | 
|                 name = value;  | 
|             } | 
|         } | 
|   | 
|         public string Path { | 
|             get { return (path == null) ? String.Empty : path; } | 
|             set { path = (value == null) ? String.Empty : value; } | 
|         } | 
|   | 
|         public string Port { | 
|             get { return port; } | 
|             set {  | 
|                 if (String.IsNullOrEmpty (value)) { | 
|                     port = String.Empty; | 
|                     return; | 
|                 } | 
|                 if (value [0] != '"' || value [value.Length - 1] != '"') { | 
|                     throw new CookieException("The 'Port'='" + value + "' part of the cookie is invalid. Port must be enclosed by double quotes."); | 
|                 } | 
|                 port = value;  | 
|                 string [] values = port.Split (portSeparators); | 
|                 ports = new int[values.Length]; | 
|                 for (int i = 0; i < ports.Length; i++) { | 
|                     ports [i] = Int32.MinValue; | 
|                     if (values [i].Length == 0) | 
|                         continue; | 
|                     try {                         | 
|                         ports [i] = Int32.Parse (values [i]); | 
|                     } catch (Exception e) { | 
|                         throw new CookieException("The 'Port'='" + value + "' part of the cookie is invalid. Invalid value: " + values [i], e); | 
|                     } | 
|                 } | 
|                 Version = 1; | 
|             } | 
|         } | 
|   | 
|         internal int [] Ports { | 
|             get { return ports; } | 
|         } | 
|   | 
|         public bool Secure { | 
|             get { return secure; } | 
|             set { secure = value; } | 
|         } | 
|   | 
|         public DateTime TimeStamp { | 
|             get { return timestamp; } | 
|         } | 
|   | 
|         public string Value { | 
|             get { return val; } | 
|             set {  | 
|                 if (value == null) { | 
|                     val = String.Empty; | 
|                     return; | 
|                 } | 
|                  | 
|                 // LAMESPEC: According to .Net specs the Value property should not accept  | 
|                 // the semicolon and comma characters, yet it does. For now we'll follow | 
|                 // the behaviour of MS.Net instead of the specs. | 
|                 /* | 
|                 if (value.IndexOfAny(reservedCharsValue) != -1) | 
|                     throw new CookieException("Invalid value. Value cannot contain semicolon or comma characters."); | 
|                 */ | 
|                  | 
|                 val = value;  | 
|             } | 
|         } | 
|   | 
|         public int Version { | 
|             get { return version; } | 
|             set {  | 
|                 if ((value < 0) || (value > 10))  | 
|                     version = 0; | 
|                 else  | 
|                     version = value;  | 
|             } | 
|         } | 
|   | 
|         public override bool Equals (Object obj)  | 
|         { | 
|             System.Net.Cookie c = obj as System.Net.Cookie; | 
|   | 
|             return c != null && | 
|                    String.Compare (this.name, c.Name, true, CultureInfo.InvariantCulture) == 0 && | 
|                    String.Compare (this.val, c.Value, false, CultureInfo.InvariantCulture) == 0 && | 
|                    String.Compare (this.Path, c.Path, false, CultureInfo.InvariantCulture) == 0 && | 
|                    String.Compare (this.domain, c.Domain, true, CultureInfo.InvariantCulture) == 0 && | 
|                    this.version == c.Version; | 
|         } | 
|   | 
|         public override int GetHashCode () | 
|         { | 
|             return hash ( | 
|                 StringComparer.InvariantCultureIgnoreCase.GetHashCode (name), | 
|                 val.GetHashCode (), | 
|                 Path.GetHashCode (), | 
|                 StringComparer.InvariantCultureIgnoreCase.GetHashCode (domain), | 
|                 version); | 
|         } | 
|   | 
|         private static int hash (int i, int j, int k, int l, int m)  | 
|         { | 
|             return i ^ (j << 13 | j >> 19) ^ (k << 26 | k >> 6) ^ (l << 7 | l >> 25) ^ (m << 20 | m >> 12); | 
|         } | 
|   | 
|         // returns a string that can be used to send a cookie to an Origin Server | 
|         // i.e., only used for clients | 
|         // see para 4.2.2 of RFC 2109 and para 3.3.4 of RFC 2965 | 
|         // see also bug #316017 | 
|         public override string ToString ()  | 
|         { | 
|             return ToString (null); | 
|         } | 
|   | 
|         internal string ToString (Uri uri) | 
|         { | 
|             if (name.Length == 0)  | 
|                 return String.Empty; | 
|   | 
|             StringBuilder result = new StringBuilder (64); | 
|      | 
|             if (version > 0) | 
|                 result.Append ("$Version=").Append (version).Append ("; ");         | 
|                  | 
|             result.Append (name).Append ("=").Append (val); | 
|              | 
|             if (version == 0) | 
|                 return result.ToString (); | 
|   | 
|             if (!String.IsNullOrEmpty (path)) | 
|                 result.Append ("; $Path=").Append (path); | 
|             else if (uri != null) | 
|                 result.Append ("; $Path=/").Append (path); | 
|   | 
|             bool append_domain = (uri == null) || (uri.Host != domain); | 
|             if (append_domain && !String.IsNullOrEmpty (domain)) | 
|                 result.Append ("; $Domain=").Append (domain);             | 
|      | 
|             if (port != null && port.Length != 0) | 
|                 result.Append ("; $Port=").Append (port);     | 
|                          | 
|             return result.ToString (); | 
|         } | 
|   | 
|         internal string ToClientString ()  | 
|         { | 
|             if (name.Length == 0)  | 
|                 return String.Empty; | 
|   | 
|             StringBuilder result = new StringBuilder (64); | 
|      | 
|             if (version > 0)  | 
|                 result.Append ("Version=").Append (version).Append (";"); | 
|                  | 
|             result.Append (name).Append ("=").Append (val); | 
|   | 
|             if (path != null && path.Length != 0) | 
|                 result.Append (";Path=").Append (QuotedString (path)); | 
|                  | 
|             if (domain != null && domain.Length != 0) | 
|                 result.Append (";Domain=").Append (QuotedString (domain));             | 
|      | 
|             if (port != null && port.Length != 0) | 
|                 result.Append (";Port=").Append (port);     | 
|                          | 
|             return result.ToString (); | 
|         } | 
|   | 
|         // See par 3.6 of RFC 2616 | 
|               string QuotedString (string value) | 
|             { | 
|             if (version == 0 || IsToken (value)) | 
|                 return value; | 
|             else  | 
|                 return "\"" + value.Replace("\"", "\\\"") + "\""; | 
|             }                         | 
|   | 
|             bool IsToken (string value)  | 
|             { | 
|             int len = value.Length; | 
|             for (int i = 0; i < len; i++) { | 
|                     char c = value [i]; | 
|                 if (c < 0x20 || c >= 0x7f || tspecials.IndexOf (c) != -1) | 
|                           return false; | 
|             } | 
|             return true; | 
|             }         | 
|     } | 
| } |