blob: 3d8c3cea23be0f2d4cfac3930e4886b364040cfc [file] [log] [blame]
[email protected]9bc8cff2010-04-03 01:05:391// Copyright (c) 2010 The Chromium Authors. All rights reserved.
[email protected]861c6c62009-04-20 16:50:562// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/proxy/proxy_config_service_linux.h"
6
[email protected]d7395e732009-08-28 23:13:437#include <errno.h>
8#include <fcntl.h>
[email protected]861c6c62009-04-20 16:50:569#include <gconf/gconf-client.h>
[email protected]d7395e732009-08-28 23:13:4310#include <limits.h>
11#include <stdio.h>
[email protected]861c6c62009-04-20 16:50:5612#include <stdlib.h>
[email protected]d7395e732009-08-28 23:13:4313#include <sys/inotify.h>
14#include <unistd.h>
[email protected]861c6c62009-04-20 16:50:5615
[email protected]9bc8cff2010-04-03 01:05:3916#include <map>
17
[email protected]76b90d312010-08-03 03:00:5018#include "base/environment.h"
[email protected]d7395e732009-08-28 23:13:4319#include "base/file_path.h"
20#include "base/file_util.h"
[email protected]861c6c62009-04-20 16:50:5621#include "base/logging.h"
[email protected]d7395e732009-08-28 23:13:4322#include "base/message_loop.h"
[email protected]528c56d2010-07-30 19:28:4423#include "base/string_number_conversions.h"
[email protected]861c6c62009-04-20 16:50:5624#include "base/string_tokenizer.h"
25#include "base/string_util.h"
[email protected]3e44697f2009-05-22 14:37:3926#include "base/task.h"
[email protected]d7395e732009-08-28 23:13:4327#include "base/timer.h"
[email protected]6b0349ef2010-10-16 04:56:0628#include "base/nix/xdg_util.h"
[email protected]861c6c62009-04-20 16:50:5629#include "googleurl/src/url_canon.h"
30#include "net/base/net_errors.h"
31#include "net/http/http_util.h"
32#include "net/proxy/proxy_config.h"
33#include "net/proxy/proxy_server.h"
34
35namespace net {
36
37namespace {
38
[email protected]861c6c62009-04-20 16:50:5639// Given a proxy hostname from a setting, returns that hostname with
40// an appropriate proxy server scheme prefix.
41// scheme indicates the desired proxy scheme: usually http, with
42// socks 4 or 5 as special cases.
[email protected]87a102b2009-07-14 05:23:3043// TODO(arindam): Remove URI string manipulation by using MapUrlSchemeToProxy.
[email protected]861c6c62009-04-20 16:50:5644std::string FixupProxyHostScheme(ProxyServer::Scheme scheme,
45 std::string host) {
[email protected]e8c50812010-09-28 00:16:1746 if (scheme == ProxyServer::SCHEME_SOCKS5 &&
47 StartsWithASCII(host, "socks4://", false)) {
48 // We default to socks 5, but if the user specifically set it to
49 // socks4://, then use that.
50 scheme = ProxyServer::SCHEME_SOCKS4;
[email protected]861c6c62009-04-20 16:50:5651 }
52 // Strip the scheme if any.
53 std::string::size_type colon = host.find("://");
54 if (colon != std::string::npos)
55 host = host.substr(colon + 3);
56 // If a username and perhaps password are specified, give a warning.
57 std::string::size_type at_sign = host.find("@");
58 // Should this be supported?
59 if (at_sign != std::string::npos) {
[email protected]62749f182009-07-15 13:16:5460 // ProxyConfig does not support authentication parameters, but Chrome
61 // will prompt for the password later. Disregard the
62 // authentication parameters and continue with this hostname.
63 LOG(WARNING) << "Proxy authentication parameters ignored, see bug 16709";
[email protected]861c6c62009-04-20 16:50:5664 host = host.substr(at_sign + 1);
65 }
66 // If this is a socks proxy, prepend a scheme so as to tell
67 // ProxyServer. This also allows ProxyServer to choose the right
68 // default port.
69 if (scheme == ProxyServer::SCHEME_SOCKS4)
70 host = "socks4://" + host;
71 else if (scheme == ProxyServer::SCHEME_SOCKS5)
72 host = "socks5://" + host;
[email protected]d7395e732009-08-28 23:13:4373 // If there is a trailing slash, remove it so |host| will parse correctly
74 // even if it includes a port number (since the slash is not numeric).
75 if (host.length() && host[host.length() - 1] == '/')
76 host.resize(host.length() - 1);
[email protected]861c6c62009-04-20 16:50:5677 return host;
78}
79
80} // namespace
81
[email protected]8e1845e12010-09-15 19:22:2482ProxyConfigServiceLinux::Delegate::~Delegate() {
83}
84
[email protected]3e44697f2009-05-22 14:37:3985bool ProxyConfigServiceLinux::Delegate::GetProxyFromEnvVarForScheme(
[email protected]861c6c62009-04-20 16:50:5686 const char* variable, ProxyServer::Scheme scheme,
87 ProxyServer* result_server) {
88 std::string env_value;
[email protected]3ba7e082010-08-07 02:57:5989 if (env_var_getter_->GetVar(variable, &env_value)) {
[email protected]861c6c62009-04-20 16:50:5690 if (!env_value.empty()) {
91 env_value = FixupProxyHostScheme(scheme, env_value);
[email protected]87a102b2009-07-14 05:23:3092 ProxyServer proxy_server =
93 ProxyServer::FromURI(env_value, ProxyServer::SCHEME_HTTP);
[email protected]861c6c62009-04-20 16:50:5694 if (proxy_server.is_valid() && !proxy_server.is_direct()) {
95 *result_server = proxy_server;
96 return true;
97 } else {
[email protected]3e44697f2009-05-22 14:37:3998 LOG(ERROR) << "Failed to parse environment variable " << variable;
[email protected]861c6c62009-04-20 16:50:5699 }
100 }
101 }
102 return false;
103}
104
[email protected]3e44697f2009-05-22 14:37:39105bool ProxyConfigServiceLinux::Delegate::GetProxyFromEnvVar(
[email protected]861c6c62009-04-20 16:50:56106 const char* variable, ProxyServer* result_server) {
107 return GetProxyFromEnvVarForScheme(variable, ProxyServer::SCHEME_HTTP,
108 result_server);
109}
110
[email protected]3e44697f2009-05-22 14:37:39111bool ProxyConfigServiceLinux::Delegate::GetConfigFromEnv(ProxyConfig* config) {
[email protected]861c6c62009-04-20 16:50:56112 // Check for automatic configuration first, in
113 // "auto_proxy". Possibly only the "environment_proxy" firefox
114 // extension has ever used this, but it still sounds like a good
115 // idea.
116 std::string auto_proxy;
[email protected]3ba7e082010-08-07 02:57:59117 if (env_var_getter_->GetVar("auto_proxy", &auto_proxy)) {
[email protected]861c6c62009-04-20 16:50:56118 if (auto_proxy.empty()) {
119 // Defined and empty => autodetect
[email protected]ed4ed0f2010-02-24 00:20:48120 config->set_auto_detect(true);
[email protected]861c6c62009-04-20 16:50:56121 } else {
122 // specified autoconfig URL
[email protected]ed4ed0f2010-02-24 00:20:48123 config->set_pac_url(GURL(auto_proxy));
[email protected]861c6c62009-04-20 16:50:56124 }
125 return true;
126 }
127 // "all_proxy" is a shortcut to avoid defining {http,https,ftp}_proxy.
128 ProxyServer proxy_server;
129 if (GetProxyFromEnvVar("all_proxy", &proxy_server)) {
[email protected]ed4ed0f2010-02-24 00:20:48130 config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
131 config->proxy_rules().single_proxy = proxy_server;
[email protected]861c6c62009-04-20 16:50:56132 } else {
133 bool have_http = GetProxyFromEnvVar("http_proxy", &proxy_server);
134 if (have_http)
[email protected]ed4ed0f2010-02-24 00:20:48135 config->proxy_rules().proxy_for_http = proxy_server;
[email protected]861c6c62009-04-20 16:50:56136 // It would be tempting to let http_proxy apply for all protocols
137 // if https_proxy and ftp_proxy are not defined. Googling turns up
138 // several documents that mention only http_proxy. But then the
139 // user really might not want to proxy https. And it doesn't seem
140 // like other apps do this. So we will refrain.
141 bool have_https = GetProxyFromEnvVar("https_proxy", &proxy_server);
142 if (have_https)
[email protected]ed4ed0f2010-02-24 00:20:48143 config->proxy_rules().proxy_for_https = proxy_server;
[email protected]861c6c62009-04-20 16:50:56144 bool have_ftp = GetProxyFromEnvVar("ftp_proxy", &proxy_server);
145 if (have_ftp)
[email protected]ed4ed0f2010-02-24 00:20:48146 config->proxy_rules().proxy_for_ftp = proxy_server;
[email protected]861c6c62009-04-20 16:50:56147 if (have_http || have_https || have_ftp) {
148 // mustn't change type unless some rules are actually set.
[email protected]ed4ed0f2010-02-24 00:20:48149 config->proxy_rules().type =
150 ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
[email protected]861c6c62009-04-20 16:50:56151 }
152 }
[email protected]ed4ed0f2010-02-24 00:20:48153 if (config->proxy_rules().empty()) {
[email protected]861c6c62009-04-20 16:50:56154 // If the above were not defined, try for socks.
[email protected]e8c50812010-09-28 00:16:17155 // For environment variables, we default to version 5, per the gnome
156 // documentation: http://library.gnome.org/devel/gnet/stable/gnet-socks.html
157 ProxyServer::Scheme scheme = ProxyServer::SCHEME_SOCKS5;
[email protected]861c6c62009-04-20 16:50:56158 std::string env_version;
[email protected]3ba7e082010-08-07 02:57:59159 if (env_var_getter_->GetVar("SOCKS_VERSION", &env_version)
[email protected]e8c50812010-09-28 00:16:17160 && env_version == "4")
161 scheme = ProxyServer::SCHEME_SOCKS4;
[email protected]861c6c62009-04-20 16:50:56162 if (GetProxyFromEnvVarForScheme("SOCKS_SERVER", scheme, &proxy_server)) {
[email protected]ed4ed0f2010-02-24 00:20:48163 config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
164 config->proxy_rules().single_proxy = proxy_server;
[email protected]861c6c62009-04-20 16:50:56165 }
166 }
167 // Look for the proxy bypass list.
168 std::string no_proxy;
[email protected]3ba7e082010-08-07 02:57:59169 env_var_getter_->GetVar("no_proxy", &no_proxy);
[email protected]ed4ed0f2010-02-24 00:20:48170 if (config->proxy_rules().empty()) {
[email protected]861c6c62009-04-20 16:50:56171 // Having only "no_proxy" set, presumably to "*", makes it
172 // explicit that env vars do specify a configuration: having no
173 // rules specified only means the user explicitly asks for direct
174 // connections.
175 return !no_proxy.empty();
176 }
[email protected]7541206c2010-02-19 20:24:06177 // Note that this uses "suffix" matching. So a bypass of "google.com"
178 // is understood to mean a bypass of "*google.com".
[email protected]ed4ed0f2010-02-24 00:20:48179 config->proxy_rules().bypass_rules.ParseFromStringUsingSuffixMatching(
180 no_proxy);
[email protected]861c6c62009-04-20 16:50:56181 return true;
182}
183
184namespace {
185
[email protected]d7395e732009-08-28 23:13:43186const int kDebounceTimeoutMilliseconds = 250;
[email protected]3e44697f2009-05-22 14:37:39187
[email protected]d7395e732009-08-28 23:13:43188// This is the "real" gconf version that actually uses gconf.
189class GConfSettingGetterImplGConf
[email protected]861c6c62009-04-20 16:50:56190 : public ProxyConfigServiceLinux::GConfSettingGetter {
191 public:
[email protected]d7395e732009-08-28 23:13:43192 GConfSettingGetterImplGConf()
193 : client_(NULL), notify_delegate_(NULL), loop_(NULL) {}
[email protected]3e44697f2009-05-22 14:37:39194
[email protected]d7395e732009-08-28 23:13:43195 virtual ~GConfSettingGetterImplGConf() {
[email protected]3e44697f2009-05-22 14:37:39196 // client_ should have been released before now, from
[email protected]f5b13442009-07-13 15:23:59197 // Delegate::OnDestroy(), while running on the UI thread. However
198 // on exiting the process, it may happen that
199 // Delegate::OnDestroy() task is left pending on the glib loop
200 // after the loop was quit, and pending tasks may then be deleted
201 // without being run.
202 if (client_) {
203 // gconf client was not cleaned up.
204 if (MessageLoop::current() == loop_) {
205 // We are on the UI thread so we can clean it safely. This is
206 // the case at least for ui_tests running under Valgrind in
207 // bug 16076.
[email protected]b30a3f52010-10-16 01:05:46208 VLOG(1) << "~GConfSettingGetterImplGConf: releasing gconf client";
[email protected]d7395e732009-08-28 23:13:43209 Shutdown();
[email protected]f5b13442009-07-13 15:23:59210 } else {
[email protected]d7395e732009-08-28 23:13:43211 LOG(WARNING) << "~GConfSettingGetterImplGConf: leaking gconf client";
[email protected]f5b13442009-07-13 15:23:59212 client_ = NULL;
213 }
214 }
[email protected]3e44697f2009-05-22 14:37:39215 DCHECK(!client_);
[email protected]861c6c62009-04-20 16:50:56216 }
217
[email protected]d7395e732009-08-28 23:13:43218 virtual bool Init(MessageLoop* glib_default_loop,
219 MessageLoopForIO* file_loop) {
220 DCHECK(MessageLoop::current() == glib_default_loop);
[email protected]3e44697f2009-05-22 14:37:39221 DCHECK(!client_);
222 DCHECK(!loop_);
[email protected]d7395e732009-08-28 23:13:43223 loop_ = glib_default_loop;
[email protected]3e44697f2009-05-22 14:37:39224 client_ = gconf_client_get_default();
[email protected]861c6c62009-04-20 16:50:56225 if (!client_) {
[email protected]861c6c62009-04-20 16:50:56226 // It's not clear whether/when this can return NULL.
[email protected]3e44697f2009-05-22 14:37:39227 LOG(ERROR) << "Unable to create a gconf client";
228 loop_ = NULL;
229 return false;
[email protected]861c6c62009-04-20 16:50:56230 }
[email protected]3e44697f2009-05-22 14:37:39231 GError* error = NULL;
232 // We need to add the directories for which we'll be asking
233 // notifications, and we might as well ask to preload them.
234 gconf_client_add_dir(client_, "/system/proxy",
235 GCONF_CLIENT_PRELOAD_ONELEVEL, &error);
236 if (error == NULL) {
237 gconf_client_add_dir(client_, "/system/http_proxy",
238 GCONF_CLIENT_PRELOAD_ONELEVEL, &error);
239 }
240 if (error != NULL) {
241 LOG(ERROR) << "Error requesting gconf directory: " << error->message;
242 g_error_free(error);
[email protected]d7395e732009-08-28 23:13:43243 Shutdown();
[email protected]3e44697f2009-05-22 14:37:39244 return false;
245 }
246 return true;
247 }
248
[email protected]d7395e732009-08-28 23:13:43249 void Shutdown() {
[email protected]3e44697f2009-05-22 14:37:39250 if (client_) {
251 DCHECK(MessageLoop::current() == loop_);
252 // This also disables gconf notifications.
253 g_object_unref(client_);
254 client_ = NULL;
255 loop_ = NULL;
256 }
257 }
258
[email protected]d7395e732009-08-28 23:13:43259 bool SetupNotification(ProxyConfigServiceLinux::Delegate* delegate) {
[email protected]3e44697f2009-05-22 14:37:39260 DCHECK(client_);
261 DCHECK(MessageLoop::current() == loop_);
262 GError* error = NULL;
[email protected]d7395e732009-08-28 23:13:43263 notify_delegate_ = delegate;
[email protected]3e44697f2009-05-22 14:37:39264 gconf_client_notify_add(
265 client_, "/system/proxy",
[email protected]d7395e732009-08-28 23:13:43266 OnGConfChangeNotification, this,
[email protected]3e44697f2009-05-22 14:37:39267 NULL, &error);
268 if (error == NULL) {
269 gconf_client_notify_add(
270 client_, "/system/http_proxy",
[email protected]d7395e732009-08-28 23:13:43271 OnGConfChangeNotification, this,
[email protected]3e44697f2009-05-22 14:37:39272 NULL, &error);
273 }
274 if (error != NULL) {
275 LOG(ERROR) << "Error requesting gconf notifications: " << error->message;
276 g_error_free(error);
[email protected]d7395e732009-08-28 23:13:43277 Shutdown();
[email protected]3e44697f2009-05-22 14:37:39278 return false;
279 }
280 return true;
[email protected]861c6c62009-04-20 16:50:56281 }
282
[email protected]9a3d8d42009-09-03 17:01:46283 virtual MessageLoop* GetNotificationLoop() {
[email protected]d7395e732009-08-28 23:13:43284 return loop_;
285 }
286
287 virtual const char* GetDataSource() {
288 return "gconf";
289 }
290
[email protected]861c6c62009-04-20 16:50:56291 virtual bool GetString(const char* key, std::string* result) {
[email protected]3e44697f2009-05-22 14:37:39292 DCHECK(client_);
293 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56294 GError* error = NULL;
295 gchar* value = gconf_client_get_string(client_, key, &error);
296 if (HandleGError(error, key))
297 return false;
298 if (!value)
299 return false;
300 *result = value;
301 g_free(value);
302 return true;
303 }
304 virtual bool GetBoolean(const char* key, bool* result) {
[email protected]3e44697f2009-05-22 14:37:39305 DCHECK(client_);
306 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56307 GError* error = NULL;
308 // We want to distinguish unset values from values defaulting to
309 // false. For that we need to use the type-generic
310 // gconf_client_get() rather than gconf_client_get_bool().
311 GConfValue* gconf_value = gconf_client_get(client_, key, &error);
312 if (HandleGError(error, key))
313 return false;
314 if (!gconf_value) {
315 // Unset.
316 return false;
317 }
318 if (gconf_value->type != GCONF_VALUE_BOOL) {
319 gconf_value_free(gconf_value);
320 return false;
321 }
322 gboolean bool_value = gconf_value_get_bool(gconf_value);
323 *result = static_cast<bool>(bool_value);
324 gconf_value_free(gconf_value);
325 return true;
326 }
327 virtual bool GetInt(const char* key, int* result) {
[email protected]3e44697f2009-05-22 14:37:39328 DCHECK(client_);
329 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56330 GError* error = NULL;
331 int value = gconf_client_get_int(client_, key, &error);
332 if (HandleGError(error, key))
333 return false;
334 // We don't bother to distinguish an unset value because callers
335 // don't care. 0 is returned if unset.
336 *result = value;
337 return true;
338 }
339 virtual bool GetStringList(const char* key,
340 std::vector<std::string>* result) {
[email protected]3e44697f2009-05-22 14:37:39341 DCHECK(client_);
342 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56343 GError* error = NULL;
344 GSList* list = gconf_client_get_list(client_, key,
345 GCONF_VALUE_STRING, &error);
346 if (HandleGError(error, key))
347 return false;
348 if (!list) {
349 // unset
350 return false;
351 }
352 for (GSList *it = list; it; it = it->next) {
353 result->push_back(static_cast<char*>(it->data));
354 g_free(it->data);
355 }
356 g_slist_free(list);
357 return true;
358 }
359
[email protected]a48bf4a2010-06-14 18:24:53360 virtual bool BypassListIsReversed() {
361 // This is a KDE-specific setting.
362 return false;
363 }
364
[email protected]1a597192010-07-09 16:58:38365 virtual bool MatchHostsUsingSuffixMatching() {
366 return false;
367 }
368
[email protected]861c6c62009-04-20 16:50:56369 private:
370 // Logs and frees a glib error. Returns false if there was no error
371 // (error is NULL).
372 bool HandleGError(GError* error, const char* key) {
373 if (error != NULL) {
[email protected]3e44697f2009-05-22 14:37:39374 LOG(ERROR) << "Error getting gconf value for " << key
375 << ": " << error->message;
[email protected]861c6c62009-04-20 16:50:56376 g_error_free(error);
377 return true;
378 }
379 return false;
380 }
381
[email protected]d7395e732009-08-28 23:13:43382 // This is the callback from the debounce timer.
383 void OnDebouncedNotification() {
384 DCHECK(MessageLoop::current() == loop_);
385 DCHECK(notify_delegate_);
386 // Forward to a method on the proxy config service delegate object.
387 notify_delegate_->OnCheckProxyConfigSettings();
388 }
389
390 void OnChangeNotification() {
391 // We don't use Reset() because the timer may not yet be running.
392 // (In that case Stop() is a no-op.)
393 debounce_timer_.Stop();
394 debounce_timer_.Start(base::TimeDelta::FromMilliseconds(
395 kDebounceTimeoutMilliseconds), this,
396 &GConfSettingGetterImplGConf::OnDebouncedNotification);
397 }
398
399 // gconf notification callback, dispatched from the default glib main loop.
400 static void OnGConfChangeNotification(
401 GConfClient* client, guint cnxn_id,
402 GConfEntry* entry, gpointer user_data) {
[email protected]b30a3f52010-10-16 01:05:46403 VLOG(1) << "gconf change notification for key "
404 << gconf_entry_get_key(entry);
[email protected]d7395e732009-08-28 23:13:43405 // We don't track which key has changed, just that something did change.
406 GConfSettingGetterImplGConf* setting_getter =
407 reinterpret_cast<GConfSettingGetterImplGConf*>(user_data);
408 setting_getter->OnChangeNotification();
409 }
410
[email protected]861c6c62009-04-20 16:50:56411 GConfClient* client_;
[email protected]d7395e732009-08-28 23:13:43412 ProxyConfigServiceLinux::Delegate* notify_delegate_;
413 base::OneShotTimer<GConfSettingGetterImplGConf> debounce_timer_;
[email protected]861c6c62009-04-20 16:50:56414
[email protected]3e44697f2009-05-22 14:37:39415 // Message loop of the thread that we make gconf calls on. It should
416 // be the UI thread and all our methods should be called on this
417 // thread. Only for assertions.
418 MessageLoop* loop_;
419
[email protected]d7395e732009-08-28 23:13:43420 DISALLOW_COPY_AND_ASSIGN(GConfSettingGetterImplGConf);
421};
422
423// This is the KDE version that reads kioslaverc and simulates gconf.
424// Doing this allows the main Delegate code, as well as the unit tests
425// for it, to stay the same - and the settings map fairly well besides.
426class GConfSettingGetterImplKDE
427 : public ProxyConfigServiceLinux::GConfSettingGetter,
428 public base::MessagePumpLibevent::Watcher {
429 public:
[email protected]76b90d312010-08-03 03:00:50430 explicit GConfSettingGetterImplKDE(base::Environment* env_var_getter)
[email protected]d7395e732009-08-28 23:13:43431 : inotify_fd_(-1), notify_delegate_(NULL), indirect_manual_(false),
[email protected]a48bf4a2010-06-14 18:24:53432 auto_no_pac_(false), reversed_bypass_list_(false),
[email protected]f18fde22010-05-18 23:49:54433 env_var_getter_(env_var_getter), file_loop_(NULL) {
434 // Derive the location of the kde config dir from the environment.
[email protected]92d2dc82010-04-08 17:49:59435 std::string home;
[email protected]3ba7e082010-08-07 02:57:59436 if (env_var_getter->GetVar("KDEHOME", &home) && !home.empty()) {
[email protected]2e8cfe22010-06-12 00:26:24437 // $KDEHOME is set. Use it unconditionally.
[email protected]92d2dc82010-04-08 17:49:59438 kde_config_dir_ = KDEHomeToConfigPath(FilePath(home));
439 } else {
[email protected]2e8cfe22010-06-12 00:26:24440 // $KDEHOME is unset. Try to figure out what to use. This seems to be
[email protected]92d2dc82010-04-08 17:49:59441 // the common case on most distributions.
[email protected]3ba7e082010-08-07 02:57:59442 if (!env_var_getter->GetVar(base::env_vars::kHome, &home))
[email protected]d7395e732009-08-28 23:13:43443 // User has no $HOME? Give up. Later we'll report the failure.
444 return;
[email protected]6b0349ef2010-10-16 04:56:06445 if (base::nix::GetDesktopEnvironment(env_var_getter) ==
446 base::nix::DESKTOP_ENVIRONMENT_KDE3) {
[email protected]92d2dc82010-04-08 17:49:59447 // KDE3 always uses .kde for its configuration.
448 FilePath kde_path = FilePath(home).Append(".kde");
449 kde_config_dir_ = KDEHomeToConfigPath(kde_path);
450 } else {
451 // Some distributions patch KDE4 to use .kde4 instead of .kde, so that
[email protected]fad9c8a52010-06-10 22:30:53452 // both can be installed side-by-side. Sadly they don't all do this, and
453 // they don't always do this: some distributions have started switching
454 // back as well. So if there is a .kde4 directory, check the timestamps
455 // of the config directories within and use the newest one.
[email protected]92d2dc82010-04-08 17:49:59456 // Note that we should currently be running in the UI thread, because in
457 // the gconf version, that is the only thread that can access the proxy
458 // settings (a gconf restriction). As noted below, the initial read of
459 // the proxy settings will be done in this thread anyway, so we check
460 // for .kde4 here in this thread as well.
[email protected]fad9c8a52010-06-10 22:30:53461 FilePath kde3_path = FilePath(home).Append(".kde");
462 FilePath kde3_config = KDEHomeToConfigPath(kde3_path);
[email protected]92d2dc82010-04-08 17:49:59463 FilePath kde4_path = FilePath(home).Append(".kde4");
[email protected]fad9c8a52010-06-10 22:30:53464 FilePath kde4_config = KDEHomeToConfigPath(kde4_path);
465 bool use_kde4 = false;
[email protected]92d2dc82010-04-08 17:49:59466 if (file_util::DirectoryExists(kde4_path)) {
[email protected]2f0193c22010-09-03 02:28:37467 base::PlatformFileInfo kde3_info;
468 base::PlatformFileInfo kde4_info;
[email protected]fad9c8a52010-06-10 22:30:53469 if (file_util::GetFileInfo(kde4_config, &kde4_info)) {
470 if (file_util::GetFileInfo(kde3_config, &kde3_info)) {
471 use_kde4 = kde4_info.last_modified >= kde3_info.last_modified;
472 } else {
473 use_kde4 = true;
474 }
475 }
476 }
477 if (use_kde4) {
[email protected]92d2dc82010-04-08 17:49:59478 kde_config_dir_ = KDEHomeToConfigPath(kde4_path);
479 } else {
[email protected]fad9c8a52010-06-10 22:30:53480 kde_config_dir_ = KDEHomeToConfigPath(kde3_path);
[email protected]92d2dc82010-04-08 17:49:59481 }
482 }
[email protected]d7395e732009-08-28 23:13:43483 }
[email protected]d7395e732009-08-28 23:13:43484 }
485
486 virtual ~GConfSettingGetterImplKDE() {
487 // inotify_fd_ should have been closed before now, from
488 // Delegate::OnDestroy(), while running on the file thread. However
489 // on exiting the process, it may happen that Delegate::OnDestroy()
490 // task is left pending on the file loop after the loop was quit,
491 // and pending tasks may then be deleted without being run.
492 // Here in the KDE version, we can safely close the file descriptor
493 // anyway. (Not that it really matters; the process is exiting.)
494 if (inotify_fd_ >= 0)
495 Shutdown();
496 DCHECK(inotify_fd_ < 0);
497 }
498
499 virtual bool Init(MessageLoop* glib_default_loop,
500 MessageLoopForIO* file_loop) {
501 DCHECK(inotify_fd_ < 0);
502 inotify_fd_ = inotify_init();
503 if (inotify_fd_ < 0) {
[email protected]57b765672009-10-13 18:27:40504 PLOG(ERROR) << "inotify_init failed";
[email protected]d7395e732009-08-28 23:13:43505 return false;
506 }
507 int flags = fcntl(inotify_fd_, F_GETFL);
508 if (fcntl(inotify_fd_, F_SETFL, flags | O_NONBLOCK) < 0) {
[email protected]57b765672009-10-13 18:27:40509 PLOG(ERROR) << "fcntl failed";
[email protected]d7395e732009-08-28 23:13:43510 close(inotify_fd_);
511 inotify_fd_ = -1;
512 return false;
513 }
514 file_loop_ = file_loop;
515 // The initial read is done on the current thread, not |file_loop_|,
516 // since we will need to have it for SetupAndFetchInitialConfig().
517 UpdateCachedSettings();
518 return true;
519 }
520
521 void Shutdown() {
522 if (inotify_fd_ >= 0) {
523 ResetCachedSettings();
524 inotify_watcher_.StopWatchingFileDescriptor();
525 close(inotify_fd_);
526 inotify_fd_ = -1;
527 }
528 }
529
530 bool SetupNotification(ProxyConfigServiceLinux::Delegate* delegate) {
531 DCHECK(inotify_fd_ >= 0);
532 DCHECK(file_loop_);
533 // We can't just watch the kioslaverc file directly, since KDE will write
534 // a new copy of it and then rename it whenever settings are changed and
535 // inotify watches inodes (so we'll be watching the old deleted file after
536 // the first change, and it will never change again). So, we watch the
537 // directory instead. We then act only on changes to the kioslaverc entry.
538 if (inotify_add_watch(inotify_fd_, kde_config_dir_.value().c_str(),
539 IN_MODIFY | IN_MOVED_TO) < 0)
540 return false;
541 notify_delegate_ = delegate;
542 return file_loop_->WatchFileDescriptor(inotify_fd_, true,
543 MessageLoopForIO::WATCH_READ, &inotify_watcher_, this);
544 }
545
[email protected]9a3d8d42009-09-03 17:01:46546 virtual MessageLoop* GetNotificationLoop() {
[email protected]d7395e732009-08-28 23:13:43547 return file_loop_;
548 }
549
550 // Implement base::MessagePumpLibevent::Delegate.
551 void OnFileCanReadWithoutBlocking(int fd) {
552 DCHECK(fd == inotify_fd_);
553 DCHECK(MessageLoop::current() == file_loop_);
554 OnChangeNotification();
555 }
556 void OnFileCanWriteWithoutBlocking(int fd) {
557 NOTREACHED();
558 }
559
560 virtual const char* GetDataSource() {
561 return "KDE";
562 }
563
564 virtual bool GetString(const char* key, std::string* result) {
565 string_map_type::iterator it = string_table_.find(key);
566 if (it == string_table_.end())
567 return false;
568 *result = it->second;
569 return true;
570 }
571 virtual bool GetBoolean(const char* key, bool* result) {
572 // We don't ever have any booleans.
573 return false;
574 }
575 virtual bool GetInt(const char* key, int* result) {
576 // We don't ever have any integers. (See AddProxy() below about ports.)
577 return false;
578 }
579 virtual bool GetStringList(const char* key,
580 std::vector<std::string>* result) {
581 strings_map_type::iterator it = strings_table_.find(key);
582 if (it == strings_table_.end())
583 return false;
584 *result = it->second;
585 return true;
586 }
587
[email protected]a48bf4a2010-06-14 18:24:53588 virtual bool BypassListIsReversed() {
589 return reversed_bypass_list_;
590 }
591
[email protected]1a597192010-07-09 16:58:38592 virtual bool MatchHostsUsingSuffixMatching() {
593 return true;
594 }
595
[email protected]d7395e732009-08-28 23:13:43596 private:
597 void ResetCachedSettings() {
598 string_table_.clear();
599 strings_table_.clear();
600 indirect_manual_ = false;
601 auto_no_pac_ = false;
[email protected]a48bf4a2010-06-14 18:24:53602 reversed_bypass_list_ = false;
[email protected]d7395e732009-08-28 23:13:43603 }
604
[email protected]92d2dc82010-04-08 17:49:59605 FilePath KDEHomeToConfigPath(const FilePath& kde_home) {
606 return kde_home.Append("share").Append("config");
607 }
608
[email protected]a8185d02010-06-11 00:19:50609 void AddProxy(const std::string& prefix, const std::string& value) {
[email protected]d7395e732009-08-28 23:13:43610 if (value.empty() || value.substr(0, 3) == "//:")
611 // No proxy.
612 return;
613 // We don't need to parse the port number out; GetProxyFromGConf()
614 // would only append it right back again. So we just leave the port
615 // number right in the host string.
616 string_table_[prefix + "host"] = value;
617 }
618
[email protected]a8185d02010-06-11 00:19:50619 void AddHostList(const std::string& key, const std::string& value) {
[email protected]f18fde22010-05-18 23:49:54620 std::vector<std::string> tokens;
[email protected]1a597192010-07-09 16:58:38621 StringTokenizer tk(value, ", ");
[email protected]f18fde22010-05-18 23:49:54622 while (tk.GetNext()) {
623 std::string token = tk.token();
624 if (!token.empty())
625 tokens.push_back(token);
626 }
627 strings_table_[key] = tokens;
628 }
629
[email protected]9a3d8d42009-09-03 17:01:46630 void AddKDESetting(const std::string& key, const std::string& value) {
[email protected]d7395e732009-08-28 23:13:43631 // The astute reader may notice that there is no mention of SOCKS
632 // here. That's because KDE handles socks is a strange way, and we
633 // don't support it. Rather than just a setting for the SOCKS server,
634 // it has a setting for a library to LD_PRELOAD in all your programs
635 // that will transparently SOCKSify them. Such libraries each have
636 // their own configuration, and thus, we can't get it from KDE.
637 if (key == "ProxyType") {
638 const char* mode = "none";
639 indirect_manual_ = false;
640 auto_no_pac_ = false;
[email protected]e83326f2010-07-31 17:29:25641 int int_value;
642 base::StringToInt(value, &int_value);
643 switch (int_value) {
[email protected]d7395e732009-08-28 23:13:43644 case 0: // No proxy, or maybe kioslaverc syntax error.
645 break;
646 case 1: // Manual configuration.
647 mode = "manual";
648 break;
649 case 2: // PAC URL.
650 mode = "auto";
651 break;
652 case 3: // WPAD.
653 mode = "auto";
654 auto_no_pac_ = true;
655 break;
656 case 4: // Indirect manual via environment variables.
657 mode = "manual";
658 indirect_manual_ = true;
659 break;
660 }
661 string_table_["/system/proxy/mode"] = mode;
662 } else if (key == "Proxy Config Script") {
663 string_table_["/system/proxy/autoconfig_url"] = value;
664 } else if (key == "httpProxy") {
665 AddProxy("/system/http_proxy/", value);
666 } else if (key == "httpsProxy") {
667 AddProxy("/system/proxy/secure_", value);
668 } else if (key == "ftpProxy") {
669 AddProxy("/system/proxy/ftp_", value);
670 } else if (key == "ReversedException") {
671 // We count "true" or any nonzero number as true, otherwise false.
672 // Note that if the value is not actually numeric StringToInt()
673 // will return 0, which we count as false.
[email protected]e83326f2010-07-31 17:29:25674 int int_value;
675 base::StringToInt(value, &int_value);
676 reversed_bypass_list_ = (value == "true" || int_value);
[email protected]d7395e732009-08-28 23:13:43677 } else if (key == "NoProxyFor") {
[email protected]f18fde22010-05-18 23:49:54678 AddHostList("/system/http_proxy/ignore_hosts", value);
[email protected]d7395e732009-08-28 23:13:43679 } else if (key == "AuthMode") {
680 // Check for authentication, just so we can warn.
[email protected]e83326f2010-07-31 17:29:25681 int mode;
682 base::StringToInt(value, &mode);
[email protected]d7395e732009-08-28 23:13:43683 if (mode) {
684 // ProxyConfig does not support authentication parameters, but
685 // Chrome will prompt for the password later. So we ignore this.
686 LOG(WARNING) <<
687 "Proxy authentication parameters ignored, see bug 16709";
688 }
689 }
690 }
691
[email protected]a8185d02010-06-11 00:19:50692 void ResolveIndirect(const std::string& key) {
[email protected]d7395e732009-08-28 23:13:43693 string_map_type::iterator it = string_table_.find(key);
694 if (it != string_table_.end()) {
[email protected]f18fde22010-05-18 23:49:54695 std::string value;
[email protected]3ba7e082010-08-07 02:57:59696 if (env_var_getter_->GetVar(it->second.c_str(), &value))
[email protected]d7395e732009-08-28 23:13:43697 it->second = value;
[email protected]8425adc02010-04-18 17:45:31698 else
699 string_table_.erase(it);
[email protected]d7395e732009-08-28 23:13:43700 }
701 }
702
[email protected]a8185d02010-06-11 00:19:50703 void ResolveIndirectList(const std::string& key) {
[email protected]f18fde22010-05-18 23:49:54704 strings_map_type::iterator it = strings_table_.find(key);
705 if (it != strings_table_.end()) {
706 std::string value;
707 if (!it->second.empty() &&
[email protected]3ba7e082010-08-07 02:57:59708 env_var_getter_->GetVar(it->second[0].c_str(), &value))
[email protected]f18fde22010-05-18 23:49:54709 AddHostList(key, value);
710 else
711 strings_table_.erase(it);
712 }
713 }
714
[email protected]d7395e732009-08-28 23:13:43715 // The settings in kioslaverc could occur in any order, but some affect
716 // others. Rather than read the whole file in and then query them in an
717 // order that allows us to handle that, we read the settings in whatever
718 // order they occur and do any necessary tweaking after we finish.
719 void ResolveModeEffects() {
720 if (indirect_manual_) {
721 ResolveIndirect("/system/http_proxy/host");
722 ResolveIndirect("/system/proxy/secure_host");
723 ResolveIndirect("/system/proxy/ftp_host");
[email protected]f18fde22010-05-18 23:49:54724 ResolveIndirectList("/system/http_proxy/ignore_hosts");
[email protected]d7395e732009-08-28 23:13:43725 }
726 if (auto_no_pac_) {
727 // Remove the PAC URL; we're not supposed to use it.
728 string_table_.erase("/system/proxy/autoconfig_url");
729 }
[email protected]d7395e732009-08-28 23:13:43730 }
731
732 // Reads kioslaverc one line at a time and calls AddKDESetting() to add
733 // each relevant name-value pair to the appropriate value table.
734 void UpdateCachedSettings() {
[email protected]92d2dc82010-04-08 17:49:59735 FilePath kioslaverc = kde_config_dir_.Append("kioslaverc");
[email protected]d7395e732009-08-28 23:13:43736 file_util::ScopedFILE input(file_util::OpenFile(kioslaverc, "r"));
737 if (!input.get())
738 return;
739 ResetCachedSettings();
740 bool in_proxy_settings = false;
741 bool line_too_long = false;
[email protected]9a3d8d42009-09-03 17:01:46742 char line[BUFFER_SIZE];
743 // fgets() will return NULL on EOF or error.
[email protected]d7395e732009-08-28 23:13:43744 while (fgets(line, sizeof(line), input.get())) {
745 // fgets() guarantees the line will be properly terminated.
746 size_t length = strlen(line);
747 if (!length)
748 continue;
749 // This should be true even with CRLF endings.
750 if (line[length - 1] != '\n') {
751 line_too_long = true;
752 continue;
753 }
754 if (line_too_long) {
755 // The previous line had no line ending, but this done does. This is
756 // the end of the line that was too long, so warn here and skip it.
757 LOG(WARNING) << "skipped very long line in " << kioslaverc.value();
758 line_too_long = false;
759 continue;
760 }
761 // Remove the LF at the end, and the CR if there is one.
762 line[--length] = '\0';
763 if (length && line[length - 1] == '\r')
764 line[--length] = '\0';
765 // Now parse the line.
766 if (line[0] == '[') {
767 // Switching sections. All we care about is whether this is
768 // the (a?) proxy settings section, for both KDE3 and KDE4.
769 in_proxy_settings = !strncmp(line, "[Proxy Settings]", 16);
770 } else if (in_proxy_settings) {
771 // A regular line, in the (a?) proxy settings section.
[email protected]9a3d8d42009-09-03 17:01:46772 char* split = strchr(line, '=');
773 // Skip this line if it does not contain an = sign.
774 if (!split)
[email protected]d7395e732009-08-28 23:13:43775 continue;
[email protected]9a3d8d42009-09-03 17:01:46776 // Split the line on the = and advance |split|.
777 *(split++) = 0;
778 std::string key = line;
779 std::string value = split;
780 TrimWhitespaceASCII(key, TRIM_ALL, &key);
781 TrimWhitespaceASCII(value, TRIM_ALL, &value);
782 // Skip this line if the key name is empty.
783 if (key.empty())
[email protected]d7395e732009-08-28 23:13:43784 continue;
785 // Is the value name localized?
[email protected]9a3d8d42009-09-03 17:01:46786 if (key[key.length() - 1] == ']') {
787 // Find the matching bracket.
788 length = key.rfind('[');
789 // Skip this line if the localization indicator is malformed.
790 if (length == std::string::npos)
[email protected]d7395e732009-08-28 23:13:43791 continue;
792 // Trim the localization indicator off.
[email protected]9a3d8d42009-09-03 17:01:46793 key.resize(length);
794 // Remove any resulting trailing whitespace.
795 TrimWhitespaceASCII(key, TRIM_TRAILING, &key);
796 // Skip this line if the key name is now empty.
797 if (key.empty())
798 continue;
[email protected]d7395e732009-08-28 23:13:43799 }
[email protected]d7395e732009-08-28 23:13:43800 // Now fill in the tables.
[email protected]9a3d8d42009-09-03 17:01:46801 AddKDESetting(key, value);
[email protected]d7395e732009-08-28 23:13:43802 }
803 }
804 if (ferror(input.get()))
805 LOG(ERROR) << "error reading " << kioslaverc.value();
806 ResolveModeEffects();
807 }
808
809 // This is the callback from the debounce timer.
810 void OnDebouncedNotification() {
811 DCHECK(MessageLoop::current() == file_loop_);
[email protected]b30a3f52010-10-16 01:05:46812 VLOG(1) << "inotify change notification for kioslaverc";
[email protected]d7395e732009-08-28 23:13:43813 UpdateCachedSettings();
814 DCHECK(notify_delegate_);
815 // Forward to a method on the proxy config service delegate object.
816 notify_delegate_->OnCheckProxyConfigSettings();
817 }
818
819 // Called by OnFileCanReadWithoutBlocking() on the file thread. Reads
820 // from the inotify file descriptor and starts up a debounce timer if
821 // an event for kioslaverc is seen.
822 void OnChangeNotification() {
823 DCHECK(inotify_fd_ >= 0);
824 DCHECK(MessageLoop::current() == file_loop_);
825 char event_buf[(sizeof(inotify_event) + NAME_MAX + 1) * 4];
826 bool kioslaverc_touched = false;
827 ssize_t r;
828 while ((r = read(inotify_fd_, event_buf, sizeof(event_buf))) > 0) {
829 // inotify returns variable-length structures, which is why we have
830 // this strange-looking loop instead of iterating through an array.
831 char* event_ptr = event_buf;
832 while (event_ptr < event_buf + r) {
833 inotify_event* event = reinterpret_cast<inotify_event*>(event_ptr);
834 // The kernel always feeds us whole events.
[email protected]b1f031dd2010-03-02 23:19:33835 CHECK_LE(event_ptr + sizeof(inotify_event), event_buf + r);
836 CHECK_LE(event->name + event->len, event_buf + r);
[email protected]d7395e732009-08-28 23:13:43837 if (!strcmp(event->name, "kioslaverc"))
838 kioslaverc_touched = true;
839 // Advance the pointer just past the end of the filename.
840 event_ptr = event->name + event->len;
841 }
842 // We keep reading even if |kioslaverc_touched| is true to drain the
843 // inotify event queue.
844 }
845 if (!r)
846 // Instead of returning -1 and setting errno to EINVAL if there is not
847 // enough buffer space, older kernels (< 2.6.21) return 0. Simulate the
848 // new behavior (EINVAL) so we can reuse the code below.
849 errno = EINVAL;
850 if (errno != EAGAIN) {
[email protected]57b765672009-10-13 18:27:40851 PLOG(WARNING) << "error reading inotify file descriptor";
[email protected]d7395e732009-08-28 23:13:43852 if (errno == EINVAL) {
853 // Our buffer is not large enough to read the next event. This should
854 // not happen (because its size is calculated to always be sufficiently
855 // large), but if it does we'd warn continuously since |inotify_fd_|
856 // would be forever ready to read. Close it and stop watching instead.
857 LOG(ERROR) << "inotify failure; no longer watching kioslaverc!";
858 inotify_watcher_.StopWatchingFileDescriptor();
859 close(inotify_fd_);
860 inotify_fd_ = -1;
861 }
862 }
863 if (kioslaverc_touched) {
864 // We don't use Reset() because the timer may not yet be running.
865 // (In that case Stop() is a no-op.)
866 debounce_timer_.Stop();
867 debounce_timer_.Start(base::TimeDelta::FromMilliseconds(
868 kDebounceTimeoutMilliseconds), this,
869 &GConfSettingGetterImplKDE::OnDebouncedNotification);
870 }
871 }
872
873 typedef std::map<std::string, std::string> string_map_type;
874 typedef std::map<std::string, std::vector<std::string> > strings_map_type;
875
876 int inotify_fd_;
877 base::MessagePumpLibevent::FileDescriptorWatcher inotify_watcher_;
878 ProxyConfigServiceLinux::Delegate* notify_delegate_;
879 base::OneShotTimer<GConfSettingGetterImplKDE> debounce_timer_;
880 FilePath kde_config_dir_;
881 bool indirect_manual_;
882 bool auto_no_pac_;
[email protected]a48bf4a2010-06-14 18:24:53883 bool reversed_bypass_list_;
[email protected]f18fde22010-05-18 23:49:54884 // We don't own |env_var_getter_|. It's safe to hold a pointer to it, since
885 // both it and us are owned by ProxyConfigServiceLinux::Delegate, and have the
886 // same lifetime.
[email protected]76b90d312010-08-03 03:00:50887 base::Environment* env_var_getter_;
[email protected]d7395e732009-08-28 23:13:43888
889 // We cache these settings whenever we re-read the kioslaverc file.
890 string_map_type string_table_;
891 strings_map_type strings_table_;
892
893 // Message loop of the file thread, for reading kioslaverc. If NULL,
894 // just read it directly (for testing). We also handle inotify events
895 // on this thread.
896 MessageLoopForIO* file_loop_;
897
898 DISALLOW_COPY_AND_ASSIGN(GConfSettingGetterImplKDE);
[email protected]861c6c62009-04-20 16:50:56899};
900
901} // namespace
902
[email protected]3e44697f2009-05-22 14:37:39903bool ProxyConfigServiceLinux::Delegate::GetProxyFromGConf(
[email protected]861c6c62009-04-20 16:50:56904 const char* key_prefix, bool is_socks, ProxyServer* result_server) {
905 std::string key(key_prefix);
906 std::string host;
907 if (!gconf_getter_->GetString((key + "host").c_str(), &host)
908 || host.empty()) {
909 // Unset or empty.
910 return false;
911 }
912 // Check for an optional port.
[email protected]d7395e732009-08-28 23:13:43913 int port = 0;
[email protected]861c6c62009-04-20 16:50:56914 gconf_getter_->GetInt((key + "port").c_str(), &port);
915 if (port != 0) {
916 // If a port is set and non-zero:
[email protected]528c56d2010-07-30 19:28:44917 host += ":" + base::IntToString(port);
[email protected]861c6c62009-04-20 16:50:56918 }
919 host = FixupProxyHostScheme(
[email protected]e8c50812010-09-28 00:16:17920 is_socks ? ProxyServer::SCHEME_SOCKS5 : ProxyServer::SCHEME_HTTP,
[email protected]861c6c62009-04-20 16:50:56921 host);
[email protected]87a102b2009-07-14 05:23:30922 ProxyServer proxy_server = ProxyServer::FromURI(host,
923 ProxyServer::SCHEME_HTTP);
[email protected]861c6c62009-04-20 16:50:56924 if (proxy_server.is_valid()) {
925 *result_server = proxy_server;
926 return true;
927 }
928 return false;
929}
930
[email protected]3e44697f2009-05-22 14:37:39931bool ProxyConfigServiceLinux::Delegate::GetConfigFromGConf(
932 ProxyConfig* config) {
[email protected]861c6c62009-04-20 16:50:56933 std::string mode;
934 if (!gconf_getter_->GetString("/system/proxy/mode", &mode)) {
935 // We expect this to always be set, so if we don't see it then we
936 // probably have a gconf problem, and so we don't have a valid
937 // proxy config.
938 return false;
939 }
[email protected]3e44697f2009-05-22 14:37:39940 if (mode == "none") {
[email protected]861c6c62009-04-20 16:50:56941 // Specifically specifies no proxy.
942 return true;
[email protected]3e44697f2009-05-22 14:37:39943 }
[email protected]861c6c62009-04-20 16:50:56944
[email protected]3e44697f2009-05-22 14:37:39945 if (mode == "auto") {
[email protected]861c6c62009-04-20 16:50:56946 // automatic proxy config
947 std::string pac_url_str;
948 if (gconf_getter_->GetString("/system/proxy/autoconfig_url",
949 &pac_url_str)) {
950 if (!pac_url_str.empty()) {
951 GURL pac_url(pac_url_str);
952 if (!pac_url.is_valid())
953 return false;
[email protected]ed4ed0f2010-02-24 00:20:48954 config->set_pac_url(pac_url);
[email protected]861c6c62009-04-20 16:50:56955 return true;
956 }
957 }
[email protected]ed4ed0f2010-02-24 00:20:48958 config->set_auto_detect(true);
[email protected]861c6c62009-04-20 16:50:56959 return true;
960 }
961
[email protected]3e44697f2009-05-22 14:37:39962 if (mode != "manual") {
[email protected]861c6c62009-04-20 16:50:56963 // Mode is unrecognized.
964 return false;
965 }
966 bool use_http_proxy;
967 if (gconf_getter_->GetBoolean("/system/http_proxy/use_http_proxy",
968 &use_http_proxy)
969 && !use_http_proxy) {
970 // Another master switch for some reason. If set to false, then no
971 // proxy. But we don't panic if the key doesn't exist.
972 return true;
973 }
974
975 bool same_proxy = false;
976 // Indicates to use the http proxy for all protocols. This one may
977 // not exist (presumably on older versions), assume false in that
978 // case.
979 gconf_getter_->GetBoolean("/system/http_proxy/use_same_proxy",
980 &same_proxy);
981
982 ProxyServer proxy_server;
983 if (!same_proxy) {
984 // Try socks.
985 if (GetProxyFromGConf("/system/proxy/socks_", true, &proxy_server)) {
986 // gconf settings do not appear to distinguish between socks
[email protected]e8c50812010-09-28 00:16:17987 // version. We default to version 5. For more information on this policy
988 // decisions, see:
989 // http://code.google.com/p/chromium/issues/detail?id=55912#c2
[email protected]ed4ed0f2010-02-24 00:20:48990 config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
991 config->proxy_rules().single_proxy = proxy_server;
[email protected]861c6c62009-04-20 16:50:56992 }
993 }
[email protected]ed4ed0f2010-02-24 00:20:48994 if (config->proxy_rules().empty()) {
[email protected]861c6c62009-04-20 16:50:56995 bool have_http = GetProxyFromGConf("/system/http_proxy/", false,
996 &proxy_server);
997 if (same_proxy) {
998 if (have_http) {
[email protected]ed4ed0f2010-02-24 00:20:48999 config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
1000 config->proxy_rules().single_proxy = proxy_server;
[email protected]861c6c62009-04-20 16:50:561001 }
1002 } else {
1003 // Protocol specific settings.
1004 if (have_http)
[email protected]ed4ed0f2010-02-24 00:20:481005 config->proxy_rules().proxy_for_http = proxy_server;
[email protected]861c6c62009-04-20 16:50:561006 bool have_secure = GetProxyFromGConf("/system/proxy/secure_", false,
1007 &proxy_server);
1008 if (have_secure)
[email protected]ed4ed0f2010-02-24 00:20:481009 config->proxy_rules().proxy_for_https = proxy_server;
[email protected]861c6c62009-04-20 16:50:561010 bool have_ftp = GetProxyFromGConf("/system/proxy/ftp_", false,
1011 &proxy_server);
1012 if (have_ftp)
[email protected]ed4ed0f2010-02-24 00:20:481013 config->proxy_rules().proxy_for_ftp = proxy_server;
[email protected]861c6c62009-04-20 16:50:561014 if (have_http || have_secure || have_ftp)
[email protected]ed4ed0f2010-02-24 00:20:481015 config->proxy_rules().type =
[email protected]861c6c62009-04-20 16:50:561016 ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
1017 }
1018 }
1019
[email protected]ed4ed0f2010-02-24 00:20:481020 if (config->proxy_rules().empty()) {
[email protected]861c6c62009-04-20 16:50:561021 // Manual mode but we couldn't parse any rules.
1022 return false;
1023 }
1024
1025 // Check for authentication, just so we can warn.
[email protected]d7395e732009-08-28 23:13:431026 bool use_auth = false;
[email protected]861c6c62009-04-20 16:50:561027 gconf_getter_->GetBoolean("/system/http_proxy/use_authentication",
1028 &use_auth);
[email protected]62749f182009-07-15 13:16:541029 if (use_auth) {
1030 // ProxyConfig does not support authentication parameters, but
1031 // Chrome will prompt for the password later. So we ignore
1032 // /system/http_proxy/*auth* settings.
1033 LOG(WARNING) << "Proxy authentication parameters ignored, see bug 16709";
1034 }
[email protected]861c6c62009-04-20 16:50:561035
1036 // Now the bypass list.
[email protected]7541206c2010-02-19 20:24:061037 std::vector<std::string> ignore_hosts_list;
[email protected]ed4ed0f2010-02-24 00:20:481038 config->proxy_rules().bypass_rules.Clear();
[email protected]a8185d02010-06-11 00:19:501039 if (gconf_getter_->GetStringList("/system/http_proxy/ignore_hosts",
1040 &ignore_hosts_list)) {
1041 std::vector<std::string>::const_iterator it(ignore_hosts_list.begin());
[email protected]1a597192010-07-09 16:58:381042 for (; it != ignore_hosts_list.end(); ++it) {
1043 if (gconf_getter_->MatchHostsUsingSuffixMatching()) {
1044 config->proxy_rules().bypass_rules.
1045 AddRuleFromStringUsingSuffixMatching(*it);
1046 } else {
1047 config->proxy_rules().bypass_rules.AddRuleFromString(*it);
1048 }
1049 }
[email protected]a8185d02010-06-11 00:19:501050 }
[email protected]861c6c62009-04-20 16:50:561051 // Note that there are no settings with semantics corresponding to
[email protected]1a597192010-07-09 16:58:381052 // bypass of local names in GNOME. In KDE, "<local>" is supported
1053 // as a hostname rule.
[email protected]861c6c62009-04-20 16:50:561054
[email protected]a48bf4a2010-06-14 18:24:531055 // KDE allows one to reverse the bypass rules.
1056 config->proxy_rules().reverse_bypass = gconf_getter_->BypassListIsReversed();
1057
[email protected]861c6c62009-04-20 16:50:561058 return true;
1059}
1060
[email protected]76b90d312010-08-03 03:00:501061ProxyConfigServiceLinux::Delegate::Delegate(base::Environment* env_var_getter)
[email protected]d7395e732009-08-28 23:13:431062 : env_var_getter_(env_var_getter),
1063 glib_default_loop_(NULL), io_loop_(NULL) {
1064 // Figure out which GConfSettingGetterImpl to use, if any.
[email protected]6b0349ef2010-10-16 04:56:061065 switch (base::nix::GetDesktopEnvironment(env_var_getter)) {
1066 case base::nix::DESKTOP_ENVIRONMENT_GNOME:
[email protected]d7395e732009-08-28 23:13:431067 gconf_getter_.reset(new GConfSettingGetterImplGConf());
1068 break;
[email protected]6b0349ef2010-10-16 04:56:061069 case base::nix::DESKTOP_ENVIRONMENT_KDE3:
1070 case base::nix::DESKTOP_ENVIRONMENT_KDE4:
[email protected]d7395e732009-08-28 23:13:431071 gconf_getter_.reset(new GConfSettingGetterImplKDE(env_var_getter));
1072 break;
[email protected]6b0349ef2010-10-16 04:56:061073 case base::nix::DESKTOP_ENVIRONMENT_XFCE:
1074 case base::nix::DESKTOP_ENVIRONMENT_OTHER:
[email protected]d7395e732009-08-28 23:13:431075 break;
1076 }
1077}
1078
[email protected]76b90d312010-08-03 03:00:501079ProxyConfigServiceLinux::Delegate::Delegate(base::Environment* env_var_getter,
[email protected]861c6c62009-04-20 16:50:561080 GConfSettingGetter* gconf_getter)
[email protected]3e44697f2009-05-22 14:37:391081 : env_var_getter_(env_var_getter), gconf_getter_(gconf_getter),
1082 glib_default_loop_(NULL), io_loop_(NULL) {
[email protected]861c6c62009-04-20 16:50:561083}
1084
[email protected]3e44697f2009-05-22 14:37:391085void ProxyConfigServiceLinux::Delegate::SetupAndFetchInitialConfig(
[email protected]d7395e732009-08-28 23:13:431086 MessageLoop* glib_default_loop, MessageLoop* io_loop,
1087 MessageLoopForIO* file_loop) {
[email protected]3e44697f2009-05-22 14:37:391088 // We should be running on the default glib main loop thread right
1089 // now. gconf can only be accessed from this thread.
1090 DCHECK(MessageLoop::current() == glib_default_loop);
1091 glib_default_loop_ = glib_default_loop;
1092 io_loop_ = io_loop;
1093
[email protected]d7395e732009-08-28 23:13:431094 // If we are passed a NULL io_loop or file_loop, then we don't set up
1095 // proxy setting change notifications. This should not be the usual
1096 // case but is intended to simplify test setups.
1097 if (!io_loop_ || !file_loop)
[email protected]b30a3f52010-10-16 01:05:461098 VLOG(1) << "Monitoring of proxy setting changes is disabled";
[email protected]3e44697f2009-05-22 14:37:391099
1100 // Fetch and cache the current proxy config. The config is left in
[email protected]119655002010-07-23 06:02:401101 // cached_config_, where GetLatestProxyConfig() running on the IO thread
[email protected]3e44697f2009-05-22 14:37:391102 // will expect to find it. This is safe to do because we return
1103 // before this ProxyConfigServiceLinux is passed on to
1104 // the ProxyService.
[email protected]d6cb85b2009-07-23 22:10:531105
1106 // Note: It would be nice to prioritize environment variables
[email protected]92d2dc82010-04-08 17:49:591107 // and only fall back to gconf if env vars were unset. But
[email protected]d6cb85b2009-07-23 22:10:531108 // gnome-terminal "helpfully" sets http_proxy and no_proxy, and it
1109 // does so even if the proxy mode is set to auto, which would
1110 // mislead us.
1111
[email protected]3e44697f2009-05-22 14:37:391112 bool got_config = false;
[email protected]d7395e732009-08-28 23:13:431113 if (gconf_getter_.get()) {
1114 if (gconf_getter_->Init(glib_default_loop, file_loop) &&
1115 (!io_loop || !file_loop || gconf_getter_->SetupNotification(this))) {
1116 if (GetConfigFromGConf(&cached_config_)) {
1117 cached_config_.set_id(1); // mark it as valid
1118 got_config = true;
[email protected]b30a3f52010-10-16 01:05:461119 VLOG(1) << "Obtained proxy settings from "
1120 << gconf_getter_->GetDataSource();
[email protected]d7395e732009-08-28 23:13:431121 // If gconf proxy mode is "none", meaning direct, then we take
1122 // that to be a valid config and will not check environment
1123 // variables. The alternative would have been to look for a proxy
1124 // whereever we can find one.
1125 //
1126 // Keep a copy of the config for use from this thread for
1127 // comparison with updated settings when we get notifications.
1128 reference_config_ = cached_config_;
1129 reference_config_.set_id(1); // mark it as valid
1130 } else {
1131 gconf_getter_->Shutdown(); // Stop notifications
[email protected]d6cb85b2009-07-23 22:10:531132 }
[email protected]d7395e732009-08-28 23:13:431133 }
[email protected]861c6c62009-04-20 16:50:561134 }
[email protected]d6cb85b2009-07-23 22:10:531135
[email protected]3e44697f2009-05-22 14:37:391136 if (!got_config) {
[email protected]d6cb85b2009-07-23 22:10:531137 // We fall back on environment variables.
[email protected]3e44697f2009-05-22 14:37:391138 //
1139 // Consulting environment variables doesn't need to be done from
1140 // the default glib main loop, but it's a tiny enough amount of
1141 // work.
1142 if (GetConfigFromEnv(&cached_config_)) {
1143 cached_config_.set_id(1); // mark it as valid
[email protected]b30a3f52010-10-16 01:05:461144 VLOG(1) << "Obtained proxy settings from environment variables";
[email protected]3e44697f2009-05-22 14:37:391145 }
[email protected]861c6c62009-04-20 16:50:561146 }
[email protected]3e44697f2009-05-22 14:37:391147}
1148
[email protected]119655002010-07-23 06:02:401149void ProxyConfigServiceLinux::Delegate::AddObserver(Observer* observer) {
1150 observers_.AddObserver(observer);
1151}
1152
1153void ProxyConfigServiceLinux::Delegate::RemoveObserver(Observer* observer) {
1154 observers_.RemoveObserver(observer);
1155}
1156
1157bool ProxyConfigServiceLinux::Delegate::GetLatestProxyConfig(
1158 ProxyConfig* config) {
[email protected]3e44697f2009-05-22 14:37:391159 // This is called from the IO thread.
1160 DCHECK(!io_loop_ || MessageLoop::current() == io_loop_);
1161
1162 // Simply return the last proxy configuration that glib_default_loop
1163 // notified us of.
[email protected]119655002010-07-23 06:02:401164 *config = cached_config_.is_valid() ?
1165 cached_config_ : ProxyConfig::CreateDirect();
1166
1167 // We return true to indicate that *config was filled in. It is always
1168 // going to be available since we initialized eagerly on the UI thread.
1169 // TODO(eroman): do lazy initialization instead, so we no longer need
1170 // to construct ProxyConfigServiceLinux on the UI thread.
1171 // In which case, we may return false here.
1172 return true;
[email protected]3e44697f2009-05-22 14:37:391173}
1174
[email protected]d7395e732009-08-28 23:13:431175// Depending on the GConfSettingGetter in use, this method will be called
1176// on either the UI thread (GConf) or the file thread (KDE).
[email protected]3e44697f2009-05-22 14:37:391177void ProxyConfigServiceLinux::Delegate::OnCheckProxyConfigSettings() {
[email protected]d7395e732009-08-28 23:13:431178 MessageLoop* required_loop = gconf_getter_->GetNotificationLoop();
1179 DCHECK(!required_loop || MessageLoop::current() == required_loop);
[email protected]3e44697f2009-05-22 14:37:391180 ProxyConfig new_config;
1181 bool valid = GetConfigFromGConf(&new_config);
1182 if (valid)
1183 new_config.set_id(1); // mark it as valid
1184
[email protected]119655002010-07-23 06:02:401185 // See if it is different from what we had before.
[email protected]3e44697f2009-05-22 14:37:391186 if (new_config.is_valid() != reference_config_.is_valid() ||
1187 !new_config.Equals(reference_config_)) {
1188 // Post a task to |io_loop| with the new configuration, so it can
1189 // update |cached_config_|.
1190 io_loop_->PostTask(
1191 FROM_HERE,
1192 NewRunnableMethod(
1193 this,
1194 &ProxyConfigServiceLinux::Delegate::SetNewProxyConfig,
1195 new_config));
[email protected]d1f9d472009-08-13 19:59:301196 // Update the thread-private copy in |reference_config_| as well.
1197 reference_config_ = new_config;
[email protected]3e44697f2009-05-22 14:37:391198 }
1199}
1200
1201void ProxyConfigServiceLinux::Delegate::SetNewProxyConfig(
1202 const ProxyConfig& new_config) {
1203 DCHECK(MessageLoop::current() == io_loop_);
[email protected]b30a3f52010-10-16 01:05:461204 VLOG(1) << "Proxy configuration changed";
[email protected]3e44697f2009-05-22 14:37:391205 cached_config_ = new_config;
[email protected]119655002010-07-23 06:02:401206 FOR_EACH_OBSERVER(Observer, observers_, OnProxyConfigChanged(new_config));
[email protected]3e44697f2009-05-22 14:37:391207}
1208
1209void ProxyConfigServiceLinux::Delegate::PostDestroyTask() {
[email protected]d7395e732009-08-28 23:13:431210 if (!gconf_getter_.get())
1211 return;
1212 MessageLoop* shutdown_loop = gconf_getter_->GetNotificationLoop();
1213 if (!shutdown_loop || MessageLoop::current() == shutdown_loop) {
[email protected]3e44697f2009-05-22 14:37:391214 // Already on the right thread, call directly.
1215 // This is the case for the unittests.
1216 OnDestroy();
1217 } else {
[email protected]d7395e732009-08-28 23:13:431218 // Post to shutdown thread. Note that on browser shutdown, we may quit
1219 // this MessageLoop and exit the program before ever running this.
1220 shutdown_loop->PostTask(
[email protected]3e44697f2009-05-22 14:37:391221 FROM_HERE,
1222 NewRunnableMethod(
1223 this,
1224 &ProxyConfigServiceLinux::Delegate::OnDestroy));
1225 }
1226}
1227void ProxyConfigServiceLinux::Delegate::OnDestroy() {
[email protected]d7395e732009-08-28 23:13:431228 MessageLoop* shutdown_loop = gconf_getter_->GetNotificationLoop();
1229 DCHECK(!shutdown_loop || MessageLoop::current() == shutdown_loop);
1230 gconf_getter_->Shutdown();
[email protected]3e44697f2009-05-22 14:37:391231}
1232
1233ProxyConfigServiceLinux::ProxyConfigServiceLinux()
[email protected]76b90d312010-08-03 03:00:501234 : delegate_(new Delegate(base::Environment::Create())) {
[email protected]3e44697f2009-05-22 14:37:391235}
1236
[email protected]8e1845e12010-09-15 19:22:241237ProxyConfigServiceLinux::~ProxyConfigServiceLinux() {
1238 delegate_->PostDestroyTask();
1239}
1240
[email protected]3e44697f2009-05-22 14:37:391241ProxyConfigServiceLinux::ProxyConfigServiceLinux(
[email protected]76b90d312010-08-03 03:00:501242 base::Environment* env_var_getter)
[email protected]9a3d8d42009-09-03 17:01:461243 : delegate_(new Delegate(env_var_getter)) {
1244}
1245
1246ProxyConfigServiceLinux::ProxyConfigServiceLinux(
[email protected]76b90d312010-08-03 03:00:501247 base::Environment* env_var_getter,
[email protected]3e44697f2009-05-22 14:37:391248 GConfSettingGetter* gconf_getter)
1249 : delegate_(new Delegate(env_var_getter, gconf_getter)) {
[email protected]861c6c62009-04-20 16:50:561250}
1251
1252} // namespace net