// WMSOAPClient.cpp: implementation of the WMSOAPClient class.
//
//////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2001 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.
//
//////////////////////////////////////////////////////////////////////
//
// WMSOAPClient class - a SOAP RPC processor for the 'client' side.
// 
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "WMSOAPClient.h"
#include "WSDLEventHandler.h"
#include "WMSOAPWSDLClient.h"
#include "SOAPAutomationProxy.h"
#include <time.h>

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

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

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

WMSOAPClient::WMSOAPClient()
{
	m_pIwmsoapMsg = NULL;
	m_pIwmwsdlclient = NULL;
	m_bUseExplicitTyping = false;
    m_bUseSOAP12NodeType = false;
    m_nSOAPVersion = 11;
    m_bUseProxy = false;
    m_nProxyPort = 0;
}

WMSOAPClient::~WMSOAPClient()
{
	Cleanup();
}

void WMSOAPClient::Cleanup()
{
	ReleaseMessage();
	ReleaseWSDLClient();
}

void WMSOAPClient::ReleaseMessage()
{
	if (m_pIwmsoapMsg != NULL)
	{
		m_pIwmsoapMsg->Release();
	}
	m_pIwmsoapMsg = NULL;
}

void WMSOAPClient::ReleaseWSDLClient()
{
	if (m_pIwmwsdlclient != NULL)
	{
		m_pIwmwsdlclient->Release();
	}
	m_pIwmwsdlclient = NULL;
}

bool WMSOAPClient::NewMessage()
{
	if (m_pIwmwsdlclient == NULL)
	{
		return false;
	}
	// release any existing msg object
	ReleaseMessage();
	// get WSDL client IDispatch ptr to pass in
	IDispatch *pIDispatch;
	HRESULT res = m_pIwmwsdlclient->QueryInterface(IID_IDispatch, (void **)&pIDispatch);
	if (res != S_OK)
	{
		return false;
	}
	// instantiate SOAP Msg object
	Iwmsoapmsg2 *pIwmsoapMsg;
	res = CoCreateInstance( CLSID_wmsoapmsg2,
							NULL, CLSCTX_SERVER,
							IID_Iwmsoapmsg2,
							(void **) &pIwmsoapMsg);
	if (FAILED(res))
	{
		pIDispatch->Release();
		return false;
	}
	// set up Result
	VARIANT_BOOL bResult;
	// set up BSTR's
	BSTR bstrSvcName = SysAllocString(m_wstrServiceName.c_str());
	BSTR bstrPortName = SysAllocString(m_wstrPortName.c_str());
	BSTR bstrNodeURI = SysAllocString(m_wstrNodeURI.c_str());
	BSTR bstrSOAPAction = SysAllocString(L"");
	BSTR bstrNextHopURL = SysAllocString(L"");
	VARIANT_BOOL bUseExplTyping = m_bUseExplicitTyping == true ? VARIANT_TRUE : VARIANT_FALSE;
	VARIANT_BOOL bUseNodeType = m_bUseSOAP12NodeType == true ? VARIANT_TRUE : VARIANT_FALSE;
	// initialize SOAP message object
	pIwmsoapMsg->Initialize(bstrSvcName,
							bstrPortName,
							bstrNodeURI,
							bstrSOAPAction,
							bstrNextHopURL,
							bUseExplTyping,
                            bUseNodeType,
							pIDispatch,
							&bResult);
	// free storage
	SysFreeString(bstrSvcName);
	SysFreeString(bstrPortName);
	SysFreeString(bstrNodeURI);
	SysFreeString(bstrSOAPAction);
	SysFreeString(bstrNextHopURL);
    // set SOAP Version
    pIwmsoapMsg->SetSOAPVersion(m_nSOAPVersion);
	// done with disp ptr
	pIDispatch->Release();
	// any luck?
	if (bResult == VARIANT_FALSE)
	{
		pIwmsoapMsg->Release();
		return false;
	}
	// cache SOAP message object ptr
	m_pIwmsoapMsg = pIwmsoapMsg;
	return true;
}

