blob: 7c53258bfc516e7468cd406c155ac291762367e4 [file] [log] [blame]
[email protected]861c6c62009-04-20 16:50:561// Copyright (c) 2009 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/proxy/proxy_config_service_linux.h"
6
7#include <gconf/gconf-client.h>
[email protected]861c6c62009-04-20 16:50:568#include <stdlib.h>
9
10#include "base/logging.h"
11#include "base/string_tokenizer.h"
12#include "base/string_util.h"
[email protected]3e44697f2009-05-22 14:37:3913#include "base/task.h"
[email protected]861c6c62009-04-20 16:50:5614#include "googleurl/src/url_canon.h"
15#include "net/base/net_errors.h"
16#include "net/http/http_util.h"
17#include "net/proxy/proxy_config.h"
18#include "net/proxy/proxy_server.h"
19
20namespace net {
21
22namespace {
23
24class EnvironmentVariableGetterImpl
25 : public ProxyConfigServiceLinux::EnvironmentVariableGetter {
26 public:
27 virtual bool Getenv(const char* variable_name, std::string* result) {
28 const char* env_value = ::getenv(variable_name);
29 if (env_value) {
30 // Note that the variable may be defined but empty.
31 *result = env_value;
32 return true;
33 }
34 // Some commonly used variable names are uppercase while others
35 // are lowercase, which is inconsistent. Let's try to be helpful
36 // and look for a variable name with the reverse case.
37 char first_char = variable_name[0];
38 std::string alternate_case_var;
39 if (first_char >= 'a' && first_char <= 'z')
40 alternate_case_var = StringToUpperASCII(std::string(variable_name));
41 else if (first_char >= 'A' && first_char <= 'Z')
42 alternate_case_var = StringToLowerASCII(std::string(variable_name));
43 else
44 return false;
45 env_value = ::getenv(alternate_case_var.c_str());
46 if (env_value) {
47 *result = env_value;
48 return true;
49 }
50 return false;
51 }
52};
53
54// Given a proxy hostname from a setting, returns that hostname with
55// an appropriate proxy server scheme prefix.
56// scheme indicates the desired proxy scheme: usually http, with
57// socks 4 or 5 as special cases.
58std::string FixupProxyHostScheme(ProxyServer::Scheme scheme,
59 std::string host) {
60 if (scheme == ProxyServer::SCHEME_SOCKS4 &&
61 StartsWithASCII(host, "socks5://", false)) {
62 // We default to socks 4, but if the user specifically set it to
63 // socks5://, then use that.
64 scheme = ProxyServer::SCHEME_SOCKS5;
65 }
66 // Strip the scheme if any.
67 std::string::size_type colon = host.find("://");
68 if (colon != std::string::npos)
69 host = host.substr(colon + 3);
70 // If a username and perhaps password are specified, give a warning.
71 std::string::size_type at_sign = host.find("@");
72 // Should this be supported?
73 if (at_sign != std::string::npos) {
[email protected]3e44697f2009-05-22 14:37:3974 LOG(ERROR) << "Proxy authentication not supported";
[email protected]861c6c62009-04-20 16:50:5675 // Disregard the authentication parameters and continue with this hostname.
76 host = host.substr(at_sign + 1);
77 }
78 // If this is a socks proxy, prepend a scheme so as to tell
79 // ProxyServer. This also allows ProxyServer to choose the right
80 // default port.
81 if (scheme == ProxyServer::SCHEME_SOCKS4)
82 host = "socks4://" + host;
83 else if (scheme == ProxyServer::SCHEME_SOCKS5)
84 host = "socks5://" + host;
85 return host;
86}
87
88} // namespace
89
[email protected]3e44697f2009-05-22 14:37:3990bool ProxyConfigServiceLinux::Delegate::GetProxyFromEnvVarForScheme(
[email protected]861c6c62009-04-20 16:50:5691 const char* variable, ProxyServer::Scheme scheme,
92 ProxyServer* result_server) {
93 std::string env_value;
94 if (env_var_getter_->Getenv(variable, &env_value)) {
95 if (!env_value.empty()) {
96 env_value = FixupProxyHostScheme(scheme, env_value);
97 ProxyServer proxy_server = ProxyServer::FromURI(env_value);
98 if (proxy_server.is_valid() && !proxy_server.is_direct()) {
99 *result_server = proxy_server;
100 return true;
101 } else {
[email protected]3e44697f2009-05-22 14:37:39102 LOG(ERROR) << "Failed to parse environment variable " << variable;
[email protected]861c6c62009-04-20 16:50:56103 }
104 }
105 }
106 return false;
107}
108
[email protected]3e44697f2009-05-22 14:37:39109bool ProxyConfigServiceLinux::Delegate::GetProxyFromEnvVar(
[email protected]861c6c62009-04-20 16:50:56110 const char* variable, ProxyServer* result_server) {
111 return GetProxyFromEnvVarForScheme(variable, ProxyServer::SCHEME_HTTP,
112 result_server);
113}
114
[email protected]3e44697f2009-05-22 14:37:39115bool ProxyConfigServiceLinux::Delegate::GetConfigFromEnv(ProxyConfig* config) {
[email protected]861c6c62009-04-20 16:50:56116 // Check for automatic configuration first, in
117 // "auto_proxy". Possibly only the "environment_proxy" firefox
118 // extension has ever used this, but it still sounds like a good
119 // idea.
120 std::string auto_proxy;
121 if (env_var_getter_->Getenv("auto_proxy", &auto_proxy)) {
122 if (auto_proxy.empty()) {
123 // Defined and empty => autodetect
124 config->auto_detect = true;
125 } else {
126 // specified autoconfig URL
127 config->pac_url = GURL(auto_proxy);
128 }
129 return true;
130 }
131 // "all_proxy" is a shortcut to avoid defining {http,https,ftp}_proxy.
132 ProxyServer proxy_server;
133 if (GetProxyFromEnvVar("all_proxy", &proxy_server)) {
134 config->proxy_rules.type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
135 config->proxy_rules.single_proxy = proxy_server;
136 } else {
137 bool have_http = GetProxyFromEnvVar("http_proxy", &proxy_server);
138 if (have_http)
139 config->proxy_rules.proxy_for_http = proxy_server;
140 // It would be tempting to let http_proxy apply for all protocols
141 // if https_proxy and ftp_proxy are not defined. Googling turns up
142 // several documents that mention only http_proxy. But then the
143 // user really might not want to proxy https. And it doesn't seem
144 // like other apps do this. So we will refrain.
145 bool have_https = GetProxyFromEnvVar("https_proxy", &proxy_server);
146 if (have_https)
147 config->proxy_rules.proxy_for_https = proxy_server;
148 bool have_ftp = GetProxyFromEnvVar("ftp_proxy", &proxy_server);
149 if (have_ftp)
150 config->proxy_rules.proxy_for_ftp = proxy_server;
151 if (have_http || have_https || have_ftp) {
152 // mustn't change type unless some rules are actually set.
153 config->proxy_rules.type = ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
154 }
155 }
156 if (config->proxy_rules.empty()) {
157 // If the above were not defined, try for socks.
158 ProxyServer::Scheme scheme = ProxyServer::SCHEME_SOCKS4;
159 std::string env_version;
160 if (env_var_getter_->Getenv("SOCKS_VERSION", &env_version)
[email protected]3e44697f2009-05-22 14:37:39161 && env_version == "5")
[email protected]861c6c62009-04-20 16:50:56162 scheme = ProxyServer::SCHEME_SOCKS5;
163 if (GetProxyFromEnvVarForScheme("SOCKS_SERVER", scheme, &proxy_server)) {
164 config->proxy_rules.type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
165 config->proxy_rules.single_proxy = proxy_server;
166 }
167 }
168 // Look for the proxy bypass list.
169 std::string no_proxy;
170 env_var_getter_->Getenv("no_proxy", &no_proxy);
171 if (config->proxy_rules.empty()) {
172 // Having only "no_proxy" set, presumably to "*", makes it
173 // explicit that env vars do specify a configuration: having no
174 // rules specified only means the user explicitly asks for direct
175 // connections.
176 return !no_proxy.empty();
177 }
[email protected]ab501a6a2009-05-12 15:07:50178 config->ParseNoProxyList(no_proxy);
[email protected]861c6c62009-04-20 16:50:56179 return true;
180}
181
182namespace {
183
[email protected]3e44697f2009-05-22 14:37:39184// static
185// gconf notification callback, dispatched from the default
186// glib main loop.
187void OnGConfChangeNotification(
188 GConfClient* client, guint cnxn_id,
189 GConfEntry* entry, gpointer user_data) {
190 // It would be nice to debounce multiple callbacks in quick
191 // succession, since I guess we'll get one for each changed key. As
192 // it is we will read settings from gconf once for each callback.
193 LOG(INFO) << "gconf change notification for key "
194 << gconf_entry_get_key(entry);
195 // We don't track which key has changed, just that something did change.
196 // Forward to a method on the proxy config service delegate object.
197 ProxyConfigServiceLinux::Delegate* config_service_delegate =
198 reinterpret_cast<ProxyConfigServiceLinux::Delegate*>(user_data);
199 config_service_delegate->OnCheckProxyConfigSettings();
200}
201
[email protected]861c6c62009-04-20 16:50:56202class GConfSettingGetterImpl
203 : public ProxyConfigServiceLinux::GConfSettingGetter {
204 public:
[email protected]3e44697f2009-05-22 14:37:39205 GConfSettingGetterImpl() : client_(NULL), loop_(NULL) {}
206
[email protected]861c6c62009-04-20 16:50:56207 virtual ~GConfSettingGetterImpl() {
[email protected]3e44697f2009-05-22 14:37:39208 // client_ should have been released before now, from
[email protected]f5b13442009-07-13 15:23:59209 // Delegate::OnDestroy(), while running on the UI thread. However
210 // on exiting the process, it may happen that
211 // Delegate::OnDestroy() task is left pending on the glib loop
212 // after the loop was quit, and pending tasks may then be deleted
213 // without being run.
214 if (client_) {
215 // gconf client was not cleaned up.
216 if (MessageLoop::current() == loop_) {
217 // We are on the UI thread so we can clean it safely. This is
218 // the case at least for ui_tests running under Valgrind in
219 // bug 16076.
220 LOG(INFO) << "~GConfSettingGetterImpl: releasing gconf client";
221 Release();
222 } else {
223 LOG(WARNING) << "~GConfSettingGetterImpl: leaking gconf client";
224 client_ = NULL;
225 }
226 }
[email protected]3e44697f2009-05-22 14:37:39227 DCHECK(!client_);
[email protected]861c6c62009-04-20 16:50:56228 }
229
[email protected]3e44697f2009-05-22 14:37:39230 virtual bool Init() {
231 DCHECK(!client_);
232 DCHECK(!loop_);
233 loop_ = MessageLoopForUI::current();
234 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);
253 Release();
254 return false;
255 }
256 return true;
257 }
258
259 void Release() {
260 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
269 bool SetupNotification(void* callback_user_data) {
270 DCHECK(client_);
271 DCHECK(MessageLoop::current() == loop_);
272 GError* error = NULL;
273 gconf_client_notify_add(
274 client_, "/system/proxy",
275 OnGConfChangeNotification, callback_user_data,
276 NULL, &error);
277 if (error == NULL) {
278 gconf_client_notify_add(
279 client_, "/system/http_proxy",
280 OnGConfChangeNotification, callback_user_data,
281 NULL, &error);
282 }
283 if (error != NULL) {
284 LOG(ERROR) << "Error requesting gconf notifications: " << error->message;
285 g_error_free(error);
286 Release();
287 return false;
288 }
289 return true;
[email protected]861c6c62009-04-20 16:50:56290 }
291
292 virtual bool GetString(const char* key, std::string* result) {
[email protected]3e44697f2009-05-22 14:37:39293 DCHECK(client_);
294 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56295 GError* error = NULL;
296 gchar* value = gconf_client_get_string(client_, key, &error);
297 if (HandleGError(error, key))
298 return false;
299 if (!value)
300 return false;
301 *result = value;
302 g_free(value);
303 return true;
304 }
305 virtual bool GetBoolean(const char* key, bool* result) {
[email protected]3e44697f2009-05-22 14:37:39306 DCHECK(client_);
307 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56308 GError* error = NULL;
309 // We want to distinguish unset values from values defaulting to
310 // false. For that we need to use the type-generic
311 // gconf_client_get() rather than gconf_client_get_bool().
312 GConfValue* gconf_value = gconf_client_get(client_, key, &error);
313 if (HandleGError(error, key))
314 return false;
315 if (!gconf_value) {
316 // Unset.
317 return false;
318 }
319 if (gconf_value->type != GCONF_VALUE_BOOL) {
320 gconf_value_free(gconf_value);
321 return false;
322 }
323 gboolean bool_value = gconf_value_get_bool(gconf_value);
324 *result = static_cast<bool>(bool_value);
325 gconf_value_free(gconf_value);
326 return true;
327 }
328 virtual bool GetInt(const char* key, int* result) {
[email protected]3e44697f2009-05-22 14:37:39329 DCHECK(client_);
330 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56331 GError* error = NULL;
332 int value = gconf_client_get_int(client_, key, &error);
333 if (HandleGError(error, key))
334 return false;
335 // We don't bother to distinguish an unset value because callers
336 // don't care. 0 is returned if unset.
337 *result = value;
338 return true;
339 }
340 virtual bool GetStringList(const char* key,
341 std::vector<std::string>* result) {
[email protected]3e44697f2009-05-22 14:37:39342 DCHECK(client_);
343 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56344 GError* error = NULL;
345 GSList* list = gconf_client_get_list(client_, key,
346 GCONF_VALUE_STRING, &error);
347 if (HandleGError(error, key))
348 return false;
349 if (!list) {
350 // unset
351 return false;
352 }
353 for (GSList *it = list; it; it = it->next) {
354 result->push_back(static_cast<char*>(it->data));
355 g_free(it->data);
356 }
357 g_slist_free(list);
358 return true;
359 }
360
361 private:
362 // Logs and frees a glib error. Returns false if there was no error
363 // (error is NULL).
364 bool HandleGError(GError* error, const char* key) {
365 if (error != NULL) {
[email protected]3e44697f2009-05-22 14:37:39366 LOG(ERROR) << "Error getting gconf value for " << key
367 << ": " << error->message;
[email protected]861c6c62009-04-20 16:50:56368 g_error_free(error);
369 return true;
370 }
371 return false;
372 }
373
374 GConfClient* client_;
375
[email protected]3e44697f2009-05-22 14:37:39376 // Message loop of the thread that we make gconf calls on. It should
377 // be the UI thread and all our methods should be called on this
378 // thread. Only for assertions.
379 MessageLoop* loop_;
380
[email protected]861c6c62009-04-20 16:50:56381 DISALLOW_COPY_AND_ASSIGN(GConfSettingGetterImpl);
382};
383
384} // namespace
385
[email protected]3e44697f2009-05-22 14:37:39386bool ProxyConfigServiceLinux::Delegate::GetProxyFromGConf(
[email protected]861c6c62009-04-20 16:50:56387 const char* key_prefix, bool is_socks, ProxyServer* result_server) {
388 std::string key(key_prefix);
389 std::string host;
390 if (!gconf_getter_->GetString((key + "host").c_str(), &host)
391 || host.empty()) {
392 // Unset or empty.
393 return false;
394 }
395 // Check for an optional port.
396 int port;
397 gconf_getter_->GetInt((key + "port").c_str(), &port);
398 if (port != 0) {
399 // If a port is set and non-zero:
400 host += ":" + IntToString(port);
401 }
402 host = FixupProxyHostScheme(
403 is_socks ? ProxyServer::SCHEME_SOCKS4 : ProxyServer::SCHEME_HTTP,
404 host);
405 ProxyServer proxy_server = ProxyServer::FromURI(host);
406 if (proxy_server.is_valid()) {
407 *result_server = proxy_server;
408 return true;
409 }
410 return false;
411}
412
[email protected]3e44697f2009-05-22 14:37:39413bool ProxyConfigServiceLinux::Delegate::GetConfigFromGConf(
414 ProxyConfig* config) {
[email protected]861c6c62009-04-20 16:50:56415 std::string mode;
416 if (!gconf_getter_->GetString("/system/proxy/mode", &mode)) {
417 // We expect this to always be set, so if we don't see it then we
418 // probably have a gconf problem, and so we don't have a valid
419 // proxy config.
420 return false;
421 }
[email protected]3e44697f2009-05-22 14:37:39422 if (mode == "none") {
[email protected]861c6c62009-04-20 16:50:56423 // Specifically specifies no proxy.
424 return true;
[email protected]3e44697f2009-05-22 14:37:39425 }
[email protected]861c6c62009-04-20 16:50:56426
[email protected]3e44697f2009-05-22 14:37:39427 if (mode == "auto") {
[email protected]861c6c62009-04-20 16:50:56428 // automatic proxy config
429 std::string pac_url_str;
430 if (gconf_getter_->GetString("/system/proxy/autoconfig_url",
431 &pac_url_str)) {
432 if (!pac_url_str.empty()) {
433 GURL pac_url(pac_url_str);
434 if (!pac_url.is_valid())
435 return false;
436 config->pac_url = pac_url;
437 return true;
438 }
439 }
440 config->auto_detect = true;
441 return true;
442 }
443
[email protected]3e44697f2009-05-22 14:37:39444 if (mode != "manual") {
[email protected]861c6c62009-04-20 16:50:56445 // Mode is unrecognized.
446 return false;
447 }
448 bool use_http_proxy;
449 if (gconf_getter_->GetBoolean("/system/http_proxy/use_http_proxy",
450 &use_http_proxy)
451 && !use_http_proxy) {
452 // Another master switch for some reason. If set to false, then no
453 // proxy. But we don't panic if the key doesn't exist.
454 return true;
455 }
456
457 bool same_proxy = false;
458 // Indicates to use the http proxy for all protocols. This one may
459 // not exist (presumably on older versions), assume false in that
460 // case.
461 gconf_getter_->GetBoolean("/system/http_proxy/use_same_proxy",
462 &same_proxy);
463
464 ProxyServer proxy_server;
465 if (!same_proxy) {
466 // Try socks.
467 if (GetProxyFromGConf("/system/proxy/socks_", true, &proxy_server)) {
468 // gconf settings do not appear to distinguish between socks
469 // version. We default to version 4.
470 config->proxy_rules.type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
471 config->proxy_rules.single_proxy = proxy_server;
472 }
473 }
474 if (config->proxy_rules.empty()) {
475 bool have_http = GetProxyFromGConf("/system/http_proxy/", false,
476 &proxy_server);
477 if (same_proxy) {
478 if (have_http) {
479 config->proxy_rules.type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
480 config->proxy_rules.single_proxy = proxy_server;
481 }
482 } else {
483 // Protocol specific settings.
484 if (have_http)
485 config->proxy_rules.proxy_for_http = proxy_server;
486 bool have_secure = GetProxyFromGConf("/system/proxy/secure_", false,
487 &proxy_server);
488 if (have_secure)
489 config->proxy_rules.proxy_for_https = proxy_server;
490 bool have_ftp = GetProxyFromGConf("/system/proxy/ftp_", false,
491 &proxy_server);
492 if (have_ftp)
493 config->proxy_rules.proxy_for_ftp = proxy_server;
494 if (have_http || have_secure || have_ftp)
495 config->proxy_rules.type =
496 ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
497 }
498 }
499
500 if (config->proxy_rules.empty()) {
501 // Manual mode but we couldn't parse any rules.
502 return false;
503 }
504
505 // Check for authentication, just so we can warn.
506 bool use_auth;
507 gconf_getter_->GetBoolean("/system/http_proxy/use_authentication",
508 &use_auth);
509 if (use_auth)
[email protected]3e44697f2009-05-22 14:37:39510 LOG(ERROR) << "Proxy authentication not supported";
[email protected]861c6c62009-04-20 16:50:56511
512 // Now the bypass list.
513 gconf_getter_->GetStringList("/system/http_proxy/ignore_hosts",
514 &config->proxy_bypass);
515 // Note that there are no settings with semantics corresponding to
516 // config->proxy_bypass_local_names.
517
518 return true;
519}
520
[email protected]3e44697f2009-05-22 14:37:39521ProxyConfigServiceLinux::Delegate::Delegate(
[email protected]861c6c62009-04-20 16:50:56522 EnvironmentVariableGetter* env_var_getter,
523 GConfSettingGetter* gconf_getter)
[email protected]3e44697f2009-05-22 14:37:39524 : env_var_getter_(env_var_getter), gconf_getter_(gconf_getter),
525 glib_default_loop_(NULL), io_loop_(NULL) {
[email protected]861c6c62009-04-20 16:50:56526}
527
[email protected]3e44697f2009-05-22 14:37:39528bool ProxyConfigServiceLinux::Delegate::ShouldTryGConf() {
[email protected]861c6c62009-04-20 16:50:56529 // GNOME_DESKTOP_SESSION_ID being defined is a good indication that
530 // we are probably running under GNOME.
531 // Note: KDE_FULL_SESSION is a corresponding env var to recognize KDE.
532 std::string dummy, desktop_session;
[email protected]3e44697f2009-05-22 14:37:39533 return env_var_getter_->Getenv("GNOME_DESKTOP_SESSION_ID", &dummy)
[email protected]861c6c62009-04-20 16:50:56534 || (env_var_getter_->Getenv("DESKTOP_SESSION", &desktop_session)
[email protected]3e44697f2009-05-22 14:37:39535 && desktop_session == "gnome");
536 // I (sdoyon) would have liked to prioritize environment variables
537 // and only fallback to gconf if env vars were unset. But
538 // gnome-terminal "helpfully" sets http_proxy and no_proxy, and it
539 // does so even if the proxy mode is set to auto, which would
540 // mislead us.
541 //
542 // We could introduce a CHROME_PROXY_OBEY_ENV_VARS variable...??
543}
544
545void ProxyConfigServiceLinux::Delegate::SetupAndFetchInitialConfig(
546 MessageLoop* glib_default_loop, MessageLoop* io_loop) {
547 // We should be running on the default glib main loop thread right
548 // now. gconf can only be accessed from this thread.
549 DCHECK(MessageLoop::current() == glib_default_loop);
550 glib_default_loop_ = glib_default_loop;
551 io_loop_ = io_loop;
552
553 // If we are passed a NULL io_loop, then we don't setup gconf
554 // notifications. This should not be the usual case but is intended
555 // to simplify test setups.
556 if (!io_loop_)
557 LOG(INFO) << "Monitoring of gconf setting changes is disabled";
558
559 // Fetch and cache the current proxy config. The config is left in
560 // cached_config_, where GetProxyConfig() running on the IO thread
561 // will expect to find it. This is safe to do because we return
562 // before this ProxyConfigServiceLinux is passed on to
563 // the ProxyService.
564 bool got_config = false;
565 if (ShouldTryGConf() &&
566 gconf_getter_->Init() &&
567 (!io_loop || gconf_getter_->SetupNotification(this))) {
568 if (GetConfigFromGConf(&cached_config_)) {
569 cached_config_.set_id(1); // mark it as valid
570 got_config = true;
571 LOG(INFO) << "Obtained proxy setting from gconf";
[email protected]861c6c62009-04-20 16:50:56572 // If gconf proxy mode is "none", meaning direct, then we take
[email protected]3e44697f2009-05-22 14:37:39573 // that to be a valid config and will not check environment
574 // variables. The alternative would have been to look for a proxy
575 // where ever we can find one.
[email protected]861c6c62009-04-20 16:50:56576 //
[email protected]3e44697f2009-05-22 14:37:39577 // Keep a copy of the config for use from this thread for
578 // comparison with updated settings when we get notifications.
579 reference_config_ = cached_config_;
580 reference_config_.set_id(1); // mark it as valid
581 } else {
582 gconf_getter_->Release(); // Stop notifications
[email protected]861c6c62009-04-20 16:50:56583 }
584 }
[email protected]3e44697f2009-05-22 14:37:39585 if (!got_config) {
586 // An implementation for KDE settings would be welcome here.
587 //
588 // Consulting environment variables doesn't need to be done from
589 // the default glib main loop, but it's a tiny enough amount of
590 // work.
591 if (GetConfigFromEnv(&cached_config_)) {
592 cached_config_.set_id(1); // mark it as valid
593 LOG(INFO) << "Obtained proxy setting from environment variables";
594 }
[email protected]861c6c62009-04-20 16:50:56595 }
[email protected]3e44697f2009-05-22 14:37:39596}
597
598void ProxyConfigServiceLinux::Delegate::Reset() {
599 DCHECK(!glib_default_loop_ || MessageLoop::current() == glib_default_loop_);
600 gconf_getter_->Release();
601 cached_config_ = ProxyConfig();
602}
603
604int ProxyConfigServiceLinux::Delegate::GetProxyConfig(ProxyConfig* config) {
605 // This is called from the IO thread.
606 DCHECK(!io_loop_ || MessageLoop::current() == io_loop_);
607
608 // Simply return the last proxy configuration that glib_default_loop
609 // notified us of.
610 *config = cached_config_;
611 return cached_config_.is_valid() ? OK : ERR_FAILED;
612}
613
614void ProxyConfigServiceLinux::Delegate::OnCheckProxyConfigSettings() {
615 // This should be dispatched from the thread with the default glib
616 // main loop, which allows us to access gconf.
617 DCHECK(MessageLoop::current() == glib_default_loop_);
618
619 ProxyConfig new_config;
620 bool valid = GetConfigFromGConf(&new_config);
621 if (valid)
622 new_config.set_id(1); // mark it as valid
623
624 // See if it is different than what we had before.
625 if (new_config.is_valid() != reference_config_.is_valid() ||
626 !new_config.Equals(reference_config_)) {
627 // Post a task to |io_loop| with the new configuration, so it can
628 // update |cached_config_|.
629 io_loop_->PostTask(
630 FROM_HERE,
631 NewRunnableMethod(
632 this,
633 &ProxyConfigServiceLinux::Delegate::SetNewProxyConfig,
634 new_config));
635 }
636}
637
638void ProxyConfigServiceLinux::Delegate::SetNewProxyConfig(
639 const ProxyConfig& new_config) {
640 DCHECK(MessageLoop::current() == io_loop_);
641 LOG(INFO) << "Proxy configuration changed";
642 cached_config_ = new_config;
643}
644
645void ProxyConfigServiceLinux::Delegate::PostDestroyTask() {
646 if (MessageLoop::current() == glib_default_loop_) {
647 // Already on the right thread, call directly.
648 // This is the case for the unittests.
649 OnDestroy();
650 } else {
651 // Post to UI thread. Note that on browser shutdown, we may quit
652 // the UI MessageLoop and exit the program before ever running
653 // this.
654 glib_default_loop_->PostTask(
655 FROM_HERE,
656 NewRunnableMethod(
657 this,
658 &ProxyConfigServiceLinux::Delegate::OnDestroy));
659 }
660}
661void ProxyConfigServiceLinux::Delegate::OnDestroy() {
662 DCHECK(!glib_default_loop_ || MessageLoop::current() == glib_default_loop_);
663 gconf_getter_->Release();
664}
665
666ProxyConfigServiceLinux::ProxyConfigServiceLinux()
667 : delegate_(new Delegate(new EnvironmentVariableGetterImpl(),
668 new GConfSettingGetterImpl())) {
669}
670
671ProxyConfigServiceLinux::ProxyConfigServiceLinux(
672 EnvironmentVariableGetter* env_var_getter,
673 GConfSettingGetter* gconf_getter)
674 : delegate_(new Delegate(env_var_getter, gconf_getter)) {
[email protected]861c6c62009-04-20 16:50:56675}
676
677} // namespace net