2 // System.Net.Cookie.cs
5 // Lawrence Pit (loz@cable.a2000.nl)
6 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
8 // (c) Copyright 2004 Novell, Inc. (http://www.ximian.com)
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System.Globalization;
36 namespace System.Net {
38 // Supported cookie formats are:
39 // Netscape: http://home.netscape.com/newsref/std/cookie_spec.html
40 // RFC 2109: http://www.ietf.org/rfc/rfc2109.txt
41 // RFC 2965: http://www.ietf.org/rfc/rfc2965.txt
43 public sealed class Cookie
60 static char [] reservedCharsName = new char [] {' ', '=', ';', ',', '\n', '\r', '\t'};
61 static char [] portSeparators = new char [] {'"', ','};
62 static string tspecials = "()<>@,;:\\\"/[]?={} \t"; // from RFC 2965, 2068
66 expires = DateTime.MinValue;
67 timestamp = DateTime.Now;
76 public Cookie (string name, string value)
83 public Cookie (string name, string value, string path)
89 public Cookie (string name, string value, string path, string domain)
90 : this (name, value, path)
95 public string Comment {
96 get { return comment; }
97 set { comment = value == null ? String.Empty : value; }
100 public Uri CommentUri {
101 get { return commentUri; }
102 set { commentUri = value; }
105 public bool Discard {
106 get { return discard; }
107 set { discard = value; }
110 public string Domain {
111 get { return domain; }
112 set { domain = value == null ? String.Empty : value; }
115 public bool Expired {
117 return expires <= DateTime.Now &&
118 expires != DateTime.MinValue;
123 expires = DateTime.Now;
128 public DateTime Expires {
129 get { return expires; }
130 set { expires = value; }
136 if (value == null || value.Length == 0) {
137 throw new CookieException ("Name cannot be empty");
140 if (value [0] == '$' || value.IndexOfAny (reservedCharsName) != -1) {
141 // see CookieTest, according to MS implementation
142 // the name value changes even though it's incorrect
144 throw new CookieException ("Name contains invalid characters");
152 get { return (path == null || path == "") ? "/" : path; }
153 set { path = (value == null) ? String.Empty : value; }
159 if (value == null || value.Length == 0) {
163 if (value [0] != '"' || value [value.Length - 1] != '"') {
164 throw new CookieException("The 'Port'='" + value + "' part of the cookie is invalid. Port must be enclosed by double quotes.");
167 string [] values = port.Split (portSeparators);
168 ports = new int[values.Length];
169 for (int i = 0; i < ports.Length; i++) {
170 ports [i] = Int32.MinValue;
171 if (values [i].Length == 0)
174 ports [i] = Int32.Parse (values [i]);
175 } catch (Exception e) {
176 throw new CookieException("The 'Port'='" + value + "' part of the cookie is invalid. Invalid value: " + values [i], e);
182 internal int [] Ports {
183 get { return ports; }
187 get { return secure; }
188 set { secure = value; }
191 public DateTime TimeStamp {
192 get { return timestamp; }
195 public string Value {
203 // LAMESPEC: According to .Net specs the Value property should not accept
204 // the semicolon and comma characters, yet it does. For now we'll follow
205 // the behaviour of MS.Net instead of the specs.
207 if (value.IndexOfAny(reservedCharsValue) != -1)
208 throw new CookieException("Invalid value. Value cannot contain semicolon or comma characters.");
216 get { return version; }
218 if ((value < 0) || (value > 10))
225 public override bool Equals (Object obj)
227 System.Net.Cookie c = obj as System.Net.Cookie;
230 String.Compare (this.name, c.name, true, CultureInfo.InvariantCulture) == 0 &&
231 String.Compare (this.val, c.val, false, CultureInfo.InvariantCulture) == 0 &&
232 String.Compare (this.path, c.path, false, CultureInfo.InvariantCulture) == 0 &&
233 String.Compare (this.domain, c.domain, true, CultureInfo.InvariantCulture) == 0 &&
234 this.version == c.version;
237 public override int GetHashCode ()
239 return hash(name.ToLower ().GetHashCode (),
242 domain.ToLower ().GetHashCode (),
246 private static int hash (int i, int j, int k, int l, int m)
248 return i ^ (j << 13 | j >> 19) ^ (k << 26 | k >> 6) ^ (l << 7 | l >> 25) ^ (m << 20 | m >> 12);
251 // returns a string that can be used to send a cookie to an Origin Server
252 // i.e., only used for clients
253 // see also para 3.3.4 of RFC 1965
254 public override string ToString ()
256 if (name.Length == 0)
259 StringBuilder result = new StringBuilder (64);
262 result.Append ("$Version=").Append (version).Append (";");
265 result.Append (name).Append ("=").Append (val);
267 // in the MS.Net implementation path and domain don't show up in
268 // the result, I guess that's a bug in their implementation...
269 if (path != null && path.Length != 0)
270 result.Append (";$Path=").Append (QuotedString (path));
272 if (domain != null && domain.Length != 0)
273 result.Append (";$Domain=").Append (QuotedString (domain));
275 if (port != null && port.Length != 0)
276 result.Append (";$Port=").Append (port);
278 return result.ToString ();
281 // See par 3.6 of RFC 2616
282 string QuotedString (string value)
284 if (version == 0 || IsToken (value))
287 return "\"" + value.Replace("\"", "\\\"") + "\"";
290 bool IsToken (string value)
292 int len = value.Length;
293 for (int i = 0; i < len; i++) {
295 if (c < 0x20 || c >= 0x7f || tspecials.IndexOf (c) != -1)