// WMSOAPRespDeserializer.cpp: implementation of the WMSOAPRespDeserializer 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.
//
//////////////////////////////////////////////////////////////////////
//
// WMSOAPRespDeserializer class - a SOAP message deserializer.
// Populates WMSOAPMessage object with binary representation of message.
// Both "encoded" (SOAP encoding only) or "literal" usage supported.
// 
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "WMSOAPRespDeserializer.h"
#include "WMXMLMessage.h"
#include "base64.h"
#include "hexcode.h"
#include "..\wmwsdlclient\wmwsdlclient.h"
#include "..\wmsoapstruct\wmsoapstruct.h"

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

WMSOAPRespDeserializer::WMSOAPRespDeserializer()
{
}

WMSOAPRespDeserializer::~WMSOAPRespDeserializer()
{
}

bool WMSOAPRespDeserializer::Deserialize(WMSOAPMessage& Msg,
									 Iwmwsdlclient2 *pIWSDLClient)
{
	try
	{
		// parse the message XML
		m_ParseEventHandler.SetTopHandler(&m_MsgHandler);
		m_Parser.ParseXML(Msg.GetBufferPtr(), Msg.GetBufferLength(), &m_ParseEventHandler);
        // envelope found in message?
		if (m_MsgHandler.EnvelopeFound() == false)
		{
			throw new WMSOAPFault(WMSOAPFault::Sender, L"SOAP Envelope not found in message.", false);
		}
        // set SOAP version
        Msg.SetSOAPVersion(m_MsgHandler.GetSOAPVersion());
		// now complete the deserialization
		DeserializeHeader(Msg, pIWSDLClient);
		DeserializeBody(Msg, pIWSDLClient);
	}
	catch (int e)
	{
		wchar_t wszBuffer[64];
		_snwprintf(wszBuffer, 63, L"XML Parser: Parse Error on Line %d.", e);
        wszBuffer[63] = 0;
		throw new WMSOAPFault(WMSOAPFault::Sender, wszBuffer, false);
	}
	catch (WMSOAPFault *f)
	{
        // set SOAP version
        Msg.SetSOAPVersion(m_MsgHandler.GetSOAPVersion());
        throw f;
    }
	return true;
}

