| /* | 
 |  * Copyright 2008 Piotr Caban | 
 |  * | 
 |  * 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 | 
 |  */ | 
 |  | 
 | #define COBJMACROS | 
 | #define NONAMELESSUNION | 
 |  | 
 | #include "config.h" | 
 |  | 
 | #include <stdarg.h> | 
 | #include <assert.h> | 
 | #include "windef.h" | 
 | #include "winbase.h" | 
 | #include "winuser.h" | 
 | #include "ole2.h" | 
 | #include "msxml2.h" | 
 | #include "wininet.h" | 
 | #include "urlmon.h" | 
 | #include "winreg.h" | 
 | #include "shlwapi.h" | 
 |  | 
 | #include "wine/debug.h" | 
 |  | 
 | #include "msxml_private.h" | 
 |  | 
 | WINE_DEFAULT_DEBUG_CHANNEL(msxml); | 
 |  | 
 | struct bsc_t { | 
 |     const struct IBindStatusCallbackVtbl *lpVtbl; | 
 |  | 
 |     LONG ref; | 
 |  | 
 |     void *obj; | 
 |     HRESULT (*onDataAvailable)(void*,char*,DWORD); | 
 |  | 
 |     IBinding *binding; | 
 |     IStream *memstream; | 
 | }; | 
 |  | 
 | static inline bsc_t *impl_from_IBindStatusCallback( IBindStatusCallback *iface ) | 
 | { | 
 |     return (bsc_t *)((char*)iface - FIELD_OFFSET(bsc_t, lpVtbl)); | 
 | } | 
 |  | 
 | static HRESULT WINAPI bsc_QueryInterface( | 
 |     IBindStatusCallback *iface, | 
 |     REFIID riid, | 
 |     LPVOID *ppobj ) | 
 | { | 
 |     if (IsEqualGUID(riid, &IID_IUnknown) || | 
 |         IsEqualGUID(riid, &IID_IBindStatusCallback)) | 
 |     { | 
 |         IBindStatusCallback_AddRef( iface ); | 
 |         *ppobj = iface; | 
 |         return S_OK; | 
 |     } | 
 |  | 
 |     TRACE("interface %s not implemented\n", debugstr_guid(riid)); | 
 |     return E_NOINTERFACE; | 
 | } | 
 |  | 
 | static ULONG WINAPI bsc_AddRef( | 
 |     IBindStatusCallback *iface ) | 
 | { | 
 |     bsc_t *This = impl_from_IBindStatusCallback(iface); | 
 |     LONG ref = InterlockedIncrement(&This->ref); | 
 |  | 
 |     TRACE("(%p) ref=%d\n", This, ref); | 
 |  | 
 |     return ref; | 
 | } | 
 |  | 
 | static ULONG WINAPI bsc_Release( | 
 |     IBindStatusCallback *iface ) | 
 | { | 
 |     bsc_t *This = impl_from_IBindStatusCallback(iface); | 
 |     LONG ref = InterlockedDecrement(&This->ref); | 
 |  | 
 |     TRACE("(%p) ref=%d\n", This, ref); | 
 |  | 
 |     if(!ref) { | 
 |         if (This->binding)   IBinding_Release(This->binding); | 
 |         if (This->memstream) IStream_Release(This->memstream); | 
 |         heap_free(This); | 
 |     } | 
 |  | 
 |     return ref; | 
 | } | 
 |  | 
 | static HRESULT WINAPI bsc_OnStartBinding( | 
 |         IBindStatusCallback* iface, | 
 |         DWORD dwReserved, | 
 |         IBinding* pib) | 
 | { | 
 |     bsc_t *This = impl_from_IBindStatusCallback(iface); | 
 |     HRESULT hr; | 
 |  | 
 |     TRACE("(%p)->(%x %p)\n", This, dwReserved, pib); | 
 |  | 
 |     This->binding = pib; | 
 |     IBinding_AddRef(pib); | 
 |  | 
 |     hr = CreateStreamOnHGlobal(NULL, TRUE, &This->memstream); | 
 |     if(FAILED(hr)) | 
 |         return hr; | 
 |  | 
 |     return S_OK; | 
 | } | 
 |  | 
 | static HRESULT WINAPI bsc_GetPriority( | 
 |         IBindStatusCallback* iface, | 
 |         LONG* pnPriority) | 
 | { | 
 |     return S_OK; | 
 | } | 
 |  | 
 | static HRESULT WINAPI bsc_OnLowResource( | 
 |         IBindStatusCallback* iface, | 
 |         DWORD reserved) | 
 | { | 
 |     return S_OK; | 
 | } | 
 |  | 
 | static HRESULT WINAPI bsc_OnProgress( | 
 |         IBindStatusCallback* iface, | 
 |         ULONG ulProgress, | 
 |         ULONG ulProgressMax, | 
 |         ULONG ulStatusCode, | 
 |         LPCWSTR szStatusText) | 
 | { | 
 |     return S_OK; | 
 | } | 
 |  | 
 | static HRESULT WINAPI bsc_OnStopBinding( | 
 |         IBindStatusCallback* iface, | 
 |         HRESULT hresult, | 
 |         LPCWSTR szError) | 
 | { | 
 |     bsc_t *This = impl_from_IBindStatusCallback(iface); | 
 |     HRESULT hr = S_OK; | 
 |  | 
 |     TRACE("(%p)->(%08x %s)\n", This, hresult, debugstr_w(szError)); | 
 |  | 
 |     if(This->binding) { | 
 |         IBinding_Release(This->binding); | 
 |         This->binding = NULL; | 
 |     } | 
 |  | 
 |     if(This->obj && SUCCEEDED(hresult)) { | 
 |         HGLOBAL hglobal; | 
 |         hr = GetHGlobalFromStream(This->memstream, &hglobal); | 
 |         if(SUCCEEDED(hr)) | 
 |         { | 
 |             DWORD len = GlobalSize(hglobal); | 
 |             char *ptr = GlobalLock(hglobal); | 
 |  | 
 |             hr = This->onDataAvailable(This->obj, ptr, len); | 
 |  | 
 |             GlobalUnlock(hglobal); | 
 |         } | 
 |     } | 
 |  | 
 |     return hr; | 
 | } | 
 |  | 
 | static HRESULT WINAPI bsc_GetBindInfo( | 
 |         IBindStatusCallback* iface, | 
 |         DWORD* grfBINDF, | 
 |         BINDINFO* pbindinfo) | 
 | { | 
 |     *grfBINDF = BINDF_GETNEWESTVERSION|BINDF_PULLDATA|BINDF_RESYNCHRONIZE|BINDF_PRAGMA_NO_CACHE; | 
 |  | 
 |     return S_OK; | 
 | } | 
 |  | 
 | static HRESULT WINAPI bsc_OnDataAvailable( | 
 |         IBindStatusCallback* iface, | 
 |         DWORD grfBSCF, | 
 |         DWORD dwSize, | 
 |         FORMATETC* pformatetc, | 
 |         STGMEDIUM* pstgmed) | 
 | { | 
 |     bsc_t *This = impl_from_IBindStatusCallback(iface); | 
 |     BYTE buf[4096]; | 
 |     DWORD read, written; | 
 |     HRESULT hr; | 
 |  | 
 |     TRACE("(%p)->(%x %d %p %p)\n", This, grfBSCF, dwSize, pformatetc, pstgmed); | 
 |  | 
 |     do | 
 |     { | 
 |         hr = IStream_Read(pstgmed->u.pstm, buf, sizeof(buf), &read); | 
 |         if(FAILED(hr)) | 
 |             break; | 
 |  | 
 |         hr = IStream_Write(This->memstream, buf, read, &written); | 
 |     } while(SUCCEEDED(hr) && written != 0 && read != 0); | 
 |  | 
 |     return S_OK; | 
 | } | 
 |  | 
 | static HRESULT WINAPI bsc_OnObjectAvailable( | 
 |         IBindStatusCallback* iface, | 
 |         REFIID riid, | 
 |         IUnknown* punk) | 
 | { | 
 |     return S_OK; | 
 | } | 
 |  | 
 | static const struct IBindStatusCallbackVtbl bsc_vtbl = | 
 | { | 
 |     bsc_QueryInterface, | 
 |     bsc_AddRef, | 
 |     bsc_Release, | 
 |     bsc_OnStartBinding, | 
 |     bsc_GetPriority, | 
 |     bsc_OnLowResource, | 
 |     bsc_OnProgress, | 
 |     bsc_OnStopBinding, | 
 |     bsc_GetBindInfo, | 
 |     bsc_OnDataAvailable, | 
 |     bsc_OnObjectAvailable | 
 | }; | 
 |  | 
 | HRESULT bind_url(LPCWSTR url, HRESULT (*onDataAvailable)(void*,char*,DWORD), void *obj, bsc_t **ret) | 
 | { | 
 |     WCHAR fileUrl[INTERNET_MAX_URL_LENGTH]; | 
 |     bsc_t *bsc; | 
 |     IBindCtx *pbc; | 
 |     HRESULT hr; | 
 |  | 
 |     TRACE("%s\n", debugstr_w(url)); | 
 |  | 
 |     if(!PathIsURLW(url)) | 
 |     { | 
 |         WCHAR fullpath[MAX_PATH]; | 
 |         DWORD needed = sizeof(fileUrl)/sizeof(WCHAR); | 
 |  | 
 |         if(!PathSearchAndQualifyW(url, fullpath, sizeof(fullpath)/sizeof(WCHAR))) | 
 |         { | 
 |             WARN("can't find path\n"); | 
 |             return E_FAIL; | 
 |         } | 
 |  | 
 |         if(FAILED(UrlCreateFromPathW(url, fileUrl, &needed, 0))) | 
 |         { | 
 |             ERR("can't create url from path\n"); | 
 |             return E_FAIL; | 
 |         } | 
 |         url = fileUrl; | 
 |     } | 
 |  | 
 |     hr = CreateBindCtx(0, &pbc); | 
 |     if(FAILED(hr)) | 
 |         return hr; | 
 |  | 
 |     bsc = heap_alloc(sizeof(bsc_t)); | 
 |  | 
 |     bsc->lpVtbl = &bsc_vtbl; | 
 |     bsc->ref = 1; | 
 |     bsc->obj = obj; | 
 |     bsc->onDataAvailable = onDataAvailable; | 
 |     bsc->binding = NULL; | 
 |     bsc->memstream = NULL; | 
 |  | 
 |     hr = RegisterBindStatusCallback(pbc, (IBindStatusCallback*)&bsc->lpVtbl, NULL, 0); | 
 |     if(SUCCEEDED(hr)) | 
 |     { | 
 |         IMoniker *moniker; | 
 |  | 
 |         hr = CreateURLMoniker(NULL, url, &moniker); | 
 |         if(SUCCEEDED(hr)) | 
 |         { | 
 |             IStream *stream; | 
 |             hr = IMoniker_BindToStorage(moniker, pbc, NULL, &IID_IStream, (LPVOID*)&stream); | 
 |             IMoniker_Release(moniker); | 
 |             if(stream) | 
 |                 IStream_Release(stream); | 
 |         } | 
 |         IBindCtx_Release(pbc); | 
 |     } | 
 |  | 
 |     if(FAILED(hr)) | 
 |     { | 
 |         IBindStatusCallback_Release((IBindStatusCallback*)&bsc->lpVtbl); | 
 |         bsc = NULL; | 
 |     } | 
 |  | 
 |     *ret = bsc; | 
 |     return hr; | 
 | } | 
 |  | 
 | void detach_bsc(bsc_t *bsc) | 
 | { | 
 |     if(bsc->binding) | 
 |         IBinding_Abort(bsc->binding); | 
 |  | 
 |     bsc->obj = NULL; | 
 |     IBindStatusCallback_Release((IBindStatusCallback*)&bsc->lpVtbl); | 
 | } |