// WMXMLMessage.cpp: implementation of the WMXMLMessage 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.
//
//////////////////////////////////////////////////////////////////////
//
// WMXMLMessage class - implementation of XML message classes
// Stores internal representation of parsed XML message for processing.
// Both "encoded" (SOAP encoding only) or "literal" usage supported.
// Both "document" and "rpc" messaging styles are supported.
//  
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "WMXMLMessage.h"
#include "..\wmwsdlclient\wmwsdlclient.h"
#include "..\wmwsdlclient\wmwsdlclient_i.c"

void LogFault(WMSOAPFault *f)
{
	HANDLE  hEventSource;
	LPTSTR  lpszStrings[1];

	USES_CONVERSION;
	char buf[128];
    _snprintf(buf, 127, "%s", OLE2A(f->GetFaultString().c_str()));
    buf[127] = 0;
	lpszStrings[0] = buf;
	hEventSource = RegisterEventSource(NULL, "White Mesa SOAP Message Handler");
	if (hEventSource != NULL)
	{
		ReportEvent(hEventSource, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, (LPCTSTR*) &lpszStrings[0], NULL);
		DeregisterEventSource(hEventSource);
	}
}

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

WMXMLMessageElement::WMXMLMessageElement(const wstring& wstrNamespaceURI,
										const wstring& wstrName,
										const variant_t& varContent)
{
	m_wstrNamespaceURI = wstrNamespaceURI;
	m_wstrName = wstrName;
	m_varContent = varContent;
}

WMXMLMessageElement::WMXMLMessageElement(const wstring& wstrNamespaceURI,
										const wstring& wstrName)
{
	m_wstrNamespaceURI = wstrNamespaceURI;
	m_wstrName = wstrName;
}

WMSOAPHeaderMsgElement::WMSOAPHeaderMsgElement( const wstring& wstrNamespaceURI,
												const wstring& wstrName,
												const variant_t& varContent,
												bool bMustUnderstand,
												const wstring& wstrActor,
                                                bool bRelay,
                                                bool bRcvdInMsg) :
												WMXMLMessageElement(
																	wstrNamespaceURI,
																	wstrName,
																	varContent)
{
    m_bRcvdInMsg = bRcvdInMsg;
	m_bMustUnderstand = bMustUnderstand;
	m_wstrActor = wstrActor;
    m_bRelay = bRelay;
}

WMSOAPHeaderMsgElementLit::WMSOAPHeaderMsgElementLit(   const wstring& wstrNamespaceURI,
														const wstring& wstrName,
														const wstring& wstrLiteralXML,
														bool bMustUnderstand,
														const wstring& wstrActor,
                                                        bool bRelay,
														const wstring& wstrEncodingStyle,
                                                        bool bRcvdInMsg) :
														WMXMLMessageElement(
																			wstrNamespaceURI,
																			wstrName)
{
    m_bRcvdInMsg = bRcvdInMsg;
	m_bMustUnderstand = bMustUnderstand;
	m_wstrActor = wstrActor;
    m_bRelay = bRelay;
	m_wstrEncodingStyle = wstrEncodingStyle;
	m_wstrLiteralXML = wstrLiteralXML;
}

WMSOAPBodyElementLit::WMSOAPBodyElementLit( const wstring& wstrLiteralXML,
											const wstring& wstrEncodingStyle)
{
	m_wstrEncodingStyle = wstrEncodingStyle;
	m_wstrLiteralXML = wstrLiteralXML;
}

WMSOAPParamMsgElement::WMSOAPParamMsgElement(const wstring& wstrNamespaceURI,
											const wstring& wstrName,
											const variant_t& varContent,
											bool bIsInParam,
											bool bIsOutParam) :
											WMXMLMessageElement(
																wstrNamespaceURI,
																wstrName,
																varContent)
{
	m_bIsInParam = bIsInParam;
	m_bIsOutParam = bIsOutParam;
}					   

WMXMLMessage::WMXMLMessage()
{
	m_pRootElement = NULL;
	m_pszBuffer = NULL;
	m_ulBufLength = 0;
}

WMXMLMessage::WMXMLMessage(char *pszPtr, unsigned long ulLength)
{
	m_pRootElement = NULL;
	m_pszBuffer = pszPtr;
	m_ulBufLength = ulLength;
}

WMXMLMessage::~WMXMLMessage()
{
	if (m_pRootElement != NULL)
	{
		delete m_pRootElement;
	}
	if (m_pszBuffer != NULL)
	{
		free(m_pszBuffer);
	}
}

void WMXMLMessage::SetBuffer(char *pszPtr, unsigned long ulLength)
{
	if (m_pszBuffer != NULL)
	{
		free(m_pszBuffer);
	}
	m_pszBuffer = pszPtr;
	m_ulBufLength = ulLength;
}

void WMXMLMessage::SetRootElement(WMXMLElement *pRoot)
{
	if (m_pRootElement != NULL)
	{
		delete m_pRootElement;
	}
	m_pRootElement = pRoot;
}

WMXMLElement *WMXMLMessage::DetachRootElement()
{
	WMXMLElement *pTemp = m_pRootElement;
	m_pRootElement = NULL;
	return pTemp;
}

WMSOAPMessage::WMSOAPMessage()
{
    m_nSOAPVersion = 11;
	m_bIsSOAPFault = false;
	m_bIsLocalFault = true;
	m_pIWSDLClient = NULL;
	m_pBodyElementLit = NULL;
}

WMSOAPMessage::~WMSOAPMessage()
{
	if (m_pIWSDLClient != NULL)
	{
		m_pIWSDLClient->Release();
	}
	ClearHeaderElems();
	ClearMethodParams();
	ClearFaultHeaderElems();
	ClearFaultDetailElems();
	ClearAttachments();
}

bool WMSOAPMessage::Initialize(	const wstring& wstrServiceName,
								const wstring& wstrPortName,
								const wstring& wstrNodeURI,
								const wstring& wstrSOAPAction,
								const wstring& wstrNextHopURL,
								bool bExplicitTypingEnabled,
                                bool bNodeTypeEnabled,
								IDispatch *pWSDLClient)
{
	if (pWSDLClient == NULL)
	{
		return false;
	}
	HRESULT hRes = pWSDLClient->QueryInterface(IID_Iwmwsdlclient2, (void **)&m_pIWSDLClient);
	if (FAILED(hRes))
	{
		return false;
	}
	m_wstrNodeURI = wstrNodeURI;
	m_wstrSOAPAction = wstrSOAPAction;
	m_wstrNextHopURL = wstrNextHopURL;
	m_wstrServiceName = wstrServiceName;
	m_wstrPortName = wstrPortName;
	m_bIsRequestPhase = true;
	m_bExplicitTypingEnabled = bExplicitTypingEnabled;
    m_bNodeTypeEnabled = bNodeTypeEnabled;
	m_bNoContent = false;
	return true;
}

bool WMSOAPMessage::AddHeaderElem(  const wstring& wstrNamespaceURI,
									const wstring& wstrName,
									const variant_t& varContent,
									bool bMustUnderstand,
									const wstring& wstrActor,
                                    bool bRelay,
                                    bool bRcvdInMsg)
{
	WMSOAPHeaderMsgElement *pElem = new WMSOAPHeaderMsgElement( wstrNamespaceURI,
																wstrName,
																varContent,
																bMustUnderstand,
																wstrActor,
                                                                bRelay,
                                                                bRcvdInMsg);
	if (pElem != NULL)
	{
		m_arrHeaderElements.push_back(pElem);
	}
	return (pElem != NULL);
}

