/*
 *	OLE2 COM objects
 *
 *	Copyright 1998 Eric Kohl
 *      Copyright 1999 Francis Beaudet
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */


#include <stdarg.h>
#include <string.h>

#define COBJMACROS
#define NONAMELESSUNION

#include "windef.h"
#include "winbase.h"
#include "winuser.h"
#include "winerror.h"
#include "wine/debug.h"
#include "ole2.h"

#include "compobj_private.h"

WINE_DEFAULT_DEBUG_CHANNEL(ole);

#define INITIAL_SINKS 10

static void release_statdata(STATDATA *data)
{
    if(data->formatetc.ptd)
    {
        CoTaskMemFree(data->formatetc.ptd);
        data->formatetc.ptd = NULL;
    }

    if(data->pAdvSink)
    {
        IAdviseSink_Release(data->pAdvSink);
        data->pAdvSink = NULL;
    }
}

static HRESULT copy_statdata(STATDATA *dst, const STATDATA *src)
{
    HRESULT hr;

    hr = copy_formatetc( &dst->formatetc, &src->formatetc );
    if (FAILED(hr)) return hr;
    dst->advf = src->advf;
    dst->pAdvSink = src->pAdvSink;
    if (dst->pAdvSink) IAdviseSink_AddRef( dst->pAdvSink );
    dst->dwConnection = src->dwConnection;
    return S_OK;
}

/**************************************************************************
 *  EnumSTATDATA Implementation
 */

typedef struct
{
    IEnumSTATDATA IEnumSTATDATA_iface;
    LONG ref;

    ULONG index;
    DWORD num_of_elems;
    STATDATA *statdata;
    IUnknown *holder;
} EnumSTATDATA;

static inline EnumSTATDATA *impl_from_IEnumSTATDATA(IEnumSTATDATA *iface)
{
    return CONTAINING_RECORD(iface, EnumSTATDATA, IEnumSTATDATA_iface);
}

static HRESULT WINAPI EnumSTATDATA_QueryInterface(IEnumSTATDATA *iface, REFIID riid, void **ppv)
{
    TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
    if (IsEqualIID(riid, &IID_IUnknown) ||
        IsEqualIID(riid, &IID_IEnumSTATDATA))
    {
        IEnumSTATDATA_AddRef(iface);
        *ppv = iface;
        return S_OK;
    }
    return E_NOINTERFACE;
}

static ULONG WINAPI EnumSTATDATA_AddRef(IEnumSTATDATA *iface)
{
    EnumSTATDATA *This = impl_from_IEnumSTATDATA(iface);
    TRACE("()\n");
    return InterlockedIncrement(&This->ref);
}

static ULONG WINAPI EnumSTATDATA_Release(IEnumSTATDATA *iface)
{
    EnumSTATDATA *This = impl_from_IEnumSTATDATA(iface);
    LONG refs = InterlockedDecrement(&This->ref);
    TRACE("()\n");
    if (!refs)
    {
        DWORD i;
        for(i = 0; i < This->num_of_elems; i++)
            release_statdata(This->statdata + i);
        HeapFree(GetProcessHeap(), 0, This->statdata);
        if (This->holder) IUnknown_Release(This->holder);
        HeapFree(GetProcessHeap(), 0, This);
    }
    return refs;
}

static HRESULT WINAPI EnumSTATDATA_Next(IEnumSTATDATA *iface, ULONG num, LPSTATDATA data,
                                        ULONG *fetched)
{
    EnumSTATDATA *This = impl_from_IEnumSTATDATA(iface);
    DWORD count = 0;
    HRESULT hr = S_OK;

    TRACE("(%d, %p, %p)\n", num, data, fetched);

    while(num--)
    {
        if (This->index >= This->num_of_elems)
        {
            hr = S_FALSE;
            break;
        }

        copy_statdata(data + count, This->statdata + This->index);

        count++;
        This->index++;
    }

    if (fetched) *fetched = count;

    return hr;
}

