We started redesigning GpuMemoryBuffer interface to handle multiple buffers [0].
[chromium-blink-merge.git] / net / cookies / canonical_cookie.cc
blob5932c23a8818ef4cf6e0f60983f9694d246c8b78
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.
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
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/
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.
20 * The Original Code is mozilla.org code.
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.
27 * Contributor(s):
28 * Daniel Witte (dwitte@stanford.edu)
29 * Michiel van Leeuwen (mvl@exedo.nl)
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.
43 * ***** END LICENSE BLOCK ***** */
45 #include "net/cookies/canonical_cookie.h"
47 #include "base/basictypes.h"
48 #include "base/format_macros.h"
49 #include "base/logging.h"
50 #include "base/strings/stringprintf.h"
51 #include "net/cookies/cookie_util.h"
52 #include "net/cookies/parsed_cookie.h"
53 #include "url/gurl.h"
54 #include "url/url_canon.h"
56 using base::Time;
57 using base::TimeDelta;
59 namespace net {
61 namespace {
63 const int kVlogSetCookies = 7;
65 // Determine the cookie domain to use for setting the specified cookie.
66 bool GetCookieDomain(const GURL& url,
67 const ParsedCookie& pc,
68 std::string* result) {
69 std::string domain_string;
70 if (pc.HasDomain())
71 domain_string = pc.Domain();
72 return cookie_util::GetCookieDomainWithString(url, domain_string, result);
75 std::string CanonPathWithString(const GURL& url,
76 const std::string& path_string) {
77 // The RFC says the path should be a prefix of the current URL path.
78 // However, Mozilla allows you to set any path for compatibility with
79 // broken websites. We unfortunately will mimic this behavior. We try
80 // to be generous and accept cookies with an invalid path attribute, and
81 // default the path to something reasonable.
83 // The path was supplied in the cookie, we'll take it.
84 if (!path_string.empty() && path_string[0] == '/')
85 return path_string;
87 // The path was not supplied in the cookie or invalid, we will default
88 // to the current URL path.
89 // """Defaults to the path of the request URL that generated the
90 // Set-Cookie response, up to, but not including, the
91 // right-most /."""
92 // How would this work for a cookie on /? We will include it then.
93 const std::string& url_path = url.path();
95 size_t idx = url_path.find_last_of('/');
97 // The cookie path was invalid or a single '/'.
98 if (idx == 0 || idx == std::string::npos)
99 return std::string("/");
101 // Return up to the rightmost '/'.
102 return url_path.substr(0, idx);
105 // Compares cookies using name, domain and path, so that "equivalent" cookies
106 // (per RFC 2965) are equal to each other.
107 int PartialCookieOrdering(const net::CanonicalCookie& a,
108 const net::CanonicalCookie& b) {
109 int diff = a.Name().compare(b.Name());
110 if (diff != 0)
111 return diff;
113 diff = a.Domain().compare(b.Domain());
114 if (diff != 0)
115 return diff;
117 return a.Path().compare(b.Path());
120 } // namespace
122 CanonicalCookie::CanonicalCookie()
123 : secure_(false),
124 httponly_(false) {
127 CanonicalCookie::CanonicalCookie(const GURL& url,
128 const std::string& name,
129 const std::string& value,
130 const std::string& domain,
131 const std::string& path,
132 const base::Time& creation,
133 const base::Time& expiration,
134 const base::Time& last_access,
135 bool secure,
136 bool httponly,
137 bool firstpartyonly,
138 CookiePriority priority)
139 : source_(GetCookieSourceFromURL(url)),
140 name_(name),
141 value_(value),
142 domain_(domain),
143 path_(path),
144 creation_date_(creation),
145 expiry_date_(expiration),
146 last_access_date_(last_access),
147 secure_(secure),
148 httponly_(httponly),
149 first_party_only_(firstpartyonly),
150 priority_(priority) {
153 CanonicalCookie::CanonicalCookie(const GURL& url, const ParsedCookie& pc)
154 : source_(GetCookieSourceFromURL(url)),
155 name_(pc.Name()),
156 value_(pc.Value()),
157 path_(CanonPath(url, pc)),
158 creation_date_(Time::Now()),
159 last_access_date_(Time()),
160 secure_(pc.IsSecure()),
161 httponly_(pc.IsHttpOnly()),
162 first_party_only_(pc.IsFirstPartyOnly()),
163 priority_(pc.Priority()) {
164 if (pc.HasExpires())
165 expiry_date_ = CanonExpiration(pc, creation_date_, creation_date_);
167 // Do the best we can with the domain.
168 std::string cookie_domain;
169 std::string domain_string;
170 if (pc.HasDomain()) {
171 domain_string = pc.Domain();
173 bool result
174 = cookie_util::GetCookieDomainWithString(url, domain_string,
175 &cookie_domain);
176 // Caller is responsible for passing in good arguments.
177 DCHECK(result);
178 domain_ = cookie_domain;
181 CanonicalCookie::~CanonicalCookie() {
184 std::string CanonicalCookie::GetCookieSourceFromURL(const GURL& url) {
185 if (url.SchemeIsFile())
186 return url.spec();
188 url::Replacements<char> replacements;
189 replacements.ClearPort();
190 if (url.SchemeIsSecure())
191 replacements.SetScheme("http", url::Component(0, 4));
193 return url.GetOrigin().ReplaceComponents(replacements).spec();
196 // static
197 std::string CanonicalCookie::CanonPath(const GURL& url,
198 const ParsedCookie& pc) {
199 std::string path_string;
200 if (pc.HasPath())
201 path_string = pc.Path();
202 return CanonPathWithString(url, path_string);
205 // static
206 Time CanonicalCookie::CanonExpiration(const ParsedCookie& pc,
207 const Time& current,
208 const Time& server_time) {
209 // First, try the Max-Age attribute.
210 uint64 max_age = 0;
211 if (pc.HasMaxAge() &&
212 #ifdef COMPILER_MSVC
213 sscanf_s(
214 #else
215 sscanf(
216 #endif
217 pc.MaxAge().c_str(), " %" PRIu64, &max_age) == 1) {
218 return current + TimeDelta::FromSeconds(max_age);
221 // Try the Expires attribute.
222 if (pc.HasExpires() && !pc.Expires().empty()) {
223 // Adjust for clock skew between server and host.
224 base::Time parsed_expiry = cookie_util::ParseCookieTime(pc.Expires());
225 if (!parsed_expiry.is_null())
226 return parsed_expiry + (current - server_time);
229 // Invalid or no expiration, persistent cookie.
230 return Time();
233 // static
234 CanonicalCookie* CanonicalCookie::Create(const GURL& url,
235 const std::string& cookie_line,
236 const base::Time& creation_time,
237 const CookieOptions& options) {
238 ParsedCookie parsed_cookie(cookie_line);
240 if (!parsed_cookie.IsValid()) {
241 VLOG(kVlogSetCookies) << "WARNING: Couldn't parse cookie";
242 return NULL;
245 if (options.exclude_httponly() && parsed_cookie.IsHttpOnly()) {
246 VLOG(kVlogSetCookies) << "Create() is not creating a httponly cookie";
247 return NULL;
250 std::string cookie_domain;
251 if (!GetCookieDomain(url, parsed_cookie, &cookie_domain)) {
252 return NULL;
255 std::string cookie_path = CanonicalCookie::CanonPath(url, parsed_cookie);
256 Time server_time(creation_time);
257 if (options.has_server_time())
258 server_time = options.server_time();
260 Time cookie_expires = CanonicalCookie::CanonExpiration(parsed_cookie,
261 creation_time,
262 server_time);
264 return new CanonicalCookie(
265 url, parsed_cookie.Name(), parsed_cookie.Value(), cookie_domain,
266 cookie_path, creation_time, cookie_expires, creation_time,
267 parsed_cookie.IsSecure(), parsed_cookie.IsHttpOnly(),
268 parsed_cookie.IsFirstPartyOnly(), parsed_cookie.Priority());
271 CanonicalCookie* CanonicalCookie::Create(const GURL& url,
272 const std::string& name,
273 const std::string& value,
274 const std::string& domain,
275 const std::string& path,
276 const base::Time& creation,
277 const base::Time& expiration,
278 bool secure,
279 bool http_only,
280 bool first_party_only,
281 CookiePriority priority) {
282 // Expect valid attribute tokens and values, as defined by the ParsedCookie
283 // logic, otherwise don't create the cookie.
284 std::string parsed_name = ParsedCookie::ParseTokenString(name);
285 if (parsed_name != name)
286 return NULL;
287 std::string parsed_value = ParsedCookie::ParseValueString(value);
288 if (parsed_value != value)
289 return NULL;
291 std::string parsed_domain = ParsedCookie::ParseValueString(domain);
292 if (parsed_domain != domain)
293 return NULL;
294 std::string cookie_domain;
295 if (!cookie_util::GetCookieDomainWithString(url, parsed_domain,
296 &cookie_domain)) {
297 return NULL;
300 std::string parsed_path = ParsedCookie::ParseValueString(path);
301 if (parsed_path != path)
302 return NULL;
304 std::string cookie_path = CanonPathWithString(url, parsed_path);
305 // Expect that the path was either not specified (empty), or is valid.
306 if (!parsed_path.empty() && cookie_path != parsed_path)
307 return NULL;
308 // Canonicalize path again to make sure it escapes characters as needed.
309 url::Component path_component(0, cookie_path.length());
310 url::RawCanonOutputT<char> canon_path;
311 url::Component canon_path_component;
312 url::CanonicalizePath(cookie_path.data(), path_component, &canon_path,
313 &canon_path_component);
314 cookie_path = std::string(canon_path.data() + canon_path_component.begin,
315 canon_path_component.len);
317 return new CanonicalCookie(url, parsed_name, parsed_value, cookie_domain,
318 cookie_path, creation, expiration, creation,
319 secure, http_only, first_party_only, priority);
322 bool CanonicalCookie::IsOnPath(const std::string& url_path) const {
324 // A zero length would be unsafe for our trailing '/' checks, and
325 // would also make no sense for our prefix match. The code that
326 // creates a CanonicalCookie should make sure the path is never zero length,
327 // but we double check anyway.
328 if (path_.empty())
329 return false;
331 // The Mozilla code broke this into three cases, based on if the cookie path
332 // was longer, the same length, or shorter than the length of the url path.
333 // I think the approach below is simpler.
335 // Make sure the cookie path is a prefix of the url path. If the
336 // url path is shorter than the cookie path, then the cookie path
337 // can't be a prefix.
338 if (url_path.find(path_) != 0)
339 return false;
341 // Now we know that url_path is >= cookie_path, and that cookie_path
342 // is a prefix of url_path. If they are the are the same length then
343 // they are identical, otherwise we need an additional check:
345 // In order to avoid in correctly matching a cookie path of /blah
346 // with a request path of '/blahblah/', we need to make sure that either
347 // the cookie path ends in a trailing '/', or that we prefix up to a '/'
348 // in the url path. Since we know that the url path length is greater
349 // than the cookie path length, it's safe to index one byte past.
350 if (path_.length() != url_path.length() &&
351 path_[path_.length() - 1] != '/' &&
352 url_path[path_.length()] != '/')
353 return false;
355 return true;
358 bool CanonicalCookie::IsDomainMatch(const std::string& host) const {
359 // Can domain match in two ways; as a domain cookie (where the cookie
360 // domain begins with ".") or as a host cookie (where it doesn't).
362 // Some consumers of the CookieMonster expect to set cookies on
363 // URLs like http://.strange.url. To retrieve cookies in this instance,
364 // we allow matching as a host cookie even when the domain_ starts with
365 // a period.
366 if (host == domain_)
367 return true;
369 // Domain cookie must have an initial ".". To match, it must be
370 // equal to url's host with initial period removed, or a suffix of
371 // it.
373 // Arguably this should only apply to "http" or "https" cookies, but
374 // extension cookie tests currently use the funtionality, and if we
375 // ever decide to implement that it should be done by preventing
376 // such cookies from being set.
377 if (domain_.empty() || domain_[0] != '.')
378 return false;
380 // The host with a "." prefixed.
381 if (domain_.compare(1, std::string::npos, host) == 0)
382 return true;
384 // A pure suffix of the host (ok since we know the domain already
385 // starts with a ".")
386 return (host.length() > domain_.length() &&
387 host.compare(host.length() - domain_.length(),
388 domain_.length(), domain_) == 0);
391 bool CanonicalCookie::IncludeForRequestURL(const GURL& url,
392 const CookieOptions& options) const {
393 // Filter out HttpOnly cookies, per options.
394 if (options.exclude_httponly() && IsHttpOnly())
395 return false;
396 // Secure cookies should not be included in requests for URLs with an
397 // insecure scheme.
398 if (IsSecure() && !url.SchemeIsSecure())
399 return false;
400 // Don't include cookies for requests that don't apply to the cookie domain.
401 if (!IsDomainMatch(url.host()))
402 return false;
403 // Don't include cookies for requests with a url path that does not path
404 // match the cookie-path.
405 if (!IsOnPath(url.path()))
406 return false;
408 // Include first-party-only cookies iff |options| tells us to include all of
409 // them, or if a first-party URL is set and its origin matches the origin of
410 // |url|.
411 if (IsFirstPartyOnly() && !options.include_first_party_only() &&
412 options.first_party_url().GetOrigin() != url.GetOrigin()) {
413 return false;
416 return true;
419 std::string CanonicalCookie::DebugString() const {
420 return base::StringPrintf(
421 "name: %s value: %s domain: %s path: %s creation: %"
422 PRId64,
423 name_.c_str(), value_.c_str(),
424 domain_.c_str(), path_.c_str(),
425 static_cast<int64>(creation_date_.ToTimeT()));
428 bool CanonicalCookie::PartialCompare(const CanonicalCookie& other) const {
429 return PartialCookieOrdering(*this, other) < 0;
432 bool CanonicalCookie::FullCompare(const CanonicalCookie& other) const {
433 // Do the partial comparison first.
434 int diff = PartialCookieOrdering(*this, other);
435 if (diff != 0)
436 return diff < 0;
438 DCHECK(IsEquivalent(other));
440 // Compare other fields.
441 diff = Value().compare(other.Value());
442 if (diff != 0)
443 return diff < 0;
445 if (CreationDate() != other.CreationDate())
446 return CreationDate() < other.CreationDate();
448 if (ExpiryDate() != other.ExpiryDate())
449 return ExpiryDate() < other.ExpiryDate();
451 if (LastAccessDate() != other.LastAccessDate())
452 return LastAccessDate() < other.LastAccessDate();
454 if (IsSecure() != other.IsSecure())
455 return IsSecure();
457 if (IsHttpOnly() != other.IsHttpOnly())
458 return IsHttpOnly();
460 return Priority() < other.Priority();
463 } // namespace net