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