15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Portions of this code based on Mozilla: 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// (netwerk/cookie/src/nsCookieService.cpp) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/* ***** BEGIN LICENSE BLOCK ***** 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Version: MPL 1.1/GPL 2.0/LGPL 2.1 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * The contents of this file are subject to the Mozilla Public License Version 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 1.1 (the "License"); you may not use this file except in compliance with 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the License. You may obtain a copy of the License at 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * http://www.mozilla.org/MPL/ 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Software distributed under the License is distributed on an "AS IS" basis, 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * for the specific language governing rights and limitations under the 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * License. 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * The Original Code is mozilla.org code. 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * The Initial Developer of the Original Code is 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Netscape Communications Corporation. 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Portions created by the Initial Developer are Copyright (C) 2003 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the Initial Developer. All Rights Reserved. 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Contributor(s): 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Daniel Witte (dwitte@stanford.edu) 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Michiel van Leeuwen (mvl@exedo.nl) 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Alternatively, the contents of this file may be used under the terms of 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * either the GNU General Public License Version 2 or later (the "GPL"), or 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * in which case the provisions of the GPL or the LGPL are applicable instead 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * of those above. If you wish to allow use of your version of this file only 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * under the terms of either the GPL or the LGPL, and not to allow others to 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * use your version of this file under the terms of the MPL, indicate your 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * decision by deleting the provisions above and replace them with the notice 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * and other provisions required by the GPL or the LGPL. If you do not delete 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the provisions above, a recipient may use your version of this file under 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the terms of any one of the MPL, the GPL or the LGPL. 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * ***** END LICENSE BLOCK ***** */ 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/cookies/canonical_cookie.h" 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/basictypes.h" 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/format_macros.h" 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h" 505e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)#include "base/strings/stringprintf.h" 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/cookies/cookie_util.h" 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/cookies/parsed_cookie.h" 537dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "url/gurl.h" 547dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "url/url_canon.h" 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using base::Time; 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using base::TimeDelta; 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace net { 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace { 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const int kVlogSetCookies = 7; 642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Determine the cookie domain to use for setting the specified cookie. 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool GetCookieDomain(const GURL& url, 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const ParsedCookie& pc, 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string* result) { 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string domain_string; 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (pc.HasDomain()) 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) domain_string = pc.Domain(); 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return cookie_util::GetCookieDomainWithString(url, domain_string, result); 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::string CanonPathWithString(const GURL& url, 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::string& path_string) { 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The RFC says the path should be a prefix of the current URL path. 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // However, Mozilla allows you to set any path for compatibility with 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // broken websites. We unfortunately will mimic this behavior. We try 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // to be generous and accept cookies with an invalid path attribute, and 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // default the path to something reasonable. 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The path was supplied in the cookie, we'll take it. 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!path_string.empty() && path_string[0] == '/') 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return path_string; 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The path was not supplied in the cookie or invalid, we will default 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // to the current URL path. 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // """Defaults to the path of the request URL that generated the 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Set-Cookie response, up to, but not including, the 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // right-most /.""" 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // How would this work for a cookie on /? We will include it then. 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::string& url_path = url.path(); 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) size_t idx = url_path.find_last_of('/'); 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The cookie path was invalid or a single '/'. 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (idx == 0 || idx == std::string::npos) 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return std::string("/"); 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Return up to the rightmost '/'. 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return url_path.substr(0, idx); 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CanonicalCookie::CanonicalCookie() 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : secure_(false), 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) httponly_(false) { 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CanonicalCookie::CanonicalCookie( 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const GURL& url, const std::string& name, const std::string& value, 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::string& domain, const std::string& path, 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const base::Time& creation, const base::Time& expiration, 116c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const base::Time& last_access, bool secure, bool httponly, 117c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) CookiePriority priority) 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : source_(GetCookieSourceFromURL(url)), 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) name_(name), 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) value_(value), 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) domain_(domain), 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) path_(path), 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) creation_date_(creation), 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) expiry_date_(expiration), 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) last_access_date_(last_access), 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) secure_(secure), 127c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) httponly_(httponly), 128c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) priority_(priority) { 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CanonicalCookie::CanonicalCookie(const GURL& url, const ParsedCookie& pc) 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : source_(GetCookieSourceFromURL(url)), 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) name_(pc.Name()), 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) value_(pc.Value()), 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) path_(CanonPath(url, pc)), 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) creation_date_(Time::Now()), 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) last_access_date_(Time()), 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) secure_(pc.IsSecure()), 139c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) httponly_(pc.IsHttpOnly()), 140c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) priority_(pc.Priority()) { 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (pc.HasExpires()) 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) expiry_date_ = CanonExpiration(pc, creation_date_, creation_date_); 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Do the best we can with the domain. 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string cookie_domain; 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string domain_string; 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (pc.HasDomain()) { 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) domain_string = pc.Domain(); 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool result 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) = cookie_util::GetCookieDomainWithString(url, domain_string, 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) &cookie_domain); 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Caller is responsible for passing in good arguments. 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(result); 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) domain_ = cookie_domain; 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CanonicalCookie::~CanonicalCookie() { 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::string CanonicalCookie::GetCookieSourceFromURL(const GURL& url) { 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (url.SchemeIsFile()) 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return url.spec(); 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1655c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu url::Replacements<char> replacements; 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) replacements.ClearPort(); 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (url.SchemeIsSecure()) 1685c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu replacements.SetScheme("http", url::Component(0, 4)); 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return url.GetOrigin().ReplaceComponents(replacements).spec(); 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::string CanonicalCookie::CanonPath(const GURL& url, 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const ParsedCookie& pc) { 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string path_string; 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (pc.HasPath()) 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) path_string = pc.Path(); 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return CanonPathWithString(url, path_string); 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Time CanonicalCookie::CanonExpiration(const ParsedCookie& pc, 1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const Time& current, 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const Time& server_time) { 1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // First, try the Max-Age attribute. 1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) uint64 max_age = 0; 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (pc.HasMaxAge() && 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#ifdef COMPILER_MSVC 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sscanf_s( 1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#else 1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sscanf( 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pc.MaxAge().c_str(), " %" PRIu64, &max_age) == 1) { 1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return current + TimeDelta::FromSeconds(max_age); 1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Try the Expires attribute. 1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (pc.HasExpires() && !pc.Expires().empty()) { 2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Adjust for clock skew between server and host. 2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::Time parsed_expiry = cookie_util::ParseCookieTime(pc.Expires()); 2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!parsed_expiry.is_null()) 2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return parsed_expiry + (current - server_time); 2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Invalid or no expiration, persistent cookie. 2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return Time(); 2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// static 2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CanonicalCookie* CanonicalCookie::Create(const GURL& url, 2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const std::string& cookie_line, 2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const base::Time& creation_time, 2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const CookieOptions& options) { 2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) ParsedCookie parsed_cookie(cookie_line); 2162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!parsed_cookie.IsValid()) { 2182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) VLOG(kVlogSetCookies) << "WARNING: Couldn't parse cookie"; 2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return NULL; 2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (options.exclude_httponly() && parsed_cookie.IsHttpOnly()) { 2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) VLOG(kVlogSetCookies) << "Create() is not creating a httponly cookie"; 2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return NULL; 2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) std::string cookie_domain; 2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!GetCookieDomain(url, parsed_cookie, &cookie_domain)) { 2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return NULL; 2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) std::string cookie_path = CanonicalCookie::CanonPath(url, parsed_cookie); 2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) Time server_time(creation_time); 2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (options.has_server_time()) 2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) server_time = options.server_time(); 2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) Time cookie_expires = CanonicalCookie::CanonExpiration(parsed_cookie, 2382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) creation_time, 2392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) server_time); 2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return new CanonicalCookie(url, parsed_cookie.Name(), parsed_cookie.Value(), 2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) cookie_domain, cookie_path, creation_time, 2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) cookie_expires, creation_time, 2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) parsed_cookie.IsSecure(), 245c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) parsed_cookie.IsHttpOnly(), 246c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) parsed_cookie.Priority()); 2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CanonicalCookie* CanonicalCookie::Create(const GURL& url, 2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::string& name, 2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::string& value, 2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::string& domain, 2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::string& path, 2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const base::Time& creation, 2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const base::Time& expiration, 2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool secure, 257c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) bool http_only, 258c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) CookiePriority priority) { 2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Expect valid attribute tokens and values, as defined by the ParsedCookie 2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // logic, otherwise don't create the cookie. 2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string parsed_name = ParsedCookie::ParseTokenString(name); 2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (parsed_name != name) 2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return NULL; 2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string parsed_value = ParsedCookie::ParseValueString(value); 2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (parsed_value != value) 2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return NULL; 2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string parsed_domain = ParsedCookie::ParseValueString(domain); 2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (parsed_domain != domain) 2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return NULL; 2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string cookie_domain; 2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!cookie_util::GetCookieDomainWithString(url, parsed_domain, 2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) &cookie_domain)) { 2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return NULL; 2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string parsed_path = ParsedCookie::ParseValueString(path); 2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (parsed_path != path) 2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return NULL; 2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string cookie_path = CanonPathWithString(url, parsed_path); 2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Expect that the path was either not specified (empty), or is valid. 2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!parsed_path.empty() && cookie_path != parsed_path) 2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return NULL; 2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Canonicalize path again to make sure it escapes characters as needed. 2865c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu url::Component path_component(0, cookie_path.length()); 2875c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu url::RawCanonOutputT<char> canon_path; 2885c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu url::Component canon_path_component; 2895c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu url::CanonicalizePath(cookie_path.data(), path_component, &canon_path, 2905c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu &canon_path_component); 2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cookie_path = std::string(canon_path.data() + canon_path_component.begin, 2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) canon_path_component.len); 2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return new CanonicalCookie(url, parsed_name, parsed_value, cookie_domain, 2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) cookie_path, creation, expiration, creation, 296c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) secure, http_only, priority); 2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool CanonicalCookie::IsOnPath(const std::string& url_path) const { 3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // A zero length would be unsafe for our trailing '/' checks, and 3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // would also make no sense for our prefix match. The code that 3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // creates a CanonicalCookie should make sure the path is never zero length, 3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // but we double check anyway. 3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (path_.empty()) 3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The Mozilla code broke this into three cases, based on if the cookie path 3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // was longer, the same length, or shorter than the length of the url path. 3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // I think the approach below is simpler. 3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Make sure the cookie path is a prefix of the url path. If the 3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // url path is shorter than the cookie path, then the cookie path 3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // can't be a prefix. 3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (url_path.find(path_) != 0) 3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Now we know that url_path is >= cookie_path, and that cookie_path 3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // is a prefix of url_path. If they are the are the same length then 3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // they are identical, otherwise we need an additional check: 3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // In order to avoid in correctly matching a cookie path of /blah 3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // with a request path of '/blahblah/', we need to make sure that either 3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // the cookie path ends in a trailing '/', or that we prefix up to a '/' 3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // in the url path. Since we know that the url path length is greater 3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // than the cookie path length, it's safe to index one byte past. 3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (path_.length() != url_path.length() && 3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) path_[path_.length() - 1] != '/' && 3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) url_path[path_.length()] != '/') 3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool CanonicalCookie::IsDomainMatch(const std::string& host) const { 3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Can domain match in two ways; as a domain cookie (where the cookie 3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // domain begins with ".") or as a host cookie (where it doesn't). 3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Some consumers of the CookieMonster expect to set cookies on 3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // URLs like http://.strange.url. To retrieve cookies in this instance, 3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // we allow matching as a host cookie even when the domain_ starts with 3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // a period. 3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (host == domain_) 3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Domain cookie must have an initial ".". To match, it must be 3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // equal to url's host with initial period removed, or a suffix of 3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // it. 3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Arguably this should only apply to "http" or "https" cookies, but 3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // extension cookie tests currently use the funtionality, and if we 3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // ever decide to implement that it should be done by preventing 3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // such cookies from being set. 3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (domain_.empty() || domain_[0] != '.') 3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The host with a "." prefixed. 3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (domain_.compare(1, std::string::npos, host) == 0) 3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // A pure suffix of the host (ok since we know the domain already 3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // starts with a ".") 3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return (host.length() > domain_.length() && 3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) host.compare(host.length() - domain_.length(), 3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) domain_.length(), domain_) == 0); 3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool CanonicalCookie::IncludeForRequestURL(const GURL& url, 3692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const CookieOptions& options) const { 3702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Filter out HttpOnly cookies, per options. 3712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (options.exclude_httponly() && IsHttpOnly()) 3722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return false; 3732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Secure cookies should not be included in requests for URLs with an 3742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // insecure scheme. 3752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (IsSecure() && !url.SchemeIsSecure()) 3762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return false; 3772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Don't include cookies for requests that don't apply to the cookie domain. 3782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!IsDomainMatch(url.host())) 3792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return false; 3802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Don't include cookies for requests with a url path that does not path 3812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // match the cookie-path. 3822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!IsOnPath(url.path())) 3832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return false; 3842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 3852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return true; 3862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 3872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::string CanonicalCookie::DebugString() const { 3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return base::StringPrintf( 3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "name: %s value: %s domain: %s path: %s creation: %" 3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PRId64, 3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) name_.c_str(), value_.c_str(), 3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) domain_.c_str(), path_.c_str(), 3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) static_cast<int64>(creation_date_.ToTimeT())); 3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 397cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)CanonicalCookie* CanonicalCookie::Duplicate() { 398cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) CanonicalCookie* cc = new CanonicalCookie(); 399cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) cc->source_ = source_; 400cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) cc->name_ = name_; 401cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) cc->value_ = value_; 402cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) cc->domain_ = domain_; 403cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) cc->path_ = path_; 404cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) cc->creation_date_ = creation_date_; 405cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) cc->expiry_date_ = expiry_date_; 406cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) cc->last_access_date_ = last_access_date_; 407cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) cc->secure_ = secure_; 408cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) cc->httponly_ = httponly_; 409cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) cc->priority_ = priority_; 410cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return cc; 411cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 412cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace net 414