| /* | |
| * Copyright 2017 Jacek Caban for CodeWeavers | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| */ | |
| #define COBJMACROS | |
| #define NONAMELESSUNION | |
| #include <assert.h> | |
| #include "mimeole.h" | |
| #include "inetcomm_private.h" | |
| #include "wine/debug.h" | |
| #include "wine/unicode.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(inetcomm); | |
| typedef struct { | |
| IUnknown IUnknown_inner; | |
| IInternetProtocol IInternetProtocol_iface; | |
| IInternetProtocolInfo IInternetProtocolInfo_iface; | |
| LONG ref; | |
| IUnknown *outer_unk; | |
| WCHAR *location; | |
| IStream *stream; | |
| IInternetProtocolSink *sink; | |
| } MimeHtmlProtocol; | |
| typedef struct { | |
| const WCHAR *mhtml; | |
| size_t mhtml_len; | |
| const WCHAR *location; | |
| } mhtml_url_t; | |
| typedef struct { | |
| IBindStatusCallback IBindStatusCallback_iface; | |
| LONG ref; | |
| MimeHtmlProtocol *protocol; | |
| HRESULT status; | |
| IStream *stream; | |
| WCHAR url[1]; | |
| } MimeHtmlBinding; | |
| static const WCHAR mhtml_prefixW[] = {'m','h','t','m','l',':'}; | |
| static const WCHAR mhtml_separatorW[] = {'!','x','-','u','s','c',':'}; | |
| static inline LPWSTR heap_strdupW(LPCWSTR str) | |
| { | |
| LPWSTR ret = NULL; | |
| if(str) { | |
| DWORD size; | |
| size = (strlenW(str)+1)*sizeof(WCHAR); | |
| ret = heap_alloc(size); | |
| if(ret) | |
| memcpy(ret, str, size); | |
| } | |
| return ret; | |
| } | |
| static HRESULT parse_mhtml_url(const WCHAR *url, mhtml_url_t *r) | |
| { | |
| const WCHAR *p; | |
| if(strncmpiW(url, mhtml_prefixW, sizeof(mhtml_prefixW)/sizeof(WCHAR))) | |
| return E_FAIL; | |
| r->mhtml = url + sizeof(mhtml_prefixW)/sizeof(WCHAR); | |
| p = strchrW(r->mhtml, '!'); | |
| if(p) { | |
| r->mhtml_len = p - r->mhtml; | |
| /* FIXME: We handle '!' and '!x-usc:' in URLs as the same thing. Those should not be the same. */ | |
| if(!strncmpW(p, mhtml_separatorW, sizeof(mhtml_separatorW)/sizeof(WCHAR))) | |
| p += sizeof(mhtml_separatorW)/sizeof(WCHAR); | |
| else | |
| p++; | |
| }else { | |
| r->mhtml_len = strlenW(r->mhtml); | |
| } | |
| r->location = p; | |
| return S_OK; | |
| } | |
| static HRESULT report_result(MimeHtmlProtocol *protocol, HRESULT result) | |
| { | |
| if(protocol->sink) { | |
| IInternetProtocolSink_ReportResult(protocol->sink, result, ERROR_SUCCESS, NULL); | |
| IInternetProtocolSink_Release(protocol->sink); | |
| protocol->sink = NULL; | |
| } | |
| return result; | |
| } | |
| static HRESULT on_mime_message_available(MimeHtmlProtocol *protocol, IMimeMessage *mime_message) | |
| { | |
| FINDBODY find = {NULL}; | |
| IMimeBody *mime_body; | |
| PROPVARIANT value; | |
| HBODY body; | |
| HRESULT hres; | |
| hres = IMimeMessage_FindFirst(mime_message, &find, &body); | |
| if(FAILED(hres)) | |
| return report_result(protocol, hres); | |
| if(protocol->location) { | |
| BOOL found = FALSE; | |
| do { | |
| hres = IMimeMessage_FindNext(mime_message, &find, &body); | |
| if(FAILED(hres)) { | |
| WARN("location %s not found\n", debugstr_w(protocol->location)); | |
| return report_result(protocol, hres); | |
| } | |
| value.vt = VT_LPWSTR; | |
| hres = IMimeMessage_GetBodyProp(mime_message, body, "content-location", 0, &value); | |
| if(hres == MIME_E_NOT_FOUND) | |
| continue; | |
| if(FAILED(hres)) | |
| return report_result(protocol, hres); | |
| found = !strcmpW(protocol->location, value.u.pwszVal); | |
| PropVariantClear(&value); | |
| }while(!found); | |
| }else { | |
| hres = IMimeMessage_FindNext(mime_message, &find, &body); | |
| if(FAILED(hres)) { | |
| WARN("location %s not found\n", debugstr_w(protocol->location)); | |
| return report_result(protocol, hres); | |
| } | |
| } | |
| hres = IMimeMessage_BindToObject(mime_message, body, &IID_IMimeBody, (void**)&mime_body); | |
| if(FAILED(hres)) | |
| return report_result(protocol, hres); | |
| value.vt = VT_LPWSTR; | |
| hres = IMimeBody_GetProp(mime_body, "content-type", 0, &value); | |
| if(SUCCEEDED(hres)) { | |
| hres = IInternetProtocolSink_ReportProgress(protocol->sink, BINDSTATUS_MIMETYPEAVAILABLE, value.u.pwszVal); | |
| PropVariantClear(&value); | |
| } | |
| /* FIXME: Create and report cache file. */ | |
| hres = IMimeBody_GetData(mime_body, IET_DECODED, &protocol->stream); | |
| if(FAILED(hres)) | |
| return report_result(protocol, hres); | |
| IInternetProtocolSink_ReportData(protocol->sink, BSCF_FIRSTDATANOTIFICATION | |
| | BSCF_INTERMEDIATEDATANOTIFICATION | |
| | BSCF_LASTDATANOTIFICATION | |
| | BSCF_DATAFULLYAVAILABLE | |
| | BSCF_AVAILABLEDATASIZEUNKNOWN, 0, 0); | |
| return report_result(protocol, S_OK); | |
| } | |
| static HRESULT load_mime_message(IStream *stream, IMimeMessage **ret) | |
| { | |
| IMimeMessage *mime_message; | |
| HRESULT hres; | |
| hres = MimeMessage_create(NULL, (void**)&mime_message); | |
| if(FAILED(hres)) | |
| return hres; | |
| IMimeMessage_InitNew(mime_message); | |
| hres = IMimeMessage_Load(mime_message, stream); | |
| if(FAILED(hres)) { | |
| IMimeMessage_Release(mime_message); | |
| return hres; | |
| } | |
| *ret = mime_message; | |
| return S_OK; | |
| } | |
| static inline MimeHtmlBinding *impl_from_IBindStatusCallback(IBindStatusCallback *iface) | |
| { | |
| return CONTAINING_RECORD(iface, MimeHtmlBinding, IBindStatusCallback_iface); | |
| } | |
| static HRESULT WINAPI BindStatusCallback_QueryInterface(IBindStatusCallback *iface, | |
| REFIID riid, void **ppv) | |
| { | |
| MimeHtmlBinding *This = impl_from_IBindStatusCallback(iface); | |
| if(IsEqualGUID(&IID_IUnknown, riid)) { | |
| TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv); | |
| *ppv = &This->IBindStatusCallback_iface; | |
| }else if(IsEqualGUID(&IID_IBindStatusCallback, riid)) { | |
| TRACE("(%p)->(IID_IBindStatusCallback %p)\n", This, ppv); | |
| *ppv = &This->IBindStatusCallback_iface; | |
| }else { | |
| TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); | |
| *ppv = NULL; | |
| return E_NOINTERFACE; | |
| } | |
| IUnknown_AddRef((IUnknown*)*ppv); | |
| return S_OK; | |
| } | |
| static ULONG WINAPI BindStatusCallback_AddRef(IBindStatusCallback *iface) | |
| { | |
| MimeHtmlBinding *This = impl_from_IBindStatusCallback(iface); | |
| LONG ref = InterlockedIncrement(&This->ref); | |
| TRACE("(%p) ref=%d\n", This, ref); | |
| return ref; | |
| } | |
| static ULONG WINAPI BindStatusCallback_Release(IBindStatusCallback *iface) | |
| { | |
| MimeHtmlBinding *This = impl_from_IBindStatusCallback(iface); | |
| LONG ref = InterlockedDecrement(&This->ref); | |
| TRACE("(%p) ref=%d\n", This, ref); | |
| if(!ref) { | |
| if(This->protocol) | |
| IInternetProtocol_Release(&This->protocol->IInternetProtocol_iface); | |
| if(This->stream) | |
| IStream_Release(This->stream); | |
| heap_free(This); | |
| } | |
| return ref; | |
| } | |
| static HRESULT WINAPI BindStatusCallback_OnStartBinding(IBindStatusCallback *iface, | |
| DWORD dwReserved, IBinding *pib) | |
| { | |
| MimeHtmlBinding *This = impl_from_IBindStatusCallback(iface); | |
| TRACE("(%p)->(%x %p)\n", This, dwReserved, pib); | |
| assert(!This->stream); | |
| return CreateStreamOnHGlobal(NULL, TRUE, &This->stream); | |
| } | |
| static HRESULT WINAPI BindStatusCallback_GetPriority(IBindStatusCallback *iface, LONG *pnPriority) | |
| { | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI BindStatusCallback_OnLowResource(IBindStatusCallback *iface, DWORD dwReserved) | |
| { | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI BindStatusCallback_OnProgress(IBindStatusCallback *iface, ULONG ulProgress, | |
| ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText) | |
| { | |
| MimeHtmlBinding *This = impl_from_IBindStatusCallback(iface); | |
| TRACE("(%p)->(%u/%u %u %s)\n", This, ulProgress, ulProgressMax, ulStatusCode, debugstr_w(szStatusText)); | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI BindStatusCallback_OnStopBinding(IBindStatusCallback *iface, HRESULT hresult, LPCWSTR szError) | |
| { | |
| MimeHtmlBinding *This = impl_from_IBindStatusCallback(iface); | |
| IMimeMessage *mime_message = NULL; | |
| TRACE("(%p)->(%x %s)\n", This, hresult, debugstr_w(szError)); | |
| if(SUCCEEDED(hresult)) { | |
| hresult = load_mime_message(This->stream, &mime_message); | |
| IStream_Release(This->stream); | |
| This->stream = NULL; | |
| } | |
| This->status = hresult; | |
| if(mime_message) | |
| on_mime_message_available(This->protocol, mime_message); | |
| else | |
| report_result(This->protocol, hresult); | |
| if(mime_message) | |
| IMimeMessage_Release(mime_message); | |
| IInternetProtocol_Release(&This->protocol->IInternetProtocol_iface); | |
| This->protocol = NULL; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI BindStatusCallback_GetBindInfo(IBindStatusCallback *iface, | |
| DWORD* grfBINDF, BINDINFO* pbindinfo) | |
| { | |
| MimeHtmlBinding *This = impl_from_IBindStatusCallback(iface); | |
| TRACE("(%p)\n", This); | |
| *grfBINDF = BINDF_ASYNCHRONOUS; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI BindStatusCallback_OnDataAvailable(IBindStatusCallback *iface, DWORD grfBSCF, | |
| DWORD dwSize, FORMATETC* pformatetc, STGMEDIUM* pstgmed) | |
| { | |
| MimeHtmlBinding *This = impl_from_IBindStatusCallback(iface); | |
| BYTE buf[4*1024]; | |
| DWORD read; | |
| HRESULT hres; | |
| TRACE("(%p)\n", This); | |
| assert(pstgmed->tymed == TYMED_ISTREAM); | |
| while(1) { | |
| hres = IStream_Read(pstgmed->u.pstm, buf, sizeof(buf), &read); | |
| if(FAILED(hres)) | |
| return hres; | |
| if(!read) | |
| break; | |
| hres = IStream_Write(This->stream, buf, read, NULL); | |
| if(FAILED(hres)) | |
| return hres; | |
| } | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI BindStatusCallback_OnObjectAvailable(IBindStatusCallback *iface, | |
| REFIID riid, IUnknown* punk) | |
| { | |
| ERR("\n"); | |
| return E_NOTIMPL; | |
| } | |
| static const IBindStatusCallbackVtbl BindStatusCallbackVtbl = { | |
| BindStatusCallback_QueryInterface, | |
| BindStatusCallback_AddRef, | |
| BindStatusCallback_Release, | |
| BindStatusCallback_OnStartBinding, | |
| BindStatusCallback_GetPriority, | |
| BindStatusCallback_OnLowResource, | |
| BindStatusCallback_OnProgress, | |
| BindStatusCallback_OnStopBinding, | |
| BindStatusCallback_GetBindInfo, | |
| BindStatusCallback_OnDataAvailable, | |
| BindStatusCallback_OnObjectAvailable | |
| }; | |
| static inline MimeHtmlProtocol *impl_from_IUnknown(IUnknown *iface) | |
| { | |
| return CONTAINING_RECORD(iface, MimeHtmlProtocol, IUnknown_inner); | |
| } | |
| static HRESULT WINAPI MimeHtmlProtocol_QueryInterface(IUnknown *iface, REFIID riid, void **ppv) | |
| { | |
| MimeHtmlProtocol *This = impl_from_IUnknown(iface); | |
| if(IsEqualGUID(&IID_IUnknown, riid)) { | |
| TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv); | |
| *ppv = &This->IInternetProtocol_iface; | |
| }else if(IsEqualGUID(&IID_IInternetProtocolRoot, riid)) { | |
| TRACE("(%p)->(IID_IInternetProtocolRoot %p)\n", This, ppv); | |
| *ppv = &This->IInternetProtocol_iface; | |
| }else if(IsEqualGUID(&IID_IInternetProtocol, riid)) { | |
| TRACE("(%p)->(IID_IInternetProtocol %p)\n", This, ppv); | |
| *ppv = &This->IInternetProtocol_iface; | |
| }else if(IsEqualGUID(&IID_IInternetProtocolInfo, riid)) { | |
| TRACE("(%p)->(IID_IInternetProtocolInfo %p)\n", This, ppv); | |
| *ppv = &This->IInternetProtocolInfo_iface; | |
| }else { | |
| FIXME("unknown interface %s\n", debugstr_guid(riid)); | |
| *ppv = NULL; | |
| return E_NOINTERFACE; | |
| } | |
| IUnknown_AddRef((IUnknown*)*ppv); | |
| return S_OK; | |
| } | |
| static ULONG WINAPI MimeHtmlProtocol_AddRef(IUnknown *iface) | |
| { | |
| MimeHtmlProtocol *This = impl_from_IUnknown(iface); | |
| ULONG ref = InterlockedIncrement(&This->ref); | |
| TRACE("(%p) ref=%d\n", This, ref); | |
| return ref; | |
| } | |
| static ULONG WINAPI MimeHtmlProtocol_Release(IUnknown *iface) | |
| { | |
| MimeHtmlProtocol *This = impl_from_IUnknown(iface); | |
| ULONG ref = InterlockedDecrement(&This->ref); | |
| TRACE("(%p) ref=%x\n", This, ref); | |
| if(!ref) { | |
| if(This->sink) | |
| IInternetProtocolSink_Release(This->sink); | |
| if(This->stream) | |
| IStream_Release(This->stream); | |
| heap_free(This->location); | |
| heap_free(This); | |
| } | |
| return ref; | |
| } | |
| static const IUnknownVtbl MimeHtmlProtocolInnerVtbl = { | |
| MimeHtmlProtocol_QueryInterface, | |
| MimeHtmlProtocol_AddRef, | |
| MimeHtmlProtocol_Release | |
| }; | |
| static inline MimeHtmlProtocol *impl_from_IInternetProtocol(IInternetProtocol *iface) | |
| { | |
| return CONTAINING_RECORD(iface, MimeHtmlProtocol, IInternetProtocol_iface); | |
| } | |
| static HRESULT WINAPI InternetProtocol_QueryInterface(IInternetProtocol *iface, REFIID riid, void **ppv) | |
| { | |
| MimeHtmlProtocol *This = impl_from_IInternetProtocol(iface); | |
| return IUnknown_QueryInterface(This->outer_unk, riid, ppv); | |
| } | |
| static ULONG WINAPI InternetProtocol_AddRef(IInternetProtocol *iface) | |
| { | |
| MimeHtmlProtocol *This = impl_from_IInternetProtocol(iface); | |
| return IUnknown_AddRef(This->outer_unk); | |
| } | |
| static ULONG WINAPI InternetProtocol_Release(IInternetProtocol *iface) | |
| { | |
| MimeHtmlProtocol *This = impl_from_IInternetProtocol(iface); | |
| return IUnknown_Release(This->outer_unk); | |
| } | |
| static HRESULT WINAPI MimeHtmlProtocol_Start(IInternetProtocol *iface, const WCHAR *szUrl, | |
| IInternetProtocolSink* pOIProtSink, IInternetBindInfo* pOIBindInfo, | |
| DWORD grfPI, HANDLE_PTR dwReserved) | |
| { | |
| MimeHtmlProtocol *This = impl_from_IInternetProtocol(iface); | |
| BINDINFO bindinfo = { sizeof(bindinfo) }; | |
| MimeHtmlBinding *binding; | |
| IBindCtx *bind_ctx; | |
| IStream *stream; | |
| mhtml_url_t url; | |
| DWORD bindf = 0; | |
| IMoniker *mon; | |
| HRESULT hres; | |
| TRACE("(%p)->(%s %p %p %08x %lx)\n", This, debugstr_w(szUrl), pOIProtSink, pOIBindInfo, grfPI, dwReserved); | |
| hres = parse_mhtml_url(szUrl, &url); | |
| if(FAILED(hres)) | |
| return hres; | |
| if(url.location && !(This->location = heap_strdupW(url.location))) | |
| return E_OUTOFMEMORY; | |
| hres = IInternetBindInfo_GetBindInfo(pOIBindInfo, &bindf, &bindinfo); | |
| if(FAILED(hres)) { | |
| WARN("GetBindInfo failed: %08x\n", hres); | |
| return hres; | |
| } | |
| if((bindf & (BINDF_ASYNCHRONOUS|BINDF_FROMURLMON|BINDF_NEEDFILE)) != (BINDF_ASYNCHRONOUS|BINDF_FROMURLMON|BINDF_NEEDFILE)) | |
| FIXME("unsupported bindf %x\n", bindf); | |
| IInternetProtocolSink_AddRef(This->sink = pOIProtSink); | |
| binding = heap_alloc(FIELD_OFFSET(MimeHtmlBinding, url[url.mhtml_len+1])); | |
| if(!binding) | |
| return E_OUTOFMEMORY; | |
| memcpy(binding->url, url.mhtml, url.mhtml_len*sizeof(WCHAR)); | |
| binding->url[url.mhtml_len] = 0; | |
| hres = CreateURLMoniker(NULL, binding->url, &mon); | |
| if(FAILED(hres)) { | |
| heap_free(binding); | |
| return hres; | |
| } | |
| binding->IBindStatusCallback_iface.lpVtbl = &BindStatusCallbackVtbl; | |
| binding->ref = 1; | |
| binding->status = E_PENDING; | |
| binding->stream = NULL; | |
| binding->protocol = NULL; | |
| hres = CreateAsyncBindCtx(0, &binding->IBindStatusCallback_iface, NULL, &bind_ctx); | |
| if(FAILED(hres)) { | |
| IMoniker_Release(mon); | |
| IBindStatusCallback_Release(&binding->IBindStatusCallback_iface); | |
| return hres; | |
| } | |
| IInternetProtocol_AddRef(&This->IInternetProtocol_iface); | |
| binding->protocol = This; | |
| hres = IMoniker_BindToStorage(mon, bind_ctx, NULL, &IID_IStream, (void**)&stream); | |
| IBindCtx_Release(bind_ctx); | |
| IMoniker_Release(mon); | |
| if(stream) | |
| IStream_Release(stream); | |
| hres = binding->status; | |
| IBindStatusCallback_Release(&binding->IBindStatusCallback_iface); | |
| if(FAILED(hres) && hres != E_PENDING) | |
| report_result(This, hres); | |
| return hres; | |
| } | |
| static HRESULT WINAPI MimeHtmlProtocol_Continue(IInternetProtocol *iface, PROTOCOLDATA *pProtocolData) | |
| { | |
| MimeHtmlProtocol *This = impl_from_IInternetProtocol(iface); | |
| FIXME("(%p)->(%p)\n", This, pProtocolData); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI MimeHtmlProtocol_Abort(IInternetProtocol *iface, HRESULT hrReason, DWORD dwOptions) | |
| { | |
| MimeHtmlProtocol *This = impl_from_IInternetProtocol(iface); | |
| FIXME("(%p)->(%08x %08x)\n", This, hrReason, dwOptions); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI MimeHtmlProtocol_Terminate(IInternetProtocol *iface, DWORD dwOptions) | |
| { | |
| MimeHtmlProtocol *This = impl_from_IInternetProtocol(iface); | |
| TRACE("(%p)->(%08x)\n", This, dwOptions); | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI MimeHtmlProtocol_Suspend(IInternetProtocol *iface) | |
| { | |
| MimeHtmlProtocol *This = impl_from_IInternetProtocol(iface); | |
| FIXME("(%p)\n", This); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI MimeHtmlProtocol_Resume(IInternetProtocol *iface) | |
| { | |
| MimeHtmlProtocol *This = impl_from_IInternetProtocol(iface); | |
| FIXME("(%p)\n", This); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI MimeHtmlProtocol_Read(IInternetProtocol *iface, void* pv, ULONG cb, ULONG* pcbRead) | |
| { | |
| MimeHtmlProtocol *This = impl_from_IInternetProtocol(iface); | |
| ULONG read = 0; | |
| HRESULT hres; | |
| TRACE("(%p)->(%p %u %p)\n", This, pv, cb, pcbRead); | |
| hres = IStream_Read(This->stream, pv, cb, &read); | |
| if(pcbRead) | |
| *pcbRead = read; | |
| if(hres != S_OK) | |
| return hres; | |
| return read ? S_OK : S_FALSE; | |
| } | |
| static HRESULT WINAPI MimeHtmlProtocol_Seek(IInternetProtocol *iface, LARGE_INTEGER dlibMove, | |
| DWORD dwOrigin, ULARGE_INTEGER* plibNewPosition) | |
| { | |
| MimeHtmlProtocol *This = impl_from_IInternetProtocol(iface); | |
| FIXME("(%p)->(%d %d %p)\n", This, dlibMove.u.LowPart, dwOrigin, plibNewPosition); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI MimeHtmlProtocol_LockRequest(IInternetProtocol *iface, DWORD dwOptions) | |
| { | |
| MimeHtmlProtocol *This = impl_from_IInternetProtocol(iface); | |
| FIXME("(%p)->(%d)\n", This, dwOptions); | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI MimeHtmlProtocol_UnlockRequest(IInternetProtocol *iface) | |
| { | |
| MimeHtmlProtocol *This = impl_from_IInternetProtocol(iface); | |
| FIXME("(%p)\n", This); | |
| return S_OK; | |
| } | |
| static const IInternetProtocolVtbl MimeHtmlProtocolVtbl = { | |
| InternetProtocol_QueryInterface, | |
| InternetProtocol_AddRef, | |
| InternetProtocol_Release, | |
| MimeHtmlProtocol_Start, | |
| MimeHtmlProtocol_Continue, | |
| MimeHtmlProtocol_Abort, | |
| MimeHtmlProtocol_Terminate, | |
| MimeHtmlProtocol_Suspend, | |
| MimeHtmlProtocol_Resume, | |
| MimeHtmlProtocol_Read, | |
| MimeHtmlProtocol_Seek, | |
| MimeHtmlProtocol_LockRequest, | |
| MimeHtmlProtocol_UnlockRequest | |
| }; | |
| static inline MimeHtmlProtocol *impl_from_IInternetProtocolInfo(IInternetProtocolInfo *iface) | |
| { | |
| return CONTAINING_RECORD(iface, MimeHtmlProtocol, IInternetProtocolInfo_iface); | |
| } | |
| static HRESULT WINAPI MimeHtmlProtocolInfo_QueryInterface(IInternetProtocolInfo *iface, REFIID riid, void **ppv) | |
| { | |
| MimeHtmlProtocol *This = impl_from_IInternetProtocolInfo(iface); | |
| return IUnknown_QueryInterface(This->outer_unk, riid, ppv); | |
| } | |
| static ULONG WINAPI MimeHtmlProtocolInfo_AddRef(IInternetProtocolInfo *iface) | |
| { | |
| MimeHtmlProtocol *This = impl_from_IInternetProtocolInfo(iface); | |
| return IUnknown_AddRef(This->outer_unk); | |
| } | |
| static ULONG WINAPI MimeHtmlProtocolInfo_Release(IInternetProtocolInfo *iface) | |
| { | |
| MimeHtmlProtocol *This = impl_from_IInternetProtocolInfo(iface); | |
| return IUnknown_Release(This->outer_unk); | |
| } | |
| static HRESULT WINAPI MimeHtmlProtocolInfo_ParseUrl(IInternetProtocolInfo *iface, LPCWSTR pwzUrl, | |
| PARSEACTION ParseAction, DWORD dwParseFlags, LPWSTR pwzResult, DWORD cchResult, | |
| DWORD* pcchResult, DWORD dwReserved) | |
| { | |
| MimeHtmlProtocol *This = impl_from_IInternetProtocolInfo(iface); | |
| FIXME("(%p)->(%s %d %x %p %d %p %d)\n", This, debugstr_w(pwzUrl), ParseAction, | |
| dwParseFlags, pwzResult, cchResult, pcchResult, dwReserved); | |
| return INET_E_DEFAULT_ACTION; | |
| } | |
| static HRESULT WINAPI MimeHtmlProtocolInfo_CombineUrl(IInternetProtocolInfo *iface, | |
| LPCWSTR pwzBaseUrl, LPCWSTR pwzRelativeUrl, DWORD dwCombineFlags, LPWSTR pwzResult, | |
| DWORD cchResult, DWORD* pcchResult, DWORD dwReserved) | |
| { | |
| MimeHtmlProtocol *This = impl_from_IInternetProtocolInfo(iface); | |
| size_t len = sizeof(mhtml_prefixW)/sizeof(WCHAR); | |
| mhtml_url_t url; | |
| WCHAR *p; | |
| HRESULT hres; | |
| TRACE("(%p)->(%s %s %08x %p %d %p %d)\n", This, debugstr_w(pwzBaseUrl), | |
| debugstr_w(pwzRelativeUrl), dwCombineFlags, pwzResult, cchResult, | |
| pcchResult, dwReserved); | |
| hres = parse_mhtml_url(pwzBaseUrl, &url); | |
| if(FAILED(hres)) | |
| return hres; | |
| if(!strncmpiW(pwzRelativeUrl, mhtml_prefixW, sizeof(mhtml_prefixW)/sizeof(WCHAR))) { | |
| FIXME("Relative URL is mhtml protocol\n"); | |
| return INET_E_USE_DEFAULT_PROTOCOLHANDLER; | |
| } | |
| len += url.mhtml_len; | |
| if(*pwzRelativeUrl) | |
| len += strlenW(pwzRelativeUrl) + sizeof(mhtml_separatorW)/sizeof(WCHAR); | |
| if(len >= cchResult) { | |
| *pcchResult = 0; | |
| return E_FAIL; | |
| } | |
| memcpy(pwzResult, mhtml_prefixW, sizeof(mhtml_prefixW)); | |
| p = pwzResult + sizeof(mhtml_prefixW)/sizeof(WCHAR); | |
| memcpy(p, url.mhtml, url.mhtml_len*sizeof(WCHAR)); | |
| p += url.mhtml_len; | |
| if(*pwzRelativeUrl) { | |
| memcpy(p, mhtml_separatorW, sizeof(mhtml_separatorW)); | |
| p += sizeof(mhtml_separatorW)/sizeof(WCHAR); | |
| strcpyW(p, pwzRelativeUrl); | |
| }else { | |
| *p = 0; | |
| } | |
| *pcchResult = len; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI MimeHtmlProtocolInfo_CompareUrl(IInternetProtocolInfo *iface, LPCWSTR pwzUrl1, | |
| LPCWSTR pwzUrl2, DWORD dwCompareFlags) | |
| { | |
| MimeHtmlProtocol *This = impl_from_IInternetProtocolInfo(iface); | |
| FIXME("(%p)->(%s %s %08x)\n", This, debugstr_w(pwzUrl1), debugstr_w(pwzUrl2), dwCompareFlags); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI MimeHtmlProtocolInfo_QueryInfo(IInternetProtocolInfo *iface, LPCWSTR pwzUrl, | |
| QUERYOPTION QueryOption, DWORD dwQueryFlags, LPVOID pBuffer, DWORD cbBuffer, DWORD* pcbBuf, | |
| DWORD dwReserved) | |
| { | |
| MimeHtmlProtocol *This = impl_from_IInternetProtocolInfo(iface); | |
| FIXME("(%p)->(%s %08x %08x %p %d %p %d)\n", This, debugstr_w(pwzUrl), QueryOption, dwQueryFlags, pBuffer, | |
| cbBuffer, pcbBuf, dwReserved); | |
| return INET_E_USE_DEFAULT_PROTOCOLHANDLER; | |
| } | |
| static const IInternetProtocolInfoVtbl MimeHtmlProtocolInfoVtbl = { | |
| MimeHtmlProtocolInfo_QueryInterface, | |
| MimeHtmlProtocolInfo_AddRef, | |
| MimeHtmlProtocolInfo_Release, | |
| MimeHtmlProtocolInfo_ParseUrl, | |
| MimeHtmlProtocolInfo_CombineUrl, | |
| MimeHtmlProtocolInfo_CompareUrl, | |
| MimeHtmlProtocolInfo_QueryInfo | |
| }; | |
| HRESULT MimeHtmlProtocol_create(IUnknown *outer, void **obj) | |
| { | |
| MimeHtmlProtocol *protocol; | |
| protocol = heap_alloc(sizeof(*protocol)); | |
| if(!protocol) | |
| return E_OUTOFMEMORY; | |
| protocol->IUnknown_inner.lpVtbl = &MimeHtmlProtocolInnerVtbl; | |
| protocol->IInternetProtocol_iface.lpVtbl = &MimeHtmlProtocolVtbl; | |
| protocol->IInternetProtocolInfo_iface.lpVtbl = &MimeHtmlProtocolInfoVtbl; | |
| protocol->ref = 1; | |
| protocol->outer_unk = outer ? outer : &protocol->IUnknown_inner; | |
| protocol->location = NULL; | |
| protocol->stream = NULL; | |
| protocol->sink = NULL; | |
| *obj = &protocol->IUnknown_inner; | |
| return S_OK; | |
| } |