1 /*
2  * Copyright 2007 Robert Shearman 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 #define WIN32_NO_STATUS
20 #define _INC_WINDOWS
21 #define COM_NO_WINDOWS_H
22 
23 #define COBJMACROS
24 #define CONST_VTABLE
25 
26 #include <wine/test.h>
27 //#include <stdarg.h>
28 //#include <stddef.h>
29 
30 //#include "windef.h"
31 //#include "winbase.h"
32 #include <winnls.h>
33 #include <ole2.h>
34 //#include "urlmon.h"
35 #include <wininet.h>
36 
37 #define DEFINE_EXPECT(func) \
38     static BOOL expect_ ## func = FALSE, called_ ## func = FALSE
39 
40 #define SET_EXPECT(func) \
41     expect_ ## func = TRUE
42 
43 #define CHECK_EXPECT(func) \
44     do { \
45         ok(expect_ ##func, "unexpected call " #func "\n"); \
46         expect_ ## func = FALSE; \
47         called_ ## func = TRUE; \
48     }while(0)
49 
50 #define CHECK_EXPECT2(func) \
51     do { \
52         ok(expect_ ##func, "unexpected call " #func "\n"); \
53         called_ ## func = TRUE; \
54     }while(0)
55 
56 #define CHECK_CALLED(func) \
57     do { \
58         ok(called_ ## func, "expected " #func "\n"); \
59         expect_ ## func = called_ ## func = FALSE; \
60     }while(0)
61 
62 #define CHECK_NOT_CALLED(func) \
63     do { \
64         ok(!called_ ## func, "unexpected " #func "\n"); \
65         expect_ ## func = called_ ## func = FALSE; \
66     }while(0)
67 
68 #define CLEAR_CALLED(func) \
69     expect_ ## func = called_ ## func = FALSE
70 
71 DEFINE_EXPECT(QueryInterface_IServiceProvider);
72 DEFINE_EXPECT(OnStartBinding);
73 DEFINE_EXPECT(OnProgress_FINDINGRESOURCE);
74 DEFINE_EXPECT(OnProgress_CONNECTING);
75 DEFINE_EXPECT(OnProgress_SENDINGREQUEST);
76 DEFINE_EXPECT(OnProgress_MIMETYPEAVAILABLE);
77 DEFINE_EXPECT(OnProgress_BEGINDOWNLOADDATA);
78 DEFINE_EXPECT(OnProgress_DOWNLOADINGDATA);
79 DEFINE_EXPECT(OnProgress_ENDDOWNLOADDATA);
80 DEFINE_EXPECT(OnStopBinding);
81 DEFINE_EXPECT(OnDataAvailable);
82 DEFINE_EXPECT(GetBindInfo);
83 
84 static const CHAR wszIndexHtmlA[] = "index.html";
85 static WCHAR INDEX_HTML[MAX_PATH];
86 static const char szHtmlDoc[] = "<HTML></HTML>";
87 
88 static HRESULT WINAPI statusclb_QueryInterface(IBindStatusCallback *iface, REFIID riid, void **ppv)
89 {
90     if (IsEqualGUID(&IID_IBindStatusCallback, riid) ||
91         IsEqualGUID(&IID_IUnknown, riid))
92     {
93         *ppv = iface;
94         return S_OK;
95     }
96     else if (IsEqualGUID(&IID_IServiceProvider, riid))
97     {
98         CHECK_EXPECT(QueryInterface_IServiceProvider);
99     }
100 
101     return E_NOINTERFACE;
102 }
103 
104 static ULONG WINAPI statusclb_AddRef(IBindStatusCallback *iface)
105 {
106     return 2;
107 }
108 
109 static ULONG WINAPI statusclb_Release(IBindStatusCallback *iface)
110 {
111     return 1;
112 }
113 
114 static HRESULT WINAPI statusclb_OnStartBinding(IBindStatusCallback *iface, DWORD dwReserved,
115                                                IBinding *pib)
116 {
117     HRESULT hres;
118     IMoniker *mon;
119 
120     CHECK_EXPECT(OnStartBinding);
121 
122     ok(pib != NULL, "pib should not be NULL\n");
123 
124     hres = IBinding_QueryInterface(pib, &IID_IMoniker, (void**)&mon);
125     ok(hres == E_NOINTERFACE, "IBinding should not have IMoniker interface\n");
126     if(SUCCEEDED(hres))
127         IMoniker_Release(mon);
128 
129     return S_OK;
130 }
131 
132 static HRESULT WINAPI statusclb_GetPriority(IBindStatusCallback *iface, LONG *pnPriority)
133 {
134     ok(0, "unexpected call\n");
135     return E_NOTIMPL;
136 }
137 
138 static HRESULT WINAPI statusclb_OnLowResource(IBindStatusCallback *iface, DWORD reserved)
139 {
140     ok(0, "unexpected call\n");
141     return E_NOTIMPL;
142 }
143 
144 static HRESULT WINAPI statusclb_OnProgress(IBindStatusCallback *iface, ULONG ulProgress,
145                                            ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
146 {
147     switch(ulStatusCode) {
148         case BINDSTATUS_FINDINGRESOURCE:
149             CHECK_EXPECT(OnProgress_FINDINGRESOURCE);
150             break;
151         case BINDSTATUS_CONNECTING:
152             CHECK_EXPECT(OnProgress_CONNECTING);
153             break;
154         case BINDSTATUS_SENDINGREQUEST:
155             CHECK_EXPECT(OnProgress_SENDINGREQUEST);
156             break;
157         case BINDSTATUS_MIMETYPEAVAILABLE:
158             CHECK_EXPECT(OnProgress_MIMETYPEAVAILABLE);
159             break;
160         case BINDSTATUS_BEGINDOWNLOADDATA:
161             CHECK_EXPECT(OnProgress_BEGINDOWNLOADDATA);
162             ok(szStatusText != NULL, "szStatusText == NULL\n");
163             break;
164         case BINDSTATUS_DOWNLOADINGDATA:
165             CHECK_EXPECT2(OnProgress_DOWNLOADINGDATA);
166             break;
167         case BINDSTATUS_ENDDOWNLOADDATA:
168             CHECK_EXPECT(OnProgress_ENDDOWNLOADDATA);
169             ok(szStatusText != NULL, "szStatusText == NULL\n");
170             break;
171         case BINDSTATUS_CACHEFILENAMEAVAILABLE:
172             ok(szStatusText != NULL, "szStatusText == NULL\n");
173             break;
174         default:
175             todo_wine { ok(0, "unexpected code %d\n", ulStatusCode); }
176     };
177     return S_OK;
178 }
179 
180 static HRESULT WINAPI statusclb_OnStopBinding(IBindStatusCallback *iface, HRESULT hresult, LPCWSTR szError)
181 {
182     CHECK_EXPECT(OnStopBinding);
183 
184     /* ignore DNS failure */
185     if (hresult != HRESULT_FROM_WIN32(ERROR_INTERNET_NAME_NOT_RESOLVED))
186     {
187         ok(SUCCEEDED(hresult), "Download failed: %08x\n", hresult);
188         ok(szError == NULL, "szError should be NULL\n");
189     }
190 
191     return S_OK;
192 }
193 
194 static HRESULT WINAPI statusclb_GetBindInfo(IBindStatusCallback *iface, DWORD *grfBINDF, BINDINFO *pbindinfo)
195 {
196     DWORD cbSize;
197 
198     CHECK_EXPECT(GetBindInfo);
199 
200     *grfBINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA;
201     cbSize = pbindinfo->cbSize;
202     memset(pbindinfo, 0, cbSize);
203     pbindinfo->cbSize = cbSize;
204 
205     return S_OK;
206 }
207 
208 static HRESULT WINAPI statusclb_OnDataAvailable(IBindStatusCallback *iface, DWORD grfBSCF,
209                                                 DWORD dwSize, FORMATETC* pformatetc, STGMEDIUM* pstgmed)
210 {
211     HRESULT hres;
212     DWORD readed;
213     BYTE buf[512];
214 
215     CHECK_EXPECT2(OnDataAvailable);
216 
217     if (0)
218     {
219         /* FIXME: Uncomment after removing BindToStorage hack. */
220         ok(pformatetc != NULL, "pformatetx == NULL\n");
221         if(pformatetc) {
222             ok(pformatetc->cfFormat == 0xc02d, "clipformat=%x\n", pformatetc->cfFormat);
223             ok(pformatetc->ptd == NULL, "ptd = %p\n", pformatetc->ptd);
224             ok(pformatetc->dwAspect == 1, "dwAspect=%u\n", pformatetc->dwAspect);
225             ok(pformatetc->lindex == -1, "lindex=%d\n", pformatetc->lindex);
226             ok(pformatetc->tymed == TYMED_ISTREAM, "tymed=%u\n", pformatetc->tymed);
227         }
228 
229         ok(pstgmed != NULL, "stgmeg == NULL\n");
230         if(pstgmed) {
231             ok(pstgmed->tymed == TYMED_ISTREAM, "tymed=%u\n", pstgmed->tymed);
232             ok(U(*pstgmed).pstm != NULL, "pstm == NULL\n");
233             ok(pstgmed->pUnkForRelease != NULL, "pUnkForRelease == NULL\n");
234         }
235     }
236 
237     if(U(*pstgmed).pstm) {
238         do hres = IStream_Read(U(*pstgmed).pstm, buf, 512, &readed);
239         while(hres == S_OK);
240         ok(hres == S_FALSE || hres == E_PENDING, "IStream_Read returned %08x\n", hres);
241     }
242 
243     return S_OK;
244 }
245 
246 static HRESULT WINAPI statusclb_OnObjectAvailable(IBindStatusCallback *iface, REFIID riid, IUnknown *punk)
247 {
248     ok(0, "unexpected call\n");
249     return E_NOTIMPL;
250 }
251 
252 static const IBindStatusCallbackVtbl BindStatusCallbackVtbl = {
253     statusclb_QueryInterface,
254     statusclb_AddRef,
255     statusclb_Release,
256     statusclb_OnStartBinding,
257     statusclb_GetPriority,
258     statusclb_OnLowResource,
259     statusclb_OnProgress,
260     statusclb_OnStopBinding,
261     statusclb_GetBindInfo,
262     statusclb_OnDataAvailable,
263     statusclb_OnObjectAvailable
264 };
265 
266 static IBindStatusCallback BindStatusCallback = { &BindStatusCallbackVtbl };
267 
268 static void set_file_url(char *path)
269 {
270     char INDEX_HTML_A[MAX_PATH];
271 
272     lstrcpyA(INDEX_HTML_A, "file:///");
273     lstrcatA(INDEX_HTML_A, path);
274     MultiByteToWideChar(CP_ACP, 0, INDEX_HTML_A, -1, INDEX_HTML, MAX_PATH);
275 }
276 
277 static void create_file(void)
278 {
279     HANDLE file;
280     DWORD size;
281     CHAR path[MAX_PATH];
282 
283     file = CreateFileA(wszIndexHtmlA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
284                        FILE_ATTRIBUTE_NORMAL, NULL);
285     ok(file != INVALID_HANDLE_VALUE, "CreateFile failed\n");
286     if(file == INVALID_HANDLE_VALUE)
287         return;
288 
289     WriteFile(file, szHtmlDoc, sizeof(szHtmlDoc)-1, &size, NULL);
290     CloseHandle(file);
291 
292     GetCurrentDirectoryA(MAX_PATH, path);
293     lstrcatA(path, "\\");
294     lstrcatA(path, wszIndexHtmlA);
295     set_file_url(path);
296 }
297 
298 static void test_URLOpenBlockingStreamW(void)
299 {
300     HRESULT hr;
301     IStream *pStream = NULL;
302     char buffer[256];
303 
304     hr = URLOpenBlockingStreamW(NULL, NULL, &pStream, 0, &BindStatusCallback);
305     ok(hr == E_INVALIDARG, "URLOpenBlockingStreamW should have failed with E_INVALIDARG instead of 0x%08x\n", hr);
306     if (0)  /* crashes on Win2k */
307     {
308         hr = URLOpenBlockingStreamW(NULL, INDEX_HTML, NULL, 0, &BindStatusCallback);
309         ok(hr == E_INVALIDARG, "URLOpenBlockingStreamW should have failed with E_INVALIDARG instead of 0x%08x\n", hr);
310     }
311 
312     SET_EXPECT(GetBindInfo);
313     SET_EXPECT(QueryInterface_IServiceProvider);
314     SET_EXPECT(OnStartBinding);
315     SET_EXPECT(OnProgress_SENDINGREQUEST);
316     SET_EXPECT(OnProgress_MIMETYPEAVAILABLE);
317     SET_EXPECT(OnProgress_BEGINDOWNLOADDATA);
318     SET_EXPECT(OnProgress_ENDDOWNLOADDATA);
319     SET_EXPECT(OnStopBinding);
320 
321     hr = URLOpenBlockingStreamW(NULL, INDEX_HTML, &pStream, 0, &BindStatusCallback);
322     ok(hr == S_OK, "URLOpenBlockingStreamW failed with error 0x%08x\n", hr);
323 
324     CHECK_CALLED(GetBindInfo);
325     todo_wine CHECK_CALLED(QueryInterface_IServiceProvider);
326     CHECK_CALLED(OnStartBinding);
327     CHECK_CALLED(OnProgress_SENDINGREQUEST);
328     CHECK_CALLED(OnProgress_MIMETYPEAVAILABLE);
329     CHECK_CALLED(OnProgress_BEGINDOWNLOADDATA);
330     CHECK_CALLED(OnProgress_ENDDOWNLOADDATA);
331     CHECK_CALLED(OnStopBinding);
332 
333     ok(pStream != NULL, "pStream is NULL\n");
334     if(pStream)
335     {
336         buffer[0] = 0;
337         hr = IStream_Read(pStream, buffer, sizeof(buffer), NULL);
338         ok(hr == S_OK, "IStream_Read failed with error 0x%08x\n", hr);
339         ok(!memcmp(buffer, szHtmlDoc, sizeof(szHtmlDoc)-1), "read data differs from file\n");
340 
341         IStream_Release(pStream);
342     }
343 
344     hr = URLOpenBlockingStreamW(NULL, INDEX_HTML, &pStream, 0, NULL);
345     ok(hr == S_OK, "URLOpenBlockingStreamW failed with error 0x%08x\n", hr);
346 
347     ok(pStream != NULL, "pStream is NULL\n");
348     if(pStream)
349     {
350         buffer[0] = 0;
351         hr = IStream_Read(pStream, buffer, sizeof(buffer), NULL);
352         ok(hr == S_OK, "IStream_Read failed with error 0x%08x\n", hr);
353         ok(!memcmp(buffer, szHtmlDoc, sizeof(szHtmlDoc)-1), "read data differs from file\n");
354 
355         IStream_Release(pStream);
356     }
357 }
358 
359 static void test_URLOpenStreamW(void)
360 {
361     HRESULT hr;
362 
363     hr = URLOpenStreamW(NULL, NULL, 0, &BindStatusCallback);
364     ok(hr == E_INVALIDARG, "URLOpenStreamW should have failed with E_INVALIDARG instead of 0x%08x\n", hr);
365 
366     SET_EXPECT(GetBindInfo);
367     SET_EXPECT(QueryInterface_IServiceProvider);
368     SET_EXPECT(OnStartBinding);
369     SET_EXPECT(OnProgress_SENDINGREQUEST);
370     SET_EXPECT(OnProgress_MIMETYPEAVAILABLE);
371     SET_EXPECT(OnProgress_BEGINDOWNLOADDATA);
372     SET_EXPECT(OnProgress_ENDDOWNLOADDATA);
373     SET_EXPECT(OnDataAvailable);
374     SET_EXPECT(OnStopBinding);
375 
376     hr = URLOpenStreamW(NULL, INDEX_HTML, 0, &BindStatusCallback);
377     ok(hr == S_OK, "URLOpenStreamW failed with error 0x%08x\n", hr);
378 
379     CHECK_CALLED(GetBindInfo);
380     todo_wine CHECK_CALLED(QueryInterface_IServiceProvider);
381     CHECK_CALLED(OnStartBinding);
382     CHECK_CALLED(OnProgress_SENDINGREQUEST);
383     CHECK_CALLED(OnProgress_MIMETYPEAVAILABLE);
384     CHECK_CALLED(OnProgress_BEGINDOWNLOADDATA);
385     CHECK_CALLED(OnProgress_ENDDOWNLOADDATA);
386     CHECK_CALLED(OnDataAvailable);
387     CHECK_CALLED(OnStopBinding);
388 
389     hr = URLOpenStreamW(NULL, INDEX_HTML, 0, NULL);
390     ok(hr == S_OK, "URLOpenStreamW failed with error 0x%08x\n", hr);
391 }
392 
393 START_TEST(stream)
394 {
395     if(!GetProcAddress(GetModuleHandleA("urlmon.dll"), "CompareSecurityIds")) {
396         win_skip("Too old IE\n");
397         return;
398     }
399 
400     create_file();
401     test_URLOpenBlockingStreamW();
402     test_URLOpenStreamW();
403     DeleteFileA(wszIndexHtmlA);
404 }
405