bool WMSOAPMessage::AddHeaderElemLit(   const wstring& wstrNamespaceURI,
										const wstring& wstrName,
										const wstring& wstrLiteralXML,
										bool bMustUnderstand,
										const wstring& wstrActor,
                                        bool bRelay,
										const wstring& wstrEncodingStyle,
                                        bool bRcvdInMsg)
{
	WMSOAPHeaderMsgElementLit *pElem = new WMSOAPHeaderMsgElementLit(   wstrNamespaceURI,
																		wstrName,
																		wstrLiteralXML,
																		bMustUnderstand,
																		wstrActor,
                                                                        bRelay,
																		wstrEncodingStyle,
                                                                        bRcvdInMsg);
	if (pElem != NULL)
	{
		m_arrHeaderElementsLit.push_back(pElem);
	}
	return (pElem != NULL);
}

bool WMSOAPMessage::GetHeaderElemLit(   const wstring& wstrNamespaceURI,
										const wstring& wstrName,
										wstring& wstrLiteralXML)
{
	vector<WMSOAPHeaderMsgElementLit *>::iterator iter;
	for (iter = m_arrHeaderElementsLit.begin();
				iter != m_arrHeaderElementsLit.end(); iter++)
	{
		WMSOAPHeaderMsgElementLit *pElem = *iter;
		if (wstrNamespaceURI == pElem->m_wstrNamespaceURI &&
			wstrName == pElem->m_wstrName)
        {
            if (m_setRoles.find(pElem->m_wstrActor) != m_setRoles.end() ||
                (IsIntermediary() == false && pElem->m_wstrActor.empty() == true))
		    {
			    // name and actor URI match
			    wstrLiteralXML = pElem->m_wstrLiteralXML;
                if (GetSOAPVersion() == SOAP_VERSION_1_1 ||
                    pElem->m_wstrActor != g_pwszSoap12RoleNone)
                {
			        // consume header element unless targeted at SOAP 1.2 role "none"
			        delete *iter;
			        m_arrHeaderElementsLit.erase(iter);
                }
			    return true;
		    }
        }
	}
	return false;
}

bool WMSOAPMessage::GetFirstHeaderElemLit(  WMSOAPHeaderMsgElementLit **ppElem,
											long& lPosition)
{
	if (m_arrHeaderElementsLit.empty() == true)
	{
		return false;
	}
	*ppElem = m_arrHeaderElementsLit[0];
	lPosition = 0;
	return true;
}

bool WMSOAPMessage::GetNextHeaderElemLit(   WMSOAPHeaderMsgElementLit **ppElem,
											long& lPosition)
{
	if (((unsigned long)lPosition + 1) >= m_arrHeaderElementsLit.size())
	{
		return false;
	}
	lPosition++;
	*ppElem = m_arrHeaderElementsLit[lPosition];
	return true;
}

bool WMSOAPMessage::GetFirstHeaderElem( WMSOAPHeaderMsgElement **ppElem,
										long& lPosition)
{
	if (m_arrHeaderElements.empty() == true)
	{
		return false;
	}
	*ppElem = m_arrHeaderElements[0];
	lPosition = 0;
	return true;
}

bool WMSOAPMessage::GetNextHeaderElem(  WMSOAPHeaderMsgElement **ppElem,
										long& lPosition)
{
	if (((unsigned long)lPosition + 1) >= m_arrHeaderElements.size())
	{
		return false;
	}
	lPosition++;
	*ppElem = m_arrHeaderElements[lPosition];
	return true;
}

bool WMSOAPMessage::GetHeaderElem(	const wstring& wstrNamespaceURI,
									const wstring& wstrName,
									variant_t& varContent)
{
	vector<WMSOAPHeaderMsgElement *>::iterator iter;
	for (iter = m_arrHeaderElements.begin();
				iter != m_arrHeaderElements.end(); iter++)
	{
		WMSOAPHeaderMsgElement *pElem = *iter;
		if (wstrNamespaceURI == pElem->m_wstrNamespaceURI &&
			wstrName == pElem->m_wstrName)
        {
            if (m_setRoles.find(pElem->m_wstrActor) != m_setRoles.end() ||
                (IsIntermediary() == false && pElem->m_wstrActor.empty() == true) ||
                pElem->m_wstrActor == g_pwszSoap12RoleNone)
		    {
			    // name and actor URI match
			    varContent = pElem->m_varContent;
                if (GetSOAPVersion() == SOAP_VERSION_1_1 ||
                    pElem->m_wstrActor != g_pwszSoap12RoleNone)
                {
			        // consume header element unless targeted at SOAP 1.2 role "none"
			        delete *iter;
			        m_arrHeaderElements.erase(iter);
                }
			    return true;
		    }
        }
	}
	return false;
}

bool WMSOAPMessage::IsRPCStyle()
{
	if (m_pIWSDLClient != NULL)
	{
		VARIANT_BOOL bResult;
		BSTR bstrStyle;
		m_pIWSDLClient->GetSOAPBindingStyle(&bstrStyle, &bResult);
		if (bResult == VARIANT_FALSE)
		{
			return false;
		}
		bool bRtn = (wcscmp(bstrStyle, L"rpc") == 0);
		SysFreeString(bstrStyle);
		return bRtn;
	}
	return false;
}

