Index: components/update_client/protocol_builder.cc |
diff --git a/components/update_client/protocol_builder.cc b/components/update_client/protocol_builder.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..966e1ac37ad7095f0e6d9de5406c79dd3deef51a |
--- /dev/null |
+++ b/components/update_client/protocol_builder.cc |
@@ -0,0 +1,374 @@ |
+// 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 <stdint.h> |
+ |
+#include "base/guid.h" |
+#include "base/logging.h" |
+#include "base/strings/string_number_conversions.h" |
+#include "base/strings/stringprintf.h" |
+#include "base/sys_info.h" |
+#include "build/build_config.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<int>(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 string representing a sequence of download complete events |
+// corresponding to each download metrics in |item|. |
+std::string BuildDownloadCompleteEventElements(const Component& component) { |
+ using base::StringAppendF; |
+ std::string download_events; |
+ for (const auto& metrics : component.download_metrics()) { |
+ std::string event("<event eventtype=\"14\""); |
+ StringAppendF(&event, " eventresult=\"%d\"", metrics.error == 0); |
+ StringAppendF(&event, " downloader=\"%s\"", |
+ DownloaderToString(metrics.downloader)); |
+ if (metrics.error) { |
+ StringAppendF(&event, " errorcode=\"%d\"", metrics.error); |
+ } |
+ StringAppendF(&event, " url=\"%s\"", metrics.url.spec().c_str()); |
+ |
+ // -1 means that the byte counts are not known. |
+ if (metrics.downloaded_bytes != -1) { |
+ StringAppendF(&event, " downloaded=\"%s\"", |
+ base::Int64ToString(metrics.downloaded_bytes).c_str()); |
+ } |
+ if (metrics.total_bytes != -1) { |
+ StringAppendF(&event, " total=\"%s\"", |
+ base::Int64ToString(metrics.total_bytes).c_str()); |
+ } |
+ |
+ if (metrics.download_time_ms) { |
+ StringAppendF(&event, " download_time_ms=\"%s\"", |
+ base::Uint64ToString(metrics.download_time_ms).c_str()); |
+ } |
+ StringAppendF(&event, "/>"); |
+ |
+ download_events += event; |
+ } |
+ return download_events; |
+} |
+ |
+// Returns a string representing one ping event for the update of a component. |
+// The event type for this ping event is 3. |
+std::string BuildUpdateCompleteEventElement(const Component& component) { |
+ DCHECK(component.state() == ComponentState::kUpdateError || |
+ component.state() == ComponentState::kUpdated); |
+ |
+ using base::StringAppendF; |
+ |
+ std::string ping_event("<event eventtype=\"3\""); |
+ const int event_result = component.state() == ComponentState::kUpdated; |
+ StringAppendF(&ping_event, " eventresult=\"%d\"", event_result); |
+ if (component.error_category()) |
+ StringAppendF(&ping_event, " errorcat=\"%d\"", component.error_category()); |
+ if (component.error_code()) |
+ StringAppendF(&ping_event, " errorcode=\"%d\"", component.error_code()); |
+ if (component.extra_code1()) |
+ StringAppendF(&ping_event, " extracode1=\"%d\"", component.extra_code1()); |
+ if (HasDiffUpdate(component)) |
+ StringAppendF(&ping_event, " diffresult=\"%d\"", |
+ !component.diff_update_failed()); |
+ if (component.diff_error_category()) { |
+ StringAppendF(&ping_event, " differrorcat=\"%d\"", |
+ component.diff_error_category()); |
+ } |
+ if (component.diff_error_code()) |
+ StringAppendF(&ping_event, " differrorcode=\"%d\"", |
+ component.diff_error_code()); |
+ if (component.diff_extra_code1()) { |
+ StringAppendF(&ping_event, " diffextracode1=\"%d\"", |
+ component.diff_extra_code1()); |
+ } |
+ if (!component.previous_fp().empty()) |
+ StringAppendF(&ping_event, " previousfp=\"%s\"", |
+ component.previous_fp().c_str()); |
+ if (!component.next_fp().empty()) |
+ StringAppendF(&ping_event, " nextfp=\"%s\"", component.next_fp().c_str()); |
+ StringAppendF(&ping_event, "/>"); |
+ return ping_event; |
+} |
+ |
+// Returns a string representing one ping event for the uninstall of a |
+// component. The event type for this ping event is 4. |
+std::string BuildUninstalledEventElement(const Component& component) { |
+ DCHECK(component.state() == ComponentState::kUninstalled); |
+ |
+ using base::StringAppendF; |
+ |
+ std::string ping_event("<event eventtype=\"4\" eventresult=\"1\""); |
+ if (component.extra_code1()) |
+ StringAppendF(&ping_event, " extracode1=\"%d\"", component.extra_code1()); |
+ StringAppendF(&ping_event, "/>"); |
+ return ping_event; |
+} |
+ |
+} // namespace |
+ |
+std::string BuildProtocolRequest( |
+ 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<UpdaterState::Attributes>& updater_state_attributes) { |
+ std::string request = base::StringPrintf( |
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" |
+ "<request protocol=\"%s\" ", |
+ kProtocolVersion); |
+ |
+ if (!additional_attributes.empty()) |
+ base::StringAppendF(&request, "%s ", additional_attributes.c_str()); |
+ |
+ // Chrome version and platform information. |
+ base::StringAppendF( |
+ &request, |
+ "version=\"%s-%s\" prodversion=\"%s\" " |
+ "requestid=\"{%s}\" lang=\"%s\" updaterchannel=\"%s\" prodchannel=\"%s\" " |
+ "os=\"%s\" arch=\"%s\" nacl_arch=\"%s\"", |
+ prod_id.c_str(), // "version" is prefixed by prod_id. |
+ browser_version.c_str(), |
+ browser_version.c_str(), // "prodversion" |
+ base::GenerateGUID().c_str(), // "requestid" |
+ lang.c_str(), // "lang" |
+ channel.c_str(), // "updaterchannel" |
+ channel.c_str(), // "prodchannel" |
+ UpdateQueryParams::GetOS(), // "os" |
+ UpdateQueryParams::GetArch(), // "arch" |
+ UpdateQueryParams::GetNaclArch()); // "nacl_arch" |
+#if defined(OS_WIN) |
+ const bool is_wow64(base::win::OSInfo::GetInstance()->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, "<hw physmemory=\"%d\"/>", |
+ GetPhysicalMemoryGB()); // "physmem" in GB. |
+ |
+ // OS version and platform information. |
+ const std::string os_version = GetOSVersion(); |
+ const std::string os_sp = GetServicePack(); |
+ base::StringAppendF( |
+ &request, "<os platform=\"%s\" arch=\"%s\"", |
+ os_long_name.c_str(), // "platform" |
+ base::SysInfo().OperatingSystemArchitecture().c_str()); // "arch" |
+ if (!os_version.empty()) |
+ base::StringAppendF(&request, " version=\"%s\"", os_version.c_str()); |
+ if (!os_sp.empty()) |
+ base::StringAppendF(&request, " sp=\"%s\"", os_sp.c_str()); |
+ base::StringAppendF(&request, "/>"); |
+ |
+#if defined(GOOGLE_CHROME_BUILD) |
+ // Updater state. |
+ if (updater_state_attributes) { |
+ base::StringAppendF(&request, "<updater"); |
+ for (const auto& attr : *updater_state_attributes) { |
+ if (attr.first != UpdaterState::kIsEnterpriseManaged) { |
+ base::StringAppendF(&request, " %s=\"%s\"", attr.first.c_str(), |
+ attr.second.c_str()); |
+ } |
+ } |
+ base::StringAppendF(&request, "/>"); |
+ } |
+#endif // GOOGLE_CHROME_BUILD |
+ |
+ // The actual payload of the request. |
+ base::StringAppendF(&request, "%s</request>", request_body.c_str()); |
+ |
+ return request; |
+} |
+ |
+std::string BuildUpdateCheckRequest( |
+ const Configurator& config, |
+ const std::vector<std::string>& ids_checked, |
+ const IdToComponentPtrMap& components, |
+ PersistedData* metadata, |
+ const std::string& additional_attributes, |
+ bool enabled_component_updates, |
+ const std::unique_ptr<UpdaterState::Attributes>& 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 Component& component = *components.at(id); |
+ |
+ const update_client::InstallerAttributes installer_attributes( |
+ SanitizeInstallerAttributes( |
+ component.crx_component().installer_attributes)); |
+ std::string app("<app "); |
+ base::StringAppendF(&app, "appid=\"%s\" version=\"%s\"", |
+ component.id().c_str(), |
+ component.crx_component().version.GetString().c_str()); |
+ if (!brand.empty()) |
+ base::StringAppendF(&app, " brand=\"%s\"", brand.c_str()); |
+ if (component.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(component.id()); |
+ const std::string cohort_name = metadata->GetCohortName(component.id()); |
+ const std::string cohort_hint = metadata->GetCohortHint(component.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, "<updatecheck"); |
+ if (component.crx_component() |
+ .supports_group_policy_enable_component_updates && |
+ !enabled_component_updates) { |
+ base::StringAppendF(&app, " updatedisabled=\"true\""); |
+ } |
+ base::StringAppendF(&app, "/>"); |
+ |
+ base::StringAppendF(&app, "<ping rd=\"%d\" ping_freshness=\"%s\"/>", |
+ metadata->GetDateLastRollCall(component.id()), |
+ metadata->GetPingFreshness(component.id()).c_str()); |
+ if (!component.crx_component().fingerprint.empty()) { |
+ base::StringAppendF(&app, |
+ "<packages>" |
+ "<package fp=\"%s\"/>" |
+ "</packages>", |
+ component.crx_component().fingerprint.c_str()); |
+ } |
+ base::StringAppendF(&app, "</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); |
+} |
+ |
+std::string BuildPing(const Configurator& config, const Component& component) { |
+ const char app_element_format[] = |
+ "<app appid=\"%s\" version=\"%s\" nextversion=\"%s\">" |
+ "%s" |
+ "%s" |
+ "</app>"; |
+ |
+ std::string ping_event; |
+ switch (component.state()) { |
+ case ComponentState::kUpdateError: // Fall through. |
+ case ComponentState::kUpdated: |
+ ping_event = BuildUpdateCompleteEventElement(component); |
+ break; |
+ case ComponentState::kUninstalled: |
+ ping_event = BuildUninstalledEventElement(component); |
+ break; |
+ default: |
+ NOTREACHED(); |
+ break; |
+ } |
+ |
+ const std::string app_element(base::StringPrintf( |
+ app_element_format, |
+ component.id().c_str(), // "appid" |
+ component.previous_version().GetString().c_str(), // "version" |
+ component.next_version().GetString().c_str(), // "nextversion" |
+ ping_event.c_str(), // ping event |
+ BuildDownloadCompleteEventElements(component) |
+ .c_str())); // download events |
+ |
+ // The ping request does not include any updater state. |
+ return BuildProtocolRequest( |
+ config.GetProdId(), config.GetBrowserVersion().GetString(), |
+ config.GetChannel(), config.GetLang(), config.GetOSLongName(), |
+ config.GetDownloadPreference(), app_element, "", nullptr); |
+} |
+ |
+} // namespace update_client |