2010-06-21 Marek Habersack <mhabersack@novell.com>
[mcs.git] / class / System / System.Net / Cookie.cs
blobe55289535fb69cf32dbfa0f5aca24262b4271bf8
1 //
2 // System.Net.Cookie.cs
3 //
4 // Authors:
5 // Lawrence Pit (loz@cable.a2000.nl)
6 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 // Daniel Nauck (dna(at)mono-project(dot)de)
8 // Sebastien Pouliot <sebastien@ximian.com>
9 //
10 // Copyright (C) 2004,2009 Novell, Inc (http://www.novell.com)
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 //
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 //
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System;
35 using System.Text;
36 using System.Globalization;
37 using System.Collections;
39 namespace System.Net {
41 // Supported cookie formats are:
42 // Netscape: http://home.netscape.com/newsref/std/cookie_spec.html
43 // RFC 2109: http://www.ietf.org/rfc/rfc2109.txt
44 // RFC 2965: http://www.ietf.org/rfc/rfc2965.txt
45 [Serializable]
46 public sealed class Cookie
48 string comment;
49 Uri commentUri;
50 bool discard;
51 string domain;
52 DateTime expires;
53 bool httpOnly;
54 string name;
55 string path;
56 string port;
57 int [] ports;
58 bool secure;
59 DateTime timestamp;
60 string val;
61 int version;
63 static char [] reservedCharsName = new char [] {' ', '=', ';', ',', '\n', '\r', '\t'};
64 static char [] portSeparators = new char [] {'"', ','};
65 static string tspecials = "()<>@,;:\\\"/[]?={} \t"; // from RFC 2965, 2068
67 public Cookie ()
69 expires = DateTime.MinValue;
70 timestamp = DateTime.Now;
71 domain = String.Empty;
72 name = String.Empty;
73 val = String.Empty;
74 comment = String.Empty;
75 port = String.Empty;
78 public Cookie (string name, string value)
79 : this ()
81 Name = name;
82 Value = value;
85 public Cookie (string name, string value, string path)
86 : this (name, value)
88 Path = path;
91 public Cookie (string name, string value, string path, string domain)
92 : this (name, value, path)
94 Domain = domain;
97 public string Comment {
98 get { return comment; }
99 set { comment = value == null ? String.Empty : value; }
102 public Uri CommentUri {
103 get { return commentUri; }
104 set { commentUri = value; }
107 public bool Discard {
108 get { return discard; }
109 set { discard = value; }
112 public string Domain {
113 get { return domain; }
114 set {
115 if (String.IsNullOrEmpty (value)) {
116 domain = String.Empty;
117 ExactDomain = true;
118 } else {
119 domain = value;
120 ExactDomain = (value [0] != '.');
125 internal bool ExactDomain { get; set; }
127 public bool Expired {
128 get {
129 return expires <= DateTime.Now &&
130 expires != DateTime.MinValue;
132 set {
133 if (value)
134 expires = DateTime.Now;
138 public DateTime Expires {
139 get { return expires; }
140 set { expires = value; }
143 public bool HttpOnly {
144 get { return httpOnly; }
145 set { httpOnly = value; }
148 public string Name {
149 get { return name; }
150 set {
151 if (String.IsNullOrEmpty (value))
152 throw new CookieException ("Name cannot be empty");
154 if (value [0] == '$' || value.IndexOfAny (reservedCharsName) != -1) {
155 // see CookieTest, according to MS implementation
156 // the name value changes even though it's incorrect
157 name = String.Empty;
158 throw new CookieException ("Name contains invalid characters");
161 name = value;
165 public string Path {
166 get { return (path == null) ? String.Empty : path; }
167 set { path = (value == null) ? String.Empty : value; }
170 public string Port {
171 get { return port; }
172 set {
173 if (String.IsNullOrEmpty (value)) {
174 port = String.Empty;
175 return;
177 if (value [0] != '"' || value [value.Length - 1] != '"') {
178 throw new CookieException("The 'Port'='" + value + "' part of the cookie is invalid. Port must be enclosed by double quotes.");
180 port = value;
181 string [] values = port.Split (portSeparators);
182 ports = new int[values.Length];
183 for (int i = 0; i < ports.Length; i++) {
184 ports [i] = Int32.MinValue;
185 if (values [i].Length == 0)
186 continue;
187 try {
188 ports [i] = Int32.Parse (values [i]);
189 } catch (Exception e) {
190 throw new CookieException("The 'Port'='" + value + "' part of the cookie is invalid. Invalid value: " + values [i], e);
193 Version = 1;
197 internal int [] Ports {
198 get { return ports; }
201 public bool Secure {
202 get { return secure; }
203 set { secure = value; }
206 public DateTime TimeStamp {
207 get { return timestamp; }
210 public string Value {
211 get { return val; }
212 set {
213 if (value == null) {
214 val = String.Empty;
215 return;
218 // LAMESPEC: According to .Net specs the Value property should not accept
219 // the semicolon and comma characters, yet it does. For now we'll follow
220 // the behaviour of MS.Net instead of the specs.
222 if (value.IndexOfAny(reservedCharsValue) != -1)
223 throw new CookieException("Invalid value. Value cannot contain semicolon or comma characters.");
226 val = value;
230 public int Version {
231 get { return version; }
232 set {
233 if ((value < 0) || (value > 10))
234 version = 0;
235 else
236 version = value;
240 public override bool Equals (Object obj)
242 System.Net.Cookie c = obj as System.Net.Cookie;
244 return c != null &&
245 String.Compare (this.name, c.name, true, CultureInfo.InvariantCulture) == 0 &&
246 String.Compare (this.val, c.val, false, CultureInfo.InvariantCulture) == 0 &&
247 String.Compare (this.Path, c.Path, false, CultureInfo.InvariantCulture) == 0 &&
248 String.Compare (this.domain, c.domain, true, CultureInfo.InvariantCulture) == 0 &&
249 this.version == c.version;
252 public override int GetHashCode ()
254 return hash(CaseInsensitiveHashCodeProvider.DefaultInvariant.GetHashCode(name),
255 val.GetHashCode (),
256 Path.GetHashCode (),
257 CaseInsensitiveHashCodeProvider.DefaultInvariant.GetHashCode (domain),
258 version);
261 private static int hash (int i, int j, int k, int l, int m)
263 return i ^ (j << 13 | j >> 19) ^ (k << 26 | k >> 6) ^ (l << 7 | l >> 25) ^ (m << 20 | m >> 12);
266 // returns a string that can be used to send a cookie to an Origin Server
267 // i.e., only used for clients
268 // see para 4.2.2 of RFC 2109 and para 3.3.4 of RFC 2965
269 // see also bug #316017
270 public override string ToString ()
272 return ToString (null);
275 internal string ToString (Uri uri)
277 if (name.Length == 0)
278 return String.Empty;
280 StringBuilder result = new StringBuilder (64);
282 if (version > 0)
283 result.Append ("$Version=").Append (version).Append ("; ");
285 result.Append (name).Append ("=").Append (val);
287 if (version == 0)
288 return result.ToString ();
290 if (!String.IsNullOrEmpty (path))
291 result.Append ("; $Path=").Append (path);
292 else if (uri != null)
293 result.Append ("; $Path=/").Append (path);
295 bool append_domain = (uri == null) || (uri.Host != domain);
296 if (append_domain && !String.IsNullOrEmpty (domain))
297 result.Append ("; $Domain=").Append (domain);
299 if (port != null && port.Length != 0)
300 result.Append ("; $Port=").Append (port);
302 return result.ToString ();
305 internal string ToClientString ()
307 if (name.Length == 0)
308 return String.Empty;
310 StringBuilder result = new StringBuilder (64);
312 if (version > 0)
313 result.Append ("Version=").Append (version).Append (";");
315 result.Append (name).Append ("=").Append (val);
317 if (path != null && path.Length != 0)
318 result.Append (";Path=").Append (QuotedString (path));
320 if (domain != null && domain.Length != 0)
321 result.Append (";Domain=").Append (QuotedString (domain));
323 if (port != null && port.Length != 0)
324 result.Append (";Port=").Append (port);
326 return result.ToString ();
329 // See par 3.6 of RFC 2616
330 string QuotedString (string value)
332 if (version == 0 || IsToken (value))
333 return value;
334 else
335 return "\"" + value.Replace("\"", "\\\"") + "\"";
338 bool IsToken (string value)
340 int len = value.Length;
341 for (int i = 0; i < len; i++) {
342 char c = value [i];
343 if (c < 0x20 || c >= 0x7f || tspecials.IndexOf (c) != -1)
344 return false;
346 return true;