2 // System.Net.Cookie.cs
\r
5 // Lawrence Pit (loz@cable.a2000.nl)
\r
11 namespace System.Net {
\r
13 // Supported cookie formats are:
\r
14 // Netscape: http://home.netscape.com/newsref/std/cookie_spec.html
\r
15 // RFC 2109: http://www.ietf.org/rfc/rfc2109.txt
\r
16 // RFC 2965: http://www.ietf.org/rfc/rfc2965.txt
\r
18 public sealed class Cookie
\r
20 private string comment;
\r
21 private Uri commentUri;
\r
22 private bool discard;
\r
23 private string domain;
\r
24 private bool expired;
\r
25 private DateTime expires;
\r
26 private string name;
\r
27 private string path;
\r
28 private string port;
\r
29 private int [] ports;
\r
30 private bool secure;
\r
31 private DateTime timestamp;
\r
33 private int version;
\r
35 private static char [] reservedCharsName = new char [] {' ', '=', ';', ',', '\n', '\r', '\t'};
\r
36 private static char [] reservedCharsValue = new char [] {';', ','};
\r
37 private static char [] portSeparators = new char [] {'"', ','};
\r
38 private static string tspecials = "()<>@,;:\\\"/[]?={} \t"; // from RFC 2965, 2068
\r
41 : this (String.Empty, String.Empty) {}
\r
43 public Cookie (string name, string value)
\r
51 expires = DateTime.MinValue;
\r
52 timestamp = DateTime.Now;
\r
56 public Cookie (string name, string value, string path)
\r
57 : this (name, value)
\r
62 public Cookie (string name, string value, string path, string domain)
\r
63 : this (name, value, path)
\r
68 public string Comment {
\r
69 get { return comment; }
\r
70 set { comment = value == null ? String.Empty : value; }
\r
73 public Uri CommentUri {
\r
74 get { return commentUri; }
\r
75 set { commentUri = value; }
\r
78 public bool Discard {
\r
79 get { return discard; }
\r
80 set { discard = value; }
\r
83 public string Domain {
\r
84 get { return domain; }
\r
85 set { domain = value == null ? String.Empty : value; }
\r
88 public bool Expired {
\r
90 return expires <= DateTime.Now &&
\r
91 expires != DateTime.MinValue;
\r
96 expires = DateTime.Now;
\r
101 public DateTime Expires {
\r
102 get { return expires; }
\r
103 set { expires = value; }
\r
106 public string Name {
\r
107 get { return name; }
\r
109 if (value == null || value.Length == 0) {
\r
110 throw new CookieException ("Name cannot be empty");
\r
113 if (value [0] == '$' || value.IndexOfAny (reservedCharsName) != -1) {
\r
114 // see CookieTest, according to MS implementation
\r
115 // the name value changes even though it's incorrect
\r
116 name = String.Empty;
\r
117 throw new CookieException ("Name contains invalid characters");
\r
124 public string Path {
\r
125 get { return (path == null) ? "/" : path; }
\r
126 set { path = (value == null) ? String.Empty : value; }
\r
129 public string Port {
\r
130 get { return port; }
\r
132 if (value == null || value.Length == 0) {
\r
133 port = String.Empty;
\r
136 if (value [0] != '"' || value [value.Length - 1] != '"') {
\r
137 throw new CookieException("The 'Port'='" + value + "' part of the cookie is invalid. Port must be enclosed by double quotes.");
\r
140 string [] values = port.Split (portSeparators);
\r
141 ports = new int[values.Length];
\r
142 for (int i = 0; i < ports.Length; i++) {
\r
143 ports [i] = Int32.MinValue;
\r
144 if (values [i].Length == 0)
\r
147 ports [i] = Int32.Parse (values [i]);
\r
148 } catch (Exception e) {
\r
149 throw new CookieException("The 'Port'='" + value + "' part of the cookie is invalid. Invalid value: " + values [i], e);
\r
156 get { return ports; }
\r
159 public bool Secure {
\r
160 get { return secure; }
\r
161 set { secure = value; }
\r
164 public DateTime TimeStamp {
\r
165 get { return timestamp; }
\r
168 public string Value {
\r
169 get { return val; }
\r
171 if (value == null) {
\r
172 val = String.Empty;
\r
176 // LAMESPEC: According to .Net specs the Value property should not accept
\r
177 // the semicolon and comma characters, yet it does. For now we'll follow
\r
178 // the behaviour of MS.Net instead of the specs.
\r
180 if (value.IndexOfAny(reservedCharsValue) != -1)
\r
181 throw new CookieException("Invalid value. Value cannot contain semicolon or comma characters.");
\r
188 public int Version {
\r
189 get { return version; }
\r
191 if ((value < 0) || (value > 10))
\r
198 public override bool Equals (Object obj)
\r
200 System.Net.Cookie c = obj as System.Net.Cookie;
\r
202 return c != null &&
\r
203 String.Compare (this.name, c.name, true) == 0 &&
\r
204 String.Compare (this.val, c.val, false) == 0 &&
\r
205 String.Compare (this.path, c.path, false) == 0 &&
\r
206 String.Compare (this.domain, c.domain, true) == 0 &&
\r
207 this.version == c.version;
\r
210 public override int GetHashCode ()
\r
212 return hash(name.ToLower ().GetHashCode (),
\r
213 val.GetHashCode (),
\r
214 path.GetHashCode (),
\r
215 domain.ToLower ().GetHashCode (),
\r
219 private static int hash (int i, int j, int k, int l, int m)
\r
221 return i ^ (j << 13 | j >> 19) ^ (k << 26 | k >> 6) ^ (l << 7 | l >> 25) ^ (m << 20 | m >> 12);
\r
224 // returns a string that can be used to send a cookie to an Origin Server
\r
225 // i.e., only used for clients
\r
226 // see also para 3.3.4 of RFC 1965
\r
227 public override string ToString ()
\r
229 if (name.Length == 0)
\r
230 return String.Empty;
\r
232 StringBuilder result = new StringBuilder (64);
\r
235 result.Append ("$Version=").Append (version).Append (";");
\r
238 result.Append (name).Append ("=").Append (val);
\r
240 // in the MS.Net implementation path and domain don't show up in
\r
241 // the result, I guess that's a bug in their implementation...
\r
242 if (path != null && path.Length != 0)
\r
243 result.Append (";$Path=").Append (QuotedString (path));
\r
245 if (domain != null && domain.Length != 0)
\r
246 result.Append (";$Domain=").Append (QuotedString (domain));
\r
248 if (port != null && port.Length != 0)
\r
249 result.Append (";$Port=").Append (port);
\r
251 return result.ToString ();
\r
254 // See par 3.6 of RFC 2616
\r
255 private string QuotedString (string value)
\r
257 if (version == 0 || IsToken (value))
\r
260 return "\"" + value.Replace("\"", "\\\"") + "\"";
\r
263 private bool IsToken (string value)
\r
265 int len = value.Length;
\r
266 for (int i = 0; i < len; i++) {
\r
267 char c = value [i];
\r
268 if (c < 0x20 || c >= 0x7f || tspecials.IndexOf (c) != -1)
\r