blob: ae7d5019c5a28bdc9c190785aeb1559182605f2e [file] [log] [blame]
[email protected]3a29593d2011-04-11 10:07:521// Copyright (c) 2011 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]3a29593d2011-04-11 10:07:5225#include "base/nix/xdg_util.h"
[email protected]528c56d2010-07-30 19:28:4426#include "base/string_number_conversions.h"
[email protected]861c6c62009-04-20 16:50:5627#include "base/string_tokenizer.h"
28#include "base/string_util.h"
[email protected]3e44697f2009-05-22 14:37:3929#include "base/task.h"
[email protected]9a8c4022011-01-25 14:25:3330#include "base/threading/thread_restrictions.h"
[email protected]d7395e732009-08-28 23:13:4331#include "base/timer.h"
[email protected]861c6c62009-04-20 16:50:5632#include "googleurl/src/url_canon.h"
33#include "net/base/net_errors.h"
34#include "net/http/http_util.h"
35#include "net/proxy/proxy_config.h"
36#include "net/proxy/proxy_server.h"
37
38namespace net {
39
40namespace {
41
[email protected]861c6c62009-04-20 16:50:5642// Given a proxy hostname from a setting, returns that hostname with
43// an appropriate proxy server scheme prefix.
44// scheme indicates the desired proxy scheme: usually http, with
45// socks 4 or 5 as special cases.
[email protected]87a102b2009-07-14 05:23:3046// TODO(arindam): Remove URI string manipulation by using MapUrlSchemeToProxy.
[email protected]861c6c62009-04-20 16:50:5647std::string FixupProxyHostScheme(ProxyServer::Scheme scheme,
48 std::string host) {
[email protected]e8c50812010-09-28 00:16:1749 if (scheme == ProxyServer::SCHEME_SOCKS5 &&
50 StartsWithASCII(host, "socks4://", false)) {
51 // We default to socks 5, but if the user specifically set it to
52 // socks4://, then use that.
53 scheme = ProxyServer::SCHEME_SOCKS4;
[email protected]861c6c62009-04-20 16:50:5654 }
55 // Strip the scheme if any.
56 std::string::size_type colon = host.find("://");
57 if (colon != std::string::npos)
58 host = host.substr(colon + 3);
59 // If a username and perhaps password are specified, give a warning.
60 std::string::size_type at_sign = host.find("@");
61 // Should this be supported?
62 if (at_sign != std::string::npos) {
[email protected]62749f182009-07-15 13:16:5463 // ProxyConfig does not support authentication parameters, but Chrome
64 // will prompt for the password later. Disregard the
65 // authentication parameters and continue with this hostname.
66 LOG(WARNING) << "Proxy authentication parameters ignored, see bug 16709";
[email protected]861c6c62009-04-20 16:50:5667 host = host.substr(at_sign + 1);
68 }
69 // If this is a socks proxy, prepend a scheme so as to tell
70 // ProxyServer. This also allows ProxyServer to choose the right
71 // default port.
72 if (scheme == ProxyServer::SCHEME_SOCKS4)
73 host = "socks4://" + host;
74 else if (scheme == ProxyServer::SCHEME_SOCKS5)
75 host = "socks5://" + host;
[email protected]d7395e732009-08-28 23:13:4376 // If there is a trailing slash, remove it so |host| will parse correctly
77 // even if it includes a port number (since the slash is not numeric).
78 if (host.length() && host[host.length() - 1] == '/')
79 host.resize(host.length() - 1);
[email protected]861c6c62009-04-20 16:50:5680 return host;
81}
82
83} // namespace
84
[email protected]8e1845e12010-09-15 19:22:2485ProxyConfigServiceLinux::Delegate::~Delegate() {
86}
87
[email protected]3e44697f2009-05-22 14:37:3988bool ProxyConfigServiceLinux::Delegate::GetProxyFromEnvVarForScheme(
[email protected]861c6c62009-04-20 16:50:5689 const char* variable, ProxyServer::Scheme scheme,
90 ProxyServer* result_server) {
91 std::string env_value;
[email protected]3ba7e082010-08-07 02:57:5992 if (env_var_getter_->GetVar(variable, &env_value)) {
[email protected]861c6c62009-04-20 16:50:5693 if (!env_value.empty()) {
94 env_value = FixupProxyHostScheme(scheme, env_value);
[email protected]87a102b2009-07-14 05:23:3095 ProxyServer proxy_server =
96 ProxyServer::FromURI(env_value, ProxyServer::SCHEME_HTTP);
[email protected]861c6c62009-04-20 16:50:5697 if (proxy_server.is_valid() && !proxy_server.is_direct()) {
98 *result_server = proxy_server;
99 return true;
100 } else {
[email protected]3e44697f2009-05-22 14:37:39101 LOG(ERROR) << "Failed to parse environment variable " << variable;
[email protected]861c6c62009-04-20 16:50:56102 }
103 }
104 }
105 return false;
106}
107
[email protected]3e44697f2009-05-22 14:37:39108bool ProxyConfigServiceLinux::Delegate::GetProxyFromEnvVar(
[email protected]861c6c62009-04-20 16:50:56109 const char* variable, ProxyServer* result_server) {
110 return GetProxyFromEnvVarForScheme(variable, ProxyServer::SCHEME_HTTP,
111 result_server);
112}
113
[email protected]3e44697f2009-05-22 14:37:39114bool ProxyConfigServiceLinux::Delegate::GetConfigFromEnv(ProxyConfig* config) {
[email protected]861c6c62009-04-20 16:50:56115 // Check for automatic configuration first, in
116 // "auto_proxy". Possibly only the "environment_proxy" firefox
117 // extension has ever used this, but it still sounds like a good
118 // idea.
119 std::string auto_proxy;
[email protected]3ba7e082010-08-07 02:57:59120 if (env_var_getter_->GetVar("auto_proxy", &auto_proxy)) {
[email protected]861c6c62009-04-20 16:50:56121 if (auto_proxy.empty()) {
122 // Defined and empty => autodetect
[email protected]ed4ed0f2010-02-24 00:20:48123 config->set_auto_detect(true);
[email protected]861c6c62009-04-20 16:50:56124 } else {
125 // specified autoconfig URL
[email protected]ed4ed0f2010-02-24 00:20:48126 config->set_pac_url(GURL(auto_proxy));
[email protected]861c6c62009-04-20 16:50:56127 }
128 return true;
129 }
130 // "all_proxy" is a shortcut to avoid defining {http,https,ftp}_proxy.
131 ProxyServer proxy_server;
132 if (GetProxyFromEnvVar("all_proxy", &proxy_server)) {
[email protected]ed4ed0f2010-02-24 00:20:48133 config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
134 config->proxy_rules().single_proxy = proxy_server;
[email protected]861c6c62009-04-20 16:50:56135 } else {
136 bool have_http = GetProxyFromEnvVar("http_proxy", &proxy_server);
137 if (have_http)
[email protected]ed4ed0f2010-02-24 00:20:48138 config->proxy_rules().proxy_for_http = proxy_server;
[email protected]861c6c62009-04-20 16:50:56139 // It would be tempting to let http_proxy apply for all protocols
140 // if https_proxy and ftp_proxy are not defined. Googling turns up
141 // several documents that mention only http_proxy. But then the
142 // user really might not want to proxy https. And it doesn't seem
143 // like other apps do this. So we will refrain.
144 bool have_https = GetProxyFromEnvVar("https_proxy", &proxy_server);
145 if (have_https)
[email protected]ed4ed0f2010-02-24 00:20:48146 config->proxy_rules().proxy_for_https = proxy_server;
[email protected]861c6c62009-04-20 16:50:56147 bool have_ftp = GetProxyFromEnvVar("ftp_proxy", &proxy_server);
148 if (have_ftp)
[email protected]ed4ed0f2010-02-24 00:20:48149 config->proxy_rules().proxy_for_ftp = proxy_server;
[email protected]861c6c62009-04-20 16:50:56150 if (have_http || have_https || have_ftp) {
151 // mustn't change type unless some rules are actually set.
[email protected]ed4ed0f2010-02-24 00:20:48152 config->proxy_rules().type =
153 ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
[email protected]861c6c62009-04-20 16:50:56154 }
155 }
[email protected]ed4ed0f2010-02-24 00:20:48156 if (config->proxy_rules().empty()) {
[email protected]861c6c62009-04-20 16:50:56157 // If the above were not defined, try for socks.
[email protected]e8c50812010-09-28 00:16:17158 // For environment variables, we default to version 5, per the gnome
159 // documentation: http://library.gnome.org/devel/gnet/stable/gnet-socks.html
160 ProxyServer::Scheme scheme = ProxyServer::SCHEME_SOCKS5;
[email protected]861c6c62009-04-20 16:50:56161 std::string env_version;
[email protected]3ba7e082010-08-07 02:57:59162 if (env_var_getter_->GetVar("SOCKS_VERSION", &env_version)
[email protected]e8c50812010-09-28 00:16:17163 && env_version == "4")
164 scheme = ProxyServer::SCHEME_SOCKS4;
[email protected]861c6c62009-04-20 16:50:56165 if (GetProxyFromEnvVarForScheme("SOCKS_SERVER", scheme, &proxy_server)) {
[email protected]ed4ed0f2010-02-24 00:20:48166 config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
167 config->proxy_rules().single_proxy = proxy_server;
[email protected]861c6c62009-04-20 16:50:56168 }
169 }
170 // Look for the proxy bypass list.
171 std::string no_proxy;
[email protected]3ba7e082010-08-07 02:57:59172 env_var_getter_->GetVar("no_proxy", &no_proxy);
[email protected]ed4ed0f2010-02-24 00:20:48173 if (config->proxy_rules().empty()) {
[email protected]861c6c62009-04-20 16:50:56174 // Having only "no_proxy" set, presumably to "*", makes it
175 // explicit that env vars do specify a configuration: having no
176 // rules specified only means the user explicitly asks for direct
177 // connections.
178 return !no_proxy.empty();
179 }
[email protected]7541206c2010-02-19 20:24:06180 // Note that this uses "suffix" matching. So a bypass of "google.com"
181 // is understood to mean a bypass of "*google.com".
[email protected]ed4ed0f2010-02-24 00:20:48182 config->proxy_rules().bypass_rules.ParseFromStringUsingSuffixMatching(
183 no_proxy);
[email protected]861c6c62009-04-20 16:50:56184 return true;
185}
186
187namespace {
188
[email protected]d7395e732009-08-28 23:13:43189const int kDebounceTimeoutMilliseconds = 250;
[email protected]3e44697f2009-05-22 14:37:39190
[email protected]6de53d42010-11-09 07:33:19191#if defined(USE_GCONF)
[email protected]d7395e732009-08-28 23:13:43192// This is the "real" gconf version that actually uses gconf.
193class GConfSettingGetterImplGConf
[email protected]861c6c62009-04-20 16:50:56194 : public ProxyConfigServiceLinux::GConfSettingGetter {
195 public:
[email protected]d7395e732009-08-28 23:13:43196 GConfSettingGetterImplGConf()
197 : client_(NULL), notify_delegate_(NULL), loop_(NULL) {}
[email protected]3e44697f2009-05-22 14:37:39198
[email protected]d7395e732009-08-28 23:13:43199 virtual ~GConfSettingGetterImplGConf() {
[email protected]3e44697f2009-05-22 14:37:39200 // client_ should have been released before now, from
[email protected]f5b13442009-07-13 15:23:59201 // Delegate::OnDestroy(), while running on the UI thread. However
202 // on exiting the process, it may happen that
203 // Delegate::OnDestroy() task is left pending on the glib loop
204 // after the loop was quit, and pending tasks may then be deleted
205 // without being run.
206 if (client_) {
207 // gconf client was not cleaned up.
208 if (MessageLoop::current() == loop_) {
209 // We are on the UI thread so we can clean it safely. This is
210 // the case at least for ui_tests running under Valgrind in
211 // bug 16076.
[email protected]b30a3f52010-10-16 01:05:46212 VLOG(1) << "~GConfSettingGetterImplGConf: releasing gconf client";
[email protected]d7395e732009-08-28 23:13:43213 Shutdown();
[email protected]f5b13442009-07-13 15:23:59214 } else {
[email protected]d7395e732009-08-28 23:13:43215 LOG(WARNING) << "~GConfSettingGetterImplGConf: leaking gconf client";
[email protected]f5b13442009-07-13 15:23:59216 client_ = NULL;
217 }
218 }
[email protected]3e44697f2009-05-22 14:37:39219 DCHECK(!client_);
[email protected]861c6c62009-04-20 16:50:56220 }
221
[email protected]d7395e732009-08-28 23:13:43222 virtual bool Init(MessageLoop* glib_default_loop,
223 MessageLoopForIO* file_loop) {
224 DCHECK(MessageLoop::current() == glib_default_loop);
[email protected]3e44697f2009-05-22 14:37:39225 DCHECK(!client_);
226 DCHECK(!loop_);
[email protected]d7395e732009-08-28 23:13:43227 loop_ = glib_default_loop;
[email protected]3e44697f2009-05-22 14:37:39228 client_ = gconf_client_get_default();
[email protected]861c6c62009-04-20 16:50:56229 if (!client_) {
[email protected]861c6c62009-04-20 16:50:56230 // It's not clear whether/when this can return NULL.
[email protected]3e44697f2009-05-22 14:37:39231 LOG(ERROR) << "Unable to create a gconf client";
232 loop_ = NULL;
233 return false;
[email protected]861c6c62009-04-20 16:50:56234 }
[email protected]3e44697f2009-05-22 14:37:39235 GError* error = NULL;
236 // We need to add the directories for which we'll be asking
237 // notifications, and we might as well ask to preload them.
238 gconf_client_add_dir(client_, "/system/proxy",
239 GCONF_CLIENT_PRELOAD_ONELEVEL, &error);
240 if (error == NULL) {
241 gconf_client_add_dir(client_, "/system/http_proxy",
242 GCONF_CLIENT_PRELOAD_ONELEVEL, &error);
243 }
244 if (error != NULL) {
245 LOG(ERROR) << "Error requesting gconf directory: " << error->message;
246 g_error_free(error);
[email protected]d7395e732009-08-28 23:13:43247 Shutdown();
[email protected]3e44697f2009-05-22 14:37:39248 return false;
249 }
250 return true;
251 }
252
[email protected]d7395e732009-08-28 23:13:43253 void Shutdown() {
[email protected]3e44697f2009-05-22 14:37:39254 if (client_) {
255 DCHECK(MessageLoop::current() == loop_);
256 // This also disables gconf notifications.
257 g_object_unref(client_);
258 client_ = NULL;
259 loop_ = NULL;
260 }
261 }
262
[email protected]d7395e732009-08-28 23:13:43263 bool SetupNotification(ProxyConfigServiceLinux::Delegate* delegate) {
[email protected]3e44697f2009-05-22 14:37:39264 DCHECK(client_);
265 DCHECK(MessageLoop::current() == loop_);
266 GError* error = NULL;
[email protected]d7395e732009-08-28 23:13:43267 notify_delegate_ = delegate;
[email protected]3e44697f2009-05-22 14:37:39268 gconf_client_notify_add(
269 client_, "/system/proxy",
[email protected]d7395e732009-08-28 23:13:43270 OnGConfChangeNotification, this,
[email protected]3e44697f2009-05-22 14:37:39271 NULL, &error);
272 if (error == NULL) {
273 gconf_client_notify_add(
274 client_, "/system/http_proxy",
[email protected]d7395e732009-08-28 23:13:43275 OnGConfChangeNotification, this,
[email protected]3e44697f2009-05-22 14:37:39276 NULL, &error);
277 }
278 if (error != NULL) {
279 LOG(ERROR) << "Error requesting gconf notifications: " << error->message;
280 g_error_free(error);
[email protected]d7395e732009-08-28 23:13:43281 Shutdown();
[email protected]3e44697f2009-05-22 14:37:39282 return false;
283 }
284 return true;
[email protected]861c6c62009-04-20 16:50:56285 }
286
[email protected]9a3d8d42009-09-03 17:01:46287 virtual MessageLoop* GetNotificationLoop() {
[email protected]d7395e732009-08-28 23:13:43288 return loop_;
289 }
290
291 virtual const char* GetDataSource() {
292 return "gconf";
293 }
294
[email protected]861c6c62009-04-20 16:50:56295 virtual bool GetString(const char* key, std::string* result) {
[email protected]3e44697f2009-05-22 14:37:39296 DCHECK(client_);
297 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56298 GError* error = NULL;
299 gchar* value = gconf_client_get_string(client_, key, &error);
300 if (HandleGError(error, key))
301 return false;
302 if (!value)
303 return false;
304 *result = value;
305 g_free(value);
306 return true;
307 }
308 virtual bool GetBoolean(const char* key, bool* result) {
[email protected]3e44697f2009-05-22 14:37:39309 DCHECK(client_);
310 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56311 GError* error = NULL;
312 // We want to distinguish unset values from values defaulting to
313 // false. For that we need to use the type-generic
314 // gconf_client_get() rather than gconf_client_get_bool().
315 GConfValue* gconf_value = gconf_client_get(client_, key, &error);
316 if (HandleGError(error, key))
317 return false;
318 if (!gconf_value) {
319 // Unset.
320 return false;
321 }
322 if (gconf_value->type != GCONF_VALUE_BOOL) {
323 gconf_value_free(gconf_value);
324 return false;
325 }
326 gboolean bool_value = gconf_value_get_bool(gconf_value);
327 *result = static_cast<bool>(bool_value);
328 gconf_value_free(gconf_value);
329 return true;
330 }
331 virtual bool GetInt(const char* key, int* result) {
[email protected]3e44697f2009-05-22 14:37:39332 DCHECK(client_);
333 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56334 GError* error = NULL;
335 int value = gconf_client_get_int(client_, key, &error);
336 if (HandleGError(error, key))
337 return false;
338 // We don't bother to distinguish an unset value because callers
339 // don't care. 0 is returned if unset.
340 *result = value;
341 return true;
342 }
343 virtual bool GetStringList(const char* key,
344 std::vector<std::string>* result) {
[email protected]3e44697f2009-05-22 14:37:39345 DCHECK(client_);
346 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56347 GError* error = NULL;
348 GSList* list = gconf_client_get_list(client_, key,
349 GCONF_VALUE_STRING, &error);
350 if (HandleGError(error, key))
351 return false;
352 if (!list) {
353 // unset
354 return false;
355 }
356 for (GSList *it = list; it; it = it->next) {
357 result->push_back(static_cast<char*>(it->data));
358 g_free(it->data);
359 }
360 g_slist_free(list);
361 return true;
362 }
363
[email protected]a48bf4a2010-06-14 18:24:53364 virtual bool BypassListIsReversed() {
365 // This is a KDE-specific setting.
366 return false;
367 }
368
[email protected]1a597192010-07-09 16:58:38369 virtual bool MatchHostsUsingSuffixMatching() {
370 return false;
371 }
372
[email protected]861c6c62009-04-20 16:50:56373 private:
374 // Logs and frees a glib error. Returns false if there was no error
375 // (error is NULL).
376 bool HandleGError(GError* error, const char* key) {
377 if (error != NULL) {
[email protected]3e44697f2009-05-22 14:37:39378 LOG(ERROR) << "Error getting gconf value for " << key
379 << ": " << error->message;
[email protected]861c6c62009-04-20 16:50:56380 g_error_free(error);
381 return true;
382 }
383 return false;
384 }
385
[email protected]d7395e732009-08-28 23:13:43386 // This is the callback from the debounce timer.
387 void OnDebouncedNotification() {
388 DCHECK(MessageLoop::current() == loop_);
[email protected]961ac942011-04-28 18:18:14389 CHECK(notify_delegate_);
[email protected]d7395e732009-08-28 23:13:43390 // Forward to a method on the proxy config service delegate object.
391 notify_delegate_->OnCheckProxyConfigSettings();
392 }
393
394 void OnChangeNotification() {
395 // We don't use Reset() because the timer may not yet be running.
396 // (In that case Stop() is a no-op.)
397 debounce_timer_.Stop();
398 debounce_timer_.Start(base::TimeDelta::FromMilliseconds(
399 kDebounceTimeoutMilliseconds), this,
400 &GConfSettingGetterImplGConf::OnDebouncedNotification);
401 }
402
403 // gconf notification callback, dispatched from the default glib main loop.
404 static void OnGConfChangeNotification(
405 GConfClient* client, guint cnxn_id,
406 GConfEntry* entry, gpointer user_data) {
[email protected]b30a3f52010-10-16 01:05:46407 VLOG(1) << "gconf change notification for key "
408 << gconf_entry_get_key(entry);
[email protected]d7395e732009-08-28 23:13:43409 // We don't track which key has changed, just that something did change.
410 GConfSettingGetterImplGConf* setting_getter =
411 reinterpret_cast<GConfSettingGetterImplGConf*>(user_data);
412 setting_getter->OnChangeNotification();
413 }
414
[email protected]861c6c62009-04-20 16:50:56415 GConfClient* client_;
[email protected]d7395e732009-08-28 23:13:43416 ProxyConfigServiceLinux::Delegate* notify_delegate_;
417 base::OneShotTimer<GConfSettingGetterImplGConf> debounce_timer_;
[email protected]861c6c62009-04-20 16:50:56418
[email protected]3e44697f2009-05-22 14:37:39419 // Message loop of the thread that we make gconf calls on. It should
420 // be the UI thread and all our methods should be called on this
421 // thread. Only for assertions.
422 MessageLoop* loop_;
423
[email protected]d7395e732009-08-28 23:13:43424 DISALLOW_COPY_AND_ASSIGN(GConfSettingGetterImplGConf);
425};
[email protected]6de53d42010-11-09 07:33:19426#endif // defined(USE_GCONF)
[email protected]d7395e732009-08-28 23:13:43427
428// This is the KDE version that reads kioslaverc and simulates gconf.
429// Doing this allows the main Delegate code, as well as the unit tests
430// for it, to stay the same - and the settings map fairly well besides.
431class GConfSettingGetterImplKDE
432 : public ProxyConfigServiceLinux::GConfSettingGetter,
433 public base::MessagePumpLibevent::Watcher {
434 public:
[email protected]76b90d312010-08-03 03:00:50435 explicit GConfSettingGetterImplKDE(base::Environment* env_var_getter)
[email protected]d7395e732009-08-28 23:13:43436 : inotify_fd_(-1), notify_delegate_(NULL), indirect_manual_(false),
[email protected]a48bf4a2010-06-14 18:24:53437 auto_no_pac_(false), reversed_bypass_list_(false),
[email protected]f18fde22010-05-18 23:49:54438 env_var_getter_(env_var_getter), file_loop_(NULL) {
[email protected]9a8c4022011-01-25 14:25:33439 // This has to be called on the UI thread (http://crbug.com/69057).
440 base::ThreadRestrictions::ScopedAllowIO allow_io;
441
[email protected]f18fde22010-05-18 23:49:54442 // Derive the location of the kde config dir from the environment.
[email protected]92d2dc82010-04-08 17:49:59443 std::string home;
[email protected]3ba7e082010-08-07 02:57:59444 if (env_var_getter->GetVar("KDEHOME", &home) && !home.empty()) {
[email protected]2e8cfe22010-06-12 00:26:24445 // $KDEHOME is set. Use it unconditionally.
[email protected]92d2dc82010-04-08 17:49:59446 kde_config_dir_ = KDEHomeToConfigPath(FilePath(home));
447 } else {
[email protected]2e8cfe22010-06-12 00:26:24448 // $KDEHOME is unset. Try to figure out what to use. This seems to be
[email protected]92d2dc82010-04-08 17:49:59449 // the common case on most distributions.
[email protected]3ba7e082010-08-07 02:57:59450 if (!env_var_getter->GetVar(base::env_vars::kHome, &home))
[email protected]d7395e732009-08-28 23:13:43451 // User has no $HOME? Give up. Later we'll report the failure.
452 return;
[email protected]6b0349ef2010-10-16 04:56:06453 if (base::nix::GetDesktopEnvironment(env_var_getter) ==
454 base::nix::DESKTOP_ENVIRONMENT_KDE3) {
[email protected]92d2dc82010-04-08 17:49:59455 // KDE3 always uses .kde for its configuration.
456 FilePath kde_path = FilePath(home).Append(".kde");
457 kde_config_dir_ = KDEHomeToConfigPath(kde_path);
458 } else {
459 // Some distributions patch KDE4 to use .kde4 instead of .kde, so that
[email protected]fad9c8a52010-06-10 22:30:53460 // both can be installed side-by-side. Sadly they don't all do this, and
461 // they don't always do this: some distributions have started switching
462 // back as well. So if there is a .kde4 directory, check the timestamps
463 // of the config directories within and use the newest one.
[email protected]92d2dc82010-04-08 17:49:59464 // Note that we should currently be running in the UI thread, because in
465 // the gconf version, that is the only thread that can access the proxy
466 // settings (a gconf restriction). As noted below, the initial read of
467 // the proxy settings will be done in this thread anyway, so we check
468 // for .kde4 here in this thread as well.
[email protected]fad9c8a52010-06-10 22:30:53469 FilePath kde3_path = FilePath(home).Append(".kde");
470 FilePath kde3_config = KDEHomeToConfigPath(kde3_path);
[email protected]92d2dc82010-04-08 17:49:59471 FilePath kde4_path = FilePath(home).Append(".kde4");
[email protected]fad9c8a52010-06-10 22:30:53472 FilePath kde4_config = KDEHomeToConfigPath(kde4_path);
473 bool use_kde4 = false;
[email protected]92d2dc82010-04-08 17:49:59474 if (file_util::DirectoryExists(kde4_path)) {
[email protected]2f0193c22010-09-03 02:28:37475 base::PlatformFileInfo kde3_info;
476 base::PlatformFileInfo kde4_info;
[email protected]fad9c8a52010-06-10 22:30:53477 if (file_util::GetFileInfo(kde4_config, &kde4_info)) {
478 if (file_util::GetFileInfo(kde3_config, &kde3_info)) {
479 use_kde4 = kde4_info.last_modified >= kde3_info.last_modified;
480 } else {
481 use_kde4 = true;
482 }
483 }
484 }
485 if (use_kde4) {
[email protected]92d2dc82010-04-08 17:49:59486 kde_config_dir_ = KDEHomeToConfigPath(kde4_path);
487 } else {
[email protected]fad9c8a52010-06-10 22:30:53488 kde_config_dir_ = KDEHomeToConfigPath(kde3_path);
[email protected]92d2dc82010-04-08 17:49:59489 }
490 }
[email protected]d7395e732009-08-28 23:13:43491 }
[email protected]d7395e732009-08-28 23:13:43492 }
493
494 virtual ~GConfSettingGetterImplKDE() {
495 // inotify_fd_ should have been closed before now, from
496 // Delegate::OnDestroy(), while running on the file thread. However
497 // on exiting the process, it may happen that Delegate::OnDestroy()
498 // task is left pending on the file loop after the loop was quit,
499 // and pending tasks may then be deleted without being run.
500 // Here in the KDE version, we can safely close the file descriptor
501 // anyway. (Not that it really matters; the process is exiting.)
502 if (inotify_fd_ >= 0)
503 Shutdown();
504 DCHECK(inotify_fd_ < 0);
505 }
506
507 virtual bool Init(MessageLoop* glib_default_loop,
508 MessageLoopForIO* file_loop) {
[email protected]9a8c4022011-01-25 14:25:33509 // This has to be called on the UI thread (http://crbug.com/69057).
510 base::ThreadRestrictions::ScopedAllowIO allow_io;
[email protected]d7395e732009-08-28 23:13:43511 DCHECK(inotify_fd_ < 0);
512 inotify_fd_ = inotify_init();
513 if (inotify_fd_ < 0) {
[email protected]57b765672009-10-13 18:27:40514 PLOG(ERROR) << "inotify_init failed";
[email protected]d7395e732009-08-28 23:13:43515 return false;
516 }
517 int flags = fcntl(inotify_fd_, F_GETFL);
518 if (fcntl(inotify_fd_, F_SETFL, flags | O_NONBLOCK) < 0) {
[email protected]57b765672009-10-13 18:27:40519 PLOG(ERROR) << "fcntl failed";
[email protected]d7395e732009-08-28 23:13:43520 close(inotify_fd_);
521 inotify_fd_ = -1;
522 return false;
523 }
524 file_loop_ = file_loop;
525 // The initial read is done on the current thread, not |file_loop_|,
526 // since we will need to have it for SetupAndFetchInitialConfig().
527 UpdateCachedSettings();
528 return true;
529 }
530
531 void Shutdown() {
532 if (inotify_fd_ >= 0) {
533 ResetCachedSettings();
534 inotify_watcher_.StopWatchingFileDescriptor();
535 close(inotify_fd_);
536 inotify_fd_ = -1;
537 }
538 }
539
540 bool SetupNotification(ProxyConfigServiceLinux::Delegate* delegate) {
541 DCHECK(inotify_fd_ >= 0);
542 DCHECK(file_loop_);
543 // We can't just watch the kioslaverc file directly, since KDE will write
544 // a new copy of it and then rename it whenever settings are changed and
545 // inotify watches inodes (so we'll be watching the old deleted file after
546 // the first change, and it will never change again). So, we watch the
547 // directory instead. We then act only on changes to the kioslaverc entry.
548 if (inotify_add_watch(inotify_fd_, kde_config_dir_.value().c_str(),
549 IN_MODIFY | IN_MOVED_TO) < 0)
550 return false;
551 notify_delegate_ = delegate;
552 return file_loop_->WatchFileDescriptor(inotify_fd_, true,
553 MessageLoopForIO::WATCH_READ, &inotify_watcher_, this);
554 }
555
[email protected]9a3d8d42009-09-03 17:01:46556 virtual MessageLoop* GetNotificationLoop() {
[email protected]d7395e732009-08-28 23:13:43557 return file_loop_;
558 }
559
560 // Implement base::MessagePumpLibevent::Delegate.
561 void OnFileCanReadWithoutBlocking(int fd) {
562 DCHECK(fd == inotify_fd_);
563 DCHECK(MessageLoop::current() == file_loop_);
564 OnChangeNotification();
565 }
566 void OnFileCanWriteWithoutBlocking(int fd) {
567 NOTREACHED();
568 }
569
570 virtual const char* GetDataSource() {
571 return "KDE";
572 }
573
574 virtual bool GetString(const char* key, std::string* result) {
575 string_map_type::iterator it = string_table_.find(key);
576 if (it == string_table_.end())
577 return false;
578 *result = it->second;
579 return true;
580 }
581 virtual bool GetBoolean(const char* key, bool* result) {
582 // We don't ever have any booleans.
583 return false;
584 }
585 virtual bool GetInt(const char* key, int* result) {
586 // We don't ever have any integers. (See AddProxy() below about ports.)
587 return false;
588 }
589 virtual bool GetStringList(const char* key,
590 std::vector<std::string>* result) {
591 strings_map_type::iterator it = strings_table_.find(key);
592 if (it == strings_table_.end())
593 return false;
594 *result = it->second;
595 return true;
596 }
597
[email protected]a48bf4a2010-06-14 18:24:53598 virtual bool BypassListIsReversed() {
599 return reversed_bypass_list_;
600 }
601
[email protected]1a597192010-07-09 16:58:38602 virtual bool MatchHostsUsingSuffixMatching() {
603 return true;
604 }
605
[email protected]d7395e732009-08-28 23:13:43606 private:
607 void ResetCachedSettings() {
608 string_table_.clear();
609 strings_table_.clear();
610 indirect_manual_ = false;
611 auto_no_pac_ = false;
[email protected]a48bf4a2010-06-14 18:24:53612 reversed_bypass_list_ = false;
[email protected]d7395e732009-08-28 23:13:43613 }
614
[email protected]92d2dc82010-04-08 17:49:59615 FilePath KDEHomeToConfigPath(const FilePath& kde_home) {
616 return kde_home.Append("share").Append("config");
617 }
618
[email protected]a8185d02010-06-11 00:19:50619 void AddProxy(const std::string& prefix, const std::string& value) {
[email protected]d7395e732009-08-28 23:13:43620 if (value.empty() || value.substr(0, 3) == "//:")
621 // No proxy.
622 return;
623 // We don't need to parse the port number out; GetProxyFromGConf()
624 // would only append it right back again. So we just leave the port
625 // number right in the host string.
626 string_table_[prefix + "host"] = value;
627 }
628
[email protected]a8185d02010-06-11 00:19:50629 void AddHostList(const std::string& key, const std::string& value) {
[email protected]f18fde22010-05-18 23:49:54630 std::vector<std::string> tokens;
[email protected]1a597192010-07-09 16:58:38631 StringTokenizer tk(value, ", ");
[email protected]f18fde22010-05-18 23:49:54632 while (tk.GetNext()) {
633 std::string token = tk.token();
634 if (!token.empty())
635 tokens.push_back(token);
636 }
637 strings_table_[key] = tokens;
638 }
639
[email protected]9a3d8d42009-09-03 17:01:46640 void AddKDESetting(const std::string& key, const std::string& value) {
[email protected]d7395e732009-08-28 23:13:43641 // The astute reader may notice that there is no mention of SOCKS
642 // here. That's because KDE handles socks is a strange way, and we
643 // don't support it. Rather than just a setting for the SOCKS server,
644 // it has a setting for a library to LD_PRELOAD in all your programs
645 // that will transparently SOCKSify them. Such libraries each have
646 // their own configuration, and thus, we can't get it from KDE.
647 if (key == "ProxyType") {
648 const char* mode = "none";
649 indirect_manual_ = false;
650 auto_no_pac_ = false;
[email protected]e83326f2010-07-31 17:29:25651 int int_value;
652 base::StringToInt(value, &int_value);
653 switch (int_value) {
[email protected]d7395e732009-08-28 23:13:43654 case 0: // No proxy, or maybe kioslaverc syntax error.
655 break;
656 case 1: // Manual configuration.
657 mode = "manual";
658 break;
659 case 2: // PAC URL.
660 mode = "auto";
661 break;
662 case 3: // WPAD.
663 mode = "auto";
664 auto_no_pac_ = true;
665 break;
666 case 4: // Indirect manual via environment variables.
667 mode = "manual";
668 indirect_manual_ = true;
669 break;
670 }
671 string_table_["/system/proxy/mode"] = mode;
672 } else if (key == "Proxy Config Script") {
673 string_table_["/system/proxy/autoconfig_url"] = value;
674 } else if (key == "httpProxy") {
675 AddProxy("/system/http_proxy/", value);
676 } else if (key == "httpsProxy") {
677 AddProxy("/system/proxy/secure_", value);
678 } else if (key == "ftpProxy") {
679 AddProxy("/system/proxy/ftp_", value);
680 } else if (key == "ReversedException") {
681 // We count "true" or any nonzero number as true, otherwise false.
682 // Note that if the value is not actually numeric StringToInt()
683 // will return 0, which we count as false.
[email protected]e83326f2010-07-31 17:29:25684 int int_value;
685 base::StringToInt(value, &int_value);
686 reversed_bypass_list_ = (value == "true" || int_value);
[email protected]d7395e732009-08-28 23:13:43687 } else if (key == "NoProxyFor") {
[email protected]f18fde22010-05-18 23:49:54688 AddHostList("/system/http_proxy/ignore_hosts", value);
[email protected]d7395e732009-08-28 23:13:43689 } else if (key == "AuthMode") {
690 // Check for authentication, just so we can warn.
[email protected]e83326f2010-07-31 17:29:25691 int mode;
692 base::StringToInt(value, &mode);
[email protected]d7395e732009-08-28 23:13:43693 if (mode) {
694 // ProxyConfig does not support authentication parameters, but
695 // Chrome will prompt for the password later. So we ignore this.
696 LOG(WARNING) <<
697 "Proxy authentication parameters ignored, see bug 16709";
698 }
699 }
700 }
701
[email protected]a8185d02010-06-11 00:19:50702 void ResolveIndirect(const std::string& key) {
[email protected]d7395e732009-08-28 23:13:43703 string_map_type::iterator it = string_table_.find(key);
704 if (it != string_table_.end()) {
[email protected]f18fde22010-05-18 23:49:54705 std::string value;
[email protected]3ba7e082010-08-07 02:57:59706 if (env_var_getter_->GetVar(it->second.c_str(), &value))
[email protected]d7395e732009-08-28 23:13:43707 it->second = value;
[email protected]8425adc02010-04-18 17:45:31708 else
709 string_table_.erase(it);
[email protected]d7395e732009-08-28 23:13:43710 }
711 }
712
[email protected]a8185d02010-06-11 00:19:50713 void ResolveIndirectList(const std::string& key) {
[email protected]f18fde22010-05-18 23:49:54714 strings_map_type::iterator it = strings_table_.find(key);
715 if (it != strings_table_.end()) {
716 std::string value;
717 if (!it->second.empty() &&
[email protected]3ba7e082010-08-07 02:57:59718 env_var_getter_->GetVar(it->second[0].c_str(), &value))
[email protected]f18fde22010-05-18 23:49:54719 AddHostList(key, value);
720 else
721 strings_table_.erase(it);
722 }
723 }
724
[email protected]d7395e732009-08-28 23:13:43725 // The settings in kioslaverc could occur in any order, but some affect
726 // others. Rather than read the whole file in and then query them in an
727 // order that allows us to handle that, we read the settings in whatever
728 // order they occur and do any necessary tweaking after we finish.
729 void ResolveModeEffects() {
730 if (indirect_manual_) {
731 ResolveIndirect("/system/http_proxy/host");
732 ResolveIndirect("/system/proxy/secure_host");
733 ResolveIndirect("/system/proxy/ftp_host");
[email protected]f18fde22010-05-18 23:49:54734 ResolveIndirectList("/system/http_proxy/ignore_hosts");
[email protected]d7395e732009-08-28 23:13:43735 }
736 if (auto_no_pac_) {
737 // Remove the PAC URL; we're not supposed to use it.
738 string_table_.erase("/system/proxy/autoconfig_url");
739 }
[email protected]d7395e732009-08-28 23:13:43740 }
741
742 // Reads kioslaverc one line at a time and calls AddKDESetting() to add
743 // each relevant name-value pair to the appropriate value table.
744 void UpdateCachedSettings() {
[email protected]92d2dc82010-04-08 17:49:59745 FilePath kioslaverc = kde_config_dir_.Append("kioslaverc");
[email protected]d7395e732009-08-28 23:13:43746 file_util::ScopedFILE input(file_util::OpenFile(kioslaverc, "r"));
747 if (!input.get())
748 return;
749 ResetCachedSettings();
750 bool in_proxy_settings = false;
751 bool line_too_long = false;
[email protected]9a3d8d42009-09-03 17:01:46752 char line[BUFFER_SIZE];
753 // fgets() will return NULL on EOF or error.
[email protected]d7395e732009-08-28 23:13:43754 while (fgets(line, sizeof(line), input.get())) {
755 // fgets() guarantees the line will be properly terminated.
756 size_t length = strlen(line);
757 if (!length)
758 continue;
759 // This should be true even with CRLF endings.
760 if (line[length - 1] != '\n') {
761 line_too_long = true;
762 continue;
763 }
764 if (line_too_long) {
765 // The previous line had no line ending, but this done does. This is
766 // the end of the line that was too long, so warn here and skip it.
767 LOG(WARNING) << "skipped very long line in " << kioslaverc.value();
768 line_too_long = false;
769 continue;
770 }
771 // Remove the LF at the end, and the CR if there is one.
772 line[--length] = '\0';
773 if (length && line[length - 1] == '\r')
774 line[--length] = '\0';
775 // Now parse the line.
776 if (line[0] == '[') {
777 // Switching sections. All we care about is whether this is
778 // the (a?) proxy settings section, for both KDE3 and KDE4.
779 in_proxy_settings = !strncmp(line, "[Proxy Settings]", 16);
780 } else if (in_proxy_settings) {
781 // A regular line, in the (a?) proxy settings section.
[email protected]9a3d8d42009-09-03 17:01:46782 char* split = strchr(line, '=');
783 // Skip this line if it does not contain an = sign.
784 if (!split)
[email protected]d7395e732009-08-28 23:13:43785 continue;
[email protected]9a3d8d42009-09-03 17:01:46786 // Split the line on the = and advance |split|.
787 *(split++) = 0;
788 std::string key = line;
789 std::string value = split;
790 TrimWhitespaceASCII(key, TRIM_ALL, &key);
791 TrimWhitespaceASCII(value, TRIM_ALL, &value);
792 // Skip this line if the key name is empty.
793 if (key.empty())
[email protected]d7395e732009-08-28 23:13:43794 continue;
795 // Is the value name localized?
[email protected]9a3d8d42009-09-03 17:01:46796 if (key[key.length() - 1] == ']') {
797 // Find the matching bracket.
798 length = key.rfind('[');
799 // Skip this line if the localization indicator is malformed.
800 if (length == std::string::npos)
[email protected]d7395e732009-08-28 23:13:43801 continue;
802 // Trim the localization indicator off.
[email protected]9a3d8d42009-09-03 17:01:46803 key.resize(length);
804 // Remove any resulting trailing whitespace.
805 TrimWhitespaceASCII(key, TRIM_TRAILING, &key);
806 // Skip this line if the key name is now empty.
807 if (key.empty())
808 continue;
[email protected]d7395e732009-08-28 23:13:43809 }
[email protected]d7395e732009-08-28 23:13:43810 // Now fill in the tables.
[email protected]9a3d8d42009-09-03 17:01:46811 AddKDESetting(key, value);
[email protected]d7395e732009-08-28 23:13:43812 }
813 }
814 if (ferror(input.get()))
815 LOG(ERROR) << "error reading " << kioslaverc.value();
816 ResolveModeEffects();
817 }
818
819 // This is the callback from the debounce timer.
820 void OnDebouncedNotification() {
821 DCHECK(MessageLoop::current() == file_loop_);
[email protected]b30a3f52010-10-16 01:05:46822 VLOG(1) << "inotify change notification for kioslaverc";
[email protected]d7395e732009-08-28 23:13:43823 UpdateCachedSettings();
[email protected]961ac942011-04-28 18:18:14824 CHECK(notify_delegate_);
[email protected]d7395e732009-08-28 23:13:43825 // Forward to a method on the proxy config service delegate object.
826 notify_delegate_->OnCheckProxyConfigSettings();
827 }
828
829 // Called by OnFileCanReadWithoutBlocking() on the file thread. Reads
830 // from the inotify file descriptor and starts up a debounce timer if
831 // an event for kioslaverc is seen.
832 void OnChangeNotification() {
833 DCHECK(inotify_fd_ >= 0);
834 DCHECK(MessageLoop::current() == file_loop_);
835 char event_buf[(sizeof(inotify_event) + NAME_MAX + 1) * 4];
836 bool kioslaverc_touched = false;
837 ssize_t r;
838 while ((r = read(inotify_fd_, event_buf, sizeof(event_buf))) > 0) {
839 // inotify returns variable-length structures, which is why we have
840 // this strange-looking loop instead of iterating through an array.
841 char* event_ptr = event_buf;
842 while (event_ptr < event_buf + r) {
843 inotify_event* event = reinterpret_cast<inotify_event*>(event_ptr);
844 // The kernel always feeds us whole events.
[email protected]b1f031dd2010-03-02 23:19:33845 CHECK_LE(event_ptr + sizeof(inotify_event), event_buf + r);
846 CHECK_LE(event->name + event->len, event_buf + r);
[email protected]d7395e732009-08-28 23:13:43847 if (!strcmp(event->name, "kioslaverc"))
848 kioslaverc_touched = true;
849 // Advance the pointer just past the end of the filename.
850 event_ptr = event->name + event->len;
851 }
852 // We keep reading even if |kioslaverc_touched| is true to drain the
853 // inotify event queue.
854 }
855 if (!r)
856 // Instead of returning -1 and setting errno to EINVAL if there is not
857 // enough buffer space, older kernels (< 2.6.21) return 0. Simulate the
858 // new behavior (EINVAL) so we can reuse the code below.
859 errno = EINVAL;
860 if (errno != EAGAIN) {
[email protected]57b765672009-10-13 18:27:40861 PLOG(WARNING) << "error reading inotify file descriptor";
[email protected]d7395e732009-08-28 23:13:43862 if (errno == EINVAL) {
863 // Our buffer is not large enough to read the next event. This should
864 // not happen (because its size is calculated to always be sufficiently
865 // large), but if it does we'd warn continuously since |inotify_fd_|
866 // would be forever ready to read. Close it and stop watching instead.
867 LOG(ERROR) << "inotify failure; no longer watching kioslaverc!";
868 inotify_watcher_.StopWatchingFileDescriptor();
869 close(inotify_fd_);
870 inotify_fd_ = -1;
871 }
872 }
873 if (kioslaverc_touched) {
874 // We don't use Reset() because the timer may not yet be running.
875 // (In that case Stop() is a no-op.)
876 debounce_timer_.Stop();
877 debounce_timer_.Start(base::TimeDelta::FromMilliseconds(
878 kDebounceTimeoutMilliseconds), this,
879 &GConfSettingGetterImplKDE::OnDebouncedNotification);
880 }
881 }
882
883 typedef std::map<std::string, std::string> string_map_type;
884 typedef std::map<std::string, std::vector<std::string> > strings_map_type;
885
886 int inotify_fd_;
887 base::MessagePumpLibevent::FileDescriptorWatcher inotify_watcher_;
888 ProxyConfigServiceLinux::Delegate* notify_delegate_;
889 base::OneShotTimer<GConfSettingGetterImplKDE> debounce_timer_;
890 FilePath kde_config_dir_;
891 bool indirect_manual_;
892 bool auto_no_pac_;
[email protected]a48bf4a2010-06-14 18:24:53893 bool reversed_bypass_list_;
[email protected]f18fde22010-05-18 23:49:54894 // We don't own |env_var_getter_|. It's safe to hold a pointer to it, since
895 // both it and us are owned by ProxyConfigServiceLinux::Delegate, and have the
896 // same lifetime.
[email protected]76b90d312010-08-03 03:00:50897 base::Environment* env_var_getter_;
[email protected]d7395e732009-08-28 23:13:43898
899 // We cache these settings whenever we re-read the kioslaverc file.
900 string_map_type string_table_;
901 strings_map_type strings_table_;
902
903 // Message loop of the file thread, for reading kioslaverc. If NULL,
904 // just read it directly (for testing). We also handle inotify events
905 // on this thread.
906 MessageLoopForIO* file_loop_;
907
908 DISALLOW_COPY_AND_ASSIGN(GConfSettingGetterImplKDE);
[email protected]861c6c62009-04-20 16:50:56909};
910
911} // namespace
912
[email protected]3e44697f2009-05-22 14:37:39913bool ProxyConfigServiceLinux::Delegate::GetProxyFromGConf(
[email protected]861c6c62009-04-20 16:50:56914 const char* key_prefix, bool is_socks, ProxyServer* result_server) {
915 std::string key(key_prefix);
916 std::string host;
917 if (!gconf_getter_->GetString((key + "host").c_str(), &host)
918 || host.empty()) {
919 // Unset or empty.
920 return false;
921 }
922 // Check for an optional port.
[email protected]d7395e732009-08-28 23:13:43923 int port = 0;
[email protected]861c6c62009-04-20 16:50:56924 gconf_getter_->GetInt((key + "port").c_str(), &port);
925 if (port != 0) {
926 // If a port is set and non-zero:
[email protected]528c56d2010-07-30 19:28:44927 host += ":" + base::IntToString(port);
[email protected]861c6c62009-04-20 16:50:56928 }
[email protected]76960f3d2011-04-30 02:15:23929
930 // gconf settings do not appear to distinguish between SOCKS
931 // version. We default to version 5. For more information on this policy
932 // decisions, see:
933 // http://code.google.com/p/chromium/issues/detail?id=55912#c2
[email protected]861c6c62009-04-20 16:50:56934 host = FixupProxyHostScheme(
[email protected]e8c50812010-09-28 00:16:17935 is_socks ? ProxyServer::SCHEME_SOCKS5 : ProxyServer::SCHEME_HTTP,
[email protected]861c6c62009-04-20 16:50:56936 host);
[email protected]87a102b2009-07-14 05:23:30937 ProxyServer proxy_server = ProxyServer::FromURI(host,
938 ProxyServer::SCHEME_HTTP);
[email protected]861c6c62009-04-20 16:50:56939 if (proxy_server.is_valid()) {
940 *result_server = proxy_server;
941 return true;
942 }
943 return false;
944}
945
[email protected]3e44697f2009-05-22 14:37:39946bool ProxyConfigServiceLinux::Delegate::GetConfigFromGConf(
947 ProxyConfig* config) {
[email protected]861c6c62009-04-20 16:50:56948 std::string mode;
949 if (!gconf_getter_->GetString("/system/proxy/mode", &mode)) {
950 // We expect this to always be set, so if we don't see it then we
951 // probably have a gconf problem, and so we don't have a valid
952 // proxy config.
953 return false;
954 }
[email protected]3e44697f2009-05-22 14:37:39955 if (mode == "none") {
[email protected]861c6c62009-04-20 16:50:56956 // Specifically specifies no proxy.
957 return true;
[email protected]3e44697f2009-05-22 14:37:39958 }
[email protected]861c6c62009-04-20 16:50:56959
[email protected]3e44697f2009-05-22 14:37:39960 if (mode == "auto") {
[email protected]861c6c62009-04-20 16:50:56961 // automatic proxy config
962 std::string pac_url_str;
963 if (gconf_getter_->GetString("/system/proxy/autoconfig_url",
964 &pac_url_str)) {
965 if (!pac_url_str.empty()) {
966 GURL pac_url(pac_url_str);
967 if (!pac_url.is_valid())
968 return false;
[email protected]ed4ed0f2010-02-24 00:20:48969 config->set_pac_url(pac_url);
[email protected]861c6c62009-04-20 16:50:56970 return true;
971 }
972 }
[email protected]ed4ed0f2010-02-24 00:20:48973 config->set_auto_detect(true);
[email protected]861c6c62009-04-20 16:50:56974 return true;
975 }
976
[email protected]3e44697f2009-05-22 14:37:39977 if (mode != "manual") {
[email protected]861c6c62009-04-20 16:50:56978 // Mode is unrecognized.
979 return false;
980 }
981 bool use_http_proxy;
982 if (gconf_getter_->GetBoolean("/system/http_proxy/use_http_proxy",
983 &use_http_proxy)
984 && !use_http_proxy) {
985 // Another master switch for some reason. If set to false, then no
986 // proxy. But we don't panic if the key doesn't exist.
987 return true;
988 }
989
990 bool same_proxy = false;
991 // Indicates to use the http proxy for all protocols. This one may
992 // not exist (presumably on older versions), assume false in that
993 // case.
994 gconf_getter_->GetBoolean("/system/http_proxy/use_same_proxy",
995 &same_proxy);
996
[email protected]76960f3d2011-04-30 02:15:23997 ProxyServer proxy_for_http;
998 ProxyServer proxy_for_https;
999 ProxyServer proxy_for_ftp;
1000 ProxyServer socks_proxy; // (socks)
1001
1002 // This counts how many of the above ProxyServers were defined and valid.
1003 size_t num_proxies_specified = 0;
1004
1005 // Extract the per-scheme proxies. If we failed to parse it, or no proxy was
1006 // specified for the scheme, then the resulting ProxyServer will be invalid.
1007 if (GetProxyFromGConf("/system/http_proxy/", false, &proxy_for_http))
1008 num_proxies_specified++;
1009 if (GetProxyFromGConf("/system/proxy/secure_", false, &proxy_for_https))
1010 num_proxies_specified++;
1011 if (GetProxyFromGConf("/system/proxy/ftp_", false, &proxy_for_ftp))
1012 num_proxies_specified++;
1013 if (GetProxyFromGConf("/system/proxy/socks_", true, &socks_proxy))
1014 num_proxies_specified++;
1015
1016 if (same_proxy) {
1017 if (proxy_for_http.is_valid()) {
1018 // Use the http proxy for all schemes.
[email protected]ed4ed0f2010-02-24 00:20:481019 config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
[email protected]76960f3d2011-04-30 02:15:231020 config->proxy_rules().single_proxy = proxy_for_http;
[email protected]861c6c62009-04-20 16:50:561021 }
[email protected]76960f3d2011-04-30 02:15:231022 } else if (num_proxies_specified > 0) {
1023 if (socks_proxy.is_valid() && num_proxies_specified == 1) {
1024 // If the only proxy specified was for SOCKS, use it for all schemes.
1025 config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
1026 config->proxy_rules().single_proxy = socks_proxy;
[email protected]861c6c62009-04-20 16:50:561027 } else {
[email protected]76960f3d2011-04-30 02:15:231028 // Otherwise use the indicate proxies per-scheme.
1029 config->proxy_rules().type =
1030 ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
1031 config->proxy_rules().proxy_for_http = proxy_for_http;
1032 config->proxy_rules().proxy_for_https = proxy_for_https;
1033 config->proxy_rules().proxy_for_ftp = proxy_for_ftp;
1034 config->proxy_rules().fallback_proxy = socks_proxy;
[email protected]861c6c62009-04-20 16:50:561035 }
1036 }
1037
[email protected]ed4ed0f2010-02-24 00:20:481038 if (config->proxy_rules().empty()) {
[email protected]861c6c62009-04-20 16:50:561039 // Manual mode but we couldn't parse any rules.
1040 return false;
1041 }
1042
1043 // Check for authentication, just so we can warn.
[email protected]d7395e732009-08-28 23:13:431044 bool use_auth = false;
[email protected]861c6c62009-04-20 16:50:561045 gconf_getter_->GetBoolean("/system/http_proxy/use_authentication",
1046 &use_auth);
[email protected]62749f182009-07-15 13:16:541047 if (use_auth) {
1048 // ProxyConfig does not support authentication parameters, but
1049 // Chrome will prompt for the password later. So we ignore
1050 // /system/http_proxy/*auth* settings.
1051 LOG(WARNING) << "Proxy authentication parameters ignored, see bug 16709";
1052 }
[email protected]861c6c62009-04-20 16:50:561053
1054 // Now the bypass list.
[email protected]7541206c2010-02-19 20:24:061055 std::vector<std::string> ignore_hosts_list;
[email protected]ed4ed0f2010-02-24 00:20:481056 config->proxy_rules().bypass_rules.Clear();
[email protected]a8185d02010-06-11 00:19:501057 if (gconf_getter_->GetStringList("/system/http_proxy/ignore_hosts",
1058 &ignore_hosts_list)) {
1059 std::vector<std::string>::const_iterator it(ignore_hosts_list.begin());
[email protected]1a597192010-07-09 16:58:381060 for (; it != ignore_hosts_list.end(); ++it) {
1061 if (gconf_getter_->MatchHostsUsingSuffixMatching()) {
1062 config->proxy_rules().bypass_rules.
1063 AddRuleFromStringUsingSuffixMatching(*it);
1064 } else {
1065 config->proxy_rules().bypass_rules.AddRuleFromString(*it);
1066 }
1067 }
[email protected]a8185d02010-06-11 00:19:501068 }
[email protected]861c6c62009-04-20 16:50:561069 // Note that there are no settings with semantics corresponding to
[email protected]1a597192010-07-09 16:58:381070 // bypass of local names in GNOME. In KDE, "<local>" is supported
1071 // as a hostname rule.
[email protected]861c6c62009-04-20 16:50:561072
[email protected]a48bf4a2010-06-14 18:24:531073 // KDE allows one to reverse the bypass rules.
1074 config->proxy_rules().reverse_bypass = gconf_getter_->BypassListIsReversed();
1075
[email protected]861c6c62009-04-20 16:50:561076 return true;
1077}
1078
[email protected]76b90d312010-08-03 03:00:501079ProxyConfigServiceLinux::Delegate::Delegate(base::Environment* env_var_getter)
[email protected]d7395e732009-08-28 23:13:431080 : env_var_getter_(env_var_getter),
1081 glib_default_loop_(NULL), io_loop_(NULL) {
1082 // Figure out which GConfSettingGetterImpl to use, if any.
[email protected]6b0349ef2010-10-16 04:56:061083 switch (base::nix::GetDesktopEnvironment(env_var_getter)) {
1084 case base::nix::DESKTOP_ENVIRONMENT_GNOME:
[email protected]6de53d42010-11-09 07:33:191085#if defined(USE_GCONF)
[email protected]d7395e732009-08-28 23:13:431086 gconf_getter_.reset(new GConfSettingGetterImplGConf());
[email protected]6de53d42010-11-09 07:33:191087#endif
[email protected]d7395e732009-08-28 23:13:431088 break;
[email protected]6b0349ef2010-10-16 04:56:061089 case base::nix::DESKTOP_ENVIRONMENT_KDE3:
1090 case base::nix::DESKTOP_ENVIRONMENT_KDE4:
[email protected]d7395e732009-08-28 23:13:431091 gconf_getter_.reset(new GConfSettingGetterImplKDE(env_var_getter));
1092 break;
[email protected]6b0349ef2010-10-16 04:56:061093 case base::nix::DESKTOP_ENVIRONMENT_XFCE:
1094 case base::nix::DESKTOP_ENVIRONMENT_OTHER:
[email protected]d7395e732009-08-28 23:13:431095 break;
1096 }
1097}
1098
[email protected]76b90d312010-08-03 03:00:501099ProxyConfigServiceLinux::Delegate::Delegate(base::Environment* env_var_getter,
[email protected]861c6c62009-04-20 16:50:561100 GConfSettingGetter* gconf_getter)
[email protected]3e44697f2009-05-22 14:37:391101 : env_var_getter_(env_var_getter), gconf_getter_(gconf_getter),
1102 glib_default_loop_(NULL), io_loop_(NULL) {
[email protected]861c6c62009-04-20 16:50:561103}
1104
[email protected]3e44697f2009-05-22 14:37:391105void ProxyConfigServiceLinux::Delegate::SetupAndFetchInitialConfig(
[email protected]d7395e732009-08-28 23:13:431106 MessageLoop* glib_default_loop, MessageLoop* io_loop,
1107 MessageLoopForIO* file_loop) {
[email protected]3e44697f2009-05-22 14:37:391108 // We should be running on the default glib main loop thread right
1109 // now. gconf can only be accessed from this thread.
1110 DCHECK(MessageLoop::current() == glib_default_loop);
1111 glib_default_loop_ = glib_default_loop;
1112 io_loop_ = io_loop;
1113
[email protected]d7395e732009-08-28 23:13:431114 // If we are passed a NULL io_loop or file_loop, then we don't set up
1115 // proxy setting change notifications. This should not be the usual
1116 // case but is intended to simplify test setups.
1117 if (!io_loop_ || !file_loop)
[email protected]b30a3f52010-10-16 01:05:461118 VLOG(1) << "Monitoring of proxy setting changes is disabled";
[email protected]3e44697f2009-05-22 14:37:391119
1120 // Fetch and cache the current proxy config. The config is left in
[email protected]119655002010-07-23 06:02:401121 // cached_config_, where GetLatestProxyConfig() running on the IO thread
[email protected]3e44697f2009-05-22 14:37:391122 // will expect to find it. This is safe to do because we return
1123 // before this ProxyConfigServiceLinux is passed on to
1124 // the ProxyService.
[email protected]d6cb85b2009-07-23 22:10:531125
1126 // Note: It would be nice to prioritize environment variables
[email protected]92d2dc82010-04-08 17:49:591127 // and only fall back to gconf if env vars were unset. But
[email protected]d6cb85b2009-07-23 22:10:531128 // gnome-terminal "helpfully" sets http_proxy and no_proxy, and it
1129 // does so even if the proxy mode is set to auto, which would
1130 // mislead us.
1131
[email protected]3e44697f2009-05-22 14:37:391132 bool got_config = false;
[email protected]d7395e732009-08-28 23:13:431133 if (gconf_getter_.get()) {
1134 if (gconf_getter_->Init(glib_default_loop, file_loop) &&
1135 (!io_loop || !file_loop || gconf_getter_->SetupNotification(this))) {
1136 if (GetConfigFromGConf(&cached_config_)) {
1137 cached_config_.set_id(1); // mark it as valid
1138 got_config = true;
[email protected]b30a3f52010-10-16 01:05:461139 VLOG(1) << "Obtained proxy settings from "
1140 << gconf_getter_->GetDataSource();
[email protected]d7395e732009-08-28 23:13:431141 // If gconf proxy mode is "none", meaning direct, then we take
1142 // that to be a valid config and will not check environment
1143 // variables. The alternative would have been to look for a proxy
1144 // whereever we can find one.
1145 //
1146 // Keep a copy of the config for use from this thread for
1147 // comparison with updated settings when we get notifications.
1148 reference_config_ = cached_config_;
1149 reference_config_.set_id(1); // mark it as valid
1150 } else {
1151 gconf_getter_->Shutdown(); // Stop notifications
[email protected]d6cb85b2009-07-23 22:10:531152 }
[email protected]d7395e732009-08-28 23:13:431153 }
[email protected]861c6c62009-04-20 16:50:561154 }
[email protected]d6cb85b2009-07-23 22:10:531155
[email protected]3e44697f2009-05-22 14:37:391156 if (!got_config) {
[email protected]d6cb85b2009-07-23 22:10:531157 // We fall back on environment variables.
[email protected]3e44697f2009-05-22 14:37:391158 //
1159 // Consulting environment variables doesn't need to be done from
1160 // the default glib main loop, but it's a tiny enough amount of
1161 // work.
1162 if (GetConfigFromEnv(&cached_config_)) {
1163 cached_config_.set_id(1); // mark it as valid
[email protected]b30a3f52010-10-16 01:05:461164 VLOG(1) << "Obtained proxy settings from environment variables";
[email protected]3e44697f2009-05-22 14:37:391165 }
[email protected]861c6c62009-04-20 16:50:561166 }
[email protected]3e44697f2009-05-22 14:37:391167}
1168
[email protected]119655002010-07-23 06:02:401169void ProxyConfigServiceLinux::Delegate::AddObserver(Observer* observer) {
1170 observers_.AddObserver(observer);
1171}
1172
1173void ProxyConfigServiceLinux::Delegate::RemoveObserver(Observer* observer) {
1174 observers_.RemoveObserver(observer);
1175}
1176
[email protected]3a29593d2011-04-11 10:07:521177ProxyConfigService::ConfigAvailability
1178 ProxyConfigServiceLinux::Delegate::GetLatestProxyConfig(
1179 ProxyConfig* config) {
[email protected]3e44697f2009-05-22 14:37:391180 // This is called from the IO thread.
1181 DCHECK(!io_loop_ || MessageLoop::current() == io_loop_);
1182
1183 // Simply return the last proxy configuration that glib_default_loop
1184 // notified us of.
[email protected]119655002010-07-23 06:02:401185 *config = cached_config_.is_valid() ?
1186 cached_config_ : ProxyConfig::CreateDirect();
1187
[email protected]3a29593d2011-04-11 10:07:521188 // We return CONFIG_VALID to indicate that *config was filled in. It is always
[email protected]119655002010-07-23 06:02:401189 // going to be available since we initialized eagerly on the UI thread.
1190 // TODO(eroman): do lazy initialization instead, so we no longer need
1191 // to construct ProxyConfigServiceLinux on the UI thread.
1192 // In which case, we may return false here.
[email protected]3a29593d2011-04-11 10:07:521193 return CONFIG_VALID;
[email protected]3e44697f2009-05-22 14:37:391194}
1195
[email protected]d7395e732009-08-28 23:13:431196// Depending on the GConfSettingGetter in use, this method will be called
1197// on either the UI thread (GConf) or the file thread (KDE).
[email protected]3e44697f2009-05-22 14:37:391198void ProxyConfigServiceLinux::Delegate::OnCheckProxyConfigSettings() {
[email protected]d7395e732009-08-28 23:13:431199 MessageLoop* required_loop = gconf_getter_->GetNotificationLoop();
1200 DCHECK(!required_loop || MessageLoop::current() == required_loop);
[email protected]3e44697f2009-05-22 14:37:391201 ProxyConfig new_config;
1202 bool valid = GetConfigFromGConf(&new_config);
1203 if (valid)
1204 new_config.set_id(1); // mark it as valid
1205
[email protected]119655002010-07-23 06:02:401206 // See if it is different from what we had before.
[email protected]3e44697f2009-05-22 14:37:391207 if (new_config.is_valid() != reference_config_.is_valid() ||
1208 !new_config.Equals(reference_config_)) {
1209 // Post a task to |io_loop| with the new configuration, so it can
1210 // update |cached_config_|.
1211 io_loop_->PostTask(
1212 FROM_HERE,
1213 NewRunnableMethod(
1214 this,
1215 &ProxyConfigServiceLinux::Delegate::SetNewProxyConfig,
1216 new_config));
[email protected]d1f9d472009-08-13 19:59:301217 // Update the thread-private copy in |reference_config_| as well.
1218 reference_config_ = new_config;
[email protected]3e44697f2009-05-22 14:37:391219 }
1220}
1221
1222void ProxyConfigServiceLinux::Delegate::SetNewProxyConfig(
1223 const ProxyConfig& new_config) {
1224 DCHECK(MessageLoop::current() == io_loop_);
[email protected]b30a3f52010-10-16 01:05:461225 VLOG(1) << "Proxy configuration changed";
[email protected]3e44697f2009-05-22 14:37:391226 cached_config_ = new_config;
[email protected]3a29593d2011-04-11 10:07:521227 FOR_EACH_OBSERVER(
1228 Observer, observers_,
1229 OnProxyConfigChanged(new_config, ProxyConfigService::CONFIG_VALID));
[email protected]3e44697f2009-05-22 14:37:391230}
1231
1232void ProxyConfigServiceLinux::Delegate::PostDestroyTask() {
[email protected]d7395e732009-08-28 23:13:431233 if (!gconf_getter_.get())
1234 return;
1235 MessageLoop* shutdown_loop = gconf_getter_->GetNotificationLoop();
1236 if (!shutdown_loop || MessageLoop::current() == shutdown_loop) {
[email protected]3e44697f2009-05-22 14:37:391237 // Already on the right thread, call directly.
1238 // This is the case for the unittests.
1239 OnDestroy();
1240 } else {
[email protected]d7395e732009-08-28 23:13:431241 // Post to shutdown thread. Note that on browser shutdown, we may quit
1242 // this MessageLoop and exit the program before ever running this.
1243 shutdown_loop->PostTask(
[email protected]3e44697f2009-05-22 14:37:391244 FROM_HERE,
1245 NewRunnableMethod(
1246 this,
1247 &ProxyConfigServiceLinux::Delegate::OnDestroy));
1248 }
1249}
1250void ProxyConfigServiceLinux::Delegate::OnDestroy() {
[email protected]d7395e732009-08-28 23:13:431251 MessageLoop* shutdown_loop = gconf_getter_->GetNotificationLoop();
1252 DCHECK(!shutdown_loop || MessageLoop::current() == shutdown_loop);
1253 gconf_getter_->Shutdown();
[email protected]3e44697f2009-05-22 14:37:391254}
1255
1256ProxyConfigServiceLinux::ProxyConfigServiceLinux()
[email protected]76b90d312010-08-03 03:00:501257 : delegate_(new Delegate(base::Environment::Create())) {
[email protected]3e44697f2009-05-22 14:37:391258}
1259
[email protected]8e1845e12010-09-15 19:22:241260ProxyConfigServiceLinux::~ProxyConfigServiceLinux() {
1261 delegate_->PostDestroyTask();
1262}
1263
[email protected]3e44697f2009-05-22 14:37:391264ProxyConfigServiceLinux::ProxyConfigServiceLinux(
[email protected]76b90d312010-08-03 03:00:501265 base::Environment* env_var_getter)
[email protected]9a3d8d42009-09-03 17:01:461266 : delegate_(new Delegate(env_var_getter)) {
1267}
1268
1269ProxyConfigServiceLinux::ProxyConfigServiceLinux(
[email protected]76b90d312010-08-03 03:00:501270 base::Environment* env_var_getter,
[email protected]3e44697f2009-05-22 14:37:391271 GConfSettingGetter* gconf_getter)
1272 : delegate_(new Delegate(env_var_getter, gconf_getter)) {
[email protected]861c6c62009-04-20 16:50:561273}
1274
[email protected]e4be2dd2010-12-14 00:44:391275void ProxyConfigServiceLinux::AddObserver(Observer* observer) {
1276 delegate_->AddObserver(observer);
1277}
1278
1279void ProxyConfigServiceLinux::RemoveObserver(Observer* observer) {
1280 delegate_->RemoveObserver(observer);
1281}
1282
[email protected]3a29593d2011-04-11 10:07:521283ProxyConfigService::ConfigAvailability
1284 ProxyConfigServiceLinux::GetLatestProxyConfig(ProxyConfig* config) {
[email protected]e4be2dd2010-12-14 00:44:391285 return delegate_->GetLatestProxyConfig(config);
1286}
1287
[email protected]861c6c62009-04-20 16:50:561288} // namespace net