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 "chrome/browser/extensions/updater/extension_downloader.h" 6 7 #include <utility> 8 9 #include "base/bind.h" 10 #include "base/command_line.h" 11 #include "base/files/file_path.h" 12 #include "base/location.h" 13 #include "base/logging.h" 14 #include "base/metrics/histogram.h" 15 #include "base/metrics/sparse_histogram.h" 16 #include "base/stl_util.h" 17 #include "base/strings/string_number_conversions.h" 18 #include "base/strings/string_util.h" 19 #include "base/strings/stringprintf.h" 20 #include "base/time/time.h" 21 #include "base/version.h" 22 #include "chrome/browser/chrome_notification_types.h" 23 #include "chrome/browser/extensions/updater/extension_cache.h" 24 #include "chrome/browser/extensions/updater/request_queue_impl.h" 25 #include "chrome/browser/extensions/updater/safe_manifest_parser.h" 26 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h" 27 #include "chrome/common/chrome_switches.h" 28 #include "chrome/common/chrome_version_info.h" 29 #include "chrome/common/extensions/extension_constants.h" 30 #include "chrome/common/extensions/manifest_url_handler.h" 31 #include "content/public/browser/browser_thread.h" 32 #include "content/public/browser/notification_details.h" 33 #include "content/public/browser/notification_service.h" 34 #include "net/base/backoff_entry.h" 35 #include "net/base/load_flags.h" 36 #include "net/base/net_errors.h" 37 #include "net/url_request/url_fetcher.h" 38 #include "net/url_request/url_request_context_getter.h" 39 #include "net/url_request/url_request_status.h" 40 41 using base::Time; 42 using base::TimeDelta; 43 using content::BrowserThread; 44 45 namespace extensions { 46 47 const char ExtensionDownloader::kBlacklistAppID[] = "com.google.crx.blacklist"; 48 49 namespace { 50 51 const net::BackoffEntry::Policy kDefaultBackoffPolicy = { 52 // Number of initial errors (in sequence) to ignore before applying 53 // exponential back-off rules. 54 0, 55 56 // Initial delay for exponential back-off in ms. 57 2000, 58 59 // Factor by which the waiting time will be multiplied. 60 2, 61 62 // Fuzzing percentage. ex: 10% will spread requests randomly 63 // between 90%-100% of the calculated time. 64 0.1, 65 66 // Maximum amount of time we are willing to delay our request in ms. 67 -1, 68 69 // Time to keep an entry from being discarded even when it 70 // has no significant state, -1 to never discard. 71 -1, 72 73 // Don't use initial delay unless the last request was an error. 74 false, 75 }; 76 77 const char kAuthUserQueryKey[] = "authuser"; 78 79 const int kMaxAuthUserValue = 10; 80 81 const char kNotFromWebstoreInstallSource[] = "notfromwebstore"; 82 const char kDefaultInstallSource[] = ""; 83 84 #define RETRY_HISTOGRAM(name, retry_count, url) \ 85 if ((url).DomainIs("google.com")) { \ 86 UMA_HISTOGRAM_CUSTOM_COUNTS( \ 87 "Extensions." name "RetryCountGoogleUrl", retry_count, 1, \ 88 kMaxRetries, kMaxRetries+1); \ 89 } else { \ 90 UMA_HISTOGRAM_CUSTOM_COUNTS( \ 91 "Extensions." name "RetryCountOtherUrl", retry_count, 1, \ 92 kMaxRetries, kMaxRetries+1); \ 93 } 94 95 bool ShouldRetryRequest(const net::URLRequestStatus& status, 96 int response_code) { 97 // Retry if the response code is a server error, or the request failed because 98 // of network errors as opposed to file errors. 99 return ((response_code >= 500 && status.is_success()) || 100 status.status() == net::URLRequestStatus::FAILED); 101 } 102 103 bool ShouldRetryRequestWithCookies(const net::URLRequestStatus& status, 104 int response_code, 105 bool included_cookies) { 106 if (included_cookies) 107 return false; 108 109 if (status.status() == net::URLRequestStatus::CANCELED) 110 return true; 111 112 // Retry if a 401 or 403 is received. 113 return (status.status() == net::URLRequestStatus::SUCCESS && 114 (response_code == 401 || response_code == 403)); 115 } 116 117 bool ShouldRetryRequestWithNextUser(const net::URLRequestStatus& status, 118 int response_code, 119 bool included_cookies) { 120 // Retry if a 403 is received in response to a request including cookies. 121 // Note that receiving a 401 in response to a request which included cookies 122 // should indicate that the |authuser| index was out of bounds for the profile 123 // and therefore Chrome should NOT retry with another index. 124 return (status.status() == net::URLRequestStatus::SUCCESS && 125 response_code == 403 && included_cookies); 126 } 127 128 // This parses and updates a URL query such that the value of the |authuser| 129 // query parameter is incremented by 1. If parameter was not present in the URL, 130 // it will be added with a value of 1. All other query keys and values are 131 // preserved as-is. Returns |false| if the user index exceeds a hard-coded 132 // maximum. 133 bool IncrementAuthUserIndex(GURL* url) { 134 int user_index = 0; 135 std::string old_query = url->query(); 136 std::vector<std::string> new_query_parts; 137 url::Component query(0, old_query.length()); 138 url::Component key, value; 139 while (url::ExtractQueryKeyValue(old_query.c_str(), &query, &key, &value)) { 140 std::string key_string = old_query.substr(key.begin, key.len); 141 std::string value_string = old_query.substr(value.begin, value.len); 142 if (key_string == kAuthUserQueryKey) { 143 base::StringToInt(value_string, &user_index); 144 } else { 145 new_query_parts.push_back(base::StringPrintf( 146 "%s=%s", key_string.c_str(), value_string.c_str())); 147 } 148 } 149 if (user_index >= kMaxAuthUserValue) 150 return false; 151 new_query_parts.push_back( 152 base::StringPrintf("%s=%d", kAuthUserQueryKey, user_index + 1)); 153 std::string new_query_string = JoinString(new_query_parts, '&'); 154 url::Component new_query(0, new_query_string.size()); 155 url::Replacements<char> replacements; 156 replacements.SetQuery(new_query_string.c_str(), new_query); 157 *url = url->ReplaceComponents(replacements); 158 return true; 159 } 160 161 } // namespace 162 163 UpdateDetails::UpdateDetails(const std::string& id, const Version& version) 164 : id(id), version(version) {} 165 166 UpdateDetails::~UpdateDetails() {} 167 168 ExtensionDownloader::ExtensionFetch::ExtensionFetch() 169 : url(), is_protected(false) {} 170 171 ExtensionDownloader::ExtensionFetch::ExtensionFetch( 172 const std::string& id, 173 const GURL& url, 174 const std::string& package_hash, 175 const std::string& version, 176 const std::set<int>& request_ids) 177 : id(id), url(url), package_hash(package_hash), version(version), 178 request_ids(request_ids), is_protected(false) {} 179 180 ExtensionDownloader::ExtensionFetch::~ExtensionFetch() {} 181 182 ExtensionDownloader::ExtensionDownloader( 183 ExtensionDownloaderDelegate* delegate, 184 net::URLRequestContextGetter* request_context) 185 : delegate_(delegate), 186 request_context_(request_context), 187 weak_ptr_factory_(this), 188 manifests_queue_(&kDefaultBackoffPolicy, 189 base::Bind(&ExtensionDownloader::CreateManifestFetcher, 190 base::Unretained(this))), 191 extensions_queue_(&kDefaultBackoffPolicy, 192 base::Bind(&ExtensionDownloader::CreateExtensionFetcher, 193 base::Unretained(this))), 194 extension_cache_(NULL) { 195 DCHECK(delegate_); 196 DCHECK(request_context_); 197 } 198 199 ExtensionDownloader::~ExtensionDownloader() {} 200 201 bool ExtensionDownloader::AddExtension(const Extension& extension, 202 int request_id) { 203 // Skip extensions with empty update URLs converted from user 204 // scripts. 205 if (extension.converted_from_user_script() && 206 ManifestURL::GetUpdateURL(&extension).is_empty()) { 207 return false; 208 } 209 210 // If the extension updates itself from the gallery, ignore any update URL 211 // data. At the moment there is no extra data that an extension can 212 // communicate to the the gallery update servers. 213 std::string update_url_data; 214 if (!ManifestURL::UpdatesFromGallery(&extension)) 215 update_url_data = delegate_->GetUpdateUrlData(extension.id()); 216 217 return AddExtensionData(extension.id(), *extension.version(), 218 extension.GetType(), 219 ManifestURL::GetUpdateURL(&extension), 220 update_url_data, request_id); 221 } 222 223 bool ExtensionDownloader::AddPendingExtension(const std::string& id, 224 const GURL& update_url, 225 int request_id) { 226 // Use a zero version to ensure that a pending extension will always 227 // be updated, and thus installed (assuming all extensions have 228 // non-zero versions). 229 Version version("0.0.0.0"); 230 DCHECK(version.IsValid()); 231 232 return AddExtensionData(id, 233 version, 234 Manifest::TYPE_UNKNOWN, 235 update_url, 236 std::string(), 237 request_id); 238 } 239 240 void ExtensionDownloader::StartAllPending(ExtensionCache* cache) { 241 if (cache) { 242 extension_cache_ = cache; 243 extension_cache_->Start(base::Bind( 244 &ExtensionDownloader::DoStartAllPending, 245 weak_ptr_factory_.GetWeakPtr())); 246 } else { 247 DoStartAllPending(); 248 } 249 } 250 251 void ExtensionDownloader::DoStartAllPending() { 252 ReportStats(); 253 url_stats_ = URLStats(); 254 255 for (FetchMap::iterator it = fetches_preparing_.begin(); 256 it != fetches_preparing_.end(); ++it) { 257 std::vector<linked_ptr<ManifestFetchData> >& list = it->second; 258 for (size_t i = 0; i < list.size(); ++i) { 259 StartUpdateCheck(scoped_ptr<ManifestFetchData>(list[i].release())); 260 } 261 } 262 fetches_preparing_.clear(); 263 } 264 265 void ExtensionDownloader::StartBlacklistUpdate( 266 const std::string& version, 267 const ManifestFetchData::PingData& ping_data, 268 int request_id) { 269 // Note: it is very important that we use the https version of the update 270 // url here to avoid DNS hijacking of the blacklist, which is not validated 271 // by a public key signature like .crx files are. 272 scoped_ptr<ManifestFetchData> blacklist_fetch( 273 new ManifestFetchData(extension_urls::GetWebstoreUpdateUrl(), 274 request_id)); 275 DCHECK(blacklist_fetch->base_url().SchemeIsSecure()); 276 blacklist_fetch->AddExtension(kBlacklistAppID, 277 version, 278 &ping_data, 279 std::string(), 280 kDefaultInstallSource); 281 StartUpdateCheck(blacklist_fetch.Pass()); 282 } 283 284 bool ExtensionDownloader::AddExtensionData(const std::string& id, 285 const Version& version, 286 Manifest::Type extension_type, 287 const GURL& extension_update_url, 288 const std::string& update_url_data, 289 int request_id) { 290 GURL update_url(extension_update_url); 291 // Skip extensions with non-empty invalid update URLs. 292 if (!update_url.is_empty() && !update_url.is_valid()) { 293 LOG(WARNING) << "Extension " << id << " has invalid update url " 294 << update_url; 295 return false; 296 } 297 298 // Make sure we use SSL for store-hosted extensions. 299 if (extension_urls::IsWebstoreUpdateUrl(update_url) && 300 !update_url.SchemeIsSecure()) 301 update_url = extension_urls::GetWebstoreUpdateUrl(); 302 303 // Skip extensions with empty IDs. 304 if (id.empty()) { 305 LOG(WARNING) << "Found extension with empty ID"; 306 return false; 307 } 308 309 if (update_url.DomainIs("google.com")) { 310 url_stats_.google_url_count++; 311 } else if (update_url.is_empty()) { 312 url_stats_.no_url_count++; 313 // Fill in default update URL. 314 update_url = extension_urls::GetWebstoreUpdateUrl(); 315 } else { 316 url_stats_.other_url_count++; 317 } 318 319 switch (extension_type) { 320 case Manifest::TYPE_THEME: 321 ++url_stats_.theme_count; 322 break; 323 case Manifest::TYPE_EXTENSION: 324 case Manifest::TYPE_USER_SCRIPT: 325 ++url_stats_.extension_count; 326 break; 327 case Manifest::TYPE_HOSTED_APP: 328 case Manifest::TYPE_LEGACY_PACKAGED_APP: 329 ++url_stats_.app_count; 330 break; 331 case Manifest::TYPE_PLATFORM_APP: 332 ++url_stats_.platform_app_count; 333 break; 334 case Manifest::TYPE_UNKNOWN: 335 default: 336 ++url_stats_.pending_count; 337 break; 338 } 339 340 std::vector<GURL> update_urls; 341 update_urls.push_back(update_url); 342 // If UMA is enabled, also add to ManifestFetchData for the 343 // webstore update URL. 344 if (!extension_urls::IsWebstoreUpdateUrl(update_url) && 345 ChromeMetricsServiceAccessor::IsMetricsReportingEnabled()) { 346 update_urls.push_back(extension_urls::GetWebstoreUpdateUrl()); 347 } 348 349 for (size_t i = 0; i < update_urls.size(); ++i) { 350 DCHECK(!update_urls[i].is_empty()); 351 DCHECK(update_urls[i].is_valid()); 352 353 std::string install_source = i == 0 ? 354 kDefaultInstallSource : kNotFromWebstoreInstallSource; 355 356 ManifestFetchData::PingData ping_data; 357 ManifestFetchData::PingData* optional_ping_data = NULL; 358 if (delegate_->GetPingDataForExtension(id, &ping_data)) 359 optional_ping_data = &ping_data; 360 361 // Find or create a ManifestFetchData to add this extension to. 362 bool added = false; 363 FetchMap::iterator existing_iter = fetches_preparing_.find( 364 std::make_pair(request_id, update_urls[i])); 365 if (existing_iter != fetches_preparing_.end() && 366 !existing_iter->second.empty()) { 367 // Try to add to the ManifestFetchData at the end of the list. 368 ManifestFetchData* existing_fetch = existing_iter->second.back().get(); 369 if (existing_fetch->AddExtension(id, version.GetString(), 370 optional_ping_data, update_url_data, 371 install_source)) { 372 added = true; 373 } 374 } 375 if (!added) { 376 // Otherwise add a new element to the list, if the list doesn't exist or 377 // if its last element is already full. 378 linked_ptr<ManifestFetchData> fetch( 379 new ManifestFetchData(update_urls[i], request_id)); 380 fetches_preparing_[std::make_pair(request_id, update_urls[i])]. 381 push_back(fetch); 382 added = fetch->AddExtension(id, version.GetString(), 383 optional_ping_data, 384 update_url_data, 385 install_source); 386 DCHECK(added); 387 } 388 } 389 390 return true; 391 } 392 393 void ExtensionDownloader::ReportStats() const { 394 UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckExtension", 395 url_stats_.extension_count); 396 UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckTheme", 397 url_stats_.theme_count); 398 UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckApp", 399 url_stats_.app_count); 400 UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckPackagedApp", 401 url_stats_.platform_app_count); 402 UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckPending", 403 url_stats_.pending_count); 404 UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckGoogleUrl", 405 url_stats_.google_url_count); 406 UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckOtherUrl", 407 url_stats_.other_url_count); 408 UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckNoUrl", 409 url_stats_.no_url_count); 410 } 411 412 void ExtensionDownloader::StartUpdateCheck( 413 scoped_ptr<ManifestFetchData> fetch_data) { 414 const std::set<std::string>& id_set(fetch_data->extension_ids()); 415 416 if (CommandLine::ForCurrentProcess()->HasSwitch( 417 switches::kDisableBackgroundNetworking)) { 418 NotifyExtensionsDownloadFailed(id_set, 419 fetch_data->request_ids(), 420 ExtensionDownloaderDelegate::DISABLED); 421 return; 422 } 423 424 RequestQueue<ManifestFetchData>::iterator i; 425 for (i = manifests_queue_.begin(); i != manifests_queue_.end(); ++i) { 426 if (fetch_data->full_url() == i->full_url()) { 427 // This url is already scheduled to be fetched. 428 i->Merge(*fetch_data); 429 return; 430 } 431 } 432 433 if (manifests_queue_.active_request() && 434 manifests_queue_.active_request()->full_url() == fetch_data->full_url()) { 435 manifests_queue_.active_request()->Merge(*fetch_data); 436 } else { 437 UMA_HISTOGRAM_COUNTS("Extensions.UpdateCheckUrlLength", 438 fetch_data->full_url().possibly_invalid_spec().length()); 439 440 manifests_queue_.ScheduleRequest(fetch_data.Pass()); 441 } 442 } 443 444 void ExtensionDownloader::CreateManifestFetcher() { 445 if (VLOG_IS_ON(2)) { 446 std::vector<std::string> id_vector( 447 manifests_queue_.active_request()->extension_ids().begin(), 448 manifests_queue_.active_request()->extension_ids().end()); 449 std::string id_list = JoinString(id_vector, ','); 450 VLOG(2) << "Fetching " << manifests_queue_.active_request()->full_url() 451 << " for " << id_list; 452 } 453 454 manifest_fetcher_.reset(net::URLFetcher::Create( 455 kManifestFetcherId, manifests_queue_.active_request()->full_url(), 456 net::URLFetcher::GET, this)); 457 manifest_fetcher_->SetRequestContext(request_context_); 458 manifest_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | 459 net::LOAD_DO_NOT_SAVE_COOKIES | 460 net::LOAD_DISABLE_CACHE); 461 // Update checks can be interrupted if a network change is detected; this is 462 // common for the retail mode AppPack on ChromeOS. Retrying once should be 463 // enough to recover in those cases; let the fetcher retry up to 3 times 464 // just in case. http://crosbug.com/130602 465 manifest_fetcher_->SetAutomaticallyRetryOnNetworkChanges(3); 466 manifest_fetcher_->Start(); 467 } 468 469 void ExtensionDownloader::OnURLFetchComplete( 470 const net::URLFetcher* source) { 471 VLOG(2) << source->GetResponseCode() << " " << source->GetURL(); 472 473 if (source == manifest_fetcher_.get()) { 474 std::string data; 475 source->GetResponseAsString(&data); 476 OnManifestFetchComplete(source->GetURL(), 477 source->GetStatus(), 478 source->GetResponseCode(), 479 source->GetBackoffDelay(), 480 data); 481 } else if (source == extension_fetcher_.get()) { 482 OnCRXFetchComplete(source, 483 source->GetURL(), 484 source->GetStatus(), 485 source->GetResponseCode(), 486 source->GetBackoffDelay()); 487 } else { 488 NOTREACHED(); 489 } 490 } 491 492 void ExtensionDownloader::OnManifestFetchComplete( 493 const GURL& url, 494 const net::URLRequestStatus& status, 495 int response_code, 496 const base::TimeDelta& backoff_delay, 497 const std::string& data) { 498 // We want to try parsing the manifest, and if it indicates updates are 499 // available, we want to fire off requests to fetch those updates. 500 if (status.status() == net::URLRequestStatus::SUCCESS && 501 (response_code == 200 || (url.SchemeIsFile() && data.length() > 0))) { 502 RETRY_HISTOGRAM("ManifestFetchSuccess", 503 manifests_queue_.active_request_failure_count(), url); 504 VLOG(2) << "beginning manifest parse for " << url; 505 scoped_refptr<SafeManifestParser> safe_parser( 506 new SafeManifestParser( 507 data, 508 manifests_queue_.reset_active_request().release(), 509 base::Bind(&ExtensionDownloader::HandleManifestResults, 510 weak_ptr_factory_.GetWeakPtr()))); 511 safe_parser->Start(); 512 } else { 513 VLOG(1) << "Failed to fetch manifest '" << url.possibly_invalid_spec() 514 << "' response code:" << response_code; 515 if (ShouldRetryRequest(status, response_code) && 516 manifests_queue_.active_request_failure_count() < kMaxRetries) { 517 manifests_queue_.RetryRequest(backoff_delay); 518 } else { 519 RETRY_HISTOGRAM("ManifestFetchFailure", 520 manifests_queue_.active_request_failure_count(), url); 521 NotifyExtensionsDownloadFailed( 522 manifests_queue_.active_request()->extension_ids(), 523 manifests_queue_.active_request()->request_ids(), 524 ExtensionDownloaderDelegate::MANIFEST_FETCH_FAILED); 525 } 526 } 527 manifest_fetcher_.reset(); 528 manifests_queue_.reset_active_request(); 529 530 // If we have any pending manifest requests, fire off the next one. 531 manifests_queue_.StartNextRequest(); 532 } 533 534 void ExtensionDownloader::HandleManifestResults( 535 const ManifestFetchData& fetch_data, 536 const UpdateManifest::Results* results) { 537 // Keep a list of extensions that will not be updated, so that the |delegate_| 538 // can be notified once we're done here. 539 std::set<std::string> not_updated(fetch_data.extension_ids()); 540 541 if (!results) { 542 NotifyExtensionsDownloadFailed( 543 not_updated, 544 fetch_data.request_ids(), 545 ExtensionDownloaderDelegate::MANIFEST_INVALID); 546 return; 547 } 548 549 // Examine the parsed manifest and kick off fetches of any new crx files. 550 std::vector<int> updates; 551 DetermineUpdates(fetch_data, *results, &updates); 552 for (size_t i = 0; i < updates.size(); i++) { 553 const UpdateManifest::Result* update = &(results->list.at(updates[i])); 554 const std::string& id = update->extension_id; 555 not_updated.erase(id); 556 557 GURL crx_url = update->crx_url; 558 if (id != kBlacklistAppID) { 559 NotifyUpdateFound(update->extension_id, update->version); 560 } else { 561 // The URL of the blacklist file is returned by the server and we need to 562 // be sure that we continue to be able to reliably detect whether a URL 563 // references a blacklist file. 564 DCHECK(extension_urls::IsBlacklistUpdateUrl(crx_url)) << crx_url; 565 566 // Force https (crbug.com/129587). 567 if (!crx_url.SchemeIsSecure()) { 568 url::Replacements<char> replacements; 569 std::string scheme("https"); 570 replacements.SetScheme(scheme.c_str(), 571 url::Component(0, scheme.size())); 572 crx_url = crx_url.ReplaceComponents(replacements); 573 } 574 } 575 scoped_ptr<ExtensionFetch> fetch(new ExtensionFetch( 576 update->extension_id, crx_url, update->package_hash, 577 update->version, fetch_data.request_ids())); 578 FetchUpdatedExtension(fetch.Pass()); 579 } 580 581 // If the manifest response included a <daystart> element, we want to save 582 // that value for any extensions which had sent a ping in the request. 583 if (fetch_data.base_url().DomainIs("google.com") && 584 results->daystart_elapsed_seconds >= 0) { 585 Time day_start = 586 Time::Now() - TimeDelta::FromSeconds(results->daystart_elapsed_seconds); 587 588 const std::set<std::string>& extension_ids = fetch_data.extension_ids(); 589 std::set<std::string>::const_iterator i; 590 for (i = extension_ids.begin(); i != extension_ids.end(); i++) { 591 const std::string& id = *i; 592 ExtensionDownloaderDelegate::PingResult& result = ping_results_[id]; 593 result.did_ping = fetch_data.DidPing(id, ManifestFetchData::ROLLCALL); 594 result.day_start = day_start; 595 } 596 } 597 598 NotifyExtensionsDownloadFailed( 599 not_updated, 600 fetch_data.request_ids(), 601 ExtensionDownloaderDelegate::NO_UPDATE_AVAILABLE); 602 } 603 604 void ExtensionDownloader::DetermineUpdates( 605 const ManifestFetchData& fetch_data, 606 const UpdateManifest::Results& possible_updates, 607 std::vector<int>* result) { 608 // This will only be valid if one of possible_updates specifies 609 // browser_min_version. 610 Version browser_version; 611 612 for (size_t i = 0; i < possible_updates.list.size(); i++) { 613 const UpdateManifest::Result* update = &possible_updates.list[i]; 614 const std::string& id = update->extension_id; 615 616 if (!fetch_data.Includes(id)) { 617 VLOG(2) << "Ignoring " << id << " from this manifest"; 618 continue; 619 } 620 621 if (VLOG_IS_ON(2)) { 622 if (update->version.empty()) 623 VLOG(2) << "manifest indicates " << id << " has no update"; 624 else 625 VLOG(2) << "manifest indicates " << id 626 << " latest version is '" << update->version << "'"; 627 } 628 629 if (!delegate_->IsExtensionPending(id)) { 630 // If we're not installing pending extension, and the update 631 // version is the same or older than what's already installed, 632 // we don't want it. 633 std::string version; 634 if (!delegate_->GetExtensionExistingVersion(id, &version)) { 635 VLOG(2) << id << " is not installed"; 636 continue; 637 } 638 639 VLOG(2) << id << " is at '" << version << "'"; 640 641 Version existing_version(version); 642 Version update_version(update->version); 643 644 if (!update_version.IsValid() || 645 update_version.CompareTo(existing_version) <= 0) { 646 continue; 647 } 648 } 649 650 // If the update specifies a browser minimum version, do we qualify? 651 if (update->browser_min_version.length() > 0) { 652 // First determine the browser version if we haven't already. 653 if (!browser_version.IsValid()) { 654 chrome::VersionInfo version_info; 655 if (version_info.is_valid()) 656 browser_version = Version(version_info.Version()); 657 } 658 Version browser_min_version(update->browser_min_version); 659 if (browser_version.IsValid() && browser_min_version.IsValid() && 660 browser_min_version.CompareTo(browser_version) > 0) { 661 // TODO(asargent) - We may want this to show up in the extensions UI 662 // eventually. (http://crbug.com/12547). 663 LOG(WARNING) << "Updated version of extension " << id 664 << " available, but requires chrome version " 665 << update->browser_min_version; 666 continue; 667 } 668 } 669 VLOG(2) << "will try to update " << id; 670 result->push_back(i); 671 } 672 } 673 674 // Begins (or queues up) download of an updated extension. 675 void ExtensionDownloader::FetchUpdatedExtension( 676 scoped_ptr<ExtensionFetch> fetch_data) { 677 if (!fetch_data->url.is_valid()) { 678 // TODO(asargent): This can sometimes be invalid. See crbug.com/130881. 679 LOG(ERROR) << "Invalid URL: '" << fetch_data->url.possibly_invalid_spec() 680 << "' for extension " << fetch_data->id; 681 return; 682 } 683 684 for (RequestQueue<ExtensionFetch>::iterator iter = 685 extensions_queue_.begin(); 686 iter != extensions_queue_.end(); ++iter) { 687 if (iter->id == fetch_data->id || iter->url == fetch_data->url) { 688 iter->request_ids.insert(fetch_data->request_ids.begin(), 689 fetch_data->request_ids.end()); 690 return; // already scheduled 691 } 692 } 693 694 if (extensions_queue_.active_request() && 695 extensions_queue_.active_request()->url == fetch_data->url) { 696 extensions_queue_.active_request()->request_ids.insert( 697 fetch_data->request_ids.begin(), fetch_data->request_ids.end()); 698 } else { 699 std::string version; 700 if (extension_cache_ && 701 extension_cache_->GetExtension(fetch_data->id, NULL, &version) && 702 version == fetch_data->version) { 703 base::FilePath crx_path; 704 // Now get .crx file path and mark extension as used. 705 extension_cache_->GetExtension(fetch_data->id, &crx_path, &version); 706 NotifyDelegateDownloadFinished(fetch_data.Pass(), crx_path, false); 707 } else { 708 extensions_queue_.ScheduleRequest(fetch_data.Pass()); 709 } 710 } 711 } 712 713 void ExtensionDownloader::NotifyDelegateDownloadFinished( 714 scoped_ptr<ExtensionFetch> fetch_data, 715 const base::FilePath& crx_path, 716 bool file_ownership_passed) { 717 delegate_->OnExtensionDownloadFinished(fetch_data->id, crx_path, 718 file_ownership_passed, fetch_data->url, fetch_data->version, 719 ping_results_[fetch_data->id], fetch_data->request_ids); 720 ping_results_.erase(fetch_data->id); 721 } 722 723 void ExtensionDownloader::CreateExtensionFetcher() { 724 const ExtensionFetch* fetch = extensions_queue_.active_request(); 725 int load_flags = net::LOAD_DISABLE_CACHE; 726 if (!fetch->is_protected || !fetch->url.SchemeIs("https")) { 727 load_flags |= net::LOAD_DO_NOT_SEND_COOKIES | 728 net::LOAD_DO_NOT_SAVE_COOKIES; 729 } 730 extension_fetcher_.reset(net::URLFetcher::Create( 731 kExtensionFetcherId, fetch->url, net::URLFetcher::GET, this)); 732 extension_fetcher_->SetRequestContext(request_context_); 733 extension_fetcher_->SetLoadFlags(load_flags); 734 extension_fetcher_->SetAutomaticallyRetryOnNetworkChanges(3); 735 // Download CRX files to a temp file. The blacklist is small and will be 736 // processed in memory, so it is fetched into a string. 737 if (fetch->id != kBlacklistAppID) { 738 extension_fetcher_->SaveResponseToTemporaryFile( 739 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)); 740 } 741 742 VLOG(2) << "Starting fetch of " << fetch->url << " for " << fetch->id; 743 744 extension_fetcher_->Start(); 745 } 746 747 void ExtensionDownloader::OnCRXFetchComplete( 748 const net::URLFetcher* source, 749 const GURL& url, 750 const net::URLRequestStatus& status, 751 int response_code, 752 const base::TimeDelta& backoff_delay) { 753 const std::string& id = extensions_queue_.active_request()->id; 754 if (status.status() == net::URLRequestStatus::SUCCESS && 755 (response_code == 200 || url.SchemeIsFile())) { 756 RETRY_HISTOGRAM("CrxFetchSuccess", 757 extensions_queue_.active_request_failure_count(), url); 758 base::FilePath crx_path; 759 // Take ownership of the file at |crx_path|. 760 CHECK(source->GetResponseAsFilePath(true, &crx_path)); 761 scoped_ptr<ExtensionFetch> fetch_data = 762 extensions_queue_.reset_active_request(); 763 if (extension_cache_) { 764 const std::string& version = fetch_data->version; 765 extension_cache_->PutExtension(id, crx_path, version, 766 base::Bind(&ExtensionDownloader::NotifyDelegateDownloadFinished, 767 weak_ptr_factory_.GetWeakPtr(), 768 base::Passed(&fetch_data))); 769 } else { 770 NotifyDelegateDownloadFinished(fetch_data.Pass(), crx_path, true); 771 } 772 } else if (ShouldRetryRequestWithCookies( 773 status, 774 response_code, 775 extensions_queue_.active_request()->is_protected)) { 776 // Requeue the fetch with |is_protected| set, enabling cookies. 777 extensions_queue_.active_request()->is_protected = true; 778 extensions_queue_.RetryRequest(backoff_delay); 779 } else if (ShouldRetryRequestWithNextUser( 780 status, 781 response_code, 782 extensions_queue_.active_request()->is_protected) && 783 IncrementAuthUserIndex(&extensions_queue_.active_request()->url)) { 784 extensions_queue_.RetryRequest(backoff_delay); 785 } else { 786 const std::set<int>& request_ids = 787 extensions_queue_.active_request()->request_ids; 788 const ExtensionDownloaderDelegate::PingResult& ping = ping_results_[id]; 789 VLOG(1) << "Failed to fetch extension '" << url.possibly_invalid_spec() 790 << "' response code:" << response_code; 791 if (ShouldRetryRequest(status, response_code) && 792 extensions_queue_.active_request_failure_count() < kMaxRetries) { 793 extensions_queue_.RetryRequest(backoff_delay); 794 } else { 795 RETRY_HISTOGRAM("CrxFetchFailure", 796 extensions_queue_.active_request_failure_count(), url); 797 // status.error() is 0 (net::OK) or negative. (See net/base/net_errors.h) 798 UMA_HISTOGRAM_SPARSE_SLOWLY("Extensions.CrxFetchError", -status.error()); 799 delegate_->OnExtensionDownloadFailed( 800 id, ExtensionDownloaderDelegate::CRX_FETCH_FAILED, ping, request_ids); 801 } 802 ping_results_.erase(id); 803 extensions_queue_.reset_active_request(); 804 } 805 806 extension_fetcher_.reset(); 807 808 // If there are any pending downloads left, start the next one. 809 extensions_queue_.StartNextRequest(); 810 } 811 812 void ExtensionDownloader::NotifyExtensionsDownloadFailed( 813 const std::set<std::string>& extension_ids, 814 const std::set<int>& request_ids, 815 ExtensionDownloaderDelegate::Error error) { 816 for (std::set<std::string>::const_iterator it = extension_ids.begin(); 817 it != extension_ids.end(); ++it) { 818 const ExtensionDownloaderDelegate::PingResult& ping = ping_results_[*it]; 819 delegate_->OnExtensionDownloadFailed(*it, error, ping, request_ids); 820 ping_results_.erase(*it); 821 } 822 } 823 824 void ExtensionDownloader::NotifyUpdateFound(const std::string& id, 825 const std::string& version) { 826 UpdateDetails updateInfo(id, Version(version)); 827 content::NotificationService::current()->Notify( 828 chrome::NOTIFICATION_EXTENSION_UPDATE_FOUND, 829 content::NotificationService::AllBrowserContextsAndSources(), 830 content::Details<UpdateDetails>(&updateInfo)); 831 } 832 833 } // namespace extensions 834