static HRESULT WINAPI EnumSTATDATA_Skip(IEnumSTATDATA *iface, ULONG num)
{
    EnumSTATDATA *This = impl_from_IEnumSTATDATA(iface);

    TRACE("(%d)\n", num);

    if(This->index + num >= This->num_of_elems)
    {
        This->index = This->num_of_elems;
        return S_FALSE;
    }

    This->index += num;
    return S_OK;
}

static HRESULT WINAPI EnumSTATDATA_Reset(IEnumSTATDATA *iface)
{
    EnumSTATDATA *This = impl_from_IEnumSTATDATA(iface);

    TRACE("()\n");

    This->index = 0;
    return S_OK;
}

static HRESULT WINAPI EnumSTATDATA_Clone(IEnumSTATDATA *iface, IEnumSTATDATA **ppenum)
{
    EnumSTATDATA *This = impl_from_IEnumSTATDATA(iface);

    return EnumSTATDATA_Construct(This->holder, This->index, This->num_of_elems, This->statdata,
                                  TRUE, ppenum);
}

static const IEnumSTATDATAVtbl EnumSTATDATA_VTable =
{
    EnumSTATDATA_QueryInterface,
    EnumSTATDATA_AddRef,
    EnumSTATDATA_Release,
    EnumSTATDATA_Next,
    EnumSTATDATA_Skip,
    EnumSTATDATA_Reset,
    EnumSTATDATA_Clone
};

HRESULT EnumSTATDATA_Construct(IUnknown *holder, ULONG index, DWORD array_len, STATDATA *data,
                               BOOL copy, IEnumSTATDATA **ppenum)
{
    EnumSTATDATA *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
    DWORD i, count;

    if (!This) return E_OUTOFMEMORY;

    This->IEnumSTATDATA_iface.lpVtbl = &EnumSTATDATA_VTable;
    This->ref = 1;
    This->index = index;

    if (copy)
    {
        This->statdata = HeapAlloc(GetProcessHeap(), 0, array_len * sizeof(*This->statdata));
        if(!This->statdata)
        {
            HeapFree(GetProcessHeap(), 0, This);
            return E_OUTOFMEMORY;
        }

        for(i = 0, count = 0; i < array_len; i++)
        {
            if(data[i].pAdvSink)
            {
                copy_statdata(This->statdata + count, data + i);
                count++;
            }
        }
    }
    else
    {
        This->statdata = data;
        count = array_len;
    }

    This->num_of_elems = count;
    This->holder = holder;
    if (holder) IUnknown_AddRef(holder);
    *ppenum = &This->IEnumSTATDATA_iface;
    return S_OK;
}

/**************************************************************************
 *  OleAdviseHolder Implementation
 */
typedef struct
{
    IOleAdviseHolder IOleAdviseHolder_iface;

    LONG ref;

    DWORD max_cons;
    STATDATA *connections;
} OleAdviseHolderImpl;

static inline OleAdviseHolderImpl *impl_from_IOleAdviseHolder(IOleAdviseHolder *iface)
{
    return CONTAINING_RECORD(iface, OleAdviseHolderImpl, IOleAdviseHolder_iface);
}

/**************************************************************************
 *  OleAdviseHolderImpl_Destructor
 */
static void OleAdviseHolderImpl_Destructor(OleAdviseHolderImpl *This)
{
    DWORD index;
    TRACE("%p\n", This);

    for (index = 0; index < This->max_cons; index++)
    {
        if (This->connections[index].pAdvSink != NULL)
            release_statdata(This->connections + index);
    }

    HeapFree(GetProcessHeap(), 0, This->connections);
    HeapFree(GetProcessHeap(), 0, This);
}

/**************************************************************************
 *  OleAdviseHolderImpl_QueryInterface
 */
static HRESULT WINAPI OleAdviseHolderImpl_QueryInterface(IOleAdviseHolder *iface,
                                                         REFIID iid, void **obj)
{
  OleAdviseHolderImpl *This = impl_from_IOleAdviseHolder(iface);
  TRACE("(%p)->(%s,%p)\n",This, debugstr_guid(iid), obj);

  if (obj == NULL)
    return E_POINTER;

  *obj = NULL;

  if (IsEqualIID(iid, &IID_IUnknown) ||
      IsEqualIID(iid, &IID_IOleAdviseHolder))
  {
    *obj = &This->IOleAdviseHolder_iface;
  }

  if(*obj == NULL)
    return E_NOINTERFACE;

  IUnknown_AddRef((IUnknown*)*obj);

  return S_OK;
}

