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 |