// WMSOAPProcessor.cpp: implementation of the WMSOAPProcessor class.
//
//////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2003 Robert Cunnings. All rights reserved.
//
// You may copy, modify, distribute or publish this code free of charge.
//
// NO WARRANTY: This software is provided AS IS, without warranty of any kind.
//
//////////////////////////////////////////////////////////////////////
//
// WMSOAPProcessor class - implementation of SOAP processor for 'server' side.
//  
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "WMSOAPProcessor.h"
#include "DIMEParser.h"
#include <time.h>

#include "..\wmsoapmsg\wmsoapmsg.h"
#include "..\wmsoapmsg\wmsoapmsg_i.c"
#include "..\wmwsdlclient\wmwsdlclient.h"
#include "wmsoaplistener.h"

static char *g_pszUserAgent = "White Mesa SOAP Server/3.2 (Acting as Intermediary)";

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

unsigned long GetXMLOutUTF8(Iwmsoapmsg2 *pIwmsoapMsg, char **ppszPtr)
{
	VARIANT varBuffer;
    VariantInit(&varBuffer);
	VARIANT_BOOL bResult;
	*ppszPtr = NULL;
    pIwmsoapMsg->GetBuffer(&varBuffer, &bResult);
	if (bResult == VARIANT_FALSE)
	{
		return 0;
	}
	void *pData;
	SAFEARRAY *pArray = varBuffer.parray;
	if (FAILED(SafeArrayAccessData(pArray, &pData)))
	{
		return 0;
	}
	unsigned long ulLength = pArray->rgsabound[0].cElements;
	// copy data into buffer
	*ppszPtr = (char *)malloc(ulLength); 
	if (*ppszPtr == NULL)
	{
		SafeArrayDestroy(pArray);
		return 0;
	}
	memcpy(*ppszPtr, pData, ulLength);
	SafeArrayUnaccessData(pArray);
	SafeArrayDestroy(pArray);
	return ulLength;
}

bool WMWebMethodProcessor::LoadDIMEParser(  Iwmsoapmsg2 *pIwmsoapMsg,
                                            DIMEParser *pDIMEParser,
                                            char *pszOutput,
                                            unsigned long ulOutputSize)
{
	// create primary payload, accept default record size of 4096 octets
	try
	{
		pDIMEParser->SerializeInit();
	}
	catch (DIMEParserException *e)
	{
		delete e;
		return false;
	}
    string strType;
    if (m_lSOAPVersion == SOAP_VERSION_1_1)
    {
		strType = g_pszSoapEnvelopeNS;
    }
    else
    {
        strType = g_pszSoap12EnvelopeNS;
    }
	if (pDIMEParser->AddPayload(pszOutput,
								ulOutputSize,
								string(""),
								strType,
								DIME_TYPENAME_FORMAT_URI) == false)
	{
		return false;
	}
	// now get secondary payloads
    VARIANT_BOOL bResult = VARIANT_FALSE;
	long lPosition;
	BSTR bstrId, bstrType;
	long lTNF;
	IUnknown *pIUnknown;
	_variant_t varOptions;
	for (pIwmsoapMsg->GetFirstDIMEAttachmentResp(  &bstrId,
													&bstrType,
													&lTNF,
													&pIUnknown,
													&varOptions,
													&lPosition,
													&bResult);
			bResult == VARIANT_TRUE;
			pIwmsoapMsg->GetNextDIMEAttachmentResp(&bstrId,
												&bstrType,
												&lTNF,
												&pIUnknown,
												&varOptions,
												&lPosition,
												&bResult)
	)
	{
		string strId, strType;
		WideStringToUTF8(wstring(bstrId), strId);
		WideStringToUTF8(wstring(bstrType), strType);
		IStorage *pIStorage;
		HRESULT res = pIUnknown->QueryInterface(IID_IStorage, (void **)&pIStorage);
		if (FAILED(res))
		{
			return false;
		}
		SAFEARRAY *pArray = NULL;
		if ((varOptions.vt & VT_TYPEMASK) & (VT_ARRAY | VT_UI1))
		{
			pArray = varOptions.parray;
		}
		if (pDIMEParser->AddPayload(pIStorage,
									strId,
									strType,
									lTNF,
									pArray) == false)
		{
			// this will result in 500 "Internal Server Error"
			pIStorage->Release();
			return false;
		}
		varOptions.Clear();
		pIStorage->Release();
		SysFreeString(bstrId);
		SysFreeString(bstrType);
	}
    return true;
}

bool WMWebMethodProcessor::UnloadDIMEParser(Iwmsoapmsg2 *pIwmsoapMsg,
                                            DIMEParser *pDIMEParser,
                                            SAFEARRAY **ppArray,
                                            char **ppszPrimaryPayload,
                                            unsigned long& ulPrimaryPayloadSize)
{
	int counter = 0;
	vector<DIMEPayload *>::iterator iter;
	for (iter = pDIMEParser->GetPayloads().begin(); iter != pDIMEParser->GetPayloads().end(); iter++)
	{
		DIMEPayload *pPayload = *iter;
		if (counter++ == 0)
		{
			// primary SOAP payload
			pPayload->OpenForRead();
			ulPrimaryPayloadSize = pPayload->GetDataLength();
			*ppszPrimaryPayload = (char *)malloc(ulPrimaryPayloadSize);
			if (*ppszPrimaryPayload == NULL)
			{
				return false;
			}
			unsigned long ulNumBytes = 0;
			pPayload->Read(*ppszPrimaryPayload, ulPrimaryPayloadSize, &ulNumBytes);
			if (ulNumBytes != ulPrimaryPayloadSize)
			{
				return false;
			}
			pPayload->Close();
			// make SAFEARRAY containing input XML
			void *pData;
			*ppArray = SafeArrayCreateVector(VT_UI1, 0, ulPrimaryPayloadSize);
			if (*ppArray == NULL || FAILED(SafeArrayAccessData(*ppArray, &pData)))
			{
				return false;
			}
			memcpy(pData, *ppszPrimaryPayload, ulPrimaryPayloadSize);
			SafeArrayUnaccessData(*ppArray);
		}
		else
		{
			// add attachment to message
			wstring wstrId, wstrType;
			UTF8ToWideString(pPayload->GetId(), wstrId);
			UTF8ToWideString(pPayload->GetType(), wstrType);
			BSTR bstrId = SysAllocString(wstrId.c_str());
			BSTR bstrType = SysAllocString(wstrType.c_str());
			long lTNF = pPayload->GetTNF();
			// dime options
			_variant_t varOptions;
			unsigned long ulLength = pPayload->GetOptionsLength();
			SAFEARRAY *pOptions = SafeArrayCreateVector(VT_UI1, 0L, ulLength);
			if (pOptions == NULL)
			{
				return false;
			}
			if (ulLength > 0)
			{
				void *pData = NULL;
				if (FAILED(SafeArrayAccessData(pOptions, &pData)))
				{
					SafeArrayDestroy(pOptions);
					return false;
				}
				// copy data into vector
				pPayload->GetOptions((unsigned char *)pData, ulLength);
				SafeArrayUnaccessData(pOptions);
				varOptions.parray = pOptions;
				varOptions.vt = VT_ARRAY | VT_UI1;
			}
			VARIANT_BOOL bResult;
			pIwmsoapMsg->AddDIMEAttachmentReq(  bstrId,
												bstrType,
												lTNF,
												pPayload->GetStorage(),
												varOptions,
												&bResult);
			SysFreeString(bstrId);
			SysFreeString(bstrType);
			varOptions.Clear();
			if (bResult == VARIANT_FALSE)
			{
				return false;
			}
		}
	}
    return true;
}

