blob: b001c1120530f7b33d354e2d8553c8ff60f3fd74 [file] [log] [blame]
[email protected]9bc8cff2010-04-03 01:05:391// Copyright (c) 2010 The Chromium Authors. All rights reserved.
[email protected]861c6c62009-04-20 16:50:562// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/proxy/proxy_config_service_linux.h"
6
[email protected]d7395e732009-08-28 23:13:437#include <errno.h>
8#include <fcntl.h>
[email protected]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]9bc8cff2010-04-03 01:05:3916#include <map>
17
[email protected]76b90d312010-08-03 03:00:5018#include "base/environment.h"
[email protected]d7395e732009-08-28 23:13:4319#include "base/file_path.h"
20#include "base/file_util.h"
[email protected]861c6c62009-04-20 16:50:5621#include "base/logging.h"
[email protected]d7395e732009-08-28 23:13:4322#include "base/message_loop.h"
[email protected]528c56d2010-07-30 19:28:4423#include "base/string_number_conversions.h"
[email protected]861c6c62009-04-20 16:50:5624#include "base/string_tokenizer.h"
25#include "base/string_util.h"
[email protected]3e44697f2009-05-22 14:37:3926#include "base/task.h"
[email protected]d7395e732009-08-28 23:13:4327#include "base/timer.h"
[email protected]1c657852010-04-22 23:28:0528#include "base/xdg_util.h"
[email protected]861c6c62009-04-20 16:50:5629#include "googleurl/src/url_canon.h"
30#include "net/base/net_errors.h"
31#include "net/http/http_util.h"
32#include "net/proxy/proxy_config.h"
33#include "net/proxy/proxy_server.h"
34
35namespace net {
36
37namespace {
38
[email protected]861c6c62009-04-20 16:50:5639// Given a proxy hostname from a setting, returns that hostname with
40// an appropriate proxy server scheme prefix.
41// scheme indicates the desired proxy scheme: usually http, with
42// socks 4 or 5 as special cases.
[email protected]87a102b2009-07-14 05:23:3043// TODO(arindam): Remove URI string manipulation by using MapUrlSchemeToProxy.
[email protected]861c6c62009-04-20 16:50:5644std::string FixupProxyHostScheme(ProxyServer::Scheme scheme,
45 std::string host) {
46 if (scheme == ProxyServer::SCHEME_SOCKS4 &&
47 StartsWithASCII(host, "socks5://", false)) {
48 // We default to socks 4, but if the user specifically set it to
49 // socks5://, then use that.
50 scheme = ProxyServer::SCHEME_SOCKS5;
51 }
52 // Strip the scheme if any.
53 std::string::size_type colon = host.find("://");
54 if (colon != std::string::npos)
55 host = host.substr(colon + 3);
56 // If a username and perhaps password are specified, give a warning.
57 std::string::size_type at_sign = host.find("@");
58 // Should this be supported?
59 if (at_sign != std::string::npos) {
[email protected]62749f182009-07-15 13:16:5460 // ProxyConfig does not support authentication parameters, but Chrome
61 // will prompt for the password later. Disregard the
62 // authentication parameters and continue with this hostname.
63 LOG(WARNING) << "Proxy authentication parameters ignored, see bug 16709";
[email protected]861c6c62009-04-20 16:50:5664 host = host.substr(at_sign + 1);
65 }
66 // If this is a socks proxy, prepend a scheme so as to tell
67 // ProxyServer. This also allows ProxyServer to choose the right
68 // default port.
69 if (scheme == ProxyServer::SCHEME_SOCKS4)
70 host = "socks4://" + host;
71 else if (scheme == ProxyServer::SCHEME_SOCKS5)
72 host = "socks5://" + host;
[email protected]d7395e732009-08-28 23:13:4373 // If there is a trailing slash, remove it so |host| will parse correctly
74 // even if it includes a port number (since the slash is not numeric).
75 if (host.length() && host[host.length() - 1] == '/')
76 host.resize(host.length() - 1);
[email protected]861c6c62009-04-20 16:50:5677 return host;
78}
79
80} // namespace
81
[email protected]3e44697f2009-05-22 14:37:3982bool ProxyConfigServiceLinux::Delegate::GetProxyFromEnvVarForScheme(
[email protected]861c6c62009-04-20 16:50:5683 const char* variable, ProxyServer::Scheme scheme,
84 ProxyServer* result_server) {
85 std::string env_value;
[email protected]9bc8cff2010-04-03 01:05:3986 if (env_var_getter_->GetEnv(variable, &env_value)) {
[email protected]861c6c62009-04-20 16:50:5687 if (!env_value.empty()) {
88 env_value = FixupProxyHostScheme(scheme, env_value);
[email protected]87a102b2009-07-14 05:23:3089 ProxyServer proxy_server =
90 ProxyServer::FromURI(env_value, ProxyServer::SCHEME_HTTP);
[email protected]861c6c62009-04-20 16:50:5691 if (proxy_server.is_valid() && !proxy_server.is_direct()) {
92 *result_server = proxy_server;
93 return true;
94 } else {
[email protected]3e44697f2009-05-22 14:37:3995 LOG(ERROR) << "Failed to parse environment variable " << variable;
[email protected]861c6c62009-04-20 16:50:5696 }
97 }
98 }
99 return false;
100}
101
[email protected]3e44697f2009-05-22 14:37:39102bool ProxyConfigServiceLinux::Delegate::GetProxyFromEnvVar(
[email protected]861c6c62009-04-20 16:50:56103 const char* variable, ProxyServer* result_server) {
104 return GetProxyFromEnvVarForScheme(variable, ProxyServer::SCHEME_HTTP,
105 result_server);
106}
107
[email protected]3e44697f2009-05-22 14:37:39108bool ProxyConfigServiceLinux::Delegate::GetConfigFromEnv(ProxyConfig* config) {
[email protected]861c6c62009-04-20 16:50:56109 // Check for automatic configuration first, in
110 // "auto_proxy". Possibly only the "environment_proxy" firefox
111 // extension has ever used this, but it still sounds like a good
112 // idea.
113 std::string auto_proxy;
[email protected]9bc8cff2010-04-03 01:05:39114 if (env_var_getter_->GetEnv("auto_proxy", &auto_proxy)) {
[email protected]861c6c62009-04-20 16:50:56115 if (auto_proxy.empty()) {
116 // Defined and empty => autodetect
[email protected]ed4ed0f2010-02-24 00:20:48117 config->set_auto_detect(true);
[email protected]861c6c62009-04-20 16:50:56118 } else {
119 // specified autoconfig URL
[email protected]ed4ed0f2010-02-24 00:20:48120 config->set_pac_url(GURL(auto_proxy));
[email protected]861c6c62009-04-20 16:50:56121 }
122 return true;
123 }
124 // "all_proxy" is a shortcut to avoid defining {http,https,ftp}_proxy.
125 ProxyServer proxy_server;
126 if (GetProxyFromEnvVar("all_proxy", &proxy_server)) {
[email protected]ed4ed0f2010-02-24 00:20:48127 config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
128 config->proxy_rules().single_proxy = proxy_server;
[email protected]861c6c62009-04-20 16:50:56129 } else {
130 bool have_http = GetProxyFromEnvVar("http_proxy", &proxy_server);
131 if (have_http)
[email protected]ed4ed0f2010-02-24 00:20:48132 config->proxy_rules().proxy_for_http = proxy_server;
[email protected]861c6c62009-04-20 16:50:56133 // It would be tempting to let http_proxy apply for all protocols
134 // if https_proxy and ftp_proxy are not defined. Googling turns up
135 // several documents that mention only http_proxy. But then the
136 // user really might not want to proxy https. And it doesn't seem
137 // like other apps do this. So we will refrain.
138 bool have_https = GetProxyFromEnvVar("https_proxy", &proxy_server);
139 if (have_https)
[email protected]ed4ed0f2010-02-24 00:20:48140 config->proxy_rules().proxy_for_https = proxy_server;
[email protected]861c6c62009-04-20 16:50:56141 bool have_ftp = GetProxyFromEnvVar("ftp_proxy", &proxy_server);
142 if (have_ftp)
[email protected]ed4ed0f2010-02-24 00:20:48143 config->proxy_rules().proxy_for_ftp = proxy_server;
[email protected]861c6c62009-04-20 16:50:56144 if (have_http || have_https || have_ftp) {
145 // mustn't change type unless some rules are actually set.
[email protected]ed4ed0f2010-02-24 00:20:48146 config->proxy_rules().type =
147 ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
[email protected]861c6c62009-04-20 16:50:56148 }
149 }
[email protected]ed4ed0f2010-02-24 00:20:48150 if (config->proxy_rules().empty()) {
[email protected]861c6c62009-04-20 16:50:56151 // If the above were not defined, try for socks.
152 ProxyServer::Scheme scheme = ProxyServer::SCHEME_SOCKS4;
153 std::string env_version;
[email protected]9bc8cff2010-04-03 01:05:39154 if (env_var_getter_->GetEnv("SOCKS_VERSION", &env_version)
[email protected]3e44697f2009-05-22 14:37:39155 && env_version == "5")
[email protected]861c6c62009-04-20 16:50:56156 scheme = ProxyServer::SCHEME_SOCKS5;
157 if (GetProxyFromEnvVarForScheme("SOCKS_SERVER", scheme, &proxy_server)) {
[email protected]ed4ed0f2010-02-24 00:20:48158 config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
159 config->proxy_rules().single_proxy = proxy_server;
[email protected]861c6c62009-04-20 16:50:56160 }
161 }
162 // Look for the proxy bypass list.
163 std::string no_proxy;
[email protected]9bc8cff2010-04-03 01:05:39164 env_var_getter_->GetEnv("no_proxy", &no_proxy);
[email protected]ed4ed0f2010-02-24 00:20:48165 if (config->proxy_rules().empty()) {
[email protected]861c6c62009-04-20 16:50:56166 // Having only "no_proxy" set, presumably to "*", makes it
167 // explicit that env vars do specify a configuration: having no
168 // rules specified only means the user explicitly asks for direct
169 // connections.
170 return !no_proxy.empty();
171 }
[email protected]7541206c2010-02-19 20:24:06172 // Note that this uses "suffix" matching. So a bypass of "google.com"
173 // is understood to mean a bypass of "*google.com".
[email protected]ed4ed0f2010-02-24 00:20:48174 config->proxy_rules().bypass_rules.ParseFromStringUsingSuffixMatching(
175 no_proxy);
[email protected]861c6c62009-04-20 16:50:56176 return true;
177}
178
179namespace {
180
[email protected]d7395e732009-08-28 23:13:43181const int kDebounceTimeoutMilliseconds = 250;
[email protected]3e44697f2009-05-22 14:37:39182
[email protected]d7395e732009-08-28 23:13:43183// This is the "real" gconf version that actually uses gconf.
184class GConfSettingGetterImplGConf
[email protected]861c6c62009-04-20 16:50:56185 : public ProxyConfigServiceLinux::GConfSettingGetter {
186 public:
[email protected]d7395e732009-08-28 23:13:43187 GConfSettingGetterImplGConf()
188 : client_(NULL), notify_delegate_(NULL), loop_(NULL) {}
[email protected]3e44697f2009-05-22 14:37:39189
[email protected]d7395e732009-08-28 23:13:43190 virtual ~GConfSettingGetterImplGConf() {
[email protected]3e44697f2009-05-22 14:37:39191 // client_ should have been released before now, from
[email protected]f5b13442009-07-13 15:23:59192 // Delegate::OnDestroy(), while running on the UI thread. However
193 // on exiting the process, it may happen that
194 // Delegate::OnDestroy() task is left pending on the glib loop
195 // after the loop was quit, and pending tasks may then be deleted
196 // without being run.
197 if (client_) {
198 // gconf client was not cleaned up.
199 if (MessageLoop::current() == loop_) {
200 // We are on the UI thread so we can clean it safely. This is
201 // the case at least for ui_tests running under Valgrind in
202 // bug 16076.
[email protected]d7395e732009-08-28 23:13:43203 LOG(INFO) << "~GConfSettingGetterImplGConf: releasing gconf client";
204 Shutdown();
[email protected]f5b13442009-07-13 15:23:59205 } else {
[email protected]d7395e732009-08-28 23:13:43206 LOG(WARNING) << "~GConfSettingGetterImplGConf: leaking gconf client";
[email protected]f5b13442009-07-13 15:23:59207 client_ = NULL;
208 }
209 }
[email protected]3e44697f2009-05-22 14:37:39210 DCHECK(!client_);
[email protected]861c6c62009-04-20 16:50:56211 }
212
[email protected]d7395e732009-08-28 23:13:43213 virtual bool Init(MessageLoop* glib_default_loop,
214 MessageLoopForIO* file_loop) {
215 DCHECK(MessageLoop::current() == glib_default_loop);
[email protected]3e44697f2009-05-22 14:37:39216 DCHECK(!client_);
217 DCHECK(!loop_);
[email protected]d7395e732009-08-28 23:13:43218 loop_ = glib_default_loop;
[email protected]3e44697f2009-05-22 14:37:39219 client_ = gconf_client_get_default();
[email protected]861c6c62009-04-20 16:50:56220 if (!client_) {
[email protected]861c6c62009-04-20 16:50:56221 // It's not clear whether/when this can return NULL.
[email protected]3e44697f2009-05-22 14:37:39222 LOG(ERROR) << "Unable to create a gconf client";
223 loop_ = NULL;
224 return false;
[email protected]861c6c62009-04-20 16:50:56225 }
[email protected]3e44697f2009-05-22 14:37:39226 GError* error = NULL;
227 // We need to add the directories for which we'll be asking
228 // notifications, and we might as well ask to preload them.
229 gconf_client_add_dir(client_, "/system/proxy",
230 GCONF_CLIENT_PRELOAD_ONELEVEL, &error);
231 if (error == NULL) {
232 gconf_client_add_dir(client_, "/system/http_proxy",
233 GCONF_CLIENT_PRELOAD_ONELEVEL, &error);
234 }
235 if (error != NULL) {
236 LOG(ERROR) << "Error requesting gconf directory: " << error->message;
237 g_error_free(error);
[email protected]d7395e732009-08-28 23:13:43238 Shutdown();
[email protected]3e44697f2009-05-22 14:37:39239 return false;
240 }
241 return true;
242 }
243
[email protected]d7395e732009-08-28 23:13:43244 void Shutdown() {
[email protected]3e44697f2009-05-22 14:37:39245 if (client_) {
246 DCHECK(MessageLoop::current() == loop_);
247 // This also disables gconf notifications.
248 g_object_unref(client_);
249 client_ = NULL;
250 loop_ = NULL;
251 }
252 }
253
[email protected]d7395e732009-08-28 23:13:43254 bool SetupNotification(ProxyConfigServiceLinux::Delegate* delegate) {
[email protected]3e44697f2009-05-22 14:37:39255 DCHECK(client_);
256 DCHECK(MessageLoop::current() == loop_);
257 GError* error = NULL;
[email protected]d7395e732009-08-28 23:13:43258 notify_delegate_ = delegate;
[email protected]3e44697f2009-05-22 14:37:39259 gconf_client_notify_add(
260 client_, "/system/proxy",
[email protected]d7395e732009-08-28 23:13:43261 OnGConfChangeNotification, this,
[email protected]3e44697f2009-05-22 14:37:39262 NULL, &error);
263 if (error == NULL) {
264 gconf_client_notify_add(
265 client_, "/system/http_proxy",
[email protected]d7395e732009-08-28 23:13:43266 OnGConfChangeNotification, this,
[email protected]3e44697f2009-05-22 14:37:39267 NULL, &error);
268 }
269 if (error != NULL) {
270 LOG(ERROR) << "Error requesting gconf notifications: " << error->message;
271 g_error_free(error);
[email protected]d7395e732009-08-28 23:13:43272 Shutdown();
[email protected]3e44697f2009-05-22 14:37:39273 return false;
274 }
275 return true;
[email protected]861c6c62009-04-20 16:50:56276 }
277
[email protected]9a3d8d42009-09-03 17:01:46278 virtual MessageLoop* GetNotificationLoop() {
[email protected]d7395e732009-08-28 23:13:43279 return loop_;
280 }
281
282 virtual const char* GetDataSource() {
283 return "gconf";
284 }
285
[email protected]861c6c62009-04-20 16:50:56286 virtual bool GetString(const char* key, std::string* result) {
[email protected]3e44697f2009-05-22 14:37:39287 DCHECK(client_);
288 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56289 GError* error = NULL;
290 gchar* value = gconf_client_get_string(client_, key, &error);
291 if (HandleGError(error, key))
292 return false;
293 if (!value)
294 return false;
295 *result = value;
296 g_free(value);
297 return true;
298 }
299 virtual bool GetBoolean(const char* key, bool* result) {
[email protected]3e44697f2009-05-22 14:37:39300 DCHECK(client_);
301 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56302 GError* error = NULL;
303 // We want to distinguish unset values from values defaulting to
304 // false. For that we need to use the type-generic
305 // gconf_client_get() rather than gconf_client_get_bool().
306 GConfValue* gconf_value = gconf_client_get(client_, key, &error);
307 if (HandleGError(error, key))
308 return false;
309 if (!gconf_value) {
310 // Unset.
311 return false;
312 }
313 if (gconf_value->type != GCONF_VALUE_BOOL) {
314 gconf_value_free(gconf_value);
315 return false;
316 }
317 gboolean bool_value = gconf_value_get_bool(gconf_value);
318 *result = static_cast<bool>(bool_value);
319 gconf_value_free(gconf_value);
320 return true;
321 }
322 virtual bool GetInt(const char* key, int* result) {
[email protected]3e44697f2009-05-22 14:37:39323 DCHECK(client_);
324 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56325 GError* error = NULL;
326 int value = gconf_client_get_int(client_, key, &error);
327 if (HandleGError(error, key))
328 return false;
329 // We don't bother to distinguish an unset value because callers
330 // don't care. 0 is returned if unset.
331 *result = value;
332 return true;
333 }
334 virtual bool GetStringList(const char* key,
335 std::vector<std::string>* result) {
[email protected]3e44697f2009-05-22 14:37:39336 DCHECK(client_);
337 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56338 GError* error = NULL;
339 GSList* list = gconf_client_get_list(client_, key,
340 GCONF_VALUE_STRING, &error);
341 if (HandleGError(error, key))
342 return false;
343 if (!list) {
344 // unset
345 return false;
346 }
347 for (GSList *it = list; it; it = it->next) {
348 result->push_back(static_cast<char*>(it->data));
349 g_free(it->data);
350 }
351 g_slist_free(list);
352 return true;
353 }
354
[email protected]a48bf4a2010-06-14 18:24:53355 virtual bool BypassListIsReversed() {
356 // This is a KDE-specific setting.
357 return false;
358 }
359
[email protected]1a597192010-07-09 16:58:38360 virtual bool MatchHostsUsingSuffixMatching() {
361 return false;
362 }
363
[email protected]861c6c62009-04-20 16:50:56364 private:
365 // Logs and frees a glib error. Returns false if there was no error
366 // (error is NULL).
367 bool HandleGError(GError* error, const char* key) {
368 if (error != NULL) {
[email protected]3e44697f2009-05-22 14:37:39369 LOG(ERROR) << "Error getting gconf value for " << key
370 << ": " << error->message;
[email protected]861c6c62009-04-20 16:50:56371 g_error_free(error);
372 return true;
373 }
374 return false;
375 }
376
[email protected]d7395e732009-08-28 23:13:43377 // This is the callback from the debounce timer.
378 void OnDebouncedNotification() {
379 DCHECK(MessageLoop::current() == loop_);
380 DCHECK(notify_delegate_);
381 // Forward to a method on the proxy config service delegate object.
382 notify_delegate_->OnCheckProxyConfigSettings();
383 }
384
385 void OnChangeNotification() {
386 // We don't use Reset() because the timer may not yet be running.
387 // (In that case Stop() is a no-op.)
388 debounce_timer_.Stop();
389 debounce_timer_.Start(base::TimeDelta::FromMilliseconds(
390 kDebounceTimeoutMilliseconds), this,
391 &GConfSettingGetterImplGConf::OnDebouncedNotification);
392 }
393
394 // gconf notification callback, dispatched from the default glib main loop.
395 static void OnGConfChangeNotification(
396 GConfClient* client, guint cnxn_id,
397 GConfEntry* entry, gpointer user_data) {
398 LOG(INFO) << "gconf change notification for key "
399 << gconf_entry_get_key(entry);
400 // We don't track which key has changed, just that something did change.
401 GConfSettingGetterImplGConf* setting_getter =
402 reinterpret_cast<GConfSettingGetterImplGConf*>(user_data);
403 setting_getter->OnChangeNotification();
404 }
405
[email protected]861c6c62009-04-20 16:50:56406 GConfClient* client_;
[email protected]d7395e732009-08-28 23:13:43407 ProxyConfigServiceLinux::Delegate* notify_delegate_;
408 base::OneShotTimer<GConfSettingGetterImplGConf> debounce_timer_;
[email protected]861c6c62009-04-20 16:50:56409
[email protected]3e44697f2009-05-22 14:37:39410 // Message loop of the thread that we make gconf calls on. It should
411 // be the UI thread and all our methods should be called on this
412 // thread. Only for assertions.
413 MessageLoop* loop_;
414
[email protected]d7395e732009-08-28 23:13:43415 DISALLOW_COPY_AND_ASSIGN(GConfSettingGetterImplGConf);
416};
417
418// This is the KDE version that reads kioslaverc and simulates gconf.
419// Doing this allows the main Delegate code, as well as the unit tests
420// for it, to stay the same - and the settings map fairly well besides.
421class GConfSettingGetterImplKDE
422 : public ProxyConfigServiceLinux::GConfSettingGetter,
423 public base::MessagePumpLibevent::Watcher {
424 public:
[email protected]76b90d312010-08-03 03:00:50425 explicit GConfSettingGetterImplKDE(base::Environment* env_var_getter)
[email protected]d7395e732009-08-28 23:13:43426 : inotify_fd_(-1), notify_delegate_(NULL), indirect_manual_(false),
[email protected]a48bf4a2010-06-14 18:24:53427 auto_no_pac_(false), reversed_bypass_list_(false),
[email protected]f18fde22010-05-18 23:49:54428 env_var_getter_(env_var_getter), file_loop_(NULL) {
429 // Derive the location of the kde config dir from the environment.
[email protected]92d2dc82010-04-08 17:49:59430 std::string home;
[email protected]2e8cfe22010-06-12 00:26:24431 if (env_var_getter->GetEnv("KDEHOME", &home) && !home.empty()) {
432 // $KDEHOME is set. Use it unconditionally.
[email protected]92d2dc82010-04-08 17:49:59433 kde_config_dir_ = KDEHomeToConfigPath(FilePath(home));
434 } else {
[email protected]2e8cfe22010-06-12 00:26:24435 // $KDEHOME is unset. Try to figure out what to use. This seems to be
[email protected]92d2dc82010-04-08 17:49:59436 // the common case on most distributions.
[email protected]574f6f0c2010-07-21 02:59:28437 if (!env_var_getter->GetEnv(base::env_vars::kHome, &home))
[email protected]d7395e732009-08-28 23:13:43438 // User has no $HOME? Give up. Later we'll report the failure.
439 return;
[email protected]92d2dc82010-04-08 17:49:59440 if (base::GetDesktopEnvironment(env_var_getter) ==
441 base::DESKTOP_ENVIRONMENT_KDE3) {
442 // KDE3 always uses .kde for its configuration.
443 FilePath kde_path = FilePath(home).Append(".kde");
444 kde_config_dir_ = KDEHomeToConfigPath(kde_path);
445 } else {
446 // Some distributions patch KDE4 to use .kde4 instead of .kde, so that
[email protected]fad9c8a52010-06-10 22:30:53447 // both can be installed side-by-side. Sadly they don't all do this, and
448 // they don't always do this: some distributions have started switching
449 // back as well. So if there is a .kde4 directory, check the timestamps
450 // of the config directories within and use the newest one.
[email protected]92d2dc82010-04-08 17:49:59451 // Note that we should currently be running in the UI thread, because in
452 // the gconf version, that is the only thread that can access the proxy
453 // settings (a gconf restriction). As noted below, the initial read of
454 // the proxy settings will be done in this thread anyway, so we check
455 // for .kde4 here in this thread as well.
[email protected]fad9c8a52010-06-10 22:30:53456 FilePath kde3_path = FilePath(home).Append(".kde");
457 FilePath kde3_config = KDEHomeToConfigPath(kde3_path);
[email protected]92d2dc82010-04-08 17:49:59458 FilePath kde4_path = FilePath(home).Append(".kde4");
[email protected]fad9c8a52010-06-10 22:30:53459 FilePath kde4_config = KDEHomeToConfigPath(kde4_path);
460 bool use_kde4 = false;
[email protected]92d2dc82010-04-08 17:49:59461 if (file_util::DirectoryExists(kde4_path)) {
[email protected]fad9c8a52010-06-10 22:30:53462 file_util::FileInfo kde3_info;
463 file_util::FileInfo kde4_info;
464 if (file_util::GetFileInfo(kde4_config, &kde4_info)) {
465 if (file_util::GetFileInfo(kde3_config, &kde3_info)) {
466 use_kde4 = kde4_info.last_modified >= kde3_info.last_modified;
467 } else {
468 use_kde4 = true;
469 }
470 }
471 }
472 if (use_kde4) {
[email protected]92d2dc82010-04-08 17:49:59473 kde_config_dir_ = KDEHomeToConfigPath(kde4_path);
474 } else {
[email protected]fad9c8a52010-06-10 22:30:53475 kde_config_dir_ = KDEHomeToConfigPath(kde3_path);
[email protected]92d2dc82010-04-08 17:49:59476 }
477 }
[email protected]d7395e732009-08-28 23:13:43478 }
[email protected]d7395e732009-08-28 23:13:43479 }
480
481 virtual ~GConfSettingGetterImplKDE() {
482 // inotify_fd_ should have been closed before now, from
483 // Delegate::OnDestroy(), while running on the file thread. However
484 // on exiting the process, it may happen that Delegate::OnDestroy()
485 // task is left pending on the file loop after the loop was quit,
486 // and pending tasks may then be deleted without being run.
487 // Here in the KDE version, we can safely close the file descriptor
488 // anyway. (Not that it really matters; the process is exiting.)
489 if (inotify_fd_ >= 0)
490 Shutdown();
491 DCHECK(inotify_fd_ < 0);
492 }
493
494 virtual bool Init(MessageLoop* glib_default_loop,
495 MessageLoopForIO* file_loop) {
496 DCHECK(inotify_fd_ < 0);
497 inotify_fd_ = inotify_init();
498 if (inotify_fd_ < 0) {
[email protected]57b765672009-10-13 18:27:40499 PLOG(ERROR) << "inotify_init failed";
[email protected]d7395e732009-08-28 23:13:43500 return false;
501 }
502 int flags = fcntl(inotify_fd_, F_GETFL);
503 if (fcntl(inotify_fd_, F_SETFL, flags | O_NONBLOCK) < 0) {
[email protected]57b765672009-10-13 18:27:40504 PLOG(ERROR) << "fcntl failed";
[email protected]d7395e732009-08-28 23:13:43505 close(inotify_fd_);
506 inotify_fd_ = -1;
507 return false;
508 }
509 file_loop_ = file_loop;
510 // The initial read is done on the current thread, not |file_loop_|,
511 // since we will need to have it for SetupAndFetchInitialConfig().
512 UpdateCachedSettings();
513 return true;
514 }
515
516 void Shutdown() {
517 if (inotify_fd_ >= 0) {
518 ResetCachedSettings();
519 inotify_watcher_.StopWatchingFileDescriptor();
520 close(inotify_fd_);
521 inotify_fd_ = -1;
522 }
523 }
524
525 bool SetupNotification(ProxyConfigServiceLinux::Delegate* delegate) {
526 DCHECK(inotify_fd_ >= 0);
527 DCHECK(file_loop_);
528 // We can't just watch the kioslaverc file directly, since KDE will write
529 // a new copy of it and then rename it whenever settings are changed and
530 // inotify watches inodes (so we'll be watching the old deleted file after
531 // the first change, and it will never change again). So, we watch the
532 // directory instead. We then act only on changes to the kioslaverc entry.
533 if (inotify_add_watch(inotify_fd_, kde_config_dir_.value().c_str(),
534 IN_MODIFY | IN_MOVED_TO) < 0)
535 return false;
536 notify_delegate_ = delegate;
537 return file_loop_->WatchFileDescriptor(inotify_fd_, true,
538 MessageLoopForIO::WATCH_READ, &inotify_watcher_, this);
539 }
540
[email protected]9a3d8d42009-09-03 17:01:46541 virtual MessageLoop* GetNotificationLoop() {
[email protected]d7395e732009-08-28 23:13:43542 return file_loop_;
543 }
544
545 // Implement base::MessagePumpLibevent::Delegate.
546 void OnFileCanReadWithoutBlocking(int fd) {
547 DCHECK(fd == inotify_fd_);
548 DCHECK(MessageLoop::current() == file_loop_);
549 OnChangeNotification();
550 }
551 void OnFileCanWriteWithoutBlocking(int fd) {
552 NOTREACHED();
553 }
554
555 virtual const char* GetDataSource() {
556 return "KDE";
557 }
558
559 virtual bool GetString(const char* key, std::string* result) {
560 string_map_type::iterator it = string_table_.find(key);
561 if (it == string_table_.end())
562 return false;
563 *result = it->second;
564 return true;
565 }
566 virtual bool GetBoolean(const char* key, bool* result) {
567 // We don't ever have any booleans.
568 return false;
569 }
570 virtual bool GetInt(const char* key, int* result) {
571 // We don't ever have any integers. (See AddProxy() below about ports.)
572 return false;
573 }
574 virtual bool GetStringList(const char* key,
575 std::vector<std::string>* result) {
576 strings_map_type::iterator it = strings_table_.find(key);
577 if (it == strings_table_.end())
578 return false;
579 *result = it->second;
580 return true;
581 }
582
[email protected]a48bf4a2010-06-14 18:24:53583 virtual bool BypassListIsReversed() {
584 return reversed_bypass_list_;
585 }
586
[email protected]1a597192010-07-09 16:58:38587 virtual bool MatchHostsUsingSuffixMatching() {
588 return true;
589 }
590
[email protected]d7395e732009-08-28 23:13:43591 private:
592 void ResetCachedSettings() {
593 string_table_.clear();
594 strings_table_.clear();
595 indirect_manual_ = false;
596 auto_no_pac_ = false;
[email protected]a48bf4a2010-06-14 18:24:53597 reversed_bypass_list_ = false;
[email protected]d7395e732009-08-28 23:13:43598 }
599
[email protected]92d2dc82010-04-08 17:49:59600 FilePath KDEHomeToConfigPath(const FilePath& kde_home) {
601 return kde_home.Append("share").Append("config");
602 }
603
[email protected]a8185d02010-06-11 00:19:50604 void AddProxy(const std::string& prefix, const std::string& value) {
[email protected]d7395e732009-08-28 23:13:43605 if (value.empty() || value.substr(0, 3) == "//:")
606 // No proxy.
607 return;
608 // We don't need to parse the port number out; GetProxyFromGConf()
609 // would only append it right back again. So we just leave the port
610 // number right in the host string.
611 string_table_[prefix + "host"] = value;
612 }
613
[email protected]a8185d02010-06-11 00:19:50614 void AddHostList(const std::string& key, const std::string& value) {
[email protected]f18fde22010-05-18 23:49:54615 std::vector<std::string> tokens;
[email protected]1a597192010-07-09 16:58:38616 StringTokenizer tk(value, ", ");
[email protected]f18fde22010-05-18 23:49:54617 while (tk.GetNext()) {
618 std::string token = tk.token();
619 if (!token.empty())
620 tokens.push_back(token);
621 }
622 strings_table_[key] = tokens;
623 }
624
[email protected]9a3d8d42009-09-03 17:01:46625 void AddKDESetting(const std::string& key, const std::string& value) {
[email protected]d7395e732009-08-28 23:13:43626 // The astute reader may notice that there is no mention of SOCKS
627 // here. That's because KDE handles socks is a strange way, and we
628 // don't support it. Rather than just a setting for the SOCKS server,
629 // it has a setting for a library to LD_PRELOAD in all your programs
630 // that will transparently SOCKSify them. Such libraries each have
631 // their own configuration, and thus, we can't get it from KDE.
632 if (key == "ProxyType") {
633 const char* mode = "none";
634 indirect_manual_ = false;
635 auto_no_pac_ = false;
[email protected]e83326f2010-07-31 17:29:25636 int int_value;
637 base::StringToInt(value, &int_value);
638 switch (int_value) {
[email protected]d7395e732009-08-28 23:13:43639 case 0: // No proxy, or maybe kioslaverc syntax error.
640 break;
641 case 1: // Manual configuration.
642 mode = "manual";
643 break;
644 case 2: // PAC URL.
645 mode = "auto";
646 break;
647 case 3: // WPAD.
648 mode = "auto";
649 auto_no_pac_ = true;
650 break;
651 case 4: // Indirect manual via environment variables.
652 mode = "manual";
653 indirect_manual_ = true;
654 break;
655 }
656 string_table_["/system/proxy/mode"] = mode;
657 } else if (key == "Proxy Config Script") {
658 string_table_["/system/proxy/autoconfig_url"] = value;
659 } else if (key == "httpProxy") {
660 AddProxy("/system/http_proxy/", value);
661 } else if (key == "httpsProxy") {
662 AddProxy("/system/proxy/secure_", value);
663 } else if (key == "ftpProxy") {
664 AddProxy("/system/proxy/ftp_", value);
665 } else if (key == "ReversedException") {
666 // We count "true" or any nonzero number as true, otherwise false.
667 // Note that if the value is not actually numeric StringToInt()
668 // will return 0, which we count as false.
[email protected]e83326f2010-07-31 17:29:25669 int int_value;
670 base::StringToInt(value, &int_value);
671 reversed_bypass_list_ = (value == "true" || int_value);
[email protected]d7395e732009-08-28 23:13:43672 } else if (key == "NoProxyFor") {
[email protected]f18fde22010-05-18 23:49:54673 AddHostList("/system/http_proxy/ignore_hosts", value);
[email protected]d7395e732009-08-28 23:13:43674 } else if (key == "AuthMode") {
675 // Check for authentication, just so we can warn.
[email protected]e83326f2010-07-31 17:29:25676 int mode;
677 base::StringToInt(value, &mode);
[email protected]d7395e732009-08-28 23:13:43678 if (mode) {
679 // ProxyConfig does not support authentication parameters, but
680 // Chrome will prompt for the password later. So we ignore this.
681 LOG(WARNING) <<
682 "Proxy authentication parameters ignored, see bug 16709";
683 }
684 }
685 }
686
[email protected]a8185d02010-06-11 00:19:50687 void ResolveIndirect(const std::string& key) {
[email protected]d7395e732009-08-28 23:13:43688 string_map_type::iterator it = string_table_.find(key);
689 if (it != string_table_.end()) {
[email protected]f18fde22010-05-18 23:49:54690 std::string value;
691 if (env_var_getter_->GetEnv(it->second.c_str(), &value))
[email protected]d7395e732009-08-28 23:13:43692 it->second = value;
[email protected]8425adc02010-04-18 17:45:31693 else
694 string_table_.erase(it);
[email protected]d7395e732009-08-28 23:13:43695 }
696 }
697
[email protected]a8185d02010-06-11 00:19:50698 void ResolveIndirectList(const std::string& key) {
[email protected]f18fde22010-05-18 23:49:54699 strings_map_type::iterator it = strings_table_.find(key);
700 if (it != strings_table_.end()) {
701 std::string value;
702 if (!it->second.empty() &&
703 env_var_getter_->GetEnv(it->second[0].c_str(), &value))
704 AddHostList(key, value);
705 else
706 strings_table_.erase(it);
707 }
708 }
709
[email protected]d7395e732009-08-28 23:13:43710 // The settings in kioslaverc could occur in any order, but some affect
711 // others. Rather than read the whole file in and then query them in an
712 // order that allows us to handle that, we read the settings in whatever
713 // order they occur and do any necessary tweaking after we finish.
714 void ResolveModeEffects() {
715 if (indirect_manual_) {
716 ResolveIndirect("/system/http_proxy/host");
717 ResolveIndirect("/system/proxy/secure_host");
718 ResolveIndirect("/system/proxy/ftp_host");
[email protected]f18fde22010-05-18 23:49:54719 ResolveIndirectList("/system/http_proxy/ignore_hosts");
[email protected]d7395e732009-08-28 23:13:43720 }
721 if (auto_no_pac_) {
722 // Remove the PAC URL; we're not supposed to use it.
723 string_table_.erase("/system/proxy/autoconfig_url");
724 }
[email protected]d7395e732009-08-28 23:13:43725 }
726
727 // Reads kioslaverc one line at a time and calls AddKDESetting() to add
728 // each relevant name-value pair to the appropriate value table.
729 void UpdateCachedSettings() {
[email protected]92d2dc82010-04-08 17:49:59730 FilePath kioslaverc = kde_config_dir_.Append("kioslaverc");
[email protected]d7395e732009-08-28 23:13:43731 file_util::ScopedFILE input(file_util::OpenFile(kioslaverc, "r"));
732 if (!input.get())
733 return;
734 ResetCachedSettings();
735 bool in_proxy_settings = false;
736 bool line_too_long = false;
[email protected]9a3d8d42009-09-03 17:01:46737 char line[BUFFER_SIZE];
738 // fgets() will return NULL on EOF or error.
[email protected]d7395e732009-08-28 23:13:43739 while (fgets(line, sizeof(line), input.get())) {
740 // fgets() guarantees the line will be properly terminated.
741 size_t length = strlen(line);
742 if (!length)
743 continue;
744 // This should be true even with CRLF endings.
745 if (line[length - 1] != '\n') {
746 line_too_long = true;
747 continue;
748 }
749 if (line_too_long) {
750 // The previous line had no line ending, but this done does. This is
751 // the end of the line that was too long, so warn here and skip it.
752 LOG(WARNING) << "skipped very long line in " << kioslaverc.value();
753 line_too_long = false;
754 continue;
755 }
756 // Remove the LF at the end, and the CR if there is one.
757 line[--length] = '\0';
758 if (length && line[length - 1] == '\r')
759 line[--length] = '\0';
760 // Now parse the line.
761 if (line[0] == '[') {
762 // Switching sections. All we care about is whether this is
763 // the (a?) proxy settings section, for both KDE3 and KDE4.
764 in_proxy_settings = !strncmp(line, "[Proxy Settings]", 16);
765 } else if (in_proxy_settings) {
766 // A regular line, in the (a?) proxy settings section.
[email protected]9a3d8d42009-09-03 17:01:46767 char* split = strchr(line, '=');
768 // Skip this line if it does not contain an = sign.
769 if (!split)
[email protected]d7395e732009-08-28 23:13:43770 continue;
[email protected]9a3d8d42009-09-03 17:01:46771 // Split the line on the = and advance |split|.
772 *(split++) = 0;
773 std::string key = line;
774 std::string value = split;
775 TrimWhitespaceASCII(key, TRIM_ALL, &key);
776 TrimWhitespaceASCII(value, TRIM_ALL, &value);
777 // Skip this line if the key name is empty.
778 if (key.empty())
[email protected]d7395e732009-08-28 23:13:43779 continue;
780 // Is the value name localized?
[email protected]9a3d8d42009-09-03 17:01:46781 if (key[key.length() - 1] == ']') {
782 // Find the matching bracket.
783 length = key.rfind('[');
784 // Skip this line if the localization indicator is malformed.
785 if (length == std::string::npos)
[email protected]d7395e732009-08-28 23:13:43786 continue;
787 // Trim the localization indicator off.
[email protected]9a3d8d42009-09-03 17:01:46788 key.resize(length);
789 // Remove any resulting trailing whitespace.
790 TrimWhitespaceASCII(key, TRIM_TRAILING, &key);
791 // Skip this line if the key name is now empty.
792 if (key.empty())
793 continue;
[email protected]d7395e732009-08-28 23:13:43794 }
[email protected]d7395e732009-08-28 23:13:43795 // Now fill in the tables.
[email protected]9a3d8d42009-09-03 17:01:46796 AddKDESetting(key, value);
[email protected]d7395e732009-08-28 23:13:43797 }
798 }
799 if (ferror(input.get()))
800 LOG(ERROR) << "error reading " << kioslaverc.value();
801 ResolveModeEffects();
802 }
803
804 // This is the callback from the debounce timer.
805 void OnDebouncedNotification() {
806 DCHECK(MessageLoop::current() == file_loop_);
807 LOG(INFO) << "inotify change notification for kioslaverc";
808 UpdateCachedSettings();
809 DCHECK(notify_delegate_);
810 // Forward to a method on the proxy config service delegate object.
811 notify_delegate_->OnCheckProxyConfigSettings();
812 }
813
814 // Called by OnFileCanReadWithoutBlocking() on the file thread. Reads
815 // from the inotify file descriptor and starts up a debounce timer if
816 // an event for kioslaverc is seen.
817 void OnChangeNotification() {
818 DCHECK(inotify_fd_ >= 0);
819 DCHECK(MessageLoop::current() == file_loop_);
820 char event_buf[(sizeof(inotify_event) + NAME_MAX + 1) * 4];
821 bool kioslaverc_touched = false;
822 ssize_t r;
823 while ((r = read(inotify_fd_, event_buf, sizeof(event_buf))) > 0) {
824 // inotify returns variable-length structures, which is why we have
825 // this strange-looking loop instead of iterating through an array.
826 char* event_ptr = event_buf;
827 while (event_ptr < event_buf + r) {
828 inotify_event* event = reinterpret_cast<inotify_event*>(event_ptr);
829 // The kernel always feeds us whole events.
[email protected]b1f031dd2010-03-02 23:19:33830 CHECK_LE(event_ptr + sizeof(inotify_event), event_buf + r);
831 CHECK_LE(event->name + event->len, event_buf + r);
[email protected]d7395e732009-08-28 23:13:43832 if (!strcmp(event->name, "kioslaverc"))
833 kioslaverc_touched = true;
834 // Advance the pointer just past the end of the filename.
835 event_ptr = event->name + event->len;
836 }
837 // We keep reading even if |kioslaverc_touched| is true to drain the
838 // inotify event queue.
839 }
840 if (!r)
841 // Instead of returning -1 and setting errno to EINVAL if there is not
842 // enough buffer space, older kernels (< 2.6.21) return 0. Simulate the
843 // new behavior (EINVAL) so we can reuse the code below.
844 errno = EINVAL;
845 if (errno != EAGAIN) {
[email protected]57b765672009-10-13 18:27:40846 PLOG(WARNING) << "error reading inotify file descriptor";
[email protected]d7395e732009-08-28 23:13:43847 if (errno == EINVAL) {
848 // Our buffer is not large enough to read the next event. This should
849 // not happen (because its size is calculated to always be sufficiently
850 // large), but if it does we'd warn continuously since |inotify_fd_|
851 // would be forever ready to read. Close it and stop watching instead.
852 LOG(ERROR) << "inotify failure; no longer watching kioslaverc!";
853 inotify_watcher_.StopWatchingFileDescriptor();
854 close(inotify_fd_);
855 inotify_fd_ = -1;
856 }
857 }
858 if (kioslaverc_touched) {
859 // We don't use Reset() because the timer may not yet be running.
860 // (In that case Stop() is a no-op.)
861 debounce_timer_.Stop();
862 debounce_timer_.Start(base::TimeDelta::FromMilliseconds(
863 kDebounceTimeoutMilliseconds), this,
864 &GConfSettingGetterImplKDE::OnDebouncedNotification);
865 }
866 }
867
868 typedef std::map<std::string, std::string> string_map_type;
869 typedef std::map<std::string, std::vector<std::string> > strings_map_type;
870
871 int inotify_fd_;
872 base::MessagePumpLibevent::FileDescriptorWatcher inotify_watcher_;
873 ProxyConfigServiceLinux::Delegate* notify_delegate_;
874 base::OneShotTimer<GConfSettingGetterImplKDE> debounce_timer_;
875 FilePath kde_config_dir_;
876 bool indirect_manual_;
877 bool auto_no_pac_;
[email protected]a48bf4a2010-06-14 18:24:53878 bool reversed_bypass_list_;
[email protected]f18fde22010-05-18 23:49:54879 // We don't own |env_var_getter_|. It's safe to hold a pointer to it, since
880 // both it and us are owned by ProxyConfigServiceLinux::Delegate, and have the
881 // same lifetime.
[email protected]76b90d312010-08-03 03:00:50882 base::Environment* env_var_getter_;
[email protected]d7395e732009-08-28 23:13:43883
884 // We cache these settings whenever we re-read the kioslaverc file.
885 string_map_type string_table_;
886 strings_map_type strings_table_;
887
888 // Message loop of the file thread, for reading kioslaverc. If NULL,
889 // just read it directly (for testing). We also handle inotify events
890 // on this thread.
891 MessageLoopForIO* file_loop_;
892
893 DISALLOW_COPY_AND_ASSIGN(GConfSettingGetterImplKDE);
[email protected]861c6c62009-04-20 16:50:56894};
895
896} // namespace
897
[email protected]3e44697f2009-05-22 14:37:39898bool ProxyConfigServiceLinux::Delegate::GetProxyFromGConf(
[email protected]861c6c62009-04-20 16:50:56899 const char* key_prefix, bool is_socks, ProxyServer* result_server) {
900 std::string key(key_prefix);
901 std::string host;
902 if (!gconf_getter_->GetString((key + "host").c_str(), &host)
903 || host.empty()) {
904 // Unset or empty.
905 return false;
906 }
907 // Check for an optional port.
[email protected]d7395e732009-08-28 23:13:43908 int port = 0;
[email protected]861c6c62009-04-20 16:50:56909 gconf_getter_->GetInt((key + "port").c_str(), &port);
910 if (port != 0) {
911 // If a port is set and non-zero:
[email protected]528c56d2010-07-30 19:28:44912 host += ":" + base::IntToString(port);
[email protected]861c6c62009-04-20 16:50:56913 }
914 host = FixupProxyHostScheme(
915 is_socks ? ProxyServer::SCHEME_SOCKS4 : ProxyServer::SCHEME_HTTP,
916 host);
[email protected]87a102b2009-07-14 05:23:30917 ProxyServer proxy_server = ProxyServer::FromURI(host,
918 ProxyServer::SCHEME_HTTP);
[email protected]861c6c62009-04-20 16:50:56919 if (proxy_server.is_valid()) {
920 *result_server = proxy_server;
921 return true;
922 }
923 return false;
924}
925
[email protected]3e44697f2009-05-22 14:37:39926bool ProxyConfigServiceLinux::Delegate::GetConfigFromGConf(
927 ProxyConfig* config) {
[email protected]861c6c62009-04-20 16:50:56928 std::string mode;
929 if (!gconf_getter_->GetString("/system/proxy/mode", &mode)) {
930 // We expect this to always be set, so if we don't see it then we
931 // probably have a gconf problem, and so we don't have a valid
932 // proxy config.
933 return false;
934 }
[email protected]3e44697f2009-05-22 14:37:39935 if (mode == "none") {
[email protected]861c6c62009-04-20 16:50:56936 // Specifically specifies no proxy.
937 return true;
[email protected]3e44697f2009-05-22 14:37:39938 }
[email protected]861c6c62009-04-20 16:50:56939
[email protected]3e44697f2009-05-22 14:37:39940 if (mode == "auto") {
[email protected]861c6c62009-04-20 16:50:56941 // automatic proxy config
942 std::string pac_url_str;
943 if (gconf_getter_->GetString("/system/proxy/autoconfig_url",
944 &pac_url_str)) {
945 if (!pac_url_str.empty()) {
946 GURL pac_url(pac_url_str);
947 if (!pac_url.is_valid())
948 return false;
[email protected]ed4ed0f2010-02-24 00:20:48949 config->set_pac_url(pac_url);
[email protected]861c6c62009-04-20 16:50:56950 return true;
951 }
952 }
[email protected]ed4ed0f2010-02-24 00:20:48953 config->set_auto_detect(true);
[email protected]861c6c62009-04-20 16:50:56954 return true;
955 }
956
[email protected]3e44697f2009-05-22 14:37:39957 if (mode != "manual") {
[email protected]861c6c62009-04-20 16:50:56958 // Mode is unrecognized.
959 return false;
960 }
961 bool use_http_proxy;
962 if (gconf_getter_->GetBoolean("/system/http_proxy/use_http_proxy",
963 &use_http_proxy)
964 && !use_http_proxy) {
965 // Another master switch for some reason. If set to false, then no
966 // proxy. But we don't panic if the key doesn't exist.
967 return true;
968 }
969
970 bool same_proxy = false;
971 // Indicates to use the http proxy for all protocols. This one may
972 // not exist (presumably on older versions), assume false in that
973 // case.
974 gconf_getter_->GetBoolean("/system/http_proxy/use_same_proxy",
975 &same_proxy);
976
977 ProxyServer proxy_server;
978 if (!same_proxy) {
979 // Try socks.
980 if (GetProxyFromGConf("/system/proxy/socks_", true, &proxy_server)) {
981 // gconf settings do not appear to distinguish between socks
982 // version. We default to version 4.
[email protected]ed4ed0f2010-02-24 00:20:48983 config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
984 config->proxy_rules().single_proxy = proxy_server;
[email protected]861c6c62009-04-20 16:50:56985 }
986 }
[email protected]ed4ed0f2010-02-24 00:20:48987 if (config->proxy_rules().empty()) {
[email protected]861c6c62009-04-20 16:50:56988 bool have_http = GetProxyFromGConf("/system/http_proxy/", false,
989 &proxy_server);
990 if (same_proxy) {
991 if (have_http) {
[email protected]ed4ed0f2010-02-24 00:20:48992 config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
993 config->proxy_rules().single_proxy = proxy_server;
[email protected]861c6c62009-04-20 16:50:56994 }
995 } else {
996 // Protocol specific settings.
997 if (have_http)
[email protected]ed4ed0f2010-02-24 00:20:48998 config->proxy_rules().proxy_for_http = proxy_server;
[email protected]861c6c62009-04-20 16:50:56999 bool have_secure = GetProxyFromGConf("/system/proxy/secure_", false,
1000 &proxy_server);
1001 if (have_secure)
[email protected]ed4ed0f2010-02-24 00:20:481002 config->proxy_rules().proxy_for_https = proxy_server;
[email protected]861c6c62009-04-20 16:50:561003 bool have_ftp = GetProxyFromGConf("/system/proxy/ftp_", false,
1004 &proxy_server);
1005 if (have_ftp)
[email protected]ed4ed0f2010-02-24 00:20:481006 config->proxy_rules().proxy_for_ftp = proxy_server;
[email protected]861c6c62009-04-20 16:50:561007 if (have_http || have_secure || have_ftp)
[email protected]ed4ed0f2010-02-24 00:20:481008 config->proxy_rules().type =
[email protected]861c6c62009-04-20 16:50:561009 ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
1010 }
1011 }
1012
[email protected]ed4ed0f2010-02-24 00:20:481013 if (config->proxy_rules().empty()) {
[email protected]861c6c62009-04-20 16:50:561014 // Manual mode but we couldn't parse any rules.
1015 return false;
1016 }
1017
1018 // Check for authentication, just so we can warn.
[email protected]d7395e732009-08-28 23:13:431019 bool use_auth = false;
[email protected]861c6c62009-04-20 16:50:561020 gconf_getter_->GetBoolean("/system/http_proxy/use_authentication",
1021 &use_auth);
[email protected]62749f182009-07-15 13:16:541022 if (use_auth) {
1023 // ProxyConfig does not support authentication parameters, but
1024 // Chrome will prompt for the password later. So we ignore
1025 // /system/http_proxy/*auth* settings.
1026 LOG(WARNING) << "Proxy authentication parameters ignored, see bug 16709";
1027 }
[email protected]861c6c62009-04-20 16:50:561028
1029 // Now the bypass list.
[email protected]7541206c2010-02-19 20:24:061030 std::vector<std::string> ignore_hosts_list;
[email protected]ed4ed0f2010-02-24 00:20:481031 config->proxy_rules().bypass_rules.Clear();
[email protected]a8185d02010-06-11 00:19:501032 if (gconf_getter_->GetStringList("/system/http_proxy/ignore_hosts",
1033 &ignore_hosts_list)) {
1034 std::vector<std::string>::const_iterator it(ignore_hosts_list.begin());
[email protected]1a597192010-07-09 16:58:381035 for (; it != ignore_hosts_list.end(); ++it) {
1036 if (gconf_getter_->MatchHostsUsingSuffixMatching()) {
1037 config->proxy_rules().bypass_rules.
1038 AddRuleFromStringUsingSuffixMatching(*it);
1039 } else {
1040 config->proxy_rules().bypass_rules.AddRuleFromString(*it);
1041 }
1042 }
[email protected]a8185d02010-06-11 00:19:501043 }
[email protected]861c6c62009-04-20 16:50:561044 // Note that there are no settings with semantics corresponding to
[email protected]1a597192010-07-09 16:58:381045 // bypass of local names in GNOME. In KDE, "<local>" is supported
1046 // as a hostname rule.
[email protected]861c6c62009-04-20 16:50:561047
[email protected]a48bf4a2010-06-14 18:24:531048 // KDE allows one to reverse the bypass rules.
1049 config->proxy_rules().reverse_bypass = gconf_getter_->BypassListIsReversed();
1050
[email protected]861c6c62009-04-20 16:50:561051 return true;
1052}
1053
[email protected]76b90d312010-08-03 03:00:501054ProxyConfigServiceLinux::Delegate::Delegate(base::Environment* env_var_getter)
[email protected]d7395e732009-08-28 23:13:431055 : env_var_getter_(env_var_getter),
1056 glib_default_loop_(NULL), io_loop_(NULL) {
1057 // Figure out which GConfSettingGetterImpl to use, if any.
1058 switch (base::GetDesktopEnvironment(env_var_getter)) {
1059 case base::DESKTOP_ENVIRONMENT_GNOME:
1060 gconf_getter_.reset(new GConfSettingGetterImplGConf());
1061 break;
1062 case base::DESKTOP_ENVIRONMENT_KDE3:
1063 case base::DESKTOP_ENVIRONMENT_KDE4:
1064 gconf_getter_.reset(new GConfSettingGetterImplKDE(env_var_getter));
1065 break;
[email protected]88c50ab2010-03-26 20:04:061066 case base::DESKTOP_ENVIRONMENT_XFCE:
[email protected]d7395e732009-08-28 23:13:431067 case base::DESKTOP_ENVIRONMENT_OTHER:
1068 break;
1069 }
1070}
1071
[email protected]76b90d312010-08-03 03:00:501072ProxyConfigServiceLinux::Delegate::Delegate(base::Environment* env_var_getter,
[email protected]861c6c62009-04-20 16:50:561073 GConfSettingGetter* gconf_getter)
[email protected]3e44697f2009-05-22 14:37:391074 : env_var_getter_(env_var_getter), gconf_getter_(gconf_getter),
1075 glib_default_loop_(NULL), io_loop_(NULL) {
[email protected]861c6c62009-04-20 16:50:561076}
1077
[email protected]3e44697f2009-05-22 14:37:391078void ProxyConfigServiceLinux::Delegate::SetupAndFetchInitialConfig(
[email protected]d7395e732009-08-28 23:13:431079 MessageLoop* glib_default_loop, MessageLoop* io_loop,
1080 MessageLoopForIO* file_loop) {
[email protected]3e44697f2009-05-22 14:37:391081 // We should be running on the default glib main loop thread right
1082 // now. gconf can only be accessed from this thread.
1083 DCHECK(MessageLoop::current() == glib_default_loop);
1084 glib_default_loop_ = glib_default_loop;
1085 io_loop_ = io_loop;
1086
[email protected]d7395e732009-08-28 23:13:431087 // If we are passed a NULL io_loop or file_loop, then we don't set up
1088 // proxy setting change notifications. This should not be the usual
1089 // case but is intended to simplify test setups.
1090 if (!io_loop_ || !file_loop)
1091 LOG(INFO) << "Monitoring of proxy setting changes is disabled";
[email protected]3e44697f2009-05-22 14:37:391092
1093 // Fetch and cache the current proxy config. The config is left in
[email protected]119655002010-07-23 06:02:401094 // cached_config_, where GetLatestProxyConfig() running on the IO thread
[email protected]3e44697f2009-05-22 14:37:391095 // will expect to find it. This is safe to do because we return
1096 // before this ProxyConfigServiceLinux is passed on to
1097 // the ProxyService.
[email protected]d6cb85b2009-07-23 22:10:531098
1099 // Note: It would be nice to prioritize environment variables
[email protected]92d2dc82010-04-08 17:49:591100 // and only fall back to gconf if env vars were unset. But
[email protected]d6cb85b2009-07-23 22:10:531101 // gnome-terminal "helpfully" sets http_proxy and no_proxy, and it
1102 // does so even if the proxy mode is set to auto, which would
1103 // mislead us.
1104
[email protected]3e44697f2009-05-22 14:37:391105 bool got_config = false;
[email protected]d7395e732009-08-28 23:13:431106 if (gconf_getter_.get()) {
1107 if (gconf_getter_->Init(glib_default_loop, file_loop) &&
1108 (!io_loop || !file_loop || gconf_getter_->SetupNotification(this))) {
1109 if (GetConfigFromGConf(&cached_config_)) {
1110 cached_config_.set_id(1); // mark it as valid
1111 got_config = true;
1112 LOG(INFO) << "Obtained proxy settings from " <<
1113 gconf_getter_->GetDataSource();
1114 // If gconf proxy mode is "none", meaning direct, then we take
1115 // that to be a valid config and will not check environment
1116 // variables. The alternative would have been to look for a proxy
1117 // whereever we can find one.
1118 //
1119 // Keep a copy of the config for use from this thread for
1120 // comparison with updated settings when we get notifications.
1121 reference_config_ = cached_config_;
1122 reference_config_.set_id(1); // mark it as valid
1123 } else {
1124 gconf_getter_->Shutdown(); // Stop notifications
[email protected]d6cb85b2009-07-23 22:10:531125 }
[email protected]d7395e732009-08-28 23:13:431126 }
[email protected]861c6c62009-04-20 16:50:561127 }
[email protected]d6cb85b2009-07-23 22:10:531128
[email protected]3e44697f2009-05-22 14:37:391129 if (!got_config) {
[email protected]d6cb85b2009-07-23 22:10:531130 // We fall back on environment variables.
[email protected]3e44697f2009-05-22 14:37:391131 //
1132 // Consulting environment variables doesn't need to be done from
1133 // the default glib main loop, but it's a tiny enough amount of
1134 // work.
1135 if (GetConfigFromEnv(&cached_config_)) {
1136 cached_config_.set_id(1); // mark it as valid
[email protected]d7395e732009-08-28 23:13:431137 LOG(INFO) << "Obtained proxy settings from environment variables";
[email protected]3e44697f2009-05-22 14:37:391138 }
[email protected]861c6c62009-04-20 16:50:561139 }
[email protected]3e44697f2009-05-22 14:37:391140}
1141
[email protected]119655002010-07-23 06:02:401142void ProxyConfigServiceLinux::Delegate::AddObserver(Observer* observer) {
1143 observers_.AddObserver(observer);
1144}
1145
1146void ProxyConfigServiceLinux::Delegate::RemoveObserver(Observer* observer) {
1147 observers_.RemoveObserver(observer);
1148}
1149
1150bool ProxyConfigServiceLinux::Delegate::GetLatestProxyConfig(
1151 ProxyConfig* config) {
[email protected]3e44697f2009-05-22 14:37:391152 // This is called from the IO thread.
1153 DCHECK(!io_loop_ || MessageLoop::current() == io_loop_);
1154
1155 // Simply return the last proxy configuration that glib_default_loop
1156 // notified us of.
[email protected]119655002010-07-23 06:02:401157 *config = cached_config_.is_valid() ?
1158 cached_config_ : ProxyConfig::CreateDirect();
1159
1160 // We return true to indicate that *config was filled in. It is always
1161 // going to be available since we initialized eagerly on the UI thread.
1162 // TODO(eroman): do lazy initialization instead, so we no longer need
1163 // to construct ProxyConfigServiceLinux on the UI thread.
1164 // In which case, we may return false here.
1165 return true;
[email protected]3e44697f2009-05-22 14:37:391166}
1167
[email protected]d7395e732009-08-28 23:13:431168// Depending on the GConfSettingGetter in use, this method will be called
1169// on either the UI thread (GConf) or the file thread (KDE).
[email protected]3e44697f2009-05-22 14:37:391170void ProxyConfigServiceLinux::Delegate::OnCheckProxyConfigSettings() {
[email protected]d7395e732009-08-28 23:13:431171 MessageLoop* required_loop = gconf_getter_->GetNotificationLoop();
1172 DCHECK(!required_loop || MessageLoop::current() == required_loop);
[email protected]3e44697f2009-05-22 14:37:391173 ProxyConfig new_config;
1174 bool valid = GetConfigFromGConf(&new_config);
1175 if (valid)
1176 new_config.set_id(1); // mark it as valid
1177
[email protected]119655002010-07-23 06:02:401178 // See if it is different from what we had before.
[email protected]3e44697f2009-05-22 14:37:391179 if (new_config.is_valid() != reference_config_.is_valid() ||
1180 !new_config.Equals(reference_config_)) {
1181 // Post a task to |io_loop| with the new configuration, so it can
1182 // update |cached_config_|.
1183 io_loop_->PostTask(
1184 FROM_HERE,
1185 NewRunnableMethod(
1186 this,
1187 &ProxyConfigServiceLinux::Delegate::SetNewProxyConfig,
1188 new_config));
[email protected]d1f9d472009-08-13 19:59:301189 // Update the thread-private copy in |reference_config_| as well.
1190 reference_config_ = new_config;
[email protected]3e44697f2009-05-22 14:37:391191 }
1192}
1193
1194void ProxyConfigServiceLinux::Delegate::SetNewProxyConfig(
1195 const ProxyConfig& new_config) {
1196 DCHECK(MessageLoop::current() == io_loop_);
1197 LOG(INFO) << "Proxy configuration changed";
1198 cached_config_ = new_config;
[email protected]119655002010-07-23 06:02:401199 FOR_EACH_OBSERVER(Observer, observers_, OnProxyConfigChanged(new_config));
[email protected]3e44697f2009-05-22 14:37:391200}
1201
1202void ProxyConfigServiceLinux::Delegate::PostDestroyTask() {
[email protected]d7395e732009-08-28 23:13:431203 if (!gconf_getter_.get())
1204 return;
1205 MessageLoop* shutdown_loop = gconf_getter_->GetNotificationLoop();
1206 if (!shutdown_loop || MessageLoop::current() == shutdown_loop) {
[email protected]3e44697f2009-05-22 14:37:391207 // Already on the right thread, call directly.
1208 // This is the case for the unittests.
1209 OnDestroy();
1210 } else {
[email protected]d7395e732009-08-28 23:13:431211 // Post to shutdown thread. Note that on browser shutdown, we may quit
1212 // this MessageLoop and exit the program before ever running this.
1213 shutdown_loop->PostTask(
[email protected]3e44697f2009-05-22 14:37:391214 FROM_HERE,
1215 NewRunnableMethod(
1216 this,
1217 &ProxyConfigServiceLinux::Delegate::OnDestroy));
1218 }
1219}
1220void ProxyConfigServiceLinux::Delegate::OnDestroy() {
[email protected]d7395e732009-08-28 23:13:431221 MessageLoop* shutdown_loop = gconf_getter_->GetNotificationLoop();
1222 DCHECK(!shutdown_loop || MessageLoop::current() == shutdown_loop);
1223 gconf_getter_->Shutdown();
[email protected]3e44697f2009-05-22 14:37:391224}
1225
1226ProxyConfigServiceLinux::ProxyConfigServiceLinux()
[email protected]76b90d312010-08-03 03:00:501227 : delegate_(new Delegate(base::Environment::Create())) {
[email protected]3e44697f2009-05-22 14:37:391228}
1229
1230ProxyConfigServiceLinux::ProxyConfigServiceLinux(
[email protected]76b90d312010-08-03 03:00:501231 base::Environment* env_var_getter)
[email protected]9a3d8d42009-09-03 17:01:461232 : delegate_(new Delegate(env_var_getter)) {
1233}
1234
1235ProxyConfigServiceLinux::ProxyConfigServiceLinux(
[email protected]76b90d312010-08-03 03:00:501236 base::Environment* env_var_getter,
[email protected]3e44697f2009-05-22 14:37:391237 GConfSettingGetter* gconf_getter)
1238 : delegate_(new Delegate(env_var_getter, gconf_getter)) {
[email protected]861c6c62009-04-20 16:50:561239}
1240
1241} // namespace net