bool WMSOAPClient::Initialize(   const string& strWSDLFileURL,
								 const string& strServiceName,
								 const string& strPortName,
								 const string& strNodeURI,
								 const string& strUserAgent,
								 list<wstring>& listHeaderProcProgIDs,
								 bool bUseExplicitTyping,
                                 int nSOAPVersion)
{
	// make Service and Port names wide strings
	UTF8ToWideString(strServiceName, m_wstrServiceName);
	UTF8ToWideString(strPortName, m_wstrPortName);

	// make Node URI and WSDL file URL wide strings
	UTF8ToWideString(strNodeURI, m_wstrNodeURI);
	UTF8ToWideString(strWSDLFileURL, m_wstrWSDLFileURL);

    // using proxy?
    if (m_bUseProxy == true && m_wstrWSDLFileURL.find(L"http://") != wstring::npos)
    {
        wstring wstrOrigURL = m_wstrWSDLFileURL;
        // create munged request URI
        char szBuffer[16];
        _snprintf(szBuffer, 15, ":%d/", m_nProxyPort);
        szBuffer[15] = 0;
        string strTemp("http://");
        strTemp += m_strProxyHost;
        strTemp += szBuffer;
        wstring wstrTemp;
        UTF8ToWideString(strTemp, wstrTemp);
        m_wstrWSDLFileURL = wstrTemp;
        m_wstrWSDLFileURL += wstrOrigURL;
    }

	// HTTP request info
	m_strUserAgent = strUserAgent;

	// instantiate WSDL client object
	Iwmwsdlclient2 *pIwmwsdlclient;
	HRESULT res = CoCreateInstance(CLSID_wmwsdlclient2,
									NULL, CLSCTX_SERVER,
									IID_Iwmwsdlclient2,
									(void **) &pIwmwsdlclient);
	if (FAILED(res))
	{
		return false;
	}
	BSTR bstrSvcName = SysAllocString(m_wstrServiceName.c_str());
	BSTR bstrPortName = SysAllocString(m_wstrPortName.c_str());
	BSTR bstrWSDLFileURL = SysAllocString(m_wstrWSDLFileURL.c_str());
	VARIANT_BOOL bResult;
	// try to load and parse WSDL file
	pIwmwsdlclient->ReadServiceDesc(bstrSvcName,
									bstrPortName,
									bstrWSDLFileURL,
									&bResult);
	SysFreeString(bstrSvcName);
	SysFreeString(bstrPortName);
	SysFreeString(bstrWSDLFileURL);
	// success?
	if (bResult == VARIANT_FALSE)
	{
		pIwmwsdlclient->Release();
		return false;
	}
	// cache WSDL client interface ptr
	m_pIwmwsdlclient = pIwmwsdlclient;

	// get WSDL service info items
	BSTR bstrHost, bstrURI;
	m_pIwmwsdlclient->GetSOAPAddress(&bstrHost, &bstrURI, &m_nPortNbr, &bResult);
	WideStringToUTF8(wstring(bstrHost), m_strHost);
	WideStringToUTF8(wstring(bstrURI), m_strSvcURI);
	SysFreeString(bstrHost);
	SysFreeString(bstrURI);
    if (nSOAPVersion == 0)
    {
        m_nSOAPVersion = SOAP_VERSION_1_1;
        BSTR bstrBindingVersion = NULL;
    	m_pIwmwsdlclient->GetSOAPBindingVersion(&bstrBindingVersion, &bResult);
        if (bResult == VARIANT_TRUE)
        {
            wstring wstrVersionURI(bstrBindingVersion);
            SysFreeString(bstrBindingVersion);
            if (wstrVersionURI == L"http://schemas.xmlsoap.org/wsdl/soap12/")
            {
                m_nSOAPVersion = SOAP_VERSION_1_2;
            }
        }
    }
    else
    {
        m_nSOAPVersion = nSOAPVersion;
    }
    m_listHeaderProcProgIDs = listHeaderProcProgIDs;
	m_bProcessHeader = (m_listHeaderProcProgIDs.empty() == false ? true : false);
	m_bUseExplicitTyping = bUseExplicitTyping;
	// create new message object
	return NewMessage();
}

