//+--------------------------------------------------------------------------- // // 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 #pragma hdrstop #include #include #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; }