bool WMSOAPMessage::CheckMustUnderstand()
{
    wstring wstrName;
    wstring wstrNamespaceURI;
	// first the de-serialized headers (encoded)
	vector<WMSOAPHeaderMsgElement *>::iterator iter1;
	vector<WMSOAPHeaderMsgElementLit *>::iterator iter2;
	for (iter1 = m_arrHeaderElements.begin();
			iter1 != m_arrHeaderElements.end(); iter1++)
	{
		WMSOAPHeaderMsgElement *pElem = *iter1;
        if (pElem->m_bRcvdInMsg == false)
        {
            // inserted by header processor on this node, so ignore
            continue;
        }
        if (m_setRoles.find(pElem->m_wstrActor) != m_setRoles.end() ||
            (IsIntermediary() == false && pElem->m_wstrActor.empty() == true))
		{
			if (pElem->m_bMustUnderstand == true)
			{
				// unhandled mustUnderstand header element
                wstrName = pElem->m_wstrName;
                wstrNamespaceURI = pElem->m_wstrNamespaceURI;
				goto mustUnderstandFault;
			}
			else
			{
                // not a mandatory header block
                if (GetSOAPVersion() == SOAP_VERSION_1_1 ||
                    pElem->m_bRelay == false)
                {
			        // consume header element unless marked for "relay"
				    delete *iter1;
				    iter1 = m_arrHeaderElements.erase(iter1);
				    // end of the line?
				    if (iter1 == m_arrHeaderElements.end())
				    {
					    break;
				    }
				    else
				    {
					    // back up
					    --iter1;
				    }
                }
			}
		}
	}
	// now the un-deserialized headers (literal)
	for (iter2 = m_arrHeaderElementsLit.begin();
				iter2 != m_arrHeaderElementsLit.end(); iter2++)
	{
		// if next hop URL is empty, then we are ultimate destination
		WMSOAPHeaderMsgElementLit *pElem = *iter2;
        if (pElem->m_bRcvdInMsg == false)
        {
            // inserted by header processor on this node, so ignore
            continue;
        }
        if (m_setRoles.find(pElem->m_wstrActor) != m_setRoles.end() ||
            (IsIntermediary() == false && pElem->m_wstrActor.empty() == true))
		{
			if (pElem->m_bMustUnderstand == true)
			{
				// unhandled mustUnderstand header element
                wstrName = pElem->m_wstrName;
                wstrNamespaceURI = pElem->m_wstrNamespaceURI;
				goto mustUnderstandFault;
			}
			else
			{
                // not a mandatory header block
                if (GetSOAPVersion() == SOAP_VERSION_1_1 ||
                    pElem->m_bRelay == false)
                {
			        // consume header element unless marked for "relay"
				    delete *iter2;
				    iter2 = m_arrHeaderElementsLit.erase(iter2);
				    // end of the line?
				    if (iter2 == m_arrHeaderElementsLit.end())
				    {
					    break;
				    }
				    else
				    {
					    // back up
					    --iter2;
				    }
                }
			}
		}
	}
	return true;

mustUnderstandFault:
	// unhandled mustUnderstand header element
    if (GetSOAPVersion() == SOAP_VERSION_1_1)
    {
        SetSOAPFault(   wstring(L"MustUnderstand"),
                        wstring(g_pwszSoapEnvelopeNS),
					    wstring(L"Unhandled 'mustUnderstand' header block."),
					    wstring(L""),
					    false);
    }
    else
    {
        map<wstring, wstring> mapReasonText;
        mapReasonText[wstring(L"en")] = wstring(L"Unhandled 'mustUnderstand' header block.");
		SetSOAPFault(   wstring(L"MustUnderstand"),
                        wstring(g_pwszSoap12EnvelopeNS),
                        wstring(L""),
                        wstring(L""),
                        mapReasonText,
                        wstring(L""),
                        false);
	    // create header block
	    WMXMLElement *pBlock = new WMXMLElement(wstring(L"SOAP-ENV:NotUnderstood"));
	    if (pBlock != NULL)
	    {
		    pBlock->SetNSPrefix(wstring(L"SOAP-ENV"), wstring(g_pwszSoap12EnvelopeNS));
		    pBlock->SetNSPrefix(wstring(L"ns"), wstrNamespaceURI);
            wstring wstrAttr(L"ns:");
            wstrAttr += wstrName;
		    pBlock->SetAttribute(wstring(L"qname"), wstrAttr);
	    }
	    wstring wstrBuffer;
	    pBlock->Serialize(NULL, wstrBuffer);
	    delete pBlock;
	    AddFaultHeaderElemLit(  wstring(g_pwszSoap12EnvelopeNS),
							    wstring(L"NotUnderstood"),
							    wstrBuffer,
							    false,
							    wstring(L""),
                                true, 
							    wstring(L""));
    }
	return false;
}

void WMSOAPMessage::ClearHeaderElems()
{
	for (unsigned int i = 0; i < m_arrHeaderElements.size(); i++)
	{
		delete m_arrHeaderElements[i];
	}
	for (i = 0; i < m_arrHeaderElementsLit.size(); i++)
	{
		delete m_arrHeaderElementsLit[i];
	}
	m_arrHeaderElements.clear();
	m_arrHeaderElementsLit.clear();
}

bool WMSOAPMessage::SetBodyElemLit( const wstring& wstrLiteralXML,
									const wstring& wstrEncodingStyle)
{
	if (m_pBodyElementLit != NULL)
	{
		delete m_pBodyElementLit;
	}
	m_pBodyElementLit = new WMSOAPBodyElementLit(   wstrLiteralXML,
													wstrEncodingStyle);
	return (m_pBodyElementLit != NULL);
}

bool WMSOAPMessage::GetBodyElemLit(wstring& wstrLiteralXML)
{
	if (m_pBodyElementLit != NULL)
	{
		wstrLiteralXML = m_pBodyElementLit->m_wstrLiteralXML;
		return true;
	}
	return false;
}

bool WMSOAPMessage::AddMethodParam( const wstring& wstrNamespaceURI,
									const wstring& wstrName,
									const variant_t& varContent,
									bool bIsInParam,
									bool bIsOutParam)
{
	WMSOAPParamMsgElement *pElem = new WMSOAPParamMsgElement(   wstrNamespaceURI,
																wstrName,
																varContent,
																bIsInParam,
																bIsOutParam);
	if (pElem != NULL)
	{
		m_arrParamElements.push_back(pElem);
	}
	return (pElem != NULL);
}

bool WMSOAPMessage::GetFirstMethodParam(wstring& wstrNamespaceURI,
										wstring& wstrName,
										variant_t& varContent,
										bool& bIsInParam,
										bool& bIsOutParam,
										long& lPosition)
{
	if (m_arrParamElements.empty() == true)
	{
		return false;
	}
	WMSOAPParamMsgElement *pElem = m_arrParamElements[0];
	wstrNamespaceURI = pElem->m_wstrNamespaceURI;
	wstrName = pElem->m_wstrName;
	varContent = pElem->m_varContent;
	bIsInParam = pElem->m_bIsInParam;
	bIsOutParam = pElem->m_bIsOutParam;
	lPosition = 0;
	return true;
}

bool WMSOAPMessage::GetNextMethodParam( wstring& wstrNamespaceURI,
										wstring& wstrName,
										variant_t& varContent,
										bool& bIsInParam,
										bool& bIsOutParam,
										long& lPosition)
{
	if (((unsigned long)lPosition + 1) >= m_arrParamElements.size())
	{
		return false;
	}
	lPosition++;
	WMSOAPParamMsgElement *pElem = m_arrParamElements[lPosition];
	wstrNamespaceURI = pElem->m_wstrNamespaceURI;
	wstrName = pElem->m_wstrName;
	varContent = pElem->m_varContent;
	bIsInParam = pElem->m_bIsInParam;
	bIsOutParam = pElem->m_bIsOutParam;
	return true;
}

void WMSOAPMessage::ClearMethodParams()
{
	for (unsigned int i = 0; i < m_arrParamElements.size(); i++)
	{
		delete m_arrParamElements[i];
	}
	m_arrParamElements.clear();
	// release the literal body element
	if (m_pBodyElementLit != NULL)
	{
		delete m_pBodyElementLit;
		m_pBodyElementLit = NULL;
	}
}

void WMSOAPMessage::SetMethodName(const wstring& wstrName)
{
	m_wstrMethodName = wstrName;
}

_variant_t& WMSOAPMessage::GetReturnValue()
{
	return m_varReturn;
}

void WMSOAPMessage::SetReturnValue(variant_t& varValue)
{
	m_varReturn = varValue;
}

void WMSOAPMessage::SetReturnValue(VARIANT varValue)
{
	m_varReturn = varValue;
}