/******************************************************************************
 * OleAdviseHolderImpl_AddRef
 */
static ULONG WINAPI OleAdviseHolderImpl_AddRef(IOleAdviseHolder *iface)
{
  OleAdviseHolderImpl *This = impl_from_IOleAdviseHolder(iface);
  ULONG ref = InterlockedIncrement(&This->ref);

  TRACE("(%p)->(ref=%d)\n", This, ref - 1);

  return ref;
}

/******************************************************************************
 * OleAdviseHolderImpl_Release
 */
static ULONG WINAPI OleAdviseHolderImpl_Release(IOleAdviseHolder *iface)
{
  OleAdviseHolderImpl *This = impl_from_IOleAdviseHolder(iface);
  ULONG ref;
  TRACE("(%p)->(ref=%d)\n", This, This->ref);
  ref = InterlockedDecrement(&This->ref);

  if (ref == 0) OleAdviseHolderImpl_Destructor(This);

  return ref;
}

/******************************************************************************
 * OleAdviseHolderImpl_Advise
 */
static HRESULT WINAPI OleAdviseHolderImpl_Advise(IOleAdviseHolder *iface,
                                                 IAdviseSink *pAdvise,
                                                 DWORD *pdwConnection)
{
  DWORD index;
  OleAdviseHolderImpl *This = impl_from_IOleAdviseHolder(iface);
  STATDATA new_conn;
  static const FORMATETC empty_fmtetc = {0, NULL, 0, -1, 0};

  TRACE("(%p)->(%p, %p)\n", This, pAdvise, pdwConnection);

  if (pdwConnection==NULL)
    return E_POINTER;

  *pdwConnection = 0;

  for (index = 0; index < This->max_cons; index++)
  {
    if (This->connections[index].pAdvSink == NULL)
      break;
  }

  if (index == This->max_cons)
  {
    This->max_cons += INITIAL_SINKS;
    This->connections = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->connections,
                                    This->max_cons * sizeof(*This->connections));
  }

  new_conn.pAdvSink = pAdvise;
  new_conn.advf = 0;
  new_conn.formatetc = empty_fmtetc;
  new_conn.dwConnection = index + 1; /* 0 is not a valid cookie, so increment the index */

  copy_statdata(This->connections + index, &new_conn);

  *pdwConnection = new_conn.dwConnection;

  return S_OK;
}

/******************************************************************************
 * OleAdviseHolderImpl_Unadvise
 */
static HRESULT WINAPI OleAdviseHolderImpl_Unadvise(IOleAdviseHolder *iface,
                                                   DWORD dwConnection)
{
  OleAdviseHolderImpl *This = impl_from_IOleAdviseHolder(iface);
  DWORD index;

  TRACE("(%p)->(%u)\n", This, dwConnection);

  /* The connection number is 1 more than the index, see OleAdviseHolder_Advise */
  index = dwConnection - 1;

  if (index >= This->max_cons || This->connections[index].pAdvSink == NULL)
     return OLE_E_NOCONNECTION;

  release_statdata(This->connections + index);

  return S_OK;
}

/******************************************************************************
 * OleAdviseHolderImpl_EnumAdvise
 */
static HRESULT WINAPI OleAdviseHolderImpl_EnumAdvise(IOleAdviseHolder *iface, IEnumSTATDATA **enum_advise)
{
    OleAdviseHolderImpl *This = impl_from_IOleAdviseHolder(iface);
    IUnknown *unk;
    HRESULT hr;

    TRACE("(%p)->(%p)\n", This, enum_advise);

    IOleAdviseHolder_QueryInterface(iface, &IID_IUnknown, (void**)&unk);
    hr = EnumSTATDATA_Construct(unk, 0, This->max_cons, This->connections, TRUE, enum_advise);
    IUnknown_Release(unk);
    return hr;
}

/******************************************************************************
 * OleAdviseHolderImpl_SendOnRename
 */
