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 21 typedef struct { 22 IBindStatusCallback IBindStatusCallback_iface; 23 IServiceProvider IServiceProvider_iface; 24 25 LONG ref; 26 27 IBindStatusCallback *callback; 28 IBinding *binding; 29 LPWSTR file_name; 30 LPWSTR cache_file; 31 DWORD bindf; 32 33 stop_cache_binding_proc_t onstop_proc; 34 void *ctx; 35 } DownloadBSC; 36 37 static inline DownloadBSC *impl_from_IBindStatusCallback(IBindStatusCallback *iface) 38 { 39 return CONTAINING_RECORD(iface, DownloadBSC, IBindStatusCallback_iface); 40 } 41 42 static inline DownloadBSC *impl_from_IServiceProvider(IServiceProvider *iface) 43 { 44 return CONTAINING_RECORD(iface, DownloadBSC, IServiceProvider_iface); 45 } 46 47 static HRESULT WINAPI DownloadBSC_QueryInterface(IBindStatusCallback *iface, 48 REFIID riid, void **ppv) 49 { 50 DownloadBSC *This = impl_from_IBindStatusCallback(iface); 51 52 *ppv = NULL; 53 54 if(IsEqualGUID(&IID_IUnknown, riid)) { 55 TRACE("(%p)->(IID_IUnknown, %p)\n", This, ppv); 56 *ppv = &This->IBindStatusCallback_iface; 57 }else if(IsEqualGUID(&IID_IBindStatusCallback, riid)) { 58 TRACE("(%p)->(IID_IBindStatusCallback, %p)\n", This, ppv); 59 *ppv = &This->IBindStatusCallback_iface; 60 }else if(IsEqualGUID(&IID_IServiceProvider, riid)) { 61 TRACE("(%p)->(IID_IServiceProvider, %p)\n", This, ppv); 62 *ppv = &This->IServiceProvider_iface; 63 } 64 65 if(*ppv) { 66 IUnknown_AddRef((IUnknown*)*ppv); 67 return S_OK; 68 } 69 70 TRACE("Unsupported riid = %s\n", debugstr_guid(riid)); 71 return E_NOINTERFACE; 72 } 73 74 static ULONG WINAPI DownloadBSC_AddRef(IBindStatusCallback *iface) 75 { 76 DownloadBSC *This = impl_from_IBindStatusCallback(iface); 77 LONG ref = InterlockedIncrement(&This->ref); 78 79 TRACE("(%p) ref = %d\n", This, ref); 80 81 return ref; 82 } 83 84 static ULONG WINAPI DownloadBSC_Release(IBindStatusCallback *iface) 85 { 86 DownloadBSC *This = impl_from_IBindStatusCallback(iface); 87 LONG ref = InterlockedDecrement(&This->ref); 88 89 TRACE("(%p) ref = %d\n", This, ref); 90 91 if(!ref) { 92 if(This->callback) 93 IBindStatusCallback_Release(This->callback); 94 if(This->binding) 95 IBinding_Release(This->binding); 96 heap_free(This->file_name); 97 heap_free(This->cache_file); 98 heap_free(This); 99 } 100 101 return ref; 102 } 103 104 static HRESULT WINAPI DownloadBSC_OnStartBinding(IBindStatusCallback *iface, 105 DWORD dwReserved, IBinding *pbind) 106 { 107 DownloadBSC *This = impl_from_IBindStatusCallback(iface); 108 HRESULT hres = S_OK; 109 110 TRACE("(%p)->(%d %p)\n", This, dwReserved, pbind); 111 112 if(This->callback) { 113 hres = IBindStatusCallback_OnStartBinding(This->callback, dwReserved, pbind); 114 115 IBinding_AddRef(pbind); 116 This->binding = pbind; 117 } 118 119 /* Windows seems to ignore E_NOTIMPL if it's returned from the client. */ 120 return hres == E_NOTIMPL ? S_OK : hres; 121 } 122 123 static HRESULT WINAPI DownloadBSC_GetPriority(IBindStatusCallback *iface, LONG *pnPriority) 124 { 125 DownloadBSC *This = impl_from_IBindStatusCallback(iface); 126 FIXME("(%p)->(%p)\n", This, pnPriority); 127 return E_NOTIMPL; 128 } 129 130 static HRESULT WINAPI DownloadBSC_OnLowResource(IBindStatusCallback *iface, DWORD reserved) 131 { 132 DownloadBSC *This = impl_from_IBindStatusCallback(iface); 133 FIXME("(%p)->(%d)\n", This, reserved); 134 return E_NOTIMPL; 135 } 136 137 static HRESULT on_progress(DownloadBSC *This, ULONG progress, ULONG progress_max, ULONG status_code, LPCWSTR status_text) 138 { 139 HRESULT hres; 140 141 if(!This->callback) 142 return S_OK; 143 144 hres = IBindStatusCallback_OnProgress(This->callback, progress, progress_max, status_code, status_text); 145 if(hres == E_ABORT) { 146 if(This->binding) 147 IBinding_Abort(This->binding); 148 else 149 FIXME("No binding, not sure what to do!\n"); 150 } 151 152 return hres; 153 } 154 155 static HRESULT WINAPI DownloadBSC_OnProgress(IBindStatusCallback *iface, ULONG ulProgress, 156 ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText) 157 { 158 DownloadBSC *This = impl_from_IBindStatusCallback(iface); 159 HRESULT hres = S_OK; 160 161 TRACE("%p)->(%u %u %u %s)\n", This, ulProgress, ulProgressMax, ulStatusCode, 162 debugstr_w(szStatusText)); 163 164 switch(ulStatusCode) { 165 case BINDSTATUS_CONNECTING: 166 case BINDSTATUS_BEGINDOWNLOADDATA: 167 case BINDSTATUS_DOWNLOADINGDATA: 168 case BINDSTATUS_ENDDOWNLOADDATA: 169 case BINDSTATUS_SENDINGREQUEST: 170 case BINDSTATUS_MIMETYPEAVAILABLE: 171 hres = on_progress(This, ulProgress, ulProgressMax, ulStatusCode, szStatusText); 172 break; 173 174 case BINDSTATUS_CACHEFILENAMEAVAILABLE: 175 hres = on_progress(This, ulProgress, ulProgressMax, ulStatusCode, szStatusText); 176 This->cache_file = heap_strdupW(szStatusText); 177 break; 178 179 case BINDSTATUS_FINDINGRESOURCE: /* FIXME */ 180 break; 181 182 default: 183 FIXME("Unsupported status %u\n", ulStatusCode); 184 } 185 186 return hres; 187 } 188 189 static HRESULT WINAPI DownloadBSC_OnStopBinding(IBindStatusCallback *iface, 190 HRESULT hresult, LPCWSTR szError) 191 { 192 DownloadBSC *This = impl_from_IBindStatusCallback(iface); 193 HRESULT hres = S_OK; 194 195 TRACE("(%p)->(%08x %s)\n", This, hresult, debugstr_w(szError)); 196 197 if(This->file_name) { 198 if(This->cache_file) { 199 BOOL b; 200 201 b = CopyFileW(This->cache_file, This->file_name, FALSE); 202 if(!b) 203 FIXME("CopyFile failed: %u\n", GetLastError()); 204 }else { 205 FIXME("No cache file\n"); 206 } 207 } 208 209 if(This->onstop_proc) 210 hres = This->onstop_proc(This->ctx, This->cache_file, hresult, szError); 211 else if(This->callback) 212 IBindStatusCallback_OnStopBinding(This->callback, hresult, szError); 213 214 if(This->binding) { 215 IBinding_Release(This->binding); 216 This->binding = NULL; 217 } 218 219 return hres; 220 } 221 222 static HRESULT WINAPI DownloadBSC_GetBindInfo(IBindStatusCallback *iface, 223 DWORD *grfBINDF, BINDINFO *pbindinfo) 224 { 225 DownloadBSC *This = impl_from_IBindStatusCallback(iface); 226 DWORD bindf = 0; 227 228 TRACE("(%p)->(%p %p)\n", This, grfBINDF, pbindinfo); 229 230 if(This->callback) { 231 BINDINFO bindinfo; 232 HRESULT hres; 233 234 memset(&bindinfo, 0, sizeof(bindinfo)); 235 bindinfo.cbSize = sizeof(bindinfo); 236 237 hres = IBindStatusCallback_GetBindInfo(This->callback, &bindf, &bindinfo); 238 if(SUCCEEDED(hres)) 239 ReleaseBindInfo(&bindinfo); 240 } 241 242 *grfBINDF = BINDF_PULLDATA | BINDF_NEEDFILE | (bindf & BINDF_ENFORCERESTRICTED) | This->bindf; 243 return S_OK; 244 } 245 246 static HRESULT WINAPI DownloadBSC_OnDataAvailable(IBindStatusCallback *iface, 247 DWORD grfBSCF, DWORD dwSize, FORMATETC *pformatetc, STGMEDIUM *pstgmed) 248 { 249 DownloadBSC *This = impl_from_IBindStatusCallback(iface); 250 251 TRACE("(%p)->(%08x %d %p %p)\n", This, grfBSCF, dwSize, pformatetc, pstgmed); 252 253 return S_OK; 254 } 255 256 static HRESULT WINAPI DownloadBSC_OnObjectAvailable(IBindStatusCallback *iface, 257 REFIID riid, IUnknown *punk) 258 { 259 DownloadBSC *This = impl_from_IBindStatusCallback(iface); 260 FIXME("(%p)->(%s %p)\n", This, debugstr_guid(riid), punk); 261 return E_NOTIMPL; 262 } 263 264 static const IBindStatusCallbackVtbl BindStatusCallbackVtbl = { 265 DownloadBSC_QueryInterface, 266 DownloadBSC_AddRef, 267 DownloadBSC_Release, 268 DownloadBSC_OnStartBinding, 269 DownloadBSC_GetPriority, 270 DownloadBSC_OnLowResource, 271 DownloadBSC_OnProgress, 272 DownloadBSC_OnStopBinding, 273 DownloadBSC_GetBindInfo, 274 DownloadBSC_OnDataAvailable, 275 DownloadBSC_OnObjectAvailable 276 }; 277 278 static HRESULT WINAPI DwlServiceProvider_QueryInterface(IServiceProvider *iface, 279 REFIID riid, void **ppv) 280 { 281 DownloadBSC *This = impl_from_IServiceProvider(iface); 282 return IBindStatusCallback_QueryInterface(&This->IBindStatusCallback_iface, riid, ppv); 283 } 284 285 static ULONG WINAPI DwlServiceProvider_AddRef(IServiceProvider *iface) 286 { 287 DownloadBSC *This = impl_from_IServiceProvider(iface); 288 return IBindStatusCallback_AddRef(&This->IBindStatusCallback_iface); 289 } 290 291 static ULONG WINAPI DwlServiceProvider_Release(IServiceProvider *iface) 292 { 293 DownloadBSC *This = impl_from_IServiceProvider(iface); 294 return IBindStatusCallback_Release(&This->IBindStatusCallback_iface); 295 } 296 297 static HRESULT WINAPI DwlServiceProvider_QueryService(IServiceProvider *iface, 298 REFGUID guidService, REFIID riid, void **ppv) 299 { 300 DownloadBSC *This = impl_from_IServiceProvider(iface); 301 IServiceProvider *serv_prov; 302 HRESULT hres; 303 304 TRACE("(%p)->(%s %s %p)\n", This, debugstr_guid(guidService), debugstr_guid(riid), ppv); 305 306 if(!This->callback) 307 return E_NOINTERFACE; 308 309 hres = IBindStatusCallback_QueryInterface(This->callback, riid, ppv); 310 if(SUCCEEDED(hres)) 311 return S_OK; 312 313 hres = IBindStatusCallback_QueryInterface(This->callback, &IID_IServiceProvider, (void**)&serv_prov); 314 if(SUCCEEDED(hres)) { 315 hres = IServiceProvider_QueryService(serv_prov, guidService, riid, ppv); 316 IServiceProvider_Release(serv_prov); 317 return hres; 318 } 319 320 return E_NOINTERFACE; 321 } 322 323 static const IServiceProviderVtbl ServiceProviderVtbl = { 324 DwlServiceProvider_QueryInterface, 325 DwlServiceProvider_AddRef, 326 DwlServiceProvider_Release, 327 DwlServiceProvider_QueryService 328 }; 329 330 static HRESULT DownloadBSC_Create(IBindStatusCallback *callback, LPCWSTR file_name, DownloadBSC **ret_callback) 331 { 332 DownloadBSC *ret; 333 334 ret = heap_alloc_zero(sizeof(*ret)); 335 if(!ret) 336 return E_OUTOFMEMORY; 337 338 ret->IBindStatusCallback_iface.lpVtbl = &BindStatusCallbackVtbl; 339 ret->IServiceProvider_iface.lpVtbl = &ServiceProviderVtbl; 340 ret->ref = 1; 341 342 if(file_name) { 343 ret->file_name = heap_strdupW(file_name); 344 if(!ret->file_name) { 345 heap_free(ret); 346 return E_OUTOFMEMORY; 347 } 348 } 349 350 if(callback) 351 IBindStatusCallback_AddRef(callback); 352 ret->callback = callback; 353 354 *ret_callback = ret; 355 return S_OK; 356 } 357 358 HRESULT create_default_callback(IBindStatusCallback **ret) 359 { 360 DownloadBSC *callback; 361 HRESULT hres; 362 363 hres = DownloadBSC_Create(NULL, NULL, &callback); 364 if(FAILED(hres)) 365 return hres; 366 367 hres = wrap_callback(&callback->IBindStatusCallback_iface, ret); 368 IBindStatusCallback_Release(&callback->IBindStatusCallback_iface); 369 return hres; 370 } 371 372 HRESULT download_to_cache(IUri *uri, stop_cache_binding_proc_t proc, void *ctx, IBindStatusCallback *callback) 373 { 374 DownloadBSC *dwl_bsc; 375 IBindCtx *bindctx; 376 IMoniker *mon; 377 IUnknown *unk; 378 HRESULT hres; 379 380 hres = DownloadBSC_Create(callback, NULL, &dwl_bsc); 381 if(FAILED(hres)) 382 return hres; 383 384 dwl_bsc->onstop_proc = proc; 385 dwl_bsc->ctx = ctx; 386 dwl_bsc->bindf = BINDF_ASYNCHRONOUS; 387 388 hres = CreateAsyncBindCtx(0, &dwl_bsc->IBindStatusCallback_iface, NULL, &bindctx); 389 IBindStatusCallback_Release(&dwl_bsc->IBindStatusCallback_iface); 390 if(FAILED(hres)) 391 return hres; 392 393 hres = CreateURLMonikerEx2(NULL, uri, &mon, 0); 394 if(FAILED(hres)) { 395 IBindCtx_Release(bindctx); 396 return hres; 397 } 398 399 hres = IMoniker_BindToStorage(mon, bindctx, NULL, &IID_IUnknown, (void**)&unk); 400 IMoniker_Release(mon); 401 IBindCtx_Release(bindctx); 402 if(SUCCEEDED(hres) && unk) 403 IUnknown_Release(unk); 404 return hres; 405 406 } 407 408 /*********************************************************************** 409 * URLDownloadToFileW (URLMON.@) 410 * 411 * Downloads URL szURL to file szFileName and call lpfnCB callback to 412 * report progress. 413 * 414 * PARAMS 415 * pCaller [I] controlling IUnknown interface. 416 * szURL [I] URL of the file to download 417 * szFileName [I] file name to store the content of the URL 418 * dwReserved [I] reserved - set to 0 419 * lpfnCB [I] callback for progress report 420 * 421 * RETURNS 422 * S_OK on success 423 */ 424 HRESULT WINAPI URLDownloadToFileW(LPUNKNOWN pCaller, LPCWSTR szURL, LPCWSTR szFileName, 425 DWORD dwReserved, LPBINDSTATUSCALLBACK lpfnCB) 426 { 427 DownloadBSC *callback; 428 IUnknown *unk; 429 IMoniker *mon; 430 IBindCtx *bindctx; 431 HRESULT hres; 432 433 TRACE("(%p %s %s %d %p)\n", pCaller, debugstr_w(szURL), debugstr_w(szFileName), dwReserved, lpfnCB); 434 435 if(pCaller) 436 FIXME("pCaller not supported\n"); 437 438 hres = DownloadBSC_Create(lpfnCB, szFileName, &callback); 439 if(FAILED(hres)) 440 return hres; 441 442 hres = CreateAsyncBindCtx(0, &callback->IBindStatusCallback_iface, NULL, &bindctx); 443 IBindStatusCallback_Release(&callback->IBindStatusCallback_iface); 444 if(FAILED(hres)) 445 return hres; 446 447 hres = CreateURLMoniker(NULL, szURL, &mon); 448 if(FAILED(hres)) { 449 IBindCtx_Release(bindctx); 450 return hres; 451 } 452 453 hres = IMoniker_BindToStorage(mon, bindctx, NULL, &IID_IUnknown, (void**)&unk); 454 IMoniker_Release(mon); 455 IBindCtx_Release(bindctx); 456 457 if(unk) 458 IUnknown_Release(unk); 459 460 return hres == MK_S_ASYNCHRONOUS ? S_OK : hres; 461 } 462 463 /*********************************************************************** 464 * URLDownloadToFileA (URLMON.@) 465 * 466 * Downloads URL szURL to rile szFileName and call lpfnCB callback to 467 * report progress. 468 * 469 * PARAMS 470 * pCaller [I] controlling IUnknown interface. 471 * szURL [I] URL of the file to download 472 * szFileName [I] file name to store the content of the URL 473 * dwReserved [I] reserved - set to 0 474 * lpfnCB [I] callback for progress report 475 * 476 * RETURNS 477 * S_OK on success 478 */ 479 HRESULT WINAPI URLDownloadToFileA(LPUNKNOWN pCaller, LPCSTR szURL, LPCSTR szFileName, DWORD dwReserved, 480 LPBINDSTATUSCALLBACK lpfnCB) 481 { 482 LPWSTR urlW, file_nameW; 483 HRESULT hres; 484 485 TRACE("(%p %s %s %d %p)\n", pCaller, debugstr_a(szURL), debugstr_a(szFileName), dwReserved, lpfnCB); 486 487 urlW = heap_strdupAtoW(szURL); 488 file_nameW = heap_strdupAtoW(szFileName); 489 490 hres = URLDownloadToFileW(pCaller, urlW, file_nameW, dwReserved, lpfnCB); 491 492 heap_free(urlW); 493 heap_free(file_nameW); 494 495 return hres; 496 } 497