blob: 44e1cd745a8af9f72aa6239a9017fb4f263d1000 [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
[email protected]d7395e732009-08-28 23:13:437#include <errno.h>
8#include <fcntl.h>
[email protected]861c6c62009-04-20 16:50:569#include <gconf/gconf-client.h>
[email protected]d7395e732009-08-28 23:13:4310#include <limits.h>
11#include <stdio.h>
[email protected]861c6c62009-04-20 16:50:5612#include <stdlib.h>
[email protected]d7395e732009-08-28 23:13:4313#include <sys/inotify.h>
14#include <unistd.h>
[email protected]861c6c62009-04-20 16:50:5615
[email protected]d7395e732009-08-28 23:13:4316#include "base/file_path.h"
17#include "base/file_util.h"
[email protected]861c6c62009-04-20 16:50:5618#include "base/logging.h"
[email protected]d7395e732009-08-28 23:13:4319#include "base/message_loop.h"
[email protected]861c6c62009-04-20 16:50:5620#include "base/string_tokenizer.h"
21#include "base/string_util.h"
[email protected]3e44697f2009-05-22 14:37:3922#include "base/task.h"
[email protected]d7395e732009-08-28 23:13:4323#include "base/timer.h"
[email protected]861c6c62009-04-20 16:50:5624#include "googleurl/src/url_canon.h"
25#include "net/base/net_errors.h"
26#include "net/http/http_util.h"
27#include "net/proxy/proxy_config.h"
28#include "net/proxy/proxy_server.h"
29
30namespace net {
31
32namespace {
33
[email protected]861c6c62009-04-20 16:50:5634// Given a proxy hostname from a setting, returns that hostname with
35// an appropriate proxy server scheme prefix.
36// scheme indicates the desired proxy scheme: usually http, with
37// socks 4 or 5 as special cases.
[email protected]87a102b2009-07-14 05:23:3038// TODO(arindam): Remove URI string manipulation by using MapUrlSchemeToProxy.
[email protected]861c6c62009-04-20 16:50:5639std::string FixupProxyHostScheme(ProxyServer::Scheme scheme,
40 std::string host) {
41 if (scheme == ProxyServer::SCHEME_SOCKS4 &&
42 StartsWithASCII(host, "socks5://", false)) {
43 // We default to socks 4, but if the user specifically set it to
44 // socks5://, then use that.
45 scheme = ProxyServer::SCHEME_SOCKS5;
46 }
47 // Strip the scheme if any.
48 std::string::size_type colon = host.find("://");
49 if (colon != std::string::npos)
50 host = host.substr(colon + 3);
51 // If a username and perhaps password are specified, give a warning.
52 std::string::size_type at_sign = host.find("@");
53 // Should this be supported?
54 if (at_sign != std::string::npos) {
[email protected]62749f182009-07-15 13:16:5455 // ProxyConfig does not support authentication parameters, but Chrome
56 // will prompt for the password later. Disregard the
57 // authentication parameters and continue with this hostname.
58 LOG(WARNING) << "Proxy authentication parameters ignored, see bug 16709";
[email protected]861c6c62009-04-20 16:50:5659 host = host.substr(at_sign + 1);
60 }
61 // If this is a socks proxy, prepend a scheme so as to tell
62 // ProxyServer. This also allows ProxyServer to choose the right
63 // default port.
64 if (scheme == ProxyServer::SCHEME_SOCKS4)
65 host = "socks4://" + host;
66 else if (scheme == ProxyServer::SCHEME_SOCKS5)
67 host = "socks5://" + host;
[email protected]d7395e732009-08-28 23:13:4368 // If there is a trailing slash, remove it so |host| will parse correctly
69 // even if it includes a port number (since the slash is not numeric).
70 if (host.length() && host[host.length() - 1] == '/')
71 host.resize(host.length() - 1);
[email protected]861c6c62009-04-20 16:50:5672 return host;
73}
74
75} // namespace
76
[email protected]3e44697f2009-05-22 14:37:3977bool ProxyConfigServiceLinux::Delegate::GetProxyFromEnvVarForScheme(
[email protected]861c6c62009-04-20 16:50:5678 const char* variable, ProxyServer::Scheme scheme,
79 ProxyServer* result_server) {
80 std::string env_value;
81 if (env_var_getter_->Getenv(variable, &env_value)) {
82 if (!env_value.empty()) {
83 env_value = FixupProxyHostScheme(scheme, env_value);
[email protected]87a102b2009-07-14 05:23:3084 ProxyServer proxy_server =
85 ProxyServer::FromURI(env_value, ProxyServer::SCHEME_HTTP);
[email protected]861c6c62009-04-20 16:50:5686 if (proxy_server.is_valid() && !proxy_server.is_direct()) {
87 *result_server = proxy_server;
88 return true;
89 } else {
[email protected]3e44697f2009-05-22 14:37:3990 LOG(ERROR) << "Failed to parse environment variable " << variable;
[email protected]861c6c62009-04-20 16:50:5691 }
92 }
93 }
94 return false;
95}
96
[email protected]3e44697f2009-05-22 14:37:3997bool ProxyConfigServiceLinux::Delegate::GetProxyFromEnvVar(
[email protected]861c6c62009-04-20 16:50:5698 const char* variable, ProxyServer* result_server) {
99 return GetProxyFromEnvVarForScheme(variable, ProxyServer::SCHEME_HTTP,
100 result_server);
101}
102
[email protected]3e44697f2009-05-22 14:37:39103bool ProxyConfigServiceLinux::Delegate::GetConfigFromEnv(ProxyConfig* config) {
[email protected]861c6c62009-04-20 16:50:56104 // Check for automatic configuration first, in
105 // "auto_proxy". Possibly only the "environment_proxy" firefox
106 // extension has ever used this, but it still sounds like a good
107 // idea.
108 std::string auto_proxy;
109 if (env_var_getter_->Getenv("auto_proxy", &auto_proxy)) {
110 if (auto_proxy.empty()) {
111 // Defined and empty => autodetect
112 config->auto_detect = true;
113 } else {
114 // specified autoconfig URL
115 config->pac_url = GURL(auto_proxy);
116 }
117 return true;
118 }
119 // "all_proxy" is a shortcut to avoid defining {http,https,ftp}_proxy.
120 ProxyServer proxy_server;
121 if (GetProxyFromEnvVar("all_proxy", &proxy_server)) {
122 config->proxy_rules.type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
123 config->proxy_rules.single_proxy = proxy_server;
124 } else {
125 bool have_http = GetProxyFromEnvVar("http_proxy", &proxy_server);
126 if (have_http)
127 config->proxy_rules.proxy_for_http = proxy_server;
128 // It would be tempting to let http_proxy apply for all protocols
129 // if https_proxy and ftp_proxy are not defined. Googling turns up
130 // several documents that mention only http_proxy. But then the
131 // user really might not want to proxy https. And it doesn't seem
132 // like other apps do this. So we will refrain.
133 bool have_https = GetProxyFromEnvVar("https_proxy", &proxy_server);
134 if (have_https)
135 config->proxy_rules.proxy_for_https = proxy_server;
136 bool have_ftp = GetProxyFromEnvVar("ftp_proxy", &proxy_server);
137 if (have_ftp)
138 config->proxy_rules.proxy_for_ftp = proxy_server;
139 if (have_http || have_https || have_ftp) {
140 // mustn't change type unless some rules are actually set.
141 config->proxy_rules.type = ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
142 }
143 }
144 if (config->proxy_rules.empty()) {
145 // If the above were not defined, try for socks.
146 ProxyServer::Scheme scheme = ProxyServer::SCHEME_SOCKS4;
147 std::string env_version;
148 if (env_var_getter_->Getenv("SOCKS_VERSION", &env_version)
[email protected]3e44697f2009-05-22 14:37:39149 && env_version == "5")
[email protected]861c6c62009-04-20 16:50:56150 scheme = ProxyServer::SCHEME_SOCKS5;
151 if (GetProxyFromEnvVarForScheme("SOCKS_SERVER", scheme, &proxy_server)) {
152 config->proxy_rules.type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
153 config->proxy_rules.single_proxy = proxy_server;
154 }
155 }
156 // Look for the proxy bypass list.
157 std::string no_proxy;
158 env_var_getter_->Getenv("no_proxy", &no_proxy);
159 if (config->proxy_rules.empty()) {
160 // Having only "no_proxy" set, presumably to "*", makes it
161 // explicit that env vars do specify a configuration: having no
162 // rules specified only means the user explicitly asks for direct
163 // connections.
164 return !no_proxy.empty();
165 }
[email protected]ab501a6a2009-05-12 15:07:50166 config->ParseNoProxyList(no_proxy);
[email protected]861c6c62009-04-20 16:50:56167 return true;
168}
169
170namespace {
171
[email protected]d7395e732009-08-28 23:13:43172const int kDebounceTimeoutMilliseconds = 250;
[email protected]3e44697f2009-05-22 14:37:39173
[email protected]d7395e732009-08-28 23:13:43174// This is the "real" gconf version that actually uses gconf.
175class GConfSettingGetterImplGConf
[email protected]861c6c62009-04-20 16:50:56176 : public ProxyConfigServiceLinux::GConfSettingGetter {
177 public:
[email protected]d7395e732009-08-28 23:13:43178 GConfSettingGetterImplGConf()
179 : client_(NULL), notify_delegate_(NULL), loop_(NULL) {}
[email protected]3e44697f2009-05-22 14:37:39180
[email protected]d7395e732009-08-28 23:13:43181 virtual ~GConfSettingGetterImplGConf() {
[email protected]3e44697f2009-05-22 14:37:39182 // client_ should have been released before now, from
[email protected]f5b13442009-07-13 15:23:59183 // Delegate::OnDestroy(), while running on the UI thread. However
184 // on exiting the process, it may happen that
185 // Delegate::OnDestroy() task is left pending on the glib loop
186 // after the loop was quit, and pending tasks may then be deleted
187 // without being run.
188 if (client_) {
189 // gconf client was not cleaned up.
190 if (MessageLoop::current() == loop_) {
191 // We are on the UI thread so we can clean it safely. This is
192 // the case at least for ui_tests running under Valgrind in
193 // bug 16076.
[email protected]d7395e732009-08-28 23:13:43194 LOG(INFO) << "~GConfSettingGetterImplGConf: releasing gconf client";
195 Shutdown();
[email protected]f5b13442009-07-13 15:23:59196 } else {
[email protected]d7395e732009-08-28 23:13:43197 LOG(WARNING) << "~GConfSettingGetterImplGConf: leaking gconf client";
[email protected]f5b13442009-07-13 15:23:59198 client_ = NULL;
199 }
200 }
[email protected]3e44697f2009-05-22 14:37:39201 DCHECK(!client_);
[email protected]861c6c62009-04-20 16:50:56202 }
203
[email protected]d7395e732009-08-28 23:13:43204 virtual bool Init(MessageLoop* glib_default_loop,
205 MessageLoopForIO* file_loop) {
206 DCHECK(MessageLoop::current() == glib_default_loop);
[email protected]3e44697f2009-05-22 14:37:39207 DCHECK(!client_);
208 DCHECK(!loop_);
[email protected]d7395e732009-08-28 23:13:43209 loop_ = glib_default_loop;
[email protected]3e44697f2009-05-22 14:37:39210 client_ = gconf_client_get_default();
[email protected]861c6c62009-04-20 16:50:56211 if (!client_) {
[email protected]861c6c62009-04-20 16:50:56212 // It's not clear whether/when this can return NULL.
[email protected]3e44697f2009-05-22 14:37:39213 LOG(ERROR) << "Unable to create a gconf client";
214 loop_ = NULL;
215 return false;
[email protected]861c6c62009-04-20 16:50:56216 }
[email protected]3e44697f2009-05-22 14:37:39217 GError* error = NULL;
218 // We need to add the directories for which we'll be asking
219 // notifications, and we might as well ask to preload them.
220 gconf_client_add_dir(client_, "/system/proxy",
221 GCONF_CLIENT_PRELOAD_ONELEVEL, &error);
222 if (error == NULL) {
223 gconf_client_add_dir(client_, "/system/http_proxy",
224 GCONF_CLIENT_PRELOAD_ONELEVEL, &error);
225 }
226 if (error != NULL) {
227 LOG(ERROR) << "Error requesting gconf directory: " << error->message;
228 g_error_free(error);
[email protected]d7395e732009-08-28 23:13:43229 Shutdown();
[email protected]3e44697f2009-05-22 14:37:39230 return false;
231 }
232 return true;
233 }
234
[email protected]d7395e732009-08-28 23:13:43235 void Shutdown() {
[email protected]3e44697f2009-05-22 14:37:39236 if (client_) {
237 DCHECK(MessageLoop::current() == loop_);
238 // This also disables gconf notifications.
239 g_object_unref(client_);
240 client_ = NULL;
241 loop_ = NULL;
242 }
243 }
244
[email protected]d7395e732009-08-28 23:13:43245 bool SetupNotification(ProxyConfigServiceLinux::Delegate* delegate) {
[email protected]3e44697f2009-05-22 14:37:39246 DCHECK(client_);
247 DCHECK(MessageLoop::current() == loop_);
248 GError* error = NULL;
[email protected]d7395e732009-08-28 23:13:43249 notify_delegate_ = delegate;
[email protected]3e44697f2009-05-22 14:37:39250 gconf_client_notify_add(
251 client_, "/system/proxy",
[email protected]d7395e732009-08-28 23:13:43252 OnGConfChangeNotification, this,
[email protected]3e44697f2009-05-22 14:37:39253 NULL, &error);
254 if (error == NULL) {
255 gconf_client_notify_add(
256 client_, "/system/http_proxy",
[email protected]d7395e732009-08-28 23:13:43257 OnGConfChangeNotification, this,
[email protected]3e44697f2009-05-22 14:37:39258 NULL, &error);
259 }
260 if (error != NULL) {
261 LOG(ERROR) << "Error requesting gconf notifications: " << error->message;
262 g_error_free(error);
[email protected]d7395e732009-08-28 23:13:43263 Shutdown();
[email protected]3e44697f2009-05-22 14:37:39264 return false;
265 }
266 return true;
[email protected]861c6c62009-04-20 16:50:56267 }
268
[email protected]9a3d8d42009-09-03 17:01:46269 virtual MessageLoop* GetNotificationLoop() {
[email protected]d7395e732009-08-28 23:13:43270 return loop_;
271 }
272
273 virtual const char* GetDataSource() {
274 return "gconf";
275 }
276
[email protected]861c6c62009-04-20 16:50:56277 virtual bool GetString(const char* key, std::string* result) {
[email protected]3e44697f2009-05-22 14:37:39278 DCHECK(client_);
279 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56280 GError* error = NULL;
281 gchar* value = gconf_client_get_string(client_, key, &error);
282 if (HandleGError(error, key))
283 return false;
284 if (!value)
285 return false;
286 *result = value;
287 g_free(value);
288 return true;
289 }
290 virtual bool GetBoolean(const char* key, bool* result) {
[email protected]3e44697f2009-05-22 14:37:39291 DCHECK(client_);
292 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56293 GError* error = NULL;
294 // We want to distinguish unset values from values defaulting to
295 // false. For that we need to use the type-generic
296 // gconf_client_get() rather than gconf_client_get_bool().
297 GConfValue* gconf_value = gconf_client_get(client_, key, &error);
298 if (HandleGError(error, key))
299 return false;
300 if (!gconf_value) {
301 // Unset.
302 return false;
303 }
304 if (gconf_value->type != GCONF_VALUE_BOOL) {
305 gconf_value_free(gconf_value);
306 return false;
307 }
308 gboolean bool_value = gconf_value_get_bool(gconf_value);
309 *result = static_cast<bool>(bool_value);
310 gconf_value_free(gconf_value);
311 return true;
312 }
313 virtual bool GetInt(const char* key, int* result) {
[email protected]3e44697f2009-05-22 14:37:39314 DCHECK(client_);
315 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56316 GError* error = NULL;
317 int value = gconf_client_get_int(client_, key, &error);
318 if (HandleGError(error, key))
319 return false;
320 // We don't bother to distinguish an unset value because callers
321 // don't care. 0 is returned if unset.
322 *result = value;
323 return true;
324 }
325 virtual bool GetStringList(const char* key,
326 std::vector<std::string>* result) {
[email protected]3e44697f2009-05-22 14:37:39327 DCHECK(client_);
328 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56329 GError* error = NULL;
330 GSList* list = gconf_client_get_list(client_, key,
331 GCONF_VALUE_STRING, &error);
332 if (HandleGError(error, key))
333 return false;
334 if (!list) {
335 // unset
336 return false;
337 }
338 for (GSList *it = list; it; it = it->next) {
339 result->push_back(static_cast<char*>(it->data));
340 g_free(it->data);
341 }
342 g_slist_free(list);
343 return true;
344 }
345
346 private:
347 // Logs and frees a glib error. Returns false if there was no error
348 // (error is NULL).
349 bool HandleGError(GError* error, const char* key) {
350 if (error != NULL) {
[email protected]3e44697f2009-05-22 14:37:39351 LOG(ERROR) << "Error getting gconf value for " << key
352 << ": " << error->message;
[email protected]861c6c62009-04-20 16:50:56353 g_error_free(error);
354 return true;
355 }
356 return false;
357 }
358
[email protected]d7395e732009-08-28 23:13:43359 // This is the callback from the debounce timer.
360 void OnDebouncedNotification() {
361 DCHECK(MessageLoop::current() == loop_);
362 DCHECK(notify_delegate_);
363 // Forward to a method on the proxy config service delegate object.
364 notify_delegate_->OnCheckProxyConfigSettings();
365 }
366
367 void OnChangeNotification() {
368 // We don't use Reset() because the timer may not yet be running.
369 // (In that case Stop() is a no-op.)
370 debounce_timer_.Stop();
371 debounce_timer_.Start(base::TimeDelta::FromMilliseconds(
372 kDebounceTimeoutMilliseconds), this,
373 &GConfSettingGetterImplGConf::OnDebouncedNotification);
374 }
375
376 // gconf notification callback, dispatched from the default glib main loop.
377 static void OnGConfChangeNotification(
378 GConfClient* client, guint cnxn_id,
379 GConfEntry* entry, gpointer user_data) {
380 LOG(INFO) << "gconf change notification for key "
381 << gconf_entry_get_key(entry);
382 // We don't track which key has changed, just that something did change.
383 GConfSettingGetterImplGConf* setting_getter =
384 reinterpret_cast<GConfSettingGetterImplGConf*>(user_data);
385 setting_getter->OnChangeNotification();
386 }
387
[email protected]861c6c62009-04-20 16:50:56388 GConfClient* client_;
[email protected]d7395e732009-08-28 23:13:43389 ProxyConfigServiceLinux::Delegate* notify_delegate_;
390 base::OneShotTimer<GConfSettingGetterImplGConf> debounce_timer_;
[email protected]861c6c62009-04-20 16:50:56391
[email protected]3e44697f2009-05-22 14:37:39392 // Message loop of the thread that we make gconf calls on. It should
393 // be the UI thread and all our methods should be called on this
394 // thread. Only for assertions.
395 MessageLoop* loop_;
396
[email protected]d7395e732009-08-28 23:13:43397 DISALLOW_COPY_AND_ASSIGN(GConfSettingGetterImplGConf);
398};
399
400// This is the KDE version that reads kioslaverc and simulates gconf.
401// Doing this allows the main Delegate code, as well as the unit tests
402// for it, to stay the same - and the settings map fairly well besides.
403class GConfSettingGetterImplKDE
404 : public ProxyConfigServiceLinux::GConfSettingGetter,
405 public base::MessagePumpLibevent::Watcher {
406 public:
407 explicit GConfSettingGetterImplKDE(
408 base::EnvironmentVariableGetter* env_var_getter)
409 : inotify_fd_(-1), notify_delegate_(NULL), indirect_manual_(false),
410 auto_no_pac_(false), reversed_exception_(false), file_loop_(NULL) {
411 // We don't save the env var getter for later use since we don't own it.
412 // Instead we use it here and save the result we actually care about.
413 std::string kde_home;
414 if (!env_var_getter->Getenv("KDE_HOME", &kde_home)) {
415 if (!env_var_getter->Getenv("HOME", &kde_home))
416 // User has no $HOME? Give up. Later we'll report the failure.
417 return;
418 kde_home = FilePath(kde_home).Append(FILE_PATH_LITERAL(".kde")).value();
419 }
420 kde_config_dir_ = FilePath(kde_home).Append(
421 FILE_PATH_LITERAL("share")).Append(FILE_PATH_LITERAL("config"));
422 }
423
424 virtual ~GConfSettingGetterImplKDE() {
425 // inotify_fd_ should have been closed before now, from
426 // Delegate::OnDestroy(), while running on the file thread. However
427 // on exiting the process, it may happen that Delegate::OnDestroy()
428 // task is left pending on the file loop after the loop was quit,
429 // and pending tasks may then be deleted without being run.
430 // Here in the KDE version, we can safely close the file descriptor
431 // anyway. (Not that it really matters; the process is exiting.)
432 if (inotify_fd_ >= 0)
433 Shutdown();
434 DCHECK(inotify_fd_ < 0);
435 }
436
437 virtual bool Init(MessageLoop* glib_default_loop,
438 MessageLoopForIO* file_loop) {
439 DCHECK(inotify_fd_ < 0);
440 inotify_fd_ = inotify_init();
441 if (inotify_fd_ < 0) {
442 LOG(ERROR) << "inotify_init failed: " << strerror(errno);
443 return false;
444 }
445 int flags = fcntl(inotify_fd_, F_GETFL);
446 if (fcntl(inotify_fd_, F_SETFL, flags | O_NONBLOCK) < 0) {
447 LOG(ERROR) << "fcntl failed: " << strerror(errno);
448 close(inotify_fd_);
449 inotify_fd_ = -1;
450 return false;
451 }
452 file_loop_ = file_loop;
453 // The initial read is done on the current thread, not |file_loop_|,
454 // since we will need to have it for SetupAndFetchInitialConfig().
455 UpdateCachedSettings();
456 return true;
457 }
458
459 void Shutdown() {
460 if (inotify_fd_ >= 0) {
461 ResetCachedSettings();
462 inotify_watcher_.StopWatchingFileDescriptor();
463 close(inotify_fd_);
464 inotify_fd_ = -1;
465 }
466 }
467
468 bool SetupNotification(ProxyConfigServiceLinux::Delegate* delegate) {
469 DCHECK(inotify_fd_ >= 0);
470 DCHECK(file_loop_);
471 // We can't just watch the kioslaverc file directly, since KDE will write
472 // a new copy of it and then rename it whenever settings are changed and
473 // inotify watches inodes (so we'll be watching the old deleted file after
474 // the first change, and it will never change again). So, we watch the
475 // directory instead. We then act only on changes to the kioslaverc entry.
476 if (inotify_add_watch(inotify_fd_, kde_config_dir_.value().c_str(),
477 IN_MODIFY | IN_MOVED_TO) < 0)
478 return false;
479 notify_delegate_ = delegate;
480 return file_loop_->WatchFileDescriptor(inotify_fd_, true,
481 MessageLoopForIO::WATCH_READ, &inotify_watcher_, this);
482 }
483
[email protected]9a3d8d42009-09-03 17:01:46484 virtual MessageLoop* GetNotificationLoop() {
[email protected]d7395e732009-08-28 23:13:43485 return file_loop_;
486 }
487
488 // Implement base::MessagePumpLibevent::Delegate.
489 void OnFileCanReadWithoutBlocking(int fd) {
490 DCHECK(fd == inotify_fd_);
491 DCHECK(MessageLoop::current() == file_loop_);
492 OnChangeNotification();
493 }
494 void OnFileCanWriteWithoutBlocking(int fd) {
495 NOTREACHED();
496 }
497
498 virtual const char* GetDataSource() {
499 return "KDE";
500 }
501
502 virtual bool GetString(const char* key, std::string* result) {
503 string_map_type::iterator it = string_table_.find(key);
504 if (it == string_table_.end())
505 return false;
506 *result = it->second;
507 return true;
508 }
509 virtual bool GetBoolean(const char* key, bool* result) {
510 // We don't ever have any booleans.
511 return false;
512 }
513 virtual bool GetInt(const char* key, int* result) {
514 // We don't ever have any integers. (See AddProxy() below about ports.)
515 return false;
516 }
517 virtual bool GetStringList(const char* key,
518 std::vector<std::string>* result) {
519 strings_map_type::iterator it = strings_table_.find(key);
520 if (it == strings_table_.end())
521 return false;
522 *result = it->second;
523 return true;
524 }
525
526 private:
527 void ResetCachedSettings() {
528 string_table_.clear();
529 strings_table_.clear();
530 indirect_manual_ = false;
531 auto_no_pac_ = false;
532 reversed_exception_ = false;
533 }
534
535 void AddProxy(std::string prefix, std::string value) {
536 if (value.empty() || value.substr(0, 3) == "//:")
537 // No proxy.
538 return;
539 // We don't need to parse the port number out; GetProxyFromGConf()
540 // would only append it right back again. So we just leave the port
541 // number right in the host string.
542 string_table_[prefix + "host"] = value;
543 }
544
[email protected]9a3d8d42009-09-03 17:01:46545 void AddKDESetting(const std::string& key, const std::string& value) {
[email protected]d7395e732009-08-28 23:13:43546 // The astute reader may notice that there is no mention of SOCKS
547 // here. That's because KDE handles socks is a strange way, and we
548 // don't support it. Rather than just a setting for the SOCKS server,
549 // it has a setting for a library to LD_PRELOAD in all your programs
550 // that will transparently SOCKSify them. Such libraries each have
551 // their own configuration, and thus, we can't get it from KDE.
552 if (key == "ProxyType") {
553 const char* mode = "none";
554 indirect_manual_ = false;
555 auto_no_pac_ = false;
556 switch (StringToInt(value)) {
557 case 0: // No proxy, or maybe kioslaverc syntax error.
558 break;
559 case 1: // Manual configuration.
560 mode = "manual";
561 break;
562 case 2: // PAC URL.
563 mode = "auto";
564 break;
565 case 3: // WPAD.
566 mode = "auto";
567 auto_no_pac_ = true;
568 break;
569 case 4: // Indirect manual via environment variables.
570 mode = "manual";
571 indirect_manual_ = true;
572 break;
573 }
574 string_table_["/system/proxy/mode"] = mode;
575 } else if (key == "Proxy Config Script") {
576 string_table_["/system/proxy/autoconfig_url"] = value;
577 } else if (key == "httpProxy") {
578 AddProxy("/system/http_proxy/", value);
579 } else if (key == "httpsProxy") {
580 AddProxy("/system/proxy/secure_", value);
581 } else if (key == "ftpProxy") {
582 AddProxy("/system/proxy/ftp_", value);
583 } else if (key == "ReversedException") {
584 // We count "true" or any nonzero number as true, otherwise false.
585 // Note that if the value is not actually numeric StringToInt()
586 // will return 0, which we count as false.
[email protected]9a3d8d42009-09-03 17:01:46587 reversed_exception_ = value == "true" || StringToInt(value);
[email protected]d7395e732009-08-28 23:13:43588 } else if (key == "NoProxyFor") {
589 std::vector<std::string> exceptions;
590 StringTokenizer tk(value, ",");
591 while (tk.GetNext()) {
592 std::string token = tk.token();
593 if (!token.empty())
594 exceptions.push_back(token);
595 }
596 strings_table_["/system/http_proxy/ignore_hosts"] = exceptions;
597 } else if (key == "AuthMode") {
598 // Check for authentication, just so we can warn.
599 int mode = StringToInt(value);
600 if (mode) {
601 // ProxyConfig does not support authentication parameters, but
602 // Chrome will prompt for the password later. So we ignore this.
603 LOG(WARNING) <<
604 "Proxy authentication parameters ignored, see bug 16709";
605 }
606 }
607 }
608
609 void ResolveIndirect(std::string key) {
610 // We can't save the environment variable getter that was passed
611 // when this object was constructed, but this setting is likely
612 // to be pretty unusual and the actual values it would return can
613 // be tested without using it. So we just use getenv() here.
614 string_map_type::iterator it = string_table_.find(key);
615 if (it != string_table_.end()) {
616 char* value = getenv(it->second.c_str());
617 if (value)
618 it->second = value;
619 }
620 }
621
622 // The settings in kioslaverc could occur in any order, but some affect
623 // others. Rather than read the whole file in and then query them in an
624 // order that allows us to handle that, we read the settings in whatever
625 // order they occur and do any necessary tweaking after we finish.
626 void ResolveModeEffects() {
627 if (indirect_manual_) {
628 ResolveIndirect("/system/http_proxy/host");
629 ResolveIndirect("/system/proxy/secure_host");
630 ResolveIndirect("/system/proxy/ftp_host");
631 }
632 if (auto_no_pac_) {
633 // Remove the PAC URL; we're not supposed to use it.
634 string_table_.erase("/system/proxy/autoconfig_url");
635 }
636 if (reversed_exception_) {
637 // We don't actually support this setting. (It means to use the proxy
638 // *only* for the exception list, rather than everything but them.)
639 // Nevertheless we can do better than *exactly the opposite* of the
640 // desired behavior by clearing the exception list and warning.
641 strings_table_.erase("/system/http_proxy/ignore_hosts");
642 LOG(WARNING) << "KDE reversed proxy exception list not supported";
643 }
644 }
645
646 // Reads kioslaverc one line at a time and calls AddKDESetting() to add
647 // each relevant name-value pair to the appropriate value table.
648 void UpdateCachedSettings() {
649 FilePath kioslaverc = kde_config_dir_.Append(
650 FILE_PATH_LITERAL("kioslaverc"));
651 file_util::ScopedFILE input(file_util::OpenFile(kioslaverc, "r"));
652 if (!input.get())
653 return;
654 ResetCachedSettings();
655 bool in_proxy_settings = false;
656 bool line_too_long = false;
[email protected]9a3d8d42009-09-03 17:01:46657 char line[BUFFER_SIZE];
658 // fgets() will return NULL on EOF or error.
[email protected]d7395e732009-08-28 23:13:43659 while (fgets(line, sizeof(line), input.get())) {
660 // fgets() guarantees the line will be properly terminated.
661 size_t length = strlen(line);
662 if (!length)
663 continue;
664 // This should be true even with CRLF endings.
665 if (line[length - 1] != '\n') {
666 line_too_long = true;
667 continue;
668 }
669 if (line_too_long) {
670 // The previous line had no line ending, but this done does. This is
671 // the end of the line that was too long, so warn here and skip it.
672 LOG(WARNING) << "skipped very long line in " << kioslaverc.value();
673 line_too_long = false;
674 continue;
675 }
676 // Remove the LF at the end, and the CR if there is one.
677 line[--length] = '\0';
678 if (length && line[length - 1] == '\r')
679 line[--length] = '\0';
680 // Now parse the line.
681 if (line[0] == '[') {
682 // Switching sections. All we care about is whether this is
683 // the (a?) proxy settings section, for both KDE3 and KDE4.
684 in_proxy_settings = !strncmp(line, "[Proxy Settings]", 16);
685 } else if (in_proxy_settings) {
686 // A regular line, in the (a?) proxy settings section.
[email protected]9a3d8d42009-09-03 17:01:46687 char* split = strchr(line, '=');
688 // Skip this line if it does not contain an = sign.
689 if (!split)
[email protected]d7395e732009-08-28 23:13:43690 continue;
[email protected]9a3d8d42009-09-03 17:01:46691 // Split the line on the = and advance |split|.
692 *(split++) = 0;
693 std::string key = line;
694 std::string value = split;
695 TrimWhitespaceASCII(key, TRIM_ALL, &key);
696 TrimWhitespaceASCII(value, TRIM_ALL, &value);
697 // Skip this line if the key name is empty.
698 if (key.empty())
[email protected]d7395e732009-08-28 23:13:43699 continue;
700 // Is the value name localized?
[email protected]9a3d8d42009-09-03 17:01:46701 if (key[key.length() - 1] == ']') {
702 // Find the matching bracket.
703 length = key.rfind('[');
704 // Skip this line if the localization indicator is malformed.
705 if (length == std::string::npos)
[email protected]d7395e732009-08-28 23:13:43706 continue;
707 // Trim the localization indicator off.
[email protected]9a3d8d42009-09-03 17:01:46708 key.resize(length);
709 // Remove any resulting trailing whitespace.
710 TrimWhitespaceASCII(key, TRIM_TRAILING, &key);
711 // Skip this line if the key name is now empty.
712 if (key.empty())
713 continue;
[email protected]d7395e732009-08-28 23:13:43714 }
[email protected]d7395e732009-08-28 23:13:43715 // Now fill in the tables.
[email protected]9a3d8d42009-09-03 17:01:46716 AddKDESetting(key, value);
[email protected]d7395e732009-08-28 23:13:43717 }
718 }
719 if (ferror(input.get()))
720 LOG(ERROR) << "error reading " << kioslaverc.value();
721 ResolveModeEffects();
722 }
723
724 // This is the callback from the debounce timer.
725 void OnDebouncedNotification() {
726 DCHECK(MessageLoop::current() == file_loop_);
727 LOG(INFO) << "inotify change notification for kioslaverc";
728 UpdateCachedSettings();
729 DCHECK(notify_delegate_);
730 // Forward to a method on the proxy config service delegate object.
731 notify_delegate_->OnCheckProxyConfigSettings();
732 }
733
734 // Called by OnFileCanReadWithoutBlocking() on the file thread. Reads
735 // from the inotify file descriptor and starts up a debounce timer if
736 // an event for kioslaverc is seen.
737 void OnChangeNotification() {
738 DCHECK(inotify_fd_ >= 0);
739 DCHECK(MessageLoop::current() == file_loop_);
740 char event_buf[(sizeof(inotify_event) + NAME_MAX + 1) * 4];
741 bool kioslaverc_touched = false;
742 ssize_t r;
743 while ((r = read(inotify_fd_, event_buf, sizeof(event_buf))) > 0) {
744 // inotify returns variable-length structures, which is why we have
745 // this strange-looking loop instead of iterating through an array.
746 char* event_ptr = event_buf;
747 while (event_ptr < event_buf + r) {
748 inotify_event* event = reinterpret_cast<inotify_event*>(event_ptr);
749 // The kernel always feeds us whole events.
750 CHECK(event_ptr + sizeof(inotify_event) <= event_buf + r);
751 CHECK(event->name + event->len <= event_buf + r);
752 if (!strcmp(event->name, "kioslaverc"))
753 kioslaverc_touched = true;
754 // Advance the pointer just past the end of the filename.
755 event_ptr = event->name + event->len;
756 }
757 // We keep reading even if |kioslaverc_touched| is true to drain the
758 // inotify event queue.
759 }
760 if (!r)
761 // Instead of returning -1 and setting errno to EINVAL if there is not
762 // enough buffer space, older kernels (< 2.6.21) return 0. Simulate the
763 // new behavior (EINVAL) so we can reuse the code below.
764 errno = EINVAL;
765 if (errno != EAGAIN) {
766 LOG(WARNING) << "error reading inotify file descriptor: "
767 << strerror(errno);
768 if (errno == EINVAL) {
769 // Our buffer is not large enough to read the next event. This should
770 // not happen (because its size is calculated to always be sufficiently
771 // large), but if it does we'd warn continuously since |inotify_fd_|
772 // would be forever ready to read. Close it and stop watching instead.
773 LOG(ERROR) << "inotify failure; no longer watching kioslaverc!";
774 inotify_watcher_.StopWatchingFileDescriptor();
775 close(inotify_fd_);
776 inotify_fd_ = -1;
777 }
778 }
779 if (kioslaverc_touched) {
780 // We don't use Reset() because the timer may not yet be running.
781 // (In that case Stop() is a no-op.)
782 debounce_timer_.Stop();
783 debounce_timer_.Start(base::TimeDelta::FromMilliseconds(
784 kDebounceTimeoutMilliseconds), this,
785 &GConfSettingGetterImplKDE::OnDebouncedNotification);
786 }
787 }
788
789 typedef std::map<std::string, std::string> string_map_type;
790 typedef std::map<std::string, std::vector<std::string> > strings_map_type;
791
792 int inotify_fd_;
793 base::MessagePumpLibevent::FileDescriptorWatcher inotify_watcher_;
794 ProxyConfigServiceLinux::Delegate* notify_delegate_;
795 base::OneShotTimer<GConfSettingGetterImplKDE> debounce_timer_;
796 FilePath kde_config_dir_;
797 bool indirect_manual_;
798 bool auto_no_pac_;
799 bool reversed_exception_;
800
801 // We cache these settings whenever we re-read the kioslaverc file.
802 string_map_type string_table_;
803 strings_map_type strings_table_;
804
805 // Message loop of the file thread, for reading kioslaverc. If NULL,
806 // just read it directly (for testing). We also handle inotify events
807 // on this thread.
808 MessageLoopForIO* file_loop_;
809
810 DISALLOW_COPY_AND_ASSIGN(GConfSettingGetterImplKDE);
[email protected]861c6c62009-04-20 16:50:56811};
812
813} // namespace
814
[email protected]3e44697f2009-05-22 14:37:39815bool ProxyConfigServiceLinux::Delegate::GetProxyFromGConf(
[email protected]861c6c62009-04-20 16:50:56816 const char* key_prefix, bool is_socks, ProxyServer* result_server) {
817 std::string key(key_prefix);
818 std::string host;
819 if (!gconf_getter_->GetString((key + "host").c_str(), &host)
820 || host.empty()) {
821 // Unset or empty.
822 return false;
823 }
824 // Check for an optional port.
[email protected]d7395e732009-08-28 23:13:43825 int port = 0;
[email protected]861c6c62009-04-20 16:50:56826 gconf_getter_->GetInt((key + "port").c_str(), &port);
827 if (port != 0) {
828 // If a port is set and non-zero:
829 host += ":" + IntToString(port);
830 }
831 host = FixupProxyHostScheme(
832 is_socks ? ProxyServer::SCHEME_SOCKS4 : ProxyServer::SCHEME_HTTP,
833 host);
[email protected]87a102b2009-07-14 05:23:30834 ProxyServer proxy_server = ProxyServer::FromURI(host,
835 ProxyServer::SCHEME_HTTP);
[email protected]861c6c62009-04-20 16:50:56836 if (proxy_server.is_valid()) {
837 *result_server = proxy_server;
838 return true;
839 }
840 return false;
841}
842
[email protected]3e44697f2009-05-22 14:37:39843bool ProxyConfigServiceLinux::Delegate::GetConfigFromGConf(
844 ProxyConfig* config) {
[email protected]861c6c62009-04-20 16:50:56845 std::string mode;
846 if (!gconf_getter_->GetString("/system/proxy/mode", &mode)) {
847 // We expect this to always be set, so if we don't see it then we
848 // probably have a gconf problem, and so we don't have a valid
849 // proxy config.
850 return false;
851 }
[email protected]3e44697f2009-05-22 14:37:39852 if (mode == "none") {
[email protected]861c6c62009-04-20 16:50:56853 // Specifically specifies no proxy.
854 return true;
[email protected]3e44697f2009-05-22 14:37:39855 }
[email protected]861c6c62009-04-20 16:50:56856
[email protected]3e44697f2009-05-22 14:37:39857 if (mode == "auto") {
[email protected]861c6c62009-04-20 16:50:56858 // automatic proxy config
859 std::string pac_url_str;
860 if (gconf_getter_->GetString("/system/proxy/autoconfig_url",
861 &pac_url_str)) {
862 if (!pac_url_str.empty()) {
863 GURL pac_url(pac_url_str);
864 if (!pac_url.is_valid())
865 return false;
866 config->pac_url = pac_url;
867 return true;
868 }
869 }
870 config->auto_detect = true;
871 return true;
872 }
873
[email protected]3e44697f2009-05-22 14:37:39874 if (mode != "manual") {
[email protected]861c6c62009-04-20 16:50:56875 // Mode is unrecognized.
876 return false;
877 }
878 bool use_http_proxy;
879 if (gconf_getter_->GetBoolean("/system/http_proxy/use_http_proxy",
880 &use_http_proxy)
881 && !use_http_proxy) {
882 // Another master switch for some reason. If set to false, then no
883 // proxy. But we don't panic if the key doesn't exist.
884 return true;
885 }
886
887 bool same_proxy = false;
888 // Indicates to use the http proxy for all protocols. This one may
889 // not exist (presumably on older versions), assume false in that
890 // case.
891 gconf_getter_->GetBoolean("/system/http_proxy/use_same_proxy",
892 &same_proxy);
893
894 ProxyServer proxy_server;
895 if (!same_proxy) {
896 // Try socks.
897 if (GetProxyFromGConf("/system/proxy/socks_", true, &proxy_server)) {
898 // gconf settings do not appear to distinguish between socks
899 // version. We default to version 4.
900 config->proxy_rules.type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
901 config->proxy_rules.single_proxy = proxy_server;
902 }
903 }
904 if (config->proxy_rules.empty()) {
905 bool have_http = GetProxyFromGConf("/system/http_proxy/", false,
906 &proxy_server);
907 if (same_proxy) {
908 if (have_http) {
909 config->proxy_rules.type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
910 config->proxy_rules.single_proxy = proxy_server;
911 }
912 } else {
913 // Protocol specific settings.
914 if (have_http)
915 config->proxy_rules.proxy_for_http = proxy_server;
916 bool have_secure = GetProxyFromGConf("/system/proxy/secure_", false,
917 &proxy_server);
918 if (have_secure)
919 config->proxy_rules.proxy_for_https = proxy_server;
920 bool have_ftp = GetProxyFromGConf("/system/proxy/ftp_", false,
921 &proxy_server);
922 if (have_ftp)
923 config->proxy_rules.proxy_for_ftp = proxy_server;
924 if (have_http || have_secure || have_ftp)
925 config->proxy_rules.type =
926 ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
927 }
928 }
929
930 if (config->proxy_rules.empty()) {
931 // Manual mode but we couldn't parse any rules.
932 return false;
933 }
934
935 // Check for authentication, just so we can warn.
[email protected]d7395e732009-08-28 23:13:43936 bool use_auth = false;
[email protected]861c6c62009-04-20 16:50:56937 gconf_getter_->GetBoolean("/system/http_proxy/use_authentication",
938 &use_auth);
[email protected]62749f182009-07-15 13:16:54939 if (use_auth) {
940 // ProxyConfig does not support authentication parameters, but
941 // Chrome will prompt for the password later. So we ignore
942 // /system/http_proxy/*auth* settings.
943 LOG(WARNING) << "Proxy authentication parameters ignored, see bug 16709";
944 }
[email protected]861c6c62009-04-20 16:50:56945
946 // Now the bypass list.
947 gconf_getter_->GetStringList("/system/http_proxy/ignore_hosts",
948 &config->proxy_bypass);
949 // Note that there are no settings with semantics corresponding to
950 // config->proxy_bypass_local_names.
951
952 return true;
953}
954
[email protected]3e44697f2009-05-22 14:37:39955ProxyConfigServiceLinux::Delegate::Delegate(
[email protected]d7395e732009-08-28 23:13:43956 base::EnvironmentVariableGetter* env_var_getter)
957 : env_var_getter_(env_var_getter),
958 glib_default_loop_(NULL), io_loop_(NULL) {
959 // Figure out which GConfSettingGetterImpl to use, if any.
960 switch (base::GetDesktopEnvironment(env_var_getter)) {
961 case base::DESKTOP_ENVIRONMENT_GNOME:
962 gconf_getter_.reset(new GConfSettingGetterImplGConf());
963 break;
964 case base::DESKTOP_ENVIRONMENT_KDE3:
965 case base::DESKTOP_ENVIRONMENT_KDE4:
966 gconf_getter_.reset(new GConfSettingGetterImplKDE(env_var_getter));
967 break;
968 case base::DESKTOP_ENVIRONMENT_OTHER:
969 break;
970 }
971}
972
973ProxyConfigServiceLinux::Delegate::Delegate(
[email protected]87fc1682009-07-22 00:22:49974 base::EnvironmentVariableGetter* env_var_getter,
[email protected]861c6c62009-04-20 16:50:56975 GConfSettingGetter* gconf_getter)
[email protected]3e44697f2009-05-22 14:37:39976 : env_var_getter_(env_var_getter), gconf_getter_(gconf_getter),
977 glib_default_loop_(NULL), io_loop_(NULL) {
[email protected]861c6c62009-04-20 16:50:56978}
979
[email protected]3e44697f2009-05-22 14:37:39980void ProxyConfigServiceLinux::Delegate::SetupAndFetchInitialConfig(
[email protected]d7395e732009-08-28 23:13:43981 MessageLoop* glib_default_loop, MessageLoop* io_loop,
982 MessageLoopForIO* file_loop) {
[email protected]3e44697f2009-05-22 14:37:39983 // We should be running on the default glib main loop thread right
984 // now. gconf can only be accessed from this thread.
985 DCHECK(MessageLoop::current() == glib_default_loop);
986 glib_default_loop_ = glib_default_loop;
987 io_loop_ = io_loop;
988
[email protected]d7395e732009-08-28 23:13:43989 // If we are passed a NULL io_loop or file_loop, then we don't set up
990 // proxy setting change notifications. This should not be the usual
991 // case but is intended to simplify test setups.
992 if (!io_loop_ || !file_loop)
993 LOG(INFO) << "Monitoring of proxy setting changes is disabled";
[email protected]3e44697f2009-05-22 14:37:39994
995 // Fetch and cache the current proxy config. The config is left in
996 // cached_config_, where GetProxyConfig() running on the IO thread
997 // will expect to find it. This is safe to do because we return
998 // before this ProxyConfigServiceLinux is passed on to
999 // the ProxyService.
[email protected]d6cb85b2009-07-23 22:10:531000
1001 // Note: It would be nice to prioritize environment variables
1002 // and only fallback to gconf if env vars were unset. But
1003 // gnome-terminal "helpfully" sets http_proxy and no_proxy, and it
1004 // does so even if the proxy mode is set to auto, which would
1005 // mislead us.
1006
[email protected]3e44697f2009-05-22 14:37:391007 bool got_config = false;
[email protected]d7395e732009-08-28 23:13:431008 if (gconf_getter_.get()) {
1009 if (gconf_getter_->Init(glib_default_loop, file_loop) &&
1010 (!io_loop || !file_loop || gconf_getter_->SetupNotification(this))) {
1011 if (GetConfigFromGConf(&cached_config_)) {
1012 cached_config_.set_id(1); // mark it as valid
1013 got_config = true;
1014 LOG(INFO) << "Obtained proxy settings from " <<
1015 gconf_getter_->GetDataSource();
1016 // If gconf proxy mode is "none", meaning direct, then we take
1017 // that to be a valid config and will not check environment
1018 // variables. The alternative would have been to look for a proxy
1019 // whereever we can find one.
1020 //
1021 // Keep a copy of the config for use from this thread for
1022 // comparison with updated settings when we get notifications.
1023 reference_config_ = cached_config_;
1024 reference_config_.set_id(1); // mark it as valid
1025 } else {
1026 gconf_getter_->Shutdown(); // Stop notifications
[email protected]d6cb85b2009-07-23 22:10:531027 }
[email protected]d7395e732009-08-28 23:13:431028 }
[email protected]861c6c62009-04-20 16:50:561029 }
[email protected]d6cb85b2009-07-23 22:10:531030
[email protected]3e44697f2009-05-22 14:37:391031 if (!got_config) {
[email protected]d6cb85b2009-07-23 22:10:531032 // We fall back on environment variables.
[email protected]3e44697f2009-05-22 14:37:391033 //
1034 // Consulting environment variables doesn't need to be done from
1035 // the default glib main loop, but it's a tiny enough amount of
1036 // work.
1037 if (GetConfigFromEnv(&cached_config_)) {
1038 cached_config_.set_id(1); // mark it as valid
[email protected]d7395e732009-08-28 23:13:431039 LOG(INFO) << "Obtained proxy settings from environment variables";
[email protected]3e44697f2009-05-22 14:37:391040 }
[email protected]861c6c62009-04-20 16:50:561041 }
[email protected]3e44697f2009-05-22 14:37:391042}
1043
1044void ProxyConfigServiceLinux::Delegate::Reset() {
1045 DCHECK(!glib_default_loop_ || MessageLoop::current() == glib_default_loop_);
[email protected]d7395e732009-08-28 23:13:431046 gconf_getter_->Shutdown();
[email protected]3e44697f2009-05-22 14:37:391047 cached_config_ = ProxyConfig();
1048}
1049
1050int ProxyConfigServiceLinux::Delegate::GetProxyConfig(ProxyConfig* config) {
1051 // This is called from the IO thread.
1052 DCHECK(!io_loop_ || MessageLoop::current() == io_loop_);
1053
1054 // Simply return the last proxy configuration that glib_default_loop
1055 // notified us of.
1056 *config = cached_config_;
1057 return cached_config_.is_valid() ? OK : ERR_FAILED;
1058}
1059
[email protected]d7395e732009-08-28 23:13:431060// Depending on the GConfSettingGetter in use, this method will be called
1061// on either the UI thread (GConf) or the file thread (KDE).
[email protected]3e44697f2009-05-22 14:37:391062void ProxyConfigServiceLinux::Delegate::OnCheckProxyConfigSettings() {
[email protected]d7395e732009-08-28 23:13:431063 MessageLoop* required_loop = gconf_getter_->GetNotificationLoop();
1064 DCHECK(!required_loop || MessageLoop::current() == required_loop);
[email protected]3e44697f2009-05-22 14:37:391065 ProxyConfig new_config;
1066 bool valid = GetConfigFromGConf(&new_config);
1067 if (valid)
1068 new_config.set_id(1); // mark it as valid
1069
1070 // See if it is different than what we had before.
1071 if (new_config.is_valid() != reference_config_.is_valid() ||
1072 !new_config.Equals(reference_config_)) {
1073 // Post a task to |io_loop| with the new configuration, so it can
1074 // update |cached_config_|.
1075 io_loop_->PostTask(
1076 FROM_HERE,
1077 NewRunnableMethod(
1078 this,
1079 &ProxyConfigServiceLinux::Delegate::SetNewProxyConfig,
1080 new_config));
[email protected]d1f9d472009-08-13 19:59:301081 // Update the thread-private copy in |reference_config_| as well.
1082 reference_config_ = new_config;
[email protected]3e44697f2009-05-22 14:37:391083 }
1084}
1085
1086void ProxyConfigServiceLinux::Delegate::SetNewProxyConfig(
1087 const ProxyConfig& new_config) {
1088 DCHECK(MessageLoop::current() == io_loop_);
1089 LOG(INFO) << "Proxy configuration changed";
1090 cached_config_ = new_config;
1091}
1092
1093void ProxyConfigServiceLinux::Delegate::PostDestroyTask() {
[email protected]d7395e732009-08-28 23:13:431094 if (!gconf_getter_.get())
1095 return;
1096 MessageLoop* shutdown_loop = gconf_getter_->GetNotificationLoop();
1097 if (!shutdown_loop || MessageLoop::current() == shutdown_loop) {
[email protected]3e44697f2009-05-22 14:37:391098 // Already on the right thread, call directly.
1099 // This is the case for the unittests.
1100 OnDestroy();
1101 } else {
[email protected]d7395e732009-08-28 23:13:431102 // Post to shutdown thread. Note that on browser shutdown, we may quit
1103 // this MessageLoop and exit the program before ever running this.
1104 shutdown_loop->PostTask(
[email protected]3e44697f2009-05-22 14:37:391105 FROM_HERE,
1106 NewRunnableMethod(
1107 this,
1108 &ProxyConfigServiceLinux::Delegate::OnDestroy));
1109 }
1110}
1111void ProxyConfigServiceLinux::Delegate::OnDestroy() {
[email protected]d7395e732009-08-28 23:13:431112 MessageLoop* shutdown_loop = gconf_getter_->GetNotificationLoop();
1113 DCHECK(!shutdown_loop || MessageLoop::current() == shutdown_loop);
1114 gconf_getter_->Shutdown();
[email protected]3e44697f2009-05-22 14:37:391115}
1116
1117ProxyConfigServiceLinux::ProxyConfigServiceLinux()
[email protected]d7395e732009-08-28 23:13:431118 : delegate_(new Delegate(base::EnvironmentVariableGetter::Create())) {
[email protected]3e44697f2009-05-22 14:37:391119}
1120
1121ProxyConfigServiceLinux::ProxyConfigServiceLinux(
[email protected]9a3d8d42009-09-03 17:01:461122 base::EnvironmentVariableGetter* env_var_getter)
1123 : delegate_(new Delegate(env_var_getter)) {
1124}
1125
1126ProxyConfigServiceLinux::ProxyConfigServiceLinux(
[email protected]87fc1682009-07-22 00:22:491127 base::EnvironmentVariableGetter* env_var_getter,
[email protected]3e44697f2009-05-22 14:37:391128 GConfSettingGetter* gconf_getter)
1129 : delegate_(new Delegate(env_var_getter, gconf_getter)) {
[email protected]861c6c62009-04-20 16:50:561130}
1131
1132} // namespace net