1 //
2 // System.Web.HttpCookie.cs
3 //
4 // Author:
5 //	Chris Toshok (toshok@novell.com)
6 //	Katharina Bogad (bogad@cs.tum.edu)
7 //
8 
9 //
10 // Copyright (C) 2005-2009 Novell, Inc (http://www.novell.com)
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 //
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 //
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31 
32 using System.Text;
33 using System.Collections.Specialized;
34 using System.Security.Permissions;
35 using System.Web.Configuration;
36 
37 namespace System.Web
38 {
39 	[Flags]
40 	internal enum CookieFlags : byte {
41 		Secure = 1,
42 			HttpOnly = 2
43 			}
44 
45 	// CAS - no InheritanceDemand here as the class is sealed
46 	[AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
47 	public sealed class HttpCookie {
48 
49 		string path = "/";
50 		string domain;
51 		DateTime expires = DateTime.MinValue;
52 		string name;
53 		CookieFlags flags = 0;
54 		NameValueCollection values;
55 
56 		[Obsolete]
HttpCookie(string name, string value, string path, DateTime expires)57 		internal HttpCookie (string name, string value, string path, DateTime expires)
58 		{
59 			this.name = name;
60 			this.values = new CookieNVC();
61 			this.Value = value;
62 			this.path = path;
63 			this.expires = expires;
64 		}
65 
HttpCookie(string name)66 		public HttpCookie (string name)
67 		{
68 			this.name = name;
69 			values = new CookieNVC();
70 			Value = "";
71 
72 			HttpCookiesSection cookieConfig = (HttpCookiesSection) WebConfigurationManager.GetSection ("system.web/httpCookies");
73 
74 			if(!string.IsNullOrWhiteSpace(cookieConfig.Domain))
75 				domain = cookieConfig.Domain;
76 
77 			if(cookieConfig.HttpOnlyCookies)
78 				flags |= CookieFlags.HttpOnly;
79 
80 			if(cookieConfig.RequireSSL)
81 				flags |= CookieFlags.Secure;
82 		}
83 
HttpCookie(string name, string value)84 		public HttpCookie (string name, string value)
85 		: this (name)
86 		{
87 			Value = value;
88 		}
89 
GetCookieHeaderValue()90 		internal string GetCookieHeaderValue ()
91 		{
92 			StringBuilder builder = new StringBuilder ();
93 
94 			builder.Append (name);
95 			builder.Append ("=");
96 			builder.Append (Value);
97 
98 			if (domain != null) {
99 				builder.Append ("; domain=");
100 				builder.Append (domain);
101 			}
102 
103 			if (path != null) {
104 				builder.Append ("; path=");
105 				builder.Append (path);
106 			}
107 
108 			if (expires != DateTime.MinValue) {
109 				builder.Append ("; expires=");
110 				builder.Append (expires.ToUniversalTime().ToString("r"));
111 			}
112 
113 			if ((flags & CookieFlags.Secure) != 0) {
114 				builder.Append ("; secure");
115 			}
116 
117 			if ((flags & CookieFlags.HttpOnly) != 0){
118 				builder.Append ("; HttpOnly");
119 			}
120 
121 			return builder.ToString ();
122 		}
123 
124 		public string Domain {
125 			get {
126 				return domain;
127 			}
128 			set {
129 				domain = value;
130 			}
131 		}
132 
133 		public DateTime Expires {
134 			get {
135 				return expires;
136 			}
137 			set {
138 				expires = value;
139 			}
140 		}
141 
142 		public bool HasKeys {
143 			get {
144 				return values.HasKeys();
145 			}
146 		}
147 
148 
149 		public string this [ string key ] {
150 			get {
151 				return values [ key ];
152 			}
153 			set {
154 				values [ key ] = value;
155 			}
156 		}
157 
158 		public string Name {
159 			get {
160 				return name;
161 			}
162 			set {
163 				name = value;
164 			}
165 		}
166 
167 		public string Path {
168 			get {
169 				return path;
170 			}
171 			set {
172 				path = value;
173 			}
174 		}
175 
176 		public bool Secure {
177 			get {
178 				return (flags & CookieFlags.Secure) == CookieFlags.Secure;
179 			}
180 			set {
181 				if (value)
182 					flags |= CookieFlags.Secure;
183 				else
184 					flags &= ~CookieFlags.Secure;
185 			}
186 		}
187 
188 		public string Value {
189 			get {
190 				return HttpUtility.UrlDecode(values.ToString ());
191 			}
192 			set {
193 				values.Clear ();
194 
195 				if (value != null && value != "") {
196 					string [] components = value.Split ('&');
197 					foreach (string kv in components){
198 						int pos = kv.IndexOf ('=');
199 						if (pos == -1){
200 							values.Add (null, kv);
201 						} else {
202 							string key = kv.Substring (0, pos);
203 							string val = kv.Substring (pos+1);
204 
205 							values.Add (key, val);
206 						}
207 					}
208 				}
209 			}
210 		}
211 
212 		public NameValueCollection Values {
213 			get {
214 				return values;
215 			}
216 		}
217 
218 		public bool HttpOnly {
219 			get {
220 				return (flags & CookieFlags.HttpOnly) == CookieFlags.HttpOnly;
221 			}
222 
223 			set {
224 				if (value)
225 					flags |= CookieFlags.HttpOnly;
226 				else
227 					flags &= ~CookieFlags.HttpOnly;
228 			}
229 		}
230 
231 		/*
232 		 * simple utility class that just overrides ToString
233 		 * to get the desired behavior for
234 		 * HttpCookie.Values
235 		 */
236 		[Serializable]
237 		sealed class CookieNVC : NameValueCollection
238 		{
CookieNVC()239 			public CookieNVC ()
240 				: base (StringComparer.OrdinalIgnoreCase)
241 			{
242 			}
243 
ToString()244 			public override string ToString ()
245 			{
246 				StringBuilder builder = new StringBuilder ("");
247 
248 				bool first_key = true;
249 				foreach (string key in Keys) {
250 					if (!first_key)
251 						builder.Append ("&");
252 
253                                        string[] vals = GetValues (key);
254                                        if(vals == null)
255                                                vals = new string[1] {String.Empty};
256 
257 				       bool first_val = true;
258                                        foreach (string v in vals) {
259 					       if (!first_val)
260 						       builder.Append ("&");
261 
262 					       if (key != null && key.Length > 0) {
263                                                        builder.Append (HttpUtility.UrlEncode(key));
264 						       builder.Append ("=");
265 					       }
266                                                if(v != null && v.Length > 0)
267                                                        builder.Append (HttpUtility.UrlEncode(v));
268 
269 					       first_val = false;
270 					}
271 					first_key = false;
272 				}
273 
274 				return builder.ToString();
275 			}
276 
277 			/* MS's implementation has the interesting quirk that if you do:
278 			 * cookie.Values[null] = "foo"
279 			 * it clears out the rest of the values.
280 			 */
Set(string name, string value)281 			public override void Set (string name, string value)
282 			{
283 				if (this.IsReadOnly)
284 					throw new NotSupportedException ("Collection is read-only");
285 
286 				if (name == null) {
287 					Clear();
288 					name = string.Empty;
289 				}
290 //				if (value == null)
291 //					value = string.Empty;
292 
293 				base.Set (name, value);
294 			}
295 		}
296 	}
297 }
298