// interopsvcdimedocObj.cpp : Implementation of CinteropsvcdimedocObj

#include "stdafx.h"
#include "interopsvcdimedocObj.h"

#include "..\soap_source\xmlparser.h"
#include "..\wmsoapmsg\wmsoapmsg_i.c"
#include "..\wmsoapmsg\wmsoapmsg.h"
#include "..\soap_source\charencode.h"
#include "..\soap_source\DIMEPayload.h"
#include "..\soap_source\base64.h"
#include "..\soap_source\soap.h"
#include "comdef.h"

// CinteropsvcdimedocObj

static wchar_t *g_pwszMsgNS = L"http://soapinterop.org/attachments/xsd";
static wchar_t *g_pwszRefNS = L"http://schemas.xmlsoap.org/ws/2002/04/reference/";

void LeftTrim(string& strData)
{
    // left trim
    string::size_type pos;
    for (pos = 0; pos < strData.length(); pos++)
    {
        if (!isspace(strData[pos]))
        {
            break;
        }
    }
    if (pos > 0)
    {
        strData = strData.substr(pos);
    }
}

void RightTrim(string& strData)
{
    // right trim
    string::size_type pos;
    string::size_type upperbound = strData.length() - 1;
    for (pos = upperbound; pos >= 0; pos--)
    {
        if (!isspace(strData[pos]))
        {
            break;
        }
    }
    if (pos < upperbound)
    {
        strData = strData.substr(0, pos + 1);
    }
}

void TrimWhitespace(string& strData)
{
    LeftTrim(strData);
    RightTrim(strData);
}

bool ParseParameters(string& strParams, map<string, string>& mapParams)
{
    while (true)
    {
        // seek delimiting ';' between parameters
        string::size_type pos = strParams.find(';');
        // extract attribute/value pair
        string strTemp = strParams.substr(0, pos);
        // find '=' in attribute/value pair
        string::size_type index = strTemp.find('=');
        // if found then process
        if (index != string::npos)
        {
            string strAttr = strTemp.substr(0, index);
            // trim whitespace
            TrimWhitespace(strAttr);
            // attribute name is not case sensitive
            for (string::size_type i = 0; i < strAttr.length(); i++)
            {
                // force to uppercase... comparisons are case-insensitive
                strAttr[i] = toupper(strAttr[i]);
            }
            string strValue = strTemp.substr(index + 1);
            // trim whitespace
            TrimWhitespace(strValue);
            // unquote value if necessary
            string::size_type start, stop;
            start = strValue.find('"');
            stop = strValue.rfind('"');
            if (start != string::npos && stop != string::npos)
            {
                ++start;
                strValue = strValue.substr(start, stop - start);
            }
            // now add to map
            mapParams[strAttr] = strValue;
        }
        // was this the last one?
        if (pos == string::npos)
        {
            // yes, we are done
            break;
        }
        strParams = strParams.substr(pos + 1);
    }
    return true;
}

bool ParseMIMEType( string& strFieldBody,
                    string& strMIMEType,
                    map<string, string>& mapParams)
{
    // split content type and parameters
    string::size_type pos;
    pos = strFieldBody.find(';');
    strMIMEType = strFieldBody.substr(0, pos);
    TrimWhitespace(strMIMEType);
    // content type is not case sensitive
    for (string::size_type i = 0; i < strMIMEType.length(); i++)
    {
        // force to uppercase... comparisons are case-insensitive
        strMIMEType[i] = toupper(strMIMEType[i]);
    }
    // now parse parameters
    ParseParameters(strFieldBody.substr(pos + 1), mapParams);
    return true;
}

BSTR GenerateUUID()
{
    GUID g;
    CoCreateGuid(&g);
    OLECHAR wszBuffer1[64];
    OLECHAR wszBuffer2[64];
    StringFromGUID2(g, wszBuffer1, 64);
    wszBuffer1[37] = 0;
    swprintf(wszBuffer2, L"uuid:%s", &wszBuffer1[1]);
    return SysAllocString(wszBuffer2);
}