bool WMSOAPRespDeserializer::DeserializeHeader(	 WMSOAPMessage& Msg,
												 Iwmwsdlclient2 *pIWSDLClient)
{
	// content storage for encoded header entry elements
	_variant_t varContent;
	for (unsigned int i = 0; i < GetHeaderEntries().size(); i++)
	{
		// initalize
		varContent.Clear();
		// get header entry
		WMSOAPHeaderElemHandler *pElemHandler = GetHeaderEntries()[i];
		// check for SOAP 1.2 "ultimate" role
        if (Msg.GetSOAPVersion() == SOAP_VERSION_1_2)
        {
            if (pElemHandler->GetActorURI() == g_pwszSoap12RoleUltimate)
            {
                pElemHandler->SetActorURI(wstring(L""));
            }
        }
		// is this an encoded header entry?
        bool bIsSoapEncoded = false;
        wstring wstrEncStyle(pElemHandler->GetEncodingStyle());
        if (wstrEncStyle.find(g_pwszSoapEncodingNS) == 0 ||
                wstrEncStyle == g_pwszSoap12EncodingNS)
        {
            bIsSoapEncoded = true;
        }
        else if (wstrEncStyle.empty() == true)
        {
        	// get operation name
			BSTR bstrOpName = SysAllocString(Msg.GetMethodName().c_str());
            // get part name
			BSTR bstrName = SysAllocString(pElemHandler->GetLocalName().c_str());
			BSTR bstrNS = SysAllocString(pElemHandler->GetURI().c_str());
			// see if it is defined in WSDL
            BSTR bstrEncStyle;
            VARIANT_BOOL bResult;
			if (IsSOAPFault() == true)
			{
				// see if it is declared in soap:headerfault ext element
				pIWSDLClient->GetInHeaderFaultPartEncStyle( bstrOpName,
														    bstrName,
														    bstrNS,
														    &bstrEncStyle,
														    &bResult);
			}
			else
			{
				// see if it is declared in soap:header ext element
				pIWSDLClient->GetOutputHeaderPartEncStyle(  bstrOpName,
														    bstrName,
														    bstrNS,
														    &bstrEncStyle,
														    &bResult);

			}
			if (bResult == VARIANT_TRUE)
            {
                // it is defined, so use the encoding style prescribed in WSDL
                pElemHandler->SetEncodingStyle(wstring(bstrEncStyle));
                SysFreeString(bstrEncStyle);
                bIsSoapEncoded = true;
            }
			SysFreeString(bstrOpName);
			SysFreeString(bstrName);
			SysFreeString(bstrNS);
        }
        if (bIsSoapEncoded == true)
		{
            // ensure that encodingStyles of child elements don't conflict
            if (pElemHandler->CheckEncodingStyle(wstrEncStyle) == false)
            {
			    wchar_t wszMsg[512];
                _snwprintf(wszMsg, 511, L"Unsupported encodingStyle for header block: '%s' in Response message.", pElemHandler->GetLocalName().c_str());
                wszMsg[511] = 0;
                if (Msg.GetSOAPVersion() == SOAP_VERSION_1_1)
                {
			        throw new WMSOAPFault(WMSOAPFault::Sender, wszMsg, false);
                }
                else
                {
                    // SOAP 1.2
			        throw new WMSOAPFault(  WMSOAPFault::Sender,
                                            L"DataEncodingUnknown",
                                            g_pwszSoap12EnvelopeNS,
                                            wszMsg,
                                            false);
                }
            }
            // SOAP 1.1 encoding?
            if (pElemHandler->GetEncodingStyle().find(g_pwszSoapEncodingNS) == 0)
            {
			    // is it a non-root?
			    if (pElemHandler->GetRoot() == L"0")
			    {
				    // not a serialization root... store it for now
				    wstring wstrXML;
				    pElemHandler->GetXMLElement()->Serialize(NULL, wstrXML);
				    Msg.AddHeaderElemLit(   pElemHandler->GetURI(),
										    pElemHandler->GetLocalName(),
										    wstrXML,
										    pElemHandler->IsMustUnderstand(),
										    pElemHandler->GetActorURI(),
                                            pElemHandler->IsRelayable(),
										    pElemHandler->GetEncodingStyle());
				    continue;
			    }
            }
			// get operation name
			BSTR bstrOpName = SysAllocString(Msg.GetMethodName().c_str());
			// determine data type
			BSTR bstrName, bstrNS, bstrType, bstrTypeNS;
			bstrName = SysAllocString(pElemHandler->GetLocalName().c_str());
			bstrNS = SysAllocString(pElemHandler->GetURI().c_str());
			VARIANT_BOOL bResult;
			if (IsSOAPFault() == true)
			{
				// see if it is declared in soap:headerfault ext element
				pIWSDLClient->GetInHeaderFaultPartType(bstrOpName,
														bstrName,
														bstrNS,
														&bstrType,
														&bstrTypeNS,
														&bResult);
			}
			else
			{
				// see if it is declared in soap:header ext element
				pIWSDLClient->GetOutputHeaderPartType(  bstrOpName,
														bstrName,
														bstrNS,
														&bstrType,
														&bstrTypeNS,
														&bResult);

			}
			if (bResult == VARIANT_FALSE)
			{
				// see if it is declared in a schema
				pIWSDLClient->GetHeaderElemType(bstrName,
												bstrNS,
												&bstrType,
												&bstrTypeNS,
												&bResult);
			}
			SysFreeString(bstrOpName);
			SysFreeString(bstrName);
			SysFreeString(bstrNS);
			if (bResult == VARIANT_FALSE)
			{
				// not defined in WSDL... store it for now
				wstring wstrXML;
				pElemHandler->GetXMLElement()->Serialize(NULL, wstrXML);
				Msg.AddHeaderElemLit(   pElemHandler->GetURI(),
										pElemHandler->GetLocalName(),
										wstrXML,
										pElemHandler->IsMustUnderstand(),
										pElemHandler->GetActorURI(),
                                        pElemHandler->IsRelayable(),
										pElemHandler->GetEncodingStyle());
				continue;
			}
			wstring wstrType(bstrType);
			wstring wstrTypeNS(bstrTypeNS);
			// is this a multi-ref?
			bool bIsRef = false;
			bool bIsAttachment = false;
			WMSOAPParamHandler *pRefValue = pElemHandler;
			if (pElemHandler->IsRef() == true)
			{
				bIsRef = true;
				pRefValue = GetMultiRefElem(pElemHandler);
				if (pRefValue == NULL)
				{
					// is it a ref to a DIME attachment?
					bIsAttachment = GetDIMEAttachment(  Msg,
														pIWSDLClient,
														pElemHandler,
														wstrType,
														wstrTypeNS,
														varContent);
					if (bIsAttachment == false)
					{
						SysFreeString(bstrType);
						SysFreeString(bstrTypeNS);
						wchar_t wszMsg[512];
						_snwprintf(wszMsg, 511, L"Header block: '%s' has invalid reference.", pElemHandler->GetLocalName().c_str());
                        wszMsg[511] = 0;
                        if (Msg.GetSOAPVersion() == SOAP_VERSION_1_1)
                        {
			                throw new WMSOAPFault(WMSOAPFault::Sender, wszMsg, false);
                        }
                        else
                        {
                            // SOAP 1.2
			                throw new WMSOAPFault(  WMSOAPFault::Sender,
                                                    L"MissingID",
                                                    g_pwszSoap12EnvelopeNS,
                                                    wszMsg,
                                                    false);
                        }
					}
				}
			}
			// if not an attachment we need to deserialize
			if (bIsAttachment == false)
			{
                try
                {
				    // is this a simple type?
				    if (IsXSDType(wstrTypeNS) || IsSoapEncType(wstrTypeNS))
				    {
					    DeserializeBuiltInTypeEnc(  wstrEncStyle,
                                                    Msg,
                                                    pRefValue,
                                                    wstrType,
                                                    varContent);
				    }
				    else
				    {
					    // must be a complex type
					    DeserializeComplexTypeEnc(  wstrEncStyle,
                                                    Msg,
												    pIWSDLClient,
												    pRefValue,
												    pElemHandler,
												    bIsRef,
												    bstrType,
												    bstrTypeNS,
												    varContent);
				    }
                }
                catch (WMSOAPFault *f)
                {
					SysFreeString(bstrType);
					SysFreeString(bstrTypeNS);
                    if (Msg.GetSOAPVersion() == SOAP_VERSION_1_1)
                    {
                        f->SetNeedDetail(false);
                    }
                    throw f;
                }
			}
			// now store it
			Msg.AddHeaderElem(  pElemHandler->GetURI(),
								pElemHandler->GetLocalName(),
								varContent,
								pElemHandler->IsMustUnderstand(),
								pElemHandler->GetActorURI(),
                                pElemHandler->IsRelayable());
			SysFreeString(bstrType);
			SysFreeString(bstrTypeNS);
		}
		else
		{
			// literal XML... store it
			wstring wstrXML;
			pElemHandler->GetXMLElement()->Serialize(NULL, wstrXML);
			Msg.AddHeaderElemLit(   pElemHandler->GetURI(),
									pElemHandler->GetLocalName(),
									wstrXML,
									pElemHandler->IsMustUnderstand(),
									pElemHandler->GetActorURI(),
                                    pElemHandler->IsRelayable(),
									pElemHandler->GetEncodingStyle());
		}
	}
	return true;
}

