#include <urlmon.hxx>

#undef  URLMONOFFSETOF
#define URLMONOFFSETOF(t,f)   ((DWORD_PTR)(&((t*)0)->f))

STDAPI_(void)
ReleaseBindInfo(BINDINFO * pbindinfo)
{
    if ( !pbindinfo)
        return;

    DWORD cb = pbindinfo->cbSize;

    UrlMkAssert( cb && 
        "CopyBindInfo(): cbSize of the BINDINFO is ZERO. We need the cbSize to be set before calling us.");
    if(!cb )
        return; 

    if (cb >= URLMONOFFSETOF(BINDINFO, dwReserved) && pbindinfo->pUnk)
    {
        pbindinfo->pUnk->Release();
    }

    if (pbindinfo->szExtraInfo)
    {
        delete [] pbindinfo->szExtraInfo;
    }

    if (pbindinfo->szCustomVerb)
    {
        delete [] pbindinfo->szCustomVerb;
    }

    ReleaseStgMedium(&pbindinfo->stgmedData);

    ZeroMemory(pbindinfo, cb);
    pbindinfo->cbSize = cb;  

    return;
}

STDAPI
CopyStgMedium(const STGMEDIUM * pcstgmedSrc,
                 STGMEDIUM * pstgmedDest)
{
    HRESULT         hr = S_OK;

    if ( !pcstgmedSrc || !pstgmedDest )
        return E_POINTER;

    ZeroMemory(pstgmedDest, sizeof(*pstgmedDest));

    switch (pcstgmedSrc->tymed)
    {

    case TYMED_HGLOBAL:
        {
            void *          pvDest;
            const void *    pcvSrc;
            DWORD_PTR       dwcbLen;
            HGLOBAL         hGlobalDest;

            if (!pcstgmedSrc->hGlobal)
                break;  

            hr = E_OUTOFMEMORY;
            pcvSrc = GlobalLock(pcstgmedSrc->hGlobal);
            if (!pcvSrc)
                goto Cleanup;

            dwcbLen = GlobalSize(pcstgmedSrc->hGlobal);

            hGlobalDest = GlobalAlloc(GMEM_FIXED | GMEM_SHARE, dwcbLen);
            if (!hGlobalDest)
            {
                GlobalUnlock(pcstgmedSrc->hGlobal);
                goto Cleanup;
            }

            pvDest = GlobalLock(hGlobalDest);
            if (!pvDest)
            {
                GlobalFree(hGlobalDest);
                GlobalUnlock(pcstgmedSrc->hGlobal);
                goto Cleanup;
            }

            UrlMkAssert(dwcbLen>>32 == 0);
            memcpy(pvDest, pcvSrc, (unsigned long)dwcbLen);

            pstgmedDest->hGlobal = hGlobalDest;

            GlobalUnlock(hGlobalDest);
            GlobalUnlock(pcstgmedSrc->hGlobal);
            hr = S_OK;
        }
        break;

    case TYMED_FILE:
        {
            if (!pcstgmedSrc->lpszFileName)
                break; 

            hr = E_OUTOFMEMORY;

            LPWSTR lpwstr = OLESTRDuplicate(pcstgmedSrc->lpszFileName);
            if (!lpwstr)
                goto Cleanup;

            pstgmedDest->lpszFileName = lpwstr;

            hr = S_OK;
        }
        break;

    case TYMED_ISTREAM:
        {
            pstgmedDest->pstm = pcstgmedSrc->pstm;
            if ( pstgmedDest->pstm )
                pstgmedDest->pstm->AddRef();
        }
        break;

    case TYMED_ISTORAGE:
        {
            pstgmedDest->pstg = pcstgmedSrc->pstg;
            if ( pstgmedDest->pstg )
                pstgmedDest->pstg->AddRef();
        }
        break;

    default:
        UrlMkAssert( !"CloneStgMedium has encountered a TYMED it doesn't know how to copy." );

    case TYMED_NULL: 
    case TYMED_GDI:  
        memcpy(pstgmedDest, pcstgmedSrc, sizeof(*pstgmedDest));
        break;
    }

    pstgmedDest->tymed = pcstgmedSrc->tymed;
    pstgmedDest->pUnkForRelease = pcstgmedSrc->pUnkForRelease;
    if (pstgmedDest->pUnkForRelease)
        (pstgmedDest->pUnkForRelease)->AddRef();

Cleanup:
    return hr;
}

