Update mcs/class/Commons.Xml.Relaxng/Commons.Xml.Relaxng/RelaxngPattern.cs
[mono.git] / mcs / class / System / System.Net / Cookie.cs
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)
11 //
12
13 //
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.
32 //
33
34 using System;
35 using System.Text;
36 using System.Globalization;
37 using System.Collections;
38
39 namespace System.Net {
40
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 
47         {
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;
62                 
63                 static char [] reservedCharsName = new char [] {' ', '=', ';', ',', '\n', '\r', '\t'};
64                 static char [] portSeparators = new char [] {'"', ','};
65                 static string tspecials = "()<>@,;:\\\"/[]?={} \t";   // from RFC 2965, 2068
66
67                 public Cookie ()
68                 {
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;
76                 }
77
78                 public Cookie (string name, string value)
79                         : this ()
80                 {
81                         Name = name;
82                         Value = value;
83                 }
84
85                 public Cookie (string name, string value, string path) 
86                         : this (name, value) 
87                 {
88                         Path = path;
89                 }
90
91                 public Cookie (string name, string value, string path, string domain)
92                         : this (name, value, path)
93                 {
94                         Domain = domain;
95                 }
96
97                 public string Comment {
98                         get { return comment; }
99                         set { comment = value == null ? String.Empty : value; }
100                 }
101
102                 public Uri CommentUri {
103                         get { return commentUri; }
104                         set { commentUri = value; }
105                 }
106
107                 public bool Discard {
108                         get { return discard; }
109                         set { discard = value; }
110                 }
111
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] != '.');
121                                 }
122                         }
123                 }
124
125                 internal bool ExactDomain { get; set; }
126
127                 public bool Expired {
128                         get { 
129                                 return expires <= DateTime.Now && 
130                                        expires != DateTime.MinValue;
131                         }
132                         set {  
133                                 if (value)
134                                         expires = DateTime.Now;
135                         }
136                 }
137
138                 public DateTime Expires {
139                         get { return expires; }
140                         set { expires = value; }
141                 }
142
143                 public bool HttpOnly {
144                         get { return httpOnly; }
145                         set { httpOnly = value; }
146                 }
147
148                 public string Name {
149                         get { return name; }
150                         set { 
151                                 if (String.IsNullOrEmpty (value))
152                                         throw new CookieException ("Name cannot be empty");
153                                 
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");
159                                 }
160                                         
161                                 name = value; 
162                         }
163                 }
164
165                 public string Path {
166                         get { return (path == null) ? String.Empty : path; }
167                         set { path = (value == null) ? String.Empty : value; }
168                 }
169
170                 public string Port {
171                         get { return port; }
172                         set { 
173                                 if (String.IsNullOrEmpty (value)) {
174                                         port = String.Empty;
175                                         return;
176                                 }
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.");
179                                 }
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);
191                                         }
192                                 }
193                                 Version = 1;
194                         }
195                 }
196
197                 internal int [] Ports {
198                         get { return ports; }
199                         set { ports = value; }
200                 }
201
202                 public bool Secure {
203                         get { return secure; }
204                         set { secure = value; }
205                 }
206
207                 public DateTime TimeStamp {
208                         get { return timestamp; }
209                 }
210
211                 public string Value {
212                         get { return val; }
213                         set { 
214                                 if (value == null) {
215                                         val = String.Empty;
216                                         return;
217                                 }
218                                 
219                                 // LAMESPEC: According to .Net specs the Value property should not accept 
220                                 // the semicolon and comma characters, yet it does. For now we'll follow
221                                 // the behaviour of MS.Net instead of the specs.
222                                 /*
223                                 if (value.IndexOfAny(reservedCharsValue) != -1)
224                                         throw new CookieException("Invalid value. Value cannot contain semicolon or comma characters.");
225                                 */
226                                 
227                                 val = value; 
228                         }
229                 }
230                 
231                 public int Version {
232                         get { return version; }
233                         set { 
234                                 if ((value < 0) || (value > 10)) 
235                                         version = 0;
236                                 else 
237                                         version = value; 
238                         }
239                 }
240
241                 public override bool Equals (Object comparand) 
242                 {
243                         System.Net.Cookie c = comparand as System.Net.Cookie;                   
244                         
245                         return c != null &&
246                                String.Compare (this.name, c.name, true, CultureInfo.InvariantCulture) == 0 &&
247                                String.Compare (this.val, c.val, false, CultureInfo.InvariantCulture) == 0 &&
248                                String.Compare (this.Path, c.Path, false, CultureInfo.InvariantCulture) == 0 &&
249                                String.Compare (this.domain, c.domain, true, CultureInfo.InvariantCulture) == 0 &&
250                                this.version == c.version;
251                 }
252
253                 public override int GetHashCode ()
254                 {
255                         return hash(CaseInsensitiveHashCodeProvider.DefaultInvariant.GetHashCode(name),
256                                     val.GetHashCode (),
257                                     Path.GetHashCode (),
258                                     CaseInsensitiveHashCodeProvider.DefaultInvariant.GetHashCode (domain),
259                                     version);
260                 }
261
262                 private static int hash (int i, int j, int k, int l, int m) 
263                 {
264                         return i ^ (j << 13 | j >> 19) ^ (k << 26 | k >> 6) ^ (l << 7 | l >> 25) ^ (m << 20 | m >> 12);
265                 }
266
267                 // returns a string that can be used to send a cookie to an Origin Server
268                 // i.e., only used for clients
269                 // see para 4.2.2 of RFC 2109 and para 3.3.4 of RFC 2965
270                 // see also bug #316017
271                 public override string ToString () 
272                 {
273                         return ToString (null);
274                 }
275
276                 internal string ToString (Uri uri)
277                 {
278                         if (name.Length == 0) 
279                                 return String.Empty;
280
281                         StringBuilder result = new StringBuilder (64);
282         
283                         if (version > 0)
284                                 result.Append ("$Version=").Append (version).Append ("; ");             
285                                 
286                         result.Append (name).Append ("=").Append (val);
287                         
288                         if (version == 0)
289                                 return result.ToString ();
290
291                         if (!String.IsNullOrEmpty (path))
292                                 result.Append ("; $Path=").Append (path);
293                         else if (uri != null)
294                                 result.Append ("; $Path=/").Append (path);
295
296                         bool append_domain = (uri == null) || (uri.Host != domain);
297                         if (append_domain && !String.IsNullOrEmpty (domain))
298                                 result.Append ("; $Domain=").Append (domain);                   
299         
300                         if (port != null && port.Length != 0)
301                                 result.Append ("; $Port=").Append (port);       
302                                                 
303                         return result.ToString ();
304                 }
305
306                 internal string ToClientString () 
307                 {
308                         if (name.Length == 0) 
309                                 return String.Empty;
310
311                         StringBuilder result = new StringBuilder (64);
312         
313                         if (version > 0) 
314                                 result.Append ("Version=").Append (version).Append (";");
315                                 
316                         result.Append (name).Append ("=").Append (val);
317
318                         if (path != null && path.Length != 0)
319                                 result.Append (";Path=").Append (QuotedString (path));
320                                 
321                         if (domain != null && domain.Length != 0)
322                                 result.Append (";Domain=").Append (QuotedString (domain));                      
323         
324                         if (port != null && port.Length != 0)
325                                 result.Append (";Port=").Append (port); 
326                                                 
327                         return result.ToString ();
328                 }
329
330                 // See par 3.6 of RFC 2616
331                 string QuotedString (string value)
332                 {
333                         if (version == 0 || IsToken (value))
334                                 return value;
335                         else 
336                                 return "\"" + value.Replace("\"", "\\\"") + "\"";
337                 }                                   
338
339                 bool IsToken (string value) 
340                 {
341                         int len = value.Length;
342                         for (int i = 0; i < len; i++) {
343                                 char c = value [i];
344                                 if (c < 0x20 || c >= 0x7f || tspecials.IndexOf (c) != -1)
345                                         return false;
346                         }
347                         return true;
348                 }           
349         }
350 }
351