bool WMSOAPMessage::Deserialize()
{
	bool bRtn = true;
	m_bIsLocalFault = true;
	try
	{
		if (IsRequestPhase() == true)
		{
			m_ReqDeserializer.Deserialize(*this, m_pIWSDLClient);
		}
		else
		{
			m_RespDeserializer.Deserialize(*this, m_pIWSDLClient);
		}
		VARIANT_BOOL bResult;
        // check SOAP binding version
        BSTR bstrBindingVersion = NULL;
        m_pIWSDLClient->GetSOAPBindingVersion(&bstrBindingVersion, &bResult);
        if (bResult == VARIANT_FALSE)
		{
		    throw new WMSOAPFault(  WMSOAPFault::Receiver,
                                    L"SOAP Binding not found in WSDL description for this port.",
                                    true);
        }
        if (GetSOAPVersion() == SOAP_VERSION_1_1)
        {
			if (wcscmp(bstrBindingVersion, L"http://schemas.xmlsoap.org/wsdl/soap/"))
			{
                SysFreeString(bstrBindingVersion);
		        throw new WMSOAPFault(  WMSOAPFault::VersionMismatch,
                                        L"SOAP 1.1 Binding not found in WSDL description for this port.",
                                        true);
            }
        }
        else // SOAP 1.2
        {
			if (wcscmp(bstrBindingVersion, L"http://schemas.xmlsoap.org/wsdl/soap12/"))
			{
                SysFreeString(bstrBindingVersion);
		        throw new WMSOAPFault(  WMSOAPFault::VersionMismatch,
                                        L"SOAP 1.2 Binding not found in WSDL description for this port.",
                                        true);
            }
        }
        SysFreeString(bstrBindingVersion);
	}
	catch (WMSOAPFault *f)
	{
        if (GetSOAPVersion() == SOAP_VERSION_1_1)
        {
            wstring wstrFaultCode, wstrFaultCodeNS;
            f->GetSoapFaultCode(GetSOAPVersion(), wstrFaultCode, wstrFaultCodeNS);
		    SetSOAPFault(   wstrFaultCode,
                            wstrFaultCodeNS,
                            f->GetFaultString(),
                            wstring(L""),
                            f->NeedDetail());
        }
        else
        {
            wstring wstrFaultCode, wstrFaultCodeNS;
            wstring wstrSubCode, wstrSubCodeNS;
            f->GetSoapFaultCode(GetSOAPVersion(), wstrFaultCode, wstrFaultCodeNS);
            f->GetFaultSubCode(wstrSubCode, wstrSubCodeNS);
            map<wstring, wstring> mapReasonText;
            mapReasonText[wstring(L"en")] = f->GetFaultString();
		    SetSOAPFault(   wstrFaultCode,
                            wstrFaultCodeNS,
                            wstrSubCode,
                            wstrSubCodeNS,
                            mapReasonText,
                            wstring(L""),
                            f->NeedDetail());
        }
        LogFault(f);
		delete f;
		bRtn = false;
	}
	return bRtn;
}

bool WMSOAPMessage::ProcessRPC()
{
	bool bRtn = true;
	try
	{
		if (IsRequestPhase() == true)
		{
			// check to see if method is recognized
			BSTR bstrMethodName = SysAllocString(GetMethodName().c_str());
            BSTR bstrMethodNS = NULL;
			VARIANT_BOOL bResult;
			m_pIWSDLClient->GetMethodNamespaceURI(bstrMethodName, &bstrMethodNS, &bResult);
			if (bResult == VARIANT_FALSE || GetMethodNS() != bstrMethodNS)
			{
	            wchar_t wszMsg[512];
                BSTR bstrRcvdMethodNS = SysAllocString(GetMethodNS().c_str());
                _snwprintf(wszMsg, 511, L"Unrecognized Remote Procedure: '{%s}%s'.", bstrRcvdMethodNS, bstrMethodName);
                wszMsg[511] = 0;
				SysFreeString(bstrRcvdMethodNS);
				SysFreeString(bstrMethodNS);
				SysFreeString(bstrMethodName);
                if (GetSOAPVersion() == SOAP_VERSION_1_1)
                {
		            throw new WMSOAPFault(  WMSOAPFault::Sender,
                                            wszMsg,
                                            true);
                }
                else // SOAP 1.2
                {
		            throw new WMSOAPFault(  WMSOAPFault::Sender,
                                            L"ProcedureNotPresent",
                                            g_pwszSoap12RpcNS,
                                            wszMsg,
                                            true);
                }
			}
			SysFreeString(bstrMethodNS);
            // if SOAP 1.1 then check SOAPAction value
            if (GetSOAPVersion() == SOAP_VERSION_1_1)
            {
			    BSTR bstrSOAPAction = NULL;
			    m_pIWSDLClient->GetMethodSOAPAction(bstrMethodName, &bstrSOAPAction, &bResult);
                if (bResult == VARIANT_FALSE)
                {
    			    SysFreeString(bstrMethodName);
		            throw new WMSOAPFault(  WMSOAPFault::Receiver,
                                            L"SOAPAction not defined in WSDL document for this operation.",
                                            true);
                }
			    if (GetSOAPAction() != bstrSOAPAction)
			    {
	                wchar_t wszMsg[512];
                    BSTR bstrRcvdSOAPAction = SysAllocString(GetSOAPAction().c_str());
                    _snwprintf(wszMsg, 511, L"Unrecognized SOAPAction HTTP header: %s.", bstrRcvdSOAPAction);
                    wszMsg[511] = 0;
                    SysFreeString(bstrRcvdSOAPAction);
				    SysFreeString(bstrSOAPAction);
    			    SysFreeString(bstrMethodName);
				    throw new WMSOAPFault(WMSOAPFault::Sender, wszMsg, false);
			    }
			    SysFreeString(bstrSOAPAction);
            }
            else // SOAP 1.2
            {
                // consult WSDL to see if soap action is required
                VARIANT_BOOL bSOAPActionRequired = VARIANT_FALSE;
    			m_pIWSDLClient->SOAPActionRequired(bstrMethodName, &bSOAPActionRequired, &bResult);
                if (bSOAPActionRequired == VARIANT_TRUE)
                {
			        BSTR bstrSOAPAction;
			        m_pIWSDLClient->GetMethodSOAPAction(bstrMethodName, &bstrSOAPAction, &bResult);
                    if (bResult == VARIANT_FALSE)
                    {
    			        SysFreeString(bstrMethodName);
		                throw new WMSOAPFault(  WMSOAPFault::Receiver,
                                                L"SOAPAction not defined in WSDL document for this operation.",
                                                true);
                    }
			        if (GetSOAPAction() != bstrSOAPAction)
			        {
	                    wchar_t wszMsg[512];
                        BSTR bstrRcvdSOAPAction = SysAllocString(GetSOAPAction().c_str());
                        _snwprintf(wszMsg, 511, L"Unrecognized SOAPAction HTTP header: %s.", bstrRcvdSOAPAction);
                        wszMsg[511] = 0;
                        SysFreeString(bstrRcvdSOAPAction);
				        SysFreeString(bstrSOAPAction);
    			        SysFreeString(bstrMethodName);
				        throw new WMSOAPFault(WMSOAPFault::Sender, wszMsg, false);
			        }
			        SysFreeString(bstrSOAPAction);
                }
            }
            // get "use"
            BSTR bstrUse;
            wstring wstrUse;
            m_pIWSDLClient->GetSOAPInputBodyUse(bstrMethodName, &bstrUse, &bResult);
            if (bResult == VARIANT_TRUE)
            {
                wstrUse = bstrUse;
                SysFreeString(bstrUse);
            }
		    SysFreeString(bstrMethodName);
            if (wstrUse == L"encoded")
            {
			    m_ReqDeserializer.ProcessRPCRequestEnc(*this, m_pIWSDLClient);
            }
            else
            {
			    m_ReqDeserializer.ProcessRPCRequestLit(*this, m_pIWSDLClient);
            }
		}
		else
		{
			BSTR bstrMethodName = SysAllocString(GetMethodName().c_str());
			VARIANT_BOOL bResult;
            // get "use"
            BSTR bstrUse;
            wstring wstrUse;
            m_pIWSDLClient->GetSOAPOutputBodyUse(bstrMethodName, &bstrUse, &bResult);
            if (bResult == VARIANT_TRUE)
            {
                wstrUse = bstrUse;
                SysFreeString(bstrUse);
            }
		    SysFreeString(bstrMethodName);
            if (wstrUse == L"encoded")
            {
    			m_RespDeserializer.ProcessRPCResponseEnc(*this, m_pIWSDLClient);
            }
            else
            {
			    m_RespDeserializer.ProcessRPCResponseLit(*this, m_pIWSDLClient);
            }
		}
	}
	catch (WMSOAPFault *f)
	{
        if (GetSOAPVersion() == SOAP_VERSION_1_1)
        {
            wstring wstrFaultCode, wstrFaultCodeNS;
            f->GetSoapFaultCode(GetSOAPVersion(), wstrFaultCode, wstrFaultCodeNS);
		    SetSOAPFault(   wstrFaultCode,
                            wstrFaultCodeNS,
                            f->GetFaultString(),
                            wstring(L""),
                            f->NeedDetail());
        }
        else
        {
            wstring wstrFaultCode, wstrFaultCodeNS;
            wstring wstrSubCode, wstrSubCodeNS;
            f->GetSoapFaultCode(GetSOAPVersion(), wstrFaultCode, wstrFaultCodeNS);
            f->GetFaultSubCode(wstrSubCode, wstrSubCodeNS);
            map<wstring, wstring> mapReasonText;
            mapReasonText[wstring(L"en")] = f->GetFaultString();
		    SetSOAPFault(   wstrFaultCode,
                            wstrFaultCodeNS,
                            wstrSubCode,
                            wstrSubCodeNS,
                            mapReasonText,
                            wstring(L""),
                            f->NeedDetail());
        }
        LogFault(f);
		delete f;
		bRtn = false;
	}
	return bRtn;
}

