blob: e33a487e8eaf01379c36f750570b71c5d4f4a36e [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]574f6f0c2010-07-21 02:59:2818#include "base/env_var.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]861c6c62009-04-20 16:50:5623#include "base/string_tokenizer.h"
24#include "base/string_util.h"
[email protected]3e44697f2009-05-22 14:37:3925#include "base/task.h"
[email protected]d7395e732009-08-28 23:13:4326#include "base/timer.h"
[email protected]1c657852010-04-22 23:28:0527#include "base/xdg_util.h"
[email protected]861c6c62009-04-20 16:50:5628#include "googleurl/src/url_canon.h"
29#include "net/base/net_errors.h"
30#include "net/http/http_util.h"
31#include "net/proxy/proxy_config.h"
32#include "net/proxy/proxy_server.h"
33
34namespace net {
35
36namespace {
37
[email protected]861c6c62009-04-20 16:50:5638// Given a proxy hostname from a setting, returns that hostname with
39// an appropriate proxy server scheme prefix.
40// scheme indicates the desired proxy scheme: usually http, with
41// socks 4 or 5 as special cases.
[email protected]87a102b2009-07-14 05:23:3042// TODO(arindam): Remove URI string manipulation by using MapUrlSchemeToProxy.
[email protected]861c6c62009-04-20 16:50:5643std::string FixupProxyHostScheme(ProxyServer::Scheme scheme,
44 std::string host) {
45 if (scheme == ProxyServer::SCHEME_SOCKS4 &&
46 StartsWithASCII(host, "socks5://", false)) {
47 // We default to socks 4, but if the user specifically set it to
48 // socks5://, then use that.
49 scheme = ProxyServer::SCHEME_SOCKS5;
50 }
51 // Strip the scheme if any.
52 std::string::size_type colon = host.find("://");
53 if (colon != std::string::npos)
54 host = host.substr(colon + 3);
55 // If a username and perhaps password are specified, give a warning.
56 std::string::size_type at_sign = host.find("@");
57 // Should this be supported?
58 if (at_sign != std::string::npos) {
[email protected]62749f182009-07-15 13:16:5459 // ProxyConfig does not support authentication parameters, but Chrome
60 // will prompt for the password later. Disregard the
61 // authentication parameters and continue with this hostname.
62 LOG(WARNING) << "Proxy authentication parameters ignored, see bug 16709";
[email protected]861c6c62009-04-20 16:50:5663 host = host.substr(at_sign + 1);
64 }
65 // If this is a socks proxy, prepend a scheme so as to tell
66 // ProxyServer. This also allows ProxyServer to choose the right
67 // default port.
68 if (scheme == ProxyServer::SCHEME_SOCKS4)
69 host = "socks4://" + host;
70 else if (scheme == ProxyServer::SCHEME_SOCKS5)
71 host = "socks5://" + host;
[email protected]d7395e732009-08-28 23:13:4372 // If there is a trailing slash, remove it so |host| will parse correctly
73 // even if it includes a port number (since the slash is not numeric).
74 if (host.length() && host[host.length() - 1] == '/')
75 host.resize(host.length() - 1);
[email protected]861c6c62009-04-20 16:50:5676 return host;
77}
78
79} // namespace
80
[email protected]3e44697f2009-05-22 14:37:3981bool ProxyConfigServiceLinux::Delegate::GetProxyFromEnvVarForScheme(
[email protected]861c6c62009-04-20 16:50:5682 const char* variable, ProxyServer::Scheme scheme,
83 ProxyServer* result_server) {
84 std::string env_value;
[email protected]9bc8cff2010-04-03 01:05:3985 if (env_var_getter_->GetEnv(variable, &env_value)) {
[email protected]861c6c62009-04-20 16:50:5686 if (!env_value.empty()) {
87 env_value = FixupProxyHostScheme(scheme, env_value);
[email protected]87a102b2009-07-14 05:23:3088 ProxyServer proxy_server =
89 ProxyServer::FromURI(env_value, ProxyServer::SCHEME_HTTP);
[email protected]861c6c62009-04-20 16:50:5690 if (proxy_server.is_valid() && !proxy_server.is_direct()) {
91 *result_server = proxy_server;
92 return true;
93 } else {
[email protected]3e44697f2009-05-22 14:37:3994 LOG(ERROR) << "Failed to parse environment variable " << variable;
[email protected]861c6c62009-04-20 16:50:5695 }
96 }
97 }
98 return false;
99}
100
[email protected]3e44697f2009-05-22 14:37:39101bool ProxyConfigServiceLinux::Delegate::GetProxyFromEnvVar(
[email protected]861c6c62009-04-20 16:50:56102 const char* variable, ProxyServer* result_server) {
103 return GetProxyFromEnvVarForScheme(variable, ProxyServer::SCHEME_HTTP,
104 result_server);
105}
106
[email protected]3e44697f2009-05-22 14:37:39107bool ProxyConfigServiceLinux::Delegate::GetConfigFromEnv(ProxyConfig* config) {
[email protected]861c6c62009-04-20 16:50:56108 // Check for automatic configuration first, in
109 // "auto_proxy". Possibly only the "environment_proxy" firefox
110 // extension has ever used this, but it still sounds like a good
111 // idea.
112 std::string auto_proxy;
[email protected]9bc8cff2010-04-03 01:05:39113 if (env_var_getter_->GetEnv("auto_proxy", &auto_proxy)) {
[email protected]861c6c62009-04-20 16:50:56114 if (auto_proxy.empty()) {
115 // Defined and empty => autodetect
[email protected]ed4ed0f2010-02-24 00:20:48116 config->set_auto_detect(true);
[email protected]861c6c62009-04-20 16:50:56117 } else {
118 // specified autoconfig URL
[email protected]ed4ed0f2010-02-24 00:20:48119 config->set_pac_url(GURL(auto_proxy));
[email protected]861c6c62009-04-20 16:50:56120 }
121 return true;
122 }
123 // "all_proxy" is a shortcut to avoid defining {http,https,ftp}_proxy.
124 ProxyServer proxy_server;
125 if (GetProxyFromEnvVar("all_proxy", &proxy_server)) {
[email protected]ed4ed0f2010-02-24 00:20:48126 config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
127 config->proxy_rules().single_proxy = proxy_server;
[email protected]861c6c62009-04-20 16:50:56128 } else {
129 bool have_http = GetProxyFromEnvVar("http_proxy", &proxy_server);
130 if (have_http)
[email protected]ed4ed0f2010-02-24 00:20:48131 config->proxy_rules().proxy_for_http = proxy_server;
[email protected]861c6c62009-04-20 16:50:56132 // It would be tempting to let http_proxy apply for all protocols
133 // if https_proxy and ftp_proxy are not defined. Googling turns up
134 // several documents that mention only http_proxy. But then the
135 // user really might not want to proxy https. And it doesn't seem
136 // like other apps do this. So we will refrain.
137 bool have_https = GetProxyFromEnvVar("https_proxy", &proxy_server);
138 if (have_https)
[email protected]ed4ed0f2010-02-24 00:20:48139 config->proxy_rules().proxy_for_https = proxy_server;
[email protected]861c6c62009-04-20 16:50:56140 bool have_ftp = GetProxyFromEnvVar("ftp_proxy", &proxy_server);
141 if (have_ftp)
[email protected]ed4ed0f2010-02-24 00:20:48142 config->proxy_rules().proxy_for_ftp = proxy_server;
[email protected]861c6c62009-04-20 16:50:56143 if (have_http || have_https || have_ftp) {
144 // mustn't change type unless some rules are actually set.
[email protected]ed4ed0f2010-02-24 00:20:48145 config->proxy_rules().type =
146 ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
[email protected]861c6c62009-04-20 16:50:56147 }
148 }
[email protected]ed4ed0f2010-02-24 00:20:48149 if (config->proxy_rules().empty()) {
[email protected]861c6c62009-04-20 16:50:56150 // If the above were not defined, try for socks.
151 ProxyServer::Scheme scheme = ProxyServer::SCHEME_SOCKS4;
152 std::string env_version;
[email protected]9bc8cff2010-04-03 01:05:39153 if (env_var_getter_->GetEnv("SOCKS_VERSION", &env_version)
[email protected]3e44697f2009-05-22 14:37:39154 && env_version == "5")
[email protected]861c6c62009-04-20 16:50:56155 scheme = ProxyServer::SCHEME_SOCKS5;
156 if (GetProxyFromEnvVarForScheme("SOCKS_SERVER", scheme, &proxy_server)) {
[email protected]ed4ed0f2010-02-24 00:20:48157 config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
158 config->proxy_rules().single_proxy = proxy_server;
[email protected]861c6c62009-04-20 16:50:56159 }
160 }
161 // Look for the proxy bypass list.
162 std::string no_proxy;
[email protected]9bc8cff2010-04-03 01:05:39163 env_var_getter_->GetEnv("no_proxy", &no_proxy);
[email protected]ed4ed0f2010-02-24 00:20:48164 if (config->proxy_rules().empty()) {
[email protected]861c6c62009-04-20 16:50:56165 // Having only "no_proxy" set, presumably to "*", makes it
166 // explicit that env vars do specify a configuration: having no
167 // rules specified only means the user explicitly asks for direct
168 // connections.
169 return !no_proxy.empty();
170 }
[email protected]7541206c2010-02-19 20:24:06171 // Note that this uses "suffix" matching. So a bypass of "google.com"
172 // is understood to mean a bypass of "*google.com".
[email protected]ed4ed0f2010-02-24 00:20:48173 config->proxy_rules().bypass_rules.ParseFromStringUsingSuffixMatching(
174 no_proxy);
[email protected]861c6c62009-04-20 16:50:56175 return true;
176}
177
178namespace {
179
[email protected]d7395e732009-08-28 23:13:43180const int kDebounceTimeoutMilliseconds = 250;
[email protected]3e44697f2009-05-22 14:37:39181
[email protected]d7395e732009-08-28 23:13:43182// This is the "real" gconf version that actually uses gconf.
183class GConfSettingGetterImplGConf
[email protected]861c6c62009-04-20 16:50:56184 : public ProxyConfigServiceLinux::GConfSettingGetter {
185 public:
[email protected]d7395e732009-08-28 23:13:43186 GConfSettingGetterImplGConf()
187 : client_(NULL), notify_delegate_(NULL), loop_(NULL) {}
[email protected]3e44697f2009-05-22 14:37:39188
[email protected]d7395e732009-08-28 23:13:43189 virtual ~GConfSettingGetterImplGConf() {
[email protected]3e44697f2009-05-22 14:37:39190 // client_ should have been released before now, from
[email protected]f5b13442009-07-13 15:23:59191 // Delegate::OnDestroy(), while running on the UI thread. However
192 // on exiting the process, it may happen that
193 // Delegate::OnDestroy() task is left pending on the glib loop
194 // after the loop was quit, and pending tasks may then be deleted
195 // without being run.
196 if (client_) {
197 // gconf client was not cleaned up.
198 if (MessageLoop::current() == loop_) {
199 // We are on the UI thread so we can clean it safely. This is
200 // the case at least for ui_tests running under Valgrind in
201 // bug 16076.
[email protected]d7395e732009-08-28 23:13:43202 LOG(INFO) << "~GConfSettingGetterImplGConf: releasing gconf client";
203 Shutdown();
[email protected]f5b13442009-07-13 15:23:59204 } else {
[email protected]d7395e732009-08-28 23:13:43205 LOG(WARNING) << "~GConfSettingGetterImplGConf: leaking gconf client";
[email protected]f5b13442009-07-13 15:23:59206 client_ = NULL;
207 }
208 }
[email protected]3e44697f2009-05-22 14:37:39209 DCHECK(!client_);
[email protected]861c6c62009-04-20 16:50:56210 }
211
[email protected]d7395e732009-08-28 23:13:43212 virtual bool Init(MessageLoop* glib_default_loop,
213 MessageLoopForIO* file_loop) {
214 DCHECK(MessageLoop::current() == glib_default_loop);
[email protected]3e44697f2009-05-22 14:37:39215 DCHECK(!client_);
216 DCHECK(!loop_);
[email protected]d7395e732009-08-28 23:13:43217 loop_ = glib_default_loop;
[email protected]3e44697f2009-05-22 14:37:39218 client_ = gconf_client_get_default();
[email protected]861c6c62009-04-20 16:50:56219 if (!client_) {
[email protected]861c6c62009-04-20 16:50:56220 // It's not clear whether/when this can return NULL.
[email protected]3e44697f2009-05-22 14:37:39221 LOG(ERROR) << "Unable to create a gconf client";
222 loop_ = NULL;
223 return false;
[email protected]861c6c62009-04-20 16:50:56224 }
[email protected]3e44697f2009-05-22 14:37:39225 GError* error = NULL;
226 // We need to add the directories for which we'll be asking
227 // notifications, and we might as well ask to preload them.
228 gconf_client_add_dir(client_, "/system/proxy",
229 GCONF_CLIENT_PRELOAD_ONELEVEL, &error);
230 if (error == NULL) {
231 gconf_client_add_dir(client_, "/system/http_proxy",
232 GCONF_CLIENT_PRELOAD_ONELEVEL, &error);
233 }
234 if (error != NULL) {
235 LOG(ERROR) << "Error requesting gconf directory: " << error->message;
236 g_error_free(error);
[email protected]d7395e732009-08-28 23:13:43237 Shutdown();
[email protected]3e44697f2009-05-22 14:37:39238 return false;
239 }
240 return true;
241 }
242
[email protected]d7395e732009-08-28 23:13:43243 void Shutdown() {
[email protected]3e44697f2009-05-22 14:37:39244 if (client_) {
245 DCHECK(MessageLoop::current() == loop_);
246 // This also disables gconf notifications.
247 g_object_unref(client_);
248 client_ = NULL;
249 loop_ = NULL;
250 }
251 }
252
[email protected]d7395e732009-08-28 23:13:43253 bool SetupNotification(ProxyConfigServiceLinux::Delegate* delegate) {
[email protected]3e44697f2009-05-22 14:37:39254 DCHECK(client_);
255 DCHECK(MessageLoop::current() == loop_);
256 GError* error = NULL;
[email protected]d7395e732009-08-28 23:13:43257 notify_delegate_ = delegate;
[email protected]3e44697f2009-05-22 14:37:39258 gconf_client_notify_add(
259 client_, "/system/proxy",
[email protected]d7395e732009-08-28 23:13:43260 OnGConfChangeNotification, this,
[email protected]3e44697f2009-05-22 14:37:39261 NULL, &error);
262 if (error == NULL) {
263 gconf_client_notify_add(
264 client_, "/system/http_proxy",
[email protected]d7395e732009-08-28 23:13:43265 OnGConfChangeNotification, this,
[email protected]3e44697f2009-05-22 14:37:39266 NULL, &error);
267 }
268 if (error != NULL) {
269 LOG(ERROR) << "Error requesting gconf notifications: " << error->message;
270 g_error_free(error);
[email protected]d7395e732009-08-28 23:13:43271 Shutdown();
[email protected]3e44697f2009-05-22 14:37:39272 return false;
273 }
274 return true;
[email protected]861c6c62009-04-20 16:50:56275 }
276
[email protected]9a3d8d42009-09-03 17:01:46277 virtual MessageLoop* GetNotificationLoop() {
[email protected]d7395e732009-08-28 23:13:43278 return loop_;
279 }
280
281 virtual const char* GetDataSource() {
282 return "gconf";
283 }
284
[email protected]861c6c62009-04-20 16:50:56285 virtual bool GetString(const char* key, std::string* result) {
[email protected]3e44697f2009-05-22 14:37:39286 DCHECK(client_);
287 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56288 GError* error = NULL;
289 gchar* value = gconf_client_get_string(client_, key, &error);
290 if (HandleGError(error, key))
291 return false;
292 if (!value)
293 return false;
294 *result = value;
295 g_free(value);
296 return true;
297 }
298 virtual bool GetBoolean(const char* key, bool* result) {
[email protected]3e44697f2009-05-22 14:37:39299 DCHECK(client_);
300 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56301 GError* error = NULL;
302 // We want to distinguish unset values from values defaulting to
303 // false. For that we need to use the type-generic
304 // gconf_client_get() rather than gconf_client_get_bool().
305 GConfValue* gconf_value = gconf_client_get(client_, key, &error);
306 if (HandleGError(error, key))
307 return false;
308 if (!gconf_value) {
309 // Unset.
310 return false;
311 }
312 if (gconf_value->type != GCONF_VALUE_BOOL) {
313 gconf_value_free(gconf_value);
314 return false;
315 }
316 gboolean bool_value = gconf_value_get_bool(gconf_value);
317 *result = static_cast<bool>(bool_value);
318 gconf_value_free(gconf_value);
319 return true;
320 }
321 virtual bool GetInt(const char* key, int* result) {
[email protected]3e44697f2009-05-22 14:37:39322 DCHECK(client_);
323 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56324 GError* error = NULL;
325 int value = gconf_client_get_int(client_, key, &error);
326 if (HandleGError(error, key))
327 return false;
328 // We don't bother to distinguish an unset value because callers
329 // don't care. 0 is returned if unset.
330 *result = value;
331 return true;
332 }
333 virtual bool GetStringList(const char* key,
334 std::vector<std::string>* result) {
[email protected]3e44697f2009-05-22 14:37:39335 DCHECK(client_);
336 DCHECK(MessageLoop::current() == loop_);
[email protected]861c6c62009-04-20 16:50:56337 GError* error = NULL;
338 GSList* list = gconf_client_get_list(client_, key,
339 GCONF_VALUE_STRING, &error);
340 if (HandleGError(error, key))
341 return false;
342 if (!list) {
343 // unset
344 return false;
345 }
346 for (GSList *it = list; it; it = it->next) {
347 result->push_back(static_cast<char*>(it->data));
348 g_free(it->data);
349 }
350 g_slist_free(list);
351 return true;
352 }
353
[email protected]a48bf4a2010-06-14 18:24:53354 virtual bool BypassListIsReversed() {
355 // This is a KDE-specific setting.
356 return false;
357 }
358
[email protected]1a597192010-07-09 16:58:38359 virtual bool MatchHostsUsingSuffixMatching() {
360 return false;
361 }
362
[email protected]861c6c62009-04-20 16:50:56363 private:
364 // Logs and frees a glib error. Returns false if there was no error
365 // (error is NULL).
366 bool HandleGError(GError* error, const char* key) {
367 if (error != NULL) {
[email protected]3e44697f2009-05-22 14:37:39368 LOG(ERROR) << "Error getting gconf value for " << key
369 << ": " << error->message;
[email protected]861c6c62009-04-20 16:50:56370 g_error_free(error);
371 return true;
372 }
373 return false;
374 }
375
[email protected]d7395e732009-08-28 23:13:43376 // This is the callback from the debounce timer.
377 void OnDebouncedNotification() {
378 DCHECK(MessageLoop::current() == loop_);
379 DCHECK(notify_delegate_);
380 // Forward to a method on the proxy config service delegate object.
381 notify_delegate_->OnCheckProxyConfigSettings();
382 }
383
384 void OnChangeNotification() {
385 // We don't use Reset() because the timer may not yet be running.
386 // (In that case Stop() is a no-op.)
387 debounce_timer_.Stop();
388 debounce_timer_.Start(base::TimeDelta::FromMilliseconds(
389 kDebounceTimeoutMilliseconds), this,
390 &GConfSettingGetterImplGConf::OnDebouncedNotification);
391 }
392
393 // gconf notification callback, dispatched from the default glib main loop.
394 static void OnGConfChangeNotification(
395 GConfClient* client, guint cnxn_id,
396 GConfEntry* entry, gpointer user_data) {
397 LOG(INFO) << "gconf change notification for key "
398 << gconf_entry_get_key(entry);
399 // We don't track which key has changed, just that something did change.
400 GConfSettingGetterImplGConf* setting_getter =
401 reinterpret_cast<GConfSettingGetterImplGConf*>(user_data);
402 setting_getter->OnChangeNotification();
403 }
404
[email protected]861c6c62009-04-20 16:50:56405 GConfClient* client_;
[email protected]d7395e732009-08-28 23:13:43406 ProxyConfigServiceLinux::Delegate* notify_delegate_;
407 base::OneShotTimer<GConfSettingGetterImplGConf> debounce_timer_;
[email protected]861c6c62009-04-20 16:50:56408
[email protected]3e44697f2009-05-22 14:37:39409 // Message loop of the thread that we make gconf calls on. It should
410 // be the UI thread and all our methods should be called on this
411 // thread. Only for assertions.
412 MessageLoop* loop_;
413
[email protected]d7395e732009-08-28 23:13:43414 DISALLOW_COPY_AND_ASSIGN(GConfSettingGetterImplGConf);
415};
416
417// This is the KDE version that reads kioslaverc and simulates gconf.
418// Doing this allows the main Delegate code, as well as the unit tests
419// for it, to stay the same - and the settings map fairly well besides.
420class GConfSettingGetterImplKDE
421 : public ProxyConfigServiceLinux::GConfSettingGetter,
422 public base::MessagePumpLibevent::Watcher {
423 public:
[email protected]9bc8cff2010-04-03 01:05:39424 explicit GConfSettingGetterImplKDE(base::EnvVarGetter* env_var_getter)
[email protected]d7395e732009-08-28 23:13:43425 : inotify_fd_(-1), notify_delegate_(NULL), indirect_manual_(false),
[email protected]a48bf4a2010-06-14 18:24:53426 auto_no_pac_(false), reversed_bypass_list_(false),
[email protected]f18fde22010-05-18 23:49:54427 env_var_getter_(env_var_getter), file_loop_(NULL) {
428 // Derive the location of the kde config dir from the environment.
[email protected]92d2dc82010-04-08 17:49:59429 std::string home;
[email protected]2e8cfe22010-06-12 00:26:24430 if (env_var_getter->GetEnv("KDEHOME", &home) && !home.empty()) {
431 // $KDEHOME is set. Use it unconditionally.
[email protected]92d2dc82010-04-08 17:49:59432 kde_config_dir_ = KDEHomeToConfigPath(FilePath(home));
433 } else {
[email protected]2e8cfe22010-06-12 00:26:24434 // $KDEHOME is unset. Try to figure out what to use. This seems to be
[email protected]92d2dc82010-04-08 17:49:59435 // the common case on most distributions.
[email protected]574f6f0c2010-07-21 02:59:28436 if (!env_var_getter->GetEnv(base::env_vars::kHome, &home))
[email protected]d7395e732009-08-28 23:13:43437 // User has no $HOME? Give up. Later we'll report the failure.
438 return;
[email protected]92d2dc82010-04-08 17:49:59439 if (base::GetDesktopEnvironment(env_var_getter) ==
440 base::DESKTOP_ENVIRONMENT_KDE3) {
441 // KDE3 always uses .kde for its configuration.
442 FilePath kde_path = FilePath(home).Append(".kde");
443 kde_config_dir_ = KDEHomeToConfigPath(kde_path);
444 } else {
445 // Some distributions patch KDE4 to use .kde4 instead of .kde, so that
[email protected]fad9c8a52010-06-10 22:30:53446 // both can be installed side-by-side. Sadly they don't all do this, and
447 // they don't always do this: some distributions have started switching
448 // back as well. So if there is a .kde4 directory, check the timestamps
449 // of the config directories within and use the newest one.
[email protected]92d2dc82010-04-08 17:49:59450 // Note that we should currently be running in the UI thread, because in
451 // the gconf version, that is the only thread that can access the proxy
452 // settings (a gconf restriction). As noted below, the initial read of
453 // the proxy settings will be done in this thread anyway, so we check
454 // for .kde4 here in this thread as well.
[email protected]fad9c8a52010-06-10 22:30:53455 FilePath kde3_path = FilePath(home).Append(".kde");
456 FilePath kde3_config = KDEHomeToConfigPath(kde3_path);
[email protected]92d2dc82010-04-08 17:49:59457 FilePath kde4_path = FilePath(home).Append(".kde4");
[email protected]fad9c8a52010-06-10 22:30:53458 FilePath kde4_config = KDEHomeToConfigPath(kde4_path);
459 bool use_kde4 = false;
[email protected]92d2dc82010-04-08 17:49:59460 if (file_util::DirectoryExists(kde4_path)) {
[email protected]fad9c8a52010-06-10 22:30:53461 file_util::FileInfo kde3_info;
462 file_util::FileInfo kde4_info;
463 if (file_util::GetFileInfo(kde4_config, &kde4_info)) {
464 if (file_util::GetFileInfo(kde3_config, &kde3_info)) {
465 use_kde4 = kde4_info.last_modified >= kde3_info.last_modified;
466 } else {
467 use_kde4 = true;
468 }
469 }
470 }
471 if (use_kde4) {
[email protected]92d2dc82010-04-08 17:49:59472 kde_config_dir_ = KDEHomeToConfigPath(kde4_path);
473 } else {
[email protected]fad9c8a52010-06-10 22:30:53474 kde_config_dir_ = KDEHomeToConfigPath(kde3_path);
[email protected]92d2dc82010-04-08 17:49:59475 }
476 }
[email protected]d7395e732009-08-28 23:13:43477 }
[email protected]d7395e732009-08-28 23:13:43478 }
479
480 virtual ~GConfSettingGetterImplKDE() {
481 // inotify_fd_ should have been closed before now, from
482 // Delegate::OnDestroy(), while running on the file thread. However
483 // on exiting the process, it may happen that Delegate::OnDestroy()
484 // task is left pending on the file loop after the loop was quit,
485 // and pending tasks may then be deleted without being run.
486 // Here in the KDE version, we can safely close the file descriptor
487 // anyway. (Not that it really matters; the process is exiting.)
488 if (inotify_fd_ >= 0)
489 Shutdown();
490 DCHECK(inotify_fd_ < 0);
491 }
492
493 virtual bool Init(MessageLoop* glib_default_loop,
494 MessageLoopForIO* file_loop) {
495 DCHECK(inotify_fd_ < 0);
496 inotify_fd_ = inotify_init();
497 if (inotify_fd_ < 0) {
[email protected]57b765672009-10-13 18:27:40498 PLOG(ERROR) << "inotify_init failed";
[email protected]d7395e732009-08-28 23:13:43499 return false;
500 }
501 int flags = fcntl(inotify_fd_, F_GETFL);
502 if (fcntl(inotify_fd_, F_SETFL, flags | O_NONBLOCK) < 0) {
[email protected]57b765672009-10-13 18:27:40503 PLOG(ERROR) << "fcntl failed";
[email protected]d7395e732009-08-28 23:13:43504 close(inotify_fd_);
505 inotify_fd_ = -1;
506 return false;
507 }
508 file_loop_ = file_loop;
509 // The initial read is done on the current thread, not |file_loop_|,
510 // since we will need to have it for SetupAndFetchInitialConfig().
511 UpdateCachedSettings();
512 return true;
513 }
514
515 void Shutdown() {
516 if (inotify_fd_ >= 0) {
517 ResetCachedSettings();
518 inotify_watcher_.StopWatchingFileDescriptor();
519 close(inotify_fd_);
520 inotify_fd_ = -1;
521 }
522 }
523
524 bool SetupNotification(ProxyConfigServiceLinux::Delegate* delegate) {
525 DCHECK(inotify_fd_ >= 0);
526 DCHECK(file_loop_);
527 // We can't just watch the kioslaverc file directly, since KDE will write
528 // a new copy of it and then rename it whenever settings are changed and
529 // inotify watches inodes (so we'll be watching the old deleted file after
530 // the first change, and it will never change again). So, we watch the
531 // directory instead. We then act only on changes to the kioslaverc entry.
532 if (inotify_add_watch(inotify_fd_, kde_config_dir_.value().c_str(),
533 IN_MODIFY | IN_MOVED_TO) < 0)
534 return false;
535 notify_delegate_ = delegate;
536 return file_loop_->WatchFileDescriptor(inotify_fd_, true,
537 MessageLoopForIO::WATCH_READ, &inotify_watcher_, this);
538 }
539
[email protected]9a3d8d42009-09-03 17:01:46540 virtual MessageLoop* GetNotificationLoop() {
[email protected]d7395e732009-08-28 23:13:43541 return file_loop_;
542 }
543
544 // Implement base::MessagePumpLibevent::Delegate.
545 void OnFileCanReadWithoutBlocking(int fd) {
546 DCHECK(fd == inotify_fd_);
547 DCHECK(MessageLoop::current() == file_loop_);
548 OnChangeNotification();
549 }
550 void OnFileCanWriteWithoutBlocking(int fd) {
551 NOTREACHED();
552 }
553
554 virtual const char* GetDataSource() {
555 return "KDE";
556 }
557
558 virtual bool GetString(const char* key, std::string* result) {
559 string_map_type::iterator it = string_table_.find(key);
560 if (it == string_table_.end())
561 return false;
562 *result = it->second;
563 return true;
564 }
565 virtual bool GetBoolean(const char* key, bool* result) {
566 // We don't ever have any booleans.
567 return false;
568 }
569 virtual bool GetInt(const char* key, int* result) {
570 // We don't ever have any integers. (See AddProxy() below about ports.)
571 return false;
572 }
573 virtual bool GetStringList(const char* key,
574 std::vector<std::string>* result) {
575 strings_map_type::iterator it = strings_table_.find(key);
576 if (it == strings_table_.end())
577 return false;
578 *result = it->second;
579 return true;
580 }
581
[email protected]a48bf4a2010-06-14 18:24:53582 virtual bool BypassListIsReversed() {
583 return reversed_bypass_list_;
584 }
585
[email protected]1a597192010-07-09 16:58:38586 virtual bool MatchHostsUsingSuffixMatching() {
587 return true;
588 }
589
[email protected]d7395e732009-08-28 23:13:43590 private:
591 void ResetCachedSettings() {
592 string_table_.clear();
593 strings_table_.clear();
594 indirect_manual_ = false;
595 auto_no_pac_ = false;
[email protected]a48bf4a2010-06-14 18:24:53596 reversed_bypass_list_ = false;
[email protected]d7395e732009-08-28 23:13:43597 }
598
[email protected]92d2dc82010-04-08 17:49:59599 FilePath KDEHomeToConfigPath(const FilePath& kde_home) {
600 return kde_home.Append("share").Append("config");
601 }
602
[email protected]a8185d02010-06-11 00:19:50603 void AddProxy(const std::string& prefix, const std::string& value) {
[email protected]d7395e732009-08-28 23:13:43604 if (value.empty() || value.substr(0, 3) == "//:")
605 // No proxy.
606 return;
607 // We don't need to parse the port number out; GetProxyFromGConf()
608 // would only append it right back again. So we just leave the port
609 // number right in the host string.
610 string_table_[prefix + "host"] = value;
611 }
612
[email protected]a8185d02010-06-11 00:19:50613 void AddHostList(const std::string& key, const std::string& value) {
[email protected]f18fde22010-05-18 23:49:54614 std::vector<std::string> tokens;
[email protected]1a597192010-07-09 16:58:38615 StringTokenizer tk(value, ", ");
[email protected]f18fde22010-05-18 23:49:54616 while (tk.GetNext()) {
617 std::string token = tk.token();
618 if (!token.empty())
619 tokens.push_back(token);
620 }
621 strings_table_[key] = tokens;
622 }
623
[email protected]9a3d8d42009-09-03 17:01:46624 void AddKDESetting(const std::string& key, const std::string& value) {
[email protected]d7395e732009-08-28 23:13:43625 // The astute reader may notice that there is no mention of SOCKS
626 // here. That's because KDE handles socks is a strange way, and we
627 // don't support it. Rather than just a setting for the SOCKS server,
628 // it has a setting for a library to LD_PRELOAD in all your programs
629 // that will transparently SOCKSify them. Such libraries each have
630 // their own configuration, and thus, we can't get it from KDE.
631 if (key == "ProxyType") {
632 const char* mode = "none";
633 indirect_manual_ = false;
634 auto_no_pac_ = false;
635 switch (StringToInt(value)) {
636 case 0: // No proxy, or maybe kioslaverc syntax error.
637 break;
638 case 1: // Manual configuration.
639 mode = "manual";
640 break;
641 case 2: // PAC URL.
642 mode = "auto";
643 break;
644 case 3: // WPAD.
645 mode = "auto";
646 auto_no_pac_ = true;
647 break;
648 case 4: // Indirect manual via environment variables.
649 mode = "manual";
650 indirect_manual_ = true;
651 break;
652 }
653 string_table_["/system/proxy/mode"] = mode;
654 } else if (key == "Proxy Config Script") {
655 string_table_["/system/proxy/autoconfig_url"] = value;
656 } else if (key == "httpProxy") {
657 AddProxy("/system/http_proxy/", value);
658 } else if (key == "httpsProxy") {
659 AddProxy("/system/proxy/secure_", value);
660 } else if (key == "ftpProxy") {
661 AddProxy("/system/proxy/ftp_", value);
662 } else if (key == "ReversedException") {
663 // We count "true" or any nonzero number as true, otherwise false.
664 // Note that if the value is not actually numeric StringToInt()
665 // will return 0, which we count as false.
[email protected]a48bf4a2010-06-14 18:24:53666 reversed_bypass_list_ = (value == "true" || StringToInt(value));
[email protected]d7395e732009-08-28 23:13:43667 } else if (key == "NoProxyFor") {
[email protected]f18fde22010-05-18 23:49:54668 AddHostList("/system/http_proxy/ignore_hosts", value);
[email protected]d7395e732009-08-28 23:13:43669 } else if (key == "AuthMode") {
670 // Check for authentication, just so we can warn.
671 int mode = StringToInt(value);
672 if (mode) {
673 // ProxyConfig does not support authentication parameters, but
674 // Chrome will prompt for the password later. So we ignore this.
675 LOG(WARNING) <<
676 "Proxy authentication parameters ignored, see bug 16709";
677 }
678 }
679 }
680
[email protected]a8185d02010-06-11 00:19:50681 void ResolveIndirect(const std::string& key) {
[email protected]d7395e732009-08-28 23:13:43682 string_map_type::iterator it = string_table_.find(key);
683 if (it != string_table_.end()) {
[email protected]f18fde22010-05-18 23:49:54684 std::string value;
685 if (env_var_getter_->GetEnv(it->second.c_str(), &value))
[email protected]d7395e732009-08-28 23:13:43686 it->second = value;
[email protected]8425adc02010-04-18 17:45:31687 else
688 string_table_.erase(it);
[email protected]d7395e732009-08-28 23:13:43689 }
690 }
691
[email protected]a8185d02010-06-11 00:19:50692 void ResolveIndirectList(const std::string& key) {
[email protected]f18fde22010-05-18 23:49:54693 strings_map_type::iterator it = strings_table_.find(key);
694 if (it != strings_table_.end()) {
695 std::string value;
696 if (!it->second.empty() &&
697 env_var_getter_->GetEnv(it->second[0].c_str(), &value))
698 AddHostList(key, value);
699 else
700 strings_table_.erase(it);
701 }
702 }
703
[email protected]d7395e732009-08-28 23:13:43704 // The settings in kioslaverc could occur in any order, but some affect
705 // others. Rather than read the whole file in and then query them in an
706 // order that allows us to handle that, we read the settings in whatever
707 // order they occur and do any necessary tweaking after we finish.
708 void ResolveModeEffects() {
709 if (indirect_manual_) {
710 ResolveIndirect("/system/http_proxy/host");
711 ResolveIndirect("/system/proxy/secure_host");
712 ResolveIndirect("/system/proxy/ftp_host");
[email protected]f18fde22010-05-18 23:49:54713 ResolveIndirectList("/system/http_proxy/ignore_hosts");
[email protected]d7395e732009-08-28 23:13:43714 }
715 if (auto_no_pac_) {
716 // Remove the PAC URL; we're not supposed to use it.
717 string_table_.erase("/system/proxy/autoconfig_url");
718 }
[email protected]d7395e732009-08-28 23:13:43719 }
720
721 // Reads kioslaverc one line at a time and calls AddKDESetting() to add
722 // each relevant name-value pair to the appropriate value table.
723 void UpdateCachedSettings() {
[email protected]92d2dc82010-04-08 17:49:59724 FilePath kioslaverc = kde_config_dir_.Append("kioslaverc");
[email protected]d7395e732009-08-28 23:13:43725 file_util::ScopedFILE input(file_util::OpenFile(kioslaverc, "r"));
726 if (!input.get())
727 return;
728 ResetCachedSettings();
729 bool in_proxy_settings = false;
730 bool line_too_long = false;
[email protected]9a3d8d42009-09-03 17:01:46731 char line[BUFFER_SIZE];
732 // fgets() will return NULL on EOF or error.
[email protected]d7395e732009-08-28 23:13:43733 while (fgets(line, sizeof(line), input.get())) {
734 // fgets() guarantees the line will be properly terminated.
735 size_t length = strlen(line);
736 if (!length)
737 continue;
738 // This should be true even with CRLF endings.
739 if (line[length - 1] != '\n') {
740 line_too_long = true;
741 continue;
742 }
743 if (line_too_long) {
744 // The previous line had no line ending, but this done does. This is
745 // the end of the line that was too long, so warn here and skip it.
746 LOG(WARNING) << "skipped very long line in " << kioslaverc.value();
747 line_too_long = false;
748 continue;
749 }
750 // Remove the LF at the end, and the CR if there is one.
751 line[--length] = '\0';
752 if (length && line[length - 1] == '\r')
753 line[--length] = '\0';
754 // Now parse the line.
755 if (line[0] == '[') {
756 // Switching sections. All we care about is whether this is
757 // the (a?) proxy settings section, for both KDE3 and KDE4.
758 in_proxy_settings = !strncmp(line, "[Proxy Settings]", 16);
759 } else if (in_proxy_settings) {
760 // A regular line, in the (a?) proxy settings section.
[email protected]9a3d8d42009-09-03 17:01:46761 char* split = strchr(line, '=');
762 // Skip this line if it does not contain an = sign.
763 if (!split)
[email protected]d7395e732009-08-28 23:13:43764 continue;
[email protected]9a3d8d42009-09-03 17:01:46765 // Split the line on the = and advance |split|.
766 *(split++) = 0;
767 std::string key = line;
768 std::string value = split;
769 TrimWhitespaceASCII(key, TRIM_ALL, &key);
770 TrimWhitespaceASCII(value, TRIM_ALL, &value);
771 // Skip this line if the key name is empty.
772 if (key.empty())
[email protected]d7395e732009-08-28 23:13:43773 continue;
774 // Is the value name localized?
[email protected]9a3d8d42009-09-03 17:01:46775 if (key[key.length() - 1] == ']') {
776 // Find the matching bracket.
777 length = key.rfind('[');
778 // Skip this line if the localization indicator is malformed.
779 if (length == std::string::npos)
[email protected]d7395e732009-08-28 23:13:43780 continue;
781 // Trim the localization indicator off.
[email protected]9a3d8d42009-09-03 17:01:46782 key.resize(length);
783 // Remove any resulting trailing whitespace.
784 TrimWhitespaceASCII(key, TRIM_TRAILING, &key);
785 // Skip this line if the key name is now empty.
786 if (key.empty())
787 continue;
[email protected]d7395e732009-08-28 23:13:43788 }
[email protected]d7395e732009-08-28 23:13:43789 // Now fill in the tables.
[email protected]9a3d8d42009-09-03 17:01:46790 AddKDESetting(key, value);
[email protected]d7395e732009-08-28 23:13:43791 }
792 }
793 if (ferror(input.get()))
794 LOG(ERROR) << "error reading " << kioslaverc.value();
795 ResolveModeEffects();
796 }
797
798 // This is the callback from the debounce timer.
799 void OnDebouncedNotification() {
800 DCHECK(MessageLoop::current() == file_loop_);
801 LOG(INFO) << "inotify change notification for kioslaverc";
802 UpdateCachedSettings();
803 DCHECK(notify_delegate_);
804 // Forward to a method on the proxy config service delegate object.
805 notify_delegate_->OnCheckProxyConfigSettings();
806 }
807
808 // Called by OnFileCanReadWithoutBlocking() on the file thread. Reads
809 // from the inotify file descriptor and starts up a debounce timer if
810 // an event for kioslaverc is seen.
811 void OnChangeNotification() {
812 DCHECK(inotify_fd_ >= 0);
813 DCHECK(MessageLoop::current() == file_loop_);
814 char event_buf[(sizeof(inotify_event) + NAME_MAX + 1) * 4];
815 bool kioslaverc_touched = false;
816 ssize_t r;
817 while ((r = read(inotify_fd_, event_buf, sizeof(event_buf))) > 0) {
818 // inotify returns variable-length structures, which is why we have
819 // this strange-looking loop instead of iterating through an array.
820 char* event_ptr = event_buf;
821 while (event_ptr < event_buf + r) {
822 inotify_event* event = reinterpret_cast<inotify_event*>(event_ptr);
823 // The kernel always feeds us whole events.
[email protected]b1f031dd2010-03-02 23:19:33824 CHECK_LE(event_ptr + sizeof(inotify_event), event_buf + r);
825 CHECK_LE(event->name + event->len, event_buf + r);
[email protected]d7395e732009-08-28 23:13:43826 if (!strcmp(event->name, "kioslaverc"))
827 kioslaverc_touched = true;
828 // Advance the pointer just past the end of the filename.
829 event_ptr = event->name + event->len;
830 }
831 // We keep reading even if |kioslaverc_touched| is true to drain the
832 // inotify event queue.
833 }
834 if (!r)
835 // Instead of returning -1 and setting errno to EINVAL if there is not
836 // enough buffer space, older kernels (< 2.6.21) return 0. Simulate the
837 // new behavior (EINVAL) so we can reuse the code below.
838 errno = EINVAL;
839 if (errno != EAGAIN) {
[email protected]57b765672009-10-13 18:27:40840 PLOG(WARNING) << "error reading inotify file descriptor";
[email protected]d7395e732009-08-28 23:13:43841 if (errno == EINVAL) {
842 // Our buffer is not large enough to read the next event. This should
843 // not happen (because its size is calculated to always be sufficiently
844 // large), but if it does we'd warn continuously since |inotify_fd_|
845 // would be forever ready to read. Close it and stop watching instead.
846 LOG(ERROR) << "inotify failure; no longer watching kioslaverc!";
847 inotify_watcher_.StopWatchingFileDescriptor();
848 close(inotify_fd_);
849 inotify_fd_ = -1;
850 }
851 }
852 if (kioslaverc_touched) {
853 // We don't use Reset() because the timer may not yet be running.
854 // (In that case Stop() is a no-op.)
855 debounce_timer_.Stop();
856 debounce_timer_.Start(base::TimeDelta::FromMilliseconds(
857 kDebounceTimeoutMilliseconds), this,
858 &GConfSettingGetterImplKDE::OnDebouncedNotification);
859 }
860 }
861
862 typedef std::map<std::string, std::string> string_map_type;
863 typedef std::map<std::string, std::vector<std::string> > strings_map_type;
864
865 int inotify_fd_;
866 base::MessagePumpLibevent::FileDescriptorWatcher inotify_watcher_;
867 ProxyConfigServiceLinux::Delegate* notify_delegate_;
868 base::OneShotTimer<GConfSettingGetterImplKDE> debounce_timer_;
869 FilePath kde_config_dir_;
870 bool indirect_manual_;
871 bool auto_no_pac_;
[email protected]a48bf4a2010-06-14 18:24:53872 bool reversed_bypass_list_;
[email protected]f18fde22010-05-18 23:49:54873 // We don't own |env_var_getter_|. It's safe to hold a pointer to it, since
874 // both it and us are owned by ProxyConfigServiceLinux::Delegate, and have the
875 // same lifetime.
876 base::EnvVarGetter* env_var_getter_;
[email protected]d7395e732009-08-28 23:13:43877
878 // We cache these settings whenever we re-read the kioslaverc file.
879 string_map_type string_table_;
880 strings_map_type strings_table_;
881
882 // Message loop of the file thread, for reading kioslaverc. If NULL,
883 // just read it directly (for testing). We also handle inotify events
884 // on this thread.
885 MessageLoopForIO* file_loop_;
886
887 DISALLOW_COPY_AND_ASSIGN(GConfSettingGetterImplKDE);
[email protected]861c6c62009-04-20 16:50:56888};
889
890} // namespace
891
[email protected]3e44697f2009-05-22 14:37:39892bool ProxyConfigServiceLinux::Delegate::GetProxyFromGConf(
[email protected]861c6c62009-04-20 16:50:56893 const char* key_prefix, bool is_socks, ProxyServer* result_server) {
894 std::string key(key_prefix);
895 std::string host;
896 if (!gconf_getter_->GetString((key + "host").c_str(), &host)
897 || host.empty()) {
898 // Unset or empty.
899 return false;
900 }
901 // Check for an optional port.
[email protected]d7395e732009-08-28 23:13:43902 int port = 0;
[email protected]861c6c62009-04-20 16:50:56903 gconf_getter_->GetInt((key + "port").c_str(), &port);
904 if (port != 0) {
905 // If a port is set and non-zero:
906 host += ":" + IntToString(port);
907 }
908 host = FixupProxyHostScheme(
909 is_socks ? ProxyServer::SCHEME_SOCKS4 : ProxyServer::SCHEME_HTTP,
910 host);
[email protected]87a102b2009-07-14 05:23:30911 ProxyServer proxy_server = ProxyServer::FromURI(host,
912 ProxyServer::SCHEME_HTTP);
[email protected]861c6c62009-04-20 16:50:56913 if (proxy_server.is_valid()) {
914 *result_server = proxy_server;
915 return true;
916 }
917 return false;
918}
919
[email protected]3e44697f2009-05-22 14:37:39920bool ProxyConfigServiceLinux::Delegate::GetConfigFromGConf(
921 ProxyConfig* config) {
[email protected]861c6c62009-04-20 16:50:56922 std::string mode;
923 if (!gconf_getter_->GetString("/system/proxy/mode", &mode)) {
924 // We expect this to always be set, so if we don't see it then we
925 // probably have a gconf problem, and so we don't have a valid
926 // proxy config.
927 return false;
928 }
[email protected]3e44697f2009-05-22 14:37:39929 if (mode == "none") {
[email protected]861c6c62009-04-20 16:50:56930 // Specifically specifies no proxy.
931 return true;
[email protected]3e44697f2009-05-22 14:37:39932 }
[email protected]861c6c62009-04-20 16:50:56933
[email protected]3e44697f2009-05-22 14:37:39934 if (mode == "auto") {
[email protected]861c6c62009-04-20 16:50:56935 // automatic proxy config
936 std::string pac_url_str;
937 if (gconf_getter_->GetString("/system/proxy/autoconfig_url",
938 &pac_url_str)) {
939 if (!pac_url_str.empty()) {
940 GURL pac_url(pac_url_str);
941 if (!pac_url.is_valid())
942 return false;
[email protected]ed4ed0f2010-02-24 00:20:48943 config->set_pac_url(pac_url);
[email protected]861c6c62009-04-20 16:50:56944 return true;
945 }
946 }
[email protected]ed4ed0f2010-02-24 00:20:48947 config->set_auto_detect(true);
[email protected]861c6c62009-04-20 16:50:56948 return true;
949 }
950
[email protected]3e44697f2009-05-22 14:37:39951 if (mode != "manual") {
[email protected]861c6c62009-04-20 16:50:56952 // Mode is unrecognized.
953 return false;
954 }
955 bool use_http_proxy;
956 if (gconf_getter_->GetBoolean("/system/http_proxy/use_http_proxy",
957 &use_http_proxy)
958 && !use_http_proxy) {
959 // Another master switch for some reason. If set to false, then no
960 // proxy. But we don't panic if the key doesn't exist.
961 return true;
962 }
963
964 bool same_proxy = false;
965 // Indicates to use the http proxy for all protocols. This one may
966 // not exist (presumably on older versions), assume false in that
967 // case.
968 gconf_getter_->GetBoolean("/system/http_proxy/use_same_proxy",
969 &same_proxy);
970
971 ProxyServer proxy_server;
972 if (!same_proxy) {
973 // Try socks.
974 if (GetProxyFromGConf("/system/proxy/socks_", true, &proxy_server)) {
975 // gconf settings do not appear to distinguish between socks
976 // version. We default to version 4.
[email protected]ed4ed0f2010-02-24 00:20:48977 config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
978 config->proxy_rules().single_proxy = proxy_server;
[email protected]861c6c62009-04-20 16:50:56979 }
980 }
[email protected]ed4ed0f2010-02-24 00:20:48981 if (config->proxy_rules().empty()) {
[email protected]861c6c62009-04-20 16:50:56982 bool have_http = GetProxyFromGConf("/system/http_proxy/", false,
983 &proxy_server);
984 if (same_proxy) {
985 if (have_http) {
[email protected]ed4ed0f2010-02-24 00:20:48986 config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
987 config->proxy_rules().single_proxy = proxy_server;
[email protected]861c6c62009-04-20 16:50:56988 }
989 } else {
990 // Protocol specific settings.
991 if (have_http)
[email protected]ed4ed0f2010-02-24 00:20:48992 config->proxy_rules().proxy_for_http = proxy_server;
[email protected]861c6c62009-04-20 16:50:56993 bool have_secure = GetProxyFromGConf("/system/proxy/secure_", false,
994 &proxy_server);
995 if (have_secure)
[email protected]ed4ed0f2010-02-24 00:20:48996 config->proxy_rules().proxy_for_https = proxy_server;
[email protected]861c6c62009-04-20 16:50:56997 bool have_ftp = GetProxyFromGConf("/system/proxy/ftp_", false,
998 &proxy_server);
999 if (have_ftp)
[email protected]ed4ed0f2010-02-24 00:20:481000 config->proxy_rules().proxy_for_ftp = proxy_server;
[email protected]861c6c62009-04-20 16:50:561001 if (have_http || have_secure || have_ftp)
[email protected]ed4ed0f2010-02-24 00:20:481002 config->proxy_rules().type =
[email protected]861c6c62009-04-20 16:50:561003 ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
1004 }
1005 }
1006
[email protected]ed4ed0f2010-02-24 00:20:481007 if (config->proxy_rules().empty()) {
[email protected]861c6c62009-04-20 16:50:561008 // Manual mode but we couldn't parse any rules.
1009 return false;
1010 }
1011
1012 // Check for authentication, just so we can warn.
[email protected]d7395e732009-08-28 23:13:431013 bool use_auth = false;
[email protected]861c6c62009-04-20 16:50:561014 gconf_getter_->GetBoolean("/system/http_proxy/use_authentication",
1015 &use_auth);
[email protected]62749f182009-07-15 13:16:541016 if (use_auth) {
1017 // ProxyConfig does not support authentication parameters, but
1018 // Chrome will prompt for the password later. So we ignore
1019 // /system/http_proxy/*auth* settings.
1020 LOG(WARNING) << "Proxy authentication parameters ignored, see bug 16709";
1021 }
[email protected]861c6c62009-04-20 16:50:561022
1023 // Now the bypass list.
[email protected]7541206c2010-02-19 20:24:061024 std::vector<std::string> ignore_hosts_list;
[email protected]ed4ed0f2010-02-24 00:20:481025 config->proxy_rules().bypass_rules.Clear();
[email protected]a8185d02010-06-11 00:19:501026 if (gconf_getter_->GetStringList("/system/http_proxy/ignore_hosts",
1027 &ignore_hosts_list)) {
1028 std::vector<std::string>::const_iterator it(ignore_hosts_list.begin());
[email protected]1a597192010-07-09 16:58:381029 for (; it != ignore_hosts_list.end(); ++it) {
1030 if (gconf_getter_->MatchHostsUsingSuffixMatching()) {
1031 config->proxy_rules().bypass_rules.
1032 AddRuleFromStringUsingSuffixMatching(*it);
1033 } else {
1034 config->proxy_rules().bypass_rules.AddRuleFromString(*it);
1035 }
1036 }
[email protected]a8185d02010-06-11 00:19:501037 }
[email protected]861c6c62009-04-20 16:50:561038 // Note that there are no settings with semantics corresponding to
[email protected]1a597192010-07-09 16:58:381039 // bypass of local names in GNOME. In KDE, "<local>" is supported
1040 // as a hostname rule.
[email protected]861c6c62009-04-20 16:50:561041
[email protected]a48bf4a2010-06-14 18:24:531042 // KDE allows one to reverse the bypass rules.
1043 config->proxy_rules().reverse_bypass = gconf_getter_->BypassListIsReversed();
1044
[email protected]861c6c62009-04-20 16:50:561045 return true;
1046}
1047
[email protected]9bc8cff2010-04-03 01:05:391048ProxyConfigServiceLinux::Delegate::Delegate(base::EnvVarGetter* env_var_getter)
[email protected]d7395e732009-08-28 23:13:431049 : env_var_getter_(env_var_getter),
1050 glib_default_loop_(NULL), io_loop_(NULL) {
1051 // Figure out which GConfSettingGetterImpl to use, if any.
1052 switch (base::GetDesktopEnvironment(env_var_getter)) {
1053 case base::DESKTOP_ENVIRONMENT_GNOME:
1054 gconf_getter_.reset(new GConfSettingGetterImplGConf());
1055 break;
1056 case base::DESKTOP_ENVIRONMENT_KDE3:
1057 case base::DESKTOP_ENVIRONMENT_KDE4:
1058 gconf_getter_.reset(new GConfSettingGetterImplKDE(env_var_getter));
1059 break;
[email protected]88c50ab2010-03-26 20:04:061060 case base::DESKTOP_ENVIRONMENT_XFCE:
[email protected]d7395e732009-08-28 23:13:431061 case base::DESKTOP_ENVIRONMENT_OTHER:
1062 break;
1063 }
1064}
1065
[email protected]9bc8cff2010-04-03 01:05:391066ProxyConfigServiceLinux::Delegate::Delegate(base::EnvVarGetter* env_var_getter,
[email protected]861c6c62009-04-20 16:50:561067 GConfSettingGetter* gconf_getter)
[email protected]3e44697f2009-05-22 14:37:391068 : env_var_getter_(env_var_getter), gconf_getter_(gconf_getter),
1069 glib_default_loop_(NULL), io_loop_(NULL) {
[email protected]861c6c62009-04-20 16:50:561070}
1071
[email protected]3e44697f2009-05-22 14:37:391072void ProxyConfigServiceLinux::Delegate::SetupAndFetchInitialConfig(
[email protected]d7395e732009-08-28 23:13:431073 MessageLoop* glib_default_loop, MessageLoop* io_loop,
1074 MessageLoopForIO* file_loop) {
[email protected]3e44697f2009-05-22 14:37:391075 // We should be running on the default glib main loop thread right
1076 // now. gconf can only be accessed from this thread.
1077 DCHECK(MessageLoop::current() == glib_default_loop);
1078 glib_default_loop_ = glib_default_loop;
1079 io_loop_ = io_loop;
1080
[email protected]d7395e732009-08-28 23:13:431081 // If we are passed a NULL io_loop or file_loop, then we don't set up
1082 // proxy setting change notifications. This should not be the usual
1083 // case but is intended to simplify test setups.
1084 if (!io_loop_ || !file_loop)
1085 LOG(INFO) << "Monitoring of proxy setting changes is disabled";
[email protected]3e44697f2009-05-22 14:37:391086
1087 // Fetch and cache the current proxy config. The config is left in
[email protected]119655002010-07-23 06:02:401088 // cached_config_, where GetLatestProxyConfig() running on the IO thread
[email protected]3e44697f2009-05-22 14:37:391089 // will expect to find it. This is safe to do because we return
1090 // before this ProxyConfigServiceLinux is passed on to
1091 // the ProxyService.
[email protected]d6cb85b2009-07-23 22:10:531092
1093 // Note: It would be nice to prioritize environment variables
[email protected]92d2dc82010-04-08 17:49:591094 // and only fall back to gconf if env vars were unset. But
[email protected]d6cb85b2009-07-23 22:10:531095 // gnome-terminal "helpfully" sets http_proxy and no_proxy, and it
1096 // does so even if the proxy mode is set to auto, which would
1097 // mislead us.
1098
[email protected]3e44697f2009-05-22 14:37:391099 bool got_config = false;
[email protected]d7395e732009-08-28 23:13:431100 if (gconf_getter_.get()) {
1101 if (gconf_getter_->Init(glib_default_loop, file_loop) &&
1102 (!io_loop || !file_loop || gconf_getter_->SetupNotification(this))) {
1103 if (GetConfigFromGConf(&cached_config_)) {
1104 cached_config_.set_id(1); // mark it as valid
1105 got_config = true;
1106 LOG(INFO) << "Obtained proxy settings from " <<
1107 gconf_getter_->GetDataSource();
1108 // If gconf proxy mode is "none", meaning direct, then we take
1109 // that to be a valid config and will not check environment
1110 // variables. The alternative would have been to look for a proxy
1111 // whereever we can find one.
1112 //
1113 // Keep a copy of the config for use from this thread for
1114 // comparison with updated settings when we get notifications.
1115 reference_config_ = cached_config_;
1116 reference_config_.set_id(1); // mark it as valid
1117 } else {
1118 gconf_getter_->Shutdown(); // Stop notifications
[email protected]d6cb85b2009-07-23 22:10:531119 }
[email protected]d7395e732009-08-28 23:13:431120 }
[email protected]861c6c62009-04-20 16:50:561121 }
[email protected]d6cb85b2009-07-23 22:10:531122
[email protected]3e44697f2009-05-22 14:37:391123 if (!got_config) {
[email protected]d6cb85b2009-07-23 22:10:531124 // We fall back on environment variables.
[email protected]3e44697f2009-05-22 14:37:391125 //
1126 // Consulting environment variables doesn't need to be done from
1127 // the default glib main loop, but it's a tiny enough amount of
1128 // work.
1129 if (GetConfigFromEnv(&cached_config_)) {
1130 cached_config_.set_id(1); // mark it as valid
[email protected]d7395e732009-08-28 23:13:431131 LOG(INFO) << "Obtained proxy settings from environment variables";
[email protected]3e44697f2009-05-22 14:37:391132 }
[email protected]861c6c62009-04-20 16:50:561133 }
[email protected]3e44697f2009-05-22 14:37:391134}
1135
[email protected]119655002010-07-23 06:02:401136void ProxyConfigServiceLinux::Delegate::AddObserver(Observer* observer) {
1137 observers_.AddObserver(observer);
1138}
1139
1140void ProxyConfigServiceLinux::Delegate::RemoveObserver(Observer* observer) {
1141 observers_.RemoveObserver(observer);
1142}
1143
1144bool ProxyConfigServiceLinux::Delegate::GetLatestProxyConfig(
1145 ProxyConfig* config) {
[email protected]3e44697f2009-05-22 14:37:391146 // This is called from the IO thread.
1147 DCHECK(!io_loop_ || MessageLoop::current() == io_loop_);
1148
1149 // Simply return the last proxy configuration that glib_default_loop
1150 // notified us of.
[email protected]119655002010-07-23 06:02:401151 *config = cached_config_.is_valid() ?
1152 cached_config_ : ProxyConfig::CreateDirect();
1153
1154 // We return true to indicate that *config was filled in. It is always
1155 // going to be available since we initialized eagerly on the UI thread.
1156 // TODO(eroman): do lazy initialization instead, so we no longer need
1157 // to construct ProxyConfigServiceLinux on the UI thread.
1158 // In which case, we may return false here.
1159 return true;
[email protected]3e44697f2009-05-22 14:37:391160}
1161
[email protected]d7395e732009-08-28 23:13:431162// Depending on the GConfSettingGetter in use, this method will be called
1163// on either the UI thread (GConf) or the file thread (KDE).
[email protected]3e44697f2009-05-22 14:37:391164void ProxyConfigServiceLinux::Delegate::OnCheckProxyConfigSettings() {
[email protected]d7395e732009-08-28 23:13:431165 MessageLoop* required_loop = gconf_getter_->GetNotificationLoop();
1166 DCHECK(!required_loop || MessageLoop::current() == required_loop);
[email protected]3e44697f2009-05-22 14:37:391167 ProxyConfig new_config;
1168 bool valid = GetConfigFromGConf(&new_config);
1169 if (valid)
1170 new_config.set_id(1); // mark it as valid
1171
[email protected]119655002010-07-23 06:02:401172 // See if it is different from what we had before.
[email protected]3e44697f2009-05-22 14:37:391173 if (new_config.is_valid() != reference_config_.is_valid() ||
1174 !new_config.Equals(reference_config_)) {
1175 // Post a task to |io_loop| with the new configuration, so it can
1176 // update |cached_config_|.
1177 io_loop_->PostTask(
1178 FROM_HERE,
1179 NewRunnableMethod(
1180 this,
1181 &ProxyConfigServiceLinux::Delegate::SetNewProxyConfig,
1182 new_config));
[email protected]d1f9d472009-08-13 19:59:301183 // Update the thread-private copy in |reference_config_| as well.
1184 reference_config_ = new_config;
[email protected]3e44697f2009-05-22 14:37:391185 }
1186}
1187
1188void ProxyConfigServiceLinux::Delegate::SetNewProxyConfig(
1189 const ProxyConfig& new_config) {
1190 DCHECK(MessageLoop::current() == io_loop_);
1191 LOG(INFO) << "Proxy configuration changed";
1192 cached_config_ = new_config;
[email protected]119655002010-07-23 06:02:401193 FOR_EACH_OBSERVER(Observer, observers_, OnProxyConfigChanged(new_config));
[email protected]3e44697f2009-05-22 14:37:391194}
1195
1196void ProxyConfigServiceLinux::Delegate::PostDestroyTask() {
[email protected]d7395e732009-08-28 23:13:431197 if (!gconf_getter_.get())
1198 return;
1199 MessageLoop* shutdown_loop = gconf_getter_->GetNotificationLoop();
1200 if (!shutdown_loop || MessageLoop::current() == shutdown_loop) {
[email protected]3e44697f2009-05-22 14:37:391201 // Already on the right thread, call directly.
1202 // This is the case for the unittests.
1203 OnDestroy();
1204 } else {
[email protected]d7395e732009-08-28 23:13:431205 // Post to shutdown thread. Note that on browser shutdown, we may quit
1206 // this MessageLoop and exit the program before ever running this.
1207 shutdown_loop->PostTask(
[email protected]3e44697f2009-05-22 14:37:391208 FROM_HERE,
1209 NewRunnableMethod(
1210 this,
1211 &ProxyConfigServiceLinux::Delegate::OnDestroy));
1212 }
1213}
1214void ProxyConfigServiceLinux::Delegate::OnDestroy() {
[email protected]d7395e732009-08-28 23:13:431215 MessageLoop* shutdown_loop = gconf_getter_->GetNotificationLoop();
1216 DCHECK(!shutdown_loop || MessageLoop::current() == shutdown_loop);
1217 gconf_getter_->Shutdown();
[email protected]3e44697f2009-05-22 14:37:391218}
1219
1220ProxyConfigServiceLinux::ProxyConfigServiceLinux()
[email protected]9bc8cff2010-04-03 01:05:391221 : delegate_(new Delegate(base::EnvVarGetter::Create())) {
[email protected]3e44697f2009-05-22 14:37:391222}
1223
1224ProxyConfigServiceLinux::ProxyConfigServiceLinux(
[email protected]9bc8cff2010-04-03 01:05:391225 base::EnvVarGetter* env_var_getter)
[email protected]9a3d8d42009-09-03 17:01:461226 : delegate_(new Delegate(env_var_getter)) {
1227}
1228
1229ProxyConfigServiceLinux::ProxyConfigServiceLinux(
[email protected]9bc8cff2010-04-03 01:05:391230 base::EnvVarGetter* env_var_getter,
[email protected]3e44697f2009-05-22 14:37:391231 GConfSettingGetter* gconf_getter)
1232 : delegate_(new Delegate(env_var_getter, gconf_getter)) {
[email protected]861c6c62009-04-20 16:50:561233}
1234
1235} // namespace net