// XMLDocument.cpp: implementation of the WMXMLDocument class.
//
//////////////////////////////////////////////////////////////////////
//
// 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.
//
//////////////////////////////////////////////////////////////////////
//
// WMXMLDocument class - implementation of simple XML document reader/writer
//  
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "XMLDocument.h"
#include "HTTPWinsocket.h"
#include "charencode.h"

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

static char *g_pszUserAgent = "White Mesa XML DocReader/1.1";
static char *g_pszXMLDecl = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n";

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

WMXMLDocument::WMXMLDocument()
{
    m_nHTTPTimeout = 30000;
    m_strUserAgent = g_pszUserAgent;
}

WMXMLDocument::~WMXMLDocument()
{
}

bool WMXMLDocument::Write(const string& strURL)
{
    string strFilename;
    string::size_type pos;
	if ((pos = strURL.find("file://")) != string::npos)
	{
		strFilename = strURL.substr(pos + 7);
    }
    else
    {
        strFilename = strURL;
    }
	// open the XML file
    FILE* pFile = fopen(strFilename.c_str(), "wb");
    if(pFile == NULL)
	{
        return false;
	}
    string strTemp;
    SerializeXML(strTemp, true);
    fwrite(g_pszXMLDecl, sizeof(char), strlen(g_pszXMLDecl), pFile);
    fwrite(strTemp.c_str(), sizeof(char), strTemp.length(), pFile);
    // Close the file
    fclose(pFile);
    return true;
}

bool WMXMLDocument::Read(const string& strURL)
{
	// http:// or file:// ?
    string::size_type pos;
	if ((pos = strURL.find("http://")) != string::npos)
	{
		string strHost;
		int nPort = 80;
		string strTemp = strURL.substr(pos + 7);
		pos = strTemp.find('/');
		string strReqURI = strTemp.substr(pos);
		strTemp = strTemp.substr(0, pos);
		if ((pos = strTemp.find(':')) != string::npos)
		{
			// port specified explicitly
			nPort = atoi(strTemp.substr(pos + 1).c_str());
			strHost = strTemp.substr(0, pos);
		}
		else
		{
			strHost = strTemp;
		}
		if (GetAndParseXMLDoc(  strHost,
								strReqURI,
								nPort,
								m_nHTTPTimeout) == false)
		{
			return false;
		}
	}
	else
	{
        string strFilename;
	    if ((pos = strURL.find("file://")) != string::npos)
	    {
		    strFilename = strURL.substr(pos + 7);
        }
        else
        {
            strFilename = strURL;
        }
		if (LoadAndParseXMLDoc(strFilename) == false)
		{
			return false;
		}
	}
    return true;
}

bool WMXMLDocument::LoadAndParseXMLDoc(const string& strFilePath)
{
	// open the WDSL file
    FILE* pFile = fopen(strFilePath.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;

	bool bRtn = true;
	// parse XML doc
	if (DeserializeXML(pszBuffer, nSize) == false)
	{
		bRtn = false;
	}
	free(pszBuffer);
	return bRtn;
}

bool WMXMLDocument::GetAndParseXMLDoc(  const string& strHost,
									    const string& strURI,
									    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 = strURI.find("http://")) != string::npos)
	{
		string strTemp = strURI.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: WhiteMesa XMLDocReader/1.0 (C++ Class Library)\r\n";
    string strBuffer, strQueryString, strRequestURI;
    if ((pos = strURI.find('?')) != string::npos)
    {
        strRequestURI = strURI.substr(0, pos);
        strQueryString = strURI.substr(pos + 1);
    }
    else
    {
        strRequestURI = strURI;
    }
	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;
	}
	// parse XML doc
	return DeserializeXML(sock.GetContent(), sock.GetContentLength());
}

