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.

1684 lines
45 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 2000.
  5. //
  6. // File: V A L I D A T E S D . C P P
  7. //
  8. // Contents: Implementation of service description validation routines
  9. //
  10. // Notes:
  11. //
  12. // Author: spather 2000/10/17
  13. //
  14. //----------------------------------------------------------------------------
  15. #include <pch.h>
  16. #pragma hdrstop
  17. #include "upnp.h"
  18. #include "Validate.h"
  19. #include "ncxml.h"
  20. #include "ncstring.h"
  21. LPCWSTR CWSZ_UPNP_SERVICE_NAMESPACE = L"urn:schemas-upnp-org:service-1-0";
  22. BOOL
  23. fIsNodeValueInList(
  24. IN IXMLDOMNode * pxdn,
  25. IN BOOL fCaseSensitive,
  26. IN const DWORD cValues,
  27. IN LPCWSTR rgcszValues[],
  28. OUT DWORD * pdwIndex)
  29. {
  30. HRESULT hr = S_OK;
  31. BOOL fResult = FALSE;
  32. BSTR bstrText = NULL;
  33. Assert(cValues > 0);
  34. Assert(rgcszValues);
  35. hr = pxdn->get_text(&bstrText);
  36. if (SUCCEEDED(hr))
  37. {
  38. Assert(bstrText);
  39. for (DWORD i = 0; i < cValues; i++)
  40. {
  41. if (fCaseSensitive)
  42. {
  43. if (0 == lstrcmpW(bstrText, rgcszValues[i]))
  44. {
  45. fResult = TRUE;
  46. *pdwIndex = i;
  47. break;
  48. }
  49. }
  50. else
  51. {
  52. if (0 == lstrcmpiW(bstrText, rgcszValues[i]))
  53. {
  54. fResult = TRUE;
  55. *pdwIndex = i;
  56. break;
  57. }
  58. }
  59. }
  60. SysFreeString(bstrText);
  61. }
  62. else
  63. {
  64. TraceError("fIsNodeValueInList(): "
  65. "Failed to get node text",
  66. hr);
  67. }
  68. TraceError("fIsNodeValueInList(): "
  69. "Before returning",
  70. hr);
  71. return fResult;
  72. }
  73. LPWSTR
  74. SzAllocateErrorString(
  75. LPCWSTR cszError,
  76. LPCWSTR cszOptionalParam)
  77. {
  78. DWORD cchError = 0;
  79. LPWSTR szError = NULL;
  80. Assert(cszError);
  81. cchError = lstrlenW(cszError);
  82. if (cszOptionalParam)
  83. {
  84. cchError += lstrlenW(cszOptionalParam);
  85. }
  86. szError = new WCHAR[cchError+1];
  87. if (szError)
  88. {
  89. if (cszOptionalParam)
  90. {
  91. wsprintf(szError, cszError, cszOptionalParam);
  92. }
  93. else
  94. {
  95. lstrcpyW(szError, cszError);
  96. }
  97. }
  98. else
  99. {
  100. TraceTag(ttidValidate,
  101. "SzAllocateErrorString(): "
  102. "Failed to allocate memory to duplicate error string %S",
  103. cszError);
  104. }
  105. return szError;
  106. }
  107. inline
  108. HRESULT
  109. HrAllocErrorStringAndReturnHr(
  110. HRESULT hrDesired,
  111. LPCWSTR cszErrorText,
  112. LPCWSTR cszOptonalParam,
  113. LPWSTR* pszError)
  114. {
  115. HRESULT hr = hrDesired;
  116. Assert(pszError);
  117. Assert(cszErrorText);
  118. *pszError = SzAllocateErrorString(cszErrorText, cszOptonalParam);
  119. if (NULL == *pszError)
  120. {
  121. hr = E_OUTOFMEMORY;
  122. }
  123. return hr;
  124. }
  125. HRESULT
  126. HrValidateName(
  127. IN IXMLDOMNode * pxdnName,
  128. IN BOOL fCheckAUnderscore,
  129. OUT LPWSTR * pszError)
  130. {
  131. HRESULT hr = S_OK;
  132. LPWSTR szError = NULL;
  133. BSTR bstrName = NULL;
  134. Assert(pszError);
  135. hr = pxdnName->get_text(&bstrName);
  136. if (SUCCEEDED(hr))
  137. {
  138. Assert(bstrName);
  139. if (0 == lstrcmpW(bstrName, L""))
  140. {
  141. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  142. L"<name> element was empty",
  143. NULL, &szError);
  144. }
  145. #if DBG
  146. if (SUCCEEDED(hr))
  147. {
  148. if (lstrlenW(bstrName) >= 32)
  149. {
  150. HRESULT hrTemp = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  151. L"<name> element should be less than 32 characters",
  152. NULL, &szError);
  153. if(SUCCEEDED(hrTemp))
  154. {
  155. char * szaError = SzFromWsz(szError);
  156. if(szaError)
  157. {
  158. TraceTag(ttidDefault, szaError);
  159. delete [] szaError;
  160. }
  161. delete [] szError;
  162. szError = NULL;
  163. }
  164. }
  165. }
  166. #endif
  167. if (SUCCEEDED(hr))
  168. {
  169. WCHAR * sz = bstrName;
  170. int i = 0;
  171. BOOL hasHyphen = FALSE;
  172. while (sz[i])
  173. {
  174. if (sz[i] == L'-')
  175. {
  176. hasHyphen = TRUE;
  177. break;
  178. }
  179. i++;
  180. }
  181. if (hasHyphen)
  182. {
  183. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  184. L"<name> element may not contain '-'",
  185. NULL, &szError);
  186. }
  187. }
  188. SysFreeString(bstrName);
  189. }
  190. else
  191. {
  192. TraceError("HrValidateName(): "
  193. "Failed to get node text",
  194. hr);
  195. }
  196. if (szError)
  197. {
  198. *pszError = szError;
  199. }
  200. TraceError("HrValidateName(): "
  201. "Exiting",
  202. hr);
  203. return hr;
  204. }
  205. HRESULT
  206. HrValidateRelatedStateVariable(
  207. IN IXMLDOMNode * pxdnRSV,
  208. IN IXMLDOMNode * pxdnSST,
  209. OUT LPWSTR * pszError)
  210. {
  211. HRESULT hr = S_OK;
  212. LPWSTR szError = NULL;
  213. BSTR bstrRSVName = NULL;
  214. Assert(pszError);
  215. hr = HrGetTextValueFromElement(pxdnRSV, &bstrRSVName);
  216. if (SUCCEEDED(hr))
  217. {
  218. BSTR bstrXSLPattern = NULL;
  219. Assert(bstrRSVName);
  220. bstrXSLPattern = SysAllocString(L"stateVariable/name");
  221. if (bstrXSLPattern)
  222. {
  223. IXMLDOMNodeList * pxdnlSVNames = NULL;
  224. hr = pxdnSST->selectNodes(bstrXSLPattern, &pxdnlSVNames);
  225. if (SUCCEEDED(hr))
  226. {
  227. LONG listLength = 0;
  228. Assert(pxdnlSVNames);
  229. hr = pxdnlSVNames->get_length(&listLength);
  230. if (SUCCEEDED(hr))
  231. {
  232. BOOL fMatch = FALSE;
  233. for (LONG i = 0;
  234. SUCCEEDED(hr) && (i < listLength) && (FALSE == fMatch);
  235. i++)
  236. {
  237. IXMLDOMNode * pxdnName = NULL;
  238. hr = pxdnlSVNames->get_item(i, &pxdnName);
  239. if (SUCCEEDED(hr))
  240. {
  241. Assert(pxdnName);
  242. fMatch = fMatch ||FIsThisTheNodeTextValue(pxdnName,
  243. bstrRSVName);
  244. pxdnName->Release();
  245. }
  246. else
  247. {
  248. TraceError("HrValidateRelatedStateVariable(): "
  249. "Failed to get list item",
  250. hr);
  251. }
  252. }
  253. if (FALSE == fMatch)
  254. {
  255. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  256. L"<relatedStateVariable> '%s' did not contain the "
  257. L"name of a state variable", bstrRSVName, &szError);
  258. }
  259. }
  260. else
  261. {
  262. TraceError("HrValidateRelatedStateVariable(): "
  263. "Failed to get list length",
  264. hr);
  265. }
  266. pxdnlSVNames->Release();
  267. }
  268. else
  269. {
  270. TraceError("HrValidateRelatedStateVariable(): "
  271. "Failed to select nodes",
  272. hr);
  273. }
  274. SysFreeString(bstrXSLPattern);
  275. }
  276. else
  277. {
  278. hr = E_OUTOFMEMORY;
  279. TraceError("HrValidateRelatedStateVariable(): "
  280. "Failed to allocate XSL pattern string",
  281. hr);
  282. }
  283. SysFreeString(bstrRSVName);
  284. }
  285. if (szError)
  286. {
  287. *pszError = szError;
  288. }
  289. TraceError("HrValidateRelatedStateVariable(): "
  290. "Exiting(): ",
  291. hr);
  292. return hr;
  293. }
  294. HRESULT
  295. HrValidateArg(
  296. IN IXMLDOMNode * pxdnArg,
  297. IN IXMLDOMNode * pxdnSST,
  298. OUT BOOL * pbIsInArg,
  299. OUT BOOL * pbIsRetval,
  300. OUT LPWSTR * pszError)
  301. {
  302. HRESULT hr = S_OK;
  303. LPWSTR szError = NULL;
  304. IXMLDOMNode * pxdnChild = NULL;
  305. LONG lFoundName = 0;
  306. LONG lFoundDir = 0;
  307. LONG lFoundRSV = 0;
  308. BOOL bFoundRetval = FALSE;
  309. Assert(pbIsInArg);
  310. Assert(pbIsRetval);
  311. Assert(pszError);
  312. hr = pxdnArg->get_firstChild(&pxdnChild);
  313. while (SUCCEEDED(hr) && pxdnChild)
  314. {
  315. IXMLDOMNode * pxdnNextSibling = NULL;
  316. if (FIsThisTheNodeNameWithNamespace(pxdnChild,
  317. L"name",
  318. CWSZ_UPNP_SERVICE_NAMESPACE))
  319. {
  320. lFoundName++;
  321. hr = HrValidateName(pxdnChild, FALSE, &szError);
  322. }
  323. else if (FIsThisTheNodeNameWithNamespace(pxdnChild,
  324. L"direction",
  325. CWSZ_UPNP_SERVICE_NAMESPACE))
  326. {
  327. lFoundDir++;
  328. if (FIsThisTheNodeTextValue(pxdnChild, L"in"))
  329. {
  330. *pbIsInArg = TRUE;
  331. }
  332. else if (FIsThisTheNodeTextValue(pxdnChild, L"out"))
  333. {
  334. *pbIsInArg = FALSE;
  335. }
  336. else
  337. {
  338. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  339. L"<direction> contained invalid value."
  340. L" Must be \"in\" or \"out\".", NULL, &szError);
  341. }
  342. }
  343. else if (FIsThisTheNodeNameWithNamespace(pxdnChild,
  344. L"retval",
  345. CWSZ_UPNP_SERVICE_NAMESPACE))
  346. {
  347. BSTR bstrText = NULL;
  348. bFoundRetval = TRUE;
  349. hr = pxdnChild->get_text(&bstrText);
  350. if (SUCCEEDED(hr))
  351. {
  352. Assert(bstrText);
  353. if (0 == lstrcmpW(bstrText, L""))
  354. {
  355. *pbIsRetval = TRUE;
  356. }
  357. else
  358. {
  359. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  360. L"<retval> contained a text value. Must be empty.",
  361. NULL, &szError);
  362. }
  363. SysFreeString(bstrText);
  364. }
  365. else
  366. {
  367. TraceError("HrValidateArg(): "
  368. "Failed to get text of <retval> element",
  369. hr);
  370. }
  371. }
  372. else if (FIsThisTheNodeNameWithNamespace(pxdnChild,
  373. L"relatedStateVariable",
  374. CWSZ_UPNP_SERVICE_NAMESPACE))
  375. {
  376. lFoundRSV++;
  377. hr = HrValidateRelatedStateVariable(pxdnChild, pxdnSST, &szError);
  378. }
  379. if (FAILED(hr))
  380. {
  381. pxdnChild->Release();
  382. break;
  383. }
  384. hr = pxdnChild->get_nextSibling(&pxdnNextSibling);
  385. pxdnChild->Release();
  386. pxdnChild = pxdnNextSibling;
  387. }
  388. if (SUCCEEDED(hr))
  389. {
  390. if (1 != lFoundName)
  391. {
  392. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  393. L"<argument> did not contain exactly one <name> element",
  394. NULL, &szError);
  395. }
  396. else if (1 != lFoundDir)
  397. {
  398. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  399. L"<argument> did not contain exactly one <directio> element",
  400. NULL, &szError);
  401. }
  402. else if (1 != lFoundRSV)
  403. {
  404. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  405. L"<argument> did not contain exactly one "
  406. L"<relatedStateVariable> element", NULL, &szError);
  407. }
  408. else
  409. {
  410. hr = S_OK;
  411. if (FALSE == bFoundRetval)
  412. {
  413. *pbIsRetval = FALSE;
  414. }
  415. }
  416. }
  417. if (szError)
  418. {
  419. *pszError = szError;
  420. }
  421. TraceError("HrValidateArg(): "
  422. "Exiting",
  423. hr);
  424. return hr;
  425. }
  426. HRESULT
  427. HrValidateArgList(
  428. IN IXMLDOMNode * pxdnArgList,
  429. IN IXMLDOMNode * pxdnSST,
  430. OUT LPWSTR * pszError)
  431. {
  432. HRESULT hr = S_OK;
  433. LPWSTR szError = NULL;
  434. IXMLDOMNode * pxdnChild = NULL;
  435. BOOL bFoundArg = FALSE;
  436. BOOL bIsInArg = FALSE;
  437. BOOL bIsRetval = FALSE;
  438. BOOL bFoundAtLeastOneOutArg = FALSE;
  439. Assert(pszError);
  440. hr = pxdnArgList->get_firstChild(&pxdnChild);
  441. while (SUCCEEDED(hr) && pxdnChild)
  442. {
  443. IXMLDOMNode * pxdnNextSibling = NULL;
  444. if (FIsThisTheNodeNameWithNamespace(pxdnChild,
  445. L"argument",
  446. CWSZ_UPNP_SERVICE_NAMESPACE))
  447. {
  448. bFoundArg = TRUE;
  449. bIsInArg = FALSE;
  450. bIsRetval = FALSE;
  451. hr = HrValidateArg(pxdnChild,
  452. pxdnSST,
  453. &bIsInArg,
  454. &bIsRetval,
  455. &szError);
  456. if (SUCCEEDED(hr))
  457. {
  458. if (bIsRetval)
  459. {
  460. // <retval> can only appear in the first "out" argument.
  461. if (bIsInArg)
  462. {
  463. // <retval> appeared in an "in" argument.
  464. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  465. L"<retval> element found in \"in\" <argument>",
  466. NULL, &szError);
  467. }
  468. else if (bFoundAtLeastOneOutArg)
  469. {
  470. // <retval> appeared in an "out" argument that was not
  471. // the first one.
  472. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  473. L"<retval> element found in \"out\" <argument> "
  474. L"that was not the first one", NULL, &szError);
  475. }
  476. }
  477. if (SUCCEEDED(hr))
  478. {
  479. if (bIsInArg)
  480. {
  481. if (bFoundAtLeastOneOutArg)
  482. {
  483. // Found an "in" arg after some "out" args.
  484. hr = HrAllocErrorStringAndReturnHr(
  485. UPNP_E_INVALID_DOCUMENT,
  486. L"\"in\" <argument> found after one or "
  487. L"more \"out\" <argument>s", NULL, &szError);
  488. }
  489. }
  490. else
  491. {
  492. bFoundAtLeastOneOutArg = TRUE;
  493. }
  494. }
  495. }
  496. }
  497. if (FAILED(hr))
  498. {
  499. pxdnChild->Release();
  500. break;
  501. }
  502. hr = pxdnChild->get_nextSibling(&pxdnNextSibling);
  503. pxdnChild->Release();
  504. pxdnChild = pxdnNextSibling;
  505. }
  506. if (SUCCEEDED(hr))
  507. {
  508. if (FALSE == bFoundArg)
  509. {
  510. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  511. L"<argumentList> did not contain any <argument> elements",
  512. NULL, &szError);
  513. }
  514. else
  515. {
  516. hr = S_OK;
  517. }
  518. }
  519. if (SUCCEEDED(hr))
  520. {
  521. BOOL bDuplicatesExist = FALSE;
  522. // Check for duplicate names.
  523. hr = HrAreThereDuplicatesInChildNodeTextValues(pxdnArgList,
  524. L"argument/name",
  525. FALSE,
  526. &bDuplicatesExist);
  527. if (SUCCEEDED(hr))
  528. {
  529. if (bDuplicatesExist)
  530. {
  531. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  532. L"<argumentList> contained <argument> elements with "
  533. L"duplicate names", NULL, &szError);
  534. }
  535. }
  536. else
  537. {
  538. TraceError("HrValidateArgList(): "
  539. "Failed to check for duplicate names",
  540. hr);
  541. }
  542. }
  543. if (szError)
  544. {
  545. *pszError = szError;
  546. }
  547. TraceError("HrValidateArgList(): "
  548. "Exiting",
  549. hr);
  550. return hr;
  551. }
  552. HRESULT
  553. HrValidateAction(
  554. IN IXMLDOMNode * pxdnAction,
  555. IN IXMLDOMNode * pxdnSST,
  556. OUT LPWSTR * pszError)
  557. {
  558. HRESULT hr = S_OK;
  559. LPWSTR szError = NULL;
  560. IXMLDOMNode * pxdnChild = NULL;
  561. LONG lFoundName = 0;
  562. Assert(pszError);
  563. hr = pxdnAction->get_firstChild(&pxdnChild);
  564. while (SUCCEEDED(hr) && pxdnChild)
  565. {
  566. IXMLDOMNode * pxdnNextSibling = NULL;
  567. if (FIsThisTheNodeNameWithNamespace(pxdnChild,
  568. L"name",
  569. CWSZ_UPNP_SERVICE_NAMESPACE))
  570. {
  571. lFoundName++;
  572. hr = HrValidateName(pxdnChild, TRUE, &szError);
  573. }
  574. else if (FIsThisTheNodeNameWithNamespace(pxdnChild,
  575. L"argumentList",
  576. CWSZ_UPNP_SERVICE_NAMESPACE))
  577. {
  578. hr = HrValidateArgList(pxdnChild, pxdnSST, &szError);
  579. }
  580. if (FAILED(hr))
  581. {
  582. pxdnChild->Release();
  583. break;
  584. }
  585. hr = pxdnChild->get_nextSibling(&pxdnNextSibling);
  586. pxdnChild->Release();
  587. pxdnChild = pxdnNextSibling;
  588. }
  589. if (SUCCEEDED(hr))
  590. {
  591. if (1 != lFoundName)
  592. {
  593. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L"<action> "
  594. L"did not contain exactly one <name> element", NULL, &szError);
  595. }
  596. else
  597. {
  598. hr = S_OK;
  599. }
  600. }
  601. if (szError)
  602. {
  603. *pszError = szError;
  604. }
  605. TraceError("HrValidateAction(): "
  606. "Exiting",
  607. hr);
  608. return hr;
  609. }
  610. HRESULT
  611. HrValidateActionList(
  612. IN IXMLDOMNode * pxdnActionList,
  613. IN IXMLDOMNode * pxdnSST,
  614. OUT LPWSTR * pszError)
  615. {
  616. HRESULT hr = S_OK;
  617. LPWSTR szError = NULL;
  618. IXMLDOMNode * pxdnChild = NULL;
  619. BOOL bFoundAction = FALSE;
  620. Assert(pszError);
  621. hr = pxdnActionList->get_firstChild(&pxdnChild);
  622. while (SUCCEEDED(hr) && pxdnChild)
  623. {
  624. IXMLDOMNode * pxdnNextSibling = NULL;
  625. if (FIsThisTheNodeNameWithNamespace(pxdnChild,
  626. L"action",
  627. CWSZ_UPNP_SERVICE_NAMESPACE))
  628. {
  629. bFoundAction = TRUE;
  630. hr = HrValidateAction(pxdnChild, pxdnSST, &szError);
  631. }
  632. if (FAILED(hr))
  633. {
  634. pxdnChild->Release();
  635. break;
  636. }
  637. hr = pxdnChild->get_nextSibling(&pxdnNextSibling);
  638. pxdnChild->Release();
  639. pxdnChild = pxdnNextSibling;
  640. }
  641. if (SUCCEEDED(hr))
  642. {
  643. if (FALSE == bFoundAction)
  644. {
  645. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  646. L"<actionList> did not contain any <action> elements",
  647. NULL, &szError);
  648. }
  649. else
  650. {
  651. hr = S_OK;
  652. }
  653. }
  654. if (SUCCEEDED(hr))
  655. {
  656. BOOL bDuplicatesExist = FALSE;
  657. // Check for duplicate names.
  658. hr = HrAreThereDuplicatesInChildNodeTextValues(pxdnActionList,
  659. L"action/name",
  660. FALSE,
  661. &bDuplicatesExist);
  662. if (SUCCEEDED(hr))
  663. {
  664. if (bDuplicatesExist)
  665. {
  666. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  667. L"<actionList> contained <action> elements with duplicate "
  668. L"names", NULL, &szError);
  669. }
  670. }
  671. else
  672. {
  673. TraceError("HrValidateActionList(): "
  674. "Failed to check for duplicate names",
  675. hr);
  676. }
  677. }
  678. if (szError)
  679. {
  680. *pszError = szError;
  681. }
  682. TraceError("HrValidateActionList(): "
  683. "Exiting",
  684. hr);
  685. return hr;
  686. }
  687. HRESULT
  688. HrValidateAllowedValueRange(
  689. IN IXMLDOMNode * pxdnAllowedValueRange,
  690. OUT LPWSTR * pszError)
  691. {
  692. HRESULT hr = S_OK;
  693. LPWSTR szError = NULL;
  694. IXMLDOMNode * pxdnChild = NULL;
  695. LONG lFoundMin = 0;
  696. LONG lFoundMax = 0;
  697. LONG lFoundStep = 0;
  698. Assert(pszError);
  699. hr = pxdnAllowedValueRange->get_firstChild(&pxdnChild);
  700. while (SUCCEEDED(hr) && pxdnChild)
  701. {
  702. IXMLDOMNode * pxdnNextSibling = NULL;
  703. if (FIsThisTheNodeNameWithNamespace(pxdnChild,
  704. L"minimum",
  705. CWSZ_UPNP_SERVICE_NAMESPACE))
  706. {
  707. lFoundMin++;
  708. }
  709. else if (FIsThisTheNodeNameWithNamespace(pxdnChild,
  710. L"maximum",
  711. CWSZ_UPNP_SERVICE_NAMESPACE))
  712. {
  713. lFoundMax++;
  714. }
  715. else if (FIsThisTheNodeNameWithNamespace(pxdnChild,
  716. L"step",
  717. CWSZ_UPNP_SERVICE_NAMESPACE))
  718. {
  719. lFoundStep++;
  720. }
  721. if (FAILED(hr))
  722. {
  723. pxdnChild->Release();
  724. break;
  725. }
  726. hr = pxdnChild->get_nextSibling(&pxdnNextSibling);
  727. pxdnChild->Release();
  728. pxdnChild = pxdnNextSibling;
  729. }
  730. if (SUCCEEDED(hr))
  731. {
  732. if (1 != lFoundMin)
  733. {
  734. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  735. L"<allowedValueRange> did not contain exactly one <minimum> "
  736. L"element", NULL, &szError);
  737. }
  738. else if (1 != lFoundMax)
  739. {
  740. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  741. L"<allowedValueRange> did not contain exactly one <maximum> "
  742. L"element", NULL, &szError);
  743. }
  744. else if (1 < lFoundStep)
  745. {
  746. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  747. L"<allowedValueRange> contained multiple <step> elements",
  748. NULL, &szError);
  749. }
  750. else
  751. {
  752. hr = S_OK;
  753. }
  754. }
  755. if (szError)
  756. {
  757. *pszError = szError;
  758. }
  759. TraceError("HrValidateAllowedValueRange(): "
  760. "Exiting",
  761. hr);
  762. return hr;
  763. }
  764. HRESULT
  765. HrValidateAllowedValue(
  766. IN IXMLDOMNode * pxdnAllowedValue,
  767. OUT LPWSTR * pszError)
  768. {
  769. HRESULT hr = S_OK;
  770. LPWSTR szError = NULL;
  771. BSTR bstrName = NULL;
  772. Assert(pszError);
  773. Assert(pxdnAllowedValue);
  774. hr = pxdnAllowedValue->get_text(&bstrName);
  775. if (SUCCEEDED(hr))
  776. {
  777. int len = lstrlenW(bstrName);
  778. if (len == 0)
  779. {
  780. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  781. L"<allowedValue> element must not be empty",
  782. NULL, &szError);
  783. }
  784. if (len >= 32)
  785. {
  786. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  787. L"<allowedValue> element must be less than 32 characters",
  788. NULL, &szError);
  789. }
  790. SysFreeString(bstrName);
  791. }
  792. if (szError)
  793. {
  794. *pszError = szError;
  795. }
  796. TraceError("HrValidateAllowedValue(): Exiting", hr);
  797. return hr;
  798. }
  799. HRESULT
  800. HrValidateAllowedValueList(
  801. IN IXMLDOMNode * pxdnAllowedValueList,
  802. OUT LPWSTR * pszError)
  803. {
  804. HRESULT hr = S_OK;
  805. LPWSTR szError = NULL;
  806. IXMLDOMNodeList * pxdnlChildren = NULL;
  807. Assert(pszError);
  808. hr = pxdnAllowedValueList->get_childNodes(&pxdnlChildren);
  809. if (SUCCEEDED(hr))
  810. {
  811. LONG lNumChildren = 0;
  812. Assert(pxdnlChildren);
  813. hr = pxdnlChildren->get_length(&lNumChildren);
  814. if (SUCCEEDED(hr))
  815. {
  816. BOOL fMatch = FALSE;
  817. for (LONG i = 0; SUCCEEDED(hr) && (i < lNumChildren); i++)
  818. {
  819. IXMLDOMNode * pxdnChild = NULL;
  820. hr = pxdnlChildren->get_item(i, &pxdnChild);
  821. if (SUCCEEDED(hr))
  822. {
  823. Assert(pxdnChild);
  824. if (FIsThisTheNodeNameWithNamespace(pxdnChild,
  825. L"allowedValue",
  826. CWSZ_UPNP_SERVICE_NAMESPACE)
  827. )
  828. {
  829. fMatch = TRUE;
  830. hr = HrValidateAllowedValue(pxdnChild, &szError);
  831. }
  832. pxdnChild->Release();
  833. }
  834. else
  835. {
  836. TraceError("HrValidateAllowedValueList(): "
  837. "Failed to get list item",
  838. hr);
  839. break;
  840. }
  841. }
  842. if (fMatch == FALSE)
  843. {
  844. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  845. L"<allowedValueList> element was empty", NULL, &szError);
  846. }
  847. }
  848. else
  849. {
  850. TraceError("HrValidateAllowedValueList(): "
  851. "Failed to get list length",
  852. hr);
  853. }
  854. pxdnlChildren->Release();
  855. }
  856. else
  857. {
  858. TraceError("HrValidateAllowedValueList(): "
  859. "Failed to get node children",
  860. hr);
  861. }
  862. if (szError)
  863. {
  864. *pszError = szError;
  865. }
  866. TraceError("HrValidateAllowedValueList(): "
  867. "Exiting",
  868. hr);
  869. return hr;
  870. }
  871. HRESULT
  872. HrValidateDataType(
  873. IN IXMLDOMNode * pxdnDT,
  874. OUT BOOL * pfIsString,
  875. OUT BOOL * pfIsNumeric,
  876. OUT LPWSTR * pszError)
  877. {
  878. HRESULT hr = S_OK;
  879. LPWSTR szError = NULL;
  880. DWORD dwIndex = 0;
  881. LPCWSTR rgcszTypeStrings[] =
  882. {
  883. L"char", // iFirstString
  884. L"string", // iLastString
  885. L"ui1", // iFirstNumeric
  886. L"ui2",
  887. L"ui4",
  888. L"i1",
  889. L"i2",
  890. L"i4",
  891. L"int",
  892. L"number",
  893. L"r4",
  894. L"r8",
  895. L"fixed.14.4",
  896. L"float", // iLastNumeric
  897. L"bin.base64",
  898. L"bin.hex",
  899. L"boolean",
  900. L"date",
  901. L"dateTime",
  902. L"dateTime.tz",
  903. L"time",
  904. L"time.tz",
  905. L"uri",
  906. L"uuid",
  907. };
  908. const DWORD ccTypeStrings = celems(rgcszTypeStrings);
  909. const DWORD iFirstString = 0;
  910. const DWORD iLastString = 1;
  911. const DWORD iFirstNumeric = 2;
  912. const DWORD iLastNumeric = 13;
  913. if (FALSE == fIsNodeValueInList(pxdnDT,
  914. FALSE,
  915. ccTypeStrings,
  916. rgcszTypeStrings,
  917. &dwIndex))
  918. {
  919. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L"<dataType> "
  920. L"element contained invalid value", NULL, &szError);
  921. }
  922. // Don't need to compare iFirstString <= dwIndex because this is always
  923. // true.
  924. *pfIsString = dwIndex <= iLastString;
  925. *pfIsNumeric = (iFirstNumeric <= dwIndex && dwIndex <= iLastNumeric);
  926. if (szError)
  927. {
  928. *pszError = szError;
  929. }
  930. TraceError("HrValidateDataType(): "
  931. "Exiting",
  932. hr);
  933. return hr;
  934. }
  935. HRESULT
  936. HrValidateStateVariable(
  937. IN IXMLDOMNode * pxdnSV,
  938. OUT LPWSTR * pszError)
  939. {
  940. HRESULT hr = S_OK;
  941. LPWSTR szError = NULL;
  942. IXMLDOMNode * pxdnChild = NULL;
  943. LONG lFoundName = 0;
  944. LONG lFoundDataType = 0;
  945. LONG lFoundAllowedValueList = 0;
  946. LONG lFoundAllowedValueRange = 0;
  947. BOOL fIsString = FALSE;
  948. BOOL fIsNumeric = FALSE;
  949. BSTR bstrSendEvents = NULL;
  950. Assert(pszError);
  951. Assert(pxdnSV);
  952. hr = HrGetTextValueFromAttribute(pxdnSV, L"sendEvents", &bstrSendEvents);
  953. if (SUCCEEDED(hr))
  954. {
  955. if (hr == S_OK)
  956. {
  957. if (!(0 == lstrcmpiW(bstrSendEvents, L"yes") || 0 == lstrcmpiW(bstrSendEvents, L"no")))
  958. {
  959. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  960. L"<stateVariable> optional attribute \"sendEvents\" must be "
  961. L" \"yes\" or \"no\"", NULL, &szError);
  962. }
  963. SysFreeString(bstrSendEvents);
  964. }
  965. else
  966. {
  967. hr = S_OK; // we don't want to pass on the S_FALSE
  968. }
  969. }
  970. if (SUCCEEDED(hr))
  971. {
  972. hr = pxdnSV->get_firstChild(&pxdnChild);
  973. }
  974. while (SUCCEEDED(hr) && pxdnChild)
  975. {
  976. IXMLDOMNode * pxdnNextSibling = NULL;
  977. if (FIsThisTheNodeNameWithNamespace(pxdnChild,
  978. L"name",
  979. CWSZ_UPNP_SERVICE_NAMESPACE))
  980. {
  981. lFoundName++;
  982. hr = HrValidateName(pxdnChild, TRUE, &szError);
  983. }
  984. else if (FIsThisTheNodeNameWithNamespace(pxdnChild,
  985. L"dataType",
  986. CWSZ_UPNP_SERVICE_NAMESPACE))
  987. {
  988. lFoundDataType++;
  989. hr = HrValidateDataType(pxdnChild, &fIsString, &fIsNumeric, &szError);
  990. }
  991. else if (FIsThisTheNodeNameWithNamespace(pxdnChild,
  992. L"allowedValueList",
  993. CWSZ_UPNP_SERVICE_NAMESPACE))
  994. {
  995. if (lFoundAllowedValueRange)
  996. {
  997. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  998. L"<stateVariable> contains both <allowedValueRange>"
  999. L" and <allowedValueList>", NULL, &szError);
  1000. }
  1001. else
  1002. {
  1003. lFoundAllowedValueList++;
  1004. hr = HrValidateAllowedValueList(pxdnChild, &szError);
  1005. }
  1006. }
  1007. else if (FIsThisTheNodeNameWithNamespace(pxdnChild,
  1008. L"allowedValueRange",
  1009. CWSZ_UPNP_SERVICE_NAMESPACE))
  1010. {
  1011. if (lFoundAllowedValueList)
  1012. {
  1013. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  1014. L"<stateVariable> contains both <allowedValueRange>"
  1015. L" and <allowedValueList>", NULL, &szError);
  1016. }
  1017. else
  1018. {
  1019. lFoundAllowedValueRange++;
  1020. hr = HrValidateAllowedValueRange(pxdnChild, &szError);
  1021. }
  1022. }
  1023. else if (FIsThisTheNodeNameWithNamespace(pxdnChild,
  1024. L"defaultValue",
  1025. CWSZ_UPNP_SERVICE_NAMESPACE))
  1026. {
  1027. // ISSUE-2000/11/6-spather: Need to validate defaultValue somehow.
  1028. }
  1029. if (FAILED(hr))
  1030. {
  1031. pxdnChild->Release();
  1032. break;
  1033. }
  1034. hr = pxdnChild->get_nextSibling(&pxdnNextSibling);
  1035. pxdnChild->Release();
  1036. pxdnChild = pxdnNextSibling;
  1037. }
  1038. if (SUCCEEDED(hr))
  1039. {
  1040. if (1 != lFoundName)
  1041. {
  1042. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  1043. L"<stateVariable> did not contain exactly one <name> element",
  1044. NULL, &szError);
  1045. }
  1046. else if (1 != lFoundDataType)
  1047. {
  1048. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  1049. L"<stateVariable> did not contain exactly one <dataType> "
  1050. L"element", NULL, &szError);
  1051. }
  1052. else if (1 < lFoundAllowedValueList)
  1053. {
  1054. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  1055. L"<stateVariable> contains multiple <allowedValueList> "
  1056. L"elements", NULL, &szError);
  1057. }
  1058. else if (1 < lFoundAllowedValueRange)
  1059. {
  1060. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  1061. L"<stateVariable> contains multiple <allowedValueRange> "
  1062. L"elements", NULL, &szError);
  1063. }
  1064. else if (lFoundAllowedValueList && FALSE == fIsString)
  1065. {
  1066. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  1067. L"<allowedValueList> may only modify a string variable",
  1068. NULL, &szError);
  1069. }
  1070. else if (lFoundAllowedValueRange && FALSE == fIsNumeric)
  1071. {
  1072. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  1073. L"<allowedValueRange> may only modify a numeric variable",
  1074. NULL, &szError);
  1075. }
  1076. else
  1077. {
  1078. hr = S_OK;
  1079. }
  1080. }
  1081. if (szError)
  1082. {
  1083. *pszError = szError;
  1084. }
  1085. TraceError("HrValidateStateVariable(): "
  1086. "Exiting",
  1087. hr);
  1088. return hr;
  1089. }
  1090. HRESULT
  1091. HrValidateSST(
  1092. IN IXMLDOMNode * pxdnSST,
  1093. OUT LPWSTR * pszError)
  1094. {
  1095. HRESULT hr = S_OK;
  1096. LPWSTR szError = NULL;
  1097. IXMLDOMNode * pxdnChild = NULL;
  1098. BOOL bFoundStateVariable = FALSE;
  1099. Assert(pszError);
  1100. hr = pxdnSST->get_firstChild(&pxdnChild);
  1101. while (SUCCEEDED(hr) && pxdnChild)
  1102. {
  1103. IXMLDOMNode * pxdnNextSibling = NULL;
  1104. if (FIsThisTheNodeNameWithNamespace(pxdnChild,
  1105. L"stateVariable",
  1106. CWSZ_UPNP_SERVICE_NAMESPACE))
  1107. {
  1108. bFoundStateVariable = TRUE;
  1109. hr = HrValidateStateVariable(pxdnChild, &szError);
  1110. }
  1111. if (FAILED(hr))
  1112. {
  1113. pxdnChild->Release();
  1114. break;
  1115. }
  1116. hr = pxdnChild->get_nextSibling(&pxdnNextSibling);
  1117. pxdnChild->Release();
  1118. pxdnChild = pxdnNextSibling;
  1119. }
  1120. if (SUCCEEDED(hr))
  1121. {
  1122. if (FALSE == bFoundStateVariable)
  1123. {
  1124. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  1125. L"<serviceStateTable> did not contain any <stateVariable> "
  1126. L"elements", NULL, &szError);
  1127. }
  1128. else
  1129. {
  1130. hr = S_OK;
  1131. }
  1132. }
  1133. if (SUCCEEDED(hr))
  1134. {
  1135. BOOL bDuplicatesExist = FALSE;
  1136. // Check for duplicate names.
  1137. hr = HrAreThereDuplicatesInChildNodeTextValues(pxdnSST,
  1138. L"stateVariable/name",
  1139. FALSE,
  1140. &bDuplicatesExist);
  1141. if (SUCCEEDED(hr))
  1142. {
  1143. if (bDuplicatesExist)
  1144. {
  1145. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  1146. L"<serviceStateTable> contained <stateVariable> elements "
  1147. L"with duplicate names", NULL, &szError);
  1148. }
  1149. }
  1150. else
  1151. {
  1152. TraceError("HrValidateSST(): "
  1153. "Failed to check for duplicate names",
  1154. hr);
  1155. }
  1156. }
  1157. if (szError)
  1158. {
  1159. *pszError = szError;
  1160. }
  1161. TraceError("HrValidateSST(): "
  1162. "Exiting",
  1163. hr);
  1164. return hr;
  1165. }
  1166. HRESULT
  1167. HrValidateSpecVersion(
  1168. IN IXMLDOMNode * pxdnSpecVersion,
  1169. OUT LPWSTR * pszError)
  1170. {
  1171. HRESULT hr = S_OK;
  1172. LPWSTR szError = NULL;
  1173. IXMLDOMNode * pxdnChild = NULL;
  1174. LONG lFoundMajor = 0;
  1175. LONG lFoundMinor = 0;
  1176. Assert(pszError);
  1177. hr = pxdnSpecVersion->get_firstChild(&pxdnChild);
  1178. while (SUCCEEDED(hr) && pxdnChild)
  1179. {
  1180. IXMLDOMNode * pxdnNextSibling = NULL;
  1181. if (FIsThisTheNodeNameWithNamespace(pxdnChild,
  1182. L"major",
  1183. CWSZ_UPNP_SERVICE_NAMESPACE))
  1184. {
  1185. BSTR bstrText = NULL;
  1186. lFoundMajor++;
  1187. hr = pxdnChild->get_text(&bstrText);
  1188. if (SUCCEEDED(hr))
  1189. {
  1190. Assert(bstrText);
  1191. if (0 != lstrcmpW(bstrText, L"1"))
  1192. {
  1193. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  1194. L"<major> version number is incorrect - "
  1195. L"should be \"1\"", NULL, &szError);
  1196. }
  1197. SysFreeString(bstrText);
  1198. }
  1199. }
  1200. else if (FIsThisTheNodeNameWithNamespace(pxdnChild,
  1201. L"minor",
  1202. CWSZ_UPNP_SERVICE_NAMESPACE))
  1203. {
  1204. BSTR bstrText = NULL;
  1205. lFoundMinor++;
  1206. hr = pxdnChild->get_text(&bstrText);
  1207. if (SUCCEEDED(hr))
  1208. {
  1209. Assert(bstrText);
  1210. if (0 != lstrcmpW(bstrText, L"0"))
  1211. {
  1212. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  1213. L"<minor> version number is incorrect - "
  1214. L"should be \"0\"", NULL, &szError);
  1215. }
  1216. SysFreeString(bstrText);
  1217. }
  1218. }
  1219. if (FAILED(hr))
  1220. {
  1221. pxdnChild->Release();
  1222. break;
  1223. }
  1224. hr = pxdnChild->get_nextSibling(&pxdnNextSibling);
  1225. pxdnChild->Release();
  1226. pxdnChild = pxdnNextSibling;
  1227. }
  1228. if (SUCCEEDED(hr))
  1229. {
  1230. if (1 != lFoundMajor)
  1231. {
  1232. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L"<major> "
  1233. L"specVersion element must appear exactly once", NULL, &szError);
  1234. }
  1235. else if (1 != lFoundMinor)
  1236. {
  1237. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L"<minor> "
  1238. L"specVersion element must appear exactly once", NULL, &szError);
  1239. }
  1240. else
  1241. {
  1242. hr = S_OK;
  1243. }
  1244. }
  1245. if (szError)
  1246. {
  1247. *pszError = szError;
  1248. }
  1249. TraceError("HrValidateSpecVersion(): "
  1250. "Exiting",
  1251. hr);
  1252. return hr;
  1253. }
  1254. //+---------------------------------------------------------------------------
  1255. //
  1256. // Function: HrValidateServiceDescription
  1257. //
  1258. // Purpose: Validates a service description document.
  1259. //
  1260. // Arguments:
  1261. // pxdeRoot [in] The DOM element representing the root of the document.
  1262. // pszError [out] Receives a pointer to an error description string.
  1263. //
  1264. // Returns:
  1265. // If the function succeeds, the return value is S_OK. Otherwise, the
  1266. // function returns one of the COM error codes defined in WinError.h.
  1267. //
  1268. // Author: spather 2000/10/19
  1269. //
  1270. // Notes:
  1271. // The string returned at pszError must be freed by the caller using the
  1272. // "delete" operator.
  1273. //
  1274. HRESULT
  1275. HrValidateServiceDescription(
  1276. IN IXMLDOMElement * pxdeRoot,
  1277. OUT LPWSTR * pszError)
  1278. {
  1279. HRESULT hr = S_OK;
  1280. IXMLDOMNode * pxdnSCPD = NULL;
  1281. IXMLDOMNode * pxdnSpecVersion = NULL;
  1282. IXMLDOMNode * pxdnActionList = NULL;
  1283. IXMLDOMNode * pxdnSST = NULL;
  1284. LPWSTR szError = NULL;
  1285. // Check the overall structure to make sure the required root and top-level
  1286. // elements are there.
  1287. hr = pxdeRoot->QueryInterface(IID_IXMLDOMNode, (void **) &pxdnSCPD);
  1288. if (SUCCEEDED(hr))
  1289. {
  1290. IXMLDOMNode * pxdnChild = NULL;
  1291. Assert(pxdnSCPD);
  1292. if (FIsThisTheNodeNameWithNamespace(pxdnSCPD,
  1293. L"scpd",
  1294. CWSZ_UPNP_SERVICE_NAMESPACE))
  1295. {
  1296. LONG lFoundSpecVersion = 0;
  1297. LONG lFoundActionList = 0;
  1298. LONG lFoundSST = 0;
  1299. hr = pxdnSCPD->get_firstChild(&pxdnChild);
  1300. while (SUCCEEDED(hr) && pxdnChild)
  1301. {
  1302. IXMLDOMNode * pxdnNextSibling = NULL;
  1303. if (FIsThisTheNodeNameWithNamespace(pxdnChild,
  1304. L"specVersion",
  1305. CWSZ_UPNP_SERVICE_NAMESPACE))
  1306. {
  1307. pxdnChild->AddRef();
  1308. pxdnSpecVersion = pxdnChild;
  1309. lFoundSpecVersion++;
  1310. }
  1311. else if (FIsThisTheNodeNameWithNamespace(pxdnChild,
  1312. L"actionList",
  1313. CWSZ_UPNP_SERVICE_NAMESPACE))
  1314. {
  1315. pxdnChild->AddRef();
  1316. pxdnActionList = pxdnChild;
  1317. lFoundActionList++;
  1318. }
  1319. else if (FIsThisTheNodeNameWithNamespace(pxdnChild,
  1320. L"serviceStateTable",
  1321. CWSZ_UPNP_SERVICE_NAMESPACE))
  1322. {
  1323. pxdnChild->AddRef();
  1324. pxdnSST = pxdnChild;
  1325. lFoundSST++;
  1326. }
  1327. if (FAILED(hr))
  1328. {
  1329. pxdnChild->Release();
  1330. break;
  1331. }
  1332. hr = pxdnChild->get_nextSibling(&pxdnNextSibling);
  1333. pxdnChild->Release();
  1334. pxdnChild = pxdnNextSibling;
  1335. }
  1336. // If we didn't find an unknown node and there was no other
  1337. // error, hr will be S_FALSE now. But there may have been some
  1338. // required nodes missing, so we'll check. If everything was
  1339. // there, change hr to S_OK.
  1340. if (SUCCEEDED(hr))
  1341. {
  1342. if (1 != lFoundSpecVersion)
  1343. {
  1344. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  1345. L"<specVersion> element must appear exactly once",
  1346. NULL, &szError);
  1347. }
  1348. else if (1 != lFoundSST)
  1349. {
  1350. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  1351. L"<serviceStateTable> element must appear exactly once",
  1352. NULL, &szError);
  1353. }
  1354. else if (1 < lFoundActionList)
  1355. {
  1356. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
  1357. L"<actionList> element must appear no more than once",
  1358. NULL, &szError);
  1359. }
  1360. else
  1361. {
  1362. hr = S_OK;
  1363. }
  1364. }
  1365. }
  1366. else
  1367. {
  1368. // Wrong root node name.
  1369. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L"Root node "
  1370. L"invalid - should be <scpd "
  1371. L"xmlns=\"urn:schemas-upnp-org:service-1-0\">", NULL, &szError);
  1372. }
  1373. }
  1374. else
  1375. {
  1376. TraceError("HrValidateServiceDescription(): "
  1377. "Failed to get SCPD node",
  1378. hr);
  1379. }
  1380. // Next, validate each top-level node and its children.
  1381. if (SUCCEEDED(hr))
  1382. {
  1383. hr = HrValidateSpecVersion(pxdnSpecVersion, &szError);
  1384. if (SUCCEEDED(hr))
  1385. {
  1386. hr = HrValidateSST(pxdnSST, &szError);
  1387. if (SUCCEEDED(hr) && NULL != pxdnActionList)
  1388. {
  1389. hr = HrValidateActionList(pxdnActionList, pxdnSST, &szError);
  1390. }
  1391. }
  1392. }
  1393. // Cleanup.
  1394. if (pxdnSCPD)
  1395. {
  1396. pxdnSCPD->Release();
  1397. pxdnSCPD = NULL;
  1398. }
  1399. if (pxdnSpecVersion)
  1400. {
  1401. pxdnSpecVersion->Release();
  1402. pxdnSpecVersion = NULL;
  1403. }
  1404. if (pxdnActionList)
  1405. {
  1406. pxdnActionList->Release();
  1407. pxdnActionList = NULL;
  1408. }
  1409. if (pxdnSST)
  1410. {
  1411. pxdnSST->Release();
  1412. pxdnSST = NULL;
  1413. }
  1414. // Copy the error string to the output if necessary.
  1415. if (szError)
  1416. {
  1417. if (pszError)
  1418. {
  1419. *pszError = szError;
  1420. }
  1421. else
  1422. {
  1423. delete [] szError;
  1424. szError = NULL;
  1425. }
  1426. }
  1427. TraceError("HrValidateServiceDescription(): "
  1428. "Exiting",
  1429. hr);
  1430. return hr;
  1431. }