static HRESULT WINAPI OleAdviseHolderImpl_SendOnRename(IOleAdviseHolder *iface, IMoniker *pmk)
{
    IEnumSTATDATA *pEnum;
    HRESULT hr;

    TRACE("(%p)->(%p)\n", iface, pmk);

    hr = IOleAdviseHolder_EnumAdvise(iface, &pEnum);
    if (SUCCEEDED(hr))
    {
        STATDATA statdata;
        while (IEnumSTATDATA_Next(pEnum, 1, &statdata, NULL) == S_OK)
        {
            IAdviseSink_OnRename(statdata.pAdvSink, pmk);

            IAdviseSink_Release(statdata.pAdvSink);
        }
        IEnumSTATDATA_Release(pEnum);
    }

    return hr;
}

/******************************************************************************
 * OleAdviseHolderImpl_SendOnSave
 */
static HRESULT WINAPI OleAdviseHolderImpl_SendOnSave(IOleAdviseHolder *iface)
{
    IEnumSTATDATA *pEnum;
    HRESULT hr;

    TRACE("(%p)->()\n", iface);

    hr = IOleAdviseHolder_EnumAdvise(iface, &pEnum);
    if (SUCCEEDED(hr))
    {
        STATDATA statdata;
        while (IEnumSTATDATA_Next(pEnum, 1, &statdata, NULL) == S_OK)
        {
            IAdviseSink_OnSave(statdata.pAdvSink);

            IAdviseSink_Release(statdata.pAdvSink);
        }
        IEnumSTATDATA_Release(pEnum);
    }

    return hr;
}

/******************************************************************************
 * OleAdviseHolderImpl_SendOnClose
 */
static HRESULT WINAPI OleAdviseHolderImpl_SendOnClose(IOleAdviseHolder *iface)
{
    IEnumSTATDATA *pEnum;
    HRESULT hr;

    TRACE("(%p)->()\n", iface);

    hr = IOleAdviseHolder_EnumAdvise(iface, &pEnum);
    if (SUCCEEDED(hr))
    {
        STATDATA statdata;
        while (IEnumSTATDATA_Next(pEnum, 1, &statdata, NULL) == S_OK)
        {
            IAdviseSink_OnClose(statdata.pAdvSink);

            IAdviseSink_Release(statdata.pAdvSink);
        }
        IEnumSTATDATA_Release(pEnum);
    }

    return hr;
}

/**************************************************************************
 *  OleAdviseHolderImpl_VTable
 */
static const IOleAdviseHolderVtbl oahvt =
{
    OleAdviseHolderImpl_QueryInterface,
    OleAdviseHolderImpl_AddRef,
    OleAdviseHolderImpl_Release,
    OleAdviseHolderImpl_Advise,
    OleAdviseHolderImpl_Unadvise,
    OleAdviseHolderImpl_EnumAdvise,
    OleAdviseHolderImpl_SendOnRename,
    OleAdviseHolderImpl_SendOnSave,
    OleAdviseHolderImpl_SendOnClose
};

/**************************************************************************
 *  OleAdviseHolderImpl_Constructor
 */

static IOleAdviseHolder *OleAdviseHolderImpl_Constructor(void)
{
  OleAdviseHolderImpl* lpoah;

  lpoah = HeapAlloc(GetProcessHeap(), 0, sizeof(OleAdviseHolderImpl));

  lpoah->IOleAdviseHolder_iface.lpVtbl = &oahvt;
  lpoah->ref = 1;
  lpoah->max_cons = INITIAL_SINKS;
  lpoah->connections = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
                                 lpoah->max_cons * sizeof(*lpoah->connections));

  TRACE("returning %p\n",  &lpoah->IOleAdviseHolder_iface);
  return &lpoah->IOleAdviseHolder_iface;
}

/**************************************************************************
 *  DataAdviseHolder Implementation
 */
typedef struct
{
  IDataAdviseHolder     IDataAdviseHolder_iface;

  LONG                  ref;
  DWORD                 maxCons;
  STATDATA*             connections;
  DWORD*                remote_connections;
  IDataObject*          delegate;
} DataAdviseHolder;

/* this connection has also has been advised to the delegate data object */
#define WINE_ADVF_REMOTE 0x80000000

