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