STDMETHODIMP CinteropsvcdimedocObj::OnProcessBody(VARIANT SOAPMsgInterface, VARIANT_BOOL *pResult)
{
    *pResult = VARIANT_TRUE;
    // get interface ptr
    IDispatch *pIDispatch = SOAPMsgInterface.pdispVal;
    Iwmsoapmsg2 *pIwmsoapmsg;
    HRESULT hRes = pIDispatch->QueryInterface(IID_Iwmsoapmsg2, (void **)&pIwmsoapmsg);
    if (hRes != S_OK)
    {
        return E_FAIL;
    }
    // process
    if (ProcessMessage(pIwmsoapmsg) == false)
    {
        GenerateFault(pIwmsoapmsg);
    }
    pIwmsoapmsg->Release();
    return S_OK;
}

bool CinteropsvcdimedocObj::ProcessMessage(Iwmsoapmsg2 *pIwmsoapmsg)
{
    BSTR bstrXML;
    wstring wstrXML;
    VARIANT_BOOL bResult;
    HRESULT hRes = pIwmsoapmsg->GetBodyElemLiteral( &bstrXML,
                                                    &bResult);
    if (hRes != S_OK || bResult == VARIANT_FALSE)
    {
        return false;
    }
    // set up for parsing
    WMXMLParser Parser;
    bool bRtn = true;
    // parse the XML fragment representing the message
    try
    {
        Parser.ParseXML_WideString(bstrXML, NULL);
    }
    catch(int e)
    {
        int x = e;
        SysFreeString(bstrXML);
        return false;
    }
    SysFreeString(bstrXML);
    // get the WMXMLElement pointer representing root node of parse tree
    WMXMLElement *pRoot = Parser.DetachDocRoot();
    if (pRoot == NULL)
    {
        return false;
    }

    vector<wstring> arrAttachments;
    bool bEchoAttachmentElemFound = false;
    bool bEchoAttachmentsElemFound = false;
    bool bEchoAttachmentAsBase64ElemFound = false;
    bool bEchoBase64AsAttachmentElemFound = false;
    bool bEchoUnrefAttachmentsElemFound = false;
    bool bAttachmentAsStringElemFound = false;

    wstring wstrBase64Encoded;
    wstring wstrStringOut;

    WMXMLElement *pWrapper = NULL;

    // look for 'EchoAttachment' element
    pWrapper = pRoot->GetChildElement(wstring(g_pwszMsgNS), wstring(L"EchoAttachment"));
    if (pWrapper != NULL)
    {
        bEchoAttachmentElemFound = true;
        WMXMLElement *pAttachment = pWrapper->GetChildElement(wstring(g_pwszMsgNS), wstring(L"In"));
        if (pAttachment != NULL)
        {
            wstring wstrValue;
            wstring wstrKey(g_pwszRefNS);
            if (pAttachment->GetAttribute(wstrKey, wstring(L"location"), wstrValue) == true)
            {
                // found "location" attribute, get value
                wstring wstrId = wstrValue;
                // now we will query message object for attachment with matching id
                IStorage *pIStorage;
                IUnknown *pIUnknown;
                BSTR bstrId = SysAllocString(wstrValue.c_str());
                BSTR bstrType;
                long lTNF;
                VARIANT_BOOL bResult;
                _variant_t varOptions;
                pIwmsoapmsg->GetDIMEAttachmentReq(  bstrId,
                                                    &bstrType,
                                                    &lTNF,
                                                    &pIUnknown,
                                                    &varOptions,
                                                    &bResult);
                if (bResult == VARIANT_FALSE)
                {
                    // no such attachment
                    return false;
                }
                // found referenced attachment, get interface ptr
                HRESULT res = pIUnknown->QueryInterface(IID_IStorage, (void **)&pIStorage);
                if (FAILED(res))
                {
                    return false;
                }
                string strId, strType;
                WideStringToUTF8(wstring(bstrId), strId);
                WideStringToUTF8(wstring(bstrType), strType);
                // wrap it
                DIMEPayload *pPayload = new DIMEPayload(pIStorage,
                                                        strId,
                                                        strType,
                                                        lTNF);
                pIStorage->Release();
                // create output storage
                IStorage *pIStorageOut;
                HRESULT hRes = StgCreateDocfile(NULL,
                                                STGM_DELETEONRELEASE | STGM_DIRECT | STGM_READWRITE| STGM_CREATE | STGM_SHARE_EXCLUSIVE,
                                                0,
                                                &pIStorageOut);
                if (FAILED(hRes))
                {
                    return false;
                }
                // wrap it
                DIMEPayload *pPayloadOut = new DIMEPayload( pIStorageOut,
                                                            strId,
                                                            strType,
                                                            lTNF);

                // now copy contents from input storage to output storage
                char szTemp[4096];
                unsigned long ulNumBytes = 0;
                unsigned long ulBytesWritten = 0;
                pPayload->OpenForRead();
                pPayloadOut->OpenForWrite();
                int count = 0;
                do
                {
                    pPayload->Read(szTemp, 4096, &ulNumBytes);
                    pPayloadOut->Write(szTemp, ulNumBytes, &ulBytesWritten);
                    count += ulNumBytes;
                } while (ulNumBytes == 4096);
                pPayload->Close();
                pPayloadOut->Close();
                // add response header entry to message
                pIwmsoapmsg->AddDIMEAttachmentResp( bstrId,
                                                    bstrType,
                                                    lTNF,
                                                    pIStorageOut,
                                                    varOptions,
                                                    &bResult);
                pIStorageOut->Release();
                SysFreeString(bstrId);
                SysFreeString(bstrType);
                delete pPayload;
                delete pPayloadOut;
                if (bResult == VARIANT_FALSE)
                {
                    return false;
                }
                // store id string
                arrAttachments.push_back(wstrId);
            }
            else
            {
            }
        }
        goto response;
    }
    // look for 'EchoAttachments' element
    pWrapper = pRoot->GetChildElement(wstring(g_pwszMsgNS), wstring(L"EchoAttachments"));
    if (pWrapper != NULL)
    {
        bEchoAttachmentsElemFound = true;
        ChildElementsList::iterator iter;
        unsigned long ulCounter = 0;
        for (iter = pWrapper->GetChildElements().begin();
                iter != pWrapper->GetChildElements().end();
                iter++)
        {
            WMXMLElement *pAttachment = *iter;
            // is this child an "Item" element?
            if (pAttachment->GetLocalName() == L"Item" &&
                pAttachment->GetURI() == g_pwszMsgNS)
            {
                // yes, so look for "location" attribute
                wstring wstrValue;
                wstring wstrKey(g_pwszRefNS);
                if (pAttachment->GetAttribute(wstrKey, wstring(L"location"), wstrValue) == true)
                {
                    // found "location" attribute, get value
                    wstring wstrId = wstrValue;
                    // now we will query message object for attachment with matching id
                    IStorage *pIStorage;
                    IUnknown *pIUnknown;
                    BSTR bstrId = SysAllocString(wstrValue.c_str());
                    BSTR bstrType;
                    long lTNF;
                    VARIANT_BOOL bResult;
                    _variant_t varOptions;
                    pIwmsoapmsg->GetDIMEAttachmentReq(  bstrId,
                                                        &bstrType,
                                                        &lTNF,
                                                        &pIUnknown,
                                                        &varOptions,
                                                        &bResult);
                    if (bResult == VARIANT_FALSE)
                    {
                        // no such attachment
                        return false;
                    }
                    // found referenced attachment, get interface ptr
                    HRESULT res = pIUnknown->QueryInterface(IID_IStorage, (void **)&pIStorage);
                    if (FAILED(res))
                    {
                        return false;
                    }
                    string strId, strType;
                    WideStringToUTF8(wstring(bstrId), strId);
                    WideStringToUTF8(wstring(bstrType), strType);
                    // wrap it
                    DIMEPayload *pPayload = new DIMEPayload(pIStorage,
                                                            strId,
                                                            strType,
                                                            lTNF);
                    pIStorage->Release();
                    // create output storage
                    IStorage *pIStorageOut;
                    HRESULT hRes = StgCreateDocfile(NULL,
                                                    STGM_DELETEONRELEASE | STGM_DIRECT | STGM_READWRITE| STGM_CREATE | STGM_SHARE_EXCLUSIVE,
                                                    0,
                                                    &pIStorageOut);
                    if (FAILED(hRes))
                    {
                        return false;
                    }
                    // wrap it
                    DIMEPayload *pPayloadOut = new DIMEPayload( pIStorageOut,
                                                                strId,
                                                                strType,
                                                                lTNF);

                    // now copy contents from input storage to output storage
                    char szTemp[4096];
                    unsigned long ulNumBytes = 0;
                    unsigned long ulBytesWritten = 0;
                    pPayload->OpenForRead();
                    pPayloadOut->OpenForWrite();
                    int count = 0;
                    do
                    {
                        pPayload->Read(szTemp, 4096, &ulNumBytes);
                        pPayloadOut->Write(szTemp, ulNumBytes, &ulBytesWritten);
                        count += ulNumBytes;
                    } while (ulNumBytes == 4096);
                    pPayload->Close();
                    pPayloadOut->Close();
                    // add response header entry to message
                    pIwmsoapmsg->AddDIMEAttachmentResp( bstrId,
                                                        bstrType,
                                                        lTNF,
                                                        pIStorageOut,
                                                        varOptions,
                                                        &bResult);
                    varOptions.Clear();
                    pIStorageOut->Release();
                    SysFreeString(bstrId);
                    SysFreeString(bstrType);
                    delete pPayload;
                    delete pPayloadOut;
                    if (bResult == VARIANT_FALSE)
                    {
                        return false;
                    }
                    // store id string
                    arrAttachments.push_back(wstrId);
                }
            }
        }
        goto response;
    }
    // look for 'EchoAttachmentAsBase64' element
    pWrapper = pRoot->GetChildElement(wstring(g_pwszMsgNS), wstring(L"EchoAttachmentAsBase64"));
    if (pWrapper != NULL)
    {
        bEchoAttachmentAsBase64ElemFound = true;
        WMXMLElement *pAttachment = pWrapper->GetChildElement(wstring(g_pwszMsgNS), wstring(L"In"));
        if (pAttachment != NULL)
        {
            wstring wstrValue;
            wstring wstrKey(g_pwszRefNS);
            if (pAttachment->GetAttribute(wstrKey, wstring(L"location"), wstrValue) == true)
            {
                // found "location" attribute, get value
                wstring wstrId = wstrValue;
                // now we will query message object for attachment with matching id
                IStorage *pIStorage;
                IUnknown *pIUnknown;
                BSTR bstrId = SysAllocString(wstrValue.c_str());
                BSTR bstrType;
                long lTNF;
                VARIANT_BOOL bResult;
                _variant_t varOptions;
                pIwmsoapmsg->GetDIMEAttachmentReq(  bstrId,
                                                    &bstrType,
                                                    &lTNF,
                                                    &pIUnknown,
                                                    &varOptions,
                                                    &bResult);
                if (bResult == VARIANT_FALSE)
                {
                    // no such attachment
                    return false;
                }
                // found referenced attachment, get interface ptr
                HRESULT res = pIUnknown->QueryInterface(IID_IStorage, (void **)&pIStorage);
                if (FAILED(res))
                {
                    return false;
                }
                string strId, strType;
                WideStringToUTF8(wstring(bstrId), strId);
                WideStringToUTF8(wstring(bstrType), strType);
                // wrap it
                DIMEPayload *pPayload = new DIMEPayload(pIStorage,
                                                        strId,
                                                        strType,
                                                        lTNF);
                pIStorage->Release();
                pPayload->OpenForRead();
                unsigned long ulNumBytesRead = 0;
                unsigned long ulLength = pPayload->GetDataLength();
                unsigned char *pBuffer = (unsigned char *)malloc(ulLength);
                pPayload->Read((char *)pBuffer, ulLength, &ulNumBytesRead);
                pPayload->Close();
                delete pPayload;
                // base64 encode
                WMBase64 base64;
                string strEncoded;
                base64.Encode(pBuffer, ulLength, strEncoded);
                free(pBuffer);
                UTF8ToWideString(strEncoded, wstrBase64Encoded);
                // store id string
                arrAttachments.push_back(wstrId);
            }
        }
        goto response;
    }
    // look for 'EchoBase64AsAttachment' element
    pWrapper = pRoot->GetChildElement(wstring(g_pwszMsgNS), wstring(L"EchoBase64AsAttachment"));
    if (pWrapper != NULL)
    {
        bEchoBase64AsAttachmentElemFound = true;
        WMXMLElement *pData = pWrapper->GetChildElement(wstring(g_pwszMsgNS), wstring(L"In"));
        if (pData != NULL)
        {
            wstring wstrValue = pData->GetContent();
            string strEncodedData;
            WideStringToUTF8(wstrValue, strEncodedData);
            WMBase64 base64;
            // get length of decoded data
            int nLength;
            if (base64.Decode(strEncodedData, NULL, nLength) == false)
            {
                return false;
            }
            char *pszBuffer = NULL;
            bool bIsZeroLength = false;
            if (nLength == 0)
            {
                bIsZeroLength = true;
            }
            else
            {
                // decode
                pszBuffer = (char *)malloc(nLength);
                if (pszBuffer == NULL)
                {
                    return false;
                }
                base64.Decode(strEncodedData, (unsigned char*)pszBuffer, nLength);
            }
            // create output storage
            IStorage *pIStorageOut;
            HRESULT hRes = StgCreateDocfile(NULL,
                                            STGM_DELETEONRELEASE | STGM_DIRECT | STGM_READWRITE| STGM_CREATE | STGM_SHARE_EXCLUSIVE,
                                            0,
                                            &pIStorageOut);
            if (FAILED(hRes))
            {
                return false;
            }
            // wrap it
            long lTNF = 1;
            BSTR bstrType = SysAllocString(L"application/octetstream");
            BSTR bstrId = GenerateUUID();
            string strId, strType;
            wstring wstrId(bstrId);
            WideStringToUTF8(wstring(bstrId), strId);
            WideStringToUTF8(wstring(bstrType), strType);
            DIMEPayload *pPayloadOut = new DIMEPayload( pIStorageOut,
                                                        strId,
                                                        strType,
                                                        lTNF);
            if (bIsZeroLength == false && pszBuffer != NULL)
            {
                // now copy decoded data to output storage
                unsigned long ulBytesWritten = 0;
                pPayloadOut->OpenForWrite();
                pPayloadOut->Write(pszBuffer, nLength, &ulBytesWritten);
                pPayloadOut->Close();
                free(pszBuffer);
            }
            // add payload to message
            _variant_t varOptions;
            pIwmsoapmsg->AddDIMEAttachmentResp( bstrId,
                                                bstrType,
                                                lTNF,
                                                pIStorageOut,
                                                varOptions,
                                                &bResult);
            SysFreeString(bstrId);
            SysFreeString(bstrType);
            pIStorageOut->Release();
            delete pPayloadOut;
            if (bResult == VARIANT_FALSE)
            {
                return false;
            }
            // store id string
            arrAttachments.push_back(wstrId);
        }
        goto response;
    }
    // look for 'EchoUnrefAttachments' element
    pWrapper = pRoot->GetChildElement(wstring(g_pwszMsgNS), wstring(L"EchoUnrefAttachments"));
    if (pWrapper != NULL)
    {
        bEchoUnrefAttachmentsElemFound = true;
        // now get payloads
        long lPosition;
        BSTR bstrId, bstrType;
        long lTNF;
        IUnknown *pIUnknown;
        _variant_t varOptions;
        for (pIwmsoapmsg->GetFirstDIMEAttachmentReq(&bstrId,
                                                    &bstrType,
                                                    &lTNF,
                                                    &pIUnknown,
                                                    &varOptions,
                                                    &lPosition,
                                                    &bResult);
             bResult == VARIANT_TRUE;
             pIwmsoapmsg->GetNextDIMEAttachmentReq( &bstrId,
                                                    &bstrType,
                                                    &lTNF,
                                                    &pIUnknown,
                                                    &varOptions,
                                                    &lPosition,
                                                    &bResult)
        )
        {
            string strId, strType;
            WideStringToUTF8(wstring(bstrId), strId);
            WideStringToUTF8(wstring(bstrType), strType);
            SysFreeString(bstrId);
            SysFreeString(bstrType);
            IStorage *pIStorage;
            HRESULT res = pIUnknown->QueryInterface(IID_IStorage, (void **)&pIStorage);
            if (FAILED(res))
            {
                return false;
            }
            // wrap it
            DIMEPayload *pPayload = new DIMEPayload(pIStorage,
                                                    strId,
                                                    strType,
                                                    lTNF);
            pIStorage->Release();
            // create output storage
            IStorage *pIStorageOut;
            HRESULT hRes = StgCreateDocfile(NULL,
                                            STGM_DELETEONRELEASE | STGM_DIRECT | STGM_READWRITE| STGM_CREATE | STGM_SHARE_EXCLUSIVE,
                                            0,
                                            &pIStorageOut);
            if (FAILED(hRes))
            {
                return false;
            }
            // wrap it
            DIMEPayload *pPayloadOut = new DIMEPayload( pIStorageOut,
                                                        strId,
                                                        strType,
                                                        lTNF);

            // now copy contents from input storage to output storage
            char szTemp[4096];
            unsigned long ulNumBytes = 0;
            unsigned long ulBytesWritten = 0;
            pPayload->OpenForRead();
            pPayloadOut->OpenForWrite();
            int count = 0;
            do
            {
                pPayload->Read(szTemp, 4096, &ulNumBytes);
                pPayloadOut->Write(szTemp, ulNumBytes, &ulBytesWritten);
                count += ulNumBytes;
            } while (ulNumBytes == 4096);
            pPayload->Close();
            pPayloadOut->Close();
            // add payload to message
            pIwmsoapmsg->AddDIMEAttachmentResp( bstrId,
                                                bstrType,
                                                lTNF,
                                                pIStorageOut,
                                                varOptions,
                                                &bResult);
            varOptions.Clear();
            pIStorageOut->Release();
            SysFreeString(bstrId);
            SysFreeString(bstrType);
            delete pPayload;
            delete pPayloadOut;
            if (bResult == VARIANT_FALSE)
            {
                return false;
            }
        }
        goto response;
    }
    // look for 'EchoAttachmentAsString' element
    pWrapper = pRoot->GetChildElement(wstring(g_pwszMsgNS), wstring(L"EchoAttachmentAsString"));
    if (pWrapper != NULL)
    {
        bAttachmentAsStringElemFound = true;
        WMXMLElement *pAttachment = pWrapper->GetChildElement(wstring(g_pwszMsgNS), wstring(L"In"));
        if (pAttachment != NULL)
        {
            wstring wstrValue;
            wstring wstrKey(g_pwszRefNS);
            if (pAttachment->GetAttribute(wstrKey, wstring(L"location"), wstrValue) == true)
            {
                // found "location" attribute, get value
                wstring wstrId = wstrValue;
                // now we will query message object for attachment with matching id
                IStorage *pIStorage;
                IUnknown *pIUnknown;
                BSTR bstrId = SysAllocString(wstrValue.c_str());
                BSTR bstrType;
                long lTNF;
                VARIANT_BOOL bResult;
                _variant_t varOptions;
                pIwmsoapmsg->GetDIMEAttachmentReq(  bstrId,
                                                    &bstrType,
                                                    &lTNF,
                                                    &pIUnknown,
                                                    &varOptions,
                                                    &bResult);
                if (bResult == VARIANT_FALSE)
                {
                    // no such attachment
                    return false;
                }
                // found referenced attachment, get interface ptr
                HRESULT res = pIUnknown->QueryInterface(IID_IStorage, (void **)&pIStorage);
                if (FAILED(res))
                {
                    return false;
                }
                string strId, strType;
                WideStringToUTF8(wstring(bstrId), strId);
                WideStringToUTF8(wstring(bstrType), strType);
                // wrap it
                DIMEPayload *pPayload = new DIMEPayload(pIStorage,
                                                        strId,
                                                        strType,
                                                        lTNF);
                pIStorage->Release();
                pPayload->OpenForRead();
                unsigned long ulNumBytesRead = 0;
                unsigned long ulLength = pPayload->GetDataLength();
                unsigned char *pBuffer = (unsigned char *)malloc(ulLength);
                pPayload->Read((char *)pBuffer, ulLength, &ulNumBytesRead);
                pPayload->Close();
                delete pPayload;
                // MIME type?
                map<string, string> mapParams;
                string strMIMEType;
                ParseMIMEType(  strType,
                                strMIMEType,
                                mapParams);
                if (strMIMEType.find("TEXT/") != 0)
                {
                    return false;
                }
                // charset param?
                string strCharset = "US-ASCII";
                map<string, string>::iterator iM;
                if ((iM = mapParams.find(string("CHARSET"))) != mapParams.end())
                {
                    strCharset = iM->second;
                    // charset value is not case sensitive
                    for (string::size_type i = 0; i < strCharset.length(); i++)
                    {
                        // force to uppercase... comparisons are case-insensitive
                        strCharset[i] = toupper(strCharset[i]);
                    }
                }
                if (strCharset == "UTF-16")
                {
                    wstrStringOut.append((wchar_t *)pBuffer, ulLength / 2); 
                }
                else if (strCharset == "UTF-8" || strCharset == "US-ASCII")
                {
                    string strTemp((char *)pBuffer, ulLength);
                    UTF8ToWideString(strTemp, wstrStringOut);
                }
                // store id string
                arrAttachments.push_back(wstrId);
            }
        }
        goto response;
    }

response:
    delete pRoot;
    // create response
    long lSOAPVersion;
    pIwmsoapmsg->GetSOAPVersion(&lSOAPVersion);
    WMXMLElement *pBody = new WMXMLElement(wstring(L"SOAP-ENV:Body"));
    if (pBody != NULL)
    {
        if (lSOAPVersion == SOAP_VERSION_1_1)
        {
            pBody->SetNSPrefix(wstring(L"SOAP-ENV"), wstring(g_pwszSoapEnvelopeNS));
        }
        else
        {
            pBody->SetNSPrefix(wstring(L"SOAP-ENV"), wstring(g_pwszSoap12EnvelopeNS));
        }
        if (bEchoAttachmentElemFound == true)
        {
            WMXMLElement *pElem = new WMXMLElement(wstring(L"iop:EchoAttachmentResponse"));
            if (pElem != NULL)
            {
                pElem->SetNSPrefix(wstring(L"iop"), wstring(g_pwszMsgNS));
                pBody->AddChildElement(pElem);
                WMXMLElement *pItem = new WMXMLElement(wstring(L"iop:Out"));
                if (pItem != NULL && arrAttachments.size() > 0)
                {
                    pItem->SetAttribute(wstring(L"ref:location"), arrAttachments[0]);
                    pItem->SetNSPrefix(wstring(L"ref"), wstring(g_pwszRefNS));
                    pElem->AddChildElement(pItem);
                }
            }
        }
        else if (bEchoAttachmentsElemFound == true)
        {
            WMXMLElement *pList = new WMXMLElement(wstring(L"iop:EchoAttachmentsResponse"));
            if (pList != NULL)
            {
                pList->SetNSPrefix(wstring(L"iop"), wstring(g_pwszMsgNS));
                pBody->AddChildElement(pList);
                for (unsigned int i = 0; i < arrAttachments.size(); i++)
                {
                    WMXMLElement *pItem = new WMXMLElement(wstring(L"iop:Item"));
                    if (pItem != NULL)
                    {
                        pItem->SetAttribute(wstring(L"ref:location"), arrAttachments[i]);
                        pItem->SetNSPrefix(wstring(L"ref"), wstring(g_pwszRefNS));
                        pList->AddChildElement(pItem);
                    }
                }
            }
        }
        else if (bEchoAttachmentAsBase64ElemFound == true)
        {
            WMXMLElement *pElem = new WMXMLElement(wstring(L"iop:EchoAttachmentAsBase64Response"));
            if (pElem != NULL)
            {
                pElem->SetNSPrefix(wstring(L"iop"), wstring(g_pwszMsgNS));
                pBody->AddChildElement(pElem);
                WMXMLElement *pItem = new WMXMLElement(wstring(L"iop:Out"));
                if (pItem != NULL)
                {
                    pItem->SetContent(wstrBase64Encoded);
                    pElem->AddChildElement(pItem);
                }
            }
        }
        else if (bEchoBase64AsAttachmentElemFound == true)
        {
            WMXMLElement *pElem = new WMXMLElement(wstring(L"iop:EchoBase64AsAttachmentResponse"));
            if (pElem != NULL)
            {
                pElem->SetNSPrefix(wstring(L"iop"), wstring(g_pwszMsgNS));
                pBody->AddChildElement(pElem);
                WMXMLElement *pItem = new WMXMLElement(wstring(L"iop:Out"));
                if (pItem != NULL && arrAttachments.size() > 0)
                {
                    pItem->SetAttribute(wstring(L"ref:location"), arrAttachments[0]);
                    pItem->SetNSPrefix(wstring(L"ref"), wstring(g_pwszRefNS));
                    pElem->AddChildElement(pItem);
                }
            }
        }
        else if (bEchoUnrefAttachmentsElemFound == true)
        {
            WMXMLElement *pElem = new WMXMLElement(wstring(L"iop:EchoUnrefAttachmentsResponse"));
            if (pElem != NULL)
            {
                pElem->SetNSPrefix(wstring(L"iop"), wstring(g_pwszMsgNS));
                pBody->AddChildElement(pElem);
            }
        }
        else if (bAttachmentAsStringElemFound == true)
        {
            WMXMLElement *pElem = new WMXMLElement(wstring(L"iop:EchoAttachmentAsStringResponse"));
            if (pElem != NULL)
            {
                pElem->SetNSPrefix(wstring(L"iop"), wstring(g_pwszMsgNS));
                pBody->AddChildElement(pElem);
                WMXMLElement *pItem = new WMXMLElement(wstring(L"iop:Out"));
                if (pItem != NULL)
                {
                    pItem->SetContent(wstrStringOut);
                    pElem->AddChildElement(pItem);
                }
            }
        }
        // finish up
        BSTR bstrXML = pBody->Serialize(NULL);
        BSTR bstrEncodingStyle = SysAllocString(L"");
        pIwmsoapmsg->SetBodyElemLiteral(bstrXML, bstrEncodingStyle, &bResult);
        SysFreeString(bstrXML);
        SysFreeString(bstrEncodingStyle);
        delete pBody;
    }
    return true;
}

bool CinteropsvcdimedocObj::GenerateFault(Iwmsoapmsg2 *pIwmsoapmsg)
{
    long lSOAPVersion;
    pIwmsoapmsg->GetSOAPVersion(&lSOAPVersion);
    BSTR bstrFaultcode, bstrFaultcodeNS;
    if (lSOAPVersion == SOAP_VERSION_1_1)
    {
        bstrFaultcode = SysAllocString(L"Client");
        bstrFaultcodeNS = SysAllocString(g_pwszSoapEnvelopeNS);
    }
    else
    {
        bstrFaultcode = SysAllocString(L"Sender");
        bstrFaultcodeNS = SysAllocString(g_pwszSoap12EnvelopeNS);
    }
    BSTR bstrFaultstring = SysAllocString(L"Message processing failed.");
    VARIANT_BOOL bNeedDetail = VARIANT_TRUE;
    VARIANT_BOOL bResult = VARIANT_FALSE;
    pIwmsoapmsg->SetSOAPFault(bstrFaultcode, bstrFaultcodeNS, bstrFaultstring, bNeedDetail, &bResult);
    SysFreeString(bstrFaultcode);
    SysFreeString(bstrFaultcodeNS);
    SysFreeString(bstrFaultstring);
    return true;
}