STDAPI
CopyBindInfo( const BINDINFO * pcbiSrc,
                BINDINFO *pbiDest )
{

    HRESULT hr    = E_INVALIDARG;  
    DWORD   cbSrc;
    DWORD   cbDst;

    if (!pcbiSrc || !pbiDest)
        return E_POINTER;

    cbSrc = pcbiSrc->cbSize;
    cbDst = pbiDest->cbSize;

    UrlMkAssert( cbSrc &&
        "CopyBindInfo(): cbSize of the source is ZERO. You must set the cbSize.");
    UrlMkAssert( cbDst &&
        "CopyBindInfo(): cbSize of the destination is ZERO. It needs to be set to the size of the BINDINFO before calling us.");
    if (!cbSrc || !cbDst)
        goto Abort;   

    memcpy(pbiDest, pcbiSrc, min(cbSrc, cbDst));
    pbiDest->cbSize = cbDst; 

    if (cbDst > cbSrc)
    {
        ZeroMemory((BYTE *)pbiDest + cbSrc, cbDst - cbSrc);
    }

    if (cbDst >= URLMONOFFSETOF(BINDINFO, dwReserved))
    {
        ZeroMemory(&pbiDest->securityAttributes, sizeof(SECURITY_ATTRIBUTES));
    }

    if (pcbiSrc->pUnk && cbDst >= URLMONOFFSETOF(BINDINFO, dwReserved))
    {
        pbiDest->pUnk->AddRef();
    }

    pbiDest->szExtraInfo  = NULL;
    pbiDest->szCustomVerb = NULL;    

    hr = CopyStgMedium( &pcbiSrc->stgmedData, &pbiDest->stgmedData );
    if (hr)
        goto Cleanup;

    if (pcbiSrc->szExtraInfo)
    {
        LPWSTR lpwstr = OLESTRDuplicate(pcbiSrc->szExtraInfo);
        if (!lpwstr)
        {
            hr = E_OUTOFMEMORY;
            goto Cleanup;
        }

        pbiDest->szExtraInfo= lpwstr;
    }

    if (pcbiSrc->szCustomVerb)
    {
        LPWSTR lpwstr = OLESTRDuplicate(pcbiSrc->szCustomVerb);
        if (!lpwstr)
        {
            hr = E_OUTOFMEMORY;
            goto Cleanup;
        }

        pbiDest->szCustomVerb= lpwstr;
    }

Cleanup:
    if (hr)
    {

        ReleaseBindInfo( pbiDest );
    }

Abort:
    return(hr);
}

// Microsoft Corporation 1997
//
// This file contains methods for copying and releasing STGMEDIUMs and BINDINFOs
// (which contains a STGMEDIUM).
//
// Created: 8-15-97 t-gpease 
//

#include <urlmon.hxx>

#undef  URLMONOFFSETOF
#define URLMONOFFSETOF(t,f)   ((DWORD_PTR)(&((t*)0)->f))

//
// This function clears the BINDINFO structure. 
//
//- The "cbSize" will not be clear and will be the same as it 
// was pasted in. This is by design so that you can reuse the 
// BINDINFO (eg copy another BINDINFO into it). 
//
//- It does NOT release the memory that the BINDINFO oocupies 
// (this could be on the stack). 
//
//- It does properly release items that the BINDINFO might be 
// pointing to.
//
STDAPI_(void)
ReleaseBindInfo(BINDINFO * pbindinfo)
{
    if ( !pbindinfo)
        return;

    DWORD cb = pbindinfo->cbSize;

    UrlMkAssert( cb && 
        "CopyBindInfo(): cbSize of the BINDINFO is ZERO. We need the cbSize to be set before calling us.");
    if(!cb )
        return; // we don't know which structure it is, so don't touch it.

    if (cb >= URLMONOFFSETOF(BINDINFO, dwReserved) && pbindinfo->pUnk)
    {
        pbindinfo->pUnk->Release();
    }

    // Orginal BINDINFO no need to check size
    if (pbindinfo->szExtraInfo)
    {
        delete [] pbindinfo->szExtraInfo;
    }

    // Orginal BINDINFO no need to check size
    if (pbindinfo->szCustomVerb)
    {
        delete [] pbindinfo->szCustomVerb;
    }

    // Orginal BINDINFO no need to check size
    ReleaseStgMedium(&pbindinfo->stgmedData);

    // set this to zero so other function will not try to use the BINDINFO
    ZeroMemory(pbindinfo, cb);
    pbindinfo->cbSize = cb;  // but keep this intact

    return;
}

