ntdll: The FileMailslotSetInformation and FileCompletionInformation cases of NtSetInf...
[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     ok_(__FILE__,line) (hres == S_OK, "Start failed: %08x\n", hres);
307
308     if(FAILED(hres)) {
309         SET_CALLED(GetBindInfo);
310         if(test_protocol == MK_PROTOCOL)
311             SET_CALLED(ReportProgress_DIRECTBIND);
312         SET_CALLED(ReportProgress_SENDINGREQUEST);
313         if(expect_mime)
314             SET_CALLED(ReportProgress_MIMETYPEAVAILABLE);
315         if(test_protocol == MK_PROTOCOL)
316             SET_EXPECT(ReportProgress_CACHEFILENAMEAVAIABLE);
317         SET_CALLED(ReportData);
318         if(test_protocol == ITS_PROTOCOL)
319             SET_CALLED(ReportProgress_BEGINDOWNLOADDATA);
320         SET_CALLED(ReportResult);
321     }else {
322         CHECK_CALLED(GetBindInfo);
323         if(test_protocol == MK_PROTOCOL)
324             SET_CALLED(ReportProgress_DIRECTBIND);
325         CHECK_CALLED(ReportProgress_SENDINGREQUEST);
326         if(expect_mime)
327             CHECK_CALLED(ReportProgress_MIMETYPEAVAILABLE);
328         if(test_protocol == MK_PROTOCOL)
329             SET_EXPECT(ReportProgress_CACHEFILENAMEAVAIABLE);
330         CHECK_CALLED(ReportData);
331         if(test_protocol == ITS_PROTOCOL)
332             CHECK_CALLED(ReportProgress_BEGINDOWNLOADDATA);
333         CHECK_CALLED(ReportResult);
334     }
335
336     return hres;
337 }
338
339 static void test_protocol_url(IClassFactory *factory, LPCWSTR url, BOOL expect_mime)
340 {
341     IInternetProtocol *protocol;
342     BYTE buf[512];
343     ULONG cb, ref;
344     HRESULT hres;
345
346     hres = IClassFactory_CreateInstance(factory, NULL, &IID_IInternetProtocol, (void**)&protocol);
347     ok(hres == S_OK, "Could not get IInternetProtocol: %08x\n", hres);
348     if(FAILED(hres))
349         return;
350
351     hres = protocol_start(protocol, url, expect_mime);
352     if(FAILED(hres)) {
353         IInternetProtocol_Release(protocol);
354         return;
355     }
356
357     hres = IInternetProtocol_Read(protocol, buf, sizeof(buf), &cb);
358     ok(hres == S_OK, "Read failed: %08x\n", hres);
359     ok(cb == 13, "cb=%u expected 13\n", cb);
360     ok(!memcmp(buf, "<html></html>", 13), "unexpected data\n");
361     ref = IInternetProtocol_Release(protocol);
362     ok(!ref, "protocol ref=%d\n", ref);
363
364     hres = IClassFactory_CreateInstance(factory, NULL, &IID_IInternetProtocol, (void**)&protocol);
365     ok(hres == S_OK, "Could not get IInternetProtocol: %08x\n", hres);
366     if(FAILED(hres))
367         return;
368
369     cb = 0xdeadbeef;
370     hres = IInternetProtocol_Read(protocol, buf, sizeof(buf), &cb);
371     ok(hres == (test_protocol == ITS_PROTOCOL ? INET_E_DATA_NOT_AVAILABLE : E_FAIL),
372        "Read returned %08x\n", hres);
373     ok(cb == 0xdeadbeef, "cb=%u expected 0xdeadbeef\n", cb);
374
375     protocol_start(protocol, url, expect_mime);
376     hres = IInternetProtocol_Read(protocol, buf, 2, &cb);
377     ok(hres == S_OK, "Read failed: %08x\n", hres);
378     ok(cb == 2, "cb=%u expected 2\n", cb);
379     hres = IInternetProtocol_Read(protocol, buf, sizeof(buf), &cb);
380     ok(hres == S_OK, "Read failed: %08x\n", hres);
381     ok(cb == 11, "cb=%u, expected 11\n", cb);
382     hres = IInternetProtocol_Read(protocol, buf, sizeof(buf), &cb);
383     ok(hres == S_FALSE, "Read failed: %08x expected S_FALSE\n", hres);
384     ok(cb == 0, "cb=%u expected 0\n", cb);
385     hres = IInternetProtocol_UnlockRequest(protocol);
386     ok(hres == S_OK, "UnlockRequest failed: %08x\n", hres);
387     ref = IInternetProtocol_Release(protocol);
388     ok(!ref, "protocol ref=%d\n", ref);
389
390     hres = IClassFactory_CreateInstance(factory, NULL, &IID_IInternetProtocol, (void**)&protocol);
391     ok(hres == S_OK, "Could not get IInternetProtocol: %08x\n", hres);
392     if(FAILED(hres))
393         return;
394
395     protocol_start(protocol, url, expect_mime);
396     hres = IInternetProtocol_Read(protocol, buf, 2, &cb);
397     ok(hres == S_OK, "Read failed: %08x\n", hres);
398     hres = IInternetProtocol_LockRequest(protocol, 0);
399     ok(hres == S_OK, "LockRequest failed: %08x\n", hres);
400     hres = IInternetProtocol_UnlockRequest(protocol);
401     ok(hres == S_OK, "UnlockRequest failed: %08x\n", hres);
402     hres = IInternetProtocol_Read(protocol, buf, sizeof(buf), &cb);
403     ok(hres == S_OK, "Read failed: %08x\n", hres);
404     ok(cb == 11, "cb=%u, expected 11\n", cb);
405     ref = IInternetProtocol_Release(protocol);
406     ok(!ref, "protocol ref=%d\n", ref);
407
408     hres = IClassFactory_CreateInstance(factory, NULL, &IID_IInternetProtocol, (void**)&protocol);
409     ok(hres == S_OK, "Could not get IInternetProtocol: %08x\n", hres);
410     if(FAILED(hres))
411         return;
412
413     protocol_start(protocol, url, expect_mime);
414     hres = IInternetProtocol_LockRequest(protocol, 0);
415     ok(hres == S_OK, "LockRequest failed: %08x\n", hres);
416     hres = IInternetProtocol_Terminate(protocol, 0);
417     ok(hres == S_OK, "Terminate failed: %08x\n", hres);
418     hres = IInternetProtocol_Read(protocol, buf, 2, &cb);
419     ok(hres == S_OK, "Read failed: %08x\n", hres);
420     ok(cb == 2, "cb=%u, expected 2\n", cb);
421     hres = IInternetProtocol_UnlockRequest(protocol);
422     ok(hres == S_OK, "UnlockRequest failed: %08x\n", hres);
423     hres = IInternetProtocol_Read(protocol, buf, 2, &cb);
424     ok(hres == S_OK, "Read failed: %08x\n", hres);
425     ok(cb == 2, "cb=%u, expected 2\n", cb);
426     hres = IInternetProtocol_Terminate(protocol, 0);
427     ok(hres == S_OK, "Terminate failed: %08x\n", hres);
428     hres = IInternetProtocol_Read(protocol, buf, 2, &cb);
429     ok(hres == S_OK, "Read failed: %08x\n", hres);
430     ok(cb == 2, "cb=%u expected 2\n", cb);
431     ref = IInternetProtocol_Release(protocol);
432     ok(!ref, "protocol ref=%d\n", ref);
433
434     hres = IClassFactory_CreateInstance(factory, NULL, &IID_IInternetProtocol, (void**)&read_protocol);
435     ok(hres == S_OK, "Could not get IInternetProtocol: %08x\n", hres);
436     if(FAILED(hres))
437         return;
438
439     protocol_start(read_protocol, url, expect_mime);
440     ref = IInternetProtocol_Release(read_protocol);
441     ok(!ref, "protocol ref=%d\n", ref);
442     read_protocol = NULL;
443 }
444
445 static const WCHAR rel_url1[] =
446     {'t','e','s','t','.','h','t','m','l',0};
447 static const WCHAR rel_url2[] =
448     {'t','e','s','t','.','c','h','m',':',':','/','t','e','s','t','.','h','t','m','l',0};
449 static const WCHAR rel_url3[] =
450     {'/','t','e','s','t','.','h','t','m','l',0};
451 static const WCHAR rel_url4[] =
452     {'t','e',':','t','.','h','t','m','l',0};
453 static const WCHAR rel_url5[] =
454     {'d','i','r','/','t','e','s','t','.','h','t','m','l',0};
455
456 static const WCHAR base_url1[] = {'i','t','s',':',
457     't','e','s','t',':','.','c','h','m',':',':','/','b','l','a','n','k','.','h','t','m','l',0};
458 static const WCHAR base_url2[] = {'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_url3[] = {'m','s','-','i','t','s',':','t','e','s','t','.','c','h','m',
461     ':',':','/','d','i','r','/','b','l','a','n','k','.','h','t','m','l',0};
462 static const WCHAR base_url4[] = {'m','k',':','@','M','S','I','T','S','t','o','r','e',':',
463     't','e','s','t','.','c','h','m',':',':','/','d','i','r','/',
464     'b','l','a','n','k','.','h','t','m','l',0};
465 static const WCHAR base_url5[] = {'x','x','x',':','t','e','s','t','.','c','h','m',
466     ':',':','/','d','i','r','/','b','l','a','n','k','.','h','t','m','l',0};
467
468 static const WCHAR combined_url1[] = {'i','t','s',':',
469     't','e','s','t','.','c','h','m',':',':','/','t','e','s','t','.','h','t','m','l',0};
470 static const WCHAR combined_url2[] = {'i','t','s',':',
471     't','e','s','t','.','c','h','m',':',':','/','d','i','r','/','t','e','s','t','.','h','t','m','l',0};
472 static const WCHAR combined_url3[] = {'i','t','s',':',
473     't','e','s','t',':','.','c','h','m',':',':','/','t','e','s','t','.','h','t','m','l',0};
474 static const WCHAR combined_url4[] = {'i','t','s',':','t','e','s','t','.','c','h','m',
475     ':',':','b','l','a','n','k','.','h','t','m','l','t','e','s','t','.','h','t','m','l',0};
476 static const WCHAR combined_url5[] = {'m','s','-','i','t','s',':',
477     't','e','s','t','.','c','h','m',':',':','/','d','i','r','/','t','e','s','t','.','h','t','m','l',0};
478 static const WCHAR combined_url6[] = {'m','k',':','@','M','S','I','T','S','t','o','r','e',':',
479     't','e','s','t','.','c','h','m',':',':','/','d','i','r','/','t','e','s','t','.','h','t','m','l',0};
480
481 static const struct {
482     LPCWSTR base_url;
483     LPCWSTR rel_url;
484     DWORD flags;
485     HRESULT hres;
486     LPCWSTR combined_url;
487 } combine_tests[] = {
488     {blank_url1, blank_url1, 0, STG_E_INVALIDNAME, NULL},
489     {blank_url2, blank_url2, 0, STG_E_INVALIDNAME, NULL},
490     {blank_url1, rel_url1, 0, S_OK, combined_url1},
491     {blank_url1, rel_url2, 0, STG_E_INVALIDNAME, NULL},
492     {blank_url1, rel_url3, 0, S_OK, combined_url1},
493     {blank_url1, rel_url4, 0, STG_E_INVALIDNAME, NULL},
494     {blank_url1, rel_url3, URL_ESCAPE_SPACES_ONLY|URL_DONT_ESCAPE_EXTRA_INFO, S_OK, combined_url1},
495     {blank_url1, rel_url5, 0, S_OK, combined_url2},
496     {rel_url1, rel_url2, 0, 0x80041001, NULL},
497     {base_url1, rel_url1, 0, S_OK, combined_url3},
498     {base_url2, rel_url1, 0, S_OK, combined_url2},
499     {blank_url4, rel_url1, 0, S_OK, combined_url4},
500     {base_url3, rel_url1, 0, S_OK, combined_url5},
501     {base_url4, rel_url1, 0, S_OK, combined_url6},
502     {base_url5, rel_url1, 0, INET_E_USE_DEFAULT_PROTOCOLHANDLER, NULL},
503     {base_url2, rel_url3, 0, S_OK, combined_url1},
504 };
505
506 static void test_its_protocol_info(IInternetProtocol *protocol)
507 {
508     IInternetProtocolInfo *info;
509     WCHAR buf[1024];
510     DWORD size, i;
511     HRESULT hres;
512
513     hres = IInternetProtocol_QueryInterface(protocol, &IID_IInternetProtocolInfo, (void**)&info);
514     ok(hres == S_OK, "Could not get IInternetProtocolInfo interface: %08x\n", hres);
515     if(FAILED(hres))
516         return;
517
518     for(i = PARSE_CANONICALIZE; i <= PARSE_UNESCAPE; i++) {
519         if(i != PARSE_CANONICALIZE && i != PARSE_SECURITY_URL) {
520             hres = IInternetProtocolInfo_ParseUrl(info, blank_url1, i, 0, buf,
521                     sizeof(buf)/sizeof(buf[0]), &size, 0);
522             ok(hres == INET_E_DEFAULT_ACTION,
523                "[%d] failed: %08x, expected INET_E_DEFAULT_ACTION\n", i, hres);
524         }
525     }
526
527     for(i=0; i < sizeof(combine_tests)/sizeof(combine_tests[0]); i++) {
528         size = 0xdeadbeef;
529         memset(buf, 0xfe, sizeof(buf));
530         hres = IInternetProtocolInfo_CombineUrl(info, combine_tests[i].base_url,
531                 combine_tests[i].rel_url, combine_tests[i].flags, buf,
532                 sizeof(buf)/sizeof(WCHAR), &size, 0);
533         ok(hres == combine_tests[i].hres, "[%d] CombineUrl returned %08x, expected %08x\n",
534            i, hres, combine_tests[i].hres);
535         ok(size == (combine_tests[i].combined_url ? lstrlenW(combine_tests[i].combined_url)+1
536            : 0xdeadbeef), "[%d] unexpected size=%d\n", i, size);
537         if(combine_tests[i].combined_url)
538             ok(!lstrcmpW(combine_tests[i].combined_url, buf), "[%d] unexpected result\n", i);
539         else
540             ok(buf[0] == 0xfefe, "buf changed\n");
541     }
542
543     size = 0xdeadbeef;
544     memset(buf, 0xfe, sizeof(buf));
545     hres = IInternetProtocolInfo_CombineUrl(info, blank_url1, rel_url1, 0, buf,
546             1, &size, 0);
547     ok(hres == E_OUTOFMEMORY, "CombineUrl failed: %08x\n", hres);
548     ok(size == sizeof(combined_url1)/sizeof(WCHAR), "size=%d\n", size);
549     ok(buf[0] == 0xfefe, "buf changed\n");
550
551     IInternetProtocolInfo_Release(info);
552 }
553
554 static void test_its_protocol(void)
555 {
556     IInternetProtocolInfo *info;
557     IClassFactory *factory;
558     IUnknown *unk;
559     ULONG ref;
560     HRESULT hres;
561
562     static const WCHAR wrong_url1[] =
563         {'i','t','s',':','t','e','s','t','.','c','h','m',':',':','/','b','l','a','n','.','h','t','m','l',0};
564     static const WCHAR wrong_url2[] =
565         {'i','t','s',':','t','e','s','.','c','h','m',':',':','b','/','l','a','n','k','.','h','t','m','l',0};
566     static const WCHAR wrong_url3[] =
567         {'i','t','s',':','t','e','s','t','.','c','h','m','/','b','l','a','n','k','.','h','t','m','l',0};
568     static const WCHAR wrong_url4[] = {'m','k',':','@','M','S','I','T','S','t','o','r',':',
569          't','e','s','t','.','c','h','m',':',':','/','b','l','a','n','k','.','h','t','m','l',0};
570     static const WCHAR wrong_url5[] = {'f','i','l','e',':',
571         't','e','s','.','c','h','m',':',':','/','b','l','a','n','k','.','h','t','m','l',0};
572
573     test_protocol = ITS_PROTOCOL;
574
575     hres = CoGetClassObject(&CLSID_ITSProtocol, CLSCTX_INPROC_SERVER, NULL, &IID_IUnknown, (void**)&unk);
576     ok(hres == S_OK, "CoGetClassObject failed: %08x\n", hres);
577     if(!SUCCEEDED(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, "CoGetClassObject failed: %08x\n", hres);
635     if(!SUCCEEDED(hres))
636         return;
637
638     cache_file = cache_file1;
639     test_protocol_url(cf, blank_url3, TRUE);
640     cache_file = cache_file2;
641     test_protocol_url(cf, blank_url7, TRUE);
642     cache_file = cache_file3;
643     test_protocol_url(cf, blank_url8, FALSE);
644
645     IClassFactory_Release(cf);
646 }
647
648 static BOOL create_chm(void)
649 {
650     HANDLE file;
651     HRSRC src;
652     DWORD size;
653
654     file = CreateFileA("test.chm", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
655             FILE_ATTRIBUTE_NORMAL, NULL);
656     ok(file != INVALID_HANDLE_VALUE, "Could not create test.chm file\n");
657     if(file == INVALID_HANDLE_VALUE)
658         return FALSE;
659
660     src = FindResourceA(NULL, MAKEINTRESOURCEA(60), MAKEINTRESOURCEA(60));
661
662     WriteFile(file, LoadResource(NULL, src), SizeofResource(NULL, src), &size, NULL);
663     CloseHandle(file);
664
665     return TRUE;
666 }
667
668 static void delete_chm(void)
669 {
670     BOOL ret;
671
672     ret = DeleteFileA("test.chm");
673     ok(ret, "DeleteFileA failed: %d\n", GetLastError());
674 }
675
676 START_TEST(protocol)
677 {
678     OleInitialize(NULL);
679
680     if(!create_chm())
681         return;
682
683     test_its_protocol();
684     test_mk_protocol();
685
686     delete_chm();
687     OleUninitialize();
688 }