bool WMSOAPClient::Initialize(   const string& strWSDLFileURL,
								 const string& strServiceName,
								 const string& strPortName,
								 const string& strNodeURI,
								 const string& strUserAgent,
								 list<wstring>& listHeaderProcProgIDs,
								 bool bUseExplicitTyping,
                                 const string& strProxyHost,
                                 int nProxyPort,
                                 int nSOAPVersion)
{
    m_bUseProxy = true;
    m_strProxyHost = strProxyHost;
    m_nProxyPort = nProxyPort;
    m_AutoProxy.SetHttpProxy(strProxyHost, nProxyPort);
    return Initialize(  strWSDLFileURL,
						strServiceName,
						strPortName,
						strNodeURI,
						strUserAgent,
						listHeaderProcProgIDs,
						bUseExplicitTyping,
                        nSOAPVersion);
}

bool WMSOAPClient::InvokeSOAPMethod(const wstring& wstrMethod,
									wstring *arrNames,
									_variant_t *arrArgs,
									int nNumArgs,
									_variant_t& varReturn)
{
    return InvokeSOAPMethod(string("POST"),
                            string(""),
                            string(""),
                            wstrMethod,
                            arrNames,
                            arrArgs,
                            nNumArgs,
                            varReturn);
}

bool WMSOAPClient::InvokeSOAPMethod(const string& strHTTPMethod,
                                    const wstring& wstrMethod,
									wstring *arrNames,
									_variant_t *arrArgs,
									int nNumArgs,
									_variant_t& varReturn)
{
    return InvokeSOAPMethod(strHTTPMethod,
                            string(""),
                            string(""),
                            wstrMethod,
                            arrNames,
                            arrArgs,
                            nNumArgs,
                            varReturn);
}

bool WMSOAPClient::InvokeSOAPMethod(const string& strHTTPMethod,
                                    const string& strExtraPath,
                                    const wstring& wstrMethod,
									wstring *arrNames,
									_variant_t *arrArgs,
									int nNumArgs,
									_variant_t& varReturn)
{
    return InvokeSOAPMethod(strHTTPMethod,
                            strExtraPath,
                            string(""),
                            wstrMethod,
                            arrNames,
                            arrArgs,
                            nNumArgs,
                            varReturn);
}

