shell32: Fix an uninitialized memory access in SHGetFileInfoW.
[wine] / dlls / itss / tests / protocol.c
1 /*
2  * Copyright 2006 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 #define COBJMACROS
20
21 #include <wine/test.h>
22 #include <stdarg.h>
23
24 #include "windef.h"
25 #include "winbase.h"
26 #include "initguid.h"
27 #include "ole2.h"
28 #include "urlmon.h"
29 #include "shlwapi.h"
30
31 #define DEFINE_EXPECT(func) \
32     static BOOL expect_ ## func = FALSE, called_ ## func = FALSE
33
34 #define SET_EXPECT(func) \
35     expect_ ## func = TRUE
36
37 #define CHECK_EXPECT(func) \
38     do { \
39         ok(expect_ ##func, "unexpected call " #func "\n"); \
40         expect_ ## func = FALSE; \
41         called_ ## func = TRUE; \
42     }while(0)
43
44 #define CHECK_EXPECT2(func) \
45     do { \
46         ok(expect_ ##func, "unexpected call " #func  "\n"); \
47         called_ ## func = TRUE; \
48     }while(0)
49
50 #define SET_CALLED(func) \
51     expect_ ## func = called_ ## func = FALSE
52
53 #define CHECK_CALLED(func) \
54     do { \
55         ok(called_ ## func, "expected " #func "\n"); \
56         SET_CALLED(func); \
57     }while(0)
58
59 DEFINE_GUID(CLSID_ITSProtocol,0x9d148291,0xb9c8,0x11d0,0xa4,0xcc,0x00,0x00,0xf8,0x01,0x49,0xf6);
60
61 DEFINE_EXPECT(GetBindInfo);
62 DEFINE_EXPECT(ReportProgress_BEGINDOWNLOADDATA);
63 DEFINE_EXPECT(ReportProgress_SENDINGREQUEST);
64 DEFINE_EXPECT(ReportProgress_MIMETYPEAVAILABLE);
65 DEFINE_EXPECT(ReportProgress_CACHEFILENAMEAVAIABLE);
66 DEFINE_EXPECT(ReportProgress_DIRECTBIND);
67 DEFINE_EXPECT(ReportData);
68 DEFINE_EXPECT(ReportResult);
69
70 static HRESULT expect_hrResult;
71 static IInternetProtocol *read_protocol = NULL;
72 static DWORD bindf;
73
74 static const WCHAR blank_url1[] = {'i','t','s',':',
75     't','e','s','t','.','c','h','m',':',':','/','b','l','a','n','k','.','h','t','m','l',0};
76 static const WCHAR blank_url2[] = {'m','S','-','i','T','s',':',
77     't','e','s','t','.','c','h','m',':',':','/','b','l','a','n','k','.','h','t','m','l',0};
78 static const WCHAR blank_url3[] = {'m','k',':','@','M','S','I','T','S','t','o','r','e',':',
79     't','e','s','t','.','c','h','m',':',':','/','b','l','a','n','k','.','h','t','m','l',0};
80 static const WCHAR blank_url4[] = {'i','t','s',':',
81     't','e','s','t','.','c','h','m',':',':','b','l','a','n','k','.','h','t','m','l',0};
82 static const WCHAR blank_url5[] = {'i','t','s',':',
83     't','e','s','t','.','c','h','m',':',':','\\','b','l','a','n','k','.','h','t','m','l',0};
84 static const WCHAR blank_url6[] = {'i','t','s',':',
85     't','e','s','t','.','c','h','m',':',':','/','%','6','2','l','a','n','k','.','h','t','m','l',0};
86 static const WCHAR blank_url7[] = {'m','k',':','@','M','S','I','T','S','t','o','r','e',':',
87     't','e','s','t','.','c','h','m',':',':','\\','b','l','a','n','k','.','h','t','m','l',0};
88 static const WCHAR blank_url8[] = {'m','k',':','@','M','S','I','T','S','t','o','r','e',':',
89     't','e','s','t','.','c','h','m',':',':','/','b','l','a','n','k','.','h','t','m','l','/',0};
90
91 static enum {
92     ITS_PROTOCOL,
93     MK_PROTOCOL
94 } test_protocol;
95
96 static const WCHAR cache_file1[] =
97     {'t','e','s','t','.','c','h','m',':',':','/','b','l','a','n','k','.','h','t','m','l',0};
98 static const WCHAR cache_file2[] =
99     {'t','e','s','t','.','c','h','m',':',':','\\','b','l','a','n','k','.','h','t','m','l',0};
100 static const WCHAR cache_file3[] =
101     {'t','e','s','t','.','c','h','m',':',':','/','b','l','a','n','k','.','h','t','m','l','/',0};
102 static const WCHAR *cache_file = cache_file1;
103
104 static HRESULT WINAPI ProtocolSink_QueryInterface(IInternetProtocolSink *iface, REFIID riid, void **ppv)
105 {
106     if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IInternetProtocolSink, riid)) {
107         *ppv = iface;
108         return S_OK;
109     }
110     return E_NOINTERFACE;
111 }
112
113 static ULONG WINAPI ProtocolSink_AddRef(IInternetProtocolSink *iface)
114 {
115     return 2;
116 }
117
118 static ULONG WINAPI ProtocolSink_Release(IInternetProtocolSink *iface)
119 {
120     return 1;
121 }
122
123 static HRESULT WINAPI ProtocolSink_Switch(IInternetProtocolSink *iface, PROTOCOLDATA *pProtocolData)
124 {
125     ok(0, "unexpected call\n");
126     return E_NOTIMPL;
127 }
128
129 static HRESULT WINAPI ProtocolSink_ReportProgress(IInternetProtocolSink *iface, ULONG ulStatusCode,
130         LPCWSTR szStatusText)
131 {
132     static const WCHAR blank_html[] = {'b','l','a','n','k','.','h','t','m','l',0};
133     static const WCHAR text_html[] = {'t','e','x','t','/','h','t','m','l',0};
134
135     switch(ulStatusCode) {
136     case BINDSTATUS_BEGINDOWNLOADDATA:
137         CHECK_EXPECT(ReportProgress_BEGINDOWNLOADDATA);
138         ok(!szStatusText, "szStatusText != NULL\n");
139         break;
140     case BINDSTATUS_SENDINGREQUEST:
141         CHECK_EXPECT(ReportProgress_SENDINGREQUEST);
142         if(test_protocol == ITS_PROTOCOL)
143             ok(!lstrcmpW(szStatusText, blank_html), "unexpected szStatusText\n");
144         else
145             ok(szStatusText == NULL, "szStatusText != NULL\n");
146         break;
147     case BINDSTATUS_MIMETYPEAVAILABLE:
148         CHECK_EXPECT(ReportProgress_MIMETYPEAVAILABLE);
149         ok(!lstrcmpW(szStatusText, text_html), "unexpected szStatusText\n");
150         break;
151     case BINDSTATUS_CACHEFILENAMEAVAILABLE:
152         CHECK_EXPECT(ReportProgress_CACHEFILENAMEAVAIABLE);
153         ok(!lstrcmpW(szStatusText, cache_file), "unexpected szStatusText\n");
154         break;
155     case BINDSTATUS_DIRECTBIND:
156         CHECK_EXPECT(ReportProgress_DIRECTBIND);
157         ok(!szStatusText, "szStatusText != NULL\n");
158         break;
159     default:
160         ok(0, "unexpected ulStatusCode %d\n", ulStatusCode);
161         break;
162     }
163
164     return S_OK;
165 }
166
167 static HRESULT WINAPI ProtocolSink_ReportData(IInternetProtocolSink *iface, DWORD grfBSCF, ULONG ulProgress,
168         ULONG ulProgressMax)
169 {
170     CHECK_EXPECT(ReportData);
171
172     ok(ulProgress == ulProgressMax, "ulProgress != ulProgressMax\n");
173     if(test_protocol == ITS_PROTOCOL)
174         ok(grfBSCF == (BSCF_FIRSTDATANOTIFICATION | BSCF_DATAFULLYAVAILABLE), "grcf = %08x\n", grfBSCF);
175     else
176         ok(grfBSCF == (BSCF_FIRSTDATANOTIFICATION | BSCF_LASTDATANOTIFICATION), "grcf = %08x\n", grfBSCF);
177
178     if(read_protocol) {
179         BYTE buf[100];
180         DWORD cb = 0xdeadbeef;
181         HRESULT hres;
182
183         hres = IInternetProtocol_Read(read_protocol, buf, sizeof(buf), &cb);
184         ok(hres == S_OK, "Read failed: %08x\n", hres);
185         ok(cb == 13, "cb=%u expected 13\n", cb);
186         ok(!memcmp(buf, "<html></html>", 13), "unexpected data\n");
187     }
188
189     return S_OK;
190 }
191
192 static HRESULT WINAPI ProtocolSink_ReportResult(IInternetProtocolSink *iface, HRESULT hrResult,
193         DWORD dwError, LPCWSTR szResult)
194 {
195     CHECK_EXPECT(ReportResult);
196
197     ok(hrResult == expect_hrResult, "expected: %08x got: %08x\n", expect_hrResult, hrResult);
198     ok(dwError == 0, "dwError = %d\n", dwError);
199     ok(!szResult, "szResult != NULL\n");
200
201     return S_OK;
202 }
203
204 static IInternetProtocolSinkVtbl protocol_sink_vtbl = {
205     ProtocolSink_QueryInterface,
206     ProtocolSink_AddRef,
207     ProtocolSink_Release,
208     ProtocolSink_Switch,
209     ProtocolSink_ReportProgress,
210     ProtocolSink_ReportData,
211     ProtocolSink_ReportResult
212 };
213
214 static IInternetProtocolSink protocol_sink = {
215     &protocol_sink_vtbl
216 };
217
218 static HRESULT WINAPI BindInfo_QueryInterface(IInternetBindInfo *iface, REFIID riid, void **ppv)
219 {
220     if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IInternetBindInfo, riid)) {
221         *ppv = iface;
222         return S_OK;
223     }
224     return E_NOINTERFACE;
225 }
226
227 static ULONG WINAPI BindInfo_AddRef(IInternetBindInfo *iface)
228 {
229     return 2;
230 }
231
232 static ULONG WINAPI BindInfo_Release(IInternetBindInfo *iface)
233 {
234     return 1;
235 }
236
237 static HRESULT WINAPI BindInfo_GetBindInfo(IInternetBindInfo *iface, DWORD *grfBINDF, BINDINFO *pbindinfo)
238 {
239     CHECK_EXPECT(GetBindInfo);
240
241     ok(grfBINDF != NULL, "grfBINDF == NULL\n");
242     if(grfBINDF)
243         ok(!*grfBINDF, "*grfBINDF != 0\n");
244     ok(pbindinfo != NULL, "pbindinfo == NULL\n");
245     ok(pbindinfo->cbSize == sizeof(BINDINFO), "wrong size of pbindinfo: %d\n", pbindinfo->cbSize);
246
247     *grfBINDF = bindf;
248     return S_OK;
249 }
250
251 static HRESULT WINAPI BindInfo_GetBindString(IInternetBindInfo *iface, ULONG ulStringType, LPOLESTR *ppwzStr,
252         ULONG cEl, ULONG *pcElFetched)
253 {
254     ok(0, "unexpected call\n");
255     return E_NOTIMPL;
256 }
257
258 static IInternetBindInfoVtbl bind_info_vtbl = {
259     BindInfo_QueryInterface,
260     BindInfo_AddRef,
261     BindInfo_Release,
262     BindInfo_GetBindInfo,
263     BindInfo_GetBindString
264 };
265
266 static IInternetBindInfo bind_info = {
267     &bind_info_vtbl
268 };
269
270 static void test_protocol_fail(IInternetProtocol *protocol, LPCWSTR url, HRESULT expected_hres)
271 {
272     HRESULT hres;
273
274     SET_EXPECT(GetBindInfo);
275     SET_EXPECT(ReportResult);
276
277     expect_hrResult = expected_hres;
278     hres = IInternetProtocol_Start(protocol, url, &protocol_sink, &bind_info, 0, 0);
279     ok(hres == expected_hres, "expected: %08x got: %08x\n", expected_hres, hres);
280
281     CHECK_CALLED(GetBindInfo);
282     CHECK_CALLED(ReportResult);
283 }
284
285 #define protocol_start(p,u,e) _protocol_start(__LINE__,p,u,e)
286 static HRESULT _protocol_start(unsigned line, IInternetProtocol *protocol, LPCWSTR url, BOOL expect_mime)
287 {
288     HRESULT hres;
289
290     SET_EXPECT(GetBindInfo);
291     if(test_protocol == MK_PROTOCOL)
292         SET_EXPECT(ReportProgress_DIRECTBIND);
293     SET_EXPECT(ReportProgress_SENDINGREQUEST);
294     if(expect_mime)
295         SET_EXPECT(ReportProgress_MIMETYPEAVAILABLE);
296     if(test_protocol == MK_PROTOCOL)
297         SET_EXPECT(ReportProgress_CACHEFILENAMEAVAIABLE);
298     SET_EXPECT(ReportData);
299     if(test_protocol == ITS_PROTOCOL)
300         SET_EXPECT(ReportProgress_BEGINDOWNLOADDATA);
301     SET_EXPECT(ReportResult);
302     expect_hrResult = S_OK;
303
304     hres = IInternetProtocol_Start(protocol, url, &protocol_sink, &bind_info, 0, 0);
305
306     if(FAILED(hres)) {
307         SET_CALLED(GetBindInfo);
308         if(test_protocol == MK_PROTOCOL)
309             SET_CALLED(ReportProgress_DIRECTBIND);
310         SET_CALLED(ReportProgress_SENDINGREQUEST);
311         if(expect_mime)
312             SET_CALLED(ReportProgress_MIMETYPEAVAILABLE);
313         if(test_protocol == MK_PROTOCOL)
314             SET_EXPECT(ReportProgress_CACHEFILENAMEAVAIABLE);
315         SET_CALLED(ReportData);
316         if(test_protocol == ITS_PROTOCOL)
317             SET_CALLED(ReportProgress_BEGINDOWNLOADDATA);
318         SET_CALLED(ReportResult);
319     }else {
320         CHECK_CALLED(GetBindInfo);
321         if(test_protocol == MK_PROTOCOL)
322             SET_CALLED(ReportProgress_DIRECTBIND);
323         CHECK_CALLED(ReportProgress_SENDINGREQUEST);
324         if(expect_mime)
325             CHECK_CALLED(ReportProgress_MIMETYPEAVAILABLE);
326         if(test_protocol == MK_PROTOCOL)
327             SET_EXPECT(ReportProgress_CACHEFILENAMEAVAIABLE);
328         CHECK_CALLED(ReportData);
329         if(test_protocol == ITS_PROTOCOL)
330             CHECK_CALLED(ReportProgress_BEGINDOWNLOADDATA);
331         CHECK_CALLED(ReportResult);
332     }
333
334     return hres;
335 }
336
337 static void test_protocol_url(IClassFactory *factory, LPCWSTR url, BOOL expect_mime)
338 {
339     IInternetProtocol *protocol;
340     BYTE buf[512];
341     ULONG cb, ref;
342     HRESULT hres;
343
344     hres = IClassFactory_CreateInstance(factory, NULL, &IID_IInternetProtocol, (void**)&protocol);
345     ok(hres == S_OK, "Could not get IInternetProtocol: %08x\n", hres);
346     if(FAILED(hres))
347         return;
348
349     hres = protocol_start(protocol, url, expect_mime);
350     if(FAILED(hres)) {
351         IInternetProtocol_Release(protocol);
352         return;
353     }
354
355     hres = IInternetProtocol_Read(protocol, buf, sizeof(buf), &cb);
356     ok(hres == S_OK, "Read failed: %08x\n", hres);
357     ok(cb == 13, "cb=%u expected 13\n", cb);
358     ok(!memcmp(buf, "<html></html>", 13), "unexpected data\n");
359     ref = IInternetProtocol_Release(protocol);
360     ok(!ref, "protocol ref=%d\n", ref);
361
362     hres = IClassFactory_CreateInstance(factory, NULL, &IID_IInternetProtocol, (void**)&protocol);
363     ok(hres == S_OK, "Could not get IInternetProtocol: %08x\n", hres);
364     if(FAILED(hres))
365         return;
366
367     cb = 0xdeadbeef;
368     hres = IInternetProtocol_Read(protocol, buf, sizeof(buf), &cb);
369     ok(hres == (test_protocol == ITS_PROTOCOL ? INET_E_DATA_NOT_AVAILABLE : E_FAIL),
370        "Read returned %08x\n", hres);
371     ok(cb == 0xdeadbeef, "cb=%u expected 0xdeadbeef\n", cb);
372
373     protocol_start(protocol, url, expect_mime);
374     hres = IInternetProtocol_Read(protocol, buf, 2, &cb);
375     ok(hres == S_OK, "Read failed: %08x\n", hres);
376     ok(cb == 2, "cb=%u expected 2\n", cb);
377     hres = IInternetProtocol_Read(protocol, buf, sizeof(buf), &cb);
378     ok(hres == S_OK, "Read failed: %08x\n", hres);
379     ok(cb == 11, "cb=%u, expected 11\n", cb);
380     hres = IInternetProtocol_Read(protocol, buf, sizeof(buf), &cb);
381     ok(hres == S_FALSE, "Read failed: %08x expected S_FALSE\n", hres);
382     ok(cb == 0, "cb=%u expected 0\n", cb);
383     hres = IInternetProtocol_UnlockRequest(protocol);
384     ok(hres == S_OK, "UnlockRequest failed: %08x\n", hres);
385     ref = IInternetProtocol_Release(protocol);
386     ok(!ref, "protocol ref=%d\n", ref);
387
388     hres = IClassFactory_CreateInstance(factory, NULL, &IID_IInternetProtocol, (void**)&protocol);
389     ok(hres == S_OK, "Could not get IInternetProtocol: %08x\n", hres);
390     if(FAILED(hres))
391         return;
392
393     protocol_start(protocol, url, expect_mime);
394     hres = IInternetProtocol_Read(protocol, buf, 2, &cb);
395     ok(hres == S_OK, "Read failed: %08x\n", hres);
396     hres = IInternetProtocol_LockRequest(protocol, 0);
397     ok(hres == S_OK, "LockRequest failed: %08x\n", hres);
398     hres = IInternetProtocol_UnlockRequest(protocol);
399     ok(hres == S_OK, "UnlockRequest failed: %08x\n", hres);
400     hres = IInternetProtocol_Read(protocol, buf, sizeof(buf), &cb);
401     ok(hres == S_OK, "Read failed: %08x\n", hres);
402     ok(cb == 11, "cb=%u, expected 11\n", cb);
403     ref = IInternetProtocol_Release(protocol);
404     ok(!ref, "protocol ref=%d\n", ref);
405
406     hres = IClassFactory_CreateInstance(factory, NULL, &IID_IInternetProtocol, (void**)&protocol);
407     ok(hres == S_OK, "Could not get IInternetProtocol: %08x\n", hres);
408     if(FAILED(hres))
409         return;
410
411     protocol_start(protocol, url, expect_mime);
412     hres = IInternetProtocol_LockRequest(protocol, 0);
413     ok(hres == S_OK, "LockRequest failed: %08x\n", hres);
414     hres = IInternetProtocol_Terminate(protocol, 0);
415     ok(hres == S_OK, "Terminate failed: %08x\n", hres);
416     hres = IInternetProtocol_Read(protocol, buf, 2, &cb);
417     ok(hres == S_OK, "Read failed: %08x\n", hres);
418     ok(cb == 2, "cb=%u, expected 2\n", cb);
419     hres = IInternetProtocol_UnlockRequest(protocol);
420     ok(hres == S_OK, "UnlockRequest failed: %08x\n", hres);
421     hres = IInternetProtocol_Read(protocol, buf, 2, &cb);
422     ok(hres == S_OK, "Read failed: %08x\n", hres);
423     ok(cb == 2, "cb=%u, expected 2\n", cb);
424     hres = IInternetProtocol_Terminate(protocol, 0);
425     ok(hres == S_OK, "Terminate failed: %08x\n", hres);
426     hres = IInternetProtocol_Read(protocol, buf, 2, &cb);
427     ok(hres == S_OK, "Read failed: %08x\n", hres);
428     ok(cb == 2, "cb=%u expected 2\n", cb);
429     ref = IInternetProtocol_Release(protocol);
430     ok(!ref, "protocol ref=%d\n", ref);
431
432     hres = IClassFactory_CreateInstance(factory, NULL, &IID_IInternetProtocol, (void**)&read_protocol);
433     ok(hres == S_OK, "Could not get IInternetProtocol: %08x\n", hres);
434     if(FAILED(hres))
435         return;
436
437     protocol_start(read_protocol, url, expect_mime);
438     ref = IInternetProtocol_Release(read_protocol);
439     ok(!ref, "protocol ref=%d\n", ref);
440     read_protocol = NULL;
441 }
442
443 static const WCHAR rel_url1[] =
444     {'t','e','s','t','.','h','t','m','l',0};
445 static const WCHAR rel_url2[] =
446     {'t','e','s','t','.','c','h','m',':',':','/','t','e','s','t','.','h','t','m','l',0};
447 static const WCHAR rel_url3[] =
448     {'/','t','e','s','t','.','h','t','m','l',0};
449 static const WCHAR rel_url4[] =
450     {'t','e',':','t','.','h','t','m','l',0};
451 static const WCHAR rel_url5[] =
452     {'d','i','r','/','t','e','s','t','.','h','t','m','l',0};
453
454 static const WCHAR base_url1[] = {'i','t','s',':',
455     't','e','s','t',':','.','c','h','m',':',':','/','b','l','a','n','k','.','h','t','m','l',0};
456 static const WCHAR base_url2[] = {'i','t','s',':','t','e','s','t','.','c','h','m',
457     ':',':','/','d','i','r','/','b','l','a','n','k','.','h','t','m','l',0};
458 static const WCHAR base_url3[] = {'m','s','-','i','t','s',':','t','e','s','t','.','c','h','m',
459     ':',':','/','d','i','r','/','b','l','a','n','k','.','h','t','m','l',0};
460 static const WCHAR base_url4[] = {'m','k',':','@','M','S','I','T','S','t','o','r','e',':',
461     't','e','s','t','.','c','h','m',':',':','/','d','i','r','/',
462     'b','l','a','n','k','.','h','t','m','l',0};
463 static const WCHAR base_url5[] = {'x','x','x',':','t','e','s','t','.','c','h','m',
464     ':',':','/','d','i','r','/','b','l','a','n','k','.','h','t','m','l',0};
465
466 static const WCHAR combined_url1[] = {'i','t','s',':',
467     't','e','s','t','.','c','h','m',':',':','/','t','e','s','t','.','h','t','m','l',0};
468 static const WCHAR combined_url2[] = {'i','t','s',':',
469     't','e','s','t','.','c','h','m',':',':','/','d','i','r','/','t','e','s','t','.','h','t','m','l',0};
470 static const WCHAR combined_url3[] = {'i','t','s',':',
471     't','e','s','t',':','.','c','h','m',':',':','/','t','e','s','t','.','h','t','m','l',0};
472 static const WCHAR combined_url4[] = {'i','t','s',':','t','e','s','t','.','c','h','m',
473     ':',':','b','l','a','n','k','.','h','t','m','l','t','e','s','t','.','h','t','m','l',0};
474 static const WCHAR combined_url5[] = {'m','s','-','i','t','s',':',
475     't','e','s','t','.','c','h','m',':',':','/','d','i','r','/','t','e','s','t','.','h','t','m','l',0};
476 static const WCHAR combined_url6[] = {'m','k',':','@','M','S','I','T','S','t','o','r','e',':',
477     't','e','s','t','.','c','h','m',':',':','/','d','i','r','/','t','e','s','t','.','h','t','m','l',0};
478
479 static const struct {
480     LPCWSTR base_url;
481     LPCWSTR rel_url;
482     DWORD flags;
483     HRESULT hres;
484     LPCWSTR combined_url;
485 } combine_tests[] = {
486     {blank_url1, blank_url1, 0, STG_E_INVALIDNAME, NULL},
487     {blank_url2, blank_url2, 0, STG_E_INVALIDNAME, NULL},
488     {blank_url1, rel_url1, 0, S_OK, combined_url1},
489     {blank_url1, rel_url2, 0, STG_E_INVALIDNAME, NULL},
490     {blank_url1, rel_url3, 0, S_OK, combined_url1},
491     {blank_url1, rel_url4, 0, STG_E_INVALIDNAME, NULL},
492     {blank_url1, rel_url3, URL_ESCAPE_SPACES_ONLY|URL_DONT_ESCAPE_EXTRA_INFO, S_OK, combined_url1},
493     {blank_url1, rel_url5, 0, S_OK, combined_url2},
494     {rel_url1, rel_url2, 0, 0x80041001, NULL},
495     {base_url1, rel_url1, 0, S_OK, combined_url3},
496     {base_url2, rel_url1, 0, S_OK, combined_url2},
497     {blank_url4, rel_url1, 0, S_OK, combined_url4},
498     {base_url3, rel_url1, 0, S_OK, combined_url5},
499     {base_url4, rel_url1, 0, S_OK, combined_url6},
500     {base_url5, rel_url1, 0, INET_E_USE_DEFAULT_PROTOCOLHANDLER, NULL},
501     {base_url2, rel_url3, 0, S_OK, combined_url1},
502 };
503
504 static void test_its_protocol_info(IInternetProtocol *protocol)
505 {
506     IInternetProtocolInfo *info;
507     WCHAR buf[1024];
508     DWORD size, i;
509     HRESULT hres;
510
511     hres = IInternetProtocol_QueryInterface(protocol, &IID_IInternetProtocolInfo, (void**)&info);
512     ok(hres == S_OK, "Could not get IInternetProtocolInfo interface: %08x\n", hres);
513     if(FAILED(hres))
514         return;
515
516     for(i = PARSE_CANONICALIZE; i <= PARSE_UNESCAPE; i++) {
517         if(i != PARSE_CANONICALIZE && i != PARSE_SECURITY_URL) {
518             hres = IInternetProtocolInfo_ParseUrl(info, blank_url1, i, 0, buf,
519                     sizeof(buf)/sizeof(buf[0]), &size, 0);
520             ok(hres == INET_E_DEFAULT_ACTION,
521                "[%d] failed: %08x, expected INET_E_DEFAULT_ACTION\n", i, hres);
522         }
523     }
524
525     for(i=0; i < sizeof(combine_tests)/sizeof(combine_tests[0]); i++) {
526         size = 0xdeadbeef;
527         memset(buf, 0xfe, sizeof(buf));
528         hres = IInternetProtocolInfo_CombineUrl(info, combine_tests[i].base_url,
529                 combine_tests[i].rel_url, combine_tests[i].flags, buf,
530                 sizeof(buf)/sizeof(WCHAR), &size, 0);
531         ok(hres == combine_tests[i].hres, "[%d] CombineUrl returned %08x, expected %08x\n",
532            i, hres, combine_tests[i].hres);
533         ok(size == (combine_tests[i].combined_url ? lstrlenW(combine_tests[i].combined_url)+1
534            : 0xdeadbeef), "[%d] unexpected size=%d\n", i, size);
535         if(combine_tests[i].combined_url)
536             ok(!lstrcmpW(combine_tests[i].combined_url, buf), "[%d] unexpected result\n", i);
537         else
538             ok(buf[0] == 0xfefe, "buf changed\n");
539     }
540
541     size = 0xdeadbeef;
542     memset(buf, 0xfe, sizeof(buf));
543     hres = IInternetProtocolInfo_CombineUrl(info, blank_url1, rel_url1, 0, buf,
544             1, &size, 0);
545     ok(hres == E_OUTOFMEMORY, "CombineUrl failed: %08x\n", hres);
546     ok(size == sizeof(combined_url1)/sizeof(WCHAR), "size=%d\n", size);
547     ok(buf[0] == 0xfefe, "buf changed\n");
548
549     IInternetProtocolInfo_Release(info);
550 }
551
552 static void test_its_protocol(void)
553 {
554     IInternetProtocolInfo *info;
555     IClassFactory *factory;
556     IUnknown *unk;
557     ULONG ref;
558     HRESULT hres;
559
560     static const WCHAR wrong_url1[] =
561         {'i','t','s',':','t','e','s','t','.','c','h','m',':',':','/','b','l','a','n','.','h','t','m','l',0};
562     static const WCHAR wrong_url2[] =
563         {'i','t','s',':','t','e','s','.','c','h','m',':',':','b','/','l','a','n','k','.','h','t','m','l',0};
564     static const WCHAR wrong_url3[] =
565         {'i','t','s',':','t','e','s','t','.','c','h','m','/','b','l','a','n','k','.','h','t','m','l',0};
566     static const WCHAR wrong_url4[] = {'m','k',':','@','M','S','I','T','S','t','o','r',':',
567          't','e','s','t','.','c','h','m',':',':','/','b','l','a','n','k','.','h','t','m','l',0};
568     static const WCHAR wrong_url5[] = {'f','i','l','e',':',
569         't','e','s','.','c','h','m',':',':','/','b','l','a','n','k','.','h','t','m','l',0};
570
571     test_protocol = ITS_PROTOCOL;
572
573     hres = CoGetClassObject(&CLSID_ITSProtocol, CLSCTX_INPROC_SERVER, NULL, &IID_IUnknown, (void**)&unk);
574     ok(hres == S_OK ||
575        broken(hres == REGDB_E_CLASSNOTREG), /* Some W95 and NT4 */
576        "CoGetClassObject failed: %08x\n", hres);
577     if(FAILED(hres))
578         return;
579
580     hres = IUnknown_QueryInterface(unk, &IID_IInternetProtocolInfo, (void**)&info);
581     ok(hres == E_NOINTERFACE, "Could not get IInternetProtocolInfo: %08x\n", hres);
582
583     hres = IUnknown_QueryInterface(unk, &IID_IClassFactory, (void**)&factory);
584     ok(hres == S_OK, "Could not get IClassFactory interface\n");
585     if(SUCCEEDED(hres)) {
586         IInternetProtocol *protocol;
587
588         hres = IClassFactory_CreateInstance(factory, NULL, &IID_IInternetProtocol, (void**)&protocol);
589         ok(hres == S_OK, "Could not get IInternetProtocol: %08x\n", hres);
590         if(SUCCEEDED(hres)) {
591             test_its_protocol_info(protocol);
592
593             test_protocol_fail(protocol, wrong_url1, STG_E_FILENOTFOUND);
594             test_protocol_fail(protocol, wrong_url2, STG_E_FILENOTFOUND);
595             test_protocol_fail(protocol, wrong_url3, STG_E_FILENOTFOUND);
596
597             hres = IInternetProtocol_Start(protocol, wrong_url4, &protocol_sink, &bind_info, 0, 0);
598             ok(hres == INET_E_USE_DEFAULT_PROTOCOLHANDLER,
599                "Start failed: %08x, expected INET_E_USE_DEFAULT_PROTOCOLHANDLER\n", hres);
600
601             hres = IInternetProtocol_Start(protocol, wrong_url5, &protocol_sink, &bind_info, 0, 0);
602             ok(hres == INET_E_USE_DEFAULT_PROTOCOLHANDLER,
603                "Start failed: %08x, expected INET_E_USE_DEFAULT_PROTOCOLHANDLER\n", hres);
604
605             ref = IInternetProtocol_Release(protocol);
606             ok(!ref, "protocol ref=%d\n", ref);
607
608             test_protocol_url(factory, blank_url1, TRUE);
609             test_protocol_url(factory, blank_url2, TRUE);
610             test_protocol_url(factory, blank_url3, TRUE);
611             test_protocol_url(factory, blank_url4, TRUE);
612             test_protocol_url(factory, blank_url5, TRUE);
613             test_protocol_url(factory, blank_url6, TRUE);
614             test_protocol_url(factory, blank_url8, TRUE);
615             bindf = BINDF_FROMURLMON | BINDF_NEEDFILE;
616             test_protocol_url(factory, blank_url1, TRUE);
617         }
618
619         IClassFactory_Release(factory);
620     }
621
622     IUnknown_Release(unk);
623 }
624
625 static void test_mk_protocol(void)
626 {
627     IClassFactory *cf;
628     HRESULT hres;
629
630     test_protocol = MK_PROTOCOL;
631
632     hres = CoGetClassObject(&CLSID_MkProtocol, CLSCTX_INPROC_SERVER, NULL, &IID_IClassFactory,
633                             (void**)&cf);
634     ok(hres == S_OK ||
635        broken(hres == REGDB_E_CLASSNOTREG), /* Some W95 and NT4 */
636        "CoGetClassObject failed: %08x\n", hres);
637     if(FAILED(hres))
638         return;
639
640     cache_file = cache_file1;
641     test_protocol_url(cf, blank_url3, TRUE);
642     cache_file = cache_file2;
643     test_protocol_url(cf, blank_url7, TRUE);
644     cache_file = cache_file3;
645     test_protocol_url(cf, blank_url8, FALSE);
646
647     IClassFactory_Release(cf);
648 }
649
650 static BOOL create_chm(void)
651 {
652     HANDLE file;
653     HRSRC src;
654     DWORD size;
655
656     file = CreateFileA("test.chm", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
657             FILE_ATTRIBUTE_NORMAL, NULL);
658     ok(file != INVALID_HANDLE_VALUE, "Could not create test.chm file\n");
659     if(file == INVALID_HANDLE_VALUE)
660         return FALSE;
661
662     src = FindResourceA(NULL, MAKEINTRESOURCEA(60), MAKEINTRESOURCEA(60));
663
664     WriteFile(file, LoadResource(NULL, src), SizeofResource(NULL, src), &size, NULL);
665     CloseHandle(file);
666
667     return TRUE;
668 }
669
670 static void delete_chm(void)
671 {
672     BOOL ret;
673
674     ret = DeleteFileA("test.chm");
675     ok(ret, "DeleteFileA failed: %d\n", GetLastError());
676 }
677
678 START_TEST(protocol)
679 {
680     OleInitialize(NULL);
681
682     if(!create_chm())
683         return;
684
685     test_its_protocol();
686     test_mk_protocol();
687
688     delete_chm();
689     OleUninitialize();
690 }