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