// WMSOAPReqDeserializer.cpp: implementation of the WMSOAPReqDeserializer 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.
//
//////////////////////////////////////////////////////////////////////
//
// WMSOAPReqDeserializer 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 "WMSOAPReqDeserializer.h"
#include "WMXMLMessage.h"
#include "base64.h"
#include "hexcode.h"
#include "..\wmwsdlclient\wmwsdlclient.h"
#include "..\wmsoapstruct\wmsoapstruct.h"

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

WMSOAPReqDeserializer::WMSOAPReqDeserializer()
{
}

WMSOAPReqDeserializer::~WMSOAPReqDeserializer()
{
}

bool WMSOAPReqDeserializer::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());
		// get method wrapper element parse event handler
		WMSOAPRequestHandler *pRequestHandler = GetRequestHandler();
		if (pRequestHandler != NULL)
		{
            if (Msg.IsRPCStyle() == true)
            {
			    // set method name and namespace
			    Msg.SetMethodName(pRequestHandler->GetLocalName());
			    Msg.SetMethodNS(pRequestHandler->GetURI());
            }
		}
		// 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 WMSOAPReqDeserializer::DeserializeHeader(	WMSOAPMessage& Msg,
												Iwmwsdlclient2 *pIWSDLClient)
{
	// default roles
    list<wstring> listRoles;
    if (Msg.GetSOAPVersion() == SOAP_VERSION_1_1)
    {
        listRoles.push_back(wstring(g_pwszSoapActorNext));
    }
    else
    {
        listRoles.push_back(wstring(g_pwszSoap12RoleNext));
    }
    Msg.SetRoles(listRoles);
	// content storage for encoded header entry elements
	_variant_t varContent;
	for (unsigned int i = 0; i < GetHeaderEntries().size(); i++)
	{
		// initialize
		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;
			// see if it is declared in soap:header ext element
			pIWSDLClient->GetInputHeaderPartEncStyle(   bstrOpName,
														bstrName,
														bstrNS,
														&bstrEncStyle,
														&bResult);
			if (bResult == VARIANT_TRUE)
            {
                // it is defined, so use the encoding style prescribed in WSDL
                wstrEncStyle = bstrEncStyle;
                SysFreeString(bstrEncStyle);
                pElemHandler->SetEncodingStyle(wstrEncStyle);
                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 Request 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;
			// see if it is declared in soap:header ext element
			pIWSDLClient->GetInputHeaderPartType(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_pwszSoap12EncodingNS,
                                                    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... 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 WMSOAPReqDeserializer::DeserializeBody(WMSOAPMessage& Msg,
											Iwmwsdlclient2 *pIWSDLClient)
{
	// store the literal body element object
	wstring wstrXML;
	GetBodyHandler().GetXMLElement()->Serialize(NULL, wstrXML);
	return Msg.SetBodyElemLit( wstrXML,	GetBodyHandler().GetEncodingStyle());
}

bool WMSOAPReqDeserializer::ProcessRPCRequestEnc(   WMSOAPMessage& Msg,
												    Iwmwsdlclient2 *pIWSDLClient)
{
	if (GetRequestHandler() == 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->GetSOAPInputBodyEncStyle(bstrMethod, &bstrEncStyle, &bResult);
    if (bResult == VARIANT_TRUE)
    {
        wstrEncStyle = bstrEncStyle;
        SysFreeString(bstrEncStyle);
    }
    // make sure encodingStyle on request doesn't conflict with WSDL definition
    if (GetRequestHandler()->CheckEncodingStyle(wstrEncStyle) == false)
    {
		wchar_t *pwszMsg = L"Unsupported encodingStyle on method wrapper.";
        if (Msg.GetSOAPVersion() == SOAP_VERSION_1_1)
        {
            SysFreeString(bstrMethod);
		    throw new WMSOAPFault(WMSOAPFault::Sender, pwszMsg, true);
        }
        else if (GetRequestHandler()->GetEncodingStyle() != g_pwszSoap12EncodingStyleNone)
        {
            SysFreeString(bstrMethod);
		    throw new WMSOAPFault(WMSOAPFault::DataEncodingUnknown, pwszMsg, true);
        }
    }
    // [out] parameter map
	map<long, BSTR> mapOutParams;
	map<long, BSTR>::iterator iMI = NULL;
	// [in], [out] param flags
	bool bIsInParam;
	bool bIsOutParam;
    // parameter name
	BSTR bstrParamName = NULL;
	// content
	_variant_t varContent;
	// iterate over parameters received
	for (unsigned int i = 0; i < GetMethodParameters().size(); i++)
	{
		WMSOAPParamHandler *pParamHandler = GetMethodParameters()[i];
		bstrParamName = SysAllocString(pParamHandler->GetLocalName().c_str());
		// determine data type expected
		BSTR bstrPartType, bstrPartTypeNS;
		VARIANT_BOOL bResult;
		pIWSDLClient->GetInputMsgPartType(  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_pwszSoap12EncodingNS,
                                                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 this pass by ref?
		bIsInParam = true;
		pIWSDLClient->IsOutputMsgPart(bstrMethod, bstrParamName, &bResult);
		bIsOutParam = (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);
		varContent.Clear();
	}
	// next set up the [out] params
	long lPosition = 0;
	for (   pIWSDLClient->GetFirstOutParamName(bstrMethod, &bstrParamName, &lPosition, &bResult);
			bResult == VARIANT_TRUE;
			pIWSDLClient->GetNextOutParamName(bstrMethod, &bstrParamName, &lPosition, &bResult))
	{
		// is it the return part?
		VARIANT_BOOL bTemp;
		pIWSDLClient->IsMethodParam(bstrMethod, bstrParamName, &bTemp);
		if (bTemp == VARIANT_FALSE)
		{
			// yes, so ignore it
			Msg.SetRetValName(wstring(bstrParamName));
			SysFreeString(bstrParamName);
			continue;
		}
		// is it an [in, out] param?
		pIWSDLClient->IsInputMsgPart(bstrMethod, bstrParamName, &bTemp);
		if (bTemp == VARIANT_TRUE)
		{
			// yes, so it's already processed
			SysFreeString(bstrParamName);
			continue;
		}
		// associate [out] param with proper index in method signature
		long lIndex = 0;
		pIWSDLClient->GetMethodParamIndex(bstrMethod, bstrParamName, &lIndex, &bTemp);
		if (bTemp == VARIANT_FALSE)
		{
			// not in parameterOrder, so ignore it
			SysFreeString(bstrParamName);
			continue;
		}
		mapOutParams.insert(map<long, BSTR>::value_type(lIndex, bstrParamName));
	}
	for (iMI = mapOutParams.begin(); iMI != mapOutParams.end(); iMI++)
	{
		BSTR bstrPartType, bstrPartTypeNS;
		bstrParamName = (*iMI).second;
		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);
		VARTYPE Vartype;
		map<wstring, VARTYPE>::iterator iV;
		// see if it's a simple type
		if (IsXSDType(wstrPartTypeNS) || IsSoapEncType(wstrPartTypeNS))
		{
			if ((iV = m_mapVarTypes.find(wstrPartType)) != m_mapVarTypes.end())
			{
				Vartype = (*iV).second;
				if (Vartype < XT_ANYTYPE)
				{
					// directly mapped to automation type
					Vartype = (*iV).second;
					varContent.vt = Vartype;
					// initialize content to 0
					varContent.lVal = 0;
				}
				else
				{
					// yes... these types don't map directly to automation types
					switch(Vartype)
					{
					case XT_ANYTYPE:
						{
							// treat as a string
							varContent.vt = VT_BSTR;
							// initialize content to NULL
							varContent.bstrVal = NULL;
						}
						break;
					case XT_BOOLEAN:
						{
							// VT_BOOL
							varContent.vt = VT_BOOL;
							// initialize content to VARIANT_FALSE
							varContent.boolVal = VARIANT_FALSE;
						}
						break;
					case XT_BASE64:
					case XT_HEXBIN:
						{
							// xsd:base64Binary or SOAP-ENC:base64
							varContent.vt = VT_UI1 | VT_ARRAY;
							varContent.parray = NULL;
						}
						break;
					case XT_DATETIME:
					case XT_DATE:
					case XT_TIME:
						{
							// DATE
							varContent.vt = VT_DATE;
							varContent.date = 0.0;
						}
						break;
					default:
						{
							// not supported
							SysFreeString(bstrPartType);
							SysFreeString(bstrPartTypeNS);
							goto rpcfault;
						}
					}
				}
			}
			else
			{
				// not found in map
				SysFreeString(bstrPartType);
				SysFreeString(bstrPartTypeNS);
				goto rpcfault;
			}
		}
		else
		{
			// must be a complex type so look for 'base' type
			BSTR bstrBaseType, bstrBaseTypeNS;
			pIWSDLClient->GetComplexBaseType(	bstrPartType,
												bstrPartTypeNS,
												&bstrBaseType,
												&bstrBaseTypeNS,
												&bResult);
			if (bResult == VARIANT_FALSE)
			{
				// not properly defined in WSDL file
				SysFreeString(bstrPartType);
				SysFreeString(bstrPartTypeNS);
				goto rpcfault;
			}
			wstring wstrBaseType(bstrBaseType);
			wstring wstrBaseTypeNS(bstrBaseTypeNS);
			SysFreeString(bstrBaseType);
			SysFreeString(bstrBaseTypeNS);
			if (IsSoapEncType(wstrBaseTypeNS))
			{
				if (wstrBaseType == L"Array")
				{
					// VARIANT array
					varContent.vt = VT_VARIANT | VT_ARRAY;
					varContent.parray = NULL;
				}
				else if (wstrBaseType == L"Struct")
				{
					// struct
					varContent.vt = VT_DISPATCH;
					varContent.pdispVal = NULL;
				}
				else
				{
					// unrecognized SOAP-ENC type
					SysFreeString(bstrPartType);
					SysFreeString(bstrPartTypeNS);
					goto rpcfault;
				}
			}
			else
			{
				// complex type... no base type given... assume struct
				varContent.vt = VT_DISPATCH;
				varContent.pdispVal = NULL;
			}
		}
		// create parameter message element
		wstring wstrNS;
		Msg.AddMethodParam( wstrNS,
							wstring(bstrParamName),
							varContent,
							false,
							true);
		// free parameter name bstr
		SysFreeString(bstrParamName);
		SysFreeString(bstrPartType);
		SysFreeString(bstrPartTypeNS);
	}
	// 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 WMSOAPReqDeserializer::ProcessRPCRequestLit(   WMSOAPMessage& Msg,
												    Iwmwsdlclient2 *pIWSDLClient)
{
	if (GetRequestHandler() == 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());
    // [out] parameter map
	map<long, BSTR> mapOutParams;
	map<long, BSTR>::iterator iMI = NULL;
	// [in], [out] param flags
	bool bIsInParam;
	bool bIsOutParam;
    // parameter name
	BSTR bstrParamName = NULL;
	// content
	_variant_t varContent;
	// iterate over parameters received
	for (unsigned int i = 0; i < GetMethodParameters().size(); i++)
	{
		WMSOAPParamHandler *pParamHandler = GetMethodParameters()[i];
		bstrParamName = SysAllocString(pParamHandler->GetLocalName().c_str());
		// determine data type expected
		BSTR bstrPartType, bstrPartTypeNS;
		VARIANT_BOOL bResult;
		pIWSDLClient->GetInputMsgPartType(  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 this pass by ref?
		bIsInParam = true;
		pIWSDLClient->IsOutputMsgPart(bstrMethod, bstrParamName, &bResult);
		bIsOutParam = (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);
		varContent.Clear();
	}
	// next set up the [out] params
	long lPosition = 0;
    VARIANT_BOOL bResult;
	for (   pIWSDLClient->GetFirstOutParamName(bstrMethod, &bstrParamName, &lPosition, &bResult);
			bResult == VARIANT_TRUE;
			pIWSDLClient->GetNextOutParamName(bstrMethod, &bstrParamName, &lPosition, &bResult))
	{
		// is it the return part?
		VARIANT_BOOL bTemp;
		pIWSDLClient->IsMethodParam(bstrMethod, bstrParamName, &bTemp);
		if (bTemp == VARIANT_FALSE)
		{
			// yes, so ignore it
			Msg.SetRetValName(wstring(bstrParamName));
			SysFreeString(bstrParamName);
			continue;
		}
		// is it an [in, out] param?
		pIWSDLClient->IsInputMsgPart(bstrMethod, bstrParamName, &bTemp);
		if (bTemp == VARIANT_TRUE)
		{
			// yes, so it's already processed
			SysFreeString(bstrParamName);
			continue;
		}
		// associate [out] param with proper index in method signature
		long lIndex = 0;
		pIWSDLClient->GetMethodParamIndex(bstrMethod, bstrParamName, &lIndex, &bTemp);
		if (bTemp == VARIANT_FALSE)
		{
			// not in parameterOrder, so ignore it
			SysFreeString(bstrParamName);
			continue;
		}
		mapOutParams.insert(map<long, BSTR>::value_type(lIndex, bstrParamName));
	}
	for (iMI = mapOutParams.begin(); iMI != mapOutParams.end(); iMI++)
	{
		BSTR bstrPartType, bstrPartTypeNS;
		bstrParamName = (*iMI).second;
		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);
		VARTYPE Vartype;
		map<wstring, VARTYPE>::iterator iV;
		// see if it's a simple type
		if (IsXSDType(wstrPartTypeNS) || IsSoapEncType(wstrPartTypeNS))
		{
			if ((iV = m_mapVarTypes.find(wstrPartType)) != m_mapVarTypes.end())
			{
				Vartype = (*iV).second;
				if (Vartype < XT_ANYTYPE)
				{
					// directly mapped to automation type
					Vartype = (*iV).second;
					varContent.vt = Vartype;
					// initialize content to 0
					varContent.lVal = 0;
				}
				else
				{
					// yes... these types don't map directly to automation types
					switch(Vartype)
					{
					case XT_ANYTYPE:
						{
							// treat as a string
							varContent.vt = VT_BSTR;
							// initialize content to NULL
							varContent.bstrVal = NULL;
						}
						break;
					case XT_BOOLEAN:
						{
							// VT_BOOL
							varContent.vt = VT_BOOL;
							// initialize content to VARIANT_FALSE
							varContent.boolVal = VARIANT_FALSE;
						}
						break;
					case XT_BASE64:
					case XT_HEXBIN:
						{
							// xsd:base64Binary or SOAP-ENC:base64
							varContent.vt = VT_UI1 | VT_ARRAY;
							varContent.parray = NULL;
						}
						break;
					case XT_DATETIME:
					case XT_DATE:
					case XT_TIME:
						{
							// DATE
							varContent.vt = VT_DATE;
							varContent.date = 0.0;
						}
						break;
					default:
						{
							// not supported
							SysFreeString(bstrPartType);
							SysFreeString(bstrPartTypeNS);
							goto rpcfault;
						}
					}
				}
			}
			else
			{
				// not found in map
				SysFreeString(bstrPartType);
				SysFreeString(bstrPartTypeNS);
				goto rpcfault;
			}
		}
		else
		{
			// see if it is array type
			BSTR bstrItemType, bstrItemTypeNS, bstrItemName, bstrItemNS;
			pIWSDLClient->GetArrayTypeLit(	bstrPartType,
										    bstrPartTypeNS,
										    &bstrItemType,
										    &bstrItemTypeNS,
                                            &bstrItemName,
                                            &bstrItemNS,
										    &bResult);
			if (bResult == VARIANT_TRUE)
			{
			    SysFreeString(bstrItemType);
			    SysFreeString(bstrItemTypeNS);
			    SysFreeString(bstrItemName);
			    SysFreeString(bstrItemNS);
				// VARIANT array
				varContent.vt = VT_VARIANT | VT_ARRAY;
				varContent.parray = NULL;
			}
			else
			{
				// not an array type... assume struct
				varContent.vt = VT_DISPATCH;
				varContent.pdispVal = NULL;
			}
		}
		// create parameter message element
		wstring wstrNS;
		Msg.AddMethodParam( wstrNS,
							wstring(bstrParamName),
							varContent,
							false,
							true);
		// free parameter name bstr
		SysFreeString(bstrParamName);
		SysFreeString(bstrPartType);
		SysFreeString(bstrPartTypeNS);
	}
	// 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 WMSOAPReqDeserializer::ProcessRPCGetRequest(   WMSOAPMessage& Msg,
												    Iwmwsdlclient2 *pIWSDLClient)
{
    // [out] parameter map
	map<long, BSTR> mapOutParams;
	map<long, BSTR>::iterator iMI = NULL;
	// method name
	BSTR bstrMethod = SysAllocString(Msg.GetMethodName().c_str());
    // parameter name
	BSTR bstrParamName = NULL;
	// content
	_variant_t varContent;
	// next set up the [out] params
	long lPosition = 0;
    VARIANT_BOOL bResult = VARIANT_FALSE;
	for (   pIWSDLClient->GetFirstOutParamName(bstrMethod, &bstrParamName, &lPosition, &bResult);
			bResult == VARIANT_TRUE;
			pIWSDLClient->GetNextOutParamName(bstrMethod, &bstrParamName, &lPosition, &bResult))
	{
		// is it the return part?
		VARIANT_BOOL bTemp;
		pIWSDLClient->IsMethodParam(bstrMethod, bstrParamName, &bTemp);
		if (bTemp == VARIANT_FALSE)
		{
			// yes, so ignore it
			Msg.SetRetValName(wstring(bstrParamName));
			SysFreeString(bstrParamName);
			continue;
		}
		// associate [out] param with proper index in method signature
		long lIndex = 0;
		pIWSDLClient->GetMethodParamIndex(bstrMethod, bstrParamName, &lIndex, &bTemp);
		if (bTemp == VARIANT_FALSE)
		{
			// not in parameterOrder, so ignore it
			SysFreeString(bstrParamName);
			continue;
		}
		mapOutParams.insert(map<long, BSTR>::value_type(lIndex, bstrParamName));
	}
	for (iMI = mapOutParams.begin(); iMI != mapOutParams.end(); iMI++)
	{
		BSTR bstrPartType, bstrPartTypeNS;
		bstrParamName = (*iMI).second;
		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);
		VARTYPE Vartype;
		map<wstring, VARTYPE>::iterator iV;
		// see if it's a simple type
		if (IsXSDType(wstrPartTypeNS) || IsSoapEncType(wstrPartTypeNS))
		{
			if ((iV = m_mapVarTypes.find(wstrPartType)) != m_mapVarTypes.end())
			{
				Vartype = (*iV).second;
				if (Vartype < XT_ANYTYPE)
				{
					// directly mapped to automation type
					Vartype = (*iV).second;
					varContent.vt = Vartype;
					// initialize content to 0
					varContent.lVal = 0;
				}
				else
				{
					// yes... these types don't map directly to automation types
					switch(Vartype)
					{
					case XT_ANYTYPE:
						{
							// treat as a string
							varContent.vt = VT_BSTR;
							// initialize content to NULL
							varContent.bstrVal = NULL;
						}
						break;
					case XT_BOOLEAN:
						{
							// VT_BOOL
							varContent.vt = VT_BOOL;
							// initialize content to VARIANT_FALSE
							varContent.boolVal = VARIANT_FALSE;
						}
						break;
					case XT_BASE64:
					case XT_HEXBIN:
						{
							// xsd:base64Binary or SOAP-ENC:base64
							varContent.vt = VT_UI1 | VT_ARRAY;
							varContent.parray = NULL;
						}
						break;
					case XT_DATETIME:
					case XT_DATE:
					case XT_TIME:
						{
							// DATE
							varContent.vt = VT_DATE;
							varContent.date = 0.0;
						}
						break;
					default:
						{
							// not supported
							SysFreeString(bstrPartType);
							SysFreeString(bstrPartTypeNS);
							goto rpcfault;
						}
					}
				}
			}
			else
			{
				// not found in map
				SysFreeString(bstrPartType);
				SysFreeString(bstrPartTypeNS);
				goto rpcfault;
			}
		}
		else
		{
			// must be a complex type so look for 'base' type
			BSTR bstrBaseType, bstrBaseTypeNS;
			pIWSDLClient->GetComplexBaseType(	bstrPartType,
												bstrPartTypeNS,
												&bstrBaseType,
												&bstrBaseTypeNS,
												&bResult);
			if (bResult == VARIANT_FALSE)
			{
				// not properly defined in WSDL file
				SysFreeString(bstrPartType);
				SysFreeString(bstrPartTypeNS);
				goto rpcfault;
			}
			wstring wstrBaseType(bstrBaseType);
			wstring wstrBaseTypeNS(bstrBaseTypeNS);
			SysFreeString(bstrBaseType);
			SysFreeString(bstrBaseTypeNS);
			if (IsSoapEncType(wstrBaseTypeNS))
			{
				if (wstrBaseType == L"Array")
				{
					// VARIANT array
					varContent.vt = VT_VARIANT | VT_ARRAY;
					varContent.parray = NULL;
				}
				else if (wstrBaseType == L"Struct")
				{
					// struct
					varContent.vt = VT_DISPATCH;
					varContent.pdispVal = NULL;
				}
				else
				{
					// unrecognized SOAP-ENC type
					SysFreeString(bstrPartType);
					SysFreeString(bstrPartTypeNS);
					goto rpcfault;
				}
			}
			else
			{
				// complex type... no base type given... assume struct
				varContent.vt = VT_DISPATCH;
				varContent.pdispVal = NULL;
			}
		}
		// create parameter message element
		wstring wstrNS;
		Msg.AddMethodParam( wstrNS,
							wstring(bstrParamName),
							varContent,
							false,
							true);
		// free parameter name bstr
		SysFreeString(bstrParamName);
		SysFreeString(bstrPartType);
		SysFreeString(bstrPartTypeNS);
	}
	// free method name bstr
	SysFreeString(bstrMethod);
	return true;

rpcfault:
    SysFreeString(bstrParamName);
	SysFreeString(bstrMethod);
	throw new WMSOAPFault(  WMSOAPFault::Receiver,
                            L"",
                            L"",
                            L"Unexpected error handling SOAP GET request.",
                            true);
}

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

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

WMSOAPRequestBodyHandler& WMSOAPReqDeserializer::GetBodyHandler()
{
	return 	m_MsgHandler.GetEnvelopeHandler().GetBodyHandler();
}

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

WMSOAPRequestHandler *WMSOAPReqDeserializer::GetRequestHandler()
{
	return 	m_MsgHandler.GetEnvelopeHandler().GetBodyHandler().GetRequestHandler();
}

WMSOAPParamHandler *WMSOAPReqDeserializer::GetMultiRefElem(WMSOAPParamHandler *pParam)
{
    WMSOAPParamHandler *pTemp;
    if (pParam->GetHref()[0] == wchar_t('#'))
    {
        // fragment identifier found
	    pTemp = m_MsgHandler.GetEnvelopeHandler().GetBodyHandler().GetMultiRefElem(pParam->GetHref().substr(1));
	    if (pTemp == NULL)
	    {
		    pTemp = m_MsgHandler.GetEnvelopeHandler().GetHeaderHandler().GetMultiRefElem(pParam->GetHref().substr(1));
	    }
    }
    else
    {
        // no fragment identifier
	    pTemp = m_MsgHandler.GetEnvelopeHandler().GetBodyHandler().GetMultiRefElem(pParam->GetHref());
	    if (pTemp == NULL)
	    {
		    pTemp = m_MsgHandler.GetEnvelopeHandler().GetHeaderHandler().GetMultiRefElem(pParam->GetHref());
	    }
    }
	return pTemp;
}

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