//
// This function copies STGMEDIUM stuctures.
//
//- Users need to allocate the memory that the STGMEDIUM Dest will be copied
// into.
//
//- If anything goes wrong, we return the proper HRESULT and STGMEDIUM.will
// have been zero-ed.
//
STDAPI
CopyStgMedium(const STGMEDIUM * pcstgmedSrc,
                 STGMEDIUM * pstgmedDest)
{
    HRESULT         hr = S_OK;

    if ( !pcstgmedSrc || !pstgmedDest )
        return E_POINTER;

    ZeroMemory(pstgmedDest, sizeof(*pstgmedDest));

    switch (pcstgmedSrc->tymed)
    {

    case TYMED_HGLOBAL:
        {
            void *          pvDest;
            const void *    pcvSrc;
            DWORD_PTR       dwcbLen;
            HGLOBAL         hGlobalDest;

            if (!pcstgmedSrc->hGlobal)
                break;  // nothing to do

            hr = E_OUTOFMEMORY;
            pcvSrc = GlobalLock(pcstgmedSrc->hGlobal);
            if (!pcvSrc)
                goto Cleanup;

            dwcbLen = GlobalSize(pcstgmedSrc->hGlobal);
            // We can't do the following line:
            // hGlobalDest = GlobalAlloc((GMEM_MOVEABLE | GMEM_SHARE), dwcbLen);
            // because we hand out the bindinfo.stgmedData.hglobal to callers
            // that do NOT lock it and use the handle instead of the pointer.
            hGlobalDest = GlobalAlloc(GMEM_FIXED | GMEM_SHARE, dwcbLen);
            if (!hGlobalDest)
            {
                GlobalUnlock(pcstgmedSrc->hGlobal);
                goto Cleanup;
            }


            pvDest = GlobalLock(hGlobalDest);
            if (!pvDest)
            {
                GlobalFree(hGlobalDest);
                GlobalUnlock(pcstgmedSrc->hGlobal);
                goto Cleanup;
            }

            UrlMkAssert(dwcbLen>>32 == 0);
            memcpy(pvDest, pcvSrc, (unsigned long)dwcbLen);

            pstgmedDest->hGlobal = hGlobalDest;

            GlobalUnlock(hGlobalDest);
            GlobalUnlock(pcstgmedSrc->hGlobal);
            hr = S_OK;
        }
        break;

    case TYMED_FILE:
        {
            if (!pcstgmedSrc->lpszFileName)
                break; // nothing to do

            hr = E_OUTOFMEMORY;

            LPWSTR lpwstr = OLESTRDuplicate(pcstgmedSrc->lpszFileName);
            if (!lpwstr)
                goto Cleanup;

            pstgmedDest->lpszFileName = lpwstr;

            hr = S_OK;
        }
        break;

    case TYMED_ISTREAM:
        {
            pstgmedDest->pstm = pcstgmedSrc->pstm;
            if ( pstgmedDest->pstm )
                pstgmedDest->pstm->AddRef();
        }
        break;

    case TYMED_ISTORAGE:
        {
            pstgmedDest->pstg = pcstgmedSrc->pstg;
            if ( pstgmedDest->pstg )
                pstgmedDest->pstg->AddRef();
        }
        break;

    default:
        UrlMkAssert( !"CloneStgMedium has encountered a TYMED it doesn't know how to copy." );
        // fall thru and copy it.

    case TYMED_NULL: // blindly copy
    case TYMED_GDI:  // Just copy...
        memcpy(pstgmedDest, pcstgmedSrc, sizeof(*pstgmedDest));
        break;
    }

    // Common things that can be copied if we get to this point.
    pstgmedDest->tymed = pcstgmedSrc->tymed;
    pstgmedDest->pUnkForRelease = pcstgmedSrc->pUnkForRelease;
    if (pstgmedDest->pUnkForRelease)
        (pstgmedDest->pUnkForRelease)->AddRef();

Cleanup:
    return hr;
}

