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
57 public Cookie (string name, string value, string path)
\r
58 : this (name, value)
\r
63 public Cookie (string name, string value, string path, string domain)
\r
64 : this (name, value, path)
\r
69 public string Comment {
\r
70 get { return comment; }
\r
71 set { comment = value == null ? String.Empty : value; }
\r
74 public Uri CommentUri {
\r
75 get { return commentUri; }
\r
76 set { commentUri = value; }
\r
79 public bool Discard {
\r
80 get { return discard; }
\r
81 set { discard = value; }
\r
84 public string Domain {
\r
85 get { return domain; }
\r
86 set { domain = value == null ? String.Empty : value; }
\r
89 public bool Expired {
\r
91 return expires <= DateTime.Now &&
\r
92 expires != DateTime.MinValue;
\r
97 expires = DateTime.Now;
\r
102 public DateTime Expires {
\r
103 get { return expires; }
\r
104 set { expires = value; }
\r
107 public string Name {
\r
108 get { return name; }
\r
110 if (value == null || value.Length == 0) {
\r
111 throw new CookieException ("Name cannot be empty");
\r
114 if (value [0] == '$' || value.IndexOfAny (reservedCharsName) != -1) {
\r
115 // see CookieTest, according to MS implementation
\r
116 // the name value changes even though it's incorrect
\r
117 name = String.Empty;
\r
118 throw new CookieException ("Name contains invalid characters");
\r
125 public string Path {
\r
126 get { return (path == null) ? "/" : path; }
\r
127 set { path = (value == null) ? String.Empty : value; }
\r
130 public string Port {
\r
131 get { return port; }
\r
133 if (value == null || value.Length == 0) {
\r
134 port = String.Empty;
\r
137 if (value [0] != '"' || value [value.Length - 1] != '"') {
\r
138 throw new CookieException("The 'Port'='" + value + "' part of the cookie is invalid. Port must be enclosed by double quotes.");
\r
141 string [] values = port.Split (portSeparators);
\r
142 ports = new int[values.Length];
\r
143 for (int i = 0; i < ports.Length; i++) {
\r
144 ports [i] = Int32.MinValue;
\r
145 if (values [i].Length == 0)
\r
148 ports [i] = Int32.Parse (values [i]);
\r
149 } catch (Exception e) {
\r
150 throw new CookieException("The 'Port'='" + value + "' part of the cookie is invalid. Invalid value: " + values [i], e);
\r
157 get { return ports; }
\r
160 public bool Secure {
\r
161 get { return secure; }
\r
162 set { secure = value; }
\r
165 public DateTime TimeStamp {
\r
166 get { return timestamp; }
\r
169 public string Value {
\r
170 get { return val; }
\r
172 if (value == null) {
\r
173 val = String.Empty;
\r
177 // LAMESPEC: According to .Net specs the Value property should not accept
\r
178 // the semicolon and comma characters, yet it does. For now we'll follow
\r
179 // the behaviour of MS.Net instead of the specs.
\r
181 if (value.IndexOfAny(reservedCharsValue) != -1)
\r
182 throw new CookieException("Invalid value. Value cannot contain semicolon or comma characters.");
\r
189 public int Version {
\r
190 get { return version; }
\r
192 if ((value < 0) || (value > 10))
\r
199 public override bool Equals (Object obj)
\r
201 System.Net.Cookie c = obj as System.Net.Cookie;
\r
203 return c != null &&
\r
204 String.Compare (this.name, c.name, true) == 0 &&
\r
205 String.Compare (this.val, c.val, false) == 0 &&
\r
206 String.Compare (this.path, c.path, false) == 0 &&
\r
207 String.Compare (this.domain, c.domain, true) == 0 &&
\r
208 this.version == c.version;
\r
211 public override int GetHashCode ()
\r
213 return hash(name.ToLower ().GetHashCode (),
\r
214 val.GetHashCode (),
\r
215 path.GetHashCode (),
\r
216 domain.ToLower ().GetHashCode (),
\r
220 private static int hash (int i, int j, int k, int l, int m)
\r
222 return i ^ (j << 13 | j >> 19) ^ (k << 26 | k >> 6) ^ (l << 7 | l >> 25) ^ (m << 20 | m >> 12);
\r
225 // returns a string that can be used to send a cookie to an Origin Server
\r
226 // i.e., only used for clients
\r
227 // see also para 3.3.4 of RFC 1965
\r
228 public override string ToString ()
\r
230 if (name.Length == 0)
\r
231 return String.Empty;
\r
233 StringBuilder result = new StringBuilder (64);
\r
236 result.Append ("$Version=").Append (version).Append (";");
\r
239 result.Append (name).Append ("=").Append (val);
\r
241 // in the MS.Net implementation path and domain don't show up in
\r
242 // the result, I guess that's a bug in their implementation...
\r
243 if (path != null && path.Length != 0)
\r
244 result.Append (";$Path=").Append (QuotedString (path));
\r
246 if (domain != null && domain.Length != 0)
\r
247 result.Append (";$Domain=").Append (QuotedString (domain));
\r
249 if (port != null && port.Length != 0)
\r
250 result.Append (";$Port=").Append (port);
\r
252 return result.ToString ();
\r
255 // See par 3.6 of RFC 2616
\r
256 private string QuotedString (string value)
\r
258 if (version == 0 || IsToken (value))
\r
261 return "\"" + value.Replace("\"", "\\\"") + "\"";
\r
264 private bool IsToken (string value)
\r
266 int len = value.Length;
\r
267 for (int i = 0; i < len; i++) {
\r
268 char c = value [i];
\r
269 if (c < 0x20 || c >= 0x7f || tspecials.IndexOf (c) != -1)
\r