blob: 3421110e8286a3c9fc6286c94e7237e8f29a91ab [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 LOG(INFO) << "~GConfSettingGetterImpl called";
209 // client_ should have been released before now, from
210 // Delegate::OnDestroy(), while running on the UI thread.
211 DCHECK(!client_);
[email protected]861c6c62009-04-20 16:50:56212 }
213
[email protected]3e44697f2009-05-22 14:37:39214 virtual bool Init() {
215 DCHECK(!client_);
216 DCHECK(!loop_);
217 loop_ = MessageLoopForUI::current();
218 client_ = gconf_client_get_default();
[email protected]861c6c62009-04-20 16:50:56219 if (!client_) {
[email protected]861c6c62009-04-20 16:50:56220 // It's not clear whether/when this can return NULL.
[email protected]3e44697f2009-05-22 14:37:39221 LOG(ERROR) << "Unable to create a gconf client";
222 loop_ = NULL;
223 return false;
[email protected]861c6c62009-04-20 16:50:56224 }
[email protected]3e44697f2009-05-22 14:37:39225 GError* error = NULL;
226 // We need to add the directories for which we'll be asking
227 // notifications, and we might as well ask to preload them.
228 gconf_client_add_dir(client_, "/system/proxy",
229 GCONF_CLIENT_PRELOAD_ONELEVEL, &error);
230 if (error == NULL) {
231 gconf_client_add_dir(client_, "/system/http_proxy",
232 GCONF_CLIENT_PRELOAD_ONELEVEL, &error);
233 }
234 if (error != NULL) {
235 LOG(ERROR) << "Error requesting gconf directory: " << error->message;
236 g_error_free(error);
237 Release();
238 return false;
239 }
240 return true;
241 }
242
243 void Release() {
244 if (client_) {
245 DCHECK(MessageLoop::current() == loop_);
246 // This also disables gconf notifications.
247 g_object_unref(client_);
248 client_ = NULL;
249 loop_ = NULL;
250 }
251 }
252
253 bool SetupNotification(void* callback_user_data) {
254 DCHECK(client_);
255 DCHECK(MessageLoop::current() == loop_);
256 GError* error = NULL;
257 gconf_client_notify_add(
258 client_, "/system/proxy",
259 OnGConfChangeNotification, callback_user_data,
260 NULL, &error);
261 if (error == NULL) {
262 gconf_client_notify_add(
263 client_, "/system/http_proxy",
264 OnGConfChangeNotification, callback_user_data,
265 NULL, &error);
266 }
267 if (error != NULL) {
268 LOG(ERROR) << "Error requesting gconf notifications: " << error->message;
269 g_error_free(error);
270 Release();
271 return false;
272 }
273 return true;
[email protected]861c6c62009-04-20 16:50:56274 }
275
276 virtual bool GetString(const char* key, std::string* result) {
[email protected]3e44697f2009-05-22 14:37:39277 DCHECK(client_);
278 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56279 GError* error = NULL;
280 gchar* value = gconf_client_get_string(client_, key, &error);
281 if (HandleGError(error, key))
282 return false;
283 if (!value)
284 return false;
285 *result = value;
286 g_free(value);
287 return true;
288 }
289 virtual bool GetBoolean(const char* key, bool* result) {
[email protected]3e44697f2009-05-22 14:37:39290 DCHECK(client_);
291 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56292 GError* error = NULL;
293 // We want to distinguish unset values from values defaulting to
294 // false. For that we need to use the type-generic
295 // gconf_client_get() rather than gconf_client_get_bool().
296 GConfValue* gconf_value = gconf_client_get(client_, key, &error);
297 if (HandleGError(error, key))
298 return false;
299 if (!gconf_value) {
300 // Unset.
301 return false;
302 }
303 if (gconf_value->type != GCONF_VALUE_BOOL) {
304 gconf_value_free(gconf_value);
305 return false;
306 }
307 gboolean bool_value = gconf_value_get_bool(gconf_value);
308 *result = static_cast<bool>(bool_value);
309 gconf_value_free(gconf_value);
310 return true;
311 }
312 virtual bool GetInt(const char* key, int* result) {
[email protected]3e44697f2009-05-22 14:37:39313 DCHECK(client_);
314 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56315 GError* error = NULL;
316 int value = gconf_client_get_int(client_, key, &error);
317 if (HandleGError(error, key))
318 return false;
319 // We don't bother to distinguish an unset value because callers
320 // don't care. 0 is returned if unset.
321 *result = value;
322 return true;
323 }
324 virtual bool GetStringList(const char* key,
325 std::vector<std::string>* result) {
[email protected]3e44697f2009-05-22 14:37:39326 DCHECK(client_);
327 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56328 GError* error = NULL;
329 GSList* list = gconf_client_get_list(client_, key,
330 GCONF_VALUE_STRING, &error);
331 if (HandleGError(error, key))
332 return false;
333 if (!list) {
334 // unset
335 return false;
336 }
337 for (GSList *it = list; it; it = it->next) {
338 result->push_back(static_cast<char*>(it->data));
339 g_free(it->data);
340 }
341 g_slist_free(list);
342 return true;
343 }
344
345 private:
346 // Logs and frees a glib error. Returns false if there was no error
347 // (error is NULL).
348 bool HandleGError(GError* error, const char* key) {
349 if (error != NULL) {
[email protected]3e44697f2009-05-22 14:37:39350 LOG(ERROR) << "Error getting gconf value for " << key
351 << ": " << error->message;
[email protected]861c6c62009-04-20 16:50:56352 g_error_free(error);
353 return true;
354 }
355 return false;
356 }
357
358 GConfClient* client_;
359
[email protected]3e44697f2009-05-22 14:37:39360 // Message loop of the thread that we make gconf calls on. It should
361 // be the UI thread and all our methods should be called on this
362 // thread. Only for assertions.
363 MessageLoop* loop_;
364
[email protected]861c6c62009-04-20 16:50:56365 DISALLOW_COPY_AND_ASSIGN(GConfSettingGetterImpl);
366};
367
368} // namespace
369
[email protected]3e44697f2009-05-22 14:37:39370bool ProxyConfigServiceLinux::Delegate::GetProxyFromGConf(
[email protected]861c6c62009-04-20 16:50:56371 const char* key_prefix, bool is_socks, ProxyServer* result_server) {
372 std::string key(key_prefix);
373 std::string host;
374 if (!gconf_getter_->GetString((key + "host").c_str(), &host)
375 || host.empty()) {
376 // Unset or empty.
377 return false;
378 }
379 // Check for an optional port.
380 int port;
381 gconf_getter_->GetInt((key + "port").c_str(), &port);
382 if (port != 0) {
383 // If a port is set and non-zero:
384 host += ":" + IntToString(port);
385 }
386 host = FixupProxyHostScheme(
387 is_socks ? ProxyServer::SCHEME_SOCKS4 : ProxyServer::SCHEME_HTTP,
388 host);
389 ProxyServer proxy_server = ProxyServer::FromURI(host);
390 if (proxy_server.is_valid()) {
391 *result_server = proxy_server;
392 return true;
393 }
394 return false;
395}
396
[email protected]3e44697f2009-05-22 14:37:39397bool ProxyConfigServiceLinux::Delegate::GetConfigFromGConf(
398 ProxyConfig* config) {
[email protected]861c6c62009-04-20 16:50:56399 std::string mode;
400 if (!gconf_getter_->GetString("/system/proxy/mode", &mode)) {
401 // We expect this to always be set, so if we don't see it then we
402 // probably have a gconf problem, and so we don't have a valid
403 // proxy config.
404 return false;
405 }
[email protected]3e44697f2009-05-22 14:37:39406 if (mode == "none") {
[email protected]861c6c62009-04-20 16:50:56407 // Specifically specifies no proxy.
408 return true;
[email protected]3e44697f2009-05-22 14:37:39409 }
[email protected]861c6c62009-04-20 16:50:56410
[email protected]3e44697f2009-05-22 14:37:39411 if (mode == "auto") {
[email protected]861c6c62009-04-20 16:50:56412 // automatic proxy config
413 std::string pac_url_str;
414 if (gconf_getter_->GetString("/system/proxy/autoconfig_url",
415 &pac_url_str)) {
416 if (!pac_url_str.empty()) {
417 GURL pac_url(pac_url_str);
418 if (!pac_url.is_valid())
419 return false;
420 config->pac_url = pac_url;
421 return true;
422 }
423 }
424 config->auto_detect = true;
425 return true;
426 }
427
[email protected]3e44697f2009-05-22 14:37:39428 if (mode != "manual") {
[email protected]861c6c62009-04-20 16:50:56429 // Mode is unrecognized.
430 return false;
431 }
432 bool use_http_proxy;
433 if (gconf_getter_->GetBoolean("/system/http_proxy/use_http_proxy",
434 &use_http_proxy)
435 && !use_http_proxy) {
436 // Another master switch for some reason. If set to false, then no
437 // proxy. But we don't panic if the key doesn't exist.
438 return true;
439 }
440
441 bool same_proxy = false;
442 // Indicates to use the http proxy for all protocols. This one may
443 // not exist (presumably on older versions), assume false in that
444 // case.
445 gconf_getter_->GetBoolean("/system/http_proxy/use_same_proxy",
446 &same_proxy);
447
448 ProxyServer proxy_server;
449 if (!same_proxy) {
450 // Try socks.
451 if (GetProxyFromGConf("/system/proxy/socks_", true, &proxy_server)) {
452 // gconf settings do not appear to distinguish between socks
453 // version. We default to version 4.
454 config->proxy_rules.type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
455 config->proxy_rules.single_proxy = proxy_server;
456 }
457 }
458 if (config->proxy_rules.empty()) {
459 bool have_http = GetProxyFromGConf("/system/http_proxy/", false,
460 &proxy_server);
461 if (same_proxy) {
462 if (have_http) {
463 config->proxy_rules.type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
464 config->proxy_rules.single_proxy = proxy_server;
465 }
466 } else {
467 // Protocol specific settings.
468 if (have_http)
469 config->proxy_rules.proxy_for_http = proxy_server;
470 bool have_secure = GetProxyFromGConf("/system/proxy/secure_", false,
471 &proxy_server);
472 if (have_secure)
473 config->proxy_rules.proxy_for_https = proxy_server;
474 bool have_ftp = GetProxyFromGConf("/system/proxy/ftp_", false,
475 &proxy_server);
476 if (have_ftp)
477 config->proxy_rules.proxy_for_ftp = proxy_server;
478 if (have_http || have_secure || have_ftp)
479 config->proxy_rules.type =
480 ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
481 }
482 }
483
484 if (config->proxy_rules.empty()) {
485 // Manual mode but we couldn't parse any rules.
486 return false;
487 }
488
489 // Check for authentication, just so we can warn.
490 bool use_auth;
491 gconf_getter_->GetBoolean("/system/http_proxy/use_authentication",
492 &use_auth);
493 if (use_auth)
[email protected]3e44697f2009-05-22 14:37:39494 LOG(ERROR) << "Proxy authentication not supported";
[email protected]861c6c62009-04-20 16:50:56495
496 // Now the bypass list.
497 gconf_getter_->GetStringList("/system/http_proxy/ignore_hosts",
498 &config->proxy_bypass);
499 // Note that there are no settings with semantics corresponding to
500 // config->proxy_bypass_local_names.
501
502 return true;
503}
504
[email protected]3e44697f2009-05-22 14:37:39505ProxyConfigServiceLinux::Delegate::Delegate(
[email protected]861c6c62009-04-20 16:50:56506 EnvironmentVariableGetter* env_var_getter,
507 GConfSettingGetter* gconf_getter)
[email protected]3e44697f2009-05-22 14:37:39508 : env_var_getter_(env_var_getter), gconf_getter_(gconf_getter),
509 glib_default_loop_(NULL), io_loop_(NULL) {
[email protected]861c6c62009-04-20 16:50:56510}
511
[email protected]3e44697f2009-05-22 14:37:39512bool ProxyConfigServiceLinux::Delegate::ShouldTryGConf() {
[email protected]861c6c62009-04-20 16:50:56513 // GNOME_DESKTOP_SESSION_ID being defined is a good indication that
514 // we are probably running under GNOME.
515 // Note: KDE_FULL_SESSION is a corresponding env var to recognize KDE.
516 std::string dummy, desktop_session;
[email protected]3e44697f2009-05-22 14:37:39517 return env_var_getter_->Getenv("GNOME_DESKTOP_SESSION_ID", &dummy)
[email protected]861c6c62009-04-20 16:50:56518 || (env_var_getter_->Getenv("DESKTOP_SESSION", &desktop_session)
[email protected]3e44697f2009-05-22 14:37:39519 && desktop_session == "gnome");
520 // I (sdoyon) would have liked to prioritize environment variables
521 // and only fallback to gconf if env vars were unset. But
522 // gnome-terminal "helpfully" sets http_proxy and no_proxy, and it
523 // does so even if the proxy mode is set to auto, which would
524 // mislead us.
525 //
526 // We could introduce a CHROME_PROXY_OBEY_ENV_VARS variable...??
527}
528
529void ProxyConfigServiceLinux::Delegate::SetupAndFetchInitialConfig(
530 MessageLoop* glib_default_loop, MessageLoop* io_loop) {
531 // We should be running on the default glib main loop thread right
532 // now. gconf can only be accessed from this thread.
533 DCHECK(MessageLoop::current() == glib_default_loop);
534 glib_default_loop_ = glib_default_loop;
535 io_loop_ = io_loop;
536
537 // If we are passed a NULL io_loop, then we don't setup gconf
538 // notifications. This should not be the usual case but is intended
539 // to simplify test setups.
540 if (!io_loop_)
541 LOG(INFO) << "Monitoring of gconf setting changes is disabled";
542
543 // Fetch and cache the current proxy config. The config is left in
544 // cached_config_, where GetProxyConfig() running on the IO thread
545 // will expect to find it. This is safe to do because we return
546 // before this ProxyConfigServiceLinux is passed on to
547 // the ProxyService.
548 bool got_config = false;
549 if (ShouldTryGConf() &&
550 gconf_getter_->Init() &&
551 (!io_loop || gconf_getter_->SetupNotification(this))) {
552 if (GetConfigFromGConf(&cached_config_)) {
553 cached_config_.set_id(1); // mark it as valid
554 got_config = true;
555 LOG(INFO) << "Obtained proxy setting from gconf";
[email protected]861c6c62009-04-20 16:50:56556 // If gconf proxy mode is "none", meaning direct, then we take
[email protected]3e44697f2009-05-22 14:37:39557 // that to be a valid config and will not check environment
558 // variables. The alternative would have been to look for a proxy
559 // where ever we can find one.
[email protected]861c6c62009-04-20 16:50:56560 //
[email protected]3e44697f2009-05-22 14:37:39561 // Keep a copy of the config for use from this thread for
562 // comparison with updated settings when we get notifications.
563 reference_config_ = cached_config_;
564 reference_config_.set_id(1); // mark it as valid
565 } else {
566 gconf_getter_->Release(); // Stop notifications
[email protected]861c6c62009-04-20 16:50:56567 }
568 }
[email protected]3e44697f2009-05-22 14:37:39569 if (!got_config) {
570 // An implementation for KDE settings would be welcome here.
571 //
572 // Consulting environment variables doesn't need to be done from
573 // the default glib main loop, but it's a tiny enough amount of
574 // work.
575 if (GetConfigFromEnv(&cached_config_)) {
576 cached_config_.set_id(1); // mark it as valid
577 LOG(INFO) << "Obtained proxy setting from environment variables";
578 }
[email protected]861c6c62009-04-20 16:50:56579 }
[email protected]3e44697f2009-05-22 14:37:39580}
581
582void ProxyConfigServiceLinux::Delegate::Reset() {
583 DCHECK(!glib_default_loop_ || MessageLoop::current() == glib_default_loop_);
584 gconf_getter_->Release();
585 cached_config_ = ProxyConfig();
586}
587
588int ProxyConfigServiceLinux::Delegate::GetProxyConfig(ProxyConfig* config) {
589 // This is called from the IO thread.
590 DCHECK(!io_loop_ || MessageLoop::current() == io_loop_);
591
592 // Simply return the last proxy configuration that glib_default_loop
593 // notified us of.
594 *config = cached_config_;
595 return cached_config_.is_valid() ? OK : ERR_FAILED;
596}
597
598void ProxyConfigServiceLinux::Delegate::OnCheckProxyConfigSettings() {
599 // This should be dispatched from the thread with the default glib
600 // main loop, which allows us to access gconf.
601 DCHECK(MessageLoop::current() == glib_default_loop_);
602
603 ProxyConfig new_config;
604 bool valid = GetConfigFromGConf(&new_config);
605 if (valid)
606 new_config.set_id(1); // mark it as valid
607
608 // See if it is different than what we had before.
609 if (new_config.is_valid() != reference_config_.is_valid() ||
610 !new_config.Equals(reference_config_)) {
611 // Post a task to |io_loop| with the new configuration, so it can
612 // update |cached_config_|.
613 io_loop_->PostTask(
614 FROM_HERE,
615 NewRunnableMethod(
616 this,
617 &ProxyConfigServiceLinux::Delegate::SetNewProxyConfig,
618 new_config));
619 }
620}
621
622void ProxyConfigServiceLinux::Delegate::SetNewProxyConfig(
623 const ProxyConfig& new_config) {
624 DCHECK(MessageLoop::current() == io_loop_);
625 LOG(INFO) << "Proxy configuration changed";
626 cached_config_ = new_config;
627}
628
629void ProxyConfigServiceLinux::Delegate::PostDestroyTask() {
630 if (MessageLoop::current() == glib_default_loop_) {
631 // Already on the right thread, call directly.
632 // This is the case for the unittests.
633 OnDestroy();
634 } else {
635 // Post to UI thread. Note that on browser shutdown, we may quit
636 // the UI MessageLoop and exit the program before ever running
637 // this.
638 glib_default_loop_->PostTask(
639 FROM_HERE,
640 NewRunnableMethod(
641 this,
642 &ProxyConfigServiceLinux::Delegate::OnDestroy));
643 }
644}
645void ProxyConfigServiceLinux::Delegate::OnDestroy() {
646 DCHECK(!glib_default_loop_ || MessageLoop::current() == glib_default_loop_);
647 gconf_getter_->Release();
648}
649
650ProxyConfigServiceLinux::ProxyConfigServiceLinux()
651 : delegate_(new Delegate(new EnvironmentVariableGetterImpl(),
652 new GConfSettingGetterImpl())) {
653}
654
655ProxyConfigServiceLinux::ProxyConfigServiceLinux(
656 EnvironmentVariableGetter* env_var_getter,
657 GConfSettingGetter* gconf_getter)
658 : delegate_(new Delegate(env_var_getter, gconf_getter)) {
[email protected]861c6c62009-04-20 16:50:56659}
660
661} // namespace net