text: // 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 "chrome/browser/importer/firefox_proxy_settings.h"
  
  #include "base/files/file_path.h"
  #include "base/string_util.h"
  #include "base/strings/string_tokenizer.h"
  #include "base/values.h"
  #include "chrome/browser/importer/firefox_importer_utils.h"
  #include "net/proxy/proxy_config.h"
  
  namespace {
  
  const char* const kNetworkProxyTypeKey = "network.proxy.type";
  const char* const kHTTPProxyKey = "network.proxy.http";
  const char* const kHTTPProxyPortKey = "network.proxy.http_port";
  const char* const kSSLProxyKey = "network.proxy.ssl";
  const char* const kSSLProxyPortKey = "network.proxy.ssl_port";
  const char* const kFTPProxyKey = "network.proxy.ftp";
  const char* const kFTPProxyPortKey = "network.proxy.ftp_port";
  const char* const kGopherProxyKey = "network.proxy.gopher";
  const char* const kGopherProxyPortKey = "network.proxy.gopher_port";
  const char* const kSOCKSHostKey = "network.proxy.socks";
  const char* const kSOCKSHostPortKey = "network.proxy.socks_port";
  const char* const kSOCKSVersionKey = "network.proxy.socks_version";
  const char* const kAutoconfigURL = "network.proxy.autoconfig_url";
  const char* const kNoProxyListKey = "network.proxy.no_proxies_on";
  const char* const kPrefFileName = "prefs.js";
  
  FirefoxProxySettings::ProxyConfig IntToProxyConfig(int type) {
    switch (type) {
      case 1:
        return FirefoxProxySettings::MANUAL;
      case 2:
        return FirefoxProxySettings::AUTO_FROM_URL;
      case 4:
        return FirefoxProxySettings::AUTO_DETECT;
      case 5:
        return FirefoxProxySettings::SYSTEM;
      default:
        LOG(ERROR) << "Unknown Firefox proxy config type: " << type;
        return FirefoxProxySettings::NO_PROXY;
    }
  }
  
  FirefoxProxySettings::SOCKSVersion IntToSOCKSVersion(int type) {
    switch (type) {
      case 4:
        return FirefoxProxySettings::V4;
      case 5:
        return FirefoxProxySettings::V5;
      default:
        LOG(ERROR) << "Unknown Firefox proxy config type: " << type;
        return FirefoxProxySettings::UNKNONW;
    }
  }
  
  }  // namespace
  
  FirefoxProxySettings::FirefoxProxySettings() {
    Reset();
  }
  
  FirefoxProxySettings::~FirefoxProxySettings() {
  }
  
  void FirefoxProxySettings::Reset() {
    config_type_ = NO_PROXY;
    http_proxy_.clear();
    http_proxy_port_ = 0;
    ssl_proxy_.clear();
    ssl_proxy_port_ = 0;
    ftp_proxy_.clear();
    ftp_proxy_port_ = 0;
    gopher_proxy_.clear();
    gopher_proxy_port_ = 0;
    socks_host_.clear();
    socks_port_ = 0;
    socks_version_ = UNKNONW;
    proxy_bypass_list_.clear();
    autoconfig_url_.clear();
  }
  
  // static
  bool FirefoxProxySettings::GetSettings(FirefoxProxySettings* settings) {
    DCHECK(settings);
    settings->Reset();
  
    base::FilePath profile_path = GetFirefoxProfilePath();
    if (profile_path.empty())
      return false;
    base::FilePath pref_file = profile_path.AppendASCII(kPrefFileName);
    return GetSettingsFromFile(pref_file, settings);
  }
  
  bool FirefoxProxySettings::ToProxyConfig(net::ProxyConfig* config) {
    switch (config_type()) {
      case NO_PROXY:
        *config = net::ProxyConfig::CreateDirect();
        return true;
      case AUTO_DETECT:
        *config = net::ProxyConfig::CreateAutoDetect();
        return true;
      case AUTO_FROM_URL:
        *config = net::ProxyConfig::CreateFromCustomPacURL(
            GURL(autoconfig_url()));
        return true;
      case SYSTEM:
        // Can't convert this directly to a ProxyConfig.
        return false;
      case MANUAL:
        // Handled outside of the switch (since it is a lot of code.)
        break;
      default:
        NOTREACHED();
        return false;
    }
  
    // The rest of this funciton is for handling the MANUAL case.
    DCHECK_EQ(MANUAL, config_type());
  
    *config = net::ProxyConfig();
    config->proxy_rules().type =
        net::ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
  
    if (!http_proxy().empty()) {
      config->proxy_rules().proxy_for_http = net::ProxyServer(
          net::ProxyServer::SCHEME_HTTP,
          net::HostPortPair(http_proxy(), http_proxy_port()));
    }
  
    if (!ftp_proxy().empty()) {
      config->proxy_rules().proxy_for_ftp = net::ProxyServer(
          net::ProxyServer::SCHEME_HTTP,
          net::HostPortPair(ftp_proxy(), ftp_proxy_port()));
    }
  
    if (!ssl_proxy().empty()) {
      config->proxy_rules().proxy_for_https = net::ProxyServer(
          net::ProxyServer::SCHEME_HTTP,
          net::HostPortPair(ssl_proxy(), ssl_proxy_port()));
    }
  
    if (!socks_host().empty()) {
      net::ProxyServer::Scheme proxy_scheme = V5 == socks_version() ?
          net::ProxyServer::SCHEME_SOCKS5 : net::ProxyServer::SCHEME_SOCKS4;
  
      config->proxy_rules().fallback_proxy = net::ProxyServer(
          proxy_scheme,
          net::HostPortPair(socks_host(), socks_port()));
    }
  
    config->proxy_rules().bypass_rules.ParseFromStringUsingSuffixMatching(
        JoinString(proxy_bypass_list_, ';'));
  
    return true;
  }
  
  // static
  bool FirefoxProxySettings::GetSettingsFromFile(const base::FilePath& pref_file,
                                                 FirefoxProxySettings* settings) {
    DictionaryValue dictionary;
    if (!ParsePrefFile(pref_file, &dictionary))
      return false;
  
    int proxy_type = 0;
    if (!dictionary.GetInteger(kNetworkProxyTypeKey, &proxy_type))
      return true;  // No type means no proxy.
  
    settings->config_type_ = IntToProxyConfig(proxy_type);
    if (settings->config_type_ == AUTO_FROM_URL) {
      if (!dictionary.GetStringASCII(kAutoconfigURL,
                                     &(settings->autoconfig_url_))) {
        LOG(ERROR) << "Failed to retrieve Firefox proxy autoconfig URL";
      }
      return true;
    }
  
    if (settings->config_type_ == MANUAL) {
      if (!dictionary.GetStringASCII(kHTTPProxyKey, &(settings->http_proxy_)))
        LOG(ERROR) << "Failed to retrieve Firefox proxy HTTP host";
      if (!dictionary.GetInteger(kHTTPProxyPortKey,
                                 &(settings->http_proxy_port_))) {
        LOG(ERROR) << "Failed to retrieve Firefox proxy HTTP port";
      }
      if (!dictionary.GetStringASCII(kSSLProxyKey, &(settings->ssl_proxy_)))
        LOG(ERROR) << "Failed to retrieve Firefox proxy SSL host";
      if (!dictionary.GetInteger(kSSLProxyPortKey, &(settings->ssl_proxy_port_)))
        LOG(ERROR) << "Failed to retrieve Firefox proxy SSL port";
      if (!dictionary.GetStringASCII(kFTPProxyKey, &(settings->ftp_proxy_)))
        LOG(ERROR) << "Failed to retrieve Firefox proxy FTP host";
      if (!dictionary.GetInteger(kFTPProxyPortKey, &(settings->ftp_proxy_port_)))
        LOG(ERROR) << "Failed to retrieve Firefox proxy SSL port";
      if (!dictionary.GetStringASCII(kGopherProxyKey, &(settings->gopher_proxy_)))
        LOG(ERROR) << "Failed to retrieve Firefox proxy gopher host";
      if (!dictionary.GetInteger(kGopherProxyPortKey,
                                 &(settings->gopher_proxy_port_))) {
        LOG(ERROR) << "Failed to retrieve Firefox proxy gopher port";
      }
      if (!dictionary.GetStringASCII(kSOCKSHostKey, &(settings->socks_host_)))
        LOG(ERROR) << "Failed to retrieve Firefox SOCKS host";
      if (!dictionary.GetInteger(kSOCKSHostPortKey, &(settings->socks_port_)))
        LOG(ERROR) << "Failed to retrieve Firefox SOCKS port";
      int socks_version;
      if (dictionary.GetInteger(kSOCKSVersionKey, &socks_version))
        settings->socks_version_ = IntToSOCKSVersion(socks_version);
  
      std::string proxy_bypass;
      if (dictionary.GetStringASCII(kNoProxyListKey, &proxy_bypass) &&
          !proxy_bypass.empty()) {
        base::StringTokenizer string_tok(proxy_bypass, ",");
        while (string_tok.GetNext()) {
          std::string token = string_tok.token();
          TrimWhitespaceASCII(token, TRIM_ALL, &token);
          if (!token.empty())
            settings->proxy_bypass_list_.push_back(token);
        }
      }
    }
    return true;
  }
  