OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 // Portions of this code based on Mozilla: | |
6 // (netwerk/cookie/src/nsCookieService.cpp) | |
7 /* ***** BEGIN LICENSE BLOCK ***** | |
8 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 | |
9 * | |
10 * The contents of this file are subject to the Mozilla Public License Version | |
11 * 1.1 (the "License"); you may not use this file except in compliance with | |
12 * the License. You may obtain a copy of the License at | |
13 * http://www.mozilla.org/MPL/ | |
14 * | |
15 * Software distributed under the License is distributed on an "AS IS" basis, | |
16 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |
17 * for the specific language governing rights and limitations under the | |
18 * License. | |
19 * | |
20 * The Original Code is mozilla.org code. | |
21 * | |
22 * The Initial Developer of the Original Code is | |
23 * Netscape Communications Corporation. | |
24 * Portions created by the Initial Developer are Copyright (C) 2003 | |
25 * the Initial Developer. All Rights Reserved. | |
26 * | |
27 * Contributor(s): | |
28 * Daniel Witte (dwitte@stanford.edu) | |
29 * Michiel van Leeuwen (mvl@exedo.nl) | |
30 * | |
31 * Alternatively, the contents of this file may be used under the terms of | |
32 * either the GNU General Public License Version 2 or later (the "GPL"), or | |
33 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), | |
34 * in which case the provisions of the GPL or the LGPL are applicable instead | |
35 * of those above. If you wish to allow use of your version of this file only | |
36 * under the terms of either the GPL or the LGPL, and not to allow others to | |
37 * use your version of this file under the terms of the MPL, indicate your | |
38 * decision by deleting the provisions above and replace them with the notice | |
39 * and other provisions required by the GPL or the LGPL. If you do not delete | |
40 * the provisions above, a recipient may use your version of this file under | |
41 * the terms of any one of the MPL, the GPL or the LGPL. | |
42 * | |
43 * ***** END LICENSE BLOCK ***** */ | |
44 | |
45 #include "net/cookies/canonical_cookie.h" | |
46 | |
47 #include "base/basictypes.h" | |
48 #include "base/format_macros.h" | |
49 #include "base/logging.h" | |
50 #include "base/stringprintf.h" | |
51 #include "googleurl/src/gurl.h" | |
52 #include "googleurl/src/url_canon.h" | |
53 #include "net/cookies/cookie_util.h" | |
54 #include "net/cookies/parsed_cookie.h" | |
55 | |
56 using base::Time; | |
57 using base::TimeDelta; | |
58 | |
59 namespace net { | |
60 | |
61 namespace { | |
62 | |
63 #if defined(ENABLE_PERSISTENT_SESSION_COOKIES) | |
64 const int kPersistentSessionCookieExpiryInDays = 14; | |
65 #endif | |
66 | |
67 // Determine the cookie domain to use for setting the specified cookie. | |
68 bool GetCookieDomain(const GURL& url, | |
69 const ParsedCookie& pc, | |
70 std::string* result) { | |
71 std::string domain_string; | |
72 if (pc.HasDomain()) | |
73 domain_string = pc.Domain(); | |
74 return cookie_util::GetCookieDomainWithString(url, domain_string, result); | |
75 } | |
76 | |
77 std::string CanonPathWithString(const GURL& url, | |
78 const std::string& path_string) { | |
79 // The RFC says the path should be a prefix of the current URL path. | |
80 // However, Mozilla allows you to set any path for compatibility with | |
81 // broken websites. We unfortunately will mimic this behavior. We try | |
82 // to be generous and accept cookies with an invalid path attribute, and | |
83 // default the path to something reasonable. | |
84 | |
85 // The path was supplied in the cookie, we'll take it. | |
86 if (!path_string.empty() && path_string[0] == '/') | |
87 return path_string; | |
88 | |
89 // The path was not supplied in the cookie or invalid, we will default | |
90 // to the current URL path. | |
91 // """Defaults to the path of the request URL that generated the | |
92 // Set-Cookie response, up to, but not including, the | |
93 // right-most /.""" | |
94 // How would this work for a cookie on /? We will include it then. | |
95 const std::string& url_path = url.path(); | |
96 | |
97 size_t idx = url_path.find_last_of('/'); | |
98 | |
99 // The cookie path was invalid or a single '/'. | |
100 if (idx == 0 || idx == std::string::npos) | |
101 return std::string("/"); | |
102 | |
103 // Return up to the rightmost '/'. | |
104 return url_path.substr(0, idx); | |
105 } | |
106 | |
107 } // namespace | |
108 | |
109 CanonicalCookie::CanonicalCookie() | |
110 : secure_(false), | |
111 httponly_(false) { | |
112 SetSessionCookieExpiryTime(); | |
113 } | |
114 | |
115 CanonicalCookie::CanonicalCookie( | |
116 const GURL& url, const std::string& name, const std::string& value, | |
117 const std::string& domain, const std::string& path, | |
118 const std::string& mac_key, const std::string& mac_algorithm, | |
119 const base::Time& creation, const base::Time& expiration, | |
120 const base::Time& last_access, bool secure, bool httponly) | |
121 : source_(GetCookieSourceFromURL(url)), | |
122 name_(name), | |
123 value_(value), | |
124 domain_(domain), | |
125 path_(path), | |
126 mac_key_(mac_key), | |
127 mac_algorithm_(mac_algorithm), | |
128 creation_date_(creation), | |
129 expiry_date_(expiration), | |
130 last_access_date_(last_access), | |
131 secure_(secure), | |
132 httponly_(httponly) { | |
133 if (expiration.is_null()) | |
134 SetSessionCookieExpiryTime(); | |
135 } | |
136 | |
137 CanonicalCookie::CanonicalCookie(const GURL& url, const ParsedCookie& pc) | |
138 : source_(GetCookieSourceFromURL(url)), | |
139 name_(pc.Name()), | |
140 value_(pc.Value()), | |
141 path_(CanonPath(url, pc)), | |
142 mac_key_(pc.MACKey()), | |
143 mac_algorithm_(pc.MACAlgorithm()), | |
144 creation_date_(Time::Now()), | |
145 last_access_date_(Time()), | |
146 secure_(pc.IsSecure()), | |
147 httponly_(pc.IsHttpOnly()) { | |
148 if (pc.HasExpires()) | |
149 expiry_date_ = CanonExpiration(pc, creation_date_, creation_date_); | |
150 else | |
151 SetSessionCookieExpiryTime(); | |
152 | |
153 // Do the best we can with the domain. | |
154 std::string cookie_domain; | |
155 std::string domain_string; | |
156 if (pc.HasDomain()) { | |
157 domain_string = pc.Domain(); | |
158 } | |
159 bool result | |
160 = cookie_util::GetCookieDomainWithString(url, domain_string, | |
161 &cookie_domain); | |
162 // Caller is responsible for passing in good arguments. | |
163 DCHECK(result); | |
164 domain_ = cookie_domain; | |
165 } | |
166 | |
167 CanonicalCookie::~CanonicalCookie() { | |
168 } | |
169 | |
170 std::string CanonicalCookie::GetCookieSourceFromURL(const GURL& url) { | |
171 if (url.SchemeIsFile()) | |
172 return url.spec(); | |
173 | |
174 url_canon::Replacements<char> replacements; | |
175 replacements.ClearPort(); | |
176 if (url.SchemeIsSecure()) | |
177 replacements.SetScheme("http", url_parse::Component(0, 4)); | |
178 | |
179 return url.GetOrigin().ReplaceComponents(replacements).spec(); | |
180 } | |
181 | |
182 // static | |
183 std::string CanonicalCookie::CanonPath(const GURL& url, | |
184 const ParsedCookie& pc) { | |
185 std::string path_string; | |
186 if (pc.HasPath()) | |
187 path_string = pc.Path(); | |
188 return CanonPathWithString(url, path_string); | |
189 } | |
190 | |
191 // static | |
192 Time CanonicalCookie::CanonExpiration(const ParsedCookie& pc, | |
193 const Time& current, | |
194 const Time& server_time) { | |
195 // First, try the Max-Age attribute. | |
196 uint64 max_age = 0; | |
197 if (pc.HasMaxAge() && | |
198 #ifdef COMPILER_MSVC | |
199 sscanf_s( | |
200 #else | |
201 sscanf( | |
202 #endif | |
203 pc.MaxAge().c_str(), " %" PRIu64, &max_age) == 1) { | |
204 return current + TimeDelta::FromSeconds(max_age); | |
205 } | |
206 | |
207 // Try the Expires attribute. | |
208 if (pc.HasExpires()) { | |
209 // Adjust for clock skew between server and host. | |
210 return current + (cookie_util::ParseCookieTime(pc.Expires()) - server_time); | |
211 } | |
212 | |
213 // Invalid or no expiration, persistent cookie. | |
214 return Time(); | |
215 } | |
216 | |
217 void CanonicalCookie::SetSessionCookieExpiryTime() { | |
218 #if defined(ENABLE_PERSISTENT_SESSION_COOKIES) | |
219 // Mobile apps can sometimes be shut down without any warning, so the session | |
220 // cookie has to be persistent and given a default expiration time. | |
221 expiry_date_ = base::Time::Now() + | |
222 base::TimeDelta::FromDays(kPersistentSessionCookieExpiryInDays); | |
223 #endif | |
224 } | |
225 | |
226 CanonicalCookie* CanonicalCookie::Create(const GURL& url, | |
227 const ParsedCookie& pc) { | |
228 if (!pc.IsValid()) { | |
229 return NULL; | |
230 } | |
231 | |
232 std::string domain_string; | |
233 if (!GetCookieDomain(url, pc, &domain_string)) { | |
234 return NULL; | |
235 } | |
236 std::string path_string = CanonPath(url, pc); | |
237 std::string mac_key = pc.HasMACKey() ? pc.MACKey() : std::string(); | |
238 std::string mac_algorithm = pc.HasMACAlgorithm() ? | |
239 pc.MACAlgorithm() : std::string(); | |
240 Time creation_time = Time::Now(); | |
241 Time expiration_time; | |
242 if (pc.HasExpires()) | |
243 expiration_time = cookie_util::ParseCookieTime(pc.Expires()); | |
244 | |
245 return (Create(url, pc.Name(), pc.Value(), domain_string, path_string, | |
246 mac_key, mac_algorithm, creation_time, expiration_time, | |
247 pc.IsSecure(), pc.IsHttpOnly())); | |
248 } | |
249 | |
250 CanonicalCookie* CanonicalCookie::Create(const GURL& url, | |
251 const std::string& name, | |
252 const std::string& value, | |
253 const std::string& domain, | |
254 const std::string& path, | |
255 const std::string& mac_key, | |
256 const std::string& mac_algorithm, | |
257 const base::Time& creation, | |
258 const base::Time& expiration, | |
259 bool secure, | |
260 bool http_only) { | |
261 // Expect valid attribute tokens and values, as defined by the ParsedCookie | |
262 // logic, otherwise don't create the cookie. | |
263 std::string parsed_name = ParsedCookie::ParseTokenString(name); | |
264 if (parsed_name != name) | |
265 return NULL; | |
266 std::string parsed_value = ParsedCookie::ParseValueString(value); | |
267 if (parsed_value != value) | |
268 return NULL; | |
269 | |
270 std::string parsed_domain = ParsedCookie::ParseValueString(domain); | |
271 if (parsed_domain != domain) | |
272 return NULL; | |
273 std::string cookie_domain; | |
274 if (!cookie_util::GetCookieDomainWithString(url, parsed_domain, | |
275 &cookie_domain)) { | |
276 return NULL; | |
277 } | |
278 | |
279 std::string parsed_path = ParsedCookie::ParseValueString(path); | |
280 if (parsed_path != path) | |
281 return NULL; | |
282 | |
283 std::string cookie_path = CanonPathWithString(url, parsed_path); | |
284 // Expect that the path was either not specified (empty), or is valid. | |
285 if (!parsed_path.empty() && cookie_path != parsed_path) | |
286 return NULL; | |
287 // Canonicalize path again to make sure it escapes characters as needed. | |
288 url_parse::Component path_component(0, cookie_path.length()); | |
289 url_canon::RawCanonOutputT<char> canon_path; | |
290 url_parse::Component canon_path_component; | |
291 url_canon::CanonicalizePath(cookie_path.data(), path_component, | |
292 &canon_path, &canon_path_component); | |
293 cookie_path = std::string(canon_path.data() + canon_path_component.begin, | |
294 canon_path_component.len); | |
295 | |
296 return new CanonicalCookie(url, parsed_name, parsed_value, cookie_domain, | |
297 cookie_path, mac_key, mac_algorithm, creation, | |
298 expiration, creation, secure, http_only); | |
299 } | |
300 | |
301 bool CanonicalCookie::IsOnPath(const std::string& url_path) const { | |
302 | |
303 // A zero length would be unsafe for our trailing '/' checks, and | |
304 // would also make no sense for our prefix match. The code that | |
305 // creates a CanonicalCookie should make sure the path is never zero length, | |
306 // but we double check anyway. | |
307 if (path_.empty()) | |
308 return false; | |
309 | |
310 // The Mozilla code broke this into three cases, based on if the cookie path | |
311 // was longer, the same length, or shorter than the length of the url path. | |
312 // I think the approach below is simpler. | |
313 | |
314 // Make sure the cookie path is a prefix of the url path. If the | |
315 // url path is shorter than the cookie path, then the cookie path | |
316 // can't be a prefix. | |
317 if (url_path.find(path_) != 0) | |
318 return false; | |
319 | |
320 // Now we know that url_path is >= cookie_path, and that cookie_path | |
321 // is a prefix of url_path. If they are the are the same length then | |
322 // they are identical, otherwise we need an additional check: | |
323 | |
324 // In order to avoid in correctly matching a cookie path of /blah | |
325 // with a request path of '/blahblah/', we need to make sure that either | |
326 // the cookie path ends in a trailing '/', or that we prefix up to a '/' | |
327 // in the url path. Since we know that the url path length is greater | |
328 // than the cookie path length, it's safe to index one byte past. | |
329 if (path_.length() != url_path.length() && | |
330 path_[path_.length() - 1] != '/' && | |
331 url_path[path_.length()] != '/') | |
332 return false; | |
333 | |
334 return true; | |
335 } | |
336 | |
337 bool CanonicalCookie::IsDomainMatch(const std::string& scheme, | |
338 const std::string& host) const { | |
339 // Can domain match in two ways; as a domain cookie (where the cookie | |
340 // domain begins with ".") or as a host cookie (where it doesn't). | |
341 | |
342 // Some consumers of the CookieMonster expect to set cookies on | |
343 // URLs like http://.strange.url. To retrieve cookies in this instance, | |
344 // we allow matching as a host cookie even when the domain_ starts with | |
345 // a period. | |
346 if (host == domain_) | |
347 return true; | |
348 | |
349 // Domain cookie must have an initial ".". To match, it must be | |
350 // equal to url's host with initial period removed, or a suffix of | |
351 // it. | |
352 | |
353 // Arguably this should only apply to "http" or "https" cookies, but | |
354 // extension cookie tests currently use the funtionality, and if we | |
355 // ever decide to implement that it should be done by preventing | |
356 // such cookies from being set. | |
357 if (domain_.empty() || domain_[0] != '.') | |
358 return false; | |
359 | |
360 // The host with a "." prefixed. | |
361 if (domain_.compare(1, std::string::npos, host) == 0) | |
362 return true; | |
363 | |
364 // A pure suffix of the host (ok since we know the domain already | |
365 // starts with a ".") | |
366 return (host.length() > domain_.length() && | |
367 host.compare(host.length() - domain_.length(), | |
368 domain_.length(), domain_) == 0); | |
369 } | |
370 | |
371 std::string CanonicalCookie::DebugString() const { | |
372 return base::StringPrintf( | |
373 "name: %s value: %s domain: %s path: %s creation: %" | |
374 PRId64, | |
375 name_.c_str(), value_.c_str(), | |
376 domain_.c_str(), path_.c_str(), | |
377 static_cast<int64>(creation_date_.ToTimeT())); | |
378 } | |
379 | |
380 } // namespace | |
eroman
2012/07/18 20:58:36
Isn't this namespace net?
| |
OLD | NEW |