Home | History | Annotate | Download | only in net
      1 // Copyright 2013 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/net/firefox_proxy_settings.h"
      6 
      7 #include "base/file_util.h"
      8 #include "base/files/file_path.h"
      9 #include "base/strings/string_number_conversions.h"
     10 #include "base/strings/string_tokenizer.h"
     11 #include "base/strings/string_util.h"
     12 #include "base/values.h"
     13 #include "chrome/common/importer/firefox_importer_utils.h"
     14 #include "net/proxy/proxy_config.h"
     15 
     16 namespace {
     17 
     18 const char* const kNetworkProxyTypeKey = "network.proxy.type";
     19 const char* const kHTTPProxyKey = "network.proxy.http";
     20 const char* const kHTTPProxyPortKey = "network.proxy.http_port";
     21 const char* const kSSLProxyKey = "network.proxy.ssl";
     22 const char* const kSSLProxyPortKey = "network.proxy.ssl_port";
     23 const char* const kFTPProxyKey = "network.proxy.ftp";
     24 const char* const kFTPProxyPortKey = "network.proxy.ftp_port";
     25 const char* const kGopherProxyKey = "network.proxy.gopher";
     26 const char* const kGopherProxyPortKey = "network.proxy.gopher_port";
     27 const char* const kSOCKSHostKey = "network.proxy.socks";
     28 const char* const kSOCKSHostPortKey = "network.proxy.socks_port";
     29 const char* const kSOCKSVersionKey = "network.proxy.socks_version";
     30 const char* const kAutoconfigURL = "network.proxy.autoconfig_url";
     31 const char* const kNoProxyListKey = "network.proxy.no_proxies_on";
     32 const char* const kPrefFileName = "prefs.js";
     33 
     34 FirefoxProxySettings::ProxyConfig IntToProxyConfig(int type) {
     35   switch (type) {
     36     case 1:
     37       return FirefoxProxySettings::MANUAL;
     38     case 2:
     39       return FirefoxProxySettings::AUTO_FROM_URL;
     40     case 4:
     41       return FirefoxProxySettings::AUTO_DETECT;
     42     case 5:
     43       return FirefoxProxySettings::SYSTEM;
     44     default:
     45       LOG(ERROR) << "Unknown Firefox proxy config type: " << type;
     46       return FirefoxProxySettings::NO_PROXY;
     47   }
     48 }
     49 
     50 FirefoxProxySettings::SOCKSVersion IntToSOCKSVersion(int type) {
     51   switch (type) {
     52     case 4:
     53       return FirefoxProxySettings::V4;
     54     case 5:
     55       return FirefoxProxySettings::V5;
     56     default:
     57       LOG(ERROR) << "Unknown Firefox proxy config type: " << type;
     58       return FirefoxProxySettings::UNKNONW;
     59   }
     60 }
     61 
     62 // Parses the prefs found in the file |pref_file| and puts the key/value pairs
     63 // in |prefs|. Keys are strings, and values can be strings, booleans or
     64 // integers.  Returns true if it succeeded, false otherwise (in which case
     65 // |prefs| is not filled).
     66 // Note: for strings, only valid UTF-8 string values are supported. If a
     67 // key/pair is not valid UTF-8, it is ignored and will not appear in |prefs|.
     68 bool ParsePrefFile(const base::FilePath& pref_file,
     69                    base::DictionaryValue* prefs) {
     70   // The string that is before a pref key.
     71   const std::string kUserPrefString = "user_pref(\"";
     72   std::string contents;
     73   if (!base::ReadFileToString(pref_file, &contents))
     74     return false;
     75 
     76   std::vector<std::string> lines;
     77   Tokenize(contents, "\n", &lines);
     78 
     79   for (std::vector<std::string>::const_iterator iter = lines.begin();
     80        iter != lines.end(); ++iter) {
     81     const std::string& line = *iter;
     82     size_t start_key = line.find(kUserPrefString);
     83     if (start_key == std::string::npos)
     84       continue;  // Could be a comment or a blank line.
     85     start_key += kUserPrefString.length();
     86     size_t stop_key = line.find('"', start_key);
     87     if (stop_key == std::string::npos) {
     88       LOG(ERROR) << "Invalid key found in Firefox pref file '" <<
     89           pref_file.value() << "' line is '" << line << "'.";
     90       continue;
     91     }
     92     std::string key = line.substr(start_key, stop_key - start_key);
     93     size_t start_value = line.find(',', stop_key + 1);
     94     if (start_value == std::string::npos) {
     95       LOG(ERROR) << "Invalid value found in Firefox pref file '" <<
     96           pref_file.value() << "' line is '" << line << "'.";
     97       continue;
     98     }
     99     size_t stop_value = line.find(");", start_value + 1);
    100     if (stop_value == std::string::npos) {
    101       LOG(ERROR) << "Invalid value found in Firefox pref file '" <<
    102           pref_file.value() << "' line is '" << line << "'.";
    103       continue;
    104     }
    105     std::string value = line.substr(start_value + 1,
    106                                     stop_value - start_value - 1);
    107     base::TrimWhitespace(value, base::TRIM_ALL, &value);
    108     // Value could be a boolean.
    109     bool is_value_true = LowerCaseEqualsASCII(value, "true");
    110     if (is_value_true || LowerCaseEqualsASCII(value, "false")) {
    111       prefs->SetBoolean(key, is_value_true);
    112       continue;
    113     }
    114 
    115     // Value could be a string.
    116     if (value.size() >= 2U &&
    117         value[0] == '"' && value[value.size() - 1] == '"') {
    118       value = value.substr(1, value.size() - 2);
    119       // ValueString only accept valid UTF-8.  Simply ignore that entry if it is
    120       // not UTF-8.
    121       if (base::IsStringUTF8(value))
    122         prefs->SetString(key, value);
    123       else
    124         VLOG(1) << "Non UTF8 value for key " << key << ", ignored.";
    125       continue;
    126     }
    127 
    128     // Or value could be an integer.
    129     int int_value = 0;
    130     if (base::StringToInt(value, &int_value)) {
    131       prefs->SetInteger(key, int_value);
    132       continue;
    133     }
    134 
    135     LOG(ERROR) << "Invalid value found in Firefox pref file '"
    136                << pref_file.value() << "' value is '" << value << "'.";
    137   }
    138   return true;
    139 }
    140 
    141 }  // namespace
    142 
    143 FirefoxProxySettings::FirefoxProxySettings() {
    144   Reset();
    145 }
    146 
    147 FirefoxProxySettings::~FirefoxProxySettings() {
    148 }
    149 
    150 void FirefoxProxySettings::Reset() {
    151   config_type_ = NO_PROXY;
    152   http_proxy_.clear();
    153   http_proxy_port_ = 0;
    154   ssl_proxy_.clear();
    155   ssl_proxy_port_ = 0;
    156   ftp_proxy_.clear();
    157   ftp_proxy_port_ = 0;
    158   gopher_proxy_.clear();
    159   gopher_proxy_port_ = 0;
    160   socks_host_.clear();
    161   socks_port_ = 0;
    162   socks_version_ = UNKNONW;
    163   proxy_bypass_list_.clear();
    164   autoconfig_url_.clear();
    165 }
    166 
    167 // static
    168 bool FirefoxProxySettings::GetSettings(FirefoxProxySettings* settings) {
    169   DCHECK(settings);
    170   settings->Reset();
    171 
    172   base::FilePath profile_path = GetFirefoxProfilePath();
    173   if (profile_path.empty())
    174     return false;
    175   base::FilePath pref_file = profile_path.AppendASCII(kPrefFileName);
    176   return GetSettingsFromFile(pref_file, settings);
    177 }
    178 
    179 bool FirefoxProxySettings::ToProxyConfig(net::ProxyConfig* config) {
    180   switch (config_type()) {
    181     case NO_PROXY:
    182       *config = net::ProxyConfig::CreateDirect();
    183       return true;
    184     case AUTO_DETECT:
    185       *config = net::ProxyConfig::CreateAutoDetect();
    186       return true;
    187     case AUTO_FROM_URL:
    188       *config = net::ProxyConfig::CreateFromCustomPacURL(
    189           GURL(autoconfig_url()));
    190       return true;
    191     case SYSTEM:
    192       // Can't convert this directly to a ProxyConfig.
    193       return false;
    194     case MANUAL:
    195       // Handled outside of the switch (since it is a lot of code.)
    196       break;
    197     default:
    198       NOTREACHED();
    199       return false;
    200   }
    201 
    202   // The rest of this funciton is for handling the MANUAL case.
    203   DCHECK_EQ(MANUAL, config_type());
    204 
    205   *config = net::ProxyConfig();
    206   config->proxy_rules().type =
    207       net::ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
    208 
    209   if (!http_proxy().empty()) {
    210     config->proxy_rules().proxies_for_http.SetSingleProxyServer(
    211         net::ProxyServer(
    212             net::ProxyServer::SCHEME_HTTP,
    213             net::HostPortPair(http_proxy(), http_proxy_port())));
    214   }
    215 
    216   if (!ftp_proxy().empty()) {
    217     config->proxy_rules().proxies_for_ftp.SetSingleProxyServer(
    218         net::ProxyServer(
    219             net::ProxyServer::SCHEME_HTTP,
    220             net::HostPortPair(ftp_proxy(), ftp_proxy_port())));
    221   }
    222 
    223   if (!ssl_proxy().empty()) {
    224     config->proxy_rules().proxies_for_https.SetSingleProxyServer(
    225         net::ProxyServer(
    226             net::ProxyServer::SCHEME_HTTP,
    227             net::HostPortPair(ssl_proxy(), ssl_proxy_port())));
    228   }
    229 
    230   if (!socks_host().empty()) {
    231     net::ProxyServer::Scheme proxy_scheme = V5 == socks_version() ?
    232         net::ProxyServer::SCHEME_SOCKS5 : net::ProxyServer::SCHEME_SOCKS4;
    233 
    234     config->proxy_rules().fallback_proxies.SetSingleProxyServer(
    235         net::ProxyServer(
    236             proxy_scheme,
    237             net::HostPortPair(socks_host(), socks_port())));
    238   }
    239 
    240   config->proxy_rules().bypass_rules.ParseFromStringUsingSuffixMatching(
    241       JoinString(proxy_bypass_list_, ';'));
    242 
    243   return true;
    244 }
    245 
    246 // static
    247 bool FirefoxProxySettings::GetSettingsFromFile(const base::FilePath& pref_file,
    248                                                FirefoxProxySettings* settings) {
    249   base::DictionaryValue dictionary;
    250   if (!ParsePrefFile(pref_file, &dictionary))
    251     return false;
    252 
    253   int proxy_type = 0;
    254   if (!dictionary.GetInteger(kNetworkProxyTypeKey, &proxy_type))
    255     return true;  // No type means no proxy.
    256 
    257   settings->config_type_ = IntToProxyConfig(proxy_type);
    258   if (settings->config_type_ == AUTO_FROM_URL) {
    259     if (!dictionary.GetStringASCII(kAutoconfigURL,
    260                                    &(settings->autoconfig_url_))) {
    261       LOG(ERROR) << "Failed to retrieve Firefox proxy autoconfig URL";
    262     }
    263     return true;
    264   }
    265 
    266   if (settings->config_type_ == MANUAL) {
    267     if (!dictionary.GetStringASCII(kHTTPProxyKey, &(settings->http_proxy_)))
    268       LOG(ERROR) << "Failed to retrieve Firefox proxy HTTP host";
    269     if (!dictionary.GetInteger(kHTTPProxyPortKey,
    270                                &(settings->http_proxy_port_))) {
    271       LOG(ERROR) << "Failed to retrieve Firefox proxy HTTP port";
    272     }
    273     if (!dictionary.GetStringASCII(kSSLProxyKey, &(settings->ssl_proxy_)))
    274       LOG(ERROR) << "Failed to retrieve Firefox proxy SSL host";
    275     if (!dictionary.GetInteger(kSSLProxyPortKey, &(settings->ssl_proxy_port_)))
    276       LOG(ERROR) << "Failed to retrieve Firefox proxy SSL port";
    277     if (!dictionary.GetStringASCII(kFTPProxyKey, &(settings->ftp_proxy_)))
    278       LOG(ERROR) << "Failed to retrieve Firefox proxy FTP host";
    279     if (!dictionary.GetInteger(kFTPProxyPortKey, &(settings->ftp_proxy_port_)))
    280       LOG(ERROR) << "Failed to retrieve Firefox proxy SSL port";
    281     if (!dictionary.GetStringASCII(kGopherProxyKey, &(settings->gopher_proxy_)))
    282       LOG(ERROR) << "Failed to retrieve Firefox proxy gopher host";
    283     if (!dictionary.GetInteger(kGopherProxyPortKey,
    284                                &(settings->gopher_proxy_port_))) {
    285       LOG(ERROR) << "Failed to retrieve Firefox proxy gopher port";
    286     }
    287     if (!dictionary.GetStringASCII(kSOCKSHostKey, &(settings->socks_host_)))
    288       LOG(ERROR) << "Failed to retrieve Firefox SOCKS host";
    289     if (!dictionary.GetInteger(kSOCKSHostPortKey, &(settings->socks_port_)))
    290       LOG(ERROR) << "Failed to retrieve Firefox SOCKS port";
    291     int socks_version;
    292     if (dictionary.GetInteger(kSOCKSVersionKey, &socks_version))
    293       settings->socks_version_ = IntToSOCKSVersion(socks_version);
    294 
    295     std::string proxy_bypass;
    296     if (dictionary.GetStringASCII(kNoProxyListKey, &proxy_bypass) &&
    297         !proxy_bypass.empty()) {
    298       base::StringTokenizer string_tok(proxy_bypass, ",");
    299       while (string_tok.GetNext()) {
    300         std::string token = string_tok.token();
    301         base::TrimWhitespaceASCII(token, base::TRIM_ALL, &token);
    302         if (!token.empty())
    303           settings->proxy_bypass_list_.push_back(token);
    304       }
    305     }
    306   }
    307   return true;
    308 }
    309