Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / proxy / proxy_api_helpers.cc
1 // Copyright (c) 2012 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/api/proxy/proxy_api_helpers.h"
15
16 #include "base/base64.h"
17 #include "base/basictypes.h"
18 #include "base/strings/string_tokenizer.h"
19 #include "base/strings/string_util.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "base/values.h"
22 #include "chrome/browser/extensions/api/proxy/proxy_api_constants.h"
23 #include "chrome/browser/prefs/proxy_config_dictionary.h"
24 #include "extensions/common/error_utils.h"
25 #include "net/base/data_url.h"
26 #include "net/proxy/proxy_config.h"
27
28 namespace extensions {
29
30 namespace keys = proxy_api_constants;
31
32 namespace proxy_api_helpers {
33
34 bool CreateDataURLFromPACScript(const std::string& pac_script,
35                                 std::string* pac_script_url_base64_encoded) {
36   // Encode pac_script in base64.
37   std::string pac_script_base64_encoded;
38   base::Base64Encode(pac_script, &pac_script_base64_encoded);
39
40   // Make it a correct data url.
41   *pac_script_url_base64_encoded =
42       std::string(keys::kPACDataUrlPrefix) + pac_script_base64_encoded;
43   return true;
44 }
45
46 bool CreatePACScriptFromDataURL(
47     const std::string& pac_script_url_base64_encoded,
48     std::string* pac_script) {
49   GURL url(pac_script_url_base64_encoded);
50   if (!url.is_valid())
51     return false;
52
53   std::string mime_type;
54   std::string charset;
55   return net::DataURL::Parse(url, &mime_type, &charset, pac_script);
56 }
57
58 // Extension Pref -> Browser Pref conversion.
59
60 bool GetProxyModeFromExtensionPref(const base::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 base::DictionaryValue* proxy_config,
78                                       bool* out,
79                                       std::string* error,
80                                       bool* bad_message){
81   const base::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 base::DictionaryValue* proxy_config,
99                                 std::string* out,
100                                 std::string* error,
101                                 bool* bad_message) {
102   const base::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   base::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 base::DictionaryValue* proxy_config,
125                                  std::string* out,
126                                  std::string* error,
127                                  bool* bad_message) {
128   const base::DictionaryValue* pac_dict = NULL;
129   proxy_config->GetDictionary(keys::kProxyConfigPacScript, &pac_dict);
130   if (!pac_dict)
131     return true;
132
133   base::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 base::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   base::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 = ErrorUtils::FormatErrorMessage(
174         "Invalid 'rules.???.host' entry '*'. 'host' field supports only ASCII "
175         "URLs (encode URLs in Punycode format).",
176         base::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(
191     const base::DictionaryValue* proxy_config,
192     std::string* out,
193     std::string* error,
194     bool* bad_message) {
195   const base::DictionaryValue* proxy_rules = NULL;
196   proxy_config->GetDictionary(keys::kProxyConfigRules, &proxy_rules);
197   if (!proxy_rules)
198     return true;
199
200   // Local data into which the parameters will be parsed. has_proxy describes
201   // whether a setting was found for the scheme; proxy_server holds the
202   // respective ProxyServer objects containing those descriptions.
203   bool has_proxy[keys::SCHEME_MAX + 1];
204   net::ProxyServer proxy_server[keys::SCHEME_MAX + 1];
205
206   // Looking for all possible proxy types is inefficient if we have a
207   // singleProxy that will supersede per-URL proxies, but it's worth it to keep
208   // the code simple and extensible.
209   for (size_t i = 0; i <= keys::SCHEME_MAX; ++i) {
210     const base::DictionaryValue* proxy_dict = NULL;
211     has_proxy[i] = proxy_rules->GetDictionary(keys::field_name[i],
212                                               &proxy_dict);
213     if (has_proxy[i]) {
214       net::ProxyServer::Scheme default_scheme = net::ProxyServer::SCHEME_HTTP;
215       if (!GetProxyServer(proxy_dict, default_scheme,
216                           &proxy_server[i], error, bad_message)) {
217         // Don't set |error| here, as GetProxyServer takes care of that.
218         return false;
219       }
220     }
221   }
222
223   COMPILE_ASSERT(keys::SCHEME_ALL == 0, singleProxy_must_be_first_option);
224
225   // Handle case that only singleProxy is specified.
226   if (has_proxy[keys::SCHEME_ALL]) {
227     for (size_t i = 1; i <= keys::SCHEME_MAX; ++i) {
228       if (has_proxy[i]) {
229         *error = ErrorUtils::FormatErrorMessage(
230             "Proxy rule for * and * cannot be set at the same time.",
231             keys::field_name[keys::SCHEME_ALL], keys::field_name[i]);
232         return false;
233       }
234     }
235     *out = proxy_server[keys::SCHEME_ALL].ToURI();
236     return true;
237   }
238
239   // Handle case that anything but singleProxy is specified.
240
241   // Build the proxy preference string.
242   std::string proxy_pref;
243   for (size_t i = 1; i <= keys::SCHEME_MAX; ++i) {
244     if (has_proxy[i]) {
245       // http=foopy:4010;ftp=socks5://foopy2:80
246       if (!proxy_pref.empty())
247         proxy_pref.append(";");
248       proxy_pref.append(keys::scheme_name[i]);
249       proxy_pref.append("=");
250       proxy_pref.append(proxy_server[i].ToURI());
251     }
252   }
253
254   *out = proxy_pref;
255   return true;
256 }
257
258 bool JoinUrlList(const base::ListValue* list,
259                  const std::string& joiner,
260                  std::string* out,
261                  std::string* error,
262                  bool* bad_message) {
263   std::string result;
264   for (size_t i = 0; i < list->GetSize(); ++i) {
265     if (!result.empty())
266       result.append(joiner);
267
268     // TODO(battre): handle UTF-8 (http://crbug.com/72692).
269     base::string16 entry;
270     if (!list->GetString(i, &entry)) {
271       LOG(ERROR) << "'rules.bypassList' could not be parsed.";
272       *bad_message = true;
273       return false;
274     }
275     if (!IsStringASCII(entry)) {
276       *error = "'rules.bypassList' supports only ASCII URLs "
277                "(encode URLs in Punycode format).";
278       return false;
279     }
280     result.append(UTF16ToASCII(entry));
281   }
282   *out = result;
283   return true;
284 }
285
286 bool GetBypassListFromExtensionPref(const base::DictionaryValue* proxy_config,
287                                     std::string* out,
288                                     std::string* error,
289                                     bool* bad_message) {
290   const base::DictionaryValue* proxy_rules = NULL;
291   proxy_config->GetDictionary(keys::kProxyConfigRules, &proxy_rules);
292   if (!proxy_rules)
293     return true;
294
295   if (!proxy_rules->HasKey(keys::kProxyConfigBypassList)) {
296     *out = "";
297     return true;
298   }
299   const base::ListValue* bypass_list = NULL;
300   if (!proxy_rules->GetList(keys::kProxyConfigBypassList, &bypass_list)) {
301     LOG(ERROR) << "'rules.bypassList' could not be parsed.";
302     *bad_message = true;
303     return false;
304   }
305
306   return JoinUrlList(bypass_list, ",", out, error, bad_message);
307 }
308
309 base::DictionaryValue* CreateProxyConfigDict(
310     ProxyPrefs::ProxyMode mode_enum,
311     bool pac_mandatory,
312     const std::string& pac_url,
313     const std::string& pac_data,
314     const std::string& proxy_rules_string,
315     const std::string& bypass_list,
316     std::string* error) {
317   base::DictionaryValue* result_proxy_config = NULL;
318   switch (mode_enum) {
319     case ProxyPrefs::MODE_DIRECT:
320       result_proxy_config = ProxyConfigDictionary::CreateDirect();
321       break;
322     case ProxyPrefs::MODE_AUTO_DETECT:
323       result_proxy_config = ProxyConfigDictionary::CreateAutoDetect();
324       break;
325     case ProxyPrefs::MODE_PAC_SCRIPT: {
326       std::string url;
327       if (!pac_url.empty()) {
328         url = pac_url;
329       } else if (!pac_data.empty()) {
330         if (!CreateDataURLFromPACScript(pac_data, &url)) {
331           *error = "Internal error, at base64 encoding of 'pacScript.data'.";
332           return NULL;
333         }
334       } else {
335         *error = "Proxy mode 'pac_script' requires a 'pacScript' field with "
336                  "either a 'url' field or a 'data' field.";
337         return NULL;
338       }
339       result_proxy_config =
340           ProxyConfigDictionary::CreatePacScript(url, pac_mandatory);
341       break;
342     }
343     case ProxyPrefs::MODE_FIXED_SERVERS: {
344       if (proxy_rules_string.empty()) {
345         *error = "Proxy mode 'fixed_servers' requires a 'rules' field.";
346         return NULL;
347       }
348       result_proxy_config = ProxyConfigDictionary::CreateFixedServers(
349           proxy_rules_string, bypass_list);
350       break;
351     }
352     case ProxyPrefs::MODE_SYSTEM:
353       result_proxy_config = ProxyConfigDictionary::CreateSystem();
354       break;
355     case ProxyPrefs::kModeCount:
356       NOTREACHED();
357   }
358   return result_proxy_config;
359 }
360
361 base::DictionaryValue* CreateProxyRulesDict(
362     const ProxyConfigDictionary& proxy_config) {
363   ProxyPrefs::ProxyMode mode;
364   CHECK(proxy_config.GetMode(&mode) && mode == ProxyPrefs::MODE_FIXED_SERVERS);
365
366   scoped_ptr<base::DictionaryValue> extension_proxy_rules(
367       new base::DictionaryValue);
368
369   std::string proxy_servers;
370   if (!proxy_config.GetProxyServer(&proxy_servers)) {
371     LOG(ERROR) << "Missing proxy servers in configuration.";
372     return NULL;
373   }
374
375   net::ProxyConfig::ProxyRules rules;
376   rules.ParseFromString(proxy_servers);
377
378   switch (rules.type) {
379     case net::ProxyConfig::ProxyRules::TYPE_NO_RULES:
380       return NULL;
381     case net::ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY:
382       if (!rules.single_proxies.IsEmpty()) {
383         extension_proxy_rules->Set(
384             keys::field_name[keys::SCHEME_ALL],
385             CreateProxyServerDict(rules.single_proxies.Get()));
386       }
387       break;
388     case net::ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME:
389       if (!rules.proxies_for_http.IsEmpty()) {
390         extension_proxy_rules->Set(
391             keys::field_name[keys::SCHEME_HTTP],
392             CreateProxyServerDict(rules.proxies_for_http.Get()));
393       }
394       if (!rules.proxies_for_https.IsEmpty()) {
395         extension_proxy_rules->Set(
396             keys::field_name[keys::SCHEME_HTTPS],
397             CreateProxyServerDict(rules.proxies_for_https.Get()));
398       }
399       if (!rules.proxies_for_ftp.IsEmpty()) {
400         extension_proxy_rules->Set(
401             keys::field_name[keys::SCHEME_FTP],
402             CreateProxyServerDict(rules.proxies_for_ftp.Get()));
403       }
404       if (!rules.fallback_proxies.IsEmpty()) {
405         extension_proxy_rules->Set(
406             keys::field_name[keys::SCHEME_FALLBACK],
407             CreateProxyServerDict(rules.fallback_proxies.Get()));
408       }
409       break;
410   }
411
412   // If we add a new scheme some time, we need to also store a new dictionary
413   // representing this scheme in the code above.
414   COMPILE_ASSERT(keys::SCHEME_MAX == 4, SCHEME_FORGOTTEN);
415
416   if (proxy_config.HasBypassList()) {
417     std::string bypass_list_string;
418     if (!proxy_config.GetBypassList(&bypass_list_string)) {
419       LOG(ERROR) << "Invalid bypassList in configuration.";
420       return NULL;
421     }
422     base::ListValue* bypass_list =
423         TokenizeToStringList(bypass_list_string, ",;");
424     extension_proxy_rules->Set(keys::kProxyConfigBypassList, bypass_list);
425   }
426
427   return extension_proxy_rules.release();
428 }
429
430 base::DictionaryValue* CreateProxyServerDict(const net::ProxyServer& proxy) {
431   scoped_ptr<base::DictionaryValue> out(new base::DictionaryValue);
432   switch (proxy.scheme()) {
433     case net::ProxyServer::SCHEME_HTTP:
434       out->SetString(keys::kProxyConfigRuleScheme, "http");
435       break;
436     case net::ProxyServer::SCHEME_HTTPS:
437       out->SetString(keys::kProxyConfigRuleScheme, "https");
438       break;
439     case net::ProxyServer::SCHEME_QUIC:
440       out->SetString(keys::kProxyConfigRuleScheme, "quic");
441       break;
442     case net::ProxyServer::SCHEME_SOCKS4:
443       out->SetString(keys::kProxyConfigRuleScheme, "socks4");
444       break;
445     case net::ProxyServer::SCHEME_SOCKS5:
446       out->SetString(keys::kProxyConfigRuleScheme, "socks5");
447       break;
448     case net::ProxyServer::SCHEME_DIRECT:
449     case net::ProxyServer::SCHEME_INVALID:
450       NOTREACHED();
451       return NULL;
452   }
453   out->SetString(keys::kProxyConfigRuleHost, proxy.host_port_pair().host());
454   out->SetInteger(keys::kProxyConfigRulePort, proxy.host_port_pair().port());
455   return out.release();
456 }
457
458 base::DictionaryValue* CreatePacScriptDict(
459     const ProxyConfigDictionary& proxy_config) {
460   ProxyPrefs::ProxyMode mode;
461   CHECK(proxy_config.GetMode(&mode) && mode == ProxyPrefs::MODE_PAC_SCRIPT);
462
463   scoped_ptr<base::DictionaryValue> pac_script_dict(new base::DictionaryValue);
464   std::string pac_url;
465   if (!proxy_config.GetPacUrl(&pac_url)) {
466     LOG(ERROR) << "Invalid proxy configuration. Missing PAC URL.";
467     return NULL;
468   }
469   bool pac_mandatory = false;
470   if (!proxy_config.GetPacMandatory(&pac_mandatory)) {
471     LOG(ERROR) << "Invalid proxy configuration. Missing PAC mandatory field.";
472     return NULL;
473   }
474
475   if (pac_url.find("data") == 0) {
476     std::string pac_data;
477     if (!CreatePACScriptFromDataURL(pac_url, &pac_data)) {
478       LOG(ERROR) << "Cannot decode base64-encoded PAC data URL: " << pac_url;
479       return NULL;
480     }
481     pac_script_dict->SetString(keys::kProxyConfigPacScriptData, pac_data);
482   } else {
483     pac_script_dict->SetString(keys::kProxyConfigPacScriptUrl, pac_url);
484   }
485   pac_script_dict->SetBoolean(keys::kProxyConfigPacScriptMandatory,
486                               pac_mandatory);
487   return pac_script_dict.release();
488 }
489
490 base::ListValue* TokenizeToStringList(const std::string& in,
491                                 const std::string& delims) {
492   base::ListValue* out = new base::ListValue;
493   base::StringTokenizer entries(in, delims);
494   while (entries.GetNext())
495     out->Append(new base::StringValue(entries.token()));
496   return out;
497 }
498
499 }  // namespace proxy_api_helpers
500 }  // namespace extensions