bool WMSOAPClient::InvokeSOAPMethod(const string& strHTTPMethod,
                                    const string& strExtraPath,
                                    const string& strQueryString,
                                    const wstring& wstrMethod,
									wstring *arrNames,
									_variant_t *arrArgs,
									int nNumArgs,
									_variant_t& varReturn)
{
	if (m_pIwmsoapMsg == NULL || m_pIwmwsdlclient == NULL)
	{
		return false;
	}
    if (strHTTPMethod != "POST" && strHTTPMethod != "GET")
    {
        return false;
    }
	VARIANT_BOOL bResult;
	BSTR bstrMethodName = SysAllocString(wstrMethod.c_str());
	// check binding style... must be 'rpc'
	BSTR bstrBindingStyle;
	m_pIwmwsdlclient->GetOperationBindingStyle(bstrMethodName, &bstrBindingStyle, &bResult);
	if (bResult == VARIANT_FALSE)
	{
		SysFreeString(bstrMethodName);
		return false;
	}
	if (wstring(L"rpc") != bstrBindingStyle)
	{
		SysFreeString(bstrMethodName);
		SysFreeString(bstrBindingStyle);
		return false;
	}
	SysFreeString(bstrBindingStyle);
    // is SOAPAction required?
    bool bSOAPActionRequired = true;
    if (m_nSOAPVersion > SOAP_VERSION_1_1)
    {
        VARIANT_BOOL bIsRequired;
   	    m_pIwmwsdlclient->SOAPActionRequired(bstrMethodName, &bIsRequired, &bResult);
        if (bResult == VARIANT_TRUE)
        {
            bSOAPActionRequired = (bIsRequired == VARIANT_TRUE ? true : false);
        }
    }
    string strSOAPAction;
    if (bSOAPActionRequired == true)
    {
	    // get SOAPAction from Message object
	    BSTR bstrSOAPAction;
	    m_pIwmwsdlclient->GetMethodSOAPAction(bstrMethodName, &bstrSOAPAction, &bResult);
	    if (bResult == VARIANT_FALSE)
	    {
		    // no method/SOAPAction match
		    SysFreeString(bstrMethodName);
		    return false;
	    }
	    WideStringToUTF8(wstring(bstrSOAPAction), strSOAPAction);
	    SysFreeString(bstrSOAPAction);
    }
	// set method name
	m_pIwmsoapMsg->SetMethodName(bstrMethodName);
	// set method NS in message object
	BSTR bstrMethodNS;
	m_pIwmwsdlclient->GetMethodNamespaceURI(bstrMethodName, &bstrMethodNS, &bResult);
	if (bResult == VARIANT_FALSE)
	{
		// no method NS found
		SysFreeString(bstrMethodName);
		return false;
	}
	m_pIwmsoapMsg->SetMethodNS(bstrMethodNS);
	SysFreeString(bstrMethodNS);
    // using POST?
    bool bPost = false;
    if (strHTTPMethod == "POST")
    {
        bPost = true;
	    // set up parameters
	    for (int i = 0; i < nNumArgs; i++)
	    {
		    // process only for the [in] or [in, out] parameters
		    BSTR bstrParamName = SysAllocString(arrNames[i].c_str());
		    m_pIwmwsdlclient->IsInputMsgPart(bstrMethodName, bstrParamName, &bResult);
		    if (bResult == VARIANT_FALSE)
		    {
			    SysFreeString(bstrParamName);
			    continue;
		    }
		    VARIANT_BOOL bIsOutParam;
		    m_pIwmwsdlclient->IsOutputMsgPart(bstrMethodName, bstrParamName, &bIsOutParam);
		    BSTR bstrNS = SysAllocString(L"");
		    // add [in] or [in, out] parameter to message
		    m_pIwmsoapMsg->AddMethodParam(  bstrNS,
										    bstrParamName,
										    arrArgs[i],
										    VARIANT_TRUE,
										    bIsOutParam,
										    &bResult);
		    SysFreeString(bstrNS);
		    SysFreeString(bstrParamName);
		    if (bResult == VARIANT_FALSE)
		    {
			    // failed to add param
			    SysFreeString(bstrMethodName);
			    return false;
		    }
	    }
    }
	SysFreeString(bstrMethodName);
	// request header processing
	bool bFault = false;
	if (m_bProcessHeader == true)
	{
		// initialize header processor dispatcher
		if (m_HeaderDispatcher.Initialize(m_listHeaderProcProgIDs, m_pIwmsoapMsg) == false)
		{
			// failure... results in SOAP:Fault
			bFault = true;
		}
		else
		{
            if (bPost == true)
            {
			    // call request header processors
			    m_HeaderDispatcher.ProcessHeader(m_pIwmsoapMsg);
			    // SOAP Fault?
			    m_pIwmsoapMsg->IsSOAPFault(&bResult);
			    bFault = (bResult == VARIANT_TRUE ? true : false);
            }
		}
	}
	if (bFault == true)
	{
		return false;
	}
    if (bPost == true)
    {
	    // serialize request message
	    m_pIwmsoapMsg->Serialize(&bResult);
	    if (bResult == VARIANT_FALSE)
	    {
		    // serialization failed
		    return false;
	    }
    }
    // append extra path to svc URI
    string strTemp(m_strSvcURI);
    strTemp += strExtraPath;
	// call method dispatcher
	if (m_AutoProxy.InvokeMethod(strSOAPAction,
                                 bSOAPActionRequired,
								 m_strHost,
								 m_nPortNbr,
                                 strHTTPMethod,
								 strTemp,
                                 strQueryString,
								 m_strUserAgent,
								 m_pIwmsoapMsg,
                                 m_nSOAPVersion) == false)
	{
		// method invocation failed
		return false;
	}
	// now deserialize response message
    if (bPost == true)
    {
	    m_pIwmsoapMsg->ClearMethodParams();
	    m_pIwmsoapMsg->ClearHeaderElems();
    }
	m_pIwmsoapMsg->SetRequestPhase(VARIANT_FALSE);
	m_pIwmsoapMsg->Deserialize(&bResult);
	if (bResult == VARIANT_FALSE)
	{
		// deserialization failed
		return false;
	}
	// process headers
	if (m_bProcessHeader == true)
	{
		// call response header processors
		m_HeaderDispatcher.ProcessHeader(m_pIwmsoapMsg);
		// release header proc instances
		m_HeaderDispatcher.Cleanup();
		// check for SOAP Fault
		m_pIwmsoapMsg->IsSOAPFault(&bResult);
		if (bResult == VARIANT_TRUE)
		{
			return false;
		}
	}
	// process RPC response
	m_pIwmsoapMsg->ProcessRPC(&bResult);
	if (bResult == VARIANT_FALSE)
	{
		// trouble
		return false;
	}
	// if no fault (HTTP status == 200) then process response body elems
	if (m_AutoProxy.GetHTTPStatus() == 200)
	{
		// iterate over [out] parameters
		BSTR bstrNamespaceURI, bstrName;
		VARIANT_BOOL bIsInParam, bIsOutParam;
		_variant_t varContent;
		long lPosition;
		VARIANT_BOOL bResult;

		for (m_pIwmsoapMsg->GetFirstMethodParam(&bstrNamespaceURI,
												&bstrName,
												&varContent,
												&bIsInParam,
												&bIsOutParam,
												&lPosition,
												&bResult);

			 bResult == VARIANT_TRUE;
			 m_pIwmsoapMsg->GetNextMethodParam( &bstrNamespaceURI,
												&bstrName,
												&varContent,
												&bIsInParam,
												&bIsOutParam,
												&lPosition,
												&bResult)
		)
		{
			// search for arg by name
			for (int i = 0; i < nNumArgs; i++)
			{
				if (arrNames[i] == bstrName)
				{
					arrArgs[i] = varContent;
				}
			}
			SysFreeString(bstrNamespaceURI);
			SysFreeString(bstrName);
			varContent.Clear();
		}
		m_pIwmsoapMsg->GetReturnValue(&varReturn);
	}
	return true;
}

