1 /* 2 * Copyright 2008 Jacek Caban for CodeWeavers 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation; either 7 * version 2.1 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public 15 * License along with this library; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 17 */ 18 19 #include "urlmon_main.h" 20 #include "wine/debug.h" 21 22 WINE_DEFAULT_DEBUG_CHANNEL(urlmon); 23 24 typedef struct { 25 const IBindStatusCallbackVtbl *lpBindStatusCallbackVtbl; 26 const IServiceProviderVtbl *lpServiceProviderVtbl; 27 28 LONG ref; 29 30 IBindStatusCallback *callback; 31 LPWSTR file_name; 32 LPWSTR cache_file; 33 } DownloadBSC; 34 35 #define STATUSCLB(x) ((IBindStatusCallback*) &(x)->lpBindStatusCallbackVtbl) 36 #define SERVPROV(x) ((IServiceProvider*) &(x)->lpServiceProviderVtbl) 37 38 #define STATUSCLB_THIS(iface) DEFINE_THIS(DownloadBSC, BindStatusCallback, iface) 39 40 static HRESULT WINAPI DownloadBSC_QueryInterface(IBindStatusCallback *iface, 41 REFIID riid, void **ppv) 42 { 43 DownloadBSC *This = STATUSCLB_THIS(iface); 44 45 *ppv = NULL; 46 47 if(IsEqualGUID(&IID_IUnknown, riid)) { 48 TRACE("(%p)->(IID_IUnknown, %p)\n", This, ppv); 49 *ppv = STATUSCLB(This); 50 }else if(IsEqualGUID(&IID_IBindStatusCallback, riid)) { 51 TRACE("(%p)->(IID_IBindStatusCallback, %p)\n", This, ppv); 52 *ppv = STATUSCLB(This); 53 }else if(IsEqualGUID(&IID_IServiceProvider, riid)) { 54 TRACE("(%p)->(IID_IServiceProvider, %p)\n", This, ppv); 55 *ppv = SERVPROV(This); 56 } 57 58 if(*ppv) { 59 IBindStatusCallback_AddRef((IUnknown*)*ppv); 60 return S_OK; 61 } 62 63 TRACE("Unsupported riid = %s\n", debugstr_guid(riid)); 64 return E_NOINTERFACE; 65 } 66 67 static ULONG WINAPI DownloadBSC_AddRef(IBindStatusCallback *iface) 68 { 69 DownloadBSC *This = STATUSCLB_THIS(iface); 70 LONG ref = InterlockedIncrement(&This->ref); 71 72 TRACE("(%p) ref = %d\n", This, ref); 73 74 return ref; 75 } 76 77 static ULONG WINAPI DownloadBSC_Release(IBindStatusCallback *iface) 78 { 79 DownloadBSC *This = STATUSCLB_THIS(iface); 80 LONG ref = InterlockedDecrement(&This->ref); 81 82 TRACE("(%p) ref = %d\n", This, ref); 83 84 if(!ref) { 85 if(This->callback) 86 IBindStatusCallback_Release(This->callback); 87 heap_free(This->file_name); 88 heap_free(This->cache_file); 89 heap_free(This); 90 } 91 92 return ref; 93 } 94 95 static HRESULT WINAPI DownloadBSC_OnStartBinding(IBindStatusCallback *iface, 96 DWORD dwReserved, IBinding *pbind) 97 { 98 DownloadBSC *This = STATUSCLB_THIS(iface); 99 100 TRACE("(%p)->(%d %p)\n", This, dwReserved, pbind); 101 102 if(This->callback) 103 IBindStatusCallback_OnStartBinding(This->callback, dwReserved, pbind); 104 105 return S_OK; 106 } 107 108 static HRESULT WINAPI DownloadBSC_GetPriority(IBindStatusCallback *iface, LONG *pnPriority) 109 { 110 DownloadBSC *This = STATUSCLB_THIS(iface); 111 FIXME("(%p)->(%p)\n", This, pnPriority); 112 return E_NOTIMPL; 113 } 114 115 static HRESULT WINAPI DownloadBSC_OnLowResource(IBindStatusCallback *iface, DWORD reserved) 116 { 117 DownloadBSC *This = STATUSCLB_THIS(iface); 118 FIXME("(%p)->(%d)\n", This, reserved); 119 return E_NOTIMPL; 120 } 121 122 static void on_progress(DownloadBSC *This, ULONG progress, ULONG progress_max, ULONG status_code, LPCWSTR status_text) 123 { 124 HRESULT hres; 125 126 if(!This->callback) 127 return; 128 129 hres = IBindStatusCallback_OnProgress(This->callback, progress, progress_max, status_code, status_text); 130 if(FAILED(hres)) 131 FIXME("OnProgress failed: %08x\n", hres); 132 } 133 134 static HRESULT WINAPI DownloadBSC_OnProgress(IBindStatusCallback *iface, ULONG ulProgress, 135 ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText) 136 { 137 DownloadBSC *This = STATUSCLB_THIS(iface); 138 139 TRACE("%p)->(%u %u %u %s)\n", This, ulProgress, ulProgressMax, ulStatusCode, 140 debugstr_w(szStatusText)); 141 142 switch(ulStatusCode) { 143 case BINDSTATUS_BEGINDOWNLOADDATA: 144 case BINDSTATUS_DOWNLOADINGDATA: 145 case BINDSTATUS_ENDDOWNLOADDATA: 146 case BINDSTATUS_SENDINGREQUEST: 147 case BINDSTATUS_MIMETYPEAVAILABLE: 148 on_progress(This, ulProgress, ulProgressMax, ulStatusCode, szStatusText); 149 break; 150 151 case BINDSTATUS_CACHEFILENAMEAVAILABLE: 152 on_progress(This, ulProgress, ulProgressMax, ulStatusCode, szStatusText); 153 This->cache_file = heap_strdupW(szStatusText); 154 break; 155 156 case BINDSTATUS_FINDINGRESOURCE: 157 case BINDSTATUS_CONNECTING: 158 break; 159 160 default: 161 FIXME("Unsupported status %u\n", ulStatusCode); 162 } 163 164 return S_OK; 165 } 166 167 static HRESULT WINAPI DownloadBSC_OnStopBinding(IBindStatusCallback *iface, 168 HRESULT hresult, LPCWSTR szError) 169 { 170 DownloadBSC *This = STATUSCLB_THIS(iface); 171 172 TRACE("(%p)->(%08x %s)\n", This, hresult, debugstr_w(szError)); 173 174 if(This->cache_file) { 175 BOOL b; 176 177 b = CopyFileW(This->cache_file, This->file_name, FALSE); 178 if(!b) 179 FIXME("CopyFile failed: %u\n", GetLastError()); 180 }else { 181 FIXME("No cache file\n"); 182 } 183 184 if(This->callback) 185 IBindStatusCallback_OnStopBinding(This->callback, hresult, szError); 186 187 return S_OK; 188 } 189 190 static HRESULT WINAPI DownloadBSC_GetBindInfo(IBindStatusCallback *iface, 191 DWORD *grfBINDF, BINDINFO *pbindinfo) 192 { 193 DownloadBSC *This = STATUSCLB_THIS(iface); 194 DWORD bindf = 0; 195 196 TRACE("(%p)->(%p %p)\n", This, grfBINDF, pbindinfo); 197 198 if(This->callback) { 199 BINDINFO bindinfo; 200 HRESULT hres; 201 202 memset(&bindinfo, 0, sizeof(bindinfo)); 203 bindinfo.cbSize = sizeof(bindinfo); 204 205 hres = IBindStatusCallback_GetBindInfo(This->callback, &bindf, &bindinfo); 206 if(SUCCEEDED(hres)) 207 ReleaseBindInfo(&bindinfo); 208 } 209 210 *grfBINDF = BINDF_PULLDATA | BINDF_NEEDFILE | (bindf & BINDF_ENFORCERESTRICTED); 211 return S_OK; 212 } 213 214 static HRESULT WINAPI DownloadBSC_OnDataAvailable(IBindStatusCallback *iface, 215 DWORD grfBSCF, DWORD dwSize, FORMATETC *pformatetc, STGMEDIUM *pstgmed) 216 { 217 DownloadBSC *This = STATUSCLB_THIS(iface); 218 219 TRACE("(%p)->(%08x %d %p %p)\n", This, grfBSCF, dwSize, pformatetc, pstgmed); 220 221 return S_OK; 222 } 223 224 static HRESULT WINAPI DownloadBSC_OnObjectAvailable(IBindStatusCallback *iface, 225 REFIID riid, IUnknown *punk) 226 { 227 DownloadBSC *This = STATUSCLB_THIS(iface); 228 FIXME("(%p)->(%s %p)\n", This, debugstr_guid(riid), punk); 229 return E_NOTIMPL; 230 } 231 232 #undef STATUSCLB_THIS 233 234 static const IBindStatusCallbackVtbl BindStatusCallbackVtbl = { 235 DownloadBSC_QueryInterface, 236 DownloadBSC_AddRef, 237 DownloadBSC_Release, 238 DownloadBSC_OnStartBinding, 239 DownloadBSC_GetPriority, 240 DownloadBSC_OnLowResource, 241 DownloadBSC_OnProgress, 242 DownloadBSC_OnStopBinding, 243 DownloadBSC_GetBindInfo, 244 DownloadBSC_OnDataAvailable, 245 DownloadBSC_OnObjectAvailable 246 }; 247 248 #define SERVPROV_THIS(iface) DEFINE_THIS(DownloadBSC, ServiceProvider, iface) 249 250 static HRESULT WINAPI DwlServiceProvider_QueryInterface(IServiceProvider *iface, 251 REFIID riid, void **ppv) 252 { 253 DownloadBSC *This = SERVPROV_THIS(iface); 254 return IBindStatusCallback_QueryInterface(STATUSCLB(This), riid, ppv); 255 } 256 257 static ULONG WINAPI DwlServiceProvider_AddRef(IServiceProvider *iface) 258 { 259 DownloadBSC *This = SERVPROV_THIS(iface); 260 return IBindStatusCallback_AddRef(STATUSCLB(This)); 261 } 262 263 static ULONG WINAPI DwlServiceProvider_Release(IServiceProvider *iface) 264 { 265 DownloadBSC *This = SERVPROV_THIS(iface); 266 return IBindStatusCallback_Release(STATUSCLB(This)); 267 } 268 269 static HRESULT WINAPI DwlServiceProvider_QueryService(IServiceProvider *iface, 270 REFGUID guidService, REFIID riid, void **ppv) 271 { 272 DownloadBSC *This = SERVPROV_THIS(iface); 273 IServiceProvider *serv_prov; 274 HRESULT hres; 275 276 TRACE("(%p)->(%s %s %p)\n", This, debugstr_guid(guidService), debugstr_guid(riid), ppv); 277 278 if(!This->callback) 279 return E_NOINTERFACE; 280 281 hres = IBindStatusCallback_QueryInterface(This->callback, riid, ppv); 282 if(SUCCEEDED(hres)) 283 return S_OK; 284 285 hres = IBindStatusCallback_QueryInterface(This->callback, &IID_IServiceProvider, (void**)&serv_prov); 286 if(SUCCEEDED(hres)) { 287 hres = IServiceProvider_QueryService(serv_prov, guidService, riid, ppv); 288 IServiceProvider_Release(serv_prov); 289 return hres; 290 } 291 292 return E_NOINTERFACE; 293 } 294 295 #undef SERVPROV_THIS 296 297 static const IServiceProviderVtbl ServiceProviderVtbl = { 298 DwlServiceProvider_QueryInterface, 299 DwlServiceProvider_AddRef, 300 DwlServiceProvider_Release, 301 DwlServiceProvider_QueryService 302 }; 303 304 static IBindStatusCallback *DownloadBSC_Create(IBindStatusCallback *callback, LPCWSTR file_name) 305 { 306 DownloadBSC *ret = heap_alloc(sizeof(*ret)); 307 308 ret->lpBindStatusCallbackVtbl = &BindStatusCallbackVtbl; 309 ret->lpServiceProviderVtbl = &ServiceProviderVtbl; 310 ret->ref = 1; 311 ret->file_name = heap_strdupW(file_name); 312 ret->cache_file = NULL; 313 314 if(callback) 315 IBindStatusCallback_AddRef(callback); 316 ret->callback = callback; 317 318 return STATUSCLB(ret); 319 } 320 321 /*********************************************************************** 322 * URLDownloadToFileW (URLMON.@) 323 * 324 * Downloads URL szURL to file szFileName and call lpfnCB callback to 325 * report progress. 326 * 327 * PARAMS 328 * pCaller [I] controlling IUnknown interface. 329 * szURL [I] URL of the file to download 330 * szFileName [I] file name to store the content of the URL 331 * dwReserved [I] reserved - set to 0 332 * lpfnCB [I] callback for progress report 333 * 334 * RETURNS 335 * S_OK on success 336 */ 337 HRESULT WINAPI URLDownloadToFileW(LPUNKNOWN pCaller, LPCWSTR szURL, LPCWSTR szFileName, 338 DWORD dwReserved, LPBINDSTATUSCALLBACK lpfnCB) 339 { 340 IBindStatusCallback *callback; 341 IUnknown *unk; 342 IMoniker *mon; 343 IBindCtx *bindctx; 344 HRESULT hres; 345 346 TRACE("(%p %s %s %d %p)\n", pCaller, debugstr_w(szURL), debugstr_w(szFileName), dwReserved, lpfnCB); 347 348 if(pCaller) 349 FIXME("pCaller not supported\n"); 350 351 callback = DownloadBSC_Create(lpfnCB, szFileName); 352 hres = CreateAsyncBindCtx(0, callback, NULL, &bindctx); 353 IBindStatusCallback_Release(callback); 354 if(FAILED(hres)) 355 return hres; 356 357 hres = CreateURLMoniker(NULL, szURL, &mon); 358 if(FAILED(hres)) { 359 IBindCtx_Release(bindctx); 360 return hres; 361 } 362 363 hres = IMoniker_BindToStorage(mon, bindctx, NULL, &IID_IUnknown, (void**)&unk); 364 IMoniker_Release(mon); 365 IBindCtx_Release(bindctx); 366 367 if(unk) 368 IUnknown_Release(unk); 369 370 return hres == MK_S_ASYNCHRONOUS ? S_OK : hres; 371 } 372 373 /*********************************************************************** 374 * URLDownloadToFileA (URLMON.@) 375 * 376 * Downloads URL szURL to rile szFileName and call lpfnCB callback to 377 * report progress. 378 * 379 * PARAMS 380 * pCaller [I] controlling IUnknown interface. 381 * szURL [I] URL of the file to download 382 * szFileName [I] file name to store the content of the URL 383 * dwReserved [I] reserved - set to 0 384 * lpfnCB [I] callback for progress report 385 * 386 * RETURNS 387 * S_OK on success 388 */ 389 HRESULT WINAPI URLDownloadToFileA(LPUNKNOWN pCaller, LPCSTR szURL, LPCSTR szFileName, DWORD dwReserved, 390 LPBINDSTATUSCALLBACK lpfnCB) 391 { 392 LPWSTR urlW, file_nameW; 393 HRESULT hres; 394 395 TRACE("(%p %s %s %d %p)\n", pCaller, debugstr_a(szURL), debugstr_a(szFileName), dwReserved, lpfnCB); 396 397 urlW = heap_strdupAtoW(szURL); 398 file_nameW = heap_strdupAtoW(szFileName); 399 400 hres = URLDownloadToFileW(pCaller, urlW, file_nameW, dwReserved, lpfnCB); 401 402 heap_free(urlW); 403 heap_free(file_nameW); 404 405 return hres; 406 } 407