bool WMSOAPRespDeserializer::DeserializeBody(WMSOAPMessage& Msg,
											Iwmwsdlclient2 *pIWSDLClient)
{
    // was fault element found in msg body?
	if (IsSOAPFault() == true)
	{
		// store in deserialized form for convenience
		DeserializeSOAPFault(Msg, pIWSDLClient);
	}
	// store the literal body element object
	wstring wstrXML;
	GetBodyHandler().GetXMLElement()->Serialize(NULL, wstrXML);
	return Msg.SetBodyElemLit(wstrXML, GetBodyHandler().GetEncodingStyle());
}

bool WMSOAPRespDeserializer::ProcessRPCResponseEnc(WMSOAPMessage& Msg,
												   Iwmwsdlclient2 *pIWSDLClient)
{
	// was this a fault message?
	if (IsSOAPFault() == true)
	{
		// no RPC response to process
		return true;
	}
	if (GetResponseHandler() == NULL)
	{
		throw new WMSOAPFault(  WMSOAPFault::Sender,
                                L"Unable to process RPC representation in SOAP message.",
                                true);
	}
	// method name
	BSTR bstrMethod = SysAllocString(Msg.GetMethodName().c_str());
    // get encodingStyle specified in WSDL
	VARIANT_BOOL bResult;
    BSTR bstrEncStyle = NULL;
    wstring wstrEncStyle;
    pIWSDLClient->GetSOAPOutputBodyEncStyle(bstrMethod, &bstrEncStyle, &bResult);
    if (bResult == VARIANT_TRUE)
    {
        wstrEncStyle = bstrEncStyle;
        SysFreeString(bstrEncStyle);
    }
    if (GetResponseHandler()->CheckEncodingStyle(wstrEncStyle) == false)
    {
		wchar_t *pwszMsg = L"encodingStyle on method wrapper conflicts with WSDL definition.";
        if (Msg.GetSOAPVersion() == SOAP_VERSION_1_1)
        {
            SysFreeString(bstrMethod);
		    throw new WMSOAPFault(WMSOAPFault::Sender, pwszMsg, true);
        }
        else if (GetResponseHandler()->GetEncodingStyle() != g_pwszSoap12EncodingStyleNone)
        {
            SysFreeString(bstrMethod);
		    throw new WMSOAPFault(WMSOAPFault::DataEncodingUnknown, pwszMsg, true);
        }
    }
	// [in], [out] param flags
	bool bIsInParam;
	bool bIsOutParam;
	bool bIsRetVal;
    bool bRetValFound = false;
    // parameter name
    BSTR bstrParamName = NULL;
	// content
	_variant_t varContent;
	// iterate over parameters received
	for (unsigned int i = 0; i < GetMethodParameters().size(); i++)
	{
		// initialize
		varContent.Clear();
		WMSOAPParamHandler *pParamHandler = GetMethodParameters()[i];
		bstrParamName = SysAllocString(pParamHandler->GetLocalName().c_str());
		// determine data type expected
		BSTR bstrPartType, bstrPartTypeNS;
		VARIANT_BOOL bResult;
		// is it the return part?
		bIsRetVal = false;
        if (Msg.GetSOAPVersion() == SOAP_VERSION_1_1)
        {
		    if (i == 0)
		    {
			    // possibly... make sure it's not an [out] param
			    pIWSDLClient->IsMethodParam(bstrMethod, bstrParamName, &bResult);
			    if (bResult == VARIANT_FALSE)
			    {
				    // not found... assume it's retval
				    bIsRetVal = true;
                    bRetValFound = true;
			    }
		    }
        }
        else // SOAP 1.2
        {
            // if return value expected but not yet found see if this is it
            if (GetResponseHandler()->ResultElemFound() == true &&
                bRetValFound == false &&
                GetResponseHandler()->GetRetValName() == pParamHandler->GetLocalName() &&
                GetResponseHandler()->GetRetValNS() == pParamHandler->GetURI())
            {
			    bIsRetVal = true;
                bRetValFound = true;
            }
        }
		if (bIsRetVal == true)
		{
			pIWSDLClient->GetOperationReturnType(   bstrMethod,
													bstrParamName,
													&bstrPartType,
													&bstrPartTypeNS,
													&bResult);
		}
		else
		{
			pIWSDLClient->GetOutputMsgPartType( bstrMethod,
												bstrParamName,
												&bstrPartType,
												&bstrPartTypeNS,
												&bResult);
		}
		if (bResult == VARIANT_FALSE)
		{
			// parameter not properly defined in WSDL file
            goto rpcfault;
		}
		wstring wstrPartType(bstrPartType);
		wstring wstrPartTypeNS(bstrPartTypeNS);
		// is this a multi-ref or ref to attachment?
		bool bIsRef = false;
		bool bIsAttachment = false;
		WMSOAPParamHandler *pRefValue = pParamHandler;
		if (pParamHandler->IsRef() == true)
		{
			bIsRef = true;
			pRefValue = GetMultiRefElem(pParamHandler);
			if (pRefValue == NULL)
			{
				// is it a ref to a DIME attachment?
				bIsAttachment = GetDIMEAttachment(  Msg,
													pIWSDLClient,
													pParamHandler,
													wstrPartType,
													wstrPartTypeNS,
													varContent);
				if (bIsAttachment == false)
				{
					// no, bad reference
					SysFreeString(bstrPartType);
					SysFreeString(bstrPartTypeNS);
					SysFreeString(bstrMethod);
					wchar_t wszMsg[512];
                    _snwprintf(wszMsg, 511, L"Parameter: '%s' has invalid reference.", bstrParamName);
                    wszMsg[511] = 0;
					SysFreeString(bstrParamName);
                    if (Msg.GetSOAPVersion() == SOAP_VERSION_1_1)
                    {
			            throw new WMSOAPFault(WMSOAPFault::Sender, wszMsg, true);
                    }
                    else
                    {
                        // SOAP 1.2
			            throw new WMSOAPFault(  WMSOAPFault::Sender,
                                                L"MissingID",
                                                g_pwszSoap12EnvelopeNS,
                                                wszMsg,
                                                true);
                    }
				}
			}
		}
		// if not an attachment we need to deserialize
		if (bIsAttachment == false)
		{
            try
            {
			    // is this a simple type?
			    if (IsXSDType(wstrPartTypeNS) || IsSoapEncType(wstrPartTypeNS))
			    {
				    DeserializeBuiltInTypeEnc(  wstrEncStyle,
                                                Msg,
                                                pRefValue,
                                                wstrPartType,
                                                varContent);
			    }
			    else
			    {
				    // complex type
				    DeserializeComplexTypeEnc(  wstrEncStyle,
                                                Msg,
											    pIWSDLClient,
											    pRefValue,
											    pParamHandler,
											    bIsRef,
											    bstrPartType,
											    bstrPartTypeNS,
											    varContent);
			    }
            }
            catch (WMSOAPFault *f)
            {
				SysFreeString(bstrPartType);
				SysFreeString(bstrPartTypeNS);
                SysFreeString(bstrParamName);
				SysFreeString(bstrMethod);
                if (Msg.GetSOAPVersion() == SOAP_VERSION_1_2)
                {
                    // if subcode undefined then use rpc:BadArguments
                    wstring wstrCode, wstrCodeNS, wstrSubCode, wstrSubCodeNS;
                    f->GetSoapFaultCode(SOAP_VERSION_1_2, wstrCode, wstrCodeNS);
                    f->GetFaultSubCode(wstrSubCode, wstrSubCodeNS);
                    if (wstrCode == L"Sender" && wstrSubCode.empty() == true)
                    {
                        f->SetFaultSubCode(wstring(L"BadArguments"), wstring(g_pwszSoap12RpcNS));
                    }
                }
                throw f;
            }
		}
		// is it the return part?
		if (bIsRetVal == true)
		{
			// yes, set the return value
			Msg.SetReturnValue(varContent);
		}
		else
		{
			// is this [in, out]?
			bIsOutParam = true;
			pIWSDLClient->IsInputMsgPart(bstrMethod, bstrParamName, &bResult);
			bIsInParam = (bResult == VARIANT_TRUE) ? true : false;
			// create parameter message element
			wstring wstrNS;
			Msg.AddMethodParam( wstrNS,
								pParamHandler->GetLocalName(),
								varContent,
								bIsInParam,
								bIsOutParam);
		}
		// free parameter name bstr
		SysFreeString(bstrParamName);
		SysFreeString(bstrPartType);
		SysFreeString(bstrPartTypeNS);
	}
    // was return value found if expected? (SOAP 1.2 only)
    if (Msg.GetSOAPVersion() == SOAP_VERSION_1_2 &&
        GetResponseHandler()->ResultElemFound() == true &&
        bRetValFound == false)
    {
		// set a nil return value
		varContent.Clear();
        varContent.vt = VT_NULL;
		Msg.SetReturnValue(varContent);
    }
	// free method name bstr
	SysFreeString(bstrMethod);
	return true;

rpcfault:
	wchar_t wszMsg[512];
	_snwprintf(wszMsg, 511, L"Bad parameter: '%s'.", bstrParamName);
    wszMsg[511] = 0;
    SysFreeString(bstrParamName);
	SysFreeString(bstrMethod);
    if (Msg.GetSOAPVersion() == SOAP_VERSION_1_1)
    {
		throw new WMSOAPFault(WMSOAPFault::Sender, wszMsg, true);
    }
    else // SOAP 1.2
    {
		throw new WMSOAPFault(  WMSOAPFault::Sender,
                                L"BadArguments",
                                g_pwszSoap12RpcNS,
                                wszMsg,
                                true);
    }
}

