Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(137)

Side by Side Diff: common/xml_parser.cc

Issue 624713003: Keep only base/extractor.[cc|h]. (Closed) Base URL: https://chromium.googlesource.com/external/omaha.git@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « common/xml_parser.h ('k') | common/xml_parser_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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
OLDNEW
« no previous file with comments | « common/xml_parser.h ('k') | common/xml_parser_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698