| /* | 
 |  * Copyright 2008 Jacek Caban for CodeWeavers | 
 |  * | 
 |  * 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 "urlmon_main.h" | 
 | #include "wine/debug.h" | 
 |  | 
 | WINE_DEFAULT_DEBUG_CHANNEL(urlmon); | 
 |  | 
 | typedef struct { | 
 |     IBindStatusCallback IBindStatusCallback_iface; | 
 |     IServiceProvider    IServiceProvider_iface; | 
 |  | 
 |     LONG ref; | 
 |  | 
 |     IBindStatusCallback *callback; | 
 |     LPWSTR file_name; | 
 |     LPWSTR cache_file; | 
 | } DownloadBSC; | 
 |  | 
 | static inline DownloadBSC *impl_from_IBindStatusCallback(IBindStatusCallback *iface) | 
 | { | 
 |     return CONTAINING_RECORD(iface, DownloadBSC, IBindStatusCallback_iface); | 
 | } | 
 |  | 
 | static inline DownloadBSC *impl_from_IServiceProvider(IServiceProvider *iface) | 
 | { | 
 |     return CONTAINING_RECORD(iface, DownloadBSC, IServiceProvider_iface); | 
 | } | 
 |  | 
 | static HRESULT WINAPI DownloadBSC_QueryInterface(IBindStatusCallback *iface, | 
 |         REFIID riid, void **ppv) | 
 | { | 
 |     DownloadBSC *This = impl_from_IBindStatusCallback(iface); | 
 |  | 
 |     *ppv = NULL; | 
 |  | 
 |     if(IsEqualGUID(&IID_IUnknown, riid)) { | 
 |         TRACE("(%p)->(IID_IUnknown, %p)\n", This, ppv); | 
 |         *ppv = &This->IBindStatusCallback_iface; | 
 |     }else if(IsEqualGUID(&IID_IBindStatusCallback, riid)) { | 
 |         TRACE("(%p)->(IID_IBindStatusCallback, %p)\n", This, ppv); | 
 |         *ppv = &This->IBindStatusCallback_iface; | 
 |     }else if(IsEqualGUID(&IID_IServiceProvider, riid)) { | 
 |         TRACE("(%p)->(IID_IServiceProvider, %p)\n", This, ppv); | 
 |         *ppv = &This->IServiceProvider_iface; | 
 |     } | 
 |  | 
 |     if(*ppv) { | 
 |         IBindStatusCallback_AddRef((IUnknown*)*ppv); | 
 |         return S_OK; | 
 |     } | 
 |  | 
 |     TRACE("Unsupported riid = %s\n", debugstr_guid(riid)); | 
 |     return E_NOINTERFACE; | 
 | } | 
 |  | 
 | static ULONG WINAPI DownloadBSC_AddRef(IBindStatusCallback *iface) | 
 | { | 
 |     DownloadBSC *This = impl_from_IBindStatusCallback(iface); | 
 |     LONG ref = InterlockedIncrement(&This->ref); | 
 |  | 
 |     TRACE("(%p) ref = %d\n", This, ref); | 
 |  | 
 |     return ref; | 
 | } | 
 |  | 
 | static ULONG WINAPI DownloadBSC_Release(IBindStatusCallback *iface) | 
 | { | 
 |     DownloadBSC *This = impl_from_IBindStatusCallback(iface); | 
 |     LONG ref = InterlockedDecrement(&This->ref); | 
 |  | 
 |     TRACE("(%p) ref = %d\n", This, ref); | 
 |  | 
 |     if(!ref) { | 
 |         if(This->callback) | 
 |             IBindStatusCallback_Release(This->callback); | 
 |         heap_free(This->file_name); | 
 |         heap_free(This->cache_file); | 
 |         heap_free(This); | 
 |     } | 
 |  | 
 |     return ref; | 
 | } | 
 |  | 
 | static HRESULT WINAPI DownloadBSC_OnStartBinding(IBindStatusCallback *iface, | 
 |         DWORD dwReserved, IBinding *pbind) | 
 | { | 
 |     DownloadBSC *This = impl_from_IBindStatusCallback(iface); | 
 |  | 
 |     TRACE("(%p)->(%d %p)\n", This, dwReserved, pbind); | 
 |  | 
 |     if(This->callback) | 
 |         IBindStatusCallback_OnStartBinding(This->callback, dwReserved, pbind); | 
 |  | 
 |     return S_OK; | 
 | } | 
 |  | 
 | static HRESULT WINAPI DownloadBSC_GetPriority(IBindStatusCallback *iface, LONG *pnPriority) | 
 | { | 
 |     DownloadBSC *This = impl_from_IBindStatusCallback(iface); | 
 |     FIXME("(%p)->(%p)\n", This, pnPriority); | 
 |     return E_NOTIMPL; | 
 | } | 
 |  | 
 | static HRESULT WINAPI DownloadBSC_OnLowResource(IBindStatusCallback *iface, DWORD reserved) | 
 | { | 
 |     DownloadBSC *This = impl_from_IBindStatusCallback(iface); | 
 |     FIXME("(%p)->(%d)\n", This, reserved); | 
 |     return E_NOTIMPL; | 
 | } | 
 |  | 
 | static HRESULT on_progress(DownloadBSC *This, ULONG progress, ULONG progress_max, ULONG status_code, LPCWSTR status_text) | 
 | { | 
 |     HRESULT hres; | 
 |  | 
 |     if(!This->callback) | 
 |         return S_OK; | 
 |  | 
 |     hres = IBindStatusCallback_OnProgress(This->callback, progress, progress_max, status_code, status_text); | 
 |     return hres; | 
 | } | 
 |  | 
 | static HRESULT WINAPI DownloadBSC_OnProgress(IBindStatusCallback *iface, ULONG ulProgress, | 
 |         ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText) | 
 | { | 
 |     DownloadBSC *This = impl_from_IBindStatusCallback(iface); | 
 |     HRESULT hres = S_OK; | 
 |  | 
 |     TRACE("%p)->(%u %u %u %s)\n", This, ulProgress, ulProgressMax, ulStatusCode, | 
 |             debugstr_w(szStatusText)); | 
 |  | 
 |     switch(ulStatusCode) { | 
 |     case BINDSTATUS_CONNECTING: | 
 |     case BINDSTATUS_BEGINDOWNLOADDATA: | 
 |     case BINDSTATUS_DOWNLOADINGDATA: | 
 |     case BINDSTATUS_ENDDOWNLOADDATA: | 
 |     case BINDSTATUS_SENDINGREQUEST: | 
 |     case BINDSTATUS_MIMETYPEAVAILABLE: | 
 |         hres = on_progress(This, ulProgress, ulProgressMax, ulStatusCode, szStatusText); | 
 |         break; | 
 |  | 
 |     case BINDSTATUS_CACHEFILENAMEAVAILABLE: | 
 |         hres = on_progress(This, ulProgress, ulProgressMax, ulStatusCode, szStatusText); | 
 |         This->cache_file = heap_strdupW(szStatusText); | 
 |         break; | 
 |  | 
 |     case BINDSTATUS_FINDINGRESOURCE: /* FIXME */ | 
 |         break; | 
 |  | 
 |     default: | 
 |         FIXME("Unsupported status %u\n", ulStatusCode); | 
 |     } | 
 |  | 
 |     return hres; | 
 | } | 
 |  | 
 | static HRESULT WINAPI DownloadBSC_OnStopBinding(IBindStatusCallback *iface, | 
 |         HRESULT hresult, LPCWSTR szError) | 
 | { | 
 |     DownloadBSC *This = impl_from_IBindStatusCallback(iface); | 
 |  | 
 |     TRACE("(%p)->(%08x %s)\n", This, hresult, debugstr_w(szError)); | 
 |  | 
 |     if(This->file_name) { | 
 |         if(This->cache_file) { | 
 |             BOOL b; | 
 |  | 
 |             b = CopyFileW(This->cache_file, This->file_name, FALSE); | 
 |             if(!b) | 
 |                 FIXME("CopyFile failed: %u\n", GetLastError()); | 
 |         }else { | 
 |             FIXME("No cache file\n"); | 
 |         } | 
 |     } | 
 |  | 
 |     if(This->callback) | 
 |         IBindStatusCallback_OnStopBinding(This->callback, hresult, szError); | 
 |  | 
 |     return S_OK; | 
 | } | 
 |  | 
 | static HRESULT WINAPI DownloadBSC_GetBindInfo(IBindStatusCallback *iface, | 
 |         DWORD *grfBINDF, BINDINFO *pbindinfo) | 
 | { | 
 |     DownloadBSC *This = impl_from_IBindStatusCallback(iface); | 
 |     DWORD bindf = 0; | 
 |  | 
 |     TRACE("(%p)->(%p %p)\n", This, grfBINDF, pbindinfo); | 
 |  | 
 |     if(This->callback) { | 
 |         BINDINFO bindinfo; | 
 |         HRESULT hres; | 
 |  | 
 |         memset(&bindinfo, 0, sizeof(bindinfo)); | 
 |         bindinfo.cbSize = sizeof(bindinfo); | 
 |  | 
 |         hres = IBindStatusCallback_GetBindInfo(This->callback, &bindf, &bindinfo); | 
 |         if(SUCCEEDED(hres)) | 
 |             ReleaseBindInfo(&bindinfo); | 
 |     } | 
 |  | 
 |     *grfBINDF = BINDF_PULLDATA | BINDF_NEEDFILE | (bindf & BINDF_ENFORCERESTRICTED); | 
 |     return S_OK; | 
 | } | 
 |  | 
 | static HRESULT WINAPI DownloadBSC_OnDataAvailable(IBindStatusCallback *iface, | 
 |         DWORD grfBSCF, DWORD dwSize, FORMATETC *pformatetc, STGMEDIUM *pstgmed) | 
 | { | 
 |     DownloadBSC *This = impl_from_IBindStatusCallback(iface); | 
 |  | 
 |     TRACE("(%p)->(%08x %d %p %p)\n", This, grfBSCF, dwSize, pformatetc, pstgmed); | 
 |  | 
 |     return S_OK; | 
 | } | 
 |  | 
 | static HRESULT WINAPI DownloadBSC_OnObjectAvailable(IBindStatusCallback *iface, | 
 |         REFIID riid, IUnknown *punk) | 
 | { | 
 |     DownloadBSC *This = impl_from_IBindStatusCallback(iface); | 
 |     FIXME("(%p)->(%s %p)\n", This, debugstr_guid(riid), punk); | 
 |     return E_NOTIMPL; | 
 | } | 
 |  | 
 | static const IBindStatusCallbackVtbl BindStatusCallbackVtbl = { | 
 |     DownloadBSC_QueryInterface, | 
 |     DownloadBSC_AddRef, | 
 |     DownloadBSC_Release, | 
 |     DownloadBSC_OnStartBinding, | 
 |     DownloadBSC_GetPriority, | 
 |     DownloadBSC_OnLowResource, | 
 |     DownloadBSC_OnProgress, | 
 |     DownloadBSC_OnStopBinding, | 
 |     DownloadBSC_GetBindInfo, | 
 |     DownloadBSC_OnDataAvailable, | 
 |     DownloadBSC_OnObjectAvailable | 
 | }; | 
 |  | 
 | static HRESULT WINAPI DwlServiceProvider_QueryInterface(IServiceProvider *iface, | 
 |         REFIID riid, void **ppv) | 
 | { | 
 |     DownloadBSC *This = impl_from_IServiceProvider(iface); | 
 |     return IBindStatusCallback_QueryInterface(&This->IBindStatusCallback_iface, riid, ppv); | 
 | } | 
 |  | 
 | static ULONG WINAPI DwlServiceProvider_AddRef(IServiceProvider *iface) | 
 | { | 
 |     DownloadBSC *This = impl_from_IServiceProvider(iface); | 
 |     return IBindStatusCallback_AddRef(&This->IBindStatusCallback_iface); | 
 | } | 
 |  | 
 | static ULONG WINAPI DwlServiceProvider_Release(IServiceProvider *iface) | 
 | { | 
 |     DownloadBSC *This = impl_from_IServiceProvider(iface); | 
 |     return IBindStatusCallback_Release(&This->IBindStatusCallback_iface); | 
 | } | 
 |  | 
 | static HRESULT WINAPI DwlServiceProvider_QueryService(IServiceProvider *iface, | 
 |         REFGUID guidService, REFIID riid, void **ppv) | 
 | { | 
 |     DownloadBSC *This = impl_from_IServiceProvider(iface); | 
 |     IServiceProvider *serv_prov; | 
 |     HRESULT hres; | 
 |  | 
 |     TRACE("(%p)->(%s %s %p)\n", This, debugstr_guid(guidService), debugstr_guid(riid), ppv); | 
 |  | 
 |     if(!This->callback) | 
 |         return E_NOINTERFACE; | 
 |  | 
 |     hres = IBindStatusCallback_QueryInterface(This->callback, riid, ppv); | 
 |     if(SUCCEEDED(hres)) | 
 |         return S_OK; | 
 |  | 
 |     hres = IBindStatusCallback_QueryInterface(This->callback, &IID_IServiceProvider, (void**)&serv_prov); | 
 |     if(SUCCEEDED(hres)) { | 
 |         hres = IServiceProvider_QueryService(serv_prov, guidService, riid, ppv); | 
 |         IServiceProvider_Release(serv_prov); | 
 |         return hres; | 
 |     } | 
 |  | 
 |     return E_NOINTERFACE; | 
 | } | 
 |  | 
 | static const IServiceProviderVtbl ServiceProviderVtbl = { | 
 |     DwlServiceProvider_QueryInterface, | 
 |     DwlServiceProvider_AddRef, | 
 |     DwlServiceProvider_Release, | 
 |     DwlServiceProvider_QueryService | 
 | }; | 
 |  | 
 | static HRESULT DownloadBSC_Create(IBindStatusCallback *callback, LPCWSTR file_name, IBindStatusCallback **ret_callback) | 
 | { | 
 |     DownloadBSC *ret = heap_alloc(sizeof(*ret)); | 
 |  | 
 |     ret->IBindStatusCallback_iface.lpVtbl = &BindStatusCallbackVtbl; | 
 |     ret->IServiceProvider_iface.lpVtbl = &ServiceProviderVtbl; | 
 |     ret->ref = 1; | 
 |     ret->file_name = heap_strdupW(file_name); | 
 |     ret->cache_file = NULL; | 
 |  | 
 |     if(callback) | 
 |         IBindStatusCallback_AddRef(callback); | 
 |     ret->callback = callback; | 
 |  | 
 |     *ret_callback = &ret->IBindStatusCallback_iface; | 
 |     return S_OK; | 
 | } | 
 |  | 
 | HRESULT create_default_callback(IBindStatusCallback **ret) | 
 | { | 
 |     IBindStatusCallback *callback; | 
 |     HRESULT hres; | 
 |  | 
 |     hres = DownloadBSC_Create(NULL, NULL, &callback); | 
 |     if(FAILED(hres)) | 
 |         return hres; | 
 |  | 
 |     hres = wrap_callback(callback, ret); | 
 |     IBindStatusCallback_Release(callback); | 
 |     return hres; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           URLDownloadToFileW (URLMON.@) | 
 |  * | 
 |  * Downloads URL szURL to file szFileName and call lpfnCB callback to | 
 |  * report progress. | 
 |  * | 
 |  * PARAMS | 
 |  *  pCaller    [I] controlling IUnknown interface. | 
 |  *  szURL      [I] URL of the file to download | 
 |  *  szFileName [I] file name to store the content of the URL | 
 |  *  dwReserved [I] reserved - set to 0 | 
 |  *  lpfnCB     [I] callback for progress report | 
 |  * | 
 |  * RETURNS | 
 |  *  S_OK on success | 
 |  */ | 
 | HRESULT WINAPI URLDownloadToFileW(LPUNKNOWN pCaller, LPCWSTR szURL, LPCWSTR szFileName, | 
 |         DWORD dwReserved, LPBINDSTATUSCALLBACK lpfnCB) | 
 | { | 
 |     IBindStatusCallback *callback; | 
 |     IUnknown *unk; | 
 |     IMoniker *mon; | 
 |     IBindCtx *bindctx; | 
 |     HRESULT hres; | 
 |  | 
 |     TRACE("(%p %s %s %d %p)\n", pCaller, debugstr_w(szURL), debugstr_w(szFileName), dwReserved, lpfnCB); | 
 |  | 
 |     if(pCaller) | 
 |         FIXME("pCaller not supported\n"); | 
 |  | 
 |     hres = DownloadBSC_Create(lpfnCB, szFileName, &callback); | 
 |     if(FAILED(hres)) | 
 |         return hres; | 
 |  | 
 |     hres = CreateAsyncBindCtx(0, callback, NULL, &bindctx); | 
 |     IBindStatusCallback_Release(callback); | 
 |     if(FAILED(hres)) | 
 |         return hres; | 
 |  | 
 |     hres = CreateURLMoniker(NULL, szURL, &mon); | 
 |     if(FAILED(hres)) { | 
 |         IBindCtx_Release(bindctx); | 
 |         return hres; | 
 |     } | 
 |  | 
 |     hres = IMoniker_BindToStorage(mon, bindctx, NULL, &IID_IUnknown, (void**)&unk); | 
 |     IMoniker_Release(mon); | 
 |     IBindCtx_Release(bindctx); | 
 |  | 
 |     if(unk) | 
 |         IUnknown_Release(unk); | 
 |  | 
 |     return hres == MK_S_ASYNCHRONOUS ? S_OK : hres; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           URLDownloadToFileA (URLMON.@) | 
 |  * | 
 |  * Downloads URL szURL to rile szFileName and call lpfnCB callback to | 
 |  * report progress. | 
 |  * | 
 |  * PARAMS | 
 |  *  pCaller    [I] controlling IUnknown interface. | 
 |  *  szURL      [I] URL of the file to download | 
 |  *  szFileName [I] file name to store the content of the URL | 
 |  *  dwReserved [I] reserved - set to 0 | 
 |  *  lpfnCB     [I] callback for progress report | 
 |  * | 
 |  * RETURNS | 
 |  *  S_OK on success | 
 |  */ | 
 | HRESULT WINAPI URLDownloadToFileA(LPUNKNOWN pCaller, LPCSTR szURL, LPCSTR szFileName, DWORD dwReserved, | 
 |         LPBINDSTATUSCALLBACK lpfnCB) | 
 | { | 
 |     LPWSTR urlW, file_nameW; | 
 |     HRESULT hres; | 
 |  | 
 |     TRACE("(%p %s %s %d %p)\n", pCaller, debugstr_a(szURL), debugstr_a(szFileName), dwReserved, lpfnCB); | 
 |  | 
 |     urlW = heap_strdupAtoW(szURL); | 
 |     file_nameW = heap_strdupAtoW(szFileName); | 
 |  | 
 |     hres = URLDownloadToFileW(pCaller, urlW, file_nameW, dwReserved, lpfnCB); | 
 |  | 
 |     heap_free(urlW); | 
 |     heap_free(file_nameW); | 
 |  | 
 |     return hres; | 
 | } |