bool WMSOAPClient::IsSOAPFault()
{
	if (m_pIwmsoapMsg != NULL)
	{
		VARIANT_BOOL bResult;
		m_pIwmsoapMsg->IsSOAPFault(&bResult);
		return (bResult == VARIANT_TRUE ? true : false);
	}
	return false;
}

void WMSOAPClient::ClearHeaders()
{
	if (m_pIwmsoapMsg != NULL)
	{
		m_pIwmsoapMsg->ClearHeaderElems();
	}
}

bool WMSOAPClient::SetRequestHeaderEntry(	const wstring& wstrNS,
											const wstring& wstrActor,
											const wstring& wstrName,
											_variant_t& varValue,
											bool bMustUnderstand)
{
	if (m_pIwmsoapMsg != NULL)
	{
		VARIANT_BOOL bResult;
		BSTR bstrNamespaceURI = SysAllocString(wstrNS.c_str());
		BSTR bstrName = SysAllocString(wstrName.c_str());
		BSTR bstrActor = SysAllocString(wstrActor.c_str());
		VARIANT_BOOL bMU = (bMustUnderstand == true ? VARIANT_TRUE : VARIANT_FALSE);

		m_pIwmsoapMsg->AddHeaderElem(   bstrNamespaceURI,
										bstrName,
										varValue,
										bMU,
										bstrActor,
										&bResult);
		SysFreeString(bstrNamespaceURI);
		SysFreeString(bstrName);
		SysFreeString(bstrActor);
		return (bResult == VARIANT_TRUE ? true : false);
	}
	return false;
}

