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