1// Copyright (c) 2010 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/extension_cookies_helpers.h"
8
9#include "base/logging.h"
10#include "base/values.h"
11#include "chrome/browser/extensions/extension_cookies_api_constants.h"
12#include "chrome/browser/extensions/extension_tabs_module.h"
13#include "chrome/browser/profiles/profile.h"
14#include "chrome/browser/tabs/tab_strip_model.h"
15#include "chrome/browser/ui/browser.h"
16#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
17#include "chrome/common/extensions/extension.h"
18#include "chrome/common/url_constants.h"
19#include "googleurl/src/gurl.h"
20
21namespace keys = extension_cookies_api_constants;
22
23namespace extension_cookies_helpers {
24
25static const char kOriginalProfileStoreId[] = "0";
26static const char kOffTheRecordProfileStoreId[] = "1";
27
28Profile* ChooseProfileFromStoreId(const std::string& store_id,
29                                  Profile* profile,
30                                  bool include_incognito) {
31  DCHECK(profile);
32  bool allow_original = !profile->IsOffTheRecord();
33  bool allow_incognito = profile->IsOffTheRecord() ||
34      (include_incognito && profile->HasOffTheRecordProfile());
35  if (store_id == kOriginalProfileStoreId && allow_original)
36    return profile->GetOriginalProfile();
37  if (store_id == kOffTheRecordProfileStoreId && allow_incognito)
38    return profile->GetOffTheRecordProfile();
39  return NULL;
40}
41
42const char* GetStoreIdFromProfile(Profile* profile) {
43  DCHECK(profile);
44  return profile->IsOffTheRecord() ?
45      kOffTheRecordProfileStoreId : kOriginalProfileStoreId;
46}
47
48DictionaryValue* CreateCookieValue(
49    const net::CookieMonster::CanonicalCookie& cookie,
50    const std::string& store_id) {
51  DictionaryValue* result = new DictionaryValue();
52
53  result->SetString(keys::kNameKey, cookie.Name());
54  result->SetString(keys::kValueKey, cookie.Value());
55  result->SetString(keys::kDomainKey, cookie.Domain());
56  result->SetBoolean(keys::kHostOnlyKey,
57                     net::CookieMonster::DomainIsHostOnly(cookie.Domain()));
58  result->SetString(keys::kPathKey, cookie.Path());
59  result->SetBoolean(keys::kSecureKey, cookie.IsSecure());
60  result->SetBoolean(keys::kHttpOnlyKey, cookie.IsHttpOnly());
61  result->SetBoolean(keys::kSessionKey, !cookie.DoesExpire());
62  if (cookie.DoesExpire()) {
63    result->SetDouble(keys::kExpirationDateKey,
64                      cookie.ExpiryDate().ToDoubleT());
65  }
66  result->SetString(keys::kStoreIdKey, store_id);
67
68  return result;
69}
70
71DictionaryValue* CreateCookieStoreValue(Profile* profile,
72                                        ListValue* tab_ids) {
73  DCHECK(profile);
74  DCHECK(tab_ids);
75  DictionaryValue* result = new DictionaryValue();
76  result->SetString(keys::kIdKey, GetStoreIdFromProfile(profile));
77  result->Set(keys::kTabIdsKey, tab_ids);
78  return result;
79}
80
81net::CookieList GetCookieListFromStore(
82    net::CookieStore* cookie_store, const GURL& url) {
83  DCHECK(cookie_store);
84  net::CookieMonster* monster = cookie_store->GetCookieMonster();
85  if (!url.is_empty()) {
86    DCHECK(url.is_valid());
87    return monster->GetAllCookiesForURL(url);
88  }
89  return monster->GetAllCookies();
90}
91
92GURL GetURLFromCanonicalCookie(
93    const net::CookieMonster::CanonicalCookie& cookie) {
94  const std::string& domain_key = cookie.Domain();
95  const std::string scheme =
96      cookie.IsSecure() ? chrome::kHttpsScheme : chrome::kHttpScheme;
97  const std::string host =
98      domain_key.find('.') != 0 ? domain_key : domain_key.substr(1);
99  return GURL(scheme + chrome::kStandardSchemeSeparator + host + "/");
100}
101
102void AppendMatchingCookiesToList(
103    const net::CookieList& all_cookies,
104    const std::string& store_id,
105    const GURL& url, const DictionaryValue* details,
106    const Extension* extension,
107    ListValue* match_list) {
108  net::CookieList::const_iterator it;
109  for (it = all_cookies.begin(); it != all_cookies.end(); ++it) {
110    // Ignore any cookie whose domain doesn't match the extension's
111    // host permissions.
112    GURL cookie_domain_url = GetURLFromCanonicalCookie(*it);
113    if (!extension->HasHostPermission(cookie_domain_url))
114      continue;
115    // Filter the cookie using the match filter.
116    extension_cookies_helpers::MatchFilter filter(details);
117    if (filter.MatchesCookie(*it))
118      match_list->Append(CreateCookieValue(*it, store_id));
119  }
120}
121
122void AppendToTabIdList(Browser* browser, ListValue* tab_ids) {
123  DCHECK(browser);
124  DCHECK(tab_ids);
125  TabStripModel* tab_strip = browser->tabstrip_model();
126  for (int i = 0; i < tab_strip->count(); ++i) {
127    tab_ids->Append(Value::CreateIntegerValue(
128        ExtensionTabUtil::GetTabId(
129            tab_strip->GetTabContentsAt(i)->tab_contents())));
130  }
131}
132
133MatchFilter::MatchFilter(const DictionaryValue* details)
134    : details_(details) {
135  DCHECK(details_);
136}
137
138bool MatchFilter::MatchesCookie(
139    const net::CookieMonster::CanonicalCookie& cookie) {
140  return MatchesString(keys::kNameKey, cookie.Name()) &&
141         MatchesDomain(cookie.Domain()) &&
142         MatchesString(keys::kPathKey, cookie.Path()) &&
143         MatchesBoolean(keys::kSecureKey, cookie.IsSecure()) &&
144         MatchesBoolean(keys::kSessionKey, !cookie.DoesExpire());
145}
146
147bool MatchFilter::MatchesString(const char* key, const std::string& value) {
148  if (!details_->HasKey(key))
149    return true;
150  std::string filter_value;
151  return (details_->GetString(key, &filter_value) &&
152          value == filter_value);
153}
154
155bool MatchFilter::MatchesBoolean(const char* key, bool value) {
156  if (!details_->HasKey(key))
157    return true;
158  bool filter_value = false;
159  return (details_->GetBoolean(key, &filter_value) &&
160          value == filter_value);
161}
162
163bool MatchFilter::MatchesDomain(const std::string& domain) {
164  if (!details_->HasKey(keys::kDomainKey))
165    return true;
166
167  std::string filter_value;
168  if (!details_->GetString(keys::kDomainKey, &filter_value))
169    return false;
170  // Add a leading '.' character to the filter domain if it doesn't exist.
171  if (net::CookieMonster::DomainIsHostOnly(filter_value))
172    filter_value.insert(0, ".");
173
174  std::string sub_domain(domain);
175  // Strip any leading '.' character from the input cookie domain.
176  if (!net::CookieMonster::DomainIsHostOnly(sub_domain))
177    sub_domain = sub_domain.substr(1);
178
179  // Now check whether the domain argument is a subdomain of the filter domain.
180  for (sub_domain.insert(0, ".");
181       sub_domain.length() >= filter_value.length();) {
182    if (sub_domain == filter_value)
183      return true;
184    const size_t next_dot = sub_domain.find('.', 1);  // Skip over leading dot.
185    sub_domain.erase(0, next_dot);
186  }
187  return false;
188}
189
190}  // namespace extension_cookies_helpers
191