xref: /reactos/dll/win32/inetcomm/protocol.c (revision f986527d)
1 /*
2  * Copyright 2017 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 #define NONAMELESSUNION
21 
22 #include <assert.h>
23 
24 #include "mimeole.h"
25 #include "inetcomm_private.h"
26 
27 #include "wine/debug.h"
28 #include "wine/heap.h"
29 
30 WINE_DEFAULT_DEBUG_CHANNEL(inetcomm);
31 
32 typedef struct {
33     IUnknown IUnknown_inner;
34     IInternetProtocol IInternetProtocol_iface;
35     IInternetProtocolInfo IInternetProtocolInfo_iface;
36 
37     LONG ref;
38     IUnknown *outer_unk;
39 
40     WCHAR *location;
41     IStream *stream;
42     IInternetProtocolSink *sink;
43 } MimeHtmlProtocol;
44 
45 typedef struct {
46     const WCHAR *mhtml;
47     size_t mhtml_len;
48     const WCHAR *location;
49 } mhtml_url_t;
50 
51 typedef struct {
52     IBindStatusCallback IBindStatusCallback_iface;
53 
54     LONG ref;
55 
56     MimeHtmlProtocol *protocol;
57     HRESULT status;
58     IStream *stream;
59     WCHAR url[1];
60 } MimeHtmlBinding;
61 
62 static const WCHAR mhtml_prefixW[] = {'m','h','t','m','l',':'};
63 static const WCHAR mhtml_separatorW[] = {'!','x','-','u','s','c',':'};
64 
65 static inline LPWSTR heap_strdupW(LPCWSTR str)
66 {
67     LPWSTR ret = NULL;
68 
69     if(str) {
70         DWORD size;
71 
72         size = (lstrlenW(str)+1)*sizeof(WCHAR);
73         ret = heap_alloc(size);
74         if(ret)
75             memcpy(ret, str, size);
76     }
77 
78     return ret;
79 }
80 
81 static HRESULT parse_mhtml_url(const WCHAR *url, mhtml_url_t *r)
82 {
83     const WCHAR *p;
84 
85     if(_wcsnicmp(url, mhtml_prefixW, ARRAY_SIZE(mhtml_prefixW)))
86         return E_FAIL;
87 
88     r->mhtml = url + ARRAY_SIZE(mhtml_prefixW);
89     p = wcschr(r->mhtml, '!');
90     if(p) {
91         r->mhtml_len = p - r->mhtml;
92         /* FIXME: We handle '!' and '!x-usc:' in URLs as the same thing. Those should not be the same. */
93         if(!wcsncmp(p, mhtml_separatorW, ARRAY_SIZE(mhtml_separatorW)))
94             p += ARRAY_SIZE(mhtml_separatorW);
95         else
96             p++;
97     }else {
98         r->mhtml_len = lstrlenW(r->mhtml);
99     }
100 
101     r->location = p;
102     return S_OK;
103 }
104 
105 static HRESULT report_result(MimeHtmlProtocol *protocol, HRESULT result)
106 {
107     if(protocol->sink) {
108         IInternetProtocolSink_ReportResult(protocol->sink, result, ERROR_SUCCESS, NULL);
109         IInternetProtocolSink_Release(protocol->sink);
110         protocol->sink = NULL;
111     }
112 
113     return result;
114 }
115 
116 static HRESULT on_mime_message_available(MimeHtmlProtocol *protocol, IMimeMessage *mime_message)
117 {
118     FINDBODY find = {NULL};
119     IMimeBody *mime_body;
120     PROPVARIANT value;
121     HBODY body;
122     HRESULT hres;
123 
124     hres = IMimeMessage_FindFirst(mime_message, &find, &body);
125     if(FAILED(hres))
126         return report_result(protocol, hres);
127 
128     if(protocol->location) {
129         BOOL found = FALSE;
130         do {
131             hres = IMimeMessage_FindNext(mime_message, &find, &body);
132             if(FAILED(hres)) {
133                 WARN("location %s not found\n", debugstr_w(protocol->location));
134                 return report_result(protocol, hres);
135             }
136 
137             value.vt = VT_LPWSTR;
138             hres = IMimeMessage_GetBodyProp(mime_message, body, "content-location", 0, &value);
139             if(hres == MIME_E_NOT_FOUND)
140                 continue;
141             if(FAILED(hres))
142                 return report_result(protocol, hres);
143 
144             found = !lstrcmpW(protocol->location, value.u.pwszVal);
145             PropVariantClear(&value);
146         }while(!found);
147     }else {
148         hres = IMimeMessage_FindNext(mime_message, &find, &body);
149         if(FAILED(hres)) {
150             WARN("location %s not found\n", debugstr_w(protocol->location));
151             return report_result(protocol, hres);
152         }
153     }
154 
155     hres = IMimeMessage_BindToObject(mime_message, body, &IID_IMimeBody, (void**)&mime_body);
156     if(FAILED(hres))
157         return report_result(protocol, hres);
158 
159     value.vt = VT_LPWSTR;
160     hres = IMimeBody_GetProp(mime_body, "content-type", 0, &value);
161     if(SUCCEEDED(hres)) {
162         hres = IInternetProtocolSink_ReportProgress(protocol->sink, BINDSTATUS_MIMETYPEAVAILABLE, value.u.pwszVal);
163         PropVariantClear(&value);
164     }
165 
166     /* FIXME: Create and report cache file. */
167 
168     hres = IMimeBody_GetData(mime_body, IET_DECODED, &protocol->stream);
169     if(FAILED(hres))
170         return report_result(protocol, hres);
171 
172     IInternetProtocolSink_ReportData(protocol->sink, BSCF_FIRSTDATANOTIFICATION
173                                      | BSCF_INTERMEDIATEDATANOTIFICATION
174                                      | BSCF_LASTDATANOTIFICATION
175                                      | BSCF_DATAFULLYAVAILABLE
176                                      | BSCF_AVAILABLEDATASIZEUNKNOWN, 0, 0);
177 
178     return report_result(protocol, S_OK);
179 }
180 
181 static HRESULT load_mime_message(IStream *stream, IMimeMessage **ret)
182 {
183     IMimeMessage *mime_message;
184     HRESULT hres;
185 
186     hres = MimeMessage_create(NULL, (void**)&mime_message);
187     if(FAILED(hres))
188         return hres;
189 
190     IMimeMessage_InitNew(mime_message);
191 
192     hres = IMimeMessage_Load(mime_message, stream);
193     if(FAILED(hres)) {
194         IMimeMessage_Release(mime_message);
195         return hres;
196     }
197 
198     *ret = mime_message;
199     return S_OK;
200 }
201 
202 static inline MimeHtmlBinding *impl_from_IBindStatusCallback(IBindStatusCallback *iface)
203 {
204     return CONTAINING_RECORD(iface, MimeHtmlBinding, IBindStatusCallback_iface);
205 }
206 
207 static HRESULT WINAPI BindStatusCallback_QueryInterface(IBindStatusCallback *iface,
208         REFIID riid, void **ppv)
209 {
210     MimeHtmlBinding *This = impl_from_IBindStatusCallback(iface);
211 
212     if(IsEqualGUID(&IID_IUnknown, riid)) {
213         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
214         *ppv = &This->IBindStatusCallback_iface;
215     }else if(IsEqualGUID(&IID_IBindStatusCallback, riid)) {
216         TRACE("(%p)->(IID_IBindStatusCallback %p)\n", This, ppv);
217         *ppv = &This->IBindStatusCallback_iface;
218     }else {
219         TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
220         *ppv = NULL;
221         return E_NOINTERFACE;
222     }
223 
224     IUnknown_AddRef((IUnknown*)*ppv);
225     return S_OK;
226 }
227 
228 static ULONG WINAPI BindStatusCallback_AddRef(IBindStatusCallback *iface)
229 {
230     MimeHtmlBinding *This = impl_from_IBindStatusCallback(iface);
231     LONG ref = InterlockedIncrement(&This->ref);
232 
233     TRACE("(%p) ref=%d\n", This, ref);
234 
235     return ref;
236 }
237 
238 static ULONG WINAPI BindStatusCallback_Release(IBindStatusCallback *iface)
239 {
240     MimeHtmlBinding *This = impl_from_IBindStatusCallback(iface);
241     LONG ref = InterlockedDecrement(&This->ref);
242 
243     TRACE("(%p) ref=%d\n", This, ref);
244 
245     if(!ref) {
246         if(This->protocol)
247             IInternetProtocol_Release(&This->protocol->IInternetProtocol_iface);
248         if(This->stream)
249             IStream_Release(This->stream);
250         heap_free(This);
251     }
252 
253     return ref;
254 }
255 
256 static HRESULT WINAPI BindStatusCallback_OnStartBinding(IBindStatusCallback *iface,
257         DWORD dwReserved, IBinding *pib)
258 {
259     MimeHtmlBinding *This = impl_from_IBindStatusCallback(iface);
260 
261     TRACE("(%p)->(%x %p)\n", This, dwReserved, pib);
262 
263     assert(!This->stream);
264     return CreateStreamOnHGlobal(NULL, TRUE, &This->stream);
265 }
266 
267 static HRESULT WINAPI BindStatusCallback_GetPriority(IBindStatusCallback *iface, LONG *pnPriority)
268 {
269     return E_NOTIMPL;
270 }
271 
272 static HRESULT WINAPI BindStatusCallback_OnLowResource(IBindStatusCallback *iface, DWORD dwReserved)
273 {
274     return E_NOTIMPL;
275 }
276 
277 static HRESULT WINAPI BindStatusCallback_OnProgress(IBindStatusCallback *iface, ULONG ulProgress,
278         ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
279 {
280     MimeHtmlBinding *This = impl_from_IBindStatusCallback(iface);
281     TRACE("(%p)->(%u/%u %u %s)\n", This, ulProgress, ulProgressMax, ulStatusCode, debugstr_w(szStatusText));
282     return S_OK;
283 }
284 
285 static HRESULT WINAPI BindStatusCallback_OnStopBinding(IBindStatusCallback *iface, HRESULT hresult, LPCWSTR szError)
286 {
287     MimeHtmlBinding *This = impl_from_IBindStatusCallback(iface);
288     IMimeMessage *mime_message = NULL;
289 
290     TRACE("(%p)->(%x %s)\n", This, hresult, debugstr_w(szError));
291 
292     if(SUCCEEDED(hresult)) {
293         hresult = load_mime_message(This->stream, &mime_message);
294         IStream_Release(This->stream);
295         This->stream = NULL;
296     }
297 
298     This->status = hresult;
299 
300     if(mime_message)
301         on_mime_message_available(This->protocol, mime_message);
302     else
303         report_result(This->protocol, hresult);
304 
305     if(mime_message)
306         IMimeMessage_Release(mime_message);
307     IInternetProtocol_Release(&This->protocol->IInternetProtocol_iface);
308     This->protocol = NULL;
309     return S_OK;
310 }
311 
312 static HRESULT WINAPI BindStatusCallback_GetBindInfo(IBindStatusCallback *iface,
313         DWORD* grfBINDF, BINDINFO* pbindinfo)
314 {
315     MimeHtmlBinding *This = impl_from_IBindStatusCallback(iface);
316 
317     TRACE("(%p)\n", This);
318 
319     *grfBINDF = BINDF_ASYNCHRONOUS;
320     return S_OK;
321 }
322 
323 static HRESULT WINAPI BindStatusCallback_OnDataAvailable(IBindStatusCallback *iface, DWORD grfBSCF,
324         DWORD dwSize, FORMATETC* pformatetc, STGMEDIUM* pstgmed)
325 {
326     MimeHtmlBinding *This = impl_from_IBindStatusCallback(iface);
327     BYTE buf[4*1024];
328     DWORD read;
329     HRESULT hres;
330 
331     TRACE("(%p)\n", This);
332 
333     assert(pstgmed->tymed == TYMED_ISTREAM);
334 
335     while(1) {
336         hres = IStream_Read(pstgmed->u.pstm, buf, sizeof(buf), &read);
337         if(FAILED(hres))
338             return hres;
339         if(!read)
340             break;
341         hres = IStream_Write(This->stream, buf, read, NULL);
342         if(FAILED(hres))
343             return hres;
344     }
345     return S_OK;
346 }
347 
348 static HRESULT WINAPI BindStatusCallback_OnObjectAvailable(IBindStatusCallback *iface,
349         REFIID riid, IUnknown* punk)
350 {
351     ERR("\n");
352     return E_NOTIMPL;
353 }
354 
355 static const IBindStatusCallbackVtbl BindStatusCallbackVtbl = {
356     BindStatusCallback_QueryInterface,
357     BindStatusCallback_AddRef,
358     BindStatusCallback_Release,
359     BindStatusCallback_OnStartBinding,
360     BindStatusCallback_GetPriority,
361     BindStatusCallback_OnLowResource,
362     BindStatusCallback_OnProgress,
363     BindStatusCallback_OnStopBinding,
364     BindStatusCallback_GetBindInfo,
365     BindStatusCallback_OnDataAvailable,
366     BindStatusCallback_OnObjectAvailable
367 };
368 
369 static inline MimeHtmlProtocol *impl_from_IUnknown(IUnknown *iface)
370 {
371     return CONTAINING_RECORD(iface, MimeHtmlProtocol, IUnknown_inner);
372 }
373 
374 static HRESULT WINAPI MimeHtmlProtocol_QueryInterface(IUnknown *iface, REFIID riid, void **ppv)
375 {
376     MimeHtmlProtocol *This = impl_from_IUnknown(iface);
377 
378     if(IsEqualGUID(&IID_IUnknown, riid)) {
379         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
380         *ppv = &This->IInternetProtocol_iface;
381     }else if(IsEqualGUID(&IID_IInternetProtocolRoot, riid)) {
382         TRACE("(%p)->(IID_IInternetProtocolRoot %p)\n", This, ppv);
383         *ppv = &This->IInternetProtocol_iface;
384     }else if(IsEqualGUID(&IID_IInternetProtocol, riid)) {
385         TRACE("(%p)->(IID_IInternetProtocol %p)\n", This, ppv);
386         *ppv = &This->IInternetProtocol_iface;
387     }else if(IsEqualGUID(&IID_IInternetProtocolInfo, riid)) {
388         TRACE("(%p)->(IID_IInternetProtocolInfo %p)\n", This, ppv);
389         *ppv = &This->IInternetProtocolInfo_iface;
390     }else {
391         FIXME("unknown interface %s\n", debugstr_guid(riid));
392         *ppv = NULL;
393         return E_NOINTERFACE;
394     }
395 
396     IUnknown_AddRef((IUnknown*)*ppv);
397     return S_OK;
398 }
399 
400 static ULONG WINAPI MimeHtmlProtocol_AddRef(IUnknown *iface)
401 {
402     MimeHtmlProtocol *This = impl_from_IUnknown(iface);
403     ULONG ref = InterlockedIncrement(&This->ref);
404 
405     TRACE("(%p) ref=%d\n", This, ref);
406 
407     return ref;
408 }
409 
410 static ULONG WINAPI MimeHtmlProtocol_Release(IUnknown *iface)
411 {
412     MimeHtmlProtocol *This = impl_from_IUnknown(iface);
413     ULONG ref = InterlockedDecrement(&This->ref);
414 
415     TRACE("(%p) ref=%x\n", This, ref);
416 
417     if(!ref) {
418         if(This->sink)
419             IInternetProtocolSink_Release(This->sink);
420         if(This->stream)
421             IStream_Release(This->stream);
422         heap_free(This->location);
423         heap_free(This);
424     }
425 
426     return ref;
427 }
428 
429 static const IUnknownVtbl MimeHtmlProtocolInnerVtbl = {
430     MimeHtmlProtocol_QueryInterface,
431     MimeHtmlProtocol_AddRef,
432     MimeHtmlProtocol_Release
433 };
434 
435 static inline MimeHtmlProtocol *impl_from_IInternetProtocol(IInternetProtocol *iface)
436 {
437     return CONTAINING_RECORD(iface, MimeHtmlProtocol, IInternetProtocol_iface);
438 }
439 
440 static HRESULT WINAPI InternetProtocol_QueryInterface(IInternetProtocol *iface, REFIID riid, void **ppv)
441 {
442     MimeHtmlProtocol *This = impl_from_IInternetProtocol(iface);
443     return IUnknown_QueryInterface(This->outer_unk, riid, ppv);
444 }
445 
446 static ULONG WINAPI InternetProtocol_AddRef(IInternetProtocol *iface)
447 {
448     MimeHtmlProtocol *This = impl_from_IInternetProtocol(iface);
449     return IUnknown_AddRef(This->outer_unk);
450 }
451 
452 static ULONG WINAPI InternetProtocol_Release(IInternetProtocol *iface)
453 {
454     MimeHtmlProtocol *This = impl_from_IInternetProtocol(iface);
455     return IUnknown_Release(This->outer_unk);
456 }
457 
458 static HRESULT WINAPI MimeHtmlProtocol_Start(IInternetProtocol *iface, const WCHAR *szUrl,
459         IInternetProtocolSink* pOIProtSink, IInternetBindInfo* pOIBindInfo,
460         DWORD grfPI, HANDLE_PTR dwReserved)
461 {
462     MimeHtmlProtocol *This = impl_from_IInternetProtocol(iface);
463     BINDINFO bindinfo = { sizeof(bindinfo) };
464     MimeHtmlBinding *binding;
465     IBindCtx *bind_ctx;
466     IStream *stream;
467     mhtml_url_t url;
468     DWORD bindf = 0;
469     IMoniker *mon;
470     HRESULT hres;
471 
472     TRACE("(%p)->(%s %p %p %08x %lx)\n", This, debugstr_w(szUrl), pOIProtSink, pOIBindInfo, grfPI, dwReserved);
473 
474     hres = parse_mhtml_url(szUrl, &url);
475     if(FAILED(hres))
476         return hres;
477 
478     if(url.location && !(This->location = heap_strdupW(url.location)))
479         return E_OUTOFMEMORY;
480 
481     hres = IInternetBindInfo_GetBindInfo(pOIBindInfo, &bindf, &bindinfo);
482     if(FAILED(hres)) {
483         WARN("GetBindInfo failed: %08x\n", hres);
484         return hres;
485     }
486     if((bindf & (BINDF_ASYNCHRONOUS|BINDF_FROMURLMON|BINDF_NEEDFILE)) != (BINDF_ASYNCHRONOUS|BINDF_FROMURLMON|BINDF_NEEDFILE))
487         FIXME("unsupported bindf %x\n", bindf);
488 
489     IInternetProtocolSink_AddRef(This->sink = pOIProtSink);
490 
491     binding = heap_alloc(FIELD_OFFSET(MimeHtmlBinding,  url[url.mhtml_len+1]));
492     if(!binding)
493         return E_OUTOFMEMORY;
494     memcpy(binding->url, url.mhtml, url.mhtml_len*sizeof(WCHAR));
495     binding->url[url.mhtml_len] = 0;
496 
497     hres = CreateURLMoniker(NULL, binding->url, &mon);
498     if(FAILED(hres)) {
499         heap_free(binding);
500         return hres;
501     }
502 
503     binding->IBindStatusCallback_iface.lpVtbl = &BindStatusCallbackVtbl;
504     binding->ref = 1;
505     binding->status = E_PENDING;
506     binding->stream = NULL;
507     binding->protocol = NULL;
508 
509     hres = CreateAsyncBindCtx(0, &binding->IBindStatusCallback_iface, NULL, &bind_ctx);
510     if(FAILED(hres)) {
511         IMoniker_Release(mon);
512         IBindStatusCallback_Release(&binding->IBindStatusCallback_iface);
513         return hres;
514     }
515 
516     IInternetProtocol_AddRef(&This->IInternetProtocol_iface);
517     binding->protocol = This;
518 
519     hres = IMoniker_BindToStorage(mon, bind_ctx, NULL, &IID_IStream, (void**)&stream);
520     IBindCtx_Release(bind_ctx);
521     IMoniker_Release(mon);
522     if(stream)
523         IStream_Release(stream);
524     hres = binding->status;
525     IBindStatusCallback_Release(&binding->IBindStatusCallback_iface);
526     if(FAILED(hres) && hres != E_PENDING)
527         report_result(This, hres);
528     return hres;
529 }
530 
531 static HRESULT WINAPI MimeHtmlProtocol_Continue(IInternetProtocol *iface, PROTOCOLDATA *pProtocolData)
532 {
533     MimeHtmlProtocol *This = impl_from_IInternetProtocol(iface);
534     FIXME("(%p)->(%p)\n", This, pProtocolData);
535     return E_NOTIMPL;
536 }
537 
538 static HRESULT WINAPI MimeHtmlProtocol_Abort(IInternetProtocol *iface, HRESULT hrReason, DWORD dwOptions)
539 {
540     MimeHtmlProtocol *This = impl_from_IInternetProtocol(iface);
541     FIXME("(%p)->(%08x %08x)\n", This, hrReason, dwOptions);
542     return E_NOTIMPL;
543 }
544 
545 static HRESULT WINAPI MimeHtmlProtocol_Terminate(IInternetProtocol *iface, DWORD dwOptions)
546 {
547     MimeHtmlProtocol *This = impl_from_IInternetProtocol(iface);
548     TRACE("(%p)->(%08x)\n", This, dwOptions);
549     return S_OK;
550 }
551 
552 static HRESULT WINAPI MimeHtmlProtocol_Suspend(IInternetProtocol *iface)
553 {
554     MimeHtmlProtocol *This = impl_from_IInternetProtocol(iface);
555     FIXME("(%p)\n", This);
556     return E_NOTIMPL;
557 }
558 
559 static HRESULT WINAPI MimeHtmlProtocol_Resume(IInternetProtocol *iface)
560 {
561     MimeHtmlProtocol *This = impl_from_IInternetProtocol(iface);
562     FIXME("(%p)\n", This);
563     return E_NOTIMPL;
564 }
565 
566 static HRESULT WINAPI MimeHtmlProtocol_Read(IInternetProtocol *iface, void* pv, ULONG cb, ULONG* pcbRead)
567 {
568     MimeHtmlProtocol *This = impl_from_IInternetProtocol(iface);
569     ULONG read = 0;
570     HRESULT hres;
571 
572     TRACE("(%p)->(%p %u %p)\n", This, pv, cb, pcbRead);
573 
574     hres = IStream_Read(This->stream, pv, cb, &read);
575     if(pcbRead)
576         *pcbRead = read;
577     if(hres != S_OK)
578         return hres;
579 
580     return read ? S_OK : S_FALSE;
581 }
582 
583 static HRESULT WINAPI MimeHtmlProtocol_Seek(IInternetProtocol *iface, LARGE_INTEGER dlibMove,
584         DWORD dwOrigin, ULARGE_INTEGER* plibNewPosition)
585 {
586     MimeHtmlProtocol *This = impl_from_IInternetProtocol(iface);
587     FIXME("(%p)->(%d %d %p)\n", This, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
588     return E_NOTIMPL;
589 }
590 
591 static HRESULT WINAPI MimeHtmlProtocol_LockRequest(IInternetProtocol *iface, DWORD dwOptions)
592 {
593     MimeHtmlProtocol *This = impl_from_IInternetProtocol(iface);
594     FIXME("(%p)->(%d)\n", This, dwOptions);
595     return S_OK;
596 }
597 
598 static HRESULT WINAPI MimeHtmlProtocol_UnlockRequest(IInternetProtocol *iface)
599 {
600     MimeHtmlProtocol *This = impl_from_IInternetProtocol(iface);
601     FIXME("(%p)\n", This);
602     return S_OK;
603 }
604 
605 static const IInternetProtocolVtbl MimeHtmlProtocolVtbl = {
606     InternetProtocol_QueryInterface,
607     InternetProtocol_AddRef,
608     InternetProtocol_Release,
609     MimeHtmlProtocol_Start,
610     MimeHtmlProtocol_Continue,
611     MimeHtmlProtocol_Abort,
612     MimeHtmlProtocol_Terminate,
613     MimeHtmlProtocol_Suspend,
614     MimeHtmlProtocol_Resume,
615     MimeHtmlProtocol_Read,
616     MimeHtmlProtocol_Seek,
617     MimeHtmlProtocol_LockRequest,
618     MimeHtmlProtocol_UnlockRequest
619 };
620 
621 static inline MimeHtmlProtocol *impl_from_IInternetProtocolInfo(IInternetProtocolInfo *iface)
622 {
623     return CONTAINING_RECORD(iface, MimeHtmlProtocol, IInternetProtocolInfo_iface);
624 }
625 
626 static HRESULT WINAPI MimeHtmlProtocolInfo_QueryInterface(IInternetProtocolInfo *iface, REFIID riid, void **ppv)
627 {
628     MimeHtmlProtocol *This = impl_from_IInternetProtocolInfo(iface);
629     return IUnknown_QueryInterface(This->outer_unk, riid, ppv);
630 }
631 
632 static ULONG WINAPI MimeHtmlProtocolInfo_AddRef(IInternetProtocolInfo *iface)
633 {
634     MimeHtmlProtocol *This = impl_from_IInternetProtocolInfo(iface);
635     return IUnknown_AddRef(This->outer_unk);
636 }
637 
638 static ULONG WINAPI MimeHtmlProtocolInfo_Release(IInternetProtocolInfo *iface)
639 {
640     MimeHtmlProtocol *This = impl_from_IInternetProtocolInfo(iface);
641     return IUnknown_Release(This->outer_unk);
642 }
643 
644 static HRESULT WINAPI MimeHtmlProtocolInfo_ParseUrl(IInternetProtocolInfo *iface, LPCWSTR pwzUrl,
645         PARSEACTION ParseAction, DWORD dwParseFlags, LPWSTR pwzResult, DWORD cchResult,
646         DWORD* pcchResult, DWORD dwReserved)
647 {
648     MimeHtmlProtocol *This = impl_from_IInternetProtocolInfo(iface);
649     FIXME("(%p)->(%s %d %x %p %d %p %d)\n", This, debugstr_w(pwzUrl), ParseAction,
650           dwParseFlags, pwzResult, cchResult, pcchResult, dwReserved);
651     return INET_E_DEFAULT_ACTION;
652 }
653 
654 static HRESULT WINAPI MimeHtmlProtocolInfo_CombineUrl(IInternetProtocolInfo *iface,
655         LPCWSTR pwzBaseUrl, LPCWSTR pwzRelativeUrl, DWORD dwCombineFlags, LPWSTR pwzResult,
656         DWORD cchResult, DWORD* pcchResult, DWORD dwReserved)
657 {
658     MimeHtmlProtocol *This = impl_from_IInternetProtocolInfo(iface);
659     size_t len = ARRAY_SIZE(mhtml_prefixW);
660     mhtml_url_t url;
661     WCHAR *p;
662     HRESULT hres;
663 
664     TRACE("(%p)->(%s %s %08x %p %d %p %d)\n", This, debugstr_w(pwzBaseUrl),
665           debugstr_w(pwzRelativeUrl), dwCombineFlags, pwzResult, cchResult,
666           pcchResult, dwReserved);
667 
668     hres = parse_mhtml_url(pwzBaseUrl, &url);
669     if(FAILED(hres))
670         return hres;
671 
672     if(!_wcsnicmp(pwzRelativeUrl, mhtml_prefixW, ARRAY_SIZE(mhtml_prefixW))) {
673         FIXME("Relative URL is mhtml protocol\n");
674         return INET_E_USE_DEFAULT_PROTOCOLHANDLER;
675     }
676 
677     len += url.mhtml_len;
678     if(*pwzRelativeUrl)
679         len += lstrlenW(pwzRelativeUrl) + ARRAY_SIZE(mhtml_separatorW);
680     if(len >= cchResult) {
681         *pcchResult = 0;
682         return E_FAIL;
683     }
684 
685     memcpy(pwzResult, mhtml_prefixW, sizeof(mhtml_prefixW));
686     p = pwzResult + ARRAY_SIZE(mhtml_prefixW);
687     memcpy(p, url.mhtml, url.mhtml_len*sizeof(WCHAR));
688     p += url.mhtml_len;
689     if(*pwzRelativeUrl) {
690         memcpy(p, mhtml_separatorW, sizeof(mhtml_separatorW));
691         p += ARRAY_SIZE(mhtml_separatorW);
692         lstrcpyW(p, pwzRelativeUrl);
693     }else {
694         *p = 0;
695     }
696 
697     *pcchResult = len;
698     return S_OK;
699 }
700 
701 static HRESULT WINAPI MimeHtmlProtocolInfo_CompareUrl(IInternetProtocolInfo *iface, LPCWSTR pwzUrl1,
702         LPCWSTR pwzUrl2, DWORD dwCompareFlags)
703 {
704     MimeHtmlProtocol *This = impl_from_IInternetProtocolInfo(iface);
705     FIXME("(%p)->(%s %s %08x)\n", This, debugstr_w(pwzUrl1), debugstr_w(pwzUrl2), dwCompareFlags);
706     return E_NOTIMPL;
707 }
708 
709 static HRESULT WINAPI MimeHtmlProtocolInfo_QueryInfo(IInternetProtocolInfo *iface, LPCWSTR pwzUrl,
710         QUERYOPTION QueryOption, DWORD dwQueryFlags, LPVOID pBuffer, DWORD cbBuffer, DWORD* pcbBuf,
711         DWORD dwReserved)
712 {
713     MimeHtmlProtocol *This = impl_from_IInternetProtocolInfo(iface);
714     FIXME("(%p)->(%s %08x %08x %p %d %p %d)\n", This, debugstr_w(pwzUrl), QueryOption, dwQueryFlags, pBuffer,
715           cbBuffer, pcbBuf, dwReserved);
716     return INET_E_USE_DEFAULT_PROTOCOLHANDLER;
717 }
718 
719 static const IInternetProtocolInfoVtbl MimeHtmlProtocolInfoVtbl = {
720     MimeHtmlProtocolInfo_QueryInterface,
721     MimeHtmlProtocolInfo_AddRef,
722     MimeHtmlProtocolInfo_Release,
723     MimeHtmlProtocolInfo_ParseUrl,
724     MimeHtmlProtocolInfo_CombineUrl,
725     MimeHtmlProtocolInfo_CompareUrl,
726     MimeHtmlProtocolInfo_QueryInfo
727 };
728 
729 HRESULT MimeHtmlProtocol_create(IUnknown *outer, void **obj)
730 {
731     MimeHtmlProtocol *protocol;
732 
733     protocol = heap_alloc(sizeof(*protocol));
734     if(!protocol)
735         return E_OUTOFMEMORY;
736 
737     protocol->IUnknown_inner.lpVtbl = &MimeHtmlProtocolInnerVtbl;
738     protocol->IInternetProtocol_iface.lpVtbl = &MimeHtmlProtocolVtbl;
739     protocol->IInternetProtocolInfo_iface.lpVtbl = &MimeHtmlProtocolInfoVtbl;
740     protocol->ref = 1;
741     protocol->outer_unk = outer ? outer : &protocol->IUnknown_inner;
742     protocol->location = NULL;
743     protocol->stream = NULL;
744     protocol->sink = NULL;
745 
746     *obj = &protocol->IUnknown_inner;
747     return S_OK;
748 }
749