// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "components/update_client/update_checker.h" #include #include #include #include #include "base/bind.h" #include "base/bind_helpers.h" #include "base/location.h" #include "base/logging.h" #include "base/macros.h" #include "base/strings/stringprintf.h" #include "base/threading/thread_checker.h" #include "base/threading/thread_task_runner_handle.h" #include "components/update_client/configurator.h" #include "components/update_client/crx_update_item.h" #include "components/update_client/persisted_data.h" #include "components/update_client/request_sender.h" #include "components/update_client/update_client.h" #include "components/update_client/updater_state.h" #include "components/update_client/utils.h" #include "url/gurl.h" namespace update_client { namespace { // Returns a sanitized version of the brand or an empty string otherwise. std::string SanitizeBrand(const std::string& brand) { return IsValidBrand(brand) ? brand : std::string(""); } // Filters invalid attributes from |installer_attributes|. update_client::InstallerAttributes SanitizeInstallerAttributes( const update_client::InstallerAttributes& installer_attributes) { update_client::InstallerAttributes sanitized_attrs; for (const auto& attr : installer_attributes) { if (IsValidInstallerAttribute(attr)) sanitized_attrs.insert(attr); } return sanitized_attrs; } // Returns true if at least one item requires network encryption. bool IsEncryptionRequired(const IdToCrxUpdateItemMap& items) { for (const auto& item : items) { if (item.second->component.requires_network_encryption) return true; } return false; } // Builds an update check request for |components|. |additional_attributes| is // serialized as part of the element of the request to customize it // with data that is not platform or component specific. For each |item|, a // corresponding element is created and inserted as a child node of // the . // // An app element looks like this: // // // // // // std::string BuildUpdateCheckRequest( const Configurator& config, const IdToCrxUpdateItemMap& items, PersistedData* metadata, const std::string& additional_attributes, bool enabled_component_updates, const std::unique_ptr& updater_state_attributes) { const std::string brand(SanitizeBrand(config.GetBrand())); std::string app_elements; for (const auto& item_pair : items) { const CrxUpdateItem* item = item_pair.second.get(); const update_client::InstallerAttributes installer_attributes( SanitizeInstallerAttributes(item->component.installer_attributes)); std::string app("id.c_str(), item->component.version.GetString().c_str()); if (!brand.empty()) base::StringAppendF(&app, " brand=\"%s\"", brand.c_str()); if (item->on_demand) base::StringAppendF(&app, " installsource=\"ondemand\""); for (const auto& attr : installer_attributes) { base::StringAppendF(&app, " %s=\"%s\"", attr.first.c_str(), attr.second.c_str()); } const std::string cohort = metadata->GetCohort(item->id); const std::string cohort_name = metadata->GetCohortName(item->id); const std::string cohort_hint = metadata->GetCohortHint(item->id); if (!cohort.empty()) base::StringAppendF(&app, " cohort=\"%s\"", cohort.c_str()); if (!cohort_name.empty()) base::StringAppendF(&app, " cohortname=\"%s\"", cohort_name.c_str()); if (!cohort_hint.empty()) base::StringAppendF(&app, " cohorthint=\"%s\"", cohort_hint.c_str()); base::StringAppendF(&app, ">"); base::StringAppendF(&app, "component.supports_group_policy_enable_component_updates && !enabled_component_updates) { base::StringAppendF(&app, " updatedisabled=\"true\""); } base::StringAppendF(&app, "/>"); base::StringAppendF(&app, "", metadata->GetDateLastRollCall(item->id), metadata->GetPingFreshness(item->id).c_str()); if (!item->component.fingerprint.empty()) { base::StringAppendF(&app, "" "" "", item->component.fingerprint.c_str()); } base::StringAppendF(&app, ""); app_elements.append(app); VLOG(1) << "Appending to update request: " << app; } // Include the updater state in the update check request. return BuildProtocolRequest( config.GetProdId(), config.GetBrowserVersion().GetString(), config.GetChannel(), config.GetLang(), config.GetOSLongName(), config.GetDownloadPreference(), app_elements, additional_attributes, updater_state_attributes); } class UpdateCheckerImpl : public UpdateChecker { public: UpdateCheckerImpl(const scoped_refptr& config, PersistedData* metadata); ~UpdateCheckerImpl() override; // Overrides for UpdateChecker. bool CheckForUpdates( const IdToCrxUpdateItemMap& items_to_check, const std::string& additional_attributes, bool enabled_component_updates, const UpdateCheckCallback& update_check_callback) override; private: void ReadUpdaterStateAttributes(); void CheckForUpdatesHelper(const IdToCrxUpdateItemMap& items_to_check, const std::string& additional_attributes, bool enabled_component_updates); void OnRequestSenderComplete( std::unique_ptr> ids_checked, int error, const std::string& response, int retry_after_sec); base::ThreadChecker thread_checker_; const scoped_refptr config_; PersistedData* metadata_; UpdateCheckCallback update_check_callback_; std::unique_ptr updater_state_attributes_; std::unique_ptr request_sender_; DISALLOW_COPY_AND_ASSIGN(UpdateCheckerImpl); }; UpdateCheckerImpl::UpdateCheckerImpl(const scoped_refptr& config, PersistedData* metadata) : config_(config), metadata_(metadata) {} UpdateCheckerImpl::~UpdateCheckerImpl() { DCHECK(thread_checker_.CalledOnValidThread()); } bool UpdateCheckerImpl::CheckForUpdates( const IdToCrxUpdateItemMap& items_to_check, const std::string& additional_attributes, bool enabled_component_updates, const UpdateCheckCallback& update_check_callback) { DCHECK(thread_checker_.CalledOnValidThread()); update_check_callback_ = update_check_callback; return config_->GetSequencedTaskRunner()->PostTaskAndReply( FROM_HERE, base::Bind(&UpdateCheckerImpl::ReadUpdaterStateAttributes, base::Unretained(this)), base::Bind(&UpdateCheckerImpl::CheckForUpdatesHelper, base::Unretained(this), base::ConstRef(items_to_check), additional_attributes, enabled_component_updates)); } // This function runs on the blocking pool task runner. void UpdateCheckerImpl::ReadUpdaterStateAttributes() { const bool is_machine_install = !config_->IsPerUserInstall(); updater_state_attributes_ = UpdaterState::GetState(is_machine_install); } void UpdateCheckerImpl::CheckForUpdatesHelper( const IdToCrxUpdateItemMap& items_to_check, const std::string& additional_attributes, bool enabled_component_updates) { DCHECK(thread_checker_.CalledOnValidThread()); auto urls(config_->UpdateUrl()); if (IsEncryptionRequired(items_to_check)) RemoveUnsecureUrls(&urls); std::unique_ptr> ids_checked( new std::vector()); for (const auto& item : items_to_check) ids_checked->push_back(item.second->id); request_sender_.reset(new RequestSender(config_)); request_sender_->Send( config_->EnabledCupSigning(), BuildUpdateCheckRequest(*config_, items_to_check, metadata_, additional_attributes, enabled_component_updates, updater_state_attributes_), urls, base::Bind(&UpdateCheckerImpl::OnRequestSenderComplete, base::Unretained(this), base::Passed(&ids_checked))); } void UpdateCheckerImpl::OnRequestSenderComplete( std::unique_ptr> ids_checked, int error, const std::string& response, int retry_after_sec) { DCHECK(thread_checker_.CalledOnValidThread()); if (!error) { UpdateResponse update_response; if (update_response.Parse(response)) { int daynum = update_response.results().daystart_elapsed_days; if (daynum != UpdateResponse::kNoDaystart) metadata_->SetDateLastRollCall(*ids_checked, daynum); for (const auto& result : update_response.results().list) { auto entry = result.cohort_attrs.find(UpdateResponse::Result::kCohort); if (entry != result.cohort_attrs.end()) metadata_->SetCohort(result.extension_id, entry->second); entry = result.cohort_attrs.find(UpdateResponse::Result::kCohortName); if (entry != result.cohort_attrs.end()) metadata_->SetCohortName(result.extension_id, entry->second); entry = result.cohort_attrs.find(UpdateResponse::Result::kCohortHint); if (entry != result.cohort_attrs.end()) metadata_->SetCohortHint(result.extension_id, entry->second); } base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(update_check_callback_, error, update_response.results(), retry_after_sec)); return; } error = -1; VLOG(1) << "Parse failed " << update_response.errors(); } base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(update_check_callback_, error, UpdateResponse::Results(), retry_after_sec)); } } // namespace std::unique_ptr UpdateChecker::Create( const scoped_refptr& config, PersistedData* persistent) { return std::unique_ptr( new UpdateCheckerImpl(config, persistent)); } } // namespace update_client