extension_proxy_api.cc revision 21d179b334e59e9a3bfcaed4c4430bef1bc5759d
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#include "chrome/browser/extensions/extension_proxy_api.h"
6
7#include "base/string_util.h"
8#include "base/stringprintf.h"
9#include "base/values.h"
10#include "chrome/browser/prefs/proxy_prefs.h"
11#include "chrome/browser/profiles/profile.h"
12#include "chrome/browser/extensions/extension_service.h"
13#include "chrome/common/pref_names.h"
14
15namespace {
16
17// The scheme for which to use a manually specified proxy, not of the proxy URI
18// itself.
19enum {
20  SCHEME_ALL = 0,
21  SCHEME_HTTP,
22  SCHEME_HTTPS,
23  SCHEME_FTP,
24  SCHEME_SOCKS,
25  SCHEME_MAX = SCHEME_SOCKS  // Keep this value up to date.
26};
27
28// The names of the JavaScript properties to extract from the proxy_rules.
29// These must be kept in sync with the SCHEME_* constants.
30const char* field_name[] = { "singleProxy",
31                             "proxyForHttp",
32                             "proxyForHttps",
33                             "proxyForFtp",
34                             "socksProxy" };
35
36// The names of the schemes to be used to build the preference value string
37// for manual proxy settings.  These must be kept in sync with the SCHEME_*
38// constants.
39const char* scheme_name[] = { "*error*",
40                              "http",
41                              "https",
42                              "ftp",
43                              "socks" };
44
45}  // namespace
46
47COMPILE_ASSERT(SCHEME_MAX == SCHEME_SOCKS, SCHEME_MAX_must_equal_SCHEME_SOCKS);
48COMPILE_ASSERT(arraysize(field_name) == SCHEME_MAX + 1,
49               field_name_array_is_wrong_size);
50COMPILE_ASSERT(arraysize(scheme_name) == SCHEME_MAX + 1,
51               scheme_name_array_is_wrong_size);
52
53bool UseCustomProxySettingsFunction::RunImpl() {
54  DictionaryValue* proxy_config;
55  EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &proxy_config));
56
57  std::string proxy_mode;
58  proxy_config->GetString("mode", &proxy_mode);
59
60  DictionaryValue* pac_dict = NULL;
61  proxy_config->GetDictionary("pacScript", &pac_dict);
62
63  DictionaryValue* proxy_rules = NULL;
64  proxy_config->GetDictionary("rules", &proxy_rules);
65
66  // TODO(battre,gfeher): Make sure all the preferences get always
67  // overwritten.
68  return ApplyMode(proxy_mode) &&
69      ApplyPacScript(pac_dict) &&
70      ApplyProxyRules(proxy_rules);
71}
72
73bool UseCustomProxySettingsFunction::GetProxyServer(
74    const DictionaryValue* dict, ProxyServer* proxy_server) {
75  dict->GetString("scheme", &proxy_server->scheme);
76  EXTENSION_FUNCTION_VALIDATE(dict->GetString("host", &proxy_server->host));
77  dict->GetInteger("port", &proxy_server->port);
78  return true;
79}
80
81bool UseCustomProxySettingsFunction::ApplyMode(const std::string& mode) {
82  // We take control of the mode preference even if none was specified, so that
83  // all proxy preferences are controlled by the same extension (if not by a
84  // higher-priority source).
85  bool result = true;
86  ProxyPrefs::ProxyMode mode_enum;
87  if (!ProxyPrefs::StringToProxyMode(mode, &mode_enum)) {
88    mode_enum = ProxyPrefs::MODE_SYSTEM;
89    LOG(WARNING) << "Invalid mode for proxy settings: " << mode;
90    result = false;
91  }
92  ApplyPreference(prefs::kProxyMode, Value::CreateIntegerValue(mode_enum));
93  return result;
94}
95
96bool UseCustomProxySettingsFunction::ApplyPacScript(DictionaryValue* pac_dict) {
97  std::string pac_url;
98  if (pac_dict)
99    pac_dict->GetString("url", &pac_url);
100
101  // We take control of the PAC preference even if none was specified, so that
102  // all proxy preferences are controlled by the same extension (if not by a
103  // higher-priority source).
104  ApplyPreference(prefs::kProxyPacUrl, Value::CreateStringValue(pac_url));
105  return true;
106}
107
108bool UseCustomProxySettingsFunction::ApplyProxyRules(
109    DictionaryValue* proxy_rules) {
110  if (!proxy_rules) {
111    ApplyPreference(prefs::kProxyServer, Value::CreateStringValue(""));
112    return true;
113  }
114
115  // Local data into which the parameters will be parsed. has_proxy describes
116  // whether a setting was found for the scheme; proxy_dict holds the
117  // DictionaryValues which in turn contain proxy server descriptions, and
118  // proxy_server holds ProxyServer structs containing those descriptions.
119  bool has_proxy[SCHEME_MAX + 1];
120  DictionaryValue* proxy_dict[SCHEME_MAX + 1];
121  ProxyServer proxy_server[SCHEME_MAX + 1];
122
123  // Looking for all possible proxy types is inefficient if we have a
124  // singleProxy that will supersede per-URL proxies, but it's worth it to keep
125  // the code simple and extensible.
126  for (size_t i = 0; i <= SCHEME_MAX; ++i) {
127    has_proxy[i] = proxy_rules->GetDictionary(field_name[i], &proxy_dict[i]);
128    if (has_proxy[i]) {
129      if (!GetProxyServer(proxy_dict[i], &proxy_server[i]))
130        return false;
131    }
132  }
133
134  // A single proxy supersedes individual HTTP, HTTPS, and FTP proxies.
135  if (has_proxy[SCHEME_ALL]) {
136    proxy_server[SCHEME_HTTP] = proxy_server[SCHEME_ALL];
137    proxy_server[SCHEME_HTTPS] = proxy_server[SCHEME_ALL];
138    proxy_server[SCHEME_FTP] = proxy_server[SCHEME_ALL];
139    has_proxy[SCHEME_HTTP] = true;
140    has_proxy[SCHEME_HTTPS] = true;
141    has_proxy[SCHEME_FTP] = true;
142    has_proxy[SCHEME_ALL] = false;
143  }
144
145  // TODO(pamg): Ensure that if a value is empty, that means "don't use a proxy
146  // for this scheme".
147
148  // Build the proxy preference string.
149  std::string proxy_pref;
150  for (size_t i = 0; i <= SCHEME_MAX; ++i) {
151    if (has_proxy[i]) {
152      // http=foopy:4010;ftp=socks://foopy2:80
153      if (!proxy_pref.empty())
154        proxy_pref.append(";");
155      proxy_pref.append(scheme_name[i]);
156      proxy_pref.append("=");
157      proxy_pref.append(proxy_server[i].scheme);
158      proxy_pref.append("://");
159      proxy_pref.append(proxy_server[i].host);
160      if (proxy_server[i].port != ProxyServer::INVALID_PORT) {
161        proxy_pref.append(":");
162        proxy_pref.append(base::StringPrintf("%d", proxy_server[i].port));
163      }
164    }
165  }
166
167  ApplyPreference(prefs::kProxyServer, Value::CreateStringValue(proxy_pref));
168  return true;
169}
170
171void UseCustomProxySettingsFunction::ApplyPreference(const char* pref_path,
172                                                     Value* pref_value) {
173  profile()->GetExtensionService()->extension_prefs()
174      ->SetExtensionControlledPref(extension_id(), pref_path, pref_value);
175}
176