// SOAPAutomationProxy.cpp: implementation of the WMSOAPAutomationProxy class.
//
//////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2000 - 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.
//
//////////////////////////////////////////////////////////////////////
//
// WMSOAPAutomationProxy class - a proxy for SOAP RPC calls to 
// COM based web services... for use by the 'client'.
// 
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "SOAPAutomationProxy.h"
#include "WMXMLMessage.h"
#include "..\wmsoapmsg\wmsoapmsg.h"

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

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

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

WMSOAPAutomationProxy::WMSOAPAutomationProxy()
{
	m_nHTTPStatus = 0;
    m_bUseProxy = false;
    m_nProxyPort = 0;
}

WMSOAPAutomationProxy::~WMSOAPAutomationProxy()
{
}

int WMSOAPAutomationProxy::GetHTTPStatus()
{
	return m_nHTTPStatus;
}

bool WMSOAPAutomationProxy::InvokeMethod(   const string& strSOAPAction,
                                            bool bSOAPActionRequired,
										    const string& strHost,
										    int nPortNbr,
                                            const string& strHTTPMethod,
										    const string& strSvcURI,
                                            const string& strQueryString,
										    const string& strUserAgent,
										    Iwmsoapmsg2 *pIwmsoapmsg,
                                            int nSOAPVersion)
{
	try
	{
		InvokeSOAPMethod(   strSOAPAction,
                            bSOAPActionRequired,
							strHost,
							nPortNbr,
                            strHTTPMethod,
							strSvcURI,
                            strQueryString,
							strUserAgent,
							pIwmsoapmsg,
                            nSOAPVersion);
	}
	catch(WMSOAPProxyFault *f)
	{
		LogFault(f);
		// consume the exception
		delete f;
		return false;
	}
	return true;
}

