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 "chrome/browser/chromeos/proxy_config_service_impl.h" 6 7#include <ostream> 8 9#include "base/logging.h" 10#include "base/string_util.h" 11#include "base/task.h" 12#include "chrome/browser/chromeos/cros/cros_library.h" 13#include "chrome/browser/chromeos/cros_settings_names.h" 14#include "chrome/browser/policy/proto/chrome_device_policy.pb.h" 15#include "chrome/browser/prefs/proxy_prefs.h" 16#include "content/browser/browser_thread.h" 17 18namespace em = enterprise_management; 19 20namespace chromeos { 21 22namespace { 23 24const char* SourceToString(ProxyConfigServiceImpl::ProxyConfig::Source source) { 25 switch (source) { 26 case ProxyConfigServiceImpl::ProxyConfig::SOURCE_NONE: 27 return "SOURCE_NONE"; 28 case ProxyConfigServiceImpl::ProxyConfig::SOURCE_POLICY: 29 return "SOURCE_POLICY"; 30 case ProxyConfigServiceImpl::ProxyConfig::SOURCE_OWNER: 31 return "SOURCE_OWNER"; 32 } 33 NOTREACHED() << "Unrecognized source type"; 34 return ""; 35} 36 37std::ostream& operator<<(std::ostream& out, 38 const ProxyConfigServiceImpl::ProxyConfig::ManualProxy& proxy) { 39 out << " " << SourceToString(proxy.source) << "\n" 40 << " server: " << (proxy.server.is_valid() ? proxy.server.ToURI() : "") 41 << "\n"; 42 return out; 43} 44 45std::ostream& operator<<(std::ostream& out, 46 const ProxyConfigServiceImpl::ProxyConfig& config) { 47 switch (config.mode) { 48 case ProxyConfigServiceImpl::ProxyConfig::MODE_DIRECT: 49 out << "Direct connection:\n " 50 << SourceToString(config.automatic_proxy.source) << "\n"; 51 break; 52 case ProxyConfigServiceImpl::ProxyConfig::MODE_AUTO_DETECT: 53 out << "Auto detection:\n " 54 << SourceToString(config.automatic_proxy.source) << "\n"; 55 break; 56 case ProxyConfigServiceImpl::ProxyConfig::MODE_PAC_SCRIPT: 57 out << "Custom PAC script:\n " 58 << SourceToString(config.automatic_proxy.source) 59 << "\n PAC: " << config.automatic_proxy.pac_url << "\n"; 60 break; 61 case ProxyConfigServiceImpl::ProxyConfig::MODE_SINGLE_PROXY: 62 out << "Single proxy:\n" << config.single_proxy; 63 break; 64 case ProxyConfigServiceImpl::ProxyConfig::MODE_PROXY_PER_SCHEME: 65 out << "HTTP proxy: " << config.http_proxy; 66 out << "HTTPS proxy: " << config.https_proxy; 67 out << "FTP proxy: " << config.ftp_proxy; 68 out << "SOCKS proxy: " << config.socks_proxy; 69 break; 70 default: 71 NOTREACHED() << "Unrecognized proxy config mode"; 72 break; 73 } 74 if (config.mode == ProxyConfigServiceImpl::ProxyConfig::MODE_SINGLE_PROXY || 75 config.mode == 76 ProxyConfigServiceImpl::ProxyConfig::MODE_PROXY_PER_SCHEME) { 77 out << "Bypass list: "; 78 if (config.bypass_rules.rules().empty()) { 79 out << "[None]"; 80 } else { 81 const net::ProxyBypassRules& bypass_rules = config.bypass_rules; 82 net::ProxyBypassRules::RuleList::const_iterator it; 83 for (it = bypass_rules.rules().begin(); 84 it != bypass_rules.rules().end(); ++it) { 85 out << "\n " << (*it)->ToString(); 86 } 87 } 88 } 89 return out; 90} 91 92std::string ProxyConfigToString( 93 const ProxyConfigServiceImpl::ProxyConfig& proxy_config) { 94 std::ostringstream stream; 95 stream << proxy_config; 96 return stream.str(); 97} 98 99} // namespace 100 101//---------- ProxyConfigServiceImpl::ProxyConfig::Setting methods -------------- 102 103bool ProxyConfigServiceImpl::ProxyConfig::Setting::CanBeWrittenByUser( 104 bool user_is_owner) { 105 // Setting can only be written by user if user is owner and setting is not 106 // from policy. 107 return user_is_owner && source != ProxyConfig::SOURCE_POLICY; 108} 109 110//----------- ProxyConfigServiceImpl::ProxyConfig: public methods -------------- 111 112void ProxyConfigServiceImpl::ProxyConfig::ToNetProxyConfig( 113 net::ProxyConfig* net_config) { 114 switch (mode) { 115 case MODE_DIRECT: 116 *net_config = net::ProxyConfig::CreateDirect(); 117 break; 118 case MODE_AUTO_DETECT: 119 *net_config = net::ProxyConfig::CreateAutoDetect(); 120 break; 121 case MODE_PAC_SCRIPT: 122 *net_config = net::ProxyConfig::CreateFromCustomPacURL( 123 automatic_proxy.pac_url); 124 break; 125 case MODE_SINGLE_PROXY: 126 *net_config = net::ProxyConfig(); 127 net_config->proxy_rules().type = 128 net::ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY; 129 net_config->proxy_rules().single_proxy = single_proxy.server; 130 net_config->proxy_rules().bypass_rules = bypass_rules; 131 break; 132 case MODE_PROXY_PER_SCHEME: 133 *net_config = net::ProxyConfig(); 134 net_config->proxy_rules().type = 135 net::ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME; 136 net_config->proxy_rules().proxy_for_http = http_proxy.server; 137 net_config->proxy_rules().proxy_for_https = https_proxy.server; 138 net_config->proxy_rules().proxy_for_ftp = ftp_proxy.server; 139 net_config->proxy_rules().fallback_proxy = socks_proxy.server; 140 net_config->proxy_rules().bypass_rules = bypass_rules; 141 break; 142 default: 143 NOTREACHED() << "Unrecognized proxy config mode"; 144 break; 145 } 146} 147 148bool ProxyConfigServiceImpl::ProxyConfig::CanBeWrittenByUser( 149 bool user_is_owner, const std::string& scheme) { 150 // Setting can only be written by user if user is owner and setting is not 151 // from policy. 152 Setting* setting = NULL; 153 switch (mode) { 154 case MODE_DIRECT: 155 case MODE_AUTO_DETECT: 156 case MODE_PAC_SCRIPT: 157 setting = &automatic_proxy; 158 break; 159 case MODE_SINGLE_PROXY: 160 setting = &single_proxy; 161 break; 162 case MODE_PROXY_PER_SCHEME: 163 setting = MapSchemeToProxy(scheme); 164 break; 165 default: 166 break; 167 } 168 if (!setting) { 169 NOTREACHED() << "Unrecognized proxy config mode"; 170 return false; 171 } 172 return setting->CanBeWrittenByUser(user_is_owner); 173} 174 175ProxyConfigServiceImpl::ProxyConfig::ManualProxy* 176 ProxyConfigServiceImpl::ProxyConfig::MapSchemeToProxy( 177 const std::string& scheme) { 178 if (scheme == "http") 179 return &http_proxy; 180 if (scheme == "https") 181 return &https_proxy; 182 if (scheme == "ftp") 183 return &ftp_proxy; 184 if (scheme == "socks") 185 return &socks_proxy; 186 NOTREACHED() << "Invalid scheme: " << scheme; 187 return NULL; 188} 189 190bool ProxyConfigServiceImpl::ProxyConfig::Serialize(std::string* output) { 191 em::DeviceProxySettingsProto proxy_proto; 192 switch (mode) { 193 case MODE_DIRECT: { 194 proxy_proto.set_proxy_mode(ProxyPrefs::kDirectProxyModeName); 195 break; 196 } 197 case MODE_AUTO_DETECT: { 198 proxy_proto.set_proxy_mode(ProxyPrefs::kAutoDetectProxyModeName); 199 break; 200 } 201 case MODE_PAC_SCRIPT: { 202 proxy_proto.set_proxy_mode(ProxyPrefs::kPacScriptProxyModeName); 203 if (!automatic_proxy.pac_url.is_empty()) 204 proxy_proto.set_proxy_pac_url(automatic_proxy.pac_url.spec()); 205 break; 206 } 207 case MODE_SINGLE_PROXY: { 208 proxy_proto.set_proxy_mode(ProxyPrefs::kFixedServersProxyModeName); 209 if (single_proxy.server.is_valid()) 210 proxy_proto.set_proxy_server(single_proxy.server.ToURI()); 211 break; 212 } 213 case MODE_PROXY_PER_SCHEME: { 214 proxy_proto.set_proxy_mode(ProxyPrefs::kFixedServersProxyModeName); 215 std::string spec; 216 EncodeAndAppendProxyServer("http", http_proxy.server, &spec); 217 EncodeAndAppendProxyServer("https", https_proxy.server, &spec); 218 EncodeAndAppendProxyServer("ftp", ftp_proxy.server, &spec); 219 EncodeAndAppendProxyServer("socks", socks_proxy.server, &spec); 220 if (!spec.empty()) 221 proxy_proto.set_proxy_server(spec); 222 break; 223 } 224 default: { 225 NOTREACHED() << "Unrecognized proxy config mode"; 226 break; 227 } 228 } 229 proxy_proto.set_proxy_bypass_list(bypass_rules.ToString()); 230 return proxy_proto.SerializeToString(output); 231} 232 233bool ProxyConfigServiceImpl::ProxyConfig::Deserialize( 234 const std::string& input) { 235 em::DeviceProxySettingsProto proxy_proto; 236 if (!proxy_proto.ParseFromString(input)) 237 return false; 238 239 const std::string& mode_string(proxy_proto.proxy_mode()); 240 if (mode_string == ProxyPrefs::kDirectProxyModeName) { 241 mode = MODE_DIRECT; 242 } else if (mode_string == ProxyPrefs::kAutoDetectProxyModeName) { 243 mode = MODE_AUTO_DETECT; 244 } else if (mode_string == ProxyPrefs::kPacScriptProxyModeName) { 245 mode = MODE_PAC_SCRIPT; 246 if (proxy_proto.has_proxy_pac_url()) 247 automatic_proxy.pac_url = GURL(proxy_proto.proxy_pac_url()); 248 } else if (mode_string == ProxyPrefs::kFixedServersProxyModeName) { 249 net::ProxyConfig::ProxyRules rules; 250 rules.ParseFromString(proxy_proto.proxy_server()); 251 switch (rules.type) { 252 case net::ProxyConfig::ProxyRules::TYPE_NO_RULES: 253 return false; 254 case net::ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY: 255 if (!rules.single_proxy.is_valid()) 256 return false; 257 mode = MODE_SINGLE_PROXY; 258 single_proxy.server = rules.single_proxy; 259 break; 260 case net::ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME: 261 // Make sure we have valid server for at least one of the protocols. 262 if (!rules.proxy_for_http.is_valid() && 263 !rules.proxy_for_https.is_valid() && 264 !rules.proxy_for_ftp.is_valid() && 265 !rules.fallback_proxy.is_valid()) { 266 return false; 267 } 268 mode = MODE_PROXY_PER_SCHEME; 269 if (rules.proxy_for_http.is_valid()) 270 http_proxy.server = rules.proxy_for_http; 271 if (rules.proxy_for_https.is_valid()) 272 https_proxy.server = rules.proxy_for_https; 273 if (rules.proxy_for_ftp.is_valid()) 274 ftp_proxy.server = rules.proxy_for_ftp; 275 if (rules.fallback_proxy.is_valid()) 276 socks_proxy.server = rules.fallback_proxy; 277 break; 278 } 279 } else { 280 NOTREACHED() << "Unrecognized proxy config mode"; 281 return false; 282 } 283 284 if (proxy_proto.has_proxy_bypass_list()) 285 bypass_rules.ParseFromString(proxy_proto.proxy_bypass_list()); 286 287 return true; 288} 289 290std::string ProxyConfigServiceImpl::ProxyConfig::ToString() const { 291 return ProxyConfigToString(*this); 292} 293 294//----------- ProxyConfigServiceImpl::ProxyConfig: private methods ------------- 295 296// static 297void ProxyConfigServiceImpl::ProxyConfig::EncodeAndAppendProxyServer( 298 const std::string& scheme, 299 const net::ProxyServer& server, 300 std::string* spec) { 301 if (!server.is_valid()) 302 return; 303 304 if (!spec->empty()) 305 *spec += ';'; 306 307 if (!scheme.empty()) { 308 *spec += scheme; 309 *spec += "="; 310 } 311 *spec += server.ToURI(); 312} 313 314//------------------- ProxyConfigServiceImpl: public methods ------------------- 315 316ProxyConfigServiceImpl::ProxyConfigServiceImpl() 317 : can_post_task_(false), 318 config_availability_(net::ProxyConfigService::CONFIG_PENDING), 319 persist_to_device_(true), 320 persist_to_device_pending_(false) { 321 // Start async fetch of proxy config from settings persisted on device. 322 // TODO(kuan): retrieve config from policy and owner and merge them 323 bool use_default = true; 324 if (CrosLibrary::Get()->EnsureLoaded()) { 325 retrieve_property_op_ = SignedSettings::CreateRetrievePropertyOp( 326 kSettingProxyEverywhere, this); 327 if (retrieve_property_op_) { 328 retrieve_property_op_->Execute(); 329 VLOG(1) << "Start retrieving proxy setting from device"; 330 use_default = false; 331 } else { 332 VLOG(1) << "Fail to retrieve proxy setting from device"; 333 } 334 } 335 if (use_default) 336 config_availability_ = net::ProxyConfigService::CONFIG_UNSET; 337 can_post_task_ = true; 338} 339 340ProxyConfigServiceImpl::ProxyConfigServiceImpl(const ProxyConfig& init_config) 341 : can_post_task_(true), 342 config_availability_(net::ProxyConfigService::CONFIG_VALID), 343 persist_to_device_(false), 344 persist_to_device_pending_(false) { 345 reference_config_ = init_config; 346 // Update the IO-accessible copy in |cached_config_| as well. 347 cached_config_ = reference_config_; 348} 349 350ProxyConfigServiceImpl::~ProxyConfigServiceImpl() { 351} 352 353void ProxyConfigServiceImpl::UIGetProxyConfig(ProxyConfig* config) { 354 // Should be called from UI thread. 355 CheckCurrentlyOnUIThread(); 356 // Simply returns the copy on the UI thread. 357 *config = reference_config_; 358} 359 360bool ProxyConfigServiceImpl::UISetProxyConfigToDirect() { 361 // Should be called from UI thread. 362 CheckCurrentlyOnUIThread(); 363 reference_config_.mode = ProxyConfig::MODE_DIRECT; 364 OnUISetProxyConfig(persist_to_device_); 365 return true; 366} 367 368bool ProxyConfigServiceImpl::UISetProxyConfigToAutoDetect() { 369 // Should be called from UI thread. 370 CheckCurrentlyOnUIThread(); 371 reference_config_.mode = ProxyConfig::MODE_AUTO_DETECT; 372 OnUISetProxyConfig(persist_to_device_); 373 return true; 374} 375 376bool ProxyConfigServiceImpl::UISetProxyConfigToPACScript(const GURL& pac_url) { 377 // Should be called from UI thread. 378 CheckCurrentlyOnUIThread(); 379 reference_config_.mode = ProxyConfig::MODE_PAC_SCRIPT; 380 reference_config_.automatic_proxy.pac_url = pac_url; 381 OnUISetProxyConfig(persist_to_device_); 382 return true; 383} 384 385bool ProxyConfigServiceImpl::UISetProxyConfigToSingleProxy( 386 const net::ProxyServer& server) { 387 // Should be called from UI thread. 388 CheckCurrentlyOnUIThread(); 389 reference_config_.mode = ProxyConfig::MODE_SINGLE_PROXY; 390 reference_config_.single_proxy.server = server; 391 OnUISetProxyConfig(persist_to_device_); 392 return true; 393} 394 395bool ProxyConfigServiceImpl::UISetProxyConfigToProxyPerScheme( 396 const std::string& scheme, const net::ProxyServer& server) { 397 // Should be called from UI thread. 398 CheckCurrentlyOnUIThread(); 399 ProxyConfig::ManualProxy* proxy = reference_config_.MapSchemeToProxy(scheme); 400 if (!proxy) { 401 NOTREACHED() << "Cannot set proxy: invalid scheme [" << scheme << "]"; 402 return false; 403 } 404 reference_config_.mode = ProxyConfig::MODE_PROXY_PER_SCHEME; 405 proxy->server = server; 406 OnUISetProxyConfig(persist_to_device_); 407 return true; 408} 409 410bool ProxyConfigServiceImpl::UISetProxyConfigBypassRules( 411 const net::ProxyBypassRules& bypass_rules) { 412 // Should be called from UI thread. 413 CheckCurrentlyOnUIThread(); 414 DCHECK(reference_config_.mode == ProxyConfig::MODE_SINGLE_PROXY || 415 reference_config_.mode == ProxyConfig::MODE_PROXY_PER_SCHEME); 416 if (reference_config_.mode != ProxyConfig::MODE_SINGLE_PROXY && 417 reference_config_.mode != ProxyConfig::MODE_PROXY_PER_SCHEME) { 418 VLOG(1) << "Cannot set bypass rules for proxy mode [" 419 << reference_config_.mode << "]"; 420 return false; 421 } 422 reference_config_.bypass_rules = bypass_rules; 423 OnUISetProxyConfig(persist_to_device_); 424 return true; 425} 426 427void ProxyConfigServiceImpl::AddObserver( 428 net::ProxyConfigService::Observer* observer) { 429 // Should be called from IO thread. 430 CheckCurrentlyOnIOThread(); 431 observers_.AddObserver(observer); 432} 433 434void ProxyConfigServiceImpl::RemoveObserver( 435 net::ProxyConfigService::Observer* observer) { 436 // Should be called from IO thread. 437 CheckCurrentlyOnIOThread(); 438 observers_.RemoveObserver(observer); 439} 440 441net::ProxyConfigService::ConfigAvailability 442 ProxyConfigServiceImpl::IOGetProxyConfig(net::ProxyConfig* net_config) { 443 // Should be called from IO thread. 444 CheckCurrentlyOnIOThread(); 445 if (config_availability_ == net::ProxyConfigService::CONFIG_VALID) 446 cached_config_.ToNetProxyConfig(net_config); 447 448 return config_availability_; 449} 450 451void ProxyConfigServiceImpl::OnSettingsOpCompleted( 452 SignedSettings::ReturnCode code, 453 bool value) { 454 if (SignedSettings::SUCCESS == code) 455 VLOG(1) << "Stored proxy setting to device"; 456 else 457 LOG(WARNING) << "Error storing proxy setting to device"; 458 store_property_op_ = NULL; 459 if (persist_to_device_pending_) 460 PersistConfigToDevice(); 461} 462 463void ProxyConfigServiceImpl::OnSettingsOpCompleted( 464 SignedSettings::ReturnCode code, 465 std::string value) { 466 retrieve_property_op_ = NULL; 467 if (SignedSettings::SUCCESS == code) { 468 VLOG(1) << "Retrieved proxy setting from device, value=[" << value << "]"; 469 if (reference_config_.Deserialize(value)) { 470 IOSetProxyConfig(reference_config_, 471 net::ProxyConfigService::CONFIG_VALID); 472 return; 473 } else { 474 LOG(WARNING) << "Error deserializing device's proxy setting"; 475 } 476 } else { 477 LOG(WARNING) << "Error retrieving proxy setting from device"; 478 } 479 480 // Update the configuration state on the IO thread. 481 IOSetProxyConfig(reference_config_, net::ProxyConfigService::CONFIG_UNSET); 482} 483 484//------------------ ProxyConfigServiceImpl: private methods ------------------- 485 486void ProxyConfigServiceImpl::PersistConfigToDevice() { 487 DCHECK(!store_property_op_); 488 persist_to_device_pending_ = false; 489 std::string value; 490 if (!reference_config_.Serialize(&value)) { 491 LOG(WARNING) << "Error serializing proxy config"; 492 return; 493 } 494 store_property_op_ = SignedSettings::CreateStorePropertyOp( 495 kSettingProxyEverywhere, value, this); 496 store_property_op_->Execute(); 497 VLOG(1) << "Start storing proxy setting to device, value=" << value; 498} 499 500void ProxyConfigServiceImpl::OnUISetProxyConfig(bool persist_to_device) { 501 IOSetProxyConfig(reference_config_, net::ProxyConfigService::CONFIG_VALID); 502 if (persist_to_device && CrosLibrary::Get()->EnsureLoaded()) { 503 if (store_property_op_) { 504 persist_to_device_pending_ = true; 505 VLOG(1) << "Pending persisting proxy setting to device"; 506 } else { 507 PersistConfigToDevice(); 508 } 509 } 510} 511 512void ProxyConfigServiceImpl::IOSetProxyConfig( 513 const ProxyConfig& new_config, 514 net::ProxyConfigService::ConfigAvailability new_availability) { 515 if (!BrowserThread::CurrentlyOn(BrowserThread::IO) && can_post_task_) { 516 // Posts a task to IO thread with the new config, so it can update 517 // |cached_config_|. 518 Task* task = NewRunnableMethod(this, 519 &ProxyConfigServiceImpl::IOSetProxyConfig, 520 new_config, 521 new_availability); 522 if (!BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, task)) 523 VLOG(1) << "Couldn't post task to IO thread to set new proxy config"; 524 return; 525 } 526 527 // Now guaranteed to be on the correct thread. 528 VLOG(1) << "Proxy configuration changed"; 529 cached_config_ = new_config; 530 config_availability_ = new_availability; 531 // Notify observers of new proxy config. 532 net::ProxyConfig net_config; 533 cached_config_.ToNetProxyConfig(&net_config); 534 FOR_EACH_OBSERVER(net::ProxyConfigService::Observer, observers_, 535 OnProxyConfigChanged(net_config, config_availability_)); 536} 537 538void ProxyConfigServiceImpl::CheckCurrentlyOnIOThread() { 539 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 540} 541 542void ProxyConfigServiceImpl::CheckCurrentlyOnUIThread() { 543 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 544} 545 546} // namespace chromeos 547