// SoapDocClient.cpp
//
//////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2002 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.
//
//////////////////////////////////////////////////////////////////////
//
// WMSOAPDocClient class - a class implementing an HTTP client
// for sending SOAP messages using the "document/literal" convention.
// 
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "soapdocclient.h"
#include "charencode.h"
#include "xmlparser.h"
#include "httpwinsocket.h"
#include "soap.h"
#include "..\wmwsdlclient\wmwsdlclient.h"
#include "..\wmwsdlclient\wmwsdlclient_i.c"

WMSOAPDocClient::WMSOAPDocClient(void)
{
    m_pIwmwsdlclient = NULL;
    m_nSOAPVersion = SOAP_VERSION_1_1;
    m_bInitialized = false;
    m_bUseProxy = false;
}

WMSOAPDocClient::~WMSOAPDocClient(void)
{
    if (m_pIwmwsdlclient != NULL)
    {
        m_pIwmwsdlclient->Release();
    }
}

bool WMSOAPDocClient::Initialize(   const wstring& wstrWSDLFileURL,
                                    const wstring& wstrServiceName,
                                    const wstring& wstrPortName,
                                    const wstring& wstrUserAgent)
{
    m_bInitialized = false;
    if (ReadWSDLDocument(wstrWSDLFileURL, wstrServiceName, wstrPortName) == false)
    {
        m_wstrErrorMsg = L"Initialize: Failed to read WSDL document.";
        return false;
    }
    WideStringToUTF8(wstrUserAgent, m_strUserAgent);
    m_bInitialized = true;
    return true;
}

bool WMSOAPDocClient::SetHttpProxy( const string& strProxyHost,
                                    int nProxyPort)
{
    if (m_bInitialized == false)
    {
    	m_wstrErrorMsg = L"SetHttpProxy: Client not initialized yet.";
        return false;
    }
    m_bUseProxy = true;
    m_strProxyHost = strProxyHost;
    m_nProxyPort = nProxyPort;
    return true;
}

bool WMSOAPDocClient::DoOperation(const wstring& wstrOperation)
{
    return DoOperation(wstrOperation, string("POST"), string(""), string(""));
}

bool WMSOAPDocClient::DoOperation(const wstring& wstrOperation,
                                  const string& strHTTPMethod)
{
    return DoOperation(wstrOperation, strHTTPMethod, string(""), string(""));
}

bool WMSOAPDocClient::DoOperation(const wstring& wstrOperation,
                                  const string& strHTTPMethod,
                                  const string& strExtraPath)
{
    return DoOperation(wstrOperation, strHTTPMethod, strExtraPath, string(""));
}

