1685 lines
45 KiB
C++
1685 lines
45 KiB
C++
//+---------------------------------------------------------------------------
|
|
//
|
|
// 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 <pch.h>
|
|
#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"<name> element was empty",
|
|
NULL, &szError);
|
|
}
|
|
|
|
#if DBG
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (lstrlenW(bstrName) >= 32)
|
|
{
|
|
HRESULT hrTemp = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
|
|
L"<name> 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"<name> 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"<relatedStateVariable> '%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"<direction> 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"<retval> contained a text value. Must be empty.",
|
|
NULL, &szError);
|
|
}
|
|
SysFreeString(bstrText);
|
|
}
|
|
else
|
|
{
|
|
TraceError("HrValidateArg(): "
|
|
"Failed to get text of <retval> 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"<argument> did not contain exactly one <name> element",
|
|
NULL, &szError);
|
|
}
|
|
else if (1 != lFoundDir)
|
|
{
|
|
hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
|
|
L"<argument> did not contain exactly one <directio> element",
|
|
NULL, &szError);
|
|
}
|
|
else if (1 != lFoundRSV)
|
|
{
|
|
hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
|
|
L"<argument> did not contain exactly one "
|
|
L"<relatedStateVariable> 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)
|
|
{
|
|
// <retval> can only appear in the first "out" argument.
|
|
|
|
if (bIsInArg)
|
|
{
|
|
// <retval> appeared in an "in" argument.
|
|
hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
|
|
L"<retval> element found in \"in\" <argument>",
|
|
NULL, &szError);
|
|
}
|
|
else if (bFoundAtLeastOneOutArg)
|
|
{
|
|
// <retval> appeared in an "out" argument that was not
|
|
// the first one.
|
|
|
|
hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
|
|
L"<retval> element found in \"out\" <argument> "
|
|
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\" <argument> found after one or "
|
|
L"more \"out\" <argument>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"<argumentList> did not contain any <argument> 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"<argumentList> contained <argument> 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"<action> "
|
|
L"did not contain exactly one <name> 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"<actionList> did not contain any <action> 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"<actionList> contained <action> 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"<allowedValueRange> did not contain exactly one <minimum> "
|
|
L"element", NULL, &szError);
|
|
}
|
|
else if (1 != lFoundMax)
|
|
{
|
|
hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
|
|
L"<allowedValueRange> did not contain exactly one <maximum> "
|
|
L"element", NULL, &szError);
|
|
}
|
|
else if (1 < lFoundStep)
|
|
{
|
|
hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
|
|
L"<allowedValueRange> contained multiple <step> 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"<allowedValue> element must not be empty",
|
|
NULL, &szError);
|
|
}
|
|
|
|
if (len >= 32)
|
|
{
|
|
hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
|
|
L"<allowedValue> 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"<allowedValueList> 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"<dataType> "
|
|
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"<stateVariable> 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"<stateVariable> contains both <allowedValueRange>"
|
|
L" and <allowedValueList>", 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"<stateVariable> contains both <allowedValueRange>"
|
|
L" and <allowedValueList>", 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"<stateVariable> did not contain exactly one <name> element",
|
|
NULL, &szError);
|
|
}
|
|
else if (1 != lFoundDataType)
|
|
{
|
|
hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
|
|
L"<stateVariable> did not contain exactly one <dataType> "
|
|
L"element", NULL, &szError);
|
|
}
|
|
else if (1 < lFoundAllowedValueList)
|
|
{
|
|
hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
|
|
L"<stateVariable> contains multiple <allowedValueList> "
|
|
L"elements", NULL, &szError);
|
|
}
|
|
else if (1 < lFoundAllowedValueRange)
|
|
{
|
|
hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
|
|
L"<stateVariable> contains multiple <allowedValueRange> "
|
|
L"elements", NULL, &szError);
|
|
}
|
|
else if (lFoundAllowedValueList && FALSE == fIsString)
|
|
{
|
|
hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
|
|
L"<allowedValueList> may only modify a string variable",
|
|
NULL, &szError);
|
|
}
|
|
else if (lFoundAllowedValueRange && FALSE == fIsNumeric)
|
|
{
|
|
hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
|
|
L"<allowedValueRange> 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"<serviceStateTable> did not contain any <stateVariable> "
|
|
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"<serviceStateTable> contained <stateVariable> 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"<major> 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"<minor> 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"<major> "
|
|
L"specVersion element must appear exactly once", NULL, &szError);
|
|
}
|
|
else if (1 != lFoundMinor)
|
|
{
|
|
hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT, L"<minor> "
|
|
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"<specVersion> element must appear exactly once",
|
|
NULL, &szError);
|
|
}
|
|
else if (1 != lFoundSST)
|
|
{
|
|
hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
|
|
L"<serviceStateTable> element must appear exactly once",
|
|
NULL, &szError);
|
|
}
|
|
else if (1 < lFoundActionList)
|
|
{
|
|
hr = HrAllocErrorStringAndReturnHr(UPNP_E_INVALID_DOCUMENT,
|
|
L"<actionList> 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 <scpd "
|
|
L"xmlns=\"urn:schemas-upnp-org:service-1-0\">", 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;
|
|
}
|
|
|