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