blob: de41b14f5bffa6a5a38d8716835f0e1457058af8 [file] [log] [blame]
[email protected]d2e6d592012-02-03 21:49:041// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]861c6c62009-04-20 16:50:562// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/proxy/proxy_config_service_linux.h"
6
[email protected]d7395e732009-08-28 23:13:437#include <errno.h>
8#include <fcntl.h>
[email protected]6de53d42010-11-09 07:33:199#if defined(USE_GCONF)
[email protected]861c6c62009-04-20 16:50:5610#include <gconf/gconf-client.h>
[email protected]8c20e3d2011-05-19 21:03:5711#endif // defined(USE_GCONF)
[email protected]d7395e732009-08-28 23:13:4312#include <limits.h>
13#include <stdio.h>
[email protected]861c6c62009-04-20 16:50:5614#include <stdlib.h>
[email protected]d7395e732009-08-28 23:13:4315#include <sys/inotify.h>
16#include <unistd.h>
[email protected]861c6c62009-04-20 16:50:5617
[email protected]9bc8cff2010-04-03 01:05:3918#include <map>
19
[email protected]6af889c2011-10-06 23:11:4120#include "base/bind.h"
[email protected]c4c1b482011-07-22 17:24:2621#include "base/compiler_specific.h"
[email protected]76b90d312010-08-03 03:00:5022#include "base/environment.h"
[email protected]d7395e732009-08-28 23:13:4323#include "base/file_util.h"
[email protected]57999812013-02-24 05:40:5224#include "base/files/file_path.h"
[email protected]b9b4a572014-03-17 23:11:1225#include "base/files/scoped_file.h"
[email protected]861c6c62009-04-20 16:50:5626#include "base/logging.h"
[email protected]18b577412013-07-18 04:19:1527#include "base/message_loop/message_loop.h"
[email protected]3a29593d2011-04-11 10:07:5228#include "base/nix/xdg_util.h"
[email protected]76722472012-05-24 08:26:4629#include "base/single_thread_task_runner.h"
[email protected]fc9be5802013-06-11 10:56:5130#include "base/strings/string_number_conversions.h"
[email protected]f4ebe772013-02-02 00:21:3931#include "base/strings/string_tokenizer.h"
[email protected]66e96c42013-06-28 15:20:3132#include "base/strings/string_util.h"
[email protected]9a8c4022011-01-25 14:25:3333#include "base/threading/thread_restrictions.h"
[email protected]66e96c42013-06-28 15:20:3134#include "base/timer/timer.h"
[email protected]861c6c62009-04-20 16:50:5635#include "net/base/net_errors.h"
36#include "net/http/http_util.h"
37#include "net/proxy/proxy_config.h"
38#include "net/proxy/proxy_server.h"
[email protected]f89276a72013-07-12 06:41:5439#include "url/url_canon.h"
[email protected]861c6c62009-04-20 16:50:5640
[email protected]3fc24f52012-11-30 21:22:3441#if defined(USE_GIO)
42#include "library_loaders/libgio.h"
43#endif // defined(USE_GIO)
44
[email protected]861c6c62009-04-20 16:50:5645namespace net {
46
47namespace {
48
[email protected]861c6c62009-04-20 16:50:5649// Given a proxy hostname from a setting, returns that hostname with
50// an appropriate proxy server scheme prefix.
51// scheme indicates the desired proxy scheme: usually http, with
52// socks 4 or 5 as special cases.
[email protected]87a102b2009-07-14 05:23:3053// TODO(arindam): Remove URI string manipulation by using MapUrlSchemeToProxy.
[email protected]861c6c62009-04-20 16:50:5654std::string FixupProxyHostScheme(ProxyServer::Scheme scheme,
55 std::string host) {
[email protected]e8c50812010-09-28 00:16:1756 if (scheme == ProxyServer::SCHEME_SOCKS5 &&
57 StartsWithASCII(host, "socks4://", false)) {
58 // We default to socks 5, but if the user specifically set it to
59 // socks4://, then use that.
60 scheme = ProxyServer::SCHEME_SOCKS4;
[email protected]861c6c62009-04-20 16:50:5661 }
62 // Strip the scheme if any.
63 std::string::size_type colon = host.find("://");
64 if (colon != std::string::npos)
65 host = host.substr(colon + 3);
66 // If a username and perhaps password are specified, give a warning.
67 std::string::size_type at_sign = host.find("@");
68 // Should this be supported?
69 if (at_sign != std::string::npos) {
[email protected]62749f182009-07-15 13:16:5470 // ProxyConfig does not support authentication parameters, but Chrome
71 // will prompt for the password later. Disregard the
72 // authentication parameters and continue with this hostname.
73 LOG(WARNING) << "Proxy authentication parameters ignored, see bug 16709";
[email protected]861c6c62009-04-20 16:50:5674 host = host.substr(at_sign + 1);
75 }
76 // If this is a socks proxy, prepend a scheme so as to tell
77 // ProxyServer. This also allows ProxyServer to choose the right
78 // default port.
79 if (scheme == ProxyServer::SCHEME_SOCKS4)
80 host = "socks4://" + host;
81 else if (scheme == ProxyServer::SCHEME_SOCKS5)
82 host = "socks5://" + host;
[email protected]d7395e732009-08-28 23:13:4383 // If there is a trailing slash, remove it so |host| will parse correctly
84 // even if it includes a port number (since the slash is not numeric).
85 if (host.length() && host[host.length() - 1] == '/')
86 host.resize(host.length() - 1);
[email protected]861c6c62009-04-20 16:50:5687 return host;
88}
89
90} // namespace
91
[email protected]8e1845e12010-09-15 19:22:2492ProxyConfigServiceLinux::Delegate::~Delegate() {
93}
94
[email protected]3e44697f2009-05-22 14:37:3995bool ProxyConfigServiceLinux::Delegate::GetProxyFromEnvVarForScheme(
[email protected]861c6c62009-04-20 16:50:5696 const char* variable, ProxyServer::Scheme scheme,
97 ProxyServer* result_server) {
98 std::string env_value;
[email protected]3ba7e082010-08-07 02:57:5999 if (env_var_getter_->GetVar(variable, &env_value)) {
[email protected]861c6c62009-04-20 16:50:56100 if (!env_value.empty()) {
101 env_value = FixupProxyHostScheme(scheme, env_value);
[email protected]87a102b2009-07-14 05:23:30102 ProxyServer proxy_server =
103 ProxyServer::FromURI(env_value, ProxyServer::SCHEME_HTTP);
[email protected]861c6c62009-04-20 16:50:56104 if (proxy_server.is_valid() && !proxy_server.is_direct()) {
105 *result_server = proxy_server;
106 return true;
107 } else {
[email protected]3e44697f2009-05-22 14:37:39108 LOG(ERROR) << "Failed to parse environment variable " << variable;
[email protected]861c6c62009-04-20 16:50:56109 }
110 }
111 }
112 return false;
113}
114
[email protected]3e44697f2009-05-22 14:37:39115bool ProxyConfigServiceLinux::Delegate::GetProxyFromEnvVar(
[email protected]861c6c62009-04-20 16:50:56116 const char* variable, ProxyServer* result_server) {
117 return GetProxyFromEnvVarForScheme(variable, ProxyServer::SCHEME_HTTP,
118 result_server);
119}
120
[email protected]3e44697f2009-05-22 14:37:39121bool ProxyConfigServiceLinux::Delegate::GetConfigFromEnv(ProxyConfig* config) {
[email protected]861c6c62009-04-20 16:50:56122 // Check for automatic configuration first, in
123 // "auto_proxy". Possibly only the "environment_proxy" firefox
124 // extension has ever used this, but it still sounds like a good
125 // idea.
126 std::string auto_proxy;
[email protected]3ba7e082010-08-07 02:57:59127 if (env_var_getter_->GetVar("auto_proxy", &auto_proxy)) {
[email protected]861c6c62009-04-20 16:50:56128 if (auto_proxy.empty()) {
129 // Defined and empty => autodetect
[email protected]ed4ed0f2010-02-24 00:20:48130 config->set_auto_detect(true);
[email protected]861c6c62009-04-20 16:50:56131 } else {
132 // specified autoconfig URL
[email protected]ed4ed0f2010-02-24 00:20:48133 config->set_pac_url(GURL(auto_proxy));
[email protected]861c6c62009-04-20 16:50:56134 }
135 return true;
136 }
137 // "all_proxy" is a shortcut to avoid defining {http,https,ftp}_proxy.
138 ProxyServer proxy_server;
139 if (GetProxyFromEnvVar("all_proxy", &proxy_server)) {
[email protected]ed4ed0f2010-02-24 00:20:48140 config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
[email protected]2189e092013-03-16 18:02:02141 config->proxy_rules().single_proxies.SetSingleProxyServer(proxy_server);
[email protected]861c6c62009-04-20 16:50:56142 } else {
143 bool have_http = GetProxyFromEnvVar("http_proxy", &proxy_server);
144 if (have_http)
[email protected]2189e092013-03-16 18:02:02145 config->proxy_rules().proxies_for_http.SetSingleProxyServer(proxy_server);
[email protected]861c6c62009-04-20 16:50:56146 // It would be tempting to let http_proxy apply for all protocols
147 // if https_proxy and ftp_proxy are not defined. Googling turns up
148 // several documents that mention only http_proxy. But then the
149 // user really might not want to proxy https. And it doesn't seem
150 // like other apps do this. So we will refrain.
151 bool have_https = GetProxyFromEnvVar("https_proxy", &proxy_server);
152 if (have_https)
[email protected]2189e092013-03-16 18:02:02153 config->proxy_rules().proxies_for_https.
154 SetSingleProxyServer(proxy_server);
[email protected]861c6c62009-04-20 16:50:56155 bool have_ftp = GetProxyFromEnvVar("ftp_proxy", &proxy_server);
156 if (have_ftp)
[email protected]2189e092013-03-16 18:02:02157 config->proxy_rules().proxies_for_ftp.SetSingleProxyServer(proxy_server);
[email protected]861c6c62009-04-20 16:50:56158 if (have_http || have_https || have_ftp) {
159 // mustn't change type unless some rules are actually set.
[email protected]ed4ed0f2010-02-24 00:20:48160 config->proxy_rules().type =
161 ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
[email protected]861c6c62009-04-20 16:50:56162 }
163 }
[email protected]ed4ed0f2010-02-24 00:20:48164 if (config->proxy_rules().empty()) {
[email protected]861c6c62009-04-20 16:50:56165 // If the above were not defined, try for socks.
[email protected]e8c50812010-09-28 00:16:17166 // For environment variables, we default to version 5, per the gnome
167 // documentation: http://library.gnome.org/devel/gnet/stable/gnet-socks.html
168 ProxyServer::Scheme scheme = ProxyServer::SCHEME_SOCKS5;
[email protected]861c6c62009-04-20 16:50:56169 std::string env_version;
[email protected]3ba7e082010-08-07 02:57:59170 if (env_var_getter_->GetVar("SOCKS_VERSION", &env_version)
[email protected]e8c50812010-09-28 00:16:17171 && env_version == "4")
172 scheme = ProxyServer::SCHEME_SOCKS4;
[email protected]861c6c62009-04-20 16:50:56173 if (GetProxyFromEnvVarForScheme("SOCKS_SERVER", scheme, &proxy_server)) {
[email protected]ed4ed0f2010-02-24 00:20:48174 config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
[email protected]2189e092013-03-16 18:02:02175 config->proxy_rules().single_proxies.SetSingleProxyServer(proxy_server);
[email protected]861c6c62009-04-20 16:50:56176 }
177 }
178 // Look for the proxy bypass list.
179 std::string no_proxy;
[email protected]3ba7e082010-08-07 02:57:59180 env_var_getter_->GetVar("no_proxy", &no_proxy);
[email protected]ed4ed0f2010-02-24 00:20:48181 if (config->proxy_rules().empty()) {
[email protected]861c6c62009-04-20 16:50:56182 // Having only "no_proxy" set, presumably to "*", makes it
183 // explicit that env vars do specify a configuration: having no
184 // rules specified only means the user explicitly asks for direct
185 // connections.
186 return !no_proxy.empty();
187 }
[email protected]7541206c2010-02-19 20:24:06188 // Note that this uses "suffix" matching. So a bypass of "google.com"
189 // is understood to mean a bypass of "*google.com".
[email protected]ed4ed0f2010-02-24 00:20:48190 config->proxy_rules().bypass_rules.ParseFromStringUsingSuffixMatching(
191 no_proxy);
[email protected]861c6c62009-04-20 16:50:56192 return true;
193}
194
195namespace {
196
[email protected]d7395e732009-08-28 23:13:43197const int kDebounceTimeoutMilliseconds = 250;
[email protected]3e44697f2009-05-22 14:37:39198
[email protected]6de53d42010-11-09 07:33:19199#if defined(USE_GCONF)
[email protected]573c0502011-05-17 22:19:50200// This setting getter uses gconf, as used in GNOME 2 and some GNOME 3 desktops.
201class SettingGetterImplGConf : public ProxyConfigServiceLinux::SettingGetter {
[email protected]861c6c62009-04-20 16:50:56202 public:
[email protected]573c0502011-05-17 22:19:50203 SettingGetterImplGConf()
[email protected]b160df32012-02-06 20:39:41204 : client_(NULL), system_proxy_id_(0), system_http_proxy_id_(0),
[email protected]76722472012-05-24 08:26:46205 notify_delegate_(NULL) {
[email protected]b160df32012-02-06 20:39:41206 }
[email protected]3e44697f2009-05-22 14:37:39207
[email protected]573c0502011-05-17 22:19:50208 virtual ~SettingGetterImplGConf() {
[email protected]3e44697f2009-05-22 14:37:39209 // client_ should have been released before now, from
[email protected]f5b13442009-07-13 15:23:59210 // Delegate::OnDestroy(), while running on the UI thread. However
[email protected]b160df32012-02-06 20:39:41211 // on exiting the process, it may happen that Delegate::OnDestroy()
212 // task is left pending on the glib loop after the loop was quit,
213 // and pending tasks may then be deleted without being run.
[email protected]f5b13442009-07-13 15:23:59214 if (client_) {
215 // gconf client was not cleaned up.
[email protected]76722472012-05-24 08:26:46216 if (task_runner_->BelongsToCurrentThread()) {
[email protected]f5b13442009-07-13 15:23:59217 // We are on the UI thread so we can clean it safely. This is
218 // the case at least for ui_tests running under Valgrind in
219 // bug 16076.
[email protected]573c0502011-05-17 22:19:50220 VLOG(1) << "~SettingGetterImplGConf: releasing gconf client";
[email protected]d3066142011-05-10 02:36:20221 ShutDown();
[email protected]f5b13442009-07-13 15:23:59222 } else {
[email protected]ed348992011-09-19 20:27:57223 // This is very bad! We are deleting the setting getter but we're not on
224 // the UI thread. This is not supposed to happen: the setting getter is
225 // owned by the proxy config service's delegate, which is supposed to be
226 // destroyed on the UI thread only. We will get change notifications to
227 // a deleted object if we continue here, so fail now.
228 LOG(FATAL) << "~SettingGetterImplGConf: deleting on wrong thread!";
[email protected]f5b13442009-07-13 15:23:59229 }
230 }
[email protected]3e44697f2009-05-22 14:37:39231 DCHECK(!client_);
[email protected]861c6c62009-04-20 16:50:56232 }
233
[email protected]76722472012-05-24 08:26:46234 virtual bool Init(base::SingleThreadTaskRunner* glib_thread_task_runner,
[email protected]2da659e2013-05-23 20:51:34235 base::MessageLoopForIO* file_loop) OVERRIDE {
[email protected]76722472012-05-24 08:26:46236 DCHECK(glib_thread_task_runner->BelongsToCurrentThread());
[email protected]3e44697f2009-05-22 14:37:39237 DCHECK(!client_);
[email protected]90499482013-06-01 00:39:50238 DCHECK(!task_runner_.get());
[email protected]76722472012-05-24 08:26:46239 task_runner_ = glib_thread_task_runner;
[email protected]3e44697f2009-05-22 14:37:39240 client_ = gconf_client_get_default();
[email protected]861c6c62009-04-20 16:50:56241 if (!client_) {
[email protected]861c6c62009-04-20 16:50:56242 // It's not clear whether/when this can return NULL.
[email protected]3e44697f2009-05-22 14:37:39243 LOG(ERROR) << "Unable to create a gconf client";
[email protected]76722472012-05-24 08:26:46244 task_runner_ = NULL;
[email protected]3e44697f2009-05-22 14:37:39245 return false;
[email protected]861c6c62009-04-20 16:50:56246 }
[email protected]3e44697f2009-05-22 14:37:39247 GError* error = NULL;
[email protected]b160df32012-02-06 20:39:41248 bool added_system_proxy = false;
[email protected]3e44697f2009-05-22 14:37:39249 // We need to add the directories for which we'll be asking
[email protected]b160df32012-02-06 20:39:41250 // for notifications, and we might as well ask to preload them.
251 // These need to be removed again in ShutDown(); we are careful
252 // here to only leave client_ non-NULL if both have been added.
[email protected]3e44697f2009-05-22 14:37:39253 gconf_client_add_dir(client_, "/system/proxy",
254 GCONF_CLIENT_PRELOAD_ONELEVEL, &error);
255 if (error == NULL) {
[email protected]b160df32012-02-06 20:39:41256 added_system_proxy = true;
[email protected]3e44697f2009-05-22 14:37:39257 gconf_client_add_dir(client_, "/system/http_proxy",
258 GCONF_CLIENT_PRELOAD_ONELEVEL, &error);
259 }
260 if (error != NULL) {
261 LOG(ERROR) << "Error requesting gconf directory: " << error->message;
262 g_error_free(error);
[email protected]b160df32012-02-06 20:39:41263 if (added_system_proxy)
264 gconf_client_remove_dir(client_, "/system/proxy", NULL);
265 g_object_unref(client_);
266 client_ = NULL;
[email protected]76722472012-05-24 08:26:46267 task_runner_ = NULL;
[email protected]3e44697f2009-05-22 14:37:39268 return false;
269 }
270 return true;
271 }
272
[email protected]749bf5c2012-09-17 03:15:21273 virtual void ShutDown() OVERRIDE {
[email protected]3e44697f2009-05-22 14:37:39274 if (client_) {
[email protected]76722472012-05-24 08:26:46275 DCHECK(task_runner_->BelongsToCurrentThread());
[email protected]b160df32012-02-06 20:39:41276 // We must explicitly disable gconf notifications here, because the gconf
277 // client will be shared between all setting getters, and they do not all
278 // have the same lifetimes. (For instance, incognito sessions get their
279 // own, which is destroyed when the session ends.)
280 gconf_client_notify_remove(client_, system_http_proxy_id_);
281 gconf_client_notify_remove(client_, system_proxy_id_);
282 gconf_client_remove_dir(client_, "/system/http_proxy", NULL);
283 gconf_client_remove_dir(client_, "/system/proxy", NULL);
[email protected]3e44697f2009-05-22 14:37:39284 g_object_unref(client_);
285 client_ = NULL;
[email protected]76722472012-05-24 08:26:46286 task_runner_ = NULL;
[email protected]3e44697f2009-05-22 14:37:39287 }
288 }
289
[email protected]749bf5c2012-09-17 03:15:21290 virtual bool SetUpNotifications(
291 ProxyConfigServiceLinux::Delegate* delegate) OVERRIDE {
[email protected]3e44697f2009-05-22 14:37:39292 DCHECK(client_);
[email protected]76722472012-05-24 08:26:46293 DCHECK(task_runner_->BelongsToCurrentThread());
[email protected]3e44697f2009-05-22 14:37:39294 GError* error = NULL;
[email protected]d7395e732009-08-28 23:13:43295 notify_delegate_ = delegate;
[email protected]b160df32012-02-06 20:39:41296 // We have to keep track of the IDs returned by gconf_client_notify_add() so
297 // that we can remove them in ShutDown(). (Otherwise, notifications will be
298 // delivered to this object after it is deleted, which is bad, m'kay?)
299 system_proxy_id_ = gconf_client_notify_add(
[email protected]3e44697f2009-05-22 14:37:39300 client_, "/system/proxy",
[email protected]d7395e732009-08-28 23:13:43301 OnGConfChangeNotification, this,
[email protected]3e44697f2009-05-22 14:37:39302 NULL, &error);
303 if (error == NULL) {
[email protected]b160df32012-02-06 20:39:41304 system_http_proxy_id_ = gconf_client_notify_add(
[email protected]3e44697f2009-05-22 14:37:39305 client_, "/system/http_proxy",
[email protected]d7395e732009-08-28 23:13:43306 OnGConfChangeNotification, this,
[email protected]3e44697f2009-05-22 14:37:39307 NULL, &error);
308 }
309 if (error != NULL) {
310 LOG(ERROR) << "Error requesting gconf notifications: " << error->message;
311 g_error_free(error);
[email protected]d3066142011-05-10 02:36:20312 ShutDown();
[email protected]3e44697f2009-05-22 14:37:39313 return false;
314 }
[email protected]d3066142011-05-10 02:36:20315 // Simulate a change to avoid possibly losing updates before this point.
316 OnChangeNotification();
[email protected]3e44697f2009-05-22 14:37:39317 return true;
[email protected]861c6c62009-04-20 16:50:56318 }
319
[email protected]76722472012-05-24 08:26:46320 virtual base::SingleThreadTaskRunner* GetNotificationTaskRunner() OVERRIDE {
[email protected]90499482013-06-01 00:39:50321 return task_runner_.get();
[email protected]d7395e732009-08-28 23:13:43322 }
323
[email protected]db8ff912012-06-12 23:32:51324 virtual ProxyConfigSource GetConfigSource() OVERRIDE {
325 return PROXY_CONFIG_SOURCE_GCONF;
[email protected]d7395e732009-08-28 23:13:43326 }
327
[email protected]c4c1b482011-07-22 17:24:26328 virtual bool GetString(StringSetting key, std::string* result) OVERRIDE {
[email protected]573c0502011-05-17 22:19:50329 switch (key) {
330 case PROXY_MODE:
331 return GetStringByPath("/system/proxy/mode", result);
332 case PROXY_AUTOCONF_URL:
333 return GetStringByPath("/system/proxy/autoconfig_url", result);
334 case PROXY_HTTP_HOST:
335 return GetStringByPath("/system/http_proxy/host", result);
336 case PROXY_HTTPS_HOST:
337 return GetStringByPath("/system/proxy/secure_host", result);
338 case PROXY_FTP_HOST:
339 return GetStringByPath("/system/proxy/ftp_host", result);
340 case PROXY_SOCKS_HOST:
341 return GetStringByPath("/system/proxy/socks_host", result);
[email protected]573c0502011-05-17 22:19:50342 }
[email protected]6b5fe742011-05-20 21:46:48343 return false; // Placate compiler.
[email protected]573c0502011-05-17 22:19:50344 }
[email protected]c4c1b482011-07-22 17:24:26345 virtual bool GetBool(BoolSetting key, bool* result) OVERRIDE {
[email protected]573c0502011-05-17 22:19:50346 switch (key) {
347 case PROXY_USE_HTTP_PROXY:
348 return GetBoolByPath("/system/http_proxy/use_http_proxy", result);
349 case PROXY_USE_SAME_PROXY:
350 return GetBoolByPath("/system/http_proxy/use_same_proxy", result);
351 case PROXY_USE_AUTHENTICATION:
352 return GetBoolByPath("/system/http_proxy/use_authentication", result);
[email protected]573c0502011-05-17 22:19:50353 }
[email protected]6b5fe742011-05-20 21:46:48354 return false; // Placate compiler.
[email protected]573c0502011-05-17 22:19:50355 }
[email protected]c4c1b482011-07-22 17:24:26356 virtual bool GetInt(IntSetting key, int* result) OVERRIDE {
[email protected]573c0502011-05-17 22:19:50357 switch (key) {
358 case PROXY_HTTP_PORT:
359 return GetIntByPath("/system/http_proxy/port", result);
360 case PROXY_HTTPS_PORT:
361 return GetIntByPath("/system/proxy/secure_port", result);
362 case PROXY_FTP_PORT:
363 return GetIntByPath("/system/proxy/ftp_port", result);
364 case PROXY_SOCKS_PORT:
365 return GetIntByPath("/system/proxy/socks_port", result);
[email protected]573c0502011-05-17 22:19:50366 }
[email protected]6b5fe742011-05-20 21:46:48367 return false; // Placate compiler.
[email protected]573c0502011-05-17 22:19:50368 }
[email protected]6b5fe742011-05-20 21:46:48369 virtual bool GetStringList(StringListSetting key,
[email protected]c4c1b482011-07-22 17:24:26370 std::vector<std::string>* result) OVERRIDE {
[email protected]573c0502011-05-17 22:19:50371 switch (key) {
372 case PROXY_IGNORE_HOSTS:
373 return GetStringListByPath("/system/http_proxy/ignore_hosts", result);
[email protected]573c0502011-05-17 22:19:50374 }
[email protected]6b5fe742011-05-20 21:46:48375 return false; // Placate compiler.
[email protected]573c0502011-05-17 22:19:50376 }
377
[email protected]c4c1b482011-07-22 17:24:26378 virtual bool BypassListIsReversed() OVERRIDE {
[email protected]573c0502011-05-17 22:19:50379 // This is a KDE-specific setting.
380 return false;
381 }
382
[email protected]c4c1b482011-07-22 17:24:26383 virtual bool MatchHostsUsingSuffixMatching() OVERRIDE {
[email protected]573c0502011-05-17 22:19:50384 return false;
385 }
386
387 private:
388 bool GetStringByPath(const char* key, std::string* result) {
[email protected]3e44697f2009-05-22 14:37:39389 DCHECK(client_);
[email protected]76722472012-05-24 08:26:46390 DCHECK(task_runner_->BelongsToCurrentThread());
[email protected]861c6c62009-04-20 16:50:56391 GError* error = NULL;
392 gchar* value = gconf_client_get_string(client_, key, &error);
393 if (HandleGError(error, key))
394 return false;
395 if (!value)
396 return false;
397 *result = value;
398 g_free(value);
399 return true;
400 }
[email protected]573c0502011-05-17 22:19:50401 bool GetBoolByPath(const char* key, bool* result) {
[email protected]3e44697f2009-05-22 14:37:39402 DCHECK(client_);
[email protected]76722472012-05-24 08:26:46403 DCHECK(task_runner_->BelongsToCurrentThread());
[email protected]861c6c62009-04-20 16:50:56404 GError* error = NULL;
405 // We want to distinguish unset values from values defaulting to
406 // false. For that we need to use the type-generic
407 // gconf_client_get() rather than gconf_client_get_bool().
408 GConfValue* gconf_value = gconf_client_get(client_, key, &error);
409 if (HandleGError(error, key))
410 return false;
411 if (!gconf_value) {
412 // Unset.
413 return false;
414 }
415 if (gconf_value->type != GCONF_VALUE_BOOL) {
416 gconf_value_free(gconf_value);
417 return false;
418 }
419 gboolean bool_value = gconf_value_get_bool(gconf_value);
420 *result = static_cast<bool>(bool_value);
421 gconf_value_free(gconf_value);
422 return true;
423 }
[email protected]573c0502011-05-17 22:19:50424 bool GetIntByPath(const char* key, int* result) {
[email protected]3e44697f2009-05-22 14:37:39425 DCHECK(client_);
[email protected]76722472012-05-24 08:26:46426 DCHECK(task_runner_->BelongsToCurrentThread());
[email protected]861c6c62009-04-20 16:50:56427 GError* error = NULL;
428 int value = gconf_client_get_int(client_, key, &error);
429 if (HandleGError(error, key))
430 return false;
431 // We don't bother to distinguish an unset value because callers
432 // don't care. 0 is returned if unset.
433 *result = value;
434 return true;
435 }
[email protected]573c0502011-05-17 22:19:50436 bool GetStringListByPath(const char* key, std::vector<std::string>* result) {
[email protected]3e44697f2009-05-22 14:37:39437 DCHECK(client_);
[email protected]76722472012-05-24 08:26:46438 DCHECK(task_runner_->BelongsToCurrentThread());
[email protected]861c6c62009-04-20 16:50:56439 GError* error = NULL;
440 GSList* list = gconf_client_get_list(client_, key,
441 GCONF_VALUE_STRING, &error);
442 if (HandleGError(error, key))
443 return false;
[email protected]8c20e3d2011-05-19 21:03:57444 if (!list)
[email protected]861c6c62009-04-20 16:50:56445 return false;
[email protected]861c6c62009-04-20 16:50:56446 for (GSList *it = list; it; it = it->next) {
447 result->push_back(static_cast<char*>(it->data));
448 g_free(it->data);
449 }
450 g_slist_free(list);
451 return true;
452 }
453
[email protected]861c6c62009-04-20 16:50:56454 // Logs and frees a glib error. Returns false if there was no error
455 // (error is NULL).
456 bool HandleGError(GError* error, const char* key) {
457 if (error != NULL) {
[email protected]3e44697f2009-05-22 14:37:39458 LOG(ERROR) << "Error getting gconf value for " << key
459 << ": " << error->message;
[email protected]861c6c62009-04-20 16:50:56460 g_error_free(error);
461 return true;
462 }
463 return false;
464 }
465
[email protected]d7395e732009-08-28 23:13:43466 // This is the callback from the debounce timer.
467 void OnDebouncedNotification() {
[email protected]76722472012-05-24 08:26:46468 DCHECK(task_runner_->BelongsToCurrentThread());
[email protected]961ac942011-04-28 18:18:14469 CHECK(notify_delegate_);
[email protected]d7395e732009-08-28 23:13:43470 // Forward to a method on the proxy config service delegate object.
471 notify_delegate_->OnCheckProxyConfigSettings();
472 }
473
474 void OnChangeNotification() {
475 // We don't use Reset() because the timer may not yet be running.
476 // (In that case Stop() is a no-op.)
477 debounce_timer_.Stop();
[email protected]d323a172011-09-02 18:23:02478 debounce_timer_.Start(FROM_HERE,
[email protected]8c20e3d2011-05-19 21:03:57479 base::TimeDelta::FromMilliseconds(kDebounceTimeoutMilliseconds),
480 this, &SettingGetterImplGConf::OnDebouncedNotification);
[email protected]d7395e732009-08-28 23:13:43481 }
482
[email protected]8c20e3d2011-05-19 21:03:57483 // gconf notification callback, dispatched on the default glib main loop.
484 static void OnGConfChangeNotification(GConfClient* client, guint cnxn_id,
485 GConfEntry* entry, gpointer user_data) {
[email protected]b30a3f52010-10-16 01:05:46486 VLOG(1) << "gconf change notification for key "
487 << gconf_entry_get_key(entry);
[email protected]d7395e732009-08-28 23:13:43488 // We don't track which key has changed, just that something did change.
[email protected]573c0502011-05-17 22:19:50489 SettingGetterImplGConf* setting_getter =
490 reinterpret_cast<SettingGetterImplGConf*>(user_data);
[email protected]d7395e732009-08-28 23:13:43491 setting_getter->OnChangeNotification();
492 }
493
[email protected]861c6c62009-04-20 16:50:56494 GConfClient* client_;
[email protected]b160df32012-02-06 20:39:41495 // These ids are the values returned from gconf_client_notify_add(), which we
496 // will need in order to later call gconf_client_notify_remove().
497 guint system_proxy_id_;
498 guint system_http_proxy_id_;
499
[email protected]d7395e732009-08-28 23:13:43500 ProxyConfigServiceLinux::Delegate* notify_delegate_;
[email protected]573c0502011-05-17 22:19:50501 base::OneShotTimer<SettingGetterImplGConf> debounce_timer_;
[email protected]861c6c62009-04-20 16:50:56502
[email protected]76722472012-05-24 08:26:46503 // Task runner for the thread that we make gconf calls on. It should
[email protected]3e44697f2009-05-22 14:37:39504 // be the UI thread and all our methods should be called on this
505 // thread. Only for assertions.
[email protected]76722472012-05-24 08:26:46506 scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
[email protected]3e44697f2009-05-22 14:37:39507
[email protected]573c0502011-05-17 22:19:50508 DISALLOW_COPY_AND_ASSIGN(SettingGetterImplGConf);
[email protected]d7395e732009-08-28 23:13:43509};
[email protected]6de53d42010-11-09 07:33:19510#endif // defined(USE_GCONF)
[email protected]d7395e732009-08-28 23:13:43511
[email protected]8c20e3d2011-05-19 21:03:57512#if defined(USE_GIO)
513// This setting getter uses gsettings, as used in most GNOME 3 desktops.
514class SettingGetterImplGSettings
515 : public ProxyConfigServiceLinux::SettingGetter {
516 public:
[email protected]0e14e87d2011-06-21 21:24:19517 SettingGetterImplGSettings() :
[email protected]0e14e87d2011-06-21 21:24:19518 client_(NULL),
519 http_client_(NULL),
520 https_client_(NULL),
521 ftp_client_(NULL),
522 socks_client_(NULL),
[email protected]76722472012-05-24 08:26:46523 notify_delegate_(NULL) {
[email protected]8c20e3d2011-05-19 21:03:57524 }
525
526 virtual ~SettingGetterImplGSettings() {
527 // client_ should have been released before now, from
528 // Delegate::OnDestroy(), while running on the UI thread. However
529 // on exiting the process, it may happen that
530 // Delegate::OnDestroy() task is left pending on the glib loop
531 // after the loop was quit, and pending tasks may then be deleted
532 // without being run.
533 if (client_) {
534 // gconf client was not cleaned up.
[email protected]76722472012-05-24 08:26:46535 if (task_runner_->BelongsToCurrentThread()) {
[email protected]8c20e3d2011-05-19 21:03:57536 // We are on the UI thread so we can clean it safely. This is
537 // the case at least for ui_tests running under Valgrind in
538 // bug 16076.
539 VLOG(1) << "~SettingGetterImplGSettings: releasing gsettings client";
540 ShutDown();
541 } else {
542 LOG(WARNING) << "~SettingGetterImplGSettings: leaking gsettings client";
543 client_ = NULL;
544 }
545 }
546 DCHECK(!client_);
[email protected]8c20e3d2011-05-19 21:03:57547 }
548
[email protected]4cf80f0b2011-05-20 20:30:26549 bool SchemaExists(const char* schema_name) {
[email protected]3fc24f52012-11-30 21:22:34550 const gchar* const* schemas = libgio_loader_.g_settings_list_schemas();
[email protected]4cf80f0b2011-05-20 20:30:26551 while (*schemas) {
[email protected]a099f3ae2011-08-16 21:06:58552 if (strcmp(schema_name, static_cast<const char*>(*schemas)) == 0)
[email protected]4cf80f0b2011-05-20 20:30:26553 return true;
554 schemas++;
555 }
556 return false;
557 }
558
[email protected]8c20e3d2011-05-19 21:03:57559 // LoadAndCheckVersion() must be called *before* Init()!
560 bool LoadAndCheckVersion(base::Environment* env);
561
[email protected]76722472012-05-24 08:26:46562 virtual bool Init(base::SingleThreadTaskRunner* glib_thread_task_runner,
[email protected]2da659e2013-05-23 20:51:34563 base::MessageLoopForIO* file_loop) OVERRIDE {
[email protected]76722472012-05-24 08:26:46564 DCHECK(glib_thread_task_runner->BelongsToCurrentThread());
[email protected]8c20e3d2011-05-19 21:03:57565 DCHECK(!client_);
[email protected]90499482013-06-01 00:39:50566 DCHECK(!task_runner_.get());
[email protected]4cf80f0b2011-05-20 20:30:26567
568 if (!SchemaExists("org.gnome.system.proxy") ||
[email protected]3fc24f52012-11-30 21:22:34569 !(client_ = libgio_loader_.g_settings_new("org.gnome.system.proxy"))) {
[email protected]8c20e3d2011-05-19 21:03:57570 // It's not clear whether/when this can return NULL.
571 LOG(ERROR) << "Unable to create a gsettings client";
572 return false;
573 }
[email protected]76722472012-05-24 08:26:46574 task_runner_ = glib_thread_task_runner;
[email protected]8c20e3d2011-05-19 21:03:57575 // We assume these all work if the above call worked.
[email protected]3fc24f52012-11-30 21:22:34576 http_client_ = libgio_loader_.g_settings_get_child(client_, "http");
577 https_client_ = libgio_loader_.g_settings_get_child(client_, "https");
578 ftp_client_ = libgio_loader_.g_settings_get_child(client_, "ftp");
579 socks_client_ = libgio_loader_.g_settings_get_child(client_, "socks");
[email protected]8c20e3d2011-05-19 21:03:57580 DCHECK(http_client_ && https_client_ && ftp_client_ && socks_client_);
581 return true;
582 }
583
[email protected]749bf5c2012-09-17 03:15:21584 virtual void ShutDown() OVERRIDE {
[email protected]8c20e3d2011-05-19 21:03:57585 if (client_) {
[email protected]76722472012-05-24 08:26:46586 DCHECK(task_runner_->BelongsToCurrentThread());
[email protected]8c20e3d2011-05-19 21:03:57587 // This also disables gsettings notifications.
588 g_object_unref(socks_client_);
589 g_object_unref(ftp_client_);
590 g_object_unref(https_client_);
591 g_object_unref(http_client_);
592 g_object_unref(client_);
593 // We only need to null client_ because it's the only one that we check.
594 client_ = NULL;
[email protected]76722472012-05-24 08:26:46595 task_runner_ = NULL;
[email protected]8c20e3d2011-05-19 21:03:57596 }
597 }
598
[email protected]749bf5c2012-09-17 03:15:21599 virtual bool SetUpNotifications(
600 ProxyConfigServiceLinux::Delegate* delegate) OVERRIDE {
[email protected]8c20e3d2011-05-19 21:03:57601 DCHECK(client_);
[email protected]76722472012-05-24 08:26:46602 DCHECK(task_runner_->BelongsToCurrentThread());
[email protected]8c20e3d2011-05-19 21:03:57603 notify_delegate_ = delegate;
604 // We could watch for the change-event signal instead of changed, but
605 // since we have to watch more than one object, we'd still have to
606 // debounce change notifications. This is conceptually simpler.
607 g_signal_connect(G_OBJECT(client_), "changed",
608 G_CALLBACK(OnGSettingsChangeNotification), this);
609 g_signal_connect(G_OBJECT(http_client_), "changed",
610 G_CALLBACK(OnGSettingsChangeNotification), this);
611 g_signal_connect(G_OBJECT(https_client_), "changed",
612 G_CALLBACK(OnGSettingsChangeNotification), this);
613 g_signal_connect(G_OBJECT(ftp_client_), "changed",
614 G_CALLBACK(OnGSettingsChangeNotification), this);
615 g_signal_connect(G_OBJECT(socks_client_), "changed",
616 G_CALLBACK(OnGSettingsChangeNotification), this);
617 // Simulate a change to avoid possibly losing updates before this point.
618 OnChangeNotification();
619 return true;
620 }
621
[email protected]76722472012-05-24 08:26:46622 virtual base::SingleThreadTaskRunner* GetNotificationTaskRunner() OVERRIDE {
[email protected]90499482013-06-01 00:39:50623 return task_runner_.get();
[email protected]8c20e3d2011-05-19 21:03:57624 }
625
[email protected]db8ff912012-06-12 23:32:51626 virtual ProxyConfigSource GetConfigSource() OVERRIDE {
627 return PROXY_CONFIG_SOURCE_GSETTINGS;
[email protected]8c20e3d2011-05-19 21:03:57628 }
629
[email protected]c4c1b482011-07-22 17:24:26630 virtual bool GetString(StringSetting key, std::string* result) OVERRIDE {
[email protected]8c20e3d2011-05-19 21:03:57631 DCHECK(client_);
632 switch (key) {
633 case PROXY_MODE:
634 return GetStringByPath(client_, "mode", result);
635 case PROXY_AUTOCONF_URL:
636 return GetStringByPath(client_, "autoconfig-url", result);
637 case PROXY_HTTP_HOST:
638 return GetStringByPath(http_client_, "host", result);
639 case PROXY_HTTPS_HOST:
640 return GetStringByPath(https_client_, "host", result);
641 case PROXY_FTP_HOST:
642 return GetStringByPath(ftp_client_, "host", result);
643 case PROXY_SOCKS_HOST:
644 return GetStringByPath(socks_client_, "host", result);
[email protected]8c20e3d2011-05-19 21:03:57645 }
[email protected]6b5fe742011-05-20 21:46:48646 return false; // Placate compiler.
[email protected]8c20e3d2011-05-19 21:03:57647 }
[email protected]c4c1b482011-07-22 17:24:26648 virtual bool GetBool(BoolSetting key, bool* result) OVERRIDE {
[email protected]8c20e3d2011-05-19 21:03:57649 DCHECK(client_);
650 switch (key) {
651 case PROXY_USE_HTTP_PROXY:
652 // Although there is an "enabled" boolean in http_client_, it is not set
653 // to true by the proxy config utility. We ignore it and return false.
654 return false;
655 case PROXY_USE_SAME_PROXY:
656 // Similarly, although there is a "use-same-proxy" boolean in client_,
657 // it is never set to false by the proxy config utility. We ignore it.
658 return false;
659 case PROXY_USE_AUTHENTICATION:
660 // There is also no way to set this in the proxy config utility, but it
661 // doesn't hurt us to get the actual setting (unlike the two above).
662 return GetBoolByPath(http_client_, "use-authentication", result);
[email protected]8c20e3d2011-05-19 21:03:57663 }
[email protected]6b5fe742011-05-20 21:46:48664 return false; // Placate compiler.
[email protected]8c20e3d2011-05-19 21:03:57665 }
[email protected]c4c1b482011-07-22 17:24:26666 virtual bool GetInt(IntSetting key, int* result) OVERRIDE {
[email protected]8c20e3d2011-05-19 21:03:57667 DCHECK(client_);
668 switch (key) {
669 case PROXY_HTTP_PORT:
670 return GetIntByPath(http_client_, "port", result);
671 case PROXY_HTTPS_PORT:
672 return GetIntByPath(https_client_, "port", result);
673 case PROXY_FTP_PORT:
674 return GetIntByPath(ftp_client_, "port", result);
675 case PROXY_SOCKS_PORT:
676 return GetIntByPath(socks_client_, "port", result);
[email protected]8c20e3d2011-05-19 21:03:57677 }
[email protected]6b5fe742011-05-20 21:46:48678 return false; // Placate compiler.
[email protected]8c20e3d2011-05-19 21:03:57679 }
[email protected]6b5fe742011-05-20 21:46:48680 virtual bool GetStringList(StringListSetting key,
[email protected]c4c1b482011-07-22 17:24:26681 std::vector<std::string>* result) OVERRIDE {
[email protected]8c20e3d2011-05-19 21:03:57682 DCHECK(client_);
683 switch (key) {
684 case PROXY_IGNORE_HOSTS:
685 return GetStringListByPath(client_, "ignore-hosts", result);
[email protected]8c20e3d2011-05-19 21:03:57686 }
[email protected]6b5fe742011-05-20 21:46:48687 return false; // Placate compiler.
[email protected]8c20e3d2011-05-19 21:03:57688 }
689
[email protected]c4c1b482011-07-22 17:24:26690 virtual bool BypassListIsReversed() OVERRIDE {
[email protected]8c20e3d2011-05-19 21:03:57691 // This is a KDE-specific setting.
692 return false;
693 }
694
[email protected]c4c1b482011-07-22 17:24:26695 virtual bool MatchHostsUsingSuffixMatching() OVERRIDE {
[email protected]8c20e3d2011-05-19 21:03:57696 return false;
697 }
698
699 private:
[email protected]8c20e3d2011-05-19 21:03:57700 bool GetStringByPath(GSettings* client, const char* key,
701 std::string* result) {
[email protected]76722472012-05-24 08:26:46702 DCHECK(task_runner_->BelongsToCurrentThread());
[email protected]3fc24f52012-11-30 21:22:34703 gchar* value = libgio_loader_.g_settings_get_string(client, key);
[email protected]8c20e3d2011-05-19 21:03:57704 if (!value)
705 return false;
706 *result = value;
707 g_free(value);
708 return true;
709 }
710 bool GetBoolByPath(GSettings* client, const char* key, bool* result) {
[email protected]76722472012-05-24 08:26:46711 DCHECK(task_runner_->BelongsToCurrentThread());
[email protected]3fc24f52012-11-30 21:22:34712 *result = static_cast<bool>(
713 libgio_loader_.g_settings_get_boolean(client, key));
[email protected]8c20e3d2011-05-19 21:03:57714 return true;
715 }
716 bool GetIntByPath(GSettings* client, const char* key, int* result) {
[email protected]76722472012-05-24 08:26:46717 DCHECK(task_runner_->BelongsToCurrentThread());
[email protected]3fc24f52012-11-30 21:22:34718 *result = libgio_loader_.g_settings_get_int(client, key);
[email protected]8c20e3d2011-05-19 21:03:57719 return true;
720 }
721 bool GetStringListByPath(GSettings* client, const char* key,
722 std::vector<std::string>* result) {
[email protected]76722472012-05-24 08:26:46723 DCHECK(task_runner_->BelongsToCurrentThread());
[email protected]3fc24f52012-11-30 21:22:34724 gchar** list = libgio_loader_.g_settings_get_strv(client, key);
[email protected]8c20e3d2011-05-19 21:03:57725 if (!list)
726 return false;
727 for (size_t i = 0; list[i]; ++i) {
728 result->push_back(static_cast<char*>(list[i]));
729 g_free(list[i]);
730 }
731 g_free(list);
732 return true;
733 }
734
735 // This is the callback from the debounce timer.
736 void OnDebouncedNotification() {
[email protected]76722472012-05-24 08:26:46737 DCHECK(task_runner_->BelongsToCurrentThread());
[email protected]8c20e3d2011-05-19 21:03:57738 CHECK(notify_delegate_);
739 // Forward to a method on the proxy config service delegate object.
740 notify_delegate_->OnCheckProxyConfigSettings();
741 }
742
743 void OnChangeNotification() {
744 // We don't use Reset() because the timer may not yet be running.
745 // (In that case Stop() is a no-op.)
746 debounce_timer_.Stop();
[email protected]d323a172011-09-02 18:23:02747 debounce_timer_.Start(FROM_HERE,
[email protected]8c20e3d2011-05-19 21:03:57748 base::TimeDelta::FromMilliseconds(kDebounceTimeoutMilliseconds),
749 this, &SettingGetterImplGSettings::OnDebouncedNotification);
750 }
751
752 // gsettings notification callback, dispatched on the default glib main loop.
753 static void OnGSettingsChangeNotification(GSettings* client, gchar* key,
754 gpointer user_data) {
755 VLOG(1) << "gsettings change notification for key " << key;
756 // We don't track which key has changed, just that something did change.
757 SettingGetterImplGSettings* setting_getter =
758 reinterpret_cast<SettingGetterImplGSettings*>(user_data);
759 setting_getter->OnChangeNotification();
760 }
761
762 GSettings* client_;
763 GSettings* http_client_;
764 GSettings* https_client_;
765 GSettings* ftp_client_;
766 GSettings* socks_client_;
767 ProxyConfigServiceLinux::Delegate* notify_delegate_;
768 base::OneShotTimer<SettingGetterImplGSettings> debounce_timer_;
769
[email protected]76722472012-05-24 08:26:46770 // Task runner for the thread that we make gsettings calls on. It should
[email protected]8c20e3d2011-05-19 21:03:57771 // be the UI thread and all our methods should be called on this
772 // thread. Only for assertions.
[email protected]76722472012-05-24 08:26:46773 scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
[email protected]8c20e3d2011-05-19 21:03:57774
[email protected]3fc24f52012-11-30 21:22:34775 LibGioLoader libgio_loader_;
776
[email protected]8c20e3d2011-05-19 21:03:57777 DISALLOW_COPY_AND_ASSIGN(SettingGetterImplGSettings);
778};
779
780bool SettingGetterImplGSettings::LoadAndCheckVersion(
781 base::Environment* env) {
782 // LoadAndCheckVersion() must be called *before* Init()!
783 DCHECK(!client_);
784
785 // The APIs to query gsettings were introduced after the minimum glib
786 // version we target, so we can't link directly against them. We load them
787 // dynamically at runtime, and if they don't exist, return false here. (We
788 // support linking directly via gyp flags though.) Additionally, even when
789 // they are present, we do two additional checks to make sure we should use
790 // them and not gconf. First, we attempt to load the schema for proxy
791 // settings. Second, we check for the program that was used in older
792 // versions of GNOME to configure proxy settings, and return false if it
793 // exists. Some distributions (e.g. Ubuntu 11.04) have the API and schema
794 // but don't use gsettings for proxy settings, but they do have the old
795 // binary, so we detect these systems that way.
796
[email protected]ae82cea2012-12-06 22:52:10797 {
798 // TODO(phajdan.jr): Redesign the code to load library on different thread.
799 base::ThreadRestrictions::ScopedAllowIO allow_io;
800
801 // Try also without .0 at the end; on some systems this may be required.
802 if (!libgio_loader_.Load("libgio-2.0.so.0") &&
803 !libgio_loader_.Load("libgio-2.0.so")) {
804 VLOG(1) << "Cannot load gio library. Will fall back to gconf.";
805 return false;
806 }
[email protected]8c20e3d2011-05-19 21:03:57807 }
[email protected]8c20e3d2011-05-19 21:03:57808
[email protected]4cf80f0b2011-05-20 20:30:26809 GSettings* client;
810 if (!SchemaExists("org.gnome.system.proxy") ||
[email protected]3fc24f52012-11-30 21:22:34811 !(client = libgio_loader_.g_settings_new("org.gnome.system.proxy"))) {
[email protected]8c20e3d2011-05-19 21:03:57812 VLOG(1) << "Cannot create gsettings client. Will fall back to gconf.";
813 return false;
814 }
815 g_object_unref(client);
816
817 std::string path;
818 if (!env->GetVar("PATH", &path)) {
819 LOG(ERROR) << "No $PATH variable. Assuming no gnome-network-properties.";
820 } else {
821 // Yes, we're on the UI thread. Yes, we're accessing the file system.
822 // Sadly, we don't have much choice. We need the proxy settings and we
823 // need them now, and to figure out where to get them, we have to check
824 // for this binary. See http://crbug.com/69057 for additional details.
825 base::ThreadRestrictions::ScopedAllowIO allow_io;
826 std::vector<std::string> paths;
827 Tokenize(path, ":", &paths);
828 for (size_t i = 0; i < paths.size(); ++i) {
[email protected]6cdfd7f2013-02-08 20:40:15829 base::FilePath file(paths[i]);
[email protected]7567484142013-07-11 17:36:07830 if (base::PathExists(file.Append("gnome-network-properties"))) {
[email protected]8c20e3d2011-05-19 21:03:57831 VLOG(1) << "Found gnome-network-properties. Will fall back to gconf.";
832 return false;
833 }
834 }
835 }
836
837 VLOG(1) << "All gsettings tests OK. Will get proxy config from gsettings.";
838 return true;
839}
840#endif // defined(USE_GIO)
841
[email protected]d7395e732009-08-28 23:13:43842// This is the KDE version that reads kioslaverc and simulates gconf.
843// Doing this allows the main Delegate code, as well as the unit tests
844// for it, to stay the same - and the settings map fairly well besides.
[email protected]573c0502011-05-17 22:19:50845class SettingGetterImplKDE : public ProxyConfigServiceLinux::SettingGetter,
846 public base::MessagePumpLibevent::Watcher {
[email protected]d7395e732009-08-28 23:13:43847 public:
[email protected]573c0502011-05-17 22:19:50848 explicit SettingGetterImplKDE(base::Environment* env_var_getter)
[email protected]d7395e732009-08-28 23:13:43849 : inotify_fd_(-1), notify_delegate_(NULL), indirect_manual_(false),
[email protected]a48bf4a2010-06-14 18:24:53850 auto_no_pac_(false), reversed_bypass_list_(false),
[email protected]f18fde22010-05-18 23:49:54851 env_var_getter_(env_var_getter), file_loop_(NULL) {
[email protected]9a8c4022011-01-25 14:25:33852 // This has to be called on the UI thread (http://crbug.com/69057).
853 base::ThreadRestrictions::ScopedAllowIO allow_io;
854
[email protected]f18fde22010-05-18 23:49:54855 // Derive the location of the kde config dir from the environment.
[email protected]92d2dc82010-04-08 17:49:59856 std::string home;
[email protected]3ba7e082010-08-07 02:57:59857 if (env_var_getter->GetVar("KDEHOME", &home) && !home.empty()) {
[email protected]2e8cfe22010-06-12 00:26:24858 // $KDEHOME is set. Use it unconditionally.
[email protected]6cdfd7f2013-02-08 20:40:15859 kde_config_dir_ = KDEHomeToConfigPath(base::FilePath(home));
[email protected]92d2dc82010-04-08 17:49:59860 } else {
[email protected]2e8cfe22010-06-12 00:26:24861 // $KDEHOME is unset. Try to figure out what to use. This seems to be
[email protected]92d2dc82010-04-08 17:49:59862 // the common case on most distributions.
[email protected]3ba7e082010-08-07 02:57:59863 if (!env_var_getter->GetVar(base::env_vars::kHome, &home))
[email protected]d7395e732009-08-28 23:13:43864 // User has no $HOME? Give up. Later we'll report the failure.
865 return;
[email protected]6b0349ef2010-10-16 04:56:06866 if (base::nix::GetDesktopEnvironment(env_var_getter) ==
867 base::nix::DESKTOP_ENVIRONMENT_KDE3) {
[email protected]92d2dc82010-04-08 17:49:59868 // KDE3 always uses .kde for its configuration.
[email protected]6cdfd7f2013-02-08 20:40:15869 base::FilePath kde_path = base::FilePath(home).Append(".kde");
[email protected]92d2dc82010-04-08 17:49:59870 kde_config_dir_ = KDEHomeToConfigPath(kde_path);
871 } else {
872 // Some distributions patch KDE4 to use .kde4 instead of .kde, so that
[email protected]fad9c8a52010-06-10 22:30:53873 // both can be installed side-by-side. Sadly they don't all do this, and
874 // they don't always do this: some distributions have started switching
875 // back as well. So if there is a .kde4 directory, check the timestamps
876 // of the config directories within and use the newest one.
[email protected]92d2dc82010-04-08 17:49:59877 // Note that we should currently be running in the UI thread, because in
878 // the gconf version, that is the only thread that can access the proxy
879 // settings (a gconf restriction). As noted below, the initial read of
880 // the proxy settings will be done in this thread anyway, so we check
881 // for .kde4 here in this thread as well.
[email protected]6cdfd7f2013-02-08 20:40:15882 base::FilePath kde3_path = base::FilePath(home).Append(".kde");
883 base::FilePath kde3_config = KDEHomeToConfigPath(kde3_path);
884 base::FilePath kde4_path = base::FilePath(home).Append(".kde4");
885 base::FilePath kde4_config = KDEHomeToConfigPath(kde4_path);
[email protected]fad9c8a52010-06-10 22:30:53886 bool use_kde4 = false;
[email protected]dcd16612013-07-15 20:18:09887 if (base::DirectoryExists(kde4_path)) {
[email protected]54124ed02014-01-07 10:06:58888 base::File::Info kde3_info;
889 base::File::Info kde4_info;
[email protected]9eae4e62013-12-04 20:56:49890 if (base::GetFileInfo(kde4_config, &kde4_info)) {
891 if (base::GetFileInfo(kde3_config, &kde3_info)) {
[email protected]fad9c8a52010-06-10 22:30:53892 use_kde4 = kde4_info.last_modified >= kde3_info.last_modified;
893 } else {
894 use_kde4 = true;
895 }
896 }
897 }
898 if (use_kde4) {
[email protected]92d2dc82010-04-08 17:49:59899 kde_config_dir_ = KDEHomeToConfigPath(kde4_path);
900 } else {
[email protected]fad9c8a52010-06-10 22:30:53901 kde_config_dir_ = KDEHomeToConfigPath(kde3_path);
[email protected]92d2dc82010-04-08 17:49:59902 }
903 }
[email protected]d7395e732009-08-28 23:13:43904 }
[email protected]d7395e732009-08-28 23:13:43905 }
906
[email protected]573c0502011-05-17 22:19:50907 virtual ~SettingGetterImplKDE() {
[email protected]d7395e732009-08-28 23:13:43908 // inotify_fd_ should have been closed before now, from
909 // Delegate::OnDestroy(), while running on the file thread. However
910 // on exiting the process, it may happen that Delegate::OnDestroy()
911 // task is left pending on the file loop after the loop was quit,
912 // and pending tasks may then be deleted without being run.
913 // Here in the KDE version, we can safely close the file descriptor
914 // anyway. (Not that it really matters; the process is exiting.)
915 if (inotify_fd_ >= 0)
[email protected]d3066142011-05-10 02:36:20916 ShutDown();
[email protected]d7395e732009-08-28 23:13:43917 DCHECK(inotify_fd_ < 0);
918 }
919
[email protected]76722472012-05-24 08:26:46920 virtual bool Init(base::SingleThreadTaskRunner* glib_thread_task_runner,
[email protected]2da659e2013-05-23 20:51:34921 base::MessageLoopForIO* file_loop) OVERRIDE {
[email protected]9a8c4022011-01-25 14:25:33922 // This has to be called on the UI thread (http://crbug.com/69057).
923 base::ThreadRestrictions::ScopedAllowIO allow_io;
[email protected]d7395e732009-08-28 23:13:43924 DCHECK(inotify_fd_ < 0);
925 inotify_fd_ = inotify_init();
926 if (inotify_fd_ < 0) {
[email protected]57b765672009-10-13 18:27:40927 PLOG(ERROR) << "inotify_init failed";
[email protected]d7395e732009-08-28 23:13:43928 return false;
929 }
930 int flags = fcntl(inotify_fd_, F_GETFL);
931 if (fcntl(inotify_fd_, F_SETFL, flags | O_NONBLOCK) < 0) {
[email protected]57b765672009-10-13 18:27:40932 PLOG(ERROR) << "fcntl failed";
[email protected]d7395e732009-08-28 23:13:43933 close(inotify_fd_);
934 inotify_fd_ = -1;
935 return false;
936 }
937 file_loop_ = file_loop;
938 // The initial read is done on the current thread, not |file_loop_|,
[email protected]d3066142011-05-10 02:36:20939 // since we will need to have it for SetUpAndFetchInitialConfig().
[email protected]d7395e732009-08-28 23:13:43940 UpdateCachedSettings();
941 return true;
942 }
943
[email protected]749bf5c2012-09-17 03:15:21944 virtual void ShutDown() OVERRIDE {
[email protected]d7395e732009-08-28 23:13:43945 if (inotify_fd_ >= 0) {
946 ResetCachedSettings();
947 inotify_watcher_.StopWatchingFileDescriptor();
948 close(inotify_fd_);
949 inotify_fd_ = -1;
950 }
951 }
952
[email protected]749bf5c2012-09-17 03:15:21953 virtual bool SetUpNotifications(
954 ProxyConfigServiceLinux::Delegate* delegate) OVERRIDE {
[email protected]d7395e732009-08-28 23:13:43955 DCHECK(inotify_fd_ >= 0);
[email protected]2da659e2013-05-23 20:51:34956 DCHECK(base::MessageLoop::current() == file_loop_);
[email protected]d7395e732009-08-28 23:13:43957 // We can't just watch the kioslaverc file directly, since KDE will write
958 // a new copy of it and then rename it whenever settings are changed and
959 // inotify watches inodes (so we'll be watching the old deleted file after
960 // the first change, and it will never change again). So, we watch the
961 // directory instead. We then act only on changes to the kioslaverc entry.
962 if (inotify_add_watch(inotify_fd_, kde_config_dir_.value().c_str(),
963 IN_MODIFY | IN_MOVED_TO) < 0)
964 return false;
965 notify_delegate_ = delegate;
[email protected]2da659e2013-05-23 20:51:34966 if (!file_loop_->WatchFileDescriptor(inotify_fd_,
967 true,
968 base::MessageLoopForIO::WATCH_READ,
969 &inotify_watcher_,
970 this))
[email protected]d3066142011-05-10 02:36:20971 return false;
972 // Simulate a change to avoid possibly losing updates before this point.
973 OnChangeNotification();
974 return true;
[email protected]d7395e732009-08-28 23:13:43975 }
976
[email protected]76722472012-05-24 08:26:46977 virtual base::SingleThreadTaskRunner* GetNotificationTaskRunner() OVERRIDE {
[email protected]198b5902013-06-27 10:36:11978 return file_loop_ ? file_loop_->message_loop_proxy().get() : NULL;
[email protected]d7395e732009-08-28 23:13:43979 }
980
[email protected]b160df32012-02-06 20:39:41981 // Implement base::MessagePumpLibevent::Watcher.
[email protected]749bf5c2012-09-17 03:15:21982 virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE {
[email protected]d2e6d592012-02-03 21:49:04983 DCHECK_EQ(fd, inotify_fd_);
[email protected]2da659e2013-05-23 20:51:34984 DCHECK(base::MessageLoop::current() == file_loop_);
[email protected]d7395e732009-08-28 23:13:43985 OnChangeNotification();
986 }
[email protected]749bf5c2012-09-17 03:15:21987 virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE {
[email protected]d7395e732009-08-28 23:13:43988 NOTREACHED();
989 }
990
[email protected]db8ff912012-06-12 23:32:51991 virtual ProxyConfigSource GetConfigSource() OVERRIDE {
992 return PROXY_CONFIG_SOURCE_KDE;
[email protected]d7395e732009-08-28 23:13:43993 }
994
[email protected]c4c1b482011-07-22 17:24:26995 virtual bool GetString(StringSetting key, std::string* result) OVERRIDE {
[email protected]d7395e732009-08-28 23:13:43996 string_map_type::iterator it = string_table_.find(key);
997 if (it == string_table_.end())
998 return false;
999 *result = it->second;
1000 return true;
1001 }
[email protected]c4c1b482011-07-22 17:24:261002 virtual bool GetBool(BoolSetting key, bool* result) OVERRIDE {
[email protected]d7395e732009-08-28 23:13:431003 // We don't ever have any booleans.
1004 return false;
1005 }
[email protected]c4c1b482011-07-22 17:24:261006 virtual bool GetInt(IntSetting key, int* result) OVERRIDE {
[email protected]d7395e732009-08-28 23:13:431007 // We don't ever have any integers. (See AddProxy() below about ports.)
1008 return false;
1009 }
[email protected]6b5fe742011-05-20 21:46:481010 virtual bool GetStringList(StringListSetting key,
[email protected]c4c1b482011-07-22 17:24:261011 std::vector<std::string>* result) OVERRIDE {
[email protected]d7395e732009-08-28 23:13:431012 strings_map_type::iterator it = strings_table_.find(key);
1013 if (it == strings_table_.end())
1014 return false;
1015 *result = it->second;
1016 return true;
1017 }
1018
[email protected]c4c1b482011-07-22 17:24:261019 virtual bool BypassListIsReversed() OVERRIDE {
[email protected]a48bf4a2010-06-14 18:24:531020 return reversed_bypass_list_;
1021 }
1022
[email protected]c4c1b482011-07-22 17:24:261023 virtual bool MatchHostsUsingSuffixMatching() OVERRIDE {
[email protected]1a597192010-07-09 16:58:381024 return true;
1025 }
1026
[email protected]d7395e732009-08-28 23:13:431027 private:
1028 void ResetCachedSettings() {
1029 string_table_.clear();
1030 strings_table_.clear();
1031 indirect_manual_ = false;
1032 auto_no_pac_ = false;
[email protected]a48bf4a2010-06-14 18:24:531033 reversed_bypass_list_ = false;
[email protected]d7395e732009-08-28 23:13:431034 }
1035
[email protected]6cdfd7f2013-02-08 20:40:151036 base::FilePath KDEHomeToConfigPath(const base::FilePath& kde_home) {
[email protected]92d2dc82010-04-08 17:49:591037 return kde_home.Append("share").Append("config");
1038 }
1039
[email protected]6b5fe742011-05-20 21:46:481040 void AddProxy(StringSetting host_key, const std::string& value) {
[email protected]d7395e732009-08-28 23:13:431041 if (value.empty() || value.substr(0, 3) == "//:")
1042 // No proxy.
1043 return;
[email protected]4b90c202012-04-24 23:27:551044 size_t space = value.find(' ');
1045 if (space != std::string::npos) {
1046 // Newer versions of KDE use a space rather than a colon to separate the
1047 // port number from the hostname. If we find this, we need to convert it.
1048 std::string fixed = value;
1049 fixed[space] = ':';
1050 string_table_[host_key] = fixed;
1051 } else {
1052 // We don't need to parse the port number out; GetProxyFromSettings()
1053 // would only append it right back again. So we just leave the port
1054 // number right in the host string.
1055 string_table_[host_key] = value;
1056 }
[email protected]d7395e732009-08-28 23:13:431057 }
1058
[email protected]6b5fe742011-05-20 21:46:481059 void AddHostList(StringListSetting key, const std::string& value) {
[email protected]f18fde22010-05-18 23:49:541060 std::vector<std::string> tokens;
[email protected]f4ebe772013-02-02 00:21:391061 base::StringTokenizer tk(value, ", ");
[email protected]f18fde22010-05-18 23:49:541062 while (tk.GetNext()) {
1063 std::string token = tk.token();
1064 if (!token.empty())
1065 tokens.push_back(token);
1066 }
1067 strings_table_[key] = tokens;
1068 }
1069
[email protected]9a3d8d42009-09-03 17:01:461070 void AddKDESetting(const std::string& key, const std::string& value) {
[email protected]d7395e732009-08-28 23:13:431071 if (key == "ProxyType") {
1072 const char* mode = "none";
1073 indirect_manual_ = false;
1074 auto_no_pac_ = false;
[email protected]e83326f2010-07-31 17:29:251075 int int_value;
1076 base::StringToInt(value, &int_value);
1077 switch (int_value) {
[email protected]d7395e732009-08-28 23:13:431078 case 0: // No proxy, or maybe kioslaverc syntax error.
1079 break;
1080 case 1: // Manual configuration.
1081 mode = "manual";
1082 break;
1083 case 2: // PAC URL.
1084 mode = "auto";
1085 break;
1086 case 3: // WPAD.
1087 mode = "auto";
1088 auto_no_pac_ = true;
1089 break;
1090 case 4: // Indirect manual via environment variables.
1091 mode = "manual";
1092 indirect_manual_ = true;
1093 break;
1094 }
[email protected]573c0502011-05-17 22:19:501095 string_table_[PROXY_MODE] = mode;
[email protected]d7395e732009-08-28 23:13:431096 } else if (key == "Proxy Config Script") {
[email protected]573c0502011-05-17 22:19:501097 string_table_[PROXY_AUTOCONF_URL] = value;
[email protected]d7395e732009-08-28 23:13:431098 } else if (key == "httpProxy") {
[email protected]573c0502011-05-17 22:19:501099 AddProxy(PROXY_HTTP_HOST, value);
[email protected]d7395e732009-08-28 23:13:431100 } else if (key == "httpsProxy") {
[email protected]573c0502011-05-17 22:19:501101 AddProxy(PROXY_HTTPS_HOST, value);
[email protected]d7395e732009-08-28 23:13:431102 } else if (key == "ftpProxy") {
[email protected]573c0502011-05-17 22:19:501103 AddProxy(PROXY_FTP_HOST, value);
[email protected]bfeb7232012-06-08 00:58:371104 } else if (key == "socksProxy") {
1105 // Older versions of KDE configure SOCKS in a weird way involving
1106 // LD_PRELOAD and a library that intercepts network calls to SOCKSify
1107 // them. We don't support it. KDE 4.8 added a proper SOCKS setting.
1108 AddProxy(PROXY_SOCKS_HOST, value);
[email protected]d7395e732009-08-28 23:13:431109 } else if (key == "ReversedException") {
1110 // We count "true" or any nonzero number as true, otherwise false.
1111 // Note that if the value is not actually numeric StringToInt()
1112 // will return 0, which we count as false.
[email protected]e83326f2010-07-31 17:29:251113 int int_value;
1114 base::StringToInt(value, &int_value);
1115 reversed_bypass_list_ = (value == "true" || int_value);
[email protected]d7395e732009-08-28 23:13:431116 } else if (key == "NoProxyFor") {
[email protected]573c0502011-05-17 22:19:501117 AddHostList(PROXY_IGNORE_HOSTS, value);
[email protected]d7395e732009-08-28 23:13:431118 } else if (key == "AuthMode") {
1119 // Check for authentication, just so we can warn.
[email protected]e83326f2010-07-31 17:29:251120 int mode;
1121 base::StringToInt(value, &mode);
[email protected]d7395e732009-08-28 23:13:431122 if (mode) {
1123 // ProxyConfig does not support authentication parameters, but
1124 // Chrome will prompt for the password later. So we ignore this.
1125 LOG(WARNING) <<
1126 "Proxy authentication parameters ignored, see bug 16709";
1127 }
1128 }
1129 }
1130
[email protected]6b5fe742011-05-20 21:46:481131 void ResolveIndirect(StringSetting key) {
[email protected]d7395e732009-08-28 23:13:431132 string_map_type::iterator it = string_table_.find(key);
1133 if (it != string_table_.end()) {
[email protected]f18fde22010-05-18 23:49:541134 std::string value;
[email protected]3ba7e082010-08-07 02:57:591135 if (env_var_getter_->GetVar(it->second.c_str(), &value))
[email protected]d7395e732009-08-28 23:13:431136 it->second = value;
[email protected]8425adc02010-04-18 17:45:311137 else
1138 string_table_.erase(it);
[email protected]d7395e732009-08-28 23:13:431139 }
1140 }
1141
[email protected]6b5fe742011-05-20 21:46:481142 void ResolveIndirectList(StringListSetting key) {
[email protected]f18fde22010-05-18 23:49:541143 strings_map_type::iterator it = strings_table_.find(key);
1144 if (it != strings_table_.end()) {
1145 std::string value;
1146 if (!it->second.empty() &&
[email protected]3ba7e082010-08-07 02:57:591147 env_var_getter_->GetVar(it->second[0].c_str(), &value))
[email protected]f18fde22010-05-18 23:49:541148 AddHostList(key, value);
1149 else
1150 strings_table_.erase(it);
1151 }
1152 }
1153
[email protected]d7395e732009-08-28 23:13:431154 // The settings in kioslaverc could occur in any order, but some affect
1155 // others. Rather than read the whole file in and then query them in an
1156 // order that allows us to handle that, we read the settings in whatever
1157 // order they occur and do any necessary tweaking after we finish.
1158 void ResolveModeEffects() {
1159 if (indirect_manual_) {
[email protected]573c0502011-05-17 22:19:501160 ResolveIndirect(PROXY_HTTP_HOST);
1161 ResolveIndirect(PROXY_HTTPS_HOST);
1162 ResolveIndirect(PROXY_FTP_HOST);
1163 ResolveIndirectList(PROXY_IGNORE_HOSTS);
[email protected]d7395e732009-08-28 23:13:431164 }
1165 if (auto_no_pac_) {
1166 // Remove the PAC URL; we're not supposed to use it.
[email protected]573c0502011-05-17 22:19:501167 string_table_.erase(PROXY_AUTOCONF_URL);
[email protected]d7395e732009-08-28 23:13:431168 }
[email protected]d7395e732009-08-28 23:13:431169 }
1170
1171 // Reads kioslaverc one line at a time and calls AddKDESetting() to add
1172 // each relevant name-value pair to the appropriate value table.
1173 void UpdateCachedSettings() {
[email protected]6cdfd7f2013-02-08 20:40:151174 base::FilePath kioslaverc = kde_config_dir_.Append("kioslaverc");
[email protected]b9b4a572014-03-17 23:11:121175 base::ScopedFILE input(base::OpenFile(kioslaverc, "r"));
[email protected]d7395e732009-08-28 23:13:431176 if (!input.get())
1177 return;
1178 ResetCachedSettings();
1179 bool in_proxy_settings = false;
1180 bool line_too_long = false;
[email protected]9a3d8d42009-09-03 17:01:461181 char line[BUFFER_SIZE];
1182 // fgets() will return NULL on EOF or error.
[email protected]d7395e732009-08-28 23:13:431183 while (fgets(line, sizeof(line), input.get())) {
1184 // fgets() guarantees the line will be properly terminated.
1185 size_t length = strlen(line);
1186 if (!length)
1187 continue;
1188 // This should be true even with CRLF endings.
1189 if (line[length - 1] != '\n') {
1190 line_too_long = true;
1191 continue;
1192 }
1193 if (line_too_long) {
1194 // The previous line had no line ending, but this done does. This is
1195 // the end of the line that was too long, so warn here and skip it.
1196 LOG(WARNING) << "skipped very long line in " << kioslaverc.value();
1197 line_too_long = false;
1198 continue;
1199 }
1200 // Remove the LF at the end, and the CR if there is one.
1201 line[--length] = '\0';
1202 if (length && line[length - 1] == '\r')
1203 line[--length] = '\0';
1204 // Now parse the line.
1205 if (line[0] == '[') {
1206 // Switching sections. All we care about is whether this is
1207 // the (a?) proxy settings section, for both KDE3 and KDE4.
1208 in_proxy_settings = !strncmp(line, "[Proxy Settings]", 16);
1209 } else if (in_proxy_settings) {
1210 // A regular line, in the (a?) proxy settings section.
[email protected]9a3d8d42009-09-03 17:01:461211 char* split = strchr(line, '=');
1212 // Skip this line if it does not contain an = sign.
1213 if (!split)
[email protected]d7395e732009-08-28 23:13:431214 continue;
[email protected]9a3d8d42009-09-03 17:01:461215 // Split the line on the = and advance |split|.
1216 *(split++) = 0;
1217 std::string key = line;
1218 std::string value = split;
[email protected]8af69c6c2014-03-03 19:05:311219 base::TrimWhitespaceASCII(key, base::TRIM_ALL, &key);
1220 base::TrimWhitespaceASCII(value, base::TRIM_ALL, &value);
[email protected]9a3d8d42009-09-03 17:01:461221 // Skip this line if the key name is empty.
1222 if (key.empty())
[email protected]d7395e732009-08-28 23:13:431223 continue;
1224 // Is the value name localized?
[email protected]9a3d8d42009-09-03 17:01:461225 if (key[key.length() - 1] == ']') {
1226 // Find the matching bracket.
1227 length = key.rfind('[');
1228 // Skip this line if the localization indicator is malformed.
1229 if (length == std::string::npos)
[email protected]d7395e732009-08-28 23:13:431230 continue;
1231 // Trim the localization indicator off.
[email protected]9a3d8d42009-09-03 17:01:461232 key.resize(length);
1233 // Remove any resulting trailing whitespace.
[email protected]8af69c6c2014-03-03 19:05:311234 base::TrimWhitespaceASCII(key, base::TRIM_TRAILING, &key);
[email protected]9a3d8d42009-09-03 17:01:461235 // Skip this line if the key name is now empty.
1236 if (key.empty())
1237 continue;
[email protected]d7395e732009-08-28 23:13:431238 }
[email protected]d7395e732009-08-28 23:13:431239 // Now fill in the tables.
[email protected]9a3d8d42009-09-03 17:01:461240 AddKDESetting(key, value);
[email protected]d7395e732009-08-28 23:13:431241 }
1242 }
1243 if (ferror(input.get()))
1244 LOG(ERROR) << "error reading " << kioslaverc.value();
1245 ResolveModeEffects();
1246 }
1247
1248 // This is the callback from the debounce timer.
1249 void OnDebouncedNotification() {
[email protected]2da659e2013-05-23 20:51:341250 DCHECK(base::MessageLoop::current() == file_loop_);
[email protected]b30a3f52010-10-16 01:05:461251 VLOG(1) << "inotify change notification for kioslaverc";
[email protected]d7395e732009-08-28 23:13:431252 UpdateCachedSettings();
[email protected]961ac942011-04-28 18:18:141253 CHECK(notify_delegate_);
[email protected]d7395e732009-08-28 23:13:431254 // Forward to a method on the proxy config service delegate object.
1255 notify_delegate_->OnCheckProxyConfigSettings();
1256 }
1257
1258 // Called by OnFileCanReadWithoutBlocking() on the file thread. Reads
1259 // from the inotify file descriptor and starts up a debounce timer if
1260 // an event for kioslaverc is seen.
1261 void OnChangeNotification() {
[email protected]d2e6d592012-02-03 21:49:041262 DCHECK_GE(inotify_fd_, 0);
[email protected]2da659e2013-05-23 20:51:341263 DCHECK(base::MessageLoop::current() == file_loop_);
[email protected]d7395e732009-08-28 23:13:431264 char event_buf[(sizeof(inotify_event) + NAME_MAX + 1) * 4];
1265 bool kioslaverc_touched = false;
1266 ssize_t r;
1267 while ((r = read(inotify_fd_, event_buf, sizeof(event_buf))) > 0) {
1268 // inotify returns variable-length structures, which is why we have
1269 // this strange-looking loop instead of iterating through an array.
1270 char* event_ptr = event_buf;
1271 while (event_ptr < event_buf + r) {
1272 inotify_event* event = reinterpret_cast<inotify_event*>(event_ptr);
1273 // The kernel always feeds us whole events.
[email protected]b1f031dd2010-03-02 23:19:331274 CHECK_LE(event_ptr + sizeof(inotify_event), event_buf + r);
1275 CHECK_LE(event->name + event->len, event_buf + r);
[email protected]d7395e732009-08-28 23:13:431276 if (!strcmp(event->name, "kioslaverc"))
1277 kioslaverc_touched = true;
1278 // Advance the pointer just past the end of the filename.
1279 event_ptr = event->name + event->len;
1280 }
1281 // We keep reading even if |kioslaverc_touched| is true to drain the
1282 // inotify event queue.
1283 }
1284 if (!r)
1285 // Instead of returning -1 and setting errno to EINVAL if there is not
1286 // enough buffer space, older kernels (< 2.6.21) return 0. Simulate the
1287 // new behavior (EINVAL) so we can reuse the code below.
1288 errno = EINVAL;
1289 if (errno != EAGAIN) {
[email protected]57b765672009-10-13 18:27:401290 PLOG(WARNING) << "error reading inotify file descriptor";
[email protected]d7395e732009-08-28 23:13:431291 if (errno == EINVAL) {
1292 // Our buffer is not large enough to read the next event. This should
1293 // not happen (because its size is calculated to always be sufficiently
1294 // large), but if it does we'd warn continuously since |inotify_fd_|
1295 // would be forever ready to read. Close it and stop watching instead.
1296 LOG(ERROR) << "inotify failure; no longer watching kioslaverc!";
1297 inotify_watcher_.StopWatchingFileDescriptor();
1298 close(inotify_fd_);
1299 inotify_fd_ = -1;
1300 }
1301 }
1302 if (kioslaverc_touched) {
1303 // We don't use Reset() because the timer may not yet be running.
1304 // (In that case Stop() is a no-op.)
1305 debounce_timer_.Stop();
[email protected]d323a172011-09-02 18:23:021306 debounce_timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(
[email protected]d7395e732009-08-28 23:13:431307 kDebounceTimeoutMilliseconds), this,
[email protected]573c0502011-05-17 22:19:501308 &SettingGetterImplKDE::OnDebouncedNotification);
[email protected]d7395e732009-08-28 23:13:431309 }
1310 }
1311
[email protected]6b5fe742011-05-20 21:46:481312 typedef std::map<StringSetting, std::string> string_map_type;
1313 typedef std::map<StringListSetting,
1314 std::vector<std::string> > strings_map_type;
[email protected]d7395e732009-08-28 23:13:431315
1316 int inotify_fd_;
1317 base::MessagePumpLibevent::FileDescriptorWatcher inotify_watcher_;
1318 ProxyConfigServiceLinux::Delegate* notify_delegate_;
[email protected]573c0502011-05-17 22:19:501319 base::OneShotTimer<SettingGetterImplKDE> debounce_timer_;
[email protected]6cdfd7f2013-02-08 20:40:151320 base::FilePath kde_config_dir_;
[email protected]d7395e732009-08-28 23:13:431321 bool indirect_manual_;
1322 bool auto_no_pac_;
[email protected]a48bf4a2010-06-14 18:24:531323 bool reversed_bypass_list_;
[email protected]f18fde22010-05-18 23:49:541324 // We don't own |env_var_getter_|. It's safe to hold a pointer to it, since
1325 // both it and us are owned by ProxyConfigServiceLinux::Delegate, and have the
1326 // same lifetime.
[email protected]76b90d312010-08-03 03:00:501327 base::Environment* env_var_getter_;
[email protected]d7395e732009-08-28 23:13:431328
1329 // We cache these settings whenever we re-read the kioslaverc file.
1330 string_map_type string_table_;
1331 strings_map_type strings_table_;
1332
1333 // Message loop of the file thread, for reading kioslaverc. If NULL,
1334 // just read it directly (for testing). We also handle inotify events
1335 // on this thread.
[email protected]2da659e2013-05-23 20:51:341336 base::MessageLoopForIO* file_loop_;
[email protected]d7395e732009-08-28 23:13:431337
[email protected]573c0502011-05-17 22:19:501338 DISALLOW_COPY_AND_ASSIGN(SettingGetterImplKDE);
[email protected]861c6c62009-04-20 16:50:561339};
1340
1341} // namespace
1342
[email protected]573c0502011-05-17 22:19:501343bool ProxyConfigServiceLinux::Delegate::GetProxyFromSettings(
[email protected]6b5fe742011-05-20 21:46:481344 SettingGetter::StringSetting host_key,
[email protected]573c0502011-05-17 22:19:501345 ProxyServer* result_server) {
[email protected]861c6c62009-04-20 16:50:561346 std::string host;
[email protected]573c0502011-05-17 22:19:501347 if (!setting_getter_->GetString(host_key, &host) || host.empty()) {
[email protected]861c6c62009-04-20 16:50:561348 // Unset or empty.
1349 return false;
1350 }
1351 // Check for an optional port.
[email protected]d7395e732009-08-28 23:13:431352 int port = 0;
[email protected]6b5fe742011-05-20 21:46:481353 SettingGetter::IntSetting port_key =
[email protected]573c0502011-05-17 22:19:501354 SettingGetter::HostSettingToPortSetting(host_key);
1355 setting_getter_->GetInt(port_key, &port);
[email protected]861c6c62009-04-20 16:50:561356 if (port != 0) {
1357 // If a port is set and non-zero:
[email protected]528c56d2010-07-30 19:28:441358 host += ":" + base::IntToString(port);
[email protected]861c6c62009-04-20 16:50:561359 }
[email protected]76960f3d2011-04-30 02:15:231360
[email protected]573c0502011-05-17 22:19:501361 // gconf settings do not appear to distinguish between SOCKS version. We
1362 // default to version 5. For more information on this policy decision, see:
[email protected]76960f3d2011-04-30 02:15:231363 // http://code.google.com/p/chromium/issues/detail?id=55912#c2
[email protected]573c0502011-05-17 22:19:501364 ProxyServer::Scheme scheme = (host_key == SettingGetter::PROXY_SOCKS_HOST) ?
1365 ProxyServer::SCHEME_SOCKS5 : ProxyServer::SCHEME_HTTP;
1366 host = FixupProxyHostScheme(scheme, host);
[email protected]87a102b2009-07-14 05:23:301367 ProxyServer proxy_server = ProxyServer::FromURI(host,
1368 ProxyServer::SCHEME_HTTP);
[email protected]861c6c62009-04-20 16:50:561369 if (proxy_server.is_valid()) {
1370 *result_server = proxy_server;
1371 return true;
1372 }
1373 return false;
1374}
1375
[email protected]573c0502011-05-17 22:19:501376bool ProxyConfigServiceLinux::Delegate::GetConfigFromSettings(
[email protected]3e44697f2009-05-22 14:37:391377 ProxyConfig* config) {
[email protected]861c6c62009-04-20 16:50:561378 std::string mode;
[email protected]573c0502011-05-17 22:19:501379 if (!setting_getter_->GetString(SettingGetter::PROXY_MODE, &mode)) {
[email protected]861c6c62009-04-20 16:50:561380 // We expect this to always be set, so if we don't see it then we
[email protected]573c0502011-05-17 22:19:501381 // probably have a gconf/gsettings problem, and so we don't have a valid
[email protected]861c6c62009-04-20 16:50:561382 // proxy config.
1383 return false;
1384 }
[email protected]3e44697f2009-05-22 14:37:391385 if (mode == "none") {
[email protected]861c6c62009-04-20 16:50:561386 // Specifically specifies no proxy.
1387 return true;
[email protected]3e44697f2009-05-22 14:37:391388 }
[email protected]861c6c62009-04-20 16:50:561389
[email protected]3e44697f2009-05-22 14:37:391390 if (mode == "auto") {
[email protected]aa3ac2cc2012-06-19 00:28:041391 // Automatic proxy config.
[email protected]861c6c62009-04-20 16:50:561392 std::string pac_url_str;
[email protected]573c0502011-05-17 22:19:501393 if (setting_getter_->GetString(SettingGetter::PROXY_AUTOCONF_URL,
1394 &pac_url_str)) {
[email protected]861c6c62009-04-20 16:50:561395 if (!pac_url_str.empty()) {
[email protected]aa3ac2cc2012-06-19 00:28:041396 // If the PAC URL is actually a file path, then put file:// in front.
1397 if (pac_url_str[0] == '/')
1398 pac_url_str = "file://" + pac_url_str;
[email protected]861c6c62009-04-20 16:50:561399 GURL pac_url(pac_url_str);
1400 if (!pac_url.is_valid())
1401 return false;
[email protected]ed4ed0f2010-02-24 00:20:481402 config->set_pac_url(pac_url);
[email protected]861c6c62009-04-20 16:50:561403 return true;
1404 }
1405 }
[email protected]ed4ed0f2010-02-24 00:20:481406 config->set_auto_detect(true);
[email protected]861c6c62009-04-20 16:50:561407 return true;
1408 }
1409
[email protected]3e44697f2009-05-22 14:37:391410 if (mode != "manual") {
[email protected]861c6c62009-04-20 16:50:561411 // Mode is unrecognized.
1412 return false;
1413 }
1414 bool use_http_proxy;
[email protected]573c0502011-05-17 22:19:501415 if (setting_getter_->GetBool(SettingGetter::PROXY_USE_HTTP_PROXY,
1416 &use_http_proxy)
[email protected]861c6c62009-04-20 16:50:561417 && !use_http_proxy) {
1418 // Another master switch for some reason. If set to false, then no
1419 // proxy. But we don't panic if the key doesn't exist.
1420 return true;
1421 }
1422
1423 bool same_proxy = false;
1424 // Indicates to use the http proxy for all protocols. This one may
[email protected]573c0502011-05-17 22:19:501425 // not exist (presumably on older versions); we assume false in that
[email protected]861c6c62009-04-20 16:50:561426 // case.
[email protected]573c0502011-05-17 22:19:501427 setting_getter_->GetBool(SettingGetter::PROXY_USE_SAME_PROXY,
1428 &same_proxy);
[email protected]861c6c62009-04-20 16:50:561429
[email protected]76960f3d2011-04-30 02:15:231430 ProxyServer proxy_for_http;
1431 ProxyServer proxy_for_https;
1432 ProxyServer proxy_for_ftp;
1433 ProxyServer socks_proxy; // (socks)
1434
1435 // This counts how many of the above ProxyServers were defined and valid.
1436 size_t num_proxies_specified = 0;
1437
1438 // Extract the per-scheme proxies. If we failed to parse it, or no proxy was
1439 // specified for the scheme, then the resulting ProxyServer will be invalid.
[email protected]573c0502011-05-17 22:19:501440 if (GetProxyFromSettings(SettingGetter::PROXY_HTTP_HOST, &proxy_for_http))
[email protected]76960f3d2011-04-30 02:15:231441 num_proxies_specified++;
[email protected]573c0502011-05-17 22:19:501442 if (GetProxyFromSettings(SettingGetter::PROXY_HTTPS_HOST, &proxy_for_https))
[email protected]76960f3d2011-04-30 02:15:231443 num_proxies_specified++;
[email protected]573c0502011-05-17 22:19:501444 if (GetProxyFromSettings(SettingGetter::PROXY_FTP_HOST, &proxy_for_ftp))
[email protected]76960f3d2011-04-30 02:15:231445 num_proxies_specified++;
[email protected]573c0502011-05-17 22:19:501446 if (GetProxyFromSettings(SettingGetter::PROXY_SOCKS_HOST, &socks_proxy))
[email protected]76960f3d2011-04-30 02:15:231447 num_proxies_specified++;
1448
1449 if (same_proxy) {
1450 if (proxy_for_http.is_valid()) {
1451 // Use the http proxy for all schemes.
[email protected]ed4ed0f2010-02-24 00:20:481452 config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
[email protected]2189e092013-03-16 18:02:021453 config->proxy_rules().single_proxies.SetSingleProxyServer(proxy_for_http);
[email protected]861c6c62009-04-20 16:50:561454 }
[email protected]76960f3d2011-04-30 02:15:231455 } else if (num_proxies_specified > 0) {
1456 if (socks_proxy.is_valid() && num_proxies_specified == 1) {
1457 // If the only proxy specified was for SOCKS, use it for all schemes.
1458 config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
[email protected]2189e092013-03-16 18:02:021459 config->proxy_rules().single_proxies.SetSingleProxyServer(socks_proxy);
[email protected]861c6c62009-04-20 16:50:561460 } else {
[email protected]2189e092013-03-16 18:02:021461 // Otherwise use the indicated proxies per-scheme.
[email protected]76960f3d2011-04-30 02:15:231462 config->proxy_rules().type =
1463 ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
[email protected]2189e092013-03-16 18:02:021464 config->proxy_rules().proxies_for_http.
1465 SetSingleProxyServer(proxy_for_http);
1466 config->proxy_rules().proxies_for_https.
1467 SetSingleProxyServer(proxy_for_https);
1468 config->proxy_rules().proxies_for_ftp.SetSingleProxyServer(proxy_for_ftp);
1469 config->proxy_rules().fallback_proxies.SetSingleProxyServer(socks_proxy);
[email protected]861c6c62009-04-20 16:50:561470 }
1471 }
1472
[email protected]ed4ed0f2010-02-24 00:20:481473 if (config->proxy_rules().empty()) {
[email protected]861c6c62009-04-20 16:50:561474 // Manual mode but we couldn't parse any rules.
1475 return false;
1476 }
1477
1478 // Check for authentication, just so we can warn.
[email protected]d7395e732009-08-28 23:13:431479 bool use_auth = false;
[email protected]573c0502011-05-17 22:19:501480 setting_getter_->GetBool(SettingGetter::PROXY_USE_AUTHENTICATION,
1481 &use_auth);
[email protected]62749f182009-07-15 13:16:541482 if (use_auth) {
1483 // ProxyConfig does not support authentication parameters, but
1484 // Chrome will prompt for the password later. So we ignore
1485 // /system/http_proxy/*auth* settings.
1486 LOG(WARNING) << "Proxy authentication parameters ignored, see bug 16709";
1487 }
[email protected]861c6c62009-04-20 16:50:561488
1489 // Now the bypass list.
[email protected]7541206c2010-02-19 20:24:061490 std::vector<std::string> ignore_hosts_list;
[email protected]ed4ed0f2010-02-24 00:20:481491 config->proxy_rules().bypass_rules.Clear();
[email protected]573c0502011-05-17 22:19:501492 if (setting_getter_->GetStringList(SettingGetter::PROXY_IGNORE_HOSTS,
1493 &ignore_hosts_list)) {
[email protected]a8185d02010-06-11 00:19:501494 std::vector<std::string>::const_iterator it(ignore_hosts_list.begin());
[email protected]1a597192010-07-09 16:58:381495 for (; it != ignore_hosts_list.end(); ++it) {
[email protected]573c0502011-05-17 22:19:501496 if (setting_getter_->MatchHostsUsingSuffixMatching()) {
[email protected]1a597192010-07-09 16:58:381497 config->proxy_rules().bypass_rules.
1498 AddRuleFromStringUsingSuffixMatching(*it);
1499 } else {
1500 config->proxy_rules().bypass_rules.AddRuleFromString(*it);
1501 }
1502 }
[email protected]a8185d02010-06-11 00:19:501503 }
[email protected]861c6c62009-04-20 16:50:561504 // Note that there are no settings with semantics corresponding to
[email protected]1a597192010-07-09 16:58:381505 // bypass of local names in GNOME. In KDE, "<local>" is supported
1506 // as a hostname rule.
[email protected]861c6c62009-04-20 16:50:561507
[email protected]a48bf4a2010-06-14 18:24:531508 // KDE allows one to reverse the bypass rules.
[email protected]573c0502011-05-17 22:19:501509 config->proxy_rules().reverse_bypass =
1510 setting_getter_->BypassListIsReversed();
[email protected]a48bf4a2010-06-14 18:24:531511
[email protected]861c6c62009-04-20 16:50:561512 return true;
1513}
1514
[email protected]76b90d312010-08-03 03:00:501515ProxyConfigServiceLinux::Delegate::Delegate(base::Environment* env_var_getter)
[email protected]76722472012-05-24 08:26:461516 : env_var_getter_(env_var_getter) {
[email protected]573c0502011-05-17 22:19:501517 // Figure out which SettingGetterImpl to use, if any.
[email protected]6b0349ef2010-10-16 04:56:061518 switch (base::nix::GetDesktopEnvironment(env_var_getter)) {
1519 case base::nix::DESKTOP_ENVIRONMENT_GNOME:
[email protected]9e6c9bde2012-07-17 23:40:171520 case base::nix::DESKTOP_ENVIRONMENT_UNITY:
[email protected]8c20e3d2011-05-19 21:03:571521#if defined(USE_GIO)
1522 {
1523 scoped_ptr<SettingGetterImplGSettings> gs_getter(
1524 new SettingGetterImplGSettings());
1525 // We have to load symbols and check the GNOME version in use to decide
1526 // if we should use the gsettings getter. See LoadAndCheckVersion().
1527 if (gs_getter->LoadAndCheckVersion(env_var_getter))
1528 setting_getter_.reset(gs_getter.release());
1529 }
1530#endif
[email protected]6de53d42010-11-09 07:33:191531#if defined(USE_GCONF)
[email protected]8c20e3d2011-05-19 21:03:571532 // Fall back on gconf if gsettings is unavailable or incorrect.
1533 if (!setting_getter_.get())
1534 setting_getter_.reset(new SettingGetterImplGConf());
[email protected]6de53d42010-11-09 07:33:191535#endif
[email protected]d7395e732009-08-28 23:13:431536 break;
[email protected]6b0349ef2010-10-16 04:56:061537 case base::nix::DESKTOP_ENVIRONMENT_KDE3:
1538 case base::nix::DESKTOP_ENVIRONMENT_KDE4:
[email protected]573c0502011-05-17 22:19:501539 setting_getter_.reset(new SettingGetterImplKDE(env_var_getter));
[email protected]d7395e732009-08-28 23:13:431540 break;
[email protected]6b0349ef2010-10-16 04:56:061541 case base::nix::DESKTOP_ENVIRONMENT_XFCE:
1542 case base::nix::DESKTOP_ENVIRONMENT_OTHER:
[email protected]d7395e732009-08-28 23:13:431543 break;
1544 }
1545}
1546
[email protected]573c0502011-05-17 22:19:501547ProxyConfigServiceLinux::Delegate::Delegate(
1548 base::Environment* env_var_getter, SettingGetter* setting_getter)
[email protected]76722472012-05-24 08:26:461549 : env_var_getter_(env_var_getter), setting_getter_(setting_getter) {
[email protected]861c6c62009-04-20 16:50:561550}
1551
[email protected]d3066142011-05-10 02:36:201552void ProxyConfigServiceLinux::Delegate::SetUpAndFetchInitialConfig(
[email protected]76722472012-05-24 08:26:461553 base::SingleThreadTaskRunner* glib_thread_task_runner,
1554 base::SingleThreadTaskRunner* io_thread_task_runner,
[email protected]2da659e2013-05-23 20:51:341555 base::MessageLoopForIO* file_loop) {
[email protected]3e44697f2009-05-22 14:37:391556 // We should be running on the default glib main loop thread right
1557 // now. gconf can only be accessed from this thread.
[email protected]76722472012-05-24 08:26:461558 DCHECK(glib_thread_task_runner->BelongsToCurrentThread());
1559 glib_thread_task_runner_ = glib_thread_task_runner;
1560 io_thread_task_runner_ = io_thread_task_runner;
[email protected]3e44697f2009-05-22 14:37:391561
[email protected]76722472012-05-24 08:26:461562 // If we are passed a NULL |io_thread_task_runner| or |file_loop|,
1563 // then we don't set up proxy setting change notifications. This
1564 // should not be the usual case but is intended to simplify test
1565 // setups.
[email protected]90499482013-06-01 00:39:501566 if (!io_thread_task_runner_.get() || !file_loop)
[email protected]b30a3f52010-10-16 01:05:461567 VLOG(1) << "Monitoring of proxy setting changes is disabled";
[email protected]3e44697f2009-05-22 14:37:391568
1569 // Fetch and cache the current proxy config. The config is left in
[email protected]119655002010-07-23 06:02:401570 // cached_config_, where GetLatestProxyConfig() running on the IO thread
[email protected]3e44697f2009-05-22 14:37:391571 // will expect to find it. This is safe to do because we return
1572 // before this ProxyConfigServiceLinux is passed on to
1573 // the ProxyService.
[email protected]d6cb85b2009-07-23 22:10:531574
1575 // Note: It would be nice to prioritize environment variables
[email protected]92d2dc82010-04-08 17:49:591576 // and only fall back to gconf if env vars were unset. But
[email protected]d6cb85b2009-07-23 22:10:531577 // gnome-terminal "helpfully" sets http_proxy and no_proxy, and it
1578 // does so even if the proxy mode is set to auto, which would
1579 // mislead us.
1580
[email protected]3e44697f2009-05-22 14:37:391581 bool got_config = false;
[email protected]573c0502011-05-17 22:19:501582 if (setting_getter_.get() &&
[email protected]76722472012-05-24 08:26:461583 setting_getter_->Init(glib_thread_task_runner, file_loop) &&
[email protected]573c0502011-05-17 22:19:501584 GetConfigFromSettings(&cached_config_)) {
[email protected]d3066142011-05-10 02:36:201585 cached_config_.set_id(1); // Mark it as valid.
[email protected]db8ff912012-06-12 23:32:511586 cached_config_.set_source(setting_getter_->GetConfigSource());
[email protected]d3066142011-05-10 02:36:201587 VLOG(1) << "Obtained proxy settings from "
[email protected]db8ff912012-06-12 23:32:511588 << ProxyConfigSourceToString(cached_config_.source());
[email protected]d3066142011-05-10 02:36:201589
1590 // If gconf proxy mode is "none", meaning direct, then we take
1591 // that to be a valid config and will not check environment
1592 // variables. The alternative would have been to look for a proxy
1593 // whereever we can find one.
1594 got_config = true;
1595
1596 // Keep a copy of the config for use from this thread for
1597 // comparison with updated settings when we get notifications.
1598 reference_config_ = cached_config_;
1599 reference_config_.set_id(1); // Mark it as valid.
1600
1601 // We only set up notifications if we have IO and file loops available.
1602 // We do this after getting the initial configuration so that we don't have
1603 // to worry about cancelling it if the initial fetch above fails. Note that
1604 // setting up notifications has the side effect of simulating a change, so
1605 // that we won't lose any updates that may have happened after the initial
1606 // fetch and before setting up notifications. We'll detect the common case
1607 // of no changes in OnCheckProxyConfigSettings() (or sooner) and ignore it.
[email protected]76722472012-05-24 08:26:461608 if (io_thread_task_runner && file_loop) {
1609 scoped_refptr<base::SingleThreadTaskRunner> required_loop =
1610 setting_getter_->GetNotificationTaskRunner();
[email protected]90499482013-06-01 00:39:501611 if (!required_loop.get() || required_loop->BelongsToCurrentThread()) {
[email protected]d3066142011-05-10 02:36:201612 // In this case we are already on an acceptable thread.
1613 SetUpNotifications();
[email protected]d7395e732009-08-28 23:13:431614 } else {
[email protected]d3066142011-05-10 02:36:201615 // Post a task to set up notifications. We don't wait for success.
[email protected]6af889c2011-10-06 23:11:411616 required_loop->PostTask(FROM_HERE, base::Bind(
1617 &ProxyConfigServiceLinux::Delegate::SetUpNotifications, this));
[email protected]d6cb85b2009-07-23 22:10:531618 }
[email protected]d7395e732009-08-28 23:13:431619 }
[email protected]861c6c62009-04-20 16:50:561620 }
[email protected]d6cb85b2009-07-23 22:10:531621
[email protected]3e44697f2009-05-22 14:37:391622 if (!got_config) {
[email protected]d6cb85b2009-07-23 22:10:531623 // We fall back on environment variables.
[email protected]3e44697f2009-05-22 14:37:391624 //
[email protected]d3066142011-05-10 02:36:201625 // Consulting environment variables doesn't need to be done from the
1626 // default glib main loop, but it's a tiny enough amount of work.
[email protected]3e44697f2009-05-22 14:37:391627 if (GetConfigFromEnv(&cached_config_)) {
[email protected]db8ff912012-06-12 23:32:511628 cached_config_.set_source(PROXY_CONFIG_SOURCE_ENV);
[email protected]d3066142011-05-10 02:36:201629 cached_config_.set_id(1); // Mark it as valid.
[email protected]b30a3f52010-10-16 01:05:461630 VLOG(1) << "Obtained proxy settings from environment variables";
[email protected]3e44697f2009-05-22 14:37:391631 }
[email protected]861c6c62009-04-20 16:50:561632 }
[email protected]3e44697f2009-05-22 14:37:391633}
1634
[email protected]573c0502011-05-17 22:19:501635// Depending on the SettingGetter in use, this method will be called
[email protected]d3066142011-05-10 02:36:201636// on either the UI thread (GConf) or the file thread (KDE).
1637void ProxyConfigServiceLinux::Delegate::SetUpNotifications() {
[email protected]76722472012-05-24 08:26:461638 scoped_refptr<base::SingleThreadTaskRunner> required_loop =
1639 setting_getter_->GetNotificationTaskRunner();
[email protected]90499482013-06-01 00:39:501640 DCHECK(!required_loop.get() || required_loop->BelongsToCurrentThread());
[email protected]573c0502011-05-17 22:19:501641 if (!setting_getter_->SetUpNotifications(this))
[email protected]d3066142011-05-10 02:36:201642 LOG(ERROR) << "Unable to set up proxy configuration change notifications";
1643}
1644
[email protected]119655002010-07-23 06:02:401645void ProxyConfigServiceLinux::Delegate::AddObserver(Observer* observer) {
1646 observers_.AddObserver(observer);
1647}
1648
1649void ProxyConfigServiceLinux::Delegate::RemoveObserver(Observer* observer) {
1650 observers_.RemoveObserver(observer);
1651}
1652
[email protected]3a29593d2011-04-11 10:07:521653ProxyConfigService::ConfigAvailability
1654 ProxyConfigServiceLinux::Delegate::GetLatestProxyConfig(
1655 ProxyConfig* config) {
[email protected]3e44697f2009-05-22 14:37:391656 // This is called from the IO thread.
[email protected]90499482013-06-01 00:39:501657 DCHECK(!io_thread_task_runner_.get() ||
[email protected]76722472012-05-24 08:26:461658 io_thread_task_runner_->BelongsToCurrentThread());
[email protected]3e44697f2009-05-22 14:37:391659
1660 // Simply return the last proxy configuration that glib_default_loop
1661 // notified us of.
[email protected]db8ff912012-06-12 23:32:511662 if (cached_config_.is_valid()) {
1663 *config = cached_config_;
1664 } else {
1665 *config = ProxyConfig::CreateDirect();
1666 config->set_source(PROXY_CONFIG_SOURCE_SYSTEM_FAILED);
1667 }
[email protected]119655002010-07-23 06:02:401668
[email protected]3a29593d2011-04-11 10:07:521669 // We return CONFIG_VALID to indicate that *config was filled in. It is always
[email protected]119655002010-07-23 06:02:401670 // going to be available since we initialized eagerly on the UI thread.
1671 // TODO(eroman): do lazy initialization instead, so we no longer need
1672 // to construct ProxyConfigServiceLinux on the UI thread.
1673 // In which case, we may return false here.
[email protected]3a29593d2011-04-11 10:07:521674 return CONFIG_VALID;
[email protected]3e44697f2009-05-22 14:37:391675}
1676
[email protected]573c0502011-05-17 22:19:501677// Depending on the SettingGetter in use, this method will be called
[email protected]d7395e732009-08-28 23:13:431678// on either the UI thread (GConf) or the file thread (KDE).
[email protected]3e44697f2009-05-22 14:37:391679void ProxyConfigServiceLinux::Delegate::OnCheckProxyConfigSettings() {
[email protected]76722472012-05-24 08:26:461680 scoped_refptr<base::SingleThreadTaskRunner> required_loop =
1681 setting_getter_->GetNotificationTaskRunner();
[email protected]90499482013-06-01 00:39:501682 DCHECK(!required_loop.get() || required_loop->BelongsToCurrentThread());
[email protected]3e44697f2009-05-22 14:37:391683 ProxyConfig new_config;
[email protected]573c0502011-05-17 22:19:501684 bool valid = GetConfigFromSettings(&new_config);
[email protected]3e44697f2009-05-22 14:37:391685 if (valid)
1686 new_config.set_id(1); // mark it as valid
1687
[email protected]119655002010-07-23 06:02:401688 // See if it is different from what we had before.
[email protected]3e44697f2009-05-22 14:37:391689 if (new_config.is_valid() != reference_config_.is_valid() ||
1690 !new_config.Equals(reference_config_)) {
[email protected]76722472012-05-24 08:26:461691 // Post a task to the IO thread with the new configuration, so it can
[email protected]3e44697f2009-05-22 14:37:391692 // update |cached_config_|.
[email protected]76722472012-05-24 08:26:461693 io_thread_task_runner_->PostTask(FROM_HERE, base::Bind(
[email protected]6af889c2011-10-06 23:11:411694 &ProxyConfigServiceLinux::Delegate::SetNewProxyConfig,
1695 this, new_config));
[email protected]d1f9d472009-08-13 19:59:301696 // Update the thread-private copy in |reference_config_| as well.
1697 reference_config_ = new_config;
[email protected]d3066142011-05-10 02:36:201698 } else {
1699 VLOG(1) << "Detected no-op change to proxy settings. Doing nothing.";
[email protected]3e44697f2009-05-22 14:37:391700 }
1701}
1702
1703void ProxyConfigServiceLinux::Delegate::SetNewProxyConfig(
1704 const ProxyConfig& new_config) {
[email protected]76722472012-05-24 08:26:461705 DCHECK(io_thread_task_runner_->BelongsToCurrentThread());
[email protected]b30a3f52010-10-16 01:05:461706 VLOG(1) << "Proxy configuration changed";
[email protected]3e44697f2009-05-22 14:37:391707 cached_config_ = new_config;
[email protected]3a29593d2011-04-11 10:07:521708 FOR_EACH_OBSERVER(
1709 Observer, observers_,
1710 OnProxyConfigChanged(new_config, ProxyConfigService::CONFIG_VALID));
[email protected]3e44697f2009-05-22 14:37:391711}
1712
1713void ProxyConfigServiceLinux::Delegate::PostDestroyTask() {
[email protected]573c0502011-05-17 22:19:501714 if (!setting_getter_.get())
[email protected]d7395e732009-08-28 23:13:431715 return;
[email protected]76722472012-05-24 08:26:461716 scoped_refptr<base::SingleThreadTaskRunner> shutdown_loop =
1717 setting_getter_->GetNotificationTaskRunner();
[email protected]90499482013-06-01 00:39:501718 if (!shutdown_loop.get() || shutdown_loop->BelongsToCurrentThread()) {
[email protected]3e44697f2009-05-22 14:37:391719 // Already on the right thread, call directly.
1720 // This is the case for the unittests.
1721 OnDestroy();
1722 } else {
[email protected]d7395e732009-08-28 23:13:431723 // Post to shutdown thread. Note that on browser shutdown, we may quit
1724 // this MessageLoop and exit the program before ever running this.
[email protected]6af889c2011-10-06 23:11:411725 shutdown_loop->PostTask(FROM_HERE, base::Bind(
1726 &ProxyConfigServiceLinux::Delegate::OnDestroy, this));
[email protected]3e44697f2009-05-22 14:37:391727 }
1728}
1729void ProxyConfigServiceLinux::Delegate::OnDestroy() {
[email protected]76722472012-05-24 08:26:461730 scoped_refptr<base::SingleThreadTaskRunner> shutdown_loop =
1731 setting_getter_->GetNotificationTaskRunner();
[email protected]90499482013-06-01 00:39:501732 DCHECK(!shutdown_loop.get() || shutdown_loop->BelongsToCurrentThread());
[email protected]573c0502011-05-17 22:19:501733 setting_getter_->ShutDown();
[email protected]3e44697f2009-05-22 14:37:391734}
1735
1736ProxyConfigServiceLinux::ProxyConfigServiceLinux()
[email protected]76b90d312010-08-03 03:00:501737 : delegate_(new Delegate(base::Environment::Create())) {
[email protected]3e44697f2009-05-22 14:37:391738}
1739
[email protected]8e1845e12010-09-15 19:22:241740ProxyConfigServiceLinux::~ProxyConfigServiceLinux() {
1741 delegate_->PostDestroyTask();
1742}
1743
[email protected]3e44697f2009-05-22 14:37:391744ProxyConfigServiceLinux::ProxyConfigServiceLinux(
[email protected]76b90d312010-08-03 03:00:501745 base::Environment* env_var_getter)
[email protected]9a3d8d42009-09-03 17:01:461746 : delegate_(new Delegate(env_var_getter)) {
1747}
1748
1749ProxyConfigServiceLinux::ProxyConfigServiceLinux(
[email protected]573c0502011-05-17 22:19:501750 base::Environment* env_var_getter, SettingGetter* setting_getter)
1751 : delegate_(new Delegate(env_var_getter, setting_getter)) {
[email protected]861c6c62009-04-20 16:50:561752}
1753
[email protected]e4be2dd2010-12-14 00:44:391754void ProxyConfigServiceLinux::AddObserver(Observer* observer) {
1755 delegate_->AddObserver(observer);
1756}
1757
1758void ProxyConfigServiceLinux::RemoveObserver(Observer* observer) {
1759 delegate_->RemoveObserver(observer);
1760}
1761
[email protected]3a29593d2011-04-11 10:07:521762ProxyConfigService::ConfigAvailability
1763 ProxyConfigServiceLinux::GetLatestProxyConfig(ProxyConfig* config) {
[email protected]e4be2dd2010-12-14 00:44:391764 return delegate_->GetLatestProxyConfig(config);
1765}
1766
[email protected]861c6c62009-04-20 16:50:561767} // namespace net