// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/proxy/proxy_config.h" #include "base/logging.h" #include "base/string_tokenizer.h" #include "base/string_util.h" #include "base/values.h" #include "net/proxy/proxy_info.h" namespace net { namespace { // If |proxy| is valid, sets it in |dict| under the key |name|. void AddProxyToValue(const char* name, const ProxyServer& proxy, DictionaryValue* dict) { if (proxy.is_valid()) dict->SetString(name, proxy.ToURI()); } } // namespace ProxyConfig::ProxyRules::ProxyRules() : reverse_bypass(false), type(TYPE_NO_RULES) { } ProxyConfig::ProxyRules::~ProxyRules() { } void ProxyConfig::ProxyRules::Apply(const GURL& url, ProxyInfo* result) { if (empty()) { result->UseDirect(); return; } bool bypass_proxy = bypass_rules.Matches(url); if (reverse_bypass) bypass_proxy = !bypass_proxy; if (bypass_proxy) { result->UseDirect(); return; } switch (type) { case ProxyRules::TYPE_SINGLE_PROXY: { result->UseProxyServer(single_proxy); return; } case ProxyRules::TYPE_PROXY_PER_SCHEME: { const ProxyServer* entry = MapUrlSchemeToProxy(url.scheme()); if (entry) { result->UseProxyServer(*entry); } else { // We failed to find a matching proxy server for the current URL // scheme. Default to direct. result->UseDirect(); } return; } default: { result->UseDirect(); NOTREACHED(); return; } } } void ProxyConfig::ProxyRules::ParseFromString(const std::string& proxy_rules) { // Reset. type = TYPE_NO_RULES; single_proxy = ProxyServer(); proxy_for_http = ProxyServer(); proxy_for_https = ProxyServer(); proxy_for_ftp = ProxyServer(); fallback_proxy = ProxyServer(); StringTokenizer proxy_server_list(proxy_rules, ";"); while (proxy_server_list.GetNext()) { StringTokenizer proxy_server_for_scheme( proxy_server_list.token_begin(), proxy_server_list.token_end(), "="); while (proxy_server_for_scheme.GetNext()) { std::string url_scheme = proxy_server_for_scheme.token(); // If we fail to get the proxy server here, it means that // this is a regular proxy server configuration, i.e. proxies // are not configured per protocol. if (!proxy_server_for_scheme.GetNext()) { if (type == TYPE_PROXY_PER_SCHEME) continue; // Unexpected. single_proxy = ProxyServer::FromURI(url_scheme, ProxyServer::SCHEME_HTTP); type = TYPE_SINGLE_PROXY; return; } // Trim whitespace off the url scheme. TrimWhitespaceASCII(url_scheme, TRIM_ALL, &url_scheme); // Add it to the per-scheme mappings (if supported scheme). type = TYPE_PROXY_PER_SCHEME; ProxyServer* entry = MapUrlSchemeToProxyNoFallback(url_scheme); ProxyServer::Scheme default_scheme = ProxyServer::SCHEME_HTTP; // socks=XXX is inconsistent with the other formats, since "socks" // is not a URL scheme. Rather this means "for everything else, send // it to the SOCKS proxy server XXX". if (url_scheme == "socks") { DCHECK(!entry); entry = &fallback_proxy; default_scheme = ProxyServer::SCHEME_SOCKS4; } if (entry) { *entry = ProxyServer::FromURI(proxy_server_for_scheme.token(), default_scheme); } } } } const ProxyServer* ProxyConfig::ProxyRules::MapUrlSchemeToProxy( const std::string& url_scheme) const { const ProxyServer* proxy_server = const_cast<ProxyRules*>(this)->MapUrlSchemeToProxyNoFallback(url_scheme); if (proxy_server && proxy_server->is_valid()) return proxy_server; if (fallback_proxy.is_valid()) return &fallback_proxy; return NULL; // No mapping for this scheme. Use direct. } bool ProxyConfig::ProxyRules::Equals(const ProxyRules& other) const { return type == other.type && single_proxy == other.single_proxy && proxy_for_http == other.proxy_for_http && proxy_for_https == other.proxy_for_https && proxy_for_ftp == other.proxy_for_ftp && fallback_proxy == other.fallback_proxy && bypass_rules.Equals(other.bypass_rules) && reverse_bypass == other.reverse_bypass; } ProxyServer* ProxyConfig::ProxyRules::MapUrlSchemeToProxyNoFallback( const std::string& scheme) { DCHECK_EQ(TYPE_PROXY_PER_SCHEME, type); if (scheme == "http") return &proxy_for_http; if (scheme == "https") return &proxy_for_https; if (scheme == "ftp") return &proxy_for_ftp; return NULL; // No mapping for this scheme. } ProxyConfig::ProxyConfig() : auto_detect_(false), id_(INVALID_ID) { } ProxyConfig::ProxyConfig(const ProxyConfig& config) : auto_detect_(config.auto_detect_), pac_url_(config.pac_url_), proxy_rules_(config.proxy_rules_), id_(config.id_) { } ProxyConfig::~ProxyConfig() { } ProxyConfig& ProxyConfig::operator=(const ProxyConfig& config) { auto_detect_ = config.auto_detect_; pac_url_ = config.pac_url_; proxy_rules_ = config.proxy_rules_; id_ = config.id_; return *this; } bool ProxyConfig::Equals(const ProxyConfig& other) const { // The two configs can have different IDs. We are just interested in if they // have the same settings. return auto_detect_ == other.auto_detect_ && pac_url_ == other.pac_url_ && proxy_rules_.Equals(other.proxy_rules()); } bool ProxyConfig::HasAutomaticSettings() const { return auto_detect_ || has_pac_url(); } void ProxyConfig::ClearAutomaticSettings() { auto_detect_ = false; pac_url_ = GURL(); } Value* ProxyConfig::ToValue() const { DictionaryValue* dict = new DictionaryValue(); // Output the automatic settings. if (auto_detect_) dict->SetBoolean("auto_detect", auto_detect_); if (has_pac_url()) dict->SetString("pac_url", pac_url_.possibly_invalid_spec()); // Output the manual settings. if (proxy_rules_.type != ProxyRules::TYPE_NO_RULES) { switch (proxy_rules_.type) { case ProxyRules::TYPE_SINGLE_PROXY: AddProxyToValue("single_proxy", proxy_rules_.single_proxy, dict); break; case ProxyRules::TYPE_PROXY_PER_SCHEME: { DictionaryValue* dict2 = new DictionaryValue(); AddProxyToValue("http", proxy_rules_.proxy_for_http, dict2); AddProxyToValue("https", proxy_rules_.proxy_for_https, dict2); AddProxyToValue("ftp", proxy_rules_.proxy_for_ftp, dict2); AddProxyToValue("fallback", proxy_rules_.fallback_proxy, dict2); dict->Set("proxy_per_scheme", dict2); break; } default: NOTREACHED(); } // Output the bypass rules. const ProxyBypassRules& bypass = proxy_rules_.bypass_rules; if (!bypass.rules().empty()) { if (proxy_rules_.reverse_bypass) dict->SetBoolean("reverse_bypass", true); ListValue* list = new ListValue(); for (ProxyBypassRules::RuleList::const_iterator it = bypass.rules().begin(); it != bypass.rules().end(); ++it) { list->Append(Value::CreateStringValue((*it)->ToString())); } dict->Set("bypass_list", list); } } return dict; } } // namespace net