2551 lines
81 KiB
C++
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;
|
||
|
}
|