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 (!base::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