| // Copyright (c) 2012 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) const { | 
 |   if (empty()) { | 
 |     result->UseDirect(); | 
 |     return; | 
 |   } | 
 |  | 
 |   bool bypass_proxy = bypass_rules.Matches(url); | 
 |   if (reverse_bypass) | 
 |     bypass_proxy = !bypass_proxy; | 
 |   if (bypass_proxy) { | 
 |     result->UseDirectWithBypassedProxy(); | 
 |     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), pac_mandatory_(false), | 
 |       source_(PROXY_CONFIG_SOURCE_UNKNOWN), id_(kInvalidConfigID)  { | 
 | } | 
 |  | 
 | ProxyConfig::ProxyConfig(const ProxyConfig& config) | 
 |     : auto_detect_(config.auto_detect_), | 
 |       pac_url_(config.pac_url_), | 
 |       pac_mandatory_(config.pac_mandatory_), | 
 |       proxy_rules_(config.proxy_rules_), | 
 |       source_(config.source_), | 
 |       id_(config.id_) { | 
 | } | 
 |  | 
 | ProxyConfig::~ProxyConfig() { | 
 | } | 
 |  | 
 | ProxyConfig& ProxyConfig::operator=(const ProxyConfig& config) { | 
 |   auto_detect_ = config.auto_detect_; | 
 |   pac_url_ = config.pac_url_; | 
 |   pac_mandatory_ = config.pac_mandatory_; | 
 |   proxy_rules_ = config.proxy_rules_; | 
 |   source_ = config.source_; | 
 |   id_ = config.id_; | 
 |   return *this; | 
 | } | 
 |  | 
 | bool ProxyConfig::Equals(const ProxyConfig& other) const { | 
 |   // The two configs can have different IDs and sources.  We are just interested | 
 |   // in if they have the same settings. | 
 |   return auto_detect_ == other.auto_detect_ && | 
 |          pac_url_ == other.pac_url_ && | 
 |          pac_mandatory_ == other.pac_mandatory_ && | 
 |          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()); | 
 |     if (pac_mandatory_) | 
 |       dict->SetBoolean("pac_mandatory", pac_mandatory_); | 
 |   } | 
 |  | 
 |   // 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); | 
 |     } | 
 |   } | 
 |  | 
 |   // Output the source. | 
 |   dict->SetString("source", ProxyConfigSourceToString(source_)); | 
 |  | 
 |   return dict; | 
 | } | 
 |  | 
 | }  // namespace net |