bool WMSOAPPostProcessor::CallPostBodyHandlerA(IDispatch *pIDispatch, IDispatch *pIDispatchMsg)
{
	// body processing
	_variant_t	varRetValBody;
	DISPPARAMS	DispatchParamsBody;
	DISPID		idDispatchIDsBody[2];
	_variant_t	varParamValuesBody[2];
	BSTR		bstrNamesBody[2];

	// method name goes in first
	bstrNamesBody[0] = SysAllocString(L"OnProcessBody");
	bstrNamesBody[1] = SysAllocString(L"SOAPMsgInterface");

	varParamValuesBody[0] = pIDispatchMsg;

	HRESULT hRes = pIDispatch->GetIDsOfNames(   IID_NULL,
										        bstrNamesBody,
										        2,
										        LOCALE_USER_DEFAULT,
										        idDispatchIDsBody);
	// done with bstr's
	SysFreeString(bstrNamesBody[0]);
	SysFreeString(bstrNamesBody[1]);
	if (hRes != S_OK)
	{
        return false;
	}
	// make call
	DISPID idMethod = idDispatchIDsBody[0];
	DispatchParamsBody.rgvarg = varParamValuesBody;
	DispatchParamsBody.rgdispidNamedArgs = &idDispatchIDsBody[1];
	DispatchParamsBody.cArgs = 1;
	DispatchParamsBody.cNamedArgs = 1;

	EXCEPINFO ExceptionInfo;
	UINT dwErrorArg = 0xFFFFFFFF;
	hRes = pIDispatch->Invoke(  idMethod,
								IID_NULL, 
								LOCALE_USER_DEFAULT,
								DISPATCH_METHOD,
								&DispatchParamsBody,
								&varRetValBody,
								&ExceptionInfo,
								&dwErrorArg);
	if (hRes != S_OK || varRetValBody.boolVal == VARIANT_FALSE)
	{
        return false;
	}
    return true;
}

bool WMSOAPPostProcessor::CallPostBodyHandlerB(IDispatch *pIDispatch, IDispatch *pIDispatchMsg)
{
	// body processing
	_variant_t	varRetValBody;
	DISPPARAMS	DispatchParamsBody;
	DISPID		idDispatchIDsBody[4];
	_variant_t	varParamValuesBody[4];
	BSTR		bstrNamesBody[4];

	// method name goes in first
	bstrNamesBody[0] = SysAllocString(L"OnProcessSoapPost");
	bstrNamesBody[1] = SysAllocString(L"SOAPMsgInterface");
	bstrNamesBody[2] = SysAllocString(L"bstrSvcURI");
	bstrNamesBody[3] = SysAllocString(L"bstrExtraPath");

	varParamValuesBody[0] = pIDispatchMsg;
	varParamValuesBody[1] = m_wstrSvcURI.c_str();
	varParamValuesBody[2] = m_wstrExtraPath.c_str();

	HRESULT hRes = pIDispatch->GetIDsOfNames(   IID_NULL,
										        bstrNamesBody,
										        4,
										        LOCALE_USER_DEFAULT,
										        idDispatchIDsBody);
	// done with bstr's
    for (int i = 0; i < 4; i++)
    {
		SysFreeString(bstrNamesBody[i]);
    }
	if (hRes != S_OK)
	{
        return false;
	}
	// make call
	DISPID idMethod = idDispatchIDsBody[0];
	DispatchParamsBody.rgvarg = varParamValuesBody;
	DispatchParamsBody.rgdispidNamedArgs = &idDispatchIDsBody[1];
	DispatchParamsBody.cArgs = 3;
	DispatchParamsBody.cNamedArgs = 3;

	EXCEPINFO ExceptionInfo;
	UINT dwErrorArg = 0xFFFFFFFF;
	hRes = pIDispatch->Invoke(  idMethod,
								IID_NULL, 
								LOCALE_USER_DEFAULT,
								DISPATCH_METHOD,
								&DispatchParamsBody,
								&varRetValBody,
								&ExceptionInfo,
								&dwErrorArg);
	if (hRes != S_OK || varRetValBody.boolVal == VARIANT_FALSE)
	{
        return false;
	}
    return true;
}

bool WMSOAPPostProcessor::InvokeSoap11PostHandler(const wstring& wstrProgID, Iwmsoapmsg2 *pIwmsoapmsg)
{
	try
	{
		CLSID clsid;
		HRESULT hRes = CLSIDFromProgID(wstrProgID.c_str(), &clsid);
		if (hRes != S_OK)
		{
			throw new WMSOAPFault(
				WMSOAPFault::Receiver, L"Body Processing: Failed to resolve ProgID for Body handler.", false);
		}
		IDispatch *pIDispatch;
		hRes = CoCreateInstance(clsid, NULL, CLSCTX_SERVER, IID_IDispatch, (LPVOID *)&pIDispatch);
		if (hRes != S_OK)
		{
			throw new WMSOAPFault(
				WMSOAPFault::Receiver, L"Body Processing: Failed to instantiate Body handler object.", false);
		}
		// get dispatch interface for message object
		IDispatch *pIDispatchMsg;
		hRes = pIwmsoapmsg->QueryInterface(IID_IDispatch, (void **)&pIDispatchMsg);
		if (hRes != S_OK)
		{
			pIDispatch->Release();
			throw new WMSOAPFault(
				WMSOAPFault::Receiver, L"Body Processing: Failed to get message object Dispatch interface.", false);
		}
        if (CallPostBodyHandlerA(pIDispatch, pIDispatchMsg) == false)
        {
		    pIDispatch->Release();
		    pIDispatchMsg->Release();
		    throw new WMSOAPFault(
			    WMSOAPFault::Receiver, L"Body Processing: Invocation of Body handler failed.", false);
        }
		// done with object dispatch interfaces!
		pIDispatch->Release();
		pIDispatchMsg->Release();
	}
	catch (WMSOAPFault *f)
	{
        wstring wstrFaultCode, wstrFaultCodeNS;
        long lSOAPVersion;
        pIwmsoapmsg->GetSOAPVersion(&lSOAPVersion);
        f->GetSoapFaultCode(lSOAPVersion, wstrFaultCode, wstrFaultCodeNS);
		BSTR bstrFaultCode = SysAllocString(wstrFaultCode.c_str());
		BSTR bstrFaultCodeNS = SysAllocString(wstrFaultCodeNS.c_str());
		BSTR bstrFaultString = SysAllocString(f->GetFaultString().c_str());
		VARIANT_BOOL bNeedDetail = (f->NeedDetail() == true ? VARIANT_TRUE : VARIANT_FALSE);
		VARIANT_BOOL bResult;
    	pIwmsoapmsg->SetSOAPFault(bstrFaultCode, bstrFaultCodeNS, bstrFaultString, bNeedDetail, &bResult);
		SysFreeString(bstrFaultCode);
		SysFreeString(bstrFaultCodeNS);
		SysFreeString(bstrFaultString);
		delete f;
		return false;
	}
	return true;
}

