// SoapDocMessage.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.
//
//////////////////////////////////////////////////////////////////////
//
// WMSOAPDocMessage class - a class representing a SOAP message
// using the "document/literal" convention. For use by client code.
// 
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "soapdocmessage.h"
#include "charencode.h"
#include "xmlparser.h"
#include "soap.h"

WMSOAPDocMessage::WMSOAPDocMessage(void)
{
    m_nSOAPVersion = SOAP_VERSION_1_1;
}

WMSOAPDocMessage::~WMSOAPDocMessage(void)
{
}

WMXMLElement *WMSOAPDocMessage::BuildSOAPMsg(int nSOAPVersion)
{
    wstring wstrSOAPEnvNS;
    if (nSOAPVersion == SOAP_VERSION_1_1)
    {
        wstrSOAPEnvNS = g_pwszSoapEnvelopeNS;
    }
    else
    {
        wstrSOAPEnvNS = g_pwszSoap12EnvelopeNS;
    }

	WMXMLElement *pEnv = new WMXMLElement(wstring(L"SOAP-ENV:Envelope"));
	if (pEnv != NULL)
	{
		pEnv->SetNSPrefix(wstring(L"SOAP-ENV"), wstrSOAPEnvNS);
	}
	else
	{
		return NULL;
	}
    // Header
    if (m_listHeaderEntries.size() > 0)
    {
	    WMXMLElement *pHeader = new WMXMLElement(wstring(L"SOAP-ENV:Header"));
	    if (pHeader != NULL)
	    {
		    pEnv->AddChildElement(pHeader);
            // add header entries
            list<WMXMLElement *>::iterator iL;
            for (iL = m_listHeaderEntries.begin();
                iL != m_listHeaderEntries.end();
                iL++)
            {
                if (*iL != NULL)
                {
		            pHeader->AddChildElement(*iL);
                }
            }
	    }
	    else
	    {
		    delete pEnv;
		    return NULL;
	    }
        m_listHeaderEntries.clear();
    }
	WMXMLElement *pBody = new WMXMLElement(wstring(L"SOAP-ENV:Body"));
	if (pBody != NULL)
	{
		pEnv->AddChildElement(pBody);
        // add body entries
        list<WMXMLElement *>::iterator iL;
        for (iL = m_listBodyEntries.begin();
            iL != m_listBodyEntries.end();
            iL++)
        {
            if (*iL != NULL)
            {
		        pBody->AddChildElement(*iL);
            }
        }
	}
	else
	{
		delete pEnv;
		return NULL;
	}
    m_listBodyEntries.clear();
    return pEnv;
}

bool WMSOAPDocMessage::Serialize(string& strXML,
                                 int nSOAPVersion)
{
	WMXMLElement *pEnv = BuildSOAPMsg(nSOAPVersion);
    if (pEnv == NULL)
    {
        return false;
    }
	wstring wstrEnv;
	pEnv->Serialize(NULL, wstrEnv);
	delete pEnv;
	WideStringToUTF8(wstrEnv, strXML);
    m_nSOAPVersion = nSOAPVersion;
    return true;
}

bool WMSOAPDocMessage::Serialize(wstring& wstrXML,
                                 int nSOAPVersion)
{
	WMXMLElement *pEnv = BuildSOAPMsg(nSOAPVersion);
    if (pEnv == NULL)
    {
        return false;
    }
	pEnv->Serialize(NULL, wstrXML);
	delete pEnv;
    m_nSOAPVersion = nSOAPVersion;
    return true;
}

bool WMSOAPDocMessage::Serialize(BSTR *pbstrXML,
                                 int nSOAPVersion)
{
	WMXMLElement *pEnv = BuildSOAPMsg(nSOAPVersion);
    if (pEnv == NULL)
    {
        return false;
    }
	*pbstrXML = pEnv->Serialize(NULL);
	delete pEnv;
    m_nSOAPVersion = nSOAPVersion;
    return true;
}