bool WMSOAPMessage::ProcessRPCGet(  const wstring& wstrSvcURI,
                                    const wstring& wstrExtraPath,
                                    const wstring& wstrQueryString)
{
	bool bRtn = true;
	try
	{
		if (IsRequestPhase() == true)
		{
			// set up call to request handler method whose
            // name is determined by first extra path component
            // (less initial path delimiter '/')
            if (wstrExtraPath.length() < 2)
            {
                return false;
            }
            wstring wstrTemp = wstrExtraPath.substr(1);
            wstring::size_type pos = wstrTemp.find(wchar_t('/'));
            BSTR bstrMethodName = SysAllocString(wstrTemp.substr(0, pos).c_str());
            // save any remaining extra path
            if (pos != wstring::npos)
            {
                wstrTemp = wstrTemp.substr(pos);
            }
            else
            {
                wstrTemp.erase();
            }
            BSTR bstrMethodNS = NULL;
            // get method namespace from WSDL description of operation
			VARIANT_BOOL bResult;
			m_pIWSDLClient->GetMethodNamespaceURI(bstrMethodName, &bstrMethodNS, &bResult);
            if (bResult == VARIANT_TRUE)
            {
                SetMethodNS(wstring(bstrMethodNS));
                SysFreeString(bstrMethodNS);
                SetMethodName(wstring(bstrMethodName));
            }
            SysFreeString(bstrMethodName);
            if (bResult == VARIANT_FALSE)
            {
                // operation not found in WSDL binding description
                return false;
            }
             // add [in] parameter, extra path, possibly empty
            _variant_t varValue = wstrTemp.c_str();
	        AddMethodParam( wstring(L""),
						    wstring(L"bstrExtraPath"),
						    varValue,
						    true,
						    false);
           // add [in] parameter, query string, possibly empty
            varValue = wstrQueryString.c_str();
	        AddMethodParam( wstring(L""),
						    wstring(L"bstrQueryString"),
						    varValue,
						    true,
						    false);
            // call deserializer to set up [out] parameters
			m_ReqDeserializer.ProcessRPCGetRequest(*this, m_pIWSDLClient);
		}
		else
		{
			BSTR bstrMethodName = SysAllocString(GetMethodName().c_str());
			VARIANT_BOOL bResult;
            // get "use"
            BSTR bstrUse;
            wstring wstrUse;
            m_pIWSDLClient->GetSOAPOutputBodyUse(bstrMethodName, &bstrUse, &bResult);
            if (bResult == VARIANT_TRUE)
            {
                wstrUse = bstrUse;
                SysFreeString(bstrUse);
            }
		    SysFreeString(bstrMethodName);
            if (wstrUse == L"encoded")
            {
    			m_RespDeserializer.ProcessRPCResponseEnc(*this, m_pIWSDLClient);
            }
            else
            {
			    m_RespDeserializer.ProcessRPCResponseLit(*this, m_pIWSDLClient);
            }
		}
	}
	catch (WMSOAPFault *f)
	{
        wstring wstrFaultCode, wstrFaultCodeNS;
        wstring wstrSubCode, wstrSubCodeNS;
        f->GetSoapFaultCode(GetSOAPVersion(), wstrFaultCode, wstrFaultCodeNS);
        f->GetFaultSubCode(wstrSubCode, wstrSubCodeNS);
        map<wstring, wstring> mapReasonText;
        mapReasonText[wstring(L"en")] = f->GetFaultString();
		SetSOAPFault(   wstrFaultCode,
                        wstrFaultCodeNS,
                        wstrSubCode,
                        wstrSubCodeNS,
                        mapReasonText,
                        wstring(L""),
                        f->NeedDetail());
        LogFault(f);
		delete f;
		bRtn = false;
	}
	return bRtn;
}

bool WMSOAPMessage::Serialize()
{
	// clear out buffer
	SetBuffer(NULL, 0);
	return m_Serializer.Serialize(*this, m_pIWSDLClient);
}

bool WMSOAPMessage::SetSOAPFault(   const wstring& wstrFaultCode,
                                    const wstring& wstrFaultCodeNS,
                                    const wstring& wstrSubCode,
                                    const wstring& wstrSubCodeNS,
								    map<wstring, wstring>& mapReasonText,
								    const wstring& wstrFaultNode,
                                    bool bNeedDetail)
{
	m_wstrFaultCode = wstrFaultCode;
	m_wstrFaultCodeNS = wstrFaultCodeNS;
	m_wstrFaultSubCode = wstrSubCode;
	m_wstrFaultSubCodeNS = wstrSubCodeNS;
	m_mapReasonText = mapReasonText;
	m_wstrFaultNode = wstrFaultNode;
	m_bFaultNeedDetail = bNeedDetail;
	m_bIsSOAPFault = true;
	return true;
}