static inline DataAdviseHolder *impl_from_IDataAdviseHolder(IDataAdviseHolder *iface)
{
    return CONTAINING_RECORD(iface, DataAdviseHolder, IDataAdviseHolder_iface);
}

/******************************************************************************
 * DataAdviseHolder_Destructor
 */
static void DataAdviseHolder_Destructor(DataAdviseHolder* ptrToDestroy)
{
  DWORD index;
  TRACE("%p\n", ptrToDestroy);

  for (index = 0; index < ptrToDestroy->maxCons; index++)
  {
    if (ptrToDestroy->connections[index].pAdvSink != NULL)
    {
      if (ptrToDestroy->delegate && 
          (ptrToDestroy->connections[index].advf & WINE_ADVF_REMOTE))
        IDataObject_DUnadvise(ptrToDestroy->delegate,
          ptrToDestroy->remote_connections[index]);

      release_statdata(ptrToDestroy->connections + index);
    }
  }

  HeapFree(GetProcessHeap(), 0, ptrToDestroy->remote_connections);
  HeapFree(GetProcessHeap(), 0, ptrToDestroy->connections);
  HeapFree(GetProcessHeap(), 0, ptrToDestroy);
}

/************************************************************************
 * DataAdviseHolder_QueryInterface (IUnknown)
 */
static HRESULT WINAPI DataAdviseHolder_QueryInterface(IDataAdviseHolder *iface,
                                                      REFIID riid, void **ppvObject)
{
  DataAdviseHolder *This = impl_from_IDataAdviseHolder(iface);
  TRACE("(%p)->(%s,%p)\n",This,debugstr_guid(riid),ppvObject);

  if ( (This==0) || (ppvObject==0) )
    return E_INVALIDARG;

  *ppvObject = 0;

  if ( IsEqualIID(&IID_IUnknown, riid) ||
       IsEqualIID(&IID_IDataAdviseHolder, riid)  )
  {
    *ppvObject = iface;
  }

  if ((*ppvObject)==0)
  {
    return E_NOINTERFACE;
  }

  IUnknown_AddRef((IUnknown*)*ppvObject);
  return S_OK;
}

/************************************************************************
 * DataAdviseHolder_AddRef (IUnknown)
 */
static ULONG WINAPI DataAdviseHolder_AddRef(IDataAdviseHolder *iface)
{
  DataAdviseHolder *This = impl_from_IDataAdviseHolder(iface);
  TRACE("(%p) (ref=%d)\n", This, This->ref);
  return InterlockedIncrement(&This->ref);
}

/************************************************************************
 * DataAdviseHolder_Release (IUnknown)
 */
static ULONG WINAPI DataAdviseHolder_Release(IDataAdviseHolder *iface)
{
  DataAdviseHolder *This = impl_from_IDataAdviseHolder(iface);
  ULONG ref;
  TRACE("(%p) (ref=%d)\n", This, This->ref);

  ref = InterlockedDecrement(&This->ref);
  if (ref==0) DataAdviseHolder_Destructor(This);

  return ref;
}

/************************************************************************
 * DataAdviseHolder_Advise
 *
 */