bool WMSOAPDocMessage::Deserialize(const string& strXML)
{
    if (DeserializeXML(strXML) == false)
    {
        return false;
    }
    if (m_pRoot != NULL)
    {
        if (m_pRoot->GetURI() == g_pwszSoapEnvelopeNS)
        {
            m_nSOAPVersion = SOAP_VERSION_1_1;
        }
        else if (m_pRoot->GetURI() == g_pwszSoap12EnvelopeNS)
        {
            m_nSOAPVersion = SOAP_VERSION_1_2;
        }
        else
        {
            return false;
        }
    }
	return m_pRoot != NULL;
}

bool WMSOAPDocMessage::Deserialize(const BSTR bstrXML)
{
    if (DeserializeXML(bstrXML) == false)
    {
        return false;
    }
    if (m_pRoot != NULL)
    {
        if (m_pRoot->GetURI() == g_pwszSoapEnvelopeNS)
        {
            m_nSOAPVersion = SOAP_VERSION_1_1;
        }
        else if (m_pRoot->GetURI() == g_pwszSoap12EnvelopeNS)
        {
            m_nSOAPVersion = SOAP_VERSION_1_2;
        }
        else
        {
            return false;
        }
    }
	return m_pRoot != NULL;
}

bool WMSOAPDocMessage::Deserialize(const wstring& wstrXML)
{
    if (DeserializeXML(wstrXML) == false)
    {
        return false;
    }
    if (m_pRoot != NULL)
    {
        if (m_pRoot->GetURI() == g_pwszSoapEnvelopeNS)
        {
            m_nSOAPVersion = SOAP_VERSION_1_1;
        }
        else if (m_pRoot->GetURI() == g_pwszSoap12EnvelopeNS)
        {
            m_nSOAPVersion = SOAP_VERSION_1_2;
        }
        else
        {
            return false;
        }
    }
	return m_pRoot != NULL;
}

bool WMSOAPDocMessage::AddHeaderEntry(WMXMLElement *pElem)
{
    m_listHeaderEntries.push_back(pElem);
    return true;
}

bool WMSOAPDocMessage::AddBodyEntry(WMXMLElement *pElem)
{
   m_listBodyEntries.push_back(pElem);
   return true;
}

WMXMLElement *WMSOAPDocMessage::CreateHeaderEntry(  const wstring& wstrNamespace,
                                                    const wstring& wstrPrefix,
                                                    const wstring& wstrLocalName,
                                                    const wstring& wstrRole,
                                                    bool bIsMustUnderstand)
{
    wstring wstrName;
    if (wstrNamespace.empty() == false && wstrPrefix.empty() == false)
    {
        wstrName = wstrPrefix;
        wstrName += L":";
        wstrName += wstrLocalName;
    }
    else
    {
        wstrName = wstrLocalName;
    }
    WMXMLElement *pElem = new WMXMLElement(wstrName);
	if (pElem == NULL)
	{
        return NULL;
    }
    if (wstrNamespace.empty() == false)
    {
        if (wstrPrefix.empty() == false)
        {
    	    pElem->SetNSPrefix(wstrPrefix, wstrNamespace);
        }
        else
        {
            // default namespace
    	    pElem->SetAttribute(wstring(L"xmlns"), wstrNamespace);
        }
    }
    if (m_nSOAPVersion == SOAP_VERSION_1_1)
    {
        if (bIsMustUnderstand == true)
        {
            pElem->SetAttribute(wstring(L"SOAP-ENV:mustUnderstand"), wstring(L"1"));
        }
        if (wstrRole.empty() == false)
        {
            pElem->SetAttribute(wstring(L"SOAP-ENV:actor"), wstrRole);
        }
    }
    else
    {
        if (bIsMustUnderstand == true)
        {
            pElem->SetAttribute(wstring(L"SOAP-ENV:mustUnderstand"), wstring(L"true"));
        }
        if (wstrRole.empty() == false)
        {
            pElem->SetAttribute(wstring(L"SOAP-ENV:role"), wstrRole);
        }
    }
    AddHeaderEntry(pElem);
    return pElem;
}