bool WMSOAPPostProcessor::InvokeSoap12PostHandler(const wstring& wstrProgID, Iwmsoapmsg2 *pIwmsoapmsg)
{
	try
	{
		CLSID clsid;
		HRESULT hRes = CLSIDFromProgID(wstrProgID.c_str(), &clsid);
		if (hRes != S_OK)
		{
			throw new WMSOAPFault(
				WMSOAPFault::Receiver, L"Body Processing: Failed to resolve ProgID for Body handler.", false);
		}
		IDispatch *pIDispatch;
		hRes = CoCreateInstance(clsid, NULL, CLSCTX_SERVER, IID_IDispatch, (LPVOID *)&pIDispatch);
		if (hRes != S_OK)
		{
			throw new WMSOAPFault(
				WMSOAPFault::Receiver, L"Body Processing: Failed to instantiate Body handler object.", false);
		}

		// get dispatch interface for message object
		IDispatch *pIDispatchMsg;
		hRes = pIwmsoapmsg->QueryInterface(IID_IDispatch, (void **)&pIDispatchMsg);
		if (hRes != S_OK)
		{
			pIDispatch->Release();
			throw new WMSOAPFault(
				WMSOAPFault::Receiver, L"Body Processing: Failed to get message object Dispatch interface.", false);
		}
        // try calling "web method" enabled body handler "OnProcessSoapPost"
        if (CallPostBodyHandlerB(pIDispatch, pIDispatchMsg) == false)
        {
            // no luck, try "OnProcessBody"
            if (CallPostBodyHandlerA(pIDispatch, pIDispatchMsg) == false)
            {
		        pIDispatch->Release();
		        pIDispatchMsg->Release();
		        throw new WMSOAPFault(
			        WMSOAPFault::Receiver, L"Body Processing: Invocation of Body handler failed.", false);
            }
        }
		// done with object dispatch interfaces!
		pIDispatch->Release();
		pIDispatchMsg->Release();
	}
	catch (WMSOAPFault *f)
	{
        wstring wstrFaultCode, wstrFaultCodeNS;
        f->GetSoapFaultCode(SOAP_VERSION_1_2, wstrFaultCode, wstrFaultCodeNS);
		BSTR bstrFaultCode = SysAllocString(wstrFaultCode.c_str());
		BSTR bstrFaultCodeNS = SysAllocString(wstrFaultCodeNS.c_str());
		BSTR bstrFaultString = SysAllocString(f->GetFaultString().c_str());
		BSTR bstrLangId = SysAllocString(L"en");
        wstring wstrSubcode, wstrSubcodeNS;
        f->GetFaultSubCode(wstrSubcode, wstrSubcodeNS);
		BSTR bstrSubcode = SysAllocString(wstrSubcode.c_str());
		BSTR bstrSubcodeNS = SysAllocString(wstrSubcodeNS.c_str());
		VARIANT_BOOL bResult;
    	pIwmsoapmsg->SetSOAP12Fault(bstrFaultCode, bstrFaultCodeNS, bstrSubcode, bstrSubcodeNS, bstrFaultString, bstrLangId, &bResult);
		SysFreeString(bstrSubcode);
		SysFreeString(bstrSubcodeNS);
		SysFreeString(bstrFaultCode);
		SysFreeString(bstrFaultCodeNS);
		SysFreeString(bstrFaultString);
		SysFreeString(bstrLangId);
		delete f;
		return false;
	}
	return true;
}

bool WMSOAPGetProcessor::InvokeSoap12GetHandler(const wstring& wstrProgID, Iwmsoapmsg2 *pIwmsoapmsg)
{
	try
	{
		CLSID clsid;
		HRESULT hRes = CLSIDFromProgID(wstrProgID.c_str(), &clsid);
		if (hRes != S_OK)
		{
			throw new WMSOAPFault(
				WMSOAPFault::Receiver, L"Request: Failed to resolve ProgID for application object.", false);
		}
		IDispatch *pIDispatch;
		hRes = CoCreateInstance(clsid, NULL, CLSCTX_SERVER, IID_IDispatch, (LPVOID *)&pIDispatch);
		if (hRes != S_OK)
		{
			throw new WMSOAPFault(
				WMSOAPFault::Receiver, L"Request: Failed to create application object instance.", false);
		}
		// body processing
		_variant_t	varRetValBody;
		DISPPARAMS	DispatchParamsBody;
		DISPID		idDispatchIDsBody[5];
		_variant_t	varParamValuesBody[5];
		BSTR		bstrNamesBody[5];

		// get dispatch interface for message object
		IDispatch *pIDispatchMsg;
		hRes = pIwmsoapmsg->QueryInterface(IID_IDispatch, (void **)&pIDispatchMsg);
		if (hRes != S_OK)
		{
			pIDispatch->Release();
			throw new WMSOAPFault(
				WMSOAPFault::Receiver, L"Request: Failed to get interface to SOAP message object.", false);
		}

		// method name goes in first
		bstrNamesBody[0] = SysAllocString(L"OnProcessSoapGet");
		bstrNamesBody[1] = SysAllocString(L"SOAPMsgInterface");
		bstrNamesBody[2] = SysAllocString(L"bstrSvcURI");
		bstrNamesBody[3] = SysAllocString(L"bstrExtraPath");
		bstrNamesBody[4] = SysAllocString(L"bstrQueryString");

		varParamValuesBody[0] = pIDispatchMsg;
		varParamValuesBody[1] = m_wstrSvcURI.c_str();
		varParamValuesBody[2] = m_wstrExtraPath.c_str();
		varParamValuesBody[3] = m_wstrQueryString.c_str();

		hRes = pIDispatch->GetIDsOfNames(   IID_NULL,
											bstrNamesBody,
											5,
											LOCALE_USER_DEFAULT,
											idDispatchIDsBody);
		// done with bstr's
        for (int i = 0; i < 5; i++)
        {
		    SysFreeString(bstrNamesBody[i]);
        }
		if (hRes != S_OK)
		{
			pIDispatch->Release();
			pIDispatchMsg->Release();
			throw new WMSOAPFault(
				WMSOAPFault::Receiver, L"Request: Failed to resolve application method call parameters.", false);
		}
		// make call
		DISPID idMethod = idDispatchIDsBody[0];
		DispatchParamsBody.rgvarg = varParamValuesBody;
		DispatchParamsBody.rgdispidNamedArgs = &idDispatchIDsBody[1];
		DispatchParamsBody.cArgs = 4;
		DispatchParamsBody.cNamedArgs = 4;

		EXCEPINFO ExceptionInfo;
		UINT dwErrorArg = 0xFFFFFFFF;
		hRes = pIDispatch->Invoke(  idMethod,
									IID_NULL, 
									LOCALE_USER_DEFAULT,
									DISPATCH_METHOD,
									&DispatchParamsBody,
									&varRetValBody,
									&ExceptionInfo,
									&dwErrorArg);
		if (hRes != S_OK || varRetValBody.boolVal == VARIANT_FALSE)
		{
			pIDispatch->Release();
			pIDispatchMsg->Release();
			throw new WMSOAPFault(
				WMSOAPFault::Receiver, L"Request: Invocation of application method failed.", false);
		}
		// done with object dispatch interfaces!
		pIDispatch->Release();
		pIDispatchMsg->Release();
	}
	catch (WMSOAPFault *f)
	{
        wstring wstrFaultCode, wstrFaultCodeNS;
        f->GetSoapFaultCode(SOAP_VERSION_1_2, wstrFaultCode, wstrFaultCodeNS);
		BSTR bstrFaultCode = SysAllocString(wstrFaultCode.c_str());
		BSTR bstrFaultCodeNS = SysAllocString(wstrFaultCodeNS.c_str());
		BSTR bstrFaultString = SysAllocString(f->GetFaultString().c_str());
		BSTR bstrLangId = SysAllocString(L"en");
        wstring wstrSubcode, wstrSubcodeNS;
        f->GetFaultSubCode(wstrSubcode, wstrSubcodeNS);
		BSTR bstrSubcode = SysAllocString(wstrSubcode.c_str());
		BSTR bstrSubcodeNS = SysAllocString(wstrSubcodeNS.c_str());
		VARIANT_BOOL bResult;
    	pIwmsoapmsg->SetSOAP12Fault(bstrFaultCode, bstrFaultCodeNS, bstrSubcode, bstrSubcodeNS, bstrFaultString, bstrLangId, &bResult);
		SysFreeString(bstrSubcode);
		SysFreeString(bstrSubcodeNS);
		SysFreeString(bstrFaultCode);
		SysFreeString(bstrFaultCodeNS);
		SysFreeString(bstrFaultString);
		SysFreeString(bstrLangId);
		delete f;
		return false;
	}
	return true;
}

