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// Implements common functionality for the Chrome Extensions Cookies API. 6 7#include "chrome/browser/extensions/api/cookies/cookies_helpers.h" 8 9#include <vector> 10 11#include "base/logging.h" 12#include "base/memory/linked_ptr.h" 13#include "base/memory/scoped_ptr.h" 14#include "base/strings/string_util.h" 15#include "base/strings/utf_string_conversions.h" 16#include "base/values.h" 17#include "chrome/browser/extensions/api/cookies/cookies_api_constants.h" 18#include "chrome/browser/extensions/extension_tab_util.h" 19#include "chrome/browser/profiles/profile.h" 20#include "chrome/browser/ui/browser.h" 21#include "chrome/browser/ui/tabs/tab_strip_model.h" 22#include "chrome/common/extensions/api/cookies.h" 23#include "chrome/common/url_constants.h" 24#include "content/public/browser/web_contents.h" 25#include "extensions/common/extension.h" 26#include "extensions/common/permissions/permissions_data.h" 27#include "net/cookies/canonical_cookie.h" 28#include "net/cookies/cookie_util.h" 29#include "url/gurl.h" 30 31using extensions::api::cookies::Cookie; 32using extensions::api::cookies::CookieStore; 33 34namespace GetAll = extensions::api::cookies::GetAll; 35 36namespace extensions { 37 38namespace keys = cookies_api_constants; 39 40namespace cookies_helpers { 41 42static const char kOriginalProfileStoreId[] = "0"; 43static const char kOffTheRecordProfileStoreId[] = "1"; 44 45Profile* ChooseProfileFromStoreId(const std::string& store_id, 46 Profile* profile, 47 bool include_incognito) { 48 DCHECK(profile); 49 bool allow_original = !profile->IsOffTheRecord(); 50 bool allow_incognito = profile->IsOffTheRecord() || 51 (include_incognito && profile->HasOffTheRecordProfile()); 52 if (store_id == kOriginalProfileStoreId && allow_original) 53 return profile->GetOriginalProfile(); 54 if (store_id == kOffTheRecordProfileStoreId && allow_incognito) 55 return profile->GetOffTheRecordProfile(); 56 return NULL; 57} 58 59const char* GetStoreIdFromProfile(Profile* profile) { 60 DCHECK(profile); 61 return profile->IsOffTheRecord() ? 62 kOffTheRecordProfileStoreId : kOriginalProfileStoreId; 63} 64 65scoped_ptr<Cookie> CreateCookie( 66 const net::CanonicalCookie& canonical_cookie, 67 const std::string& store_id) { 68 scoped_ptr<Cookie> cookie(new Cookie()); 69 70 // A cookie is a raw byte sequence. By explicitly parsing it as UTF-8, we 71 // apply error correction, so the string can be safely passed to the renderer. 72 cookie->name = base::UTF16ToUTF8(base::UTF8ToUTF16(canonical_cookie.Name())); 73 cookie->value = 74 base::UTF16ToUTF8(base::UTF8ToUTF16(canonical_cookie.Value())); 75 cookie->domain = canonical_cookie.Domain(); 76 cookie->host_only = net::cookie_util::DomainIsHostOnly( 77 canonical_cookie.Domain()); 78 // A non-UTF8 path is invalid, so we just replace it with an empty string. 79 cookie->path = base::IsStringUTF8(canonical_cookie.Path()) ? 80 canonical_cookie.Path() : std::string(); 81 cookie->secure = canonical_cookie.IsSecure(); 82 cookie->http_only = canonical_cookie.IsHttpOnly(); 83 cookie->session = !canonical_cookie.IsPersistent(); 84 if (canonical_cookie.IsPersistent()) { 85 cookie->expiration_date.reset( 86 new double(canonical_cookie.ExpiryDate().ToDoubleT())); 87 } 88 cookie->store_id = store_id; 89 90 return cookie.Pass(); 91} 92 93scoped_ptr<CookieStore> CreateCookieStore(Profile* profile, 94 base::ListValue* tab_ids) { 95 DCHECK(profile); 96 DCHECK(tab_ids); 97 base::DictionaryValue dict; 98 dict.SetString(keys::kIdKey, GetStoreIdFromProfile(profile)); 99 dict.Set(keys::kTabIdsKey, tab_ids); 100 101 CookieStore* cookie_store = new CookieStore(); 102 bool rv = CookieStore::Populate(dict, cookie_store); 103 CHECK(rv); 104 return scoped_ptr<CookieStore>(cookie_store); 105} 106 107void GetCookieListFromStore( 108 net::CookieStore* cookie_store, const GURL& url, 109 const net::CookieMonster::GetCookieListCallback& callback) { 110 DCHECK(cookie_store); 111 net::CookieMonster* monster = cookie_store->GetCookieMonster(); 112 if (!url.is_empty()) { 113 DCHECK(url.is_valid()); 114 monster->GetAllCookiesForURLAsync(url, callback); 115 } else { 116 monster->GetAllCookiesAsync(callback); 117 } 118} 119 120GURL GetURLFromCanonicalCookie(const net::CanonicalCookie& cookie) { 121 const std::string& domain_key = cookie.Domain(); 122 const std::string scheme = 123 cookie.IsSecure() ? url::kHttpsScheme : url::kHttpScheme; 124 const std::string host = 125 domain_key.find('.') != 0 ? domain_key : domain_key.substr(1); 126 return GURL(scheme + url::kStandardSchemeSeparator + host + "/"); 127} 128 129void AppendMatchingCookiesToVector(const net::CookieList& all_cookies, 130 const GURL& url, 131 const GetAll::Params::Details* details, 132 const Extension* extension, 133 LinkedCookieVec* match_vector) { 134 net::CookieList::const_iterator it; 135 for (it = all_cookies.begin(); it != all_cookies.end(); ++it) { 136 // Ignore any cookie whose domain doesn't match the extension's 137 // host permissions. 138 GURL cookie_domain_url = GetURLFromCanonicalCookie(*it); 139 if (!extension->permissions_data()->HasHostPermission(cookie_domain_url)) 140 continue; 141 // Filter the cookie using the match filter. 142 cookies_helpers::MatchFilter filter(details); 143 if (filter.MatchesCookie(*it)) { 144 match_vector->push_back(make_linked_ptr( 145 CreateCookie(*it, *details->store_id).release())); 146 } 147 } 148} 149 150void AppendToTabIdList(Browser* browser, base::ListValue* tab_ids) { 151 DCHECK(browser); 152 DCHECK(tab_ids); 153 TabStripModel* tab_strip = browser->tab_strip_model(); 154 for (int i = 0; i < tab_strip->count(); ++i) { 155 tab_ids->Append(new base::FundamentalValue( 156 ExtensionTabUtil::GetTabId(tab_strip->GetWebContentsAt(i)))); 157 } 158} 159 160MatchFilter::MatchFilter(const GetAll::Params::Details* details) 161 : details_(details) { 162 DCHECK(details_); 163} 164 165bool MatchFilter::MatchesCookie( 166 const net::CanonicalCookie& cookie) { 167 if (details_->name.get() && *details_->name != cookie.Name()) 168 return false; 169 170 if (!MatchesDomain(cookie.Domain())) 171 return false; 172 173 if (details_->path.get() && *details_->path != cookie.Path()) 174 return false; 175 176 if (details_->secure.get() && *details_->secure != cookie.IsSecure()) 177 return false; 178 179 if (details_->session.get() && *details_->session != !cookie.IsPersistent()) 180 return false; 181 182 return true; 183} 184 185bool MatchFilter::MatchesDomain(const std::string& domain) { 186 if (!details_->domain.get()) 187 return true; 188 189 // Add a leading '.' character to the filter domain if it doesn't exist. 190 if (net::cookie_util::DomainIsHostOnly(*details_->domain)) 191 details_->domain->insert(0, "."); 192 193 std::string sub_domain(domain); 194 // Strip any leading '.' character from the input cookie domain. 195 if (!net::cookie_util::DomainIsHostOnly(sub_domain)) 196 sub_domain = sub_domain.substr(1); 197 198 // Now check whether the domain argument is a subdomain of the filter domain. 199 for (sub_domain.insert(0, "."); 200 sub_domain.length() >= details_->domain->length();) { 201 if (sub_domain == *details_->domain) 202 return true; 203 const size_t next_dot = sub_domain.find('.', 1); // Skip over leading dot. 204 sub_domain.erase(0, next_dot); 205 } 206 return false; 207} 208 209} // namespace cookies_helpers 210} // namespace extensions 211