- Sync urlmon with Wine head
[reactos.git] / reactos / dll / win32 / urlmon / umstream.c
1 /*
2 * Based on ../shell32/memorystream.c
3 *
4 * Copyright 1999 Juergen Schmied
5 * Copyright 2003 Mike McCormack for CodeWeavers
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 */
21
22 #include "urlmon_main.h"
23
24 #include "winreg.h"
25 #include "winternl.h"
26 #include "wininet.h"
27 #include "shlwapi.h"
28
29 #include "wine/debug.h"
30
31 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
32
33 static const IStreamVtbl stvt;
34
35 HRESULT UMCreateStreamOnCacheFile(LPCWSTR pszURL,
36 DWORD dwSize,
37 LPWSTR pszFileName,
38 HANDLE *phfile,
39 IUMCacheStream **ppstr)
40 {
41 IUMCacheStream* ucstr;
42 HANDLE handle;
43 DWORD size;
44 LPWSTR url, c, ext = NULL;
45 HRESULT hr;
46
47 size = (strlenW(pszURL)+1)*sizeof(WCHAR);
48 url = heap_alloc(size);
49 memcpy(url, pszURL, size);
50
51 for (c = url; *c && *c != '#' && *c != '?'; ++c)
52 {
53 if (*c == '.')
54 ext = c+1;
55 else if(*c == '/')
56 ext = NULL;
57 }
58
59 *c = 0;
60
61 if(!CreateUrlCacheEntryW(url, dwSize, ext, pszFileName, 0))
62 hr = HRESULT_FROM_WIN32(GetLastError());
63 else
64 hr = S_OK;
65
66 heap_free(url);
67
68 if (hr != S_OK)
69 return hr;
70
71 TRACE("Opening %s\n", debugstr_w(pszFileName) );
72
73 handle = CreateFileW( pszFileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL );
74 if( handle == INVALID_HANDLE_VALUE )
75 return HRESULT_FROM_WIN32(GetLastError());
76
77 if (phfile)
78 {
79 /* Call CreateFileW again because we need a handle with its own file pointer, and DuplicateHandle will return
80 * a handle that shares its file pointer with the original.
81 */
82 *phfile = CreateFileW( pszFileName, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL );
83
84 if (*phfile == (HANDLE) HFILE_ERROR)
85 {
86 DWORD dwError = GetLastError();
87
88 CloseHandle(handle);
89 return HRESULT_FROM_WIN32(dwError);
90 }
91 }
92
93 ucstr = heap_alloc_zero(sizeof(IUMCacheStream));
94 if(ucstr)
95 {
96 ucstr->pszURL = heap_alloc_zero(sizeof(WCHAR) * (lstrlenW(pszURL) + 1));
97 if (ucstr->pszURL)
98 {
99 ucstr->pszFileName = heap_alloc_zero(sizeof(WCHAR) * (lstrlenW(pszFileName) + 1));
100 if (ucstr->pszFileName)
101 {
102 ucstr->lpVtbl=&stvt;
103 ucstr->ref = 1;
104 ucstr->handle = handle;
105 ucstr->closed = 0;
106 lstrcpyW(ucstr->pszURL, pszURL);
107 lstrcpyW(ucstr->pszFileName, pszFileName);
108
109 *ppstr = ucstr;
110
111 return S_OK;
112 }
113 heap_free(ucstr->pszURL);
114 }
115 heap_free(ucstr);
116 }
117 CloseHandle(handle);
118 if (phfile)
119 CloseHandle(*phfile);
120 return E_OUTOFMEMORY;
121 }
122
123 void UMCloseCacheFileStream(IUMCacheStream *This)
124 {
125 if (!This->closed)
126 {
127 FILETIME ftZero;
128
129 ftZero.dwLowDateTime = ftZero.dwHighDateTime = 0;
130
131 This->closed = 1;
132 CommitUrlCacheEntryW(This->pszURL,
133 This->pszFileName,
134 ftZero,
135 ftZero,
136 NORMAL_CACHE_ENTRY,
137 0,
138 0,
139 0,
140 0);
141 }
142 }
143
144 /**************************************************************************
145 * IStream_fnQueryInterface
146 */
147 static HRESULT WINAPI IStream_fnQueryInterface(IStream *iface,
148 REFIID riid,
149 LPVOID *ppvObj)
150 {
151 IUMCacheStream *This = (IUMCacheStream *)iface;
152
153 TRACE("(%p)->(\n\tIID:\t%s,%p)\n",This,debugstr_guid(riid),ppvObj);
154
155 *ppvObj = NULL;
156
157 if(IsEqualIID(riid, &IID_IUnknown) ||
158 IsEqualIID(riid, &IID_IStream))
159 {
160 *ppvObj = This;
161 }
162
163 if(*ppvObj)
164 {
165 IStream_AddRef((IStream*)*ppvObj);
166 TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
167 return S_OK;
168 }
169 TRACE("-- Interface: E_NOINTERFACE\n");
170 return E_NOINTERFACE;
171 }
172
173 /**************************************************************************
174 * IStream_fnAddRef
175 */
176 static ULONG WINAPI IStream_fnAddRef(IStream *iface)
177 {
178 IUMCacheStream *This = (IUMCacheStream *)iface;
179 ULONG refCount = InterlockedIncrement(&This->ref);
180
181 TRACE("(%p)->(count=%u)\n", This, refCount - 1);
182
183 return refCount;
184 }
185
186 /**************************************************************************
187 * IStream_fnRelease
188 */
189 static ULONG WINAPI IStream_fnRelease(IStream *iface)
190 {
191 IUMCacheStream *This = (IUMCacheStream *)iface;
192 ULONG refCount = InterlockedDecrement(&This->ref);
193
194 TRACE("(%p)->(count=%u)\n", This, refCount + 1);
195
196 if (!refCount)
197 {
198 TRACE(" destroying UMCacheStream (%p)\n",This);
199 UMCloseCacheFileStream(This);
200 CloseHandle(This->handle);
201 heap_free(This->pszFileName);
202 heap_free(This->pszURL);
203 heap_free(This);
204 }
205 return refCount;
206 }
207
208 static HRESULT WINAPI IStream_fnRead (IStream * iface,
209 void* pv,
210 ULONG cb,
211 ULONG* pcbRead)
212 {
213 ULONG dwBytesRead;
214 IUMCacheStream *This = (IUMCacheStream *)iface;
215
216 TRACE("(%p)->(%p,0x%08x,%p)\n",This, pv, cb, pcbRead);
217
218 if ( !pv )
219 return STG_E_INVALIDPOINTER;
220
221 if ( !pcbRead)
222 pcbRead = &dwBytesRead;
223
224 if ( ! ReadFile( This->handle, pv, cb, pcbRead, NULL ) )
225 return S_FALSE;
226
227 if (!*pcbRead)
228 return This->closed ? S_FALSE : E_PENDING;
229 return S_OK;
230 }
231
232 static HRESULT WINAPI IStream_fnWrite (IStream * iface,
233 const void* pv,
234 ULONG cb,
235 ULONG* pcbWritten)
236 {
237 return E_NOTIMPL;
238 }
239
240 static HRESULT WINAPI IStream_fnSeek (IStream * iface,
241 LARGE_INTEGER dlibMove,
242 DWORD dwOrigin,
243 ULARGE_INTEGER* plibNewPosition)
244 {
245 LARGE_INTEGER newpos;
246 IUMCacheStream *This = (IUMCacheStream *)iface;
247
248 TRACE("(%p)\n",This);
249
250 if (!SetFilePointerEx( This->handle, dlibMove, &newpos, dwOrigin ))
251 return E_FAIL;
252
253 if (plibNewPosition)
254 plibNewPosition->QuadPart = newpos.QuadPart;
255
256 return S_OK;
257 }
258
259 static HRESULT WINAPI IStream_fnSetSize (IStream * iface,
260 ULARGE_INTEGER libNewSize)
261 {
262 LARGE_INTEGER newpos;
263 IUMCacheStream *This = (IUMCacheStream *)iface;
264
265 TRACE("(%p)\n",This);
266
267 newpos.QuadPart = libNewSize.QuadPart;
268 if( ! SetFilePointerEx( This->handle, newpos, NULL, FILE_BEGIN ) )
269 return E_FAIL;
270
271 if( ! SetEndOfFile( This->handle ) )
272 return E_FAIL;
273
274 return S_OK;
275 }
276
277 static HRESULT WINAPI IStream_fnCopyTo (IStream * iface,
278 IStream* pstm,
279 ULARGE_INTEGER cb,
280 ULARGE_INTEGER* pcbRead,
281 ULARGE_INTEGER* pcbWritten)
282 {
283 IUMCacheStream *This = (IUMCacheStream *)iface;
284
285 TRACE("(%p)\n",This);
286
287 return E_NOTIMPL;
288 }
289
290 static HRESULT WINAPI IStream_fnCommit (IStream * iface,
291 DWORD grfCommitFlags)
292 {
293 IUMCacheStream *This = (IUMCacheStream *)iface;
294
295 TRACE("(%p)\n",This);
296
297 return E_NOTIMPL;
298 }
299
300 static HRESULT WINAPI IStream_fnRevert (IStream * iface)
301 {
302 IUMCacheStream *This = (IUMCacheStream *)iface;
303
304 TRACE("(%p)\n",This);
305
306 return E_NOTIMPL;
307 }
308 static HRESULT WINAPI IStream_fnLockRegion (IStream * iface,
309 ULARGE_INTEGER libOffset,
310 ULARGE_INTEGER cb,
311 DWORD dwLockType)
312 {
313 IUMCacheStream *This = (IUMCacheStream *)iface;
314
315 TRACE("(%p)\n",This);
316
317 return E_NOTIMPL;
318 }
319 static HRESULT WINAPI IStream_fnUnlockRegion (IStream * iface,
320 ULARGE_INTEGER libOffset,
321 ULARGE_INTEGER cb,
322 DWORD dwLockType)
323 {
324 IUMCacheStream *This = (IUMCacheStream *)iface;
325
326 TRACE("(%p)\n",This);
327
328 return E_NOTIMPL;
329 }
330 static HRESULT WINAPI IStream_fnStat (IStream * iface,
331 STATSTG* pstatstg,
332 DWORD grfStatFlag)
333 {
334 IUMCacheStream *This = (IUMCacheStream *)iface;
335
336 TRACE("(%p)\n",This);
337
338 return E_NOTIMPL;
339 }
340 static HRESULT WINAPI IStream_fnClone (IStream * iface,
341 IStream** ppstm)
342 {
343 IUMCacheStream *This = (IUMCacheStream *)iface;
344
345 TRACE("(%p)\n",This);
346
347 return E_NOTIMPL;
348 }
349
350 static const IStreamVtbl stvt =
351 {
352 IStream_fnQueryInterface,
353 IStream_fnAddRef,
354 IStream_fnRelease,
355 IStream_fnRead,
356 IStream_fnWrite,
357 IStream_fnSeek,
358 IStream_fnSetSize,
359 IStream_fnCopyTo,
360 IStream_fnCommit,
361 IStream_fnRevert,
362 IStream_fnLockRegion,
363 IStream_fnUnlockRegion,
364 IStream_fnStat,
365 IStream_fnClone
366
367 };
368
369 typedef struct ProxyBindStatusCallback
370 {
371 const IBindStatusCallbackVtbl *lpVtbl;
372
373 IBindStatusCallback *pBSC;
374 } ProxyBindStatusCallback;
375
376 static HRESULT WINAPI ProxyBindStatusCallback_QueryInterface(IBindStatusCallback *iface, REFIID riid, void **ppv)
377 {
378 if (IsEqualGUID(&IID_IBindStatusCallback, riid) ||
379 IsEqualGUID(&IID_IUnknown, riid))
380 {
381 *ppv = iface;
382 IUnknown_AddRef(iface);
383 return S_OK;
384 }
385
386 *ppv = NULL;
387 return E_NOINTERFACE;
388 }
389
390 static ULONG WINAPI ProxyBindStatusCallback_AddRef(IBindStatusCallback *iface)
391 {
392 return 2;
393 }
394
395 static ULONG WINAPI ProxyBindStatusCallback_Release(IBindStatusCallback *iface)
396 {
397 return 1;
398 }
399
400 static HRESULT WINAPI ProxyBindStatusCallback_OnStartBinding(IBindStatusCallback *iface, DWORD dwReserved,
401 IBinding *pib)
402 {
403 ProxyBindStatusCallback *This = (ProxyBindStatusCallback *)iface;
404
405 if(This->pBSC)
406 return IBindStatusCallback_OnStartBinding(This->pBSC, dwReserved, pib);
407
408 return S_OK;
409 }
410
411 static HRESULT WINAPI ProxyBindStatusCallback_GetPriority(IBindStatusCallback *iface, LONG *pnPriority)
412 {
413 ProxyBindStatusCallback *This = (ProxyBindStatusCallback *)iface;
414
415 if(This->pBSC)
416 return IBindStatusCallback_GetPriority(This->pBSC, pnPriority);
417
418 return S_OK;
419 }
420
421 static HRESULT WINAPI ProxyBindStatusCallback_OnLowResource(IBindStatusCallback *iface, DWORD reserved)
422 {
423 ProxyBindStatusCallback *This = (ProxyBindStatusCallback *)iface;
424
425 if(This->pBSC)
426 return IBindStatusCallback_OnLowResource(This->pBSC, reserved);
427
428 return S_OK;
429 }
430
431 static HRESULT WINAPI ProxyBindStatusCallback_OnProgress(IBindStatusCallback *iface, ULONG ulProgress,
432 ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
433 {
434 ProxyBindStatusCallback *This = (ProxyBindStatusCallback *)iface;
435
436 if(This->pBSC)
437 return IBindStatusCallback_OnProgress(This->pBSC, ulProgress,
438 ulProgressMax, ulStatusCode,
439 szStatusText);
440
441 return S_OK;
442 }
443
444 static HRESULT WINAPI ProxyBindStatusCallback_OnStopBinding(IBindStatusCallback *iface, HRESULT hresult, LPCWSTR szError)
445 {
446 ProxyBindStatusCallback *This = (ProxyBindStatusCallback *)iface;
447
448 if(This->pBSC)
449 return IBindStatusCallback_OnStopBinding(This->pBSC, hresult, szError);
450
451 return S_OK;
452 }
453
454 static HRESULT WINAPI ProxyBindStatusCallback_GetBindInfo(IBindStatusCallback *iface, DWORD *grfBINDF, BINDINFO *pbindinfo)
455 {
456 ProxyBindStatusCallback *This = (ProxyBindStatusCallback *)iface;
457
458 if(This->pBSC)
459 return IBindStatusCallback_GetBindInfo(This->pBSC, grfBINDF, pbindinfo);
460
461 return E_INVALIDARG;
462 }
463
464 static HRESULT WINAPI ProxyBindStatusCallback_OnDataAvailable(IBindStatusCallback *iface, DWORD grfBSCF,
465 DWORD dwSize, FORMATETC* pformatetc, STGMEDIUM* pstgmed)
466 {
467 ProxyBindStatusCallback *This = (ProxyBindStatusCallback *)iface;
468
469 if(This->pBSC)
470 return IBindStatusCallback_OnDataAvailable(This->pBSC, grfBSCF, dwSize,
471 pformatetc, pstgmed);
472
473 return S_OK;
474 }
475
476 static HRESULT WINAPI ProxyBindStatusCallback_OnObjectAvailable(IBindStatusCallback *iface, REFIID riid, IUnknown *punk)
477 {
478 ProxyBindStatusCallback *This = (ProxyBindStatusCallback *)iface;
479
480 if(This->pBSC)
481 return IBindStatusCallback_OnObjectAvailable(This->pBSC, riid, punk);
482
483 return S_OK;
484 }
485
486 static HRESULT WINAPI BlockingBindStatusCallback_OnDataAvailable(IBindStatusCallback *iface, DWORD grfBSCF,
487 DWORD dwSize, FORMATETC* pformatetc, STGMEDIUM* pstgmed)
488 {
489 return S_OK;
490 }
491
492 static const IBindStatusCallbackVtbl BlockingBindStatusCallbackVtbl =
493 {
494 ProxyBindStatusCallback_QueryInterface,
495 ProxyBindStatusCallback_AddRef,
496 ProxyBindStatusCallback_Release,
497 ProxyBindStatusCallback_OnStartBinding,
498 ProxyBindStatusCallback_GetPriority,
499 ProxyBindStatusCallback_OnLowResource,
500 ProxyBindStatusCallback_OnProgress,
501 ProxyBindStatusCallback_OnStopBinding,
502 ProxyBindStatusCallback_GetBindInfo,
503 BlockingBindStatusCallback_OnDataAvailable,
504 ProxyBindStatusCallback_OnObjectAvailable
505 };
506
507 static HRESULT WINAPI AsyncBindStatusCallback_GetBindInfo(IBindStatusCallback *iface, DWORD *grfBINDF, BINDINFO *pbindinfo)
508 {
509 ProxyBindStatusCallback *This = (ProxyBindStatusCallback *)iface;
510 HRESULT hr = IBindStatusCallback_GetBindInfo(This->pBSC, grfBINDF, pbindinfo);
511 *grfBINDF |= BINDF_PULLDATA | BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE;
512 return hr;
513 }
514
515 static const IBindStatusCallbackVtbl AsyncBindStatusCallbackVtbl =
516 {
517 ProxyBindStatusCallback_QueryInterface,
518 ProxyBindStatusCallback_AddRef,
519 ProxyBindStatusCallback_Release,
520 ProxyBindStatusCallback_OnStartBinding,
521 ProxyBindStatusCallback_GetPriority,
522 ProxyBindStatusCallback_OnLowResource,
523 ProxyBindStatusCallback_OnProgress,
524 ProxyBindStatusCallback_OnStopBinding,
525 AsyncBindStatusCallback_GetBindInfo,
526 ProxyBindStatusCallback_OnDataAvailable,
527 ProxyBindStatusCallback_OnObjectAvailable
528 };
529
530 static HRESULT URLStartDownload(LPCWSTR szURL, LPSTREAM *ppStream, IBindStatusCallback *pBSC)
531 {
532 HRESULT hr;
533 IMoniker *pMoniker;
534 IBindCtx *pbc;
535
536 *ppStream = NULL;
537
538 hr = CreateURLMoniker(NULL, szURL, &pMoniker);
539 if (FAILED(hr))
540 return hr;
541
542 hr = CreateBindCtx(0, &pbc);
543 if (FAILED(hr))
544 {
545 IMoniker_Release(pMoniker);
546 return hr;
547 }
548
549 hr = RegisterBindStatusCallback(pbc, pBSC, NULL, 0);
550 if (FAILED(hr))
551 {
552 IBindCtx_Release(pbc);
553 IMoniker_Release(pMoniker);
554 return hr;
555 }
556
557 hr = IMoniker_BindToStorage(pMoniker, pbc, NULL, &IID_IStream, (void **)ppStream);
558
559 /* BindToStorage returning E_PENDING because it's asynchronous is not an error */
560 if (hr == E_PENDING) hr = S_OK;
561
562 IBindCtx_Release(pbc);
563 IMoniker_Release(pMoniker);
564
565 return hr;
566 }
567
568 /***********************************************************************
569 * URLOpenBlockingStreamA (URLMON.@)
570 */
571 HRESULT WINAPI URLOpenBlockingStreamA(LPUNKNOWN pCaller, LPCSTR szURL,
572 LPSTREAM *ppStream, DWORD dwReserved,
573 LPBINDSTATUSCALLBACK lpfnCB)
574 {
575 LPWSTR szURLW;
576 int len;
577 HRESULT hr;
578
579 TRACE("(%p, %s, %p, 0x%x, %p)\n", pCaller, szURL, ppStream, dwReserved, lpfnCB);
580
581 if (!szURL || !ppStream)
582 return E_INVALIDARG;
583
584 len = MultiByteToWideChar(CP_ACP, 0, szURL, -1, NULL, 0);
585 szURLW = heap_alloc(len * sizeof(WCHAR));
586 if (!szURLW)
587 {
588 *ppStream = NULL;
589 return E_OUTOFMEMORY;
590 }
591 MultiByteToWideChar(CP_ACP, 0, szURL, -1, szURLW, len);
592
593 hr = URLOpenBlockingStreamW(pCaller, szURLW, ppStream, dwReserved, lpfnCB);
594
595 heap_free(szURLW);
596
597 return hr;
598 }
599
600 /***********************************************************************
601 * URLOpenBlockingStreamW (URLMON.@)
602 */
603 HRESULT WINAPI URLOpenBlockingStreamW(LPUNKNOWN pCaller, LPCWSTR szURL,
604 LPSTREAM *ppStream, DWORD dwReserved,
605 LPBINDSTATUSCALLBACK lpfnCB)
606 {
607 ProxyBindStatusCallback blocking_bsc;
608
609 TRACE("(%p, %s, %p, 0x%x, %p)\n", pCaller, debugstr_w(szURL), ppStream,
610 dwReserved, lpfnCB);
611
612 if (!szURL || !ppStream)
613 return E_INVALIDARG;
614
615 blocking_bsc.lpVtbl = &BlockingBindStatusCallbackVtbl;
616 blocking_bsc.pBSC = lpfnCB;
617
618 return URLStartDownload(szURL, ppStream, (IBindStatusCallback *)&blocking_bsc);
619 }
620
621 /***********************************************************************
622 * URLOpenStreamA (URLMON.@)
623 */
624 HRESULT WINAPI URLOpenStreamA(LPUNKNOWN pCaller, LPCSTR szURL, DWORD dwReserved,
625 LPBINDSTATUSCALLBACK lpfnCB)
626 {
627 LPWSTR szURLW;
628 int len;
629 HRESULT hr;
630
631 TRACE("(%p, %s, 0x%x, %p)\n", pCaller, szURL, dwReserved, lpfnCB);
632
633 if (!szURL)
634 return E_INVALIDARG;
635
636 len = MultiByteToWideChar(CP_ACP, 0, szURL, -1, NULL, 0);
637 szURLW = heap_alloc(len * sizeof(WCHAR));
638 if (!szURLW)
639 return E_OUTOFMEMORY;
640 MultiByteToWideChar(CP_ACP, 0, szURL, -1, szURLW, len);
641
642 hr = URLOpenStreamW(pCaller, szURLW, dwReserved, lpfnCB);
643
644 heap_free(szURLW);
645
646 return hr;
647 }
648
649 /***********************************************************************
650 * URLOpenStreamW (URLMON.@)
651 */
652 HRESULT WINAPI URLOpenStreamW(LPUNKNOWN pCaller, LPCWSTR szURL, DWORD dwReserved,
653 LPBINDSTATUSCALLBACK lpfnCB)
654 {
655 HRESULT hr;
656 ProxyBindStatusCallback async_bsc;
657 IStream *pStream;
658
659 TRACE("(%p, %s, 0x%x, %p)\n", pCaller, debugstr_w(szURL), dwReserved,
660 lpfnCB);
661
662 if (!szURL)
663 return E_INVALIDARG;
664
665 async_bsc.lpVtbl = &AsyncBindStatusCallbackVtbl;
666 async_bsc.pBSC = lpfnCB;
667
668 hr = URLStartDownload(szURL, &pStream, (IBindStatusCallback *)&async_bsc);
669 if (SUCCEEDED(hr) && pStream)
670 IStream_Release(pStream);
671
672 return hr;
673 }