• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2009 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 #include <gconf/gconf-client.h>
10 #include <limits.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <sys/inotify.h>
14 #include <unistd.h>
15 
16 #include "base/file_path.h"
17 #include "base/file_util.h"
18 #include "base/logging.h"
19 #include "base/message_loop.h"
20 #include "base/string_tokenizer.h"
21 #include "base/string_util.h"
22 #include "base/task.h"
23 #include "base/timer.h"
24 #include "googleurl/src/url_canon.h"
25 #include "net/base/net_errors.h"
26 #include "net/http/http_util.h"
27 #include "net/proxy/proxy_config.h"
28 #include "net/proxy/proxy_server.h"
29 
30 namespace net {
31 
32 namespace {
33 
34 // Given a proxy hostname from a setting, returns that hostname with
35 // an appropriate proxy server scheme prefix.
36 // scheme indicates the desired proxy scheme: usually http, with
37 // socks 4 or 5 as special cases.
38 // TODO(arindam): Remove URI string manipulation by using MapUrlSchemeToProxy.
FixupProxyHostScheme(ProxyServer::Scheme scheme,std::string host)39 std::string FixupProxyHostScheme(ProxyServer::Scheme scheme,
40                                  std::string host) {
41   if (scheme == ProxyServer::SCHEME_SOCKS4 &&
42       StartsWithASCII(host, "socks5://", false)) {
43     // We default to socks 4, but if the user specifically set it to
44     // socks5://, then use that.
45     scheme = ProxyServer::SCHEME_SOCKS5;
46   }
47   // Strip the scheme if any.
48   std::string::size_type colon = host.find("://");
49   if (colon != std::string::npos)
50     host = host.substr(colon + 3);
51   // If a username and perhaps password are specified, give a warning.
52   std::string::size_type at_sign = host.find("@");
53   // Should this be supported?
54   if (at_sign != std::string::npos) {
55     // ProxyConfig does not support authentication parameters, but Chrome
56     // will prompt for the password later. Disregard the
57     // authentication parameters and continue with this hostname.
58     LOG(WARNING) << "Proxy authentication parameters ignored, see bug 16709";
59     host = host.substr(at_sign + 1);
60   }
61   // If this is a socks proxy, prepend a scheme so as to tell
62   // ProxyServer. This also allows ProxyServer to choose the right
63   // default port.
64   if (scheme == ProxyServer::SCHEME_SOCKS4)
65     host = "socks4://" + host;
66   else if (scheme == ProxyServer::SCHEME_SOCKS5)
67     host = "socks5://" + host;
68   // If there is a trailing slash, remove it so |host| will parse correctly
69   // even if it includes a port number (since the slash is not numeric).
70   if (host.length() && host[host.length() - 1] == '/')
71     host.resize(host.length() - 1);
72   return host;
73 }
74 
75 }  // namespace
76 
GetProxyFromEnvVarForScheme(const char * variable,ProxyServer::Scheme scheme,ProxyServer * result_server)77 bool ProxyConfigServiceLinux::Delegate::GetProxyFromEnvVarForScheme(
78     const char* variable, ProxyServer::Scheme scheme,
79     ProxyServer* result_server) {
80   std::string env_value;
81   if (env_var_getter_->Getenv(variable, &env_value)) {
82     if (!env_value.empty()) {
83       env_value = FixupProxyHostScheme(scheme, env_value);
84       ProxyServer proxy_server =
85           ProxyServer::FromURI(env_value, ProxyServer::SCHEME_HTTP);
86       if (proxy_server.is_valid() && !proxy_server.is_direct()) {
87         *result_server = proxy_server;
88         return true;
89       } else {
90         LOG(ERROR) << "Failed to parse environment variable " << variable;
91       }
92     }
93   }
94   return false;
95 }
96 
GetProxyFromEnvVar(const char * variable,ProxyServer * result_server)97 bool ProxyConfigServiceLinux::Delegate::GetProxyFromEnvVar(
98     const char* variable, ProxyServer* result_server) {
99   return GetProxyFromEnvVarForScheme(variable, ProxyServer::SCHEME_HTTP,
100                                      result_server);
101 }
102 
GetConfigFromEnv(ProxyConfig * config)103 bool ProxyConfigServiceLinux::Delegate::GetConfigFromEnv(ProxyConfig* config) {
104   // Check for automatic configuration first, in
105   // "auto_proxy". Possibly only the "environment_proxy" firefox
106   // extension has ever used this, but it still sounds like a good
107   // idea.
108   std::string auto_proxy;
109   if (env_var_getter_->Getenv("auto_proxy", &auto_proxy)) {
110     if (auto_proxy.empty()) {
111       // Defined and empty => autodetect
112       config->auto_detect = true;
113     } else {
114       // specified autoconfig URL
115       config->pac_url = GURL(auto_proxy);
116     }
117     return true;
118   }
119   // "all_proxy" is a shortcut to avoid defining {http,https,ftp}_proxy.
120   ProxyServer proxy_server;
121   if (GetProxyFromEnvVar("all_proxy", &proxy_server)) {
122     config->proxy_rules.type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
123     config->proxy_rules.single_proxy = proxy_server;
124   } else {
125     bool have_http = GetProxyFromEnvVar("http_proxy", &proxy_server);
126     if (have_http)
127       config->proxy_rules.proxy_for_http = proxy_server;
128     // It would be tempting to let http_proxy apply for all protocols
129     // if https_proxy and ftp_proxy are not defined. Googling turns up
130     // several documents that mention only http_proxy. But then the
131     // user really might not want to proxy https. And it doesn't seem
132     // like other apps do this. So we will refrain.
133     bool have_https = GetProxyFromEnvVar("https_proxy", &proxy_server);
134     if (have_https)
135       config->proxy_rules.proxy_for_https = proxy_server;
136     bool have_ftp = GetProxyFromEnvVar("ftp_proxy", &proxy_server);
137     if (have_ftp)
138       config->proxy_rules.proxy_for_ftp = proxy_server;
139     if (have_http || have_https || have_ftp) {
140       // mustn't change type unless some rules are actually set.
141       config->proxy_rules.type = ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
142     }
143   }
144   if (config->proxy_rules.empty()) {
145     // If the above were not defined, try for socks.
146     ProxyServer::Scheme scheme = ProxyServer::SCHEME_SOCKS4;
147     std::string env_version;
148     if (env_var_getter_->Getenv("SOCKS_VERSION", &env_version)
149         && env_version == "5")
150       scheme = ProxyServer::SCHEME_SOCKS5;
151     if (GetProxyFromEnvVarForScheme("SOCKS_SERVER", scheme, &proxy_server)) {
152       config->proxy_rules.type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
153       config->proxy_rules.single_proxy = proxy_server;
154     }
155   }
156   // Look for the proxy bypass list.
157   std::string no_proxy;
158   env_var_getter_->Getenv("no_proxy", &no_proxy);
159   if (config->proxy_rules.empty()) {
160     // Having only "no_proxy" set, presumably to "*", makes it
161     // explicit that env vars do specify a configuration: having no
162     // rules specified only means the user explicitly asks for direct
163     // connections.
164     return !no_proxy.empty();
165   }
166   config->ParseNoProxyList(no_proxy);
167   return true;
168 }
169 
170 namespace {
171 
172 const int kDebounceTimeoutMilliseconds = 250;
173 
174 // This is the "real" gconf version that actually uses gconf.
175 class GConfSettingGetterImplGConf
176     : public ProxyConfigServiceLinux::GConfSettingGetter {
177  public:
GConfSettingGetterImplGConf()178   GConfSettingGetterImplGConf()
179       : client_(NULL), notify_delegate_(NULL), loop_(NULL) {}
180 
~GConfSettingGetterImplGConf()181   virtual ~GConfSettingGetterImplGConf() {
182     // client_ should have been released before now, from
183     // Delegate::OnDestroy(), while running on the UI thread. However
184     // on exiting the process, it may happen that
185     // Delegate::OnDestroy() task is left pending on the glib loop
186     // after the loop was quit, and pending tasks may then be deleted
187     // without being run.
188     if (client_) {
189       // gconf client was not cleaned up.
190       if (MessageLoop::current() == loop_) {
191         // We are on the UI thread so we can clean it safely. This is
192         // the case at least for ui_tests running under Valgrind in
193         // bug 16076.
194         LOG(INFO) << "~GConfSettingGetterImplGConf: releasing gconf client";
195         Shutdown();
196       } else {
197         LOG(WARNING) << "~GConfSettingGetterImplGConf: leaking gconf client";
198         client_ = NULL;
199       }
200     }
201     DCHECK(!client_);
202   }
203 
Init(MessageLoop * glib_default_loop,MessageLoopForIO * file_loop)204   virtual bool Init(MessageLoop* glib_default_loop,
205                     MessageLoopForIO* file_loop) {
206     DCHECK(MessageLoop::current() == glib_default_loop);
207     DCHECK(!client_);
208     DCHECK(!loop_);
209     loop_ = glib_default_loop;
210     client_ = gconf_client_get_default();
211     if (!client_) {
212       // It's not clear whether/when this can return NULL.
213       LOG(ERROR) << "Unable to create a gconf client";
214       loop_ = NULL;
215       return false;
216     }
217     GError* error = NULL;
218     // We need to add the directories for which we'll be asking
219     // notifications, and we might as well ask to preload them.
220     gconf_client_add_dir(client_, "/system/proxy",
221                          GCONF_CLIENT_PRELOAD_ONELEVEL, &error);
222     if (error == NULL) {
223       gconf_client_add_dir(client_, "/system/http_proxy",
224                            GCONF_CLIENT_PRELOAD_ONELEVEL, &error);
225     }
226     if (error != NULL) {
227       LOG(ERROR) << "Error requesting gconf directory: " << error->message;
228       g_error_free(error);
229       Shutdown();
230       return false;
231     }
232     return true;
233   }
234 
Shutdown()235   void Shutdown() {
236     if (client_) {
237       DCHECK(MessageLoop::current() == loop_);
238       // This also disables gconf notifications.
239       g_object_unref(client_);
240       client_ = NULL;
241       loop_ = NULL;
242     }
243   }
244 
SetupNotification(ProxyConfigServiceLinux::Delegate * delegate)245   bool SetupNotification(ProxyConfigServiceLinux::Delegate* delegate) {
246     DCHECK(client_);
247     DCHECK(MessageLoop::current() == loop_);
248     GError* error = NULL;
249     notify_delegate_ = delegate;
250     gconf_client_notify_add(
251         client_, "/system/proxy",
252         OnGConfChangeNotification, this,
253         NULL, &error);
254     if (error == NULL) {
255       gconf_client_notify_add(
256           client_, "/system/http_proxy",
257           OnGConfChangeNotification, this,
258           NULL, &error);
259     }
260     if (error != NULL) {
261       LOG(ERROR) << "Error requesting gconf notifications: " << error->message;
262       g_error_free(error);
263       Shutdown();
264       return false;
265     }
266     return true;
267   }
268 
GetNotificationLoop()269   virtual MessageLoop* GetNotificationLoop() {
270     return loop_;
271   }
272 
GetDataSource()273   virtual const char* GetDataSource() {
274     return "gconf";
275   }
276 
GetString(const char * key,std::string * result)277   virtual bool GetString(const char* key, std::string* result) {
278     DCHECK(client_);
279     DCHECK(MessageLoop::current() == loop_);
280     GError* error = NULL;
281     gchar* value = gconf_client_get_string(client_, key, &error);
282     if (HandleGError(error, key))
283       return false;
284     if (!value)
285       return false;
286     *result = value;
287     g_free(value);
288     return true;
289   }
GetBoolean(const char * key,bool * result)290   virtual bool GetBoolean(const char* key, bool* result) {
291     DCHECK(client_);
292     DCHECK(MessageLoop::current() == loop_);
293     GError* error = NULL;
294     // We want to distinguish unset values from values defaulting to
295     // false. For that we need to use the type-generic
296     // gconf_client_get() rather than gconf_client_get_bool().
297     GConfValue* gconf_value = gconf_client_get(client_, key, &error);
298     if (HandleGError(error, key))
299       return false;
300     if (!gconf_value) {
301       // Unset.
302       return false;
303     }
304     if (gconf_value->type != GCONF_VALUE_BOOL) {
305       gconf_value_free(gconf_value);
306       return false;
307     }
308     gboolean bool_value = gconf_value_get_bool(gconf_value);
309     *result = static_cast<bool>(bool_value);
310     gconf_value_free(gconf_value);
311     return true;
312   }
GetInt(const char * key,int * result)313   virtual bool GetInt(const char* key, int* result) {
314     DCHECK(client_);
315     DCHECK(MessageLoop::current() == loop_);
316     GError* error = NULL;
317     int value = gconf_client_get_int(client_, key, &error);
318     if (HandleGError(error, key))
319       return false;
320     // We don't bother to distinguish an unset value because callers
321     // don't care. 0 is returned if unset.
322     *result = value;
323     return true;
324   }
GetStringList(const char * key,std::vector<std::string> * result)325   virtual bool GetStringList(const char* key,
326                              std::vector<std::string>* result) {
327     DCHECK(client_);
328     DCHECK(MessageLoop::current() == loop_);
329     GError* error = NULL;
330     GSList* list = gconf_client_get_list(client_, key,
331                                          GCONF_VALUE_STRING, &error);
332     if (HandleGError(error, key))
333       return false;
334     if (!list) {
335       // unset
336       return false;
337     }
338     for (GSList *it = list; it; it = it->next) {
339       result->push_back(static_cast<char*>(it->data));
340       g_free(it->data);
341     }
342     g_slist_free(list);
343     return true;
344   }
345 
346  private:
347   // Logs and frees a glib error. Returns false if there was no error
348   // (error is NULL).
HandleGError(GError * error,const char * key)349   bool HandleGError(GError* error, const char* key) {
350     if (error != NULL) {
351       LOG(ERROR) << "Error getting gconf value for " << key
352                  << ": " << error->message;
353       g_error_free(error);
354       return true;
355     }
356     return false;
357   }
358 
359   // This is the callback from the debounce timer.
OnDebouncedNotification()360   void OnDebouncedNotification() {
361     DCHECK(MessageLoop::current() == loop_);
362     DCHECK(notify_delegate_);
363     // Forward to a method on the proxy config service delegate object.
364     notify_delegate_->OnCheckProxyConfigSettings();
365   }
366 
OnChangeNotification()367   void OnChangeNotification() {
368     // We don't use Reset() because the timer may not yet be running.
369     // (In that case Stop() is a no-op.)
370     debounce_timer_.Stop();
371     debounce_timer_.Start(base::TimeDelta::FromMilliseconds(
372         kDebounceTimeoutMilliseconds), this,
373         &GConfSettingGetterImplGConf::OnDebouncedNotification);
374   }
375 
376   // gconf notification callback, dispatched from the default glib main loop.
OnGConfChangeNotification(GConfClient * client,guint cnxn_id,GConfEntry * entry,gpointer user_data)377   static void OnGConfChangeNotification(
378       GConfClient* client, guint cnxn_id,
379       GConfEntry* entry, gpointer user_data) {
380     LOG(INFO) << "gconf change notification for key "
381               << gconf_entry_get_key(entry);
382     // We don't track which key has changed, just that something did change.
383     GConfSettingGetterImplGConf* setting_getter =
384         reinterpret_cast<GConfSettingGetterImplGConf*>(user_data);
385     setting_getter->OnChangeNotification();
386   }
387 
388   GConfClient* client_;
389   ProxyConfigServiceLinux::Delegate* notify_delegate_;
390   base::OneShotTimer<GConfSettingGetterImplGConf> debounce_timer_;
391 
392   // Message loop of the thread that we make gconf calls on. It should
393   // be the UI thread and all our methods should be called on this
394   // thread. Only for assertions.
395   MessageLoop* loop_;
396 
397   DISALLOW_COPY_AND_ASSIGN(GConfSettingGetterImplGConf);
398 };
399 
400 // This is the KDE version that reads kioslaverc and simulates gconf.
401 // Doing this allows the main Delegate code, as well as the unit tests
402 // for it, to stay the same - and the settings map fairly well besides.
403 class GConfSettingGetterImplKDE
404     : public ProxyConfigServiceLinux::GConfSettingGetter,
405       public base::MessagePumpLibevent::Watcher {
406  public:
GConfSettingGetterImplKDE(base::EnvironmentVariableGetter * env_var_getter)407   explicit GConfSettingGetterImplKDE(
408       base::EnvironmentVariableGetter* env_var_getter)
409       : inotify_fd_(-1), notify_delegate_(NULL), indirect_manual_(false),
410         auto_no_pac_(false), reversed_exception_(false), file_loop_(NULL) {
411     // We don't save the env var getter for later use since we don't own it.
412     // Instead we use it here and save the result we actually care about.
413     std::string kde_home;
414     if (!env_var_getter->Getenv("KDE_HOME", &kde_home)) {
415       if (!env_var_getter->Getenv("HOME", &kde_home))
416         // User has no $HOME? Give up. Later we'll report the failure.
417         return;
418       kde_home = FilePath(kde_home).Append(FILE_PATH_LITERAL(".kde")).value();
419     }
420     kde_config_dir_ = FilePath(kde_home).Append(
421         FILE_PATH_LITERAL("share")).Append(FILE_PATH_LITERAL("config"));
422   }
423 
~GConfSettingGetterImplKDE()424   virtual ~GConfSettingGetterImplKDE() {
425     // inotify_fd_ should have been closed before now, from
426     // Delegate::OnDestroy(), while running on the file thread. However
427     // on exiting the process, it may happen that Delegate::OnDestroy()
428     // task is left pending on the file loop after the loop was quit,
429     // and pending tasks may then be deleted without being run.
430     // Here in the KDE version, we can safely close the file descriptor
431     // anyway. (Not that it really matters; the process is exiting.)
432     if (inotify_fd_ >= 0)
433       Shutdown();
434     DCHECK(inotify_fd_ < 0);
435   }
436 
Init(MessageLoop * glib_default_loop,MessageLoopForIO * file_loop)437   virtual bool Init(MessageLoop* glib_default_loop,
438                     MessageLoopForIO* file_loop) {
439     DCHECK(inotify_fd_ < 0);
440     inotify_fd_ = inotify_init();
441     if (inotify_fd_ < 0) {
442       PLOG(ERROR) << "inotify_init failed";
443       return false;
444     }
445     int flags = fcntl(inotify_fd_, F_GETFL);
446     if (fcntl(inotify_fd_, F_SETFL, flags | O_NONBLOCK) < 0) {
447       PLOG(ERROR) << "fcntl failed";
448       close(inotify_fd_);
449       inotify_fd_ = -1;
450       return false;
451     }
452     file_loop_ = file_loop;
453     // The initial read is done on the current thread, not |file_loop_|,
454     // since we will need to have it for SetupAndFetchInitialConfig().
455     UpdateCachedSettings();
456     return true;
457   }
458 
Shutdown()459   void Shutdown() {
460     if (inotify_fd_ >= 0) {
461       ResetCachedSettings();
462       inotify_watcher_.StopWatchingFileDescriptor();
463       close(inotify_fd_);
464       inotify_fd_ = -1;
465     }
466   }
467 
SetupNotification(ProxyConfigServiceLinux::Delegate * delegate)468   bool SetupNotification(ProxyConfigServiceLinux::Delegate* delegate) {
469     DCHECK(inotify_fd_ >= 0);
470     DCHECK(file_loop_);
471     // We can't just watch the kioslaverc file directly, since KDE will write
472     // a new copy of it and then rename it whenever settings are changed and
473     // inotify watches inodes (so we'll be watching the old deleted file after
474     // the first change, and it will never change again). So, we watch the
475     // directory instead. We then act only on changes to the kioslaverc entry.
476     if (inotify_add_watch(inotify_fd_, kde_config_dir_.value().c_str(),
477                           IN_MODIFY | IN_MOVED_TO) < 0)
478       return false;
479     notify_delegate_ = delegate;
480     return file_loop_->WatchFileDescriptor(inotify_fd_, true,
481         MessageLoopForIO::WATCH_READ, &inotify_watcher_, this);
482   }
483 
GetNotificationLoop()484   virtual MessageLoop* GetNotificationLoop() {
485     return file_loop_;
486   }
487 
488   // Implement base::MessagePumpLibevent::Delegate.
OnFileCanReadWithoutBlocking(int fd)489   void OnFileCanReadWithoutBlocking(int fd) {
490     DCHECK(fd == inotify_fd_);
491     DCHECK(MessageLoop::current() == file_loop_);
492     OnChangeNotification();
493   }
OnFileCanWriteWithoutBlocking(int fd)494   void OnFileCanWriteWithoutBlocking(int fd) {
495     NOTREACHED();
496   }
497 
GetDataSource()498   virtual const char* GetDataSource() {
499     return "KDE";
500   }
501 
GetString(const char * key,std::string * result)502   virtual bool GetString(const char* key, std::string* result) {
503     string_map_type::iterator it = string_table_.find(key);
504     if (it == string_table_.end())
505       return false;
506     *result = it->second;
507     return true;
508   }
GetBoolean(const char * key,bool * result)509   virtual bool GetBoolean(const char* key, bool* result) {
510     // We don't ever have any booleans.
511     return false;
512   }
GetInt(const char * key,int * result)513   virtual bool GetInt(const char* key, int* result) {
514     // We don't ever have any integers. (See AddProxy() below about ports.)
515     return false;
516   }
GetStringList(const char * key,std::vector<std::string> * result)517   virtual bool GetStringList(const char* key,
518                              std::vector<std::string>* result) {
519     strings_map_type::iterator it = strings_table_.find(key);
520     if (it == strings_table_.end())
521       return false;
522     *result = it->second;
523     return true;
524   }
525 
526  private:
ResetCachedSettings()527   void ResetCachedSettings() {
528     string_table_.clear();
529     strings_table_.clear();
530     indirect_manual_ = false;
531     auto_no_pac_ = false;
532     reversed_exception_ = false;
533   }
534 
AddProxy(std::string prefix,std::string value)535   void AddProxy(std::string prefix, std::string value) {
536     if (value.empty() || value.substr(0, 3) == "//:")
537       // No proxy.
538       return;
539     // We don't need to parse the port number out; GetProxyFromGConf()
540     // would only append it right back again. So we just leave the port
541     // number right in the host string.
542     string_table_[prefix + "host"] = value;
543   }
544 
AddKDESetting(const std::string & key,const std::string & value)545   void AddKDESetting(const std::string& key, const std::string& value) {
546     // The astute reader may notice that there is no mention of SOCKS
547     // here. That's because KDE handles socks is a strange way, and we
548     // don't support it. Rather than just a setting for the SOCKS server,
549     // it has a setting for a library to LD_PRELOAD in all your programs
550     // that will transparently SOCKSify them. Such libraries each have
551     // their own configuration, and thus, we can't get it from KDE.
552     if (key == "ProxyType") {
553       const char* mode = "none";
554       indirect_manual_ = false;
555       auto_no_pac_ = false;
556       switch (StringToInt(value)) {
557         case 0:  // No proxy, or maybe kioslaverc syntax error.
558           break;
559         case 1:  // Manual configuration.
560           mode = "manual";
561           break;
562         case 2:  // PAC URL.
563           mode = "auto";
564           break;
565         case 3:  // WPAD.
566           mode = "auto";
567           auto_no_pac_ = true;
568           break;
569         case 4:  // Indirect manual via environment variables.
570           mode = "manual";
571           indirect_manual_ = true;
572           break;
573       }
574       string_table_["/system/proxy/mode"] = mode;
575     } else if (key == "Proxy Config Script") {
576       string_table_["/system/proxy/autoconfig_url"] = value;
577     } else if (key == "httpProxy") {
578       AddProxy("/system/http_proxy/", value);
579     } else if (key == "httpsProxy") {
580       AddProxy("/system/proxy/secure_", value);
581     } else if (key == "ftpProxy") {
582       AddProxy("/system/proxy/ftp_", value);
583     } else if (key == "ReversedException") {
584       // We count "true" or any nonzero number as true, otherwise false.
585       // Note that if the value is not actually numeric StringToInt()
586       // will return 0, which we count as false.
587       reversed_exception_ = value == "true" || StringToInt(value);
588     } else if (key == "NoProxyFor") {
589       std::vector<std::string> exceptions;
590       StringTokenizer tk(value, ",");
591       while (tk.GetNext()) {
592         std::string token = tk.token();
593         if (!token.empty())
594           exceptions.push_back(token);
595       }
596       strings_table_["/system/http_proxy/ignore_hosts"] = exceptions;
597     } else if (key == "AuthMode") {
598       // Check for authentication, just so we can warn.
599       int mode = StringToInt(value);
600       if (mode) {
601         // ProxyConfig does not support authentication parameters, but
602         // Chrome will prompt for the password later. So we ignore this.
603         LOG(WARNING) <<
604             "Proxy authentication parameters ignored, see bug 16709";
605       }
606     }
607   }
608 
ResolveIndirect(std::string key)609   void ResolveIndirect(std::string key) {
610     // We can't save the environment variable getter that was passed
611     // when this object was constructed, but this setting is likely
612     // to be pretty unusual and the actual values it would return can
613     // be tested without using it. So we just use getenv() here.
614     string_map_type::iterator it = string_table_.find(key);
615     if (it != string_table_.end()) {
616       char* value = getenv(it->second.c_str());
617       if (value)
618         it->second = value;
619     }
620   }
621 
622   // The settings in kioslaverc could occur in any order, but some affect
623   // others. Rather than read the whole file in and then query them in an
624   // order that allows us to handle that, we read the settings in whatever
625   // order they occur and do any necessary tweaking after we finish.
ResolveModeEffects()626   void ResolveModeEffects() {
627     if (indirect_manual_) {
628       ResolveIndirect("/system/http_proxy/host");
629       ResolveIndirect("/system/proxy/secure_host");
630       ResolveIndirect("/system/proxy/ftp_host");
631     }
632     if (auto_no_pac_) {
633       // Remove the PAC URL; we're not supposed to use it.
634       string_table_.erase("/system/proxy/autoconfig_url");
635     }
636     if (reversed_exception_) {
637       // We don't actually support this setting. (It means to use the proxy
638       // *only* for the exception list, rather than everything but them.)
639       // Nevertheless we can do better than *exactly the opposite* of the
640       // desired behavior by clearing the exception list and warning.
641       strings_table_.erase("/system/http_proxy/ignore_hosts");
642       LOG(WARNING) << "KDE reversed proxy exception list not supported";
643     }
644   }
645 
646   // Reads kioslaverc one line at a time and calls AddKDESetting() to add
647   // each relevant name-value pair to the appropriate value table.
UpdateCachedSettings()648   void UpdateCachedSettings() {
649     FilePath kioslaverc = kde_config_dir_.Append(
650         FILE_PATH_LITERAL("kioslaverc"));
651     file_util::ScopedFILE input(file_util::OpenFile(kioslaverc, "r"));
652     if (!input.get())
653       return;
654     ResetCachedSettings();
655     bool in_proxy_settings = false;
656     bool line_too_long = false;
657     char line[BUFFER_SIZE];
658     // fgets() will return NULL on EOF or error.
659     while (fgets(line, sizeof(line), input.get())) {
660       // fgets() guarantees the line will be properly terminated.
661       size_t length = strlen(line);
662       if (!length)
663         continue;
664       // This should be true even with CRLF endings.
665       if (line[length - 1] != '\n') {
666         line_too_long = true;
667         continue;
668       }
669       if (line_too_long) {
670         // The previous line had no line ending, but this done does. This is
671         // the end of the line that was too long, so warn here and skip it.
672         LOG(WARNING) << "skipped very long line in " << kioslaverc.value();
673         line_too_long = false;
674         continue;
675       }
676       // Remove the LF at the end, and the CR if there is one.
677       line[--length] = '\0';
678       if (length && line[length - 1] == '\r')
679         line[--length] = '\0';
680       // Now parse the line.
681       if (line[0] == '[') {
682         // Switching sections. All we care about is whether this is
683         // the (a?) proxy settings section, for both KDE3 and KDE4.
684         in_proxy_settings = !strncmp(line, "[Proxy Settings]", 16);
685       } else if (in_proxy_settings) {
686         // A regular line, in the (a?) proxy settings section.
687         char* split = strchr(line, '=');
688         // Skip this line if it does not contain an = sign.
689         if (!split)
690           continue;
691         // Split the line on the = and advance |split|.
692         *(split++) = 0;
693         std::string key = line;
694         std::string value = split;
695         TrimWhitespaceASCII(key, TRIM_ALL, &key);
696         TrimWhitespaceASCII(value, TRIM_ALL, &value);
697         // Skip this line if the key name is empty.
698         if (key.empty())
699           continue;
700         // Is the value name localized?
701         if (key[key.length() - 1] == ']') {
702           // Find the matching bracket.
703           length = key.rfind('[');
704           // Skip this line if the localization indicator is malformed.
705           if (length == std::string::npos)
706             continue;
707           // Trim the localization indicator off.
708           key.resize(length);
709           // Remove any resulting trailing whitespace.
710           TrimWhitespaceASCII(key, TRIM_TRAILING, &key);
711           // Skip this line if the key name is now empty.
712           if (key.empty())
713             continue;
714         }
715         // Now fill in the tables.
716         AddKDESetting(key, value);
717       }
718     }
719     if (ferror(input.get()))
720       LOG(ERROR) << "error reading " << kioslaverc.value();
721     ResolveModeEffects();
722   }
723 
724   // This is the callback from the debounce timer.
OnDebouncedNotification()725   void OnDebouncedNotification() {
726     DCHECK(MessageLoop::current() == file_loop_);
727     LOG(INFO) << "inotify change notification for kioslaverc";
728     UpdateCachedSettings();
729     DCHECK(notify_delegate_);
730     // Forward to a method on the proxy config service delegate object.
731     notify_delegate_->OnCheckProxyConfigSettings();
732   }
733 
734   // Called by OnFileCanReadWithoutBlocking() on the file thread. Reads
735   // from the inotify file descriptor and starts up a debounce timer if
736   // an event for kioslaverc is seen.
OnChangeNotification()737   void OnChangeNotification() {
738     DCHECK(inotify_fd_ >= 0);
739     DCHECK(MessageLoop::current() == file_loop_);
740     char event_buf[(sizeof(inotify_event) + NAME_MAX + 1) * 4];
741     bool kioslaverc_touched = false;
742     ssize_t r;
743     while ((r = read(inotify_fd_, event_buf, sizeof(event_buf))) > 0) {
744       // inotify returns variable-length structures, which is why we have
745       // this strange-looking loop instead of iterating through an array.
746       char* event_ptr = event_buf;
747       while (event_ptr < event_buf + r) {
748         inotify_event* event = reinterpret_cast<inotify_event*>(event_ptr);
749         // The kernel always feeds us whole events.
750         CHECK(event_ptr + sizeof(inotify_event) <= event_buf + r);
751         CHECK(event->name + event->len <= event_buf + r);
752         if (!strcmp(event->name, "kioslaverc"))
753           kioslaverc_touched = true;
754         // Advance the pointer just past the end of the filename.
755         event_ptr = event->name + event->len;
756       }
757       // We keep reading even if |kioslaverc_touched| is true to drain the
758       // inotify event queue.
759     }
760     if (!r)
761       // Instead of returning -1 and setting errno to EINVAL if there is not
762       // enough buffer space, older kernels (< 2.6.21) return 0. Simulate the
763       // new behavior (EINVAL) so we can reuse the code below.
764       errno = EINVAL;
765     if (errno != EAGAIN) {
766       PLOG(WARNING) << "error reading inotify file descriptor";
767       if (errno == EINVAL) {
768         // Our buffer is not large enough to read the next event. This should
769         // not happen (because its size is calculated to always be sufficiently
770         // large), but if it does we'd warn continuously since |inotify_fd_|
771         // would be forever ready to read. Close it and stop watching instead.
772         LOG(ERROR) << "inotify failure; no longer watching kioslaverc!";
773         inotify_watcher_.StopWatchingFileDescriptor();
774         close(inotify_fd_);
775         inotify_fd_ = -1;
776       }
777     }
778     if (kioslaverc_touched) {
779       // We don't use Reset() because the timer may not yet be running.
780       // (In that case Stop() is a no-op.)
781       debounce_timer_.Stop();
782       debounce_timer_.Start(base::TimeDelta::FromMilliseconds(
783           kDebounceTimeoutMilliseconds), this,
784           &GConfSettingGetterImplKDE::OnDebouncedNotification);
785     }
786   }
787 
788   typedef std::map<std::string, std::string> string_map_type;
789   typedef std::map<std::string, std::vector<std::string> > strings_map_type;
790 
791   int inotify_fd_;
792   base::MessagePumpLibevent::FileDescriptorWatcher inotify_watcher_;
793   ProxyConfigServiceLinux::Delegate* notify_delegate_;
794   base::OneShotTimer<GConfSettingGetterImplKDE> debounce_timer_;
795   FilePath kde_config_dir_;
796   bool indirect_manual_;
797   bool auto_no_pac_;
798   bool reversed_exception_;
799 
800   // We cache these settings whenever we re-read the kioslaverc file.
801   string_map_type string_table_;
802   strings_map_type strings_table_;
803 
804   // Message loop of the file thread, for reading kioslaverc. If NULL,
805   // just read it directly (for testing). We also handle inotify events
806   // on this thread.
807   MessageLoopForIO* file_loop_;
808 
809   DISALLOW_COPY_AND_ASSIGN(GConfSettingGetterImplKDE);
810 };
811 
812 }  // namespace
813 
GetProxyFromGConf(const char * key_prefix,bool is_socks,ProxyServer * result_server)814 bool ProxyConfigServiceLinux::Delegate::GetProxyFromGConf(
815     const char* key_prefix, bool is_socks, ProxyServer* result_server) {
816   std::string key(key_prefix);
817   std::string host;
818   if (!gconf_getter_->GetString((key + "host").c_str(), &host)
819       || host.empty()) {
820     // Unset or empty.
821     return false;
822   }
823   // Check for an optional port.
824   int port = 0;
825   gconf_getter_->GetInt((key + "port").c_str(), &port);
826   if (port != 0) {
827     // If a port is set and non-zero:
828     host += ":" + IntToString(port);
829   }
830   host = FixupProxyHostScheme(
831       is_socks ? ProxyServer::SCHEME_SOCKS4 : ProxyServer::SCHEME_HTTP,
832       host);
833   ProxyServer proxy_server = ProxyServer::FromURI(host,
834                                                   ProxyServer::SCHEME_HTTP);
835   if (proxy_server.is_valid()) {
836     *result_server = proxy_server;
837     return true;
838   }
839   return false;
840 }
841 
GetConfigFromGConf(ProxyConfig * config)842 bool ProxyConfigServiceLinux::Delegate::GetConfigFromGConf(
843     ProxyConfig* config) {
844   std::string mode;
845   if (!gconf_getter_->GetString("/system/proxy/mode", &mode)) {
846     // We expect this to always be set, so if we don't see it then we
847     // probably have a gconf problem, and so we don't have a valid
848     // proxy config.
849     return false;
850   }
851   if (mode == "none") {
852     // Specifically specifies no proxy.
853     return true;
854   }
855 
856   if (mode == "auto") {
857     // automatic proxy config
858     std::string pac_url_str;
859     if (gconf_getter_->GetString("/system/proxy/autoconfig_url",
860                                  &pac_url_str)) {
861       if (!pac_url_str.empty()) {
862         GURL pac_url(pac_url_str);
863         if (!pac_url.is_valid())
864           return false;
865         config->pac_url = pac_url;
866         return true;
867       }
868     }
869     config->auto_detect = true;
870     return true;
871   }
872 
873   if (mode != "manual") {
874     // Mode is unrecognized.
875     return false;
876   }
877   bool use_http_proxy;
878   if (gconf_getter_->GetBoolean("/system/http_proxy/use_http_proxy",
879                                 &use_http_proxy)
880       && !use_http_proxy) {
881     // Another master switch for some reason. If set to false, then no
882     // proxy. But we don't panic if the key doesn't exist.
883     return true;
884   }
885 
886   bool same_proxy = false;
887   // Indicates to use the http proxy for all protocols. This one may
888   // not exist (presumably on older versions), assume false in that
889   // case.
890   gconf_getter_->GetBoolean("/system/http_proxy/use_same_proxy",
891                             &same_proxy);
892 
893   ProxyServer proxy_server;
894   if (!same_proxy) {
895     // Try socks.
896     if (GetProxyFromGConf("/system/proxy/socks_", true, &proxy_server)) {
897       // gconf settings do not appear to distinguish between socks
898       // version. We default to version 4.
899       config->proxy_rules.type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
900       config->proxy_rules.single_proxy = proxy_server;
901     }
902   }
903   if (config->proxy_rules.empty()) {
904     bool have_http = GetProxyFromGConf("/system/http_proxy/", false,
905                                        &proxy_server);
906     if (same_proxy) {
907       if (have_http) {
908         config->proxy_rules.type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
909         config->proxy_rules.single_proxy = proxy_server;
910       }
911     } else {
912       // Protocol specific settings.
913       if (have_http)
914         config->proxy_rules.proxy_for_http = proxy_server;
915       bool have_secure = GetProxyFromGConf("/system/proxy/secure_", false,
916                                            &proxy_server);
917       if (have_secure)
918         config->proxy_rules.proxy_for_https = proxy_server;
919       bool have_ftp = GetProxyFromGConf("/system/proxy/ftp_", false,
920                                         &proxy_server);
921       if (have_ftp)
922         config->proxy_rules.proxy_for_ftp = proxy_server;
923       if (have_http || have_secure || have_ftp)
924         config->proxy_rules.type =
925             ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
926     }
927   }
928 
929   if (config->proxy_rules.empty()) {
930     // Manual mode but we couldn't parse any rules.
931     return false;
932   }
933 
934   // Check for authentication, just so we can warn.
935   bool use_auth = false;
936   gconf_getter_->GetBoolean("/system/http_proxy/use_authentication",
937                             &use_auth);
938   if (use_auth) {
939     // ProxyConfig does not support authentication parameters, but
940     // Chrome will prompt for the password later. So we ignore
941     // /system/http_proxy/*auth* settings.
942     LOG(WARNING) << "Proxy authentication parameters ignored, see bug 16709";
943   }
944 
945   // Now the bypass list.
946   gconf_getter_->GetStringList("/system/http_proxy/ignore_hosts",
947                                &config->proxy_bypass);
948   // Note that there are no settings with semantics corresponding to
949   // config->proxy_bypass_local_names.
950 
951   return true;
952 }
953 
Delegate(base::EnvironmentVariableGetter * env_var_getter)954 ProxyConfigServiceLinux::Delegate::Delegate(
955     base::EnvironmentVariableGetter* env_var_getter)
956     : env_var_getter_(env_var_getter),
957       glib_default_loop_(NULL), io_loop_(NULL) {
958   // Figure out which GConfSettingGetterImpl to use, if any.
959   switch (base::GetDesktopEnvironment(env_var_getter)) {
960     case base::DESKTOP_ENVIRONMENT_GNOME:
961       gconf_getter_.reset(new GConfSettingGetterImplGConf());
962       break;
963     case base::DESKTOP_ENVIRONMENT_KDE3:
964     case base::DESKTOP_ENVIRONMENT_KDE4:
965       gconf_getter_.reset(new GConfSettingGetterImplKDE(env_var_getter));
966       break;
967     case base::DESKTOP_ENVIRONMENT_OTHER:
968       break;
969   }
970 }
971 
Delegate(base::EnvironmentVariableGetter * env_var_getter,GConfSettingGetter * gconf_getter)972 ProxyConfigServiceLinux::Delegate::Delegate(
973     base::EnvironmentVariableGetter* env_var_getter,
974     GConfSettingGetter* gconf_getter)
975     : env_var_getter_(env_var_getter), gconf_getter_(gconf_getter),
976       glib_default_loop_(NULL), io_loop_(NULL) {
977 }
978 
SetupAndFetchInitialConfig(MessageLoop * glib_default_loop,MessageLoop * io_loop,MessageLoopForIO * file_loop)979 void ProxyConfigServiceLinux::Delegate::SetupAndFetchInitialConfig(
980     MessageLoop* glib_default_loop, MessageLoop* io_loop,
981     MessageLoopForIO* file_loop) {
982   // We should be running on the default glib main loop thread right
983   // now. gconf can only be accessed from this thread.
984   DCHECK(MessageLoop::current() == glib_default_loop);
985   glib_default_loop_ = glib_default_loop;
986   io_loop_ = io_loop;
987 
988   // If we are passed a NULL io_loop or file_loop, then we don't set up
989   // proxy setting change notifications. This should not be the usual
990   // case but is intended to simplify test setups.
991   if (!io_loop_ || !file_loop)
992     LOG(INFO) << "Monitoring of proxy setting changes is disabled";
993 
994   // Fetch and cache the current proxy config. The config is left in
995   // cached_config_, where GetProxyConfig() running on the IO thread
996   // will expect to find it. This is safe to do because we return
997   // before this ProxyConfigServiceLinux is passed on to
998   // the ProxyService.
999 
1000   // Note: It would be nice to prioritize environment variables
1001   // and only fallback to gconf if env vars were unset. But
1002   // gnome-terminal "helpfully" sets http_proxy and no_proxy, and it
1003   // does so even if the proxy mode is set to auto, which would
1004   // mislead us.
1005 
1006   bool got_config = false;
1007   if (gconf_getter_.get()) {
1008     if (gconf_getter_->Init(glib_default_loop, file_loop) &&
1009         (!io_loop || !file_loop || gconf_getter_->SetupNotification(this))) {
1010       if (GetConfigFromGConf(&cached_config_)) {
1011         cached_config_.set_id(1);  // mark it as valid
1012         got_config = true;
1013         LOG(INFO) << "Obtained proxy settings from " <<
1014             gconf_getter_->GetDataSource();
1015         // If gconf proxy mode is "none", meaning direct, then we take
1016         // that to be a valid config and will not check environment
1017         // variables. The alternative would have been to look for a proxy
1018         // whereever we can find one.
1019         //
1020         // Keep a copy of the config for use from this thread for
1021         // comparison with updated settings when we get notifications.
1022         reference_config_ = cached_config_;
1023         reference_config_.set_id(1);  // mark it as valid
1024       } else {
1025         gconf_getter_->Shutdown();  // Stop notifications
1026       }
1027     }
1028   }
1029 
1030   if (!got_config) {
1031     // We fall back on environment variables.
1032     //
1033     // Consulting environment variables doesn't need to be done from
1034     // the default glib main loop, but it's a tiny enough amount of
1035     // work.
1036     if (GetConfigFromEnv(&cached_config_)) {
1037       cached_config_.set_id(1);  // mark it as valid
1038       LOG(INFO) << "Obtained proxy settings from environment variables";
1039     }
1040   }
1041 }
1042 
GetProxyConfig(ProxyConfig * config)1043 int ProxyConfigServiceLinux::Delegate::GetProxyConfig(ProxyConfig* config) {
1044   // This is called from the IO thread.
1045   DCHECK(!io_loop_ || MessageLoop::current() == io_loop_);
1046 
1047   // Simply return the last proxy configuration that glib_default_loop
1048   // notified us of.
1049   *config = cached_config_;
1050   return cached_config_.is_valid() ? OK : ERR_FAILED;
1051 }
1052 
1053 // Depending on the GConfSettingGetter in use, this method will be called
1054 // on either the UI thread (GConf) or the file thread (KDE).
OnCheckProxyConfigSettings()1055 void ProxyConfigServiceLinux::Delegate::OnCheckProxyConfigSettings() {
1056   MessageLoop* required_loop = gconf_getter_->GetNotificationLoop();
1057   DCHECK(!required_loop || MessageLoop::current() == required_loop);
1058   ProxyConfig new_config;
1059   bool valid = GetConfigFromGConf(&new_config);
1060   if (valid)
1061     new_config.set_id(1);  // mark it as valid
1062 
1063   // See if it is different than what we had before.
1064   if (new_config.is_valid() != reference_config_.is_valid() ||
1065       !new_config.Equals(reference_config_)) {
1066     // Post a task to |io_loop| with the new configuration, so it can
1067     // update |cached_config_|.
1068     io_loop_->PostTask(
1069         FROM_HERE,
1070         NewRunnableMethod(
1071             this,
1072             &ProxyConfigServiceLinux::Delegate::SetNewProxyConfig,
1073             new_config));
1074     // Update the thread-private copy in |reference_config_| as well.
1075     reference_config_ = new_config;
1076   }
1077 }
1078 
SetNewProxyConfig(const ProxyConfig & new_config)1079 void ProxyConfigServiceLinux::Delegate::SetNewProxyConfig(
1080     const ProxyConfig& new_config) {
1081   DCHECK(MessageLoop::current() == io_loop_);
1082   LOG(INFO) << "Proxy configuration changed";
1083   cached_config_ = new_config;
1084 }
1085 
PostDestroyTask()1086 void ProxyConfigServiceLinux::Delegate::PostDestroyTask() {
1087   if (!gconf_getter_.get())
1088     return;
1089   MessageLoop* shutdown_loop = gconf_getter_->GetNotificationLoop();
1090   if (!shutdown_loop || MessageLoop::current() == shutdown_loop) {
1091     // Already on the right thread, call directly.
1092     // This is the case for the unittests.
1093     OnDestroy();
1094   } else {
1095     // Post to shutdown thread. Note that on browser shutdown, we may quit
1096     // this MessageLoop and exit the program before ever running this.
1097     shutdown_loop->PostTask(
1098         FROM_HERE,
1099         NewRunnableMethod(
1100             this,
1101             &ProxyConfigServiceLinux::Delegate::OnDestroy));
1102   }
1103 }
OnDestroy()1104 void ProxyConfigServiceLinux::Delegate::OnDestroy() {
1105   MessageLoop* shutdown_loop = gconf_getter_->GetNotificationLoop();
1106   DCHECK(!shutdown_loop || MessageLoop::current() == shutdown_loop);
1107   gconf_getter_->Shutdown();
1108 }
1109 
ProxyConfigServiceLinux()1110 ProxyConfigServiceLinux::ProxyConfigServiceLinux()
1111     : delegate_(new Delegate(base::EnvironmentVariableGetter::Create())) {
1112 }
1113 
ProxyConfigServiceLinux(base::EnvironmentVariableGetter * env_var_getter)1114 ProxyConfigServiceLinux::ProxyConfigServiceLinux(
1115     base::EnvironmentVariableGetter* env_var_getter)
1116     : delegate_(new Delegate(env_var_getter)) {
1117 }
1118 
ProxyConfigServiceLinux(base::EnvironmentVariableGetter * env_var_getter,GConfSettingGetter * gconf_getter)1119 ProxyConfigServiceLinux::ProxyConfigServiceLinux(
1120     base::EnvironmentVariableGetter* env_var_getter,
1121     GConfSettingGetter* gconf_getter)
1122     : delegate_(new Delegate(env_var_getter, gconf_getter)) {
1123 }
1124 
1125 }  // namespace net
1126