1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// 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
7#include <errno.h>
8#include <fcntl.h>
9#if defined(USE_GCONF)
10#include <gconf/gconf-client.h>
11#endif  // defined(USE_GCONF)
12#include <limits.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <sys/inotify.h>
16#include <unistd.h>
17
18#include <map>
19
20#include "base/bind.h"
21#include "base/compiler_specific.h"
22#include "base/debug/leak_annotations.h"
23#include "base/environment.h"
24#include "base/files/file_path.h"
25#include "base/files/file_util.h"
26#include "base/files/scoped_file.h"
27#include "base/logging.h"
28#include "base/message_loop/message_loop.h"
29#include "base/nix/xdg_util.h"
30#include "base/single_thread_task_runner.h"
31#include "base/strings/string_number_conversions.h"
32#include "base/strings/string_tokenizer.h"
33#include "base/strings/string_util.h"
34#include "base/threading/thread_restrictions.h"
35#include "base/timer/timer.h"
36#include "net/base/net_errors.h"
37#include "net/http/http_util.h"
38#include "net/proxy/proxy_config.h"
39#include "net/proxy/proxy_server.h"
40#include "url/url_canon.h"
41
42#if defined(USE_GIO)
43#include "library_loaders/libgio.h"
44#endif  // defined(USE_GIO)
45
46namespace net {
47
48namespace {
49
50// Given a proxy hostname from a setting, returns that hostname with
51// an appropriate proxy server scheme prefix.
52// scheme indicates the desired proxy scheme: usually http, with
53// socks 4 or 5 as special cases.
54// TODO(arindam): Remove URI string manipulation by using MapUrlSchemeToProxy.
55std::string FixupProxyHostScheme(ProxyServer::Scheme scheme,
56                                 std::string host) {
57  if (scheme == ProxyServer::SCHEME_SOCKS5 &&
58      StartsWithASCII(host, "socks4://", false)) {
59    // We default to socks 5, but if the user specifically set it to
60    // socks4://, then use that.
61    scheme = ProxyServer::SCHEME_SOCKS4;
62  }
63  // Strip the scheme if any.
64  std::string::size_type colon = host.find("://");
65  if (colon != std::string::npos)
66    host = host.substr(colon + 3);
67  // If a username and perhaps password are specified, give a warning.
68  std::string::size_type at_sign = host.find("@");
69  // Should this be supported?
70  if (at_sign != std::string::npos) {
71    // ProxyConfig does not support authentication parameters, but Chrome
72    // will prompt for the password later. Disregard the
73    // authentication parameters and continue with this hostname.
74    LOG(WARNING) << "Proxy authentication parameters ignored, see bug 16709";
75    host = host.substr(at_sign + 1);
76  }
77  // If this is a socks proxy, prepend a scheme so as to tell
78  // ProxyServer. This also allows ProxyServer to choose the right
79  // default port.
80  if (scheme == ProxyServer::SCHEME_SOCKS4)
81    host = "socks4://" + host;
82  else if (scheme == ProxyServer::SCHEME_SOCKS5)
83    host = "socks5://" + host;
84  // If there is a trailing slash, remove it so |host| will parse correctly
85  // even if it includes a port number (since the slash is not numeric).
86  if (host.length() && host[host.length() - 1] == '/')
87    host.resize(host.length() - 1);
88  return host;
89}
90
91}  // namespace
92
93ProxyConfigServiceLinux::Delegate::~Delegate() {
94}
95
96bool ProxyConfigServiceLinux::Delegate::GetProxyFromEnvVarForScheme(
97    const char* variable, ProxyServer::Scheme scheme,
98    ProxyServer* result_server) {
99  std::string env_value;
100  if (env_var_getter_->GetVar(variable, &env_value)) {
101    if (!env_value.empty()) {
102      env_value = FixupProxyHostScheme(scheme, env_value);
103      ProxyServer proxy_server =
104          ProxyServer::FromURI(env_value, ProxyServer::SCHEME_HTTP);
105      if (proxy_server.is_valid() && !proxy_server.is_direct()) {
106        *result_server = proxy_server;
107        return true;
108      } else {
109        LOG(ERROR) << "Failed to parse environment variable " << variable;
110      }
111    }
112  }
113  return false;
114}
115
116bool ProxyConfigServiceLinux::Delegate::GetProxyFromEnvVar(
117    const char* variable, ProxyServer* result_server) {
118  return GetProxyFromEnvVarForScheme(variable, ProxyServer::SCHEME_HTTP,
119                                     result_server);
120}
121
122bool ProxyConfigServiceLinux::Delegate::GetConfigFromEnv(ProxyConfig* config) {
123  // Check for automatic configuration first, in
124  // "auto_proxy". Possibly only the "environment_proxy" firefox
125  // extension has ever used this, but it still sounds like a good
126  // idea.
127  std::string auto_proxy;
128  if (env_var_getter_->GetVar("auto_proxy", &auto_proxy)) {
129    if (auto_proxy.empty()) {
130      // Defined and empty => autodetect
131      config->set_auto_detect(true);
132    } else {
133      // specified autoconfig URL
134      config->set_pac_url(GURL(auto_proxy));
135    }
136    return true;
137  }
138  // "all_proxy" is a shortcut to avoid defining {http,https,ftp}_proxy.
139  ProxyServer proxy_server;
140  if (GetProxyFromEnvVar("all_proxy", &proxy_server)) {
141    config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
142    config->proxy_rules().single_proxies.SetSingleProxyServer(proxy_server);
143  } else {
144    bool have_http = GetProxyFromEnvVar("http_proxy", &proxy_server);
145    if (have_http)
146      config->proxy_rules().proxies_for_http.SetSingleProxyServer(proxy_server);
147    // It would be tempting to let http_proxy apply for all protocols
148    // if https_proxy and ftp_proxy are not defined. Googling turns up
149    // several documents that mention only http_proxy. But then the
150    // user really might not want to proxy https. And it doesn't seem
151    // like other apps do this. So we will refrain.
152    bool have_https = GetProxyFromEnvVar("https_proxy", &proxy_server);
153    if (have_https)
154      config->proxy_rules().proxies_for_https.
155          SetSingleProxyServer(proxy_server);
156    bool have_ftp = GetProxyFromEnvVar("ftp_proxy", &proxy_server);
157    if (have_ftp)
158      config->proxy_rules().proxies_for_ftp.SetSingleProxyServer(proxy_server);
159    if (have_http || have_https || have_ftp) {
160      // mustn't change type unless some rules are actually set.
161      config->proxy_rules().type =
162          ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
163    }
164  }
165  if (config->proxy_rules().empty()) {
166    // If the above were not defined, try for socks.
167    // For environment variables, we default to version 5, per the gnome
168    // documentation: http://library.gnome.org/devel/gnet/stable/gnet-socks.html
169    ProxyServer::Scheme scheme = ProxyServer::SCHEME_SOCKS5;
170    std::string env_version;
171    if (env_var_getter_->GetVar("SOCKS_VERSION", &env_version)
172        && env_version == "4")
173      scheme = ProxyServer::SCHEME_SOCKS4;
174    if (GetProxyFromEnvVarForScheme("SOCKS_SERVER", scheme, &proxy_server)) {
175      config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
176      config->proxy_rules().single_proxies.SetSingleProxyServer(proxy_server);
177    }
178  }
179  // Look for the proxy bypass list.
180  std::string no_proxy;
181  env_var_getter_->GetVar("no_proxy", &no_proxy);
182  if (config->proxy_rules().empty()) {
183    // Having only "no_proxy" set, presumably to "*", makes it
184    // explicit that env vars do specify a configuration: having no
185    // rules specified only means the user explicitly asks for direct
186    // connections.
187    return !no_proxy.empty();
188  }
189  // Note that this uses "suffix" matching. So a bypass of "google.com"
190  // is understood to mean a bypass of "*google.com".
191  config->proxy_rules().bypass_rules.ParseFromStringUsingSuffixMatching(
192      no_proxy);
193  return true;
194}
195
196namespace {
197
198const int kDebounceTimeoutMilliseconds = 250;
199
200#if defined(USE_GCONF)
201// This setting getter uses gconf, as used in GNOME 2 and some GNOME 3 desktops.
202class SettingGetterImplGConf : public ProxyConfigServiceLinux::SettingGetter {
203 public:
204  SettingGetterImplGConf()
205      : client_(NULL), system_proxy_id_(0), system_http_proxy_id_(0),
206        notify_delegate_(NULL) {
207  }
208
209  virtual ~SettingGetterImplGConf() {
210    // client_ should have been released before now, from
211    // Delegate::OnDestroy(), while running on the UI thread. However
212    // on exiting the process, it may happen that Delegate::OnDestroy()
213    // task is left pending on the glib loop after the loop was quit,
214    // and pending tasks may then be deleted without being run.
215    if (client_) {
216      // gconf client was not cleaned up.
217      if (task_runner_->BelongsToCurrentThread()) {
218        // We are on the UI thread so we can clean it safely. This is
219        // the case at least for ui_tests running under Valgrind in
220        // bug 16076.
221        VLOG(1) << "~SettingGetterImplGConf: releasing gconf client";
222        ShutDown();
223      } else {
224        // This is very bad! We are deleting the setting getter but we're not on
225        // the UI thread. This is not supposed to happen: the setting getter is
226        // owned by the proxy config service's delegate, which is supposed to be
227        // destroyed on the UI thread only. We will get change notifications to
228        // a deleted object if we continue here, so fail now.
229        LOG(FATAL) << "~SettingGetterImplGConf: deleting on wrong thread!";
230      }
231    }
232    DCHECK(!client_);
233  }
234
235  virtual bool Init(
236      const scoped_refptr<base::SingleThreadTaskRunner>& glib_task_runner,
237      const scoped_refptr<base::SingleThreadTaskRunner>& file_task_runner)
238      OVERRIDE {
239    DCHECK(glib_task_runner->BelongsToCurrentThread());
240    DCHECK(!client_);
241    DCHECK(!task_runner_.get());
242    task_runner_ = glib_task_runner;
243    client_ = gconf_client_get_default();
244    if (!client_) {
245      // It's not clear whether/when this can return NULL.
246      LOG(ERROR) << "Unable to create a gconf client";
247      task_runner_ = NULL;
248      return false;
249    }
250    GError* error = NULL;
251    bool added_system_proxy = false;
252    // We need to add the directories for which we'll be asking
253    // for notifications, and we might as well ask to preload them.
254    // These need to be removed again in ShutDown(); we are careful
255    // here to only leave client_ non-NULL if both have been added.
256    gconf_client_add_dir(client_, "/system/proxy",
257                         GCONF_CLIENT_PRELOAD_ONELEVEL, &error);
258    if (error == NULL) {
259      added_system_proxy = true;
260      gconf_client_add_dir(client_, "/system/http_proxy",
261                           GCONF_CLIENT_PRELOAD_ONELEVEL, &error);
262    }
263    if (error != NULL) {
264      LOG(ERROR) << "Error requesting gconf directory: " << error->message;
265      g_error_free(error);
266      if (added_system_proxy)
267        gconf_client_remove_dir(client_, "/system/proxy", NULL);
268      g_object_unref(client_);
269      client_ = NULL;
270      task_runner_ = NULL;
271      return false;
272    }
273    return true;
274  }
275
276  virtual void ShutDown() OVERRIDE {
277    if (client_) {
278      DCHECK(task_runner_->BelongsToCurrentThread());
279      // We must explicitly disable gconf notifications here, because the gconf
280      // client will be shared between all setting getters, and they do not all
281      // have the same lifetimes. (For instance, incognito sessions get their
282      // own, which is destroyed when the session ends.)
283      gconf_client_notify_remove(client_, system_http_proxy_id_);
284      gconf_client_notify_remove(client_, system_proxy_id_);
285      gconf_client_remove_dir(client_, "/system/http_proxy", NULL);
286      gconf_client_remove_dir(client_, "/system/proxy", NULL);
287      g_object_unref(client_);
288      client_ = NULL;
289      task_runner_ = NULL;
290    }
291  }
292
293  virtual bool SetUpNotifications(
294      ProxyConfigServiceLinux::Delegate* delegate) OVERRIDE {
295    DCHECK(client_);
296    DCHECK(task_runner_->BelongsToCurrentThread());
297    GError* error = NULL;
298    notify_delegate_ = delegate;
299    // We have to keep track of the IDs returned by gconf_client_notify_add() so
300    // that we can remove them in ShutDown(). (Otherwise, notifications will be
301    // delivered to this object after it is deleted, which is bad, m'kay?)
302    system_proxy_id_ = gconf_client_notify_add(
303        client_, "/system/proxy",
304        OnGConfChangeNotification, this,
305        NULL, &error);
306    if (error == NULL) {
307      system_http_proxy_id_ = gconf_client_notify_add(
308          client_, "/system/http_proxy",
309          OnGConfChangeNotification, this,
310          NULL, &error);
311    }
312    if (error != NULL) {
313      LOG(ERROR) << "Error requesting gconf notifications: " << error->message;
314      g_error_free(error);
315      ShutDown();
316      return false;
317    }
318    // Simulate a change to avoid possibly losing updates before this point.
319    OnChangeNotification();
320    return true;
321  }
322
323  virtual const scoped_refptr<base::SingleThreadTaskRunner>&
324  GetNotificationTaskRunner() OVERRIDE {
325    return task_runner_;
326  }
327
328  virtual ProxyConfigSource GetConfigSource() OVERRIDE {
329    return PROXY_CONFIG_SOURCE_GCONF;
330  }
331
332  virtual bool GetString(StringSetting key, std::string* result) OVERRIDE {
333    switch (key) {
334      case PROXY_MODE:
335        return GetStringByPath("/system/proxy/mode", result);
336      case PROXY_AUTOCONF_URL:
337        return GetStringByPath("/system/proxy/autoconfig_url", result);
338      case PROXY_HTTP_HOST:
339        return GetStringByPath("/system/http_proxy/host", result);
340      case PROXY_HTTPS_HOST:
341        return GetStringByPath("/system/proxy/secure_host", result);
342      case PROXY_FTP_HOST:
343        return GetStringByPath("/system/proxy/ftp_host", result);
344      case PROXY_SOCKS_HOST:
345        return GetStringByPath("/system/proxy/socks_host", result);
346    }
347    return false;  // Placate compiler.
348  }
349  virtual bool GetBool(BoolSetting key, bool* result) OVERRIDE {
350    switch (key) {
351      case PROXY_USE_HTTP_PROXY:
352        return GetBoolByPath("/system/http_proxy/use_http_proxy", result);
353      case PROXY_USE_SAME_PROXY:
354        return GetBoolByPath("/system/http_proxy/use_same_proxy", result);
355      case PROXY_USE_AUTHENTICATION:
356        return GetBoolByPath("/system/http_proxy/use_authentication", result);
357    }
358    return false;  // Placate compiler.
359  }
360  virtual bool GetInt(IntSetting key, int* result) OVERRIDE {
361    switch (key) {
362      case PROXY_HTTP_PORT:
363        return GetIntByPath("/system/http_proxy/port", result);
364      case PROXY_HTTPS_PORT:
365        return GetIntByPath("/system/proxy/secure_port", result);
366      case PROXY_FTP_PORT:
367        return GetIntByPath("/system/proxy/ftp_port", result);
368      case PROXY_SOCKS_PORT:
369        return GetIntByPath("/system/proxy/socks_port", result);
370    }
371    return false;  // Placate compiler.
372  }
373  virtual bool GetStringList(StringListSetting key,
374                             std::vector<std::string>* result) OVERRIDE {
375    switch (key) {
376      case PROXY_IGNORE_HOSTS:
377        return GetStringListByPath("/system/http_proxy/ignore_hosts", result);
378    }
379    return false;  // Placate compiler.
380  }
381
382  virtual bool BypassListIsReversed() OVERRIDE {
383    // This is a KDE-specific setting.
384    return false;
385  }
386
387  virtual bool MatchHostsUsingSuffixMatching() OVERRIDE {
388    return false;
389  }
390
391 private:
392  bool GetStringByPath(const char* key, std::string* result) {
393    DCHECK(client_);
394    DCHECK(task_runner_->BelongsToCurrentThread());
395    GError* error = NULL;
396    gchar* value = gconf_client_get_string(client_, key, &error);
397    if (HandleGError(error, key))
398      return false;
399    if (!value)
400      return false;
401    *result = value;
402    g_free(value);
403    return true;
404  }
405  bool GetBoolByPath(const char* key, bool* result) {
406    DCHECK(client_);
407    DCHECK(task_runner_->BelongsToCurrentThread());
408    GError* error = NULL;
409    // We want to distinguish unset values from values defaulting to
410    // false. For that we need to use the type-generic
411    // gconf_client_get() rather than gconf_client_get_bool().
412    GConfValue* gconf_value = gconf_client_get(client_, key, &error);
413    if (HandleGError(error, key))
414      return false;
415    if (!gconf_value) {
416      // Unset.
417      return false;
418    }
419    if (gconf_value->type != GCONF_VALUE_BOOL) {
420      gconf_value_free(gconf_value);
421      return false;
422    }
423    gboolean bool_value = gconf_value_get_bool(gconf_value);
424    *result = static_cast<bool>(bool_value);
425    gconf_value_free(gconf_value);
426    return true;
427  }
428  bool GetIntByPath(const char* key, int* result) {
429    DCHECK(client_);
430    DCHECK(task_runner_->BelongsToCurrentThread());
431    GError* error = NULL;
432    int value = gconf_client_get_int(client_, key, &error);
433    if (HandleGError(error, key))
434      return false;
435    // We don't bother to distinguish an unset value because callers
436    // don't care. 0 is returned if unset.
437    *result = value;
438    return true;
439  }
440  bool GetStringListByPath(const char* key, std::vector<std::string>* result) {
441    DCHECK(client_);
442    DCHECK(task_runner_->BelongsToCurrentThread());
443    GError* error = NULL;
444    GSList* list = gconf_client_get_list(client_, key,
445                                         GCONF_VALUE_STRING, &error);
446    if (HandleGError(error, key))
447      return false;
448    if (!list)
449      return false;
450    for (GSList *it = list; it; it = it->next) {
451      result->push_back(static_cast<char*>(it->data));
452      g_free(it->data);
453    }
454    g_slist_free(list);
455    return true;
456  }
457
458  // Logs and frees a glib error. Returns false if there was no error
459  // (error is NULL).
460  bool HandleGError(GError* error, const char* key) {
461    if (error != NULL) {
462      LOG(ERROR) << "Error getting gconf value for " << key
463                 << ": " << error->message;
464      g_error_free(error);
465      return true;
466    }
467    return false;
468  }
469
470  // This is the callback from the debounce timer.
471  void OnDebouncedNotification() {
472    DCHECK(task_runner_->BelongsToCurrentThread());
473    CHECK(notify_delegate_);
474    // Forward to a method on the proxy config service delegate object.
475    notify_delegate_->OnCheckProxyConfigSettings();
476  }
477
478  void OnChangeNotification() {
479    // We don't use Reset() because the timer may not yet be running.
480    // (In that case Stop() is a no-op.)
481    debounce_timer_.Stop();
482    debounce_timer_.Start(FROM_HERE,
483        base::TimeDelta::FromMilliseconds(kDebounceTimeoutMilliseconds),
484        this, &SettingGetterImplGConf::OnDebouncedNotification);
485  }
486
487  // gconf notification callback, dispatched on the default glib main loop.
488  static void OnGConfChangeNotification(GConfClient* client, guint cnxn_id,
489                                        GConfEntry* entry, gpointer user_data) {
490    VLOG(1) << "gconf change notification for key "
491            << gconf_entry_get_key(entry);
492    // We don't track which key has changed, just that something did change.
493    SettingGetterImplGConf* setting_getter =
494        reinterpret_cast<SettingGetterImplGConf*>(user_data);
495    setting_getter->OnChangeNotification();
496  }
497
498  GConfClient* client_;
499  // These ids are the values returned from gconf_client_notify_add(), which we
500  // will need in order to later call gconf_client_notify_remove().
501  guint system_proxy_id_;
502  guint system_http_proxy_id_;
503
504  ProxyConfigServiceLinux::Delegate* notify_delegate_;
505  base::OneShotTimer<SettingGetterImplGConf> debounce_timer_;
506
507  // Task runner for the thread that we make gconf calls on. It should
508  // be the UI thread and all our methods should be called on this
509  // thread. Only for assertions.
510  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
511
512  DISALLOW_COPY_AND_ASSIGN(SettingGetterImplGConf);
513};
514#endif  // defined(USE_GCONF)
515
516#if defined(USE_GIO)
517const char kProxyGConfSchema[] = "org.gnome.system.proxy";
518
519// This setting getter uses gsettings, as used in most GNOME 3 desktops.
520class SettingGetterImplGSettings
521    : public ProxyConfigServiceLinux::SettingGetter {
522 public:
523  SettingGetterImplGSettings() :
524    client_(NULL),
525    http_client_(NULL),
526    https_client_(NULL),
527    ftp_client_(NULL),
528    socks_client_(NULL),
529    notify_delegate_(NULL) {
530  }
531
532  virtual ~SettingGetterImplGSettings() {
533    // client_ should have been released before now, from
534    // Delegate::OnDestroy(), while running on the UI thread. However
535    // on exiting the process, it may happen that
536    // Delegate::OnDestroy() task is left pending on the glib loop
537    // after the loop was quit, and pending tasks may then be deleted
538    // without being run.
539    if (client_) {
540      // gconf client was not cleaned up.
541      if (task_runner_->BelongsToCurrentThread()) {
542        // We are on the UI thread so we can clean it safely. This is
543        // the case at least for ui_tests running under Valgrind in
544        // bug 16076.
545        VLOG(1) << "~SettingGetterImplGSettings: releasing gsettings client";
546        ShutDown();
547      } else {
548        LOG(WARNING) << "~SettingGetterImplGSettings: leaking gsettings client";
549        client_ = NULL;
550      }
551    }
552    DCHECK(!client_);
553  }
554
555  bool SchemaExists(const char* schema_name) {
556    const gchar* const* schemas = libgio_loader_.g_settings_list_schemas();
557    while (*schemas) {
558      if (strcmp(schema_name, static_cast<const char*>(*schemas)) == 0)
559        return true;
560      schemas++;
561    }
562    return false;
563  }
564
565  // LoadAndCheckVersion() must be called *before* Init()!
566  bool LoadAndCheckVersion(base::Environment* env);
567
568  virtual bool Init(
569      const scoped_refptr<base::SingleThreadTaskRunner>& glib_task_runner,
570      const scoped_refptr<base::SingleThreadTaskRunner>& file_task_runner)
571      OVERRIDE {
572    DCHECK(glib_task_runner->BelongsToCurrentThread());
573    DCHECK(!client_);
574    DCHECK(!task_runner_.get());
575
576    if (!SchemaExists(kProxyGConfSchema) ||
577        !(client_ = libgio_loader_.g_settings_new(kProxyGConfSchema))) {
578      // It's not clear whether/when this can return NULL.
579      LOG(ERROR) << "Unable to create a gsettings client";
580      return false;
581    }
582    task_runner_ = glib_task_runner;
583    // We assume these all work if the above call worked.
584    http_client_ = libgio_loader_.g_settings_get_child(client_, "http");
585    https_client_ = libgio_loader_.g_settings_get_child(client_, "https");
586    ftp_client_ = libgio_loader_.g_settings_get_child(client_, "ftp");
587    socks_client_ = libgio_loader_.g_settings_get_child(client_, "socks");
588    DCHECK(http_client_ && https_client_ && ftp_client_ && socks_client_);
589    return true;
590  }
591
592  virtual void ShutDown() OVERRIDE {
593    if (client_) {
594      DCHECK(task_runner_->BelongsToCurrentThread());
595      // This also disables gsettings notifications.
596      g_object_unref(socks_client_);
597      g_object_unref(ftp_client_);
598      g_object_unref(https_client_);
599      g_object_unref(http_client_);
600      g_object_unref(client_);
601      // We only need to null client_ because it's the only one that we check.
602      client_ = NULL;
603      task_runner_ = NULL;
604    }
605  }
606
607  virtual bool SetUpNotifications(
608      ProxyConfigServiceLinux::Delegate* delegate) OVERRIDE {
609    DCHECK(client_);
610    DCHECK(task_runner_->BelongsToCurrentThread());
611    notify_delegate_ = delegate;
612    // We could watch for the change-event signal instead of changed, but
613    // since we have to watch more than one object, we'd still have to
614    // debounce change notifications. This is conceptually simpler.
615    g_signal_connect(G_OBJECT(client_), "changed",
616                     G_CALLBACK(OnGSettingsChangeNotification), this);
617    g_signal_connect(G_OBJECT(http_client_), "changed",
618                     G_CALLBACK(OnGSettingsChangeNotification), this);
619    g_signal_connect(G_OBJECT(https_client_), "changed",
620                     G_CALLBACK(OnGSettingsChangeNotification), this);
621    g_signal_connect(G_OBJECT(ftp_client_), "changed",
622                     G_CALLBACK(OnGSettingsChangeNotification), this);
623    g_signal_connect(G_OBJECT(socks_client_), "changed",
624                     G_CALLBACK(OnGSettingsChangeNotification), this);
625    // Simulate a change to avoid possibly losing updates before this point.
626    OnChangeNotification();
627    return true;
628  }
629
630  virtual const scoped_refptr<base::SingleThreadTaskRunner>&
631  GetNotificationTaskRunner() OVERRIDE {
632    return task_runner_;
633  }
634
635  virtual ProxyConfigSource GetConfigSource() OVERRIDE {
636    return PROXY_CONFIG_SOURCE_GSETTINGS;
637  }
638
639  virtual bool GetString(StringSetting key, std::string* result) OVERRIDE {
640    DCHECK(client_);
641    switch (key) {
642      case PROXY_MODE:
643        return GetStringByPath(client_, "mode", result);
644      case PROXY_AUTOCONF_URL:
645        return GetStringByPath(client_, "autoconfig-url", result);
646      case PROXY_HTTP_HOST:
647        return GetStringByPath(http_client_, "host", result);
648      case PROXY_HTTPS_HOST:
649        return GetStringByPath(https_client_, "host", result);
650      case PROXY_FTP_HOST:
651        return GetStringByPath(ftp_client_, "host", result);
652      case PROXY_SOCKS_HOST:
653        return GetStringByPath(socks_client_, "host", result);
654    }
655    return false;  // Placate compiler.
656  }
657  virtual bool GetBool(BoolSetting key, bool* result) OVERRIDE {
658    DCHECK(client_);
659    switch (key) {
660      case PROXY_USE_HTTP_PROXY:
661        // Although there is an "enabled" boolean in http_client_, it is not set
662        // to true by the proxy config utility. We ignore it and return false.
663        return false;
664      case PROXY_USE_SAME_PROXY:
665        // Similarly, although there is a "use-same-proxy" boolean in client_,
666        // it is never set to false by the proxy config utility. We ignore it.
667        return false;
668      case PROXY_USE_AUTHENTICATION:
669        // There is also no way to set this in the proxy config utility, but it
670        // doesn't hurt us to get the actual setting (unlike the two above).
671        return GetBoolByPath(http_client_, "use-authentication", result);
672    }
673    return false;  // Placate compiler.
674  }
675  virtual bool GetInt(IntSetting key, int* result) OVERRIDE {
676    DCHECK(client_);
677    switch (key) {
678      case PROXY_HTTP_PORT:
679        return GetIntByPath(http_client_, "port", result);
680      case PROXY_HTTPS_PORT:
681        return GetIntByPath(https_client_, "port", result);
682      case PROXY_FTP_PORT:
683        return GetIntByPath(ftp_client_, "port", result);
684      case PROXY_SOCKS_PORT:
685        return GetIntByPath(socks_client_, "port", result);
686    }
687    return false;  // Placate compiler.
688  }
689  virtual bool GetStringList(StringListSetting key,
690                             std::vector<std::string>* result) OVERRIDE {
691    DCHECK(client_);
692    switch (key) {
693      case PROXY_IGNORE_HOSTS:
694        return GetStringListByPath(client_, "ignore-hosts", result);
695    }
696    return false;  // Placate compiler.
697  }
698
699  virtual bool BypassListIsReversed() OVERRIDE {
700    // This is a KDE-specific setting.
701    return false;
702  }
703
704  virtual bool MatchHostsUsingSuffixMatching() OVERRIDE {
705    return false;
706  }
707
708 private:
709  bool GetStringByPath(GSettings* client, const char* key,
710                       std::string* result) {
711    DCHECK(task_runner_->BelongsToCurrentThread());
712    gchar* value = libgio_loader_.g_settings_get_string(client, key);
713    if (!value)
714      return false;
715    *result = value;
716    g_free(value);
717    return true;
718  }
719  bool GetBoolByPath(GSettings* client, const char* key, bool* result) {
720    DCHECK(task_runner_->BelongsToCurrentThread());
721    *result = static_cast<bool>(
722        libgio_loader_.g_settings_get_boolean(client, key));
723    return true;
724  }
725  bool GetIntByPath(GSettings* client, const char* key, int* result) {
726    DCHECK(task_runner_->BelongsToCurrentThread());
727    *result = libgio_loader_.g_settings_get_int(client, key);
728    return true;
729  }
730  bool GetStringListByPath(GSettings* client, const char* key,
731                           std::vector<std::string>* result) {
732    DCHECK(task_runner_->BelongsToCurrentThread());
733    gchar** list = libgio_loader_.g_settings_get_strv(client, key);
734    if (!list)
735      return false;
736    for (size_t i = 0; list[i]; ++i) {
737      result->push_back(static_cast<char*>(list[i]));
738      g_free(list[i]);
739    }
740    g_free(list);
741    return true;
742  }
743
744  // This is the callback from the debounce timer.
745  void OnDebouncedNotification() {
746    DCHECK(task_runner_->BelongsToCurrentThread());
747    CHECK(notify_delegate_);
748    // Forward to a method on the proxy config service delegate object.
749    notify_delegate_->OnCheckProxyConfigSettings();
750  }
751
752  void OnChangeNotification() {
753    // We don't use Reset() because the timer may not yet be running.
754    // (In that case Stop() is a no-op.)
755    debounce_timer_.Stop();
756    debounce_timer_.Start(FROM_HERE,
757        base::TimeDelta::FromMilliseconds(kDebounceTimeoutMilliseconds),
758        this, &SettingGetterImplGSettings::OnDebouncedNotification);
759  }
760
761  // gsettings notification callback, dispatched on the default glib main loop.
762  static void OnGSettingsChangeNotification(GSettings* client, gchar* key,
763                                            gpointer user_data) {
764    VLOG(1) << "gsettings change notification for key " << key;
765    // We don't track which key has changed, just that something did change.
766    SettingGetterImplGSettings* setting_getter =
767        reinterpret_cast<SettingGetterImplGSettings*>(user_data);
768    setting_getter->OnChangeNotification();
769  }
770
771  GSettings* client_;
772  GSettings* http_client_;
773  GSettings* https_client_;
774  GSettings* ftp_client_;
775  GSettings* socks_client_;
776  ProxyConfigServiceLinux::Delegate* notify_delegate_;
777  base::OneShotTimer<SettingGetterImplGSettings> debounce_timer_;
778
779  // Task runner for the thread that we make gsettings calls on. It should
780  // be the UI thread and all our methods should be called on this
781  // thread. Only for assertions.
782  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
783
784  LibGioLoader libgio_loader_;
785
786  DISALLOW_COPY_AND_ASSIGN(SettingGetterImplGSettings);
787};
788
789bool SettingGetterImplGSettings::LoadAndCheckVersion(
790    base::Environment* env) {
791  // LoadAndCheckVersion() must be called *before* Init()!
792  DCHECK(!client_);
793
794  // The APIs to query gsettings were introduced after the minimum glib
795  // version we target, so we can't link directly against them. We load them
796  // dynamically at runtime, and if they don't exist, return false here. (We
797  // support linking directly via gyp flags though.) Additionally, even when
798  // they are present, we do two additional checks to make sure we should use
799  // them and not gconf. First, we attempt to load the schema for proxy
800  // settings. Second, we check for the program that was used in older
801  // versions of GNOME to configure proxy settings, and return false if it
802  // exists. Some distributions (e.g. Ubuntu 11.04) have the API and schema
803  // but don't use gsettings for proxy settings, but they do have the old
804  // binary, so we detect these systems that way.
805
806  {
807    // TODO(phajdan.jr): Redesign the code to load library on different thread.
808    base::ThreadRestrictions::ScopedAllowIO allow_io;
809
810    // Try also without .0 at the end; on some systems this may be required.
811    if (!libgio_loader_.Load("libgio-2.0.so.0") &&
812        !libgio_loader_.Load("libgio-2.0.so")) {
813      VLOG(1) << "Cannot load gio library. Will fall back to gconf.";
814      return false;
815    }
816  }
817
818  GSettings* client = NULL;
819  if (SchemaExists(kProxyGConfSchema)) {
820    ANNOTATE_SCOPED_MEMORY_LEAK; // http://crbug.com/380782
821    client = libgio_loader_.g_settings_new(kProxyGConfSchema);
822  }
823  if (!client) {
824    VLOG(1) << "Cannot create gsettings client. Will fall back to gconf.";
825    return false;
826  }
827  g_object_unref(client);
828
829  std::string path;
830  if (!env->GetVar("PATH", &path)) {
831    LOG(ERROR) << "No $PATH variable. Assuming no gnome-network-properties.";
832  } else {
833    // Yes, we're on the UI thread. Yes, we're accessing the file system.
834    // Sadly, we don't have much choice. We need the proxy settings and we
835    // need them now, and to figure out where to get them, we have to check
836    // for this binary. See http://crbug.com/69057 for additional details.
837    base::ThreadRestrictions::ScopedAllowIO allow_io;
838    std::vector<std::string> paths;
839    Tokenize(path, ":", &paths);
840    for (size_t i = 0; i < paths.size(); ++i) {
841      base::FilePath file(paths[i]);
842      if (base::PathExists(file.Append("gnome-network-properties"))) {
843        VLOG(1) << "Found gnome-network-properties. Will fall back to gconf.";
844        return false;
845      }
846    }
847  }
848
849  VLOG(1) << "All gsettings tests OK. Will get proxy config from gsettings.";
850  return true;
851}
852#endif  // defined(USE_GIO)
853
854// This is the KDE version that reads kioslaverc and simulates gconf.
855// Doing this allows the main Delegate code, as well as the unit tests
856// for it, to stay the same - and the settings map fairly well besides.
857class SettingGetterImplKDE : public ProxyConfigServiceLinux::SettingGetter,
858                             public base::MessagePumpLibevent::Watcher {
859 public:
860  explicit SettingGetterImplKDE(base::Environment* env_var_getter)
861      : inotify_fd_(-1), notify_delegate_(NULL), indirect_manual_(false),
862        auto_no_pac_(false), reversed_bypass_list_(false),
863        env_var_getter_(env_var_getter), file_task_runner_(NULL) {
864    // This has to be called on the UI thread (http://crbug.com/69057).
865    base::ThreadRestrictions::ScopedAllowIO allow_io;
866
867    // Derive the location of the kde config dir from the environment.
868    std::string home;
869    if (env_var_getter->GetVar("KDEHOME", &home) && !home.empty()) {
870      // $KDEHOME is set. Use it unconditionally.
871      kde_config_dir_ = KDEHomeToConfigPath(base::FilePath(home));
872    } else {
873      // $KDEHOME is unset. Try to figure out what to use. This seems to be
874      // the common case on most distributions.
875      if (!env_var_getter->GetVar(base::env_vars::kHome, &home))
876        // User has no $HOME? Give up. Later we'll report the failure.
877        return;
878      if (base::nix::GetDesktopEnvironment(env_var_getter) ==
879          base::nix::DESKTOP_ENVIRONMENT_KDE3) {
880        // KDE3 always uses .kde for its configuration.
881        base::FilePath kde_path = base::FilePath(home).Append(".kde");
882        kde_config_dir_ = KDEHomeToConfigPath(kde_path);
883      } else {
884        // Some distributions patch KDE4 to use .kde4 instead of .kde, so that
885        // both can be installed side-by-side. Sadly they don't all do this, and
886        // they don't always do this: some distributions have started switching
887        // back as well. So if there is a .kde4 directory, check the timestamps
888        // of the config directories within and use the newest one.
889        // Note that we should currently be running in the UI thread, because in
890        // the gconf version, that is the only thread that can access the proxy
891        // settings (a gconf restriction). As noted below, the initial read of
892        // the proxy settings will be done in this thread anyway, so we check
893        // for .kde4 here in this thread as well.
894        base::FilePath kde3_path = base::FilePath(home).Append(".kde");
895        base::FilePath kde3_config = KDEHomeToConfigPath(kde3_path);
896        base::FilePath kde4_path = base::FilePath(home).Append(".kde4");
897        base::FilePath kde4_config = KDEHomeToConfigPath(kde4_path);
898        bool use_kde4 = false;
899        if (base::DirectoryExists(kde4_path)) {
900          base::File::Info kde3_info;
901          base::File::Info kde4_info;
902          if (base::GetFileInfo(kde4_config, &kde4_info)) {
903            if (base::GetFileInfo(kde3_config, &kde3_info)) {
904              use_kde4 = kde4_info.last_modified >= kde3_info.last_modified;
905            } else {
906              use_kde4 = true;
907            }
908          }
909        }
910        if (use_kde4) {
911          kde_config_dir_ = KDEHomeToConfigPath(kde4_path);
912        } else {
913          kde_config_dir_ = KDEHomeToConfigPath(kde3_path);
914        }
915      }
916    }
917  }
918
919  virtual ~SettingGetterImplKDE() {
920    // inotify_fd_ should have been closed before now, from
921    // Delegate::OnDestroy(), while running on the file thread. However
922    // on exiting the process, it may happen that Delegate::OnDestroy()
923    // task is left pending on the file loop after the loop was quit,
924    // and pending tasks may then be deleted without being run.
925    // Here in the KDE version, we can safely close the file descriptor
926    // anyway. (Not that it really matters; the process is exiting.)
927    if (inotify_fd_ >= 0)
928      ShutDown();
929    DCHECK(inotify_fd_ < 0);
930  }
931
932  virtual bool Init(
933      const scoped_refptr<base::SingleThreadTaskRunner>& glib_task_runner,
934      const scoped_refptr<base::SingleThreadTaskRunner>& file_task_runner)
935      OVERRIDE {
936    // This has to be called on the UI thread (http://crbug.com/69057).
937    base::ThreadRestrictions::ScopedAllowIO allow_io;
938    DCHECK(inotify_fd_ < 0);
939    inotify_fd_ = inotify_init();
940    if (inotify_fd_ < 0) {
941      PLOG(ERROR) << "inotify_init failed";
942      return false;
943    }
944    int flags = fcntl(inotify_fd_, F_GETFL);
945    if (fcntl(inotify_fd_, F_SETFL, flags | O_NONBLOCK) < 0) {
946      PLOG(ERROR) << "fcntl failed";
947      close(inotify_fd_);
948      inotify_fd_ = -1;
949      return false;
950    }
951    file_task_runner_ = file_task_runner;
952    // The initial read is done on the current thread, not
953    // |file_task_runner_|, since we will need to have it for
954    // SetUpAndFetchInitialConfig().
955    UpdateCachedSettings();
956    return true;
957  }
958
959  virtual void ShutDown() OVERRIDE {
960    if (inotify_fd_ >= 0) {
961      ResetCachedSettings();
962      inotify_watcher_.StopWatchingFileDescriptor();
963      close(inotify_fd_);
964      inotify_fd_ = -1;
965    }
966  }
967
968  virtual bool SetUpNotifications(
969      ProxyConfigServiceLinux::Delegate* delegate) OVERRIDE {
970    DCHECK(inotify_fd_ >= 0);
971    DCHECK(file_task_runner_->BelongsToCurrentThread());
972    // We can't just watch the kioslaverc file directly, since KDE will write
973    // a new copy of it and then rename it whenever settings are changed and
974    // inotify watches inodes (so we'll be watching the old deleted file after
975    // the first change, and it will never change again). So, we watch the
976    // directory instead. We then act only on changes to the kioslaverc entry.
977    if (inotify_add_watch(inotify_fd_, kde_config_dir_.value().c_str(),
978                          IN_MODIFY | IN_MOVED_TO) < 0) {
979      return false;
980    }
981    notify_delegate_ = delegate;
982    if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
983            inotify_fd_, true, base::MessageLoopForIO::WATCH_READ,
984            &inotify_watcher_, this)) {
985      return false;
986    }
987    // Simulate a change to avoid possibly losing updates before this point.
988    OnChangeNotification();
989    return true;
990  }
991
992  virtual const scoped_refptr<base::SingleThreadTaskRunner>&
993  GetNotificationTaskRunner() OVERRIDE {
994    return file_task_runner_;
995  }
996
997  // Implement base::MessagePumpLibevent::Watcher.
998  virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE {
999    DCHECK_EQ(fd, inotify_fd_);
1000    DCHECK(file_task_runner_->BelongsToCurrentThread());
1001    OnChangeNotification();
1002  }
1003  virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE {
1004    NOTREACHED();
1005  }
1006
1007  virtual ProxyConfigSource GetConfigSource() OVERRIDE {
1008    return PROXY_CONFIG_SOURCE_KDE;
1009  }
1010
1011  virtual bool GetString(StringSetting key, std::string* result) OVERRIDE {
1012    string_map_type::iterator it = string_table_.find(key);
1013    if (it == string_table_.end())
1014      return false;
1015    *result = it->second;
1016    return true;
1017  }
1018  virtual bool GetBool(BoolSetting key, bool* result) OVERRIDE {
1019    // We don't ever have any booleans.
1020    return false;
1021  }
1022  virtual bool GetInt(IntSetting key, int* result) OVERRIDE {
1023    // We don't ever have any integers. (See AddProxy() below about ports.)
1024    return false;
1025  }
1026  virtual bool GetStringList(StringListSetting key,
1027                             std::vector<std::string>* result) OVERRIDE {
1028    strings_map_type::iterator it = strings_table_.find(key);
1029    if (it == strings_table_.end())
1030      return false;
1031    *result = it->second;
1032    return true;
1033  }
1034
1035  virtual bool BypassListIsReversed() OVERRIDE {
1036    return reversed_bypass_list_;
1037  }
1038
1039  virtual bool MatchHostsUsingSuffixMatching() OVERRIDE {
1040    return true;
1041  }
1042
1043 private:
1044  void ResetCachedSettings() {
1045    string_table_.clear();
1046    strings_table_.clear();
1047    indirect_manual_ = false;
1048    auto_no_pac_ = false;
1049    reversed_bypass_list_ = false;
1050  }
1051
1052  base::FilePath KDEHomeToConfigPath(const base::FilePath& kde_home) {
1053    return kde_home.Append("share").Append("config");
1054  }
1055
1056  void AddProxy(StringSetting host_key, const std::string& value) {
1057    if (value.empty() || value.substr(0, 3) == "//:")
1058      // No proxy.
1059      return;
1060    size_t space = value.find(' ');
1061    if (space != std::string::npos) {
1062      // Newer versions of KDE use a space rather than a colon to separate the
1063      // port number from the hostname. If we find this, we need to convert it.
1064      std::string fixed = value;
1065      fixed[space] = ':';
1066      string_table_[host_key] = fixed;
1067    } else {
1068      // We don't need to parse the port number out; GetProxyFromSettings()
1069      // would only append it right back again. So we just leave the port
1070      // number right in the host string.
1071      string_table_[host_key] = value;
1072    }
1073  }
1074
1075  void AddHostList(StringListSetting key, const std::string& value) {
1076    std::vector<std::string> tokens;
1077    base::StringTokenizer tk(value, ", ");
1078    while (tk.GetNext()) {
1079      std::string token = tk.token();
1080      if (!token.empty())
1081        tokens.push_back(token);
1082    }
1083    strings_table_[key] = tokens;
1084  }
1085
1086  void AddKDESetting(const std::string& key, const std::string& value) {
1087    if (key == "ProxyType") {
1088      const char* mode = "none";
1089      indirect_manual_ = false;
1090      auto_no_pac_ = false;
1091      int int_value;
1092      base::StringToInt(value, &int_value);
1093      switch (int_value) {
1094        case 0:  // No proxy, or maybe kioslaverc syntax error.
1095          break;
1096        case 1:  // Manual configuration.
1097          mode = "manual";
1098          break;
1099        case 2:  // PAC URL.
1100          mode = "auto";
1101          break;
1102        case 3:  // WPAD.
1103          mode = "auto";
1104          auto_no_pac_ = true;
1105          break;
1106        case 4:  // Indirect manual via environment variables.
1107          mode = "manual";
1108          indirect_manual_ = true;
1109          break;
1110      }
1111      string_table_[PROXY_MODE] = mode;
1112    } else if (key == "Proxy Config Script") {
1113      string_table_[PROXY_AUTOCONF_URL] = value;
1114    } else if (key == "httpProxy") {
1115      AddProxy(PROXY_HTTP_HOST, value);
1116    } else if (key == "httpsProxy") {
1117      AddProxy(PROXY_HTTPS_HOST, value);
1118    } else if (key == "ftpProxy") {
1119      AddProxy(PROXY_FTP_HOST, value);
1120    } else if (key == "socksProxy") {
1121      // Older versions of KDE configure SOCKS in a weird way involving
1122      // LD_PRELOAD and a library that intercepts network calls to SOCKSify
1123      // them. We don't support it. KDE 4.8 added a proper SOCKS setting.
1124      AddProxy(PROXY_SOCKS_HOST, value);
1125    } else if (key == "ReversedException") {
1126      // We count "true" or any nonzero number as true, otherwise false.
1127      // Note that if the value is not actually numeric StringToInt()
1128      // will return 0, which we count as false.
1129      int int_value;
1130      base::StringToInt(value, &int_value);
1131      reversed_bypass_list_ = (value == "true" || int_value);
1132    } else if (key == "NoProxyFor") {
1133      AddHostList(PROXY_IGNORE_HOSTS, value);
1134    } else if (key == "AuthMode") {
1135      // Check for authentication, just so we can warn.
1136      int mode;
1137      base::StringToInt(value, &mode);
1138      if (mode) {
1139        // ProxyConfig does not support authentication parameters, but
1140        // Chrome will prompt for the password later. So we ignore this.
1141        LOG(WARNING) <<
1142            "Proxy authentication parameters ignored, see bug 16709";
1143      }
1144    }
1145  }
1146
1147  void ResolveIndirect(StringSetting key) {
1148    string_map_type::iterator it = string_table_.find(key);
1149    if (it != string_table_.end()) {
1150      std::string value;
1151      if (env_var_getter_->GetVar(it->second.c_str(), &value))
1152        it->second = value;
1153      else
1154        string_table_.erase(it);
1155    }
1156  }
1157
1158  void ResolveIndirectList(StringListSetting key) {
1159    strings_map_type::iterator it = strings_table_.find(key);
1160    if (it != strings_table_.end()) {
1161      std::string value;
1162      if (!it->second.empty() &&
1163          env_var_getter_->GetVar(it->second[0].c_str(), &value))
1164        AddHostList(key, value);
1165      else
1166        strings_table_.erase(it);
1167    }
1168  }
1169
1170  // The settings in kioslaverc could occur in any order, but some affect
1171  // others. Rather than read the whole file in and then query them in an
1172  // order that allows us to handle that, we read the settings in whatever
1173  // order they occur and do any necessary tweaking after we finish.
1174  void ResolveModeEffects() {
1175    if (indirect_manual_) {
1176      ResolveIndirect(PROXY_HTTP_HOST);
1177      ResolveIndirect(PROXY_HTTPS_HOST);
1178      ResolveIndirect(PROXY_FTP_HOST);
1179      ResolveIndirectList(PROXY_IGNORE_HOSTS);
1180    }
1181    if (auto_no_pac_) {
1182      // Remove the PAC URL; we're not supposed to use it.
1183      string_table_.erase(PROXY_AUTOCONF_URL);
1184    }
1185  }
1186
1187  // Reads kioslaverc one line at a time and calls AddKDESetting() to add
1188  // each relevant name-value pair to the appropriate value table.
1189  void UpdateCachedSettings() {
1190    base::FilePath kioslaverc = kde_config_dir_.Append("kioslaverc");
1191    base::ScopedFILE input(base::OpenFile(kioslaverc, "r"));
1192    if (!input.get())
1193      return;
1194    ResetCachedSettings();
1195    bool in_proxy_settings = false;
1196    bool line_too_long = false;
1197    char line[BUFFER_SIZE];
1198    // fgets() will return NULL on EOF or error.
1199    while (fgets(line, sizeof(line), input.get())) {
1200      // fgets() guarantees the line will be properly terminated.
1201      size_t length = strlen(line);
1202      if (!length)
1203        continue;
1204      // This should be true even with CRLF endings.
1205      if (line[length - 1] != '\n') {
1206        line_too_long = true;
1207        continue;
1208      }
1209      if (line_too_long) {
1210        // The previous line had no line ending, but this done does. This is
1211        // the end of the line that was too long, so warn here and skip it.
1212        LOG(WARNING) << "skipped very long line in " << kioslaverc.value();
1213        line_too_long = false;
1214        continue;
1215      }
1216      // Remove the LF at the end, and the CR if there is one.
1217      line[--length] = '\0';
1218      if (length && line[length - 1] == '\r')
1219        line[--length] = '\0';
1220      // Now parse the line.
1221      if (line[0] == '[') {
1222        // Switching sections. All we care about is whether this is
1223        // the (a?) proxy settings section, for both KDE3 and KDE4.
1224        in_proxy_settings = !strncmp(line, "[Proxy Settings]", 16);
1225      } else if (in_proxy_settings) {
1226        // A regular line, in the (a?) proxy settings section.
1227        char* split = strchr(line, '=');
1228        // Skip this line if it does not contain an = sign.
1229        if (!split)
1230          continue;
1231        // Split the line on the = and advance |split|.
1232        *(split++) = 0;
1233        std::string key = line;
1234        std::string value = split;
1235        base::TrimWhitespaceASCII(key, base::TRIM_ALL, &key);
1236        base::TrimWhitespaceASCII(value, base::TRIM_ALL, &value);
1237        // Skip this line if the key name is empty.
1238        if (key.empty())
1239          continue;
1240        // Is the value name localized?
1241        if (key[key.length() - 1] == ']') {
1242          // Find the matching bracket.
1243          length = key.rfind('[');
1244          // Skip this line if the localization indicator is malformed.
1245          if (length == std::string::npos)
1246            continue;
1247          // Trim the localization indicator off.
1248          key.resize(length);
1249          // Remove any resulting trailing whitespace.
1250          base::TrimWhitespaceASCII(key, base::TRIM_TRAILING, &key);
1251          // Skip this line if the key name is now empty.
1252          if (key.empty())
1253            continue;
1254        }
1255        // Now fill in the tables.
1256        AddKDESetting(key, value);
1257      }
1258    }
1259    if (ferror(input.get()))
1260      LOG(ERROR) << "error reading " << kioslaverc.value();
1261    ResolveModeEffects();
1262  }
1263
1264  // This is the callback from the debounce timer.
1265  void OnDebouncedNotification() {
1266    DCHECK(file_task_runner_->BelongsToCurrentThread());
1267    VLOG(1) << "inotify change notification for kioslaverc";
1268    UpdateCachedSettings();
1269    CHECK(notify_delegate_);
1270    // Forward to a method on the proxy config service delegate object.
1271    notify_delegate_->OnCheckProxyConfigSettings();
1272  }
1273
1274  // Called by OnFileCanReadWithoutBlocking() on the file thread. Reads
1275  // from the inotify file descriptor and starts up a debounce timer if
1276  // an event for kioslaverc is seen.
1277  void OnChangeNotification() {
1278    DCHECK_GE(inotify_fd_,  0);
1279    DCHECK(file_task_runner_->BelongsToCurrentThread());
1280    char event_buf[(sizeof(inotify_event) + NAME_MAX + 1) * 4];
1281    bool kioslaverc_touched = false;
1282    ssize_t r;
1283    while ((r = read(inotify_fd_, event_buf, sizeof(event_buf))) > 0) {
1284      // inotify returns variable-length structures, which is why we have
1285      // this strange-looking loop instead of iterating through an array.
1286      char* event_ptr = event_buf;
1287      while (event_ptr < event_buf + r) {
1288        inotify_event* event = reinterpret_cast<inotify_event*>(event_ptr);
1289        // The kernel always feeds us whole events.
1290        CHECK_LE(event_ptr + sizeof(inotify_event), event_buf + r);
1291        CHECK_LE(event->name + event->len, event_buf + r);
1292        if (!strcmp(event->name, "kioslaverc"))
1293          kioslaverc_touched = true;
1294        // Advance the pointer just past the end of the filename.
1295        event_ptr = event->name + event->len;
1296      }
1297      // We keep reading even if |kioslaverc_touched| is true to drain the
1298      // inotify event queue.
1299    }
1300    if (!r)
1301      // Instead of returning -1 and setting errno to EINVAL if there is not
1302      // enough buffer space, older kernels (< 2.6.21) return 0. Simulate the
1303      // new behavior (EINVAL) so we can reuse the code below.
1304      errno = EINVAL;
1305    if (errno != EAGAIN) {
1306      PLOG(WARNING) << "error reading inotify file descriptor";
1307      if (errno == EINVAL) {
1308        // Our buffer is not large enough to read the next event. This should
1309        // not happen (because its size is calculated to always be sufficiently
1310        // large), but if it does we'd warn continuously since |inotify_fd_|
1311        // would be forever ready to read. Close it and stop watching instead.
1312        LOG(ERROR) << "inotify failure; no longer watching kioslaverc!";
1313        inotify_watcher_.StopWatchingFileDescriptor();
1314        close(inotify_fd_);
1315        inotify_fd_ = -1;
1316      }
1317    }
1318    if (kioslaverc_touched) {
1319      // We don't use Reset() because the timer may not yet be running.
1320      // (In that case Stop() is a no-op.)
1321      debounce_timer_.Stop();
1322      debounce_timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(
1323          kDebounceTimeoutMilliseconds), this,
1324          &SettingGetterImplKDE::OnDebouncedNotification);
1325    }
1326  }
1327
1328  typedef std::map<StringSetting, std::string> string_map_type;
1329  typedef std::map<StringListSetting,
1330                   std::vector<std::string> > strings_map_type;
1331
1332  int inotify_fd_;
1333  base::MessagePumpLibevent::FileDescriptorWatcher inotify_watcher_;
1334  ProxyConfigServiceLinux::Delegate* notify_delegate_;
1335  base::OneShotTimer<SettingGetterImplKDE> debounce_timer_;
1336  base::FilePath kde_config_dir_;
1337  bool indirect_manual_;
1338  bool auto_no_pac_;
1339  bool reversed_bypass_list_;
1340  // We don't own |env_var_getter_|.  It's safe to hold a pointer to it, since
1341  // both it and us are owned by ProxyConfigServiceLinux::Delegate, and have the
1342  // same lifetime.
1343  base::Environment* env_var_getter_;
1344
1345  // We cache these settings whenever we re-read the kioslaverc file.
1346  string_map_type string_table_;
1347  strings_map_type strings_table_;
1348
1349  // Task runner of the file thread, for reading kioslaverc. If NULL,
1350  // just read it directly (for testing). We also handle inotify events
1351  // on this thread.
1352  scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_;
1353
1354  DISALLOW_COPY_AND_ASSIGN(SettingGetterImplKDE);
1355};
1356
1357}  // namespace
1358
1359bool ProxyConfigServiceLinux::Delegate::GetProxyFromSettings(
1360    SettingGetter::StringSetting host_key,
1361    ProxyServer* result_server) {
1362  std::string host;
1363  if (!setting_getter_->GetString(host_key, &host) || host.empty()) {
1364    // Unset or empty.
1365    return false;
1366  }
1367  // Check for an optional port.
1368  int port = 0;
1369  SettingGetter::IntSetting port_key =
1370      SettingGetter::HostSettingToPortSetting(host_key);
1371  setting_getter_->GetInt(port_key, &port);
1372  if (port != 0) {
1373    // If a port is set and non-zero:
1374    host += ":" + base::IntToString(port);
1375  }
1376
1377  // gconf settings do not appear to distinguish between SOCKS version. We
1378  // default to version 5. For more information on this policy decision, see:
1379  // http://code.google.com/p/chromium/issues/detail?id=55912#c2
1380  ProxyServer::Scheme scheme = (host_key == SettingGetter::PROXY_SOCKS_HOST) ?
1381      ProxyServer::SCHEME_SOCKS5 : ProxyServer::SCHEME_HTTP;
1382  host = FixupProxyHostScheme(scheme, host);
1383  ProxyServer proxy_server = ProxyServer::FromURI(host,
1384                                                  ProxyServer::SCHEME_HTTP);
1385  if (proxy_server.is_valid()) {
1386    *result_server = proxy_server;
1387    return true;
1388  }
1389  return false;
1390}
1391
1392bool ProxyConfigServiceLinux::Delegate::GetConfigFromSettings(
1393    ProxyConfig* config) {
1394  std::string mode;
1395  if (!setting_getter_->GetString(SettingGetter::PROXY_MODE, &mode)) {
1396    // We expect this to always be set, so if we don't see it then we
1397    // probably have a gconf/gsettings problem, and so we don't have a valid
1398    // proxy config.
1399    return false;
1400  }
1401  if (mode == "none") {
1402    // Specifically specifies no proxy.
1403    return true;
1404  }
1405
1406  if (mode == "auto") {
1407    // Automatic proxy config.
1408    std::string pac_url_str;
1409    if (setting_getter_->GetString(SettingGetter::PROXY_AUTOCONF_URL,
1410                                   &pac_url_str)) {
1411      if (!pac_url_str.empty()) {
1412        // If the PAC URL is actually a file path, then put file:// in front.
1413        if (pac_url_str[0] == '/')
1414          pac_url_str = "file://" + pac_url_str;
1415        GURL pac_url(pac_url_str);
1416        if (!pac_url.is_valid())
1417          return false;
1418        config->set_pac_url(pac_url);
1419        return true;
1420      }
1421    }
1422    config->set_auto_detect(true);
1423    return true;
1424  }
1425
1426  if (mode != "manual") {
1427    // Mode is unrecognized.
1428    return false;
1429  }
1430  bool use_http_proxy;
1431  if (setting_getter_->GetBool(SettingGetter::PROXY_USE_HTTP_PROXY,
1432                               &use_http_proxy)
1433      && !use_http_proxy) {
1434    // Another master switch for some reason. If set to false, then no
1435    // proxy. But we don't panic if the key doesn't exist.
1436    return true;
1437  }
1438
1439  bool same_proxy = false;
1440  // Indicates to use the http proxy for all protocols. This one may
1441  // not exist (presumably on older versions); we assume false in that
1442  // case.
1443  setting_getter_->GetBool(SettingGetter::PROXY_USE_SAME_PROXY,
1444                           &same_proxy);
1445
1446  ProxyServer proxy_for_http;
1447  ProxyServer proxy_for_https;
1448  ProxyServer proxy_for_ftp;
1449  ProxyServer socks_proxy;  // (socks)
1450
1451  // This counts how many of the above ProxyServers were defined and valid.
1452  size_t num_proxies_specified = 0;
1453
1454  // Extract the per-scheme proxies. If we failed to parse it, or no proxy was
1455  // specified for the scheme, then the resulting ProxyServer will be invalid.
1456  if (GetProxyFromSettings(SettingGetter::PROXY_HTTP_HOST, &proxy_for_http))
1457    num_proxies_specified++;
1458  if (GetProxyFromSettings(SettingGetter::PROXY_HTTPS_HOST, &proxy_for_https))
1459    num_proxies_specified++;
1460  if (GetProxyFromSettings(SettingGetter::PROXY_FTP_HOST, &proxy_for_ftp))
1461    num_proxies_specified++;
1462  if (GetProxyFromSettings(SettingGetter::PROXY_SOCKS_HOST, &socks_proxy))
1463    num_proxies_specified++;
1464
1465  if (same_proxy) {
1466    if (proxy_for_http.is_valid()) {
1467      // Use the http proxy for all schemes.
1468      config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
1469      config->proxy_rules().single_proxies.SetSingleProxyServer(proxy_for_http);
1470    }
1471  } else if (num_proxies_specified > 0) {
1472    if (socks_proxy.is_valid() && num_proxies_specified == 1) {
1473      // If the only proxy specified was for SOCKS, use it for all schemes.
1474      config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
1475      config->proxy_rules().single_proxies.SetSingleProxyServer(socks_proxy);
1476    } else {
1477      // Otherwise use the indicated proxies per-scheme.
1478      config->proxy_rules().type =
1479          ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
1480      config->proxy_rules().proxies_for_http.
1481          SetSingleProxyServer(proxy_for_http);
1482      config->proxy_rules().proxies_for_https.
1483          SetSingleProxyServer(proxy_for_https);
1484      config->proxy_rules().proxies_for_ftp.SetSingleProxyServer(proxy_for_ftp);
1485      config->proxy_rules().fallback_proxies.SetSingleProxyServer(socks_proxy);
1486    }
1487  }
1488
1489  if (config->proxy_rules().empty()) {
1490    // Manual mode but we couldn't parse any rules.
1491    return false;
1492  }
1493
1494  // Check for authentication, just so we can warn.
1495  bool use_auth = false;
1496  setting_getter_->GetBool(SettingGetter::PROXY_USE_AUTHENTICATION,
1497                           &use_auth);
1498  if (use_auth) {
1499    // ProxyConfig does not support authentication parameters, but
1500    // Chrome will prompt for the password later. So we ignore
1501    // /system/http_proxy/*auth* settings.
1502    LOG(WARNING) << "Proxy authentication parameters ignored, see bug 16709";
1503  }
1504
1505  // Now the bypass list.
1506  std::vector<std::string> ignore_hosts_list;
1507  config->proxy_rules().bypass_rules.Clear();
1508  if (setting_getter_->GetStringList(SettingGetter::PROXY_IGNORE_HOSTS,
1509                                     &ignore_hosts_list)) {
1510    std::vector<std::string>::const_iterator it(ignore_hosts_list.begin());
1511    for (; it != ignore_hosts_list.end(); ++it) {
1512      if (setting_getter_->MatchHostsUsingSuffixMatching()) {
1513        config->proxy_rules().bypass_rules.
1514            AddRuleFromStringUsingSuffixMatching(*it);
1515      } else {
1516        config->proxy_rules().bypass_rules.AddRuleFromString(*it);
1517      }
1518    }
1519  }
1520  // Note that there are no settings with semantics corresponding to
1521  // bypass of local names in GNOME. In KDE, "<local>" is supported
1522  // as a hostname rule.
1523
1524  // KDE allows one to reverse the bypass rules.
1525  config->proxy_rules().reverse_bypass =
1526      setting_getter_->BypassListIsReversed();
1527
1528  return true;
1529}
1530
1531ProxyConfigServiceLinux::Delegate::Delegate(base::Environment* env_var_getter)
1532    : env_var_getter_(env_var_getter) {
1533  // Figure out which SettingGetterImpl to use, if any.
1534  switch (base::nix::GetDesktopEnvironment(env_var_getter)) {
1535    case base::nix::DESKTOP_ENVIRONMENT_GNOME:
1536    case base::nix::DESKTOP_ENVIRONMENT_UNITY:
1537#if defined(USE_GIO)
1538      {
1539        scoped_ptr<SettingGetterImplGSettings> gs_getter(
1540            new SettingGetterImplGSettings());
1541        // We have to load symbols and check the GNOME version in use to decide
1542        // if we should use the gsettings getter. See LoadAndCheckVersion().
1543        if (gs_getter->LoadAndCheckVersion(env_var_getter))
1544          setting_getter_.reset(gs_getter.release());
1545      }
1546#endif
1547#if defined(USE_GCONF)
1548      // Fall back on gconf if gsettings is unavailable or incorrect.
1549      if (!setting_getter_.get())
1550        setting_getter_.reset(new SettingGetterImplGConf());
1551#endif
1552      break;
1553    case base::nix::DESKTOP_ENVIRONMENT_KDE3:
1554    case base::nix::DESKTOP_ENVIRONMENT_KDE4:
1555      setting_getter_.reset(new SettingGetterImplKDE(env_var_getter));
1556      break;
1557    case base::nix::DESKTOP_ENVIRONMENT_XFCE:
1558    case base::nix::DESKTOP_ENVIRONMENT_OTHER:
1559      break;
1560  }
1561}
1562
1563ProxyConfigServiceLinux::Delegate::Delegate(
1564    base::Environment* env_var_getter, SettingGetter* setting_getter)
1565    : env_var_getter_(env_var_getter), setting_getter_(setting_getter) {
1566}
1567
1568void ProxyConfigServiceLinux::Delegate::SetUpAndFetchInitialConfig(
1569    const scoped_refptr<base::SingleThreadTaskRunner>& glib_task_runner,
1570    const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
1571    const scoped_refptr<base::SingleThreadTaskRunner>& file_task_runner) {
1572  // We should be running on the default glib main loop thread right
1573  // now. gconf can only be accessed from this thread.
1574  DCHECK(glib_task_runner->BelongsToCurrentThread());
1575  glib_task_runner_ = glib_task_runner;
1576  io_task_runner_ = io_task_runner;
1577
1578  // If we are passed a NULL |io_task_runner| or |file_task_runner|, then we
1579  // don't set up proxy setting change notifications. This should not be the
1580  // usual case but is intended to/ simplify test setups.
1581  if (!io_task_runner_.get() || !file_task_runner.get())
1582    VLOG(1) << "Monitoring of proxy setting changes is disabled";
1583
1584  // Fetch and cache the current proxy config. The config is left in
1585  // cached_config_, where GetLatestProxyConfig() running on the IO thread
1586  // will expect to find it. This is safe to do because we return
1587  // before this ProxyConfigServiceLinux is passed on to
1588  // the ProxyService.
1589
1590  // Note: It would be nice to prioritize environment variables
1591  // and only fall back to gconf if env vars were unset. But
1592  // gnome-terminal "helpfully" sets http_proxy and no_proxy, and it
1593  // does so even if the proxy mode is set to auto, which would
1594  // mislead us.
1595
1596  bool got_config = false;
1597  if (setting_getter_.get() &&
1598      setting_getter_->Init(glib_task_runner, file_task_runner) &&
1599      GetConfigFromSettings(&cached_config_)) {
1600    cached_config_.set_id(1);  // Mark it as valid.
1601    cached_config_.set_source(setting_getter_->GetConfigSource());
1602    VLOG(1) << "Obtained proxy settings from "
1603            << ProxyConfigSourceToString(cached_config_.source());
1604
1605    // If gconf proxy mode is "none", meaning direct, then we take
1606    // that to be a valid config and will not check environment
1607    // variables. The alternative would have been to look for a proxy
1608    // whereever we can find one.
1609    got_config = true;
1610
1611    // Keep a copy of the config for use from this thread for
1612    // comparison with updated settings when we get notifications.
1613    reference_config_ = cached_config_;
1614    reference_config_.set_id(1);  // Mark it as valid.
1615
1616    // We only set up notifications if we have IO and file loops available.
1617    // We do this after getting the initial configuration so that we don't have
1618    // to worry about cancelling it if the initial fetch above fails. Note that
1619    // setting up notifications has the side effect of simulating a change, so
1620    // that we won't lose any updates that may have happened after the initial
1621    // fetch and before setting up notifications. We'll detect the common case
1622    // of no changes in OnCheckProxyConfigSettings() (or sooner) and ignore it.
1623    if (io_task_runner.get() && file_task_runner.get()) {
1624      scoped_refptr<base::SingleThreadTaskRunner> required_loop =
1625          setting_getter_->GetNotificationTaskRunner();
1626      if (!required_loop.get() || required_loop->BelongsToCurrentThread()) {
1627        // In this case we are already on an acceptable thread.
1628        SetUpNotifications();
1629      } else {
1630        // Post a task to set up notifications. We don't wait for success.
1631        required_loop->PostTask(FROM_HERE, base::Bind(
1632            &ProxyConfigServiceLinux::Delegate::SetUpNotifications, this));
1633      }
1634    }
1635  }
1636
1637  if (!got_config) {
1638    // We fall back on environment variables.
1639    //
1640    // Consulting environment variables doesn't need to be done from the
1641    // default glib main loop, but it's a tiny enough amount of work.
1642    if (GetConfigFromEnv(&cached_config_)) {
1643      cached_config_.set_source(PROXY_CONFIG_SOURCE_ENV);
1644      cached_config_.set_id(1);  // Mark it as valid.
1645      VLOG(1) << "Obtained proxy settings from environment variables";
1646    }
1647  }
1648}
1649
1650// Depending on the SettingGetter in use, this method will be called
1651// on either the UI thread (GConf) or the file thread (KDE).
1652void ProxyConfigServiceLinux::Delegate::SetUpNotifications() {
1653  scoped_refptr<base::SingleThreadTaskRunner> required_loop =
1654      setting_getter_->GetNotificationTaskRunner();
1655  DCHECK(!required_loop.get() || required_loop->BelongsToCurrentThread());
1656  if (!setting_getter_->SetUpNotifications(this))
1657    LOG(ERROR) << "Unable to set up proxy configuration change notifications";
1658}
1659
1660void ProxyConfigServiceLinux::Delegate::AddObserver(Observer* observer) {
1661  observers_.AddObserver(observer);
1662}
1663
1664void ProxyConfigServiceLinux::Delegate::RemoveObserver(Observer* observer) {
1665  observers_.RemoveObserver(observer);
1666}
1667
1668ProxyConfigService::ConfigAvailability
1669    ProxyConfigServiceLinux::Delegate::GetLatestProxyConfig(
1670        ProxyConfig* config) {
1671  // This is called from the IO thread.
1672  DCHECK(!io_task_runner_.get() ||
1673         io_task_runner_->BelongsToCurrentThread());
1674
1675  // Simply return the last proxy configuration that glib_default_loop
1676  // notified us of.
1677  if (cached_config_.is_valid()) {
1678    *config = cached_config_;
1679  } else {
1680    *config = ProxyConfig::CreateDirect();
1681    config->set_source(PROXY_CONFIG_SOURCE_SYSTEM_FAILED);
1682  }
1683
1684  // We return CONFIG_VALID to indicate that *config was filled in. It is always
1685  // going to be available since we initialized eagerly on the UI thread.
1686  // TODO(eroman): do lazy initialization instead, so we no longer need
1687  //               to construct ProxyConfigServiceLinux on the UI thread.
1688  //               In which case, we may return false here.
1689  return CONFIG_VALID;
1690}
1691
1692// Depending on the SettingGetter in use, this method will be called
1693// on either the UI thread (GConf) or the file thread (KDE).
1694void ProxyConfigServiceLinux::Delegate::OnCheckProxyConfigSettings() {
1695  scoped_refptr<base::SingleThreadTaskRunner> required_loop =
1696      setting_getter_->GetNotificationTaskRunner();
1697  DCHECK(!required_loop.get() || required_loop->BelongsToCurrentThread());
1698  ProxyConfig new_config;
1699  bool valid = GetConfigFromSettings(&new_config);
1700  if (valid)
1701    new_config.set_id(1);  // mark it as valid
1702
1703  // See if it is different from what we had before.
1704  if (new_config.is_valid() != reference_config_.is_valid() ||
1705      !new_config.Equals(reference_config_)) {
1706    // Post a task to the IO thread with the new configuration, so it can
1707    // update |cached_config_|.
1708    io_task_runner_->PostTask(FROM_HERE, base::Bind(
1709        &ProxyConfigServiceLinux::Delegate::SetNewProxyConfig,
1710        this, new_config));
1711    // Update the thread-private copy in |reference_config_| as well.
1712    reference_config_ = new_config;
1713  } else {
1714    VLOG(1) << "Detected no-op change to proxy settings. Doing nothing.";
1715  }
1716}
1717
1718void ProxyConfigServiceLinux::Delegate::SetNewProxyConfig(
1719    const ProxyConfig& new_config) {
1720  DCHECK(io_task_runner_->BelongsToCurrentThread());
1721  VLOG(1) << "Proxy configuration changed";
1722  cached_config_ = new_config;
1723  FOR_EACH_OBSERVER(
1724      Observer, observers_,
1725      OnProxyConfigChanged(new_config, ProxyConfigService::CONFIG_VALID));
1726}
1727
1728void ProxyConfigServiceLinux::Delegate::PostDestroyTask() {
1729  if (!setting_getter_.get())
1730    return;
1731  scoped_refptr<base::SingleThreadTaskRunner> shutdown_loop =
1732      setting_getter_->GetNotificationTaskRunner();
1733  if (!shutdown_loop.get() || shutdown_loop->BelongsToCurrentThread()) {
1734    // Already on the right thread, call directly.
1735    // This is the case for the unittests.
1736    OnDestroy();
1737  } else {
1738    // Post to shutdown thread. Note that on browser shutdown, we may quit
1739    // this MessageLoop and exit the program before ever running this.
1740    shutdown_loop->PostTask(FROM_HERE, base::Bind(
1741        &ProxyConfigServiceLinux::Delegate::OnDestroy, this));
1742  }
1743}
1744void ProxyConfigServiceLinux::Delegate::OnDestroy() {
1745  scoped_refptr<base::SingleThreadTaskRunner> shutdown_loop =
1746      setting_getter_->GetNotificationTaskRunner();
1747  DCHECK(!shutdown_loop.get() || shutdown_loop->BelongsToCurrentThread());
1748  setting_getter_->ShutDown();
1749}
1750
1751ProxyConfigServiceLinux::ProxyConfigServiceLinux()
1752    : delegate_(new Delegate(base::Environment::Create())) {
1753}
1754
1755ProxyConfigServiceLinux::~ProxyConfigServiceLinux() {
1756  delegate_->PostDestroyTask();
1757}
1758
1759ProxyConfigServiceLinux::ProxyConfigServiceLinux(
1760    base::Environment* env_var_getter)
1761    : delegate_(new Delegate(env_var_getter)) {
1762}
1763
1764ProxyConfigServiceLinux::ProxyConfigServiceLinux(
1765    base::Environment* env_var_getter, SettingGetter* setting_getter)
1766    : delegate_(new Delegate(env_var_getter, setting_getter)) {
1767}
1768
1769void ProxyConfigServiceLinux::AddObserver(Observer* observer) {
1770  delegate_->AddObserver(observer);
1771}
1772
1773void ProxyConfigServiceLinux::RemoveObserver(Observer* observer) {
1774  delegate_->RemoveObserver(observer);
1775}
1776
1777ProxyConfigService::ConfigAvailability
1778    ProxyConfigServiceLinux::GetLatestProxyConfig(ProxyConfig* config) {
1779  return delegate_->GetLatestProxyConfig(config);
1780}
1781
1782}  // namespace net
1783