WMXMLElement *WMSOAPDocMessage::CreateBodyEntry(const wstring& wstrNamespace,
                                                const wstring& wstrPrefix,
                                                const wstring& wstrLocalName)
{
    wstring wstrName;
    if (wstrNamespace.empty() == false && wstrPrefix.empty() == false)
    {
        wstrName = wstrPrefix;
        wstrName += L":";
        wstrName += wstrLocalName;
    }
    else
    {
        wstrName = wstrLocalName;
    }
    WMXMLElement *pElem = new WMXMLElement(wstrName);
	if (pElem == NULL)
	{
        return NULL;
    }
    if (wstrNamespace.empty() == false)
    {
        if (wstrPrefix.empty() == false)
        {
    	    pElem->SetNSPrefix(wstrPrefix, wstrNamespace);
        }
        else
        {
            // default namespace
    	    pElem->SetAttribute(wstring(L"xmlns"), wstrNamespace);
        }
    }
    AddBodyEntry(pElem);
    return pElem;
}

WMXMLElement *WMSOAPDocMessage::GetHeaderEntry( const wstring& wstrNamespace,
                                                const wstring& wstrName)
{
    if (m_pRoot == NULL)
    {
        return NULL;
    }
    wstring wstrSOAPEnvNS;
    if (m_nSOAPVersion == SOAP_VERSION_1_1)
    {
        wstrSOAPEnvNS = g_pwszSoapEnvelopeNS;
    }
    else
    {
        wstrSOAPEnvNS = g_pwszSoap12EnvelopeNS;
    }
    WMXMLElement *pHeader = m_pRoot->GetChildElement(wstrSOAPEnvNS, wstring(L"Header"));
    if (pHeader == NULL)
    {
        return false;
    }
    return pHeader->GetChildElement(wstrNamespace, wstrName);
}

WMXMLElement *WMSOAPDocMessage::GetBodyEntry(   const wstring& wstrNamespace,
                                                const wstring& wstrName)
{
    if (m_pRoot == NULL)
    {
        return NULL;
    }
    wstring wstrSOAPEnvNS;
    if (m_nSOAPVersion == SOAP_VERSION_1_1)
    {
        wstrSOAPEnvNS = g_pwszSoapEnvelopeNS;
    }
    else
    {
        wstrSOAPEnvNS = g_pwszSoap12EnvelopeNS;
    }
    WMXMLElement *pBody = m_pRoot->GetChildElement(wstrSOAPEnvNS, wstring(L"Body"));
    if (pBody == NULL)
    {
        return false;
    }
    return pBody->GetChildElement(wstrNamespace, wstrName);
}

WMXMLElement *WMSOAPDocMessage::GetFaultMsg()
{
    if (m_pRoot == NULL)
    {
        return NULL;
    }
    wstring wstrSOAPEnvNS;
    if (m_nSOAPVersion == SOAP_VERSION_1_1)
    {
        wstrSOAPEnvNS = g_pwszSoapEnvelopeNS;
    }
    else
    {
        wstrSOAPEnvNS = g_pwszSoap12EnvelopeNS;
    }
    WMXMLElement *pBody = m_pRoot->GetChildElement(wstrSOAPEnvNS, wstring(L"Body"));
    if (pBody == NULL)
    {
        return NULL;
    }
    return pBody->GetChildElement(wstrSOAPEnvNS, wstring(L"Fault"));
}

