| 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 |