//
// This function copies BINDINFO structures.
//
// NOTE: IE4 added properties to the BINDINFO structure. This function
//       works with both versions. If the structure is extended in the
//       future, it will blindly copy the additional properties in the 
//       structure.
//
//- Users need to allocate the memory that the BINDINFO Dest will be copied
//  into.
//
//- Users also need to set the "cbSize" of the BINDINFO Dest to 
//  the sizeof(BINDINFO) before calling (see NOTE above).
//
//- If there is a "cbSize" conflict between the Src and the Dest, we will:
//  1) Src->cbSize = Dest->cbSize, normal copy.
//  2) Src->cbSize > Dest->cbSize, copy as much info about the Src into
//     the Dest as will fit.
//  3) Src->cbSize < Dest->cbSize, copy the Src into the Dest, Zero the
//     remaining unfilled portion of the Dest.
//
//- We don't not copy the securityattribute. We clear this value of the
//  BINDINFO Dest.
//
//- If anything goes wrong, we return the proper HRESULT and clear the
//  the BINDINFO structure using ReleaseBindInfo() (below).
//  NOTE: If "cbSize" of the Dest is ZERO, we don't do anything.
//
STDAPI
CopyBindInfo( const BINDINFO * pcbiSrc,
                BINDINFO *pbiDest )
{
/****** 8-15-97 t-gpease
Current structure at the time of writing... from URLMON.H
typedef struct tagBINDINFO {
    ULONG cbSize;
    LPWSTR szExtraInfo;
    STGMEDIUM stgmedData;
    DWORD grfBindInfoF;
    DWORD dwBindVerb;
    LPWSTR szCustomVerb;
    DWORD cbstgmedData;
// new part below this line //
    DWORD dwOptions;
    DWORD dwOptionsFlags;
    DWORD dwCodePage;
    SECURITY_ATTRIBUTES securityAttributes;
    IID iid;
    IUnknown __RPC_FAR *pUnk;
    DWORD dwReserved;
} BINDINFO;

Anything bigger than this we will blindly copy.the size of cbSize
*******/

    // NOTE: hr will an error until just before the Cleanup label.
    HRESULT hr    = E_INVALIDARG;  
    DWORD   cbSrc;
    DWORD   cbDst;

    if (!pcbiSrc || !pbiDest)
        return E_POINTER;

    cbSrc = pcbiSrc->cbSize;
    cbDst = pbiDest->cbSize;
    
    UrlMkAssert( cbSrc &&
        "CopyBindInfo(): cbSize of the source is ZERO. You must set the cbSize.");
    UrlMkAssert( cbDst &&
        "CopyBindInfo(): cbSize of the destination is ZERO. It needs to be set to the size of the BINDINFO before calling us.");
    if (!cbSrc || !cbDst)
        goto Abort;   // nothing to do or can do.

    // Copy those bytes in common, zero the rest if any
    memcpy(pbiDest, pcbiSrc, min(cbSrc, cbDst));
    pbiDest->cbSize = cbDst; // always keep this intact

    if (cbDst > cbSrc)
    {
        ZeroMemory((BYTE *)pbiDest + cbSrc, cbDst - cbSrc);
    }

    if (cbDst >= URLMONOFFSETOF(BINDINFO, dwReserved))
    {
        ZeroMemory(&pbiDest->securityAttributes, sizeof(SECURITY_ATTRIBUTES));
    }

    if (pcbiSrc->pUnk && cbDst >= URLMONOFFSETOF(BINDINFO, dwReserved))
    {
        pbiDest->pUnk->AddRef();
    }

    // NULL these anything fails we don't want to free the Sources resources.
    pbiDest->szExtraInfo  = NULL;
    pbiDest->szCustomVerb = NULL;    
    
    // Original BINDINFO no need to check size
    hr = CopyStgMedium( &pcbiSrc->stgmedData, &pbiDest->stgmedData );
    if (hr)
        goto Cleanup;

    // Original BINDINFO no need to check size
    if (pcbiSrc->szExtraInfo)
    {
        LPWSTR lpwstr = OLESTRDuplicate(pcbiSrc->szExtraInfo);
        if (!lpwstr)
        {
            hr = E_OUTOFMEMORY;
            goto Cleanup;
        }

        pbiDest->szExtraInfo= lpwstr;
    }

    // Original BINDINFO no need to check size
    if (pcbiSrc->szCustomVerb)
    {
        LPWSTR lpwstr = OLESTRDuplicate(pcbiSrc->szCustomVerb);
        if (!lpwstr)
        {
            hr = E_OUTOFMEMORY;
            goto Cleanup;
        }

        pbiDest->szCustomVerb= lpwstr;
    }

Cleanup:
    if (hr)
    {
        // This will set pbiDest members to zero so other function will not 
        // try to use the new BINDINFO.
        ReleaseBindInfo( pbiDest );
    }

Abort:
    return(hr);
}

