Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2150 lines
57 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1999.
  5. //
  6. // File: ncxml.cpp
  7. //
  8. // Contents: helper functions for doing remarkably simple things
  9. // with the XML DOM.
  10. //
  11. //----------------------------------------------------------------------------
  12. #include "pch.h"
  13. #pragma hdrstop
  14. #include <oleauto.h> // for SysFreeString()
  15. #include "ncbase.h"
  16. #include "ncdebug.h"
  17. #include "ncxml.h"
  18. #include "ncinet2.h" // HrCombineUrl, HrCopyAndValidateUrl
  19. struct DTNAME
  20. {
  21. LPCWSTR m_pszType;
  22. SST_DATA_TYPE m_sdt;
  23. };
  24. // Lookup table, sorted by string
  25. // Note: if you add strings here, you also have to add a
  26. // corresponding entry to SST_DATA_TYPE
  27. //
  28. static CONST DTNAME g_rgdtTypeStrings[] =
  29. {
  30. {L"bin.base64", SDT_BIN_BASE64}, // base64 as defined in the MIME IETF spec
  31. {L"bin.hex", SDT_BIN_HEX}, // Hexadecimal digits representing octets VT_ARRAY safearray or stream
  32. {L"boolean", SDT_BOOLEAN}, // boolean, "1" or "0" VT_BOOL int
  33. {L"char", SDT_CHAR}, // char, string VT_UI2 wchar
  34. {L"date", SDT_DATE_ISO8601}, // date.iso8601, A date in ISO 8601 format. (no time) VT_DATE long
  35. {L"dateTime", SDT_DATETIME_ISO8601}, // dateTime.iso8601, A date in ISO 8601 format, with optional time and no optional zone. Fractional seconds may be as precise as nanoseconds. VT_DATE long
  36. {L"dateTime.tz", SDT_DATETIME_ISO8601TZ},// dateTime.iso8601tz, A date in ISO 8601 format, with optional time and optional zone. Fractional seconds may be as precise as nanoseconds. VT_DATE long
  37. {L"fixed.14.4", SDT_FIXED_14_4}, // fixed.14.4, Same as "number" but no more than 14 digits to the left of the decimal point, and no more than 4 to the right. VT_CY large_integer
  38. {L"float", SDT_FLOAT}, // float, Same as for "number." VT_R8 double
  39. {L"i1", SDT_I1}, // i1, A number, with optional sign, no fractions, no exponent. VT_I1 char
  40. {L"i2", SDT_I2}, // i2, " VT_I2 short
  41. {L"i4", SDT_I4}, // i4, " VT_I4 long
  42. {L"int", SDT_INT}, // int, A number, with optional sign, no fractions, no exponent. VT_I4 long
  43. {L"number", SDT_NUMBER}, // number, A number, with no limit on digits, may potentially have a leading sign, fractional digits, and optionally an exponent. Punctuation as in US English. VT_R8 double
  44. {L"r4", SDT_R4}, // r4, Same as "number." VT_FLOAT float
  45. {L"r8", SDT_R8}, // r8, " VT_DOUBLE double
  46. {L"string", SDT_STRING}, // string, pcdata VT_BSTR BSTR
  47. {L"time", SDT_TIME_ISO8601}, // time.iso8601, A time in ISO 8601 format, with no date and no time zone. VT_DATE long
  48. {L"time.tz", SDT_TIME_ISO8601TZ}, // time.iso8601.tz, A time in ISO 8601 format, with no date but optional time zone. VT_DATE. long
  49. {L"ui1", SDT_UI1}, // ui1, A number, unsigned, no fractions, no exponent. VT_UI1 unsigned char
  50. {L"ui2", SDT_UI2}, // ui2, " VT_UI2 unsigned short
  51. {L"ui4", SDT_UI4}, // ui4, " VT_UI4 unsigned long
  52. {L"uri", SDT_URI}, // uri, Universal Resource Identifier VT_BSTR BSTR
  53. {L"uuid", SDT_UUID}, // uuid, Hexadecimal digits representing octets, optional embedded hyphens which should be ignored. VT_BSTR GUID
  54. //
  55. // note(cmr): ADD NEW VALUES IN ALPHABETICAL ORDER
  56. //
  57. };
  58. // NOTE: the order of elements in this array must correspond to the order of
  59. // elements in the SST_DATA_TYPE enum.
  60. static CONST VARTYPE g_rgvtTypes[] =
  61. {
  62. VT_BSTR,
  63. VT_BSTR,
  64. VT_I4,
  65. VT_CY,
  66. VT_BOOL,
  67. VT_DATE,
  68. VT_DATE,
  69. VT_DATE,
  70. VT_DATE,
  71. VT_DATE,
  72. VT_I1,
  73. VT_I2,
  74. VT_I4,
  75. VT_UI1,
  76. VT_UI2,
  77. VT_UI4,
  78. VT_R4,
  79. VT_R8,
  80. VT_R8,
  81. VT_BSTR,
  82. VT_ARRAY,
  83. VT_ARRAY,
  84. VT_UI2,
  85. VT_BSTR,
  86. //
  87. // note(cmr): ADD NEW VALUES IMMEDIATELY BEFORE THIS COMMENT.
  88. // If adding new values, see comment above.
  89. //
  90. VT_EMPTY
  91. };
  92. /*
  93. * Function: GetStringFromType()
  94. *
  95. * Purpose: Returns an xml-data type string for the specified
  96. * SDT_DATA_TYPE value.
  97. *
  98. * Arguments:
  99. * sdt [in] The data type whose descriptive string is desired.
  100. * This must be a valid SST_DATA_TYPE value (less
  101. * than SDT_INVALID)
  102. *
  103. * Returns:
  104. * A pointer to a constant string id representing the type. This string
  105. * should not be modified or freed.
  106. * This method will never return NULL.
  107. *
  108. */
  109. LPCWSTR
  110. GetStringFromType(CONST SST_DATA_TYPE sdt)
  111. {
  112. // there must be an entry in g_rgdtTypeStrings for every real
  113. // value of SST_DATA_TYPE
  114. //
  115. Assert(SDT_INVALID == celems(g_rgdtTypeStrings));
  116. Assert(sdt < SDT_INVALID);
  117. // since comparing SDT_s is somewhat cheaper than comparing whole strings,
  118. // we just walk through the whole list sequentually
  119. //
  120. CONST INT nSize = celems(g_rgdtTypeStrings);
  121. LPCWSTR pszResult;
  122. INT i;
  123. pszResult = NULL;
  124. i = 0;
  125. for ( ; i < nSize; ++i)
  126. {
  127. SST_DATA_TYPE sdtCurrent;
  128. sdtCurrent = g_rgdtTypeStrings[i].m_sdt;
  129. if (sdt == sdtCurrent)
  130. {
  131. // we have a match
  132. pszResult = g_rgdtTypeStrings[i].m_pszType;
  133. break;
  134. }
  135. }
  136. AssertSz(pszResult, "GetStringFromType: "
  137. "sdt not found in g_rgdtTypeStrings!");
  138. return pszResult;
  139. }
  140. /*
  141. * Function: GetTypeFromString()
  142. *
  143. * Purpose: Returns the appropriate SDT_DATA_TYPE value for the given
  144. * xml-data type string.
  145. *
  146. * Arguments:
  147. * pszTypeString [in] The data type identifier whose SDT_DATA_TYPE value
  148. * is desired.
  149. *
  150. * Returns:
  151. * If the string is found, it returns the appropriate value of the
  152. * SST_DATA_TYPE enumeration.
  153. * If the string is not found, it returns SDT_INVALID.
  154. *
  155. * Notes:
  156. * The source string is compared to known strings in a case-insensitive
  157. * comparison.
  158. *
  159. */
  160. SST_DATA_TYPE
  161. GetTypeFromString(LPCWSTR pszTypeString)
  162. {
  163. // there must be an entry in g_rgdtTypeStrings for every real
  164. // value of SST_DATA_TYPE
  165. //
  166. Assert(SDT_INVALID == celems(g_rgdtTypeStrings));
  167. SST_DATA_TYPE sdtResult;
  168. sdtResult = SDT_INVALID;
  169. {
  170. // now search for the string in the list, using a
  171. // standard binary search
  172. //
  173. INT nLow;
  174. INT nMid;
  175. INT nHigh;
  176. nLow = 0;
  177. nHigh = celems(g_rgdtTypeStrings) - 1;
  178. while (TRUE)
  179. {
  180. if (nLow > nHigh)
  181. {
  182. // not found
  183. //
  184. break;
  185. }
  186. nMid = (nLow + nHigh) / 2;
  187. {
  188. LPCWSTR pszCurrent;
  189. int result;
  190. pszCurrent = g_rgdtTypeStrings[nMid].m_pszType;
  191. result = _wcsicmp(pszTypeString, pszCurrent);
  192. if (result < 0)
  193. {
  194. nHigh = nMid - 1;
  195. }
  196. else if (result > 0)
  197. {
  198. nLow = nMid + 1;
  199. }
  200. else
  201. {
  202. // found
  203. //
  204. sdtResult = g_rgdtTypeStrings[nMid].m_sdt;
  205. break;
  206. }
  207. }
  208. }
  209. }
  210. return sdtResult;
  211. }
  212. VARTYPE
  213. GetVarTypeFromString(LPCWSTR pszTypeString)
  214. {
  215. SST_DATA_TYPE sdt = SDT_INVALID;
  216. sdt = GetTypeFromString(pszTypeString);
  217. return g_rgvtTypes[sdt];
  218. }
  219. // not an exported function, but one that we use internally...
  220. HRESULT
  221. HrGetChildElement(IXMLDOMNode * pxdn,
  222. LPCWSTR pszNodeName,
  223. IXMLDOMNode ** ppxdn);
  224. CONST WCHAR pszTextType [] = L"string";
  225. //+---------------------------------------------------------------------------
  226. //
  227. // Function: HrGetTypedValueFromElement
  228. //
  229. // Purpose: Given an IXMLDOMNode that should be of type
  230. // dt:string, returns a new BSTR containing that
  231. // string.
  232. //
  233. // Arguments:
  234. // pxdn IXMLDOMNode to extract the string from.
  235. // It is intended that this node be of type
  236. // NODE_ELEMENT.
  237. //
  238. // pszDataType The type of the data encoded in the element.
  239. //
  240. // pvarOut Address of a VARIANT that will obtain the
  241. // data value. This must be freed when
  242. // no longer needed.
  243. //
  244. // Returns:
  245. // S_OK if *pvarOut contains the data of the desired type
  246. //
  247. // Notes:
  248. //
  249. HRESULT
  250. HrGetTypedValueFromElement(IXMLDOMNode * pxdn,
  251. CONST LPCWSTR pszDataType,
  252. VARIANT * pvarOut)
  253. {
  254. Assert(pxdn);
  255. Assert(pszDataType);
  256. Assert(pvarOut);
  257. HRESULT hr;
  258. BSTR bstrDataType;
  259. bstrDataType = ::SysAllocString(pszDataType);
  260. if (bstrDataType)
  261. {
  262. hr = pxdn->put_dataType(bstrDataType);
  263. if (SUCCEEDED(hr))
  264. {
  265. hr = pxdn->get_nodeTypedValue(pvarOut);
  266. if (FAILED(hr))
  267. {
  268. TraceError("HrGetTypedValueFromElement: "
  269. "get_nodeTypedValue()", hr);
  270. // clear pvarOut
  271. ::VariantInit(pvarOut);
  272. }
  273. }
  274. else
  275. {
  276. TraceError("HrGetTypedValueFromElement: "
  277. "put_dataType()", hr);
  278. }
  279. ::SysFreeString(bstrDataType);
  280. }
  281. else
  282. {
  283. hr = E_OUTOFMEMORY;
  284. TraceError("HrGetTypedValueFromElement: "
  285. "SysAllocString()", hr);
  286. }
  287. TraceError("HrGetTypedValueFromElement", hr);
  288. return hr;
  289. }
  290. //+---------------------------------------------------------------------------
  291. //
  292. // Function: HrGetTypedValueFromChildElement
  293. //
  294. // Purpose: Given an IXMLDOMElement, finds its child element
  295. // of the given name, and extracts the data contained
  296. // in that child element, decoding it from the specified
  297. // format.
  298. //
  299. // Arguments:
  300. // pxdn The element which should contain the specified
  301. // children
  302. //
  303. // arypszTokens A serial list of child element names that uniquely
  304. // describe the desired element.
  305. //
  306. // cTokens The number of element names contained in
  307. // arypszTokens.
  308. //
  309. // pszDataType The type of the data encoded in the element.
  310. //
  311. // pvarOut Address of a VARIANT that will obtain the
  312. // data value. This must be freed when
  313. // no longer needed.
  314. //
  315. // Returns:
  316. // S_OK if *pbstrOut has been set to a new BSTR
  317. // S_FALSE if the specified element did not exist.
  318. // in this case, *pbstrOut is set to NULL
  319. //
  320. // Notes:
  321. // for example, if the document looked like this:
  322. // <foo><bar>text</bar></foo>
  323. // and pxdn referred to <foo>, arypszTokens = [ "bar" ]
  324. // cTokens = 1, and sdtType = "string", this would
  325. // return "text".
  326. // See the definition of HrGetNestedChildElement() for
  327. // further explination.
  328. //
  329. HRESULT
  330. HrGetTypedValueFromChildElement(IXMLDOMNode * pxdn,
  331. CONST LPCWSTR * arypszTokens,
  332. CONST ULONG cTokens,
  333. CONST LPCWSTR pszDataType,
  334. VARIANT * pvarOut)
  335. {
  336. Assert(pxdn);
  337. Assert(arypszTokens);
  338. Assert(cTokens);
  339. Assert(pszDataType);
  340. Assert(pvarOut);
  341. HRESULT hr;
  342. IXMLDOMNode * pxdnTemp;
  343. pxdnTemp = NULL;
  344. hr = HrGetNestedChildElement(pxdn, arypszTokens, cTokens, &pxdnTemp);
  345. if (FAILED(hr))
  346. {
  347. pxdnTemp = NULL;
  348. goto Cleanup;
  349. }
  350. if (S_OK == hr)
  351. {
  352. Assert(pxdn);
  353. hr = HrGetTypedValueFromElement(pxdnTemp, pszDataType, pvarOut);
  354. if (FAILED(hr))
  355. {
  356. goto Cleanup;
  357. }
  358. }
  359. // hr is S_FALSE if bstrResult is NULL, or S_OK if
  360. // pvarOut has been retrieved
  361. Cleanup:
  362. SAFE_RELEASE(pxdnTemp);
  363. TraceErrorOptional("HrGetTypedValueFromChildElement", hr, (S_FALSE == hr));
  364. return hr;
  365. }
  366. //+---------------------------------------------------------------------------
  367. //
  368. // Function: HrGetTextValueFromElement
  369. //
  370. // Purpose: Given an IXMLDOMNode that should be of type
  371. // dt:string, returns a new BSTR containing that
  372. // string.
  373. //
  374. // Arguments:
  375. // pxdn IXMLDOMNode to extract the string from.
  376. // It is intended that this node be of type
  377. // NODE_ELEMENT.
  378. //
  379. // pbstrOut Address of a BSTR that will obtain the
  380. // text value. This must be freed when
  381. // no longer needed.
  382. //
  383. // Returns:
  384. // S_OK if *pbstrOut has been set to a new BSTR
  385. //
  386. // Notes:
  387. // This function should return an error if the element
  388. // contains things other than NODE_TEXT children.
  389. //
  390. HRESULT
  391. HrGetTextValueFromElement(IXMLDOMNode * pxdn,
  392. BSTR * pbstrOut)
  393. {
  394. Assert(pxdn);
  395. Assert(pbstrOut);
  396. HRESULT hr;
  397. VARIANT varOut;
  398. *pbstrOut = NULL;
  399. hr = HrGetTypedValueFromElement(pxdn,
  400. pszTextType,
  401. &varOut);
  402. if (S_OK == hr)
  403. {
  404. Assert(VT_BSTR == V_VT(&varOut));
  405. *pbstrOut = V_BSTR(&varOut);
  406. // note: Don't clear varOut, since it's the returned BSTR
  407. }
  408. TraceErrorOptional("HrGetTextValueFromElement", hr, (S_FALSE == hr));
  409. return hr;
  410. }
  411. //+---------------------------------------------------------------------------
  412. //
  413. // Function: HrGetTextValueFromChildElement
  414. //
  415. // Purpose: Given an IXMLDOMElement, finds its child element
  416. // of the given name, and extracts the dt:type="string"
  417. // data contained in that child element.
  418. //
  419. // Arguments:
  420. // pxdn The element which should contain the specified
  421. // children
  422. //
  423. // arypszTokens A serial list of child element names that uniquely
  424. // describe the desired element.
  425. //
  426. // cTokens The number of element names contained in
  427. // arypszTokens.
  428. //
  429. // pbstrOut Address of a BSTR that will obtain the
  430. // text value. This must be freed when
  431. // no longer needed.
  432. //
  433. // Returns:
  434. // S_OK if *pbstrOut has been set to a new BSTR
  435. // S_FALSE if the specified element did not exist.
  436. // in this case, *pbstrOut is set to NULL
  437. //
  438. // Notes:
  439. // for example, if the document looked like this:
  440. // <foo><bar>text</bar></foo>
  441. // and pxdn referred to <foo>, arypszTokens = [ "bar" ]
  442. // and cTokens = 1, this would return "text".
  443. // See the definition of HrGetNestedChildElement() for
  444. // further explination.
  445. //
  446. HRESULT
  447. HrGetTextValueFromChildElement(IXMLDOMNode * pxdn,
  448. const LPCWSTR * arypszTokens,
  449. const ULONG cTokens,
  450. BSTR * pbstrOut)
  451. {
  452. HRESULT hr;
  453. VARIANT varOut;
  454. *pbstrOut = NULL;
  455. hr = HrGetTypedValueFromChildElement(pxdn,
  456. arypszTokens,
  457. cTokens,
  458. pszTextType,
  459. &varOut);
  460. if (S_OK == hr)
  461. {
  462. Assert(VT_BSTR == V_VT(&varOut));
  463. *pbstrOut = V_BSTR(&varOut);
  464. // note: Don't clear varOut, since it's the returned BSTR
  465. }
  466. TraceErrorOptional("HrGetTextValueFromChildElement", hr, (S_FALSE == hr));
  467. return hr;
  468. }
  469. //+---------------------------------------------------------------------------
  470. //
  471. // Function: FIsThisTheNodeName
  472. //
  473. // Purpose: Returns TRUE if the tag name of the specified XML DOM
  474. // element is the specified string.
  475. //
  476. // Arguments:
  477. // pxdn Node whose name is being tested
  478. //
  479. // pszNodeName Proposed name of the node
  480. //
  481. // Returns:
  482. // TRUE if the node name matches, FALSE if not
  483. //
  484. // Notes:
  485. // for the xml node declared as
  486. // <ab:foo xmlns:ab="urn:...">...</ab:foo>
  487. // IsThisTheNodeName() will return TRUE only when
  488. // pszNodeName == T"foo";
  489. BOOL
  490. FIsThisTheNodeName(IXMLDOMNode * pxdn,
  491. LPCWSTR pszNodeName)
  492. {
  493. Assert(pxdn);
  494. Assert(pszNodeName);
  495. HRESULT hr;
  496. BSTR bstrNodeName;
  497. BOOL fResult;
  498. int result;
  499. bstrNodeName = NULL;
  500. hr = pxdn->get_baseName(&bstrNodeName);
  501. if (SUCCEEDED(hr))
  502. {
  503. Assert(bstrNodeName);
  504. result = wcscmp(bstrNodeName, pszNodeName);
  505. ::SysFreeString(bstrNodeName);
  506. return (result == 0) ? TRUE : FALSE;
  507. }
  508. TraceError("OBJ: FIsThisTheNodeName - get_baseName", hr);
  509. Assert(!bstrNodeName);
  510. return FALSE;
  511. }
  512. //+---------------------------------------------------------------------------
  513. //
  514. // Function: FIsThisTheNodeNameWithNamespace
  515. //
  516. // Purpose: Returns TRUE if the element name of the specified XML DOM
  517. // element is the specified string and is in a given namespace.
  518. //
  519. // Arguments:
  520. // pxdn Node whose name is being tested
  521. // pszNodeName Proposed base name of the node
  522. // pszNamespaceURI Proposed namespace URI of the node
  523. //
  524. // Returns:
  525. // TRUE if the node name matches, FALSE if not
  526. //
  527. // Notes:
  528. // for the xml node declared as
  529. // <ab:foo xmlns:ab="urn:...">...</ab:foo>
  530. // FIsThisTheNodeNameWithNamespace() will return TRUE only when
  531. // pszNodeName == L"foo" and pszNamespaceURI == L"urn:..."
  532. BOOL
  533. FIsThisTheNodeNameWithNamespace(IXMLDOMNode * pxdn,
  534. LPCWSTR pszNodeName,
  535. LPCWSTR pszNamespaceURI)
  536. {
  537. Assert(pxdn);
  538. Assert(pszNodeName);
  539. Assert(pszNamespaceURI);
  540. HRESULT hr = S_OK;;
  541. BSTR bstrNodeName = NULL;
  542. BSTR bstrNamespaceURI = NULL;
  543. BOOL fResult = FALSE;
  544. hr = pxdn->get_baseName(&bstrNodeName);
  545. if (SUCCEEDED(hr))
  546. {
  547. if (bstrNodeName)
  548. {
  549. hr = pxdn->get_namespaceURI(&bstrNamespaceURI);
  550. if (SUCCEEDED(hr))
  551. {
  552. if (bstrNamespaceURI)
  553. {
  554. if ((lstrcmpW(bstrNodeName, pszNodeName) == 0) &&
  555. (lstrcmpW(bstrNamespaceURI, pszNamespaceURI) == 0))
  556. {
  557. fResult = TRUE;
  558. }
  559. ::SysFreeString(bstrNamespaceURI);
  560. }
  561. }
  562. ::SysFreeString(bstrNodeName);
  563. }
  564. }
  565. TraceError("FIsThisTheNodeNameWithNamespace(): "
  566. "Exiting", hr);
  567. return fResult;
  568. }
  569. //+---------------------------------------------------------------------------
  570. //
  571. // Function: FIsThisTheNodeTextValue
  572. //
  573. // Purpose: Determines whether the text value of a node matches a specified
  574. // value
  575. //
  576. // Arguments:
  577. // pxdn [in] The node to check
  578. // cszTextValue [in] The value to compare against
  579. //
  580. // Returns:
  581. // TRUE if the node's text value matches the value specified in cszTextValue
  582. // FALSE otherwise
  583. //
  584. // Author: spather 2000/11/2
  585. //
  586. // Notes:
  587. //
  588. BOOL
  589. FIsThisTheNodeTextValue(
  590. IN IXMLDOMNode * pxdn,
  591. IN LPCWSTR cszTextValue)
  592. {
  593. HRESULT hr = S_OK;
  594. BOOL fResult = FALSE;
  595. BSTR bstrNodeText = NULL;
  596. Assert(pxdn);
  597. Assert(cszTextValue);
  598. hr = pxdn->get_text(&bstrNodeText);
  599. if (SUCCEEDED(hr))
  600. {
  601. Assert(bstrNodeText);
  602. if (0 == lstrcmpW(bstrNodeText, cszTextValue))
  603. {
  604. fResult = TRUE;
  605. }
  606. SysFreeString(bstrNodeText);
  607. }
  608. else
  609. {
  610. TraceError("FIsThisTheNodeTextValue(): "
  611. "Failed to get node text",
  612. hr);
  613. }
  614. TraceError("FIsThisTheNodeTextValue(): "
  615. "Exiting",
  616. hr);
  617. return fResult;
  618. }
  619. //+---------------------------------------------------------------------------
  620. //
  621. // Function: FAreNodeValuesEqual
  622. //
  623. // Purpose: Compares the text values of two DOM nodes and, if they are
  624. // equal, returns TRUE.
  625. //
  626. // Arguments:
  627. // pxdn1 [in] The first node
  628. // pxdn2 [in] The second node
  629. //
  630. // Returns:
  631. // TRUE if the nodes' text values are equal, false otherwise.
  632. //
  633. // Author: spather 2000/11/2
  634. //
  635. // Notes:
  636. //
  637. BOOL
  638. FAreNodeValuesEqual(
  639. IN IXMLDOMNode * pxdn1,
  640. IN IXMLDOMNode * pxdn2)
  641. {
  642. HRESULT hr = S_OK;
  643. BOOL fResult = FALSE;
  644. BSTR bstrNode1Text = NULL;
  645. BSTR bstrNode2Text = NULL;
  646. hr = pxdn1->get_text(&bstrNode1Text);
  647. if (SUCCEEDED(hr))
  648. {
  649. Assert(bstrNode1Text);
  650. hr = pxdn2->get_text(&bstrNode2Text);
  651. if (SUCCEEDED(hr))
  652. {
  653. Assert(bstrNode2Text);
  654. }
  655. else
  656. {
  657. TraceError("FAreNodeValuesEqual(): "
  658. "Failed to get node 2 text",
  659. hr);
  660. }
  661. }
  662. else
  663. {
  664. TraceError("FAreNodeValuesEqual(): "
  665. "Failed to get node 1 text",
  666. hr);
  667. }
  668. if (SUCCEEDED(hr))
  669. {
  670. if (0 == lstrcmpW(bstrNode1Text, bstrNode2Text))
  671. {
  672. fResult = TRUE;
  673. }
  674. }
  675. if (bstrNode1Text)
  676. {
  677. SysFreeString(bstrNode1Text);
  678. }
  679. if (bstrNode2Text)
  680. {
  681. SysFreeString(bstrNode2Text);
  682. }
  683. TraceError("FAreNodeValuesEqual(): "
  684. "Exiting",
  685. hr);
  686. return fResult;
  687. }
  688. //+---------------------------------------------------------------------------
  689. //
  690. // Function: HrAreThereDuplicatesInChildNodeTextValues
  691. //
  692. // Purpose: Starting from a specified parent node, this function
  693. // examines all child nodes that have a given name, and
  694. // checks their text values to see whether duplicates
  695. // exist (i.e. whether two or more of these child nodes
  696. // have the same text value).
  697. //
  698. // Arguments:
  699. // pxdn [in] The parent node
  700. // cszXSLPattern [in] XSL Pattern identifying the name of the child
  701. // nodes to examine
  702. // fCaseSensitive [in] Indicates whether the text values should be
  703. // examined with a case-sensitive or case-
  704. // insensitive comparison (if TRUE, compare will
  705. // be case-sensitive)
  706. // pfDuplicatesExist [out] Receives a boolean value indicating whether or
  707. // not there were duplicates in the child node
  708. // values. (TRUE if there were duplicates
  709. //
  710. // Returns:
  711. // If the function succeeds, the return value is S_OK. Otherwise, the
  712. // function returns one of the COM error codes defined in WinError.h.
  713. //
  714. // Author: spather 2000/11/5
  715. //
  716. // Notes:
  717. // This function uses a very simple O(n^2) algorithm. Do not use it to
  718. // check nodes that have very many (> 100) children.
  719. HRESULT
  720. HrAreThereDuplicatesInChildNodeTextValues(
  721. IN IXMLDOMNode * pxdn,
  722. IN LPCWSTR cszXSLPattern,
  723. IN BOOL fCaseSensitive,
  724. OUT BOOL * pfDuplicatesExist)
  725. {
  726. HRESULT hr = S_OK;
  727. BOOL fDuplicatesExist = FALSE;
  728. LONG lNumNodes = 0;
  729. BSTR * rgbstrValues = NULL;
  730. BSTR bstrXSLPattern;
  731. IXMLDOMNodeList * pxdnlNodes = NULL;
  732. Assert(pxdn);
  733. Assert(cszXSLPattern);
  734. Assert(pfDuplicatesExist);
  735. // Get the child nodes that match the XSL pattern.
  736. bstrXSLPattern = SysAllocString(cszXSLPattern);
  737. if (bstrXSLPattern)
  738. {
  739. hr = pxdn->selectNodes(bstrXSLPattern, &pxdnlNodes);
  740. SysFreeString(bstrXSLPattern);
  741. }
  742. else
  743. {
  744. hr = E_OUTOFMEMORY;
  745. TraceError("HrAreThereDuplicatesInChildNodeTextValues(): "
  746. "Failed to allocate BSTR for XSL pattern",
  747. hr);
  748. }
  749. // Construct an array of the child nodes' text values.
  750. if (SUCCEEDED(hr))
  751. {
  752. Assert(pxdnlNodes);
  753. hr = pxdnlNodes->get_length(&lNumNodes);
  754. if (SUCCEEDED(hr))
  755. {
  756. if (lNumNodes > 0)
  757. {
  758. rgbstrValues = new BSTR[lNumNodes];
  759. if (rgbstrValues)
  760. {
  761. ZeroMemory(rgbstrValues, lNumNodes * sizeof(BSTR));
  762. for (LONG i = 0; SUCCEEDED(hr) && (i < lNumNodes); i++)
  763. {
  764. IXMLDOMNode * pxdnChild = NULL;
  765. hr = pxdnlNodes->get_item(i, &pxdnChild);
  766. if (SUCCEEDED(hr))
  767. {
  768. Assert(pxdnChild);
  769. hr = pxdnChild->get_text(&rgbstrValues[i]);
  770. if (FAILED(hr))
  771. {
  772. TraceError("HrAreThereDuplicatesInChildNodeTextValues(): "
  773. "Failed to get node text value",
  774. hr);
  775. }
  776. pxdnChild->Release();
  777. }
  778. else
  779. {
  780. TraceError("HrAreThereDuplicatesInChildNodeTextValues(): "
  781. "Failed to get list item",
  782. hr);
  783. }
  784. }
  785. }
  786. else
  787. {
  788. hr = E_OUTOFMEMORY;
  789. TraceError("HrAreThereDuplicatesInChildNodeTextValues(): "
  790. "Failed to allocate text value array",
  791. hr);
  792. }
  793. }
  794. }
  795. pxdnlNodes->Release();
  796. }
  797. else
  798. {
  799. TraceError("HrAreThereDuplicatesInChildNodeTextValues(): "
  800. "Failed to get nodes matching XSL pattern",
  801. hr);
  802. }
  803. // Check the array for duplicates.
  804. if (SUCCEEDED(hr) && (lNumNodes > 0))
  805. {
  806. Assert(rgbstrValues);
  807. for (LONG i = 0; i < lNumNodes; i++)
  808. {
  809. for (LONG j = i+1; j < lNumNodes; j++)
  810. {
  811. if (fCaseSensitive)
  812. {
  813. if (0 == lstrcmpW(rgbstrValues[i], rgbstrValues[j]))
  814. {
  815. fDuplicatesExist = TRUE;
  816. break;
  817. }
  818. }
  819. else
  820. {
  821. if (0 == lstrcmpiW(rgbstrValues[i], rgbstrValues[j]))
  822. {
  823. fDuplicatesExist = TRUE;
  824. break;
  825. }
  826. }
  827. }
  828. }
  829. }
  830. // Clean up the array.
  831. if (rgbstrValues)
  832. {
  833. for (LONG i = 0; i < lNumNodes; i++)
  834. {
  835. if (rgbstrValues[i])
  836. {
  837. SysFreeString(rgbstrValues[i]);
  838. rgbstrValues[i] = NULL;
  839. }
  840. }
  841. delete [] rgbstrValues;
  842. rgbstrValues = NULL;
  843. }
  844. if (SUCCEEDED(hr))
  845. {
  846. *pfDuplicatesExist = fDuplicatesExist;
  847. }
  848. TraceError("HrAreThereDuplicatesInChildNodeTextValues(): "
  849. "Exiting",
  850. hr);
  851. return hr;
  852. }
  853. HRESULT
  854. HrGetFirstChildElement(IXMLDOMNode * pxdn,
  855. LPCWSTR pszNodeName,
  856. IXMLDOMNode ** ppxdn)
  857. {
  858. Assert(pxdn);
  859. Assert(pszNodeName);
  860. Assert(ppxdn);
  861. HRESULT hr;
  862. IXMLDOMNode * pxdnChild;
  863. pxdnChild = NULL;
  864. hr = pxdn->get_firstChild(&pxdnChild);
  865. if (FAILED(hr))
  866. {
  867. TraceError("HrGetFirstChildElement - get_firstChild", hr);
  868. pxdnChild = NULL;
  869. goto Cleanup;
  870. }
  871. // maybe there is a child, maybe not.
  872. hr = HrGetChildElement(pxdnChild, pszNodeName, ppxdn);
  873. Cleanup:
  874. SAFE_RELEASE(pxdnChild);
  875. TraceErrorOptional("HrGetFirstChildElement", hr, (S_FALSE == hr));
  876. return hr;
  877. }
  878. HRESULT
  879. HrGetNextChildElement(IXMLDOMNode * pxdnLastChild,
  880. LPCWSTR pszNodeName,
  881. IXMLDOMNode ** ppxdn)
  882. {
  883. Assert(pxdnLastChild);
  884. Assert(pszNodeName);
  885. Assert(ppxdn);
  886. HRESULT hr;
  887. IXMLDOMNode * pxdnChild;
  888. IXMLDOMNode * pxdnResult;
  889. pxdnChild = NULL;
  890. pxdnResult = NULL;
  891. // we're searching for another child.
  892. hr = pxdnLastChild->get_nextSibling(&pxdnChild);
  893. if (FAILED(hr))
  894. {
  895. TraceError("HrGetNextChildElement - get_nextSibling", hr);
  896. pxdnChild = NULL;
  897. // we couldn't get the next child, and we haven't found a result,
  898. // so we need to barf.
  899. goto Cleanup;
  900. }
  901. // maybe there was a next node, maybe there wasn't
  902. hr = HrGetChildElement(pxdnChild, pszNodeName, &pxdnResult);
  903. if (FAILED(hr))
  904. {
  905. goto Cleanup;
  906. }
  907. Cleanup:
  908. Assert(FImplies(S_OK == hr, pxdnResult));
  909. Assert(FImplies(S_OK != hr, !pxdnResult));
  910. SAFE_RELEASE(pxdnChild);
  911. *ppxdn = pxdnResult;
  912. TraceErrorOptional("HrGetNextChildElement", hr, (S_FALSE == hr));
  913. return hr;
  914. }
  915. //+---------------------------------------------------------------------------
  916. //
  917. // Function: HrGetChildElement
  918. //
  919. // Purpose: Returns the first child element of a given name.
  920. //
  921. // Arguments:
  922. // pxdn Node to start searching at. This node and its
  923. // subsequent siblings are searched.
  924. // Note that this MAY be null, in which case this
  925. // function doesn't do much...
  926. //
  927. // pszNodeName Name of the desired child node.
  928. //
  929. // ppxdn On return, contiains an AddRef()'d IXMLDOMNode
  930. // pointer to the child element, if one of the
  931. // given name exists.
  932. //
  933. // Returns:
  934. // S_OK if a matching child node has been found. *ppxdn contains
  935. // a reference to this found node
  936. // S_FALSE if no matching child was found. *ppxdn is set to NULL.
  937. //
  938. // Notes:
  939. // To retrieve a child element declared as <xxxx:yyyy>, pszNodeName
  940. // must be "yyyy".
  941. // TODO: make sure the elements considered match a specified
  942. // namespace uri.
  943. HRESULT
  944. HrGetChildElement(IXMLDOMNode * pxdn,
  945. LPCWSTR pszNodeName,
  946. IXMLDOMNode ** ppxdn)
  947. {
  948. Assert(ppxdn);
  949. Assert(pszNodeName);
  950. HRESULT hr;
  951. IXMLDOMNode * pxdnChild;
  952. if (pxdn)
  953. {
  954. pxdn->AddRef();
  955. }
  956. // in case pxdnChild is NULL
  957. hr = S_FALSE;
  958. pxdnChild = pxdn;
  959. while (pxdnChild)
  960. {
  961. // invaiant: pxdnChild is an addref()'d pointer to the current
  962. // child of interest
  963. IXMLDOMNode * pxdnTemp;
  964. DOMNodeType dnt;
  965. pxdnTemp = NULL;
  966. // get the DOMNodeType
  967. hr = pxdnChild->get_nodeType(&dnt);
  968. if (FAILED(hr))
  969. {
  970. TraceError("HrGetChildElement - get_nodeType", hr);
  971. goto Error;
  972. }
  973. if (NODE_ELEMENT == dnt)
  974. {
  975. if (FIsThisTheNodeName(pxdnChild, pszNodeName))
  976. {
  977. break;
  978. }
  979. }
  980. hr = pxdnChild->get_nextSibling(&pxdnTemp);
  981. if (FAILED(hr))
  982. {
  983. TraceError("HrGetChildElement - get_nextSibling", hr);
  984. // we couldn't get the next child, and we haven't found a result,
  985. // so we need to barf.
  986. goto Error;
  987. }
  988. Assert((S_OK == hr) || (S_FALSE == hr));
  989. Assert(FImplies(S_OK == hr, pxdnTemp));
  990. Assert(FImplies(S_FALSE == hr, !pxdnTemp));
  991. // note: if hr is S_FALSE, we're done
  992. pxdnChild->Release();
  993. pxdnChild = pxdnTemp;
  994. }
  995. Assert((S_OK == hr) || (S_FALSE == hr));
  996. Assert(FImplies(S_OK == hr, pxdnChild));
  997. Assert(FImplies(S_FALSE == hr, !pxdnChild));
  998. // post-loop-condition: pxdnChild is an addref'd pointer to the
  999. // matching child, of type NODE_ELEMENT.
  1000. // if there is no match, pxdnChild is NULL.
  1001. Cleanup:
  1002. *ppxdn = pxdnChild;
  1003. TraceErrorOptional("HrGetChildElement", hr, (S_FALSE == hr));
  1004. return hr;
  1005. Error:
  1006. hr = E_FAIL;
  1007. SAFE_RELEASE(pxdnChild);
  1008. goto Cleanup;
  1009. }
  1010. // Used for Validation duplicate tags in Description Document - Guru
  1011. HRESULT HrIsElementPresentOnce(
  1012. IXMLDOMNode * pxdnRoot,
  1013. LPCWSTR pszNodeName ) {
  1014. HRESULT hr = S_OK ;
  1015. LONG nNumNodes = 0;
  1016. IXMLDOMNodeList * pxdnlNodes = NULL;
  1017. BSTR bstrXSLPattern = NULL;
  1018. bstrXSLPattern = SysAllocString(pszNodeName);
  1019. if (bstrXSLPattern)
  1020. {
  1021. hr = pxdnRoot->selectNodes(bstrXSLPattern, &pxdnlNodes);
  1022. if(hr == S_OK)
  1023. {
  1024. hr = pxdnlNodes->get_length(&nNumNodes);
  1025. if(SUCCEEDED(hr))
  1026. {
  1027. if ( nNumNodes == 1 )
  1028. hr = S_OK;
  1029. else {
  1030. hr = E_FAIL ;
  1031. //hr = S_FALSE;
  1032. }
  1033. }
  1034. }
  1035. else
  1036. hr = E_FAIL;
  1037. }
  1038. else
  1039. {
  1040. hr = E_OUTOFMEMORY;
  1041. TraceError("HrIsElementPresentOnce(): "
  1042. "Failed to allocate BSTR for XSL pattern",
  1043. hr);
  1044. }
  1045. SAFE_RELEASE(pxdnlNodes);
  1046. SysFreeString(bstrXSLPattern);
  1047. TraceErrorOptional("HrIsElementPresentOnce", hr, (S_FALSE == hr));
  1048. return hr;
  1049. }
  1050. // Used for Validation duplicate tags in Description Document - Guru
  1051. HRESULT HrCheckForDuplicateElement(IXMLDOMNode * pxdnRoot,
  1052. LPCWSTR pszNodeName ) {
  1053. HRESULT hr = S_OK ;
  1054. LONG nNumNodes = 0;
  1055. IXMLDOMNodeList * pxdnlNodes = NULL;
  1056. BSTR bstrXSLPattern = NULL;
  1057. bstrXSLPattern = SysAllocString(pszNodeName);
  1058. if (bstrXSLPattern)
  1059. {
  1060. hr = pxdnRoot->selectNodes(bstrXSLPattern, &pxdnlNodes);
  1061. if(hr == S_OK)
  1062. {
  1063. hr = pxdnlNodes->get_length(&nNumNodes);
  1064. if(SUCCEEDED(hr))
  1065. {
  1066. if ( nNumNodes > 1 )
  1067. {
  1068. hr = E_FAIL;
  1069. TraceError("HrCheckForDuplicateElement(): "
  1070. "Duplicate Tag Present",
  1071. hr);
  1072. }
  1073. else {
  1074. hr = S_OK;
  1075. }
  1076. }
  1077. }
  1078. }
  1079. else
  1080. {
  1081. hr = E_OUTOFMEMORY;
  1082. TraceError("HrCheckForDuplicateElement(): "
  1083. "Failed to allocate BSTR for XSL pattern",
  1084. hr);
  1085. }
  1086. SAFE_RELEASE(pxdnlNodes);
  1087. SysFreeString(bstrXSLPattern);
  1088. TraceErrorOptional("HrCheckForDuplicateElement", hr, (S_FALSE == hr));
  1089. return hr;
  1090. }
  1091. //+---------------------------------------------------------------------------
  1092. //
  1093. // Function: HrGetNestedChildElement
  1094. //
  1095. // Purpose: Returns the nth-level child of a particular element,
  1096. // given the name of the element and the name of each
  1097. // intermediate element.
  1098. //
  1099. // Arguments:
  1100. // pxdnRoot The element which should contain the specified
  1101. // children
  1102. //
  1103. // arypszTokens A serial list of child element names that uniquely
  1104. // describe the desired element. See notes below.
  1105. //
  1106. // cTokens The number of element names contained in
  1107. // arypszTokens.
  1108. //
  1109. // ppxdn On return, contiains an AddRef()'d IXMLDOMNode
  1110. // pointer to the child element, if one of the
  1111. // given name exists.
  1112. //
  1113. // Returns:
  1114. // S_OK if a matching child node has been found. *ppxdn contains
  1115. // a reference to this found node
  1116. // S_FALSE if no matching child was found. *ppxdn is set to NULL.
  1117. //
  1118. // Notes:
  1119. // To retrieve a child element declared as <xxxx:yyyy>, pszNodeName
  1120. // must be "yyyy".
  1121. // TODO: make it ensure that there is only ONE matching node!
  1122. // also...
  1123. // TODO: make sure the elements considered match a specified
  1124. // namespace uri.
  1125. //
  1126. // overview of function:
  1127. // given a list of element names and a root element, it returns the
  1128. // "n"th child element of the given element.
  1129. // e.g., if the "root" element passed in was <foo> and the tokens
  1130. // passed in were ["bar","car","dar" and "ear"], and the document
  1131. // looked like the following,
  1132. // <foo> <--- pxdnRoot
  1133. // <bar>
  1134. // <car>
  1135. // <dar>
  1136. // <ear/>
  1137. // </dar>
  1138. // </car>
  1139. // </bar>
  1140. // </foo>
  1141. // this returns the IXMLDOMNode reference to "<ear/>"
  1142. //
  1143. // if more than one of the desired name exists at a given level,
  1144. // this will chose the first element.
  1145. // <foo>
  1146. // <bar>...</bar> <!-- this one is returned -->
  1147. // <bar>...</bar>
  1148. // </foo>
  1149. //
  1150. // TODO: only return an element if its namespace matches the desired
  1151. // namespace uri
  1152. //
  1153. // pszNameSpaceURI = URI of the namespace that each element in the
  1154. // token list must be defined in
  1155. HRESULT
  1156. HrGetNestedChildElement(IXMLDOMNode * pxdnRoot,
  1157. const LPCWSTR * arypszTokens,
  1158. const ULONG cTokens,
  1159. IXMLDOMNode ** ppxdn)
  1160. {
  1161. HRESULT hr;
  1162. IXMLDOMNode * pxdnCurrent;
  1163. ULONG i;
  1164. Assert(pxdnRoot);
  1165. Assert(arypszTokens);
  1166. Assert(cTokens);
  1167. Assert(ppxdn);
  1168. pxdnCurrent = pxdnRoot;
  1169. pxdnCurrent->AddRef();
  1170. i = 0;
  1171. for ( ; i < cTokens; ++i)
  1172. {
  1173. Assert(arypszTokens[i]);
  1174. IXMLDOMNode * pxdnChild;
  1175. pxdnChild = NULL;
  1176. hr = HrGetFirstChildElement(pxdnCurrent, arypszTokens[i], &pxdnChild);
  1177. if (FAILED(hr))
  1178. {
  1179. goto Error;
  1180. }
  1181. else if (S_FALSE == hr)
  1182. {
  1183. // the child of the specified name doesn't exist. well, then...
  1184. // we return S_FALSE and NULL in this case, which is what
  1185. // we happen to have in hr and will put in pxdnCurrent
  1186. goto Error;
  1187. }
  1188. Assert(pxdnChild);
  1189. pxdnCurrent->Release();
  1190. pxdnCurrent = pxdnChild;
  1191. }
  1192. Assert(pxdnCurrent);
  1193. // pxdnCurrent is the desired child
  1194. Cleanup:
  1195. Assert(FImplies((S_OK == hr), pxdnCurrent));
  1196. Assert(FImplies(S_OK != hr, !pxdnCurrent));
  1197. *ppxdn = pxdnCurrent;
  1198. TraceErrorOptional("HrGetNestedChildElement", hr, (S_FALSE == hr));
  1199. return hr;
  1200. Error:
  1201. // we didn't find a match
  1202. SAFE_RELEASE(pxdnCurrent);
  1203. goto Cleanup;
  1204. }
  1205. // return values:
  1206. // S_OK all values have been read
  1207. // error some element couldn't be read.
  1208. // the returned array is undefined.
  1209. HRESULT
  1210. HrReadElementWithParseData (IXMLDOMNode * pxdn,
  1211. const ULONG cElems,
  1212. const DevicePropertiesParsingStruct dppsInfo [],
  1213. LPCWSTR pszBaseUrl,
  1214. LPWSTR arypszReadValues [])
  1215. {
  1216. Assert(pxdn);
  1217. Assert(cElems);
  1218. Assert(dppsInfo);
  1219. Assert(arypszReadValues);
  1220. HRESULT hr = S_OK;
  1221. ULONG i;
  1222. ::ZeroMemory(arypszReadValues, sizeof(LPWSTR *) * cElems);
  1223. i = 0;
  1224. for ( ; i < cElems; ++i )
  1225. {
  1226. BSTR bstrTemp;
  1227. bstrTemp = NULL;
  1228. hr = HrGetTextValueFromChildElement(pxdn,
  1229. &(dppsInfo[i].m_pszTagName),
  1230. 1,
  1231. &bstrTemp);
  1232. Assert(FImplies((S_FALSE == hr), !bstrTemp));
  1233. if (S_OK == hr)
  1234. {
  1235. Assert(bstrTemp);
  1236. if (dppsInfo[i].m_fIsUrl)
  1237. {
  1238. LPWSTR pszTemp;
  1239. pszTemp = NULL;
  1240. if (pszBaseUrl)
  1241. {
  1242. // we have to combine base and relative urls
  1243. hr = HrCombineUrl(pszBaseUrl, bstrTemp, &pszTemp);
  1244. }
  1245. else
  1246. {
  1247. // just copy and validate the url
  1248. hr = HrCopyAndValidateUrl(bstrTemp, &pszTemp);
  1249. if (S_FALSE == hr)
  1250. {
  1251. // invalid urls aren't acceptable
  1252. hr = E_FAIL;
  1253. }
  1254. }
  1255. if (SUCCEEDED(hr))
  1256. {
  1257. arypszReadValues[i] = pszTemp;
  1258. }
  1259. }
  1260. else
  1261. {
  1262. // just copy the value
  1263. arypszReadValues[i] = WszAllocateAndCopyWsz(bstrTemp);
  1264. if (!(arypszReadValues[i]))
  1265. {
  1266. hr = E_OUTOFMEMORY;
  1267. }
  1268. }
  1269. ::SysFreeString(bstrTemp);
  1270. }
  1271. // note: hr may have been changed by the above code
  1272. if (FAILED(hr))
  1273. {
  1274. Assert(!(arypszReadValues[i]));
  1275. goto Error;
  1276. }
  1277. TraceTag(ttidUPnPDescriptionDoc,"XML Tag - %S - %S",dppsInfo[i].m_pszTagName,arypszReadValues[i]);
  1278. }
  1279. if (S_FALSE == hr)
  1280. {
  1281. hr = S_OK;
  1282. }
  1283. Cleanup:
  1284. TraceError("HrReadElementWithParseData", hr);
  1285. return hr;
  1286. Error:
  1287. // we need to free the strings we've already allocated
  1288. {
  1289. ULONG j;
  1290. // i is the number of elements successfully read above
  1291. j = 0;
  1292. for ( ; j < i; ++j )
  1293. {
  1294. Assert(arypszReadValues[j]);
  1295. delete [] arypszReadValues[j];
  1296. arypszReadValues[j] = NULL;
  1297. }
  1298. }
  1299. goto Cleanup;
  1300. }
  1301. // Used for Validation duplicate tags in Description Document - Guru
  1302. // If the element is optional we check if the Tag is present At most Once
  1303. // IF the element is required we check if its present exactly once
  1304. HRESULT HrAreElementTagsValid(IXMLDOMNode *pxdnRoot,
  1305. const ULONG cElems,
  1306. const DevicePropertiesParsingStruct dppsInfo [])
  1307. {
  1308. HRESULT hr = S_OK;
  1309. ULONG i;
  1310. i = 0;
  1311. for ( ; i < cElems; ++i )
  1312. {
  1313. if (dppsInfo[i].m_fOptional)
  1314. {
  1315. hr = HrCheckForDuplicateElement(pxdnRoot,dppsInfo[i].m_pszTagName);
  1316. }
  1317. else {
  1318. hr = HrIsElementPresentOnce(pxdnRoot,dppsInfo[i].m_pszTagName);
  1319. }
  1320. if(FAILED(hr))
  1321. break;
  1322. }
  1323. TraceError("HrAreElementTagsValid", hr);
  1324. return hr;
  1325. }
  1326. // return TRUE if all of the non-optional values (as specified by the
  1327. // m_fOptional in dppsInfo) are non-null in arypszReadValues.
  1328. // equivalently, it returns FALSE if there exists a value marked as
  1329. // required (m_fOptional == FALSE) that is NULL in arypszReadValues.
  1330. BOOL
  1331. fAreReadValuesComplete (const ULONG cElems,
  1332. const DevicePropertiesParsingStruct dppsInfo [],
  1333. const LPCWSTR arypszReadValues [])
  1334. {
  1335. ULONG i;
  1336. BOOL fResult;
  1337. fResult = TRUE;
  1338. i = 0;
  1339. for ( ; i < cElems; ++i )
  1340. {
  1341. if (!dppsInfo[i].m_fOptional)
  1342. {
  1343. if (!(arypszReadValues[i]))
  1344. {
  1345. fResult = FALSE;
  1346. break;
  1347. }
  1348. }
  1349. }
  1350. return fResult;
  1351. }
  1352. //+---------------------------------------------------------------------------
  1353. //
  1354. // Function: HrGetTextValueFromAttribute
  1355. //
  1356. // Purpose: Retrieves the text value of an attribute on a DOM element node
  1357. //
  1358. // Arguments:
  1359. // pxdn [in] XML DOM Node representing the element whose
  1360. // attribute is to be read
  1361. // szAttrName [in] Name of the attribute
  1362. // pbstrAttrValue [in] Receives the attribute text value.
  1363. //
  1364. // Returns:
  1365. // If the function successfully reads the attribute value, the return value
  1366. // is S_OK and the attribute value is returned at pbstrAttrValue. If the
  1367. // attribute does not exist on the element, the function returns S_FALSE,
  1368. // and NULL at pbstrAttrValue. Otherwise the function returns one of the
  1369. // error codes defined in WinError.h.
  1370. //
  1371. // Author: spather 2000/11/9
  1372. //
  1373. // Notes:
  1374. //
  1375. HRESULT
  1376. HrGetTextValueFromAttribute(
  1377. IN IXMLDOMNode * pxdn,
  1378. IN LPCWSTR szAttrName,
  1379. OUT BSTR * pbstrAttrValue)
  1380. {
  1381. HRESULT hr = S_OK;
  1382. IXMLDOMElement * pxde = NULL;
  1383. BSTR bstrValue = NULL;
  1384. hr = pxdn->QueryInterface(IID_IXMLDOMElement,
  1385. (void **) &pxde);
  1386. if (SUCCEEDED(hr))
  1387. {
  1388. BSTR bstrAttrName = NULL;
  1389. Assert(pxde);
  1390. bstrAttrName = SysAllocString(szAttrName);
  1391. if (bstrAttrName)
  1392. {
  1393. VARIANT varValue;
  1394. VariantInit(&varValue);
  1395. hr = pxde->getAttribute(bstrAttrName,
  1396. &varValue);
  1397. if (SUCCEEDED(hr))
  1398. {
  1399. if (VT_NULL == varValue.vt)
  1400. {
  1401. Assert(S_FALSE == hr);
  1402. }
  1403. else if (VT_BSTR == varValue.vt)
  1404. {
  1405. bstrValue = V_BSTR(&varValue);
  1406. }
  1407. else
  1408. {
  1409. Assert(FALSE); // Should never be here.
  1410. }
  1411. }
  1412. else
  1413. {
  1414. TraceError("HrGetTextValueFromAttribute(): "
  1415. "Failed to get attribute value",
  1416. hr);
  1417. }
  1418. SysFreeString(bstrAttrName);
  1419. }
  1420. else
  1421. {
  1422. hr = E_OUTOFMEMORY;
  1423. TraceError("HrGetTextValueFromAttribute(): "
  1424. "Failed to allocate attribute name BSTR",
  1425. hr);
  1426. }
  1427. pxde->Release();
  1428. }
  1429. else
  1430. {
  1431. TraceError("HrGetTextValueFromAttribute(): "
  1432. "Failed to get IXMLDOMElement interface",
  1433. hr);
  1434. }
  1435. if (SUCCEEDED(hr))
  1436. {
  1437. *pbstrAttrValue = bstrValue;
  1438. }
  1439. else
  1440. {
  1441. if (bstrValue)
  1442. {
  1443. SysFreeString(bstrValue);
  1444. }
  1445. }
  1446. TraceHr(ttidError, FAL, hr, (hr == S_FALSE), "HrGetTextValueFromAttribute");
  1447. return hr;
  1448. }
  1449. /*
  1450. * Function: HrSetTextAttribute()
  1451. *
  1452. * Purpose: Sets an text-valued attribute on an XML element.
  1453. *
  1454. * Arguments:
  1455. * pElement [in] The element on which to set the attribute
  1456. * pcwszAttrName [in] The attribute name
  1457. * pcwszValue [in] The value to give the attribute
  1458. *
  1459. * Returns:
  1460. * S_OK if successful, other HRESULT otherwise.
  1461. */
  1462. HRESULT
  1463. HrSetTextAttribute(
  1464. IXMLDOMElement * pElement,
  1465. LPCWSTR pcwszAttrName,
  1466. LPCWSTR pcwszValue)
  1467. {
  1468. HRESULT hr = S_OK;
  1469. VARIANT vValue;
  1470. BSTR bstrValue, bstrAttrName;
  1471. // Allocate BSTRs for the attribute name and value.
  1472. bstrValue = SysAllocString(pcwszValue);
  1473. if (bstrValue)
  1474. {
  1475. bstrAttrName = SysAllocString(pcwszAttrName);
  1476. if (!bstrAttrName)
  1477. {
  1478. hr = E_OUTOFMEMORY;
  1479. SysFreeString(bstrValue);
  1480. }
  1481. }
  1482. else
  1483. {
  1484. hr = E_OUTOFMEMORY;
  1485. }
  1486. if (SUCCEEDED(hr))
  1487. {
  1488. // danielwe: Bug 144883
  1489. VariantInit(&vValue);
  1490. vValue.vt = VT_BSTR;
  1491. V_BSTR(&vValue) = bstrValue;
  1492. hr = pElement->setAttribute(bstrAttrName, vValue);
  1493. SysFreeString(bstrValue);
  1494. SysFreeString(bstrAttrName);
  1495. }
  1496. return hr;
  1497. }
  1498. /*
  1499. * Function: HrCreateElementWithType()
  1500. *
  1501. * Purpose: Creates an XML element containing a binary value that is
  1502. * encoded in bin.base64.
  1503. *
  1504. * Arguments:
  1505. * pDoc [in] Document in which to create the element
  1506. * pcwszElementName [in] Name for the new element
  1507. * pszDataType [in] The type of the data encoded in the element.
  1508. * varData [in] Data to insert as the element's value
  1509. * ppElement [out] Address at which to place the pointer to the
  1510. * new element object
  1511. *
  1512. * Returns:
  1513. * S_OK if successful, other HRESULT otherwise.
  1514. *
  1515. * Notes:
  1516. * This function does not actually insert the new element into the document
  1517. * tree.
  1518. */
  1519. HRESULT
  1520. HrCreateElementWithType(
  1521. IN IXMLDOMDocument * pDoc,
  1522. IN LPCWSTR pcwszElementName,
  1523. IN LPCWSTR pszDataType,
  1524. IN VARIANT varData,
  1525. OUT IXMLDOMElement ** ppElement)
  1526. {
  1527. Assert(pDoc);
  1528. Assert(pcwszElementName);
  1529. Assert(pszDataType);
  1530. Assert(ppElement);
  1531. HRESULT hr;
  1532. BSTR bstrElementName;
  1533. hr = S_OK;
  1534. *ppElement = NULL;
  1535. bstrElementName = SysAllocString(pcwszElementName);
  1536. if (bstrElementName)
  1537. {
  1538. IXMLDOMElement * pElement = NULL;
  1539. hr = pDoc->createElement(bstrElementName,
  1540. &pElement);
  1541. if (SUCCEEDED(hr))
  1542. {
  1543. BSTR bstrDataType;
  1544. bstrDataType = ::SysAllocString(pszDataType);
  1545. if (bstrDataType)
  1546. {
  1547. hr = pElement->put_dataType(bstrDataType);
  1548. if (SUCCEEDED(hr))
  1549. {
  1550. hr = pElement->put_nodeTypedValue(varData);
  1551. if (SUCCEEDED(hr))
  1552. {
  1553. *ppElement = pElement;
  1554. pElement->AddRef();
  1555. }
  1556. else
  1557. {
  1558. TraceError("HrCreateElementWithType(): "
  1559. "put_nodeTypedValue", hr);
  1560. }
  1561. }
  1562. else
  1563. {
  1564. TraceError("HrCreateElementWithType(): "
  1565. "put_dataType", hr);
  1566. }
  1567. ::SysFreeString(bstrDataType);
  1568. }
  1569. else
  1570. {
  1571. hr = E_OUTOFMEMORY;
  1572. TraceError("HrCreateElementWithType(): "
  1573. "SysAllocString", hr);
  1574. }
  1575. pElement->Release();
  1576. }
  1577. else
  1578. {
  1579. TraceError("HrCreateElementWithType(): "
  1580. "Could not create element", hr);
  1581. }
  1582. ::SysFreeString(bstrElementName);
  1583. }
  1584. else
  1585. {
  1586. hr = E_OUTOFMEMORY;
  1587. TraceError("HrCreateElementWithType(): "
  1588. "Could not allocate BSTR", hr);
  1589. }
  1590. return hr;
  1591. }
  1592. /*
  1593. * Function: HrCreateElementWithTextValue()
  1594. *
  1595. * Purpose: Creates an XML element containing a text string as
  1596. * a value.
  1597. *
  1598. * Arguments:
  1599. * pDoc [in] Document in which to create the element
  1600. * pcwszElementName [in] Name for the new element
  1601. * pcwszTextValue [in] Text to insert as the element's value
  1602. * ppElement [out] Address at which to place the pointer to the
  1603. * new element object
  1604. *
  1605. * Returns:
  1606. * S_OK if successful, other HRESULT otherwise.
  1607. *
  1608. * Notes:
  1609. * This function does not actually insert the new element into the document
  1610. * tree.
  1611. */
  1612. HRESULT
  1613. HrCreateElementWithTextValue(
  1614. IN IXMLDOMDocument * pDoc,
  1615. IN LPCWSTR pcwszElementName,
  1616. IN LPCWSTR pcwszTextValue,
  1617. OUT IXMLDOMElement ** ppElement)
  1618. {
  1619. HRESULT hr = S_OK;
  1620. IXMLDOMElement * pElement = NULL;
  1621. BSTR bstrElementName, bstrTextValue;
  1622. *ppElement = NULL;
  1623. // Allocate BSTRs for the element name and value.
  1624. bstrElementName = SysAllocString(pcwszElementName);
  1625. if (bstrElementName)
  1626. {
  1627. bstrTextValue = SysAllocString(pcwszTextValue);
  1628. if (!bstrTextValue)
  1629. {
  1630. hr = E_OUTOFMEMORY;
  1631. SysFreeString(bstrElementName);
  1632. }
  1633. }
  1634. else
  1635. {
  1636. hr = E_OUTOFMEMORY;
  1637. }
  1638. if (SUCCEEDED(hr))
  1639. {
  1640. hr = pDoc->createElement(bstrElementName,
  1641. &pElement);
  1642. if (SUCCEEDED(hr))
  1643. {
  1644. IXMLDOMText * pText = NULL;
  1645. hr = pDoc->createTextNode(bstrTextValue,
  1646. &pText);
  1647. if (SUCCEEDED(hr))
  1648. {
  1649. VARIANT vAfter;
  1650. // danielwe: Bug 144882
  1651. VariantInit(&vAfter);
  1652. vAfter.vt = VT_EMPTY;
  1653. hr = pElement->insertBefore(pText,
  1654. vAfter,
  1655. NULL);
  1656. if (FAILED(hr))
  1657. {
  1658. TraceError("HrCreateElementWithTextValue(): "
  1659. "Failed to insert text node", hr);
  1660. }
  1661. pText->Release();
  1662. }
  1663. else
  1664. {
  1665. TraceError("HrCreateElementWithTextValue(): "
  1666. "Failed to create text node", hr);
  1667. }
  1668. if (SUCCEEDED(hr))
  1669. {
  1670. *ppElement = pElement;
  1671. pElement->AddRef();
  1672. }
  1673. pElement->Release();
  1674. }
  1675. else
  1676. {
  1677. TraceError("HrCreateElementWithTextValue(): "
  1678. "Could not create element", hr);
  1679. }
  1680. SysFreeString(bstrElementName);
  1681. SysFreeString(bstrTextValue);
  1682. }
  1683. else
  1684. {
  1685. TraceError("HrCreateElementWithTextValue(): "
  1686. "Could not allocate BSTRs", hr);
  1687. }
  1688. return hr;
  1689. }
  1690. /*
  1691. * Function: HrCreateElement()
  1692. *
  1693. * Purpose: Create an XML Element DOM object
  1694. *
  1695. * Arguments:
  1696. * pxdd [in] The document object in whose context the
  1697. * element will be created
  1698. * pcwszElementName [in] Name of the element to be created
  1699. * pcwszNamespaceURI [in] URI identifying the namespace to which
  1700. * the element name belongs
  1701. * ppxdnNewElt [out] The newly created element object
  1702. *
  1703. * Return Value:
  1704. * S_OK if successful, other HRESULT otherwise.
  1705. *
  1706. * Notes:
  1707. * (none)
  1708. */
  1709. HRESULT
  1710. HrCreateElement(
  1711. IN IXMLDOMDocument * pxdd,
  1712. IN LPCWSTR pcwszElementName,
  1713. IN LPCWSTR pcwszNamespaceURI,
  1714. OUT IXMLDOMNode ** ppxdnNewElt)
  1715. {
  1716. HRESULT hr = S_OK;
  1717. IXMLDOMNode * pxdnNewElt = NULL;
  1718. BSTR bstrElementName = NULL;
  1719. BSTR bstrNamespaceURI = NULL;
  1720. Assert(pxdd);
  1721. Assert(pcwszElementName);
  1722. Assert(ppxdnNewElt);
  1723. // Allocate BSTRs
  1724. bstrElementName = SysAllocString(pcwszElementName);
  1725. if (bstrElementName)
  1726. {
  1727. if (pcwszNamespaceURI)
  1728. {
  1729. bstrNamespaceURI = SysAllocString(pcwszNamespaceURI);
  1730. if (NULL == bstrNamespaceURI)
  1731. {
  1732. hr = E_OUTOFMEMORY;
  1733. TraceError("HrCreateElement(): "
  1734. "Failed to allocate memory for name string",
  1735. hr);
  1736. }
  1737. }
  1738. if (SUCCEEDED(hr))
  1739. {
  1740. VARIANT varNodeType;
  1741. VariantInit(&varNodeType);
  1742. varNodeType.vt = VT_I4;
  1743. V_I4(&varNodeType) = (int) NODE_ELEMENT;
  1744. hr = pxdd->createNode(varNodeType,
  1745. bstrElementName,
  1746. bstrNamespaceURI,
  1747. &pxdnNewElt);
  1748. if (FAILED(hr))
  1749. {
  1750. TraceError("HrCreateElement(): "
  1751. "Failed to create element node",
  1752. hr);
  1753. }
  1754. if (bstrNamespaceURI)
  1755. {
  1756. SysFreeString(bstrNamespaceURI);
  1757. }
  1758. }
  1759. SysFreeString(bstrElementName);
  1760. }
  1761. else
  1762. {
  1763. hr = E_OUTOFMEMORY;
  1764. TraceError("HrCreateElement(): "
  1765. "Failed to allocate memory for name string",
  1766. hr);
  1767. }
  1768. if (SUCCEEDED(hr))
  1769. {
  1770. *ppxdnNewElt = pxdnNewElt;
  1771. }
  1772. TraceError("HrCreateElement(): "
  1773. "Exiting",
  1774. hr);
  1775. return hr;
  1776. }
  1777. /*
  1778. * Function: HrAppendProcessingInstruction()
  1779. *
  1780. * Purpose: Append a processing instruction to the DOM document
  1781. *
  1782. * Arguments:
  1783. * pxdd [in] The document object in whose context the
  1784. * element will be created
  1785. * pcwszName [in] Name of the processing instruction to be created
  1786. * pcwszValue [in] The text value of the p.i.
  1787. *
  1788. * Return Value:
  1789. * S_OK if successful, other HRESULT otherwise.
  1790. *
  1791. * Notes:
  1792. * (none)
  1793. */
  1794. HRESULT
  1795. HrAppendProcessingInstruction(
  1796. IN IXMLDOMDocument * pxdd,
  1797. IN LPCWSTR pcwszName,
  1798. IN LPCWSTR pcwszValue)
  1799. {
  1800. HRESULT hr = S_OK;
  1801. Assert(pxdd);
  1802. Assert(pcwszName);
  1803. Assert(pcwszValue);
  1804. IXMLDOMProcessingInstruction* pxdpi = NULL;
  1805. BSTR bstrTarget = SysAllocString(pcwszName);
  1806. BSTR bstrData = SysAllocString(pcwszValue);
  1807. if (bstrTarget && bstrData)
  1808. {
  1809. hr = pxdd->createProcessingInstruction(bstrTarget, bstrData, &pxdpi);
  1810. }
  1811. else
  1812. {
  1813. hr = E_OUTOFMEMORY;
  1814. }
  1815. SysFreeString(bstrTarget);
  1816. SysFreeString(bstrData);
  1817. if (SUCCEEDED(hr))
  1818. {
  1819. IXMLDOMNode* pxdn = NULL;
  1820. hr = pxdpi->QueryInterface(IID_IXMLDOMNode, (void**)&pxdn);
  1821. if (SUCCEEDED(hr))
  1822. {
  1823. hr = pxdd->appendChild(pxdn, NULL);
  1824. pxdn->Release();
  1825. }
  1826. }
  1827. if (pxdpi)
  1828. {
  1829. pxdpi->Release();
  1830. }
  1831. return hr;
  1832. }