//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 2000. // // File: V A L I D A T E S D . C P P // // Contents: Implementation of service description validation routines // // Notes: // // Author: spather 2000/10/17 // //---------------------------------------------------------------------------- #include #pragma hdrstop #include "upnp.h" #include "Validate.h" #include "ncxml.h" #include "ncstring.h" LPCWSTR CWSZ_UPNP_SERVICE_NAMESPACE = L"urn:schemas-upnp-org:service-1-0"; BOOL fIsNodeValueInList( IN IXMLDOMNode * pxdn, IN BOOL fCaseSensitive, IN const DWORD cValues, IN LPCWSTR rgcszValues[], OUT DWORD * pdwIndex) { HRESULT hr = S_OK; BOOL fResult = FALSE; BSTR bstrText = NULL; Assert(cValues > 0); Assert(rgcszValues); hr = pxdn->get_text(&bstrText); if (SUCCEEDED(hr)) { Assert(bstrText); for (DWORD i = 0; i < cValues; i++) { if (fCaseSensitive) { if (0 == lstrcmpW(bstrText, rgcszValues[i])) { fResult = TRUE; *pdwIndex = i; break; } } else { if (0 == lstrcmpiW(bstrText, rgcszValues[i])) { fResult = TRUE; *pdwIndex = i; break; } } } SysFreeString(bstrText); } else { TraceError("fIsNodeValueInList(): " "Failed to get node text", hr); } TraceError("fIsNodeValueInList(): " "Before returning", hr); return fResult; } LPWSTR SzAllocateErrorString( LPCWSTR cszError, LPCWSTR cszOptionalParam) { DWORD cchError = 0; LPWSTR szError = NULL; Assert(cszError); cchError = lstrlenW(cszError); if (cszOptionalParam) { cchError += lstrlenW(cszOptionalParam); } szError = new WCHAR[cchError+1]; if (szError) { if (cszOptionalParam) { wsprintf(szError, cszError, cszOptionalParam); } else { lstrcpyW(szError, cszError); } } else { TraceTag(ttidValidate, "SzAllocateErrorString(): " "Failed to allocate memory to duplicate error string %S", cszError); } return szError; } inline HRESULT HrAllocErrorStringAndReturnHr( HRESULT hrDesired, LPCWSTR cszErrorText, LPCWSTR cszOptonalParam, LPWSTR* pszError) { HRESULT hr = hrDesired; Assert(pszError); Assert(cszErrorText); *pszError = SzAllocateErrorString(cszErrorText, cszOptonalParam); if (NULL == *pszError) { hr = E_OUTOFMEMORY; } return hr; } HRESULT HrValidateName( IN IXMLDOMNode * pxdnName, IN BOOL fCheckAUnderscore, OUT LPWSTR * pszError) { HRESULT hr = S_OK; LPWSTR szError = NULL; BSTR bstrName = NULL; Assert(pszError); hr = pxdnName->get_text(&bstrName); if (SUCCEEDED(hr)) { Assert(bstrName); if (0 == lstrcmpW(bstrName, L"")) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" element was empty", NULL, &szError); } #if DBG if (SUCCEEDED(hr)) { if (lstrlenW(bstrName) >= 32) { HRESULT hrTemp = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" element should be less than 32 characters", NULL, &szError); if(SUCCEEDED(hrTemp)) { char * szaError = SzFromWsz(szError); if(szaError) { TraceTag(ttidDefault, szaError); delete [] szaError; } delete [] szError; szError = NULL; } } } #endif if (SUCCEEDED(hr)) { WCHAR * sz = bstrName; int i = 0; BOOL hasHyphen = FALSE; while (sz[i]) { if (sz[i] == L'-') { hasHyphen = TRUE; break; } i++; } if (hasHyphen) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" element may not contain '-'", NULL, &szError); } } SysFreeString(bstrName); } else { TraceError("HrValidateName(): " "Failed to get node text", hr); } if (szError) { *pszError = szError; } TraceError("HrValidateName(): " "Exiting", hr); return hr; } HRESULT HrValidateRelatedStateVariable( IN IXMLDOMNode * pxdnRSV, IN IXMLDOMNode * pxdnSST, OUT LPWSTR * pszError) { HRESULT hr = S_OK; LPWSTR szError = NULL; BSTR bstrRSVName = NULL; Assert(pszError); hr = HrGetTextValueFromElement(pxdnRSV, &bstrRSVName); if (SUCCEEDED(hr)) { BSTR bstrXSLPattern = NULL; Assert(bstrRSVName); bstrXSLPattern = SysAllocString(L"stateVariable/name"); if (bstrXSLPattern) { IXMLDOMNodeList * pxdnlSVNames = NULL; hr = pxdnSST->selectNodes(bstrXSLPattern, &pxdnlSVNames); if (SUCCEEDED(hr)) { LONG listLength = 0; Assert(pxdnlSVNames); hr = pxdnlSVNames->get_length(&listLength); if (SUCCEEDED(hr)) { BOOL fMatch = FALSE; for (LONG i = 0; SUCCEEDED(hr) && (i < listLength) && (FALSE == fMatch); i++) { IXMLDOMNode * pxdnName = NULL; hr = pxdnlSVNames->get_item(i, &pxdnName); if (SUCCEEDED(hr)) { Assert(pxdnName); fMatch = fMatch ||FIsThisTheNodeTextValue(pxdnName, bstrRSVName); pxdnName->Release(); } else { TraceError("HrValidateRelatedStateVariable(): " "Failed to get list item", hr); } } if (FALSE == fMatch) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" '%s' did not contain the " L"name of a state variable", bstrRSVName, &szError); } } else { TraceError("HrValidateRelatedStateVariable(): " "Failed to get list length", hr); } pxdnlSVNames->Release(); } else { TraceError("HrValidateRelatedStateVariable(): " "Failed to select nodes", hr); } SysFreeString(bstrXSLPattern); } else { hr = E_OUTOFMEMORY; TraceError("HrValidateRelatedStateVariable(): " "Failed to allocate XSL pattern string", hr); } SysFreeString(bstrRSVName); } if (szError) { *pszError = szError; } TraceError("HrValidateRelatedStateVariable(): " "Exiting(): ", hr); return hr; } HRESULT HrValidateArg( IN IXMLDOMNode * pxdnArg, IN IXMLDOMNode * pxdnSST, OUT BOOL * pbIsInArg, OUT BOOL * pbIsRetval, OUT LPWSTR * pszError) { HRESULT hr = S_OK; LPWSTR szError = NULL; IXMLDOMNode * pxdnChild = NULL; LONG lFoundName = 0; LONG lFoundDir = 0; LONG lFoundRSV = 0; BOOL bFoundRetval = FALSE; Assert(pbIsInArg); Assert(pbIsRetval); Assert(pszError); hr = pxdnArg->get_firstChild(&pxdnChild); while (SUCCEEDED(hr) && pxdnChild) { IXMLDOMNode * pxdnNextSibling = NULL; if (FIsThisTheNodeNameWithNamespace(pxdnChild, L"name", CWSZ_UPNP_SERVICE_NAMESPACE)) { lFoundName++; hr = HrValidateName(pxdnChild, FALSE, &szError); } else if (FIsThisTheNodeNameWithNamespace(pxdnChild, L"direction", CWSZ_UPNP_SERVICE_NAMESPACE)) { lFoundDir++; if (FIsThisTheNodeTextValue(pxdnChild, L"in")) { *pbIsInArg = TRUE; } else if (FIsThisTheNodeTextValue(pxdnChild, L"out")) { *pbIsInArg = FALSE; } else { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" contained invalid value." L" Must be \"in\" or \"out\".", NULL, &szError); } } else if (FIsThisTheNodeNameWithNamespace(pxdnChild, L"retval", CWSZ_UPNP_SERVICE_NAMESPACE)) { BSTR bstrText = NULL; bFoundRetval = TRUE; hr = pxdnChild->get_text(&bstrText); if (SUCCEEDED(hr)) { Assert(bstrText); if (0 == lstrcmpW(bstrText, L"")) { *pbIsRetval = TRUE; } else { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" contained a text value. Must be empty.", NULL, &szError); } SysFreeString(bstrText); } else { TraceError("HrValidateArg(): " "Failed to get text of element", hr); } } else if (FIsThisTheNodeNameWithNamespace(pxdnChild, L"relatedStateVariable", CWSZ_UPNP_SERVICE_NAMESPACE)) { lFoundRSV++; hr = HrValidateRelatedStateVariable(pxdnChild, pxdnSST, &szError); } if (FAILED(hr)) { pxdnChild->Release(); break; } hr = pxdnChild->get_nextSibling(&pxdnNextSibling); pxdnChild->Release(); pxdnChild = pxdnNextSibling; } if (SUCCEEDED(hr)) { if (1 != lFoundName) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" did not contain exactly one element", NULL, &szError); } else if (1 != lFoundDir) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" did not contain exactly one element", NULL, &szError); } else if (1 != lFoundRSV) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" did not contain exactly one " L" element", NULL, &szError); } else { hr = S_OK; if (FALSE == bFoundRetval) { *pbIsRetval = FALSE; } } } if (szError) { *pszError = szError; } TraceError("HrValidateArg(): " "Exiting", hr); return hr; } HRESULT HrValidateArgList( IN IXMLDOMNode * pxdnArgList, IN IXMLDOMNode * pxdnSST, OUT LPWSTR * pszError) { HRESULT hr = S_OK; LPWSTR szError = NULL; IXMLDOMNode * pxdnChild = NULL; BOOL bFoundArg = FALSE; BOOL bIsInArg = FALSE; BOOL bIsRetval = FALSE; BOOL bFoundAtLeastOneOutArg = FALSE; Assert(pszError); hr = pxdnArgList->get_firstChild(&pxdnChild); while (SUCCEEDED(hr) && pxdnChild) { IXMLDOMNode * pxdnNextSibling = NULL; if (FIsThisTheNodeNameWithNamespace(pxdnChild, L"argument", CWSZ_UPNP_SERVICE_NAMESPACE)) { bFoundArg = TRUE; bIsInArg = FALSE; bIsRetval = FALSE; hr = HrValidateArg(pxdnChild, pxdnSST, &bIsInArg, &bIsRetval, &szError); if (SUCCEEDED(hr)) { if (bIsRetval) { // can only appear in the first "out" argument. if (bIsInArg) { // appeared in an "in" argument. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" element found in \"in\" ", NULL, &szError); } else if (bFoundAtLeastOneOutArg) { // appeared in an "out" argument that was not // the first one. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" element found in \"out\" " L"that was not the first one", NULL, &szError); } } if (SUCCEEDED(hr)) { if (bIsInArg) { if (bFoundAtLeastOneOutArg) { // Found an "in" arg after some "out" args. hr = HrAllocErrorStringAndReturnHr( UPNP_E_INVALID_DOCUMENT, L"\"in\" found after one or " L"more \"out\" s", NULL, &szError); } } else { bFoundAtLeastOneOutArg = TRUE; } } } } if (FAILED(hr)) { pxdnChild->Release(); break; } hr = pxdnChild->get_nextSibling(&pxdnNextSibling); pxdnChild->Release(); pxdnChild = pxdnNextSibling; } if (SUCCEEDED(hr)) { if (FALSE == bFoundArg) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" did not contain any elements", NULL, &szError); } else { hr = S_OK; } } if (SUCCEEDED(hr)) { BOOL bDuplicatesExist = FALSE; // Check for duplicate names. hr = HrAreThereDuplicatesInChildNodeTextValues(pxdnArgList, L"argument/name", FALSE, &bDuplicatesExist); if (SUCCEEDED(hr)) { if (bDuplicatesExist) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" contained elements with " L"duplicate names", NULL, &szError); } } else { TraceError("HrValidateArgList(): " "Failed to check for duplicate names", hr); } } if (szError) { *pszError = szError; } TraceError("HrValidateArgList(): " "Exiting", hr); return hr; } HRESULT HrValidateAction( IN IXMLDOMNode * pxdnAction, IN IXMLDOMNode * pxdnSST, OUT LPWSTR * pszError) { HRESULT hr = S_OK; LPWSTR szError = NULL; IXMLDOMNode * pxdnChild = NULL; LONG lFoundName = 0; Assert(pszError); hr = pxdnAction->get_firstChild(&pxdnChild); while (SUCCEEDED(hr) && pxdnChild) { IXMLDOMNode * pxdnNextSibling = NULL; if (FIsThisTheNodeNameWithNamespace(pxdnChild, L"name", CWSZ_UPNP_SERVICE_NAMESPACE)) { lFoundName++; hr = HrValidateName(pxdnChild, TRUE, &szError); } else if (FIsThisTheNodeNameWithNamespace(pxdnChild, L"argumentList", CWSZ_UPNP_SERVICE_NAMESPACE)) { hr = HrValidateArgList(pxdnChild, pxdnSST, &szError); } if (FAILED(hr)) { pxdnChild->Release(); break; } hr = pxdnChild->get_nextSibling(&pxdnNextSibling); pxdnChild->Release(); pxdnChild = pxdnNextSibling; } if (SUCCEEDED(hr)) { if (1 != lFoundName) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" " L"did not contain exactly one element", NULL, &szError); } else { hr = S_OK; } } if (szError) { *pszError = szError; } TraceError("HrValidateAction(): " "Exiting", hr); return hr; } HRESULT HrValidateActionList( IN IXMLDOMNode * pxdnActionList, IN IXMLDOMNode * pxdnSST, OUT LPWSTR * pszError) { HRESULT hr = S_OK; LPWSTR szError = NULL; IXMLDOMNode * pxdnChild = NULL; BOOL bFoundAction = FALSE; Assert(pszError); hr = pxdnActionList->get_firstChild(&pxdnChild); while (SUCCEEDED(hr) && pxdnChild) { IXMLDOMNode * pxdnNextSibling = NULL; if (FIsThisTheNodeNameWithNamespace(pxdnChild, L"action", CWSZ_UPNP_SERVICE_NAMESPACE)) { bFoundAction = TRUE; hr = HrValidateAction(pxdnChild, pxdnSST, &szError); } if (FAILED(hr)) { pxdnChild->Release(); break; } hr = pxdnChild->get_nextSibling(&pxdnNextSibling); pxdnChild->Release(); pxdnChild = pxdnNextSibling; } if (SUCCEEDED(hr)) { if (FALSE == bFoundAction) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" did not contain any elements", NULL, &szError); } else { hr = S_OK; } } if (SUCCEEDED(hr)) { BOOL bDuplicatesExist = FALSE; // Check for duplicate names. hr = HrAreThereDuplicatesInChildNodeTextValues(pxdnActionList, L"action/name", FALSE, &bDuplicatesExist); if (SUCCEEDED(hr)) { if (bDuplicatesExist) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" contained elements with duplicate " L"names", NULL, &szError); } } else { TraceError("HrValidateActionList(): " "Failed to check for duplicate names", hr); } } if (szError) { *pszError = szError; } TraceError("HrValidateActionList(): " "Exiting", hr); return hr; } HRESULT HrValidateAllowedValueRange( IN IXMLDOMNode * pxdnAllowedValueRange, OUT LPWSTR * pszError) { HRESULT hr = S_OK; LPWSTR szError = NULL; IXMLDOMNode * pxdnChild = NULL; LONG lFoundMin = 0; LONG lFoundMax = 0; LONG lFoundStep = 0; Assert(pszError); hr = pxdnAllowedValueRange->get_firstChild(&pxdnChild); while (SUCCEEDED(hr) && pxdnChild) { IXMLDOMNode * pxdnNextSibling = NULL; if (FIsThisTheNodeNameWithNamespace(pxdnChild, L"minimum", CWSZ_UPNP_SERVICE_NAMESPACE)) { lFoundMin++; } else if (FIsThisTheNodeNameWithNamespace(pxdnChild, L"maximum", CWSZ_UPNP_SERVICE_NAMESPACE)) { lFoundMax++; } else if (FIsThisTheNodeNameWithNamespace(pxdnChild, L"step", CWSZ_UPNP_SERVICE_NAMESPACE)) { lFoundStep++; } if (FAILED(hr)) { pxdnChild->Release(); break; } hr = pxdnChild->get_nextSibling(&pxdnNextSibling); pxdnChild->Release(); pxdnChild = pxdnNextSibling; } if (SUCCEEDED(hr)) { if (1 != lFoundMin) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" did not contain exactly one " L"element", NULL, &szError); } else if (1 != lFoundMax) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" did not contain exactly one " L"element", NULL, &szError); } else if (1 < lFoundStep) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" contained multiple elements", NULL, &szError); } else { hr = S_OK; } } if (szError) { *pszError = szError; } TraceError("HrValidateAllowedValueRange(): " "Exiting", hr); return hr; } HRESULT HrValidateAllowedValue( IN IXMLDOMNode * pxdnAllowedValue, OUT LPWSTR * pszError) { HRESULT hr = S_OK; LPWSTR szError = NULL; BSTR bstrName = NULL; Assert(pszError); Assert(pxdnAllowedValue); hr = pxdnAllowedValue->get_text(&bstrName); if (SUCCEEDED(hr)) { int len = lstrlenW(bstrName); if (len == 0) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" element must not be empty", NULL, &szError); } if (len >= 32) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" element must be less than 32 characters", NULL, &szError); } SysFreeString(bstrName); } if (szError) { *pszError = szError; } TraceError("HrValidateAllowedValue(): Exiting", hr); return hr; } HRESULT HrValidateAllowedValueList( IN IXMLDOMNode * pxdnAllowedValueList, OUT LPWSTR * pszError) { HRESULT hr = S_OK; LPWSTR szError = NULL; IXMLDOMNodeList * pxdnlChildren = NULL; Assert(pszError); hr = pxdnAllowedValueList->get_childNodes(&pxdnlChildren); if (SUCCEEDED(hr)) { LONG lNumChildren = 0; Assert(pxdnlChildren); hr = pxdnlChildren->get_length(&lNumChildren); if (SUCCEEDED(hr)) { BOOL fMatch = FALSE; for (LONG i = 0; SUCCEEDED(hr) && (i < lNumChildren); i++) { IXMLDOMNode * pxdnChild = NULL; hr = pxdnlChildren->get_item(i, &pxdnChild); if (SUCCEEDED(hr)) { Assert(pxdnChild); if (FIsThisTheNodeNameWithNamespace(pxdnChild, L"allowedValue", CWSZ_UPNP_SERVICE_NAMESPACE) ) { fMatch = TRUE; hr = HrValidateAllowedValue(pxdnChild, &szError); } pxdnChild->Release(); } else { TraceError("HrValidateAllowedValueList(): " "Failed to get list item", hr); break; } } if (fMatch == FALSE) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" element was empty", NULL, &szError); } } else { TraceError("HrValidateAllowedValueList(): " "Failed to get list length", hr); } pxdnlChildren->Release(); } else { TraceError("HrValidateAllowedValueList(): " "Failed to get node children", hr); } if (szError) { *pszError = szError; } TraceError("HrValidateAllowedValueList(): " "Exiting", hr); return hr; } HRESULT HrValidateDataType( IN IXMLDOMNode * pxdnDT, OUT BOOL * pfIsString, OUT BOOL * pfIsNumeric, OUT LPWSTR * pszError) { HRESULT hr = S_OK; LPWSTR szError = NULL; DWORD dwIndex = 0; LPCWSTR rgcszTypeStrings[] = { L"char", // iFirstString L"string", // iLastString L"ui1", // iFirstNumeric L"ui2", L"ui4", L"i1", L"i2", L"i4", L"int", L"number", L"r4", L"r8", L"fixed.14.4", L"float", // iLastNumeric L"bin.base64", L"bin.hex", L"boolean", L"date", L"dateTime", L"dateTime.tz", L"time", L"time.tz", L"uri", L"uuid", }; const DWORD ccTypeStrings = celems(rgcszTypeStrings); const DWORD iFirstString = 0; const DWORD iLastString = 1; const DWORD iFirstNumeric = 2; const DWORD iLastNumeric = 13; if (FALSE == fIsNodeValueInList(pxdnDT, FALSE, ccTypeStrings, rgcszTypeStrings, &dwIndex)) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" " L"element contained invalid value", NULL, &szError); } // Don't need to compare iFirstString <= dwIndex because this is always // true. *pfIsString = dwIndex <= iLastString; *pfIsNumeric = (iFirstNumeric <= dwIndex && dwIndex <= iLastNumeric); if (szError) { *pszError = szError; } TraceError("HrValidateDataType(): " "Exiting", hr); return hr; } HRESULT HrValidateStateVariable( IN IXMLDOMNode * pxdnSV, OUT LPWSTR * pszError) { HRESULT hr = S_OK; LPWSTR szError = NULL; IXMLDOMNode * pxdnChild = NULL; LONG lFoundName = 0; LONG lFoundDataType = 0; LONG lFoundAllowedValueList = 0; LONG lFoundAllowedValueRange = 0; BOOL fIsString = FALSE; BOOL fIsNumeric = FALSE; BSTR bstrSendEvents = NULL; Assert(pszError); Assert(pxdnSV); hr = HrGetTextValueFromAttribute(pxdnSV, L"sendEvents", &bstrSendEvents); if (SUCCEEDED(hr)) { if (hr == S_OK) { if (!(0 == lstrcmpiW(bstrSendEvents, L"yes") || 0 == lstrcmpiW(bstrSendEvents, L"no"))) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" optional attribute \"sendEvents\" must be " L" \"yes\" or \"no\"", NULL, &szError); } SysFreeString(bstrSendEvents); } else { hr = S_OK; // we don't want to pass on the S_FALSE } } if (SUCCEEDED(hr)) { hr = pxdnSV->get_firstChild(&pxdnChild); } while (SUCCEEDED(hr) && pxdnChild) { IXMLDOMNode * pxdnNextSibling = NULL; if (FIsThisTheNodeNameWithNamespace(pxdnChild, L"name", CWSZ_UPNP_SERVICE_NAMESPACE)) { lFoundName++; hr = HrValidateName(pxdnChild, TRUE, &szError); } else if (FIsThisTheNodeNameWithNamespace(pxdnChild, L"dataType", CWSZ_UPNP_SERVICE_NAMESPACE)) { lFoundDataType++; hr = HrValidateDataType(pxdnChild, &fIsString, &fIsNumeric, &szError); } else if (FIsThisTheNodeNameWithNamespace(pxdnChild, L"allowedValueList", CWSZ_UPNP_SERVICE_NAMESPACE)) { if (lFoundAllowedValueRange) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" contains both " L" and ", NULL, &szError); } else { lFoundAllowedValueList++; hr = HrValidateAllowedValueList(pxdnChild, &szError); } } else if (FIsThisTheNodeNameWithNamespace(pxdnChild, L"allowedValueRange", CWSZ_UPNP_SERVICE_NAMESPACE)) { if (lFoundAllowedValueList) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" contains both " L" and ", NULL, &szError); } else { lFoundAllowedValueRange++; hr = HrValidateAllowedValueRange(pxdnChild, &szError); } } else if (FIsThisTheNodeNameWithNamespace(pxdnChild, L"defaultValue", CWSZ_UPNP_SERVICE_NAMESPACE)) { // ISSUE-2000/11/6-spather: Need to validate defaultValue somehow. } if (FAILED(hr)) { pxdnChild->Release(); break; } hr = pxdnChild->get_nextSibling(&pxdnNextSibling); pxdnChild->Release(); pxdnChild = pxdnNextSibling; } if (SUCCEEDED(hr)) { if (1 != lFoundName) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" did not contain exactly one element", NULL, &szError); } else if (1 != lFoundDataType) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" did not contain exactly one " L"element", NULL, &szError); } else if (1 < lFoundAllowedValueList) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" contains multiple " L"elements", NULL, &szError); } else if (1 < lFoundAllowedValueRange) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" contains multiple " L"elements", NULL, &szError); } else if (lFoundAllowedValueList && FALSE == fIsString) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" may only modify a string variable", NULL, &szError); } else if (lFoundAllowedValueRange && FALSE == fIsNumeric) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" may only modify a numeric variable", NULL, &szError); } else { hr = S_OK; } } if (szError) { *pszError = szError; } TraceError("HrValidateStateVariable(): " "Exiting", hr); return hr; } HRESULT HrValidateSST( IN IXMLDOMNode * pxdnSST, OUT LPWSTR * pszError) { HRESULT hr = S_OK; LPWSTR szError = NULL; IXMLDOMNode * pxdnChild = NULL; BOOL bFoundStateVariable = FALSE; Assert(pszError); hr = pxdnSST->get_firstChild(&pxdnChild); while (SUCCEEDED(hr) && pxdnChild) { IXMLDOMNode * pxdnNextSibling = NULL; if (FIsThisTheNodeNameWithNamespace(pxdnChild, L"stateVariable", CWSZ_UPNP_SERVICE_NAMESPACE)) { bFoundStateVariable = TRUE; hr = HrValidateStateVariable(pxdnChild, &szError); } if (FAILED(hr)) { pxdnChild->Release(); break; } hr = pxdnChild->get_nextSibling(&pxdnNextSibling); pxdnChild->Release(); pxdnChild = pxdnNextSibling; } if (SUCCEEDED(hr)) { if (FALSE == bFoundStateVariable) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" did not contain any " L"elements", NULL, &szError); } else { hr = S_OK; } } if (SUCCEEDED(hr)) { BOOL bDuplicatesExist = FALSE; // Check for duplicate names. hr = HrAreThereDuplicatesInChildNodeTextValues(pxdnSST, L"stateVariable/name", FALSE, &bDuplicatesExist); if (SUCCEEDED(hr)) { if (bDuplicatesExist) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" contained elements " L"with duplicate names", NULL, &szError); } } else { TraceError("HrValidateSST(): " "Failed to check for duplicate names", hr); } } if (szError) { *pszError = szError; } TraceError("HrValidateSST(): " "Exiting", hr); return hr; } HRESULT HrValidateSpecVersion( IN IXMLDOMNode * pxdnSpecVersion, OUT LPWSTR * pszError) { HRESULT hr = S_OK; LPWSTR szError = NULL; IXMLDOMNode * pxdnChild = NULL; LONG lFoundMajor = 0; LONG lFoundMinor = 0; Assert(pszError); hr = pxdnSpecVersion->get_firstChild(&pxdnChild); while (SUCCEEDED(hr) && pxdnChild) { IXMLDOMNode * pxdnNextSibling = NULL; if (FIsThisTheNodeNameWithNamespace(pxdnChild, L"major", CWSZ_UPNP_SERVICE_NAMESPACE)) { BSTR bstrText = NULL; lFoundMajor++; hr = pxdnChild->get_text(&bstrText); if (SUCCEEDED(hr)) { Assert(bstrText); if (0 != lstrcmpW(bstrText, L"1")) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" version number is incorrect - " L"should be \"1\"", NULL, &szError); } SysFreeString(bstrText); } } else if (FIsThisTheNodeNameWithNamespace(pxdnChild, L"minor", CWSZ_UPNP_SERVICE_NAMESPACE)) { BSTR bstrText = NULL; lFoundMinor++; hr = pxdnChild->get_text(&bstrText); if (SUCCEEDED(hr)) { Assert(bstrText); if (0 != lstrcmpW(bstrText, L"0")) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" version number is incorrect - " L"should be \"0\"", NULL, &szError); } SysFreeString(bstrText); } } if (FAILED(hr)) { pxdnChild->Release(); break; } hr = pxdnChild->get_nextSibling(&pxdnNextSibling); pxdnChild->Release(); pxdnChild = pxdnNextSibling; } if (SUCCEEDED(hr)) { if (1 != lFoundMajor) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" " L"specVersion element must appear exactly once", NULL, &szError); } else if (1 != lFoundMinor) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" " L"specVersion element must appear exactly once", NULL, &szError); } else { hr = S_OK; } } if (szError) { *pszError = szError; } TraceError("HrValidateSpecVersion(): " "Exiting", hr); return hr; } //+--------------------------------------------------------------------------- // // Function: HrValidateServiceDescription // // Purpose: Validates a service description document. // // Arguments: // pxdeRoot [in] The DOM element representing the root of the document. // pszError [out] Receives a pointer to an error description string. // // Returns: // If the function succeeds, the return value is S_OK. Otherwise, the // function returns one of the COM error codes defined in WinError.h. // // Author: spather 2000/10/19 // // Notes: // The string returned at pszError must be freed by the caller using the // "delete" operator. // HRESULT HrValidateServiceDescription( IN IXMLDOMElement * pxdeRoot, OUT LPWSTR * pszError) { HRESULT hr = S_OK; IXMLDOMNode * pxdnSCPD = NULL; IXMLDOMNode * pxdnSpecVersion = NULL; IXMLDOMNode * pxdnActionList = NULL; IXMLDOMNode * pxdnSST = NULL; LPWSTR szError = NULL; // Check the overall structure to make sure the required root and top-level // elements are there. hr = pxdeRoot->QueryInterface(IID_IXMLDOMNode, (void **) &pxdnSCPD); if (SUCCEEDED(hr)) { IXMLDOMNode * pxdnChild = NULL; Assert(pxdnSCPD); if (FIsThisTheNodeNameWithNamespace(pxdnSCPD, L"scpd", CWSZ_UPNP_SERVICE_NAMESPACE)) { LONG lFoundSpecVersion = 0; LONG lFoundActionList = 0; LONG lFoundSST = 0; hr = pxdnSCPD->get_firstChild(&pxdnChild); while (SUCCEEDED(hr) && pxdnChild) { IXMLDOMNode * pxdnNextSibling = NULL; if (FIsThisTheNodeNameWithNamespace(pxdnChild, L"specVersion", CWSZ_UPNP_SERVICE_NAMESPACE)) { pxdnChild->AddRef(); pxdnSpecVersion = pxdnChild; lFoundSpecVersion++; } else if (FIsThisTheNodeNameWithNamespace(pxdnChild, L"actionList", CWSZ_UPNP_SERVICE_NAMESPACE)) { pxdnChild->AddRef(); pxdnActionList = pxdnChild; lFoundActionList++; } else if (FIsThisTheNodeNameWithNamespace(pxdnChild, L"serviceStateTable", CWSZ_UPNP_SERVICE_NAMESPACE)) { pxdnChild->AddRef(); pxdnSST = pxdnChild; lFoundSST++; } if (FAILED(hr)) { pxdnChild->Release(); break; } hr = pxdnChild->get_nextSibling(&pxdnNextSibling); pxdnChild->Release(); pxdnChild = pxdnNextSibling; } // If we didn't find an unknown node and there was no other // error, hr will be S_FALSE now. But there may have been some // required nodes missing, so we'll check. If everything was // there, change hr to S_OK. if (SUCCEEDED(hr)) { if (1 != lFoundSpecVersion) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" element must appear exactly once", NULL, &szError); } else if (1 != lFoundSST) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" element must appear exactly once", NULL, &szError); } else if (1 < lFoundActionList) { hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L" element must appear no more than once", NULL, &szError); } else { hr = S_OK; } } } else { // Wrong root node name. hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L"Root node " L"invalid - should be ", NULL, &szError); } } else { TraceError("HrValidateServiceDescription(): " "Failed to get SCPD node", hr); } // Next, validate each top-level node and its children. if (SUCCEEDED(hr)) { hr = HrValidateSpecVersion(pxdnSpecVersion, &szError); if (SUCCEEDED(hr)) { hr = HrValidateSST(pxdnSST, &szError); if (SUCCEEDED(hr) && NULL != pxdnActionList) { hr = HrValidateActionList(pxdnActionList, pxdnSST, &szError); } } } // Cleanup. if (pxdnSCPD) { pxdnSCPD->Release(); pxdnSCPD = NULL; } if (pxdnSpecVersion) { pxdnSpecVersion->Release(); pxdnSpecVersion = NULL; } if (pxdnActionList) { pxdnActionList->Release(); pxdnActionList = NULL; } if (pxdnSST) { pxdnSST->Release(); pxdnSST = NULL; } // Copy the error string to the output if necessary. if (szError) { if (pszError) { *pszError = szError; } else { delete [] szError; szError = NULL; } } TraceError("HrValidateServiceDescription(): " "Exiting", hr); return hr; }