From 8e07a66b685962d6d23941ecf96510af9e4e23c2 Mon Sep 17 00:00:00 2001 From: Amine Khaldi Date: Sun, 5 Mar 2017 21:33:13 +0000 Subject: [PATCH] [INETCOMM] Sync with Wine Staging 2.2. CORE-12823 51e1a08 inetcomm: Return S_FALSE if no data is returned in IInternetProtocol::Read. 0672bfa inetcomm: Added support for decoding quoted-printable data. 07af9d0 inetcomm: Added mhtml binding tests. cf8e259 inetcomm: Added support for binding mhtml protocol handler. 3f644cb inetcomm: Added IInternetProtocolInfo::CombineUrl implementation. 37792a2 inetcomm: Added MimeOleObjectFromMoniker semi-stub implementation. 242cbb1 inetcomm: Added tests reading MHTML page as MIME message. eb0bd22 inetcomm: Removed unneeded type check in init_content_type. 56dc246 inetcomm: Read content encoding from MIME header. dec243f inetcomm: Don't include headers in data stream in create_sub_body. 3ccba10 inetcomm: Properly handle boundary in the beginning of body stream in create_body_offset_list. 9c01a94 inetcomm: Use relative seek in copy_headers_to_buf. 915acd6 inetcomm: Added support for decoding base64 in IMimeBody::GetData. 0b80666 inetcomm: Return a new instance of stream in IMimeBody::GetData. 393512d inetcomm: Don't restore base stream position in sub stream Read. bb5056e inetcomm: Moved sub_stream_t implementation before MimeBody implementation. a1d0c6d inetcomm: Added COM aggregation support to MimeHtmlProtocol object. fe3e5b9 inetcomm: Added IInternetProtocolInfo stub implementation. 6832273 inetcomm: Added MimeHtmlProtocol stub implementation. d5d9aa3 inetcomm: Register mhtml protocol handler. svn path=/trunk/; revision=74105 --- reactos/dll/win32/inetcomm/CMakeLists.txt | 3 +- reactos/dll/win32/inetcomm/inetcomm.idl | 7 + reactos/dll/win32/inetcomm/inetcomm.rc | 23 +- reactos/dll/win32/inetcomm/inetcomm_main.c | 18 +- reactos/dll/win32/inetcomm/inetcomm_private.h | 12 + .../inetcomm/{inetcomm.rgs => inetcomm_r.rgs} | 4 + reactos/dll/win32/inetcomm/mimeole.c | 946 +++++++++++------- reactos/dll/win32/inetcomm/protocol.c | 738 ++++++++++++++ reactos/media/doc/README.WINE | 2 +- 9 files changed, 1413 insertions(+), 340 deletions(-) rename reactos/dll/win32/inetcomm/{inetcomm.rgs => inetcomm_r.rgs} (93%) create mode 100644 reactos/dll/win32/inetcomm/protocol.c diff --git a/reactos/dll/win32/inetcomm/CMakeLists.txt b/reactos/dll/win32/inetcomm/CMakeLists.txt index fe13a15cda8..1f82a5c70ba 100644 --- a/reactos/dll/win32/inetcomm/CMakeLists.txt +++ b/reactos/dll/win32/inetcomm/CMakeLists.txt @@ -10,6 +10,7 @@ list(APPEND SOURCE mimeintl.c mimeole.c pop3transport.c + protocol.c smtptransport.c inetcomm_private.h) @@ -22,6 +23,6 @@ add_library(inetcomm SHARED set_module_type(inetcomm win32dll) target_link_libraries(inetcomm uuid wine) -add_importlibs(inetcomm ole32 oleaut32 ws2_32 user32 propsys msvcrt kernel32 ntdll) +add_importlibs(inetcomm ole32 oleaut32 ws2_32 user32 propsys urlmon msvcrt kernel32 ntdll) add_pch(inetcomm inetcomm_private.h SOURCE) add_cd_file(TARGET inetcomm DESTINATION reactos/system32 FOR all) diff --git a/reactos/dll/win32/inetcomm/inetcomm.idl b/reactos/dll/win32/inetcomm/inetcomm.idl index 03cd20abbac..ea4d716e718 100644 --- a/reactos/dll/win32/inetcomm/inetcomm.idl +++ b/reactos/dll/win32/inetcomm/inetcomm.idl @@ -124,3 +124,10 @@ coclass IPropFindRequest { interface IPropFindRequest; } uuid(ea678830-235d-11d2-a8b6-0000f8084f96) ] coclass IPropPatchRequest { interface IPropPatchRequest; } + +[ + helpstring("MHTML Asynchronous Pluggable Protocol Handler"), + threading(both), + uuid(05300401-bcbc-11d0-85e3-00c04fd85ab4) +] +coclass MimeHtmlProtocol { interface IInternetProtocol; } diff --git a/reactos/dll/win32/inetcomm/inetcomm.rc b/reactos/dll/win32/inetcomm/inetcomm.rc index 55b575e6637..f42fe0d5c0b 100644 --- a/reactos/dll/win32/inetcomm/inetcomm.rc +++ b/reactos/dll/win32/inetcomm/inetcomm.rc @@ -1 +1,22 @@ -1 WINE_REGISTRY "inetcomm.rgs" +/* + * Copyright 2016 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 + */ + +/* @makedep: inetcomm.rgs */ +1 WINE_REGISTRY inetcomm.rgs + +2 WINE_REGISTRY inetcomm_r.rgs diff --git a/reactos/dll/win32/inetcomm/inetcomm_main.c b/reactos/dll/win32/inetcomm/inetcomm_main.c index 6e4d935824b..4dec2adf894 100644 --- a/reactos/dll/win32/inetcomm/inetcomm_main.c +++ b/reactos/dll/win32/inetcomm/inetcomm_main.c @@ -74,7 +74,9 @@ static HRESULT WINAPI cf_QueryInterface( IClassFactory *iface, REFIID riid, LPVO return S_OK; } - FIXME("interface %s not implemented\n", debugstr_guid(riid)); + if (!IsEqualGUID(riid, &IID_IInternetProtocolInfo)) + FIXME("interface %s not implemented\n", debugstr_guid(riid)); + *ppobj = NULL; return E_NOINTERFACE; } @@ -99,13 +101,20 @@ static HRESULT WINAPI cf_CreateInstance( IClassFactory *iface, LPUNKNOWN pOuter, *ppobj = NULL; + if (pOuter && !IsEqualGUID(&IID_IUnknown, riid)) + return CLASS_E_NOAGGREGATION; + r = This->create_object( pOuter, (LPVOID*) &punk ); if (FAILED(r)) return r; + if (IsEqualGUID(&IID_IUnknown, riid)) { + *ppobj = punk; + return S_OK; + } + r = IUnknown_QueryInterface( punk, riid, ppobj ); IUnknown_Release( punk ); - return r; } @@ -129,6 +138,7 @@ static cf mime_allocator_cf = { { &cf_vtbl }, MimeAllocator_create }; static cf mime_message_cf = { { &cf_vtbl }, MimeMessage_create }; static cf mime_security_cf = { { &cf_vtbl }, MimeSecurity_create }; static cf virtual_stream_cf = { { &cf_vtbl }, VirtualStream_create }; +static cf mhtml_protocol_cf = { { &cf_vtbl }, MimeHtmlProtocol_create }; /*********************************************************************** * DllGetClassObject (INETCOMM.@) @@ -171,6 +181,10 @@ HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID iid, LPVOID *ppv) { cf = &virtual_stream_cf.IClassFactory_iface; } + else if( IsEqualCLSID( rclsid, &CLSID_IMimeHtmlProtocol )) + { + cf = &mhtml_protocol_cf.IClassFactory_iface; + } if ( !cf ) { diff --git a/reactos/dll/win32/inetcomm/inetcomm_private.h b/reactos/dll/win32/inetcomm/inetcomm_private.h index b713a68a761..cff28ddaaf1 100644 --- a/reactos/dll/win32/inetcomm/inetcomm_private.h +++ b/reactos/dll/win32/inetcomm/inetcomm_private.h @@ -38,6 +38,7 @@ #include #include +#include #include WINE_DEFAULT_DEBUG_CHANNEL(inetcomm); @@ -95,6 +96,7 @@ HRESULT MimeAllocator_create(IUnknown *outer, void **obj) DECLSPEC_HIDDEN; HRESULT MimeMessage_create(IUnknown *outer, void **obj) DECLSPEC_HIDDEN; HRESULT MimeSecurity_create(IUnknown *outer, void **obj) DECLSPEC_HIDDEN; HRESULT VirtualStream_create(IUnknown *outer, void **obj) DECLSPEC_HIDDEN; +HRESULT MimeHtmlProtocol_create(IUnknown *outer, void **obj) DECLSPEC_HIDDEN; HRESULT MimeInternational_Construct(IMimeInternational **internat) DECLSPEC_HIDDEN; @@ -102,4 +104,14 @@ HRESULT SMTPTransportCF_Create(REFIID riid, LPVOID *ppv) DECLSPEC_HIDDEN; HRESULT IMAPTransportCF_Create(REFIID riid, LPVOID *ppv) DECLSPEC_HIDDEN; HRESULT POP3TransportCF_Create(REFIID riid, LPVOID *ppv) DECLSPEC_HIDDEN; +static inline void * __WINE_ALLOC_SIZE(1) heap_alloc(size_t len) +{ + return HeapAlloc(GetProcessHeap(), 0, len); +} + +static inline BOOL heap_free(void *mem) +{ + return HeapFree(GetProcessHeap(), 0, mem); +} + #endif /* _INETCOMM_PRIVATE_H_ */ diff --git a/reactos/dll/win32/inetcomm/inetcomm.rgs b/reactos/dll/win32/inetcomm/inetcomm_r.rgs similarity index 93% rename from reactos/dll/win32/inetcomm/inetcomm.rgs rename to reactos/dll/win32/inetcomm/inetcomm_r.rgs index ea6da6c3331..116ba35195c 100644 --- a/reactos/dll/win32/inetcomm/inetcomm.rgs +++ b/reactos/dll/win32/inetcomm/inetcomm_r.rgs @@ -65,5 +65,9 @@ HKCR { InprocServer32 = s '%MODULE%' { val ThreadingModel = s 'Both' } } + '{05300401-BCBC-11D0-85E3-00C04FD85AB4}' = s 'MHTML Asynchronous Pluggable Protocol Handler' + { + InprocServer32 = s '%MODULE%' { val ThreadingModel = s 'Both' } + } } } diff --git a/reactos/dll/win32/inetcomm/mimeole.c b/reactos/dll/win32/inetcomm/mimeole.c index 954dcc0ef76..7a3d4690b3b 100644 --- a/reactos/dll/win32/inetcomm/mimeole.c +++ b/reactos/dll/win32/inetcomm/mimeole.c @@ -150,6 +150,298 @@ typedef struct MimeBody BODYOFFSETS body_offsets; } MimeBody; +typedef struct +{ + IStream IStream_iface; + LONG ref; + IStream *base; + ULARGE_INTEGER pos, start, length; +} sub_stream_t; + +static inline sub_stream_t *impl_from_IStream(IStream *iface) +{ + return CONTAINING_RECORD(iface, sub_stream_t, IStream_iface); +} + +static HRESULT WINAPI sub_stream_QueryInterface(IStream *iface, REFIID riid, void **ppv) +{ + sub_stream_t *This = impl_from_IStream(iface); + + TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv); + *ppv = NULL; + + if(IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_ISequentialStream) || + IsEqualIID(riid, &IID_IStream)) + { + IStream_AddRef(iface); + *ppv = iface; + return S_OK; + } + return E_NOINTERFACE; +} + +static ULONG WINAPI sub_stream_AddRef(IStream *iface) +{ + sub_stream_t *This = impl_from_IStream(iface); + LONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + return ref; +} + +static ULONG WINAPI sub_stream_Release(IStream *iface) +{ + sub_stream_t *This = impl_from_IStream(iface); + LONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + if(!ref) + { + IStream_Release(This->base); + HeapFree(GetProcessHeap(), 0, This); + } + return ref; +} + +static HRESULT WINAPI sub_stream_Read( + IStream* iface, + void *pv, + ULONG cb, + ULONG *pcbRead) +{ + sub_stream_t *This = impl_from_IStream(iface); + HRESULT hr; + LARGE_INTEGER tmp_pos; + + TRACE("(%p, %d, %p)\n", pv, cb, pcbRead); + + tmp_pos.QuadPart = This->pos.QuadPart + This->start.QuadPart; + IStream_Seek(This->base, tmp_pos, STREAM_SEEK_SET, NULL); + + if(This->pos.QuadPart + cb > This->length.QuadPart) + cb = This->length.QuadPart - This->pos.QuadPart; + + hr = IStream_Read(This->base, pv, cb, pcbRead); + + This->pos.QuadPart += *pcbRead; + + return hr; +} + +static HRESULT WINAPI sub_stream_Write( + IStream* iface, + const void *pv, + ULONG cb, + ULONG *pcbWritten) +{ + FIXME("stub\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI sub_stream_Seek( + IStream* iface, + LARGE_INTEGER dlibMove, + DWORD dwOrigin, + ULARGE_INTEGER *plibNewPosition) +{ + sub_stream_t *This = impl_from_IStream(iface); + LARGE_INTEGER new_pos; + + TRACE("(%08x.%08x, %x, %p)\n", dlibMove.u.HighPart, dlibMove.u.LowPart, dwOrigin, plibNewPosition); + + switch(dwOrigin) + { + case STREAM_SEEK_SET: + new_pos = dlibMove; + break; + case STREAM_SEEK_CUR: + new_pos.QuadPart = This->pos.QuadPart + dlibMove.QuadPart; + break; + case STREAM_SEEK_END: + new_pos.QuadPart = This->length.QuadPart + dlibMove.QuadPart; + break; + default: + return STG_E_INVALIDFUNCTION; + } + + if(new_pos.QuadPart < 0) new_pos.QuadPart = 0; + else if(new_pos.QuadPart > This->length.QuadPart) new_pos.QuadPart = This->length.QuadPart; + + This->pos.QuadPart = new_pos.QuadPart; + + if(plibNewPosition) *plibNewPosition = This->pos; + return S_OK; +} + +static HRESULT WINAPI sub_stream_SetSize( + IStream* iface, + ULARGE_INTEGER libNewSize) +{ + FIXME("stub\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI sub_stream_CopyTo( + IStream* iface, + IStream *pstm, + ULARGE_INTEGER cb, + ULARGE_INTEGER *pcbRead, + ULARGE_INTEGER *pcbWritten) +{ + HRESULT hr = S_OK; + BYTE tmpBuffer[128]; + ULONG bytesRead, bytesWritten, copySize; + ULARGE_INTEGER totalBytesRead; + ULARGE_INTEGER totalBytesWritten; + + TRACE("(%p)->(%p, %d, %p, %p)\n", iface, pstm, cb.u.LowPart, pcbRead, pcbWritten); + + totalBytesRead.QuadPart = 0; + totalBytesWritten.QuadPart = 0; + + while ( cb.QuadPart > 0 ) + { + if ( cb.QuadPart >= sizeof(tmpBuffer) ) + copySize = sizeof(tmpBuffer); + else + copySize = cb.u.LowPart; + + hr = IStream_Read(iface, tmpBuffer, copySize, &bytesRead); + if (FAILED(hr)) break; + + totalBytesRead.QuadPart += bytesRead; + + if (bytesRead) + { + hr = IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten); + if (FAILED(hr)) break; + totalBytesWritten.QuadPart += bytesWritten; + } + + if (bytesRead != copySize) + cb.QuadPart = 0; + else + cb.QuadPart -= bytesRead; + } + + if (pcbRead) pcbRead->QuadPart = totalBytesRead.QuadPart; + if (pcbWritten) pcbWritten->QuadPart = totalBytesWritten.QuadPart; + + return hr; +} + +static HRESULT WINAPI sub_stream_Commit( + IStream* iface, + DWORD grfCommitFlags) +{ + FIXME("stub\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI sub_stream_Revert( + IStream* iface) +{ + FIXME("stub\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI sub_stream_LockRegion( + IStream* iface, + ULARGE_INTEGER libOffset, + ULARGE_INTEGER cb, + DWORD dwLockType) +{ + FIXME("stub\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI sub_stream_UnlockRegion( + IStream* iface, + ULARGE_INTEGER libOffset, + ULARGE_INTEGER cb, + DWORD dwLockType) +{ + FIXME("stub\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI sub_stream_Stat( + IStream* iface, + STATSTG *pstatstg, + DWORD grfStatFlag) +{ + sub_stream_t *This = impl_from_IStream(iface); + FIXME("(%p)->(%p, %08x)\n", This, pstatstg, grfStatFlag); + memset(pstatstg, 0, sizeof(*pstatstg)); + pstatstg->cbSize = This->length; + return S_OK; +} + +static HRESULT WINAPI sub_stream_Clone( + IStream* iface, + IStream **ppstm) +{ + FIXME("stub\n"); + return E_NOTIMPL; +} + +static struct IStreamVtbl sub_stream_vtbl = +{ + sub_stream_QueryInterface, + sub_stream_AddRef, + sub_stream_Release, + sub_stream_Read, + sub_stream_Write, + sub_stream_Seek, + sub_stream_SetSize, + sub_stream_CopyTo, + sub_stream_Commit, + sub_stream_Revert, + sub_stream_LockRegion, + sub_stream_UnlockRegion, + sub_stream_Stat, + sub_stream_Clone +}; + +static HRESULT create_sub_stream(IStream *stream, ULARGE_INTEGER start, ULARGE_INTEGER length, IStream **out) +{ + sub_stream_t *This; + + *out = NULL; + This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); + if(!This) return E_OUTOFMEMORY; + + This->IStream_iface.lpVtbl = &sub_stream_vtbl; + This->ref = 1; + This->start = start; + This->length = length; + This->pos.QuadPart = 0; + IStream_AddRef(stream); + This->base = stream; + + *out = &This->IStream_iface; + return S_OK; +} + +static HRESULT get_stream_size(IStream *stream, ULARGE_INTEGER *size) +{ + STATSTG statstg = {NULL}; + LARGE_INTEGER zero; + HRESULT hres; + + hres = IStream_Stat(stream, &statstg, STATFLAG_NONAME); + if(SUCCEEDED(hres)) { + *size = statstg.cbSize; + return S_OK; + } + + zero.QuadPart = 0; + return IStream_Seek(stream, zero, STREAM_SEEK_END, size); +} + static inline MimeBody *impl_from_IMimeBody(IMimeBody *iface) { return CONTAINING_RECORD(iface, MimeBody, IMimeBody_iface); @@ -224,8 +516,8 @@ static HRESULT copy_headers_to_buf(IStream *stm, char **ptr) if(new_end - last_end == 2) { LARGE_INTEGER off; - off.QuadPart = new_end; - IStream_Seek(stm, off, STREAM_SEEK_SET, NULL); + off.QuadPart = (LONGLONG)new_end - offset; + IStream_Seek(stm, off, STREAM_SEEK_CUR, NULL); buf[new_end] = '\0'; done = TRUE; } @@ -438,12 +730,6 @@ static void init_content_type(MimeBody *body, header_t *header) char *slash; DWORD len; - if(header->prop->id != PID_HDR_CNTTYPE) - { - ERR("called with header %s\n", header->prop->name); - return; - } - slash = strchr(header->value.u.pszVal, '/'); if(!slash) { @@ -457,6 +743,22 @@ static void init_content_type(MimeBody *body, header_t *header) body->content_sub_type = strdupA(slash + 1); } +static void init_content_encoding(MimeBody *body, header_t *header) +{ + const char *encoding = header->value.u.pszVal; + + if(!strcasecmp(encoding, "base64")) + body->encoding = IET_BASE64; + else if(!strcasecmp(encoding, "quoted-printable")) + body->encoding = IET_QP; + else if(!strcasecmp(encoding, "7bit")) + body->encoding = IET_7BIT; + else if(!strcasecmp(encoding, "8bit")) + body->encoding = IET_8BIT; + else + FIXME("unknown encoding %s\n", debugstr_a(encoding)); +} + static HRESULT parse_headers(MimeBody *body, IStream *stm) { char *header_buf, *cur_header_ptr; @@ -472,8 +774,14 @@ static HRESULT parse_headers(MimeBody *body, IStream *stm) read_value(header, &cur_header_ptr); list_add_tail(&body->headers, &header->entry); - if(header->prop->id == PID_HDR_CNTTYPE) + switch(header->prop->id) { + case PID_HDR_CNTTYPE: init_content_type(body, header); + break; + case PID_HDR_CNTXFER: + init_content_encoding(body, header); + break; + } } HeapFree(GetProcessHeap(), 0, header_buf); @@ -1207,17 +1515,206 @@ static HRESULT WINAPI MimeBody_GetDataHere( return E_NOTIMPL; } +static const signed char base64_decode_table[] = +{ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20 */ + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30 */ + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40 */ + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50 */ + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60 */ + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70 */ +}; + +static HRESULT decode_base64(IStream *input, IStream **ret_stream) +{ + const unsigned char *ptr, *end; + unsigned char buf[1024]; + LARGE_INTEGER pos; + unsigned char *ret; + unsigned char in[4]; + IStream *output; + DWORD size; + int n = 0; + HRESULT hres; + + pos.QuadPart = 0; + hres = IStream_Seek(input, pos, STREAM_SEEK_SET, NULL); + if(FAILED(hres)) + return hres; + + hres = CreateStreamOnHGlobal(NULL, TRUE, &output); + if(FAILED(hres)) + return hres; + + while(1) { + hres = IStream_Read(input, buf, sizeof(buf), &size); + if(FAILED(hres) || !size) + break; + + ptr = ret = buf; + end = buf + size; + + while(1) { + /* skip invalid chars */ + while(ptr < end && + (*ptr >= sizeof(base64_decode_table)/sizeof(*base64_decode_table) + || base64_decode_table[*ptr] == -1)) + ptr++; + if(ptr == end) + break; + + in[n++] = base64_decode_table[*ptr++]; + switch(n) { + case 2: + *ret++ = in[0] << 2 | in[1] >> 4; + continue; + case 3: + *ret++ = in[1] << 4 | in[2] >> 2; + continue; + case 4: + *ret++ = ((in[2] << 6) & 0xc0) | in[3]; + n = 0; + } + } + + if(ret > buf) { + hres = IStream_Write(output, buf, ret - buf, NULL); + if(FAILED(hres)) + break; + } + } + + if(SUCCEEDED(hres)) + hres = IStream_Seek(output, pos, STREAM_SEEK_SET, NULL); + if(FAILED(hres)) { + IStream_Release(output); + return hres; + } + + *ret_stream = output; + return S_OK; +} + +static int hex_digit(char c) +{ + if('0' <= c && c <= '9') + return c - '0'; + if('A' <= c && c <= 'F') + return c - 'A' + 10; + if('a' <= c && c <= 'f') + return c - 'a' + 10; + return -1; +} + +static HRESULT decode_qp(IStream *input, IStream **ret_stream) +{ + const unsigned char *ptr, *end; + unsigned char *ret, prev = 0; + unsigned char buf[1024]; + LARGE_INTEGER pos; + IStream *output; + DWORD size; + int n = -1; + HRESULT hres; + + pos.QuadPart = 0; + hres = IStream_Seek(input, pos, STREAM_SEEK_SET, NULL); + if(FAILED(hres)) + return hres; + + hres = CreateStreamOnHGlobal(NULL, TRUE, &output); + if(FAILED(hres)) + return hres; + + while(1) { + hres = IStream_Read(input, buf, sizeof(buf), &size); + if(FAILED(hres) || !size) + break; + + ptr = ret = buf; + end = buf + size; + + while(ptr < end) { + unsigned char byte = *ptr++; + + switch(n) { + case -1: + if(byte == '=') + n = 0; + else + *ret++ = byte; + continue; + case 0: + prev = byte; + n = 1; + continue; + case 1: + if(prev != '\r' || byte != '\n') { + int h1 = hex_digit(prev), h2 = hex_digit(byte); + if(h1 != -1 && h2 != -1) + *ret++ = (h1 << 4) | h2; + else + *ret++ = '='; + } + n = -1; + continue; + } + } + + if(ret > buf) { + hres = IStream_Write(output, buf, ret - buf, NULL); + if(FAILED(hres)) + break; + } + } + + if(SUCCEEDED(hres)) + hres = IStream_Seek(output, pos, STREAM_SEEK_SET, NULL); + if(FAILED(hres)) { + IStream_Release(output); + return hres; + } + + *ret_stream = output; + return S_OK; +} + static HRESULT WINAPI MimeBody_GetData( IMimeBody* iface, ENCODINGTYPE ietEncoding, IStream** ppStream) { MimeBody *This = impl_from_IMimeBody(iface); - FIXME("(%p)->(%d, %p). Ignoring encoding type.\n", This, ietEncoding, ppStream); + ULARGE_INTEGER start, size; + HRESULT hres; - *ppStream = This->data; - IStream_AddRef(*ppStream); - return S_OK; + TRACE("(%p)->(%d %p)\n", This, ietEncoding, ppStream); + + if(This->encoding != ietEncoding) { + switch(This->encoding) { + case IET_BASE64: + hres = decode_base64(This->data, ppStream); + break; + case IET_QP: + hres = decode_qp(This->data, ppStream); + break; + default: + FIXME("Decoding %d is not supported.\n", This->encoding); + hres = S_FALSE; + } + if(ietEncoding != IET_BINARY) + FIXME("Encoding %d is not supported.\n", ietEncoding); + if(hres != S_FALSE) + return hres; + } + + start.QuadPart = 0; + hres = get_stream_size(This->data, &size); + if(SUCCEEDED(hres)) + hres = create_sub_stream(This->data, start, size, ppStream); + return hres; } static HRESULT WINAPI MimeBody_SetData( @@ -1410,291 +1907,6 @@ HRESULT MimeBody_create(IUnknown *outer, void **ppv) } } - - -typedef struct -{ - IStream IStream_iface; - LONG ref; - IStream *base; - ULARGE_INTEGER pos, start, length; -} sub_stream_t; - -static inline sub_stream_t *impl_from_IStream(IStream *iface) -{ - return CONTAINING_RECORD(iface, sub_stream_t, IStream_iface); -} - -static HRESULT WINAPI sub_stream_QueryInterface(IStream *iface, REFIID riid, void **ppv) -{ - sub_stream_t *This = impl_from_IStream(iface); - - TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv); - *ppv = NULL; - - if(IsEqualIID(riid, &IID_IUnknown) || - IsEqualIID(riid, &IID_ISequentialStream) || - IsEqualIID(riid, &IID_IStream)) - { - IStream_AddRef(iface); - *ppv = iface; - return S_OK; - } - return E_NOINTERFACE; -} - -static ULONG WINAPI sub_stream_AddRef(IStream *iface) -{ - sub_stream_t *This = impl_from_IStream(iface); - LONG ref = InterlockedIncrement(&This->ref); - - TRACE("(%p) ref=%d\n", This, ref); - - return ref; -} - -static ULONG WINAPI sub_stream_Release(IStream *iface) -{ - sub_stream_t *This = impl_from_IStream(iface); - LONG ref = InterlockedDecrement(&This->ref); - - TRACE("(%p) ref=%d\n", This, ref); - - if(!ref) - { - IStream_Release(This->base); - HeapFree(GetProcessHeap(), 0, This); - } - return ref; -} - -static HRESULT WINAPI sub_stream_Read( - IStream* iface, - void *pv, - ULONG cb, - ULONG *pcbRead) -{ - sub_stream_t *This = impl_from_IStream(iface); - HRESULT hr; - ULARGE_INTEGER base_pos; - LARGE_INTEGER tmp_pos; - - TRACE("(%p, %d, %p)\n", pv, cb, pcbRead); - - tmp_pos.QuadPart = 0; - IStream_Seek(This->base, tmp_pos, STREAM_SEEK_CUR, &base_pos); - tmp_pos.QuadPart = This->pos.QuadPart + This->start.QuadPart; - IStream_Seek(This->base, tmp_pos, STREAM_SEEK_SET, NULL); - - if(This->pos.QuadPart + cb > This->length.QuadPart) - cb = This->length.QuadPart - This->pos.QuadPart; - - hr = IStream_Read(This->base, pv, cb, pcbRead); - - This->pos.QuadPart += *pcbRead; - - tmp_pos.QuadPart = base_pos.QuadPart; - IStream_Seek(This->base, tmp_pos, STREAM_SEEK_SET, NULL); - - return hr; -} - -static HRESULT WINAPI sub_stream_Write( - IStream* iface, - const void *pv, - ULONG cb, - ULONG *pcbWritten) -{ - FIXME("stub\n"); - return E_NOTIMPL; -} - -static HRESULT WINAPI sub_stream_Seek( - IStream* iface, - LARGE_INTEGER dlibMove, - DWORD dwOrigin, - ULARGE_INTEGER *plibNewPosition) -{ - sub_stream_t *This = impl_from_IStream(iface); - LARGE_INTEGER new_pos; - - TRACE("(%08x.%08x, %x, %p)\n", dlibMove.u.HighPart, dlibMove.u.LowPart, dwOrigin, plibNewPosition); - - switch(dwOrigin) - { - case STREAM_SEEK_SET: - new_pos = dlibMove; - break; - case STREAM_SEEK_CUR: - new_pos.QuadPart = This->pos.QuadPart + dlibMove.QuadPart; - break; - case STREAM_SEEK_END: - new_pos.QuadPart = This->length.QuadPart + dlibMove.QuadPart; - break; - default: - return STG_E_INVALIDFUNCTION; - } - - if(new_pos.QuadPart < 0) new_pos.QuadPart = 0; - else if(new_pos.QuadPart > This->length.QuadPart) new_pos.QuadPart = This->length.QuadPart; - - This->pos.QuadPart = new_pos.QuadPart; - - if(plibNewPosition) *plibNewPosition = This->pos; - return S_OK; -} - -static HRESULT WINAPI sub_stream_SetSize( - IStream* iface, - ULARGE_INTEGER libNewSize) -{ - FIXME("stub\n"); - return E_NOTIMPL; -} - -static HRESULT WINAPI sub_stream_CopyTo( - IStream* iface, - IStream *pstm, - ULARGE_INTEGER cb, - ULARGE_INTEGER *pcbRead, - ULARGE_INTEGER *pcbWritten) -{ - HRESULT hr = S_OK; - BYTE tmpBuffer[128]; - ULONG bytesRead, bytesWritten, copySize; - ULARGE_INTEGER totalBytesRead; - ULARGE_INTEGER totalBytesWritten; - - TRACE("(%p)->(%p, %d, %p, %p)\n", iface, pstm, cb.u.LowPart, pcbRead, pcbWritten); - - totalBytesRead.QuadPart = 0; - totalBytesWritten.QuadPart = 0; - - while ( cb.QuadPart > 0 ) - { - if ( cb.QuadPart >= sizeof(tmpBuffer) ) - copySize = sizeof(tmpBuffer); - else - copySize = cb.u.LowPart; - - hr = IStream_Read(iface, tmpBuffer, copySize, &bytesRead); - if (FAILED(hr)) break; - - totalBytesRead.QuadPart += bytesRead; - - if (bytesRead) - { - hr = IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten); - if (FAILED(hr)) break; - totalBytesWritten.QuadPart += bytesWritten; - } - - if (bytesRead != copySize) - cb.QuadPart = 0; - else - cb.QuadPart -= bytesRead; - } - - if (pcbRead) pcbRead->QuadPart = totalBytesRead.QuadPart; - if (pcbWritten) pcbWritten->QuadPart = totalBytesWritten.QuadPart; - - return hr; -} - -static HRESULT WINAPI sub_stream_Commit( - IStream* iface, - DWORD grfCommitFlags) -{ - FIXME("stub\n"); - return E_NOTIMPL; -} - -static HRESULT WINAPI sub_stream_Revert( - IStream* iface) -{ - FIXME("stub\n"); - return E_NOTIMPL; -} - -static HRESULT WINAPI sub_stream_LockRegion( - IStream* iface, - ULARGE_INTEGER libOffset, - ULARGE_INTEGER cb, - DWORD dwLockType) -{ - FIXME("stub\n"); - return E_NOTIMPL; -} - -static HRESULT WINAPI sub_stream_UnlockRegion( - IStream* iface, - ULARGE_INTEGER libOffset, - ULARGE_INTEGER cb, - DWORD dwLockType) -{ - FIXME("stub\n"); - return E_NOTIMPL; -} - -static HRESULT WINAPI sub_stream_Stat( - IStream* iface, - STATSTG *pstatstg, - DWORD grfStatFlag) -{ - sub_stream_t *This = impl_from_IStream(iface); - FIXME("(%p)->(%p, %08x)\n", This, pstatstg, grfStatFlag); - memset(pstatstg, 0, sizeof(*pstatstg)); - pstatstg->cbSize = This->length; - return S_OK; -} - -static HRESULT WINAPI sub_stream_Clone( - IStream* iface, - IStream **ppstm) -{ - FIXME("stub\n"); - return E_NOTIMPL; -} - -static struct IStreamVtbl sub_stream_vtbl = -{ - sub_stream_QueryInterface, - sub_stream_AddRef, - sub_stream_Release, - sub_stream_Read, - sub_stream_Write, - sub_stream_Seek, - sub_stream_SetSize, - sub_stream_CopyTo, - sub_stream_Commit, - sub_stream_Revert, - sub_stream_LockRegion, - sub_stream_UnlockRegion, - sub_stream_Stat, - sub_stream_Clone -}; - -static HRESULT create_sub_stream(IStream *stream, ULARGE_INTEGER start, ULARGE_INTEGER length, IStream **out) -{ - sub_stream_t *This; - - *out = NULL; - This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); - if(!This) return E_OUTOFMEMORY; - - This->IStream_iface.lpVtbl = &sub_stream_vtbl; - This->ref = 1; - This->start = start; - This->length = length; - This->pos.QuadPart = 0; - IStream_AddRef(stream); - This->base = stream; - - *out = &This->IStream_iface; - return S_OK; -} - - typedef struct body_t { struct list entry; @@ -1821,18 +2033,16 @@ typedef struct static HRESULT create_body_offset_list(IStream *stm, const char *boundary, struct list *body_offsets) { HRESULT hr; - DWORD read; + DWORD read, boundary_start; int boundary_len = strlen(boundary); - char *buf, *nl_boundary, *ptr, *overlap; + char *buf, *ptr, *overlap; DWORD start = 0, overlap_no; offset_entry_t *cur_body = NULL; + BOOL is_first_line = TRUE; ULARGE_INTEGER cur; LARGE_INTEGER zero; list_init(body_offsets); - nl_boundary = HeapAlloc(GetProcessHeap(), 0, 4 + boundary_len + 1); - memcpy(nl_boundary, "\r\n--", 4); - memcpy(nl_boundary + 4, boundary, boundary_len + 1); overlap_no = boundary_len + 5; @@ -1849,39 +2059,44 @@ static HRESULT create_body_offset_list(IStream *stm, const char *boundary, struc overlap[read] = '\0'; ptr = buf; - do { - ptr = strstr(ptr, nl_boundary); - if(ptr) - { - DWORD boundary_start = start + ptr - buf; - char *end = ptr + boundary_len + 4; - - if(*end == '\0' || *(end + 1) == '\0') + while(1) { + if(is_first_line) { + is_first_line = FALSE; + }else { + ptr = strstr(ptr, "\r\n"); + if(!ptr) break; + ptr += 2; + } - if(*end == '\r' && *(end + 1) == '\n') + boundary_start = start + ptr - buf; + + if(*ptr == '-' && *(ptr + 1) == '-' && !memcmp(ptr + 2, boundary, boundary_len)) { + ptr += boundary_len + 2; + + if(*ptr == '\r' && *(ptr + 1) == '\n') { + ptr += 2; if(cur_body) { - cur_body->offsets.cbBodyEnd = boundary_start; + cur_body->offsets.cbBodyEnd = boundary_start - 2; list_add_tail(body_offsets, &cur_body->entry); } cur_body = HeapAlloc(GetProcessHeap(), 0, sizeof(*cur_body)); - cur_body->offsets.cbBoundaryStart = boundary_start + 2; /* doesn't including the leading \r\n */ - cur_body->offsets.cbHeaderStart = boundary_start + boundary_len + 6; + cur_body->offsets.cbBoundaryStart = boundary_start; + cur_body->offsets.cbHeaderStart = start + ptr - buf; } - else if(*end == '-' && *(end + 1) == '-') + else if(*ptr == '-' && *(ptr + 1) == '-') { if(cur_body) { - cur_body->offsets.cbBodyEnd = boundary_start; + cur_body->offsets.cbBodyEnd = boundary_start - 2; list_add_tail(body_offsets, &cur_body->entry); goto end; } } - ptr = end + 2; } - } while(ptr); + } if(overlap == buf) /* 1st iteration */ { @@ -1897,26 +2112,33 @@ static HRESULT create_body_offset_list(IStream *stm, const char *boundary, struc } while(1); end: - HeapFree(GetProcessHeap(), 0, nl_boundary); HeapFree(GetProcessHeap(), 0, buf); return hr; } static body_t *create_sub_body(MimeMessage *msg, IStream *pStm, BODYOFFSETS *offset, body_t *parent) { + ULARGE_INTEGER start, length; MimeBody *mime_body; HRESULT hr; body_t *body; - ULARGE_INTEGER cur; - LARGE_INTEGER zero; + LARGE_INTEGER pos; + + pos.QuadPart = offset->cbHeaderStart; + IStream_Seek(pStm, pos, STREAM_SEEK_SET, NULL); mime_body = mimebody_create(); IMimeBody_Load(&mime_body->IMimeBody_iface, pStm); - zero.QuadPart = 0; - hr = IStream_Seek(pStm, zero, STREAM_SEEK_CUR, &cur); - offset->cbBodyStart = cur.u.LowPart + offset->cbHeaderStart; + + pos.QuadPart = 0; + hr = IStream_Seek(pStm, pos, STREAM_SEEK_CUR, &start); + offset->cbBodyStart = start.QuadPart; if (parent) MimeBody_set_offsets(mime_body, offset); - IMimeBody_SetData(&mime_body->IMimeBody_iface, IET_BINARY, NULL, NULL, &IID_IStream, pStm); + + length.QuadPart = offset->cbBodyEnd - offset->cbBodyStart; + create_sub_stream(pStm, start, length, (IStream**)&mime_body->data); + mime_body->data_iid = IID_IStream; + body = new_body_entry(mime_body, msg->next_index++, parent); if(IMimeBody_IsContentType(&mime_body->IMimeBody_iface, "multipart", NULL) == S_OK) @@ -1941,14 +2163,8 @@ static body_t *create_sub_body(MimeMessage *msg, IStream *pStm, BODYOFFSETS *off LIST_FOR_EACH_ENTRY_SAFE(cur, cursor2, &offset_list, offset_entry_t, entry) { body_t *sub_body; - IStream *sub_stream; - ULARGE_INTEGER start, length; - start.QuadPart = cur->offsets.cbHeaderStart; - length.QuadPart = cur->offsets.cbBodyEnd - cur->offsets.cbHeaderStart; - create_sub_stream(pStm, start, length, &sub_stream); - sub_body = create_sub_body(msg, sub_stream, &cur->offsets, body); - IStream_Release(sub_stream); + sub_body = create_sub_body(msg, pStm, &cur->offsets, body); list_add_tail(&body->children, &sub_body->entry); list_remove(&cur->entry); HeapFree(GetProcessHeap(), 0, cur); @@ -3421,10 +3637,70 @@ HRESULT WINAPI MimeGetAddressFormatW(REFIID riid, void *object, DWORD addr_type, return E_NOTIMPL; } +static HRESULT WINAPI mime_obj_QueryInterface(IUnknown *iface, REFIID riid, void **ppv) +{ + FIXME("(%s %p)\n", debugstr_guid(riid), ppv); + *ppv = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI mime_obj_AddRef(IUnknown *iface) +{ + TRACE("\n"); + return 2; +} + +static ULONG WINAPI mime_obj_Release(IUnknown *iface) +{ + TRACE("\n"); + return 1; +} + +static const IUnknownVtbl mime_obj_vtbl = { + mime_obj_QueryInterface, + mime_obj_AddRef, + mime_obj_Release +}; + +static IUnknown mime_obj = { &mime_obj_vtbl }; + HRESULT WINAPI MimeOleObjectFromMoniker(BINDF bindf, IMoniker *moniker, IBindCtx *binding, REFIID riid, void **out, IMoniker **moniker_new) { - FIXME("(0x%08x, %p, %p, %s, %p, %p) stub\n", bindf, moniker, binding, debugstr_guid(riid), out, moniker_new); + WCHAR *display_name, *mhtml_url; + size_t len; + HRESULT hres; - return E_NOTIMPL; + static const WCHAR mhtml_prefixW[] = {'m','h','t','m','l',':'}; + + WARN("(0x%08x, %p, %p, %s, %p, %p) semi-stub\n", bindf, moniker, binding, debugstr_guid(riid), out, moniker_new); + + if(!IsEqualGUID(&IID_IUnknown, riid)) { + FIXME("Unsupported riid %s\n", debugstr_guid(riid)); + return E_NOINTERFACE; + } + + hres = IMoniker_GetDisplayName(moniker, NULL, NULL, &display_name); + if(FAILED(hres)) + return hres; + + TRACE("display name %s\n", debugstr_w(display_name)); + + len = strlenW(display_name); + mhtml_url = heap_alloc((len+1)*sizeof(WCHAR) + sizeof(mhtml_prefixW)); + if(!mhtml_url) + return E_OUTOFMEMORY; + + memcpy(mhtml_url, mhtml_prefixW, sizeof(mhtml_prefixW)); + strcpyW(mhtml_url + sizeof(mhtml_prefixW)/sizeof(WCHAR), display_name); + HeapFree(GetProcessHeap(), 0, display_name); + + hres = CreateURLMoniker(NULL, mhtml_url, moniker_new); + heap_free(mhtml_url); + if(FAILED(hres)) + return hres; + + /* FIXME: We most likely should start binding here and return something more meaningful as mime object. */ + *out = &mime_obj; + return S_OK; } diff --git a/reactos/dll/win32/inetcomm/protocol.c b/reactos/dll/win32/inetcomm/protocol.c new file mode 100644 index 00000000000..6006e52a05b --- /dev/null +++ b/reactos/dll/win32/inetcomm/protocol.c @@ -0,0 +1,738 @@ +/* + * 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 + */ + +#include "inetcomm_private.h" + +#include + +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); + + This->sink = pOIProtSink; + IInternetProtocolSink_AddRef(This->sink); + + 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)) + 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; +} diff --git a/reactos/media/doc/README.WINE b/reactos/media/doc/README.WINE index 7a350e1fe29..37e5f435fb3 100644 --- a/reactos/media/doc/README.WINE +++ b/reactos/media/doc/README.WINE @@ -78,7 +78,7 @@ reactos/dll/win32/ieframe # Synced to WineStaging-2.2 reactos/dll/win32/imaadp32.acm # Synced to WineStaging-2.2 reactos/dll/win32/imagehlp # Synced to WineStaging-1.9.11 reactos/dll/win32/imm32 # Synced to Wine-1.7.27 -reactos/dll/win32/inetcomm # Synced to WineStaging-1.9.23 +reactos/dll/win32/inetcomm # Synced to WineStaging-2.2 reactos/dll/win32/inetmib1 # Synced to WineStaging-1.9.11 reactos/dll/win32/initpki # Synced to WineStaging-1.9.11 reactos/dll/win32/inseng # Synced to WineStaging-2.2