bool WMSOAPClient::GetResponseHeaderEntry(  const wstring& wstrNS,
											const wstring& wstrName,
											_variant_t& varContent)
{
	if (m_pIwmsoapMsg != NULL)
	{
		VARIANT_BOOL bResult;
		BSTR bstrNamespaceURI = SysAllocString(wstrNS.c_str());
		BSTR bstrName = SysAllocString(wstrName.c_str());
		m_pIwmsoapMsg->GetHeaderElem(   bstrNamespaceURI,
										bstrName,
										&varContent,
										&bResult);
		SysFreeString(bstrNamespaceURI);
		SysFreeString(bstrName);
		return (bResult == VARIANT_TRUE ? true : false);
	}
	return false;
}

bool WMSOAPClient::GetSOAPFaultCode(wstring& wstrFaultCode)
{
	if (m_pIwmsoapMsg != NULL && IsSOAPFault() == true)
	{
		BSTR bstrFaultCode;
		if (SUCCEEDED(m_pIwmsoapMsg->GetSOAPFaultCode(&bstrFaultCode)))
		{
			wstrFaultCode = bstrFaultCode;
			SysFreeString(bstrFaultCode);
			return true;
		}
	}
	return false;
}

bool WMSOAPClient::GetSOAPFaultCodeNS(wstring& wstrFaultCodeNS)
{
	if (m_pIwmsoapMsg != NULL && IsSOAPFault() == true)
	{
		BSTR bstrFaultCodeNS;
		if (SUCCEEDED(m_pIwmsoapMsg->GetSOAPFaultCodeNS(&bstrFaultCodeNS)))
		{
			wstrFaultCodeNS = bstrFaultCodeNS;
			SysFreeString(bstrFaultCodeNS);
			return true;
		}
	}
	return false;
}

bool WMSOAPClient::GetSOAPFaultString(wstring& wstrFaultString)
{
	if (m_pIwmsoapMsg != NULL && IsSOAPFault() == true)
	{
		BSTR bstrFaultString;
		if (SUCCEEDED(m_pIwmsoapMsg->GetSOAPFaultString(&bstrFaultString)))
		{
			wstrFaultString = bstrFaultString;
			SysFreeString(bstrFaultString);
			return true;
		}
	}
	return false;
}

bool WMSOAPClient::GetSOAPFaultNode(wstring& wstrFaultNode)
{
	if (m_pIwmsoapMsg != NULL && IsSOAPFault() == true)
	{
		BSTR bstrFaultNode;
		if (SUCCEEDED(m_pIwmsoapMsg->GetSOAPFaultNode(&bstrFaultNode)))
		{
			wstrFaultNode = bstrFaultNode;
			SysFreeString(bstrFaultNode);
			return true;
		}
	}
	return false;
}

bool WMSOAPClient::GetFaultDetailEntry( const wstring& wstrNS,
										const wstring& wstrName,
										_variant_t& varContent)
{
	if (m_pIwmsoapMsg != NULL && IsSOAPFault() == true)
	{
		VARIANT_BOOL bResult;
		BSTR bstrNamespaceURI = SysAllocString(wstrNS.c_str());
		BSTR bstrName = SysAllocString(wstrName.c_str());
		m_pIwmsoapMsg->GetFaultDetailElem(  bstrNamespaceURI,
											bstrName,
											&varContent,
											&bResult);
		SysFreeString(bstrNamespaceURI);
		SysFreeString(bstrName);
		return (bResult == VARIANT_TRUE ? true : false);
	}
	return false;
}
