| OLD | NEW |
| (Empty) |
| 1 // Copyright 2007-2010 Google Inc. | |
| 2 // | |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
| 4 // you may not use this file except in compliance with the License. | |
| 5 // You may obtain a copy of the License at | |
| 6 // | |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 // | |
| 9 // Unless required by applicable law or agreed to in writing, software | |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, | |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 12 // See the License for the specific language governing permissions and | |
| 13 // limitations under the License. | |
| 14 // ======================================================================== | |
| 15 | |
| 16 #include "omaha/common/xml_parser.h" | |
| 17 #include <stdlib.h> | |
| 18 #include <vector> | |
| 19 #include "base/basictypes.h" | |
| 20 #include "omaha/base/constants.h" | |
| 21 #include "omaha/base/error.h" | |
| 22 #include "omaha/base/string.h" | |
| 23 #include "omaha/base/utils.h" | |
| 24 #include "omaha/base/xml_utils.h" | |
| 25 #include "omaha/common/config_manager.h" | |
| 26 #include "omaha/common/goopdate_utils.h" | |
| 27 #include "omaha/common/update_request.h" | |
| 28 #include "omaha/common/update_response.h" | |
| 29 #include "omaha/common/xml_const.h" | |
| 30 | |
| 31 namespace omaha { | |
| 32 | |
| 33 namespace xml { | |
| 34 | |
| 35 namespace { | |
| 36 | |
| 37 // Helper structure similar with an std::pair but without a constructor. | |
| 38 // Instance of it can be stored in arrays. | |
| 39 template <typename Type1, typename Type2> | |
| 40 struct Tuple { | |
| 41 Type1 first; | |
| 42 Type2 second; | |
| 43 }; | |
| 44 | |
| 45 // Converts a string to the SuccessfulInstallAction enum. | |
| 46 HRESULT ConvertStringToSuccessfulInstallAction( | |
| 47 const CString& str, | |
| 48 SuccessfulInstallAction* successful_install_action) { | |
| 49 ASSERT1(successful_install_action); | |
| 50 | |
| 51 const Tuple<const TCHAR*, SuccessfulInstallAction> tuples[] = { | |
| 52 { xml::value::kSuccessActionDefault, | |
| 53 SUCCESS_ACTION_DEFAULT }, | |
| 54 | |
| 55 { xml::value::kSuccessActionExitSilently, | |
| 56 SUCCESS_ACTION_EXIT_SILENTLY }, | |
| 57 | |
| 58 { xml::value::kSuccessActionExitSilentlyOnLaunchCmd, | |
| 59 SUCCESS_ACTION_EXIT_SILENTLY_ON_LAUNCH_CMD }, | |
| 60 }; | |
| 61 | |
| 62 if (str.IsEmpty()) { | |
| 63 *successful_install_action = SUCCESS_ACTION_DEFAULT; | |
| 64 return S_OK; | |
| 65 } | |
| 66 | |
| 67 for (size_t i = 0; i != arraysize(tuples); ++i) { | |
| 68 if (str.CompareNoCase(tuples[i].first) == 0) { | |
| 69 *successful_install_action = tuples[i].second; | |
| 70 return S_OK; | |
| 71 } | |
| 72 } | |
| 73 | |
| 74 // Using the default action allows Omaha to be forward-compatible with | |
| 75 // new SuccessActions, meaning older versions will not fail if a config | |
| 76 // uses a new action. | |
| 77 ASSERT(false, (_T("[Unrecognized success action][%s]"), str)); | |
| 78 *successful_install_action = SUCCESS_ACTION_DEFAULT; | |
| 79 return S_OK; | |
| 80 } | |
| 81 | |
| 82 HRESULT ConvertStringToInstallEvent( | |
| 83 const CString& str, | |
| 84 InstallAction::InstallEvent* install_event) { | |
| 85 ASSERT1(install_event); | |
| 86 | |
| 87 const Tuple<const TCHAR*, InstallAction::InstallEvent> tuples[] = { | |
| 88 {xml::value::kPreinstall, InstallAction::kPreInstall}, | |
| 89 {xml::value::kInstall, InstallAction::kInstall}, | |
| 90 {xml::value::kUpdate, InstallAction::kUpdate}, | |
| 91 {xml::value::kPostinstall, InstallAction::kPostInstall}, | |
| 92 }; | |
| 93 | |
| 94 for (size_t i = 0; i != arraysize(tuples); ++i) { | |
| 95 if (str.CompareNoCase(tuples[i].first) == 0) { | |
| 96 *install_event = tuples[i].second; | |
| 97 return S_OK; | |
| 98 } | |
| 99 } | |
| 100 | |
| 101 return E_INVALIDARG; | |
| 102 } | |
| 103 | |
| 104 // Returns S_OK if each child element of the node is any of the elements | |
| 105 // provided as an argument. This is useful to detect if the element contains | |
| 106 // only known children. | |
| 107 // TODO(omaha): implement. | |
| 108 HRESULT AreChildrenAnyOf(IXMLDOMNode* node, | |
| 109 const std::vector<const TCHAR*>& element_names) { | |
| 110 UNREFERENCED_PARAMETER(node); | |
| 111 UNREFERENCED_PARAMETER(element_names); | |
| 112 | |
| 113 return S_OK; | |
| 114 } | |
| 115 | |
| 116 // TODO(omaha): implement. | |
| 117 HRESULT HasNoChildren(IXMLDOMNode* node) { | |
| 118 UNREFERENCED_PARAMETER(node); | |
| 119 return S_OK; | |
| 120 } | |
| 121 | |
| 122 // Verify that the protocol version is understood. We accept | |
| 123 // all version numbers where major version is the same as kExpectedVersion | |
| 124 // which are greater than or equal to kExpectedVersion. In other words, | |
| 125 // we handle future minor version number increases which should be | |
| 126 // compatible. | |
| 127 HRESULT VerifyProtocolCompatibility(const CString& actual_version, | |
| 128 const CString& expected_version) { | |
| 129 if (_tcscmp(actual_version, expected_version) != 0) { | |
| 130 const double version = String_StringToDouble(actual_version); | |
| 131 const double expected = String_StringToDouble(expected_version); | |
| 132 if (expected > version) { | |
| 133 return GOOPDATEXML_E_XMLVERSION; | |
| 134 } | |
| 135 const int version_major = static_cast<int>(version); | |
| 136 const int expected_major = static_cast<int>(expected); | |
| 137 if (version_major != expected_major) { | |
| 138 return GOOPDATEXML_E_XMLVERSION; | |
| 139 } | |
| 140 } | |
| 141 return S_OK; | |
| 142 } | |
| 143 | |
| 144 } // namespace | |
| 145 | |
| 146 CString ConvertProcessorArchitectureToString(DWORD arch) { | |
| 147 switch (arch) { | |
| 148 case PROCESSOR_ARCHITECTURE_INTEL: | |
| 149 return xml::value::kArchIntel; | |
| 150 | |
| 151 case PROCESSOR_ARCHITECTURE_AMD64: | |
| 152 return xml::value::kArchAmd64; | |
| 153 | |
| 154 default: | |
| 155 ASSERT1(false); | |
| 156 return xml::value::kArchUnknown; | |
| 157 }; | |
| 158 } | |
| 159 | |
| 160 // The ElementHandler classes should also be in an anonymous namespace but | |
| 161 // the base class cannot be because it is used in the header file. | |
| 162 | |
| 163 // Defines the base class of a hierarchy that deals with validating and | |
| 164 // parsing of a single node element in the dom. The implementation uses | |
| 165 // the template method design pattern. | |
| 166 class ElementHandler { | |
| 167 public: | |
| 168 ElementHandler() {} | |
| 169 virtual ~ElementHandler() {} | |
| 170 | |
| 171 HRESULT Handle(IXMLDOMNode* node, response::Response* response) { | |
| 172 ASSERT1(node); | |
| 173 ASSERT1(response); | |
| 174 | |
| 175 HRESULT hr = Validate(node); | |
| 176 if (FAILED(hr)) { | |
| 177 return hr; | |
| 178 } | |
| 179 | |
| 180 hr = Parse(node, response); | |
| 181 if (FAILED(hr)) { | |
| 182 return hr; | |
| 183 } | |
| 184 | |
| 185 return S_OK; | |
| 186 } | |
| 187 | |
| 188 private: | |
| 189 // Validates a node and returns S_OK in case of success. | |
| 190 virtual HRESULT Validate(IXMLDOMNode* node) { | |
| 191 UNREFERENCED_PARAMETER(node); | |
| 192 return S_OK; | |
| 193 } | |
| 194 | |
| 195 // Parses the node and stores its values in the response. | |
| 196 virtual HRESULT Parse(IXMLDOMNode* node, response::Response* response) { | |
| 197 UNREFERENCED_PARAMETER(node); | |
| 198 UNREFERENCED_PARAMETER(response); | |
| 199 return S_OK; | |
| 200 } | |
| 201 | |
| 202 DISALLOW_COPY_AND_ASSIGN(ElementHandler); | |
| 203 }; | |
| 204 | |
| 205 | |
| 206 // Parses 'response'. | |
| 207 class ResponseElementHandler : public ElementHandler { | |
| 208 public: | |
| 209 static ElementHandler* Create() { return new ResponseElementHandler; } | |
| 210 | |
| 211 private: | |
| 212 virtual HRESULT Parse(IXMLDOMNode* node, response::Response* response) { | |
| 213 HRESULT hr = ReadStringAttribute(node, | |
| 214 xml::attribute::kProtocol, | |
| 215 &response->protocol); | |
| 216 if (FAILED(hr)) { | |
| 217 return hr; | |
| 218 } | |
| 219 hr = VerifyProtocolCompatibility(response->protocol, xml::value::kVersion3); | |
| 220 if (FAILED(hr)) { | |
| 221 return hr; | |
| 222 } | |
| 223 | |
| 224 return S_OK; | |
| 225 } | |
| 226 }; | |
| 227 | |
| 228 // Parses 'app'. | |
| 229 class AppElementHandler : public ElementHandler { | |
| 230 public: | |
| 231 static ElementHandler* Create() { return new AppElementHandler; } | |
| 232 | |
| 233 private: | |
| 234 virtual HRESULT Parse(IXMLDOMNode* node, response::Response* response) { | |
| 235 response::App app; | |
| 236 | |
| 237 HRESULT hr = ReadStringAttribute(node, xml::attribute::kAppId, &app.appid); | |
| 238 if (FAILED(hr)) { | |
| 239 return hr; | |
| 240 } | |
| 241 | |
| 242 hr = ReadStringAttribute(node, xml::attribute::kStatus, &app.status); | |
| 243 if (FAILED(hr)) { | |
| 244 return hr; | |
| 245 } | |
| 246 | |
| 247 // At present, the server may omit the following optional attributes if the | |
| 248 // contents would be empty strings. Guard these reads appropriately. | |
| 249 // TODO(omaha3): If we adapt the server to send an empty string for these | |
| 250 // attributes, we can remove these checks. | |
| 251 if (HasAttribute(node, xml::attribute::kExperiments)) { | |
| 252 hr = ReadStringAttribute(node, | |
| 253 xml::attribute::kExperiments, | |
| 254 &app.experiments); | |
| 255 if (FAILED(hr)) { | |
| 256 return hr; | |
| 257 } | |
| 258 } | |
| 259 | |
| 260 response->apps.push_back(app); | |
| 261 return S_OK; | |
| 262 } | |
| 263 }; | |
| 264 | |
| 265 | |
| 266 // Parses 'updatecheck'. | |
| 267 class UpdateCheckElementHandler : public ElementHandler { | |
| 268 public: | |
| 269 static ElementHandler* Create() { return new UpdateCheckElementHandler; } | |
| 270 | |
| 271 private: | |
| 272 virtual HRESULT Parse(IXMLDOMNode* node, response::Response* response) { | |
| 273 response::UpdateCheck& update_check = response->apps.back().update_check; | |
| 274 | |
| 275 ReadStringAttribute(node, | |
| 276 xml::attribute::kTTToken, | |
| 277 &update_check.tt_token); | |
| 278 | |
| 279 ReadStringAttribute(node, | |
| 280 xml::attribute::kErrorUrl, | |
| 281 &update_check.error_url); | |
| 282 | |
| 283 return ReadStringAttribute(node, | |
| 284 xml::attribute::kStatus, | |
| 285 &update_check.status); | |
| 286 } | |
| 287 }; | |
| 288 | |
| 289 | |
| 290 // Parses 'urls'. | |
| 291 class UrlsElementHandler : public ElementHandler { | |
| 292 public: | |
| 293 static ElementHandler* Create() { return new UrlsElementHandler; } | |
| 294 }; | |
| 295 | |
| 296 | |
| 297 // Parses 'url'. | |
| 298 class UrlElementHandler : public ElementHandler { | |
| 299 public: | |
| 300 static ElementHandler* Create() { return new UrlElementHandler; } | |
| 301 | |
| 302 private: | |
| 303 virtual HRESULT Parse(IXMLDOMNode* node, response::Response* response) { | |
| 304 CString url; | |
| 305 HRESULT hr = ReadStringAttribute(node, xml::attribute::kCodebase, &url); | |
| 306 if (FAILED(hr)) { | |
| 307 return hr; | |
| 308 } | |
| 309 | |
| 310 response::UpdateCheck& update_check = response->apps.back().update_check; | |
| 311 update_check.urls.push_back(url); | |
| 312 | |
| 313 return S_OK; | |
| 314 } | |
| 315 }; | |
| 316 | |
| 317 | |
| 318 // Parses 'manifest'. | |
| 319 class ManifestElementHandler : public ElementHandler { | |
| 320 public: | |
| 321 static ElementHandler* Create() { return new ManifestElementHandler; } | |
| 322 | |
| 323 private: | |
| 324 virtual HRESULT Parse(IXMLDOMNode* node, response::Response* response) { | |
| 325 InstallManifest& install_manifest = | |
| 326 response->apps.back().update_check.install_manifest; | |
| 327 // TODO(omaha3): Uncomment when version becomes a required value for | |
| 328 // version 2 configs. | |
| 329 /*return*/ ReadStringAttribute(node, | |
| 330 xml::attribute::kVersion, | |
| 331 &install_manifest.version); | |
| 332 return S_OK; | |
| 333 } | |
| 334 }; | |
| 335 | |
| 336 | |
| 337 // Parses 'packages'. | |
| 338 class PackagesElementHandler : public ElementHandler { | |
| 339 public: | |
| 340 static ElementHandler* Create() { return new PackagesElementHandler; } | |
| 341 }; | |
| 342 | |
| 343 | |
| 344 | |
| 345 // Parses 'package'. | |
| 346 class PackageElementHandler : public ElementHandler { | |
| 347 public: | |
| 348 static ElementHandler* Create() { return new PackageElementHandler; } | |
| 349 | |
| 350 private: | |
| 351 virtual HRESULT Parse(IXMLDOMNode* node, response::Response* response) { | |
| 352 InstallPackage install_package; | |
| 353 | |
| 354 HRESULT hr = ReadStringAttribute(node, | |
| 355 xml::attribute::kName, | |
| 356 &install_package.name); | |
| 357 if (FAILED(hr)) { | |
| 358 return hr; | |
| 359 } | |
| 360 | |
| 361 install_package.is_required = true; | |
| 362 hr = ReadBooleanAttribute(node, | |
| 363 xml::attribute::kRequired, | |
| 364 &install_package.is_required); | |
| 365 if (FAILED(hr)) { | |
| 366 return hr; | |
| 367 } | |
| 368 | |
| 369 hr = ReadIntAttribute(node, xml::attribute::kSize, &install_package.size); | |
| 370 if (FAILED(hr)) { | |
| 371 return hr; | |
| 372 } | |
| 373 | |
| 374 hr = ReadStringAttribute(node, | |
| 375 xml::attribute::kHash, | |
| 376 &install_package.hash); | |
| 377 if (FAILED(hr)) { | |
| 378 return hr; | |
| 379 } | |
| 380 | |
| 381 InstallManifest& install_manifest = | |
| 382 response->apps.back().update_check.install_manifest; | |
| 383 install_manifest.packages.push_back(install_package); | |
| 384 | |
| 385 return S_OK; | |
| 386 } | |
| 387 }; | |
| 388 | |
| 389 // Parses 'actions'. | |
| 390 class ActionsElementHandler : public ElementHandler { | |
| 391 public: | |
| 392 static ElementHandler* Create() { return new ActionsElementHandler; } | |
| 393 }; | |
| 394 | |
| 395 | |
| 396 // Parses 'action'. | |
| 397 class ActionElementHandler : public ElementHandler { | |
| 398 public: | |
| 399 static ElementHandler* Create() { return new ActionElementHandler; } | |
| 400 | |
| 401 private: | |
| 402 virtual HRESULT Parse(IXMLDOMNode* node, response::Response* response) { | |
| 403 InstallAction install_action; | |
| 404 | |
| 405 CString event; | |
| 406 HRESULT hr = ReadStringAttribute(node, xml::attribute::kEvent, &event); | |
| 407 if (FAILED(hr)) { | |
| 408 return hr; | |
| 409 } | |
| 410 hr = ConvertStringToInstallEvent(event, &install_action.install_event); | |
| 411 if (FAILED(hr)) { | |
| 412 return hr; | |
| 413 } | |
| 414 | |
| 415 ReadStringAttribute(node, | |
| 416 xml::attribute::kRun, | |
| 417 &install_action.program_to_run); | |
| 418 ReadStringAttribute(node, | |
| 419 xml::attribute::kArguments, | |
| 420 &install_action.program_arguments); | |
| 421 | |
| 422 ReadStringAttribute(node, | |
| 423 xml::attribute::kSuccessUrl, | |
| 424 &install_action.success_url); | |
| 425 | |
| 426 ReadBooleanAttribute(node, | |
| 427 xml::attribute::kTerminateAllBrowsers, | |
| 428 &install_action.terminate_all_browsers); | |
| 429 | |
| 430 CString success_action; | |
| 431 ReadStringAttribute(node, | |
| 432 xml::attribute::kSuccessAction, | |
| 433 &success_action); | |
| 434 ConvertStringToSuccessfulInstallAction(success_action, | |
| 435 &install_action.success_action); | |
| 436 | |
| 437 InstallManifest& install_manifest = | |
| 438 response->apps.back().update_check.install_manifest; | |
| 439 install_manifest.install_actions.push_back(install_action); | |
| 440 | |
| 441 return S_OK; | |
| 442 } | |
| 443 }; | |
| 444 | |
| 445 | |
| 446 // Parses 'data'. | |
| 447 class DataElementHandler : public ElementHandler { | |
| 448 public: | |
| 449 static ElementHandler* Create() { return new DataElementHandler; } | |
| 450 | |
| 451 private: | |
| 452 virtual HRESULT Parse(IXMLDOMNode* node, response::Response* response) { | |
| 453 response->apps.back().data.push_back(response::Data()); | |
| 454 response::Data& data = response->apps.back().data.back(); | |
| 455 | |
| 456 HRESULT hr = ReadStringAttribute(node, | |
| 457 xml::attribute::kStatus, | |
| 458 &data.status); | |
| 459 if (FAILED(hr)) { | |
| 460 return hr; | |
| 461 } | |
| 462 | |
| 463 CString data_name; | |
| 464 hr = ReadStringAttribute(node, xml::attribute::kName, &data_name); | |
| 465 if (FAILED(hr)) { | |
| 466 return hr; | |
| 467 } | |
| 468 | |
| 469 if (xml::value::kInstallData != data_name) { | |
| 470 return E_UNEXPECTED; | |
| 471 } | |
| 472 | |
| 473 hr = ReadStringAttribute(node, | |
| 474 xml::attribute::kIndex, | |
| 475 &data.install_data_index); | |
| 476 if (FAILED(hr)) { | |
| 477 return hr; | |
| 478 } | |
| 479 | |
| 480 if (data.status != kResponseStatusOkValue) { | |
| 481 // There is no data, so do not try to read it. | |
| 482 return S_OK; | |
| 483 } | |
| 484 | |
| 485 return ReadStringValue(node, &data.install_data); | |
| 486 } | |
| 487 }; | |
| 488 | |
| 489 | |
| 490 // Parses 'ping'. | |
| 491 class PingElementHandler : public ElementHandler { | |
| 492 public: | |
| 493 static ElementHandler* Create() { return new PingElementHandler; } | |
| 494 | |
| 495 private: | |
| 496 virtual HRESULT Parse(IXMLDOMNode* node, response::Response* response) { | |
| 497 response::Ping& ping = response->apps.back().ping; | |
| 498 ReadStringAttribute(node, xml::attribute::kStatus, &ping.status); | |
| 499 ASSERT1(ping.status == kResponseStatusOkValue); | |
| 500 return S_OK; | |
| 501 } | |
| 502 }; | |
| 503 | |
| 504 | |
| 505 // Parses 'event'. | |
| 506 class EventElementHandler : public ElementHandler { | |
| 507 public: | |
| 508 static ElementHandler* Create() { return new EventElementHandler; } | |
| 509 | |
| 510 private: | |
| 511 virtual HRESULT Parse(IXMLDOMNode* node, response::Response* response) { | |
| 512 response::Event event; | |
| 513 ReadStringAttribute(node, xml::attribute::kStatus, &event.status); | |
| 514 ASSERT1(event.status == kResponseStatusOkValue); | |
| 515 response::App& app = response->apps.back(); | |
| 516 app.events.push_back(event); | |
| 517 return S_OK; | |
| 518 } | |
| 519 }; | |
| 520 | |
| 521 // Parses 'daystart'. | |
| 522 class DayStartElementHandler : public ElementHandler { | |
| 523 public: | |
| 524 static ElementHandler* Create() { return new DayStartElementHandler; } | |
| 525 | |
| 526 private: | |
| 527 virtual HRESULT Parse(IXMLDOMNode* node, response::Response* response) { | |
| 528 ReadIntAttribute(node, | |
| 529 xml::attribute::kElapsedSeconds, | |
| 530 &response->day_start.elapsed_seconds); | |
| 531 return S_OK; | |
| 532 } | |
| 533 }; | |
| 534 | |
| 535 namespace v2 { | |
| 536 | |
| 537 namespace element { | |
| 538 | |
| 539 const TCHAR* const kGUpdate = _T("gupdate"); | |
| 540 | |
| 541 } // namespace element | |
| 542 | |
| 543 namespace attributev2 { | |
| 544 | |
| 545 // Some v2 offline manifests were incorrectly authored as 'Version' with a | |
| 546 // capital 'V'. | |
| 547 const TCHAR* const kVersionProperCased = _T("Version"); | |
| 548 | |
| 549 } // namespace attribute | |
| 550 | |
| 551 namespace value { | |
| 552 | |
| 553 const TCHAR* const kVersion2 = _T("2.0"); | |
| 554 | |
| 555 } // namespace value | |
| 556 | |
| 557 // Parses Omaha v2 'gupdate'. | |
| 558 class GUpdateElementHandler : public ElementHandler { | |
| 559 public: | |
| 560 static ElementHandler* Create() { return new GUpdateElementHandler; } | |
| 561 | |
| 562 private: | |
| 563 virtual HRESULT Parse(IXMLDOMNode* node, response::Response* response) { | |
| 564 HRESULT hr = ReadStringAttribute(node, | |
| 565 xml::attribute::kProtocol, | |
| 566 &response->protocol); | |
| 567 if (FAILED(hr)) { | |
| 568 return hr; | |
| 569 } | |
| 570 return VerifyProtocolCompatibility(response->protocol, | |
| 571 value::kVersion2); | |
| 572 } | |
| 573 }; | |
| 574 | |
| 575 // Parses Omaha v2 'updatecheck'. | |
| 576 class UpdateCheckElementHandler : public ElementHandler { | |
| 577 public: | |
| 578 static ElementHandler* Create() { return new UpdateCheckElementHandler; } | |
| 579 | |
| 580 private: | |
| 581 virtual HRESULT Parse(IXMLDOMNode* node, response::Response* response) { | |
| 582 response::UpdateCheck& update_check = response->apps.back().update_check; | |
| 583 | |
| 584 HRESULT hr = ReadStringAttribute(node, | |
| 585 xml::attribute::kStatus, | |
| 586 &update_check.status); | |
| 587 if (FAILED(hr)) { | |
| 588 return hr; | |
| 589 } | |
| 590 | |
| 591 if (update_check.status.CompareNoCase(kResponseStatusOkValue)) { | |
| 592 return S_OK; | |
| 593 } | |
| 594 | |
| 595 InstallManifest& install_manifest = update_check.install_manifest; | |
| 596 if (FAILED(ReadStringAttribute(node, | |
| 597 xml::attribute::kVersion, | |
| 598 &install_manifest.version))) { | |
| 599 ReadStringAttribute(node, | |
| 600 v2::attributev2::kVersionProperCased, | |
| 601 &install_manifest.version); | |
| 602 } | |
| 603 | |
| 604 CString url; | |
| 605 hr = ReadStringAttribute(node, xml::attribute::kCodebase, &url); | |
| 606 if (FAILED(hr)) { | |
| 607 return hr; | |
| 608 } | |
| 609 | |
| 610 int start_file_name_idx = url.ReverseFind(_T('/')); | |
| 611 if (start_file_name_idx <= 0) { | |
| 612 return GOOPDATEDOWNLOAD_E_INVALID_PATH; | |
| 613 } | |
| 614 CString base_url = url.Left(start_file_name_idx + 1); | |
| 615 update_check.urls.push_back(base_url); | |
| 616 | |
| 617 CString package_name = url.Right(url.GetLength() - start_file_name_idx - 1); | |
| 618 if (package_name.IsEmpty()) { | |
| 619 return GOOPDATEDOWNLOAD_E_FILE_NAME_EMPTY; | |
| 620 } | |
| 621 | |
| 622 InstallPackage install_package; | |
| 623 install_package.name = package_name; | |
| 624 install_package.is_required = true; | |
| 625 hr = ReadIntAttribute(node, xml::attribute::kSize, &install_package.size); | |
| 626 if (FAILED(hr)) { | |
| 627 return hr; | |
| 628 } | |
| 629 hr = ReadStringAttribute(node, | |
| 630 xml::attribute::kHash, | |
| 631 &install_package.hash); | |
| 632 if (FAILED(hr)) { | |
| 633 return hr; | |
| 634 } | |
| 635 | |
| 636 install_manifest.packages.push_back(install_package); | |
| 637 | |
| 638 InstallAction install_action; | |
| 639 install_action.install_event = InstallAction::kInstall; | |
| 640 install_action.program_to_run = package_name; | |
| 641 ReadStringAttribute(node, | |
| 642 xml::attribute::kArguments, | |
| 643 &install_action.program_arguments); | |
| 644 | |
| 645 install_manifest.install_actions.push_back(install_action); | |
| 646 | |
| 647 InstallAction post_install_action; | |
| 648 if (SUCCEEDED(ParsePostInstallActions(node, &post_install_action))) { | |
| 649 install_manifest.install_actions.push_back(post_install_action); | |
| 650 } | |
| 651 | |
| 652 return S_OK; | |
| 653 } | |
| 654 | |
| 655 HRESULT ParsePostInstallActions(IXMLDOMNode* node, | |
| 656 InstallAction* post_install_action) { | |
| 657 InstallAction install_action; | |
| 658 CString success_action; | |
| 659 if (FAILED(ReadStringAttribute(node, | |
| 660 xml::attribute::kSuccessAction, | |
| 661 &success_action)) && | |
| 662 FAILED(ReadStringAttribute(node, | |
| 663 xml::attribute::kSuccessUrl, | |
| 664 &install_action.success_url)) && | |
| 665 FAILED(ReadBooleanAttribute(node, | |
| 666 xml::attribute::kTerminateAllBrowsers, | |
| 667 &install_action.terminate_all_browsers))) { | |
| 668 return HRESULT_FROM_WIN32(ERROR_NOT_FOUND); | |
| 669 } | |
| 670 | |
| 671 install_action.install_event = InstallAction::kPostInstall; | |
| 672 ReadStringAttribute(node, xml::attribute::kSuccessAction, &success_action); | |
| 673 ConvertStringToSuccessfulInstallAction(success_action, | |
| 674 &install_action.success_action); | |
| 675 ReadStringAttribute(node, | |
| 676 xml::attribute::kSuccessUrl, | |
| 677 &install_action.success_url); | |
| 678 ReadBooleanAttribute(node, | |
| 679 xml::attribute::kTerminateAllBrowsers, | |
| 680 &install_action.terminate_all_browsers); | |
| 681 *post_install_action = install_action; | |
| 682 return S_OK; | |
| 683 } | |
| 684 }; | |
| 685 | |
| 686 } // namespace v2 | |
| 687 | |
| 688 XmlParser::XmlParser() {} | |
| 689 | |
| 690 void XmlParser::InitializeElementHandlers() { | |
| 691 const Tuple<const TCHAR*, ElementHandler* (*)()> tuples[] = { | |
| 692 {xml::element::kAction, &ActionElementHandler::Create}, | |
| 693 {xml::element::kActions, &ActionsElementHandler::Create}, | |
| 694 {xml::element::kApp, &AppElementHandler::Create}, | |
| 695 {xml::element::kData, &DataElementHandler::Create}, | |
| 696 {xml::element::kDayStart, &DayStartElementHandler::Create}, | |
| 697 {xml::element::kEvent, &EventElementHandler::Create}, | |
| 698 {xml::element::kManifest, &ManifestElementHandler::Create}, | |
| 699 {xml::element::kPackage, &PackageElementHandler::Create}, | |
| 700 {xml::element::kPackages, &PackagesElementHandler::Create}, | |
| 701 {xml::element::kPing, &PingElementHandler::Create}, | |
| 702 {xml::element::kResponse, &ResponseElementHandler::Create}, | |
| 703 {xml::element::kUpdateCheck, &UpdateCheckElementHandler::Create}, | |
| 704 {xml::element::kUrl, &UrlElementHandler::Create}, | |
| 705 {xml::element::kUrls, &UrlsElementHandler::Create}, | |
| 706 }; | |
| 707 | |
| 708 for (size_t i = 0; i != arraysize(tuples); ++i) { | |
| 709 VERIFY1(element_handler_factory_.Register(tuples[i].first, | |
| 710 tuples[i].second)); | |
| 711 } | |
| 712 } | |
| 713 | |
| 714 // The AppElementHandler and the DataElementHandler are shared, because the | |
| 715 // format is identical between Omaha v2 and Omaha v3. We should make copies of | |
| 716 // the shared classes if these elements diverge between v2 and v3. The worst | |
| 717 // case scenario is that we break v2 compatibility if we do not do a | |
| 718 // copy-on-write, which might be acceptable. | |
| 719 void XmlParser::InitializeLegacyElementHandlers() { | |
| 720 const Tuple<const TCHAR*, ElementHandler* (*)()> tuples[] = { | |
| 721 {xml::element::kApp, &AppElementHandler::Create}, | |
| 722 {xml::element::kData, &DataElementHandler::Create}, | |
| 723 {v2::element::kGUpdate, &v2::GUpdateElementHandler::Create}, | |
| 724 {xml::element::kUpdateCheck, &v2::UpdateCheckElementHandler::Create}, | |
| 725 }; | |
| 726 | |
| 727 for (size_t i = 0; i != arraysize(tuples); ++i) { | |
| 728 VERIFY1(element_handler_factory_.Register(tuples[i].first, | |
| 729 tuples[i].second)); | |
| 730 } | |
| 731 } | |
| 732 | |
| 733 HRESULT XmlParser::SerializeRequest(const UpdateRequest& update_request, | |
| 734 CString* buffer) { | |
| 735 ASSERT1(buffer); | |
| 736 | |
| 737 XmlParser xml_parser; | |
| 738 | |
| 739 HRESULT hr = xml_parser.BuildDom(update_request.request()); | |
| 740 if (FAILED(hr)) { | |
| 741 return hr; | |
| 742 } | |
| 743 | |
| 744 hr = xml_parser.GetXml(buffer); | |
| 745 if (FAILED(hr)) { | |
| 746 return hr; | |
| 747 } | |
| 748 | |
| 749 return S_OK; | |
| 750 } | |
| 751 | |
| 752 HRESULT XmlParser::BuildDom(const request::Request& request) { | |
| 753 CORE_LOG(L3, (_T("[XmlParser::BuildDom]"))); | |
| 754 HRESULT hr = CoCreateSafeDOMDocument(&document_); | |
| 755 if (FAILED(hr)) { | |
| 756 return hr; | |
| 757 } | |
| 758 | |
| 759 request_ = &request; | |
| 760 | |
| 761 hr = BuildRequestElement(); | |
| 762 if (FAILED(hr)) { | |
| 763 return hr; | |
| 764 } | |
| 765 | |
| 766 return S_OK; | |
| 767 } | |
| 768 | |
| 769 // Extract the string out of the DOM, and add the initial processing | |
| 770 // instruction. The xml property of the document converts to the document from | |
| 771 // its original encoding to Unicode. As a result, the xml directive and the | |
| 772 // desired encoding must be explicitely set here. | |
| 773 HRESULT XmlParser::GetXml(CString* buffer) { | |
| 774 CORE_LOG(L3, (_T("[XmlParser::GetXml]"))); | |
| 775 | |
| 776 ASSERT1(buffer); | |
| 777 | |
| 778 CComBSTR xml_body; | |
| 779 HRESULT hr = document_->get_xml(&xml_body); | |
| 780 if (FAILED(hr)) { | |
| 781 return hr; | |
| 782 } | |
| 783 | |
| 784 *buffer = kXmlDirective; | |
| 785 *buffer += xml_body; | |
| 786 | |
| 787 // The xml string contains a CR LF pair at the end. | |
| 788 buffer->TrimRight(_T("\r\n")); | |
| 789 | |
| 790 return S_OK; | |
| 791 } | |
| 792 | |
| 793 | |
| 794 HRESULT XmlParser::BuildRequestElement() { | |
| 795 CORE_LOG(L3, (_T("[XmlParser::BuildRequestElement]"))); | |
| 796 | |
| 797 CComPtr<IXMLDOMNode> element; | |
| 798 HRESULT hr = CreateElementNode(xml::element::kRequest, _T(""), &element); | |
| 799 if (FAILED(hr)) { | |
| 800 return hr; | |
| 801 } | |
| 802 | |
| 803 ASSERT1(request_); | |
| 804 | |
| 805 // Add attributes to the top element: | |
| 806 // * protocol - protocol version | |
| 807 // * version - Omaha version | |
| 808 // * ismachine - is machine Omaha | |
| 809 // * installsource - install source | |
| 810 // * originurl - origin url, primarily set by Update3Web plugins | |
| 811 // * testsource - test source | |
| 812 // * requestid - unique request ID | |
| 813 // * periodoverridesec - override value for update check frequency | |
| 814 | |
| 815 hr = AddXMLAttributeNode(element, | |
| 816 kXmlNamespace, | |
| 817 xml::attribute::kProtocol, | |
| 818 request_->protocol_version); | |
| 819 if (FAILED(hr)) { | |
| 820 return hr; | |
| 821 } | |
| 822 | |
| 823 hr = AddXMLAttributeNode(element, | |
| 824 kXmlNamespace, | |
| 825 xml::attribute::kVersion, | |
| 826 request_->omaha_version); | |
| 827 if (FAILED(hr)) { | |
| 828 return hr; | |
| 829 } | |
| 830 | |
| 831 hr = AddXMLAttributeNode(element, | |
| 832 kXmlNamespace, | |
| 833 xml::attribute::kIsMachine, | |
| 834 request_->is_machine ? _T("1") : _T("0")); | |
| 835 if (FAILED(hr)) { | |
| 836 return hr; | |
| 837 } | |
| 838 | |
| 839 hr = AddXMLAttributeNode(element, | |
| 840 kXmlNamespace, | |
| 841 xml::attribute::kSessionId, | |
| 842 request_->session_id); | |
| 843 if (FAILED(hr)) { | |
| 844 return hr; | |
| 845 } | |
| 846 | |
| 847 if (!request_->uid.IsEmpty()) { | |
| 848 hr = AddXMLAttributeNode(element, | |
| 849 kXmlNamespace, | |
| 850 xml::attribute::kUserId, | |
| 851 request_->uid); | |
| 852 if (FAILED(hr)) { | |
| 853 return hr; | |
| 854 } | |
| 855 } | |
| 856 | |
| 857 if (!request_->install_source.IsEmpty()) { | |
| 858 hr = AddXMLAttributeNode(element, | |
| 859 kXmlNamespace, | |
| 860 xml::attribute::kInstallSource, | |
| 861 request_->install_source); | |
| 862 if (FAILED(hr)) { | |
| 863 return hr; | |
| 864 } | |
| 865 } | |
| 866 | |
| 867 if (!request_->origin_url.IsEmpty()) { | |
| 868 hr = AddXMLAttributeNode(element, | |
| 869 kXmlNamespace, | |
| 870 xml::attribute::kOriginURL, | |
| 871 request_->origin_url); | |
| 872 if (FAILED(hr)) { | |
| 873 return hr; | |
| 874 } | |
| 875 } | |
| 876 | |
| 877 if (!request_->test_source.IsEmpty()) { | |
| 878 hr = AddXMLAttributeNode(element, | |
| 879 kXmlNamespace, | |
| 880 xml::attribute::kTestSource, | |
| 881 request_->test_source); | |
| 882 if (FAILED(hr)) { | |
| 883 return hr; | |
| 884 } | |
| 885 } | |
| 886 | |
| 887 if (!request_->request_id.IsEmpty()) { | |
| 888 hr = AddXMLAttributeNode(element, | |
| 889 kXmlNamespace, | |
| 890 xml::attribute::kRequestId, | |
| 891 request_->request_id); | |
| 892 if (FAILED(hr)) { | |
| 893 return hr; | |
| 894 } | |
| 895 } | |
| 896 | |
| 897 if (request_->check_period_sec != -1) { | |
| 898 hr = AddXMLAttributeNode(element, | |
| 899 kXmlNamespace, | |
| 900 xml::attribute::kPeriodOverrideSec, | |
| 901 itostr(request_->check_period_sec)); | |
| 902 if (FAILED(hr)) { | |
| 903 return hr; | |
| 904 } | |
| 905 } | |
| 906 | |
| 907 hr = BuildOsElement(element); | |
| 908 if (FAILED(hr)) { | |
| 909 return hr; | |
| 910 } | |
| 911 | |
| 912 // Add the app element sequence to the request. | |
| 913 hr = BuildAppElement(element); | |
| 914 if (FAILED(hr)) { | |
| 915 return hr; | |
| 916 } | |
| 917 | |
| 918 // Add the request node to the document root. | |
| 919 CComPtr<IXMLDOMElement> element_node; | |
| 920 hr = element->QueryInterface(&element_node); | |
| 921 if (FAILED(hr)) { | |
| 922 return hr; | |
| 923 } | |
| 924 | |
| 925 hr = document_->putref_documentElement(element_node); | |
| 926 if (FAILED(hr)) { | |
| 927 return hr; | |
| 928 } | |
| 929 | |
| 930 return S_OK; | |
| 931 } | |
| 932 | |
| 933 HRESULT XmlParser::BuildOsElement(IXMLDOMNode* parent_node) { | |
| 934 CORE_LOG(L3, (_T("[XmlParser::BuildOsElement]"))); | |
| 935 | |
| 936 ASSERT1(parent_node); | |
| 937 ASSERT1(request_); | |
| 938 | |
| 939 CComPtr<IXMLDOMNode> element; | |
| 940 HRESULT hr = CreateElementNode(xml::element::kOs, _T(""), &element); | |
| 941 if (FAILED(hr)) { | |
| 942 return hr; | |
| 943 } | |
| 944 | |
| 945 hr = AddXMLAttributeNode(element, | |
| 946 kXmlNamespace, | |
| 947 xml::attribute::kPlatform, | |
| 948 request_->os.platform); | |
| 949 if (FAILED(hr)) { | |
| 950 return hr; | |
| 951 } | |
| 952 | |
| 953 hr = AddXMLAttributeNode(element, | |
| 954 kXmlNamespace, | |
| 955 xml::attribute::kVersion, | |
| 956 request_->os.version); | |
| 957 if (FAILED(hr)) { | |
| 958 return hr; | |
| 959 } | |
| 960 | |
| 961 hr = AddXMLAttributeNode(element, | |
| 962 kXmlNamespace, | |
| 963 xml::attribute::kServicePack, | |
| 964 request_->os.service_pack); | |
| 965 if (FAILED(hr)) { | |
| 966 return hr; | |
| 967 } | |
| 968 | |
| 969 hr = AddXMLAttributeNode(element, | |
| 970 kXmlNamespace, | |
| 971 xml::attribute::kArch, | |
| 972 request_->os.arch); | |
| 973 if (FAILED(hr)) { | |
| 974 return hr; | |
| 975 } | |
| 976 | |
| 977 hr = parent_node->appendChild(element, NULL); | |
| 978 if (FAILED(hr)) { | |
| 979 return hr; | |
| 980 } | |
| 981 | |
| 982 return S_OK; | |
| 983 } | |
| 984 | |
| 985 // Create and add request's to the requests node. | |
| 986 HRESULT XmlParser::BuildAppElement(IXMLDOMNode* parent_node) { | |
| 987 CORE_LOG(L3, (_T("[XmlParser::BuildAppElement]"))); | |
| 988 | |
| 989 ASSERT1(parent_node); | |
| 990 ASSERT1(request_); | |
| 991 | |
| 992 for (size_t i = 0; i < request_->apps.size(); ++i) { | |
| 993 const request::App& app = request_->apps[i]; | |
| 994 | |
| 995 CComPtr<IXMLDOMNode> element; | |
| 996 HRESULT hr = CreateElementNode(xml::element::kApp, _T(""), &element); | |
| 997 if (FAILED(hr)) { | |
| 998 return hr; | |
| 999 } | |
| 1000 | |
| 1001 ASSERT1(IsGuid(app.app_id)); | |
| 1002 hr = AddXMLAttributeNode(element, | |
| 1003 kXmlNamespace, | |
| 1004 xml::attribute::kAppId, | |
| 1005 app.app_id); | |
| 1006 if (FAILED(hr)) { | |
| 1007 return hr; | |
| 1008 } | |
| 1009 | |
| 1010 hr = AddXMLAttributeNode(element, | |
| 1011 kXmlNamespace, | |
| 1012 xml::attribute::kVersion, | |
| 1013 app.version); | |
| 1014 if (FAILED(hr)) { | |
| 1015 return hr; | |
| 1016 } | |
| 1017 | |
| 1018 hr = AddXMLAttributeNode(element, | |
| 1019 kXmlNamespace, | |
| 1020 xml::attribute::kNextVersion, | |
| 1021 app.next_version); | |
| 1022 if (FAILED(hr)) { | |
| 1023 return hr; | |
| 1024 } | |
| 1025 | |
| 1026 if (!app.ap.IsEmpty()) { | |
| 1027 hr = AddXMLAttributeNode(element, | |
| 1028 kXmlNamespace, | |
| 1029 xml::attribute::kAdditionalParameters, | |
| 1030 app.ap); | |
| 1031 if (FAILED(hr)) { | |
| 1032 return hr; | |
| 1033 } | |
| 1034 } | |
| 1035 | |
| 1036 hr = AddXMLAttributeNode(element, | |
| 1037 kXmlNamespace, | |
| 1038 xml::attribute::kLang, | |
| 1039 app.lang); | |
| 1040 if (FAILED(hr)) { | |
| 1041 return hr; | |
| 1042 } | |
| 1043 | |
| 1044 hr = AddXMLAttributeNode(element, | |
| 1045 kXmlNamespace, | |
| 1046 xml::attribute::kBrandCode, | |
| 1047 app.brand_code); | |
| 1048 if (FAILED(hr)) { | |
| 1049 return hr; | |
| 1050 } | |
| 1051 | |
| 1052 hr = AddXMLAttributeNode(element, | |
| 1053 kXmlNamespace, | |
| 1054 xml::attribute::kClientId, | |
| 1055 app.client_id); | |
| 1056 if (FAILED(hr)) { | |
| 1057 return hr; | |
| 1058 } | |
| 1059 | |
| 1060 // TODO(omaha3): Determine whether or not the server is able to accept an | |
| 1061 // empty string here. If so, remove this IsEmpty() check, and always emit. | |
| 1062 if (!app.experiments.IsEmpty()) { | |
| 1063 hr = AddXMLAttributeNode(element, | |
| 1064 kXmlNamespace, | |
| 1065 xml::attribute::kExperiments, | |
| 1066 app.experiments); | |
| 1067 if (FAILED(hr)) { | |
| 1068 return hr; | |
| 1069 } | |
| 1070 } | |
| 1071 | |
| 1072 // 0 seconds indicates unknown install time. A new install uses -1 days. | |
| 1073 if (app.install_time_diff_sec) { | |
| 1074 const int installed_full_days = | |
| 1075 static_cast<int>(app.install_time_diff_sec) / kSecondsPerDay; | |
| 1076 ASSERT1(installed_full_days >= 0 || installed_full_days == -1); | |
| 1077 hr = AddXMLAttributeNode(element, | |
| 1078 kXmlNamespace, | |
| 1079 xml::attribute::kInstalledAgeDays, | |
| 1080 itostr(installed_full_days)); | |
| 1081 if (FAILED(hr)) { | |
| 1082 return hr; | |
| 1083 } | |
| 1084 } | |
| 1085 | |
| 1086 if (!app.iid.IsEmpty() && app.iid != GuidToString(GUID_NULL)) { | |
| 1087 hr = AddXMLAttributeNode(element, | |
| 1088 kXmlNamespace, | |
| 1089 xml::attribute::kInstallationId, | |
| 1090 app.iid); | |
| 1091 if (FAILED(hr)) { | |
| 1092 return hr; | |
| 1093 } | |
| 1094 } | |
| 1095 | |
| 1096 hr = BuildUpdateCheckElement(app, element); | |
| 1097 if (FAILED(hr)) { | |
| 1098 return hr; | |
| 1099 } | |
| 1100 | |
| 1101 hr = BuildPingRequestElement(app, element); | |
| 1102 if (FAILED(hr)) { | |
| 1103 return hr; | |
| 1104 } | |
| 1105 | |
| 1106 hr = BuildDataElement(app, element); | |
| 1107 if (FAILED(hr)) { | |
| 1108 return hr; | |
| 1109 } | |
| 1110 | |
| 1111 hr = BuildDidRunElement(app, element); | |
| 1112 if (FAILED(hr)) { | |
| 1113 return hr; | |
| 1114 } | |
| 1115 | |
| 1116 hr = parent_node->appendChild(element, NULL); | |
| 1117 if (FAILED(hr)) { | |
| 1118 return hr; | |
| 1119 } | |
| 1120 } | |
| 1121 | |
| 1122 return S_OK; | |
| 1123 } | |
| 1124 | |
| 1125 HRESULT XmlParser::BuildUpdateCheckElement(const request::App& app, | |
| 1126 IXMLDOMNode* parent_node) { | |
| 1127 CORE_LOG(L3, (_T("[XmlParser::BuildUpdateCheckElement]"))); | |
| 1128 ASSERT1(parent_node); | |
| 1129 | |
| 1130 // Create a DOM element only if the update check member is valid. | |
| 1131 if (!app.update_check.is_valid) { | |
| 1132 return S_OK; | |
| 1133 } | |
| 1134 | |
| 1135 CComPtr<IXMLDOMNode> element; | |
| 1136 HRESULT hr = CreateElementNode(xml::element::kUpdateCheck, _T(""), &element); | |
| 1137 if (FAILED(hr)) { | |
| 1138 return hr; | |
| 1139 } | |
| 1140 | |
| 1141 if (app.update_check.is_update_disabled) { | |
| 1142 hr = AddXMLAttributeNode(element, | |
| 1143 kXmlNamespace, | |
| 1144 xml::attribute::kUpdateDisabled, | |
| 1145 xml::value::kTrue); | |
| 1146 if (FAILED(hr)) { | |
| 1147 return hr; | |
| 1148 } | |
| 1149 } | |
| 1150 | |
| 1151 if (!app.update_check.tt_token.IsEmpty()) { | |
| 1152 hr = AddXMLAttributeNode(element, | |
| 1153 kXmlNamespace, | |
| 1154 xml::attribute::kTTToken, | |
| 1155 app.update_check.tt_token); | |
| 1156 if (FAILED(hr)) { | |
| 1157 return hr; | |
| 1158 } | |
| 1159 } | |
| 1160 | |
| 1161 hr = parent_node->appendChild(element, NULL); | |
| 1162 if (FAILED(hr)) { | |
| 1163 return hr; | |
| 1164 } | |
| 1165 | |
| 1166 return S_OK; | |
| 1167 } | |
| 1168 | |
| 1169 // Ping elements are called "event" elements for legacy reasons. | |
| 1170 HRESULT XmlParser::BuildPingRequestElement(const request::App& app, | |
| 1171 IXMLDOMNode* parent_node) { | |
| 1172 CORE_LOG(L3, (_T("[XmlParser::BuildPingRequestElement]"))); | |
| 1173 ASSERT1(parent_node); | |
| 1174 | |
| 1175 // Create a DOM element only if there is are ping_events. | |
| 1176 if (app.ping_events.empty()) { | |
| 1177 return S_OK; | |
| 1178 } | |
| 1179 | |
| 1180 PingEventVector::const_iterator it; | |
| 1181 for (it = app.ping_events.begin(); it != app.ping_events.end(); ++it) { | |
| 1182 const PingEventPtr ping_event = *it; | |
| 1183 CComPtr<IXMLDOMNode> element; | |
| 1184 HRESULT hr = CreateElementNode(xml::element::kEvent, _T(""), &element); | |
| 1185 if (FAILED(hr)) { | |
| 1186 return hr; | |
| 1187 } | |
| 1188 | |
| 1189 hr = ping_event->ToXml(element); | |
| 1190 if (FAILED(hr)) { | |
| 1191 return hr; | |
| 1192 } | |
| 1193 | |
| 1194 hr = parent_node->appendChild(element, NULL); | |
| 1195 if (FAILED(hr)) { | |
| 1196 return hr; | |
| 1197 } | |
| 1198 } | |
| 1199 | |
| 1200 return S_OK; | |
| 1201 } | |
| 1202 | |
| 1203 HRESULT XmlParser::BuildDataElement(const request::App& app, | |
| 1204 IXMLDOMNode* parent_node) { | |
| 1205 CORE_LOG(L3, (_T("[XmlParser::BuildDataElement]"))); | |
| 1206 ASSERT1(parent_node); | |
| 1207 | |
| 1208 // Create a DOM element only if there is a data object. | |
| 1209 if (app.data.install_data_index.IsEmpty()) { | |
| 1210 return S_OK; | |
| 1211 } | |
| 1212 | |
| 1213 ASSERT1(app.update_check.is_valid); | |
| 1214 | |
| 1215 CComPtr<IXMLDOMNode> element; | |
| 1216 HRESULT hr = CreateElementNode(xml::element::kData, _T(""), &element); | |
| 1217 if (FAILED(hr)) { | |
| 1218 return hr; | |
| 1219 } | |
| 1220 | |
| 1221 hr = AddXMLAttributeNode(element, | |
| 1222 kXmlNamespace, | |
| 1223 xml::attribute::kName, | |
| 1224 xml::value::kInstallData); | |
| 1225 if (FAILED(hr)) { | |
| 1226 return hr; | |
| 1227 } | |
| 1228 | |
| 1229 hr = AddXMLAttributeNode(element, | |
| 1230 kXmlNamespace, | |
| 1231 xml::attribute::kIndex, | |
| 1232 app.data.install_data_index); | |
| 1233 if (FAILED(hr)) { | |
| 1234 return hr; | |
| 1235 } | |
| 1236 | |
| 1237 hr = parent_node->appendChild(element, NULL); | |
| 1238 if (FAILED(hr)) { | |
| 1239 return hr; | |
| 1240 } | |
| 1241 | |
| 1242 return S_OK; | |
| 1243 } | |
| 1244 | |
| 1245 HRESULT XmlParser::BuildDidRunElement(const request::App& app, | |
| 1246 IXMLDOMNode* parent_node) { | |
| 1247 CORE_LOG(L3, (_T("[XmlParser::BuildDidRunElement]"))); | |
| 1248 ASSERT1(parent_node); | |
| 1249 | |
| 1250 bool was_active = app.ping.active == ACTIVE_RUN; | |
| 1251 bool need_active = app.ping.active != ACTIVE_UNKNOWN; | |
| 1252 bool has_sent_a_today = app.ping.days_since_last_active_ping == 0; | |
| 1253 bool need_a = was_active && !has_sent_a_today; | |
| 1254 bool need_r = app.ping.days_since_last_roll_call != 0; | |
| 1255 | |
| 1256 // Create a DOM element only if the didrun object has actual state. | |
| 1257 if (!need_active && !need_a && !need_r) { | |
| 1258 return S_OK; | |
| 1259 } | |
| 1260 | |
| 1261 ASSERT1(app.update_check.is_valid); | |
| 1262 | |
| 1263 CComPtr<IXMLDOMNode> element; | |
| 1264 HRESULT hr = CreateElementNode(xml::element::kPing, _T(""), &element); | |
| 1265 if (FAILED(hr)) { | |
| 1266 return hr; | |
| 1267 } | |
| 1268 | |
| 1269 // TODO(omaha): Remove "active" attribute after transition. | |
| 1270 if (need_active) { | |
| 1271 const TCHAR* active_str(was_active ? _T("1") : _T("0")); | |
| 1272 hr = AddXMLAttributeNode(element, | |
| 1273 kXmlNamespace, | |
| 1274 xml::attribute::kActive, | |
| 1275 active_str); | |
| 1276 if (FAILED(hr)) { | |
| 1277 return hr; | |
| 1278 } | |
| 1279 } | |
| 1280 | |
| 1281 if (need_a) { | |
| 1282 hr = AddXMLAttributeNode(element, | |
| 1283 kXmlNamespace, | |
| 1284 xml::attribute::kDaysSinceLastActivePing, | |
| 1285 itostr(app.ping.days_since_last_active_ping)); | |
| 1286 if (FAILED(hr)) { | |
| 1287 return hr; | |
| 1288 } | |
| 1289 } | |
| 1290 | |
| 1291 if (need_r) { | |
| 1292 hr = AddXMLAttributeNode(element, | |
| 1293 kXmlNamespace, | |
| 1294 xml::attribute::kDaysSinceLastRollCall, | |
| 1295 itostr(app.ping.days_since_last_roll_call)); | |
| 1296 if (FAILED(hr)) { | |
| 1297 return hr; | |
| 1298 } | |
| 1299 } | |
| 1300 | |
| 1301 hr = parent_node->appendChild(element, NULL); | |
| 1302 if (FAILED(hr)) { | |
| 1303 return hr; | |
| 1304 } | |
| 1305 | |
| 1306 return S_OK; | |
| 1307 } | |
| 1308 | |
| 1309 HRESULT XmlParser::CreateElementNode(const TCHAR* name, | |
| 1310 const TCHAR* value, | |
| 1311 IXMLDOMNode** element) { | |
| 1312 ASSERT1(name); | |
| 1313 ASSERT1(value); | |
| 1314 ASSERT1(element); | |
| 1315 | |
| 1316 // When there is a namespace, the element names get o: prepended to avoid a | |
| 1317 // size explosion where the namespace uri gets automatically added to every | |
| 1318 // element by msxml. | |
| 1319 CString namespace_qualified_name; | |
| 1320 namespace_qualified_name.Format(kXmlNamespace ? _T("o:%s") : _T("%s"), | |
| 1321 name); | |
| 1322 ASSERT1(document_); | |
| 1323 HRESULT hr = CreateXMLNode(document_, | |
| 1324 NODE_ELEMENT, | |
| 1325 namespace_qualified_name, | |
| 1326 kXmlNamespace, | |
| 1327 value, | |
| 1328 element); | |
| 1329 return hr; | |
| 1330 } | |
| 1331 | |
| 1332 | |
| 1333 HRESULT XmlParser::DeserializeResponse(const std::vector<uint8>& buffer, | |
| 1334 UpdateResponse* update_response) { | |
| 1335 ASSERT1(update_response); | |
| 1336 | |
| 1337 XmlParser xml_parser; | |
| 1338 HRESULT hr = LoadXMLFromRawData(buffer, false, &xml_parser.document_); | |
| 1339 if (FAILED(hr)) { | |
| 1340 return hr; | |
| 1341 } | |
| 1342 | |
| 1343 xml_parser.response_ = &update_response->response_; | |
| 1344 | |
| 1345 hr = xml_parser.Parse(); | |
| 1346 if (FAILED(hr)) { | |
| 1347 return hr; | |
| 1348 } | |
| 1349 | |
| 1350 return S_OK; | |
| 1351 } | |
| 1352 | |
| 1353 HRESULT XmlParser::Parse() { | |
| 1354 CORE_LOG(L3, (_T("[XmlParser::Parse]"))); | |
| 1355 ASSERT1(response_); | |
| 1356 | |
| 1357 CComPtr<IXMLDOMElement> root_node; | |
| 1358 HRESULT hr = document_->get_documentElement(&root_node); | |
| 1359 if (FAILED(hr)) { | |
| 1360 return hr; | |
| 1361 } | |
| 1362 if (!root_node) { | |
| 1363 return GOOPDATEXML_E_PARSE_ERROR; | |
| 1364 } | |
| 1365 | |
| 1366 CComBSTR root_name; | |
| 1367 hr = root_node->get_baseName(&root_name); | |
| 1368 if (FAILED(hr)) { | |
| 1369 return hr; | |
| 1370 } | |
| 1371 | |
| 1372 if (root_name == xml::element::kResponse) { | |
| 1373 InitializeElementHandlers(); | |
| 1374 return TraverseDOM(root_node); | |
| 1375 } | |
| 1376 | |
| 1377 if (root_name == v2::element::kGUpdate) { | |
| 1378 InitializeLegacyElementHandlers(); | |
| 1379 return TraverseDOM(root_node); | |
| 1380 } | |
| 1381 | |
| 1382 return GOOPDATEXML_E_RESPONSENODE; | |
| 1383 } | |
| 1384 | |
| 1385 HRESULT XmlParser::TraverseDOM(IXMLDOMNode* node) { | |
| 1386 CORE_LOG(L5, (_T("[XmlParser::TraverseDOM]"))); | |
| 1387 ASSERT1(node); | |
| 1388 | |
| 1389 HRESULT hr = VisitElement(node); | |
| 1390 if (FAILED(hr)) { | |
| 1391 return hr; | |
| 1392 } | |
| 1393 | |
| 1394 CComPtr<IXMLDOMNodeList> children_list; | |
| 1395 hr = node->get_childNodes(&children_list); | |
| 1396 if (FAILED(hr)) { | |
| 1397 return hr; | |
| 1398 } | |
| 1399 | |
| 1400 long num_children = 0; // NOLINT | |
| 1401 hr = children_list->get_length(&num_children); | |
| 1402 if (FAILED(hr)) { | |
| 1403 return hr; | |
| 1404 } | |
| 1405 | |
| 1406 for (int i = 0; i < num_children; ++i) { | |
| 1407 CComPtr<IXMLDOMNode> child_node; | |
| 1408 hr = children_list->get_item(i, &child_node); | |
| 1409 if (FAILED(hr)) { | |
| 1410 return hr; | |
| 1411 } | |
| 1412 | |
| 1413 DOMNodeType type = NODE_INVALID; | |
| 1414 hr = child_node->get_nodeType(&type); | |
| 1415 if (FAILED(hr)) { | |
| 1416 return hr; | |
| 1417 } | |
| 1418 | |
| 1419 ASSERT1(type == NODE_TEXT || type == NODE_ELEMENT || type == NODE_COMMENT); | |
| 1420 | |
| 1421 if (type == NODE_ELEMENT) { | |
| 1422 hr = TraverseDOM(child_node); | |
| 1423 if (FAILED(hr)) { | |
| 1424 return hr; | |
| 1425 } | |
| 1426 } | |
| 1427 } | |
| 1428 | |
| 1429 return S_OK; | |
| 1430 } | |
| 1431 | |
| 1432 HRESULT XmlParser::VisitElement(IXMLDOMNode* node) { | |
| 1433 XMLFQName node_name; | |
| 1434 HRESULT hr = GetXMLFQName(node, &node_name); | |
| 1435 if (FAILED(hr)) { | |
| 1436 CORE_LOG(LE, (_T("[GetXMLFQName failed][0x%x]"), hr)); | |
| 1437 return hr; | |
| 1438 } | |
| 1439 | |
| 1440 CORE_LOG(L4, (_T("[element name][%s:%s]"), node_name.uri, node_name.base)); | |
| 1441 | |
| 1442 // Ignore elements not understood. | |
| 1443 ElementHandler* element_handler = | |
| 1444 element_handler_factory_.CreateObject(node_name.base); | |
| 1445 if (element_handler) { | |
| 1446 return element_handler->Handle(node, response_); | |
| 1447 } else { | |
| 1448 CORE_LOG(LW, (_T("[VisitElement: don't know how to handle %s:%s]"), | |
| 1449 node_name.uri, node_name.base)); | |
| 1450 } | |
| 1451 return S_OK; | |
| 1452 } | |
| 1453 | |
| 1454 } // namespace xml | |
| 1455 | |
| 1456 } // namespace omaha | |
| 1457 | |
| OLD | NEW |