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

//+---------------------------------------------------------------------------
//
// 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;
}