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