OLD | NEW |
| (Empty) |
1 // Copyright 2005-2009 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 // xml_utils.cpp | |
17 // | |
18 // Utilities for working with XML files via MSXML. | |
19 | |
20 #include "omaha/base/xml_utils.h" | |
21 | |
22 #include <msxml2.h> | |
23 #include <atlsafe.h> | |
24 #include <vector> | |
25 #include "omaha/base/debug.h" | |
26 #include "omaha/base/error.h" | |
27 #include "omaha/base/logging.h" | |
28 #include "omaha/base/string.h" | |
29 #include "omaha/base/utils.h" | |
30 | |
31 namespace omaha { | |
32 | |
33 XMLFQName::XMLFQName() {} | |
34 | |
35 XMLFQName::XMLFQName(const TCHAR* u, const TCHAR* b) | |
36 : uri(u && ::_tcslen(u) ? u : 0), | |
37 base(b && ::_tcslen(b) ? b : 0) {} | |
38 | |
39 XMLFQName::~XMLFQName() {} | |
40 | |
41 HRESULT CoCreateSafeDOMDocument(IXMLDOMDocument** my_xmldoc) { | |
42 ASSERT1(my_xmldoc && !*my_xmldoc); | |
43 if (!my_xmldoc) { | |
44 UTIL_LOG(LE, (L"[CoCreateSafeDOMDocument E_INVALIDARG]")); | |
45 return E_INVALIDARG; | |
46 } | |
47 *my_xmldoc = NULL; | |
48 CComPtr<IXMLDOMDocument> xml_doc; | |
49 HRESULT hr = xml_doc.CoCreateInstance(__uuidof(DOMDocument2)); | |
50 if (FAILED(hr)) { | |
51 UTIL_LOG(LE, (_T("[xml_doc.CoCreateInstance failed][0x%x]"), hr)); | |
52 return hr; | |
53 } | |
54 ASSERT1(xml_doc); | |
55 hr = xml_doc->put_resolveExternals(VARIANT_FALSE); | |
56 if (FAILED(hr)) { | |
57 UTIL_LOG(LE, (_T("[put_resolveExternals failed][0x%x]"), hr)); | |
58 return hr; | |
59 } | |
60 *my_xmldoc = xml_doc.Detach(); | |
61 return S_OK; | |
62 } | |
63 | |
64 HRESULT LoadXMLFromFile(const TCHAR* xmlfile, | |
65 bool preserve_whitespace, | |
66 IXMLDOMDocument** xmldoc) { | |
67 ASSERT1(xmlfile); | |
68 ASSERT1(xmldoc); | |
69 ASSERT1(!*xmldoc); | |
70 | |
71 *xmldoc = NULL; | |
72 CComPtr<IXMLDOMDocument> my_xmldoc; | |
73 HRESULT hr = CoCreateSafeDOMDocument(&my_xmldoc); | |
74 if (FAILED(hr)) { | |
75 UTIL_LOG(LE, (_T("[CoCreateSafeDOMDocument failed][0x%x]"), hr)); | |
76 return hr; | |
77 } | |
78 hr = my_xmldoc->put_preserveWhiteSpace(VARIANT_BOOL(preserve_whitespace)); | |
79 if (FAILED(hr)) { | |
80 UTIL_LOG(LE, (_T("[put_preserveWhiteSpace failed][0x%x]"), hr)); | |
81 return hr; | |
82 } | |
83 CComBSTR my_xmlfile(xmlfile); | |
84 VARIANT_BOOL is_successful(VARIANT_FALSE); | |
85 hr = my_xmldoc->load(CComVariant(my_xmlfile), &is_successful); | |
86 if (FAILED(hr)) { | |
87 UTIL_LOG(LE, (_T("[my_xmldoc->load failed][0x%x]"), hr)); | |
88 return hr; | |
89 } | |
90 if (!is_successful) { | |
91 CComPtr<IXMLDOMParseError> error; | |
92 CString error_message; | |
93 hr = GetXMLParseError(my_xmldoc, &error); | |
94 if (FAILED(hr)) { | |
95 UTIL_LOG(LE, (_T("[GetXMLParseError failed][0x%x]"), hr)); | |
96 return hr; | |
97 } | |
98 ASSERT1(error); | |
99 HRESULT error_code = 0; | |
100 hr = InterpretXMLParseError(error, &error_code, &error_message); | |
101 if (FAILED(hr)) { | |
102 UTIL_LOG(LE, (_T("[InterpretXMLParseError failed][0x%x]"), hr)); | |
103 return hr; | |
104 } | |
105 UTIL_LOG(LE, (L"[LoadXMLFromFile '%s'][parse error: %s]", | |
106 xmlfile, error_message)); | |
107 ASSERT1(FAILED(error_code)); | |
108 return FAILED(error_code) ? error_code : CI_E_XML_LOAD_ERROR; | |
109 } | |
110 *xmldoc = my_xmldoc.Detach(); | |
111 return S_OK; | |
112 } | |
113 | |
114 HRESULT LoadXMLFromMemory(const TCHAR* xmlstring, | |
115 bool preserve_whitespace, | |
116 IXMLDOMDocument** xmldoc) { | |
117 ASSERT1(xmlstring); | |
118 ASSERT1(xmldoc); | |
119 ASSERT1(!*xmldoc); | |
120 | |
121 *xmldoc = NULL; | |
122 CComPtr<IXMLDOMDocument> my_xmldoc; | |
123 RET_IF_FAILED(CoCreateSafeDOMDocument(&my_xmldoc)); | |
124 RET_IF_FAILED(my_xmldoc->put_preserveWhiteSpace( | |
125 VARIANT_BOOL(preserve_whitespace))); | |
126 CComBSTR xmlmemory(xmlstring); | |
127 VARIANT_BOOL is_successful(VARIANT_FALSE); | |
128 RET_IF_FAILED(my_xmldoc->loadXML(xmlmemory, &is_successful)); | |
129 if (!is_successful) { | |
130 CComPtr<IXMLDOMParseError> error; | |
131 CString error_message; | |
132 RET_IF_FAILED(GetXMLParseError(my_xmldoc, &error)); | |
133 ASSERT1(error); | |
134 HRESULT error_code = 0; | |
135 RET_IF_FAILED(InterpretXMLParseError(error, &error_code, &error_message)); | |
136 UTIL_LOG(LE, (L"[LoadXMLFromMemory][parse error: %s]", error_message)); | |
137 ASSERT1(FAILED(error_code)); | |
138 return FAILED(error_code) ? error_code : CI_E_XML_LOAD_ERROR; | |
139 } | |
140 *xmldoc = my_xmldoc.Detach(); | |
141 return S_OK; | |
142 } | |
143 | |
144 HRESULT LoadXMLFromRawData(const std::vector<byte>& xmldata, | |
145 bool preserve_whitespace, | |
146 IXMLDOMDocument** xmldoc) { | |
147 ASSERT1(xmldoc); | |
148 ASSERT1(!*xmldoc); | |
149 | |
150 *xmldoc = NULL; | |
151 if (!xmldata.size()) { | |
152 return E_INVALIDARG; | |
153 } | |
154 | |
155 CComPtr<IXMLDOMDocument> my_xmldoc; | |
156 RET_IF_FAILED(CoCreateSafeDOMDocument(&my_xmldoc)); | |
157 RET_IF_FAILED(my_xmldoc->put_preserveWhiteSpace( | |
158 VARIANT_BOOL(preserve_whitespace))); | |
159 | |
160 CComSafeArray<byte> xmlsa; | |
161 xmlsa.Add(xmldata.size(), &xmldata.front()); | |
162 CComVariant xmlvar(xmlsa); | |
163 | |
164 VARIANT_BOOL is_successful(VARIANT_FALSE); | |
165 RET_IF_FAILED(my_xmldoc->load(xmlvar, &is_successful)); | |
166 if (!is_successful) { | |
167 CComPtr<IXMLDOMParseError> error; | |
168 CString error_message; | |
169 RET_IF_FAILED(GetXMLParseError(my_xmldoc, &error)); | |
170 ASSERT1(error); | |
171 HRESULT error_code = 0; | |
172 RET_IF_FAILED(InterpretXMLParseError(error, &error_code, &error_message)); | |
173 UTIL_LOG(LE, (_T("[LoadXMLFromRawData][parse error: %s]"), error_message)); | |
174 ASSERT1(FAILED(error_code)); | |
175 return FAILED(error_code) ? error_code : CI_E_XML_LOAD_ERROR; | |
176 } | |
177 *xmldoc = my_xmldoc.Detach(); | |
178 return S_OK; | |
179 } | |
180 | |
181 HRESULT SaveXMLToFile(IXMLDOMDocument* xmldoc, const TCHAR* xmlfile) { | |
182 ASSERT1(xmldoc); | |
183 ASSERT1(xmlfile); | |
184 | |
185 CComBSTR my_xmlfile(xmlfile); | |
186 RET_IF_FAILED(xmldoc->save(CComVariant(my_xmlfile))); | |
187 return S_OK; | |
188 } | |
189 | |
190 HRESULT SaveXMLToMemory(IXMLDOMDocument* xmldoc, CString* xmlstring) { | |
191 ASSERT1(xmldoc); | |
192 ASSERT1(xmlstring); | |
193 | |
194 CComBSTR xmlmemory; | |
195 RET_IF_FAILED(xmldoc->get_xml(&xmlmemory)); | |
196 *xmlstring = xmlmemory; | |
197 | |
198 return S_OK; | |
199 } | |
200 | |
201 HRESULT SaveXMLToRawData(IXMLDOMDocument* xmldoc, std::vector<byte>* buffer) { | |
202 ASSERT1(xmldoc); | |
203 ASSERT1(buffer); | |
204 | |
205 CComPtr<IStream> stream; | |
206 HRESULT hr = ::CreateStreamOnHGlobal(NULL, TRUE, &stream); | |
207 if (FAILED(hr)) { | |
208 return hr; | |
209 } | |
210 | |
211 ASSERT1(stream); | |
212 | |
213 hr = xmldoc->save(CComVariant(stream)); | |
214 if (FAILED(hr)) { | |
215 return hr; | |
216 } | |
217 | |
218 // To get the exact size of the stream, we have to use the seek function. | |
219 LARGE_INTEGER li = {0, 0}; | |
220 ULARGE_INTEGER uli = {0, 0}; | |
221 hr = stream->Seek(li, STREAM_SEEK_END, &uli); | |
222 if (FAILED(hr)) { | |
223 return hr; | |
224 } | |
225 | |
226 buffer->resize(static_cast<size_t>(uli.QuadPart)); | |
227 | |
228 HGLOBAL hglobal = NULL; | |
229 hr = ::GetHGlobalFromStream(stream, &hglobal); | |
230 if (FAILED(hr)) { | |
231 return hr; | |
232 } | |
233 | |
234 memcpy_s(&buffer->front(), | |
235 buffer->size(), | |
236 ::GlobalLock(hglobal), | |
237 buffer->size()); | |
238 ::GlobalUnlock(hglobal); | |
239 | |
240 return S_OK; | |
241 } | |
242 | |
243 HRESULT CanonicalizeXML(const TCHAR* xmlstring, CString* canonical_xmlstring) { | |
244 ASSERT1(xmlstring); | |
245 ASSERT1(canonical_xmlstring); | |
246 | |
247 // Round-trip through MSXML, having it strip whitespace. | |
248 | |
249 CComPtr<IXMLDOMDocument> xmldoc; | |
250 RET_IF_FAILED(CoCreateSafeDOMDocument(&xmldoc)); | |
251 RET_IF_FAILED(xmldoc->put_preserveWhiteSpace(VARIANT_FALSE)); | |
252 { | |
253 CComBSTR xmlmemory(StringAfterBOM(xmlstring)); | |
254 VARIANT_BOOL is_successful(VARIANT_FALSE); | |
255 RET_IF_FAILED(xmldoc->loadXML(xmlmemory, &is_successful)); | |
256 if (!is_successful) { | |
257 CComPtr<IXMLDOMParseError> error; | |
258 CString error_message; | |
259 RET_IF_FAILED(GetXMLParseError(xmldoc, &error)); | |
260 ASSERT1(error); | |
261 HRESULT error_code = 0; | |
262 RET_IF_FAILED(InterpretXMLParseError(error, &error_code, &error_message)); | |
263 UTIL_LOG(LE, (L"[CanonicalizeXML][parse error: %s]", error_message)); | |
264 ASSERT1(FAILED(error_code)); | |
265 return FAILED(error_code) ? error_code : CI_E_XML_LOAD_ERROR; | |
266 } | |
267 } | |
268 std::vector<CString> lines; | |
269 { | |
270 CComBSTR xmlmemory2; | |
271 RET_IF_FAILED(xmldoc->get_xml(&xmlmemory2)); | |
272 TextToLines(CString(xmlmemory2), L"\r\n", &lines); | |
273 } | |
274 { | |
275 for (size_t i = 0; i < lines.size(); ++i) { | |
276 TrimString(lines[i], L" \t"); | |
277 } | |
278 LinesToText(lines, L"", canonical_xmlstring); | |
279 } | |
280 | |
281 return S_OK; | |
282 } | |
283 | |
284 bool operator==(const XMLFQName& u, const XMLFQName& v) { | |
285 if (u.uri && v.uri) { | |
286 // Both uris are non-null -> compare all the components. | |
287 return !_tcscmp(u.uri, v.uri) && !_tcscmp(u.base, v.base); | |
288 } else if (!u.uri && !v.uri) { | |
289 // Both uris are null -> only compare the base names. | |
290 return !_tcscmp(u.base ? u.base : __T(""), v.base ? v.base : __T("")); | |
291 } else { | |
292 // Either uri is null -> the names are in different namespaces. | |
293 return false; | |
294 } | |
295 } | |
296 | |
297 bool operator!=(const XMLFQName& u, const XMLFQName& v) { | |
298 return !(u == v); | |
299 } | |
300 | |
301 bool operator<(const XMLFQName& u, const XMLFQName &v) { | |
302 if (u.uri && v.uri) { | |
303 return (_tcscmp(u.uri, v.uri) < 0) || | |
304 ((_tcscmp(u.uri, v.uri) == 0) && (_tcscmp(u.base, v.base) < 0)); | |
305 } else if (!u.uri && !v.uri) { | |
306 return _tcscmp(u.base, v.base) < 0; | |
307 } else { | |
308 return false; | |
309 } | |
310 } | |
311 | |
312 bool operator>(const XMLFQName& u, const XMLFQName& v) { | |
313 return v < u; | |
314 } | |
315 | |
316 bool operator<=(const XMLFQName& u, const XMLFQName& v) { | |
317 return !(v < u); | |
318 } | |
319 | |
320 bool operator>=(const XMLFQName& u, const XMLFQName& v) { | |
321 return !(u < v); | |
322 } | |
323 | |
324 bool EqualXMLName(const XMLFQName& u, const XMLFQName& v) { | |
325 return u == v; | |
326 } | |
327 | |
328 // msxml returns a null uri for nodes that don't belong to a namespace. | |
329 bool EqualXMLName(IXMLDOMNode* pnode, const XMLFQName& u) { | |
330 CComBSTR name; | |
331 CComBSTR uri; | |
332 if (FAILED(pnode->get_baseName(&name)) || | |
333 FAILED(pnode->get_namespaceURI(&uri))) { | |
334 return false; | |
335 } | |
336 return EqualXMLName(XMLFQName(uri, name), u); | |
337 } | |
338 | |
339 inline bool EqualXMLName(const XMLFQName& u, IXMLDOMNode* pnode) { | |
340 return EqualXMLName(pnode, u); | |
341 } | |
342 | |
343 HRESULT GetXMLFQName(IXMLDOMNode* node, XMLFQName* name) { | |
344 ASSERT1(node); | |
345 ASSERT1(name); | |
346 | |
347 CComBSTR basename, uri; | |
348 RET_IF_FAILED(node->get_baseName(&basename)); | |
349 RET_IF_FAILED(node->get_namespaceURI(&uri)); | |
350 *name = XMLFQName(uri, basename); | |
351 return S_OK; | |
352 } | |
353 | |
354 CString XMLFQNameToString(const XMLFQName& fqname) { | |
355 CString name; | |
356 if (fqname.uri) { | |
357 name += fqname.uri; | |
358 name += L":"; | |
359 } | |
360 if (fqname.base) { | |
361 name += fqname.base; | |
362 } | |
363 return name; | |
364 } | |
365 | |
366 CString NodeToString(IXMLDOMNode* pnode) { | |
367 ASSERT1(pnode); | |
368 | |
369 XMLFQName node_name; | |
370 if (SUCCEEDED(GetXMLFQName(pnode, &node_name))) { | |
371 return XMLFQNameToString(node_name); | |
372 } | |
373 return L""; | |
374 } | |
375 | |
376 HRESULT CreateXMLNode(IXMLDOMDocument* xmldoc, | |
377 int node_type, | |
378 const TCHAR* node_name, | |
379 const TCHAR* namespace_uri, | |
380 const TCHAR* text, | |
381 IXMLDOMNode** node_out) { | |
382 ASSERT1(xmldoc); | |
383 ASSERT1(node_name); | |
384 // namespace_uri can be NULL | |
385 // text can be NULL | |
386 ASSERT1(node_out); | |
387 ASSERT1(!*node_out); | |
388 | |
389 *node_out = NULL; | |
390 CComPtr<IXMLDOMNode> new_node; | |
391 CComBSTR node_name_string, namespace_uri_string; | |
392 RET_IF_FAILED(node_name_string.Append(node_name)); | |
393 RET_IF_FAILED(namespace_uri_string.Append(namespace_uri)); | |
394 RET_IF_FAILED(xmldoc->createNode(CComVariant(node_type), | |
395 node_name_string, | |
396 namespace_uri_string, | |
397 &new_node)); | |
398 ASSERT1(new_node); | |
399 | |
400 // If any text was supplied, put it in the node | |
401 if (text && text[0]) { | |
402 RET_IF_FAILED(new_node->put_text(CComBSTR(text))); | |
403 } | |
404 | |
405 *node_out = new_node.Detach(); | |
406 return S_OK; | |
407 } | |
408 | |
409 HRESULT AppendXMLNode(IXMLDOMNode* xmlnode, IXMLDOMNode* new_child) { | |
410 ASSERT1(xmlnode); | |
411 ASSERT1(new_child); | |
412 | |
413 CComPtr<IXMLDOMNode> useless; | |
414 RET_IF_FAILED(xmlnode->appendChild(new_child, &useless)); | |
415 return S_OK; | |
416 } | |
417 | |
418 HRESULT AppendXMLNode(IXMLDOMNode* xmlnode, const TCHAR* text) { | |
419 ASSERT1(xmlnode); | |
420 // text can be NULL | |
421 | |
422 if (text && text[0]) { | |
423 CComPtr<IXMLDOMDocument> xml_doc; | |
424 CComPtr<IXMLDOMText> text_node; | |
425 RET_IF_FAILED(xmlnode->get_ownerDocument(&xml_doc)); | |
426 ASSERT1(xml_doc); | |
427 RET_IF_FAILED(xml_doc->createTextNode(CComBSTR(text), &text_node)); | |
428 RET_IF_FAILED(AppendXMLNode(xmlnode, text_node)); | |
429 } | |
430 return S_OK; | |
431 } | |
432 | |
433 HRESULT AddXMLAttributeNode(IXMLDOMNode* xmlnode, IXMLDOMAttribute* new_child) { | |
434 ASSERT1(xmlnode); | |
435 ASSERT1(new_child); | |
436 | |
437 CComPtr<IXMLDOMNamedNodeMap> attributes; | |
438 CComPtr<IXMLDOMNode> useless; | |
439 RET_IF_FAILED(xmlnode->get_attributes(&attributes)); | |
440 RET_IF_FAILED(attributes->setNamedItem(new_child, &useless)); | |
441 return S_OK; | |
442 } | |
443 | |
444 HRESULT AddXMLAttributeNode(IXMLDOMElement* xmlelement, | |
445 const TCHAR* attribute_name, | |
446 const TCHAR* attribute_value) { | |
447 ASSERT1(xmlelement); | |
448 ASSERT1(attribute_name); | |
449 // attribute_value can be NULL | |
450 | |
451 RET_IF_FAILED(xmlelement->setAttribute(CComBSTR(attribute_name), | |
452 CComVariant(attribute_value))); | |
453 return S_OK; | |
454 } | |
455 | |
456 HRESULT AddXMLAttributeNode(IXMLDOMNode* xmlnode, | |
457 const TCHAR* attribute_namespace, | |
458 const TCHAR* attribute_name, | |
459 const TCHAR* attribute_value) { | |
460 ASSERT1(xmlnode); | |
461 ASSERT1(attribute_name); | |
462 // attribute_namespace can be NULL | |
463 // attribute_value can be NULL | |
464 | |
465 CComPtr<IXMLDOMDocument> xmldoc; | |
466 RET_IF_FAILED(xmlnode->get_ownerDocument(&xmldoc)); | |
467 ASSERT1(xmldoc); | |
468 | |
469 CComPtr<IXMLDOMNode> attribute_node; | |
470 RET_IF_FAILED(CreateXMLNode(xmldoc, | |
471 NODE_ATTRIBUTE, | |
472 attribute_name, | |
473 attribute_namespace, | |
474 attribute_value, | |
475 &attribute_node)); | |
476 CComQIPtr<IXMLDOMAttribute> attribute(attribute_node); | |
477 ASSERT1(attribute); | |
478 RET_IF_FAILED(AddXMLAttributeNode(xmlnode, attribute)); | |
479 return S_OK; | |
480 } | |
481 | |
482 HRESULT RemoveXMLChildrenByName(IXMLDOMNode* xmlnode, const XMLFQName& name) { | |
483 ASSERT1(xmlnode); | |
484 | |
485 CComPtr<IXMLDOMNodeList> node_list; | |
486 RET_IF_FAILED(xmlnode->get_childNodes(&node_list)); | |
487 ASSERT1(node_list); | |
488 | |
489 bool found = false; | |
490 do { | |
491 found = false; | |
492 long count = 0; // NOLINT | |
493 RET_IF_FAILED(node_list->get_length(&count)); | |
494 RET_IF_FAILED(node_list->reset()); | |
495 | |
496 for (int i = 0; i < count; ++i) { | |
497 CComPtr<IXMLDOMNode> child_node, useless; | |
498 RET_IF_FAILED(node_list->get_item(i, &child_node)); | |
499 ASSERT1(child_node); | |
500 if (EqualXMLName(child_node, name)) { | |
501 RET_IF_FAILED(xmlnode->removeChild(child_node, &useless)); | |
502 // Start loop over: the list is "alive" and changes when you remove a | |
503 // node from it. Yes this seems to be n^2 but in fact we expect at | |
504 // most one each of <Hash> and/or <Size> nodes. | |
505 found = true; | |
506 break; | |
507 } | |
508 } | |
509 } while (found); | |
510 | |
511 return S_OK; | |
512 } | |
513 | |
514 HRESULT GetXMLChildByName(IXMLDOMElement* xmlnode, | |
515 const TCHAR* child_name, | |
516 IXMLDOMNode** xmlchild) { | |
517 ASSERT1(xmlnode); | |
518 ASSERT1(child_name); | |
519 ASSERT1(xmlchild); | |
520 ASSERT1(!*xmlchild); | |
521 | |
522 *xmlchild = NULL; | |
523 CComPtr<IXMLDOMNodeList> node_list; | |
524 long node_list_length = 0; // NOLINT | |
525 RET_IF_FAILED(xmlnode->getElementsByTagName(CComBSTR(child_name), | |
526 &node_list)); | |
527 ASSERT1(node_list); | |
528 RET_IF_FAILED(node_list->get_length(&node_list_length)); | |
529 if (node_list_length <= 0) { | |
530 return HRESULT_FROM_WIN32(ERROR_NOT_FOUND); | |
531 } | |
532 // Should only be one child node with name we're looking for. | |
533 if (node_list_length > 1) { | |
534 return CI_E_INVALID_MANIFEST; | |
535 } | |
536 RET_IF_FAILED(node_list->reset()); | |
537 RET_IF_FAILED(node_list->get_item(0, xmlchild)); | |
538 ASSERT1(*xmlchild); | |
539 return S_OK; | |
540 } | |
541 | |
542 HRESULT InsertXMLBeforeItem(IXMLDOMNode* xmlnode, | |
543 IXMLDOMNode* new_child, | |
544 size_t item_number) { | |
545 ASSERT1(xmlnode); | |
546 ASSERT1(new_child); | |
547 | |
548 CComPtr<IXMLDOMNodeList> child_list; | |
549 CComPtr<IXMLDOMNode> refchild, useless; | |
550 | |
551 RET_IF_FAILED(xmlnode->get_childNodes(&child_list)); | |
552 ASSERT1(child_list); | |
553 RET_IF_FAILED(child_list->get_item(item_number, &refchild)); | |
554 ASSERT1(refchild); | |
555 RET_IF_FAILED(xmlnode->insertBefore(new_child, | |
556 CComVariant(refchild), | |
557 &useless)); | |
558 return S_OK; | |
559 } | |
560 | |
561 HRESULT GetXMLParseError(IXMLDOMDocument* xmldoc, | |
562 IXMLDOMParseError** parse_error) { | |
563 ASSERT1(xmldoc); | |
564 ASSERT1(parse_error); | |
565 ASSERT1(!*parse_error); | |
566 | |
567 *parse_error = NULL; | |
568 CComPtr<IXMLDOMParseError> error; | |
569 RET_IF_FAILED(xmldoc->get_parseError(&error)); | |
570 HRESULT error_code = 0; | |
571 HRESULT hr = error->get_errorCode(&error_code); | |
572 if (hr == S_OK) { | |
573 *parse_error = error.Detach(); | |
574 return S_OK; | |
575 } else if (hr == S_FALSE) { | |
576 // No parse error | |
577 return S_FALSE; | |
578 } else { | |
579 return hr; | |
580 } | |
581 } | |
582 | |
583 HRESULT InterpretXMLParseError(IXMLDOMParseError* parse_error, | |
584 HRESULT* error_code, | |
585 CString* message) { | |
586 ASSERT1(parse_error); | |
587 ASSERT1(error_code); | |
588 ASSERT1(message); | |
589 | |
590 long line = 0; // NOLINT | |
591 long char_pos = 0; // NOLINT | |
592 CComBSTR src_text, reason; | |
593 RET_IF_FAILED(parse_error->get_errorCode(error_code)); | |
594 RET_IF_FAILED(parse_error->get_line(&line)); | |
595 RET_IF_FAILED(parse_error->get_linepos(&char_pos)); | |
596 RET_IF_FAILED(parse_error->get_srcText(&src_text)); | |
597 RET_IF_FAILED(parse_error->get_reason(&reason)); | |
598 | |
599 // Wild guess. | |
600 size_t size_estimate = src_text.Length() + reason.Length() + 100; | |
601 | |
602 // TODO(omaha): think about replacing this call to _snwprintf with a | |
603 // safestring function. | |
604 std::vector<TCHAR> s(size_estimate); | |
605 _snwprintf_s(&s.front(), size_estimate, _TRUNCATE, | |
606 L"%d(%d) : error 0x%08lx: %s\n %s", | |
607 line, char_pos, *error_code, | |
608 reason ? reason : L"", | |
609 src_text ? src_text : L"<no source text>"); | |
610 // _snwprintf doesn't terminate the string with a null if | |
611 // the formatted string fills the entire buffer. | |
612 s[s.size()- 1] = L'\0'; | |
613 *message = &s.front(); | |
614 return S_OK; | |
615 } | |
616 | |
617 HRESULT GetNumChildren(IXMLDOMNode* node, int* num_children) { | |
618 ASSERT1(node); | |
619 ASSERT1(num_children); | |
620 | |
621 *num_children = 0; | |
622 CComPtr<IXMLDOMNodeList> children; | |
623 RET_IF_FAILED(node->get_childNodes(&children)); | |
624 ASSERT1(children); | |
625 | |
626 long len = 0; // NOLINT | |
627 RET_IF_FAILED(children->get_length(&len)); | |
628 *num_children = len; | |
629 return S_OK; | |
630 } | |
631 | |
632 int GetNumAttributes(IXMLDOMNode* node) { | |
633 ASSERT1(node); | |
634 | |
635 CComPtr<IXMLDOMNamedNodeMap> attr_map; | |
636 if (FAILED(node->get_attributes(&attr_map))) { | |
637 return 0; | |
638 } | |
639 ASSERT1(attr_map); | |
640 long len = 0; // NOLINT | |
641 if (FAILED(attr_map->get_length(&len))) { | |
642 return 0; | |
643 } | |
644 return len; | |
645 } | |
646 | |
647 bool HasAttribute(IXMLDOMNode* node, const TCHAR* attr_name) { | |
648 ASSERT1(node); | |
649 ASSERT1(attr_name); | |
650 | |
651 CComPtr<IXMLDOMNamedNodeMap> attr_map; | |
652 if (FAILED(node->get_attributes(&attr_map))) { | |
653 return false; | |
654 } | |
655 if (!attr_map) { | |
656 return false; | |
657 } | |
658 | |
659 CComBSTR temp_attr_name(attr_name); | |
660 CComPtr<IXMLDOMNode> attribute_node; | |
661 if (FAILED(attr_map->getNamedItem(static_cast<BSTR>(temp_attr_name), | |
662 &attribute_node))) { | |
663 return false; | |
664 } | |
665 | |
666 return attribute_node != NULL; | |
667 } | |
668 | |
669 HRESULT ReadBooleanAttribute(IXMLDOMNode* node, | |
670 const TCHAR* attr_name, | |
671 bool* value) { | |
672 CORE_LOG(L4, (_T("[ReadBooleanAttribute][%s]"), attr_name)); | |
673 ASSERT1(node); | |
674 ASSERT1(attr_name); | |
675 ASSERT1(value); | |
676 | |
677 CComBSTR node_value; | |
678 HRESULT hr = ReadAttribute(node, attr_name, &node_value); | |
679 if (FAILED(hr)) { | |
680 CORE_LOG(LE, (_T("[ReadAttribute failed][%s][0x%x]"), attr_name, hr)); | |
681 return hr; | |
682 } | |
683 | |
684 hr = String_StringToBool(static_cast<TCHAR*>(node_value), | |
685 value); | |
686 if (FAILED(hr)) { | |
687 CORE_LOG(LE, (_T("[String_StringToBool failed][0x%x]"), hr)); | |
688 return hr; | |
689 } | |
690 | |
691 return S_OK; | |
692 } | |
693 | |
694 HRESULT ReadIntAttribute(IXMLDOMNode* node, | |
695 const TCHAR* attr_name, | |
696 int* value) { | |
697 CORE_LOG(L4, (_T("[ReadIntAttribute][%s]"), attr_name)); | |
698 ASSERT1(node); | |
699 ASSERT1(attr_name); | |
700 ASSERT1(value); | |
701 | |
702 CComBSTR node_value; | |
703 HRESULT hr = ReadAttribute(node, attr_name, &node_value); | |
704 if (FAILED(hr)) { | |
705 CORE_LOG(LE, (_T("[ReadAttribute failed][%s][0x%x]"), attr_name, hr)); | |
706 return hr; | |
707 } | |
708 | |
709 if (!String_StringToDecimalIntChecked( | |
710 static_cast<const TCHAR*>(node_value), value)) { | |
711 return GOOPDATEXML_E_STRTOUINT; | |
712 } | |
713 return S_OK; | |
714 } | |
715 | |
716 HRESULT ReadGuidAttribute(IXMLDOMNode* node, | |
717 const TCHAR* attr_name, | |
718 GUID* value) { | |
719 CORE_LOG(L4, (_T("[ReadGuidAttribute][%s]"), attr_name)); | |
720 ASSERT1(node); | |
721 ASSERT1(attr_name); | |
722 ASSERT1(value); | |
723 | |
724 CComBSTR node_value; | |
725 HRESULT hr = ReadAttribute(node, attr_name, &node_value); | |
726 if (FAILED(hr)) { | |
727 CORE_LOG(LE, (_T("[ReadAttribute failed][%s][0x%x]"), attr_name, hr)); | |
728 return hr; | |
729 } | |
730 | |
731 hr = StringToGuidSafe(static_cast<TCHAR*>(node_value), value); | |
732 if (FAILED(hr)) { | |
733 CORE_LOG(LE, (_T("[StringToGuidSafe failed][0x%x]"), hr)); | |
734 return hr; | |
735 } | |
736 | |
737 return S_OK; | |
738 } | |
739 | |
740 HRESULT ReadStringAttribute(IXMLDOMNode* node, | |
741 const TCHAR* attr_name, | |
742 CString* value) { | |
743 CORE_LOG(L4, (_T("[ReadStringAttribute][%s]"), attr_name)); | |
744 ASSERT1(node); | |
745 ASSERT1(attr_name); | |
746 ASSERT1(value); | |
747 | |
748 CComBSTR node_value; | |
749 HRESULT hr = ReadAttribute(node, attr_name, &node_value); | |
750 if (FAILED(hr)) { | |
751 CORE_LOG(LE, (_T("[ReadAttribute failed][%s][0x%x]"), attr_name, hr)); | |
752 return hr; | |
753 } | |
754 | |
755 // Will extract the underlying string. | |
756 *value = static_cast<TCHAR*>(node_value); | |
757 | |
758 return S_OK; | |
759 } | |
760 | |
761 HRESULT ReadAttribute(IXMLDOMNode* node, | |
762 const TCHAR* attr_name, | |
763 BSTR* value) { | |
764 CORE_LOG(L4, (_T("[ReadAttribute][%s]"), attr_name)); | |
765 ASSERT1(node); | |
766 ASSERT1(attr_name); | |
767 ASSERT1(value); | |
768 | |
769 // First read the attributes. | |
770 CComPtr<IXMLDOMNamedNodeMap> attributes; | |
771 HRESULT hr = node->get_attributes(&attributes); | |
772 if (FAILED(hr)) { | |
773 CORE_LOG(LE, (_T("[get_attributes failed][0x%x]"), hr)); | |
774 return hr; | |
775 } | |
776 | |
777 if (!attributes) { | |
778 CORE_LOG(LE, (_T("[Msxml S_FALSE return]"))); | |
779 return E_FAIL; // Protect against msxml S_FALSE return. | |
780 } | |
781 | |
782 CComPtr<IXMLDOMNode> attribute_node; | |
783 CComVariant node_value; | |
784 CComBSTR temp_attr_name(attr_name); | |
785 | |
786 // Get the attribute using a named node. | |
787 hr = attributes->getNamedItem(static_cast<BSTR>(temp_attr_name), | |
788 &attribute_node); | |
789 if (FAILED(hr)) { | |
790 CORE_LOG(LE, (_T("[getNamedItem failed][0x%x]"), hr)); | |
791 return hr; | |
792 } | |
793 | |
794 if (!attribute_node) { | |
795 CORE_LOG(LE, (_T("[Msxml S_FALSE return]"))); | |
796 return E_FAIL; // Protect against msxml S_FALSE return. | |
797 } | |
798 | |
799 hr = attribute_node->get_nodeValue(&node_value); | |
800 if (FAILED(hr)) { | |
801 CORE_LOG(LE, (_T("[get_nodeValue failed][0x%x]"), hr)); | |
802 return hr; | |
803 } | |
804 | |
805 if (node_value.vt == VT_EMPTY) { | |
806 CORE_LOG(LE, (_T("[node_value.vt == VT_EMPTY]"))); | |
807 return E_FAIL; | |
808 } | |
809 | |
810 // Extract the variant into a BSTR. | |
811 node_value.CopyTo(value); | |
812 | |
813 return S_OK; | |
814 } | |
815 | |
816 HRESULT ReadStringValue(IXMLDOMNode* node, CString* value) { | |
817 CORE_LOG(L4, (_T("[ReadStringValue]"))); | |
818 ASSERT1(node); | |
819 ASSERT1(value); | |
820 | |
821 CComPtr<IXMLDOMNodeList> child_nodes; | |
822 HRESULT hr = node->get_childNodes(&child_nodes); | |
823 if (FAILED(hr)) { | |
824 CORE_LOG(LE, (_T("[get_childNodes failed][0x%x]"), hr)); | |
825 return hr; | |
826 } | |
827 if (!child_nodes) { | |
828 CORE_LOG(LE, (_T("[Msxml S_FALSE return]"))); | |
829 return E_FAIL; // Protect against msxml S_FALSE return. | |
830 } | |
831 | |
832 long count = 0; // NOLINT | |
833 hr = child_nodes->get_length(&count); | |
834 if (FAILED(hr)) { | |
835 return hr; | |
836 } | |
837 | |
838 ASSERT(count == 1, (_T("count: %u"), count)); | |
839 CComPtr<IXMLDOMNode> child_node; | |
840 hr = child_nodes->nextNode(&child_node); | |
841 if (FAILED(hr)) { | |
842 return hr; | |
843 } | |
844 | |
845 DOMNodeType type = NODE_INVALID; | |
846 hr = child_node->get_nodeType(&type); | |
847 if (FAILED(hr)) { | |
848 CORE_LOG(LE, (_T("[get_nodeType failed][0x%x]"), hr)); | |
849 return hr; | |
850 } | |
851 | |
852 if (type != NODE_TEXT) { | |
853 CORE_LOG(LE, (_T("[Invalid nodeType][%d]"), type)); | |
854 return E_INVALIDARG; | |
855 } | |
856 | |
857 CComVariant node_value; | |
858 hr = child_node->get_nodeValue(&node_value); | |
859 if (FAILED(hr)) { | |
860 CORE_LOG(LE, (_T("[get_nodeValue failed][0x%x]"), hr)); | |
861 return hr; | |
862 } | |
863 | |
864 if (node_value.vt != VT_BSTR) { | |
865 CORE_LOG(LE, (_T("[node_value.vt != VT_BSTR][%d]"), node_value.vt)); | |
866 return E_INVALIDARG; | |
867 } | |
868 | |
869 *value = V_BSTR(&node_value); | |
870 return S_OK; | |
871 } | |
872 | |
873 } // namespace omaha | |
874 | |
OLD | NEW |