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