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/chromeos/proxy_cros_settings_parser.h"
6
7#include "base/strings/string_util.h"
8#include "base/values.h"
9#include "chrome/browser/chromeos/ui_proxy_config.h"
10#include "chrome/browser/chromeos/ui_proxy_config_service.h"
11
12namespace chromeos {
13
14// Common prefix of all proxy prefs.
15const char kProxyPrefsPrefix[] = "cros.session.proxy";
16
17// Names of proxy preferences.
18const char kProxyPacUrl[]         = "cros.session.proxy.pacurl";
19const char kProxySingleHttp[]     = "cros.session.proxy.singlehttp";
20const char kProxySingleHttpPort[] = "cros.session.proxy.singlehttpport";
21const char kProxyHttpUrl[]        = "cros.session.proxy.httpurl";
22const char kProxyHttpPort[]       = "cros.session.proxy.httpport";
23const char kProxyHttpsUrl[]       = "cros.session.proxy.httpsurl";
24const char kProxyHttpsPort[]      = "cros.session.proxy.httpsport";
25const char kProxyType[]           = "cros.session.proxy.type";
26const char kProxySingle[]         = "cros.session.proxy.single";
27const char kProxyFtpUrl[]         = "cros.session.proxy.ftpurl";
28const char kProxyFtpPort[]        = "cros.session.proxy.ftpport";
29const char kProxySocks[]          = "cros.session.proxy.socks";
30const char kProxySocksPort[]      = "cros.session.proxy.socksport";
31const char kProxyIgnoreList[]     = "cros.session.proxy.ignorelist";
32const char kProxyUsePacUrl[]      = "cros.session.proxy.usepacurl";
33
34const char* const kProxySettings[] = {
35  kProxyPacUrl,
36  kProxySingleHttp,
37  kProxySingleHttpPort,
38  kProxyHttpUrl,
39  kProxyHttpPort,
40  kProxyHttpsUrl,
41  kProxyHttpsPort,
42  kProxyType,
43  kProxySingle,
44  kProxyFtpUrl,
45  kProxyFtpPort,
46  kProxySocks,
47  kProxySocksPort,
48  kProxyIgnoreList,
49  kProxyUsePacUrl,
50};
51
52// We have to explicitly export this because the arraysize macro doesn't like
53// extern arrays as their size is not known on compile time.
54const size_t kProxySettingsCount = arraysize(kProxySettings);
55
56namespace {
57
58base::Value* CreateServerHostValue(const UIProxyConfig::ManualProxy& proxy) {
59  return proxy.server.is_valid() ?
60         new base::StringValue(proxy.server.host_port_pair().host()) :
61         NULL;
62}
63
64base::Value* CreateServerPortValue(const UIProxyConfig::ManualProxy& proxy) {
65  return proxy.server.is_valid()
66             ? new base::FundamentalValue(proxy.server.host_port_pair().port())
67             : NULL;
68}
69
70net::ProxyServer CreateProxyServer(std::string host,
71                                   uint16 port,
72                                   net::ProxyServer::Scheme scheme) {
73  if (host.empty() && port == 0)
74    return net::ProxyServer();
75  uint16 default_port = net::ProxyServer::GetDefaultPortForScheme(scheme);
76  net::HostPortPair host_port_pair;
77  // Check if host is a valid URL or a string of valid format <server>::<port>.
78  GURL url(host);
79  if (url.is_valid())  // See if host is URL.
80    host_port_pair = net::HostPortPair::FromURL(url);
81  if (host_port_pair.host().empty())  // See if host is <server>::<port>.
82    host_port_pair = net::HostPortPair::FromString(host);
83  if (host_port_pair.host().empty())  // Host is not URL or <server>::<port>.
84    host_port_pair = net::HostPortPair(host, port);
85  if (host_port_pair.port() == 0)  // No port in host, use default.
86    host_port_pair.set_port(default_port);
87  return net::ProxyServer(scheme, host_port_pair);
88}
89
90net::ProxyServer CreateProxyServerFromHost(
91    const std::string& host,
92    const UIProxyConfig::ManualProxy& proxy,
93    net::ProxyServer::Scheme scheme) {
94  uint16 port = 0;
95  if (proxy.server.is_valid())
96    port = proxy.server.host_port_pair().port();
97  return CreateProxyServer(host, port, scheme);
98}
99
100net::ProxyServer CreateProxyServerFromPort(
101    uint16 port,
102    const UIProxyConfig::ManualProxy& proxy,
103    net::ProxyServer::Scheme scheme) {
104  std::string host;
105  if (proxy.server.is_valid())
106    host = proxy.server.host_port_pair().host();
107  return CreateProxyServer(host, port, scheme);
108}
109
110}  // namespace
111
112namespace proxy_cros_settings_parser {
113
114bool IsProxyPref(const std::string& path) {
115  return StartsWithASCII(path, kProxyPrefsPrefix, true);
116}
117
118void SetProxyPrefValue(const std::string& path,
119                       const base::Value* in_value,
120                       UIProxyConfigService* config_service) {
121  if (!in_value) {
122    NOTREACHED();
123    return;
124  }
125
126  // Retrieve proxy config.
127  UIProxyConfig config;
128  config_service->GetProxyConfig(&config);
129
130  if (path == kProxyPacUrl) {
131    std::string val;
132    if (in_value->GetAsString(&val)) {
133      GURL url(val);
134      if (url.is_valid())
135        config.SetPacUrl(url);
136      else
137        config.mode = UIProxyConfig::MODE_AUTO_DETECT;
138    }
139  } else if (path == kProxySingleHttp) {
140    std::string val;
141    if (in_value->GetAsString(&val)) {
142      config.SetSingleProxy(CreateProxyServerFromHost(
143          val, config.single_proxy, net::ProxyServer::SCHEME_HTTP));
144    }
145  } else if (path == kProxySingleHttpPort) {
146    int val;
147    if (in_value->GetAsInteger(&val)) {
148      config.SetSingleProxy(CreateProxyServerFromPort(
149          val, config.single_proxy, net::ProxyServer::SCHEME_HTTP));
150    }
151  } else if (path == kProxyHttpUrl) {
152    std::string val;
153    if (in_value->GetAsString(&val)) {
154      config.SetProxyForScheme(
155          "http", CreateProxyServerFromHost(
156              val, config.http_proxy, net::ProxyServer::SCHEME_HTTP));
157    }
158  } else if (path == kProxyHttpPort) {
159    int val;
160    if (in_value->GetAsInteger(&val)) {
161      config.SetProxyForScheme(
162          "http", CreateProxyServerFromPort(
163              val, config.http_proxy, net::ProxyServer::SCHEME_HTTP));
164    }
165  } else if (path == kProxyHttpsUrl) {
166    std::string val;
167    if (in_value->GetAsString(&val)) {
168      config.SetProxyForScheme(
169          "https", CreateProxyServerFromHost(
170              val, config.https_proxy, net::ProxyServer::SCHEME_HTTP));
171    }
172  } else if (path == kProxyHttpsPort) {
173    int val;
174    if (in_value->GetAsInteger(&val)) {
175      config.SetProxyForScheme(
176          "https", CreateProxyServerFromPort(
177              val, config.https_proxy, net::ProxyServer::SCHEME_HTTP));
178    }
179  } else if (path == kProxyType) {
180    int val;
181    if (in_value->GetAsInteger(&val)) {
182      if (val == 3) {
183        if (config.automatic_proxy.pac_url.is_valid())
184          config.SetPacUrl(config.automatic_proxy.pac_url);
185        else
186          config.mode = UIProxyConfig::MODE_AUTO_DETECT;
187      } else if (val == 2) {
188        if (config.single_proxy.server.is_valid()) {
189          config.SetSingleProxy(config.single_proxy.server);
190        } else {
191          bool set_config = false;
192          if (config.http_proxy.server.is_valid()) {
193            config.SetProxyForScheme("http", config.http_proxy.server);
194            set_config = true;
195          }
196          if (config.https_proxy.server.is_valid()) {
197            config.SetProxyForScheme("https", config.https_proxy.server);
198            set_config = true;
199          }
200          if (config.ftp_proxy.server.is_valid()) {
201            config.SetProxyForScheme("ftp", config.ftp_proxy.server);
202            set_config = true;
203          }
204          if (config.socks_proxy.server.is_valid()) {
205            config.SetProxyForScheme("socks", config.socks_proxy.server);
206            set_config = true;
207          }
208          if (!set_config)
209            config.SetProxyForScheme("http", net::ProxyServer());
210        }
211      } else {
212        config.mode = UIProxyConfig::MODE_DIRECT;
213      }
214    }
215  } else if (path == kProxySingle) {
216    bool val;
217    if (in_value->GetAsBoolean(&val)) {
218      if (val)
219        config.SetSingleProxy(config.single_proxy.server);
220      else
221        config.SetProxyForScheme("http", config.http_proxy.server);
222    }
223  } else if (path == kProxyUsePacUrl) {
224    bool use_pac_url;
225    if (in_value->GetAsBoolean(&use_pac_url)) {
226      if (use_pac_url && config.automatic_proxy.pac_url.is_valid())
227        config.SetPacUrl(config.automatic_proxy.pac_url);
228      else
229        config.mode = UIProxyConfig::MODE_AUTO_DETECT;
230    }
231  } else if (path == kProxyFtpUrl) {
232    std::string val;
233    if (in_value->GetAsString(&val)) {
234      config.SetProxyForScheme(
235          "ftp", CreateProxyServerFromHost(
236              val, config.ftp_proxy, net::ProxyServer::SCHEME_HTTP));
237    }
238  } else if (path == kProxyFtpPort) {
239    int val;
240    if (in_value->GetAsInteger(&val)) {
241      config.SetProxyForScheme(
242          "ftp", CreateProxyServerFromPort(
243              val, config.ftp_proxy, net::ProxyServer::SCHEME_HTTP));
244    }
245  } else if (path == kProxySocks) {
246    std::string val;
247    if (in_value->GetAsString(&val)) {
248      config.SetProxyForScheme(
249          "socks", CreateProxyServerFromHost(
250              val,
251              config.socks_proxy,
252              StartsWithASCII(val, "socks5://", false) ?
253              net::ProxyServer::SCHEME_SOCKS5 :
254              net::ProxyServer::SCHEME_SOCKS4));
255    }
256  } else if (path == kProxySocksPort) {
257    int val;
258    if (in_value->GetAsInteger(&val)) {
259      std::string host = config.socks_proxy.server.host_port_pair().host();
260      config.SetProxyForScheme(
261          "socks", CreateProxyServerFromPort(
262              val,
263              config.socks_proxy,
264              StartsWithASCII(host, "socks5://", false) ?
265              net::ProxyServer::SCHEME_SOCKS5 :
266              net::ProxyServer::SCHEME_SOCKS4));
267    }
268  } else if (path == kProxyIgnoreList) {
269    net::ProxyBypassRules bypass_rules;
270    if (in_value->GetType() == base::Value::TYPE_LIST) {
271      const base::ListValue* list_value =
272          static_cast<const base::ListValue*>(in_value);
273      for (size_t x = 0; x < list_value->GetSize(); x++) {
274        std::string val;
275        if (list_value->GetString(x, &val))
276          bypass_rules.AddRuleFromString(val);
277      }
278      config.SetBypassRules(bypass_rules);
279    }
280  } else {
281    LOG(WARNING) << "Unknown proxy settings path " << path;
282    return;
283  }
284
285  config_service->SetProxyConfig(config);
286}
287
288bool GetProxyPrefValue(const UIProxyConfigService& config_service,
289                       const std::string& path,
290                       base::Value** out_value) {
291  std::string controlled_by;
292  base::Value* data = NULL;
293  UIProxyConfig config;
294  config_service.GetProxyConfig(&config);
295
296  if (path == kProxyPacUrl) {
297    // Only show pacurl for pac-script mode.
298    if (config.mode == UIProxyConfig::MODE_PAC_SCRIPT &&
299        config.automatic_proxy.pac_url.is_valid()) {
300      data = new base::StringValue(config.automatic_proxy.pac_url.spec());
301    }
302  } else if (path == kProxySingleHttp) {
303    data = CreateServerHostValue(config.single_proxy);
304  } else if (path == kProxySingleHttpPort) {
305    data = CreateServerPortValue(config.single_proxy);
306  } else if (path == kProxyHttpUrl) {
307    data = CreateServerHostValue(config.http_proxy);
308  } else if (path == kProxyHttpsUrl) {
309    data = CreateServerHostValue(config.https_proxy);
310  } else if (path == kProxyType) {
311    if (config.mode == UIProxyConfig::MODE_AUTO_DETECT ||
312        config.mode == UIProxyConfig::MODE_PAC_SCRIPT) {
313      data = new base::FundamentalValue(3);
314    } else if (config.mode == UIProxyConfig::MODE_SINGLE_PROXY ||
315               config.mode == UIProxyConfig::MODE_PROXY_PER_SCHEME) {
316      data = new base::FundamentalValue(2);
317    } else {
318      data = new base::FundamentalValue(1);
319    }
320    switch (config.state) {
321      case ProxyPrefs::CONFIG_POLICY:
322        controlled_by = "policy";
323        break;
324      case ProxyPrefs::CONFIG_EXTENSION:
325        controlled_by = "extension";
326        break;
327      case ProxyPrefs::CONFIG_OTHER_PRECEDE:
328        controlled_by = "other";
329        break;
330      default:
331        if (!config.user_modifiable)
332          controlled_by = "shared";
333        break;
334    }
335  } else if (path == kProxySingle) {
336    data = new base::FundamentalValue(config.mode ==
337                                      UIProxyConfig::MODE_SINGLE_PROXY);
338  } else if (path == kProxyUsePacUrl) {
339    data = new base::FundamentalValue(config.mode ==
340                                      UIProxyConfig::MODE_PAC_SCRIPT);
341  } else if (path == kProxyFtpUrl) {
342    data = CreateServerHostValue(config.ftp_proxy);
343  } else if (path == kProxySocks) {
344    data = CreateServerHostValue(config.socks_proxy);
345  } else if (path == kProxyHttpPort) {
346    data = CreateServerPortValue(config.http_proxy);
347  } else if (path == kProxyHttpsPort) {
348    data = CreateServerPortValue(config.https_proxy);
349  } else if (path == kProxyFtpPort) {
350    data = CreateServerPortValue(config.ftp_proxy);
351  } else if (path == kProxySocksPort) {
352    data = CreateServerPortValue(config.socks_proxy);
353  } else if (path == kProxyIgnoreList) {
354    base::ListValue* list =  new base::ListValue();
355    net::ProxyBypassRules::RuleList bypass_rules = config.bypass_rules.rules();
356    for (size_t x = 0; x < bypass_rules.size(); x++)
357      list->Append(new base::StringValue(bypass_rules[x]->ToString()));
358    data = list;
359  } else {
360    *out_value = NULL;
361    return false;
362  }
363
364  // Decorate pref value as CoreOptionsHandler::CreateValueForPref() does.
365  base::DictionaryValue* dict = new base::DictionaryValue;
366  if (!data)
367    data = new base::StringValue("");
368  dict->Set("value", data);
369  if (path == kProxyType) {
370    if (!controlled_by.empty())
371      dict->SetString("controlledBy", controlled_by);
372    dict->SetBoolean("disabled", !config.user_modifiable);
373  } else {
374    dict->SetBoolean("disabled", false);
375  }
376  *out_value = dict;
377  return true;
378}
379
380}  // namespace proxy_cros_settings_parser
381
382}  // namespace chromeos
383