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