125827e6375c63adc92e94a641c8f9d8daeac5d3
[mono.git] / mcs / class / System / System.Net / Cookie.cs
1 //\r
2 // System.Net.Cookie.cs\r
3 //\r
4 // Author:\r
5 //   Lawrence Pit (loz@cable.a2000.nl)\r
6 //\r
7 \r
8 using System;\r
9 using System.Text;\r
10 \r
11 namespace System.Net {\r
12 \r
13         // Supported cookie formats are:\r
14         // Netscape: http://home.netscape.com/newsref/std/cookie_spec.html\r
15         // RFC 2109: http://www.ietf.org/rfc/rfc2109.txt\r
16         // RFC 2965: http://www.ietf.org/rfc/rfc2965.txt\r
17         [Serializable]\r
18         public sealed class Cookie \r
19         {\r
20                 private string comment;\r
21                 private Uri commentUri;\r
22                 private bool discard;\r
23                 private string domain;\r
24                 private bool expired;\r
25                 private DateTime expires;\r
26                 private string name;\r
27                 private string path;\r
28                 private string port;\r
29                 private int [] ports;\r
30                 private bool secure;\r
31                 private DateTime timestamp;\r
32                 private string val;\r
33                 private int version;\r
34                 \r
35                 private static char [] reservedCharsName = new char [] {' ', '=', ';', ',', '\n', '\r', '\t'};\r
36                 private static char [] reservedCharsValue = new char [] {';', ','};\r
37                 private static char [] portSeparators = new char [] {'"', ','};\r
38                 private static string tspecials = "()<>@,;:\\\"/[]?={} \t";   // from RFC 2965, 2068\r
39                 \r
40                 public Cookie () \r
41                         : this (String.Empty, String.Empty) {}\r
42                 \r
43                 public Cookie (string name, string value) \r
44                 {\r
45                         Name = name;\r
46                         Value = value;\r
47                         \r
48                         discard = false;\r
49                         expired = false;\r
50                         secure = false;\r
51                         expires = DateTime.MinValue;\r
52                         timestamp = DateTime.Now;\r
53                         version = 0;            \r
54                         domain = "";\r
55                 }               \r
56                         \r
57                 public Cookie (string name, string value, string path) \r
58                         : this (name, value) \r
59                 {\r
60                         Path = path;\r
61                 }\r
62                 \r
63                 public Cookie (string name, string value, string path, string domain)\r
64                         : this (name, value, path)\r
65                 {\r
66                         Domain = domain;\r
67                 }\r
68                 \r
69                 public string Comment {\r
70                         get { return comment; }\r
71                         set { comment = value == null ? String.Empty : value; }\r
72                 }\r
73                 \r
74                 public Uri CommentUri {\r
75                         get { return commentUri; }\r
76                         set { commentUri = value; }\r
77                 }\r
78 \r
79                 public bool Discard {\r
80                         get { return discard; }\r
81                         set { discard = value; }\r
82                 }\r
83                 \r
84                 public string Domain {\r
85                         get { return domain; }\r
86                         set { domain = value == null ? String.Empty : value; }\r
87                 }\r
88 \r
89                 public bool Expired {\r
90                         get { \r
91                                 return expires <= DateTime.Now && \r
92                                        expires != DateTime.MinValue;\r
93                         }\r
94                         set { \r
95                                 expired = value; \r
96                                 if (expired) {\r
97                                         expires = DateTime.Now;\r
98                                 }\r
99                         }\r
100                 }\r
101 \r
102                 public DateTime Expires {\r
103                         get { return expires; }\r
104                         set { expires = value; }\r
105                 }\r
106 \r
107                 public string Name {\r
108                         get { return name; }\r
109                         set { \r
110                                 if (value == null || value.Length == 0) {\r
111                                         throw new CookieException ("Name cannot be empty");\r
112                                 }                       \r
113                                 \r
114                                 if (value [0] == '$' || value.IndexOfAny (reservedCharsName) != -1) {\r
115                                         // see CookieTest, according to MS implementation\r
116                                         // the name value changes even though it's incorrect\r
117                                         name = String.Empty;\r
118                                         throw new CookieException ("Name contains invalid characters");\r
119                                 }\r
120                                         \r
121                                 name = value; \r
122                         }\r
123                 }\r
124 \r
125                 public string Path {\r
126                         get { return (path == null) ? "/" : path; }\r
127                         set { path = (value == null) ? String.Empty : value; }\r
128                 }\r
129 \r
130                 public string Port {\r
131                         get { return port; }\r
132                         set { \r
133                                 if (value == null || value.Length == 0) {\r
134                                         port = String.Empty;\r
135                                         return;\r
136                                 }\r
137                                 if (value [0] != '"' || value [value.Length - 1] != '"') {\r
138                                         throw new CookieException("The 'Port'='" + value + "' part of the cookie is invalid. Port must be enclosed by double quotes.");\r
139                                 }\r
140                                 port = value; \r
141                                 string [] values = port.Split (portSeparators);\r
142                                 ports = new int[values.Length];\r
143                                 for (int i = 0; i < ports.Length; i++) {\r
144                                         ports [i] = Int32.MinValue;\r
145                                         if (values [i].Length == 0)\r
146                                                 continue;\r
147                                         try {                                           \r
148                                                 ports [i] = Int32.Parse (values [i]);\r
149                                         } catch (Exception e) {\r
150                                                 throw new CookieException("The 'Port'='" + value + "' part of the cookie is invalid. Invalid value: " + values [i], e);\r
151                                         }\r
152                                 }\r
153                         }\r
154                 }\r
155                 \r
156                 int[] Ports {\r
157                         get { return ports; }\r
158                 }\r
159 \r
160                 public bool Secure {\r
161                         get { return secure; }\r
162                         set { secure = value; }\r
163                 }\r
164 \r
165                 public DateTime TimeStamp {\r
166                         get { return timestamp; }\r
167                 }\r
168 \r
169                 public string Value {\r
170                         get { return val; }\r
171                         set { \r
172                                 if (value == null) {\r
173                                         val = String.Empty;\r
174                                         return;\r
175                                 }\r
176                                 \r
177                                 // LAMESPEC: According to .Net specs the Value property should not accept \r
178                                 // the semicolon and comma characters, yet it does. For now we'll follow\r
179                                 // the behaviour of MS.Net instead of the specs.\r
180                                 /*\r
181                                 if (value.IndexOfAny(reservedCharsValue) != -1)\r
182                                         throw new CookieException("Invalid value. Value cannot contain semicolon or comma characters.");\r
183                                 */\r
184                                 \r
185                                 val = value; \r
186                         }\r
187                 }\r
188                 \r
189                 public int Version {\r
190                         get { return version; }\r
191                         set { \r
192                                 if ((value < 0) || (value > 10)) \r
193                                         version = 0;\r
194                                 else \r
195                                         version = value; \r
196                         }\r
197                 }\r
198                 \r
199                 public override bool Equals (Object obj) \r
200                 {\r
201                         System.Net.Cookie c = obj as System.Net.Cookie;                 \r
202                         \r
203                         return c != null &&\r
204                                String.Compare (this.name, c.name, true) == 0 &&\r
205                                String.Compare (this.val, c.val, false) == 0 &&\r
206                                String.Compare (this.path, c.path, false) == 0 &&\r
207                                String.Compare (this.domain, c.domain, true) == 0 &&\r
208                                this.version == c.version;\r
209                 }\r
210                 \r
211                 public override int GetHashCode ()\r
212                 {\r
213                         return hash(name.ToLower ().GetHashCode (),\r
214                                     val.GetHashCode (),\r
215                                     path.GetHashCode (),\r
216                                     domain.ToLower ().GetHashCode (),\r
217                                     version);\r
218                 }\r
219                 \r
220                 private static int hash (int i, int j, int k, int l, int m) \r
221                 {\r
222                         return i ^ (j << 13 | j >> 19) ^ (k << 26 | k >> 6) ^ (l << 7 | l >> 25) ^ (m << 20 | m >> 12);\r
223                 }\r
224                 \r
225                 // returns a string that can be used to send a cookie to an Origin Server\r
226                 // i.e., only used for clients\r
227                 // see also para 3.3.4 of RFC 1965\r
228                 public override string ToString () \r
229                 {\r
230                         if (name.Length == 0) \r
231                                 return String.Empty;\r
232 \r
233                         StringBuilder result = new StringBuilder (64);\r
234         \r
235                         if (version > 0) {\r
236                                 result.Append ("$Version=").Append (version).Append (";");\r
237                         }                               \r
238                                 \r
239                         result.Append (name).Append ("=").Append (val);\r
240 \r
241                         // in the MS.Net implementation path and domain don't show up in\r
242                         // the result, I guess that's a bug in their implementation...\r
243                         if (path != null && path.Length != 0)\r
244                                 result.Append (";$Path=").Append (QuotedString (path));\r
245                                 \r
246                         if (domain != null && domain.Length != 0)\r
247                                 result.Append (";$Domain=").Append (QuotedString (domain));                     \r
248         \r
249                         if (port != null && port.Length != 0)\r
250                                 result.Append (";$Port=").Append (port);        \r
251                                                 \r
252                         return result.ToString ();\r
253                 }\r
254                                 \r
255                 // See par 3.6 of RFC 2616\r
256                 private string QuotedString (string value)\r
257                 {\r
258                         if (version == 0 || IsToken (value))\r
259                                 return value;\r
260                         else \r
261                                 return "\"" + value.Replace("\"", "\\\"") + "\"";\r
262                 }                                   \r
263 \r
264                 private bool IsToken (string value) \r
265                 {\r
266                         int len = value.Length;\r
267                         for (int i = 0; i < len; i++) {\r
268                                 char c = value [i];\r
269                                 if (c < 0x20 || c >= 0x7f || tspecials.IndexOf (c) != -1)\r
270                                         return false;\r
271                         }\r
272                         return true;\r
273                 }           \r
274 \r
275         }\r
276 }\r
277 \r