blob: 5e548cf745ee59d312f2e0dfb3efb74c37854a5c [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]6de53d42010-11-09 07:33:199#if defined(USE_GCONF)
[email protected]861c6c62009-04-20 16:50:5610#include <gconf/gconf-client.h>
[email protected]6de53d42010-11-09 07:33:1911#endif
[email protected]d7395e732009-08-28 23:13:4312#include <limits.h>
13#include <stdio.h>
[email protected]861c6c62009-04-20 16:50:5614#include <stdlib.h>
[email protected]d7395e732009-08-28 23:13:4315#include <sys/inotify.h>
16#include <unistd.h>
[email protected]861c6c62009-04-20 16:50:5617
[email protected]9bc8cff2010-04-03 01:05:3918#include <map>
19
[email protected]76b90d312010-08-03 03:00:5020#include "base/environment.h"
[email protected]d7395e732009-08-28 23:13:4321#include "base/file_path.h"
22#include "base/file_util.h"
[email protected]861c6c62009-04-20 16:50:5623#include "base/logging.h"
[email protected]d7395e732009-08-28 23:13:4324#include "base/message_loop.h"
[email protected]528c56d2010-07-30 19:28:4425#include "base/string_number_conversions.h"
[email protected]861c6c62009-04-20 16:50:5626#include "base/string_tokenizer.h"
27#include "base/string_util.h"
[email protected]3e44697f2009-05-22 14:37:3928#include "base/task.h"
[email protected]d7395e732009-08-28 23:13:4329#include "base/timer.h"
[email protected]6b0349ef2010-10-16 04:56:0630#include "base/nix/xdg_util.h"
[email protected]861c6c62009-04-20 16:50:5631#include "googleurl/src/url_canon.h"
32#include "net/base/net_errors.h"
33#include "net/http/http_util.h"
34#include "net/proxy/proxy_config.h"
35#include "net/proxy/proxy_server.h"
36
37namespace net {
38
39namespace {
40
[email protected]861c6c62009-04-20 16:50:5641// Given a proxy hostname from a setting, returns that hostname with
42// an appropriate proxy server scheme prefix.
43// scheme indicates the desired proxy scheme: usually http, with
44// socks 4 or 5 as special cases.
[email protected]87a102b2009-07-14 05:23:3045// TODO(arindam): Remove URI string manipulation by using MapUrlSchemeToProxy.
[email protected]861c6c62009-04-20 16:50:5646std::string FixupProxyHostScheme(ProxyServer::Scheme scheme,
47 std::string host) {
[email protected]e8c50812010-09-28 00:16:1748 if (scheme == ProxyServer::SCHEME_SOCKS5 &&
49 StartsWithASCII(host, "socks4://", false)) {
50 // We default to socks 5, but if the user specifically set it to
51 // socks4://, then use that.
52 scheme = ProxyServer::SCHEME_SOCKS4;
[email protected]861c6c62009-04-20 16:50:5653 }
54 // Strip the scheme if any.
55 std::string::size_type colon = host.find("://");
56 if (colon != std::string::npos)
57 host = host.substr(colon + 3);
58 // If a username and perhaps password are specified, give a warning.
59 std::string::size_type at_sign = host.find("@");
60 // Should this be supported?
61 if (at_sign != std::string::npos) {
[email protected]62749f182009-07-15 13:16:5462 // ProxyConfig does not support authentication parameters, but Chrome
63 // will prompt for the password later. Disregard the
64 // authentication parameters and continue with this hostname.
65 LOG(WARNING) << "Proxy authentication parameters ignored, see bug 16709";
[email protected]861c6c62009-04-20 16:50:5666 host = host.substr(at_sign + 1);
67 }
68 // If this is a socks proxy, prepend a scheme so as to tell
69 // ProxyServer. This also allows ProxyServer to choose the right
70 // default port.
71 if (scheme == ProxyServer::SCHEME_SOCKS4)
72 host = "socks4://" + host;
73 else if (scheme == ProxyServer::SCHEME_SOCKS5)
74 host = "socks5://" + host;
[email protected]d7395e732009-08-28 23:13:4375 // If there is a trailing slash, remove it so |host| will parse correctly
76 // even if it includes a port number (since the slash is not numeric).
77 if (host.length() && host[host.length() - 1] == '/')
78 host.resize(host.length() - 1);
[email protected]861c6c62009-04-20 16:50:5679 return host;
80}
81
82} // namespace
83
[email protected]8e1845e12010-09-15 19:22:2484ProxyConfigServiceLinux::Delegate::~Delegate() {
85}
86
[email protected]3e44697f2009-05-22 14:37:3987bool ProxyConfigServiceLinux::Delegate::GetProxyFromEnvVarForScheme(
[email protected]861c6c62009-04-20 16:50:5688 const char* variable, ProxyServer::Scheme scheme,
89 ProxyServer* result_server) {
90 std::string env_value;
[email protected]3ba7e082010-08-07 02:57:5991 if (env_var_getter_->GetVar(variable, &env_value)) {
[email protected]861c6c62009-04-20 16:50:5692 if (!env_value.empty()) {
93 env_value = FixupProxyHostScheme(scheme, env_value);
[email protected]87a102b2009-07-14 05:23:3094 ProxyServer proxy_server =
95 ProxyServer::FromURI(env_value, ProxyServer::SCHEME_HTTP);
[email protected]861c6c62009-04-20 16:50:5696 if (proxy_server.is_valid() && !proxy_server.is_direct()) {
97 *result_server = proxy_server;
98 return true;
99 } else {
[email protected]3e44697f2009-05-22 14:37:39100 LOG(ERROR) << "Failed to parse environment variable " << variable;
[email protected]861c6c62009-04-20 16:50:56101 }
102 }
103 }
104 return false;
105}
106
[email protected]3e44697f2009-05-22 14:37:39107bool ProxyConfigServiceLinux::Delegate::GetProxyFromEnvVar(
[email protected]861c6c62009-04-20 16:50:56108 const char* variable, ProxyServer* result_server) {
109 return GetProxyFromEnvVarForScheme(variable, ProxyServer::SCHEME_HTTP,
110 result_server);
111}
112
[email protected]3e44697f2009-05-22 14:37:39113bool ProxyConfigServiceLinux::Delegate::GetConfigFromEnv(ProxyConfig* config) {
[email protected]861c6c62009-04-20 16:50:56114 // Check for automatic configuration first, in
115 // "auto_proxy". Possibly only the "environment_proxy" firefox
116 // extension has ever used this, but it still sounds like a good
117 // idea.
118 std::string auto_proxy;
[email protected]3ba7e082010-08-07 02:57:59119 if (env_var_getter_->GetVar("auto_proxy", &auto_proxy)) {
[email protected]861c6c62009-04-20 16:50:56120 if (auto_proxy.empty()) {
121 // Defined and empty => autodetect
[email protected]ed4ed0f2010-02-24 00:20:48122 config->set_auto_detect(true);
[email protected]861c6c62009-04-20 16:50:56123 } else {
124 // specified autoconfig URL
[email protected]ed4ed0f2010-02-24 00:20:48125 config->set_pac_url(GURL(auto_proxy));
[email protected]861c6c62009-04-20 16:50:56126 }
127 return true;
128 }
129 // "all_proxy" is a shortcut to avoid defining {http,https,ftp}_proxy.
130 ProxyServer proxy_server;
131 if (GetProxyFromEnvVar("all_proxy", &proxy_server)) {
[email protected]ed4ed0f2010-02-24 00:20:48132 config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
133 config->proxy_rules().single_proxy = proxy_server;
[email protected]861c6c62009-04-20 16:50:56134 } else {
135 bool have_http = GetProxyFromEnvVar("http_proxy", &proxy_server);
136 if (have_http)
[email protected]ed4ed0f2010-02-24 00:20:48137 config->proxy_rules().proxy_for_http = proxy_server;
[email protected]861c6c62009-04-20 16:50:56138 // It would be tempting to let http_proxy apply for all protocols
139 // if https_proxy and ftp_proxy are not defined. Googling turns up
140 // several documents that mention only http_proxy. But then the
141 // user really might not want to proxy https. And it doesn't seem
142 // like other apps do this. So we will refrain.
143 bool have_https = GetProxyFromEnvVar("https_proxy", &proxy_server);
144 if (have_https)
[email protected]ed4ed0f2010-02-24 00:20:48145 config->proxy_rules().proxy_for_https = proxy_server;
[email protected]861c6c62009-04-20 16:50:56146 bool have_ftp = GetProxyFromEnvVar("ftp_proxy", &proxy_server);
147 if (have_ftp)
[email protected]ed4ed0f2010-02-24 00:20:48148 config->proxy_rules().proxy_for_ftp = proxy_server;
[email protected]861c6c62009-04-20 16:50:56149 if (have_http || have_https || have_ftp) {
150 // mustn't change type unless some rules are actually set.
[email protected]ed4ed0f2010-02-24 00:20:48151 config->proxy_rules().type =
152 ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
[email protected]861c6c62009-04-20 16:50:56153 }
154 }
[email protected]ed4ed0f2010-02-24 00:20:48155 if (config->proxy_rules().empty()) {
[email protected]861c6c62009-04-20 16:50:56156 // If the above were not defined, try for socks.
[email protected]e8c50812010-09-28 00:16:17157 // For environment variables, we default to version 5, per the gnome
158 // documentation: http://library.gnome.org/devel/gnet/stable/gnet-socks.html
159 ProxyServer::Scheme scheme = ProxyServer::SCHEME_SOCKS5;
[email protected]861c6c62009-04-20 16:50:56160 std::string env_version;
[email protected]3ba7e082010-08-07 02:57:59161 if (env_var_getter_->GetVar("SOCKS_VERSION", &env_version)
[email protected]e8c50812010-09-28 00:16:17162 && env_version == "4")
163 scheme = ProxyServer::SCHEME_SOCKS4;
[email protected]861c6c62009-04-20 16:50:56164 if (GetProxyFromEnvVarForScheme("SOCKS_SERVER", scheme, &proxy_server)) {
[email protected]ed4ed0f2010-02-24 00:20:48165 config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
166 config->proxy_rules().single_proxy = proxy_server;
[email protected]861c6c62009-04-20 16:50:56167 }
168 }
169 // Look for the proxy bypass list.
170 std::string no_proxy;
[email protected]3ba7e082010-08-07 02:57:59171 env_var_getter_->GetVar("no_proxy", &no_proxy);
[email protected]ed4ed0f2010-02-24 00:20:48172 if (config->proxy_rules().empty()) {
[email protected]861c6c62009-04-20 16:50:56173 // Having only "no_proxy" set, presumably to "*", makes it
174 // explicit that env vars do specify a configuration: having no
175 // rules specified only means the user explicitly asks for direct
176 // connections.
177 return !no_proxy.empty();
178 }
[email protected]7541206c2010-02-19 20:24:06179 // Note that this uses "suffix" matching. So a bypass of "google.com"
180 // is understood to mean a bypass of "*google.com".
[email protected]ed4ed0f2010-02-24 00:20:48181 config->proxy_rules().bypass_rules.ParseFromStringUsingSuffixMatching(
182 no_proxy);
[email protected]861c6c62009-04-20 16:50:56183 return true;
184}
185
186namespace {
187
[email protected]d7395e732009-08-28 23:13:43188const int kDebounceTimeoutMilliseconds = 250;
[email protected]3e44697f2009-05-22 14:37:39189
[email protected]6de53d42010-11-09 07:33:19190#if defined(USE_GCONF)
[email protected]d7395e732009-08-28 23:13:43191// This is the "real" gconf version that actually uses gconf.
192class GConfSettingGetterImplGConf
[email protected]861c6c62009-04-20 16:50:56193 : public ProxyConfigServiceLinux::GConfSettingGetter {
194 public:
[email protected]d7395e732009-08-28 23:13:43195 GConfSettingGetterImplGConf()
196 : client_(NULL), notify_delegate_(NULL), loop_(NULL) {}
[email protected]3e44697f2009-05-22 14:37:39197
[email protected]d7395e732009-08-28 23:13:43198 virtual ~GConfSettingGetterImplGConf() {
[email protected]3e44697f2009-05-22 14:37:39199 // client_ should have been released before now, from
[email protected]f5b13442009-07-13 15:23:59200 // Delegate::OnDestroy(), while running on the UI thread. However
201 // on exiting the process, it may happen that
202 // Delegate::OnDestroy() task is left pending on the glib loop
203 // after the loop was quit, and pending tasks may then be deleted
204 // without being run.
205 if (client_) {
206 // gconf client was not cleaned up.
207 if (MessageLoop::current() == loop_) {
208 // We are on the UI thread so we can clean it safely. This is
209 // the case at least for ui_tests running under Valgrind in
210 // bug 16076.
[email protected]b30a3f52010-10-16 01:05:46211 VLOG(1) << "~GConfSettingGetterImplGConf: releasing gconf client";
[email protected]d7395e732009-08-28 23:13:43212 Shutdown();
[email protected]f5b13442009-07-13 15:23:59213 } else {
[email protected]d7395e732009-08-28 23:13:43214 LOG(WARNING) << "~GConfSettingGetterImplGConf: leaking gconf client";
[email protected]f5b13442009-07-13 15:23:59215 client_ = NULL;
216 }
217 }
[email protected]3e44697f2009-05-22 14:37:39218 DCHECK(!client_);
[email protected]861c6c62009-04-20 16:50:56219 }
220
[email protected]d7395e732009-08-28 23:13:43221 virtual bool Init(MessageLoop* glib_default_loop,
222 MessageLoopForIO* file_loop) {
223 DCHECK(MessageLoop::current() == glib_default_loop);
[email protected]3e44697f2009-05-22 14:37:39224 DCHECK(!client_);
225 DCHECK(!loop_);
[email protected]d7395e732009-08-28 23:13:43226 loop_ = glib_default_loop;
[email protected]3e44697f2009-05-22 14:37:39227 client_ = gconf_client_get_default();
[email protected]861c6c62009-04-20 16:50:56228 if (!client_) {
[email protected]861c6c62009-04-20 16:50:56229 // It's not clear whether/when this can return NULL.
[email protected]3e44697f2009-05-22 14:37:39230 LOG(ERROR) << "Unable to create a gconf client";
231 loop_ = NULL;
232 return false;
[email protected]861c6c62009-04-20 16:50:56233 }
[email protected]3e44697f2009-05-22 14:37:39234 GError* error = NULL;
235 // We need to add the directories for which we'll be asking
236 // notifications, and we might as well ask to preload them.
237 gconf_client_add_dir(client_, "/system/proxy",
238 GCONF_CLIENT_PRELOAD_ONELEVEL, &error);
239 if (error == NULL) {
240 gconf_client_add_dir(client_, "/system/http_proxy",
241 GCONF_CLIENT_PRELOAD_ONELEVEL, &error);
242 }
243 if (error != NULL) {
244 LOG(ERROR) << "Error requesting gconf directory: " << error->message;
245 g_error_free(error);
[email protected]d7395e732009-08-28 23:13:43246 Shutdown();
[email protected]3e44697f2009-05-22 14:37:39247 return false;
248 }
249 return true;
250 }
251
[email protected]d7395e732009-08-28 23:13:43252 void Shutdown() {
[email protected]3e44697f2009-05-22 14:37:39253 if (client_) {
254 DCHECK(MessageLoop::current() == loop_);
255 // This also disables gconf notifications.
256 g_object_unref(client_);
257 client_ = NULL;
258 loop_ = NULL;
259 }
260 }
261
[email protected]d7395e732009-08-28 23:13:43262 bool SetupNotification(ProxyConfigServiceLinux::Delegate* delegate) {
[email protected]3e44697f2009-05-22 14:37:39263 DCHECK(client_);
264 DCHECK(MessageLoop::current() == loop_);
265 GError* error = NULL;
[email protected]d7395e732009-08-28 23:13:43266 notify_delegate_ = delegate;
[email protected]3e44697f2009-05-22 14:37:39267 gconf_client_notify_add(
268 client_, "/system/proxy",
[email protected]d7395e732009-08-28 23:13:43269 OnGConfChangeNotification, this,
[email protected]3e44697f2009-05-22 14:37:39270 NULL, &error);
271 if (error == NULL) {
272 gconf_client_notify_add(
273 client_, "/system/http_proxy",
[email protected]d7395e732009-08-28 23:13:43274 OnGConfChangeNotification, this,
[email protected]3e44697f2009-05-22 14:37:39275 NULL, &error);
276 }
277 if (error != NULL) {
278 LOG(ERROR) << "Error requesting gconf notifications: " << error->message;
279 g_error_free(error);
[email protected]d7395e732009-08-28 23:13:43280 Shutdown();
[email protected]3e44697f2009-05-22 14:37:39281 return false;
282 }
283 return true;
[email protected]861c6c62009-04-20 16:50:56284 }
285
[email protected]9a3d8d42009-09-03 17:01:46286 virtual MessageLoop* GetNotificationLoop() {
[email protected]d7395e732009-08-28 23:13:43287 return loop_;
288 }
289
290 virtual const char* GetDataSource() {
291 return "gconf";
292 }
293
[email protected]861c6c62009-04-20 16:50:56294 virtual bool GetString(const char* key, std::string* result) {
[email protected]3e44697f2009-05-22 14:37:39295 DCHECK(client_);
296 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56297 GError* error = NULL;
298 gchar* value = gconf_client_get_string(client_, key, &error);
299 if (HandleGError(error, key))
300 return false;
301 if (!value)
302 return false;
303 *result = value;
304 g_free(value);
305 return true;
306 }
307 virtual bool GetBoolean(const char* key, bool* result) {
[email protected]3e44697f2009-05-22 14:37:39308 DCHECK(client_);
309 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56310 GError* error = NULL;
311 // We want to distinguish unset values from values defaulting to
312 // false. For that we need to use the type-generic
313 // gconf_client_get() rather than gconf_client_get_bool().
314 GConfValue* gconf_value = gconf_client_get(client_, key, &error);
315 if (HandleGError(error, key))
316 return false;
317 if (!gconf_value) {
318 // Unset.
319 return false;
320 }
321 if (gconf_value->type != GCONF_VALUE_BOOL) {
322 gconf_value_free(gconf_value);
323 return false;
324 }
325 gboolean bool_value = gconf_value_get_bool(gconf_value);
326 *result = static_cast<bool>(bool_value);
327 gconf_value_free(gconf_value);
328 return true;
329 }
330 virtual bool GetInt(const char* key, int* result) {
[email protected]3e44697f2009-05-22 14:37:39331 DCHECK(client_);
332 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56333 GError* error = NULL;
334 int value = gconf_client_get_int(client_, key, &error);
335 if (HandleGError(error, key))
336 return false;
337 // We don't bother to distinguish an unset value because callers
338 // don't care. 0 is returned if unset.
339 *result = value;
340 return true;
341 }
342 virtual bool GetStringList(const char* key,
343 std::vector<std::string>* result) {
[email protected]3e44697f2009-05-22 14:37:39344 DCHECK(client_);
345 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56346 GError* error = NULL;
347 GSList* list = gconf_client_get_list(client_, key,
348 GCONF_VALUE_STRING, &error);
349 if (HandleGError(error, key))
350 return false;
351 if (!list) {
352 // unset
353 return false;
354 }
355 for (GSList *it = list; it; it = it->next) {
356 result->push_back(static_cast<char*>(it->data));
357 g_free(it->data);
358 }
359 g_slist_free(list);
360 return true;
361 }
362
[email protected]a48bf4a2010-06-14 18:24:53363 virtual bool BypassListIsReversed() {
364 // This is a KDE-specific setting.
365 return false;
366 }
367
[email protected]1a597192010-07-09 16:58:38368 virtual bool MatchHostsUsingSuffixMatching() {
369 return false;
370 }
371
[email protected]861c6c62009-04-20 16:50:56372 private:
373 // Logs and frees a glib error. Returns false if there was no error
374 // (error is NULL).
375 bool HandleGError(GError* error, const char* key) {
376 if (error != NULL) {
[email protected]3e44697f2009-05-22 14:37:39377 LOG(ERROR) << "Error getting gconf value for " << key
378 << ": " << error->message;
[email protected]861c6c62009-04-20 16:50:56379 g_error_free(error);
380 return true;
381 }
382 return false;
383 }
384
[email protected]d7395e732009-08-28 23:13:43385 // This is the callback from the debounce timer.
386 void OnDebouncedNotification() {
387 DCHECK(MessageLoop::current() == loop_);
388 DCHECK(notify_delegate_);
389 // Forward to a method on the proxy config service delegate object.
390 notify_delegate_->OnCheckProxyConfigSettings();
391 }
392
393 void OnChangeNotification() {
394 // We don't use Reset() because the timer may not yet be running.
395 // (In that case Stop() is a no-op.)
396 debounce_timer_.Stop();
397 debounce_timer_.Start(base::TimeDelta::FromMilliseconds(
398 kDebounceTimeoutMilliseconds), this,
399 &GConfSettingGetterImplGConf::OnDebouncedNotification);
400 }
401
402 // gconf notification callback, dispatched from the default glib main loop.
403 static void OnGConfChangeNotification(
404 GConfClient* client, guint cnxn_id,
405 GConfEntry* entry, gpointer user_data) {
[email protected]b30a3f52010-10-16 01:05:46406 VLOG(1) << "gconf change notification for key "
407 << gconf_entry_get_key(entry);
[email protected]d7395e732009-08-28 23:13:43408 // We don't track which key has changed, just that something did change.
409 GConfSettingGetterImplGConf* setting_getter =
410 reinterpret_cast<GConfSettingGetterImplGConf*>(user_data);
411 setting_getter->OnChangeNotification();
412 }
413
[email protected]861c6c62009-04-20 16:50:56414 GConfClient* client_;
[email protected]d7395e732009-08-28 23:13:43415 ProxyConfigServiceLinux::Delegate* notify_delegate_;
416 base::OneShotTimer<GConfSettingGetterImplGConf> debounce_timer_;
[email protected]861c6c62009-04-20 16:50:56417
[email protected]3e44697f2009-05-22 14:37:39418 // Message loop of the thread that we make gconf calls on. It should
419 // be the UI thread and all our methods should be called on this
420 // thread. Only for assertions.
421 MessageLoop* loop_;
422
[email protected]d7395e732009-08-28 23:13:43423 DISALLOW_COPY_AND_ASSIGN(GConfSettingGetterImplGConf);
424};
[email protected]6de53d42010-11-09 07:33:19425#endif // defined(USE_GCONF)
[email protected]d7395e732009-08-28 23:13:43426
427// This is the KDE version that reads kioslaverc and simulates gconf.
428// Doing this allows the main Delegate code, as well as the unit tests
429// for it, to stay the same - and the settings map fairly well besides.
430class GConfSettingGetterImplKDE
431 : public ProxyConfigServiceLinux::GConfSettingGetter,
432 public base::MessagePumpLibevent::Watcher {
433 public:
[email protected]76b90d312010-08-03 03:00:50434 explicit GConfSettingGetterImplKDE(base::Environment* env_var_getter)
[email protected]d7395e732009-08-28 23:13:43435 : inotify_fd_(-1), notify_delegate_(NULL), indirect_manual_(false),
[email protected]a48bf4a2010-06-14 18:24:53436 auto_no_pac_(false), reversed_bypass_list_(false),
[email protected]f18fde22010-05-18 23:49:54437 env_var_getter_(env_var_getter), file_loop_(NULL) {
438 // Derive the location of the kde config dir from the environment.
[email protected]92d2dc82010-04-08 17:49:59439 std::string home;
[email protected]3ba7e082010-08-07 02:57:59440 if (env_var_getter->GetVar("KDEHOME", &home) && !home.empty()) {
[email protected]2e8cfe22010-06-12 00:26:24441 // $KDEHOME is set. Use it unconditionally.
[email protected]92d2dc82010-04-08 17:49:59442 kde_config_dir_ = KDEHomeToConfigPath(FilePath(home));
443 } else {
[email protected]2e8cfe22010-06-12 00:26:24444 // $KDEHOME is unset. Try to figure out what to use. This seems to be
[email protected]92d2dc82010-04-08 17:49:59445 // the common case on most distributions.
[email protected]3ba7e082010-08-07 02:57:59446 if (!env_var_getter->GetVar(base::env_vars::kHome, &home))
[email protected]d7395e732009-08-28 23:13:43447 // User has no $HOME? Give up. Later we'll report the failure.
448 return;
[email protected]6b0349ef2010-10-16 04:56:06449 if (base::nix::GetDesktopEnvironment(env_var_getter) ==
450 base::nix::DESKTOP_ENVIRONMENT_KDE3) {
[email protected]92d2dc82010-04-08 17:49:59451 // KDE3 always uses .kde for its configuration.
452 FilePath kde_path = FilePath(home).Append(".kde");
453 kde_config_dir_ = KDEHomeToConfigPath(kde_path);
454 } else {
455 // Some distributions patch KDE4 to use .kde4 instead of .kde, so that
[email protected]fad9c8a52010-06-10 22:30:53456 // both can be installed side-by-side. Sadly they don't all do this, and
457 // they don't always do this: some distributions have started switching
458 // back as well. So if there is a .kde4 directory, check the timestamps
459 // of the config directories within and use the newest one.
[email protected]92d2dc82010-04-08 17:49:59460 // Note that we should currently be running in the UI thread, because in
461 // the gconf version, that is the only thread that can access the proxy
462 // settings (a gconf restriction). As noted below, the initial read of
463 // the proxy settings will be done in this thread anyway, so we check
464 // for .kde4 here in this thread as well.
[email protected]fad9c8a52010-06-10 22:30:53465 FilePath kde3_path = FilePath(home).Append(".kde");
466 FilePath kde3_config = KDEHomeToConfigPath(kde3_path);
[email protected]92d2dc82010-04-08 17:49:59467 FilePath kde4_path = FilePath(home).Append(".kde4");
[email protected]fad9c8a52010-06-10 22:30:53468 FilePath kde4_config = KDEHomeToConfigPath(kde4_path);
469 bool use_kde4 = false;
[email protected]92d2dc82010-04-08 17:49:59470 if (file_util::DirectoryExists(kde4_path)) {
[email protected]2f0193c22010-09-03 02:28:37471 base::PlatformFileInfo kde3_info;
472 base::PlatformFileInfo kde4_info;
[email protected]fad9c8a52010-06-10 22:30:53473 if (file_util::GetFileInfo(kde4_config, &kde4_info)) {
474 if (file_util::GetFileInfo(kde3_config, &kde3_info)) {
475 use_kde4 = kde4_info.last_modified >= kde3_info.last_modified;
476 } else {
477 use_kde4 = true;
478 }
479 }
480 }
481 if (use_kde4) {
[email protected]92d2dc82010-04-08 17:49:59482 kde_config_dir_ = KDEHomeToConfigPath(kde4_path);
483 } else {
[email protected]fad9c8a52010-06-10 22:30:53484 kde_config_dir_ = KDEHomeToConfigPath(kde3_path);
[email protected]92d2dc82010-04-08 17:49:59485 }
486 }
[email protected]d7395e732009-08-28 23:13:43487 }
[email protected]d7395e732009-08-28 23:13:43488 }
489
490 virtual ~GConfSettingGetterImplKDE() {
491 // inotify_fd_ should have been closed before now, from
492 // Delegate::OnDestroy(), while running on the file thread. However
493 // on exiting the process, it may happen that Delegate::OnDestroy()
494 // task is left pending on the file loop after the loop was quit,
495 // and pending tasks may then be deleted without being run.
496 // Here in the KDE version, we can safely close the file descriptor
497 // anyway. (Not that it really matters; the process is exiting.)
498 if (inotify_fd_ >= 0)
499 Shutdown();
500 DCHECK(inotify_fd_ < 0);
501 }
502
503 virtual bool Init(MessageLoop* glib_default_loop,
504 MessageLoopForIO* file_loop) {
505 DCHECK(inotify_fd_ < 0);
506 inotify_fd_ = inotify_init();
507 if (inotify_fd_ < 0) {
[email protected]57b765672009-10-13 18:27:40508 PLOG(ERROR) << "inotify_init failed";
[email protected]d7395e732009-08-28 23:13:43509 return false;
510 }
511 int flags = fcntl(inotify_fd_, F_GETFL);
512 if (fcntl(inotify_fd_, F_SETFL, flags | O_NONBLOCK) < 0) {
[email protected]57b765672009-10-13 18:27:40513 PLOG(ERROR) << "fcntl failed";
[email protected]d7395e732009-08-28 23:13:43514 close(inotify_fd_);
515 inotify_fd_ = -1;
516 return false;
517 }
518 file_loop_ = file_loop;
519 // The initial read is done on the current thread, not |file_loop_|,
520 // since we will need to have it for SetupAndFetchInitialConfig().
521 UpdateCachedSettings();
522 return true;
523 }
524
525 void Shutdown() {
526 if (inotify_fd_ >= 0) {
527 ResetCachedSettings();
528 inotify_watcher_.StopWatchingFileDescriptor();
529 close(inotify_fd_);
530 inotify_fd_ = -1;
531 }
532 }
533
534 bool SetupNotification(ProxyConfigServiceLinux::Delegate* delegate) {
535 DCHECK(inotify_fd_ >= 0);
536 DCHECK(file_loop_);
537 // We can't just watch the kioslaverc file directly, since KDE will write
538 // a new copy of it and then rename it whenever settings are changed and
539 // inotify watches inodes (so we'll be watching the old deleted file after
540 // the first change, and it will never change again). So, we watch the
541 // directory instead. We then act only on changes to the kioslaverc entry.
542 if (inotify_add_watch(inotify_fd_, kde_config_dir_.value().c_str(),
543 IN_MODIFY | IN_MOVED_TO) < 0)
544 return false;
545 notify_delegate_ = delegate;
546 return file_loop_->WatchFileDescriptor(inotify_fd_, true,
547 MessageLoopForIO::WATCH_READ, &inotify_watcher_, this);
548 }
549
[email protected]9a3d8d42009-09-03 17:01:46550 virtual MessageLoop* GetNotificationLoop() {
[email protected]d7395e732009-08-28 23:13:43551 return file_loop_;
552 }
553
554 // Implement base::MessagePumpLibevent::Delegate.
555 void OnFileCanReadWithoutBlocking(int fd) {
556 DCHECK(fd == inotify_fd_);
557 DCHECK(MessageLoop::current() == file_loop_);
558 OnChangeNotification();
559 }
560 void OnFileCanWriteWithoutBlocking(int fd) {
561 NOTREACHED();
562 }
563
564 virtual const char* GetDataSource() {
565 return "KDE";
566 }
567
568 virtual bool GetString(const char* key, std::string* result) {
569 string_map_type::iterator it = string_table_.find(key);
570 if (it == string_table_.end())
571 return false;
572 *result = it->second;
573 return true;
574 }
575 virtual bool GetBoolean(const char* key, bool* result) {
576 // We don't ever have any booleans.
577 return false;
578 }
579 virtual bool GetInt(const char* key, int* result) {
580 // We don't ever have any integers. (See AddProxy() below about ports.)
581 return false;
582 }
583 virtual bool GetStringList(const char* key,
584 std::vector<std::string>* result) {
585 strings_map_type::iterator it = strings_table_.find(key);
586 if (it == strings_table_.end())
587 return false;
588 *result = it->second;
589 return true;
590 }
591
[email protected]a48bf4a2010-06-14 18:24:53592 virtual bool BypassListIsReversed() {
593 return reversed_bypass_list_;
594 }
595
[email protected]1a597192010-07-09 16:58:38596 virtual bool MatchHostsUsingSuffixMatching() {
597 return true;
598 }
599
[email protected]d7395e732009-08-28 23:13:43600 private:
601 void ResetCachedSettings() {
602 string_table_.clear();
603 strings_table_.clear();
604 indirect_manual_ = false;
605 auto_no_pac_ = false;
[email protected]a48bf4a2010-06-14 18:24:53606 reversed_bypass_list_ = false;
[email protected]d7395e732009-08-28 23:13:43607 }
608
[email protected]92d2dc82010-04-08 17:49:59609 FilePath KDEHomeToConfigPath(const FilePath& kde_home) {
610 return kde_home.Append("share").Append("config");
611 }
612
[email protected]a8185d02010-06-11 00:19:50613 void AddProxy(const std::string& prefix, const std::string& value) {
[email protected]d7395e732009-08-28 23:13:43614 if (value.empty() || value.substr(0, 3) == "//:")
615 // No proxy.
616 return;
617 // We don't need to parse the port number out; GetProxyFromGConf()
618 // would only append it right back again. So we just leave the port
619 // number right in the host string.
620 string_table_[prefix + "host"] = value;
621 }
622
[email protected]a8185d02010-06-11 00:19:50623 void AddHostList(const std::string& key, const std::string& value) {
[email protected]f18fde22010-05-18 23:49:54624 std::vector<std::string> tokens;
[email protected]1a597192010-07-09 16:58:38625 StringTokenizer tk(value, ", ");
[email protected]f18fde22010-05-18 23:49:54626 while (tk.GetNext()) {
627 std::string token = tk.token();
628 if (!token.empty())
629 tokens.push_back(token);
630 }
631 strings_table_[key] = tokens;
632 }
633
[email protected]9a3d8d42009-09-03 17:01:46634 void AddKDESetting(const std::string& key, const std::string& value) {
[email protected]d7395e732009-08-28 23:13:43635 // The astute reader may notice that there is no mention of SOCKS
636 // here. That's because KDE handles socks is a strange way, and we
637 // don't support it. Rather than just a setting for the SOCKS server,
638 // it has a setting for a library to LD_PRELOAD in all your programs
639 // that will transparently SOCKSify them. Such libraries each have
640 // their own configuration, and thus, we can't get it from KDE.
641 if (key == "ProxyType") {
642 const char* mode = "none";
643 indirect_manual_ = false;
644 auto_no_pac_ = false;
[email protected]e83326f2010-07-31 17:29:25645 int int_value;
646 base::StringToInt(value, &int_value);
647 switch (int_value) {
[email protected]d7395e732009-08-28 23:13:43648 case 0: // No proxy, or maybe kioslaverc syntax error.
649 break;
650 case 1: // Manual configuration.
651 mode = "manual";
652 break;
653 case 2: // PAC URL.
654 mode = "auto";
655 break;
656 case 3: // WPAD.
657 mode = "auto";
658 auto_no_pac_ = true;
659 break;
660 case 4: // Indirect manual via environment variables.
661 mode = "manual";
662 indirect_manual_ = true;
663 break;
664 }
665 string_table_["/system/proxy/mode"] = mode;
666 } else if (key == "Proxy Config Script") {
667 string_table_["/system/proxy/autoconfig_url"] = value;
668 } else if (key == "httpProxy") {
669 AddProxy("/system/http_proxy/", value);
670 } else if (key == "httpsProxy") {
671 AddProxy("/system/proxy/secure_", value);
672 } else if (key == "ftpProxy") {
673 AddProxy("/system/proxy/ftp_", value);
674 } else if (key == "ReversedException") {
675 // We count "true" or any nonzero number as true, otherwise false.
676 // Note that if the value is not actually numeric StringToInt()
677 // will return 0, which we count as false.
[email protected]e83326f2010-07-31 17:29:25678 int int_value;
679 base::StringToInt(value, &int_value);
680 reversed_bypass_list_ = (value == "true" || int_value);
[email protected]d7395e732009-08-28 23:13:43681 } else if (key == "NoProxyFor") {
[email protected]f18fde22010-05-18 23:49:54682 AddHostList("/system/http_proxy/ignore_hosts", value);
[email protected]d7395e732009-08-28 23:13:43683 } else if (key == "AuthMode") {
684 // Check for authentication, just so we can warn.
[email protected]e83326f2010-07-31 17:29:25685 int mode;
686 base::StringToInt(value, &mode);
[email protected]d7395e732009-08-28 23:13:43687 if (mode) {
688 // ProxyConfig does not support authentication parameters, but
689 // Chrome will prompt for the password later. So we ignore this.
690 LOG(WARNING) <<
691 "Proxy authentication parameters ignored, see bug 16709";
692 }
693 }
694 }
695
[email protected]a8185d02010-06-11 00:19:50696 void ResolveIndirect(const std::string& key) {
[email protected]d7395e732009-08-28 23:13:43697 string_map_type::iterator it = string_table_.find(key);
698 if (it != string_table_.end()) {
[email protected]f18fde22010-05-18 23:49:54699 std::string value;
[email protected]3ba7e082010-08-07 02:57:59700 if (env_var_getter_->GetVar(it->second.c_str(), &value))
[email protected]d7395e732009-08-28 23:13:43701 it->second = value;
[email protected]8425adc02010-04-18 17:45:31702 else
703 string_table_.erase(it);
[email protected]d7395e732009-08-28 23:13:43704 }
705 }
706
[email protected]a8185d02010-06-11 00:19:50707 void ResolveIndirectList(const std::string& key) {
[email protected]f18fde22010-05-18 23:49:54708 strings_map_type::iterator it = strings_table_.find(key);
709 if (it != strings_table_.end()) {
710 std::string value;
711 if (!it->second.empty() &&
[email protected]3ba7e082010-08-07 02:57:59712 env_var_getter_->GetVar(it->second[0].c_str(), &value))
[email protected]f18fde22010-05-18 23:49:54713 AddHostList(key, value);
714 else
715 strings_table_.erase(it);
716 }
717 }
718
[email protected]d7395e732009-08-28 23:13:43719 // The settings in kioslaverc could occur in any order, but some affect
720 // others. Rather than read the whole file in and then query them in an
721 // order that allows us to handle that, we read the settings in whatever
722 // order they occur and do any necessary tweaking after we finish.
723 void ResolveModeEffects() {
724 if (indirect_manual_) {
725 ResolveIndirect("/system/http_proxy/host");
726 ResolveIndirect("/system/proxy/secure_host");
727 ResolveIndirect("/system/proxy/ftp_host");
[email protected]f18fde22010-05-18 23:49:54728 ResolveIndirectList("/system/http_proxy/ignore_hosts");
[email protected]d7395e732009-08-28 23:13:43729 }
730 if (auto_no_pac_) {
731 // Remove the PAC URL; we're not supposed to use it.
732 string_table_.erase("/system/proxy/autoconfig_url");
733 }
[email protected]d7395e732009-08-28 23:13:43734 }
735
736 // Reads kioslaverc one line at a time and calls AddKDESetting() to add
737 // each relevant name-value pair to the appropriate value table.
738 void UpdateCachedSettings() {
[email protected]92d2dc82010-04-08 17:49:59739 FilePath kioslaverc = kde_config_dir_.Append("kioslaverc");
[email protected]d7395e732009-08-28 23:13:43740 file_util::ScopedFILE input(file_util::OpenFile(kioslaverc, "r"));
741 if (!input.get())
742 return;
743 ResetCachedSettings();
744 bool in_proxy_settings = false;
745 bool line_too_long = false;
[email protected]9a3d8d42009-09-03 17:01:46746 char line[BUFFER_SIZE];
747 // fgets() will return NULL on EOF or error.
[email protected]d7395e732009-08-28 23:13:43748 while (fgets(line, sizeof(line), input.get())) {
749 // fgets() guarantees the line will be properly terminated.
750 size_t length = strlen(line);
751 if (!length)
752 continue;
753 // This should be true even with CRLF endings.
754 if (line[length - 1] != '\n') {
755 line_too_long = true;
756 continue;
757 }
758 if (line_too_long) {
759 // The previous line had no line ending, but this done does. This is
760 // the end of the line that was too long, so warn here and skip it.
761 LOG(WARNING) << "skipped very long line in " << kioslaverc.value();
762 line_too_long = false;
763 continue;
764 }
765 // Remove the LF at the end, and the CR if there is one.
766 line[--length] = '\0';
767 if (length && line[length - 1] == '\r')
768 line[--length] = '\0';
769 // Now parse the line.
770 if (line[0] == '[') {
771 // Switching sections. All we care about is whether this is
772 // the (a?) proxy settings section, for both KDE3 and KDE4.
773 in_proxy_settings = !strncmp(line, "[Proxy Settings]", 16);
774 } else if (in_proxy_settings) {
775 // A regular line, in the (a?) proxy settings section.
[email protected]9a3d8d42009-09-03 17:01:46776 char* split = strchr(line, '=');
777 // Skip this line if it does not contain an = sign.
778 if (!split)
[email protected]d7395e732009-08-28 23:13:43779 continue;
[email protected]9a3d8d42009-09-03 17:01:46780 // Split the line on the = and advance |split|.
781 *(split++) = 0;
782 std::string key = line;
783 std::string value = split;
784 TrimWhitespaceASCII(key, TRIM_ALL, &key);
785 TrimWhitespaceASCII(value, TRIM_ALL, &value);
786 // Skip this line if the key name is empty.
787 if (key.empty())
[email protected]d7395e732009-08-28 23:13:43788 continue;
789 // Is the value name localized?
[email protected]9a3d8d42009-09-03 17:01:46790 if (key[key.length() - 1] == ']') {
791 // Find the matching bracket.
792 length = key.rfind('[');
793 // Skip this line if the localization indicator is malformed.
794 if (length == std::string::npos)
[email protected]d7395e732009-08-28 23:13:43795 continue;
796 // Trim the localization indicator off.
[email protected]9a3d8d42009-09-03 17:01:46797 key.resize(length);
798 // Remove any resulting trailing whitespace.
799 TrimWhitespaceASCII(key, TRIM_TRAILING, &key);
800 // Skip this line if the key name is now empty.
801 if (key.empty())
802 continue;
[email protected]d7395e732009-08-28 23:13:43803 }
[email protected]d7395e732009-08-28 23:13:43804 // Now fill in the tables.
[email protected]9a3d8d42009-09-03 17:01:46805 AddKDESetting(key, value);
[email protected]d7395e732009-08-28 23:13:43806 }
807 }
808 if (ferror(input.get()))
809 LOG(ERROR) << "error reading " << kioslaverc.value();
810 ResolveModeEffects();
811 }
812
813 // This is the callback from the debounce timer.
814 void OnDebouncedNotification() {
815 DCHECK(MessageLoop::current() == file_loop_);
[email protected]b30a3f52010-10-16 01:05:46816 VLOG(1) << "inotify change notification for kioslaverc";
[email protected]d7395e732009-08-28 23:13:43817 UpdateCachedSettings();
818 DCHECK(notify_delegate_);
819 // Forward to a method on the proxy config service delegate object.
820 notify_delegate_->OnCheckProxyConfigSettings();
821 }
822
823 // Called by OnFileCanReadWithoutBlocking() on the file thread. Reads
824 // from the inotify file descriptor and starts up a debounce timer if
825 // an event for kioslaverc is seen.
826 void OnChangeNotification() {
827 DCHECK(inotify_fd_ >= 0);
828 DCHECK(MessageLoop::current() == file_loop_);
829 char event_buf[(sizeof(inotify_event) + NAME_MAX + 1) * 4];
830 bool kioslaverc_touched = false;
831 ssize_t r;
832 while ((r = read(inotify_fd_, event_buf, sizeof(event_buf))) > 0) {
833 // inotify returns variable-length structures, which is why we have
834 // this strange-looking loop instead of iterating through an array.
835 char* event_ptr = event_buf;
836 while (event_ptr < event_buf + r) {
837 inotify_event* event = reinterpret_cast<inotify_event*>(event_ptr);
838 // The kernel always feeds us whole events.
[email protected]b1f031dd2010-03-02 23:19:33839 CHECK_LE(event_ptr + sizeof(inotify_event), event_buf + r);
840 CHECK_LE(event->name + event->len, event_buf + r);
[email protected]d7395e732009-08-28 23:13:43841 if (!strcmp(event->name, "kioslaverc"))
842 kioslaverc_touched = true;
843 // Advance the pointer just past the end of the filename.
844 event_ptr = event->name + event->len;
845 }
846 // We keep reading even if |kioslaverc_touched| is true to drain the
847 // inotify event queue.
848 }
849 if (!r)
850 // Instead of returning -1 and setting errno to EINVAL if there is not
851 // enough buffer space, older kernels (< 2.6.21) return 0. Simulate the
852 // new behavior (EINVAL) so we can reuse the code below.
853 errno = EINVAL;
854 if (errno != EAGAIN) {
[email protected]57b765672009-10-13 18:27:40855 PLOG(WARNING) << "error reading inotify file descriptor";
[email protected]d7395e732009-08-28 23:13:43856 if (errno == EINVAL) {
857 // Our buffer is not large enough to read the next event. This should
858 // not happen (because its size is calculated to always be sufficiently
859 // large), but if it does we'd warn continuously since |inotify_fd_|
860 // would be forever ready to read. Close it and stop watching instead.
861 LOG(ERROR) << "inotify failure; no longer watching kioslaverc!";
862 inotify_watcher_.StopWatchingFileDescriptor();
863 close(inotify_fd_);
864 inotify_fd_ = -1;
865 }
866 }
867 if (kioslaverc_touched) {
868 // We don't use Reset() because the timer may not yet be running.
869 // (In that case Stop() is a no-op.)
870 debounce_timer_.Stop();
871 debounce_timer_.Start(base::TimeDelta::FromMilliseconds(
872 kDebounceTimeoutMilliseconds), this,
873 &GConfSettingGetterImplKDE::OnDebouncedNotification);
874 }
875 }
876
877 typedef std::map<std::string, std::string> string_map_type;
878 typedef std::map<std::string, std::vector<std::string> > strings_map_type;
879
880 int inotify_fd_;
881 base::MessagePumpLibevent::FileDescriptorWatcher inotify_watcher_;
882 ProxyConfigServiceLinux::Delegate* notify_delegate_;
883 base::OneShotTimer<GConfSettingGetterImplKDE> debounce_timer_;
884 FilePath kde_config_dir_;
885 bool indirect_manual_;
886 bool auto_no_pac_;
[email protected]a48bf4a2010-06-14 18:24:53887 bool reversed_bypass_list_;
[email protected]f18fde22010-05-18 23:49:54888 // We don't own |env_var_getter_|. It's safe to hold a pointer to it, since
889 // both it and us are owned by ProxyConfigServiceLinux::Delegate, and have the
890 // same lifetime.
[email protected]76b90d312010-08-03 03:00:50891 base::Environment* env_var_getter_;
[email protected]d7395e732009-08-28 23:13:43892
893 // We cache these settings whenever we re-read the kioslaverc file.
894 string_map_type string_table_;
895 strings_map_type strings_table_;
896
897 // Message loop of the file thread, for reading kioslaverc. If NULL,
898 // just read it directly (for testing). We also handle inotify events
899 // on this thread.
900 MessageLoopForIO* file_loop_;
901
902 DISALLOW_COPY_AND_ASSIGN(GConfSettingGetterImplKDE);
[email protected]861c6c62009-04-20 16:50:56903};
904
905} // namespace
906
[email protected]3e44697f2009-05-22 14:37:39907bool ProxyConfigServiceLinux::Delegate::GetProxyFromGConf(
[email protected]861c6c62009-04-20 16:50:56908 const char* key_prefix, bool is_socks, ProxyServer* result_server) {
909 std::string key(key_prefix);
910 std::string host;
911 if (!gconf_getter_->GetString((key + "host").c_str(), &host)
912 || host.empty()) {
913 // Unset or empty.
914 return false;
915 }
916 // Check for an optional port.
[email protected]d7395e732009-08-28 23:13:43917 int port = 0;
[email protected]861c6c62009-04-20 16:50:56918 gconf_getter_->GetInt((key + "port").c_str(), &port);
919 if (port != 0) {
920 // If a port is set and non-zero:
[email protected]528c56d2010-07-30 19:28:44921 host += ":" + base::IntToString(port);
[email protected]861c6c62009-04-20 16:50:56922 }
923 host = FixupProxyHostScheme(
[email protected]e8c50812010-09-28 00:16:17924 is_socks ? ProxyServer::SCHEME_SOCKS5 : ProxyServer::SCHEME_HTTP,
[email protected]861c6c62009-04-20 16:50:56925 host);
[email protected]87a102b2009-07-14 05:23:30926 ProxyServer proxy_server = ProxyServer::FromURI(host,
927 ProxyServer::SCHEME_HTTP);
[email protected]861c6c62009-04-20 16:50:56928 if (proxy_server.is_valid()) {
929 *result_server = proxy_server;
930 return true;
931 }
932 return false;
933}
934
[email protected]3e44697f2009-05-22 14:37:39935bool ProxyConfigServiceLinux::Delegate::GetConfigFromGConf(
936 ProxyConfig* config) {
[email protected]861c6c62009-04-20 16:50:56937 std::string mode;
938 if (!gconf_getter_->GetString("/system/proxy/mode", &mode)) {
939 // We expect this to always be set, so if we don't see it then we
940 // probably have a gconf problem, and so we don't have a valid
941 // proxy config.
942 return false;
943 }
[email protected]3e44697f2009-05-22 14:37:39944 if (mode == "none") {
[email protected]861c6c62009-04-20 16:50:56945 // Specifically specifies no proxy.
946 return true;
[email protected]3e44697f2009-05-22 14:37:39947 }
[email protected]861c6c62009-04-20 16:50:56948
[email protected]3e44697f2009-05-22 14:37:39949 if (mode == "auto") {
[email protected]861c6c62009-04-20 16:50:56950 // automatic proxy config
951 std::string pac_url_str;
952 if (gconf_getter_->GetString("/system/proxy/autoconfig_url",
953 &pac_url_str)) {
954 if (!pac_url_str.empty()) {
955 GURL pac_url(pac_url_str);
956 if (!pac_url.is_valid())
957 return false;
[email protected]ed4ed0f2010-02-24 00:20:48958 config->set_pac_url(pac_url);
[email protected]861c6c62009-04-20 16:50:56959 return true;
960 }
961 }
[email protected]ed4ed0f2010-02-24 00:20:48962 config->set_auto_detect(true);
[email protected]861c6c62009-04-20 16:50:56963 return true;
964 }
965
[email protected]3e44697f2009-05-22 14:37:39966 if (mode != "manual") {
[email protected]861c6c62009-04-20 16:50:56967 // Mode is unrecognized.
968 return false;
969 }
970 bool use_http_proxy;
971 if (gconf_getter_->GetBoolean("/system/http_proxy/use_http_proxy",
972 &use_http_proxy)
973 && !use_http_proxy) {
974 // Another master switch for some reason. If set to false, then no
975 // proxy. But we don't panic if the key doesn't exist.
976 return true;
977 }
978
979 bool same_proxy = false;
980 // Indicates to use the http proxy for all protocols. This one may
981 // not exist (presumably on older versions), assume false in that
982 // case.
983 gconf_getter_->GetBoolean("/system/http_proxy/use_same_proxy",
984 &same_proxy);
985
986 ProxyServer proxy_server;
987 if (!same_proxy) {
988 // Try socks.
989 if (GetProxyFromGConf("/system/proxy/socks_", true, &proxy_server)) {
990 // gconf settings do not appear to distinguish between socks
[email protected]e8c50812010-09-28 00:16:17991 // version. We default to version 5. For more information on this policy
992 // decisions, see:
993 // http://code.google.com/p/chromium/issues/detail?id=55912#c2
[email protected]ed4ed0f2010-02-24 00:20:48994 config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
995 config->proxy_rules().single_proxy = proxy_server;
[email protected]861c6c62009-04-20 16:50:56996 }
997 }
[email protected]ed4ed0f2010-02-24 00:20:48998 if (config->proxy_rules().empty()) {
[email protected]861c6c62009-04-20 16:50:56999 bool have_http = GetProxyFromGConf("/system/http_proxy/", false,
1000 &proxy_server);
1001 if (same_proxy) {
1002 if (have_http) {
[email protected]ed4ed0f2010-02-24 00:20:481003 config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
1004 config->proxy_rules().single_proxy = proxy_server;
[email protected]861c6c62009-04-20 16:50:561005 }
1006 } else {
1007 // Protocol specific settings.
1008 if (have_http)
[email protected]ed4ed0f2010-02-24 00:20:481009 config->proxy_rules().proxy_for_http = proxy_server;
[email protected]861c6c62009-04-20 16:50:561010 bool have_secure = GetProxyFromGConf("/system/proxy/secure_", false,
1011 &proxy_server);
1012 if (have_secure)
[email protected]ed4ed0f2010-02-24 00:20:481013 config->proxy_rules().proxy_for_https = proxy_server;
[email protected]861c6c62009-04-20 16:50:561014 bool have_ftp = GetProxyFromGConf("/system/proxy/ftp_", false,
1015 &proxy_server);
1016 if (have_ftp)
[email protected]ed4ed0f2010-02-24 00:20:481017 config->proxy_rules().proxy_for_ftp = proxy_server;
[email protected]861c6c62009-04-20 16:50:561018 if (have_http || have_secure || have_ftp)
[email protected]ed4ed0f2010-02-24 00:20:481019 config->proxy_rules().type =
[email protected]861c6c62009-04-20 16:50:561020 ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
1021 }
1022 }
1023
[email protected]ed4ed0f2010-02-24 00:20:481024 if (config->proxy_rules().empty()) {
[email protected]861c6c62009-04-20 16:50:561025 // Manual mode but we couldn't parse any rules.
1026 return false;
1027 }
1028
1029 // Check for authentication, just so we can warn.
[email protected]d7395e732009-08-28 23:13:431030 bool use_auth = false;
[email protected]861c6c62009-04-20 16:50:561031 gconf_getter_->GetBoolean("/system/http_proxy/use_authentication",
1032 &use_auth);
[email protected]62749f182009-07-15 13:16:541033 if (use_auth) {
1034 // ProxyConfig does not support authentication parameters, but
1035 // Chrome will prompt for the password later. So we ignore
1036 // /system/http_proxy/*auth* settings.
1037 LOG(WARNING) << "Proxy authentication parameters ignored, see bug 16709";
1038 }
[email protected]861c6c62009-04-20 16:50:561039
1040 // Now the bypass list.
[email protected]7541206c2010-02-19 20:24:061041 std::vector<std::string> ignore_hosts_list;
[email protected]ed4ed0f2010-02-24 00:20:481042 config->proxy_rules().bypass_rules.Clear();
[email protected]a8185d02010-06-11 00:19:501043 if (gconf_getter_->GetStringList("/system/http_proxy/ignore_hosts",
1044 &ignore_hosts_list)) {
1045 std::vector<std::string>::const_iterator it(ignore_hosts_list.begin());
[email protected]1a597192010-07-09 16:58:381046 for (; it != ignore_hosts_list.end(); ++it) {
1047 if (gconf_getter_->MatchHostsUsingSuffixMatching()) {
1048 config->proxy_rules().bypass_rules.
1049 AddRuleFromStringUsingSuffixMatching(*it);
1050 } else {
1051 config->proxy_rules().bypass_rules.AddRuleFromString(*it);
1052 }
1053 }
[email protected]a8185d02010-06-11 00:19:501054 }
[email protected]861c6c62009-04-20 16:50:561055 // Note that there are no settings with semantics corresponding to
[email protected]1a597192010-07-09 16:58:381056 // bypass of local names in GNOME. In KDE, "<local>" is supported
1057 // as a hostname rule.
[email protected]861c6c62009-04-20 16:50:561058
[email protected]a48bf4a2010-06-14 18:24:531059 // KDE allows one to reverse the bypass rules.
1060 config->proxy_rules().reverse_bypass = gconf_getter_->BypassListIsReversed();
1061
[email protected]861c6c62009-04-20 16:50:561062 return true;
1063}
1064
[email protected]76b90d312010-08-03 03:00:501065ProxyConfigServiceLinux::Delegate::Delegate(base::Environment* env_var_getter)
[email protected]d7395e732009-08-28 23:13:431066 : env_var_getter_(env_var_getter),
1067 glib_default_loop_(NULL), io_loop_(NULL) {
1068 // Figure out which GConfSettingGetterImpl to use, if any.
[email protected]6b0349ef2010-10-16 04:56:061069 switch (base::nix::GetDesktopEnvironment(env_var_getter)) {
1070 case base::nix::DESKTOP_ENVIRONMENT_GNOME:
[email protected]6de53d42010-11-09 07:33:191071#if defined(USE_GCONF)
[email protected]d7395e732009-08-28 23:13:431072 gconf_getter_.reset(new GConfSettingGetterImplGConf());
[email protected]6de53d42010-11-09 07:33:191073#endif
[email protected]d7395e732009-08-28 23:13:431074 break;
[email protected]6b0349ef2010-10-16 04:56:061075 case base::nix::DESKTOP_ENVIRONMENT_KDE3:
1076 case base::nix::DESKTOP_ENVIRONMENT_KDE4:
[email protected]d7395e732009-08-28 23:13:431077 gconf_getter_.reset(new GConfSettingGetterImplKDE(env_var_getter));
1078 break;
[email protected]6b0349ef2010-10-16 04:56:061079 case base::nix::DESKTOP_ENVIRONMENT_XFCE:
1080 case base::nix::DESKTOP_ENVIRONMENT_OTHER:
[email protected]d7395e732009-08-28 23:13:431081 break;
1082 }
1083}
1084
[email protected]76b90d312010-08-03 03:00:501085ProxyConfigServiceLinux::Delegate::Delegate(base::Environment* env_var_getter,
[email protected]861c6c62009-04-20 16:50:561086 GConfSettingGetter* gconf_getter)
[email protected]3e44697f2009-05-22 14:37:391087 : env_var_getter_(env_var_getter), gconf_getter_(gconf_getter),
1088 glib_default_loop_(NULL), io_loop_(NULL) {
[email protected]861c6c62009-04-20 16:50:561089}
1090
[email protected]3e44697f2009-05-22 14:37:391091void ProxyConfigServiceLinux::Delegate::SetupAndFetchInitialConfig(
[email protected]d7395e732009-08-28 23:13:431092 MessageLoop* glib_default_loop, MessageLoop* io_loop,
1093 MessageLoopForIO* file_loop) {
[email protected]3e44697f2009-05-22 14:37:391094 // We should be running on the default glib main loop thread right
1095 // now. gconf can only be accessed from this thread.
1096 DCHECK(MessageLoop::current() == glib_default_loop);
1097 glib_default_loop_ = glib_default_loop;
1098 io_loop_ = io_loop;
1099
[email protected]d7395e732009-08-28 23:13:431100 // If we are passed a NULL io_loop or file_loop, then we don't set up
1101 // proxy setting change notifications. This should not be the usual
1102 // case but is intended to simplify test setups.
1103 if (!io_loop_ || !file_loop)
[email protected]b30a3f52010-10-16 01:05:461104 VLOG(1) << "Monitoring of proxy setting changes is disabled";
[email protected]3e44697f2009-05-22 14:37:391105
1106 // Fetch and cache the current proxy config. The config is left in
[email protected]119655002010-07-23 06:02:401107 // cached_config_, where GetLatestProxyConfig() running on the IO thread
[email protected]3e44697f2009-05-22 14:37:391108 // will expect to find it. This is safe to do because we return
1109 // before this ProxyConfigServiceLinux is passed on to
1110 // the ProxyService.
[email protected]d6cb85b2009-07-23 22:10:531111
1112 // Note: It would be nice to prioritize environment variables
[email protected]92d2dc82010-04-08 17:49:591113 // and only fall back to gconf if env vars were unset. But
[email protected]d6cb85b2009-07-23 22:10:531114 // gnome-terminal "helpfully" sets http_proxy and no_proxy, and it
1115 // does so even if the proxy mode is set to auto, which would
1116 // mislead us.
1117
[email protected]3e44697f2009-05-22 14:37:391118 bool got_config = false;
[email protected]d7395e732009-08-28 23:13:431119 if (gconf_getter_.get()) {
1120 if (gconf_getter_->Init(glib_default_loop, file_loop) &&
1121 (!io_loop || !file_loop || gconf_getter_->SetupNotification(this))) {
1122 if (GetConfigFromGConf(&cached_config_)) {
1123 cached_config_.set_id(1); // mark it as valid
1124 got_config = true;
[email protected]b30a3f52010-10-16 01:05:461125 VLOG(1) << "Obtained proxy settings from "
1126 << gconf_getter_->GetDataSource();
[email protected]d7395e732009-08-28 23:13:431127 // If gconf proxy mode is "none", meaning direct, then we take
1128 // that to be a valid config and will not check environment
1129 // variables. The alternative would have been to look for a proxy
1130 // whereever we can find one.
1131 //
1132 // Keep a copy of the config for use from this thread for
1133 // comparison with updated settings when we get notifications.
1134 reference_config_ = cached_config_;
1135 reference_config_.set_id(1); // mark it as valid
1136 } else {
1137 gconf_getter_->Shutdown(); // Stop notifications
[email protected]d6cb85b2009-07-23 22:10:531138 }
[email protected]d7395e732009-08-28 23:13:431139 }
[email protected]861c6c62009-04-20 16:50:561140 }
[email protected]d6cb85b2009-07-23 22:10:531141
[email protected]3e44697f2009-05-22 14:37:391142 if (!got_config) {
[email protected]d6cb85b2009-07-23 22:10:531143 // We fall back on environment variables.
[email protected]3e44697f2009-05-22 14:37:391144 //
1145 // Consulting environment variables doesn't need to be done from
1146 // the default glib main loop, but it's a tiny enough amount of
1147 // work.
1148 if (GetConfigFromEnv(&cached_config_)) {
1149 cached_config_.set_id(1); // mark it as valid
[email protected]b30a3f52010-10-16 01:05:461150 VLOG(1) << "Obtained proxy settings from environment variables";
[email protected]3e44697f2009-05-22 14:37:391151 }
[email protected]861c6c62009-04-20 16:50:561152 }
[email protected]3e44697f2009-05-22 14:37:391153}
1154
[email protected]119655002010-07-23 06:02:401155void ProxyConfigServiceLinux::Delegate::AddObserver(Observer* observer) {
1156 observers_.AddObserver(observer);
1157}
1158
1159void ProxyConfigServiceLinux::Delegate::RemoveObserver(Observer* observer) {
1160 observers_.RemoveObserver(observer);
1161}
1162
1163bool ProxyConfigServiceLinux::Delegate::GetLatestProxyConfig(
1164 ProxyConfig* config) {
[email protected]3e44697f2009-05-22 14:37:391165 // This is called from the IO thread.
1166 DCHECK(!io_loop_ || MessageLoop::current() == io_loop_);
1167
1168 // Simply return the last proxy configuration that glib_default_loop
1169 // notified us of.
[email protected]119655002010-07-23 06:02:401170 *config = cached_config_.is_valid() ?
1171 cached_config_ : ProxyConfig::CreateDirect();
1172
1173 // We return true to indicate that *config was filled in. It is always
1174 // going to be available since we initialized eagerly on the UI thread.
1175 // TODO(eroman): do lazy initialization instead, so we no longer need
1176 // to construct ProxyConfigServiceLinux on the UI thread.
1177 // In which case, we may return false here.
1178 return true;
[email protected]3e44697f2009-05-22 14:37:391179}
1180
[email protected]d7395e732009-08-28 23:13:431181// Depending on the GConfSettingGetter in use, this method will be called
1182// on either the UI thread (GConf) or the file thread (KDE).
[email protected]3e44697f2009-05-22 14:37:391183void ProxyConfigServiceLinux::Delegate::OnCheckProxyConfigSettings() {
[email protected]d7395e732009-08-28 23:13:431184 MessageLoop* required_loop = gconf_getter_->GetNotificationLoop();
1185 DCHECK(!required_loop || MessageLoop::current() == required_loop);
[email protected]3e44697f2009-05-22 14:37:391186 ProxyConfig new_config;
1187 bool valid = GetConfigFromGConf(&new_config);
1188 if (valid)
1189 new_config.set_id(1); // mark it as valid
1190
[email protected]119655002010-07-23 06:02:401191 // See if it is different from what we had before.
[email protected]3e44697f2009-05-22 14:37:391192 if (new_config.is_valid() != reference_config_.is_valid() ||
1193 !new_config.Equals(reference_config_)) {
1194 // Post a task to |io_loop| with the new configuration, so it can
1195 // update |cached_config_|.
1196 io_loop_->PostTask(
1197 FROM_HERE,
1198 NewRunnableMethod(
1199 this,
1200 &ProxyConfigServiceLinux::Delegate::SetNewProxyConfig,
1201 new_config));
[email protected]d1f9d472009-08-13 19:59:301202 // Update the thread-private copy in |reference_config_| as well.
1203 reference_config_ = new_config;
[email protected]3e44697f2009-05-22 14:37:391204 }
1205}
1206
1207void ProxyConfigServiceLinux::Delegate::SetNewProxyConfig(
1208 const ProxyConfig& new_config) {
1209 DCHECK(MessageLoop::current() == io_loop_);
[email protected]b30a3f52010-10-16 01:05:461210 VLOG(1) << "Proxy configuration changed";
[email protected]3e44697f2009-05-22 14:37:391211 cached_config_ = new_config;
[email protected]119655002010-07-23 06:02:401212 FOR_EACH_OBSERVER(Observer, observers_, OnProxyConfigChanged(new_config));
[email protected]3e44697f2009-05-22 14:37:391213}
1214
1215void ProxyConfigServiceLinux::Delegate::PostDestroyTask() {
[email protected]d7395e732009-08-28 23:13:431216 if (!gconf_getter_.get())
1217 return;
1218 MessageLoop* shutdown_loop = gconf_getter_->GetNotificationLoop();
1219 if (!shutdown_loop || MessageLoop::current() == shutdown_loop) {
[email protected]3e44697f2009-05-22 14:37:391220 // Already on the right thread, call directly.
1221 // This is the case for the unittests.
1222 OnDestroy();
1223 } else {
[email protected]d7395e732009-08-28 23:13:431224 // Post to shutdown thread. Note that on browser shutdown, we may quit
1225 // this MessageLoop and exit the program before ever running this.
1226 shutdown_loop->PostTask(
[email protected]3e44697f2009-05-22 14:37:391227 FROM_HERE,
1228 NewRunnableMethod(
1229 this,
1230 &ProxyConfigServiceLinux::Delegate::OnDestroy));
1231 }
1232}
1233void ProxyConfigServiceLinux::Delegate::OnDestroy() {
[email protected]d7395e732009-08-28 23:13:431234 MessageLoop* shutdown_loop = gconf_getter_->GetNotificationLoop();
1235 DCHECK(!shutdown_loop || MessageLoop::current() == shutdown_loop);
1236 gconf_getter_->Shutdown();
[email protected]3e44697f2009-05-22 14:37:391237}
1238
1239ProxyConfigServiceLinux::ProxyConfigServiceLinux()
[email protected]76b90d312010-08-03 03:00:501240 : delegate_(new Delegate(base::Environment::Create())) {
[email protected]3e44697f2009-05-22 14:37:391241}
1242
[email protected]8e1845e12010-09-15 19:22:241243ProxyConfigServiceLinux::~ProxyConfigServiceLinux() {
1244 delegate_->PostDestroyTask();
1245}
1246
[email protected]3e44697f2009-05-22 14:37:391247ProxyConfigServiceLinux::ProxyConfigServiceLinux(
[email protected]76b90d312010-08-03 03:00:501248 base::Environment* env_var_getter)
[email protected]9a3d8d42009-09-03 17:01:461249 : delegate_(new Delegate(env_var_getter)) {
1250}
1251
1252ProxyConfigServiceLinux::ProxyConfigServiceLinux(
[email protected]76b90d312010-08-03 03:00:501253 base::Environment* env_var_getter,
[email protected]3e44697f2009-05-22 14:37:391254 GConfSettingGetter* gconf_getter)
1255 : delegate_(new Delegate(env_var_getter, gconf_getter)) {
[email protected]861c6c62009-04-20 16:50:561256}
1257
1258} // namespace net