windows-nt/Source/XPSP1/NT/net/upnp/host/udhisapi/ctrlrqst.cpp
2020-09-26 16:20:57 +08:00

2551 lines
81 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 2000.
//
// File: C T R L R Q S T . C P P
//
// Contents: Implementation of control request processing for the
// UPnP Device Host ISAPI Extension
//
// Notes:
//
// Author: spather 2000/08/31
//
//----------------------------------------------------------------------------
#include <pch.h>
#pragma hdrstop
#include <wininet.h>
#include <msxml2.h>
#include "ctrlrqst.h"
#include "udhiutil.h"
#include "hostp.h"
#include "ncbase.h"
#include "ncxml.h"
#include "ValidateSOAP.h"
const WCHAR WSZ_SOAP_NAMESPACE_URI[] =
L"http://schemas.xmlsoap.org/soap/envelope/";
const WCHAR WSZ_UPNP_NAMESPACE_URI[] =
L"urn:schemas-upnp-org:control-1-0";
//+---------------------------------------------------------------------------
//
// Function: CleanupSerializedRequest
//
// Purpose: Frees resources used by the fields of a UPNP_SOAP_REQUEST
// structure
//
// Arguments:
// pusr [in] Address of the structure to cleanup
//
// Returns:
// (none)
//
// Author: spather 2000/09/24
//
// Notes:
// This function just frees the resources used by the fields within
// the passed in structure. It does not free the memory used by the
// structure itself.
//
VOID
CleanupSerializedRequest(
IN UPNP_SOAP_REQUEST * pusr)
{
if (pusr->bstrActionName)
{
SysFreeString(pusr->bstrActionName);
pusr->bstrActionName = NULL;
}
if (pusr->pxdnlArgs)
{
pusr->pxdnlArgs->Release();
pusr->pxdnlArgs = NULL;
}
}
//+---------------------------------------------------------------------------
//
// Function: CleanupSerializedResponse
//
// Purpose: Frees resources used by the fields of a UPNP_SOAP_RESPONSE
// structure
//
// Arguments:
// pusr [in] Address of the structure to cleanup
//
// Returns:
// (none)
//
// Author: spather 2000/09/24
//
// Notes:
// This function just frees the resources used by the fields within
// the passed in structure. It does not free the memory used by the
// structure itself.
//
VOID
CleanupSerializedResponse(
IN UPNP_SOAP_RESPONSE * pusr)
{
if (pusr->pxddRespEnvelope)
{
pusr->pxddRespEnvelope->Release();
pusr->pxddRespEnvelope = NULL;
}
}
//+---------------------------------------------------------------------------
//
// Function: CleanupDeserializedRequest
//
// Purpose: Frees resources used by the fields of a UPNP_CONTROL_REQUEST
// structure
//
// Arguments:
// pucr [in] Address of the structure to cleanup
//
// Returns:
// (none)
//
// Author: spather 2000/09/24
//
// Notes:
// This function just frees the resources used by the fields within
// the passed in structure. It does not free the memory used by the
// structure itself.
//
VOID
CleanupDeserializedRequest(
IN UPNP_CONTROL_REQUEST * pucr)
{
if (pucr->bstrActionName)
{
SysFreeString(pucr->bstrActionName);
pucr->bstrActionName = NULL;
}
if (pucr->rgvarInputArgs)
{
for (DWORD i = 0; i < pucr->cInputArgs; i++)
{
VariantClear(&pucr->rgvarInputArgs[i]);
}
delete [] pucr->rgvarInputArgs;
pucr->rgvarInputArgs = NULL;
pucr->cInputArgs = 0;
}
}
//+---------------------------------------------------------------------------
//
// Function: CleanupResponseData
//
// Purpose: Frees resources of a UPNP_CONTROL_RESPONSE_DATA
// structure
//
// Arguments:
// pucr [in] Address of the structure to cleanup
// fSucceeded [in] Whether the structure is valid for success
//
// Returns:
// (none)
//
// Author: spather 2000/09/24
//
// Notes:
// This function just frees the resources used by the fields within
// the passed in structure. It does not free the memory used by the
// structure itself.
//
VOID
CleanupResponseData(
IN UPNP_CONTROL_RESPONSE_DATA * pucrd,
IN BOOL fSucceeded)
{
Assert(pucrd);
if (fSucceeded)
{
if (pucrd->Success.rgvarOutputArgs)
{
for (DWORD i = 0; i < pucrd->Success.cOutputArgs; i++)
{
VariantClear(&pucrd->Success.rgvarOutputArgs[i]);
}
CoTaskMemFree(pucrd->Success.rgvarOutputArgs);
pucrd->Success.rgvarOutputArgs = NULL;
pucrd->Success.cOutputArgs = 0;
}
}
else
{
if (pucrd->Fault.bstrFaultCode)
{
SysFreeString(pucrd->Fault.bstrFaultCode);
pucrd->Fault.bstrFaultCode = NULL;
}
if (pucrd->Fault.bstrFaultString)
{
SysFreeString(pucrd->Fault.bstrFaultString);
pucrd->Fault.bstrFaultString = NULL;
}
if (pucrd->Fault.bstrUPnPErrorCode)
{
SysFreeString(pucrd->Fault.bstrUPnPErrorCode);
pucrd->Fault.bstrUPnPErrorCode = NULL;
}
if (pucrd->Fault.bstrUPnPErrorString)
{
SysFreeString(pucrd->Fault.bstrUPnPErrorString);
pucrd->Fault.bstrUPnPErrorString = NULL;
}
}
}
//+---------------------------------------------------------------------------
//
// Function: CleanupDeserializedResponse
//
// Purpose: Frees resources used by the fields of a UPNP_CONTROL_RESPONSE
// structure
//
// Arguments:
// pucr [in] Address of the structure to cleanup
//
// Returns:
// (none)
//
// Author: spather 2000/09/24
//
// Notes:
// This function just frees the resources used by the fields within
// the passed in structure. It does not free the memory used by the
// structure itself.
//
VOID
CleanupDeserializedResponse(
IN UPNP_CONTROL_RESPONSE * pucresp)
{
if (pucresp->bstrActionName)
{
SysFreeString(pucresp->bstrActionName);
pucresp->bstrActionName = NULL;
}
CleanupResponseData(&pucresp->ucrData, pucresp->fSucceeded);
}
//+---------------------------------------------------------------------------
//
// Function: HrValidateControlMethod
//
// Purpose: Validates that the HTTP verb used is valid for this
// type of request.
//
// Arguments:
// pszaMethod [in] The HTTP verb
//
// Returns:
// If the method is valid, the return value is S_OK. If the method is
// not valid, the function returns one of the COM error codes defined
// in WinError.h.
//
// Author: spather 2000/09/21
//
// Notes:
//
HRESULT
HrValidateControlMethod(
IN LPSTR pszaMethod)
{
HRESULT hr = S_OK;
AssertSz(pszaMethod,
"HrValidateControlMethod(): NULL Method passed");
if ((0 != lstrcmpiA(pszaMethod, "POST")) &&
(0 != lstrcmpiA(pszaMethod, "M-POST")))
{
if (0 == lstrcmpiA(pszaMethod, "GET") ||
0 == lstrcmpiA(pszaMethod, "HEAD"))
{
hr = UPNP_E_METHOD_NOT_ALLOWED;
}
else
{
hr = UPNP_E_METHOD_NOT_IMPLEMENTED;
}
}
TraceError("HrValidateControlMethod(): Exiting",
hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrWriteResponse
//
// Purpose: Writes a control response back to the client
//
// Arguments:
// pecb [in] The extension control block for the request
// pxdnRespEnvelope [in] The XML DOM node representing the response
// envelope
// fSucceeded [in] Indicates whether or not this is a success
// response
//
// 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/09/25
//
// Notes:
//
HRESULT
HrWriteResponse(
IN LPEXTENSION_CONTROL_BLOCK pecb,
IN IXMLDOMDocument * pxddRespEnvelope,
IN BOOL fSucceeded)
{
HRESULT hr = S_OK;
BSTR bstrRespEnvelope = NULL;
DWORD cchRespEnvelope = 0;
LPSTR pszaRespEnvelope = NULL;
DWORD cchHeaders = 0;
CHAR szaHeaders[256];
LPCSTR pcszaHeadersFmt =
"Content-Length: %d\r\n"
"Content-Type: text/xml; charset=\"utf-8\"\r\n"
"EXT:\r\n\r\n";
// Convert response envelope to UTF-8 string.
hr = pxddRespEnvelope->get_xml(&bstrRespEnvelope);
if (SUCCEEDED(hr))
{
pszaRespEnvelope = Utf8FromWsz(bstrRespEnvelope);
if (pszaRespEnvelope)
{
cchRespEnvelope = lstrlenA(pszaRespEnvelope);
TraceTag(ttidUDHISAPI,
"HrWriteResponse(): "
"Sending response:\n"
"%s",
pszaRespEnvelope);
}
else
{
hr = E_OUTOFMEMORY;
TraceError("HrWriteResponse(): "
"Failed to convert response envelope to ANSI",
hr);
}
SysFreeString(bstrRespEnvelope);
}
else
{
TraceError("HrWriteResponse(): "
"Failed to get envelope XML text",
hr);
}
wsprintfA(szaHeaders, pcszaHeadersFmt, cchRespEnvelope);
cchHeaders = lstrlenA(szaHeaders);
if (fSucceeded)
{
// Success response, so HTTP status is 200 OK
if (bSendResponseToClient(pecb,
"200 OK",
cchHeaders,
szaHeaders,
cchRespEnvelope,
pszaRespEnvelope))
{
pecb->dwHttpStatusCode = HTTP_STATUS_OK;
TraceTag(ttidUDHISAPI,
"HrWriteResponse(): "
"Successfully sent success response");
}
else
{
hr = HrFromLastWin32Error();
TraceLastWin32Error("HrWriteResponse(): "
"Failed to send success response to client");
}
}
else
{
// Failure response, so HTTP status is 500 Internal Server Error
if (bSendResponseToClient(pecb,
"500 Internal Server Error",
cchHeaders,
szaHeaders,
cchRespEnvelope,
pszaRespEnvelope))
{
pecb->dwHttpStatusCode = HTTP_STATUS_SERVER_ERROR;
TraceTag(ttidUDHISAPI,
"HrWriteResponse(): "
"Successfully sent error response");
}
else
{
hr = HrFromLastWin32Error();
TraceLastWin32Error("HrWriteResponse(): "
"Failed to send error response to client");
}
}
if (pszaRespEnvelope)
{
delete [] pszaRespEnvelope;
pszaRespEnvelope = NULL;
cchRespEnvelope = 0;
}
TraceError("HrWriteResponse(): "
"Exiting",
hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrAppendFaultElementToBody
//
// Purpose: Appends a SOAP fault element to a body element. The fault
// element contains a SOAP fault code, fault string, and possibly
// a detail element containing a UPnP error code and error string.
//
// Arguments:
// pxdd [in] Document object to use to create elements
// pxdnBody [in] The body element
// pucrespDeserialized [in] The deserialized response info
//
// 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/09/26
//
// Notes:
//
HRESULT
HrAppendFaultElementToBody(
IN IXMLDOMDocument * pxdd,
IN IXMLDOMNode * pxdnBody,
IN UPNP_CONTROL_RESPONSE * pucrespDeserialized)
{
HRESULT hr = S_OK;
IXMLDOMNode * pxdnFault = NULL;
IXMLDOMElement * pxdeFaultCode = NULL;
IXMLDOMElement * pxdeFaultString = NULL;
IXMLDOMNode * pxdnDetail = NULL;
UPNP_CONTROL_RESPONSE_DATA * pucrd = NULL;
pucrd = &pucrespDeserialized->ucrData;
// Create the fault element.
hr = HrCreateElement(pxdd,
L"SOAP-ENV:Fault",
WSZ_SOAP_NAMESPACE_URI,
&pxdnFault);
if (SUCCEEDED(hr))
{
Assert(pxdnFault);
// Create the Fault element's children.
hr = HrCreateElementWithTextValue(pxdd,
L"faultcode",
pucrd->Fault.bstrFaultCode,
&pxdeFaultCode);
if (SUCCEEDED(hr))
{
Assert(pxdeFaultCode);
hr = HrCreateElementWithTextValue(pxdd,
L"faultstring",
pucrd->Fault.bstrFaultString,
&pxdeFaultString);
if (SUCCEEDED(hr))
{
Assert(pxdeFaultString);
hr = HrCreateElement(pxdd,
L"detail",
NULL,
&pxdnDetail);
if (SUCCEEDED(hr))
{
IXMLDOMNode * pxdnUPnPError = NULL;
hr = HrCreateElement(pxdd,
L"UPnPError",
WSZ_UPNP_NAMESPACE_URI,
&pxdnUPnPError);
if (SUCCEEDED(hr))
{
IXMLDOMElement * pxdeErrorCode = NULL;
Assert(pxdnUPnPError);
// Add children to UPnPError.
hr = HrCreateElementWithTextValue(pxdd,
L"errorCode",
pucrd->Fault.bstrUPnPErrorCode,
&pxdeErrorCode);
if (SUCCEEDED(hr))
{
Assert(pxdeErrorCode);
hr = pxdnUPnPError->appendChild(pxdeErrorCode,
NULL);
if (SUCCEEDED(hr))
{
IXMLDOMElement * pxdeErrorDesc = NULL;
hr = HrCreateElementWithTextValue(pxdd,
L"errorDescription",
pucrd->Fault.bstrUPnPErrorString,
&pxdeErrorDesc);
if (SUCCEEDED(hr))
{
Assert(pxdeErrorDesc);
hr = pxdnUPnPError->appendChild(pxdeErrorDesc,
NULL);
if (SUCCEEDED(hr))
{
TraceTag(ttidUDHISAPI,
"HrAppendFaultElementToBody(): "
"Successfully appended errorCode "
"and errorDescription elements");
}
else
{
TraceError("HrAppendFaultElementToBody(): "
"Failed to append errorCode element",
hr);
}
pxdeErrorDesc->Release();
}
else
{
TraceError("HrAppendFaultElementToBody(): "
"Failed to create errorDescription element",
hr);
}
}
else
{
TraceError("HrAppendFaultElementToBody(): "
"Failed to append errorCode element",
hr);
}
pxdeErrorCode->Release();
}
else
{
TraceError("HrAppendFaultElementToBody(): "
"Failed to create errorCode element",
hr);
}
// If successful, attach UPnPError to detail.
if (SUCCEEDED(hr))
{
hr = pxdnDetail->appendChild(pxdnUPnPError,
NULL);
if (SUCCEEDED(hr))
{
TraceTag(ttidUDHISAPI,
"HrAppendFaultElementToBody(): "
"Successfully appended UPnPError "
"element");
}
else
{
TraceError("HrAppendFaultElementToBody(): "
"Failed to append UPnPError",
hr);
}
}
pxdnUPnPError->Release();
}
else
{
TraceError("HrAppendFaultElementToBody(): "
"Failed to create UPnPError element",
hr);
}
}
else
{
TraceError("HrAppendFaultElementToBody(): "
"Failed to create detail element",
hr);
}
}
else
{
TraceError("HrAppendFaultElementToBody(): "
"Failed to create fault string element",
hr);
}
}
else
{
TraceError("HrAppendFaultElementToBody(): "
"Failed to create fault code element",
hr);
}
// Attach the Fault element's children.
if (SUCCEEDED(hr))
{
Assert(pxdeFaultCode);
Assert(pxdeFaultString);
Assert(pxdnDetail);
hr = pxdnFault->appendChild(pxdeFaultCode, NULL);
if (SUCCEEDED(hr))
{
TraceTag(ttidUDHISAPI,
"HrAppendFaultElementToBody(): "
"Successfully appended fault code element");
hr = pxdnFault->appendChild(pxdeFaultString, NULL);
if (SUCCEEDED(hr))
{
TraceTag(ttidUDHISAPI,
"HrAppendFaultElementToBody(): "
"Successfully appended fault string element");
hr = pxdnFault->appendChild(pxdnDetail, NULL);
if (SUCCEEDED(hr))
{
TraceTag(ttidUDHISAPI,
"HrAppendFaultElementToBody(): "
"Successfully appended detail element");
}
else
{
TraceError("HrAppendFaultElementToBody(): "
"Failed to append detail element",
hr);
}
}
else
{
TraceError("HrAppendFaultElementToBody(): "
"Failed to append fault string element",
hr);
}
}
else
{
TraceError("HrAppendFaultElementToBody(): "
"Failed to append fault code element",
hr);
}
}
// If everything succeeded, then append the fault element to the body.
if (SUCCEEDED(hr))
{
hr = pxdnBody->appendChild(pxdnFault,
NULL);
if (SUCCEEDED(hr))
{
TraceTag(ttidUDHISAPI,
"HrAppendFaultElementToBody(): "
"Successfully appended fault element to body");
}
else
{
TraceError("HrAppendFaultElementToBody(): "
"Failed to append fault element to body",
hr);
}
}
// Clean up.
if (pxdeFaultCode)
{
pxdeFaultCode->Release();
pxdeFaultCode = NULL;
}
if (pxdeFaultString)
{
pxdeFaultString->Release();
pxdeFaultString = NULL;
}
if (pxdnDetail)
{
pxdnDetail->Release();
pxdnDetail = NULL;
}
pxdnFault->Release();
}
else
{
TraceError("HrAppendFaultElementToBody(): "
"Failed to create fault element",
hr);
}
TraceError("HrAppendFaultElementToBody(): "
"Exiting",
hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrAppendActionResponseElementToBody
//
// Purpose: Appends an action response XML element to a body element.
// The action response element contains the action name and
// the output arguments.
//
// Arguments:
// pxdd [in] Document object to use to create elements
// pxdnBody [in] The body element
// pucrDeserialized [in] Address of the deserialized request structure
// (needed only for the QueryStateVariable case,
// to get the variable name)
// pucrespDeserialized [in] The deserialized response info
// pServiceDescInfo [in] Service Description Info object for the
// service
//
// 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/09/26
//
// Notes:
//
HRESULT
HrAppendActionResponseElementToBody(
IN IUPnPAutomationProxy * pAutomationProxy,
IN IXMLDOMDocument * pxdd,
IN IXMLDOMNode * pxdnBody,
IN UPNP_CONTROL_REQUEST * pucrDeserialized,
IN UPNP_CONTROL_RESPONSE * pucrespDeserialized,
IN IUPnPServiceDescriptionInfo * pServiceDescInfo)
{
HRESULT hr = S_OK;
DWORD cchSuffix = 0;
DWORD cchPrefix = 0;
LPCWSTR pcszSuffix = L"Response";
LPCWSTR pcszPrefix = L"m:";
DWORD cchElementName = 0;
LPWSTR pszElementName = NULL;
BOOL bIsQsv = FALSE;
cchSuffix = lstrlenW(pcszSuffix);
cchPrefix = lstrlenW(pcszPrefix);
cchElementName = SysStringLen(pucrespDeserialized->bstrActionName) +
cchSuffix + cchPrefix;
pszElementName = new WCHAR[cchElementName+1];
bIsQsv = (0 == lstrcmpW(pucrespDeserialized->bstrActionName, L"QueryStateVariable"));
if (pszElementName)
{
IXMLDOMNode * pxdnActionResponse = NULL;
wsprintfW(pszElementName,
L"%s%s%s",
pcszPrefix,
pucrespDeserialized->bstrActionName,
pcszSuffix);
LPWSTR pszServiceType;
hr = pAutomationProxy->GetServiceType(&pszServiceType);
if (SUCCEEDED(hr))
{
if (bIsQsv)
{
hr = HrCreateElement(pxdd,
pszElementName,
WSZ_UPNP_NAMESPACE_URI,
&pxdnActionResponse);
}
else
{
hr = HrCreateElement(pxdd,
pszElementName,
pszServiceType,
&pxdnActionResponse);
}
CoTaskMemFree(pszServiceType);
if (SUCCEEDED(hr))
{
UPNP_CONTROL_RESPONSE_DATA * pucrd = NULL;
pucrd = &pucrespDeserialized->ucrData;
if (pucrd->Success.cOutputArgs)
{
DWORD cOutputArgs = 0;
BSTR * rgbstrNames = NULL;
BSTR * rgbstrTypes = NULL;
if (bIsQsv)
{
// Special case for QueryStateVariable.
// We know there is one "output argument" called
// "return" and its data type is the type of the
// variable being queried.
cOutputArgs = 1;
rgbstrNames = (BSTR *) CoTaskMemAlloc(sizeof(BSTR));
rgbstrTypes = (BSTR *) CoTaskMemAlloc(sizeof(BSTR));
if (rgbstrNames && rgbstrTypes)
{
rgbstrNames[0] = SysAllocString(L"return");
if (rgbstrNames[0])
{
rgbstrTypes[0] = NULL;
hr = pServiceDescInfo->GetVariableType(
V_BSTR(&pucrDeserialized->rgvarInputArgs[0]),
&rgbstrTypes[0]);
}
else
{
hr = E_OUTOFMEMORY;
}
}
else
{
hr = E_OUTOFMEMORY;
}
if (SUCCEEDED(hr))
{
TraceTag(ttidUDHISAPI,
"HrAppendActionResponseElementToBody(): "
"Successfully obtained out arg name and data "
"type for QueryStateVariable");
}
else
{
TraceError("HrAppendActionResponseElementToBody(): "
"Failed to obtain out arg name and data "
"type for QueryStateVariable",
hr);
if (rgbstrNames)
{
if (rgbstrNames[0])
{
SysFreeString(rgbstrNames[0]);
rgbstrNames[0] = NULL;
}
CoTaskMemFree(rgbstrNames);
rgbstrNames = NULL;
}
if (rgbstrTypes)
{
if (rgbstrTypes[0])
{
SysFreeString(rgbstrTypes[0]);
rgbstrTypes[0] = NULL;
}
CoTaskMemFree(rgbstrTypes);
rgbstrTypes = NULL;
}
}
}
else
{
hr = pServiceDescInfo->GetOutputArgumentNamesAndTypes(
pucrespDeserialized->bstrActionName,
&cOutputArgs,
&rgbstrNames,
&rgbstrTypes);
if (SUCCEEDED(hr))
{
TraceTag(ttidUDHISAPI,
"HrAppendActionResponseElementToBody(): "
"Successfully obtained out arg name and data "
"type for action %S",
pucrespDeserialized->bstrActionName);
}
else
{
TraceError("HrAppendActionResponseElementToBody(): "
"Failed to obtain out arg name and data "
"for action",
hr);
}
}
if (SUCCEEDED(hr))
{
Assert(cOutputArgs == pucrd->Success.cOutputArgs);
Assert(rgbstrNames);
Assert(rgbstrTypes);
for (DWORD i = 0;
SUCCEEDED(hr) && (i < cOutputArgs);
i++)
{
IXMLDOMElement * pxdeArg = NULL;
WCHAR * pwszArgName;
pwszArgName = new WCHAR[lstrlenW(rgbstrNames[i]) + 1];
if (NULL == pwszArgName)
{
hr = E_OUTOFMEMORY;
}
if (SUCCEEDED(hr))
{
lstrcpyW(pwszArgName, rgbstrNames[i]);
hr = HrCreateElementWithType(pxdd,
pwszArgName,
rgbstrTypes[i],
pucrd->Success.rgvarOutputArgs[i],
&pxdeArg);
delete [] pwszArgName;
}
if (SUCCEEDED(hr))
{
hr = pxdnActionResponse->appendChild(pxdeArg,
NULL);
if (SUCCEEDED(hr))
{
TraceTag(ttidUDHISAPI,
"HrAppendActionResponseElementToBody(): "
"Successfully appended element for "
"argument %S",
rgbstrNames[i]);
}
else
{
TraceError("HrAppendActionResponseElementToBody(): "
"Failed to append argument element",
hr);
}
pxdeArg->Release();
}
else if (E_FAIL == hr)
{
// something inside MSXML failed trying to put the value
// most likely, this is caused by an un-coercible type
TraceError("HrAppendActionResponseElementToBody(): "
"Probably failed to coerce argument element",
hr);
hr = UPNP_E_DEVICE_ERROR;
}
else
{
TraceError("HrAppendActionResponseElementToBody(): "
"Failed to get create argument element",
hr);
}
}
// Clean up.
for (DWORD i = 0; i < cOutputArgs; i++)
{
SysFreeString(rgbstrNames[i]);
rgbstrNames[i] = NULL;
SysFreeString(rgbstrTypes[i]);
rgbstrTypes[i] = NULL;
}
CoTaskMemFree(rgbstrNames);
rgbstrNames = NULL;
CoTaskMemFree(rgbstrTypes);
rgbstrTypes = NULL;
}
}
// If everything went ok, append action response element to
// body.
if (SUCCEEDED(hr))
{
hr = pxdnBody->appendChild(pxdnActionResponse, NULL);
if (SUCCEEDED(hr))
{
TraceTag(ttidUDHISAPI,
"HrAppendActionResponseElementToBody(): "
"Successfully appended %S element to body",
pszElementName);
}
else
{
TraceError("HrAppendActionResponseElementToBody(): "
"Failed to append action response element",
hr);
}
}
pxdnActionResponse->Release();
}
else
{
TraceError("HrAppendActionResponseElementToBody(): "
"Failed to create action response element",
hr);
}
}
else
{
TraceError("HrAppendActionResponseElementToBody(): "
"Failed to get service type", hr);
}
delete [] pszElementName;
pszElementName = NULL;
}
else
{
hr = E_OUTOFMEMORY;
TraceError("HrAppendActionResponseElementToBody(): "
"Failed to allocate memory for action "
"response element name",
hr);
}
TraceError("HrAppendActionResponseElementToBody(): "
"Exiting",
hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrSerializeResponse
//
// Purpose: Serializes response information.
//
// Arguments:
// pucrDeserialized [in] Address of the deserialized request structure
// (needed only for the QueryStateVariable case,
// to get the variable name)
// pucrespDeserialized [in] Address of structure containing deserialized
// response info
// pServiceDescInfo [in] Service Description Info object for the
// service
// pusrespSerialized [in] Address of structure whose fields will be
// initialized with serialized response info
//
// 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/09/25
//
// Notes:
//
HRESULT
HrSerializeResponse(
IN IUPnPAutomationProxy * pAutomationProxy,
IN UPNP_CONTROL_REQUEST * pucrDeserialized,
IN UPNP_CONTROL_RESPONSE * pucrespDeserialized,
IN IUPnPServiceDescriptionInfo * pServiceDescInfo,
IN UPNP_SOAP_RESPONSE * pusrespSerialized)
{
HRESULT hr = S_OK;
IXMLDOMDocument * pxddResponse = NULL;
IXMLDOMNode * pxdnRespEnvelope = NULL;
// Create an XML DOM Document in which we'll build the XML response.
hr = CoCreateInstance(CLSID_DOMDocument30,
NULL,
CLSCTX_INPROC_SERVER,
IID_IXMLDOMDocument,
(void **) &pxddResponse);
if (SUCCEEDED(hr))
{
Assert(pxddResponse);
hr = HrAppendProcessingInstruction(pxddResponse, L"xml", L"version=\"1.0\"");
// Create the envelope element.
if (SUCCEEDED(hr))
{
hr = HrCreateElement(pxddResponse,
L"SOAP-ENV:Envelope",
WSZ_SOAP_NAMESPACE_URI,
&pxdnRespEnvelope);
}
if (SUCCEEDED(hr))
{
Assert(pxdnRespEnvelope);
IXMLDOMElement * pxdeRespEnvelope = NULL;
hr = pxdnRespEnvelope->QueryInterface(IID_IXMLDOMElement, (void**)&pxdeRespEnvelope);
if (SUCCEEDED(hr))
{
hr = HrSetTextAttribute(pxdeRespEnvelope, L"SOAP-ENV:encodingStyle",
L"http://schemas.xmlsoap.org/soap/encoding/");
pxdeRespEnvelope->Release();
pxdeRespEnvelope = NULL;
}
if (SUCCEEDED(hr))
{
IXMLDOMNode * pxdnBody = NULL;
hr = HrCreateElement(pxddResponse,
L"SOAP-ENV:Body",
WSZ_SOAP_NAMESPACE_URI,
&pxdnBody);
if (SUCCEEDED(hr))
{
Assert(pxdnBody);
if (pucrespDeserialized->fSucceeded)
{
hr = HrAppendActionResponseElementToBody(pAutomationProxy,
pxddResponse,
pxdnBody,
pucrDeserialized,
pucrespDeserialized,
pServiceDescInfo);
if (UPNP_E_DEVICE_ERROR == hr)
{
// Failed at the last minute
// Change our minds and send a fault response
CleanupResponseData(&pucrespDeserialized->ucrData, TRUE);
pucrespDeserialized->fSucceeded = FALSE;
pucrespDeserialized->ucrData.Fault.bstrFaultCode =
SysAllocString(L"SOAP-ENV:Client");
pucrespDeserialized->ucrData.Fault.bstrFaultString =
SysAllocString(L"UPnPError");
pucrespDeserialized->ucrData.Fault.bstrUPnPErrorCode =
SysAllocString(L"501");
pucrespDeserialized->ucrData.Fault.bstrUPnPErrorString =
SysAllocString(L"Internal Device Error");
if (NULL == pucrespDeserialized->ucrData.Fault.bstrFaultCode ||
NULL == pucrespDeserialized->ucrData.Fault.bstrFaultString ||
NULL == pucrespDeserialized->ucrData.Fault.bstrUPnPErrorCode ||
NULL == pucrespDeserialized->ucrData.Fault.bstrUPnPErrorString)
{
hr = E_OUTOFMEMORY;
CleanupResponseData(&pucrespDeserialized->ucrData, FALSE);
}
else
{
hr = S_OK;
}
}
}
if (SUCCEEDED(hr) && FALSE == pucrespDeserialized->fSucceeded)
{
hr = HrAppendFaultElementToBody(pxddResponse,
pxdnBody,
pucrespDeserialized);
}
if (SUCCEEDED(hr))
{
hr = pxdnRespEnvelope->appendChild(pxdnBody,
NULL);
}
pxdnBody->Release();
}
else
{
TraceError("HrSerializeResponse(): "
"Failed to create body element",
hr);
}
}
else
{
// we weren't able to create the encodingStyle attribute
}
if (SUCCEEDED(hr))
{
hr = pxddResponse->appendChild(pxdnRespEnvelope, NULL);
}
if (pxdnRespEnvelope)
{
pxdnRespEnvelope->Release();
}
}
else
{
TraceError("HrSerializeResponse(): "
"Failed to create envelope element",
hr);
}
}
if (SUCCEEDED(hr))
{
Assert(pxddResponse);
// Cleanup any old data.
CleanupSerializedResponse(pusrespSerialized);
pusrespSerialized->fSucceeded = pucrespDeserialized->fSucceeded;
pusrespSerialized->pxddRespEnvelope = pxddResponse;
}
else
{
if (pxddResponse)
{
pxddResponse->Release();
pxddResponse = NULL;
}
}
TraceError("HrSerializeResponse(): "
"Exiting",
hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrExecuteRequest
//
// Purpose: Executes a request.
//
// Arguments:
// pAutomationProxy [in] The automation proxy for the target service
// pucreq [in] Address of the structure containing the
// deserialized request
// pucresp [in] Address of the structure whose fields will be
// initialized with the serialized response info
//
// 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/09/24
//
// Notes:
//
HRESULT
HrExecuteRequest(
IN IUPnPAutomationProxy * pAutomationProxy,
IN UPNP_CONTROL_REQUEST * pucreq,
IN UPNP_CONTROL_RESPONSE * pucresp)
{
HRESULT hr = S_OK;
hr = pAutomationProxy->ExecuteRequest(pucreq, pucresp);
if (SUCCEEDED(hr))
{
TraceTag(ttidUDHISAPI,
"HrExecuteRequest(): "
"IUPnPAutomationProxy::Execute() succeeded");
}
else
{
TraceError("HrExecuteRequest(): "
"Failed to execute request",
hr);
}
TraceError("HrExecuteRequest(): "
"Exiting",
hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrDeserializeSOAPRequest
//
// Purpose: Deserializes a parsed SOAP Request
//
// Arguments:
// pusrParsed [in] Address of structure containing parsed request
// pServiceDescInfo [in] Service Description Info object for the service
// pucrDeserialized [in] Address of structure whose fields will be
// initialized with the deserialized request info
//
// 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/09/24
//
// Notes:
//
HRESULT
HrDeserializeSOAPRequest(
IN UPNP_SOAP_REQUEST * pusrParsed,
IN IUPnPServiceDescriptionInfo * pServiceDescInfo,
IN UPNP_CONTROL_REQUEST * pucrDeserialized)
{
HRESULT hr = S_OK;
UPNP_CONTROL_REQUEST ucr;
BOOL fIsQueryStateVariable = FALSE;
ZeroMemory(&ucr, sizeof(UPNP_CONTROL_REQUEST));
// First, copy the action name.
ucr.bstrActionName = SysAllocString(pusrParsed->bstrActionName);
if (ucr.bstrActionName)
{
if (0 == lstrcmpW(ucr.bstrActionName, L"QueryStateVariable"))
{
fIsQueryStateVariable = TRUE;
}
// Next, deserialize the arguments, if any.
if (pusrParsed->pxdnlArgs)
{
LONG listLength = 0;
hr = pusrParsed->pxdnlArgs->get_length(&listLength);
if (SUCCEEDED(hr))
{
if (listLength)
{
// Allocate array of VARIANT arguments.
ucr.cInputArgs = (DWORD) listLength;
ucr.rgvarInputArgs = new VARIANT[ucr.cInputArgs];
if (ucr.rgvarInputArgs)
{
DWORD cInArgs = 0;
BSTR * rgbstrNames = NULL;
BSTR * rgbstrTypes = NULL;
ZeroMemory(ucr.rgvarInputArgs,
ucr.cInputArgs * sizeof(VARIANT));
if (FALSE == fIsQueryStateVariable)
{
// Only get the argument names and types if this is
// not a QSV request. For a QSV request, there is
// only one argument (the variable name) and it's
// type is "string".
hr = pServiceDescInfo->GetInputArgumentNamesAndTypes(ucr.bstrActionName,
&cInArgs,
&rgbstrNames,
&rgbstrTypes);
if (SUCCEEDED(hr))
{
Assert(cInArgs == ucr.cInputArgs);
Assert(rgbstrNames);
Assert(rgbstrTypes);
}
}
if (SUCCEEDED(hr))
{
// For each argument node, get the value and put it into
// the variant array.
for (LONG i = 0; SUCCEEDED(hr) && (i < listLength); i++)
{
IXMLDOMNode * pxdnItem = NULL;
hr = pusrParsed->pxdnlArgs->get_item(i, &pxdnItem);
if (SUCCEEDED(hr))
{
if (fIsQueryStateVariable)
{
BSTR * pbstr = &(V_BSTR(&ucr.rgvarInputArgs[i]));
hr = pxdnItem->get_text(pbstr);
if (SUCCEEDED(hr))
{
Assert(*pbstr);
ucr.rgvarInputArgs[i].vt = VT_BSTR;
TraceTag(ttidUDHISAPI,
"HrDeserializeSOAPRequest(): "
"Got variable name for QSV request: %S",
*pbstr);
}
else
{
TraceError("HrDeserializeSOAPRequest(): "
"Failed to get variable name for QSV request",
hr);
}
}
else
{
hr = HrGetTypedValueFromElement(pxdnItem,
rgbstrTypes[i],
&ucr.rgvarInputArgs[i]);
if (SUCCEEDED(hr))
{
TraceTag(ttidUDHISAPI,
"HrDeserializeSOAPRequest(): "
"Deserialized argument %S",
rgbstrNames[i]);
}
else
{
TraceError("HrDeserializeSOAPRequest(): "
"Failed to get node value",
hr);
}
}
pxdnItem->Release();
}
else
{
TraceError("HrDeserializeSOAPRequest(): "
"Failed to get item from list",
hr);
}
}
if (FALSE == fIsQueryStateVariable)
{
// Only got this stuff if it was not a QSV
// request, so only clean it up in this case.
for (DWORD i = 0; i < cInArgs; i++)
{
SysFreeString(rgbstrNames[i]);
rgbstrNames[i] = NULL;
SysFreeString(rgbstrTypes[i]);
rgbstrTypes[i] = NULL;
}
CoTaskMemFree(rgbstrNames);
rgbstrNames = NULL;
CoTaskMemFree(rgbstrTypes);
rgbstrTypes = NULL;
}
}
}
else
{
hr = E_OUTOFMEMORY;
TraceError("HrDeserializeSOAPRequest(): "
"Failed to allocate array of variant arguments",
hr);
}
}
else
{
ucr.cInputArgs = 0;
ucr.rgvarInputArgs = NULL;
}
}
else
{
TraceError("HrDeserializeSOAPRequest(): "
"Failed to get argument list length",
hr);
}
}
else
{
ucr.cInputArgs = 0;
ucr.rgvarInputArgs = NULL;
}
}
else
{
hr = E_OUTOFMEMORY;
TraceError("HrDeserializeSOAPRequest(): "
"Failed to allocate memory to copy action name",
hr);
}
if (SUCCEEDED(hr))
{
Assert(ucr.bstrActionName);
Assert(FImplies((ucr.cInputArgs > 0), ucr.rgvarInputArgs));
Assert(FImplies((0 == ucr.cInputArgs), (NULL == ucr.rgvarInputArgs)));
// Clean up any existing data in the output structure.
CleanupDeserializedRequest(pucrDeserialized);
pucrDeserialized->bstrActionName = ucr.bstrActionName;
pucrDeserialized->cInputArgs = ucr.cInputArgs;
pucrDeserialized->rgvarInputArgs = ucr.rgvarInputArgs;
}
else
{
// Clean up.
CleanupDeserializedRequest(&ucr);
}
TraceError("HrDeserializeSOAPRequest(): "
"Exiting",
hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrParseSOAPRequest
//
// Purpose: Parses a SOAP request and returns the parsed action name
// and list of arguments.
//
// Arguments:
// pxdnReqEnvelope [in] XML DOM node representing the SOAP envelope
// pusrParsed [in] Address of a UPNP_SOAP_REQUEST structure whose
// fields will be initialized with the parsed info
// 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/09/24
//
// Notes:
//
HRESULT
HrParseSOAPRequest(
IN IXMLDOMNode * pxdnReqEnvelope,
IN UPNP_SOAP_REQUEST * pusrParsed)
{
HRESULT hr = S_OK;
IXMLDOMNode * pxdnBody = NULL;
LPWSTR rgpszBodyTokens[] = {L"Body"};
BSTR bstrActionName = NULL;
IXMLDOMNodeList * pxdnlArgs = NULL;
hr = HrGetNestedChildElement(pxdnReqEnvelope,
rgpszBodyTokens,
1,
&pxdnBody);
if (S_OK == hr)
{
IXMLDOMNode * pxdnAction = NULL;
Assert(pxdnBody);
hr = pxdnBody->get_firstChild(&pxdnAction);
if (S_OK == hr)
{
Assert(pxdnAction);
hr = pxdnAction->get_baseName(&bstrActionName);
if (SUCCEEDED(hr))
{
Assert(bstrActionName);
hr = pxdnAction->get_childNodes(&pxdnlArgs);
if (SUCCEEDED(hr))
{
TraceTag(ttidUDHISAPI,
"HrParseSOAPRequest(): "
"Successfully obtained name and argument list "
"for action %S\n",
bstrActionName);
}
else
{
TraceError("HrParseSOAPRequest(): "
"Failed to get argument list",
hr);
}
}
else
{
TraceError("HrParseSOAPRequest(): "
"Failed to get action name",
hr);
}
pxdnAction->Release();
}
else
{
TraceError("HrParseSOAPRequest(): "
"Failed to get action element",
hr);
if (S_FALSE == hr)
{
hr = E_FAIL;
}
}
pxdnBody->Release();
}
else
{
TraceError("HrParseSOAPRequest(): "
"Failed to get Body element",
hr);
if (S_FALSE == hr)
{
hr = E_FAIL;
}
}
if (SUCCEEDED(hr))
{
// Clean up any existing data in the output strucutre.
CleanupSerializedRequest(pusrParsed);
pusrParsed->bstrActionName = bstrActionName;
pusrParsed->pxdnlArgs = pxdnlArgs;
}
else
{
// Cleanup
if (bstrActionName)
{
SysFreeString(bstrActionName);
bstrActionName = NULL;
}
if (pxdnlArgs)
{
pxdnlArgs->Release();
pxdnlArgs = NULL;
}
// any error other than E_OUTOFMEMORY implies bad SOAP
if (hr != E_OUTOFMEMORY)
{
hr = UPNP_E_BAD_REQUEST;
}
}
TraceError("HrParseSOAPRequest(): "
"Exiting",
hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrValidateControlRequest
//
// Purpose: Validates the structure and content of a control request
//
// Arguments:
// pecb [in] The extension control block for the request
// pxdnReqEnvelope [in] The request envelope
// pServiceDescInfo[in] The service description info object for the
// service
//
// 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/09/25
//
// Notes:
//
HRESULT HrValidateControlRequest(
IN LPEXTENSION_CONTROL_BLOCK pecb,
IN IXMLDOMNode * pxdnReqEnvelope,
IN IUPnPServiceDescriptionInfo * pServiceDescInfo)
{
HRESULT hr = S_OK;
hr = HrValidateSOAPRequest(pxdnReqEnvelope,
pecb,
pServiceDescInfo);
TraceError("HrValidateRequest(): "
"Exiting",
hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrReadRequest
//
// Purpose: Reads the HTTP packet body of a control request, loads
// it into an XML DOM document, validates it for correctness,
// and, if valid, returns a pointer to the XML DOM node
// representing the request envelope.
//
// Arguments:
// pecb [in] The extension control block for the request.
// ppxdnReqEnvelope [out] Receives a pointer to the XML DOM node
// representing the request envelope.
//
// 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/09/24
//
// Notes:
//
HRESULT
HrReadRequest(
IN LPEXTENSION_CONTROL_BLOCK pecb,
OUT IXMLDOMNode ** ppxdnReqEnvelope)
{
HRESULT hr = S_OK;
DWORD cbReqBody = 0;
LPSTR pszaReqBody = NULL;
BSTR bstrReqBody = NULL;
// Get the request body. If it's not all here, read it in.
if (pecb->cbAvailable < pecb->cbTotalBytes)
{
// There is some data that is not available in the extension
// control block. Read it from the client.
cbReqBody = pecb->cbTotalBytes;
pszaReqBody = new CHAR[cbReqBody];
if (pszaReqBody)
{
DWORD cbBytesToRead = cbReqBody;
DWORD cbBytesRead = 0;
LPSTR pszaTemp = pszaReqBody;
while (cbBytesRead < cbReqBody)
{
if (pecb->ReadClient(pecb->ConnID,
pszaTemp,
&cbBytesToRead))
{
// After calling ReadClient, cbBytesToRead contains the
// number of bytes actually read.
cbBytesRead += cbBytesToRead;
pszaTemp += cbBytesToRead;
cbBytesToRead = cbReqBody - cbBytesRead;
}
else
{
TraceLastWin32Error("HrReadRequest(): "
"ReadClient() failed");
hr = HrFromLastWin32Error();
break;
}
}
}
else
{
hr = E_OUTOFMEMORY;
TraceError("HrReadRequest(): "
"Could not allocate memory for request body",
hr);
}
}
else
{
cbReqBody = pecb->cbAvailable;
pszaReqBody = (LPSTR) pecb->lpbData;
if (0 == cbReqBody)
{
// There is no body in the request.
hr = UPNP_E_MISSING_CONTENT_LENGTH;
TraceError("HrReadRequest(): "
"Request had no body",
hr);
}
}
// Turn the request body into a BSTR.
if (SUCCEEDED(hr))
{
INT result = 0;
INT cchWide = 0;
Assert(pszaReqBody);
// Have to convert the request body into a BSTR to load it into
// an XML DOM document.
result = MultiByteToWideChar(CP_UTF8,
0,
pszaReqBody,
cbReqBody,
NULL,
0);
if (result)
{
LPWSTR pszBody = NULL;
cchWide = result;
pszBody = new WCHAR[cchWide+1]; // want NULL termination
if (pszBody)
{
result = MultiByteToWideChar(CP_UTF8,
0,
pszaReqBody,
cbReqBody,
pszBody,
cchWide);
if (result)
{
pszBody[cchWide] = UNICODE_NULL;
bstrReqBody = SysAllocString(pszBody);
if (bstrReqBody)
{
TraceTag(ttidUDHISAPI,
"HrReadRequest(): "
"Request Body is \n%S",
bstrReqBody);
}
else
{
hr = E_OUTOFMEMORY;
TraceError("HrReadRequest(): "
"Failed to allocate BSTR request body",
hr);
}
}
else
{
TraceLastWin32Error("HrReadRequest(): "
"MultiByteToWideChar #2 failed");
hr = HrFromLastWin32Error();
}
delete [] pszBody;
pszBody = NULL;
}
else
{
hr = E_OUTOFMEMORY;
TraceError("HrReadRequest(): "
"Failed to allocate memory for wide char body",
hr);
}
}
else
{
TraceLastWin32Error("HrReadRequest(): "
"MultiByteToWideChar #1 failed");
hr = HrFromLastWin32Error();
}
}
// Create an XML DOM document from the request body.
if (SUCCEEDED(hr))
{
IXMLDOMDocument * pxddRequest = NULL;
hr = CoCreateInstance(CLSID_DOMDocument30,
NULL,
CLSCTX_INPROC_SERVER,
IID_IXMLDOMDocument,
(void **) &pxddRequest);
if (SUCCEEDED(hr))
{
Assert(pxddRequest);
hr = pxddRequest->put_async(VARIANT_FALSE);
if (SUCCEEDED(hr))
{
VARIANT_BOOL vbSuccess = VARIANT_FALSE;
hr = HrValidateContentType(pecb);
if (SUCCEEDED(hr))
{
pxddRequest->put_resolveExternals(VARIANT_FALSE);
hr = pxddRequest->loadXML(bstrReqBody, &vbSuccess);
}
if (SUCCEEDED(hr) && (VARIANT_TRUE == vbSuccess))
{
IXMLDOMElement * pxdeEnvelope = NULL;
hr = pxddRequest->get_documentElement(&pxdeEnvelope);
if (S_OK == hr)
{
Assert(pxdeEnvelope);
hr = pxdeEnvelope->QueryInterface(IID_IXMLDOMNode,
(void **)
ppxdnReqEnvelope);
if (SUCCEEDED(hr))
{
TraceTag(ttidUDHISAPI,
"HrReadRequest(): "
"Successfully obtained XML DOM Node for "
"request envelope");
}
else
{
TraceError("HrReadRequest(): "
"Failed to QI for IXMLDOMNode",
hr);
}
pxdeEnvelope->Release();
}
else
{
TraceError("HrReadRequest(): "
"Failed to get document element",
hr);
}
}
else
{
if (S_FALSE == hr)
{
// There was a parse error.
Assert(VARIANT_FALSE == vbSuccess);
hr = UPNP_E_BAD_REQUEST;
}
TraceError("HrReadRequest(): "
"Failed to load XML",
hr);
}
}
else
{
TraceError("HrReadRequest(): "
"Failed to set async property on DOM document",
hr);
}
pxddRequest->Release();
}
else
{
TraceError("HrReadRequest(): "
"Failed to create XML DOM document",
hr);
}
}
// Cleanup
if (bstrReqBody)
{
SysFreeString(bstrReqBody);
bstrReqBody = NULL;
}
if (pszaReqBody && (pszaReqBody != (LPSTR)pecb->lpbData))
{
delete [] pszaReqBody;
pszaReqBody = NULL;
cbReqBody = 0;
}
TraceError("HrReadRequest(): "
"Exiting",
hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrParseControlQueryString
//
// Purpose: Parses the query string to a control request and uses
// the registrar to obtain the Automation Proxy object for
// the service specified in it.
//
// Arguments:
// pszaQueryString [in] The query string.
// ppAutomationProxy [out] Receives a pointer to the Automation Proxy
// object for the service
//
// 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/09/25
//
// Notes:
//
HRESULT HrParseControlQueryString(
IN LPSTR pszaQueryString,
OUT IUPnPAutomationProxy ** ppAutomationProxy)
{
HRESULT hr = S_OK;
DWORD cchQueryString = 0;
DWORD cchPrefix = 0;
LPCSTR pcszaPrefix = "control=";
LPWSTR pszUDN = NULL;
LPWSTR pszServiceID = NULL;
// Control query string is of the form:
// control=UDN+ServiceID.
// We know that at least the word "control" is there because
// that main dispatch code that called us already checked for it.
cchPrefix = lstrlenA(pcszaPrefix);
cchQueryString = lstrlenA(pszaQueryString);
if (cchQueryString > cchPrefix)
{
LPSTR pszaUDNStart = NULL;
LPSTR pszaDelimiter = NULL;
pszaUDNStart = pszaQueryString+cchPrefix;
for (DWORD i = cchPrefix; i < cchQueryString; i++)
{
if ('+' == pszaQueryString[i])
{
pszaDelimiter = &pszaQueryString[i];
break;
}
}
if (pszaDelimiter)
{
DWORD cchUDN = (DWORD) (pszaDelimiter - pszaUDNStart);
LPSTR pszaUDN = NULL;
pszaUDN = new CHAR[cchUDN+1];
if (pszaUDN)
{
LPSTR pszaServiceID = NULL;
lstrcpynA(pszaUDN, pszaUDNStart, cchUDN+1);
pszaServiceID = pszaDelimiter+1;
if (*pszaServiceID)
{
pszUDN = WszFromSz(pszaUDN);
if (pszUDN)
{
pszServiceID = WszFromSz(pszaServiceID);
if (pszServiceID)
{
TraceTag(ttidUDHISAPI,
"HrParseControlRequest(): "
"UDN == %S and ServiceID == %S",
pszUDN,
pszServiceID);
}
else
{
hr = E_OUTOFMEMORY;
TraceError("HrParseControlQueryString(): "
"Failed to convert Service ID to "
"unicode string",
hr);
}
}
else
{
hr = E_OUTOFMEMORY;
TraceError("HrParseControlQueryString(): "
"Failed to convert UDN to unicode string",
hr);
}
}
else
{
hr = E_INVALIDARG;
TraceError("HrParseControlQueryString(): "
"No service ID found",
hr);
}
delete [] pszaUDN;
pszaUDN = NULL;
}
else
{
hr = E_OUTOFMEMORY;
TraceError("HrParseControlQueryString(): "
"Failed to allocate memory for UDN ANSI string",
hr);
}
}
else
{
hr = E_INVALIDARG;
TraceError("HrParseControlQueryString(): "
"Could not find delimiter character",
hr);
}
}
else
{
hr = E_INVALIDARG;
TraceError("HrParseControlQueryString(): "
"Length of query string was <= length of the prefix",
hr);
}
// If the above succeeded, we should have the UDN and service ID
// as wide strings.
if (SUCCEEDED(hr))
{
IUPnPRegistrarLookup * pRegistrarLookup = NULL;
hr = CoCreateInstance(CLSID_UPnPRegistrar,
NULL,
CLSCTX_INPROC_SERVER,
IID_IUPnPRegistrarLookup,
(void **) &pRegistrarLookup);
if (SUCCEEDED(hr))
{
hr = pRegistrarLookup->GetAutomationProxy(pszUDN,
pszServiceID,
ppAutomationProxy);
if (SUCCEEDED(hr))
{
TraceTag(ttidUDHISAPI,
"HrParseControlQuerySring(): "
"Successfully obtained automation proxy");
}
else
{
TraceError("HrParseControlQueryString(): "
"Failed to get automation proxy",
hr);
}
pRegistrarLookup->Release();
}
else
{
TraceError("HrParseControlQueryString(): "
"Failed to create registrar object",
hr);
}
}
// Cleanup
if (pszUDN)
{
delete [] pszUDN;
pszUDN = NULL;
}
if (pszServiceID)
{
delete [] pszServiceID;
pszServiceID = NULL;
}
TraceError("HrParseControlQueryString(): "
"Exiting",
hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrDoRequestAndReturnResponse
//
// Purpose: This function does the main work in processing a control
// request. It reads the request, parses it, deserializes it,
// executes it, serializes the response, generates a SOAP
// response and sends that to the client.
//
// Arguments:
// pecb [in] The extension control block for the request.
//
// 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/09/24
//
// Notes:
//
HRESULT
HrDoRequestAndReturnResponse(
IN LPEXTENSION_CONTROL_BLOCK pecb)
{
HRESULT hr = S_OK;
IXMLDOMNode * pxdnReqEnvelope = NULL;
IUPnPAutomationProxy * pAutomationProxy = NULL;
IUPnPServiceDescriptionInfo * pServiceDescriptionInfo = NULL;
// At this point, we do not know if the request is valid. First we'll
// parse the query string, use the registrar to look up the service it
// specifies. If this succeeeds, we'll read the request body, and validate
// its structure.
hr = HrParseControlQueryString(pecb->lpszQueryString,
&pAutomationProxy);
if (SUCCEEDED(hr))
{
Assert(pAutomationProxy);
hr = pAutomationProxy->QueryInterface(IID_IUPnPServiceDescriptionInfo,
(void **) &pServiceDescriptionInfo);
if (SUCCEEDED(hr))
{
Assert(pServiceDescriptionInfo);
hr = HrReadRequest(pecb, &pxdnReqEnvelope);
if (SUCCEEDED(hr))
{
hr = HrValidateControlRequest(pecb,
pxdnReqEnvelope,
pServiceDescriptionInfo);
}
}
else
{
TraceError("HrDoRequestAndReturnResponse(): "
"Failed to get service desc info interface",
hr);
}
}
// If everything above succeeded, we can assume the request is
// completely valid.
if (SUCCEEDED(hr))
{
UPNP_SOAP_REQUEST usrParsed;
Assert(pxdnReqEnvelope);
ZeroMemory(&usrParsed, sizeof(UPNP_SOAP_REQUEST));
hr = HrParseSOAPRequest(pxdnReqEnvelope, &usrParsed);
if (SUCCEEDED(hr))
{
UPNP_CONTROL_REQUEST ucrDeserialized;
ZeroMemory(&ucrDeserialized, sizeof(UPNP_CONTROL_REQUEST));
hr = HrDeserializeSOAPRequest(&usrParsed,
pServiceDescriptionInfo,
&ucrDeserialized);
if (SUCCEEDED(hr))
{
UPNP_CONTROL_RESPONSE ucrespDeserialized;
ZeroMemory(&ucrespDeserialized, sizeof(UPNP_CONTROL_RESPONSE));
hr = HrExecuteRequest(pAutomationProxy,
&ucrDeserialized,
&ucrespDeserialized);
if (SUCCEEDED(hr))
{
UPNP_SOAP_RESPONSE usrespSerialized = {0};
hr = HrSerializeResponse(pAutomationProxy,
&ucrDeserialized,
&ucrespDeserialized,
pServiceDescriptionInfo,
&usrespSerialized);
if (SUCCEEDED(hr))
{
Assert(usrespSerialized.pxddRespEnvelope);
hr = HrWriteResponse(pecb,
usrespSerialized.pxddRespEnvelope,
usrespSerialized.fSucceeded);
CleanupSerializedResponse(&usrespSerialized);
}
CleanupDeserializedResponse(&ucrespDeserialized);
}
CleanupDeserializedRequest(&ucrDeserialized);
}
CleanupSerializedRequest(&usrParsed);
}
}
// Cleanup
if (pServiceDescriptionInfo)
{
pServiceDescriptionInfo->Release();
pServiceDescriptionInfo = NULL;
}
if (pAutomationProxy)
{
pAutomationProxy->Release();
pAutomationProxy = NULL;
}
if (pxdnReqEnvelope)
{
pxdnReqEnvelope->Release();
pxdnReqEnvelope = NULL;
}
TraceError("HrDoRequestAndReturnResponse(): "
"Exiting",
hr);
return hr;
}
DWORD WINAPI
DwHandleControlRequest(
LPVOID lpParameter)
{
LPEXTENSION_CONTROL_BLOCK pecb = NULL;
DWORD dwStatus = HSE_STATUS_SUCCESS;
HCONN ConnID;
HRESULT hr = S_OK;
BOOL fKeepConn = FALSE;
pecb = (LPEXTENSION_CONTROL_BLOCK) lpParameter;
AssertSz(pecb,
"DwHandleControlRequest(): "
"NULL extension control block");
pecb->ServerSupportFunction(
pecb->ConnID,
HSE_REQ_IS_KEEP_CONN,
&fKeepConn,
NULL,
NULL);
if(fKeepConn)
dwStatus = HSE_STATUS_SUCCESS_AND_KEEP_CONN;
else
dwStatus = HSE_STATUS_SUCCESS;
ConnID = pecb->ConnID;
AssertSz(pecb->lpszQueryString,
"DwHandleControlRequest(): "
"NULL query string passed");
// Validate the method.
hr = HrValidateControlMethod(pecb->lpszMethod);
if (SUCCEEDED(hr))
{
// Get the content GUID.
TraceTag(ttidUDHISAPI,
"DwHandleControlRequest(): ConnID(0x%x) "
"Query string is %s",
ConnID,
pecb->lpszQueryString);
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if (SUCCEEDED(hr))
{
hr = HrDoRequestAndReturnResponse(pecb);
CoUninitialize();
}
else
{
TraceTag(ttidUDHISAPI,
"DwHandleControlRequest(): ConnID(0x%x): "
"Failed to initialize COM, HRESULT == 0x%x",
ConnID,
hr);
}
}
else
{
TraceTag(ttidUDHISAPI,
"DwHandleControlRequest(): ConnID(0x%x): "
"Failed to validate method %s, HRESULT == 0x%x",
ConnID,
pecb->lpszMethod,
hr);
}
if (FAILED(hr))
{
if (UPNP_E_MISSING_SOAP_ACTION == hr || UPNP_E_MISSING_CONTENT_LENGTH == hr)
{
// these errors result in the less specific error:
hr = UPNP_E_BAD_REQUEST;
}
if (UPNP_E_INVALID_CONTENT_TYPE == hr)
{
SendSimpleResponse(pecb, HTTP_STATUS_UNSUPPORTED_MEDIA);
}
else if (UPNP_E_BAD_REQUEST == hr)
{
SendSimpleResponse(pecb, HTTP_STATUS_BAD_REQUEST);
}
else if (UPNP_E_METHOD_NOT_IMPLEMENTED == hr)
{
SendSimpleResponse(pecb, HTTP_STATUS_NOT_SUPPORTED);
}
else if (UPNP_E_METHOD_NOT_ALLOWED == hr)
{
LPCSTR pcszErrorHeaders = "Allow: POST, M-POST\r\n\r\n";
if (bSendResponseToClient(pecb,
"405 Method Not Allowed",
lstrlenA(pcszErrorHeaders),
pcszErrorHeaders,
0,
NULL))
{
pecb->dwHttpStatusCode = HTTP_STATUS_BAD_METHOD;
}
}
else
{
LPCSTR pcszErrorHeaders = "\r\n";
dwStatus = HSE_STATUS_ERROR;
if (bSendResponseToClient(pecb,
"500 Internal Server Error",
lstrlenA(pcszErrorHeaders),
pcszErrorHeaders,
0,
NULL))
{
pecb->dwHttpStatusCode = HTTP_STATUS_SERVER_ERROR;
}
}
}
return dwStatus;
}