bool WMSOAPRespDeserializer::ProcessRPCResponseLit(WMSOAPMessage& Msg,
												   Iwmwsdlclient2 *pIWSDLClient)
{
	// was this a fault message?
	if (IsSOAPFault() == true)
	{
		// no RPC response to process
		return true;
	}
	if (GetResponseHandler() == NULL)
	{
		throw new WMSOAPFault(  WMSOAPFault::Sender,
                                L"Unable to process RPC representation in SOAP message.",
                                false);
	}
	// method name
	BSTR bstrMethod = SysAllocString(Msg.GetMethodName().c_str());
	// [in], [out] param flags
	bool bIsInParam;
	bool bIsOutParam;
	bool bIsRetVal;
    bool bRetValFound = false;
    // parameter name
    BSTR bstrParamName = NULL;
	// content
	_variant_t varContent;
	// iterate over parameters received
	for (unsigned int i = 0; i < GetMethodParameters().size(); i++)
	{
		// initialize
		varContent.Clear();
		WMSOAPParamHandler *pParamHandler = GetMethodParameters()[i];
		bstrParamName = SysAllocString(pParamHandler->GetLocalName().c_str());
		// determine data type expected
		BSTR bstrPartType, bstrPartTypeNS;
		VARIANT_BOOL bResult;
		// is it the return part?
		bIsRetVal = false;
        if (Msg.GetSOAPVersion() == SOAP_VERSION_1_1)
        {
		    if (i == 0)
		    {
			    // possibly... make sure it's not an [out] param
			    pIWSDLClient->IsMethodParam(bstrMethod, bstrParamName, &bResult);
			    if (bResult == VARIANT_FALSE)
			    {
				    // not found... assume it's retval
				    bIsRetVal = true;
                    bRetValFound = true;
			    }
		    }
        }
        else // SOAP 1.2
        {
            // if return value expected but not yet found see if this is it
            if (GetResponseHandler()->ResultElemFound() == true &&
                bRetValFound == false &&
                GetResponseHandler()->GetRetValName() == pParamHandler->GetLocalName() &&
                GetResponseHandler()->GetRetValNS() == pParamHandler->GetURI())
            {
			    bIsRetVal = true;
                bRetValFound = true;
            }
        }
		if (bIsRetVal == true)
		{
			pIWSDLClient->GetOperationReturnType(   bstrMethod,
													bstrParamName,
													&bstrPartType,
													&bstrPartTypeNS,
													&bResult);
		}
		else
		{
			pIWSDLClient->GetOutputMsgPartType( bstrMethod,
												bstrParamName,
												&bstrPartType,
												&bstrPartTypeNS,
												&bResult);
		}
		if (bResult == VARIANT_FALSE)
		{
			// parameter not properly defined in WSDL file
            goto rpcfault;
		}
		wstring wstrPartType(bstrPartType);
		wstring wstrPartTypeNS(bstrPartTypeNS);
        try
        {
		    // is this a simple type?
		    if (IsXSDType(wstrPartTypeNS) || IsSoapEncType(wstrPartTypeNS))
		    {
			    DeserializeBuiltInTypeLit(  Msg,
                                            pParamHandler,
                                            wstrPartType,
                                            varContent);
		    }
		    else
		    {
			    // complex type
			    DeserializeComplexTypeLit(  Msg,
										    pIWSDLClient,
										    pParamHandler,
										    bstrPartType,
										    bstrPartTypeNS,
										    varContent);
		    }
        }
        catch (WMSOAPFault *f)
        {
			SysFreeString(bstrPartType);
			SysFreeString(bstrPartTypeNS);
            // if subcode undefined then use rpc:BadArguments
            wstring wstrSubCode, wstrNS;
            f->GetFaultSubCode(wstrSubCode, wstrNS);
            if (Msg.GetSOAPVersion() == SOAP_VERSION_1_2 &&
                wstrSubCode.empty() == true)
            {
                f->SetFaultSubCode(wstring(L"BadArguments"), wstring(g_pwszSoap12RpcNS));
            }
            throw f;
        }
		// is it the return part?
		if (bIsRetVal == true)
		{
			// yes, set the return value
			Msg.SetReturnValue(varContent);
		}
		else
		{
			// is this [in, out]?
			bIsOutParam = true;
			pIWSDLClient->IsInputMsgPart(bstrMethod, bstrParamName, &bResult);
			bIsInParam = (bResult == VARIANT_TRUE) ? true : false;
			// create parameter message element
			wstring wstrNS;
			Msg.AddMethodParam( wstrNS,
								pParamHandler->GetLocalName(),
								varContent,
								bIsInParam,
								bIsOutParam);
		}
		// free parameter name bstr
		SysFreeString(bstrParamName);
		SysFreeString(bstrPartType);
		SysFreeString(bstrPartTypeNS);
	}
    // was return value found if expected? (SOAP 1.2 only)
    if (Msg.GetSOAPVersion() == SOAP_VERSION_1_2 &&
        GetResponseHandler()->ResultElemFound() == true &&
        bRetValFound == false)
    {
		// set a nil return value
		varContent.Clear();
        varContent.vt = VT_NULL;
		Msg.SetReturnValue(varContent);
    }
	// free method name bstr
	SysFreeString(bstrMethod);
	return true;

rpcfault:
	wchar_t wszMsg[512];
	_snwprintf(wszMsg, 511, L"Bad parameter: '%s'.", bstrParamName);
    wszMsg[511] = 0;
    SysFreeString(bstrParamName);
	SysFreeString(bstrMethod);
    if (Msg.GetSOAPVersion() == SOAP_VERSION_1_1)
    {
		throw new WMSOAPFault(WMSOAPFault::Sender, wszMsg, true);
    }
    else // SOAP 1.2
    {
		throw new WMSOAPFault(  WMSOAPFault::Sender,
                                L"BadArguments",
                                g_pwszSoap12RpcNS,
                                wszMsg,
                                true);
    }
}