bool WMSOAPDocClient::DoOperation(const wstring& wstrOperation,
                                  const string& strHTTPMethod,
                                  const string& strExtraPath,
                                  const string& strQueryString)
{
    if (m_bInitialized == false)
    {
        m_wstrErrorMsg = L"Request: Client not initialized yet.";
        return false;
    }
    m_strRequestXML.erase();
    m_strResponseXML.erase();
    // using POST?
    bool bPost = false;
    if (strHTTPMethod == "POST")
    {
        bPost = true;
        // serialize request
        if (m_RequestMsg.Serialize(m_strRequestXML, m_nSOAPVersion) == false)
        {
            m_wstrErrorMsg = L"Request: Failed to serialize request message.";
            return false;
        }
    }
    // get SOAPAction value
    string strSOAPAction;
    bool bSARequired = true;
	BSTR bstrSOAPAction;
    BSTR bstrOpName = SysAllocString(wstrOperation.c_str());
    VARIANT_BOOL bResult;
	m_pIwmwsdlclient->GetMethodSOAPAction(bstrOpName, &bstrSOAPAction, &bResult);
    if (bResult == VARIANT_TRUE)
    {
	    WideStringToUTF8(wstring(bstrSOAPAction), strSOAPAction);
    	SysFreeString(bstrSOAPAction);
	}
    if (m_nSOAPVersion == SOAP_VERSION_1_2)
    {
        // is SA required?
        VARIANT_BOOL bSOAPActionRequired;
        m_pIwmwsdlclient->SOAPActionRequired(bstrOpName, &bSOAPActionRequired, &bResult);
        if (bResult == VARIANT_TRUE)
        {
            bSARequired = (bSOAPActionRequired == VARIANT_TRUE ? true : false);
	    }
    }
    SysFreeString(bstrOpName);
	// build HTTP Header
	char szBuffer[128];
	string strHeaders = "Host: ";
	strHeaders += m_strHost;
	_snprintf(szBuffer, 127, ":%d\r\n", m_nPortNbr);
    szBuffer[127] = 0;
	strHeaders += szBuffer;
	strHeaders += "User-Agent: ";
	strHeaders += m_strUserAgent;
    strHeaders += "\r\n";
    if (bPost == true)
    {
        // content-type
        if (m_nSOAPVersion == SOAP_VERSION_1_1)
        {
	        strHeaders += "Content-Type: text/xml\r\n";
            if (bSARequired == true)
            {
	            strHeaders += "SOAPAction: ";
	            strHeaders += strSOAPAction;
	            strHeaders += "\r\n";
            }
        }
        else
        {
	        strHeaders += "Content-Type: application/soap+xml; charset=\"utf-8\"";
            if (bSARequired == true)
            {
                strHeaders += "; action=";
                strHeaders += strSOAPAction;
            }
            strHeaders += "\r\n";
        }
        // content-length
	    _snprintf(szBuffer, 127, "Content-Length: %d\r\n", m_strRequestXML.length());
        szBuffer[127] = 0;
	    strHeaders += szBuffer;
    }
    // instantiate socket
	WMHTTPRequestSock sock;
    // make request
    if (m_bUseProxy == true)
    {
        // modify request URI
        char szBuffer[16];
	    _snprintf(szBuffer, 15, ":%d", m_nPortNbr);
        szBuffer[15] = 0;
        string strTemp("http://");
        strTemp += m_strHost;
        strTemp += szBuffer;
        strTemp += m_strSvcURI;
        strTemp += strExtraPath;
	    if (sock.SendHTTPRequest(   m_nProxyPort,
                                    strHTTPMethod,
		                            m_strProxyHost,
                                    strTemp,
                                    strQueryString,
                                    strHeaders,
                                    m_strRequestXML) == false)
	    {
		    m_wstrErrorMsg = L"Request: Could not connect to server.";
		    return false;
	    }
    }
    else
    {
        // build request URI
        string strTemp(m_strSvcURI);
        strTemp += strExtraPath;
	    if (sock.SendHTTPRequest(   m_nPortNbr,
                                    strHTTPMethod,
		                            m_strHost,
                                    strTemp,
                                    strQueryString,
                                    strHeaders,
                                    m_strRequestXML) == false)
	    {
		    m_wstrErrorMsg = L"Request: Could not connect to server.";
		    return false;
	    }
    }
    m_strResponseXML.append(sock.GetContent(), sock.GetContentLength());
	// get HTTP status code
	m_nHTTPStatus = sock.GetStatus();
    // no content?
    if (m_nHTTPStatus == 204)
	{
		return true;
	}
    // reduce status code to generic value
    m_nHTTPStatus = (m_nHTTPStatus / 100) * 100;
	switch(m_nHTTPStatus)
	{
		case 400:
		{
            if (m_nSOAPVersion == SOAP_VERSION_1_1)
            {
			    m_wstrErrorMsg = L"Response: Unexpected HTTP Status received.";
			    break;
            }
		}
		case 200:
		case 500:
		{
			// is this SOAP??
			if (sock.GetHeader("Content-Type").find("text/xml") != string::npos)
            {
                if (m_nSOAPVersion != SOAP_VERSION_1_1)
                {
			        m_wstrErrorMsg = L"Response: Unexpected Media Type.";
			        return false;
                }
            }
            else if (sock.GetHeader("Content-Type").find("application/soap+xml") != string::npos)
			{
                if (m_nSOAPVersion != SOAP_VERSION_1_2)
                {
			        m_wstrErrorMsg = L"Response: Unexpected Media Type.";
			        return false;
                }
            }
            else
            {
			    m_wstrErrorMsg = L"Response: Unrecogized Media Type.";
			    return false;
            }
            if (m_ResponseMsg.Deserialize(m_strResponseXML) == false)
            {
			    m_wstrErrorMsg = L"Response: Failed to deserialize message.";
			    return false;
            }
            if (m_ResponseMsg.GetSOAPVersion() != m_nSOAPVersion)
            {
			    m_wstrErrorMsg = L"Response: Unexpected SOAP Version.";
			    return false;
            }
            return true;
		}
		default:
		{
			m_wstrErrorMsg = L"Response: Unexpected HTTP Status received.";
			break;
		}
	}
    return false;
}

bool WMSOAPDocClient::ReadWSDLDocument( const wstring& wstrWSDLFileURL,
                                        const wstring& wstrServiceName,
                                        const wstring& wstrPortName)
{
    if (m_pIwmwsdlclient != NULL)
    {
        m_pIwmwsdlclient->Release();
    }
	// 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(wstrServiceName.c_str());
	BSTR bstrPortName = SysAllocString(wstrPortName.c_str());
	BSTR bstrWSDLFileURL = SysAllocString(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;
	}
	// get WSDL service info items
	BSTR bstrHost, bstrURI;
	pIwmwsdlclient->GetSOAPAddress(&bstrHost, &bstrURI, &m_nPortNbr, &bResult);
    if (bResult == VARIANT_FALSE)
    {
        pIwmwsdlclient->Release();
        return false;
    }

	WideStringToUTF8(wstring(bstrHost), m_strHost);
	WideStringToUTF8(wstring(bstrURI), m_strSvcURI);
	SysFreeString(bstrHost);
	SysFreeString(bstrURI);
    // cache pointer
    m_pIwmwsdlclient = pIwmwsdlclient;
    // get SOAP version
    m_nSOAPVersion = SOAP_VERSION_1_1;
    BSTR bstrBindingVersion = NULL;
    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;
        }
    }
    return true;
}

