blob: 2e19589447e247e15e4a02845e35299bf5dc838b [file] [log] [blame]
[email protected]3a29593d2011-04-11 10:07:521// Copyright (c) 2011 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)
12#if defined(USE_GIO)
13#include <gio/gio.h>
14#if defined(DLOPEN_GSETTINGS)
15#include <dlfcn.h>
16#endif // defined(DLOPEN_GSETTINGS)
17#endif // defined(USE_GIO)
[email protected]d7395e732009-08-28 23:13:4318#include <limits.h>
19#include <stdio.h>
[email protected]861c6c62009-04-20 16:50:5620#include <stdlib.h>
[email protected]d7395e732009-08-28 23:13:4321#include <sys/inotify.h>
22#include <unistd.h>
[email protected]861c6c62009-04-20 16:50:5623
[email protected]9bc8cff2010-04-03 01:05:3924#include <map>
25
[email protected]c4c1b482011-07-22 17:24:2626#include "base/compiler_specific.h"
[email protected]76b90d312010-08-03 03:00:5027#include "base/environment.h"
[email protected]d7395e732009-08-28 23:13:4328#include "base/file_path.h"
29#include "base/file_util.h"
[email protected]861c6c62009-04-20 16:50:5630#include "base/logging.h"
[email protected]d7395e732009-08-28 23:13:4331#include "base/message_loop.h"
[email protected]3a29593d2011-04-11 10:07:5232#include "base/nix/xdg_util.h"
[email protected]528c56d2010-07-30 19:28:4433#include "base/string_number_conversions.h"
[email protected]861c6c62009-04-20 16:50:5634#include "base/string_tokenizer.h"
35#include "base/string_util.h"
[email protected]3e44697f2009-05-22 14:37:3936#include "base/task.h"
[email protected]9a8c4022011-01-25 14:25:3337#include "base/threading/thread_restrictions.h"
[email protected]d7395e732009-08-28 23:13:4338#include "base/timer.h"
[email protected]861c6c62009-04-20 16:50:5639#include "googleurl/src/url_canon.h"
40#include "net/base/net_errors.h"
41#include "net/http/http_util.h"
42#include "net/proxy/proxy_config.h"
43#include "net/proxy/proxy_server.h"
44
45namespace 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;
141 config->proxy_rules().single_proxy = 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]ed4ed0f2010-02-24 00:20:48145 config->proxy_rules().proxy_for_http = 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]ed4ed0f2010-02-24 00:20:48153 config->proxy_rules().proxy_for_https = proxy_server;
[email protected]861c6c62009-04-20 16:50:56154 bool have_ftp = GetProxyFromEnvVar("ftp_proxy", &proxy_server);
155 if (have_ftp)
[email protected]ed4ed0f2010-02-24 00:20:48156 config->proxy_rules().proxy_for_ftp = proxy_server;
[email protected]861c6c62009-04-20 16:50:56157 if (have_http || have_https || have_ftp) {
158 // mustn't change type unless some rules are actually set.
[email protected]ed4ed0f2010-02-24 00:20:48159 config->proxy_rules().type =
160 ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
[email protected]861c6c62009-04-20 16:50:56161 }
162 }
[email protected]ed4ed0f2010-02-24 00:20:48163 if (config->proxy_rules().empty()) {
[email protected]861c6c62009-04-20 16:50:56164 // If the above were not defined, try for socks.
[email protected]e8c50812010-09-28 00:16:17165 // For environment variables, we default to version 5, per the gnome
166 // documentation: http://library.gnome.org/devel/gnet/stable/gnet-socks.html
167 ProxyServer::Scheme scheme = ProxyServer::SCHEME_SOCKS5;
[email protected]861c6c62009-04-20 16:50:56168 std::string env_version;
[email protected]3ba7e082010-08-07 02:57:59169 if (env_var_getter_->GetVar("SOCKS_VERSION", &env_version)
[email protected]e8c50812010-09-28 00:16:17170 && env_version == "4")
171 scheme = ProxyServer::SCHEME_SOCKS4;
[email protected]861c6c62009-04-20 16:50:56172 if (GetProxyFromEnvVarForScheme("SOCKS_SERVER", scheme, &proxy_server)) {
[email protected]ed4ed0f2010-02-24 00:20:48173 config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
174 config->proxy_rules().single_proxy = proxy_server;
[email protected]861c6c62009-04-20 16:50:56175 }
176 }
177 // Look for the proxy bypass list.
178 std::string no_proxy;
[email protected]3ba7e082010-08-07 02:57:59179 env_var_getter_->GetVar("no_proxy", &no_proxy);
[email protected]ed4ed0f2010-02-24 00:20:48180 if (config->proxy_rules().empty()) {
[email protected]861c6c62009-04-20 16:50:56181 // Having only "no_proxy" set, presumably to "*", makes it
182 // explicit that env vars do specify a configuration: having no
183 // rules specified only means the user explicitly asks for direct
184 // connections.
185 return !no_proxy.empty();
186 }
[email protected]7541206c2010-02-19 20:24:06187 // Note that this uses "suffix" matching. So a bypass of "google.com"
188 // is understood to mean a bypass of "*google.com".
[email protected]ed4ed0f2010-02-24 00:20:48189 config->proxy_rules().bypass_rules.ParseFromStringUsingSuffixMatching(
190 no_proxy);
[email protected]861c6c62009-04-20 16:50:56191 return true;
192}
193
194namespace {
195
[email protected]d7395e732009-08-28 23:13:43196const int kDebounceTimeoutMilliseconds = 250;
[email protected]3e44697f2009-05-22 14:37:39197
[email protected]6de53d42010-11-09 07:33:19198#if defined(USE_GCONF)
[email protected]573c0502011-05-17 22:19:50199// This setting getter uses gconf, as used in GNOME 2 and some GNOME 3 desktops.
200class SettingGetterImplGConf : public ProxyConfigServiceLinux::SettingGetter {
[email protected]861c6c62009-04-20 16:50:56201 public:
[email protected]573c0502011-05-17 22:19:50202 SettingGetterImplGConf()
[email protected]390a5b62011-06-02 06:36:53203 : this_(this), client_(NULL), notify_delegate_(NULL), loop_(NULL) {}
[email protected]3e44697f2009-05-22 14:37:39204
[email protected]573c0502011-05-17 22:19:50205 virtual ~SettingGetterImplGConf() {
[email protected]3e44697f2009-05-22 14:37:39206 // client_ should have been released before now, from
[email protected]f5b13442009-07-13 15:23:59207 // Delegate::OnDestroy(), while running on the UI thread. However
208 // on exiting the process, it may happen that
209 // Delegate::OnDestroy() task is left pending on the glib loop
210 // after the loop was quit, and pending tasks may then be deleted
211 // without being run.
212 if (client_) {
213 // gconf client was not cleaned up.
214 if (MessageLoop::current() == loop_) {
215 // We are on the UI thread so we can clean it safely. This is
216 // the case at least for ui_tests running under Valgrind in
217 // bug 16076.
[email protected]573c0502011-05-17 22:19:50218 VLOG(1) << "~SettingGetterImplGConf: releasing gconf client";
[email protected]d3066142011-05-10 02:36:20219 ShutDown();
[email protected]f5b13442009-07-13 15:23:59220 } else {
[email protected]573c0502011-05-17 22:19:50221 LOG(WARNING) << "~SettingGetterImplGConf: leaking gconf client";
[email protected]f5b13442009-07-13 15:23:59222 client_ = NULL;
223 }
224 }
[email protected]3e44697f2009-05-22 14:37:39225 DCHECK(!client_);
[email protected]861c6c62009-04-20 16:50:56226 }
227
[email protected]d7395e732009-08-28 23:13:43228 virtual bool Init(MessageLoop* glib_default_loop,
[email protected]c4c1b482011-07-22 17:24:26229 MessageLoopForIO* file_loop) OVERRIDE {
[email protected]d7395e732009-08-28 23:13:43230 DCHECK(MessageLoop::current() == glib_default_loop);
[email protected]3e44697f2009-05-22 14:37:39231 DCHECK(!client_);
232 DCHECK(!loop_);
[email protected]d7395e732009-08-28 23:13:43233 loop_ = glib_default_loop;
[email protected]3e44697f2009-05-22 14:37:39234 client_ = gconf_client_get_default();
[email protected]861c6c62009-04-20 16:50:56235 if (!client_) {
[email protected]861c6c62009-04-20 16:50:56236 // It's not clear whether/when this can return NULL.
[email protected]3e44697f2009-05-22 14:37:39237 LOG(ERROR) << "Unable to create a gconf client";
238 loop_ = NULL;
239 return false;
[email protected]861c6c62009-04-20 16:50:56240 }
[email protected]3e44697f2009-05-22 14:37:39241 GError* error = NULL;
242 // We need to add the directories for which we'll be asking
243 // notifications, and we might as well ask to preload them.
244 gconf_client_add_dir(client_, "/system/proxy",
245 GCONF_CLIENT_PRELOAD_ONELEVEL, &error);
246 if (error == NULL) {
247 gconf_client_add_dir(client_, "/system/http_proxy",
248 GCONF_CLIENT_PRELOAD_ONELEVEL, &error);
249 }
250 if (error != NULL) {
251 LOG(ERROR) << "Error requesting gconf directory: " << error->message;
252 g_error_free(error);
[email protected]d3066142011-05-10 02:36:20253 ShutDown();
[email protected]3e44697f2009-05-22 14:37:39254 return false;
255 }
256 return true;
257 }
258
[email protected]d3066142011-05-10 02:36:20259 void ShutDown() {
[email protected]3e44697f2009-05-22 14:37:39260 if (client_) {
261 DCHECK(MessageLoop::current() == loop_);
262 // This also disables gconf notifications.
263 g_object_unref(client_);
264 client_ = NULL;
265 loop_ = NULL;
266 }
267 }
268
[email protected]d3066142011-05-10 02:36:20269 bool SetUpNotifications(ProxyConfigServiceLinux::Delegate* delegate) {
[email protected]3e44697f2009-05-22 14:37:39270 DCHECK(client_);
271 DCHECK(MessageLoop::current() == loop_);
272 GError* error = NULL;
[email protected]d7395e732009-08-28 23:13:43273 notify_delegate_ = delegate;
[email protected]3e44697f2009-05-22 14:37:39274 gconf_client_notify_add(
275 client_, "/system/proxy",
[email protected]d7395e732009-08-28 23:13:43276 OnGConfChangeNotification, this,
[email protected]3e44697f2009-05-22 14:37:39277 NULL, &error);
278 if (error == NULL) {
279 gconf_client_notify_add(
280 client_, "/system/http_proxy",
[email protected]d7395e732009-08-28 23:13:43281 OnGConfChangeNotification, this,
[email protected]3e44697f2009-05-22 14:37:39282 NULL, &error);
283 }
284 if (error != NULL) {
285 LOG(ERROR) << "Error requesting gconf notifications: " << error->message;
286 g_error_free(error);
[email protected]d3066142011-05-10 02:36:20287 ShutDown();
[email protected]3e44697f2009-05-22 14:37:39288 return false;
289 }
[email protected]d3066142011-05-10 02:36:20290 // Simulate a change to avoid possibly losing updates before this point.
291 OnChangeNotification();
[email protected]3e44697f2009-05-22 14:37:39292 return true;
[email protected]861c6c62009-04-20 16:50:56293 }
294
[email protected]c4c1b482011-07-22 17:24:26295 virtual MessageLoop* GetNotificationLoop() OVERRIDE {
[email protected]d7395e732009-08-28 23:13:43296 return loop_;
297 }
298
[email protected]c4c1b482011-07-22 17:24:26299 virtual const char* GetDataSource() OVERRIDE {
[email protected]d7395e732009-08-28 23:13:43300 return "gconf";
301 }
302
[email protected]c4c1b482011-07-22 17:24:26303 virtual bool GetString(StringSetting key, std::string* result) OVERRIDE {
[email protected]573c0502011-05-17 22:19:50304 switch (key) {
305 case PROXY_MODE:
306 return GetStringByPath("/system/proxy/mode", result);
307 case PROXY_AUTOCONF_URL:
308 return GetStringByPath("/system/proxy/autoconfig_url", result);
309 case PROXY_HTTP_HOST:
310 return GetStringByPath("/system/http_proxy/host", result);
311 case PROXY_HTTPS_HOST:
312 return GetStringByPath("/system/proxy/secure_host", result);
313 case PROXY_FTP_HOST:
314 return GetStringByPath("/system/proxy/ftp_host", result);
315 case PROXY_SOCKS_HOST:
316 return GetStringByPath("/system/proxy/socks_host", result);
[email protected]573c0502011-05-17 22:19:50317 }
[email protected]6b5fe742011-05-20 21:46:48318 return false; // Placate compiler.
[email protected]573c0502011-05-17 22:19:50319 }
[email protected]c4c1b482011-07-22 17:24:26320 virtual bool GetBool(BoolSetting key, bool* result) OVERRIDE {
[email protected]573c0502011-05-17 22:19:50321 switch (key) {
322 case PROXY_USE_HTTP_PROXY:
323 return GetBoolByPath("/system/http_proxy/use_http_proxy", result);
324 case PROXY_USE_SAME_PROXY:
325 return GetBoolByPath("/system/http_proxy/use_same_proxy", result);
326 case PROXY_USE_AUTHENTICATION:
327 return GetBoolByPath("/system/http_proxy/use_authentication", result);
[email protected]573c0502011-05-17 22:19:50328 }
[email protected]6b5fe742011-05-20 21:46:48329 return false; // Placate compiler.
[email protected]573c0502011-05-17 22:19:50330 }
[email protected]c4c1b482011-07-22 17:24:26331 virtual bool GetInt(IntSetting key, int* result) OVERRIDE {
[email protected]573c0502011-05-17 22:19:50332 switch (key) {
333 case PROXY_HTTP_PORT:
334 return GetIntByPath("/system/http_proxy/port", result);
335 case PROXY_HTTPS_PORT:
336 return GetIntByPath("/system/proxy/secure_port", result);
337 case PROXY_FTP_PORT:
338 return GetIntByPath("/system/proxy/ftp_port", result);
339 case PROXY_SOCKS_PORT:
340 return GetIntByPath("/system/proxy/socks_port", result);
[email protected]573c0502011-05-17 22:19:50341 }
[email protected]6b5fe742011-05-20 21:46:48342 return false; // Placate compiler.
[email protected]573c0502011-05-17 22:19:50343 }
[email protected]6b5fe742011-05-20 21:46:48344 virtual bool GetStringList(StringListSetting key,
[email protected]c4c1b482011-07-22 17:24:26345 std::vector<std::string>* result) OVERRIDE {
[email protected]573c0502011-05-17 22:19:50346 switch (key) {
347 case PROXY_IGNORE_HOSTS:
348 return GetStringListByPath("/system/http_proxy/ignore_hosts", result);
[email protected]573c0502011-05-17 22:19:50349 }
[email protected]6b5fe742011-05-20 21:46:48350 return false; // Placate compiler.
[email protected]573c0502011-05-17 22:19:50351 }
352
[email protected]c4c1b482011-07-22 17:24:26353 virtual bool BypassListIsReversed() OVERRIDE {
[email protected]573c0502011-05-17 22:19:50354 // This is a KDE-specific setting.
355 return false;
356 }
357
[email protected]c4c1b482011-07-22 17:24:26358 virtual bool MatchHostsUsingSuffixMatching() OVERRIDE {
[email protected]573c0502011-05-17 22:19:50359 return false;
360 }
361
362 private:
363 bool GetStringByPath(const char* key, std::string* result) {
[email protected]3e44697f2009-05-22 14:37:39364 DCHECK(client_);
365 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56366 GError* error = NULL;
367 gchar* value = gconf_client_get_string(client_, key, &error);
368 if (HandleGError(error, key))
369 return false;
370 if (!value)
371 return false;
372 *result = value;
373 g_free(value);
374 return true;
375 }
[email protected]573c0502011-05-17 22:19:50376 bool GetBoolByPath(const char* key, bool* result) {
[email protected]3e44697f2009-05-22 14:37:39377 DCHECK(client_);
378 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56379 GError* error = NULL;
380 // We want to distinguish unset values from values defaulting to
381 // false. For that we need to use the type-generic
382 // gconf_client_get() rather than gconf_client_get_bool().
383 GConfValue* gconf_value = gconf_client_get(client_, key, &error);
384 if (HandleGError(error, key))
385 return false;
386 if (!gconf_value) {
387 // Unset.
388 return false;
389 }
390 if (gconf_value->type != GCONF_VALUE_BOOL) {
391 gconf_value_free(gconf_value);
392 return false;
393 }
394 gboolean bool_value = gconf_value_get_bool(gconf_value);
395 *result = static_cast<bool>(bool_value);
396 gconf_value_free(gconf_value);
397 return true;
398 }
[email protected]573c0502011-05-17 22:19:50399 bool GetIntByPath(const char* key, int* result) {
[email protected]3e44697f2009-05-22 14:37:39400 DCHECK(client_);
401 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56402 GError* error = NULL;
403 int value = gconf_client_get_int(client_, key, &error);
404 if (HandleGError(error, key))
405 return false;
406 // We don't bother to distinguish an unset value because callers
407 // don't care. 0 is returned if unset.
408 *result = value;
409 return true;
410 }
[email protected]573c0502011-05-17 22:19:50411 bool GetStringListByPath(const char* key, std::vector<std::string>* result) {
[email protected]3e44697f2009-05-22 14:37:39412 DCHECK(client_);
413 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56414 GError* error = NULL;
415 GSList* list = gconf_client_get_list(client_, key,
416 GCONF_VALUE_STRING, &error);
417 if (HandleGError(error, key))
418 return false;
[email protected]8c20e3d2011-05-19 21:03:57419 if (!list)
[email protected]861c6c62009-04-20 16:50:56420 return false;
[email protected]861c6c62009-04-20 16:50:56421 for (GSList *it = list; it; it = it->next) {
422 result->push_back(static_cast<char*>(it->data));
423 g_free(it->data);
424 }
425 g_slist_free(list);
426 return true;
427 }
428
[email protected]861c6c62009-04-20 16:50:56429 // Logs and frees a glib error. Returns false if there was no error
430 // (error is NULL).
431 bool HandleGError(GError* error, const char* key) {
432 if (error != NULL) {
[email protected]3e44697f2009-05-22 14:37:39433 LOG(ERROR) << "Error getting gconf value for " << key
434 << ": " << error->message;
[email protected]861c6c62009-04-20 16:50:56435 g_error_free(error);
436 return true;
437 }
438 return false;
439 }
440
[email protected]d7395e732009-08-28 23:13:43441 // This is the callback from the debounce timer.
442 void OnDebouncedNotification() {
443 DCHECK(MessageLoop::current() == loop_);
[email protected]961ac942011-04-28 18:18:14444 CHECK(notify_delegate_);
[email protected]d7395e732009-08-28 23:13:43445 // Forward to a method on the proxy config service delegate object.
446 notify_delegate_->OnCheckProxyConfigSettings();
447 }
448
449 void OnChangeNotification() {
[email protected]390a5b62011-06-02 06:36:53450 // See below. This check is to try and track bugs 75508 and 84673.
451 // TODO(mdm): remove this check once it gives us some results.
452 CHECK(this_ == this);
[email protected]d7395e732009-08-28 23:13:43453 // We don't use Reset() because the timer may not yet be running.
454 // (In that case Stop() is a no-op.)
455 debounce_timer_.Stop();
[email protected]8c20e3d2011-05-19 21:03:57456 debounce_timer_.Start(
457 base::TimeDelta::FromMilliseconds(kDebounceTimeoutMilliseconds),
458 this, &SettingGetterImplGConf::OnDebouncedNotification);
[email protected]d7395e732009-08-28 23:13:43459 }
460
[email protected]8c20e3d2011-05-19 21:03:57461 // gconf notification callback, dispatched on the default glib main loop.
462 static void OnGConfChangeNotification(GConfClient* client, guint cnxn_id,
463 GConfEntry* entry, gpointer user_data) {
[email protected]b30a3f52010-10-16 01:05:46464 VLOG(1) << "gconf change notification for key "
465 << gconf_entry_get_key(entry);
[email protected]d7395e732009-08-28 23:13:43466 // We don't track which key has changed, just that something did change.
[email protected]573c0502011-05-17 22:19:50467 SettingGetterImplGConf* setting_getter =
468 reinterpret_cast<SettingGetterImplGConf*>(user_data);
[email protected]d7395e732009-08-28 23:13:43469 setting_getter->OnChangeNotification();
470 }
471
[email protected]390a5b62011-06-02 06:36:53472 // See bugs 75508 and 84673. I kind of suspect we're getting bogus pointers to
473 // this object somehow in callbacks from GConf; this pointer is always set to
474 // |this| on construction and should help verify whether this object has been
475 // scribbled upon or if we have a stray pointer somehow.
476 SettingGetterImplGConf* this_;
477
[email protected]861c6c62009-04-20 16:50:56478 GConfClient* client_;
[email protected]d7395e732009-08-28 23:13:43479 ProxyConfigServiceLinux::Delegate* notify_delegate_;
[email protected]573c0502011-05-17 22:19:50480 base::OneShotTimer<SettingGetterImplGConf> debounce_timer_;
[email protected]861c6c62009-04-20 16:50:56481
[email protected]3e44697f2009-05-22 14:37:39482 // Message loop of the thread that we make gconf calls on. It should
483 // be the UI thread and all our methods should be called on this
484 // thread. Only for assertions.
485 MessageLoop* loop_;
486
[email protected]573c0502011-05-17 22:19:50487 DISALLOW_COPY_AND_ASSIGN(SettingGetterImplGConf);
[email protected]d7395e732009-08-28 23:13:43488};
[email protected]6de53d42010-11-09 07:33:19489#endif // defined(USE_GCONF)
[email protected]d7395e732009-08-28 23:13:43490
[email protected]8c20e3d2011-05-19 21:03:57491#if defined(USE_GIO)
492// This setting getter uses gsettings, as used in most GNOME 3 desktops.
493class SettingGetterImplGSettings
494 : public ProxyConfigServiceLinux::SettingGetter {
495 public:
[email protected]0e14e87d2011-06-21 21:24:19496#if 0
497GSettings* (*g_settings_new)(const gchar* schema);
498 GSettings* (*g_settings_get_child)(GSettings* settings, const gchar* name);
499 gboolean (*g_settings_get_boolean)(GSettings* settings, const gchar* key);
500 gchar* (*g_settings_get_string)(GSettings* settings, const gchar* key);
501 gint (*g_settings_get_int)(GSettings* settings, const gchar* key);
502 gchar** (*g_settings_get_strv)(GSettings* settings, const gchar* key);
503 const gchar* const* (*g_settings_list_schemas)();
504
505 // The library handle.
506 void* gio_handle_;
[email protected]8c20e3d2011-05-19 21:03:57507#endif
[email protected]0e14e87d2011-06-21 21:24:19508 SettingGetterImplGSettings() :
509#if defined(DLOPEN_GSETTINGS)
510 g_settings_new(NULL),
511 g_settings_get_child(NULL),
512 g_settings_get_boolean(NULL),
513 g_settings_get_string(NULL),
514 g_settings_get_int(NULL),
515 g_settings_get_strv(NULL),
516 g_settings_list_schemas(NULL),
517 gio_handle_(NULL),
518#endif
519 client_(NULL),
520 http_client_(NULL),
521 https_client_(NULL),
522 ftp_client_(NULL),
523 socks_client_(NULL),
524 notify_delegate_(NULL),
525 loop_(NULL) {
[email protected]8c20e3d2011-05-19 21:03:57526 }
527
528 virtual ~SettingGetterImplGSettings() {
529 // client_ should have been released before now, from
530 // Delegate::OnDestroy(), while running on the UI thread. However
531 // on exiting the process, it may happen that
532 // Delegate::OnDestroy() task is left pending on the glib loop
533 // after the loop was quit, and pending tasks may then be deleted
534 // without being run.
535 if (client_) {
536 // gconf client was not cleaned up.
537 if (MessageLoop::current() == loop_) {
538 // We are on the UI thread so we can clean it safely. This is
539 // the case at least for ui_tests running under Valgrind in
540 // bug 16076.
541 VLOG(1) << "~SettingGetterImplGSettings: releasing gsettings client";
542 ShutDown();
543 } else {
544 LOG(WARNING) << "~SettingGetterImplGSettings: leaking gsettings client";
545 client_ = NULL;
546 }
547 }
548 DCHECK(!client_);
549#if defined(DLOPEN_GSETTINGS)
550 if (gio_handle_) {
551 dlclose(gio_handle_);
552 gio_handle_ = NULL;
553 }
554#endif
555 }
556
[email protected]4cf80f0b2011-05-20 20:30:26557 bool SchemaExists(const char* schema_name) {
558 const gchar* const* schemas = g_settings_list_schemas();
559 while (*schemas) {
[email protected]a099f3ae2011-08-16 21:06:58560 if (strcmp(schema_name, static_cast<const char*>(*schemas)) == 0)
[email protected]4cf80f0b2011-05-20 20:30:26561 return true;
562 schemas++;
563 }
564 return false;
565 }
566
[email protected]8c20e3d2011-05-19 21:03:57567 // LoadAndCheckVersion() must be called *before* Init()!
568 bool LoadAndCheckVersion(base::Environment* env);
569
570 virtual bool Init(MessageLoop* glib_default_loop,
[email protected]c4c1b482011-07-22 17:24:26571 MessageLoopForIO* file_loop) OVERRIDE {
[email protected]8c20e3d2011-05-19 21:03:57572 DCHECK(MessageLoop::current() == glib_default_loop);
573 DCHECK(!client_);
574 DCHECK(!loop_);
[email protected]4cf80f0b2011-05-20 20:30:26575
576 if (!SchemaExists("org.gnome.system.proxy") ||
577 !(client_ = g_settings_new("org.gnome.system.proxy"))) {
[email protected]8c20e3d2011-05-19 21:03:57578 // It's not clear whether/when this can return NULL.
579 LOG(ERROR) << "Unable to create a gsettings client";
580 return false;
581 }
582 loop_ = glib_default_loop;
583 // We assume these all work if the above call worked.
584 http_client_ = g_settings_get_child(client_, "http");
585 https_client_ = g_settings_get_child(client_, "https");
586 ftp_client_ = g_settings_get_child(client_, "ftp");
587 socks_client_ = g_settings_get_child(client_, "socks");
588 DCHECK(http_client_ && https_client_ && ftp_client_ && socks_client_);
589 return true;
590 }
591
592 void ShutDown() {
593 if (client_) {
594 DCHECK(MessageLoop::current() == loop_);
595 // This also disables gsettings notifications.
596 g_object_unref(socks_client_);
597 g_object_unref(ftp_client_);
598 g_object_unref(https_client_);
599 g_object_unref(http_client_);
600 g_object_unref(client_);
601 // We only need to null client_ because it's the only one that we check.
602 client_ = NULL;
603 loop_ = NULL;
604 }
605 }
606
607 bool SetUpNotifications(ProxyConfigServiceLinux::Delegate* delegate) {
608 DCHECK(client_);
609 DCHECK(MessageLoop::current() == loop_);
610 notify_delegate_ = delegate;
611 // We could watch for the change-event signal instead of changed, but
612 // since we have to watch more than one object, we'd still have to
613 // debounce change notifications. This is conceptually simpler.
614 g_signal_connect(G_OBJECT(client_), "changed",
615 G_CALLBACK(OnGSettingsChangeNotification), this);
616 g_signal_connect(G_OBJECT(http_client_), "changed",
617 G_CALLBACK(OnGSettingsChangeNotification), this);
618 g_signal_connect(G_OBJECT(https_client_), "changed",
619 G_CALLBACK(OnGSettingsChangeNotification), this);
620 g_signal_connect(G_OBJECT(ftp_client_), "changed",
621 G_CALLBACK(OnGSettingsChangeNotification), this);
622 g_signal_connect(G_OBJECT(socks_client_), "changed",
623 G_CALLBACK(OnGSettingsChangeNotification), this);
624 // Simulate a change to avoid possibly losing updates before this point.
625 OnChangeNotification();
626 return true;
627 }
628
[email protected]c4c1b482011-07-22 17:24:26629 virtual MessageLoop* GetNotificationLoop() OVERRIDE {
[email protected]8c20e3d2011-05-19 21:03:57630 return loop_;
631 }
632
[email protected]c4c1b482011-07-22 17:24:26633 virtual const char* GetDataSource() OVERRIDE {
[email protected]8c20e3d2011-05-19 21:03:57634 return "gsettings";
635 }
636
[email protected]c4c1b482011-07-22 17:24:26637 virtual bool GetString(StringSetting key, std::string* result) OVERRIDE {
[email protected]8c20e3d2011-05-19 21:03:57638 DCHECK(client_);
639 switch (key) {
640 case PROXY_MODE:
641 return GetStringByPath(client_, "mode", result);
642 case PROXY_AUTOCONF_URL:
643 return GetStringByPath(client_, "autoconfig-url", result);
644 case PROXY_HTTP_HOST:
645 return GetStringByPath(http_client_, "host", result);
646 case PROXY_HTTPS_HOST:
647 return GetStringByPath(https_client_, "host", result);
648 case PROXY_FTP_HOST:
649 return GetStringByPath(ftp_client_, "host", result);
650 case PROXY_SOCKS_HOST:
651 return GetStringByPath(socks_client_, "host", result);
[email protected]8c20e3d2011-05-19 21:03:57652 }
[email protected]6b5fe742011-05-20 21:46:48653 return false; // Placate compiler.
[email protected]8c20e3d2011-05-19 21:03:57654 }
[email protected]c4c1b482011-07-22 17:24:26655 virtual bool GetBool(BoolSetting key, bool* result) OVERRIDE {
[email protected]8c20e3d2011-05-19 21:03:57656 DCHECK(client_);
657 switch (key) {
658 case PROXY_USE_HTTP_PROXY:
659 // Although there is an "enabled" boolean in http_client_, it is not set
660 // to true by the proxy config utility. We ignore it and return false.
661 return false;
662 case PROXY_USE_SAME_PROXY:
663 // Similarly, although there is a "use-same-proxy" boolean in client_,
664 // it is never set to false by the proxy config utility. We ignore it.
665 return false;
666 case PROXY_USE_AUTHENTICATION:
667 // There is also no way to set this in the proxy config utility, but it
668 // doesn't hurt us to get the actual setting (unlike the two above).
669 return GetBoolByPath(http_client_, "use-authentication", result);
[email protected]8c20e3d2011-05-19 21:03:57670 }
[email protected]6b5fe742011-05-20 21:46:48671 return false; // Placate compiler.
[email protected]8c20e3d2011-05-19 21:03:57672 }
[email protected]c4c1b482011-07-22 17:24:26673 virtual bool GetInt(IntSetting key, int* result) OVERRIDE {
[email protected]8c20e3d2011-05-19 21:03:57674 DCHECK(client_);
675 switch (key) {
676 case PROXY_HTTP_PORT:
677 return GetIntByPath(http_client_, "port", result);
678 case PROXY_HTTPS_PORT:
679 return GetIntByPath(https_client_, "port", result);
680 case PROXY_FTP_PORT:
681 return GetIntByPath(ftp_client_, "port", result);
682 case PROXY_SOCKS_PORT:
683 return GetIntByPath(socks_client_, "port", result);
[email protected]8c20e3d2011-05-19 21:03:57684 }
[email protected]6b5fe742011-05-20 21:46:48685 return false; // Placate compiler.
[email protected]8c20e3d2011-05-19 21:03:57686 }
[email protected]6b5fe742011-05-20 21:46:48687 virtual bool GetStringList(StringListSetting key,
[email protected]c4c1b482011-07-22 17:24:26688 std::vector<std::string>* result) OVERRIDE {
[email protected]8c20e3d2011-05-19 21:03:57689 DCHECK(client_);
690 switch (key) {
691 case PROXY_IGNORE_HOSTS:
692 return GetStringListByPath(client_, "ignore-hosts", result);
[email protected]8c20e3d2011-05-19 21:03:57693 }
[email protected]6b5fe742011-05-20 21:46:48694 return false; // Placate compiler.
[email protected]8c20e3d2011-05-19 21:03:57695 }
696
[email protected]c4c1b482011-07-22 17:24:26697 virtual bool BypassListIsReversed() OVERRIDE {
[email protected]8c20e3d2011-05-19 21:03:57698 // This is a KDE-specific setting.
699 return false;
700 }
701
[email protected]c4c1b482011-07-22 17:24:26702 virtual bool MatchHostsUsingSuffixMatching() OVERRIDE {
[email protected]8c20e3d2011-05-19 21:03:57703 return false;
704 }
705
706 private:
707#if defined(DLOPEN_GSETTINGS)
708 // We replicate the prototypes for the g_settings APIs we need. We may not
709 // even be compiling on a system that has them. If we are, these won't
710 // conflict both because they are identical and also due to scoping. The
711 // scoping will also ensure that these get used instead of the global ones.
712 struct _GSettings;
713 typedef struct _GSettings GSettings;
714 GSettings* (*g_settings_new)(const gchar* schema);
715 GSettings* (*g_settings_get_child)(GSettings* settings, const gchar* name);
716 gboolean (*g_settings_get_boolean)(GSettings* settings, const gchar* key);
717 gchar* (*g_settings_get_string)(GSettings* settings, const gchar* key);
718 gint (*g_settings_get_int)(GSettings* settings, const gchar* key);
719 gchar** (*g_settings_get_strv)(GSettings* settings, const gchar* key);
[email protected]4cf80f0b2011-05-20 20:30:26720 const gchar* const* (*g_settings_list_schemas)();
[email protected]8c20e3d2011-05-19 21:03:57721
722 // The library handle.
723 void* gio_handle_;
724
725 // Load a symbol from |gio_handle_| and store it into |*func_ptr|.
726 bool LoadSymbol(const char* name, void** func_ptr) {
727 dlerror();
728 *func_ptr = dlsym(gio_handle_, name);
729 const char* error = dlerror();
730 if (error) {
731 VLOG(1) << "Unable to load symbol " << name << ": " << error;
732 return false;
733 }
734 return true;
735 }
736#endif // defined(DLOPEN_GSETTINGS)
737
738 bool GetStringByPath(GSettings* client, const char* key,
739 std::string* result) {
740 DCHECK(MessageLoop::current() == loop_);
741 gchar* value = g_settings_get_string(client, key);
742 if (!value)
743 return false;
744 *result = value;
745 g_free(value);
746 return true;
747 }
748 bool GetBoolByPath(GSettings* client, const char* key, bool* result) {
749 DCHECK(MessageLoop::current() == loop_);
750 *result = static_cast<bool>(g_settings_get_boolean(client, key));
751 return true;
752 }
753 bool GetIntByPath(GSettings* client, const char* key, int* result) {
754 DCHECK(MessageLoop::current() == loop_);
755 *result = g_settings_get_int(client, key);
756 return true;
757 }
758 bool GetStringListByPath(GSettings* client, const char* key,
759 std::vector<std::string>* result) {
760 DCHECK(MessageLoop::current() == loop_);
761 gchar** list = g_settings_get_strv(client, key);
762 if (!list)
763 return false;
764 for (size_t i = 0; list[i]; ++i) {
765 result->push_back(static_cast<char*>(list[i]));
766 g_free(list[i]);
767 }
768 g_free(list);
769 return true;
770 }
771
772 // This is the callback from the debounce timer.
773 void OnDebouncedNotification() {
774 DCHECK(MessageLoop::current() == loop_);
775 CHECK(notify_delegate_);
776 // Forward to a method on the proxy config service delegate object.
777 notify_delegate_->OnCheckProxyConfigSettings();
778 }
779
780 void OnChangeNotification() {
781 // We don't use Reset() because the timer may not yet be running.
782 // (In that case Stop() is a no-op.)
783 debounce_timer_.Stop();
784 debounce_timer_.Start(
785 base::TimeDelta::FromMilliseconds(kDebounceTimeoutMilliseconds),
786 this, &SettingGetterImplGSettings::OnDebouncedNotification);
787 }
788
789 // gsettings notification callback, dispatched on the default glib main loop.
790 static void OnGSettingsChangeNotification(GSettings* client, gchar* key,
791 gpointer user_data) {
792 VLOG(1) << "gsettings change notification for key " << key;
793 // We don't track which key has changed, just that something did change.
794 SettingGetterImplGSettings* setting_getter =
795 reinterpret_cast<SettingGetterImplGSettings*>(user_data);
796 setting_getter->OnChangeNotification();
797 }
798
799 GSettings* client_;
800 GSettings* http_client_;
801 GSettings* https_client_;
802 GSettings* ftp_client_;
803 GSettings* socks_client_;
804 ProxyConfigServiceLinux::Delegate* notify_delegate_;
805 base::OneShotTimer<SettingGetterImplGSettings> debounce_timer_;
806
807 // Message loop of the thread that we make gsettings calls on. It should
808 // be the UI thread and all our methods should be called on this
809 // thread. Only for assertions.
810 MessageLoop* loop_;
811
812 DISALLOW_COPY_AND_ASSIGN(SettingGetterImplGSettings);
813};
814
815bool SettingGetterImplGSettings::LoadAndCheckVersion(
816 base::Environment* env) {
817 // LoadAndCheckVersion() must be called *before* Init()!
818 DCHECK(!client_);
819
820 // The APIs to query gsettings were introduced after the minimum glib
821 // version we target, so we can't link directly against them. We load them
822 // dynamically at runtime, and if they don't exist, return false here. (We
823 // support linking directly via gyp flags though.) Additionally, even when
824 // they are present, we do two additional checks to make sure we should use
825 // them and not gconf. First, we attempt to load the schema for proxy
826 // settings. Second, we check for the program that was used in older
827 // versions of GNOME to configure proxy settings, and return false if it
828 // exists. Some distributions (e.g. Ubuntu 11.04) have the API and schema
829 // but don't use gsettings for proxy settings, but they do have the old
830 // binary, so we detect these systems that way.
831
832#ifdef DLOPEN_GSETTINGS
833 gio_handle_ = dlopen("libgio-2.0.so", RTLD_NOW | RTLD_GLOBAL);
834 if (!gio_handle_) {
835 VLOG(1) << "Cannot load gio library. Will fall back to gconf.";
836 return false;
837 }
838 if (!LoadSymbol("g_settings_new",
839 reinterpret_cast<void**>(&g_settings_new)) ||
840 !LoadSymbol("g_settings_get_child",
841 reinterpret_cast<void**>(&g_settings_get_child)) ||
842 !LoadSymbol("g_settings_get_string",
843 reinterpret_cast<void**>(&g_settings_get_string)) ||
844 !LoadSymbol("g_settings_get_boolean",
845 reinterpret_cast<void**>(&g_settings_get_boolean)) ||
846 !LoadSymbol("g_settings_get_int",
847 reinterpret_cast<void**>(&g_settings_get_int)) ||
848 !LoadSymbol("g_settings_get_strv",
[email protected]4cf80f0b2011-05-20 20:30:26849 reinterpret_cast<void**>(&g_settings_get_strv)) ||
850 !LoadSymbol("g_settings_list_schemas",
851 reinterpret_cast<void**>(&g_settings_list_schemas))) {
[email protected]8c20e3d2011-05-19 21:03:57852 VLOG(1) << "Cannot load gsettings API. Will fall back to gconf.";
853 dlclose(gio_handle_);
854 gio_handle_ = NULL;
855 return false;
856 }
857#endif
858
[email protected]4cf80f0b2011-05-20 20:30:26859 GSettings* client;
860 if (!SchemaExists("org.gnome.system.proxy") ||
861 !(client = g_settings_new("org.gnome.system.proxy"))) {
[email protected]8c20e3d2011-05-19 21:03:57862 VLOG(1) << "Cannot create gsettings client. Will fall back to gconf.";
863 return false;
864 }
865 g_object_unref(client);
866
867 std::string path;
868 if (!env->GetVar("PATH", &path)) {
869 LOG(ERROR) << "No $PATH variable. Assuming no gnome-network-properties.";
870 } else {
871 // Yes, we're on the UI thread. Yes, we're accessing the file system.
872 // Sadly, we don't have much choice. We need the proxy settings and we
873 // need them now, and to figure out where to get them, we have to check
874 // for this binary. See http://crbug.com/69057 for additional details.
875 base::ThreadRestrictions::ScopedAllowIO allow_io;
876 std::vector<std::string> paths;
877 Tokenize(path, ":", &paths);
878 for (size_t i = 0; i < paths.size(); ++i) {
879 FilePath file(paths[i]);
880 if (file_util::PathExists(file.Append("gnome-network-properties"))) {
881 VLOG(1) << "Found gnome-network-properties. Will fall back to gconf.";
882 return false;
883 }
884 }
885 }
886
887 VLOG(1) << "All gsettings tests OK. Will get proxy config from gsettings.";
888 return true;
889}
890#endif // defined(USE_GIO)
891
[email protected]d7395e732009-08-28 23:13:43892// This is the KDE version that reads kioslaverc and simulates gconf.
893// Doing this allows the main Delegate code, as well as the unit tests
894// for it, to stay the same - and the settings map fairly well besides.
[email protected]573c0502011-05-17 22:19:50895class SettingGetterImplKDE : public ProxyConfigServiceLinux::SettingGetter,
896 public base::MessagePumpLibevent::Watcher {
[email protected]d7395e732009-08-28 23:13:43897 public:
[email protected]573c0502011-05-17 22:19:50898 explicit SettingGetterImplKDE(base::Environment* env_var_getter)
[email protected]d7395e732009-08-28 23:13:43899 : inotify_fd_(-1), notify_delegate_(NULL), indirect_manual_(false),
[email protected]a48bf4a2010-06-14 18:24:53900 auto_no_pac_(false), reversed_bypass_list_(false),
[email protected]f18fde22010-05-18 23:49:54901 env_var_getter_(env_var_getter), file_loop_(NULL) {
[email protected]9a8c4022011-01-25 14:25:33902 // This has to be called on the UI thread (http://crbug.com/69057).
903 base::ThreadRestrictions::ScopedAllowIO allow_io;
904
[email protected]f18fde22010-05-18 23:49:54905 // Derive the location of the kde config dir from the environment.
[email protected]92d2dc82010-04-08 17:49:59906 std::string home;
[email protected]3ba7e082010-08-07 02:57:59907 if (env_var_getter->GetVar("KDEHOME", &home) && !home.empty()) {
[email protected]2e8cfe22010-06-12 00:26:24908 // $KDEHOME is set. Use it unconditionally.
[email protected]92d2dc82010-04-08 17:49:59909 kde_config_dir_ = KDEHomeToConfigPath(FilePath(home));
910 } else {
[email protected]2e8cfe22010-06-12 00:26:24911 // $KDEHOME is unset. Try to figure out what to use. This seems to be
[email protected]92d2dc82010-04-08 17:49:59912 // the common case on most distributions.
[email protected]3ba7e082010-08-07 02:57:59913 if (!env_var_getter->GetVar(base::env_vars::kHome, &home))
[email protected]d7395e732009-08-28 23:13:43914 // User has no $HOME? Give up. Later we'll report the failure.
915 return;
[email protected]6b0349ef2010-10-16 04:56:06916 if (base::nix::GetDesktopEnvironment(env_var_getter) ==
917 base::nix::DESKTOP_ENVIRONMENT_KDE3) {
[email protected]92d2dc82010-04-08 17:49:59918 // KDE3 always uses .kde for its configuration.
919 FilePath kde_path = FilePath(home).Append(".kde");
920 kde_config_dir_ = KDEHomeToConfigPath(kde_path);
921 } else {
922 // Some distributions patch KDE4 to use .kde4 instead of .kde, so that
[email protected]fad9c8a52010-06-10 22:30:53923 // both can be installed side-by-side. Sadly they don't all do this, and
924 // they don't always do this: some distributions have started switching
925 // back as well. So if there is a .kde4 directory, check the timestamps
926 // of the config directories within and use the newest one.
[email protected]92d2dc82010-04-08 17:49:59927 // Note that we should currently be running in the UI thread, because in
928 // the gconf version, that is the only thread that can access the proxy
929 // settings (a gconf restriction). As noted below, the initial read of
930 // the proxy settings will be done in this thread anyway, so we check
931 // for .kde4 here in this thread as well.
[email protected]fad9c8a52010-06-10 22:30:53932 FilePath kde3_path = FilePath(home).Append(".kde");
933 FilePath kde3_config = KDEHomeToConfigPath(kde3_path);
[email protected]92d2dc82010-04-08 17:49:59934 FilePath kde4_path = FilePath(home).Append(".kde4");
[email protected]fad9c8a52010-06-10 22:30:53935 FilePath kde4_config = KDEHomeToConfigPath(kde4_path);
936 bool use_kde4 = false;
[email protected]92d2dc82010-04-08 17:49:59937 if (file_util::DirectoryExists(kde4_path)) {
[email protected]2f0193c22010-09-03 02:28:37938 base::PlatformFileInfo kde3_info;
939 base::PlatformFileInfo kde4_info;
[email protected]fad9c8a52010-06-10 22:30:53940 if (file_util::GetFileInfo(kde4_config, &kde4_info)) {
941 if (file_util::GetFileInfo(kde3_config, &kde3_info)) {
942 use_kde4 = kde4_info.last_modified >= kde3_info.last_modified;
943 } else {
944 use_kde4 = true;
945 }
946 }
947 }
948 if (use_kde4) {
[email protected]92d2dc82010-04-08 17:49:59949 kde_config_dir_ = KDEHomeToConfigPath(kde4_path);
950 } else {
[email protected]fad9c8a52010-06-10 22:30:53951 kde_config_dir_ = KDEHomeToConfigPath(kde3_path);
[email protected]92d2dc82010-04-08 17:49:59952 }
953 }
[email protected]d7395e732009-08-28 23:13:43954 }
[email protected]d7395e732009-08-28 23:13:43955 }
956
[email protected]573c0502011-05-17 22:19:50957 virtual ~SettingGetterImplKDE() {
[email protected]d7395e732009-08-28 23:13:43958 // inotify_fd_ should have been closed before now, from
959 // Delegate::OnDestroy(), while running on the file thread. However
960 // on exiting the process, it may happen that Delegate::OnDestroy()
961 // task is left pending on the file loop after the loop was quit,
962 // and pending tasks may then be deleted without being run.
963 // Here in the KDE version, we can safely close the file descriptor
964 // anyway. (Not that it really matters; the process is exiting.)
965 if (inotify_fd_ >= 0)
[email protected]d3066142011-05-10 02:36:20966 ShutDown();
[email protected]d7395e732009-08-28 23:13:43967 DCHECK(inotify_fd_ < 0);
968 }
969
970 virtual bool Init(MessageLoop* glib_default_loop,
[email protected]c4c1b482011-07-22 17:24:26971 MessageLoopForIO* file_loop) OVERRIDE {
[email protected]9a8c4022011-01-25 14:25:33972 // This has to be called on the UI thread (http://crbug.com/69057).
973 base::ThreadRestrictions::ScopedAllowIO allow_io;
[email protected]d7395e732009-08-28 23:13:43974 DCHECK(inotify_fd_ < 0);
975 inotify_fd_ = inotify_init();
976 if (inotify_fd_ < 0) {
[email protected]57b765672009-10-13 18:27:40977 PLOG(ERROR) << "inotify_init failed";
[email protected]d7395e732009-08-28 23:13:43978 return false;
979 }
980 int flags = fcntl(inotify_fd_, F_GETFL);
981 if (fcntl(inotify_fd_, F_SETFL, flags | O_NONBLOCK) < 0) {
[email protected]57b765672009-10-13 18:27:40982 PLOG(ERROR) << "fcntl failed";
[email protected]d7395e732009-08-28 23:13:43983 close(inotify_fd_);
984 inotify_fd_ = -1;
985 return false;
986 }
987 file_loop_ = file_loop;
988 // The initial read is done on the current thread, not |file_loop_|,
[email protected]d3066142011-05-10 02:36:20989 // since we will need to have it for SetUpAndFetchInitialConfig().
[email protected]d7395e732009-08-28 23:13:43990 UpdateCachedSettings();
991 return true;
992 }
993
[email protected]d3066142011-05-10 02:36:20994 void ShutDown() {
[email protected]d7395e732009-08-28 23:13:43995 if (inotify_fd_ >= 0) {
996 ResetCachedSettings();
997 inotify_watcher_.StopWatchingFileDescriptor();
998 close(inotify_fd_);
999 inotify_fd_ = -1;
1000 }
1001 }
1002
[email protected]d3066142011-05-10 02:36:201003 bool SetUpNotifications(ProxyConfigServiceLinux::Delegate* delegate) {
[email protected]d7395e732009-08-28 23:13:431004 DCHECK(inotify_fd_ >= 0);
[email protected]d3066142011-05-10 02:36:201005 DCHECK(MessageLoop::current() == file_loop_);
[email protected]d7395e732009-08-28 23:13:431006 // We can't just watch the kioslaverc file directly, since KDE will write
1007 // a new copy of it and then rename it whenever settings are changed and
1008 // inotify watches inodes (so we'll be watching the old deleted file after
1009 // the first change, and it will never change again). So, we watch the
1010 // directory instead. We then act only on changes to the kioslaverc entry.
1011 if (inotify_add_watch(inotify_fd_, kde_config_dir_.value().c_str(),
1012 IN_MODIFY | IN_MOVED_TO) < 0)
1013 return false;
1014 notify_delegate_ = delegate;
[email protected]d3066142011-05-10 02:36:201015 if (!file_loop_->WatchFileDescriptor(inotify_fd_, true,
1016 MessageLoopForIO::WATCH_READ, &inotify_watcher_, this))
1017 return false;
1018 // Simulate a change to avoid possibly losing updates before this point.
1019 OnChangeNotification();
1020 return true;
[email protected]d7395e732009-08-28 23:13:431021 }
1022
[email protected]c4c1b482011-07-22 17:24:261023 virtual MessageLoop* GetNotificationLoop() OVERRIDE {
[email protected]d7395e732009-08-28 23:13:431024 return file_loop_;
1025 }
1026
1027 // Implement base::MessagePumpLibevent::Delegate.
1028 void OnFileCanReadWithoutBlocking(int fd) {
1029 DCHECK(fd == inotify_fd_);
1030 DCHECK(MessageLoop::current() == file_loop_);
1031 OnChangeNotification();
1032 }
1033 void OnFileCanWriteWithoutBlocking(int fd) {
1034 NOTREACHED();
1035 }
1036
[email protected]c4c1b482011-07-22 17:24:261037 virtual const char* GetDataSource() OVERRIDE {
[email protected]d7395e732009-08-28 23:13:431038 return "KDE";
1039 }
1040
[email protected]c4c1b482011-07-22 17:24:261041 virtual bool GetString(StringSetting key, std::string* result) OVERRIDE {
[email protected]d7395e732009-08-28 23:13:431042 string_map_type::iterator it = string_table_.find(key);
1043 if (it == string_table_.end())
1044 return false;
1045 *result = it->second;
1046 return true;
1047 }
[email protected]c4c1b482011-07-22 17:24:261048 virtual bool GetBool(BoolSetting key, bool* result) OVERRIDE {
[email protected]d7395e732009-08-28 23:13:431049 // We don't ever have any booleans.
1050 return false;
1051 }
[email protected]c4c1b482011-07-22 17:24:261052 virtual bool GetInt(IntSetting key, int* result) OVERRIDE {
[email protected]d7395e732009-08-28 23:13:431053 // We don't ever have any integers. (See AddProxy() below about ports.)
1054 return false;
1055 }
[email protected]6b5fe742011-05-20 21:46:481056 virtual bool GetStringList(StringListSetting key,
[email protected]c4c1b482011-07-22 17:24:261057 std::vector<std::string>* result) OVERRIDE {
[email protected]d7395e732009-08-28 23:13:431058 strings_map_type::iterator it = strings_table_.find(key);
1059 if (it == strings_table_.end())
1060 return false;
1061 *result = it->second;
1062 return true;
1063 }
1064
[email protected]c4c1b482011-07-22 17:24:261065 virtual bool BypassListIsReversed() OVERRIDE {
[email protected]a48bf4a2010-06-14 18:24:531066 return reversed_bypass_list_;
1067 }
1068
[email protected]c4c1b482011-07-22 17:24:261069 virtual bool MatchHostsUsingSuffixMatching() OVERRIDE {
[email protected]1a597192010-07-09 16:58:381070 return true;
1071 }
1072
[email protected]d7395e732009-08-28 23:13:431073 private:
1074 void ResetCachedSettings() {
1075 string_table_.clear();
1076 strings_table_.clear();
1077 indirect_manual_ = false;
1078 auto_no_pac_ = false;
[email protected]a48bf4a2010-06-14 18:24:531079 reversed_bypass_list_ = false;
[email protected]d7395e732009-08-28 23:13:431080 }
1081
[email protected]92d2dc82010-04-08 17:49:591082 FilePath KDEHomeToConfigPath(const FilePath& kde_home) {
1083 return kde_home.Append("share").Append("config");
1084 }
1085
[email protected]6b5fe742011-05-20 21:46:481086 void AddProxy(StringSetting host_key, const std::string& value) {
[email protected]d7395e732009-08-28 23:13:431087 if (value.empty() || value.substr(0, 3) == "//:")
1088 // No proxy.
1089 return;
[email protected]573c0502011-05-17 22:19:501090 // We don't need to parse the port number out; GetProxyFromSettings()
[email protected]d7395e732009-08-28 23:13:431091 // would only append it right back again. So we just leave the port
1092 // number right in the host string.
[email protected]573c0502011-05-17 22:19:501093 string_table_[host_key] = value;
[email protected]d7395e732009-08-28 23:13:431094 }
1095
[email protected]6b5fe742011-05-20 21:46:481096 void AddHostList(StringListSetting key, const std::string& value) {
[email protected]f18fde22010-05-18 23:49:541097 std::vector<std::string> tokens;
[email protected]1a597192010-07-09 16:58:381098 StringTokenizer tk(value, ", ");
[email protected]f18fde22010-05-18 23:49:541099 while (tk.GetNext()) {
1100 std::string token = tk.token();
1101 if (!token.empty())
1102 tokens.push_back(token);
1103 }
1104 strings_table_[key] = tokens;
1105 }
1106
[email protected]9a3d8d42009-09-03 17:01:461107 void AddKDESetting(const std::string& key, const std::string& value) {
[email protected]d7395e732009-08-28 23:13:431108 // The astute reader may notice that there is no mention of SOCKS
1109 // here. That's because KDE handles socks is a strange way, and we
1110 // don't support it. Rather than just a setting for the SOCKS server,
1111 // it has a setting for a library to LD_PRELOAD in all your programs
1112 // that will transparently SOCKSify them. Such libraries each have
1113 // their own configuration, and thus, we can't get it from KDE.
1114 if (key == "ProxyType") {
1115 const char* mode = "none";
1116 indirect_manual_ = false;
1117 auto_no_pac_ = false;
[email protected]e83326f2010-07-31 17:29:251118 int int_value;
1119 base::StringToInt(value, &int_value);
1120 switch (int_value) {
[email protected]d7395e732009-08-28 23:13:431121 case 0: // No proxy, or maybe kioslaverc syntax error.
1122 break;
1123 case 1: // Manual configuration.
1124 mode = "manual";
1125 break;
1126 case 2: // PAC URL.
1127 mode = "auto";
1128 break;
1129 case 3: // WPAD.
1130 mode = "auto";
1131 auto_no_pac_ = true;
1132 break;
1133 case 4: // Indirect manual via environment variables.
1134 mode = "manual";
1135 indirect_manual_ = true;
1136 break;
1137 }
[email protected]573c0502011-05-17 22:19:501138 string_table_[PROXY_MODE] = mode;
[email protected]d7395e732009-08-28 23:13:431139 } else if (key == "Proxy Config Script") {
[email protected]573c0502011-05-17 22:19:501140 string_table_[PROXY_AUTOCONF_URL] = value;
[email protected]d7395e732009-08-28 23:13:431141 } else if (key == "httpProxy") {
[email protected]573c0502011-05-17 22:19:501142 AddProxy(PROXY_HTTP_HOST, value);
[email protected]d7395e732009-08-28 23:13:431143 } else if (key == "httpsProxy") {
[email protected]573c0502011-05-17 22:19:501144 AddProxy(PROXY_HTTPS_HOST, value);
[email protected]d7395e732009-08-28 23:13:431145 } else if (key == "ftpProxy") {
[email protected]573c0502011-05-17 22:19:501146 AddProxy(PROXY_FTP_HOST, value);
[email protected]d7395e732009-08-28 23:13:431147 } else if (key == "ReversedException") {
1148 // We count "true" or any nonzero number as true, otherwise false.
1149 // Note that if the value is not actually numeric StringToInt()
1150 // will return 0, which we count as false.
[email protected]e83326f2010-07-31 17:29:251151 int int_value;
1152 base::StringToInt(value, &int_value);
1153 reversed_bypass_list_ = (value == "true" || int_value);
[email protected]d7395e732009-08-28 23:13:431154 } else if (key == "NoProxyFor") {
[email protected]573c0502011-05-17 22:19:501155 AddHostList(PROXY_IGNORE_HOSTS, value);
[email protected]d7395e732009-08-28 23:13:431156 } else if (key == "AuthMode") {
1157 // Check for authentication, just so we can warn.
[email protected]e83326f2010-07-31 17:29:251158 int mode;
1159 base::StringToInt(value, &mode);
[email protected]d7395e732009-08-28 23:13:431160 if (mode) {
1161 // ProxyConfig does not support authentication parameters, but
1162 // Chrome will prompt for the password later. So we ignore this.
1163 LOG(WARNING) <<
1164 "Proxy authentication parameters ignored, see bug 16709";
1165 }
1166 }
1167 }
1168
[email protected]6b5fe742011-05-20 21:46:481169 void ResolveIndirect(StringSetting key) {
[email protected]d7395e732009-08-28 23:13:431170 string_map_type::iterator it = string_table_.find(key);
1171 if (it != string_table_.end()) {
[email protected]f18fde22010-05-18 23:49:541172 std::string value;
[email protected]3ba7e082010-08-07 02:57:591173 if (env_var_getter_->GetVar(it->second.c_str(), &value))
[email protected]d7395e732009-08-28 23:13:431174 it->second = value;
[email protected]8425adc02010-04-18 17:45:311175 else
1176 string_table_.erase(it);
[email protected]d7395e732009-08-28 23:13:431177 }
1178 }
1179
[email protected]6b5fe742011-05-20 21:46:481180 void ResolveIndirectList(StringListSetting key) {
[email protected]f18fde22010-05-18 23:49:541181 strings_map_type::iterator it = strings_table_.find(key);
1182 if (it != strings_table_.end()) {
1183 std::string value;
1184 if (!it->second.empty() &&
[email protected]3ba7e082010-08-07 02:57:591185 env_var_getter_->GetVar(it->second[0].c_str(), &value))
[email protected]f18fde22010-05-18 23:49:541186 AddHostList(key, value);
1187 else
1188 strings_table_.erase(it);
1189 }
1190 }
1191
[email protected]d7395e732009-08-28 23:13:431192 // The settings in kioslaverc could occur in any order, but some affect
1193 // others. Rather than read the whole file in and then query them in an
1194 // order that allows us to handle that, we read the settings in whatever
1195 // order they occur and do any necessary tweaking after we finish.
1196 void ResolveModeEffects() {
1197 if (indirect_manual_) {
[email protected]573c0502011-05-17 22:19:501198 ResolveIndirect(PROXY_HTTP_HOST);
1199 ResolveIndirect(PROXY_HTTPS_HOST);
1200 ResolveIndirect(PROXY_FTP_HOST);
1201 ResolveIndirectList(PROXY_IGNORE_HOSTS);
[email protected]d7395e732009-08-28 23:13:431202 }
1203 if (auto_no_pac_) {
1204 // Remove the PAC URL; we're not supposed to use it.
[email protected]573c0502011-05-17 22:19:501205 string_table_.erase(PROXY_AUTOCONF_URL);
[email protected]d7395e732009-08-28 23:13:431206 }
[email protected]d7395e732009-08-28 23:13:431207 }
1208
1209 // Reads kioslaverc one line at a time and calls AddKDESetting() to add
1210 // each relevant name-value pair to the appropriate value table.
1211 void UpdateCachedSettings() {
[email protected]92d2dc82010-04-08 17:49:591212 FilePath kioslaverc = kde_config_dir_.Append("kioslaverc");
[email protected]d7395e732009-08-28 23:13:431213 file_util::ScopedFILE input(file_util::OpenFile(kioslaverc, "r"));
1214 if (!input.get())
1215 return;
1216 ResetCachedSettings();
1217 bool in_proxy_settings = false;
1218 bool line_too_long = false;
[email protected]9a3d8d42009-09-03 17:01:461219 char line[BUFFER_SIZE];
1220 // fgets() will return NULL on EOF or error.
[email protected]d7395e732009-08-28 23:13:431221 while (fgets(line, sizeof(line), input.get())) {
1222 // fgets() guarantees the line will be properly terminated.
1223 size_t length = strlen(line);
1224 if (!length)
1225 continue;
1226 // This should be true even with CRLF endings.
1227 if (line[length - 1] != '\n') {
1228 line_too_long = true;
1229 continue;
1230 }
1231 if (line_too_long) {
1232 // The previous line had no line ending, but this done does. This is
1233 // the end of the line that was too long, so warn here and skip it.
1234 LOG(WARNING) << "skipped very long line in " << kioslaverc.value();
1235 line_too_long = false;
1236 continue;
1237 }
1238 // Remove the LF at the end, and the CR if there is one.
1239 line[--length] = '\0';
1240 if (length && line[length - 1] == '\r')
1241 line[--length] = '\0';
1242 // Now parse the line.
1243 if (line[0] == '[') {
1244 // Switching sections. All we care about is whether this is
1245 // the (a?) proxy settings section, for both KDE3 and KDE4.
1246 in_proxy_settings = !strncmp(line, "[Proxy Settings]", 16);
1247 } else if (in_proxy_settings) {
1248 // A regular line, in the (a?) proxy settings section.
[email protected]9a3d8d42009-09-03 17:01:461249 char* split = strchr(line, '=');
1250 // Skip this line if it does not contain an = sign.
1251 if (!split)
[email protected]d7395e732009-08-28 23:13:431252 continue;
[email protected]9a3d8d42009-09-03 17:01:461253 // Split the line on the = and advance |split|.
1254 *(split++) = 0;
1255 std::string key = line;
1256 std::string value = split;
1257 TrimWhitespaceASCII(key, TRIM_ALL, &key);
1258 TrimWhitespaceASCII(value, TRIM_ALL, &value);
1259 // Skip this line if the key name is empty.
1260 if (key.empty())
[email protected]d7395e732009-08-28 23:13:431261 continue;
1262 // Is the value name localized?
[email protected]9a3d8d42009-09-03 17:01:461263 if (key[key.length() - 1] == ']') {
1264 // Find the matching bracket.
1265 length = key.rfind('[');
1266 // Skip this line if the localization indicator is malformed.
1267 if (length == std::string::npos)
[email protected]d7395e732009-08-28 23:13:431268 continue;
1269 // Trim the localization indicator off.
[email protected]9a3d8d42009-09-03 17:01:461270 key.resize(length);
1271 // Remove any resulting trailing whitespace.
1272 TrimWhitespaceASCII(key, TRIM_TRAILING, &key);
1273 // Skip this line if the key name is now empty.
1274 if (key.empty())
1275 continue;
[email protected]d7395e732009-08-28 23:13:431276 }
[email protected]d7395e732009-08-28 23:13:431277 // Now fill in the tables.
[email protected]9a3d8d42009-09-03 17:01:461278 AddKDESetting(key, value);
[email protected]d7395e732009-08-28 23:13:431279 }
1280 }
1281 if (ferror(input.get()))
1282 LOG(ERROR) << "error reading " << kioslaverc.value();
1283 ResolveModeEffects();
1284 }
1285
1286 // This is the callback from the debounce timer.
1287 void OnDebouncedNotification() {
1288 DCHECK(MessageLoop::current() == file_loop_);
[email protected]b30a3f52010-10-16 01:05:461289 VLOG(1) << "inotify change notification for kioslaverc";
[email protected]d7395e732009-08-28 23:13:431290 UpdateCachedSettings();
[email protected]961ac942011-04-28 18:18:141291 CHECK(notify_delegate_);
[email protected]d7395e732009-08-28 23:13:431292 // Forward to a method on the proxy config service delegate object.
1293 notify_delegate_->OnCheckProxyConfigSettings();
1294 }
1295
1296 // Called by OnFileCanReadWithoutBlocking() on the file thread. Reads
1297 // from the inotify file descriptor and starts up a debounce timer if
1298 // an event for kioslaverc is seen.
1299 void OnChangeNotification() {
1300 DCHECK(inotify_fd_ >= 0);
1301 DCHECK(MessageLoop::current() == file_loop_);
1302 char event_buf[(sizeof(inotify_event) + NAME_MAX + 1) * 4];
1303 bool kioslaverc_touched = false;
1304 ssize_t r;
1305 while ((r = read(inotify_fd_, event_buf, sizeof(event_buf))) > 0) {
1306 // inotify returns variable-length structures, which is why we have
1307 // this strange-looking loop instead of iterating through an array.
1308 char* event_ptr = event_buf;
1309 while (event_ptr < event_buf + r) {
1310 inotify_event* event = reinterpret_cast<inotify_event*>(event_ptr);
1311 // The kernel always feeds us whole events.
[email protected]b1f031dd2010-03-02 23:19:331312 CHECK_LE(event_ptr + sizeof(inotify_event), event_buf + r);
1313 CHECK_LE(event->name + event->len, event_buf + r);
[email protected]d7395e732009-08-28 23:13:431314 if (!strcmp(event->name, "kioslaverc"))
1315 kioslaverc_touched = true;
1316 // Advance the pointer just past the end of the filename.
1317 event_ptr = event->name + event->len;
1318 }
1319 // We keep reading even if |kioslaverc_touched| is true to drain the
1320 // inotify event queue.
1321 }
1322 if (!r)
1323 // Instead of returning -1 and setting errno to EINVAL if there is not
1324 // enough buffer space, older kernels (< 2.6.21) return 0. Simulate the
1325 // new behavior (EINVAL) so we can reuse the code below.
1326 errno = EINVAL;
1327 if (errno != EAGAIN) {
[email protected]57b765672009-10-13 18:27:401328 PLOG(WARNING) << "error reading inotify file descriptor";
[email protected]d7395e732009-08-28 23:13:431329 if (errno == EINVAL) {
1330 // Our buffer is not large enough to read the next event. This should
1331 // not happen (because its size is calculated to always be sufficiently
1332 // large), but if it does we'd warn continuously since |inotify_fd_|
1333 // would be forever ready to read. Close it and stop watching instead.
1334 LOG(ERROR) << "inotify failure; no longer watching kioslaverc!";
1335 inotify_watcher_.StopWatchingFileDescriptor();
1336 close(inotify_fd_);
1337 inotify_fd_ = -1;
1338 }
1339 }
1340 if (kioslaverc_touched) {
1341 // We don't use Reset() because the timer may not yet be running.
1342 // (In that case Stop() is a no-op.)
1343 debounce_timer_.Stop();
1344 debounce_timer_.Start(base::TimeDelta::FromMilliseconds(
1345 kDebounceTimeoutMilliseconds), this,
[email protected]573c0502011-05-17 22:19:501346 &SettingGetterImplKDE::OnDebouncedNotification);
[email protected]d7395e732009-08-28 23:13:431347 }
1348 }
1349
[email protected]6b5fe742011-05-20 21:46:481350 typedef std::map<StringSetting, std::string> string_map_type;
1351 typedef std::map<StringListSetting,
1352 std::vector<std::string> > strings_map_type;
[email protected]d7395e732009-08-28 23:13:431353
1354 int inotify_fd_;
1355 base::MessagePumpLibevent::FileDescriptorWatcher inotify_watcher_;
1356 ProxyConfigServiceLinux::Delegate* notify_delegate_;
[email protected]573c0502011-05-17 22:19:501357 base::OneShotTimer<SettingGetterImplKDE> debounce_timer_;
[email protected]d7395e732009-08-28 23:13:431358 FilePath kde_config_dir_;
1359 bool indirect_manual_;
1360 bool auto_no_pac_;
[email protected]a48bf4a2010-06-14 18:24:531361 bool reversed_bypass_list_;
[email protected]f18fde22010-05-18 23:49:541362 // We don't own |env_var_getter_|. It's safe to hold a pointer to it, since
1363 // both it and us are owned by ProxyConfigServiceLinux::Delegate, and have the
1364 // same lifetime.
[email protected]76b90d312010-08-03 03:00:501365 base::Environment* env_var_getter_;
[email protected]d7395e732009-08-28 23:13:431366
1367 // We cache these settings whenever we re-read the kioslaverc file.
1368 string_map_type string_table_;
1369 strings_map_type strings_table_;
1370
1371 // Message loop of the file thread, for reading kioslaverc. If NULL,
1372 // just read it directly (for testing). We also handle inotify events
1373 // on this thread.
1374 MessageLoopForIO* file_loop_;
1375
[email protected]573c0502011-05-17 22:19:501376 DISALLOW_COPY_AND_ASSIGN(SettingGetterImplKDE);
[email protected]861c6c62009-04-20 16:50:561377};
1378
1379} // namespace
1380
[email protected]573c0502011-05-17 22:19:501381bool ProxyConfigServiceLinux::Delegate::GetProxyFromSettings(
[email protected]6b5fe742011-05-20 21:46:481382 SettingGetter::StringSetting host_key,
[email protected]573c0502011-05-17 22:19:501383 ProxyServer* result_server) {
[email protected]861c6c62009-04-20 16:50:561384 std::string host;
[email protected]573c0502011-05-17 22:19:501385 if (!setting_getter_->GetString(host_key, &host) || host.empty()) {
[email protected]861c6c62009-04-20 16:50:561386 // Unset or empty.
1387 return false;
1388 }
1389 // Check for an optional port.
[email protected]d7395e732009-08-28 23:13:431390 int port = 0;
[email protected]6b5fe742011-05-20 21:46:481391 SettingGetter::IntSetting port_key =
[email protected]573c0502011-05-17 22:19:501392 SettingGetter::HostSettingToPortSetting(host_key);
1393 setting_getter_->GetInt(port_key, &port);
[email protected]861c6c62009-04-20 16:50:561394 if (port != 0) {
1395 // If a port is set and non-zero:
[email protected]528c56d2010-07-30 19:28:441396 host += ":" + base::IntToString(port);
[email protected]861c6c62009-04-20 16:50:561397 }
[email protected]76960f3d2011-04-30 02:15:231398
[email protected]573c0502011-05-17 22:19:501399 // gconf settings do not appear to distinguish between SOCKS version. We
1400 // default to version 5. For more information on this policy decision, see:
[email protected]76960f3d2011-04-30 02:15:231401 // http://code.google.com/p/chromium/issues/detail?id=55912#c2
[email protected]573c0502011-05-17 22:19:501402 ProxyServer::Scheme scheme = (host_key == SettingGetter::PROXY_SOCKS_HOST) ?
1403 ProxyServer::SCHEME_SOCKS5 : ProxyServer::SCHEME_HTTP;
1404 host = FixupProxyHostScheme(scheme, host);
[email protected]87a102b2009-07-14 05:23:301405 ProxyServer proxy_server = ProxyServer::FromURI(host,
1406 ProxyServer::SCHEME_HTTP);
[email protected]861c6c62009-04-20 16:50:561407 if (proxy_server.is_valid()) {
1408 *result_server = proxy_server;
1409 return true;
1410 }
1411 return false;
1412}
1413
[email protected]573c0502011-05-17 22:19:501414bool ProxyConfigServiceLinux::Delegate::GetConfigFromSettings(
[email protected]3e44697f2009-05-22 14:37:391415 ProxyConfig* config) {
[email protected]861c6c62009-04-20 16:50:561416 std::string mode;
[email protected]573c0502011-05-17 22:19:501417 if (!setting_getter_->GetString(SettingGetter::PROXY_MODE, &mode)) {
[email protected]861c6c62009-04-20 16:50:561418 // We expect this to always be set, so if we don't see it then we
[email protected]573c0502011-05-17 22:19:501419 // probably have a gconf/gsettings problem, and so we don't have a valid
[email protected]861c6c62009-04-20 16:50:561420 // proxy config.
1421 return false;
1422 }
[email protected]3e44697f2009-05-22 14:37:391423 if (mode == "none") {
[email protected]861c6c62009-04-20 16:50:561424 // Specifically specifies no proxy.
1425 return true;
[email protected]3e44697f2009-05-22 14:37:391426 }
[email protected]861c6c62009-04-20 16:50:561427
[email protected]3e44697f2009-05-22 14:37:391428 if (mode == "auto") {
[email protected]861c6c62009-04-20 16:50:561429 // automatic proxy config
1430 std::string pac_url_str;
[email protected]573c0502011-05-17 22:19:501431 if (setting_getter_->GetString(SettingGetter::PROXY_AUTOCONF_URL,
1432 &pac_url_str)) {
[email protected]861c6c62009-04-20 16:50:561433 if (!pac_url_str.empty()) {
1434 GURL pac_url(pac_url_str);
1435 if (!pac_url.is_valid())
1436 return false;
[email protected]ed4ed0f2010-02-24 00:20:481437 config->set_pac_url(pac_url);
[email protected]861c6c62009-04-20 16:50:561438 return true;
1439 }
1440 }
[email protected]ed4ed0f2010-02-24 00:20:481441 config->set_auto_detect(true);
[email protected]861c6c62009-04-20 16:50:561442 return true;
1443 }
1444
[email protected]3e44697f2009-05-22 14:37:391445 if (mode != "manual") {
[email protected]861c6c62009-04-20 16:50:561446 // Mode is unrecognized.
1447 return false;
1448 }
1449 bool use_http_proxy;
[email protected]573c0502011-05-17 22:19:501450 if (setting_getter_->GetBool(SettingGetter::PROXY_USE_HTTP_PROXY,
1451 &use_http_proxy)
[email protected]861c6c62009-04-20 16:50:561452 && !use_http_proxy) {
1453 // Another master switch for some reason. If set to false, then no
1454 // proxy. But we don't panic if the key doesn't exist.
1455 return true;
1456 }
1457
1458 bool same_proxy = false;
1459 // Indicates to use the http proxy for all protocols. This one may
[email protected]573c0502011-05-17 22:19:501460 // not exist (presumably on older versions); we assume false in that
[email protected]861c6c62009-04-20 16:50:561461 // case.
[email protected]573c0502011-05-17 22:19:501462 setting_getter_->GetBool(SettingGetter::PROXY_USE_SAME_PROXY,
1463 &same_proxy);
[email protected]861c6c62009-04-20 16:50:561464
[email protected]76960f3d2011-04-30 02:15:231465 ProxyServer proxy_for_http;
1466 ProxyServer proxy_for_https;
1467 ProxyServer proxy_for_ftp;
1468 ProxyServer socks_proxy; // (socks)
1469
1470 // This counts how many of the above ProxyServers were defined and valid.
1471 size_t num_proxies_specified = 0;
1472
1473 // Extract the per-scheme proxies. If we failed to parse it, or no proxy was
1474 // specified for the scheme, then the resulting ProxyServer will be invalid.
[email protected]573c0502011-05-17 22:19:501475 if (GetProxyFromSettings(SettingGetter::PROXY_HTTP_HOST, &proxy_for_http))
[email protected]76960f3d2011-04-30 02:15:231476 num_proxies_specified++;
[email protected]573c0502011-05-17 22:19:501477 if (GetProxyFromSettings(SettingGetter::PROXY_HTTPS_HOST, &proxy_for_https))
[email protected]76960f3d2011-04-30 02:15:231478 num_proxies_specified++;
[email protected]573c0502011-05-17 22:19:501479 if (GetProxyFromSettings(SettingGetter::PROXY_FTP_HOST, &proxy_for_ftp))
[email protected]76960f3d2011-04-30 02:15:231480 num_proxies_specified++;
[email protected]573c0502011-05-17 22:19:501481 if (GetProxyFromSettings(SettingGetter::PROXY_SOCKS_HOST, &socks_proxy))
[email protected]76960f3d2011-04-30 02:15:231482 num_proxies_specified++;
1483
1484 if (same_proxy) {
1485 if (proxy_for_http.is_valid()) {
1486 // Use the http proxy for all schemes.
[email protected]ed4ed0f2010-02-24 00:20:481487 config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
[email protected]76960f3d2011-04-30 02:15:231488 config->proxy_rules().single_proxy = proxy_for_http;
[email protected]861c6c62009-04-20 16:50:561489 }
[email protected]76960f3d2011-04-30 02:15:231490 } else if (num_proxies_specified > 0) {
1491 if (socks_proxy.is_valid() && num_proxies_specified == 1) {
1492 // If the only proxy specified was for SOCKS, use it for all schemes.
1493 config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
1494 config->proxy_rules().single_proxy = socks_proxy;
[email protected]861c6c62009-04-20 16:50:561495 } else {
[email protected]76960f3d2011-04-30 02:15:231496 // Otherwise use the indicate proxies per-scheme.
1497 config->proxy_rules().type =
1498 ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
1499 config->proxy_rules().proxy_for_http = proxy_for_http;
1500 config->proxy_rules().proxy_for_https = proxy_for_https;
1501 config->proxy_rules().proxy_for_ftp = proxy_for_ftp;
1502 config->proxy_rules().fallback_proxy = socks_proxy;
[email protected]861c6c62009-04-20 16:50:561503 }
1504 }
1505
[email protected]ed4ed0f2010-02-24 00:20:481506 if (config->proxy_rules().empty()) {
[email protected]861c6c62009-04-20 16:50:561507 // Manual mode but we couldn't parse any rules.
1508 return false;
1509 }
1510
1511 // Check for authentication, just so we can warn.
[email protected]d7395e732009-08-28 23:13:431512 bool use_auth = false;
[email protected]573c0502011-05-17 22:19:501513 setting_getter_->GetBool(SettingGetter::PROXY_USE_AUTHENTICATION,
1514 &use_auth);
[email protected]62749f182009-07-15 13:16:541515 if (use_auth) {
1516 // ProxyConfig does not support authentication parameters, but
1517 // Chrome will prompt for the password later. So we ignore
1518 // /system/http_proxy/*auth* settings.
1519 LOG(WARNING) << "Proxy authentication parameters ignored, see bug 16709";
1520 }
[email protected]861c6c62009-04-20 16:50:561521
1522 // Now the bypass list.
[email protected]7541206c2010-02-19 20:24:061523 std::vector<std::string> ignore_hosts_list;
[email protected]ed4ed0f2010-02-24 00:20:481524 config->proxy_rules().bypass_rules.Clear();
[email protected]573c0502011-05-17 22:19:501525 if (setting_getter_->GetStringList(SettingGetter::PROXY_IGNORE_HOSTS,
1526 &ignore_hosts_list)) {
[email protected]a8185d02010-06-11 00:19:501527 std::vector<std::string>::const_iterator it(ignore_hosts_list.begin());
[email protected]1a597192010-07-09 16:58:381528 for (; it != ignore_hosts_list.end(); ++it) {
[email protected]573c0502011-05-17 22:19:501529 if (setting_getter_->MatchHostsUsingSuffixMatching()) {
[email protected]1a597192010-07-09 16:58:381530 config->proxy_rules().bypass_rules.
1531 AddRuleFromStringUsingSuffixMatching(*it);
1532 } else {
1533 config->proxy_rules().bypass_rules.AddRuleFromString(*it);
1534 }
1535 }
[email protected]a8185d02010-06-11 00:19:501536 }
[email protected]861c6c62009-04-20 16:50:561537 // Note that there are no settings with semantics corresponding to
[email protected]1a597192010-07-09 16:58:381538 // bypass of local names in GNOME. In KDE, "<local>" is supported
1539 // as a hostname rule.
[email protected]861c6c62009-04-20 16:50:561540
[email protected]a48bf4a2010-06-14 18:24:531541 // KDE allows one to reverse the bypass rules.
[email protected]573c0502011-05-17 22:19:501542 config->proxy_rules().reverse_bypass =
1543 setting_getter_->BypassListIsReversed();
[email protected]a48bf4a2010-06-14 18:24:531544
[email protected]861c6c62009-04-20 16:50:561545 return true;
1546}
1547
[email protected]76b90d312010-08-03 03:00:501548ProxyConfigServiceLinux::Delegate::Delegate(base::Environment* env_var_getter)
[email protected]d7395e732009-08-28 23:13:431549 : env_var_getter_(env_var_getter),
1550 glib_default_loop_(NULL), io_loop_(NULL) {
[email protected]573c0502011-05-17 22:19:501551 // Figure out which SettingGetterImpl to use, if any.
[email protected]6b0349ef2010-10-16 04:56:061552 switch (base::nix::GetDesktopEnvironment(env_var_getter)) {
1553 case base::nix::DESKTOP_ENVIRONMENT_GNOME:
[email protected]8c20e3d2011-05-19 21:03:571554#if defined(USE_GIO)
1555 {
1556 scoped_ptr<SettingGetterImplGSettings> gs_getter(
1557 new SettingGetterImplGSettings());
1558 // We have to load symbols and check the GNOME version in use to decide
1559 // if we should use the gsettings getter. See LoadAndCheckVersion().
1560 if (gs_getter->LoadAndCheckVersion(env_var_getter))
1561 setting_getter_.reset(gs_getter.release());
1562 }
1563#endif
[email protected]6de53d42010-11-09 07:33:191564#if defined(USE_GCONF)
[email protected]8c20e3d2011-05-19 21:03:571565 // Fall back on gconf if gsettings is unavailable or incorrect.
1566 if (!setting_getter_.get())
1567 setting_getter_.reset(new SettingGetterImplGConf());
[email protected]6de53d42010-11-09 07:33:191568#endif
[email protected]d7395e732009-08-28 23:13:431569 break;
[email protected]6b0349ef2010-10-16 04:56:061570 case base::nix::DESKTOP_ENVIRONMENT_KDE3:
1571 case base::nix::DESKTOP_ENVIRONMENT_KDE4:
[email protected]573c0502011-05-17 22:19:501572 setting_getter_.reset(new SettingGetterImplKDE(env_var_getter));
[email protected]d7395e732009-08-28 23:13:431573 break;
[email protected]6b0349ef2010-10-16 04:56:061574 case base::nix::DESKTOP_ENVIRONMENT_XFCE:
1575 case base::nix::DESKTOP_ENVIRONMENT_OTHER:
[email protected]d7395e732009-08-28 23:13:431576 break;
1577 }
1578}
1579
[email protected]573c0502011-05-17 22:19:501580ProxyConfigServiceLinux::Delegate::Delegate(
1581 base::Environment* env_var_getter, SettingGetter* setting_getter)
1582 : env_var_getter_(env_var_getter), setting_getter_(setting_getter),
[email protected]3e44697f2009-05-22 14:37:391583 glib_default_loop_(NULL), io_loop_(NULL) {
[email protected]861c6c62009-04-20 16:50:561584}
1585
[email protected]d3066142011-05-10 02:36:201586void ProxyConfigServiceLinux::Delegate::SetUpAndFetchInitialConfig(
[email protected]d7395e732009-08-28 23:13:431587 MessageLoop* glib_default_loop, MessageLoop* io_loop,
1588 MessageLoopForIO* file_loop) {
[email protected]3e44697f2009-05-22 14:37:391589 // We should be running on the default glib main loop thread right
1590 // now. gconf can only be accessed from this thread.
1591 DCHECK(MessageLoop::current() == glib_default_loop);
1592 glib_default_loop_ = glib_default_loop;
1593 io_loop_ = io_loop;
1594
[email protected]d7395e732009-08-28 23:13:431595 // If we are passed a NULL io_loop or file_loop, then we don't set up
1596 // proxy setting change notifications. This should not be the usual
1597 // case but is intended to simplify test setups.
1598 if (!io_loop_ || !file_loop)
[email protected]b30a3f52010-10-16 01:05:461599 VLOG(1) << "Monitoring of proxy setting changes is disabled";
[email protected]3e44697f2009-05-22 14:37:391600
1601 // Fetch and cache the current proxy config. The config is left in
[email protected]119655002010-07-23 06:02:401602 // cached_config_, where GetLatestProxyConfig() running on the IO thread
[email protected]3e44697f2009-05-22 14:37:391603 // will expect to find it. This is safe to do because we return
1604 // before this ProxyConfigServiceLinux is passed on to
1605 // the ProxyService.
[email protected]d6cb85b2009-07-23 22:10:531606
1607 // Note: It would be nice to prioritize environment variables
[email protected]92d2dc82010-04-08 17:49:591608 // and only fall back to gconf if env vars were unset. But
[email protected]d6cb85b2009-07-23 22:10:531609 // gnome-terminal "helpfully" sets http_proxy and no_proxy, and it
1610 // does so even if the proxy mode is set to auto, which would
1611 // mislead us.
1612
[email protected]3e44697f2009-05-22 14:37:391613 bool got_config = false;
[email protected]573c0502011-05-17 22:19:501614 if (setting_getter_.get() &&
1615 setting_getter_->Init(glib_default_loop, file_loop) &&
1616 GetConfigFromSettings(&cached_config_)) {
[email protected]d3066142011-05-10 02:36:201617 cached_config_.set_id(1); // Mark it as valid.
1618 VLOG(1) << "Obtained proxy settings from "
[email protected]573c0502011-05-17 22:19:501619 << setting_getter_->GetDataSource();
[email protected]d3066142011-05-10 02:36:201620
1621 // If gconf proxy mode is "none", meaning direct, then we take
1622 // that to be a valid config and will not check environment
1623 // variables. The alternative would have been to look for a proxy
1624 // whereever we can find one.
1625 got_config = true;
1626
1627 // Keep a copy of the config for use from this thread for
1628 // comparison with updated settings when we get notifications.
1629 reference_config_ = cached_config_;
1630 reference_config_.set_id(1); // Mark it as valid.
1631
1632 // We only set up notifications if we have IO and file loops available.
1633 // We do this after getting the initial configuration so that we don't have
1634 // to worry about cancelling it if the initial fetch above fails. Note that
1635 // setting up notifications has the side effect of simulating a change, so
1636 // that we won't lose any updates that may have happened after the initial
1637 // fetch and before setting up notifications. We'll detect the common case
1638 // of no changes in OnCheckProxyConfigSettings() (or sooner) and ignore it.
1639 if (io_loop && file_loop) {
[email protected]573c0502011-05-17 22:19:501640 MessageLoop* required_loop = setting_getter_->GetNotificationLoop();
[email protected]d3066142011-05-10 02:36:201641 if (!required_loop || MessageLoop::current() == required_loop) {
1642 // In this case we are already on an acceptable thread.
1643 SetUpNotifications();
[email protected]d7395e732009-08-28 23:13:431644 } else {
[email protected]d3066142011-05-10 02:36:201645 // Post a task to set up notifications. We don't wait for success.
1646 required_loop->PostTask(
1647 FROM_HERE,
1648 NewRunnableMethod(
1649 this,
1650 &ProxyConfigServiceLinux::Delegate::SetUpNotifications));
[email protected]d6cb85b2009-07-23 22:10:531651 }
[email protected]d7395e732009-08-28 23:13:431652 }
[email protected]861c6c62009-04-20 16:50:561653 }
[email protected]d6cb85b2009-07-23 22:10:531654
[email protected]3e44697f2009-05-22 14:37:391655 if (!got_config) {
[email protected]d6cb85b2009-07-23 22:10:531656 // We fall back on environment variables.
[email protected]3e44697f2009-05-22 14:37:391657 //
[email protected]d3066142011-05-10 02:36:201658 // Consulting environment variables doesn't need to be done from the
1659 // default glib main loop, but it's a tiny enough amount of work.
[email protected]3e44697f2009-05-22 14:37:391660 if (GetConfigFromEnv(&cached_config_)) {
[email protected]d3066142011-05-10 02:36:201661 cached_config_.set_id(1); // Mark it as valid.
[email protected]b30a3f52010-10-16 01:05:461662 VLOG(1) << "Obtained proxy settings from environment variables";
[email protected]3e44697f2009-05-22 14:37:391663 }
[email protected]861c6c62009-04-20 16:50:561664 }
[email protected]3e44697f2009-05-22 14:37:391665}
1666
[email protected]573c0502011-05-17 22:19:501667// Depending on the SettingGetter in use, this method will be called
[email protected]d3066142011-05-10 02:36:201668// on either the UI thread (GConf) or the file thread (KDE).
1669void ProxyConfigServiceLinux::Delegate::SetUpNotifications() {
[email protected]573c0502011-05-17 22:19:501670 MessageLoop* required_loop = setting_getter_->GetNotificationLoop();
[email protected]d3066142011-05-10 02:36:201671 DCHECK(!required_loop || MessageLoop::current() == required_loop);
[email protected]573c0502011-05-17 22:19:501672 if (!setting_getter_->SetUpNotifications(this))
[email protected]d3066142011-05-10 02:36:201673 LOG(ERROR) << "Unable to set up proxy configuration change notifications";
1674}
1675
[email protected]119655002010-07-23 06:02:401676void ProxyConfigServiceLinux::Delegate::AddObserver(Observer* observer) {
1677 observers_.AddObserver(observer);
1678}
1679
1680void ProxyConfigServiceLinux::Delegate::RemoveObserver(Observer* observer) {
1681 observers_.RemoveObserver(observer);
1682}
1683
[email protected]3a29593d2011-04-11 10:07:521684ProxyConfigService::ConfigAvailability
1685 ProxyConfigServiceLinux::Delegate::GetLatestProxyConfig(
1686 ProxyConfig* config) {
[email protected]3e44697f2009-05-22 14:37:391687 // This is called from the IO thread.
1688 DCHECK(!io_loop_ || MessageLoop::current() == io_loop_);
1689
1690 // Simply return the last proxy configuration that glib_default_loop
1691 // notified us of.
[email protected]119655002010-07-23 06:02:401692 *config = cached_config_.is_valid() ?
1693 cached_config_ : ProxyConfig::CreateDirect();
1694
[email protected]3a29593d2011-04-11 10:07:521695 // We return CONFIG_VALID to indicate that *config was filled in. It is always
[email protected]119655002010-07-23 06:02:401696 // going to be available since we initialized eagerly on the UI thread.
1697 // TODO(eroman): do lazy initialization instead, so we no longer need
1698 // to construct ProxyConfigServiceLinux on the UI thread.
1699 // In which case, we may return false here.
[email protected]3a29593d2011-04-11 10:07:521700 return CONFIG_VALID;
[email protected]3e44697f2009-05-22 14:37:391701}
1702
[email protected]573c0502011-05-17 22:19:501703// Depending on the SettingGetter in use, this method will be called
[email protected]d7395e732009-08-28 23:13:431704// on either the UI thread (GConf) or the file thread (KDE).
[email protected]3e44697f2009-05-22 14:37:391705void ProxyConfigServiceLinux::Delegate::OnCheckProxyConfigSettings() {
[email protected]573c0502011-05-17 22:19:501706 MessageLoop* required_loop = setting_getter_->GetNotificationLoop();
[email protected]d7395e732009-08-28 23:13:431707 DCHECK(!required_loop || MessageLoop::current() == required_loop);
[email protected]3e44697f2009-05-22 14:37:391708 ProxyConfig new_config;
[email protected]573c0502011-05-17 22:19:501709 bool valid = GetConfigFromSettings(&new_config);
[email protected]3e44697f2009-05-22 14:37:391710 if (valid)
1711 new_config.set_id(1); // mark it as valid
1712
[email protected]119655002010-07-23 06:02:401713 // See if it is different from what we had before.
[email protected]3e44697f2009-05-22 14:37:391714 if (new_config.is_valid() != reference_config_.is_valid() ||
1715 !new_config.Equals(reference_config_)) {
1716 // Post a task to |io_loop| with the new configuration, so it can
1717 // update |cached_config_|.
1718 io_loop_->PostTask(
1719 FROM_HERE,
1720 NewRunnableMethod(
1721 this,
1722 &ProxyConfigServiceLinux::Delegate::SetNewProxyConfig,
1723 new_config));
[email protected]d1f9d472009-08-13 19:59:301724 // Update the thread-private copy in |reference_config_| as well.
1725 reference_config_ = new_config;
[email protected]d3066142011-05-10 02:36:201726 } else {
1727 VLOG(1) << "Detected no-op change to proxy settings. Doing nothing.";
[email protected]3e44697f2009-05-22 14:37:391728 }
1729}
1730
1731void ProxyConfigServiceLinux::Delegate::SetNewProxyConfig(
1732 const ProxyConfig& new_config) {
1733 DCHECK(MessageLoop::current() == io_loop_);
[email protected]b30a3f52010-10-16 01:05:461734 VLOG(1) << "Proxy configuration changed";
[email protected]3e44697f2009-05-22 14:37:391735 cached_config_ = new_config;
[email protected]3a29593d2011-04-11 10:07:521736 FOR_EACH_OBSERVER(
1737 Observer, observers_,
1738 OnProxyConfigChanged(new_config, ProxyConfigService::CONFIG_VALID));
[email protected]3e44697f2009-05-22 14:37:391739}
1740
1741void ProxyConfigServiceLinux::Delegate::PostDestroyTask() {
[email protected]573c0502011-05-17 22:19:501742 if (!setting_getter_.get())
[email protected]d7395e732009-08-28 23:13:431743 return;
[email protected]573c0502011-05-17 22:19:501744 MessageLoop* shutdown_loop = setting_getter_->GetNotificationLoop();
[email protected]d7395e732009-08-28 23:13:431745 if (!shutdown_loop || MessageLoop::current() == shutdown_loop) {
[email protected]3e44697f2009-05-22 14:37:391746 // Already on the right thread, call directly.
1747 // This is the case for the unittests.
1748 OnDestroy();
1749 } else {
[email protected]d7395e732009-08-28 23:13:431750 // Post to shutdown thread. Note that on browser shutdown, we may quit
1751 // this MessageLoop and exit the program before ever running this.
1752 shutdown_loop->PostTask(
[email protected]3e44697f2009-05-22 14:37:391753 FROM_HERE,
1754 NewRunnableMethod(
1755 this,
1756 &ProxyConfigServiceLinux::Delegate::OnDestroy));
1757 }
1758}
1759void ProxyConfigServiceLinux::Delegate::OnDestroy() {
[email protected]573c0502011-05-17 22:19:501760 MessageLoop* shutdown_loop = setting_getter_->GetNotificationLoop();
[email protected]d7395e732009-08-28 23:13:431761 DCHECK(!shutdown_loop || MessageLoop::current() == shutdown_loop);
[email protected]573c0502011-05-17 22:19:501762 setting_getter_->ShutDown();
[email protected]3e44697f2009-05-22 14:37:391763}
1764
1765ProxyConfigServiceLinux::ProxyConfigServiceLinux()
[email protected]76b90d312010-08-03 03:00:501766 : delegate_(new Delegate(base::Environment::Create())) {
[email protected]3e44697f2009-05-22 14:37:391767}
1768
[email protected]8e1845e12010-09-15 19:22:241769ProxyConfigServiceLinux::~ProxyConfigServiceLinux() {
1770 delegate_->PostDestroyTask();
1771}
1772
[email protected]3e44697f2009-05-22 14:37:391773ProxyConfigServiceLinux::ProxyConfigServiceLinux(
[email protected]76b90d312010-08-03 03:00:501774 base::Environment* env_var_getter)
[email protected]9a3d8d42009-09-03 17:01:461775 : delegate_(new Delegate(env_var_getter)) {
1776}
1777
1778ProxyConfigServiceLinux::ProxyConfigServiceLinux(
[email protected]573c0502011-05-17 22:19:501779 base::Environment* env_var_getter, SettingGetter* setting_getter)
1780 : delegate_(new Delegate(env_var_getter, setting_getter)) {
[email protected]861c6c62009-04-20 16:50:561781}
1782
[email protected]e4be2dd2010-12-14 00:44:391783void ProxyConfigServiceLinux::AddObserver(Observer* observer) {
1784 delegate_->AddObserver(observer);
1785}
1786
1787void ProxyConfigServiceLinux::RemoveObserver(Observer* observer) {
1788 delegate_->RemoveObserver(observer);
1789}
1790
[email protected]3a29593d2011-04-11 10:07:521791ProxyConfigService::ConfigAvailability
1792 ProxyConfigServiceLinux::GetLatestProxyConfig(ProxyConfig* config) {
[email protected]e4be2dd2010-12-14 00:44:391793 return delegate_->GetLatestProxyConfig(config);
1794}
1795
[email protected]861c6c62009-04-20 16:50:561796} // namespace net