void WMSOAPMessage::AddReasonText(const wstring& wstrText, const wstring& wstrLangId)
{
    m_mapReasonText[wstrLangId] = wstrText;
}

bool WMSOAPMessage::SetSOAPFault(   const wstring& wstrFaultCode,
                                    const wstring& wstrFaultCodeNS,
									const wstring& wstrFaultString,
									const wstring& wstrFaultNode,
									bool bNeedDetail)
{
	m_wstrFaultCode = wstrFaultCode;
	m_wstrFaultCodeNS = wstrFaultCodeNS;
    m_wstrFaultSubCode.erase();
    m_wstrFaultSubCodeNS.erase();
	m_wstrFaultString = wstrFaultString;
	m_wstrFaultNode = wstrFaultNode;
	m_bFaultNeedDetail = bNeedDetail;
	m_bIsSOAPFault = true;
	return true;
}

void WMSOAPMessage::ClearSOAPFault()
{
	ClearFaultHeaderElems();
	ClearFaultDetailElems();
	m_wstrFaultCode.erase();
	m_wstrFaultCodeNS.erase();
	m_wstrFaultSubCode.erase();
	m_wstrFaultSubCodeNS.erase();
	m_wstrFaultString.erase();
	m_wstrFaultNode.erase();
	m_bFaultNeedDetail = false;
	m_bIsSOAPFault = false;
	m_bIsLocalFault = true;
    m_mapReasonText.clear();
}

void WMSOAPMessage::ClearFaultHeaderElems()
{
	for (unsigned int i = 0; i < m_arrFaultHeaderElems.size(); i++)
	{
		delete m_arrFaultHeaderElems[i];
	}
	for (i = 0; i < m_arrFaultHeaderElemsLit.size(); i++)
	{
		delete m_arrFaultHeaderElemsLit[i];
	}
	m_arrFaultHeaderElems.clear();
	m_arrFaultHeaderElemsLit.clear();
}

void WMSOAPMessage::ClearFaultDetailElems()
{
	for (unsigned int i = 0; i < m_arrFaultDetailElems.size(); i++)
	{
		delete m_arrFaultDetailElems[i];
	}
	m_arrFaultDetailElems.clear();
}

bool WMSOAPMessage::AddFaultHeaderElem( const wstring& wstrNamespaceURI,
										const wstring& wstrName,
										const variant_t& varContent,
										bool bMustUnderstand,
										const wstring& wstrActor,
                                        bool bRelay)
{
	WMSOAPHeaderMsgElement *pElem = new WMSOAPHeaderMsgElement( wstrNamespaceURI,
																wstrName,
																varContent,
																bMustUnderstand,
																wstrActor,
                                                                bRelay);
	if (pElem != NULL)
	{
		m_arrFaultHeaderElems.push_back(pElem);
	}
	return (pElem != NULL);
}

bool WMSOAPMessage::GetFirstFaultHeaderElem(WMSOAPHeaderMsgElement **ppElem,
											long& lPosition)
{
	if (m_arrFaultHeaderElems.empty() == true)
	{
		return false;
	}
	*ppElem = m_arrFaultHeaderElems[0];
	lPosition = 0;
	return true;
}

bool WMSOAPMessage::GetNextFaultHeaderElem( WMSOAPHeaderMsgElement **ppElem,
											long& lPosition)
{
	if (((unsigned long)lPosition + 1) >= m_arrFaultHeaderElems.size())
	{
		return false;
	}
	lPosition++;
	*ppElem = m_arrFaultHeaderElems[lPosition];
	return true;
}

bool WMSOAPMessage::AddFaultHeaderElemLit(  const wstring& wstrNamespaceURI,
											const wstring& wstrName,
											const wstring& wstrLiteralXML,
											bool bMustUnderstand,
											const wstring& wstrActor,
                                            bool bRelay,
											const wstring& wstrEncodingStyle)
{
	WMSOAPHeaderMsgElementLit *pElem = new WMSOAPHeaderMsgElementLit(   wstrNamespaceURI,
																		wstrName,
																		wstrLiteralXML,
																		bMustUnderstand,
																		wstrActor,
                                                                        bRelay,
																		wstrEncodingStyle);
	if (pElem != NULL)
	{
		m_arrFaultHeaderElemsLit.push_back(pElem);
	}
	return (pElem != NULL);
}

bool WMSOAPMessage::GetFirstFaultHeaderElemLit( WMSOAPHeaderMsgElementLit **ppElem,
												long& lPosition)
{
	if (m_arrFaultHeaderElemsLit.empty() == true)
	{
		return false;
	}
	*ppElem = m_arrFaultHeaderElemsLit[0];
	lPosition = 0;
	return true;
}

bool WMSOAPMessage::GetNextFaultHeaderElemLit(  WMSOAPHeaderMsgElementLit **ppElem,
												long& lPosition)
{
	if (((unsigned long)lPosition + 1) >= m_arrFaultHeaderElemsLit.size())
	{
		return false;
	}
	lPosition++;
	*ppElem = m_arrFaultHeaderElemsLit[lPosition];
	return true;
}

bool WMSOAPMessage::AddFaultDetailElem( const wstring& wstrNamespaceURI,
										const wstring& wstrName,
										const variant_t& varContent)
{
	WMXMLMessageElement *pElem = new WMXMLMessageElement( wstrNamespaceURI,
																wstrName,
																varContent);
	if (pElem != NULL)
	{
		m_arrFaultDetailElems.push_back(pElem);
	}
	return (pElem != NULL);
}

bool WMSOAPMessage::GetFirstFaultDetailElem(wstring& wstrNamespaceURI,
											wstring& wstrName,
											_variant_t& varContent,
											long& lPosition)
{
	if (m_arrFaultDetailElems.empty() == true)
	{
		return false;
	}
	WMXMLMessageElement *pElem = m_arrFaultDetailElems[0];
	wstrNamespaceURI = pElem->m_wstrNamespaceURI;
	wstrName = pElem->m_wstrName;
	varContent = pElem->m_varContent;
	lPosition = 0;
	return true;
}

bool WMSOAPMessage::GetNextFaultDetailElem( wstring& wstrNamespaceURI,
											wstring& wstrName,
											_variant_t& varContent,
											long& lPosition)
{
	if (((unsigned long)lPosition + 1) >= m_arrFaultDetailElems.size())
	{
		return false;
	}
	lPosition++;
	WMXMLMessageElement *pElem = m_arrFaultDetailElems[lPosition];
	wstrNamespaceURI = pElem->m_wstrNamespaceURI;
	wstrName = pElem->m_wstrName;
	varContent = pElem->m_varContent;
	return true;
}

bool WMSOAPMessage::GetFaultDetailElem(	const wstring& wstrNamespaceURI,
										const wstring& wstrName,
										variant_t& varContent)
{
	vector<WMXMLMessageElement *>::iterator iter;
	for (iter = m_arrFaultDetailElems.begin();
				iter != m_arrFaultDetailElems.end(); iter++)
	{
		WMXMLMessageElement *pElem = *iter;
		if (wstrNamespaceURI == pElem->m_wstrNamespaceURI &&
			wstrName == pElem->m_wstrName)
		{
			// name and actor URI match
			varContent = pElem->m_varContent;
			return true;
		}
	}
	return false;
}