int WMSOAPPostProcessor::Process(char *pszInputXML,
							    unsigned long ulInputXMLSize,
                                const string& strSvcURI,
                                const string& strExtraPath,
							    IDispatch *pIWSDLParser,
                                SOAPSvcDesc& SvcDescription,
							    const string& strSOAPAction,
							    char **ppszOutput,
							    unsigned long& ulOutputSize,
							    string& strLogEntry,
							    bool bLoggingEnabled,
							    bool bIsDIMERequest,
							    DIMEParser *pDIMEParser)
{
	// sanity check
	if (pDIMEParser == NULL)
	{
		// this will result in 500 "Internal Server Error"
		return 0;
	}
    UTF8ToWideString(strSvcURI, m_wstrSvcURI);
    UTF8ToWideString(strExtraPath, m_wstrExtraPath);
	// make SOAPAction a wide string
	wstring wstrSOAPAction;
	UTF8ToWideString(strSOAPAction, wstrSOAPAction);
	if (wstrSOAPAction[0] != wchar_t('"'))
	{
		// add quotes if missing to be nice
		wstring wstrTemp = L"\"";
		wstrTemp += wstrSOAPAction;
		wstrTemp += L"\"";
		wstrSOAPAction = wstrTemp;
	}
	// instantiate SOAP Msg object
	Iwmsoapmsg2 *pIwmsoapMsg;
	HRESULT res = CoCreateInstance( CLSID_wmsoapmsg2,
							NULL, CLSCTX_SERVER,
							IID_Iwmsoapmsg2,
							(void **) &pIwmsoapMsg);
	if (FAILED(res))
	{
		// this will result in 500 "Internal Server Error"
		return 0;
	}
	// make SAFEARRAY containing input XML
	SAFEARRAY *pArray = NULL;
	char *pszPrimaryPayload = NULL;
	char *pszOutput = NULL;
	unsigned long ulPrimaryPayloadSize = 0;
	if (bIsDIMERequest == true)
	{
		// DIME request
        if (UnloadDIMEParser(   pIwmsoapMsg,
                                pDIMEParser,
                                &pArray,
                                &pszPrimaryPayload,
                                ulPrimaryPayloadSize) == false)
        {
		    // this will result in 500 "Internal Server Error"
			if (pArray != NULL)
			{
				SafeArrayDestroy(pArray);
			}
            if (pszPrimaryPayload != NULL)
            {
                free(pszPrimaryPayload);
            }
		    pIwmsoapMsg->Release();
            return 0;
        }
	}
	else
	{
		void *pData;
		pArray = SafeArrayCreateVector(VT_UI1, 0, ulInputXMLSize);
		if (pArray == NULL || FAILED(SafeArrayAccessData(pArray, &pData)))
		{
			// this will result in 500 "Internal Server Error"
			if (pArray != NULL)
			{
				SafeArrayDestroy(pArray);
			}
			pIwmsoapMsg->Release();
			return 0;
		}
		memcpy(pData, pszInputXML, ulInputXMLSize);
		SafeArrayUnaccessData(pArray);
	}
	VARIANT varBuffer;
	varBuffer.vt = VT_ARRAY | VT_UI1;
	varBuffer.parray = pArray;
	// set up Result
	VARIANT_BOOL bResult;
	// set up BSTR's
    BSTR bstrSvcName = SysAllocString(SvcDescription.m_wstrWsdlSvc.c_str());
    BSTR bstrPortName = SysAllocString(SvcDescription.m_wstrWsdlPort.c_str());
    BSTR bstrNodeURI = SysAllocString(SvcDescription.m_wstrNodeURI.c_str());
	BSTR bstrSOAPAction = SysAllocString(wstrSOAPAction.c_str());
    BSTR bstrNextHopURL = SysAllocString(SvcDescription.m_wstrNextHopURL.c_str());
    VARIANT_BOOL bExplicitTypingEnabled = SvcDescription.m_bExplicitTypingEnabled == true ? VARIANT_TRUE : VARIANT_FALSE;
    VARIANT_BOOL bNodeTypeEnabled = SvcDescription.m_bNodeTypeEnabled == true ? VARIANT_TRUE : VARIANT_FALSE;
	// increment ref count on WSDL client interface ptr
	pIWSDLParser->AddRef();
	// initialize SOAP message object
	pIwmsoapMsg->Initialize(bstrSvcName,
							bstrPortName,
							bstrNodeURI,
							bstrSOAPAction,
							bstrNextHopURL,
							bExplicitTypingEnabled,
                            bNodeTypeEnabled,
							pIWSDLParser,
							&bResult);
	SysFreeString(bstrSvcName);
	SysFreeString(bstrPortName);
	SysFreeString(bstrNodeURI);
	SysFreeString(bstrSOAPAction);
	SysFreeString(bstrNextHopURL);
	// decrement ref count on interface ptr
	pIWSDLParser->Release();
	// any luck?
	if (bResult == VARIANT_FALSE)
	{
		// this will result in 500 "Internal Server Error"
		pIwmsoapMsg->Release();
		if (pszPrimaryPayload != NULL)
		{
			free(pszPrimaryPayload);
		}
		return 0;
	}
    // pass in data in buffer
    pIwmsoapMsg->SetBuffer(varBuffer, &bResult);
	SafeArrayDestroy(pArray);
    pArray = NULL;
    // set roles
	SAFEARRAYBOUND Bounds[1];
	Bounds[0].lLbound = 0;
	Bounds[0].cElements = SvcDescription.m_listRoles.size();
	pArray = SafeArrayCreate(VT_VARIANT, 1, Bounds);
    VARIANT varTemp;
    varTemp.parray = pArray;
    varTemp.vt = (VT_ARRAY | VT_VARIANT);
    _variant_t varItem;
    long arrIndex[1];
    int counter = 0;
    list<wstring>::iterator iL;
    for (iL = SvcDescription.m_listRoles.begin();
        iL != SvcDescription.m_listRoles.end();
        iL++)
    {
        varItem = (*iL).c_str();
		arrIndex[0] = counter++;
		// put it away
		SafeArrayPutElement(pArray, arrIndex, &varItem);
    }
    pIwmsoapMsg->SetRoles(varTemp, &bResult);
	SafeArrayDestroy(pArray);
    pArray = NULL;
    // are there header block processors to load?
    bool bProcessHeader = SvcDescription.m_listHeaderProcProgIds.empty() == true ? false : true;
    // deserialize the request
	int nHTTPStatus = 200;
	pIwmsoapMsg->Deserialize(&bResult);
    // did deserialization succeed?
	if (bResult == VARIANT_TRUE)
	{
        // get SOAP version
        pIwmsoapMsg->GetSOAPVersion(&m_lSOAPVersion);
        // see if we need to call header processors
		bool bOkToProcess = true;
		if (bProcessHeader == true)
		{
			// initialize header processor dispatcher
			if ((bOkToProcess = m_HeaderDispatcher.Initialize(SvcDescription.m_listHeaderProcProgIds, pIwmsoapMsg)) == true)
			{
				// call request header processors
				bOkToProcess = m_HeaderDispatcher.ProcessHeader(pIwmsoapMsg);
			}
		}
		// proceed with processing body?
		if (bOkToProcess == true)
		{
			// see if all mustUnderstand header elements were handled
			pIwmsoapMsg->CheckMustUnderstand(&bResult);
			// SOAP Fault?
			pIwmsoapMsg->IsSOAPFault(&bResult);
			if (bResult == VARIANT_FALSE)
			{
				// no fault while processing request headers, invoke method
				pIwmsoapMsg->IsIntermediary(&bResult);
				if (bResult == VARIANT_FALSE)
				{
					// we are endpoint so clear any residual header entries
					pIwmsoapMsg->ClearHeaderElems();
					// is this rpc or doc style?
					VARIANT_BOOL bIsRPC = VARIANT_FALSE;
					pIwmsoapMsg->IsRPCStyle(&bIsRPC);
					if (bIsRPC == VARIANT_TRUE)
					{
						// process RPC call parameters
						pIwmsoapMsg->ProcessRPC(&bResult);
						if (bResult == VARIANT_TRUE)
						{
							// now invoke method
							m_AutoStub.InvokeMethod(SvcDescription.m_wstrBodyProcProgId, pIwmsoapMsg);
						}
					}
					else
					{
						// document style operation
                        if (m_lSOAPVersion == SOAP_VERSION_1_1)
                        {
						    InvokeSoap11PostHandler(SvcDescription.m_wstrBodyProcProgId, pIwmsoapMsg);
                        }
                        else
                        {
						    InvokeSoap12PostHandler(SvcDescription.m_wstrBodyProcProgId, pIwmsoapMsg);
                        }
					}
					// toggle to 'response' phase
					pIwmsoapMsg->SetRequestPhase(VARIANT_FALSE);
				}
				else
				{
                    // acting as an intermediary
                    if (SvcDescription.m_bLoopbackEnabled == true)
                    {
					    pIwmsoapMsg->Serialize(&bResult);
			            // copy output message to output buffer
			            ulOutputSize = GetXMLOutUTF8(pIwmsoapMsg, ppszOutput);
					    pIwmsoapMsg->SetRequestPhase(VARIANT_FALSE);
                        goto writelog;
                    }
                    else
                    {
					    // must forward request, so re-serialize request message
					    pIwmsoapMsg->Serialize(&bResult);
					    // toggle to 'response' phase
					    pIwmsoapMsg->SetRequestPhase(VARIANT_FALSE);
					    if (bResult == VARIANT_TRUE)
					    {
						    // get next hop URL in case Header proc modified it
						    BSTR bstrNextHopURL;
						    pIwmsoapMsg->GetNextHopURL(&bstrNextHopURL);
						    string strNextHopURL;
						    WideStringToUTF8(wstring(bstrNextHopURL), strNextHopURL);
						    SysFreeString(bstrNextHopURL);
						    // set up proxy and make SOAP call
						    string strHost, strSvcURI, strTemp;
						    int nPortNbr = 80;
						    string::size_type pos;
						    if ((pos = strNextHopURL.find("http://")) != string::npos)
						    {
							    strTemp = strNextHopURL.substr(pos + 7);
							    pos = strTemp.find('/');
							    strSvcURI = strTemp.substr(pos);
                                // now append any extra path info
                                strSvcURI += strExtraPath;
							    strTemp = strTemp.substr(0, pos);
							    if ((pos = strTemp.find(':')) != string::npos)
							    {
								    // port specified explicitly
								    nPortNbr = atoi(strTemp.substr(pos + 1).c_str());
								    strHost = strTemp.substr(0, pos);
							    }
							    else
							    {
								    strHost = strTemp;
							    }
						    }
						    WMSOAPDIMEHTTPClient HTTPClient;
						    if (HTTPClient.SendSOAPMessage(  string("POST"),
                                                            strSOAPAction,
														    strHost,
														    nPortNbr,
														    strSvcURI,
														    string(g_pszUserAgent),
														    pIwmsoapMsg,
														    bIsDIMERequest) == false)
						    {
							    // something went wrong
                                BSTR bstrFaultCode, bstrFaultCodeNS;
                                BSTR bstrFaultString = SysAllocString(HTTPClient.GetErrorMsg().c_str());
                                if (m_lSOAPVersion == SOAP_VERSION_1_1)
                                {
							        bstrFaultCode = SysAllocString(L"Server");
                                    bstrFaultCodeNS = SysAllocString(g_pwszSoapEnvelopeNS);
							        pIwmsoapMsg->SetSOAPFault(bstrFaultCode, bstrFaultCodeNS, bstrFaultString, VARIANT_FALSE, &bResult);
                                }
                                else // SOAP 1.2
                                {
                                    BSTR bstrSubcode = SysAllocString(L"");
                                    BSTR bstrSubcodeNS = SysAllocString(L"");
                                    BSTR bstrLangId = SysAllocString(L"en");
							        bstrFaultCode = SysAllocString(L"Receiver");
                                    bstrFaultCodeNS = SysAllocString(g_pwszSoap12EnvelopeNS);
							        pIwmsoapMsg->SetSOAP12Fault(bstrFaultCode, bstrFaultCodeNS, bstrSubcode, bstrSubcodeNS, bstrFaultString, bstrLangId, &bResult);
                                    SysFreeString(bstrSubcode);
                                    SysFreeString(bstrSubcodeNS);
                                    SysFreeString(bstrLangId);
                                }
							    SysFreeString(bstrFaultCode);
							    SysFreeString(bstrFaultCodeNS);
							    SysFreeString(bstrFaultString);
						    }
						    else
						    {
							    // clear these out so we can deserialize
							    pIwmsoapMsg->ClearHeaderElems();
							    pIwmsoapMsg->ClearMethodParams();
							    // deserialize response
                                if (HTTPClient.IsSoapResponse() == true)
                                {
							        pIwmsoapMsg->Deserialize(&bResult);
                                }
                                else
                                {
							        // not a soap response, create generic soap fault
                                    int nStatus = HTTPClient.GetHTTPStatus();
                                    BSTR bstrFaultCode, bstrFaultCodeNS;
                                    BSTR bstrFaultString = SysAllocString(L"Attempt to forward SOAP request failed.");
                                    if (m_lSOAPVersion == SOAP_VERSION_1_1)
                                    {
                                        if (nStatus == 500)
                                        {
                                            bstrFaultCode = SysAllocString(L"Server");
                                        }
                                        else
                                        {
							                bstrFaultCode = SysAllocString(L"Client");
                                        }
                                        bstrFaultCodeNS = SysAllocString(g_pwszSoapEnvelopeNS);
							            pIwmsoapMsg->SetSOAPFault(bstrFaultCode, bstrFaultCodeNS, bstrFaultString, VARIANT_FALSE, &bResult);
                                    }
                                    else // SOAP 1.2
                                    {
                                        if (nStatus == 500)
                                        {
                                            bstrFaultCode = SysAllocString(L"Receiver");
                                        }
                                        else
                                        {
							                bstrFaultCode = SysAllocString(L"Sender");
                                        }
                                        bstrFaultCodeNS = SysAllocString(g_pwszSoap12EnvelopeNS);
                                        BSTR bstrSubcode = SysAllocString(L"");
                                        BSTR bstrSubcodeNS = SysAllocString(L"");
                                        BSTR bstrLangId = SysAllocString(L"en");
							            pIwmsoapMsg->SetSOAP12Fault(bstrFaultCode, bstrFaultCodeNS, bstrSubcode, bstrSubcodeNS, bstrFaultString, bstrLangId, &bResult);
                                        SysFreeString(bstrSubcode);
                                        SysFreeString(bstrSubcodeNS);
                                        SysFreeString(bstrLangId);
                                    }
							        SysFreeString(bstrFaultCode);
							        SysFreeString(bstrFaultCodeNS);
							        SysFreeString(bstrFaultString);
                                }
						    }
					    }
					    else
					    {
						    // serialization failed
                            BSTR bstrFaultCode, bstrFaultCodeNS;
                            BSTR bstrFaultString = SysAllocString(L"Attempt to re-serialize SOAP request msg by intermediary failed.");
                            if (m_lSOAPVersion == SOAP_VERSION_1_1)
                            {
							    bstrFaultCode = SysAllocString(L"Server");
                                bstrFaultCodeNS = SysAllocString(g_pwszSoapEnvelopeNS);
							    pIwmsoapMsg->SetSOAPFault(bstrFaultCode, bstrFaultCodeNS, bstrFaultString, VARIANT_FALSE, &bResult);
                            }
                            else // SOAP 1.2
                            {
                                BSTR bstrSubcode = SysAllocString(L"");
                                BSTR bstrSubcodeNS = SysAllocString(L"");
                                BSTR bstrLangId = SysAllocString(L"en");
							    bstrFaultCode = SysAllocString(L"Receiver");
                                bstrFaultCodeNS = SysAllocString(g_pwszSoap12EnvelopeNS);
							    pIwmsoapMsg->SetSOAP12Fault(bstrFaultCode, bstrFaultCodeNS, bstrSubcode, bstrSubcodeNS, bstrFaultString, bstrLangId, &bResult);
                                SysFreeString(bstrSubcode);
                                SysFreeString(bstrSubcodeNS);
                                SysFreeString(bstrLangId);
                            }
						    SysFreeString(bstrFaultCode);
						    SysFreeString(bstrFaultCodeNS);
						    SysFreeString(bstrFaultString);
					    }
                    }
				}
			}
			else
			{
				// soap fault during header processing... toggle to 'response' phase
				pIwmsoapMsg->SetRequestPhase(VARIANT_FALSE);
			}
			// process headers (response phase)
			if (bProcessHeader == true)
			{
				// call response header processors
				m_HeaderDispatcher.ProcessHeader(pIwmsoapMsg);
			}
		}
		else
		{
			// request header processing failed... toggle to 'response' phase
			pIwmsoapMsg->SetRequestPhase(VARIANT_FALSE);
		}
	}
	else
	{
		// request deserialization failed... toggle to 'response' phase
		pIwmsoapMsg->SetRequestPhase(VARIANT_FALSE);
	}
	// will this be a 204 "No Content" HTTP response?
	VARIANT_BOOL bIsNoContent;
	pIwmsoapMsg->IsNoContentResponse(&bIsNoContent);
	if (bIsNoContent == VARIANT_FALSE)
	{
		// now serialize output message
		pIwmsoapMsg->Serialize(&bResult);
		if (bResult == VARIANT_FALSE)
		{
			// big trouble... no output XML
			nHTTPStatus = 500;
		}
		else
		{
			// check for SOAP Fault
			pIwmsoapMsg->IsSOAPFault(&bResult);
			if (bResult == VARIANT_TRUE)
			{
				nHTTPStatus = 500;
                // map HTTP status to fault code
                BSTR bstrCode, bstrCodeNS;
                pIwmsoapMsg->GetSOAPFaultCode(&bstrCode);
                pIwmsoapMsg->GetSOAPFaultCodeNS(&bstrCodeNS);
                if (!wcscmp(bstrCode, L"Sender") && !wcscmp(bstrCodeNS, g_pwszSoap12EnvelopeNS))
                {
                    nHTTPStatus = 400;
                }
                SysFreeString(bstrCode);
                SysFreeString(bstrCodeNS);
			}
		}
		// is this to be DIME packaged?
		long lNumRespAttach = 0;
		pIwmsoapMsg->GetNumRespAttachments(&lNumRespAttach);
		if (lNumRespAttach > 0)
		{
	        // pass out pointer to output buffer
	        ulOutputSize = GetXMLOutUTF8(pIwmsoapMsg, &pszOutput);
            if (LoadDIMEParser( pIwmsoapMsg,
                                pDIMEParser,
                                pszOutput,
                                ulOutputSize) == false)
            {
		        // this will result in 500 "Internal Server Error"
		        pIwmsoapMsg->Release();
		        if (pszPrimaryPayload != NULL)
		        {
			        free(pszPrimaryPayload);
		        }
		        if (pszOutput != NULL)
		        {
			        free(pszOutput);
		        }
                return 0;
            }
		}
		else
		{
			// no DIME attachments or this is a Fault message
			// copy output message to output buffer
			ulOutputSize = GetXMLOutUTF8(pIwmsoapMsg, ppszOutput);
		}
	}
	else
	{
        // no content response
		nHTTPStatus = 204;
		*ppszOutput = NULL;
		ulOutputSize = 0;
	}

writelog:
	// write to log
	if (bLoggingEnabled == true)
	{
        string strTemp;
		char szBuffer[128];
		time_t t;
		time(&t);
		strftime(szBuffer, 64, _T("%d/%b/%Y:%H:%M:%S"), gmtime(&t));
		strLogEntry = _T("*\r\n*\r\nTime: ");
		strLogEntry += szBuffer;
        _snprintf(szBuffer, 127, "\r\nSOAP Version: %.1f", (float)m_lSOAPVersion/10.0);
        szBuffer[127] = 0;
        strLogEntry += szBuffer;
		strLogEntry += _T("\r\nRequest URI: ");
		strLogEntry += strSvcURI;
		strLogEntry += strExtraPath;
		if (bProcessHeader == true)
		{
			strLogEntry += _T("\r\nHeader Processor ProgID's:");
            list<wstring>::iterator iL;
            for (iL = SvcDescription.m_listHeaderProcProgIds.begin();
                iL != SvcDescription.m_listHeaderProcProgIds.end();
                iL++)
            {
                strLogEntry += ' ';
                WideStringToUTF8(*iL, strTemp);
			    strLogEntry += strTemp;
            }
		}
		strLogEntry += _T("\r\nBody Handler ProgID: ");
        WideStringToUTF8(SvcDescription.m_wstrBodyProcProgId, strTemp);
		strLogEntry += strTemp;
		strLogEntry += _T("\r\nSOAPAction: ");
		strLogEntry += strSOAPAction;
		strLogEntry += _T("\r\nNode URI: ");
        if (SvcDescription.m_wstrNodeURI.empty() == false)
		{
            WideStringToUTF8(SvcDescription.m_wstrNodeURI, strTemp);
            strLogEntry += strTemp;
		}
		else
		{
			strLogEntry += "\"\"";
		}
		strLogEntry += _T("\r\nNext Hop URL: ");
		BSTR bstrNextHopURL;
		pIwmsoapMsg->GetNextHopURL(&bstrNextHopURL);
		WideStringToUTF8(wstring(bstrNextHopURL), strTemp);
		if (strTemp.empty() == false)
		{
			strLogEntry += strTemp;
		}
		else
		{
			strLogEntry += "\"\"";
		}
		SysFreeString(bstrNextHopURL);
		_snprintf(szBuffer, 127, "\r\nHTTP Method: POST\r\nHTTP Status: %d\r\n\r\nREQUEST MESSAGE:\r\n\r\n", nHTTPStatus);
        szBuffer[127] = 0;
		strLogEntry += szBuffer;
		if (pszPrimaryPayload != NULL)
		{
			// this was DIME packaged response
			strLogEntry.append(pszPrimaryPayload, ulPrimaryPayloadSize);
		}
		else
		{
			strLogEntry.append(pszInputXML, ulInputXMLSize);
		}
		if (bIsNoContent == VARIANT_FALSE)
		{
			strLogEntry += _T("\r\n\r\nRESPONSE MESSAGE:\r\n\r\n");
			if (pszOutput != NULL)
			{
				// this was DIME packaged response
				strLogEntry.append(pszOutput, ulOutputSize);
			}
			else
			{
				strLogEntry.append(*ppszOutput, ulOutputSize);
			}
		}
		strLogEntry += "\r\n*\r\n*\r\n";
	}
	// done
	pIwmsoapMsg->Release();
	if (pszPrimaryPayload != NULL)
	{
		free(pszPrimaryPayload);
	}
	if (pszOutput != NULL)
	{
		free(pszOutput);
	}
	return nHTTPStatus;
}

