Merge pull request #439 from mono-soc-2012/garyb/iconfix
[mono.git] / mcs / class / System / System.Net / CookieParser.cs
1 //
2 // System.Net.CookieParser
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 //
9 // (c) 2002 Lawrence Pit
10 // (c) 2003 Ximian, Inc. (http://www.ximian.com)
11 // (c) 2008 Daniel Nauck
12 //
13
14 //
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 //
34
35 using System;
36 using System.Collections;
37 using System.Collections.Generic;
38 using System.Globalization;
39
40 namespace System.Net {
41
42         class CookieParser {
43                 string header;
44                 int pos;
45                 int length;
46
47                 public CookieParser (string header) : this (header, 0)
48                 {
49                 }
50
51                 public CookieParser (string header, int position)
52                 {
53                         this.header = header;
54                         this.pos = position;
55                         this.length = header.Length;
56                 }
57
58                 public IEnumerable<Cookie> Parse ()
59                 {
60                         while (pos < length) {
61                                 Cookie cookie;
62                                 try {
63                                         cookie = DoParse ();
64                                 } catch {
65                                         while ((pos < length) && (header [pos] != ','))
66                                                 pos++;
67                                         pos++;
68                                         continue;
69                                 }
70                                 yield return cookie;
71                         }
72                 }
73
74                 Cookie DoParse ()
75                 {
76                         var name = GetCookieName ();
77                         if (pos >= length)
78                                 return new Cookie (name, string.Empty);
79
80                         var value = string.Empty;
81                         if (header [pos] == '=') {
82                                 pos++;
83                                 value = GetCookieValue ();
84                         }
85
86                         var cookie = new Cookie (name, value);
87
88                         if (pos >= length) {
89                                 return cookie;
90                         } else if (header [pos] == ',') {
91                                 pos++;
92                                 return cookie;
93                         } else if ((header [pos++] != ';') || (pos >= length)) {
94                                 return cookie;
95                         }
96
97                         while (pos < length) {
98                                 var argName = GetCookieName ();
99                                 string argVal = string.Empty;
100                                 if ((pos < length) && (header [pos] == '=')) {
101                                         pos++;
102                                         argVal = GetCookieValue ();
103                                 }
104                                 ProcessArg (cookie, argName, argVal);
105
106                                 if (pos >= length)
107                                         break;
108                                 if (header [pos] == ',') {
109                                         pos++;
110                                         break;
111                                 } else if (header [pos] != ';') {
112                                         break;
113                                 }
114
115                                 pos++;
116                         }
117
118                         return cookie;
119                 }
120
121                 void ProcessArg (Cookie cookie, string name, string val)
122                 {
123                         if ((name == null) || (name == string.Empty))
124                                 throw new InvalidOperationException ();
125
126                         name = name.ToUpper ();
127                         switch (name) {
128                         case "COMMENT":
129                                 if (cookie.Comment == null)
130                                         cookie.Comment = val;
131                                 break;
132                         case "COMMENTURL":
133                                 if (cookie.CommentUri == null)
134                                         cookie.CommentUri = new Uri (val);
135                                 break;
136                         case "DISCARD":
137                                 cookie.Discard = true;
138                                 break;
139                         case "DOMAIN":
140                                 if (cookie.Domain == "")
141                                         cookie.Domain = val;
142                                 break;
143                         case "HTTPONLY":
144                                 cookie.HttpOnly = true;
145                                 break;
146                         case "MAX-AGE": // RFC Style Set-Cookie2
147                                 if (cookie.Expires == DateTime.MinValue) {
148                                         try {
149                                         cookie.Expires = cookie.TimeStamp.AddSeconds (UInt32.Parse (val));
150                                         } catch {}
151                                 }
152                                 break;
153                         case "EXPIRES": // Netscape Style Set-Cookie
154                                 if (cookie.Expires != DateTime.MinValue)
155                                         break;
156
157                                 if ((pos < length) && (header [pos] == ',') && IsWeekDay (val)) {
158                                         pos++;
159                                         val = val + ", " + GetCookieValue ();
160                                 }
161
162                                 cookie.Expires = CookieParser.TryParseCookieExpires (val);
163                                 break;
164                         case "PATH":
165                                 cookie.Path = val;
166                                 break;
167                         case "PORT":
168                                 if (cookie.Port == null)
169                                         cookie.Port = val;
170                                 break;
171                         case "SECURE":
172                                 cookie.Secure = true;
173                                 break;
174                         case "VERSION":
175                                 try {
176                                         cookie.Version = (int) UInt32.Parse (val);
177                                 } catch {}
178                                 break;
179                         }
180                 }
181
182                 string GetCookieName ()
183                 {
184                         int k = pos;
185                         while (k < length && Char.IsWhiteSpace (header [k]))
186                                 k++;
187
188                         int begin = k;
189                         while (k < length && header [k] != ';' && header [k] != ',' && header [k] != '=')
190                                 k++;
191
192                         pos = k;
193                         return header.Substring (begin, k - begin).Trim ();
194                 }
195
196                 string GetCookieValue ()
197                 {
198                         if (pos >= length)
199                                 return null;
200
201                         int k = pos;
202                         while (k < length && Char.IsWhiteSpace (header [k]))
203                                 k++;
204
205                         int begin;
206                         if (header [k] == '"'){
207                                 int j;
208                                 begin = k++;
209
210                                 while (k < length && header [k] != '"')
211                                         k++;
212
213                                 for (j = ++k; j < length && header [j] != ';' && header [j] != ','; j++)
214                                         ;
215                                 pos = j;
216                         } else {
217                                 begin = k;
218                                 while (k < length && header [k] != ';' && header [k] != ',')
219                                         k++;
220                                 pos = k;
221                         }
222
223                         return header.Substring (begin, k - begin).Trim ();
224                 }
225
226                 static bool IsWeekDay (string value)
227                 {
228                         foreach (string day in weekDays) {
229                                 if (value.ToLower ().Equals (day))
230                                         return true;
231                         }
232                         return false;
233                 }
234
235                 static string[] weekDays =
236                         new string[] { "mon", "tue", "wed", "thu", "fri", "sat", "sun",
237                                        "monday", "tuesday", "wednesday", "thursday",
238                                        "friday", "saturday", "sunday" };
239
240                 static string[] cookieExpiresFormats =
241                         new string[] { "r",
242                                         "ddd, dd'-'MMM'-'yyyy HH':'mm':'ss 'GMT'",
243                                         "ddd, dd'-'MMM'-'yy HH':'mm':'ss 'GMT'" };
244
245                 static DateTime TryParseCookieExpires (string value)
246                 {
247                         if (String.IsNullOrEmpty (value))
248                                 return DateTime.MinValue;
249
250                         for (int i = 0; i < cookieExpiresFormats.Length; i++) {
251                                 try {
252                                         DateTime cookieExpiresUtc = DateTime.ParseExact (value, cookieExpiresFormats [i], CultureInfo.InvariantCulture);
253
254                                         //convert UTC/GMT time to local time
255                                         cookieExpiresUtc = DateTime.SpecifyKind (cookieExpiresUtc, DateTimeKind.Utc);
256                                         return TimeZone.CurrentTimeZone.ToLocalTime (cookieExpiresUtc);
257                                 } catch {}
258                         }
259
260                         //If we can't parse Expires, use cookie as session cookie (expires is DateTime.MinValue)
261                         return DateTime.MinValue;
262                 }
263         }
264 }
265