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
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
ChooseProfileFromStoreId(const std::string & store_id,Profile * profile,bool include_incognito)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
GetStoreIdFromProfile(Profile * profile)59 const char* GetStoreIdFromProfile(Profile* profile) {
60 DCHECK(profile);
61 return profile->IsOffTheRecord() ?
62 kOffTheRecordProfileStoreId : kOriginalProfileStoreId;
63 }
64
CreateCookie(const net::CanonicalCookie & canonical_cookie,const std::string & store_id)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
CreateCookieStore(Profile * profile,base::ListValue * tab_ids)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
GetCookieListFromStore(net::CookieStore * cookie_store,const GURL & url,const net::CookieMonster::GetCookieListCallback & callback)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
GetURLFromCanonicalCookie(const net::CanonicalCookie & cookie)119 GURL GetURLFromCanonicalCookie(const net::CanonicalCookie& cookie) {
120 const std::string& domain_key = cookie.Domain();
121 const std::string scheme =
122 cookie.IsSecure() ? content::kHttpsScheme : content::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
AppendMatchingCookiesToVector(const net::CookieList & all_cookies,const GURL & url,const GetAll::Params::Details * details,const Extension * extension,LinkedCookieVec * match_vector)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
AppendToTabIdList(Browser * browser,base::ListValue * tab_ids)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(new base::FundamentalValue(
155 ExtensionTabUtil::GetTabId(tab_strip->GetWebContentsAt(i))));
156 }
157 }
158
MatchFilter(const GetAll::Params::Details * details)159 MatchFilter::MatchFilter(const GetAll::Params::Details* details)
160 : details_(details) {
161 DCHECK(details_);
162 }
163
MatchesCookie(const net::CanonicalCookie & cookie)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
MatchesDomain(const std::string & domain)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 extensions
210