int WMSOAPGetProcessor::Process(const string& strSvcURI,
                                const string& strExtraPath,
                                const string& strQueryString,
				                IDispatch *pIWSDLParser,
                                SOAPSvcDesc& SvcDescription,
				                char **ppszOutput,
				                unsigned long& ulOutputSize,
				                string& strLogEntry,
				                bool bLoggingEnabled,
                                DIMEParser *pDIMEParser)
{
    UTF8ToWideString(strSvcURI, m_wstrSvcURI);
    UTF8ToWideString(strExtraPath, m_wstrExtraPath);
    UTF8ToWideString(strQueryString, m_wstrQueryString);
	// sanity check
	if (pDIMEParser == NULL)
	{
		// this will result in 500 "Internal Server Error"
		return 0;
	}
	// instantiate SOAP Msg object
	Iwmsoapmsg2 *pIwmsoapMsg;
	HRESULT res = CoCreateInstance( CLSID_wmsoapmsg2,
							NULL, CLSCTX_SERVER,
							IID_Iwmsoapmsg2,
							(void **) &pIwmsoapMsg);
	if (FAILED(res))
	{
		// this will result in 500 "Internal Server Error"
		return 0;
	}
	VARIANT_BOOL bResult;
	// set up BSTR's
    BSTR bstrSvcName = SysAllocString(SvcDescription.m_wstrWsdlSvc.c_str());
    BSTR bstrPortName = SysAllocString(SvcDescription.m_wstrWsdlPort.c_str());
    BSTR bstrNodeURI = SysAllocString(SvcDescription.m_wstrNodeURI.c_str());
	BSTR bstrSOAPAction = SysAllocString(L"");
    BSTR bstrNextHopURL = SysAllocString(SvcDescription.m_wstrNextHopURL.c_str());
    VARIANT_BOOL bExplicitTypingEnabled = SvcDescription.m_bExplicitTypingEnabled == true ? VARIANT_TRUE : VARIANT_FALSE;
    VARIANT_BOOL bNodeTypeEnabled = SvcDescription.m_bNodeTypeEnabled == true ? VARIANT_TRUE : VARIANT_FALSE;
	// increment ref count on WSDL client interface ptr
	pIWSDLParser->AddRef();
	// initialize SOAP message object
	pIwmsoapMsg->Initialize(bstrSvcName,
							bstrPortName,
							bstrNodeURI,
							bstrSOAPAction,
							bstrNextHopURL,
							bExplicitTypingEnabled,
                            bNodeTypeEnabled,
							pIWSDLParser,
							&bResult);
	SysFreeString(bstrSvcName);
	SysFreeString(bstrPortName);
	SysFreeString(bstrNodeURI);
	SysFreeString(bstrSOAPAction);
	SysFreeString(bstrNextHopURL);
	// decrement ref count on interface ptr
	pIWSDLParser->Release();
	// any luck?
	if (bResult == VARIANT_FALSE)
	{
		// this will result in 500 "Internal Server Error"
		pIwmsoapMsg->Release();
		return 0;
	}
    // using SOAP 1.2
    pIwmsoapMsg->SetSOAPVersion(SOAP_VERSION_1_2);
    // are there header block processors to load?
    bool bProcessHeader = SvcDescription.m_listHeaderProcProgIds.empty() == true ? false : true;
    if (bProcessHeader == true)
    {
	    // initialize header processor dispatcher
	    if (m_HeaderDispatcher.Initialize(SvcDescription.m_listHeaderProcProgIds, pIwmsoapMsg) == false)
	    {
		    // this will result in 500 "Internal Server Error"
		    pIwmsoapMsg->Release();
		    return 0;
	    }
    }
    int nHTTPStatus = 200;
    // are we an intermediary?
	pIwmsoapMsg->IsIntermediary(&bResult);
	if (bResult == VARIANT_FALSE)
	{
		// not an intermediary, is this rpc or doc style operation?
		VARIANT_BOOL bIsRPC = VARIANT_FALSE;
		pIwmsoapMsg->IsRPCStyle(&bIsRPC);
		if (bIsRPC == VARIANT_TRUE)
		{
			// process RPC GET request
            BSTR bstrSvcURI = SysAllocString(m_wstrSvcURI.c_str());
            BSTR bstrExtraPath = SysAllocString(m_wstrExtraPath.c_str());
            BSTR bstrQueryString = SysAllocString(m_wstrQueryString.c_str());
            pIwmsoapMsg->ProcessRPCGet( bstrSvcURI,
                                        bstrExtraPath,
                                        bstrQueryString,
                                        &bResult);
            SysFreeString(bstrSvcURI);
            SysFreeString(bstrExtraPath);
            SysFreeString(bstrQueryString);
			if (bResult == VARIANT_TRUE)
			{
				// now invoke method
				m_AutoStub.InvokeMethod(SvcDescription.m_wstrBodyProcProgId, pIwmsoapMsg);
			}
            else
            {
		        // this will result in 500 "Internal Server Error"
		        pIwmsoapMsg->Release();
		        return 0;
            }
		}
		else
		{
			// document style operation, call SOAP GET handler
            InvokeSoap12GetHandler(SvcDescription.m_wstrBodyProcProgId, pIwmsoapMsg);
        }
		// toggle to 'response' phase
		pIwmsoapMsg->SetRequestPhase(VARIANT_FALSE);
    }
    else
    {
		// acting as intermediary, toggle to 'response' phase
		pIwmsoapMsg->SetRequestPhase(VARIANT_FALSE);
		// get next hop URL
		BSTR bstrNextHopURL;
		pIwmsoapMsg->GetNextHopURL(&bstrNextHopURL);
		string strNextHopURL;
		WideStringToUTF8(wstring(bstrNextHopURL), strNextHopURL);
		SysFreeString(bstrNextHopURL);
		// set up proxy and make SOAP call
		string strHost, strSvcURI, strTemp;
		int nPortNbr = 80;
		string::size_type pos;
		if ((pos = strNextHopURL.find("http://")) != string::npos)
		{
			strTemp = strNextHopURL.substr(pos + 7);
			pos = strTemp.find('/');
			strSvcURI = strTemp.substr(pos);
            // now append any extra path info
            strSvcURI += strExtraPath;
            // now append any query string
            if (strQueryString.empty() == false)
            {
                strSvcURI += '?';
                strSvcURI += strQueryString;
            }
			strTemp = strTemp.substr(0, pos);
			if ((pos = strTemp.find(':')) != string::npos)
			{
				// port specified explicitly
				nPortNbr = atoi(strTemp.substr(pos + 1).c_str());
				strHost = strTemp.substr(0, pos);
			}
			else
			{
				strHost = strTemp;
			}
		}
		WMSOAPDIMEHTTPClient HTTPClient;
		if (HTTPClient.SendSOAPMessage( string("GET"),
                                        string(""),
										strHost,
										nPortNbr,
										strSvcURI,
										string(g_pszUserAgent),
										pIwmsoapMsg,
										false) == false)
		{
			// something went wrong
            BSTR bstrReason = SysAllocString(HTTPClient.GetErrorMsg().c_str());
            BSTR bstrSubcode = SysAllocString(L"");
            BSTR bstrSubcodeNS = SysAllocString(L"");
            BSTR bstrLangId = SysAllocString(L"en");
			BSTR bstrFaultCode = SysAllocString(L"Receiver");
            BSTR bstrFaultCodeNS = SysAllocString(g_pwszSoap12EnvelopeNS);
			pIwmsoapMsg->SetSOAP12Fault(bstrFaultCode, bstrFaultCodeNS, bstrSubcode, bstrSubcodeNS, bstrReason, bstrLangId, &bResult);
            SysFreeString(bstrSubcode);
            SysFreeString(bstrSubcodeNS);
            SysFreeString(bstrLangId);
			SysFreeString(bstrFaultCode);
			SysFreeString(bstrFaultCodeNS);
			SysFreeString(bstrReason);
		}
		else
		{
			// deserialize response
            if (HTTPClient.IsSoapResponse() == true)
            {
				pIwmsoapMsg->Deserialize(&bResult);
            }
            else
            {
				// not a soap response, create generic soap fault
                int nStatus = HTTPClient.GetHTTPStatus();
                BSTR bstrFaultCode, bstrFaultCodeNS;
                BSTR bstrFaultString = SysAllocString(L"Attempt to forward request failed.");
                if (m_lSOAPVersion == SOAP_VERSION_1_1)
                {
                    if (nStatus == 500)
                    {
                        bstrFaultCode = SysAllocString(L"Server");
                    }
                    else
                    {
						bstrFaultCode = SysAllocString(L"Client");
                    }
                    bstrFaultCodeNS = SysAllocString(g_pwszSoapEnvelopeNS);
					pIwmsoapMsg->SetSOAPFault(bstrFaultCode, bstrFaultCodeNS, bstrFaultString, VARIANT_FALSE, &bResult);
                }
                else // SOAP 1.2
                {
                    if (nStatus == 500)
                    {
                        bstrFaultCode = SysAllocString(L"Receiver");
                    }
                    else
                    {
						bstrFaultCode = SysAllocString(L"Sender");
                    }
                    bstrFaultCodeNS = SysAllocString(g_pwszSoap12EnvelopeNS);
                    BSTR bstrSubcode = SysAllocString(L"");
                    BSTR bstrSubcodeNS = SysAllocString(L"");
                    BSTR bstrLangId = SysAllocString(L"en");
					pIwmsoapMsg->SetSOAP12Fault(bstrFaultCode, bstrFaultCodeNS, bstrSubcode, bstrSubcodeNS, bstrFaultString, bstrLangId, &bResult);
                    SysFreeString(bstrSubcode);
                    SysFreeString(bstrSubcodeNS);
                    SysFreeString(bstrLangId);
                }
				SysFreeString(bstrFaultCode);
				SysFreeString(bstrFaultCodeNS);
				SysFreeString(bstrFaultString);
            }
		}
    }
	// process headers (response phase)
	if (bProcessHeader == true)
	{
		// call response header processors
		m_HeaderDispatcher.ProcessHeader(pIwmsoapMsg);
	}
    // now process output
	char *pszOutput = NULL;
	// will this be a 204 "No Content" HTTP response?
	VARIANT_BOOL bIsNoContent;
	pIwmsoapMsg->IsNoContentResponse(&bIsNoContent);
	if (bIsNoContent == VARIANT_FALSE)
	{
		// now serialize output message
		pIwmsoapMsg->Serialize(&bResult);
		if (bResult == VARIANT_FALSE)
		{
			// big trouble... no output XML
			nHTTPStatus = 500;
		}
		else
		{
			// check for SOAP Fault
			pIwmsoapMsg->IsSOAPFault(&bResult);
			if (bResult == VARIANT_TRUE)
			{
				nHTTPStatus = 500;
                // map HTTP status to fault code
                BSTR bstrCode, bstrCodeNS;
                pIwmsoapMsg->GetSOAPFaultCode(&bstrCode);
                pIwmsoapMsg->GetSOAPFaultCodeNS(&bstrCodeNS);
                if (!wcscmp(bstrCode, L"Sender") && !wcscmp(bstrCodeNS, g_pwszSoap12EnvelopeNS))
                {
                    nHTTPStatus = 400;
                }
                SysFreeString(bstrCode);
                SysFreeString(bstrCodeNS);
			}
		}
		// is this to be DIME packaged?
		long lNumRespAttach = 0;
		pIwmsoapMsg->GetNumRespAttachments(&lNumRespAttach);
		if (lNumRespAttach > 0 && nHTTPStatus == 200)
		{
			// pass out pointer to output buffer
			ulOutputSize = GetXMLOutUTF8(pIwmsoapMsg, &pszOutput);
            if (LoadDIMEParser( pIwmsoapMsg,
                                pDIMEParser,
                                pszOutput,
                                ulOutputSize) == false)
            {
		        // this will result in 500 "Internal Server Error"
		        pIwmsoapMsg->Release();
		        if (pszOutput != NULL)
		        {
			        free(pszOutput);
		        }
                return 0;
            }
		}
		else
		{
			// no DIME attachments or this is a Fault message
			// copy output message to output buffer
			ulOutputSize = GetXMLOutUTF8(pIwmsoapMsg, ppszOutput);
		}
	}
	else
	{
        // no content response
		nHTTPStatus = 204;
		*ppszOutput = NULL;
		ulOutputSize = 0;
	}
	// write to log
	if (bLoggingEnabled == true)
	{
        string strTemp;
		char szBuffer[128];
		time_t t;
		time(&t);
		strftime(szBuffer, 64, _T("%d/%b/%Y:%H:%M:%S"), gmtime(&t));
		strLogEntry = _T("*\r\n*\r\nTime: ");
		strLogEntry += szBuffer;
        _snprintf(szBuffer, 127, "\r\nSOAP Version: %.1f", (float)m_lSOAPVersion/10.0);
        szBuffer[127] = 0;
        strLogEntry += szBuffer;
        strLogEntry += _T("\r\nRequest URI: ");
		strLogEntry += strSvcURI;
		strLogEntry += strExtraPath;
		strLogEntry += _T("\r\nQuery String: ");
		strLogEntry += strQueryString;
		strLogEntry += _T("\r\nBody Handler ProgID: ");
        WideStringToUTF8(SvcDescription.m_wstrBodyProcProgId, strTemp);
		strLogEntry += strTemp;
		strLogEntry += _T("\r\nNode URI: ");
        if (SvcDescription.m_wstrNodeURI.empty() == false)
		{
            WideStringToUTF8(SvcDescription.m_wstrNodeURI, strTemp);
            strLogEntry += strTemp;
		}
		else
		{
			strLogEntry += "\"\"";
		}
		_snprintf(szBuffer, 127, "\r\nHTTP Method: GET\r\nHTTP Status: %d\r\n", nHTTPStatus);
        szBuffer[127] = 0;
		strLogEntry += szBuffer;
		if (bIsNoContent == VARIANT_FALSE)
		{
			strLogEntry += _T("\r\n\r\nRESPONSE MESSAGE:\r\n\r\n");
			if (pszOutput != NULL)
			{
				// this was DIME packaged response
				strLogEntry.append(pszOutput, ulOutputSize);
			}
			else
			{
				strLogEntry.append(*ppszOutput, ulOutputSize);
			}
		}
		strLogEntry += "\r\n*\r\n*\r\n";
	}
	// done
	pIwmsoapMsg->Release();
	if (pszOutput != NULL)
	{
		free(pszOutput);
	}
	return nHTTPStatus;
}
