mlang/tests: Skip tests for unsupported code page identifiers.
[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 "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 = 0;
65
66     heap_free(url);
67
68     if (hr)
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 }