Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(212)

Side by Side Diff: net/cookies/canonical_cookie.cc

Issue 10785017: Move CanonicalCookie into separate files (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Diff Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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 <algorithm>
48 #include <set>
49
50 #include "base/basictypes.h"
51 #include "base/bind.h"
52 #include "base/callback.h"
53 #include "base/format_macros.h"
54 #include "base/logging.h"
55 #include "base/memory/scoped_ptr.h"
56 #include "base/message_loop.h"
57 #include "base/message_loop_proxy.h"
58 #include "base/metrics/histogram.h"
59 #include "base/string_tokenizer.h"
60 #include "base/string_util.h"
61 #include "base/stringprintf.h"
62 #include "googleurl/src/gurl.h"
63 #include "googleurl/src/url_canon.h"
64 #include "net/cookies/cookie_util.h"
65 #include "net/cookies/parsed_cookie.h"
66 #include "net/base/registry_controlled_domain.h"
67
68 using base::Time;
69 using base::TimeDelta;
70
71 namespace net {
72
73 namespace {
74
75 // Determine the cookie domain to use for setting the specified cookie.
76 bool GetCookieDomain(const GURL& url,
77 const ParsedCookie& pc,
78 std::string* result) {
79 std::string domain_string;
80 if (pc.HasDomain())
81 domain_string = pc.Domain();
82 return cookie_util::GetCookieDomainWithString(url, domain_string, result);
83 }
84
85 std::string CanonPathWithString(const GURL& url,
86 const std::string& path_string) {
87 // The RFC says the path should be a prefix of the current URL path.
88 // However, Mozilla allows you to set any path for compatibility with
89 // broken websites. We unfortunately will mimic this behavior. We try
90 // to be generous and accept cookies with an invalid path attribute, and
91 // default the path to something reasonable.
92
93 // The path was supplied in the cookie, we'll take it.
94 if (!path_string.empty() && path_string[0] == '/')
95 return path_string;
96
97 // The path was not supplied in the cookie or invalid, we will default
98 // to the current URL path.
99 // """Defaults to the path of the request URL that generated the
100 // Set-Cookie response, up to, but not including, the
101 // right-most /."""
102 // How would this work for a cookie on /? We will include it then.
103 const std::string& url_path = url.path();
104
105 size_t idx = url_path.find_last_of('/');
106
107 // The cookie path was invalid or a single '/'.
108 if (idx == 0 || idx == std::string::npos)
109 return std::string("/");
110
111 // Return up to the rightmost '/'.
112 return url_path.substr(0, idx);
113 }
114
115 } // namespace
116
117 CanonicalCookie::CanonicalCookie()
118 : secure_(false),
119 httponly_(false) {
120 SetSessionCookieExpiryTime();
121 }
122
123 CanonicalCookie::CanonicalCookie(
124 const GURL& url, const std::string& name, const std::string& value,
125 const std::string& domain, const std::string& path,
126 const std::string& mac_key, const std::string& mac_algorithm,
127 const base::Time& creation, const base::Time& expiration,
128 const base::Time& last_access, bool secure, bool httponly)
129 : source_(GetCookieSourceFromURL(url)),
130 name_(name),
131 value_(value),
132 domain_(domain),
133 path_(path),
134 mac_key_(mac_key),
135 mac_algorithm_(mac_algorithm),
136 creation_date_(creation),
137 expiry_date_(expiration),
138 last_access_date_(last_access),
139 secure_(secure),
140 httponly_(httponly) {
141 if (expiration.is_null())
142 SetSessionCookieExpiryTime();
143 }
144
145 CanonicalCookie::CanonicalCookie(const GURL& url, const ParsedCookie& pc)
146 : source_(GetCookieSourceFromURL(url)),
147 name_(pc.Name()),
148 value_(pc.Value()),
149 path_(CanonPath(url, pc)),
150 mac_key_(pc.MACKey()),
151 mac_algorithm_(pc.MACAlgorithm()),
152 creation_date_(Time::Now()),
153 last_access_date_(Time()),
154 secure_(pc.IsSecure()),
155 httponly_(pc.IsHttpOnly()) {
156 if (pc.HasExpires())
157 expiry_date_ = CanonExpiration(pc, creation_date_, creation_date_);
158 else
159 SetSessionCookieExpiryTime();
160
161 // Do the best we can with the domain.
162 std::string cookie_domain;
163 std::string domain_string;
164 if (pc.HasDomain()) {
165 domain_string = pc.Domain();
166 }
167 bool result
168 = cookie_util::GetCookieDomainWithString(url, domain_string,
169 &cookie_domain);
170 // Caller is responsible for passing in good arguments.
171 DCHECK(result);
172 domain_ = cookie_domain;
173 }
174
175 CanonicalCookie::~CanonicalCookie() {
176 }
177
178 std::string CanonicalCookie::GetCookieSourceFromURL(const GURL& url) {
179 if (url.SchemeIsFile())
180 return url.spec();
181
182 url_canon::Replacements<char> replacements;
183 replacements.ClearPort();
184 if (url.SchemeIsSecure())
185 replacements.SetScheme("http", url_parse::Component(0, 4));
186
187 return url.GetOrigin().ReplaceComponents(replacements).spec();
188 }
189
190 // static
191 std::string CanonicalCookie::CanonPath(const GURL& url,
192 const ParsedCookie& pc) {
193 std::string path_string;
194 if (pc.HasPath())
195 path_string = pc.Path();
196 return CanonPathWithString(url, path_string);
197 }
198
199 // static
200 Time CanonicalCookie::CanonExpiration(const ParsedCookie& pc,
201 const Time& current,
202 const Time& server_time) {
203 // First, try the Max-Age attribute.
204 uint64 max_age = 0;
205 if (pc.HasMaxAge() &&
206 #ifdef COMPILER_MSVC
207 sscanf_s(
208 #else
209 sscanf(
210 #endif
211 pc.MaxAge().c_str(), " %" PRIu64, &max_age) == 1) {
212 return current + TimeDelta::FromSeconds(max_age);
213 }
214
215 // Try the Expires attribute.
216 if (pc.HasExpires()) {
217 // Adjust for clock skew between server and host.
218 return current + (ParsedCookie::ParseCookieTime(pc.Expires()) -
219 server_time);
220 }
221
222 // Invalid or no expiration, persistent cookie.
223 return Time();
224 }
225
226 void CanonicalCookie::SetSessionCookieExpiryTime() {
227 #if defined(ENABLE_PERSISTENT_SESSION_COOKIES)
228 // Mobile apps can sometimes be shut down without any warning, so the session
229 // cookie has to be persistent and given a default expiration time.
230 expiry_date_ = base::Time::Now() +
231 base::TimeDelta::FromDays(kPersistentSessionCookieExpiryInDays);
232 #endif
233 }
234
235 CanonicalCookie* CanonicalCookie::Create(const GURL& url,
236 const ParsedCookie& pc) {
237 if (!pc.IsValid()) {
238 return NULL;
239 }
240
241 std::string domain_string;
242 if (!GetCookieDomain(url, pc, &domain_string)) {
243 return NULL;
244 }
245 std::string path_string = CanonPath(url, pc);
246 std::string mac_key = pc.HasMACKey() ? pc.MACKey() : std::string();
247 std::string mac_algorithm = pc.HasMACAlgorithm() ?
248 pc.MACAlgorithm() : std::string();
249 Time creation_time = Time::Now();
250 Time expiration_time;
251 if (pc.HasExpires())
252 expiration_time = ParsedCookie::ParseCookieTime(pc.Expires());
253
254 return (Create(url, pc.Name(), pc.Value(), domain_string, path_string,
255 mac_key, mac_algorithm, creation_time, expiration_time,
256 pc.IsSecure(), pc.IsHttpOnly()));
257 }
258
259 CanonicalCookie* CanonicalCookie::Create(const GURL& url,
260 const std::string& name,
261 const std::string& value,
262 const std::string& domain,
263 const std::string& path,
264 const std::string& mac_key,
265 const std::string& mac_algorithm,
266 const base::Time& creation,
267 const base::Time& expiration,
268 bool secure,
269 bool http_only) {
270 // Expect valid attribute tokens and values, as defined by the ParsedCookie
271 // logic, otherwise don't create the cookie.
272 std::string parsed_name = ParsedCookie::ParseTokenString(name);
273 if (parsed_name != name)
274 return NULL;
275 std::string parsed_value = ParsedCookie::ParseValueString(value);
276 if (parsed_value != value)
277 return NULL;
278
279 std::string parsed_domain = ParsedCookie::ParseValueString(domain);
280 if (parsed_domain != domain)
281 return NULL;
282 std::string cookie_domain;
283 if (!cookie_util::GetCookieDomainWithString(url, parsed_domain,
284 &cookie_domain)) {
285 return NULL;
286 }
287
288 std::string parsed_path = ParsedCookie::ParseValueString(path);
289 if (parsed_path != path)
290 return NULL;
291
292 std::string cookie_path = CanonPathWithString(url, parsed_path);
293 // Expect that the path was either not specified (empty), or is valid.
294 if (!parsed_path.empty() && cookie_path != parsed_path)
295 return NULL;
296 // Canonicalize path again to make sure it escapes characters as needed.
297 url_parse::Component path_component(0, cookie_path.length());
298 url_canon::RawCanonOutputT<char> canon_path;
299 url_parse::Component canon_path_component;
300 url_canon::CanonicalizePath(cookie_path.data(), path_component,
301 &canon_path, &canon_path_component);
302 cookie_path = std::string(canon_path.data() + canon_path_component.begin,
303 canon_path_component.len);
304
305 return new CanonicalCookie(url, parsed_name, parsed_value, cookie_domain,
306 cookie_path, mac_key, mac_algorithm, creation,
307 expiration, creation, secure, http_only);
308 }
309
310 bool CanonicalCookie::IsOnPath(const std::string& url_path) const {
311
312 // A zero length would be unsafe for our trailing '/' checks, and
313 // would also make no sense for our prefix match. The code that
314 // creates a CanonicalCookie should make sure the path is never zero length,
315 // but we double check anyway.
316 if (path_.empty())
317 return false;
318
319 // The Mozilla code broke this into three cases, based on if the cookie path
320 // was longer, the same length, or shorter than the length of the url path.
321 // I think the approach below is simpler.
322
323 // Make sure the cookie path is a prefix of the url path. If the
324 // url path is shorter than the cookie path, then the cookie path
325 // can't be a prefix.
326 if (url_path.find(path_) != 0)
327 return false;
328
329 // Now we know that url_path is >= cookie_path, and that cookie_path
330 // is a prefix of url_path. If they are the are the same length then
331 // they are identical, otherwise we need an additional check:
332
333 // In order to avoid in correctly matching a cookie path of /blah
334 // with a request path of '/blahblah/', we need to make sure that either
335 // the cookie path ends in a trailing '/', or that we prefix up to a '/'
336 // in the url path. Since we know that the url path length is greater
337 // than the cookie path length, it's safe to index one byte past.
338 if (path_.length() != url_path.length() &&
339 path_[path_.length() - 1] != '/' &&
340 url_path[path_.length()] != '/')
341 return false;
342
343 return true;
344 }
345
346 bool CanonicalCookie::IsDomainMatch(const std::string& scheme,
347 const std::string& host) const {
348 // Can domain match in two ways; as a domain cookie (where the cookie
349 // domain begins with ".") or as a host cookie (where it doesn't).
350
351 // Some consumers of the CookieMonster expect to set cookies on
352 // URLs like http://.strange.url. To retrieve cookies in this instance,
353 // we allow matching as a host cookie even when the domain_ starts with
354 // a period.
355 if (host == domain_)
356 return true;
357
358 // Domain cookie must have an initial ".". To match, it must be
359 // equal to url's host with initial period removed, or a suffix of
360 // it.
361
362 // Arguably this should only apply to "http" or "https" cookies, but
363 // extension cookie tests currently use the funtionality, and if we
364 // ever decide to implement that it should be done by preventing
365 // such cookies from being set.
366 if (domain_.empty() || domain_[0] != '.')
367 return false;
368
369 // The host with a "." prefixed.
370 if (domain_.compare(1, std::string::npos, host) == 0)
371 return true;
372
373 // A pure suffix of the host (ok since we know the domain already
374 // starts with a ".")
375 return (host.length() > domain_.length() &&
376 host.compare(host.length() - domain_.length(),
377 domain_.length(), domain_) == 0);
378 }
379
380 std::string CanonicalCookie::DebugString() const {
381 return base::StringPrintf(
382 "name: %s value: %s domain: %s path: %s creation: %"
383 PRId64,
384 name_.c_str(), value_.c_str(),
385 domain_.c_str(), path_.c_str(),
386 static_cast<int64>(creation_date_.ToTimeT()));
387 }
388
389 } // namespace
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698