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