OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 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 // Implementation of helper functions for the Chrome Extensions Proxy Settings | |
6 // API. | |
7 // | |
8 // Throughout this code, we report errors to the user by setting an |error| | |
9 // parameter, if and only if these errors can be cause by invalid input | |
10 // from the extension and we cannot expect that the extensions API has | |
11 // caught this error before. In all other cases we are dealing with internal | |
12 // errors and log to LOG(ERROR). | |
13 | |
14 #include "chrome/browser/extensions/extension_proxy_api_helpers.h" | |
15 | |
16 #include "base/base64.h" | |
17 #include "base/basictypes.h" | |
18 #include "base/string_tokenizer.h" | |
19 #include "base/string_util.h" | |
20 #include "base/utf_string_conversions.h" | |
21 #include "base/values.h" | |
22 #include "chrome/browser/extensions/extension_proxy_api_constants.h" | |
23 #include "chrome/browser/prefs/proxy_config_dictionary.h" | |
24 #include "chrome/common/extensions/extension_error_utils.h" | |
25 #include "net/proxy/proxy_config.h" | |
26 | |
27 namespace keys = extension_proxy_api_constants; | |
28 | |
29 namespace extension_proxy_api_helpers { | |
30 | |
31 bool CreateDataURLFromPACScript(const std::string& pac_script, | |
32 std::string* pac_script_url_base64_encoded) { | |
33 // Encode pac_script in base64. | |
34 std::string pac_script_base64_encoded; | |
35 if (!base::Base64Encode(pac_script, &pac_script_base64_encoded)) | |
36 return false; | |
37 | |
38 // Make it a correct data url. | |
39 *pac_script_url_base64_encoded = | |
40 std::string(keys::kPACDataUrlPrefix) + pac_script_base64_encoded; | |
41 return true; | |
42 } | |
43 | |
44 bool CreatePACScriptFromDataURL( | |
45 const std::string& pac_script_url_base64_encoded, | |
46 std::string* pac_script) { | |
47 if (pac_script_url_base64_encoded.find(keys::kPACDataUrlPrefix) != 0) | |
48 return false; | |
49 | |
50 // Strip constant data-url prefix. | |
51 std::string pac_script_base64_encoded = | |
52 pac_script_url_base64_encoded.substr(strlen(keys::kPACDataUrlPrefix)); | |
53 | |
54 // The rest is a base64 encoded PAC script. | |
55 return base::Base64Decode(pac_script_base64_encoded, pac_script); | |
56 } | |
57 | |
58 // Extension Pref -> Browser Pref conversion. | |
59 | |
60 bool GetProxyModeFromExtensionPref(const DictionaryValue* proxy_config, | |
61 ProxyPrefs::ProxyMode* out, | |
62 std::string* error, | |
63 bool* bad_message) { | |
64 std::string proxy_mode; | |
65 | |
66 // We can safely assume that this is ASCII due to the allowed enumeration | |
67 // values specified in the extension API JSON. | |
68 proxy_config->GetStringASCII(keys::kProxyConfigMode, &proxy_mode); | |
69 if (!ProxyPrefs::StringToProxyMode(proxy_mode, out)) { | |
70 LOG(ERROR) << "Invalid mode for proxy settings: " << proxy_mode; | |
71 *bad_message = true; | |
72 return false; | |
73 } | |
74 return true; | |
75 } | |
76 | |
77 bool GetPacMandatoryFromExtensionPref(const DictionaryValue* proxy_config, | |
78 bool* out, | |
79 std::string* error, | |
80 bool* bad_message){ | |
81 DictionaryValue* pac_dict = NULL; | |
82 proxy_config->GetDictionary(keys::kProxyConfigPacScript, &pac_dict); | |
83 if (!pac_dict) | |
84 return true; | |
85 | |
86 bool mandatory_pac = false; | |
87 if (pac_dict->HasKey(keys::kProxyConfigPacScriptMandatory) && | |
88 !pac_dict->GetBoolean(keys::kProxyConfigPacScriptMandatory, | |
89 &mandatory_pac)) { | |
90 LOG(ERROR) << "'pacScript.mandatory' could not be parsed."; | |
91 *bad_message = true; | |
92 return false; | |
93 } | |
94 *out = mandatory_pac; | |
95 return true; | |
96 } | |
97 | |
98 bool GetPacUrlFromExtensionPref(const DictionaryValue* proxy_config, | |
99 std::string* out, | |
100 std::string* error, | |
101 bool* bad_message) { | |
102 DictionaryValue* pac_dict = NULL; | |
103 proxy_config->GetDictionary(keys::kProxyConfigPacScript, &pac_dict); | |
104 if (!pac_dict) | |
105 return true; | |
106 | |
107 // TODO(battre): Handle UTF-8 URLs (http://crbug.com/72692). | |
108 string16 pac_url16; | |
109 if (pac_dict->HasKey(keys::kProxyConfigPacScriptUrl) && | |
110 !pac_dict->GetString(keys::kProxyConfigPacScriptUrl, &pac_url16)) { | |
111 LOG(ERROR) << "'pacScript.url' could not be parsed."; | |
112 *bad_message = true; | |
113 return false; | |
114 } | |
115 if (!IsStringASCII(pac_url16)) { | |
116 *error = "'pacScript.url' supports only ASCII URLs " | |
117 "(encode URLs in Punycode format)."; | |
118 return false; | |
119 } | |
120 *out = UTF16ToASCII(pac_url16); | |
121 return true; | |
122 } | |
123 | |
124 bool GetPacDataFromExtensionPref(const DictionaryValue* proxy_config, | |
125 std::string* out, | |
126 std::string* error, | |
127 bool* bad_message) { | |
128 DictionaryValue* pac_dict = NULL; | |
129 proxy_config->GetDictionary(keys::kProxyConfigPacScript, &pac_dict); | |
130 if (!pac_dict) | |
131 return true; | |
132 | |
133 string16 pac_data16; | |
134 if (pac_dict->HasKey(keys::kProxyConfigPacScriptData) && | |
135 !pac_dict->GetString(keys::kProxyConfigPacScriptData, &pac_data16)) { | |
136 LOG(ERROR) << "'pacScript.data' could not be parsed."; | |
137 *bad_message = true; | |
138 return false; | |
139 } | |
140 if (!IsStringASCII(pac_data16)) { | |
141 *error = "'pacScript.data' supports only ASCII code" | |
142 "(encode URLs in Punycode format)."; | |
143 return false; | |
144 } | |
145 *out = UTF16ToASCII(pac_data16); | |
146 return true; | |
147 } | |
148 | |
149 bool GetProxyServer(const DictionaryValue* proxy_server, | |
150 net::ProxyServer::Scheme default_scheme, | |
151 net::ProxyServer* out, | |
152 std::string* error, | |
153 bool* bad_message) { | |
154 std::string scheme_string; // optional. | |
155 | |
156 // We can safely assume that this is ASCII due to the allowed enumeration | |
157 // values specified in the extension API JSON. | |
158 proxy_server->GetStringASCII(keys::kProxyConfigRuleScheme, &scheme_string); | |
159 | |
160 net::ProxyServer::Scheme scheme = | |
161 net::ProxyServer::GetSchemeFromURI(scheme_string); | |
162 if (scheme == net::ProxyServer::SCHEME_INVALID) | |
163 scheme = default_scheme; | |
164 | |
165 // TODO(battre): handle UTF-8 in hostnames (http://crbug.com/72692). | |
166 string16 host16; | |
167 if (!proxy_server->GetString(keys::kProxyConfigRuleHost, &host16)) { | |
168 LOG(ERROR) << "Could not parse a 'rules.*.host' entry."; | |
169 *bad_message = true; | |
170 return false; | |
171 } | |
172 if (!IsStringASCII(host16)) { | |
173 *error = ExtensionErrorUtils::FormatErrorMessage( | |
174 "Invalid 'rules.???.host' entry '*'. 'host' field supports only ASCII " | |
175 "URLs (encode URLs in Punycode format).", | |
176 UTF16ToUTF8(host16)); | |
177 return false; | |
178 } | |
179 std::string host = UTF16ToASCII(host16); | |
180 | |
181 int port; // optional. | |
182 if (!proxy_server->GetInteger(keys::kProxyConfigRulePort, &port)) | |
183 port = net::ProxyServer::GetDefaultPortForScheme(scheme); | |
184 | |
185 *out = net::ProxyServer(scheme, net::HostPortPair(host, port)); | |
186 | |
187 return true; | |
188 } | |
189 | |
190 bool GetProxyRulesStringFromExtensionPref(const DictionaryValue* proxy_config, | |
191 std::string* out, | |
192 std::string* error, | |
193 bool* bad_message) { | |
194 DictionaryValue* proxy_rules = NULL; | |
195 proxy_config->GetDictionary(keys::kProxyConfigRules, &proxy_rules); | |
196 if (!proxy_rules) | |
197 return true; | |
198 | |
199 // Local data into which the parameters will be parsed. has_proxy describes | |
200 // whether a setting was found for the scheme; proxy_server holds the | |
201 // respective ProxyServer objects containing those descriptions. | |
202 bool has_proxy[keys::SCHEME_MAX + 1]; | |
203 net::ProxyServer proxy_server[keys::SCHEME_MAX + 1]; | |
204 | |
205 // Looking for all possible proxy types is inefficient if we have a | |
206 // singleProxy that will supersede per-URL proxies, but it's worth it to keep | |
207 // the code simple and extensible. | |
208 for (size_t i = 0; i <= keys::SCHEME_MAX; ++i) { | |
209 DictionaryValue* proxy_dict = NULL; | |
210 has_proxy[i] = proxy_rules->GetDictionary(keys::field_name[i], | |
211 &proxy_dict); | |
212 if (has_proxy[i]) { | |
213 net::ProxyServer::Scheme default_scheme = net::ProxyServer::SCHEME_HTTP; | |
214 if (!GetProxyServer(proxy_dict, default_scheme, | |
215 &proxy_server[i], error, bad_message)) { | |
216 // Don't set |error| here, as GetProxyServer takes care of that. | |
217 return false; | |
218 } | |
219 } | |
220 } | |
221 | |
222 COMPILE_ASSERT(keys::SCHEME_ALL == 0, singleProxy_must_be_first_option); | |
223 | |
224 // Handle case that only singleProxy is specified. | |
225 if (has_proxy[keys::SCHEME_ALL]) { | |
226 for (size_t i = 1; i <= keys::SCHEME_MAX; ++i) { | |
227 if (has_proxy[i]) { | |
228 *error = ExtensionErrorUtils::FormatErrorMessage( | |
229 "Proxy rule for * and * cannot be set at the same time.", | |
230 keys::field_name[keys::SCHEME_ALL], keys::field_name[i]); | |
231 return false; | |
232 } | |
233 } | |
234 *out = proxy_server[keys::SCHEME_ALL].ToURI(); | |
235 return true; | |
236 } | |
237 | |
238 // Handle case that anything but singleProxy is specified. | |
239 | |
240 // Build the proxy preference string. | |
241 std::string proxy_pref; | |
242 for (size_t i = 1; i <= keys::SCHEME_MAX; ++i) { | |
243 if (has_proxy[i]) { | |
244 // http=foopy:4010;ftp=socks5://foopy2:80 | |
245 if (!proxy_pref.empty()) | |
246 proxy_pref.append(";"); | |
247 proxy_pref.append(keys::scheme_name[i]); | |
248 proxy_pref.append("="); | |
249 proxy_pref.append(proxy_server[i].ToURI()); | |
250 } | |
251 } | |
252 | |
253 *out = proxy_pref; | |
254 return true; | |
255 } | |
256 | |
257 bool JoinUrlList(ListValue* list, | |
258 const std::string& joiner, | |
259 std::string* out, | |
260 std::string* error, | |
261 bool* bad_message) { | |
262 std::string result; | |
263 for (size_t i = 0; i < list->GetSize(); ++i) { | |
264 if (!result.empty()) | |
265 result.append(joiner); | |
266 | |
267 // TODO(battre): handle UTF-8 (http://crbug.com/72692). | |
268 string16 entry; | |
269 if (!list->GetString(i, &entry)) { | |
270 LOG(ERROR) << "'rules.bypassList' could not be parsed."; | |
271 *bad_message = true; | |
272 return false; | |
273 } | |
274 if (!IsStringASCII(entry)) { | |
275 *error = "'rules.bypassList' supports only ASCII URLs " | |
276 "(encode URLs in Punycode format)."; | |
277 return false; | |
278 } | |
279 result.append(UTF16ToASCII(entry)); | |
280 } | |
281 *out = result; | |
282 return true; | |
283 } | |
284 | |
285 bool GetBypassListFromExtensionPref(const DictionaryValue* proxy_config, | |
286 std::string *out, | |
287 std::string* error, | |
288 bool* bad_message) { | |
289 DictionaryValue* proxy_rules = NULL; | |
290 proxy_config->GetDictionary(keys::kProxyConfigRules, &proxy_rules); | |
291 if (!proxy_rules) | |
292 return true; | |
293 | |
294 if (!proxy_rules->HasKey(keys::kProxyConfigBypassList)) { | |
295 *out = ""; | |
296 return true; | |
297 } | |
298 ListValue* bypass_list = NULL; | |
299 if (!proxy_rules->GetList(keys::kProxyConfigBypassList, &bypass_list)) { | |
300 LOG(ERROR) << "'rules.bypassList' could not be parsed."; | |
301 *bad_message = true; | |
302 return false; | |
303 } | |
304 | |
305 return JoinUrlList(bypass_list, ",", out, error, bad_message); | |
306 } | |
307 | |
308 DictionaryValue* CreateProxyConfigDict(ProxyPrefs::ProxyMode mode_enum, | |
309 bool pac_mandatory, | |
310 const std::string& pac_url, | |
311 const std::string& pac_data, | |
312 const std::string& proxy_rules_string, | |
313 const std::string& bypass_list, | |
314 std::string* error) { | |
315 DictionaryValue* result_proxy_config = NULL; | |
316 switch (mode_enum) { | |
317 case ProxyPrefs::MODE_DIRECT: | |
318 result_proxy_config = ProxyConfigDictionary::CreateDirect(); | |
319 break; | |
320 case ProxyPrefs::MODE_AUTO_DETECT: | |
321 result_proxy_config = ProxyConfigDictionary::CreateAutoDetect(); | |
322 break; | |
323 case ProxyPrefs::MODE_PAC_SCRIPT: { | |
324 std::string url; | |
325 if (!pac_url.empty()) { | |
326 url = pac_url; | |
327 } else if (!pac_data.empty()) { | |
328 if (!CreateDataURLFromPACScript(pac_data, &url)) { | |
329 *error = "Internal error, at base64 encoding of 'pacScript.data'."; | |
330 return NULL; | |
331 } | |
332 } else { | |
333 *error = "Proxy mode 'pac_script' requires a 'pacScript' field with " | |
334 "either a 'url' field or a 'data' field."; | |
335 return NULL; | |
336 } | |
337 result_proxy_config = | |
338 ProxyConfigDictionary::CreatePacScript(url, pac_mandatory); | |
339 break; | |
340 } | |
341 case ProxyPrefs::MODE_FIXED_SERVERS: { | |
342 if (proxy_rules_string.empty()) { | |
343 *error = "Proxy mode 'fixed_servers' requires a 'rules' field."; | |
344 return NULL; | |
345 } | |
346 result_proxy_config = ProxyConfigDictionary::CreateFixedServers( | |
347 proxy_rules_string, bypass_list); | |
348 break; | |
349 } | |
350 case ProxyPrefs::MODE_SYSTEM: | |
351 result_proxy_config = ProxyConfigDictionary::CreateSystem(); | |
352 break; | |
353 case ProxyPrefs::kModeCount: | |
354 NOTREACHED(); | |
355 } | |
356 return result_proxy_config; | |
357 } | |
358 | |
359 DictionaryValue* CreateProxyRulesDict( | |
360 const ProxyConfigDictionary& proxy_config) { | |
361 ProxyPrefs::ProxyMode mode; | |
362 CHECK(proxy_config.GetMode(&mode) && mode == ProxyPrefs::MODE_FIXED_SERVERS); | |
363 | |
364 scoped_ptr<DictionaryValue> extension_proxy_rules(new DictionaryValue); | |
365 | |
366 std::string proxy_servers; | |
367 if (!proxy_config.GetProxyServer(&proxy_servers)) { | |
368 LOG(ERROR) << "Missing proxy servers in configuration."; | |
369 return NULL; | |
370 } | |
371 | |
372 net::ProxyConfig::ProxyRules rules; | |
373 rules.ParseFromString(proxy_servers); | |
374 | |
375 switch (rules.type) { | |
376 case net::ProxyConfig::ProxyRules::TYPE_NO_RULES: | |
377 return NULL; | |
378 case net::ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY: | |
379 if (rules.single_proxy.is_valid()) { | |
380 extension_proxy_rules->Set(keys::field_name[keys::SCHEME_ALL], | |
381 CreateProxyServerDict(rules.single_proxy)); | |
382 } | |
383 break; | |
384 case net::ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME: | |
385 if (rules.proxy_for_http.is_valid()) { | |
386 extension_proxy_rules->Set(keys::field_name[keys::SCHEME_HTTP], | |
387 CreateProxyServerDict(rules.proxy_for_http)); | |
388 } | |
389 if (rules.proxy_for_https.is_valid()) { | |
390 extension_proxy_rules->Set( | |
391 keys::field_name[keys::SCHEME_HTTPS], | |
392 CreateProxyServerDict(rules.proxy_for_https)); | |
393 } | |
394 if (rules.proxy_for_ftp.is_valid()) { | |
395 extension_proxy_rules->Set(keys::field_name[keys::SCHEME_FTP], | |
396 CreateProxyServerDict(rules.proxy_for_ftp)); | |
397 } | |
398 if (rules.fallback_proxy.is_valid()) { | |
399 extension_proxy_rules->Set(keys::field_name[keys::SCHEME_FALLBACK], | |
400 CreateProxyServerDict(rules.fallback_proxy)); | |
401 } | |
402 break; | |
403 } | |
404 | |
405 // If we add a new scheme some time, we need to also store a new dictionary | |
406 // representing this scheme in the code above. | |
407 COMPILE_ASSERT(keys::SCHEME_MAX == 4, SCHEME_FORGOTTEN); | |
408 | |
409 if (proxy_config.HasBypassList()) { | |
410 std::string bypass_list_string; | |
411 if (!proxy_config.GetBypassList(&bypass_list_string)) { | |
412 LOG(ERROR) << "Invalid bypassList in configuration."; | |
413 return NULL; | |
414 } | |
415 ListValue* bypass_list = TokenizeToStringList(bypass_list_string, ",;"); | |
416 extension_proxy_rules->Set(keys::kProxyConfigBypassList, bypass_list); | |
417 } | |
418 | |
419 return extension_proxy_rules.release(); | |
420 } | |
421 | |
422 DictionaryValue* CreateProxyServerDict(const net::ProxyServer& proxy) { | |
423 scoped_ptr<DictionaryValue> out(new DictionaryValue); | |
424 switch (proxy.scheme()) { | |
425 case net::ProxyServer::SCHEME_HTTP: | |
426 out->SetString(keys::kProxyConfigRuleScheme, "http"); | |
427 break; | |
428 case net::ProxyServer::SCHEME_HTTPS: | |
429 out->SetString(keys::kProxyConfigRuleScheme, "https"); | |
430 break; | |
431 case net::ProxyServer::SCHEME_SOCKS4: | |
432 out->SetString(keys::kProxyConfigRuleScheme, "socks4"); | |
433 break; | |
434 case net::ProxyServer::SCHEME_SOCKS5: | |
435 out->SetString(keys::kProxyConfigRuleScheme, "socks5"); | |
436 break; | |
437 case net::ProxyServer::SCHEME_DIRECT: | |
438 case net::ProxyServer::SCHEME_INVALID: | |
439 NOTREACHED(); | |
440 return NULL; | |
441 } | |
442 out->SetString(keys::kProxyConfigRuleHost, proxy.host_port_pair().host()); | |
443 out->SetInteger(keys::kProxyConfigRulePort, proxy.host_port_pair().port()); | |
444 return out.release(); | |
445 } | |
446 | |
447 DictionaryValue* CreatePacScriptDict( | |
448 const ProxyConfigDictionary& proxy_config) { | |
449 ProxyPrefs::ProxyMode mode; | |
450 CHECK(proxy_config.GetMode(&mode) && mode == ProxyPrefs::MODE_PAC_SCRIPT); | |
451 | |
452 scoped_ptr<DictionaryValue> pac_script_dict(new DictionaryValue); | |
453 std::string pac_url; | |
454 if (!proxy_config.GetPacUrl(&pac_url)) { | |
455 LOG(ERROR) << "Invalid proxy configuration. Missing PAC URL."; | |
456 return NULL; | |
457 } | |
458 bool pac_mandatory = false; | |
459 if (!proxy_config.GetPacMandatory(&pac_mandatory)) { | |
460 LOG(ERROR) << "Invalid proxy configuration. Missing PAC mandatory field."; | |
461 return NULL; | |
462 } | |
463 | |
464 if (pac_url.find("data") == 0) { | |
465 std::string pac_data; | |
466 if (!CreatePACScriptFromDataURL(pac_url, &pac_data)) { | |
467 LOG(ERROR) << "Cannot decode base64-encoded PAC data URL."; | |
468 return NULL; | |
469 } | |
470 pac_script_dict->SetString(keys::kProxyConfigPacScriptData, pac_data); | |
471 } else { | |
472 pac_script_dict->SetString(keys::kProxyConfigPacScriptUrl, pac_url); | |
473 } | |
474 pac_script_dict->SetBoolean(keys::kProxyConfigPacScriptMandatory, | |
475 pac_mandatory); | |
476 return pac_script_dict.release(); | |
477 } | |
478 | |
479 ListValue* TokenizeToStringList(const std::string& in, | |
480 const std::string& delims) { | |
481 ListValue* out = new ListValue; | |
482 StringTokenizer entries(in, delims); | |
483 while (entries.GetNext()) | |
484 out->Append(Value::CreateStringValue(entries.token())); | |
485 return out; | |
486 } | |
487 | |
488 } // namespace extension_proxy_api_helpers | |
OLD | NEW |