1074 lines
32 KiB
C++
1074 lines
32 KiB
C++
//***************************************************************************
|
|
//
|
|
// Copyright (c) 1997-1999 Microsoft Corporation
|
|
//
|
|
// CWMIXMLOP.CPP
|
|
//
|
|
// rajesh 3/25/2000 Created.
|
|
//
|
|
// This file defines a class that is used for handling WMI protocol
|
|
// operations over XML/HTTP
|
|
//
|
|
//***************************************************************************
|
|
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
#include <objbase.h>
|
|
#include <httpext.h>
|
|
#include <mshtml.h>
|
|
#include <msxml.h>
|
|
#include <time.h>
|
|
|
|
#include <wbemidl.h>
|
|
#include <genlex.h>
|
|
#include <opathlex.h>
|
|
#include <objpath.h>
|
|
|
|
#include "maindll.h"
|
|
#include "provtempl.h"
|
|
#include "common.h"
|
|
#include "wmcomm.h"
|
|
#include "strings.h"
|
|
#include "wmixmlop.h"
|
|
#include "cwmixmlop.h"
|
|
#include "wmiconv.h"
|
|
#include "request.h"
|
|
#include "xmlhelp.h"
|
|
#include "parse.h"
|
|
#include "errors.h"
|
|
|
|
extern long g_cObj;
|
|
|
|
|
|
// Forward declarations
|
|
//==========================================
|
|
|
|
// Gets the HTTP Version# in a client request
|
|
static WMI_XML_HTTP_VERSION GetHTTPVersion(LPEXTENSION_CONTROL_BLOCK pECB);
|
|
|
|
// Service a CIM HTTP request (POST or M-POST)
|
|
static void ServiceCIMRequest(LPEXTENSION_CONTROL_BLOCK pECB, BOOLEAN bIsMpostRequest, IXMLDOMDocument *pDocument);
|
|
|
|
// Service an OPTIONS request
|
|
static void ServiceOptionsRequest (LPEXTENSION_CONTROL_BLOCK pECB);
|
|
|
|
// Send an HTTP Status and optionally, some headers
|
|
static void SendHeaders(LPEXTENSION_CONTROL_BLOCK pECB, LPCSTR pszHeaders, DWORD dwHttpStatus);
|
|
// Extract dynamic header names
|
|
LPSTR GetCIMHeader (char *pszName, BOOLEAN bIsMpostRequest, DWORD dwNs);
|
|
|
|
// Check content-type of request
|
|
static BOOLEAN ContentTypeSupported (LPCSTR pszContentType, char **ppCharset);
|
|
|
|
// Check mandatory extension header
|
|
static BOOLEAN CheckManHeader (char *pszManHeader, DWORD &dwNs);
|
|
|
|
// Check CIM headers
|
|
static BOOLEAN CheckCIMOperationHeader (char *pszCIMOperationHeader);
|
|
static BOOLEAN CheckCIMProtocolVersionHeader (char *pszCIMProtocolVersionHeader, DWORD *pdwStatus);
|
|
|
|
// Check All Operation Request headers
|
|
BOOLEAN CheckRequestHeaders (LPEXTENSION_CONTROL_BLOCK pECB, BOOLEAN bIsMPostRequest,
|
|
char **ppszCharset, char **ppszProtoVersion, char **ppszMethod,
|
|
BOOLEAN &bIsBatch, char **ppszObject, BOOLEAN &bIsMicrosoftWMIClient);
|
|
BOOLEAN CheckAcceptHeader(LPCSTR pszAcceptValue);
|
|
BOOLEAN CheckAcceptEncodingHeader(LPCSTR pszAcceptValue);
|
|
BOOLEAN CheckAcceptCharsetHeader(LPCSTR pszAcceptValue);
|
|
BOOLEAN CheckAcceptLanguageHeader(LPCSTR pszAcceptValue);
|
|
|
|
extern BOOLEAN g_sRandom;
|
|
|
|
CWmiXmlOpHandler::CWmiXmlOpHandler()
|
|
{
|
|
m_cRef = 0;
|
|
InterlockedIncrement(&g_cObj);
|
|
|
|
}
|
|
|
|
//***************************************************************************
|
|
// HRESULT CWmiXmlOpHandler::QueryInterface
|
|
// long CWmiXmlOpHandler::AddRef
|
|
// long CWmiXmlOpHandler::Release
|
|
//
|
|
// DESCRIPTION:
|
|
//
|
|
// Standard Com IUNKNOWN functions.
|
|
//
|
|
//***************************************************************************
|
|
|
|
STDMETHODIMP CWmiXmlOpHandler::QueryInterface (
|
|
|
|
IN REFIID riid,
|
|
OUT LPVOID *ppv
|
|
)
|
|
{
|
|
*ppv=NULL;
|
|
|
|
if (IID_IUnknown==riid)
|
|
*ppv = reinterpret_cast<IUnknown*>(this);
|
|
else if (IID_IWbemXMLOperationsHandler==riid)
|
|
*ppv = reinterpret_cast<IWbemXMLOperationsHandler*>(this);
|
|
|
|
if (NULL!=*ppv)
|
|
{
|
|
((LPUNKNOWN)*ppv)->AddRef();
|
|
return NOERROR;
|
|
}
|
|
|
|
return ResultFromScode(E_NOINTERFACE);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CWmiXmlOpHandler::AddRef(void)
|
|
{
|
|
InterlockedIncrement(&m_cRef);
|
|
return m_cRef;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CWmiXmlOpHandler::Release(void)
|
|
{
|
|
InterlockedDecrement(&m_cRef);
|
|
if (0L!=m_cRef)
|
|
return m_cRef;
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CWmiXmlOpHandler::~CWmiXmlOpHandler
|
|
//
|
|
// DESCRIPTION:
|
|
//
|
|
// Destructor.
|
|
//
|
|
//***************************************************************************
|
|
|
|
CWmiXmlOpHandler::~CWmiXmlOpHandler(void)
|
|
{
|
|
InterlockedDecrement(&g_cObj);
|
|
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE CWmiXmlOpHandler::ProcessHTTPRequest(
|
|
/* [in] */ LPEXTENSION_CONTROL_BLOCK pECB,
|
|
/* [in] */ IUnknown *pDomDocument)
|
|
{
|
|
// Useful information extracted from the HTTP headers
|
|
BOOLEAN bIsMpostRequest = FALSE;
|
|
BOOLEAN bIsOptionsRequest = FALSE;
|
|
|
|
Log (pECB, "Cim2Xml: Called");
|
|
|
|
/*
|
|
* Check the requested METHOD
|
|
* POST, M-POST and OPTIONS are the only required methods to
|
|
* be supported.
|
|
*/
|
|
if (0 == _stricmp(pECB->lpszMethod, HTTP_MPOST_METHOD))
|
|
bIsMpostRequest = TRUE;
|
|
else if (0 == _stricmp(pECB->lpszMethod, HTTP_OPTIONS_METHOD))
|
|
bIsOptionsRequest = TRUE;
|
|
else if (0 != _stricmp(pECB->lpszMethod, HTTP_POST_METHOD))
|
|
{
|
|
// Send an 405 : Method Not ALlowed response with a list of methods
|
|
// supported in the Allow header
|
|
SendHeaders(pECB, "Allow: POST, M-POST", 405);
|
|
LogError (pECB, "Cim2Xml: FAILED Unsupported HTTP method [501]", 501);
|
|
return HSE_STATUS_SUCCESS;
|
|
}
|
|
|
|
if (bIsOptionsRequest)
|
|
ServiceOptionsRequest (pECB);
|
|
else
|
|
{
|
|
// Get the IXMLDomDocument interface pointer
|
|
IXMLDOMDocument *pDocument = NULL;
|
|
if(SUCCEEDED(pDomDocument->QueryInterface(IID_IXMLDOMDocument, (LPVOID *)&pDocument)))
|
|
{
|
|
ServiceCIMRequest(pECB, bIsMpostRequest, pDocument);
|
|
pDocument->Release();
|
|
}
|
|
}
|
|
|
|
return HSE_STATUS_SUCCESS;
|
|
}
|
|
|
|
LPSTR GetCIMHeader (char *pszName, BOOLEAN bIsMpostRequest, DWORD dwNs)
|
|
{
|
|
LPSTR pszHeader = NULL;
|
|
if (bIsMpostRequest)
|
|
{
|
|
if(pszHeader = new char [strlen("HTTP") + strlen(pszName) + 4])
|
|
sprintf (pszHeader, "%s_%02d_%s", "HTTP", dwNs, pszName);
|
|
}
|
|
else
|
|
{
|
|
if(pszHeader = new char [strlen("HTTP") + strlen (pszName) + 2])
|
|
sprintf (pszHeader, "%s_%s", "HTTP", pszName);
|
|
}
|
|
return pszHeader;
|
|
}
|
|
|
|
BOOLEAN CheckCIMOperationHeader (
|
|
char *pszCIMOperationHeader
|
|
)
|
|
{
|
|
return (pszCIMOperationHeader &&
|
|
(0 == _stricmp (pszCIMOperationHeader, "MethodCall")));
|
|
}
|
|
|
|
BOOLEAN CheckCIMProtocolVersionHeader (
|
|
char *pszCIMProtocolVersionHeader,
|
|
DWORD *pdwStatus
|
|
)
|
|
{
|
|
BOOLEAN result = FALSE;
|
|
|
|
if (!pszCIMProtocolVersionHeader)
|
|
*pdwStatus = 400;
|
|
else if (0 != _stricmp (pszCIMProtocolVersionHeader, "1.0"))
|
|
*pdwStatus = 501;
|
|
else
|
|
result = TRUE;
|
|
|
|
return result;
|
|
}
|
|
|
|
BOOLEAN CheckManHeader (
|
|
char *pszManHeader,
|
|
DWORD &dwNs
|
|
)
|
|
{
|
|
BOOLEAN result = FALSE;
|
|
|
|
LPCSTR ptr = NULL;
|
|
|
|
// Must be of the form "http://www.dmtf.org/cim/mapping/http/v1.0 ; ns=XX"
|
|
if ((ptr = pszManHeader) &&
|
|
(0 == _strnicmp (ptr, HTTP_MAN_HEADER, strlen (HTTP_MAN_HEADER))))
|
|
{
|
|
ptr += strlen (HTTP_MAN_HEADER);
|
|
|
|
if (ptr)
|
|
{
|
|
// Look for the "; ns=XX" string
|
|
SKIPWS(ptr)
|
|
|
|
if (ptr && (ptr = strchr (pszManHeader, ';')) && *(++ptr))
|
|
{
|
|
SKIPWS(ptr)
|
|
|
|
if (ptr && (0 == _strnicmp (ptr, HTTP_NS, strlen (HTTP_NS))))
|
|
{
|
|
// Now we should find ourselves a NS value
|
|
ptr += strlen (HTTP_NS);
|
|
|
|
if (ptr)
|
|
{
|
|
dwNs = strtol (ptr, NULL, 0);
|
|
result = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// BOOLEAN ContentTypeSupported
|
|
//
|
|
// Description:
|
|
//
|
|
// Checks the value of the Content-Type header in the incoming
|
|
// POST or M-POST request.
|
|
//
|
|
// Conformance Reference:
|
|
//
|
|
// 4.2.13. Content-Type
|
|
// CIM clients and CIM servers MUST specify (and accept) a
|
|
// value for this header of either "text/xml" or
|
|
// "application/xml" as defined in [18].
|
|
//
|
|
// Parameters:
|
|
//
|
|
// pszContentType Content-Type header value in received request
|
|
// ppCharset Addresses pointer to hold parsed charset on return
|
|
//
|
|
// Return Value:
|
|
//
|
|
// TRUE if Content-Type is acceptable, FALSE otherwise
|
|
//
|
|
//***************************************************************************
|
|
|
|
BOOLEAN ContentTypeSupported (LPCSTR pszContentType, char **ppCharset)
|
|
{
|
|
BOOLEAN status = FALSE;
|
|
|
|
if (pszContentType)
|
|
{
|
|
LPCSTR ptr = NULL;
|
|
|
|
if (0 == _strnicmp (pszContentType, HTTP_APPXML_CONTENTTYPE, strlen (HTTP_APPXML_CONTENTTYPE)))
|
|
{
|
|
ptr = pszContentType + strlen (HTTP_APPXML_CONTENTTYPE);
|
|
}
|
|
else if (0 == _strnicmp (pszContentType, HTTP_TEXTXML_CONTENTTYPE, strlen (HTTP_TEXTXML_CONTENTTYPE)))
|
|
{
|
|
ptr = pszContentType + strlen (HTTP_TEXTXML_CONTENTTYPE);
|
|
}
|
|
|
|
if (ptr)
|
|
{
|
|
status = TRUE;
|
|
|
|
/*
|
|
* OK so far - now look for the charset parameter; note
|
|
* that HTTP 1.1 does not allow LWS between a parameter attribute
|
|
* and its value (section 3.7)
|
|
*/
|
|
SKIPWS(ptr)
|
|
|
|
if (ptr && (ptr = strchr (pszContentType, ';')) && *(++ptr))
|
|
{
|
|
SKIPWS(ptr)
|
|
|
|
if (ptr && (0 == _strnicmp (ptr, HTTP_PARAMETER_CHARSET, strlen (HTTP_PARAMETER_CHARSET))))
|
|
{
|
|
// Now we should find ourselves a charset value
|
|
ptr += strlen (HTTP_PARAMETER_CHARSET);
|
|
|
|
if (ptr)
|
|
{
|
|
*ppCharset = new char [strlen (ptr) + 1];
|
|
strcpy (*ppCharset, ptr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// void ServiceOptionsRequest
|
|
//
|
|
// Description:
|
|
//
|
|
// The main processing routine for every incoming OPTIONS request.
|
|
//
|
|
// Parameters:
|
|
//
|
|
// pECB Pointer to EXTENSTION_CONTROL_BLOCK for this request
|
|
// pszCharset Charset extracted from Content-Type header (or NULL)
|
|
// bIsMpostRequest Whether the request was POST or M-POST
|
|
//
|
|
//***************************************************************************
|
|
|
|
void ServiceOptionsRequest (
|
|
LPEXTENSION_CONTROL_BLOCK pECB
|
|
)
|
|
{
|
|
// Generate a pseudo-random number with the current time as seed
|
|
if (!g_sRandom)
|
|
{
|
|
srand( (unsigned) time (NULL) );
|
|
g_sRandom = TRUE;
|
|
}
|
|
|
|
// Make sure result is in the range 0 to 99
|
|
int i = (100 * rand () / RAND_MAX);
|
|
if (100 == i)
|
|
i--;
|
|
|
|
DWORD dwHeaderSize = 1024;
|
|
char szTempBuffer [1024];
|
|
|
|
char szOpt [10];
|
|
sprintf (szOpt, "%02d", i);
|
|
|
|
// Generate the Opt header
|
|
sprintf (szTempBuffer, "Opt: http://www.dmtf.org/cim/mapping/http/v1.0 ; ns=");
|
|
strcat (szTempBuffer, szOpt);
|
|
strcat (szTempBuffer, "\r\n");
|
|
strcat (szTempBuffer, szOpt);
|
|
strcat (szTempBuffer, "-CIMProtocolVersion: 1.0\r\n");
|
|
strcat (szTempBuffer, szOpt);
|
|
strcat (szTempBuffer, "-CIMSupportedFunctionalGroups: schema-manipulation, association-traversal, query-execution\r\n");
|
|
strcat (szTempBuffer, szOpt);
|
|
strcat (szTempBuffer, "-CIMSupportsMultipleOperations:\r\n");
|
|
strcat (szTempBuffer, szOpt);
|
|
strcat (szTempBuffer, "-CIMSupportedQueryLanguages: WQL\r\n");
|
|
strcat (szTempBuffer, szOpt);
|
|
strcat (szTempBuffer, "-CIMValidation: loosely-validating\r\n");
|
|
|
|
if(g_platformType == WMI_XML_PLATFORM_WHISTLER)
|
|
strcat (szTempBuffer, "MicrosoftWMI: Whistler\r\n");
|
|
else
|
|
strcat (szTempBuffer, "MicrosoftWMI: \r\n");
|
|
strcat (szTempBuffer, "MicrosoftWMIProtocolVersion: 1.0\r\n");
|
|
|
|
|
|
SendHeaders(pECB, szTempBuffer, 200);
|
|
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// void ServiceCIMRequest
|
|
//
|
|
// Description:
|
|
//
|
|
// The main processing routine for every incoming (M-)POST request.
|
|
//
|
|
// Parameters:
|
|
//
|
|
// pECB Pointer to EXTENSTION_CONTROL_BLOCK for this request
|
|
// pszCharset Charset extracted from Content-Type header (or NULL)
|
|
// bIsMpostRequest Whether the request was POST or M-POST
|
|
//
|
|
//***************************************************************************
|
|
|
|
void ServiceCIMRequest(
|
|
LPEXTENSION_CONTROL_BLOCK pECB,
|
|
BOOLEAN bIsMpostRequest,
|
|
IXMLDOMDocument *pDocument
|
|
)
|
|
{
|
|
// RAJESHR - use charset in content-type header to direct XML parser
|
|
// RAJESHR - look at whether it's an M-POST to decide how to handle
|
|
|
|
// Useful information to extract from headers
|
|
char *pszProtoVersion = NULL;
|
|
char *pszMethod = NULL;
|
|
BOOLEAN bIsBatch = FALSE;
|
|
char *pszObject = NULL;
|
|
char *pszCharset = NULL;
|
|
BOOLEAN bIsMicrosoftWMIClient = FALSE;
|
|
|
|
if (CheckRequestHeaders (pECB, bIsMpostRequest, &pszCharset, &pszProtoVersion,
|
|
&pszMethod, bIsBatch, &pszObject, bIsMicrosoftWMIClient))
|
|
{
|
|
// Get the HTTP version# of the request
|
|
WMI_XML_HTTP_VERSION iHttpVersion = WMI_XML_HTTP_VERSION_INVALID;
|
|
if( (iHttpVersion = GetHTTPVersion(pECB)) != WMI_XML_HTTP_VERSION_INVALID)
|
|
{
|
|
// Parse the XML body of the HTTP packet
|
|
//=====================================
|
|
IXMLDOMNode *pMessageNode = NULL;
|
|
DWORD dwStatus = 0;
|
|
LPSTR pszErrorHeaders = NULL;
|
|
if (pMessageNode = GetMessageNode (pDocument, &dwStatus, &pszErrorHeaders))
|
|
{
|
|
// Check the version of the protocol
|
|
BSTR bsProtocolVersion = NULL;
|
|
GetBstrAttribute(pMessageNode, PROTOVERSION_ATTRIBUTE, &bsProtocolVersion);
|
|
if (bsProtocolVersion && (0 == wcscmp (bsProtocolVersion, L"1.0")))
|
|
{
|
|
// Parse the XML body of the HTTP packet
|
|
//=====================================
|
|
CCimHttpMessage *pCimMessage = NULL;
|
|
LPSTR pszCimStatusHeaders = NULL;
|
|
|
|
if(ParseXMLIntoCIMOperation (pMessageNode, &pCimMessage, &dwStatus,
|
|
bIsMpostRequest, bIsMicrosoftWMIClient, iHttpVersion, &pszCimStatusHeaders))
|
|
{
|
|
if(dwStatus == 200)
|
|
{
|
|
pCimMessage->EncodeNormalResponse(pECB);
|
|
delete pCimMessage;
|
|
}
|
|
else
|
|
{
|
|
LogError (pECB, "ServiceCIMRequest: ParseXMLIntoCIMOperation() FAILED", dwStatus);
|
|
|
|
// Send an HTTP Error to the client
|
|
SendHeaders(pECB, pszCimStatusHeaders, dwStatus);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Send an HTTP Error to the client
|
|
SendHeaders(pECB, pszErrorHeaders, dwStatus);
|
|
}
|
|
|
|
pMessageNode->Release();
|
|
}
|
|
else
|
|
{
|
|
LogError (pECB, "ServiceCIMRequest: GetMessageNode() FAILED", dwStatus);
|
|
|
|
// Send an HTTP Error to the client
|
|
SendHeaders(pECB, pszErrorHeaders, dwStatus);
|
|
}
|
|
}
|
|
}
|
|
|
|
delete pszCharset;
|
|
delete pszProtoVersion;
|
|
delete pszMethod;
|
|
delete pszObject;
|
|
}
|
|
|
|
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// BOOLEAN CheckRequestHeaders
|
|
//
|
|
// Description:
|
|
//
|
|
// Checks all HTTP (and CIM) headers associated with an Operation Request
|
|
//
|
|
// Parameters:
|
|
//
|
|
// pECB Pointer to the extension control block
|
|
// pVariant Pointer to hold wrapped XML document on return
|
|
//
|
|
// Return Value:
|
|
//
|
|
// TRUE if successful, FALSE otherwise
|
|
//
|
|
//***************************************************************************
|
|
|
|
BOOLEAN CheckRequestHeaders (
|
|
LPEXTENSION_CONTROL_BLOCK pECB,
|
|
BOOLEAN bIsMpostRequest,
|
|
char **ppszCharset,
|
|
char **ppszProtoVersion,
|
|
char **ppszMethod,
|
|
BOOLEAN &bIsBatch,
|
|
char **ppszObject,
|
|
BOOLEAN &bIsMicrosoftWMIClient
|
|
)
|
|
{
|
|
/*
|
|
* Ignore the If-Range, Range headers
|
|
*
|
|
* No need to check the Authorization, Connection headers. IIS decides to process these headers and take
|
|
* appropriate action.
|
|
*/
|
|
|
|
DWORD dwNs;
|
|
|
|
#define BUFLEN 1024 // This is undefed later
|
|
CHAR szTempBuffer [BUFLEN];
|
|
DWORD dwBufferSize = BUFLEN;
|
|
|
|
// Check the Accept header
|
|
//============================================
|
|
dwBufferSize = BUFLEN;
|
|
if (pECB->GetServerVariable (pECB->ConnID, "Accept", szTempBuffer, &dwBufferSize))
|
|
{
|
|
/*
|
|
* A CIM server MUST accept any value for this header which states that "text/xml" or "application/xml"
|
|
* is an acceptable type for an response entity. A server SHOULD return "406 Not Acceptable" if the
|
|
* Accept header indicates that neither of these content types are acceptable.
|
|
*/
|
|
if(!CheckAcceptHeader(szTempBuffer))
|
|
{
|
|
SendHeaders(pECB, NULL, 406);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Check the Accept-Charset header
|
|
//============================================
|
|
dwBufferSize = BUFLEN;
|
|
if (pECB->GetServerVariable (pECB->ConnID, "Accept-Charset", szTempBuffer, &dwBufferSize))
|
|
{
|
|
/*
|
|
* A CIM server MUST accept any value for this header which implies that "utf-8" is an
|
|
* acceptable character set for an response entity. A server SHOULD return "406 Not Acceptable"
|
|
* if the Accept-Charset header indicates that this character set is not acceptable
|
|
*/
|
|
if(!CheckAcceptCharsetHeader(szTempBuffer))
|
|
{
|
|
SendHeaders(pECB, NULL, 406);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Check the Accept-Encoding header
|
|
//============================================
|
|
dwBufferSize = BUFLEN;
|
|
if (pECB->GetServerVariable (pECB->ConnID, "Accept-Encoding", szTempBuffer, &dwBufferSize))
|
|
{
|
|
/*
|
|
* A CIM Server MUST accept any value for this header which implies that "identity" is
|
|
* an acceptable encoding for the response entity. A server MAY return "406 Not Acceptable"
|
|
* if the Accept-Encoding header indicates that the this encoding is not acceptable
|
|
*/
|
|
if(!CheckAcceptEncodingHeader(szTempBuffer))
|
|
{
|
|
SendHeaders(pECB, NULL, 406);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Check the Accept-Language header
|
|
//============================================
|
|
dwBufferSize = BUFLEN;
|
|
if (pECB->GetServerVariable (pECB->ConnID, "Accept-Language", szTempBuffer, &dwBufferSize))
|
|
{
|
|
/*
|
|
* CIM Spec does not say what the server should do if it doesnt understand the clients language
|
|
* For now, we check to see if it is '*' or is a is in the range than begins with "es" - examples
|
|
* are "es", "es-us", "es-gb" etc.
|
|
*/
|
|
if(!CheckAcceptLanguageHeader(szTempBuffer))
|
|
{
|
|
SendHeaders(pECB, NULL, 406);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Check the Accept-Ranges header
|
|
//============================================
|
|
dwBufferSize = BUFLEN;
|
|
if (pECB->GetServerVariable (pECB->ConnID, "Accept-Ranges", szTempBuffer, &dwBufferSize))
|
|
{
|
|
/*
|
|
* 4.2.5. Accept-Ranges
|
|
* CIM clients MUST NOT include this header in a request. CIM Servers MUST
|
|
* reject a request that includes an Accept-Ranges header with a status of "406
|
|
* Not Acceptable".
|
|
*/
|
|
|
|
SendHeaders(pECB, NULL, 406);
|
|
return FALSE;
|
|
}
|
|
|
|
// Check the Content-Encoding header
|
|
//============================================
|
|
dwBufferSize = BUFLEN;
|
|
if (pECB->GetServerVariable (pECB->ConnID, "Content-Encoding", szTempBuffer, &dwBufferSize))
|
|
{
|
|
/*
|
|
* 4.2.10. Content-Encoding
|
|
* If a CIM client includes a Content-Encoding header in a request,
|
|
* it SHOULD specify a value of "identity", unless it has good reason to believe
|
|
* that the Server can accept another encoding.
|
|
*/
|
|
if(_stricmp(szTempBuffer, "identity") != 0)
|
|
{
|
|
SendHeaders(pECB, NULL, 415);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Check the Content-Language header
|
|
//============================================
|
|
dwBufferSize = BUFLEN;
|
|
if (pECB->GetServerVariable (pECB->ConnID, "Content-Language", szTempBuffer, &dwBufferSize))
|
|
{
|
|
// Ignore this since we dont care about its value
|
|
// RAJESHR - Is this correct
|
|
}
|
|
|
|
// Check the Content-Range header
|
|
//============================================
|
|
dwBufferSize = BUFLEN;
|
|
if (pECB->GetServerVariable (pECB->ConnID, "HTTP_CONTENT_RANGE", szTempBuffer, &dwBufferSize))
|
|
{
|
|
/*
|
|
* 4.2.12. Content-Range
|
|
* CIM clients and CIM Servers MUST NOT use this header.
|
|
*/
|
|
|
|
SendHeaders(pECB, NULL, 416);
|
|
return FALSE;
|
|
}
|
|
|
|
// Check for valid Content-Type and parse out the charset
|
|
//============================================
|
|
dwBufferSize = BUFLEN;
|
|
if (!ContentTypeSupported (pECB->lpszContentType, ppszCharset))
|
|
{
|
|
// RAJESHR : Neither the HTTP RFC nor the CIM Operations spec tells what
|
|
// error should be reported here. Hence for the moment, we send a 406
|
|
SendHeaders(pECB, NULL, 406);
|
|
return FALSE;
|
|
}
|
|
|
|
// Check the Man Header
|
|
//============================================
|
|
dwBufferSize = BUFLEN;
|
|
if (bIsMpostRequest)
|
|
{
|
|
if (!pECB->GetServerVariable (pECB->ConnID, "HTTP_MAN", szTempBuffer, &dwBufferSize)
|
|
|| !CheckManHeader (szTempBuffer, dwNs))
|
|
{
|
|
LogError (pECB, "Cim2Xml: FAILED Missing or bad Man Header [400]", 400);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Check the CIMOperation header - this is a Mandatory header
|
|
// 3.3.4. CIMOperation : If a CIM Server receives CIM Operation request with this header,
|
|
// but with a missing value or a value that is not "MethodCall", then it MUST fail the request
|
|
// with status "400 Bad Request". The CIM Server MUST include a CIMError header in the response
|
|
// with a value of unsupported-operation.
|
|
// If a CIM Server receives a CIM Operation request without this header, it MUST NOT process it
|
|
// as if it were a CIM Operation Request. The status code returned by the CIM Server in response
|
|
// to such a request is outside of the scope of this specification
|
|
//========================================================================
|
|
dwBufferSize = BUFLEN;
|
|
LPSTR pCIMHeader = GetCIMHeader ("CIMOperation", bIsMpostRequest, dwNs);
|
|
|
|
if (pCIMHeader && pECB->GetServerVariable (pECB->ConnID, pCIMHeader, szTempBuffer, &dwBufferSize))
|
|
{
|
|
if(!CheckCIMOperationHeader (szTempBuffer))
|
|
{
|
|
// Return a "400 Bad Request" - RAJESHR include the CIMError header here as per the spec above
|
|
SendHeaders(pECB, NULL, 400);
|
|
delete pCIMHeader;
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LogError (pECB, "Cim2Xml: FAILED Missing CIMOperation Header [400]", 400);
|
|
delete pCIMHeader;
|
|
return FALSE;
|
|
}
|
|
delete pCIMHeader;
|
|
|
|
// Check the CIMProtocolVersion header - this is not a mandatory header
|
|
// 4.3. Errors and Status Codes: "501 Not Implemented": One of the following occured:
|
|
// The CIMProtocolVersion extension header specified in the request specifies a
|
|
// version of the CIM Mapping onto HTTP which is not supported by this CIM Server.
|
|
// The CIM Server MUST include a CIMError header in the response with a value of
|
|
// unsupported-protocol-version.
|
|
//==============================================================================
|
|
dwBufferSize = BUFLEN;
|
|
pCIMHeader = GetCIMHeader ("CIMProtocolVersion", bIsMpostRequest, dwNs);
|
|
DWORD dwStatus;
|
|
|
|
if (pCIMHeader && pECB->GetServerVariable (pECB->ConnID, pCIMHeader, szTempBuffer, &dwBufferSize))
|
|
{
|
|
if(!CheckCIMProtocolVersionHeader (szTempBuffer, &dwStatus))
|
|
{
|
|
// Return a "501 - Not implemented" - RAJESHR include the CIMError header here as per the spec above
|
|
SendHeaders(pECB, NULL, 501);
|
|
delete pCIMHeader;
|
|
return FALSE;
|
|
}
|
|
else
|
|
// Save this for later checking against the version in the <MESSAGE>
|
|
// RAJESHR - must check this against version in XML payload
|
|
*ppszProtoVersion = _strdup (szTempBuffer);
|
|
}
|
|
delete pCIMHeader;
|
|
|
|
// Check the CIMMethod header
|
|
// If a CIM Server receives a CIM Operation Request for which either:
|
|
// The CIMMethod header is present but has an invalid value, or;
|
|
// The CIMMethod header is not present but the Operation Request Message is a Simple Operation Request, or;
|
|
// The CIMMethod header is present but the Operation Request Message is not a Simple Operation Request, or;
|
|
// The CIMMethod header is present, the Operation Request Message is a Simple Operation Request, but
|
|
// the CIMIdentifier value (when unencoded) does not match the unique method name within the Simple
|
|
// Operation Request, then it MUST fail the request and return a status of "400 Bad Request"
|
|
// (and MUST include a CIMError header in the response with a value of header-mismatch), subject to the considerations specified in Errors.
|
|
// RAJESHR - The implementation below is not entirely accurate since it expects a CIMMethod header value
|
|
// even for MULTIREQUESTS, which is not correct
|
|
//=======================================================================================
|
|
dwBufferSize = BUFLEN;
|
|
pCIMHeader = GetCIMHeader ("CIMMethod", bIsMpostRequest, dwNs);
|
|
|
|
if (pCIMHeader && (!pECB->GetServerVariable (pECB->ConnID, pCIMHeader, szTempBuffer, &dwBufferSize)
|
|
|| !szTempBuffer || (0 == strlen (szTempBuffer))))
|
|
{
|
|
// Return a "400 Bad Request" - RAJESHR include the CIMError header here as per the spec above
|
|
SendHeaders(pECB, NULL, 400);
|
|
delete pCIMHeader;
|
|
return FALSE;
|
|
}
|
|
else
|
|
// Save this for later checking against the version in the <MESSAGE>
|
|
// RAJESHR - must check this against version in XML payload
|
|
*ppszMethod = _strdup (szTempBuffer);
|
|
delete pCIMHeader;
|
|
|
|
// Check the CIMObject header
|
|
//============================================
|
|
dwBufferSize = BUFLEN;
|
|
pCIMHeader = GetCIMHeader ("CIMObject", bIsMpostRequest, dwNs);
|
|
|
|
if (pCIMHeader && (!pECB->GetServerVariable (pECB->ConnID, pCIMHeader, szTempBuffer, &dwBufferSize)
|
|
|| !szTempBuffer || (0 == strlen (szTempBuffer))))
|
|
{
|
|
LogError (pECB, "Cim2Xml: FAILED Missing or bad CIMObject Header [400]", 400);
|
|
delete pCIMHeader;
|
|
return FALSE;
|
|
}
|
|
else
|
|
// Save this for later checking against the version in the <MESSAGE>
|
|
// RAJESHR - must check this against version in XML payload
|
|
*ppszObject = _strdup (szTempBuffer);
|
|
delete pCIMHeader;
|
|
|
|
|
|
// Check the MicrosoftWMI header - Save it for later use
|
|
//============================================
|
|
dwBufferSize = BUFLEN;
|
|
pCIMHeader = GetCIMHeader ("MicrosoftWMI", bIsMpostRequest, dwNs);
|
|
|
|
if (pCIMHeader && pECB->GetServerVariable (pECB->ConnID, pCIMHeader, szTempBuffer, &dwBufferSize))
|
|
{
|
|
bIsMicrosoftWMIClient = TRUE;
|
|
}
|
|
delete pCIMHeader;
|
|
|
|
#undef BUFLEN
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static WMI_XML_HTTP_VERSION GetHTTPVersion(LPEXTENSION_CONTROL_BLOCK pECB)
|
|
{
|
|
char pszHTTPVersion[100];
|
|
DWORD dwBuffSize = 100;
|
|
if (pECB->GetServerVariable(pECB->ConnID, SV_SERVER_PROTOCOL, pszHTTPVersion, &dwBuffSize))
|
|
{
|
|
if(_stricmp(pszHTTPVersion, SV_HTTP_1_0) == 0)
|
|
return WMI_XML_HTTP_VERSION_1_0;
|
|
else if ( _stricmp(pszHTTPVersion, SV_HTTP_1_1) == 0)
|
|
return WMI_XML_HTTP_VERSION_1_1;
|
|
}
|
|
|
|
return WMI_XML_HTTP_VERSION_INVALID;
|
|
}
|
|
|
|
static BOOLEAN CheckAcceptHeader(LPCSTR pszAcceptValue)
|
|
{
|
|
// Check to see if it begins with one of the following substrings
|
|
// 1. */*
|
|
// 2. text/*
|
|
// 3. text/xml
|
|
// 4. application/*
|
|
// 5. application/xml
|
|
//====================================================================================
|
|
if(pszAcceptValue)
|
|
{
|
|
if(strcmp(pszAcceptValue, "*/*") == 0 ||
|
|
strcmp(pszAcceptValue, "text/*") == 0 ||
|
|
strcmp(pszAcceptValue, "text/xml") == 0 ||
|
|
strcmp(pszAcceptValue, "application/*") == 0 ||
|
|
strcmp(pszAcceptValue, "application/xml") == 0 )
|
|
return TRUE;
|
|
}
|
|
|
|
// This is not a simple header, it probably contains a list of acceptable media types
|
|
// with a qvalue for each of them.
|
|
// So do the parsing
|
|
//====================================================================================
|
|
|
|
// Do the media-type matching.
|
|
// All we do here is to see that there is atleast on of the above 4 media-type values
|
|
// with a q-value greater than 0
|
|
CAcceptHeaderIterator ali;
|
|
LPCSTR pszMediaType = NULL;
|
|
DOUBLE dbQValue = 0;
|
|
HRESULT sc = ali.ScInit(pszAcceptValue);
|
|
if (SUCCEEDED(sc))
|
|
{
|
|
// Get the media type from the iterator
|
|
//
|
|
while(SUCCEEDED(ali.ScGetNextAcceptToken(&pszMediaType, &dbQValue)))
|
|
{
|
|
// See if it one of the 4 types we're interested in
|
|
if(strcmp(pszMediaType, "*/*") == 0 ||
|
|
strcmp(pszMediaType, "text/*") == 0 ||
|
|
strcmp(pszMediaType, "text/xml") == 0 ||
|
|
strcmp(pszMediaType, "application/*") == 0 ||
|
|
strcmp(pszMediaType, "application/xml") == 0 )
|
|
{
|
|
if(dbQValue > 0.0)
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOLEAN CheckAcceptCharsetHeader(LPCSTR pszAcceptValue)
|
|
{
|
|
// Check to see if it begins with one of the following substrings
|
|
// 1. *
|
|
// 2. utf-8
|
|
//====================================================================================
|
|
if(pszAcceptValue)
|
|
{
|
|
if(strcmp(pszAcceptValue, "*") == 0 ||
|
|
strcmp(pszAcceptValue, "utf-8") == 0 )
|
|
return TRUE;
|
|
}
|
|
|
|
// This is not a simple header, it probably contains a list of acceptable charset types
|
|
// with a qvalue for each of them.
|
|
// So do the parsing
|
|
//====================================================================================
|
|
|
|
// All we do here is to see that there is atleast on of the above 2 charset values
|
|
// with a q-value greater than 0
|
|
CAcceptHeaderIterator ali;
|
|
LPCSTR pszCharset = NULL;
|
|
DOUBLE dbQValue = 0;
|
|
HRESULT sc = ali.ScInit(pszAcceptValue);
|
|
if (SUCCEEDED(sc))
|
|
{
|
|
// Get the charset from the iterator
|
|
//
|
|
while(SUCCEEDED(ali.ScGetNextAcceptToken(&pszCharset, &dbQValue)))
|
|
{
|
|
// See if it one of the 2 types we're interested in
|
|
if(strcmp(pszAcceptValue, "*") == 0 ||
|
|
strcmp(pszAcceptValue, "utf-8") == 0 )
|
|
{
|
|
if(dbQValue > 0.0)
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOLEAN CheckAcceptEncodingHeader(LPCSTR pszAcceptValue)
|
|
{
|
|
// Check to see if it begins with one of the following substrings
|
|
// 1. *
|
|
// 2. identity
|
|
//====================================================================================
|
|
if(pszAcceptValue)
|
|
{
|
|
if(strcmp(pszAcceptValue, "*") == 0 ||
|
|
strcmp(pszAcceptValue, "identity") == 0 )
|
|
return TRUE;
|
|
}
|
|
|
|
// This is not a simple header, it probably contains a list of acceptable encodings
|
|
// with a qvalue for each of them.
|
|
// So do the parsing
|
|
//====================================================================================
|
|
|
|
// All we do here is to see that there is atleast one of the above 2 encoding values
|
|
// with a q-value greater than 0
|
|
CAcceptHeaderIterator ali;
|
|
LPCSTR pszCharset = NULL;
|
|
DOUBLE dbQValue = 0;
|
|
HRESULT sc = ali.ScInit(pszAcceptValue);
|
|
if (SUCCEEDED(sc))
|
|
{
|
|
// Get the encoding from the iterator
|
|
//
|
|
while(SUCCEEDED(ali.ScGetNextAcceptToken(&pszCharset, &dbQValue)))
|
|
{
|
|
// See if it one of the 2 types we're interested in
|
|
if(strcmp(pszAcceptValue, "*") == 0 ||
|
|
strcmp(pszAcceptValue, "identity") == 0 )
|
|
{
|
|
if(dbQValue > 0.0)
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOLEAN CheckAcceptLanguageHeader(LPCSTR pszAcceptValue)
|
|
{
|
|
/*
|
|
* CIM Spec does not say what the server should do if it doesnt understand the clients language
|
|
* For now, we check to see if it is '*' or is a is in the range than begins with "es" - examples
|
|
* are "es", "es-us", "es-gb" etc.
|
|
*/
|
|
|
|
// Check to see if it begins with one of the following substrings
|
|
// 1. *
|
|
// 2. en
|
|
//====================================================================================
|
|
if(pszAcceptValue)
|
|
{
|
|
if(strcmp(pszAcceptValue, "*") == 0 ||
|
|
strcmp(pszAcceptValue, "en") == 0 )
|
|
return TRUE;
|
|
}
|
|
|
|
// This is not a simple header, it probably contains a list of acceptable language
|
|
// with a qvalue for each of them.
|
|
// So do the parsing
|
|
//====================================================================================
|
|
|
|
// All we do here is to see that there is atleast on of the above 2 language values
|
|
// with a q-value greater than 0
|
|
CAcceptHeaderIterator ali;
|
|
LPCSTR pszCharset = NULL;
|
|
DOUBLE dbQValue = 0;
|
|
HRESULT sc = ali.ScInit(pszAcceptValue);
|
|
if (SUCCEEDED(sc))
|
|
{
|
|
// Get the charset from the iterator
|
|
//
|
|
while(SUCCEEDED(ali.ScGetNextAcceptToken(&pszCharset, &dbQValue)))
|
|
{
|
|
// See if it one of the 2 types we're interested in
|
|
if(strcmp(pszAcceptValue, "*") == 0 ||
|
|
strcmp(pszAcceptValue, "en") == 0 )
|
|
{
|
|
if(dbQValue > 0.0)
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
void SendHeaders(LPEXTENSION_CONTROL_BLOCK pECB, LPCSTR pszHeaders, DWORD dwHttpStatus)
|
|
{
|
|
|
|
// Send an 405 : Method Not ALlowed response with a list of methods
|
|
// supported in the Allow header
|
|
HSE_SEND_HEADER_EX_INFO sendHeader;
|
|
sendHeader.pszHeader = pszHeaders;
|
|
sendHeader.cchHeader = (pszHeaders)? strlen(pszHeaders) : 0;
|
|
sendHeader.fKeepConn = FALSE;
|
|
|
|
switch(dwHttpStatus)
|
|
{
|
|
case 200:
|
|
sendHeader.pszStatus = HTTP_STATUS_200; break;
|
|
case 400:
|
|
sendHeader.pszStatus = HTTP_ERR_400; break;
|
|
case 405:
|
|
sendHeader.pszStatus = HTTP_ERR_405; break;
|
|
case 406:
|
|
sendHeader.pszStatus = HTTP_ERR_406; break;
|
|
case 415:
|
|
sendHeader.pszStatus = HTTP_ERR_415; break;
|
|
case 416:
|
|
sendHeader.pszStatus = HTTP_ERR_416; break;
|
|
case 501:
|
|
sendHeader.pszStatus = HTTP_ERR_501; break;
|
|
default:
|
|
{
|
|
// Just convert the DWORD to a string
|
|
char pszStatus[24];
|
|
sprintf(pszStatus, "%d", dwHttpStatus);
|
|
sendHeader.pszStatus = pszStatus; break;
|
|
}
|
|
}
|
|
|
|
sendHeader.cchStatus = strlen (sendHeader.pszStatus);
|
|
pECB->ServerSupportFunction (pECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER_EX,
|
|
&sendHeader, 0, 0);
|
|
} |