bool WMSOAPMessage::GetPortAddress(wstring& wstrAddress)
{
	VARIANT_BOOL bResult;
	BSTR bstrHost, bstrURI;
	int nPort;
	m_pIWSDLClient->GetSOAPAddress(&bstrHost, &bstrURI, &nPort, &bResult);
	if (bResult == VARIANT_TRUE)
	{
		wchar_t wszBuffer[256];
		wstrAddress = L"http://";
		wstrAddress += bstrHost;
		if (nPort != 80)
		{
			wstrAddress += L":";
			wstrAddress += _itow(nPort, wszBuffer, 10);
		}
	    wstrAddress += bstrURI;
        return true;
	}
	return false;
}

bool WMSOAPMessage::AddDIMEAttachmentReq(   const wstring& wstrId,
											const wstring& wstrType,
											long lTNF,
											IUnknown *pIUnknown,
											SAFEARRAY *pOptions)
{
	if (pIUnknown == NULL)
	{
		return false;
	}
	IStorage *pIStorage;
	HRESULT hRes = pIUnknown->QueryInterface(IID_IStorage, (void **)&pIStorage);
	if (FAILED(hRes))
	{
		return false;
	}
	string strId, strType;
	WideStringToUTF8(wstrId, strId);
	WideStringToUTF8(wstrType, strType);
	DIMEPayload *pPayload = new DIMEPayload(pIStorage,
											strId,
											strType,
											lTNF);
	if (pPayload == NULL)
	{
		pIStorage->Release();
		return false;
	}
	if (pOptions != NULL)
	{
		// extract info from SAFEARRAY
		void *pData = NULL;
		SafeArrayAccessData(pOptions, &pData);
		pPayload->SetOptions((unsigned char *)pData, pOptions->rgsabound[0].cElements);
		SafeArrayUnaccessData(pOptions);
	}
	m_arrAttachmentsReq.push_back(pPayload);
	pIStorage->Release();
	return true;
}

bool WMSOAPMessage::AddDIMEAttachmentResp(  const wstring& wstrId,
											const wstring& wstrType,
											long lTNF,
											IUnknown *pIUnknown,
											SAFEARRAY *pOptions)
{
	if (pIUnknown == NULL)
	{
		return false;
	}
	IStorage *pIStorage;
	HRESULT hRes = pIUnknown->QueryInterface(IID_IStorage, (void **)&pIStorage);
	if (FAILED(hRes))
	{
		return false;
	}
	string strId, strType;
	WideStringToUTF8(wstrId, strId);
	WideStringToUTF8(wstrType, strType);
	DIMEPayload *pPayload = new DIMEPayload(pIStorage,
											strId,
											strType,
											lTNF);
	if (pPayload == NULL)
	{
		pIStorage->Release();
		return false;
	}
	if (pOptions != NULL)
	{
		// extract info from SAFEARRAY
		void *pData = NULL;
		SafeArrayAccessData(pOptions, &pData);
		pPayload->SetOptions((unsigned char *)pData, pOptions->rgsabound[0].cElements);
		SafeArrayUnaccessData(pOptions);
	}
	m_arrAttachmentsResp.push_back(pPayload);
	pIStorage->Release();
	return true;
}

bool WMSOAPMessage::GetFirstDIMEAttachmentReq(  wstring& wstrId,
												wstring& wstrType,
												long& lTNF,
												IUnknown **ppIUnknown,
												SAFEARRAY **ppOptions,
												long& lPosition)
{
	if (m_arrAttachmentsReq.empty() == true)
	{
		return false;
	}
	DIMEPayload *pPayload = m_arrAttachmentsReq[0];
	UTF8ToWideString(pPayload->GetId(), wstrId);
	UTF8ToWideString(pPayload->GetType(), wstrType);
	*ppIUnknown = pPayload->GetStorage();
	lTNF = pPayload->GetTNF();
	if (ppOptions != NULL)
	{
		// now create vector
		unsigned long ulLength = pPayload->GetOptionsLength();
		SAFEARRAY *pArray = SafeArrayCreateVector(VT_UI1, 0L, ulLength);
		if (pArray == NULL)
		{
			return false;
		}
		if (ulLength > 0)
		{
			void *pData = NULL;
			if (FAILED(SafeArrayAccessData(pArray, &pData)))
			{
				SafeArrayDestroy(pArray);
				return false;
			}
			// copy data into vector
			pPayload->GetOptions((unsigned char *)pData, ulLength);
			SafeArrayUnaccessData(pArray);
		}
		*ppOptions = pArray;
	}
	lPosition = 0;
	return true;
}

bool WMSOAPMessage::GetNextDIMEAttachmentReq(   wstring& wstrId,
												wstring& wstrType,
												long& lTNF,
												IUnknown **ppIUnknown,
												SAFEARRAY **ppOptions,
												long& lPosition)
{
	if (((unsigned long)lPosition + 1) >= m_arrAttachmentsReq.size())
	{
		return false;
	}
	lPosition++;
	DIMEPayload *pPayload = m_arrAttachmentsReq[lPosition];
	UTF8ToWideString(pPayload->GetId(), wstrId);
	UTF8ToWideString(pPayload->GetType(), wstrType);
	*ppIUnknown = pPayload->GetStorage();
	lTNF = pPayload->GetTNF();
	if (ppOptions != NULL)
	{
		// now create vector
		unsigned long ulLength = pPayload->GetOptionsLength();
		SAFEARRAY *pArray = SafeArrayCreateVector(VT_UI1, 0L, ulLength);
		if (pArray == NULL)
		{
			return false;
		}
		if (ulLength > 0)
		{
			void *pData = NULL;
			if (FAILED(SafeArrayAccessData(pArray, &pData)))
			{
				SafeArrayDestroy(pArray);
				return false;
			}
			// copy data into vector
			pPayload->GetOptions((unsigned char *)pData, ulLength);
			SafeArrayUnaccessData(pArray);
		}
		*ppOptions = pArray;
	}
	return true;
}

bool WMSOAPMessage::GetFirstDIMEAttachmentResp( wstring& wstrId,
												wstring& wstrType,
												long& lTNF,
												IUnknown **ppIUnknown,
												SAFEARRAY **ppOptions,
												long& lPosition)
{
	if (m_arrAttachmentsResp.empty() == true)
	{
		return false;
	}
	DIMEPayload *pPayload = m_arrAttachmentsResp[0];
	UTF8ToWideString(pPayload->GetId(), wstrId);
	UTF8ToWideString(pPayload->GetType(), wstrType);
	*ppIUnknown = pPayload->GetStorage();
	lTNF = pPayload->GetTNF();
	if (ppOptions != NULL)
	{
		// now create vector
		unsigned long ulLength = pPayload->GetOptionsLength();
		SAFEARRAY *pArray = SafeArrayCreateVector(VT_UI1, 0L, ulLength);
		if (pArray == NULL)
		{
			return false;
		}
		if (ulLength > 0)
		{
			void *pData = NULL;
			if (FAILED(SafeArrayAccessData(pArray, &pData)))
			{
				SafeArrayDestroy(pArray);
				return false;
			}
			// copy data into vector
			pPayload->GetOptions((unsigned char *)pData, ulLength);
			SafeArrayUnaccessData(pArray);
		}
		*ppOptions = pArray;
	}
	lPosition = 0;
	return true;
}

