// WMWSDLParser.cpp: implementation of the WMWSDLParser 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.
//
//////////////////////////////////////////////////////////////////////
//
// WMWSDLParser class - implementation of WSDL parser
//  
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "WMWSDLParser.h"
#include "WMSOAPBindingHandlers.h"
#include "WSDLEventHandler.h"
#include "HTTPWinsocket.h"
#include "charencode.h"

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

void LogFault(WMWebServicesDescFault *f, const string& strName)
{
	HANDLE  hEventSource;
	LPTSTR  lpszStrings[1];

	USES_CONVERSION;
	char buf[128];
    _snprintf(buf, 127, "Doc: <%s> %s", strName.c_str(), OLE2A(f->GetFaultString()));
    buf[127] = 0;
	lpszStrings[0] = buf;
	hEventSource = RegisterEventSource(NULL, "White Mesa WSDL Parser");
	if (hEventSource != NULL)
	{
		ReportEvent(hEventSource, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, (LPCTSTR*) &lpszStrings[0], NULL);
		DeregisterEventSource(hEventSource);
	}
}

void LogParseError(int nLineNbr, const string& strName)
{
	HANDLE  hEventSource;
	LPTSTR  lpszStrings[1];

	USES_CONVERSION;
	char buf[128];
    _snprintf(buf, 127, "Doc: <%s> Parse error on line %d", strName.c_str(), nLineNbr);
    buf[127] = 0;
	lpszStrings[0] = buf;
	hEventSource = RegisterEventSource(NULL, "White Mesa WSDL Parser");
	if (hEventSource != NULL)
	{
		ReportEvent(hEventSource, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, (LPCTSTR*) &lpszStrings[0], NULL);
		DeregisterEventSource(hEventSource);
	}
}

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

WMWSDLParser::WMWSDLParser()
{
	m_pWSDLDesc = NULL;
}

WMWSDLParser::~WMWSDLParser()
{
	if (m_pWSDLDesc != NULL)
		delete m_pWSDLDesc;
}

bool WMWSDLParser::LoadAndParseWSDLFile(const string& strWSDLDescFile)
{
	// open the WDSL file
    FILE* pFile = fopen(strWSDLDescFile.c_str(), "rb");
    if(pFile == NULL)
	{
        return false;
	}
    // Compute the size of the file
    fseek(pFile, 0, SEEK_END);
    int nSize = ftell(pFile);
    // reset pointer to the beginning
    fseek(pFile, 0, SEEK_SET);

    char* pszBuffer = (char *)malloc(nSize + 1);
    fread(pszBuffer, sizeof(char), nSize, pFile);
    // null terminate
    pszBuffer[nSize] = 0;
    // Close the file
    fclose(pFile), pFile = 0;

	// create WSDL description object
	m_pWSDLDesc = new WMSOAPWSDLDesc;
	if(m_pWSDLDesc == NULL)
	{
		return false;
	}
	// propagate current base path into description object
	m_pWSDLDesc->SetDocumentBase(m_strDocBase);
	// set up for parsing
	WMXMLParser Parser;
	WMWSDLParseEventHandler eventHandler;
	eventHandler.SetTopHandler(m_pWSDLDesc);
	bool bRtn = true;
	// parse WSDL file
	try
	{
		Parser.ParseXML(pszBuffer, nSize, &eventHandler);
	}
	catch(int e)
	{
		LogParseError(e, strWSDLDescFile);
		delete m_pWSDLDesc;
		m_pWSDLDesc = NULL;
		bRtn = false;
	}
	catch(WMWebServicesDescFault *f)
	{
		LogFault(f, strWSDLDescFile);
		delete f;
		delete m_pWSDLDesc;
		m_pWSDLDesc = NULL;
		bRtn = false;
	}
	free(pszBuffer);
	return bRtn;
}