static HRESULT WINAPI DataAdviseHolder_Advise(IDataAdviseHolder *iface,
                                              IDataObject *pDataObject, FORMATETC *pFetc,
                                              DWORD advf, IAdviseSink *pAdvise,
                                              DWORD *pdwConnection)
{
  DWORD index;
  STATDATA new_conn;
  DataAdviseHolder *This = impl_from_IDataAdviseHolder(iface);

  TRACE("(%p)->(%p, %p, %08x, %p, %p)\n", This, pDataObject, pFetc, advf,
	pAdvise, pdwConnection);

  if (pdwConnection==NULL)
    return E_POINTER;

  *pdwConnection = 0;

  for (index = 0; index < This->maxCons; index++)
  {
    if (This->connections[index].pAdvSink == NULL)
      break;
  }

  if (index == This->maxCons)
  {
    This->maxCons+=INITIAL_SINKS;
    This->connections = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
                                    This->connections,
                                    This->maxCons * sizeof(*This->connections));
    This->remote_connections = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
                                           This->remote_connections,
                                           This->maxCons * sizeof(*This->remote_connections));
  }

  new_conn.pAdvSink = pAdvise;
  new_conn.advf = advf & ~WINE_ADVF_REMOTE;
  new_conn.formatetc = *pFetc;
  new_conn.dwConnection = index + 1; /* 0 is not a valid cookie, so increment the index */

  copy_statdata(This->connections + index, &new_conn);

  if (This->connections[index].pAdvSink != NULL)
  {
    /* if we are already connected advise the remote object */
    if (This->delegate)
    {
        HRESULT hr;

        hr = IDataObject_DAdvise(This->delegate, &new_conn.formatetc,
                                 new_conn.advf, new_conn.pAdvSink,
                                 &This->remote_connections[index]);
        if (FAILED(hr))
        {
            IDataAdviseHolder_Unadvise(iface, new_conn.dwConnection);
            return hr;
        }
        This->connections[index].advf |= WINE_ADVF_REMOTE;
    }
    else if(advf & ADVF_PRIMEFIRST)
      /* only do this if we have no delegate, since in the above case the
       * delegate will do the priming for us */
      IDataAdviseHolder_SendOnDataChange(iface, pDataObject, 0, advf);
  }

  *pdwConnection = new_conn.dwConnection;

  return S_OK;
}

/******************************************************************************
 * DataAdviseHolder_Unadvise
 */
static HRESULT WINAPI DataAdviseHolder_Unadvise(IDataAdviseHolder *iface,
                                                DWORD dwConnection)
{
  DataAdviseHolder *This = impl_from_IDataAdviseHolder(iface);
  DWORD index;
  TRACE("(%p)->(%u)\n", This, dwConnection);

  /* The connection number is 1 more than the index, see DataAdviseHolder_Advise */
  index = dwConnection - 1;

  if (index >= This->maxCons || This->connections[index].pAdvSink == NULL)
     return OLE_E_NOCONNECTION;

  if (This->delegate && This->connections[index].advf & WINE_ADVF_REMOTE)
  {
    IDataObject_DUnadvise(This->delegate, This->remote_connections[index]);
    This->remote_connections[index] = 0;
  }

  release_statdata(This->connections + index);

  return S_OK;
}

/******************************************************************************
 * DataAdviseHolder_EnumAdvise
 */
static HRESULT WINAPI DataAdviseHolder_EnumAdvise(IDataAdviseHolder *iface,
                                                  IEnumSTATDATA **enum_advise)
{
    DataAdviseHolder *This = impl_from_IDataAdviseHolder(iface);
    IUnknown *unk;
    HRESULT hr;

    TRACE("(%p)->(%p)\n", This, enum_advise);

    IDataAdviseHolder_QueryInterface(iface, &IID_IUnknown, (void**)&unk);
    hr = EnumSTATDATA_Construct(unk, 0, This->maxCons, This->connections, TRUE, enum_advise);
    IUnknown_Release(unk);
    return hr;
}

/******************************************************************************
 * DataAdviseHolder_SendOnDataChange
 */
static HRESULT WINAPI DataAdviseHolder_SendOnDataChange(IDataAdviseHolder *iface,
                                                        IDataObject *data_obj,
                                                        DWORD dwReserved, DWORD advf)
{
    IEnumSTATDATA *pEnum;
    HRESULT hr;

    TRACE("(%p)->(%p, %08x, %08x)\n", iface, data_obj, dwReserved, advf);

    hr = IDataAdviseHolder_EnumAdvise(iface, &pEnum);
    if (SUCCEEDED(hr))
    {
        STATDATA statdata;
        while (IEnumSTATDATA_Next(pEnum, 1, &statdata, NULL) == S_OK)
        {
            STGMEDIUM stg;
            stg.tymed = TYMED_NULL;
            stg.u.pstg = NULL;
            stg.pUnkForRelease = NULL;

            if(!(statdata.advf & ADVF_NODATA))
            {
                hr = IDataObject_GetData(data_obj, &statdata.formatetc, &stg);
            }

            IAdviseSink_OnDataChange(statdata.pAdvSink, &statdata.formatetc, &stg);

            if(statdata.advf & ADVF_ONLYONCE)
            {
                IDataAdviseHolder_Unadvise(iface, statdata.dwConnection);
            }

            release_statdata(&statdata);
        }
        IEnumSTATDATA_Release(pEnum);
    }

    return S_OK;
}

