2 // System.Net.Cookie.cs
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>
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:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
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.
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
46 public sealed class Cookie
63 static char [] reservedCharsName
= new char [] {' ', '=', ';', ',', '\n', '\r', '\t'}
;
64 static char [] portSeparators
= new char [] {'"', ','}
;
65 static string tspecials
= "()<>@,;:\\\"/[]?={} \t"; // from RFC 2965, 2068
69 expires
= DateTime
.MinValue
;
70 timestamp
= DateTime
.Now
;
71 domain
= String
.Empty
;
74 comment
= String
.Empty
;
78 public Cookie (string name
, string value)
85 public Cookie (string name
, string value, string path
)
91 public Cookie (string name
, string value, string path
, string domain
)
92 : this (name
, value, path
)
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; }
115 if (String
.IsNullOrEmpty (value)) {
116 domain
= String
.Empty
;
120 ExactDomain
= (value [0] != '.');
125 internal bool ExactDomain { get; set; }
127 public bool Expired
{
129 return expires
<= DateTime
.Now
&&
130 expires
!= DateTime
.MinValue
;
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; }
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
158 throw new CookieException ("Name contains invalid characters");
166 get { return (path == null) ? String.Empty : path; }
167 set { path = (value == null) ? String.Empty : value; }
173 if (String
.IsNullOrEmpty (value)) {
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.");
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)
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
);
197 internal int [] Ports
{
198 get { return ports; }
202 get { return secure; }
203 set { secure = value; }
206 public DateTime TimeStamp
{
207 get { return timestamp; }
210 public string Value
{
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.");
231 get { return version; }
233 if ((value < 0) || (value > 10))
240 public override bool Equals (Object obj
)
242 System
.Net
.Cookie c
= obj
as System
.Net
.Cookie
;
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
),
257 CaseInsensitiveHashCodeProvider
.DefaultInvariant
.GetHashCode (domain
),
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)
280 StringBuilder result
= new StringBuilder (64);
283 result
.Append ("$Version=").Append (version
).Append ("; ");
285 result
.Append (name
).Append ("=").Append (val
);
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)
310 StringBuilder result
= new StringBuilder (64);
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))
335 return "\"" + value.Replace("\"", "\\\"") + "\"";
338 bool IsToken (string value)
340 int len
= value.Length
;
341 for (int i
= 0; i
< len
; i
++) {
343 if (c
< 0x20 || c
>= 0x7f || tspecials
.IndexOf (c
) != -1)