bool WMSOAPDocMessage::GetSOAPFaultCode(wstring& wstrValueNS, wstring& wstrValue)
{
    if (m_pRoot == NULL)
    {
        return NULL;
    }
    wstring wstrSOAPEnvNS, wstrXPath;
    if (m_nSOAPVersion == SOAP_VERSION_1_1)
    {
        wstrSOAPEnvNS = g_pwszSoapEnvelopeNS;
        wstrXPath = L"/e:Body/e:Fault/faultcode";
    }
    else
    {
        wstrSOAPEnvNS = g_pwszSoap12EnvelopeNS;
        wstrXPath = L"/e:Body/e:Fault/e:Code";
    }
    map<wstring, wstring> mapNS;
    mapNS.insert(map<wstring, wstring>::value_type(wstring(L"e"), wstrSOAPEnvNS));
    if (SelectElems(wstrXPath, mapNS) == false)
    {
        return false;
    }
    WMXMLElement *pElement = NextElem();
    wstring wstrTemp = pElement->GetContent();
	wstring::size_type pos = wstrTemp.find(':');
	if (pos != wstring::npos)
	{
		// get NS prefix
		NSPrefixMap::iterator iNS;
        if ((iNS = pElement->GetNSPrefixMap().find(wstrTemp.substr(0, pos))) != pElement->GetNSPrefixMap().end())
		{
			wstrValueNS = (*iNS).second;
		}
		else
		{
			// unresolvable namespace prefix
            return false;
		}
		wstrValue = wstrTemp.substr(++pos);
	}
	else
	{
		// default namespace
		NSPrefixMap::iterator iNS;
		if ((iNS = pElement->GetNSPrefixMap().find(wstring(L""))) != pElement->GetNSPrefixMap().end())
		{
			wstrValueNS = (*iNS).second;
		}
		else
		{
			// unresolvable default namespace
            return false;
		}
		wstrValue = wstrTemp;
	}
    return true;
}

bool WMSOAPDocMessage::GetSOAPFaultString(wstring& wstrValue)
{
    if (m_pRoot == NULL)
    {
        return NULL;
    }
    wstring wstrSOAPEnvNS, wstrXPath;
    if (m_nSOAPVersion == SOAP_VERSION_1_1)
    {
        wstrSOAPEnvNS = g_pwszSoapEnvelopeNS;
        wstrXPath = L"/e:Body/e:Fault/faultstring";
    }
    else
    {
        wstrSOAPEnvNS = g_pwszSoap12EnvelopeNS;
        wstrXPath = L"/e:Body/e:Fault/e:Reason";
    }
    map<wstring, wstring> mapNS;
    mapNS.insert(map<wstring, wstring>::value_type(wstring(L"e"), wstrSOAPEnvNS));
    if (SelectElems(wstrXPath, mapNS) == false)
    {
        return false;
    }
    WMXMLElement *pElement = NextElem();
    wstrValue = pElement->GetContent();
    return true;
}

WMXMLElement *WMSOAPDocMessage::GetSOAPFaultDetail( const wstring& wstrNamespace,
                                                    const wstring& wstrName)
{
    if (m_pRoot == NULL)
    {
        return NULL;
    }
    wstring wstrSOAPEnvNS, wstrXPath;
    if (m_nSOAPVersion == SOAP_VERSION_1_1)
    {
        wstrSOAPEnvNS = g_pwszSoapEnvelopeNS;
        wstrXPath = L"/e:Body/e:Fault/detail/d:";
    }
    else
    {
        wstrSOAPEnvNS = g_pwszSoap12EnvelopeNS;
        wstrXPath = L"/e:Body/e:Fault/e:Detail/d:";
    }
    wstrXPath += wstrName;

    map<wstring, wstring> mapNS;
    mapNS.insert(map<wstring, wstring>::value_type(wstring(L"e"), wstrSOAPEnvNS));
    mapNS.insert(map<wstring, wstring>::value_type(wstring(L"d"), wstrNamespace));
    if (SelectElems(wstrXPath, mapNS) == false)
    {
        return NULL;
    }
    WMXMLElement *pElement = NextElem();
    return pElement;
}