bool WMSOAPMessage::GetNextDIMEAttachmentResp(  wstring& wstrId,
												wstring& wstrType,
												long& lTNF,
												IUnknown **ppIUnknown,
												SAFEARRAY **ppOptions,
												long& lPosition)
{
	if (((unsigned long)lPosition + 1) >= m_arrAttachmentsResp.size())
	{
		return false;
	}
	lPosition++;
	DIMEPayload *pPayload = m_arrAttachmentsResp[lPosition];
	UTF8ToWideString(pPayload->GetId(), wstrId);
	UTF8ToWideString(pPayload->GetType(), wstrType);
	*ppIUnknown = pPayload->GetStorage();
	lTNF = pPayload->GetTNF();
	if (ppOptions != NULL)
	{
		// now create vector
		unsigned long ulLength = pPayload->GetOptionsLength();
		SAFEARRAY *pArray = SafeArrayCreateVector(VT_UI1, 0L, ulLength);
		if (pArray == NULL)
		{
			return false;
		}
		if (ulLength > 0)
		{
			void *pData = NULL;
			if (FAILED(SafeArrayAccessData(pArray, &pData)))
			{
				SafeArrayDestroy(pArray);
				return false;
			}
			// copy data into vector
			pPayload->GetOptions((unsigned char *)pData, ulLength);
			SafeArrayUnaccessData(pArray);
		}
		*ppOptions = pArray;
	}
	return true;
}

bool WMSOAPMessage::GetDIMEAttachmentReq(   const wstring& wstrId,
											wstring& wstrType,
											long& lTNF,
											IUnknown **ppIUnknown,
											SAFEARRAY **ppOptions)
{
	string strId;
	WideStringToUTF8(wstrId, strId);
	vector<DIMEPayload *>::iterator iter;
	for (iter = m_arrAttachmentsReq.begin();
				iter != m_arrAttachmentsReq.end(); iter++)
	{
		DIMEPayload *pPayload = *iter;
		if (strId == pPayload->GetId())
		{
			// Id matches
			UTF8ToWideString(pPayload->GetType(), wstrType);
			*ppIUnknown = pPayload->GetStorage();
			lTNF = pPayload->GetTNF();
			if (ppOptions != NULL)
			{
				// now create vector
				unsigned long ulLength = pPayload->GetOptionsLength();
				SAFEARRAY *pArray = SafeArrayCreateVector(VT_UI1, 0L, ulLength);
				if (pArray == NULL)
				{
					return false;
				}
				if (ulLength > 0)
				{
					void *pData = NULL;
					if (FAILED(SafeArrayAccessData(pArray, &pData)))
					{
						SafeArrayDestroy(pArray);
						return false;
					}
					// copy data into vector
					pPayload->GetOptions((unsigned char *)pData, ulLength);
					SafeArrayUnaccessData(pArray);
				}
				*ppOptions = pArray;
			}
			return true;
		}
	}
	return false;
}

bool WMSOAPMessage::GetDIMEAttachmentResp(  const wstring& wstrId,
											wstring& wstrType,
											long& lTNF,
											IUnknown **ppIUnknown,
											SAFEARRAY **ppOptions)
{
	string strId;
	WideStringToUTF8(wstrId, strId);
	vector<DIMEPayload *>::iterator iter;
	for (iter = m_arrAttachmentsResp.begin();
				iter != m_arrAttachmentsResp.end(); iter++)
	{
		DIMEPayload *pPayload = *iter;
		if (strId == pPayload->GetId())
		{
			// Id matches
			UTF8ToWideString(pPayload->GetType(), wstrType);
			*ppIUnknown = pPayload->GetStorage();
			lTNF = pPayload->GetTNF();
			if (ppOptions != NULL)
			{
				// now create vector
				unsigned long ulLength = pPayload->GetOptionsLength();
				SAFEARRAY *pArray = SafeArrayCreateVector(VT_UI1, 0L, ulLength);
				if (pArray == NULL)
				{
					return false;
				}
				if (ulLength > 0)
				{
					void *pData = NULL;
					if (FAILED(SafeArrayAccessData(pArray, &pData)))
					{
						SafeArrayDestroy(pArray);
						return false;
					}
					// copy data into vector
					pPayload->GetOptions((unsigned char *)pData, ulLength);
					SafeArrayUnaccessData(pArray);
				}
				*ppOptions = pArray;
			}
			return true;
		}
	}
	return false;
}

void WMSOAPMessage::ClearAttachments()
{
	for (unsigned int i = 0; i < m_arrAttachmentsReq.size(); i++)
	{
		delete m_arrAttachmentsReq[i];
	}
	for (i = 0; i < m_arrAttachmentsResp.size(); i++)
	{
		delete m_arrAttachmentsResp[i];
	}
	m_arrAttachmentsReq.clear();
	m_arrAttachmentsResp.clear();
}

long WMSOAPMessage::GetNumReqAttachments()
{
	return m_arrAttachmentsReq.size();
}

long WMSOAPMessage::GetNumRespAttachments()
{
	return m_arrAttachmentsResp.size();
}

bool WMSOAPMessage::SetRoles(list<wstring>& listRoles)
{
    list<wstring>::iterator iL;
    for (iL = listRoles.begin(); iL != listRoles.end(); iL++)
    {
        m_setRoles.insert(*iL);
    }
    return true;
}

bool WMSOAPMessage::GetRoles(list<wstring>& listRoles)
{
    set<wstring>::iterator iS;
    for (iS = m_setRoles.begin(); iS != m_setRoles.end(); iS++)
    {
        listRoles.push_back(*iS);
    }
    return true;
}

bool WMSOAPMessage::GetMUHeaderBlocks(list<wstring>& listNamespaces,
                                      list<wstring>& listLocalNames)
{
	// first the de-serialized headers
	vector<WMSOAPHeaderMsgElement *>::iterator iter1;
	for (iter1 = m_arrHeaderElements.begin();
				iter1 != m_arrHeaderElements.end(); iter1++)
	{
		// if next hop URL is empty, then we are ultimate destination
		WMSOAPHeaderMsgElement *pElem = *iter1;
        if (m_setRoles.find(pElem->m_wstrActor) != m_setRoles.end() ||
            (IsIntermediary() == false && pElem->m_wstrActor.empty() == true))
		{
			if (pElem->m_bMustUnderstand == true)
			{
				// mustUnderstand header element
                listLocalNames.push_back(pElem->m_wstrName);
                listNamespaces.push_back(pElem->m_wstrNamespaceURI);
			}
		}
	}
	// now the un-deserialized headers
	vector<WMSOAPHeaderMsgElementLit *>::iterator iter2;
	for (iter2 = m_arrHeaderElementsLit.begin();
				iter2 != m_arrHeaderElementsLit.end(); iter2++)
	{
		// if next hop URL is empty, then we are ultimate destination
		WMSOAPHeaderMsgElementLit *pElem = *iter2;
        if (m_setRoles.find(pElem->m_wstrActor) != m_setRoles.end() ||
            (IsIntermediary() == false && pElem->m_wstrActor.empty() == true))
		{
			if (pElem->m_bMustUnderstand == true)
			{
				// mustUnderstand header element
                listLocalNames.push_back(pElem->m_wstrName);
                listNamespaces.push_back(pElem->m_wstrNamespaceURI);
			}
		}
	}
	return true;
}
