mstask: Actually run a test and fix the expected behaviour.
[wine] / dlls / mshtml / protocol.c
1 /*
2  * Copyright 2005 Jacek Caban
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 #include "config.h"
20
21 #include <stdarg.h>
22 #include <stdio.h>
23
24 #define COBJMACROS
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winuser.h"
29 #include "ole2.h"
30
31 #include "wine/debug.h"
32
33 #include "mshtml_private.h"
34
35 WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
36
37 /********************************************************************
38  * common ProtocolFactory implementation
39  */
40
41 typedef struct {
42     IInternetProtocolInfo IInternetProtocolInfo_iface;
43     IClassFactory         IClassFactory_iface;
44 } ProtocolFactory;
45
46 static inline ProtocolFactory *impl_from_IInternetProtocolInfo(IInternetProtocolInfo *iface)
47 {
48     return CONTAINING_RECORD(iface, ProtocolFactory, IInternetProtocolInfo_iface);
49 }
50
51 static HRESULT WINAPI InternetProtocolInfo_QueryInterface(IInternetProtocolInfo *iface, REFIID riid, void **ppv)
52 {
53     ProtocolFactory *This = impl_from_IInternetProtocolInfo(iface);
54
55     *ppv = NULL;
56     if(IsEqualGUID(&IID_IUnknown, riid)) {
57         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
58         *ppv = &This->IInternetProtocolInfo_iface;
59     }else if(IsEqualGUID(&IID_IInternetProtocolInfo, riid)) {
60         TRACE("(%p)->(IID_IInternetProtocolInfo %p)\n", This, ppv);
61         *ppv = &This->IInternetProtocolInfo_iface;
62     }else if(IsEqualGUID(&IID_IClassFactory, riid)) {
63         TRACE("(%p)->(IID_IClassFactory %p)\n", This, ppv);
64         *ppv = &This->IClassFactory_iface;
65     }
66
67     if(!*ppv) {
68         WARN("unknown interface %s\n", debugstr_guid(riid));
69         return E_NOINTERFACE;
70     }
71
72     IInternetProtocolInfo_AddRef(iface);
73     return S_OK;
74 }
75
76 static ULONG WINAPI InternetProtocolInfo_AddRef(IInternetProtocolInfo *iface)
77 {
78     TRACE("(%p)\n", iface);
79     return 2;
80 }
81
82 static ULONG WINAPI InternetProtocolInfo_Release(IInternetProtocolInfo *iface)
83 {
84     TRACE("(%p)\n", iface);
85     return 1;
86 }
87
88 static HRESULT WINAPI InternetProtocolInfo_CombineUrl(IInternetProtocolInfo *iface,
89         LPCWSTR pwzBaseUrl, LPCWSTR pwzRelativeUrl, DWORD dwCombineFlags, LPWSTR pwzResult,
90         DWORD cchResult, DWORD* pcchResult, DWORD dwReserved)
91 {
92     TRACE("%p)->(%s %s %08x %p %d %p %d)\n", iface, debugstr_w(pwzBaseUrl),
93             debugstr_w(pwzRelativeUrl), dwCombineFlags, pwzResult, cchResult,
94             pcchResult, dwReserved);
95
96     return INET_E_USE_DEFAULT_PROTOCOLHANDLER;
97 }
98
99 static HRESULT WINAPI InternetProtocolInfo_CompareUrl(IInternetProtocolInfo *iface, LPCWSTR pwzUrl1,
100         LPCWSTR pwzUrl2, DWORD dwCompareFlags)
101 {
102     TRACE("%p)->(%s %s %08x)\n", iface, debugstr_w(pwzUrl1), debugstr_w(pwzUrl2), dwCompareFlags);
103     return E_NOTIMPL;
104 }
105
106 static inline ProtocolFactory *impl_from_IClassFactory(IClassFactory *iface)
107 {
108     return CONTAINING_RECORD(iface, ProtocolFactory, IClassFactory_iface);
109 }
110
111 static HRESULT WINAPI ClassFactory_QueryInterface(IClassFactory *iface, REFIID riid, void **ppv)
112 {
113     ProtocolFactory *This = impl_from_IClassFactory(iface);
114     return IInternetProtocolInfo_QueryInterface(&This->IInternetProtocolInfo_iface, riid, ppv);
115 }
116
117 static ULONG WINAPI ClassFactory_AddRef(IClassFactory *iface)
118 {
119     ProtocolFactory *This = impl_from_IClassFactory(iface);
120     return IInternetProtocolInfo_AddRef(&This->IInternetProtocolInfo_iface);
121 }
122
123 static ULONG WINAPI ClassFactory_Release(IClassFactory *iface)
124 {
125     ProtocolFactory *This = impl_from_IClassFactory(iface);
126     return IInternetProtocolInfo_Release(&This->IInternetProtocolInfo_iface);
127 }
128
129 static HRESULT WINAPI ClassFactory_LockServer(IClassFactory *iface, BOOL dolock)
130 {
131     TRACE("(%p)->(%x)\n", iface, dolock);
132     return S_OK;
133 }
134
135 /********************************************************************
136  * AboutProtocol implementation
137  */
138
139 typedef struct {
140     IInternetProtocol IInternetProtocol_iface;
141
142     LONG ref;
143
144     BYTE *data;
145     ULONG data_len;
146     ULONG cur;
147
148     IUnknown *pUnkOuter;
149 } AboutProtocol;
150
151 static inline AboutProtocol *AboutProtocol_from_IInternetProtocol(IInternetProtocol *iface)
152 {
153     return CONTAINING_RECORD(iface, AboutProtocol, IInternetProtocol_iface);
154 }
155
156 static HRESULT WINAPI AboutProtocol_QueryInterface(IInternetProtocol *iface, REFIID riid, void **ppv)
157 {
158     AboutProtocol *This = AboutProtocol_from_IInternetProtocol(iface);
159
160     *ppv = NULL;
161
162     if(IsEqualGUID(&IID_IUnknown, riid)) {
163         TRACE("(%p)->(IID_IUnknown %p)\n", iface, ppv);
164         if(This->pUnkOuter)
165             return IUnknown_QueryInterface(This->pUnkOuter, riid, ppv);
166         *ppv = &This->IInternetProtocol_iface;
167     }else if(IsEqualGUID(&IID_IInternetProtocolRoot, riid)) {
168         TRACE("(%p)->(IID_IInternetProtocolRoot %p)\n", iface, ppv);
169         *ppv = &This->IInternetProtocol_iface;
170     }else if(IsEqualGUID(&IID_IInternetProtocol, riid)) {
171         TRACE("(%p)->(IID_IInternetProtocol %p)\n", iface, ppv);
172         *ppv = &This->IInternetProtocol_iface;
173     }else if(IsEqualGUID(&IID_IServiceProvider, riid)) {
174         FIXME("IServiceProvider is not implemented\n");
175         return E_NOINTERFACE;
176     }
177
178     if(!*ppv) {
179         TRACE("unknown interface %s\n", debugstr_guid(riid));
180         return E_NOINTERFACE;
181     }
182
183     IInternetProtocol_AddRef(iface);
184     return S_OK;
185 }
186
187 static ULONG WINAPI AboutProtocol_AddRef(IInternetProtocol *iface)
188 {
189     AboutProtocol *This = AboutProtocol_from_IInternetProtocol(iface);
190     ULONG ref = InterlockedIncrement(&This->ref);
191     TRACE("(%p) ref=%d\n", iface, ref);
192     return This->pUnkOuter ? IUnknown_AddRef(This->pUnkOuter) : ref;
193 }
194
195 static ULONG WINAPI AboutProtocol_Release(IInternetProtocol *iface)
196 {
197     AboutProtocol *This = AboutProtocol_from_IInternetProtocol(iface);
198     IUnknown *pUnkOuter = This->pUnkOuter;
199     ULONG ref = InterlockedDecrement(&This->ref);
200
201     TRACE("(%p) ref=%x\n", iface, ref);
202
203     if(!ref) {
204         heap_free(This->data);
205         heap_free(This);
206     }
207
208     return pUnkOuter ? IUnknown_Release(pUnkOuter) : ref;
209 }
210
211 static HRESULT WINAPI AboutProtocol_Start(IInternetProtocol *iface, LPCWSTR szUrl,
212         IInternetProtocolSink* pOIProtSink, IInternetBindInfo* pOIBindInfo,
213         DWORD grfPI, HANDLE_PTR dwReserved)
214 {
215     AboutProtocol *This = AboutProtocol_from_IInternetProtocol(iface);
216     BINDINFO bindinfo;
217     DWORD grfBINDF = 0;
218     LPCWSTR text = NULL;
219     DWORD data_len;
220     BYTE *data;
221
222     static const WCHAR html_begin[] = {0xfeff,'<','H','T','M','L','>',0};
223     static const WCHAR html_end[] = {'<','/','H','T','M','L','>',0};
224     static const WCHAR wszBlank[] = {'b','l','a','n','k',0};
225     static const WCHAR wszAbout[] = {'a','b','o','u','t',':'};
226     static const WCHAR wszTextHtml[] = {'t','e','x','t','/','h','t','m','l',0};
227
228     /* NOTE:
229      * the about protocol seems not to work as I would expect. It creates html document
230      * for a given url, eg. about:some_text -> <HTML>some_text</HTML> except for the case when
231      * some_text = "blank", when document is blank (<HTML></HMTL>). The same happens
232      * when the url does not have "about:" in the beginning.
233      */
234
235     TRACE("(%p)->(%s %p %p %08x %lx)\n", This, debugstr_w(szUrl), pOIProtSink,
236             pOIBindInfo, grfPI, dwReserved);
237
238     memset(&bindinfo, 0, sizeof(bindinfo));
239     bindinfo.cbSize = sizeof(BINDINFO);
240     IInternetBindInfo_GetBindInfo(pOIBindInfo, &grfBINDF, &bindinfo);
241     ReleaseBindInfo(&bindinfo);
242
243     TRACE("bindf %x\n", grfBINDF);
244
245     if(strlenW(szUrl)>=sizeof(wszAbout)/sizeof(WCHAR) && !memcmp(wszAbout, szUrl, sizeof(wszAbout))) {
246         text = szUrl + sizeof(wszAbout)/sizeof(WCHAR);
247         if(!strcmpW(wszBlank, text))
248             text = NULL;
249     }
250
251     data_len = sizeof(html_begin)+sizeof(html_end)-sizeof(WCHAR)
252         + (text ? strlenW(text)*sizeof(WCHAR) : 0);
253     data = heap_alloc(data_len);
254     if(!data)
255         return E_OUTOFMEMORY;
256
257     heap_free(This->data);
258     This->data = data;
259     This->data_len = data_len;
260
261     memcpy(This->data, html_begin, sizeof(html_begin));
262     if(text)
263         strcatW((LPWSTR)This->data, text);
264     strcatW((LPWSTR)This->data, html_end);
265     
266     This->cur = 0;
267
268     IInternetProtocolSink_ReportProgress(pOIProtSink, BINDSTATUS_MIMETYPEAVAILABLE, wszTextHtml);
269
270     IInternetProtocolSink_ReportData(pOIProtSink,
271             BSCF_FIRSTDATANOTIFICATION | BSCF_LASTDATANOTIFICATION | BSCF_DATAFULLYAVAILABLE,
272             This->data_len, This->data_len);
273
274     IInternetProtocolSink_ReportResult(pOIProtSink, S_OK, 0, NULL);
275
276     return S_OK;
277 }
278
279 static HRESULT WINAPI AboutProtocol_Continue(IInternetProtocol *iface, PROTOCOLDATA* pProtocolData)
280 {
281     AboutProtocol *This = AboutProtocol_from_IInternetProtocol(iface);
282     FIXME("(%p)->(%p)\n", This, pProtocolData);
283     return E_NOTIMPL;
284 }
285
286 static HRESULT WINAPI AboutProtocol_Abort(IInternetProtocol *iface, HRESULT hrReason,
287         DWORD dwOptions)
288 {
289     AboutProtocol *This = AboutProtocol_from_IInternetProtocol(iface);
290     FIXME("(%p)->(%08x %08x)\n", This, hrReason, dwOptions);
291     return E_NOTIMPL;
292 }
293
294 static HRESULT WINAPI AboutProtocol_Terminate(IInternetProtocol *iface, DWORD dwOptions)
295 {
296     AboutProtocol *This = AboutProtocol_from_IInternetProtocol(iface);
297     TRACE("(%p)->(%08x)\n", This, dwOptions);
298     return S_OK;
299 }
300
301 static HRESULT WINAPI AboutProtocol_Suspend(IInternetProtocol *iface)
302 {
303     AboutProtocol *This = AboutProtocol_from_IInternetProtocol(iface);
304     FIXME("(%p)\n", This);
305     return E_NOTIMPL;
306 }
307
308 static HRESULT WINAPI AboutProtocol_Resume(IInternetProtocol *iface)
309 {
310     AboutProtocol *This = AboutProtocol_from_IInternetProtocol(iface);
311     FIXME("(%p)\n", This);
312     return E_NOTIMPL;
313 }
314
315 static HRESULT WINAPI AboutProtocol_Read(IInternetProtocol *iface, void* pv, ULONG cb, ULONG* pcbRead)
316 {
317     AboutProtocol *This = AboutProtocol_from_IInternetProtocol(iface);
318
319     TRACE("(%p)->(%p %u %p)\n", This, pv, cb, pcbRead);
320
321     if(!This->data)
322         return E_FAIL;
323
324     *pcbRead = (cb > This->data_len-This->cur ? This->data_len-This->cur : cb);
325
326     if(!*pcbRead)
327         return S_FALSE;
328
329     memcpy(pv, This->data+This->cur, *pcbRead);
330     This->cur += *pcbRead;
331
332     return S_OK;
333 }
334
335 static HRESULT WINAPI AboutProtocol_Seek(IInternetProtocol *iface, LARGE_INTEGER dlibMove,
336         DWORD dwOrigin, ULARGE_INTEGER* plibNewPosition)
337 {
338     AboutProtocol *This = AboutProtocol_from_IInternetProtocol(iface);
339     FIXME("(%p)->(%d %d %p)\n", This, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
340     return E_NOTIMPL;
341 }
342
343 static HRESULT WINAPI AboutProtocol_LockRequest(IInternetProtocol *iface, DWORD dwOptions)
344 {
345     AboutProtocol *This = AboutProtocol_from_IInternetProtocol(iface);
346
347     TRACE("(%p)->(%d)\n", This, dwOptions);
348
349     return S_OK;
350 }
351
352 static HRESULT WINAPI AboutProtocol_UnlockRequest(IInternetProtocol *iface)
353 {
354     AboutProtocol *This = AboutProtocol_from_IInternetProtocol(iface);
355
356     TRACE("(%p)\n", This);
357
358     return S_OK;
359 }
360
361 static const IInternetProtocolVtbl AboutProtocolVtbl = {
362     AboutProtocol_QueryInterface,
363     AboutProtocol_AddRef,
364     AboutProtocol_Release,
365     AboutProtocol_Start,
366     AboutProtocol_Continue,
367     AboutProtocol_Abort,
368     AboutProtocol_Terminate,
369     AboutProtocol_Suspend,
370     AboutProtocol_Resume,
371     AboutProtocol_Read,
372     AboutProtocol_Seek,
373     AboutProtocol_LockRequest,
374     AboutProtocol_UnlockRequest
375 };
376
377 static HRESULT WINAPI AboutProtocolFactory_CreateInstance(IClassFactory *iface, IUnknown *pUnkOuter,
378         REFIID riid, void **ppv)
379 {
380     AboutProtocol *ret;
381     HRESULT hres = S_OK;
382
383     TRACE("(%p)->(%p %s %p)\n", iface, pUnkOuter, debugstr_guid(riid), ppv);
384
385     ret = heap_alloc(sizeof(AboutProtocol));
386     ret->IInternetProtocol_iface.lpVtbl = &AboutProtocolVtbl;
387     ret->ref = 0;
388
389     ret->data = NULL;
390     ret->data_len = 0;
391     ret->cur = 0;
392     ret->pUnkOuter = pUnkOuter;
393
394     if(pUnkOuter) {
395         ret->ref = 1;
396         if(IsEqualGUID(&IID_IUnknown, riid))
397             *ppv = &ret->IInternetProtocol_iface;
398         else
399             hres = E_INVALIDARG;
400     }else {
401         hres = IInternetProtocol_QueryInterface(&ret->IInternetProtocol_iface, riid, ppv);
402     }
403
404     if(FAILED(hres))
405         heap_free(ret);
406
407     return hres;
408 }
409
410 static HRESULT WINAPI AboutProtocolInfo_ParseUrl(IInternetProtocolInfo *iface, LPCWSTR pwzUrl,
411         PARSEACTION ParseAction, DWORD dwParseFlags, LPWSTR pwzResult, DWORD cchResult,
412         DWORD* pcchResult, DWORD dwReserved)
413 {
414     TRACE("%p)->(%s %d %08x %p %d %p %d)\n", iface, debugstr_w(pwzUrl), ParseAction,
415             dwParseFlags, pwzResult, cchResult, pcchResult, dwReserved);
416
417     if(ParseAction == PARSE_SECURITY_URL) {
418         unsigned int len = strlenW(pwzUrl)+1;
419
420         *pcchResult = len;
421         if(len > cchResult)
422             return S_FALSE;
423
424         memcpy(pwzResult, pwzUrl, len*sizeof(WCHAR));
425         return S_OK;
426     }
427
428     if(ParseAction == PARSE_DOMAIN) {
429         if(!pcchResult)
430             return E_POINTER;
431
432         if(pwzUrl)
433             *pcchResult = strlenW(pwzUrl)+1;
434         else
435             *pcchResult = 1;
436         return E_FAIL;
437     }
438
439     return INET_E_DEFAULT_ACTION;
440 }
441
442 static HRESULT WINAPI AboutProtocolInfo_QueryInfo(IInternetProtocolInfo *iface, LPCWSTR pwzUrl,
443         QUERYOPTION QueryOption, DWORD dwQueryFlags, LPVOID pBuffer, DWORD cbBuffer, DWORD* pcbBuf,
444         DWORD dwReserved)
445 {
446     TRACE("%p)->(%s %08x %08x %p %d %p %d)\n", iface, debugstr_w(pwzUrl), QueryOption, dwQueryFlags, pBuffer,
447           cbBuffer, pcbBuf, dwReserved);
448
449     switch(QueryOption) {
450     case QUERY_CAN_NAVIGATE:
451         return INET_E_USE_DEFAULT_PROTOCOLHANDLER;
452
453     case QUERY_USES_NETWORK:
454         if(!pBuffer || cbBuffer < sizeof(DWORD))
455             return E_FAIL;
456
457         *(DWORD*)pBuffer = 0;
458         if(pcbBuf)
459             *pcbBuf = sizeof(DWORD);
460
461         break;
462
463     case QUERY_IS_CACHED:
464         FIXME("Unsupported option QUERY_IS_CACHED\n");
465         return E_NOTIMPL;
466     case QUERY_IS_INSTALLEDENTRY:
467         FIXME("Unsupported option QUERY_IS_INSTALLEDENTRY\n");
468         return E_NOTIMPL;
469     case QUERY_IS_CACHED_OR_MAPPED:
470         FIXME("Unsupported option QUERY_IS_CACHED_OR_MAPPED\n");
471         return E_NOTIMPL;
472     case QUERY_IS_SECURE:
473         FIXME("Unsupported option QUERY_IS_SECURE\n");
474         return E_NOTIMPL;
475     case QUERY_IS_SAFE:
476         FIXME("Unsupported option QUERY_IS_SAFE\n");
477         return E_NOTIMPL;
478     case QUERY_USES_HISTORYFOLDER:
479         FIXME("Unsupported option QUERY_USES_HISTORYFOLDER\n");
480         return E_FAIL;
481     case QUERY_IS_CACHED_AND_USABLE_OFFLINE:
482         FIXME("Unsupported option QUERY_IS_CACHED_AND_USABLE_OFFLINE\n");
483         return E_NOTIMPL;
484     default:
485         return E_FAIL;
486     }
487
488     return S_OK;
489 }
490
491 static const IInternetProtocolInfoVtbl AboutProtocolInfoVtbl = {
492     InternetProtocolInfo_QueryInterface,
493     InternetProtocolInfo_AddRef,
494     InternetProtocolInfo_Release,
495     AboutProtocolInfo_ParseUrl,
496     InternetProtocolInfo_CombineUrl,
497     InternetProtocolInfo_CompareUrl,
498     AboutProtocolInfo_QueryInfo
499 };
500
501 static const IClassFactoryVtbl AboutProtocolFactoryVtbl = {
502     ClassFactory_QueryInterface,
503     ClassFactory_AddRef,
504     ClassFactory_Release,
505     AboutProtocolFactory_CreateInstance,
506     ClassFactory_LockServer
507 };
508
509 static ProtocolFactory AboutProtocolFactory = {
510     { &AboutProtocolInfoVtbl },
511     { &AboutProtocolFactoryVtbl }
512 };
513
514 /********************************************************************
515  * ResProtocol implementation
516  */
517
518 typedef struct {
519     IInternetProtocol IInternetProtocol_iface;
520     LONG ref;
521
522     BYTE *data;
523     ULONG data_len;
524     ULONG cur;
525
526     IUnknown *pUnkOuter;
527 } ResProtocol;
528
529 static inline ResProtocol *ResProtocol_from_IInternetProtocol(IInternetProtocol *iface)
530 {
531     return CONTAINING_RECORD(iface, ResProtocol, IInternetProtocol_iface);
532 }
533
534 static HRESULT WINAPI ResProtocol_QueryInterface(IInternetProtocol *iface, REFIID riid, void **ppv)
535 {
536     ResProtocol *This = ResProtocol_from_IInternetProtocol(iface);
537
538     *ppv = NULL;
539
540     if(IsEqualGUID(&IID_IUnknown, riid)) {
541         TRACE("(%p)->(IID_IUnknown %p)\n", iface, ppv);
542         if(This->pUnkOuter)
543             return IUnknown_QueryInterface(This->pUnkOuter, &IID_IUnknown, ppv);
544         *ppv = &This->IInternetProtocol_iface;
545     }else if(IsEqualGUID(&IID_IInternetProtocolRoot, riid)) {
546         TRACE("(%p)->(IID_IInternetProtocolRoot %p)\n", iface, ppv);
547         *ppv = &This->IInternetProtocol_iface;
548     }else if(IsEqualGUID(&IID_IInternetProtocol, riid)) {
549         TRACE("(%p)->(IID_IInternetProtocol %p)\n", iface, ppv);
550         *ppv = &This->IInternetProtocol_iface;
551     }else if(IsEqualGUID(&IID_IServiceProvider, riid)) {
552         FIXME("IServiceProvider is not implemented\n");
553         return E_NOINTERFACE;
554     }
555
556     if(!*ppv) {
557         TRACE("unknown interface %s\n", debugstr_guid(riid));
558         return E_NOINTERFACE;
559     }
560
561     IInternetProtocol_AddRef(iface);
562     return S_OK;
563 }
564
565 static ULONG WINAPI ResProtocol_AddRef(IInternetProtocol *iface)
566 {
567     ResProtocol *This = ResProtocol_from_IInternetProtocol(iface);
568     ULONG ref = InterlockedIncrement(&This->ref);
569     TRACE("(%p) ref=%d\n", iface, ref);
570     return This->pUnkOuter ? IUnknown_AddRef(This->pUnkOuter) : ref;
571 }
572
573 static ULONG WINAPI ResProtocol_Release(IInternetProtocol *iface)
574 {
575     ResProtocol *This = (ResProtocol*)iface;
576     IUnknown *pUnkOuter = This->pUnkOuter;
577     ULONG ref = InterlockedDecrement(&This->ref);
578
579     TRACE("(%p) ref=%x\n", iface, ref);
580
581     if(!ref) {
582         heap_free(This->data);
583         heap_free(This);
584     }
585
586     return pUnkOuter ? IUnknown_Release(pUnkOuter) : ref;
587 }
588
589 static HRESULT WINAPI ResProtocol_Start(IInternetProtocol *iface, LPCWSTR szUrl,
590         IInternetProtocolSink* pOIProtSink, IInternetBindInfo* pOIBindInfo,
591         DWORD grfPI, HANDLE_PTR dwReserved)
592 {
593     ResProtocol *This = ResProtocol_from_IInternetProtocol(iface);
594     DWORD grfBINDF = 0, len;
595     BINDINFO bindinfo;
596     LPWSTR url_dll, url_file, url, mime, res_type = (LPWSTR)RT_HTML;
597     HMODULE hdll;
598     HRSRC src;
599     HRESULT hres;
600
601     static const WCHAR wszRes[] = {'r','e','s',':','/','/'};
602
603     TRACE("(%p)->(%s %p %p %08x %lx)\n", This, debugstr_w(szUrl), pOIProtSink,
604             pOIBindInfo, grfPI, dwReserved);
605
606     memset(&bindinfo, 0, sizeof(bindinfo));
607     bindinfo.cbSize = sizeof(BINDINFO);
608     IInternetBindInfo_GetBindInfo(pOIBindInfo, &grfBINDF, &bindinfo);
609     ReleaseBindInfo(&bindinfo);
610
611     len = strlenW(szUrl)+16;
612     url = heap_alloc(len*sizeof(WCHAR));
613     hres = CoInternetParseUrl(szUrl, PARSE_ENCODE, 0, url, len, &len, 0);
614     if(FAILED(hres)) {
615         WARN("CoInternetParseUrl failed: %08x\n", hres);
616         heap_free(url);
617         IInternetProtocolSink_ReportResult(pOIProtSink, hres, 0, NULL);
618         return hres;
619     }
620
621     if(len < sizeof(wszRes)/sizeof(wszRes[0]) || memcmp(url, wszRes, sizeof(wszRes))) {
622         WARN("Wrong protocol of url: %s\n", debugstr_w(url));
623         IInternetProtocolSink_ReportResult(pOIProtSink, E_INVALIDARG, 0, NULL);
624         heap_free(url);
625         return E_INVALIDARG;
626     }
627
628     url_dll = url + sizeof(wszRes)/sizeof(wszRes[0]);
629     if(!(url_file = strrchrW(url_dll, '/'))) {
630         WARN("wrong url: %s\n", debugstr_w(url));
631         IInternetProtocolSink_ReportResult(pOIProtSink, MK_E_SYNTAX, 0, NULL);
632         heap_free(url);
633         return MK_E_SYNTAX;
634     }
635
636     *url_file++ = 0;
637     hdll = LoadLibraryExW(url_dll, NULL, LOAD_LIBRARY_AS_DATAFILE);
638     if(!hdll) {
639         if (!(res_type = strrchrW(url_dll, '/'))) {
640             WARN("Could not open dll: %s\n", debugstr_w(url_dll));
641             IInternetProtocolSink_ReportResult(pOIProtSink, HRESULT_FROM_WIN32(GetLastError()), 0, NULL);
642             heap_free(url);
643             return HRESULT_FROM_WIN32(GetLastError());
644         }
645         *res_type++ = 0;
646
647         hdll = LoadLibraryExW(url_dll, NULL, LOAD_LIBRARY_AS_DATAFILE);
648         if(!hdll) {
649             WARN("Could not open dll: %s\n", debugstr_w(url_dll));
650             IInternetProtocolSink_ReportResult(pOIProtSink, HRESULT_FROM_WIN32(GetLastError()), 0, NULL);
651             heap_free(url);
652             return HRESULT_FROM_WIN32(GetLastError());
653         }
654     }
655
656     TRACE("trying to find resource type %s, name %s\n", debugstr_w(res_type), debugstr_w(url_file));
657
658     src = FindResourceW(hdll, url_file, res_type);
659     if(!src) {
660         LPWSTR endpoint = NULL;
661         DWORD file_id = strtolW(url_file, &endpoint, 10);
662         if(endpoint == url_file+strlenW(url_file))
663             src = FindResourceW(hdll, MAKEINTRESOURCEW(file_id), MAKEINTRESOURCEW(RT_HTML));
664
665         if(!src) {
666             WARN("Could not find resource\n");
667             IInternetProtocolSink_ReportResult(pOIProtSink,
668                     HRESULT_FROM_WIN32(GetLastError()), 0, NULL);
669             heap_free(url);
670             return HRESULT_FROM_WIN32(GetLastError());
671         }
672     }
673
674     if(This->data) {
675         WARN("data already loaded\n");
676         heap_free(This->data);
677     }
678
679     This->data_len = SizeofResource(hdll, src);
680     This->data = heap_alloc(This->data_len);
681     memcpy(This->data, LoadResource(hdll, src), This->data_len);
682     This->cur = 0;
683
684     FreeLibrary(hdll);
685
686     hres = FindMimeFromData(NULL, url_file, This->data, This->data_len, NULL, 0, &mime, 0);
687     heap_free(url);
688     if(SUCCEEDED(hres)) {
689         IInternetProtocolSink_ReportProgress(pOIProtSink, BINDSTATUS_MIMETYPEAVAILABLE, mime);
690         CoTaskMemFree(mime);
691     }
692
693     IInternetProtocolSink_ReportData(pOIProtSink,
694             BSCF_FIRSTDATANOTIFICATION | BSCF_LASTDATANOTIFICATION | BSCF_DATAFULLYAVAILABLE,
695             This->data_len, This->data_len);
696
697     IInternetProtocolSink_ReportResult(pOIProtSink, S_OK, 0, NULL);
698     
699     return S_OK;
700 }
701
702 static HRESULT WINAPI ResProtocol_Continue(IInternetProtocol *iface, PROTOCOLDATA* pProtocolData)
703 {
704     ResProtocol *This = ResProtocol_from_IInternetProtocol(iface);
705     FIXME("(%p)->(%p)\n", This, pProtocolData);
706     return E_NOTIMPL;
707 }
708
709 static HRESULT WINAPI ResProtocol_Abort(IInternetProtocol *iface, HRESULT hrReason,
710         DWORD dwOptions)
711 {
712     ResProtocol *This = ResProtocol_from_IInternetProtocol(iface);
713     FIXME("(%p)->(%08x %08x)\n", This, hrReason, dwOptions);
714     return E_NOTIMPL;
715 }
716
717 static HRESULT WINAPI ResProtocol_Terminate(IInternetProtocol *iface, DWORD dwOptions)
718 {
719     ResProtocol *This = ResProtocol_from_IInternetProtocol(iface);
720
721     TRACE("(%p)->(%08x)\n", This, dwOptions);
722
723     /* test show that we don't have to do anything here */
724     return S_OK;
725 }
726
727 static HRESULT WINAPI ResProtocol_Suspend(IInternetProtocol *iface)
728 {
729     ResProtocol *This = ResProtocol_from_IInternetProtocol(iface);
730     FIXME("(%p)\n", This);
731     return E_NOTIMPL;
732 }
733
734 static HRESULT WINAPI ResProtocol_Resume(IInternetProtocol *iface)
735 {
736     ResProtocol *This = ResProtocol_from_IInternetProtocol(iface);
737     FIXME("(%p)\n", This);
738     return E_NOTIMPL;
739 }
740
741 static HRESULT WINAPI ResProtocol_Read(IInternetProtocol *iface, void* pv, ULONG cb, ULONG* pcbRead)
742 {
743     ResProtocol *This = ResProtocol_from_IInternetProtocol(iface);
744
745     TRACE("(%p)->(%p %u %p)\n", This, pv, cb, pcbRead);
746
747     if(!This->data)
748         return E_FAIL;
749
750     *pcbRead = (cb > This->data_len-This->cur ? This->data_len-This->cur : cb);
751
752     if(!*pcbRead)
753         return S_FALSE;
754
755     memcpy(pv, This->data+This->cur, *pcbRead);
756     This->cur += *pcbRead;
757
758     return S_OK;
759 }
760
761 static HRESULT WINAPI ResProtocol_Seek(IInternetProtocol *iface, LARGE_INTEGER dlibMove,
762         DWORD dwOrigin, ULARGE_INTEGER* plibNewPosition)
763 {
764     ResProtocol *This = ResProtocol_from_IInternetProtocol(iface);
765     FIXME("(%p)->(%d %d %p)\n", This, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
766     return E_NOTIMPL;
767 }
768
769 static HRESULT WINAPI ResProtocol_LockRequest(IInternetProtocol *iface, DWORD dwOptions)
770 {
771     ResProtocol *This = ResProtocol_from_IInternetProtocol(iface);
772
773     TRACE("(%p)->(%d)\n", This, dwOptions);
774
775     /* test show that we don't have to do anything here */
776     return S_OK;
777 }
778
779 static HRESULT WINAPI ResProtocol_UnlockRequest(IInternetProtocol *iface)
780 {
781     ResProtocol *This = ResProtocol_from_IInternetProtocol(iface);
782
783     TRACE("(%p)\n", This);
784
785     /* test show that we don't have to do anything here */
786     return S_OK;
787 }
788
789 static const IInternetProtocolVtbl ResProtocolVtbl = {
790     ResProtocol_QueryInterface,
791     ResProtocol_AddRef,
792     ResProtocol_Release,
793     ResProtocol_Start,
794     ResProtocol_Continue,
795     ResProtocol_Abort,
796     ResProtocol_Terminate,
797     ResProtocol_Suspend,
798     ResProtocol_Resume,
799     ResProtocol_Read,
800     ResProtocol_Seek,
801     ResProtocol_LockRequest,
802     ResProtocol_UnlockRequest
803 };
804
805 static HRESULT WINAPI ResProtocolFactory_CreateInstance(IClassFactory *iface, IUnknown *pUnkOuter,
806         REFIID riid, void **ppv)
807 {
808     ResProtocol *ret;
809     HRESULT hres = S_OK;
810
811     TRACE("(%p)->(%p %s %p)\n", iface, pUnkOuter, debugstr_guid(riid), ppv);
812
813     ret = heap_alloc(sizeof(ResProtocol));
814     ret->IInternetProtocol_iface.lpVtbl = &ResProtocolVtbl;
815     ret->ref = 0;
816     ret->data = NULL;
817     ret->data_len = 0;
818     ret->cur = 0;
819     ret->pUnkOuter = pUnkOuter;
820
821     if(pUnkOuter) {
822         ret->ref = 1;
823         if(IsEqualGUID(&IID_IUnknown, riid))
824             *ppv = &ret->IInternetProtocol_iface;
825         else
826             hres = E_FAIL;
827     }else {
828         hres = IInternetProtocol_QueryInterface(&ret->IInternetProtocol_iface, riid, ppv);
829     }
830
831     if(FAILED(hres))
832         heap_free(ret);
833
834     return hres;
835 }
836
837 static HRESULT WINAPI ResProtocolInfo_ParseUrl(IInternetProtocolInfo *iface, LPCWSTR pwzUrl,
838         PARSEACTION ParseAction, DWORD dwParseFlags, LPWSTR pwzResult, DWORD cchResult,
839         DWORD* pcchResult, DWORD dwReserved)
840 {
841     TRACE("%p)->(%s %d %x %p %d %p %d)\n", iface, debugstr_w(pwzUrl), ParseAction,
842             dwParseFlags, pwzResult, cchResult, pcchResult, dwReserved);
843
844     if(ParseAction == PARSE_SECURITY_URL) {
845         WCHAR file_part[MAX_PATH], full_path[MAX_PATH];
846         WCHAR *ptr;
847         DWORD size, len;
848
849         static const WCHAR wszFile[] = {'f','i','l','e',':','/','/'};
850         static const WCHAR wszRes[] = {'r','e','s',':','/','/'};
851
852         if(strlenW(pwzUrl) <= sizeof(wszRes)/sizeof(WCHAR) || memcmp(pwzUrl, wszRes, sizeof(wszRes)))
853             return E_INVALIDARG;
854
855         ptr = strchrW(pwzUrl + sizeof(wszRes)/sizeof(WCHAR), '/');
856         if(!ptr)
857             return E_INVALIDARG;
858
859         len = ptr - (pwzUrl + sizeof(wszRes)/sizeof(WCHAR));
860         if(len >= sizeof(file_part)/sizeof(WCHAR)) {
861             FIXME("Too long URL\n");
862             return MK_E_SYNTAX;
863         }
864
865         memcpy(file_part, pwzUrl + sizeof(wszRes)/sizeof(WCHAR), len*sizeof(WCHAR));
866         file_part[len] = 0;
867
868         len = SearchPathW(NULL, file_part, NULL, sizeof(full_path)/sizeof(WCHAR), full_path, NULL);
869         if(!len) {
870             HMODULE module;
871
872             /* SearchPath does not work well with winelib files (like our test executable),
873              * so we also try to load the library here */
874             module = LoadLibraryExW(file_part, NULL, LOAD_LIBRARY_AS_DATAFILE);
875             if(!module) {
876                 WARN("Could not find file %s\n", debugstr_w(file_part));
877                 return MK_E_SYNTAX;
878             }
879
880             len = GetModuleFileNameW(module, full_path, sizeof(full_path)/sizeof(WCHAR));
881             FreeLibrary(module);
882             if(!len)
883                 return E_FAIL;
884         }
885
886         size = sizeof(wszFile)/sizeof(WCHAR) + len + 1;
887         if(pcchResult)
888             *pcchResult = size;
889         if(size > cchResult)
890             return S_FALSE;
891
892         memcpy(pwzResult, wszFile, sizeof(wszFile));
893         memcpy(pwzResult + sizeof(wszFile)/sizeof(WCHAR), full_path, (len+1)*sizeof(WCHAR));
894         return S_OK;
895     }
896
897     if(ParseAction == PARSE_DOMAIN) {
898         if(!pcchResult)
899             return E_POINTER;
900
901         if(pwzUrl)
902             *pcchResult = strlenW(pwzUrl)+1;
903         else
904             *pcchResult = 1;
905         return E_FAIL;
906     }
907
908     return INET_E_DEFAULT_ACTION;
909 }
910
911 static HRESULT WINAPI ResProtocolInfo_QueryInfo(IInternetProtocolInfo *iface, LPCWSTR pwzUrl,
912         QUERYOPTION QueryOption, DWORD dwQueryFlags, LPVOID pBuffer, DWORD cbBuffer, DWORD* pcbBuf,
913         DWORD dwReserved)
914 {
915     TRACE("%p)->(%s %08x %08x %p %d %p %d)\n", iface, debugstr_w(pwzUrl), QueryOption, dwQueryFlags, pBuffer,
916           cbBuffer, pcbBuf, dwReserved);
917
918     switch(QueryOption) {
919     case QUERY_USES_NETWORK:
920         if(!pBuffer || cbBuffer < sizeof(DWORD))
921             return E_FAIL;
922
923         *(DWORD*)pBuffer = 0;
924         if(pcbBuf)
925             *pcbBuf = sizeof(DWORD);
926         break;
927
928     case QUERY_IS_SECURE:
929         FIXME("QUERY_IS_SECURE not supported\n");
930         return E_NOTIMPL;
931     case QUERY_IS_SAFE:
932         FIXME("QUERY_IS_SAFE not supported\n");
933         return E_NOTIMPL;
934     default:
935         return INET_E_USE_DEFAULT_PROTOCOLHANDLER;
936     }
937
938     return S_OK;
939 }
940
941 static const IInternetProtocolInfoVtbl ResProtocolInfoVtbl = {
942     InternetProtocolInfo_QueryInterface,
943     InternetProtocolInfo_AddRef,
944     InternetProtocolInfo_Release,
945     ResProtocolInfo_ParseUrl,
946     InternetProtocolInfo_CombineUrl,
947     InternetProtocolInfo_CompareUrl,
948     ResProtocolInfo_QueryInfo
949 };
950
951 static const IClassFactoryVtbl ResProtocolFactoryVtbl = {
952     ClassFactory_QueryInterface,
953     ClassFactory_AddRef,
954     ClassFactory_Release,
955     ResProtocolFactory_CreateInstance,
956     ClassFactory_LockServer
957 };
958
959 static ProtocolFactory ResProtocolFactory = {
960     { &ResProtocolInfoVtbl },
961     { &ResProtocolFactoryVtbl }
962 };
963
964 /********************************************************************
965  * JSProtocol implementation
966  */
967
968 static HRESULT WINAPI JSProtocolFactory_CreateInstance(IClassFactory *iface, IUnknown *pUnkOuter,
969         REFIID riid, void **ppv)
970 {
971     FIXME("(%p)->(%p %s %p)\n", iface, pUnkOuter, debugstr_guid(riid), ppv);
972     return E_NOTIMPL;
973 }
974
975 static HRESULT WINAPI JSProtocolInfo_ParseUrl(IInternetProtocolInfo *iface, LPCWSTR pwzUrl,
976         PARSEACTION ParseAction, DWORD dwParseFlags, LPWSTR pwzResult, DWORD cchResult,
977         DWORD* pcchResult, DWORD dwReserved)
978 {
979     TRACE("%p)->(%s %d %x %p %d %p %d)\n", iface, debugstr_w(pwzUrl), ParseAction,
980           dwParseFlags, pwzResult, cchResult, pcchResult, dwReserved);
981
982     switch(ParseAction) {
983     case PARSE_SECURITY_URL:
984         FIXME("PARSE_SECURITY_URL\n");
985         return E_NOTIMPL;
986     case PARSE_DOMAIN:
987         FIXME("PARSE_DOMAIN\n");
988         return E_NOTIMPL;
989     default:
990         return INET_E_DEFAULT_ACTION;
991     }
992
993     return S_OK;
994 }
995
996 static HRESULT WINAPI JSProtocolInfo_QueryInfo(IInternetProtocolInfo *iface, LPCWSTR pwzUrl,
997         QUERYOPTION QueryOption, DWORD dwQueryFlags, LPVOID pBuffer, DWORD cbBuffer, DWORD* pcbBuf,
998         DWORD dwReserved)
999 {
1000     TRACE("%p)->(%s %08x %08x %p %d %p %d)\n", iface, debugstr_w(pwzUrl), QueryOption, dwQueryFlags, pBuffer,
1001           cbBuffer, pcbBuf, dwReserved);
1002
1003     switch(QueryOption) {
1004     case QUERY_USES_NETWORK:
1005         if(!pBuffer || cbBuffer < sizeof(DWORD))
1006             return E_FAIL;
1007
1008         *(DWORD*)pBuffer = 0;
1009         if(pcbBuf)
1010             *pcbBuf = sizeof(DWORD);
1011         break;
1012
1013     case QUERY_IS_SECURE:
1014         FIXME("QUERY_IS_SECURE not supported\n");
1015         return E_NOTIMPL;
1016
1017     default:
1018         return INET_E_USE_DEFAULT_PROTOCOLHANDLER;
1019     }
1020
1021     return S_OK;
1022 }
1023
1024 static const IInternetProtocolInfoVtbl JSProtocolInfoVtbl = {
1025     InternetProtocolInfo_QueryInterface,
1026     InternetProtocolInfo_AddRef,
1027     InternetProtocolInfo_Release,
1028     JSProtocolInfo_ParseUrl,
1029     InternetProtocolInfo_CombineUrl,
1030     InternetProtocolInfo_CompareUrl,
1031     JSProtocolInfo_QueryInfo
1032 };
1033
1034 static const IClassFactoryVtbl JSProtocolFactoryVtbl = {
1035     ClassFactory_QueryInterface,
1036     ClassFactory_AddRef,
1037     ClassFactory_Release,
1038     JSProtocolFactory_CreateInstance,
1039     ClassFactory_LockServer
1040 };
1041
1042 static ProtocolFactory JSProtocolFactory = {
1043     { &JSProtocolInfoVtbl },
1044     { &JSProtocolFactoryVtbl }
1045 };
1046
1047 HRESULT ProtocolFactory_Create(REFCLSID rclsid, REFIID riid, void **ppv)
1048 {
1049     ProtocolFactory *cf = NULL;
1050
1051     if(IsEqualGUID(&CLSID_AboutProtocol, rclsid))
1052         cf = &AboutProtocolFactory;
1053     else if(IsEqualGUID(&CLSID_ResProtocol, rclsid))
1054         cf = &ResProtocolFactory;
1055     else if(IsEqualGUID(&CLSID_JSProtocol, rclsid))
1056         cf = &JSProtocolFactory;
1057
1058     if(!cf) {
1059         FIXME("not implemented protocol %s\n", debugstr_guid(rclsid));
1060         return CLASS_E_CLASSNOTAVAILABLE;
1061     }
1062  
1063     return IInternetProtocolInfo_QueryInterface(&cf->IInternetProtocolInfo_iface, riid, ppv);
1064 }