bool WMSOAPAutomationProxy::InvokeSOAPMethod(const string& strSOAPAction,
                                             bool bSOAPActionRequired,
											 const string& strHost,
											 int nPortNbr,
                                             const string& strHTTPMethod,
											 const string& strSvcURI,
                                             const string& strQueryString,
											 const string& strUserAgent,
											 Iwmsoapmsg2 *pIwmsoapmsg,
                                             int nSOAPVersion)
{
	_variant_t varBuffer;
    // using POST?
    bool bPost = false;
    if (strHTTPMethod == "POST")
    {
        bPost = true;
	    // get serialized message
	    VARIANT_BOOL bResult;
	    pIwmsoapmsg->GetBuffer(&varBuffer, &bResult);
	    SAFEARRAY *pArray = varBuffer.parray;
	    void *pData;
	    if (	bResult == VARIANT_FALSE ||
			    !(varBuffer.vt & (VT_ARRAY | VT_UI1)) ||
			    pArray == NULL ||
			    SafeArrayGetDim(pArray) != 1 ||
			    FAILED(SafeArrayAccessData(pArray, &pData)))
	    {
		    throw new WMSOAPProxyFault(L"Request Message", L"Could not obtain request message.");
	    }
	    m_strXMLOut.erase();
	    m_strXMLOut.append((char *)pData, pArray->rgsabound[0].cElements);
	    SafeArrayUnaccessData(pArray);
    }
	// build HTTP Header
	char szBuffer[128];
	string strHeaders = "Host: ";
	strHeaders += strHost;
	_snprintf(szBuffer, 127, ":%d\r\n", nPortNbr);
    szBuffer[127] = 0;
	strHeaders += szBuffer;
	strHeaders += "User-Agent: ";
	strHeaders += strUserAgent;
    strHeaders += "\r\n";
    if (bPost == true)
    {
        if (nSOAPVersion == SOAP_VERSION_1_1)
        {
	        strHeaders += "Content-Type: text/xml; charset=\"utf-8\"\r\n";
            if (bSOAPActionRequired == true)
            {
	            strHeaders += "SOAPAction: ";
	            strHeaders += strSOAPAction;
	            strHeaders += "\r\n";
            }
        }
        else
        {
	        strHeaders += "Content-Type: application/soap+xml; charset=\"utf-8\"";
            if (bSOAPActionRequired == true)
            {
                strHeaders += "; action=";
                strHeaders += strSOAPAction;
            }
            strHeaders += "\r\n";
        }
	    _snprintf(szBuffer, 127, "Content-Length: %d\r\n", m_strXMLOut.length());
        szBuffer[127] = 0;
	    strHeaders += szBuffer;
    }
	// make HTTP request
    if (m_bUseProxy == true)
    {
        // modify request URI
        char szBuffer[16];
	    _snprintf(szBuffer, 15, ":%d", nPortNbr);
        szBuffer[15] = 0;
        string strTemp("http://");
        strTemp += strHost;
        strTemp += szBuffer;
        strTemp += strSvcURI;
	    if (m_Socket.SendHTTPRequest(   m_nProxyPort,
                                        strHTTPMethod,
		                                m_strProxyHost,
                                        strTemp,
                                        strQueryString,
                                        strHeaders,
                                        m_strXMLOut) == false)
	    {
		    throw new WMSOAPProxyFault(L"Request Message", L"Could not connect to proxy server.");
	    }
    }
    else
    {
	    if (m_Socket.SendHTTPRequest(   nPortNbr,
                                        strHTTPMethod,
		                                strHost,
                                        strSvcURI,
                                        strQueryString,
                                        strHeaders,
                                        m_strXMLOut) == false)
	    {
		    throw new WMSOAPProxyFault(L"Request Message", L"Could not connect to server.");
	    }
    }
	// get HTTP status code
	m_nHTTPStatus = m_Socket.GetStatus();
    // no content?
    if (m_nHTTPStatus == 204)
	{
		// set "no content" response
		pIwmsoapmsg->SetNoContentResponse(VARIANT_TRUE);
		return true;
	}
    // reduce status code to generic value
    m_nHTTPStatus = (m_nHTTPStatus / 100) * 100;
	switch(m_nHTTPStatus)
	{
		case 400:
		{
            if (nSOAPVersion == SOAP_VERSION_1_1)
            {
    			throw new WMSOAPProxyFault(L"Response Message", L"HTTP Client Error: Bad Request.");
            }
		}
		case 200:
		case 500:
		{
			// is this xml??
            string strContentType;
            if (nSOAPVersion == SOAP_VERSION_1_1)
            {
                strContentType = "text/xml";
            }
            else
            {
                strContentType = "application/soap+xml";
            }
			if (m_Socket.GetHeader("Content-Type").find(strContentType) == string::npos)
			{
				throw new WMSOAPProxyFault(L"Response Message", L"Unexpected MIME type in Response.");
			}
			// store response in message object
			void *pData;
			_variant_t varBuffer;
			SAFEARRAY *pArray = SafeArrayCreateVector(VT_UI1, 0, m_Socket.GetContentLength());
			if (pArray == NULL || FAILED(SafeArrayAccessData(pArray, &pData)))
			{
				if (pArray != NULL)
				{
					SafeArrayDestroy(pArray);
				}
				throw new WMSOAPProxyFault(L"Response Message", L"Unexpected memory allocation error.");
			}
			else
			{
				memcpy(pData, m_Socket.GetContent(), m_Socket.GetContentLength());
				SafeArrayUnaccessData(pArray);
				varBuffer.vt |= (VT_ARRAY | VT_UI1);
				varBuffer.parray = pArray;
			}
			VARIANT_BOOL bResult;
			pIwmsoapmsg->SetBuffer(varBuffer, &bResult);
			if (bResult == VARIANT_FALSE)
			{
				throw new WMSOAPProxyFault(L"Response Message", L"Message buffer initialization error.");
			}
			break;
		}
		default:
		{
			throw new WMSOAPProxyFault(L"Response Message", L"Unexpected HTTP Status Code.");
			break;
		}
	}
	return true;
}

void WMSOAPAutomationProxy::SetHttpProxy(const string& strHost, int nPort)
{
    m_bUseProxy = true;
    m_strProxyHost = strHost;
    m_nProxyPort = nPort;
}