bool WMSOAPRespDeserializer::DeserializeSOAPFault(  WMSOAPMessage& Msg,
											        Iwmwsdlclient2 *pIWSDLClient)
{
    WMSOAPFaultHandler *pSOAPFaultHandler = NULL;
	pSOAPFaultHandler = m_MsgHandler.GetEnvelopeHandler().GetBodyHandler().GetSOAPFaultHandler();
	if (pSOAPFaultHandler == NULL)
	{
		return false;
	}
	// fault msg received from another actor
	Msg.SetLocalFaultFlag(false);
    if (Msg.GetSOAPVersion() == SOAP_VERSION_1_1)
    {
	    // pass in pre-defined fault msg elements
	    Msg.SetSOAPFault(   pSOAPFaultHandler->GetFaultCode(),
                            pSOAPFaultHandler->GetFaultCodeNS(),
                            pSOAPFaultHandler->GetFaultString(),
                            pSOAPFaultHandler->GetFaultActor(),
                            false);
    }
    else // SOAP 1.2
    {
	    // pass in pre-defined fault msg elements
	    Msg.SetSOAPFault(   pSOAPFaultHandler->GetCode(),
                            pSOAPFaultHandler->GetCodeNS(),
                            pSOAPFaultHandler->GetSubcode(),
                            pSOAPFaultHandler->GetSubcodeNS(),
                            pSOAPFaultHandler->GetReasonTextMap(),
                            pSOAPFaultHandler->GetNode(),
                            false);
   }
	// now any details
	WMSOAPParamHandler *pDetails = pSOAPFaultHandler->GetDetails();
	if (pDetails == NULL)
	{
		return true;
	}
	// get operation name
	BSTR bstrOpName = SysAllocString(Msg.GetMethodName().c_str());
    // iterate over detail child elements
	_variant_t varContent;
	vector<WMSOAPParamHandler *>::iterator iter;
	for (iter = pDetails->GetChildren().begin();
			iter != pDetails->GetChildren().end();
			iter++)
	{
		// intialize
		varContent.Clear();
		WMSOAPParamHandler *pElemHandler = *iter;
		// determine data type
		BSTR bstrName, bstrNS, bstrType, bstrTypeNS;
		bstrName = SysAllocString(pElemHandler->GetLocalName().c_str());
		bstrNS = SysAllocString(pElemHandler->GetURI().c_str());
		VARIANT_BOOL bResult;
        // is this encoded or literal?
        wstring wstrUse;
        BSTR bstrUse;
		pIWSDLClient->GetFaultDetailUse(bstrOpName,
										bstrName,
										bstrNS,
										&bstrUse,
										&bResult);
		if (bResult == VARIANT_TRUE)
		{
            wstrUse = bstrUse;
            SysFreeString(bstrUse);
        }
		// see if it is declared in soap:fault ext element
		pIWSDLClient->GetFaultDetailPartType(   bstrOpName,
												bstrName,
												bstrNS,
												&bstrType,
												&bstrTypeNS,
												&bResult);
		if (bResult == VARIANT_FALSE)
		{
			// not defined in WSDL... do nothing
		    SysFreeString(bstrName);
		    SysFreeString(bstrNS);
			continue;
		}
		wstring wstrType(bstrType);
		wstring wstrTypeNS(bstrTypeNS);

        if (wstrUse == L"encoded")
        {
            wstring wstrEncStyle;
            BSTR bstrEncStyle;
			pIWSDLClient->GetFaultDetailEncStyle(   bstrOpName,
											        bstrName,
											        bstrNS,
											        &bstrEncStyle,
											        &bResult);
		    if (bResult == VARIANT_TRUE)
		    {
                wstrEncStyle = bstrEncStyle;
                SysFreeString(bstrEncStyle);
            }
            // is there an encodingStyle conflict?
            if (pElemHandler->GetEncodingStyle().empty() == false)
            {
                if (pElemHandler->GetEncodingStyle() != wstrEncStyle)
                {
                    if (Msg.GetSOAPVersion() == SOAP_VERSION_1_1)
                    {
                        goto failure;
                    }
                    else
                    {
                        if (pElemHandler->GetEncodingStyle() != g_pwszSoap12EncodingStyleNone)
                        {
                            goto failure;
                        }
                    }
                }
            }
		    // is this a multi-ref?
		    bool bIsRef = false;
		    WMSOAPParamHandler *pRefValue = pElemHandler;
		    if (pElemHandler->IsRef() == true)
		    {
			    bIsRef = true;
			    pRefValue = GetMultiRefElem(pElemHandler);
			    if (pRefValue == NULL)
			    {
                    goto failure;
			    }
		    }
            try
            {
		        // is this a simple type?
		        if (IsXSDType(wstrTypeNS) || IsSoapEncType(wstrTypeNS))
		        {
			        DeserializeBuiltInTypeEnc(  wstrEncStyle,
                                                Msg,
                                                pRefValue,
                                                wstrType,
                                                varContent);
		        }
		        else
		        {
			        // complex type
			        DeserializeComplexTypeEnc(  wstrEncStyle,
                                                Msg,
										        pIWSDLClient,
										        pRefValue,
										        pElemHandler,
										        bIsRef,
										        bstrType,
										        bstrTypeNS,
										        varContent);
		        }
            }
            catch (WMSOAPFault *f)
            {
                SysFreeString(bstrOpName);
	            SysFreeString(bstrName);
	            SysFreeString(bstrNS);
	            SysFreeString(bstrType);
	            SysFreeString(bstrTypeNS);
                throw f;
            }
        }
        else
        {
		    // is this a simple type?
		    if (IsXSDType(wstrTypeNS) || IsSoapEncType(wstrTypeNS))
		    {
			    if (DeserializeBuiltInTypeLit(  Msg,
                                                pElemHandler,
                                                wstrType,
                                                varContent) == false)
			    {
                    goto failure;
			    }
		    }
		    else
		    {
			    // complex type
			    if (DeserializeComplexTypeLit(  Msg,
										        pIWSDLClient,
										        pElemHandler,
										        bstrType,
										        bstrTypeNS,
										        varContent) == false)
			    {
                    goto failure;
			    }
		    }
        }
		Msg.AddFaultDetailElem( pElemHandler->GetURI(),
								pElemHandler->GetLocalName(),
								varContent);
		SysFreeString(bstrType);
		SysFreeString(bstrTypeNS);
		SysFreeString(bstrName);
		SysFreeString(bstrNS);
        continue;

failure:
        SysFreeString(bstrOpName);
	    SysFreeString(bstrName);
	    SysFreeString(bstrNS);
	    SysFreeString(bstrType);
	    SysFreeString(bstrTypeNS);
	    wchar_t wszMsg[512];
	    _snwprintf(wszMsg, 511, L"Failed to convert element: '%s' in Fault detail.", pElemHandler->GetLocalName().c_str());
        wszMsg[511] = 0;
	    throw new WMSOAPFault(WMSOAPFault::Sender, wszMsg, false);
	}
	SysFreeString(bstrOpName);
	return true;
}

