| 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/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" | |
| 55 | |
| 56 using base::Time; | |
| 57 using base::TimeDelta; | |
| 58 | |
| 59 namespace net { | |
| 60 | |
| 61 namespace { | |
| 62 | |
| 63 const int kVlogSetCookies = 7; | |
| 64 | |
| 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); | |
| 73 } | |
| 74 | |
| 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. | |
| 82 | |
| 83 // The path was supplied in the cookie, we'll take it. | |
| 84 if (!path_string.empty() && path_string[0] == '/') | |
| 85 return path_string; | |
| 86 | |
| 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(); | |
| 94 | |
| 95 size_t idx = url_path.find_last_of('/'); | |
| 96 | |
| 97 // The cookie path was invalid or a single '/'. | |
| 98 if (idx == 0 || idx == std::string::npos) | |
| 99 return std::string("/"); | |
| 100 | |
| 101 // Return up to the rightmost '/'. | |
| 102 return url_path.substr(0, idx); | |
| 103 } | |
| 104 | |
| 105 } // namespace | |
| 106 | |
| 107 CanonicalCookie::CanonicalCookie() | |
| 108 : secure_(false), | |
| 109 httponly_(false) { | |
| 110 } | |
| 111 | |
| 112 CanonicalCookie::CanonicalCookie( | |
| 113 const GURL& url, const std::string& name, const std::string& value, | |
| 114 const std::string& domain, const std::string& path, | |
| 115 const base::Time& creation, const base::Time& expiration, | |
| 116 const base::Time& last_access, bool secure, bool httponly, | |
| 117 CookiePriority priority) | |
| 118 : source_(GetCookieSourceFromURL(url)), | |
| 119 name_(name), | |
| 120 value_(value), | |
| 121 domain_(domain), | |
| 122 path_(path), | |
| 123 creation_date_(creation), | |
| 124 expiry_date_(expiration), | |
| 125 last_access_date_(last_access), | |
| 126 secure_(secure), | |
| 127 httponly_(httponly), | |
| 128 priority_(priority) { | |
| 129 } | |
| 130 | |
| 131 CanonicalCookie::CanonicalCookie(const GURL& url, const ParsedCookie& pc) | |
| 132 : source_(GetCookieSourceFromURL(url)), | |
| 133 name_(pc.Name()), | |
| 134 value_(pc.Value()), | |
| 135 path_(CanonPath(url, pc)), | |
| 136 creation_date_(Time::Now()), | |
| 137 last_access_date_(Time()), | |
| 138 secure_(pc.IsSecure()), | |
| 139 httponly_(pc.IsHttpOnly()), | |
| 140 priority_(pc.Priority()) { | |
| 141 if (pc.HasExpires()) | |
| 142 expiry_date_ = CanonExpiration(pc, creation_date_, creation_date_); | |
| 143 | |
| 144 // Do the best we can with the domain. | |
| 145 std::string cookie_domain; | |
| 146 std::string domain_string; | |
| 147 if (pc.HasDomain()) { | |
| 148 domain_string = pc.Domain(); | |
| 149 } | |
| 150 bool result | |
| 151 = cookie_util::GetCookieDomainWithString(url, domain_string, | |
| 152 &cookie_domain); | |
| 153 // Caller is responsible for passing in good arguments. | |
| 154 DCHECK(result); | |
| 155 domain_ = cookie_domain; | |
| 156 } | |
| 157 | |
| 158 CanonicalCookie::~CanonicalCookie() { | |
| 159 } | |
| 160 | |
| 161 std::string CanonicalCookie::GetCookieSourceFromURL(const GURL& url) { | |
| 162 if (url.SchemeIsFile()) | |
| 163 return url.spec(); | |
| 164 | |
| 165 url::Replacements<char> replacements; | |
| 166 replacements.ClearPort(); | |
| 167 if (url.SchemeIsSecure()) | |
| 168 replacements.SetScheme("http", url::Component(0, 4)); | |
| 169 | |
| 170 return url.GetOrigin().ReplaceComponents(replacements).spec(); | |
| 171 } | |
| 172 | |
| 173 // static | |
| 174 std::string CanonicalCookie::CanonPath(const GURL& url, | |
| 175 const ParsedCookie& pc) { | |
| 176 std::string path_string; | |
| 177 if (pc.HasPath()) | |
| 178 path_string = pc.Path(); | |
| 179 return CanonPathWithString(url, path_string); | |
| 180 } | |
| 181 | |
| 182 // static | |
| 183 Time CanonicalCookie::CanonExpiration(const ParsedCookie& pc, | |
| 184 const Time& current, | |
| 185 const Time& server_time) { | |
| 186 // First, try the Max-Age attribute. | |
| 187 uint64 max_age = 0; | |
| 188 if (pc.HasMaxAge() && | |
| 189 #ifdef COMPILER_MSVC | |
| 190 sscanf_s( | |
| 191 #else | |
| 192 sscanf( | |
| 193 #endif | |
| 194 pc.MaxAge().c_str(), " %" PRIu64, &max_age) == 1) { | |
| 195 return current + TimeDelta::FromSeconds(max_age); | |
| 196 } | |
| 197 | |
| 198 // Try the Expires attribute. | |
| 199 if (pc.HasExpires() && !pc.Expires().empty()) { | |
| 200 // Adjust for clock skew between server and host. | |
| 201 base::Time parsed_expiry = cookie_util::ParseCookieTime(pc.Expires()); | |
| 202 if (!parsed_expiry.is_null()) | |
| 203 return parsed_expiry + (current - server_time); | |
| 204 } | |
| 205 | |
| 206 // Invalid or no expiration, persistent cookie. | |
| 207 return Time(); | |
| 208 } | |
| 209 | |
| 210 // static | |
| 211 CanonicalCookie* CanonicalCookie::Create(const GURL& url, | |
| 212 const std::string& cookie_line, | |
| 213 const base::Time& creation_time, | |
| 214 const CookieOptions& options) { | |
| 215 ParsedCookie parsed_cookie(cookie_line); | |
| 216 | |
| 217 if (!parsed_cookie.IsValid()) { | |
| 218 VLOG(kVlogSetCookies) << "WARNING: Couldn't parse cookie"; | |
| 219 return NULL; | |
| 220 } | |
| 221 | |
| 222 if (options.exclude_httponly() && parsed_cookie.IsHttpOnly()) { | |
| 223 VLOG(kVlogSetCookies) << "Create() is not creating a httponly cookie"; | |
| 224 return NULL; | |
| 225 } | |
| 226 | |
| 227 std::string cookie_domain; | |
| 228 if (!GetCookieDomain(url, parsed_cookie, &cookie_domain)) { | |
| 229 return NULL; | |
| 230 } | |
| 231 | |
| 232 std::string cookie_path = CanonicalCookie::CanonPath(url, parsed_cookie); | |
| 233 Time server_time(creation_time); | |
| 234 if (options.has_server_time()) | |
| 235 server_time = options.server_time(); | |
| 236 | |
| 237 Time cookie_expires = CanonicalCookie::CanonExpiration(parsed_cookie, | |
| 238 creation_time, | |
| 239 server_time); | |
| 240 | |
| 241 return new CanonicalCookie(url, parsed_cookie.Name(), parsed_cookie.Value(), | |
| 242 cookie_domain, cookie_path, creation_time, | |
| 243 cookie_expires, creation_time, | |
| 244 parsed_cookie.IsSecure(), | |
| 245 parsed_cookie.IsHttpOnly(), | |
| 246 parsed_cookie.Priority()); | |
| 247 } | |
| 248 | |
| 249 CanonicalCookie* CanonicalCookie::Create(const GURL& url, | |
| 250 const std::string& name, | |
| 251 const std::string& value, | |
| 252 const std::string& domain, | |
| 253 const std::string& path, | |
| 254 const base::Time& creation, | |
| 255 const base::Time& expiration, | |
| 256 bool secure, | |
| 257 bool http_only, | |
| 258 CookiePriority priority) { | |
| 259 // Expect valid attribute tokens and values, as defined by the ParsedCookie | |
| 260 // logic, otherwise don't create the cookie. | |
| 261 std::string parsed_name = ParsedCookie::ParseTokenString(name); | |
| 262 if (parsed_name != name) | |
| 263 return NULL; | |
| 264 std::string parsed_value = ParsedCookie::ParseValueString(value); | |
| 265 if (parsed_value != value) | |
| 266 return NULL; | |
| 267 | |
| 268 std::string parsed_domain = ParsedCookie::ParseValueString(domain); | |
| 269 if (parsed_domain != domain) | |
| 270 return NULL; | |
| 271 std::string cookie_domain; | |
| 272 if (!cookie_util::GetCookieDomainWithString(url, parsed_domain, | |
| 273 &cookie_domain)) { | |
| 274 return NULL; | |
| 275 } | |
| 276 | |
| 277 std::string parsed_path = ParsedCookie::ParseValueString(path); | |
| 278 if (parsed_path != path) | |
| 279 return NULL; | |
| 280 | |
| 281 std::string cookie_path = CanonPathWithString(url, parsed_path); | |
| 282 // Expect that the path was either not specified (empty), or is valid. | |
| 283 if (!parsed_path.empty() && cookie_path != parsed_path) | |
| 284 return NULL; | |
| 285 // Canonicalize path again to make sure it escapes characters as needed. | |
| 286 url::Component path_component(0, cookie_path.length()); | |
| 287 url::RawCanonOutputT<char> canon_path; | |
| 288 url::Component canon_path_component; | |
| 289 url::CanonicalizePath(cookie_path.data(), path_component, &canon_path, | |
| 290 &canon_path_component); | |
| 291 cookie_path = std::string(canon_path.data() + canon_path_component.begin, | |
| 292 canon_path_component.len); | |
| 293 | |
| 294 return new CanonicalCookie(url, parsed_name, parsed_value, cookie_domain, | |
| 295 cookie_path, creation, expiration, creation, | |
| 296 secure, http_only, priority); | |
| 297 } | |
| 298 | |
| 299 bool CanonicalCookie::IsOnPath(const std::string& url_path) const { | |
| 300 | |
| 301 // A zero length would be unsafe for our trailing '/' checks, and | |
| 302 // would also make no sense for our prefix match. The code that | |
| 303 // creates a CanonicalCookie should make sure the path is never zero length, | |
| 304 // but we double check anyway. | |
| 305 if (path_.empty()) | |
| 306 return false; | |
| 307 | |
| 308 // The Mozilla code broke this into three cases, based on if the cookie path | |
| 309 // was longer, the same length, or shorter than the length of the url path. | |
| 310 // I think the approach below is simpler. | |
| 311 | |
| 312 // Make sure the cookie path is a prefix of the url path. If the | |
| 313 // url path is shorter than the cookie path, then the cookie path | |
| 314 // can't be a prefix. | |
| 315 if (url_path.find(path_) != 0) | |
| 316 return false; | |
| 317 | |
| 318 // Now we know that url_path is >= cookie_path, and that cookie_path | |
| 319 // is a prefix of url_path. If they are the are the same length then | |
| 320 // they are identical, otherwise we need an additional check: | |
| 321 | |
| 322 // In order to avoid in correctly matching a cookie path of /blah | |
| 323 // with a request path of '/blahblah/', we need to make sure that either | |
| 324 // the cookie path ends in a trailing '/', or that we prefix up to a '/' | |
| 325 // in the url path. Since we know that the url path length is greater | |
| 326 // than the cookie path length, it's safe to index one byte past. | |
| 327 if (path_.length() != url_path.length() && | |
| 328 path_[path_.length() - 1] != '/' && | |
| 329 url_path[path_.length()] != '/') | |
| 330 return false; | |
| 331 | |
| 332 return true; | |
| 333 } | |
| 334 | |
| 335 bool CanonicalCookie::IsDomainMatch(const std::string& host) const { | |
| 336 // Can domain match in two ways; as a domain cookie (where the cookie | |
| 337 // domain begins with ".") or as a host cookie (where it doesn't). | |
| 338 | |
| 339 // Some consumers of the CookieMonster expect to set cookies on | |
| 340 // URLs like http://.strange.url. To retrieve cookies in this instance, | |
| 341 // we allow matching as a host cookie even when the domain_ starts with | |
| 342 // a period. | |
| 343 if (host == domain_) | |
| 344 return true; | |
| 345 | |
| 346 // Domain cookie must have an initial ".". To match, it must be | |
| 347 // equal to url's host with initial period removed, or a suffix of | |
| 348 // it. | |
| 349 | |
| 350 // Arguably this should only apply to "http" or "https" cookies, but | |
| 351 // extension cookie tests currently use the funtionality, and if we | |
| 352 // ever decide to implement that it should be done by preventing | |
| 353 // such cookies from being set. | |
| 354 if (domain_.empty() || domain_[0] != '.') | |
| 355 return false; | |
| 356 | |
| 357 // The host with a "." prefixed. | |
| 358 if (domain_.compare(1, std::string::npos, host) == 0) | |
| 359 return true; | |
| 360 | |
| 361 // A pure suffix of the host (ok since we know the domain already | |
| 362 // starts with a ".") | |
| 363 return (host.length() > domain_.length() && | |
| 364 host.compare(host.length() - domain_.length(), | |
| 365 domain_.length(), domain_) == 0); | |
| 366 } | |
| 367 | |
| 368 bool CanonicalCookie::IncludeForRequestURL(const GURL& url, | |
| 369 const CookieOptions& options) const { | |
| 370 // Filter out HttpOnly cookies, per options. | |
| 371 if (options.exclude_httponly() && IsHttpOnly()) | |
| 372 return false; | |
| 373 // Secure cookies should not be included in requests for URLs with an | |
| 374 // insecure scheme. | |
| 375 if (IsSecure() && !url.SchemeIsSecure()) | |
| 376 return false; | |
| 377 // Don't include cookies for requests that don't apply to the cookie domain. | |
| 378 if (!IsDomainMatch(url.host())) | |
| 379 return false; | |
| 380 // Don't include cookies for requests with a url path that does not path | |
| 381 // match the cookie-path. | |
| 382 if (!IsOnPath(url.path())) | |
| 383 return false; | |
| 384 | |
| 385 return true; | |
| 386 } | |
| 387 | |
| 388 std::string CanonicalCookie::DebugString() const { | |
| 389 return base::StringPrintf( | |
| 390 "name: %s value: %s domain: %s path: %s creation: %" | |
| 391 PRId64, | |
| 392 name_.c_str(), value_.c_str(), | |
| 393 domain_.c_str(), path_.c_str(), | |
| 394 static_cast<int64>(creation_date_.ToTimeT())); | |
| 395 } | |
| 396 | |
| 397 CanonicalCookie* CanonicalCookie::Duplicate() const { | |
| 398 CanonicalCookie* cc = new CanonicalCookie(); | |
| 399 cc->source_ = source_; | |
| 400 cc->name_ = name_; | |
| 401 cc->value_ = value_; | |
| 402 cc->domain_ = domain_; | |
| 403 cc->path_ = path_; | |
| 404 cc->creation_date_ = creation_date_; | |
| 405 cc->expiry_date_ = expiry_date_; | |
| 406 cc->last_access_date_ = last_access_date_; | |
| 407 cc->secure_ = secure_; | |
| 408 cc->httponly_ = httponly_; | |
| 409 cc->priority_ = priority_; | |
| 410 return cc; | |
| 411 } | |
| 412 | |
| 413 } // namespace net | |
| OLD | NEW |