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#include "chrome/browser/extensions/api/content_settings/content_settings_api.h"
6
7#include <set>
8#include <vector>
9
10#include "base/bind.h"
11#include "base/command_line.h"
12#include "base/memory/scoped_ptr.h"
13#include "base/values.h"
14#include "chrome/browser/content_settings/cookie_settings.h"
15#include "chrome/browser/content_settings/host_content_settings_map.h"
16#include "chrome/browser/extensions/api/content_settings/content_settings_api_constants.h"
17#include "chrome/browser/extensions/api/content_settings/content_settings_helpers.h"
18#include "chrome/browser/extensions/api/content_settings/content_settings_service.h"
19#include "chrome/browser/extensions/api/content_settings/content_settings_store.h"
20#include "chrome/browser/extensions/api/preference/preference_api_constants.h"
21#include "chrome/browser/extensions/api/preference/preference_helpers.h"
22#include "chrome/browser/plugins/plugin_finder.h"
23#include "chrome/browser/plugins/plugin_installer.h"
24#include "chrome/browser/profiles/profile.h"
25#include "chrome/common/chrome_switches.h"
26#include "chrome/common/extensions/api/content_settings.h"
27#include "content/public/browser/plugin_service.h"
28#include "extensions/browser/extension_prefs_scope.h"
29#include "extensions/common/error_utils.h"
30
31using content::BrowserThread;
32using content::PluginService;
33
34namespace Clear = extensions::api::content_settings::ContentSetting::Clear;
35namespace Get = extensions::api::content_settings::ContentSetting::Get;
36namespace Set = extensions::api::content_settings::ContentSetting::Set;
37namespace pref_helpers = extensions::preference_helpers;
38namespace pref_keys = extensions::preference_api_constants;
39
40namespace {
41
42bool RemoveContentType(base::ListValue* args,
43                       ContentSettingsType* content_type) {
44  std::string content_type_str;
45  if (!args->GetString(0, &content_type_str))
46    return false;
47  // We remove the ContentSettingsType parameter since this is added by the
48  // renderer, and is not part of the JSON schema.
49  args->Remove(0, NULL);
50  *content_type =
51      extensions::content_settings_helpers::StringToContentSettingsType(
52          content_type_str);
53  return *content_type != CONTENT_SETTINGS_TYPE_DEFAULT;
54}
55
56}  // namespace
57
58namespace extensions {
59
60namespace helpers = content_settings_helpers;
61namespace keys = content_settings_api_constants;
62
63bool ContentSettingsContentSettingClearFunction::RunSync() {
64  ContentSettingsType content_type;
65  EXTENSION_FUNCTION_VALIDATE(RemoveContentType(args_.get(), &content_type));
66
67  scoped_ptr<Clear::Params> params(Clear::Params::Create(*args_));
68  EXTENSION_FUNCTION_VALIDATE(params.get());
69
70  ExtensionPrefsScope scope = kExtensionPrefsScopeRegular;
71  bool incognito = false;
72  if (params->details.scope ==
73          Clear::Params::Details::SCOPE_INCOGNITO_SESSION_ONLY) {
74    scope = kExtensionPrefsScopeIncognitoSessionOnly;
75    incognito = true;
76  }
77
78  if (incognito) {
79    // We don't check incognito permissions here, as an extension should be
80    // always allowed to clear its own settings.
81  } else {
82    // Incognito profiles can't access regular mode ever, they only exist in
83    // split mode.
84    if (GetProfile()->IsOffTheRecord()) {
85      error_ = keys::kIncognitoContextError;
86      return false;
87    }
88  }
89
90  scoped_refptr<ContentSettingsStore> store =
91      ContentSettingsService::Get(GetProfile())->content_settings_store();
92  store->ClearContentSettingsForExtension(extension_id(), scope);
93
94  return true;
95}
96
97bool ContentSettingsContentSettingGetFunction::RunSync() {
98  ContentSettingsType content_type;
99  EXTENSION_FUNCTION_VALIDATE(RemoveContentType(args_.get(), &content_type));
100
101  scoped_ptr<Get::Params> params(Get::Params::Create(*args_));
102  EXTENSION_FUNCTION_VALIDATE(params.get());
103
104  GURL primary_url(params->details.primary_url);
105  if (!primary_url.is_valid()) {
106    error_ = ErrorUtils::FormatErrorMessage(keys::kInvalidUrlError,
107        params->details.primary_url);
108    return false;
109  }
110
111  GURL secondary_url(primary_url);
112  if (params->details.secondary_url.get()) {
113    secondary_url = GURL(*params->details.secondary_url);
114    if (!secondary_url.is_valid()) {
115      error_ = ErrorUtils::FormatErrorMessage(keys::kInvalidUrlError,
116        *params->details.secondary_url);
117      return false;
118    }
119  }
120
121  std::string resource_identifier;
122  if (params->details.resource_identifier.get())
123    resource_identifier = params->details.resource_identifier->id;
124
125  bool incognito = false;
126  if (params->details.incognito.get())
127    incognito = *params->details.incognito;
128  if (incognito && !include_incognito()) {
129    error_ = pref_keys::kIncognitoErrorMessage;
130    return false;
131  }
132
133  HostContentSettingsMap* map;
134  CookieSettings* cookie_settings;
135  if (incognito) {
136    if (!GetProfile()->HasOffTheRecordProfile()) {
137      // TODO(bauerb): Allow reading incognito content settings
138      // outside of an incognito session.
139      error_ = keys::kIncognitoSessionOnlyError;
140      return false;
141    }
142    map = GetProfile()->GetOffTheRecordProfile()->GetHostContentSettingsMap();
143    cookie_settings = CookieSettings::Factory::GetForProfile(
144        GetProfile()->GetOffTheRecordProfile()).get();
145  } else {
146    map = GetProfile()->GetHostContentSettingsMap();
147    cookie_settings =
148        CookieSettings::Factory::GetForProfile(GetProfile()).get();
149  }
150
151  ContentSetting setting;
152  if (content_type == CONTENT_SETTINGS_TYPE_COOKIES) {
153    // TODO(jochen): Do we return the value for setting or for reading cookies?
154    bool setting_cookie = false;
155    setting = cookie_settings->GetCookieSetting(primary_url, secondary_url,
156                                                setting_cookie, NULL);
157  } else {
158    setting = map->GetContentSetting(primary_url, secondary_url, content_type,
159                                     resource_identifier);
160  }
161
162  base::DictionaryValue* result = new base::DictionaryValue();
163  result->SetString(keys::kContentSettingKey,
164                    helpers::ContentSettingToString(setting));
165
166  SetResult(result);
167
168  return true;
169}
170
171bool ContentSettingsContentSettingSetFunction::RunSync() {
172  ContentSettingsType content_type;
173  EXTENSION_FUNCTION_VALIDATE(RemoveContentType(args_.get(), &content_type));
174
175  scoped_ptr<Set::Params> params(Set::Params::Create(*args_));
176  EXTENSION_FUNCTION_VALIDATE(params.get());
177
178  std::string primary_error;
179  ContentSettingsPattern primary_pattern =
180      helpers::ParseExtensionPattern(params->details.primary_pattern,
181                                     &primary_error);
182  if (!primary_pattern.IsValid()) {
183    error_ = primary_error;
184    return false;
185  }
186
187  ContentSettingsPattern secondary_pattern = ContentSettingsPattern::Wildcard();
188  if (params->details.secondary_pattern.get()) {
189    std::string secondary_error;
190    secondary_pattern =
191        helpers::ParseExtensionPattern(*params->details.secondary_pattern,
192                                       &secondary_error);
193    if (!secondary_pattern.IsValid()) {
194      error_ = secondary_error;
195      return false;
196    }
197  }
198
199  std::string resource_identifier;
200  if (params->details.resource_identifier.get())
201    resource_identifier = params->details.resource_identifier->id;
202
203  std::string setting_str;
204  EXTENSION_FUNCTION_VALIDATE(
205      params->details.setting->GetAsString(&setting_str));
206  ContentSetting setting;
207  EXTENSION_FUNCTION_VALIDATE(
208      helpers::StringToContentSetting(setting_str, &setting));
209  EXTENSION_FUNCTION_VALIDATE(HostContentSettingsMap::IsSettingAllowedForType(
210      GetProfile()->GetPrefs(), setting, content_type));
211
212  ExtensionPrefsScope scope = kExtensionPrefsScopeRegular;
213  bool incognito = false;
214  if (params->details.scope ==
215          Set::Params::Details::SCOPE_INCOGNITO_SESSION_ONLY) {
216    scope = kExtensionPrefsScopeIncognitoSessionOnly;
217    incognito = true;
218  }
219
220  if (incognito) {
221    // Regular profiles can't access incognito unless include_incognito is true.
222    if (!GetProfile()->IsOffTheRecord() && !include_incognito()) {
223      error_ = pref_keys::kIncognitoErrorMessage;
224      return false;
225    }
226  } else {
227    // Incognito profiles can't access regular mode ever, they only exist in
228    // split mode.
229    if (GetProfile()->IsOffTheRecord()) {
230      error_ = keys::kIncognitoContextError;
231      return false;
232    }
233  }
234
235  if (scope == kExtensionPrefsScopeIncognitoSessionOnly &&
236      !GetProfile()->HasOffTheRecordProfile()) {
237    error_ = pref_keys::kIncognitoSessionOnlyErrorMessage;
238    return false;
239  }
240
241  scoped_refptr<ContentSettingsStore> store =
242      ContentSettingsService::Get(GetProfile())->content_settings_store();
243  store->SetExtensionContentSetting(extension_id(), primary_pattern,
244                                    secondary_pattern, content_type,
245                                    resource_identifier, setting, scope);
246  return true;
247}
248
249bool ContentSettingsContentSettingGetResourceIdentifiersFunction::RunAsync() {
250  ContentSettingsType content_type;
251  EXTENSION_FUNCTION_VALIDATE(RemoveContentType(args_.get(), &content_type));
252
253  if (content_type != CONTENT_SETTINGS_TYPE_PLUGINS) {
254    SendResponse(true);
255    return true;
256  }
257
258  PluginService::GetInstance()->GetPlugins(
259      base::Bind(&ContentSettingsContentSettingGetResourceIdentifiersFunction::
260                 OnGotPlugins,
261                 this));
262  return true;
263}
264
265void ContentSettingsContentSettingGetResourceIdentifiersFunction::OnGotPlugins(
266    const std::vector<content::WebPluginInfo>& plugins) {
267  PluginFinder* finder = PluginFinder::GetInstance();
268  std::set<std::string> group_identifiers;
269  base::ListValue* list = new base::ListValue();
270  for (std::vector<content::WebPluginInfo>::const_iterator it = plugins.begin();
271       it != plugins.end(); ++it) {
272    scoped_ptr<PluginMetadata> plugin_metadata(finder->GetPluginMetadata(*it));
273    const std::string& group_identifier = plugin_metadata->identifier();
274    if (group_identifiers.find(group_identifier) != group_identifiers.end())
275      continue;
276
277    group_identifiers.insert(group_identifier);
278    base::DictionaryValue* dict = new base::DictionaryValue();
279    dict->SetString(keys::kIdKey, group_identifier);
280    dict->SetString(keys::kDescriptionKey, plugin_metadata->name());
281    list->Append(dict);
282  }
283  SetResult(list);
284  BrowserThread::PostTask(
285      BrowserThread::UI, FROM_HERE, base::Bind(
286          &ContentSettingsContentSettingGetResourceIdentifiersFunction::
287          SendResponse,
288          this,
289          true));
290}
291
292}  // namespace extensions
293