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