bool WMSOAPRespDeserializer::IsSOAPFault()
{
	return 	m_MsgHandler.GetEnvelopeHandler().GetBodyHandler().IsSOAPFault();
}

WMSOAPHeaderHandler& WMSOAPRespDeserializer::GetHeaderHandler()
{
	return 	m_MsgHandler.GetEnvelopeHandler().GetHeaderHandler();
}

const vector<WMSOAPHeaderElemHandler *>& WMSOAPRespDeserializer::GetHeaderEntries()
{
	return 	(GetHeaderHandler()).GetHeaderEntries();
}

WMSOAPEnvelopeHandler<WMSOAPHeaderHandler, WMSOAPResponseBodyHandler>& WMSOAPRespDeserializer::GetEnvelopeHandler()
{
	return 	m_MsgHandler.GetEnvelopeHandler();
}

WMSOAPResponseBodyHandler& WMSOAPRespDeserializer::GetBodyHandler()
{
	return 	m_MsgHandler.GetEnvelopeHandler().GetBodyHandler();
}

WMSOAPResponseHandler *WMSOAPRespDeserializer::GetResponseHandler()
{
	return 	m_MsgHandler.GetEnvelopeHandler().GetBodyHandler().GetResponseHandler();
}

WMSOAPParamHandler *WMSOAPRespDeserializer::GetMultiRefElem(WMSOAPParamHandler *pParam)
{
	WMSOAPParamHandler *pTemp = m_MsgHandler.GetEnvelopeHandler().GetBodyHandler().GetMultiRefElem(pParam->GetHref().substr(1));
	if (pTemp == NULL)
	{
		pTemp = m_MsgHandler.GetEnvelopeHandler().GetHeaderHandler().GetMultiRefElem(pParam->GetHref().substr(1));
	}
	return pTemp;
}

const vector<WMSOAPParamHandler *>& WMSOAPRespDeserializer::GetMethodParameters()
{
	return 	(GetResponseHandler())->GetParameters();
}
