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;
75 public Cookie (string name, string value)
82 public Cookie (string name, string value, string path)
88 public Cookie (string name, string value, string path, string domain)
89 : this (name, value, path)
94 public string Comment {
95 get { return comment; }
96 set { comment = value == null ? String.Empty : value; }
99 public Uri CommentUri {
100 get { return commentUri; }
101 set { commentUri = value; }
104 public bool Discard {
105 get { return discard; }
106 set { discard = value; }
109 public string Domain {
110 get { return domain; }
111 set { domain = value == null ? String.Empty : value; }
114 public bool Expired {
116 return expires <= DateTime.Now &&
117 expires != DateTime.MinValue;
122 expires = DateTime.Now;
127 public DateTime Expires {
128 get { return expires; }
129 set { expires = value; }
135 if (value == null || value.Length == 0) {
136 throw new CookieException ("Name cannot be empty");
139 if (value [0] == '$' || value.IndexOfAny (reservedCharsName) != -1) {
140 // see CookieTest, according to MS implementation
141 // the name value changes even though it's incorrect
143 throw new CookieException ("Name contains invalid characters");
151 get { return (path == null || path == "") ? "/" : path; }
152 set { path = (value == null) ? String.Empty : value; }
158 if (value == null || value.Length == 0) {
162 if (value [0] != '"' || value [value.Length - 1] != '"') {
163 throw new CookieException("The 'Port'='" + value + "' part of the cookie is invalid. Port must be enclosed by double quotes.");
166 string [] values = port.Split (portSeparators);
167 ports = new int[values.Length];
168 for (int i = 0; i < ports.Length; i++) {
169 ports [i] = Int32.MinValue;
170 if (values [i].Length == 0)
173 ports [i] = Int32.Parse (values [i]);
174 } catch (Exception e) {
175 throw new CookieException("The 'Port'='" + value + "' part of the cookie is invalid. Invalid value: " + values [i], e);
181 internal int [] Ports {
182 get { return ports; }
186 get { return secure; }
187 set { secure = value; }
190 public DateTime TimeStamp {
191 get { return timestamp; }
194 public string Value {
202 // LAMESPEC: According to .Net specs the Value property should not accept
203 // the semicolon and comma characters, yet it does. For now we'll follow
204 // the behaviour of MS.Net instead of the specs.
206 if (value.IndexOfAny(reservedCharsValue) != -1)
207 throw new CookieException("Invalid value. Value cannot contain semicolon or comma characters.");
215 get { return version; }
217 if ((value < 0) || (value > 10))
224 public override bool Equals (Object obj)
226 System.Net.Cookie c = obj as System.Net.Cookie;
229 String.Compare (this.name, c.name, true, CultureInfo.InvariantCulture) == 0 &&
230 String.Compare (this.val, c.val, false, CultureInfo.InvariantCulture) == 0 &&
231 String.Compare (this.Path, c.Path, false, CultureInfo.InvariantCulture) == 0 &&
232 String.Compare (this.domain, c.domain, true, CultureInfo.InvariantCulture) == 0 &&
233 this.version == c.version;
236 public override int GetHashCode ()
238 return hash(name.ToLower ().GetHashCode (),
241 domain.ToLower ().GetHashCode (),
245 private static int hash (int i, int j, int k, int l, int m)
247 return i ^ (j << 13 | j >> 19) ^ (k << 26 | k >> 6) ^ (l << 7 | l >> 25) ^ (m << 20 | m >> 12);
250 // returns a string that can be used to send a cookie to an Origin Server
251 // i.e., only used for clients
252 // see also para 3.3.4 of RFC 1965
253 public override string ToString ()
255 if (name.Length == 0)
258 StringBuilder result = new StringBuilder (64);
261 result.Append ("$Version=").Append (version).Append (";");
264 result.Append (name).Append ("=").Append (val);
266 // in the MS.Net implementation path and domain don't show up in
267 // the result, I guess that's a bug in their implementation...
268 if (path != null && path.Length != 0)
269 result.Append (";$Path=").Append (QuotedString (path));
271 if (domain != null && domain.Length != 0)
272 result.Append (";$Domain=").Append (QuotedString (domain));
274 if (port != null && port.Length != 0)
275 result.Append (";$Port=").Append (port);
277 return result.ToString ();
280 // See par 3.6 of RFC 2616
281 string QuotedString (string value)
283 if (version == 0 || IsToken (value))
286 return "\"" + value.Replace("\"", "\\\"") + "\"";
289 bool IsToken (string value)
291 int len = value.Length;
292 for (int i = 0; i < len; i++) {
294 if (c < 0x20 || c >= 0x7f || tspecials.IndexOf (c) != -1)