/**************************************************************************
 *  DataAdviseHolderImpl_VTable
 */
static const IDataAdviseHolderVtbl DataAdviseHolderImpl_VTable =
{
  DataAdviseHolder_QueryInterface,
  DataAdviseHolder_AddRef,
  DataAdviseHolder_Release,
  DataAdviseHolder_Advise,
  DataAdviseHolder_Unadvise,
  DataAdviseHolder_EnumAdvise,
  DataAdviseHolder_SendOnDataChange
};

HRESULT DataAdviseHolder_OnConnect(IDataAdviseHolder *iface, IDataObject *pDelegate)
{
  DataAdviseHolder *This = impl_from_IDataAdviseHolder(iface);
  DWORD index;
  HRESULT hr = S_OK;

  for(index = 0; index < This->maxCons; index++)
  {
    if(This->connections[index].pAdvSink != NULL)
    {
      hr = IDataObject_DAdvise(pDelegate, &This->connections[index].formatetc,
                               This->connections[index].advf,
                               This->connections[index].pAdvSink,
                               &This->remote_connections[index]);
      if (FAILED(hr)) break;
      This->connections[index].advf |= WINE_ADVF_REMOTE;
    }
  }
  This->delegate = pDelegate;
  return hr;
}

void DataAdviseHolder_OnDisconnect(IDataAdviseHolder *iface)
{
  DataAdviseHolder *This = impl_from_IDataAdviseHolder(iface);
  DWORD index;

  for(index = 0; index < This->maxCons; index++)
  {
    if((This->connections[index].pAdvSink != NULL) &&
       (This->connections[index].advf & WINE_ADVF_REMOTE))
    {
      IDataObject_DUnadvise(This->delegate, This->remote_connections[index]);
      This->remote_connections[index] = 0;
      This->connections[index].advf &= ~WINE_ADVF_REMOTE;
    }
  }
  This->delegate = NULL;
}

/******************************************************************************
 * DataAdviseHolder_Constructor
 */
static IDataAdviseHolder *DataAdviseHolder_Constructor(void)
{
  DataAdviseHolder* newHolder;

  newHolder = HeapAlloc(GetProcessHeap(), 0, sizeof(DataAdviseHolder));

  newHolder->IDataAdviseHolder_iface.lpVtbl = &DataAdviseHolderImpl_VTable;
  newHolder->ref = 1;
  newHolder->maxCons = INITIAL_SINKS;
  newHolder->connections = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
                                     newHolder->maxCons * sizeof(*newHolder->connections));
  newHolder->remote_connections = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
                                            newHolder->maxCons * sizeof(*newHolder->remote_connections));
  newHolder->delegate = NULL;

  TRACE("returning %p\n", &newHolder->IDataAdviseHolder_iface);
  return &newHolder->IDataAdviseHolder_iface;
}

/***********************************************************************
 * API functions
 */

/***********************************************************************
 * CreateOleAdviseHolder [OLE32.@]
 */
HRESULT WINAPI CreateOleAdviseHolder(IOleAdviseHolder **ppOAHolder)
{
  TRACE("(%p)\n", ppOAHolder);

  if (ppOAHolder==NULL)
    return E_POINTER;

  *ppOAHolder = OleAdviseHolderImpl_Constructor ();

  if (*ppOAHolder != NULL)
    return S_OK;

  return E_OUTOFMEMORY;
}

/******************************************************************************
 *              CreateDataAdviseHolder        [OLE32.@]
 */
HRESULT WINAPI CreateDataAdviseHolder(IDataAdviseHolder **ppDAHolder)
{
  TRACE("(%p)\n", ppDAHolder);

  if (ppDAHolder==NULL)
    return E_POINTER;

  *ppDAHolder = DataAdviseHolder_Constructor();

  if (*ppDAHolder != NULL)
    return S_OK;

  return E_OUTOFMEMORY;
}