Index: net/cookies/canonical_cookie.cc |
diff --git a/net/cookies/canonical_cookie.cc b/net/cookies/canonical_cookie.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ebd2428497427dd5d319079d997b3e85729db879 |
--- /dev/null |
+++ b/net/cookies/canonical_cookie.cc |
@@ -0,0 +1,380 @@ |
+// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+// Portions of this code based on Mozilla: |
+// (netwerk/cookie/src/nsCookieService.cpp) |
+/* ***** BEGIN LICENSE BLOCK ***** |
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
+ * |
+ * The contents of this file are subject to the Mozilla Public License Version |
+ * 1.1 (the "License"); you may not use this file except in compliance with |
+ * the License. You may obtain a copy of the License at |
+ * http://www.mozilla.org/MPL/ |
+ * |
+ * Software distributed under the License is distributed on an "AS IS" basis, |
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
+ * for the specific language governing rights and limitations under the |
+ * License. |
+ * |
+ * The Original Code is mozilla.org code. |
+ * |
+ * The Initial Developer of the Original Code is |
+ * Netscape Communications Corporation. |
+ * Portions created by the Initial Developer are Copyright (C) 2003 |
+ * the Initial Developer. All Rights Reserved. |
+ * |
+ * Contributor(s): |
+ * Daniel Witte (dwitte@stanford.edu) |
+ * Michiel van Leeuwen (mvl@exedo.nl) |
+ * |
+ * Alternatively, the contents of this file may be used under the terms of |
+ * either the GNU General Public License Version 2 or later (the "GPL"), or |
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), |
+ * in which case the provisions of the GPL or the LGPL are applicable instead |
+ * of those above. If you wish to allow use of your version of this file only |
+ * under the terms of either the GPL or the LGPL, and not to allow others to |
+ * use your version of this file under the terms of the MPL, indicate your |
+ * decision by deleting the provisions above and replace them with the notice |
+ * and other provisions required by the GPL or the LGPL. If you do not delete |
+ * the provisions above, a recipient may use your version of this file under |
+ * the terms of any one of the MPL, the GPL or the LGPL. |
+ * |
+ * ***** END LICENSE BLOCK ***** */ |
+ |
+#include "net/cookies/canonical_cookie.h" |
+ |
+#include "base/basictypes.h" |
+#include "base/format_macros.h" |
+#include "base/logging.h" |
+#include "base/stringprintf.h" |
+#include "googleurl/src/gurl.h" |
+#include "googleurl/src/url_canon.h" |
+#include "net/cookies/cookie_util.h" |
+#include "net/cookies/parsed_cookie.h" |
+ |
+using base::Time; |
+using base::TimeDelta; |
+ |
+namespace net { |
+ |
+namespace { |
+ |
+#if defined(ENABLE_PERSISTENT_SESSION_COOKIES) |
+const int kPersistentSessionCookieExpiryInDays = 14; |
+#endif |
+ |
+// Determine the cookie domain to use for setting the specified cookie. |
+bool GetCookieDomain(const GURL& url, |
+ const ParsedCookie& pc, |
+ std::string* result) { |
+ std::string domain_string; |
+ if (pc.HasDomain()) |
+ domain_string = pc.Domain(); |
+ return cookie_util::GetCookieDomainWithString(url, domain_string, result); |
+} |
+ |
+std::string CanonPathWithString(const GURL& url, |
+ const std::string& path_string) { |
+ // The RFC says the path should be a prefix of the current URL path. |
+ // However, Mozilla allows you to set any path for compatibility with |
+ // broken websites. We unfortunately will mimic this behavior. We try |
+ // to be generous and accept cookies with an invalid path attribute, and |
+ // default the path to something reasonable. |
+ |
+ // The path was supplied in the cookie, we'll take it. |
+ if (!path_string.empty() && path_string[0] == '/') |
+ return path_string; |
+ |
+ // The path was not supplied in the cookie or invalid, we will default |
+ // to the current URL path. |
+ // """Defaults to the path of the request URL that generated the |
+ // Set-Cookie response, up to, but not including, the |
+ // right-most /.""" |
+ // How would this work for a cookie on /? We will include it then. |
+ const std::string& url_path = url.path(); |
+ |
+ size_t idx = url_path.find_last_of('/'); |
+ |
+ // The cookie path was invalid or a single '/'. |
+ if (idx == 0 || idx == std::string::npos) |
+ return std::string("/"); |
+ |
+ // Return up to the rightmost '/'. |
+ return url_path.substr(0, idx); |
+} |
+ |
+} // namespace |
+ |
+CanonicalCookie::CanonicalCookie() |
+ : secure_(false), |
+ httponly_(false) { |
+ SetSessionCookieExpiryTime(); |
+} |
+ |
+CanonicalCookie::CanonicalCookie( |
+ const GURL& url, const std::string& name, const std::string& value, |
+ const std::string& domain, const std::string& path, |
+ const std::string& mac_key, const std::string& mac_algorithm, |
+ const base::Time& creation, const base::Time& expiration, |
+ const base::Time& last_access, bool secure, bool httponly) |
+ : source_(GetCookieSourceFromURL(url)), |
+ name_(name), |
+ value_(value), |
+ domain_(domain), |
+ path_(path), |
+ mac_key_(mac_key), |
+ mac_algorithm_(mac_algorithm), |
+ creation_date_(creation), |
+ expiry_date_(expiration), |
+ last_access_date_(last_access), |
+ secure_(secure), |
+ httponly_(httponly) { |
+ if (expiration.is_null()) |
+ SetSessionCookieExpiryTime(); |
+} |
+ |
+CanonicalCookie::CanonicalCookie(const GURL& url, const ParsedCookie& pc) |
+ : source_(GetCookieSourceFromURL(url)), |
+ name_(pc.Name()), |
+ value_(pc.Value()), |
+ path_(CanonPath(url, pc)), |
+ mac_key_(pc.MACKey()), |
+ mac_algorithm_(pc.MACAlgorithm()), |
+ creation_date_(Time::Now()), |
+ last_access_date_(Time()), |
+ secure_(pc.IsSecure()), |
+ httponly_(pc.IsHttpOnly()) { |
+ if (pc.HasExpires()) |
+ expiry_date_ = CanonExpiration(pc, creation_date_, creation_date_); |
+ else |
+ SetSessionCookieExpiryTime(); |
+ |
+ // Do the best we can with the domain. |
+ std::string cookie_domain; |
+ std::string domain_string; |
+ if (pc.HasDomain()) { |
+ domain_string = pc.Domain(); |
+ } |
+ bool result |
+ = cookie_util::GetCookieDomainWithString(url, domain_string, |
+ &cookie_domain); |
+ // Caller is responsible for passing in good arguments. |
+ DCHECK(result); |
+ domain_ = cookie_domain; |
+} |
+ |
+CanonicalCookie::~CanonicalCookie() { |
+} |
+ |
+std::string CanonicalCookie::GetCookieSourceFromURL(const GURL& url) { |
+ if (url.SchemeIsFile()) |
+ return url.spec(); |
+ |
+ url_canon::Replacements<char> replacements; |
+ replacements.ClearPort(); |
+ if (url.SchemeIsSecure()) |
+ replacements.SetScheme("http", url_parse::Component(0, 4)); |
+ |
+ return url.GetOrigin().ReplaceComponents(replacements).spec(); |
+} |
+ |
+// static |
+std::string CanonicalCookie::CanonPath(const GURL& url, |
+ const ParsedCookie& pc) { |
+ std::string path_string; |
+ if (pc.HasPath()) |
+ path_string = pc.Path(); |
+ return CanonPathWithString(url, path_string); |
+} |
+ |
+// static |
+Time CanonicalCookie::CanonExpiration(const ParsedCookie& pc, |
+ const Time& current, |
+ const Time& server_time) { |
+ // First, try the Max-Age attribute. |
+ uint64 max_age = 0; |
+ if (pc.HasMaxAge() && |
+#ifdef COMPILER_MSVC |
+ sscanf_s( |
+#else |
+ sscanf( |
+#endif |
+ pc.MaxAge().c_str(), " %" PRIu64, &max_age) == 1) { |
+ return current + TimeDelta::FromSeconds(max_age); |
+ } |
+ |
+ // Try the Expires attribute. |
+ if (pc.HasExpires()) { |
+ // Adjust for clock skew between server and host. |
+ return current + (cookie_util::ParseCookieTime(pc.Expires()) - server_time); |
+ } |
+ |
+ // Invalid or no expiration, persistent cookie. |
+ return Time(); |
+} |
+ |
+void CanonicalCookie::SetSessionCookieExpiryTime() { |
+#if defined(ENABLE_PERSISTENT_SESSION_COOKIES) |
+ // Mobile apps can sometimes be shut down without any warning, so the session |
+ // cookie has to be persistent and given a default expiration time. |
+ expiry_date_ = base::Time::Now() + |
+ base::TimeDelta::FromDays(kPersistentSessionCookieExpiryInDays); |
+#endif |
+} |
+ |
+CanonicalCookie* CanonicalCookie::Create(const GURL& url, |
+ const ParsedCookie& pc) { |
+ if (!pc.IsValid()) { |
+ return NULL; |
+ } |
+ |
+ std::string domain_string; |
+ if (!GetCookieDomain(url, pc, &domain_string)) { |
+ return NULL; |
+ } |
+ std::string path_string = CanonPath(url, pc); |
+ std::string mac_key = pc.HasMACKey() ? pc.MACKey() : std::string(); |
+ std::string mac_algorithm = pc.HasMACAlgorithm() ? |
+ pc.MACAlgorithm() : std::string(); |
+ Time creation_time = Time::Now(); |
+ Time expiration_time; |
+ if (pc.HasExpires()) |
+ expiration_time = cookie_util::ParseCookieTime(pc.Expires()); |
+ |
+ return (Create(url, pc.Name(), pc.Value(), domain_string, path_string, |
+ mac_key, mac_algorithm, creation_time, expiration_time, |
+ pc.IsSecure(), pc.IsHttpOnly())); |
+} |
+ |
+CanonicalCookie* CanonicalCookie::Create(const GURL& url, |
+ const std::string& name, |
+ const std::string& value, |
+ const std::string& domain, |
+ const std::string& path, |
+ const std::string& mac_key, |
+ const std::string& mac_algorithm, |
+ const base::Time& creation, |
+ const base::Time& expiration, |
+ bool secure, |
+ bool http_only) { |
+ // Expect valid attribute tokens and values, as defined by the ParsedCookie |
+ // logic, otherwise don't create the cookie. |
+ std::string parsed_name = ParsedCookie::ParseTokenString(name); |
+ if (parsed_name != name) |
+ return NULL; |
+ std::string parsed_value = ParsedCookie::ParseValueString(value); |
+ if (parsed_value != value) |
+ return NULL; |
+ |
+ std::string parsed_domain = ParsedCookie::ParseValueString(domain); |
+ if (parsed_domain != domain) |
+ return NULL; |
+ std::string cookie_domain; |
+ if (!cookie_util::GetCookieDomainWithString(url, parsed_domain, |
+ &cookie_domain)) { |
+ return NULL; |
+ } |
+ |
+ std::string parsed_path = ParsedCookie::ParseValueString(path); |
+ if (parsed_path != path) |
+ return NULL; |
+ |
+ std::string cookie_path = CanonPathWithString(url, parsed_path); |
+ // Expect that the path was either not specified (empty), or is valid. |
+ if (!parsed_path.empty() && cookie_path != parsed_path) |
+ return NULL; |
+ // Canonicalize path again to make sure it escapes characters as needed. |
+ url_parse::Component path_component(0, cookie_path.length()); |
+ url_canon::RawCanonOutputT<char> canon_path; |
+ url_parse::Component canon_path_component; |
+ url_canon::CanonicalizePath(cookie_path.data(), path_component, |
+ &canon_path, &canon_path_component); |
+ cookie_path = std::string(canon_path.data() + canon_path_component.begin, |
+ canon_path_component.len); |
+ |
+ return new CanonicalCookie(url, parsed_name, parsed_value, cookie_domain, |
+ cookie_path, mac_key, mac_algorithm, creation, |
+ expiration, creation, secure, http_only); |
+} |
+ |
+bool CanonicalCookie::IsOnPath(const std::string& url_path) const { |
+ |
+ // A zero length would be unsafe for our trailing '/' checks, and |
+ // would also make no sense for our prefix match. The code that |
+ // creates a CanonicalCookie should make sure the path is never zero length, |
+ // but we double check anyway. |
+ if (path_.empty()) |
+ return false; |
+ |
+ // The Mozilla code broke this into three cases, based on if the cookie path |
+ // was longer, the same length, or shorter than the length of the url path. |
+ // I think the approach below is simpler. |
+ |
+ // Make sure the cookie path is a prefix of the url path. If the |
+ // url path is shorter than the cookie path, then the cookie path |
+ // can't be a prefix. |
+ if (url_path.find(path_) != 0) |
+ return false; |
+ |
+ // Now we know that url_path is >= cookie_path, and that cookie_path |
+ // is a prefix of url_path. If they are the are the same length then |
+ // they are identical, otherwise we need an additional check: |
+ |
+ // In order to avoid in correctly matching a cookie path of /blah |
+ // with a request path of '/blahblah/', we need to make sure that either |
+ // the cookie path ends in a trailing '/', or that we prefix up to a '/' |
+ // in the url path. Since we know that the url path length is greater |
+ // than the cookie path length, it's safe to index one byte past. |
+ if (path_.length() != url_path.length() && |
+ path_[path_.length() - 1] != '/' && |
+ url_path[path_.length()] != '/') |
+ return false; |
+ |
+ return true; |
+} |
+ |
+bool CanonicalCookie::IsDomainMatch(const std::string& scheme, |
+ const std::string& host) const { |
+ // Can domain match in two ways; as a domain cookie (where the cookie |
+ // domain begins with ".") or as a host cookie (where it doesn't). |
+ |
+ // Some consumers of the CookieMonster expect to set cookies on |
+ // URLs like http://.strange.url. To retrieve cookies in this instance, |
+ // we allow matching as a host cookie even when the domain_ starts with |
+ // a period. |
+ if (host == domain_) |
+ return true; |
+ |
+ // Domain cookie must have an initial ".". To match, it must be |
+ // equal to url's host with initial period removed, or a suffix of |
+ // it. |
+ |
+ // Arguably this should only apply to "http" or "https" cookies, but |
+ // extension cookie tests currently use the funtionality, and if we |
+ // ever decide to implement that it should be done by preventing |
+ // such cookies from being set. |
+ if (domain_.empty() || domain_[0] != '.') |
+ return false; |
+ |
+ // The host with a "." prefixed. |
+ if (domain_.compare(1, std::string::npos, host) == 0) |
+ return true; |
+ |
+ // A pure suffix of the host (ok since we know the domain already |
+ // starts with a ".") |
+ return (host.length() > domain_.length() && |
+ host.compare(host.length() - domain_.length(), |
+ domain_.length(), domain_) == 0); |
+} |
+ |
+std::string CanonicalCookie::DebugString() const { |
+ return base::StringPrintf( |
+ "name: %s value: %s domain: %s path: %s creation: %" |
+ PRId64, |
+ name_.c_str(), value_.c_str(), |
+ domain_.c_str(), path_.c_str(), |
+ static_cast<int64>(creation_date_.ToTimeT())); |
+} |
+ |
+} // namespace |
eroman
2012/07/18 20:58:36
Isn't this namespace net?
|