bool WMWSDLParser::GetAndParseWSDLFile(const string& strHost,
									   const string& strWSDLDescURI,
									   const string& strUserAgent,
									   const string& strOtherHeaders,
									   int nPort,
									   DWORD dwTimeout)
{
	WMHTTPRequestSock sock(nPort, dwTimeout);
	// build HTTP Header
	char szBuffer[128];
	string strHeaders = "Host: ";
    // accomodate proxied requests
    string::size_type pos;
	if ((pos = strWSDLDescURI.find("http://")) != string::npos)
	{
		string strTemp = strWSDLDescURI.substr(pos + 7);
		pos = strTemp.find('/');
		strHeaders += strTemp.substr(0, pos);
        strHeaders += "\r\n";
    }
    else
    {
	    strHeaders += strHost;
	    _snprintf(szBuffer, 127, ":%d\r\n", nPort);
        szBuffer[127] = 0;
	    strHeaders += szBuffer;
    }
	strHeaders += "User-Agent: ";
	strHeaders += strUserAgent;
	strHeaders += "\r\n";
	strHeaders += strOtherHeaders;
	string strBuffer, strQueryString, strRequestURI;
    if ((pos = strWSDLDescURI.find('?')) != string::npos)
    {
        strRequestURI = strWSDLDescURI.substr(0, pos);
        strQueryString = strWSDLDescURI.substr(pos + 1);
    }
    else
    {
        strRequestURI = strWSDLDescURI;
    }
	bool bRet = sock.SendHTTPRequest(   nPort,
                                        string("GET"),
		                                strHost,
                                        strRequestURI,
                                        strQueryString,
                                        strHeaders,
                                        strBuffer);
	if (bRet == false)
	{
		return false;
	}
	// get HTTP status code
	int nHTTPStatus = sock.GetStatus();
    // redirected?
    if (nHTTPStatus == 302 || nHTTPStatus == 301)
    {
        // get location
        string strLocation = sock.GetHeader("LOCATION");
        string::size_type pos = 0;
        while (pos < strLocation.length() && isspace(strLocation[pos]))
        {
            pos++;
        }
        strLocation = strLocation.substr(pos);
        string strNewHost, strNewRequestURI, strNewQueryString;
        int nNewPort = 80;
        // extract new host
	    if ((pos = strLocation.find("http://")) == string::npos)
	    {
            // assume a (nonconformant wrt HTTP spec) relative URI
            if (strLocation[0] != '/')
            {
                return false;
            }
            strNewHost = strHost;
            nNewPort = nPort;
        }
        else
        {
		    string strTemp = strLocation.substr(pos + 7);
		    pos = strTemp.find('/');
		    strLocation = strTemp.substr(pos);
		    strTemp = strTemp.substr(0, pos);
		    if ((pos = strTemp.find(':')) != string::npos)
		    {
			    // port specified explicitly
			    nNewPort = atoi(strTemp.substr(pos + 1).c_str());
			    strNewHost = strTemp.substr(0, pos);
		    }
		    else
		    {
			    strNewHost = strTemp;
		    }
        }
        if ((pos = strLocation.find('?')) != string::npos)
        {
            strNewRequestURI = strLocation.substr(0, pos);
            strNewQueryString = strLocation.substr(pos + 1);
        }
        else
        {
            strNewRequestURI = strLocation;
        }
        // send request
        strBuffer.erase();
        sock.Clear();
	    bRet = sock.SendHTTPRequest(nNewPort,
                                    string("GET"),
		                            strNewHost,
                                    strNewRequestURI,
                                    strNewQueryString,
                                    strHeaders,
                                    strBuffer);
        nHTTPStatus = sock.GetStatus();
	    if (bRet == false || nHTTPStatus != 200)
	    {
		    return false;
	    }
    }
	else if(nHTTPStatus != 200)
	{
		return false;
	}
	// create WSDL description object
	m_pWSDLDesc = new WMSOAPWSDLDesc();
	if(m_pWSDLDesc == NULL)
	{
		return false;
	}
	// propagate current base path into description object
	m_pWSDLDesc->SetDocumentBase(m_strDocBase);
	// set up for parsing
	WMXMLParser Parser;
	WMWSDLParseEventHandler eventHandler;
	eventHandler.SetTopHandler(m_pWSDLDesc);
	// parse WSDL file
	try
	{
		Parser.ParseXML(sock.GetContent(), sock.GetContentLength(), &eventHandler);
	}
	catch(int e)
	{
		LogParseError(e, strWSDLDescURI);
		delete m_pWSDLDesc;
		m_pWSDLDesc = NULL;
		return false;
	}
	catch(WMWebServicesDescFault *f)
	{
		LogFault(f, strWSDLDescURI);
		delete f;
		delete m_pWSDLDesc;
		m_pWSDLDesc = NULL;
		return false;
	}
	return true;
}

