// Copyright 2017 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/protocol_builder.h" #include #include "base/guid.h" #include "base/logging.h" #include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/sys_info.h" #include "build/build_config.h" #include "components/update_client/activity_data_service.h" #include "components/update_client/component.h" #include "components/update_client/configurator.h" #include "components/update_client/persisted_data.h" #include "components/update_client/protocol_parser.h" #include "components/update_client/update_query_params.h" #include "components/update_client/updater_state.h" #include "components/update_client/utils.h" #if defined(OS_WIN) #include "base/win/windows_version.h" #endif 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|. InstallerAttributes SanitizeInstallerAttributes( const InstallerAttributes& installer_attributes) { InstallerAttributes sanitized_attrs; for (const auto& attr : installer_attributes) { if (IsValidInstallerAttribute(attr)) sanitized_attrs.insert(attr); } return sanitized_attrs; } // Returns the amount of physical memory in GB, rounded to the nearest GB. int GetPhysicalMemoryGB() { const double kOneGB = 1024 * 1024 * 1024; const int64_t phys_mem = base::SysInfo::AmountOfPhysicalMemory(); return static_cast(std::floor(0.5 + phys_mem / kOneGB)); } std::string GetOSVersion() { #if defined(OS_WIN) const auto ver = base::win::OSInfo::GetInstance()->version_number(); return base::StringPrintf("%d.%d.%d.%d", ver.major, ver.minor, ver.build, ver.patch); #else return base::SysInfo().OperatingSystemVersion(); #endif } std::string GetServicePack() { #if defined(OS_WIN) return base::win::OSInfo::GetInstance()->service_pack_str(); #else return std::string(); #endif } // Returns a string literal corresponding to the value of the downloader |d|. const char* DownloaderToString(CrxDownloader::DownloadMetrics::Downloader d) { switch (d) { case CrxDownloader::DownloadMetrics::kUrlFetcher: return "direct"; case CrxDownloader::DownloadMetrics::kBits: return "bits"; default: return "unknown"; } } // Returns a formatted string of previousversion and nextversion in an event. std::string EventVersions(const Component& component) { std::string event_versions; base::StringAppendF(&event_versions, " previousversion=\"%s\"", component.previous_version().GetString().c_str()); const base::Version& next_version = component.next_version(); if (next_version.IsValid()) { base::StringAppendF(&event_versions, " nextversion=\"%s\"", next_version.GetString().c_str()); } return event_versions; } } // namespace std::string BuildDownloadCompleteEventElement( const Component& component, const CrxDownloader::DownloadMetrics& metrics) { using base::StringAppendF; std::string event(""); return event; } std::string BuildUpdateCompleteEventElement(const Component& component) { DCHECK(component.state() == ComponentState::kUpdateError || component.state() == ComponentState::kUpdated); using base::StringAppendF; std::string event(""); return event; } std::string BuildUninstalledEventElement(const Component& component) { DCHECK(component.state() == ComponentState::kUninstalled); using base::StringAppendF; std::string event; StringAppendF(&event, ""); return event; } std::string BuildActionRunEventElement(bool succeeded, int error_code, int extra_code1) { using base::StringAppendF; std::string event; StringAppendF(&event, ""); return event; } std::string BuildProtocolRequest( const std::string& session_id, const std::string& prod_id, const std::string& browser_version, const std::string& channel, const std::string& lang, const std::string& os_long_name, const std::string& download_preference, const std::string& request_body, const std::string& additional_attributes, const std::unique_ptr& updater_state_attributes) { std::string request = base::StringPrintf( "" "wow64_status() == base::win::OSInfo::WOW64_ENABLED); if (is_wow64) base::StringAppendF(&request, " wow64=\"1\""); #endif if (!download_preference.empty()) base::StringAppendF(&request, " dlpref=\"%s\"", download_preference.c_str()); if (updater_state_attributes && updater_state_attributes->count(UpdaterState::kIsEnterpriseManaged)) { base::StringAppendF( &request, " %s=\"%s\"", // domainjoined UpdaterState::kIsEnterpriseManaged, (*updater_state_attributes)[UpdaterState::kIsEnterpriseManaged] .c_str()); } base::StringAppendF(&request, ">"); // HW platform information. base::StringAppendF(&request, "", GetPhysicalMemoryGB()); // "physmem" in GB. // OS version and platform information. const std::string os_version = GetOSVersion(); const std::string os_sp = GetServicePack(); base::StringAppendF( &request, ""); #if defined(GOOGLE_CHROME_BUILD) // Updater state. if (updater_state_attributes) { base::StringAppendF(&request, ""); } #endif // GOOGLE_CHROME_BUILD // The actual payload of the request. base::StringAppendF(&request, "%s", request_body.c_str()); return request; } std::map BuildUpdateCheckExtraRequestHeaders( scoped_refptr config, const std::vector& ids, bool is_foreground) { // This number of extension ids result in an HTTP header length of about 1KB. constexpr size_t maxExtensionCount = 30; const std::vector& app_ids = ids.size() <= maxExtensionCount ? ids : std::vector(ids.cbegin(), ids.cbegin() + maxExtensionCount); return std::map{ {"X-Goog-Update-Updater", base::StringPrintf("%s-%s", config->GetProdId().c_str(), config->GetBrowserVersion().GetString().c_str())}, {"X-Goog-Update-Interactivity", is_foreground ? "fg" : "bg"}, {"X-Goog-Update-AppId", base::JoinString(app_ids, ",")}, }; } std::string BuildUpdateCheckRequest( const Configurator& config, const std::string& session_id, const std::vector& ids_checked, const IdToComponentPtrMap& components, 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& id : ids_checked) { DCHECK_EQ(1u, components.count(id)); const auto& component = *components.at(id); const auto& crx_component = component.crx_component(); const auto& component_id = component.id(); const update_client::InstallerAttributes installer_attributes( SanitizeInstallerAttributes(crx_component.installer_attributes)); std::string app("GetCohort(component_id); const auto& cohort_name = metadata->GetCohortName(component_id); const auto& cohort_hint = metadata->GetCohortHint(component_id); const auto& disabled_reasons = crx_component.disabled_reasons; 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, " enabled=\"%d\">", disabled_reasons.empty() ? 1 : 0); for (const int& disabled_reason : disabled_reasons) base::StringAppendF(&app, "", disabled_reason); base::StringAppendF(&app, ""); base::StringAppendF(&app, "GetActiveBit(component_id)) { const int date_last_active = metadata->GetDateLastActive(component_id); if (date_last_active != kDateUnknown) { base::StringAppendF(&app, " ad=\"%d\"", date_last_active); } else { // Fall back to "day" if "date" is not available. base::StringAppendF(&app, " a=\"%d\"", metadata->GetDaysSinceLastActive(component_id)); } } const int date_last_rollcall = metadata->GetDateLastRollCall(component_id); if (date_last_rollcall != kDateUnknown) { base::StringAppendF(&app, " rd=\"%d\"", date_last_rollcall); } else { // Fall back to "day" if "date" is not available. base::StringAppendF(&app, " r=\"%d\"", metadata->GetDaysSinceLastRollCall(component_id)); } base::StringAppendF(&app, " ping_freshness=\"%s\"/>", metadata->GetPingFreshness(component_id).c_str()); if (!crx_component.fingerprint.empty()) { base::StringAppendF(&app, "" "" "", crx_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( session_id, config.GetProdId(), config.GetBrowserVersion().GetString(), config.GetChannel(), config.GetLang(), config.GetOSLongName(), config.GetDownloadPreference(), app_elements, additional_attributes, updater_state_attributes); } std::string BuildEventPingRequest(const Configurator& config, const Component& component) { DCHECK(component.state() == ComponentState::kUpdateError || component.state() == ComponentState::kUpToDate || component.state() == ComponentState::kUpdated || component.state() == ComponentState::kUninstalled); std::string app = base::StringPrintf("", component.id().c_str()); base::StrAppend(&app, component.events()); app.append(""); // The ping request does not include any updater state. return BuildProtocolRequest(component.session_id(), config.GetProdId(), config.GetBrowserVersion().GetString(), config.GetChannel(), config.GetLang(), config.GetOSLongName(), config.GetDownloadPreference(), app, "", nullptr); } } // namespace update_client