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 "net/proxy/proxy_config.h"
6
7#include "base/logging.h"
8#include "base/string_tokenizer.h"
9#include "base/string_util.h"
10#include "base/values.h"
11#include "net/proxy/proxy_info.h"
12
13namespace net {
14
15namespace {
16
17// If |proxy| is valid, sets it in |dict| under the key |name|.
18void AddProxyToValue(const char* name,
19                     const ProxyServer& proxy,
20                     DictionaryValue* dict) {
21  if (proxy.is_valid())
22    dict->SetString(name, proxy.ToURI());
23}
24
25}  // namespace
26
27ProxyConfig::ProxyRules::ProxyRules()
28    : reverse_bypass(false),
29      type(TYPE_NO_RULES) {
30}
31
32ProxyConfig::ProxyRules::~ProxyRules() {
33}
34
35void ProxyConfig::ProxyRules::Apply(const GURL& url, ProxyInfo* result) {
36  if (empty()) {
37    result->UseDirect();
38    return;
39  }
40
41  bool bypass_proxy = bypass_rules.Matches(url);
42  if (reverse_bypass)
43    bypass_proxy = !bypass_proxy;
44  if (bypass_proxy) {
45    result->UseDirect();
46    return;
47  }
48
49  switch (type) {
50    case ProxyRules::TYPE_SINGLE_PROXY: {
51      result->UseProxyServer(single_proxy);
52      return;
53    }
54    case ProxyRules::TYPE_PROXY_PER_SCHEME: {
55      const ProxyServer* entry = MapUrlSchemeToProxy(url.scheme());
56      if (entry) {
57        result->UseProxyServer(*entry);
58      } else {
59        // We failed to find a matching proxy server for the current URL
60        // scheme. Default to direct.
61        result->UseDirect();
62      }
63      return;
64    }
65    default: {
66      result->UseDirect();
67      NOTREACHED();
68      return;
69    }
70  }
71}
72
73void ProxyConfig::ProxyRules::ParseFromString(const std::string& proxy_rules) {
74  // Reset.
75  type = TYPE_NO_RULES;
76  single_proxy = ProxyServer();
77  proxy_for_http = ProxyServer();
78  proxy_for_https = ProxyServer();
79  proxy_for_ftp = ProxyServer();
80  fallback_proxy = ProxyServer();
81
82  StringTokenizer proxy_server_list(proxy_rules, ";");
83  while (proxy_server_list.GetNext()) {
84    StringTokenizer proxy_server_for_scheme(
85        proxy_server_list.token_begin(), proxy_server_list.token_end(), "=");
86
87    while (proxy_server_for_scheme.GetNext()) {
88      std::string url_scheme = proxy_server_for_scheme.token();
89
90      // If we fail to get the proxy server here, it means that
91      // this is a regular proxy server configuration, i.e. proxies
92      // are not configured per protocol.
93      if (!proxy_server_for_scheme.GetNext()) {
94        if (type == TYPE_PROXY_PER_SCHEME)
95          continue;  // Unexpected.
96        single_proxy = ProxyServer::FromURI(url_scheme,
97                                            ProxyServer::SCHEME_HTTP);
98        type = TYPE_SINGLE_PROXY;
99        return;
100      }
101
102      // Trim whitespace off the url scheme.
103      TrimWhitespaceASCII(url_scheme, TRIM_ALL, &url_scheme);
104
105      // Add it to the per-scheme mappings (if supported scheme).
106      type = TYPE_PROXY_PER_SCHEME;
107      ProxyServer* entry = MapUrlSchemeToProxyNoFallback(url_scheme);
108      ProxyServer::Scheme default_scheme = ProxyServer::SCHEME_HTTP;
109
110      // socks=XXX is inconsistent with the other formats, since "socks"
111      // is not a URL scheme. Rather this means "for everything else, send
112      // it to the SOCKS proxy server XXX".
113      if (url_scheme == "socks") {
114        DCHECK(!entry);
115        entry = &fallback_proxy;
116        default_scheme = ProxyServer::SCHEME_SOCKS4;
117      }
118
119      if (entry) {
120        *entry = ProxyServer::FromURI(proxy_server_for_scheme.token(),
121                                      default_scheme);
122      }
123    }
124  }
125}
126
127const ProxyServer* ProxyConfig::ProxyRules::MapUrlSchemeToProxy(
128    const std::string& url_scheme) const {
129  const ProxyServer* proxy_server =
130      const_cast<ProxyRules*>(this)->MapUrlSchemeToProxyNoFallback(url_scheme);
131  if (proxy_server && proxy_server->is_valid())
132    return proxy_server;
133  if (fallback_proxy.is_valid())
134    return &fallback_proxy;
135  return NULL;  // No mapping for this scheme. Use direct.
136}
137
138bool ProxyConfig::ProxyRules::Equals(const ProxyRules& other) const {
139  return type == other.type &&
140         single_proxy == other.single_proxy &&
141         proxy_for_http == other.proxy_for_http &&
142         proxy_for_https == other.proxy_for_https &&
143         proxy_for_ftp == other.proxy_for_ftp &&
144         fallback_proxy == other.fallback_proxy &&
145         bypass_rules.Equals(other.bypass_rules) &&
146         reverse_bypass == other.reverse_bypass;
147}
148
149ProxyServer* ProxyConfig::ProxyRules::MapUrlSchemeToProxyNoFallback(
150    const std::string& scheme) {
151  DCHECK_EQ(TYPE_PROXY_PER_SCHEME, type);
152  if (scheme == "http")
153    return &proxy_for_http;
154  if (scheme == "https")
155    return &proxy_for_https;
156  if (scheme == "ftp")
157    return &proxy_for_ftp;
158  return NULL;  // No mapping for this scheme.
159}
160
161ProxyConfig::ProxyConfig() : auto_detect_(false), id_(INVALID_ID) {
162}
163
164ProxyConfig::ProxyConfig(const ProxyConfig& config)
165    : auto_detect_(config.auto_detect_),
166      pac_url_(config.pac_url_),
167      proxy_rules_(config.proxy_rules_),
168      id_(config.id_) {
169}
170
171ProxyConfig::~ProxyConfig() {
172}
173
174ProxyConfig& ProxyConfig::operator=(const ProxyConfig& config) {
175  auto_detect_ = config.auto_detect_;
176  pac_url_ = config.pac_url_;
177  proxy_rules_ = config.proxy_rules_;
178  id_ = config.id_;
179  return *this;
180}
181
182bool ProxyConfig::Equals(const ProxyConfig& other) const {
183  // The two configs can have different IDs.  We are just interested in if they
184  // have the same settings.
185  return auto_detect_ == other.auto_detect_ &&
186         pac_url_ == other.pac_url_ &&
187         proxy_rules_.Equals(other.proxy_rules());
188}
189
190bool ProxyConfig::HasAutomaticSettings() const {
191  return auto_detect_ || has_pac_url();
192}
193
194void ProxyConfig::ClearAutomaticSettings() {
195  auto_detect_ = false;
196  pac_url_ = GURL();
197}
198
199Value* ProxyConfig::ToValue() const {
200  DictionaryValue* dict = new DictionaryValue();
201
202  // Output the automatic settings.
203  if (auto_detect_)
204    dict->SetBoolean("auto_detect", auto_detect_);
205  if (has_pac_url())
206    dict->SetString("pac_url", pac_url_.possibly_invalid_spec());
207
208  // Output the manual settings.
209  if (proxy_rules_.type != ProxyRules::TYPE_NO_RULES) {
210    switch (proxy_rules_.type) {
211      case ProxyRules::TYPE_SINGLE_PROXY:
212        AddProxyToValue("single_proxy", proxy_rules_.single_proxy, dict);
213        break;
214      case ProxyRules::TYPE_PROXY_PER_SCHEME: {
215        DictionaryValue* dict2 = new DictionaryValue();
216        AddProxyToValue("http", proxy_rules_.proxy_for_http, dict2);
217        AddProxyToValue("https", proxy_rules_.proxy_for_https, dict2);
218        AddProxyToValue("ftp", proxy_rules_.proxy_for_ftp, dict2);
219        AddProxyToValue("fallback", proxy_rules_.fallback_proxy, dict2);
220        dict->Set("proxy_per_scheme", dict2);
221        break;
222      }
223      default:
224        NOTREACHED();
225    }
226
227    // Output the bypass rules.
228    const ProxyBypassRules& bypass = proxy_rules_.bypass_rules;
229    if (!bypass.rules().empty()) {
230      if (proxy_rules_.reverse_bypass)
231        dict->SetBoolean("reverse_bypass", true);
232
233      ListValue* list = new ListValue();
234
235      for (ProxyBypassRules::RuleList::const_iterator it =
236              bypass.rules().begin();
237           it != bypass.rules().end(); ++it) {
238        list->Append(Value::CreateStringValue((*it)->ToString()));
239      }
240
241      dict->Set("bypass_list", list);
242    }
243  }
244
245  return dict;
246}
247
248}  // namespace net
249
250