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