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