OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 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 "components/update_client/protocol_builder.h" |
| 6 |
| 7 #include <stdint.h> |
| 8 |
| 9 #include "base/guid.h" |
| 10 #include "base/logging.h" |
| 11 #include "base/strings/string_number_conversions.h" |
| 12 #include "base/strings/stringprintf.h" |
| 13 #include "base/sys_info.h" |
| 14 #include "build/build_config.h" |
| 15 #include "components/update_client/component.h" |
| 16 #include "components/update_client/configurator.h" |
| 17 #include "components/update_client/persisted_data.h" |
| 18 #include "components/update_client/protocol_parser.h" |
| 19 #include "components/update_client/update_query_params.h" |
| 20 #include "components/update_client/updater_state.h" |
| 21 #include "components/update_client/utils.h" |
| 22 |
| 23 #if defined(OS_WIN) |
| 24 #include "base/win/windows_version.h" |
| 25 #endif |
| 26 |
| 27 namespace update_client { |
| 28 |
| 29 namespace { |
| 30 |
| 31 // Returns a sanitized version of the brand or an empty string otherwise. |
| 32 std::string SanitizeBrand(const std::string& brand) { |
| 33 return IsValidBrand(brand) ? brand : std::string(""); |
| 34 } |
| 35 |
| 36 // Filters invalid attributes from |installer_attributes|. |
| 37 InstallerAttributes SanitizeInstallerAttributes( |
| 38 const InstallerAttributes& installer_attributes) { |
| 39 InstallerAttributes sanitized_attrs; |
| 40 for (const auto& attr : installer_attributes) { |
| 41 if (IsValidInstallerAttribute(attr)) |
| 42 sanitized_attrs.insert(attr); |
| 43 } |
| 44 return sanitized_attrs; |
| 45 } |
| 46 |
| 47 // Returns the amount of physical memory in GB, rounded to the nearest GB. |
| 48 int GetPhysicalMemoryGB() { |
| 49 const double kOneGB = 1024 * 1024 * 1024; |
| 50 const int64_t phys_mem = base::SysInfo::AmountOfPhysicalMemory(); |
| 51 return static_cast<int>(std::floor(0.5 + phys_mem / kOneGB)); |
| 52 } |
| 53 |
| 54 std::string GetOSVersion() { |
| 55 #if defined(OS_WIN) |
| 56 const auto ver = base::win::OSInfo::GetInstance()->version_number(); |
| 57 return base::StringPrintf("%d.%d.%d.%d", ver.major, ver.minor, ver.build, |
| 58 ver.patch); |
| 59 #else |
| 60 return base::SysInfo().OperatingSystemVersion(); |
| 61 #endif |
| 62 } |
| 63 |
| 64 std::string GetServicePack() { |
| 65 #if defined(OS_WIN) |
| 66 return base::win::OSInfo::GetInstance()->service_pack_str(); |
| 67 #else |
| 68 return std::string(); |
| 69 #endif |
| 70 } |
| 71 |
| 72 // Returns a string literal corresponding to the value of the downloader |d|. |
| 73 const char* DownloaderToString(CrxDownloader::DownloadMetrics::Downloader d) { |
| 74 switch (d) { |
| 75 case CrxDownloader::DownloadMetrics::kUrlFetcher: |
| 76 return "direct"; |
| 77 case CrxDownloader::DownloadMetrics::kBits: |
| 78 return "bits"; |
| 79 default: |
| 80 return "unknown"; |
| 81 } |
| 82 } |
| 83 |
| 84 // Returns a string representing a sequence of download complete events |
| 85 // corresponding to each download metrics in |item|. |
| 86 std::string BuildDownloadCompleteEventElements(const Component& component) { |
| 87 using base::StringAppendF; |
| 88 std::string download_events; |
| 89 for (const auto& metrics : component.download_metrics()) { |
| 90 std::string event("<event eventtype=\"14\""); |
| 91 StringAppendF(&event, " eventresult=\"%d\"", metrics.error == 0); |
| 92 StringAppendF(&event, " downloader=\"%s\"", |
| 93 DownloaderToString(metrics.downloader)); |
| 94 if (metrics.error) { |
| 95 StringAppendF(&event, " errorcode=\"%d\"", metrics.error); |
| 96 } |
| 97 StringAppendF(&event, " url=\"%s\"", metrics.url.spec().c_str()); |
| 98 |
| 99 // -1 means that the byte counts are not known. |
| 100 if (metrics.downloaded_bytes != -1) { |
| 101 StringAppendF(&event, " downloaded=\"%s\"", |
| 102 base::Int64ToString(metrics.downloaded_bytes).c_str()); |
| 103 } |
| 104 if (metrics.total_bytes != -1) { |
| 105 StringAppendF(&event, " total=\"%s\"", |
| 106 base::Int64ToString(metrics.total_bytes).c_str()); |
| 107 } |
| 108 |
| 109 if (metrics.download_time_ms) { |
| 110 StringAppendF(&event, " download_time_ms=\"%s\"", |
| 111 base::Uint64ToString(metrics.download_time_ms).c_str()); |
| 112 } |
| 113 StringAppendF(&event, "/>"); |
| 114 |
| 115 download_events += event; |
| 116 } |
| 117 return download_events; |
| 118 } |
| 119 |
| 120 // Returns a string representing one ping event for the update of a component. |
| 121 // The event type for this ping event is 3. |
| 122 std::string BuildUpdateCompleteEventElement(const Component& component) { |
| 123 DCHECK(component.state() == ComponentState::kUpdateError || |
| 124 component.state() == ComponentState::kUpdated); |
| 125 |
| 126 using base::StringAppendF; |
| 127 |
| 128 std::string ping_event("<event eventtype=\"3\""); |
| 129 const int event_result = component.state() == ComponentState::kUpdated; |
| 130 StringAppendF(&ping_event, " eventresult=\"%d\"", event_result); |
| 131 if (component.error_category()) |
| 132 StringAppendF(&ping_event, " errorcat=\"%d\"", component.error_category()); |
| 133 if (component.error_code()) |
| 134 StringAppendF(&ping_event, " errorcode=\"%d\"", component.error_code()); |
| 135 if (component.extra_code1()) |
| 136 StringAppendF(&ping_event, " extracode1=\"%d\"", component.extra_code1()); |
| 137 if (HasDiffUpdate(component)) |
| 138 StringAppendF(&ping_event, " diffresult=\"%d\"", |
| 139 !component.diff_update_failed()); |
| 140 if (component.diff_error_category()) { |
| 141 StringAppendF(&ping_event, " differrorcat=\"%d\"", |
| 142 component.diff_error_category()); |
| 143 } |
| 144 if (component.diff_error_code()) |
| 145 StringAppendF(&ping_event, " differrorcode=\"%d\"", |
| 146 component.diff_error_code()); |
| 147 if (component.diff_extra_code1()) { |
| 148 StringAppendF(&ping_event, " diffextracode1=\"%d\"", |
| 149 component.diff_extra_code1()); |
| 150 } |
| 151 if (!component.previous_fp().empty()) |
| 152 StringAppendF(&ping_event, " previousfp=\"%s\"", |
| 153 component.previous_fp().c_str()); |
| 154 if (!component.next_fp().empty()) |
| 155 StringAppendF(&ping_event, " nextfp=\"%s\"", component.next_fp().c_str()); |
| 156 StringAppendF(&ping_event, "/>"); |
| 157 return ping_event; |
| 158 } |
| 159 |
| 160 // Returns a string representing one ping event for the uninstall of a |
| 161 // component. The event type for this ping event is 4. |
| 162 std::string BuildUninstalledEventElement(const Component& component) { |
| 163 DCHECK(component.state() == ComponentState::kUninstalled); |
| 164 |
| 165 using base::StringAppendF; |
| 166 |
| 167 std::string ping_event("<event eventtype=\"4\" eventresult=\"1\""); |
| 168 if (component.extra_code1()) |
| 169 StringAppendF(&ping_event, " extracode1=\"%d\"", component.extra_code1()); |
| 170 StringAppendF(&ping_event, "/>"); |
| 171 return ping_event; |
| 172 } |
| 173 |
| 174 } // namespace |
| 175 |
| 176 std::string BuildProtocolRequest( |
| 177 const std::string& prod_id, |
| 178 const std::string& browser_version, |
| 179 const std::string& channel, |
| 180 const std::string& lang, |
| 181 const std::string& os_long_name, |
| 182 const std::string& download_preference, |
| 183 const std::string& request_body, |
| 184 const std::string& additional_attributes, |
| 185 const std::unique_ptr<UpdaterState::Attributes>& updater_state_attributes) { |
| 186 std::string request = base::StringPrintf( |
| 187 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" |
| 188 "<request protocol=\"%s\" ", |
| 189 kProtocolVersion); |
| 190 |
| 191 if (!additional_attributes.empty()) |
| 192 base::StringAppendF(&request, "%s ", additional_attributes.c_str()); |
| 193 |
| 194 // Chrome version and platform information. |
| 195 base::StringAppendF( |
| 196 &request, |
| 197 "version=\"%s-%s\" prodversion=\"%s\" " |
| 198 "requestid=\"{%s}\" lang=\"%s\" updaterchannel=\"%s\" prodchannel=\"%s\" " |
| 199 "os=\"%s\" arch=\"%s\" nacl_arch=\"%s\"", |
| 200 prod_id.c_str(), // "version" is prefixed by prod_id. |
| 201 browser_version.c_str(), |
| 202 browser_version.c_str(), // "prodversion" |
| 203 base::GenerateGUID().c_str(), // "requestid" |
| 204 lang.c_str(), // "lang" |
| 205 channel.c_str(), // "updaterchannel" |
| 206 channel.c_str(), // "prodchannel" |
| 207 UpdateQueryParams::GetOS(), // "os" |
| 208 UpdateQueryParams::GetArch(), // "arch" |
| 209 UpdateQueryParams::GetNaclArch()); // "nacl_arch" |
| 210 #if defined(OS_WIN) |
| 211 const bool is_wow64(base::win::OSInfo::GetInstance()->wow64_status() == |
| 212 base::win::OSInfo::WOW64_ENABLED); |
| 213 if (is_wow64) |
| 214 base::StringAppendF(&request, " wow64=\"1\""); |
| 215 #endif |
| 216 if (!download_preference.empty()) |
| 217 base::StringAppendF(&request, " dlpref=\"%s\"", |
| 218 download_preference.c_str()); |
| 219 if (updater_state_attributes && |
| 220 updater_state_attributes->count(UpdaterState::kIsEnterpriseManaged)) { |
| 221 base::StringAppendF( |
| 222 &request, " %s=\"%s\"", // domainjoined |
| 223 UpdaterState::kIsEnterpriseManaged, |
| 224 (*updater_state_attributes)[UpdaterState::kIsEnterpriseManaged] |
| 225 .c_str()); |
| 226 } |
| 227 base::StringAppendF(&request, ">"); |
| 228 |
| 229 // HW platform information. |
| 230 base::StringAppendF(&request, "<hw physmemory=\"%d\"/>", |
| 231 GetPhysicalMemoryGB()); // "physmem" in GB. |
| 232 |
| 233 // OS version and platform information. |
| 234 const std::string os_version = GetOSVersion(); |
| 235 const std::string os_sp = GetServicePack(); |
| 236 base::StringAppendF( |
| 237 &request, "<os platform=\"%s\" arch=\"%s\"", |
| 238 os_long_name.c_str(), // "platform" |
| 239 base::SysInfo().OperatingSystemArchitecture().c_str()); // "arch" |
| 240 if (!os_version.empty()) |
| 241 base::StringAppendF(&request, " version=\"%s\"", os_version.c_str()); |
| 242 if (!os_sp.empty()) |
| 243 base::StringAppendF(&request, " sp=\"%s\"", os_sp.c_str()); |
| 244 base::StringAppendF(&request, "/>"); |
| 245 |
| 246 #if defined(GOOGLE_CHROME_BUILD) |
| 247 // Updater state. |
| 248 if (updater_state_attributes) { |
| 249 base::StringAppendF(&request, "<updater"); |
| 250 for (const auto& attr : *updater_state_attributes) { |
| 251 if (attr.first != UpdaterState::kIsEnterpriseManaged) { |
| 252 base::StringAppendF(&request, " %s=\"%s\"", attr.first.c_str(), |
| 253 attr.second.c_str()); |
| 254 } |
| 255 } |
| 256 base::StringAppendF(&request, "/>"); |
| 257 } |
| 258 #endif // GOOGLE_CHROME_BUILD |
| 259 |
| 260 // The actual payload of the request. |
| 261 base::StringAppendF(&request, "%s</request>", request_body.c_str()); |
| 262 |
| 263 return request; |
| 264 } |
| 265 |
| 266 std::string BuildUpdateCheckRequest( |
| 267 const Configurator& config, |
| 268 const std::vector<std::string>& ids_checked, |
| 269 const IdToComponentPtrMap& components, |
| 270 PersistedData* metadata, |
| 271 const std::string& additional_attributes, |
| 272 bool enabled_component_updates, |
| 273 const std::unique_ptr<UpdaterState::Attributes>& updater_state_attributes) { |
| 274 const std::string brand(SanitizeBrand(config.GetBrand())); |
| 275 std::string app_elements; |
| 276 for (const auto& id : ids_checked) { |
| 277 DCHECK_EQ(1u, components.count(id)); |
| 278 const Component& component = *components.at(id); |
| 279 |
| 280 const update_client::InstallerAttributes installer_attributes( |
| 281 SanitizeInstallerAttributes( |
| 282 component.crx_component().installer_attributes)); |
| 283 std::string app("<app "); |
| 284 base::StringAppendF(&app, "appid=\"%s\" version=\"%s\"", |
| 285 component.id().c_str(), |
| 286 component.crx_component().version.GetString().c_str()); |
| 287 if (!brand.empty()) |
| 288 base::StringAppendF(&app, " brand=\"%s\"", brand.c_str()); |
| 289 if (component.on_demand()) |
| 290 base::StringAppendF(&app, " installsource=\"ondemand\""); |
| 291 for (const auto& attr : installer_attributes) { |
| 292 base::StringAppendF(&app, " %s=\"%s\"", attr.first.c_str(), |
| 293 attr.second.c_str()); |
| 294 } |
| 295 const std::string cohort = metadata->GetCohort(component.id()); |
| 296 const std::string cohort_name = metadata->GetCohortName(component.id()); |
| 297 const std::string cohort_hint = metadata->GetCohortHint(component.id()); |
| 298 if (!cohort.empty()) |
| 299 base::StringAppendF(&app, " cohort=\"%s\"", cohort.c_str()); |
| 300 if (!cohort_name.empty()) |
| 301 base::StringAppendF(&app, " cohortname=\"%s\"", cohort_name.c_str()); |
| 302 if (!cohort_hint.empty()) |
| 303 base::StringAppendF(&app, " cohorthint=\"%s\"", cohort_hint.c_str()); |
| 304 base::StringAppendF(&app, ">"); |
| 305 |
| 306 base::StringAppendF(&app, "<updatecheck"); |
| 307 if (component.crx_component() |
| 308 .supports_group_policy_enable_component_updates && |
| 309 !enabled_component_updates) { |
| 310 base::StringAppendF(&app, " updatedisabled=\"true\""); |
| 311 } |
| 312 base::StringAppendF(&app, "/>"); |
| 313 |
| 314 base::StringAppendF(&app, "<ping rd=\"%d\" ping_freshness=\"%s\"/>", |
| 315 metadata->GetDateLastRollCall(component.id()), |
| 316 metadata->GetPingFreshness(component.id()).c_str()); |
| 317 if (!component.crx_component().fingerprint.empty()) { |
| 318 base::StringAppendF(&app, |
| 319 "<packages>" |
| 320 "<package fp=\"%s\"/>" |
| 321 "</packages>", |
| 322 component.crx_component().fingerprint.c_str()); |
| 323 } |
| 324 base::StringAppendF(&app, "</app>"); |
| 325 app_elements.append(app); |
| 326 VLOG(1) << "Appending to update request: " << app; |
| 327 } |
| 328 |
| 329 // Include the updater state in the update check request. |
| 330 return BuildProtocolRequest( |
| 331 config.GetProdId(), config.GetBrowserVersion().GetString(), |
| 332 config.GetChannel(), config.GetLang(), config.GetOSLongName(), |
| 333 config.GetDownloadPreference(), app_elements, additional_attributes, |
| 334 updater_state_attributes); |
| 335 } |
| 336 |
| 337 std::string BuildPing(const Configurator& config, const Component& component) { |
| 338 const char app_element_format[] = |
| 339 "<app appid=\"%s\" version=\"%s\" nextversion=\"%s\">" |
| 340 "%s" |
| 341 "%s" |
| 342 "</app>"; |
| 343 |
| 344 std::string ping_event; |
| 345 switch (component.state()) { |
| 346 case ComponentState::kUpdateError: // Fall through. |
| 347 case ComponentState::kUpdated: |
| 348 ping_event = BuildUpdateCompleteEventElement(component); |
| 349 break; |
| 350 case ComponentState::kUninstalled: |
| 351 ping_event = BuildUninstalledEventElement(component); |
| 352 break; |
| 353 default: |
| 354 NOTREACHED(); |
| 355 break; |
| 356 } |
| 357 |
| 358 const std::string app_element(base::StringPrintf( |
| 359 app_element_format, |
| 360 component.id().c_str(), // "appid" |
| 361 component.previous_version().GetString().c_str(), // "version" |
| 362 component.next_version().GetString().c_str(), // "nextversion" |
| 363 ping_event.c_str(), // ping event |
| 364 BuildDownloadCompleteEventElements(component) |
| 365 .c_str())); // download events |
| 366 |
| 367 // The ping request does not include any updater state. |
| 368 return BuildProtocolRequest( |
| 369 config.GetProdId(), config.GetBrowserVersion().GetString(), |
| 370 config.GetChannel(), config.GetLang(), config.GetOSLongName(), |
| 371 config.GetDownloadPreference(), app_element, "", nullptr); |
| 372 } |
| 373 |
| 374 } // namespace update_client |
OLD | NEW |