| /* |
| * 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; |
| } |