From e007edf0286e9095a80a1696d71e03f2b7e90d5a Mon Sep 17 00:00:00 2001 From: Amine Khaldi Date: Sun, 5 Mar 2017 21:37:58 +0000 Subject: [PATCH] [INETCOMM_WINETEST] Sync with Wine Staging 2.2. CORE-12823 svn path=/trunk/; revision=74107 --- rostests/winetests/inetcomm/CMakeLists.txt | 2 +- rostests/winetests/inetcomm/mimeole.c | 935 ++++++++++++++++++++- 2 files changed, 912 insertions(+), 25 deletions(-) diff --git a/rostests/winetests/inetcomm/CMakeLists.txt b/rostests/winetests/inetcomm/CMakeLists.txt index a9c70a80bd8..648a066f260 100644 --- a/rostests/winetests/inetcomm/CMakeLists.txt +++ b/rostests/winetests/inetcomm/CMakeLists.txt @@ -8,5 +8,5 @@ list(APPEND SOURCE add_executable(inetcomm_winetest ${SOURCE}) set_module_type(inetcomm_winetest win32cui) -add_importlibs(inetcomm_winetest inetcomm oleaut32 ole32 msvcrt kernel32) +add_importlibs(inetcomm_winetest inetcomm oleaut32 ole32 urlmon msvcrt kernel32) add_rostests_file(TARGET inetcomm_winetest) diff --git a/rostests/winetests/inetcomm/mimeole.c b/rostests/winetests/inetcomm/mimeole.c index ce1e4af5a71..b11e4883114 100644 --- a/rostests/winetests/inetcomm/mimeole.c +++ b/rostests/winetests/inetcomm/mimeole.c @@ -27,11 +27,47 @@ #include "ocidl.h" #include "mimeole.h" +#include "wininet.h" #include #include "wine/test.h" +#define DEFINE_EXPECT(func) \ + static BOOL expect_ ## func = FALSE, called_ ## func = FALSE + +#define SET_EXPECT(func) \ + expect_ ## func = TRUE + +#define CHECK_EXPECT(func) \ + do { \ + ok(expect_ ##func, "unexpected call " #func "\n"); \ + expect_ ## func = FALSE; \ + called_ ## func = TRUE; \ + }while(0) + +#define CHECK_EXPECT2(func) \ + do { \ + ok(expect_ ##func, "unexpected call " #func "\n"); \ + called_ ## func = TRUE; \ + }while(0) + +#define CHECK_CALLED(func) \ + do { \ + ok(called_ ## func, "expected " #func "\n"); \ + expect_ ## func = called_ ## func = FALSE; \ + }while(0) + +DEFINE_EXPECT(Stream_Read); +DEFINE_EXPECT(Stream_Stat); +DEFINE_EXPECT(Stream_Seek); +DEFINE_EXPECT(Stream_Seek_END); +DEFINE_EXPECT(GetBindInfo); +DEFINE_EXPECT(ReportProgress_MIMETYPEAVAILABLE); +DEFINE_EXPECT(ReportProgress_CACHEFILENAMEAVAILABLE); +DEFINE_EXPECT(ReportData); +DEFINE_EXPECT(ReportResult); + static const char msg1[] = "MIME-Version: 1.0\r\n" "Content-Type: multipart/mixed;\r\n" @@ -60,6 +96,43 @@ static const char msg1[] = "More stuff\r\n" "--------------1.5.0.6--\r\n"; +static const char mhtml_page1[] = + "MIME-Version: 1.0\r\n" + "Content-Type: multipart/related; type:=\"text/html\"; boundary=\"----=_NextPart_000_00\"\r\n" + "\r\n" + "------=_NextPart_000_00\r\n" + "Content-Type: text/html; charset=\"Windows-1252\"\r\n" + "Content-Transfer-Encoding: quoted-printable\r\n" + "\r\n" + "\r\n" + "------=_NextPart_000_00\r\n" + "Content-Type: Image/Jpeg\r\n" + "Content-Transfer-Encoding: base64\r\n" + "Content-Location: http://winehq.org/mhtmltest.html\r\n" + "\r\n\t\t\t\tVGVzdA==\r\n\r\n" + "------=_NextPart_000_00--"; + +static WCHAR *a2w(const char *str) +{ + WCHAR *ret; + int len; + + if(!str) + return NULL; + + len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0); + ret = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR)); + MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len); + return ret; +} + +static int strcmp_wa(const WCHAR *strw, const char *stra) +{ + WCHAR buf[512]; + MultiByteToWideChar(CP_ACP, 0, stra, -1, buf, sizeof(buf)/sizeof(WCHAR)); + return lstrcmpW(strw, buf); +} + static void test_CreateVirtualStream(void) { HRESULT hr; @@ -82,6 +155,36 @@ static void test_CreateSecurity(void) IMimeSecurity_Release(sec); } +static IStream *create_stream_from_string(const char *data) +{ + LARGE_INTEGER off; + IStream *stream; + HRESULT hr; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &stream); + ok(hr == S_OK, "ret %08x\n", hr); + + hr = IStream_Write(stream, data, strlen(data), NULL); + ok(hr == S_OK, "Write failed: %08x\n", hr); + + off.QuadPart = 0; + hr = IStream_Seek(stream, off, STREAM_SEEK_SET, NULL); + ok(hr == S_OK, "Seek failed: %08x\n", hr); + + return stream; +} + +#define test_current_encoding(a,b) _test_current_encoding(__LINE__,a,b) +static void _test_current_encoding(unsigned line, IMimeBody *mime_body, ENCODINGTYPE encoding) +{ + ENCODINGTYPE current_encoding; + HRESULT hres; + + hres = IMimeBody_GetCurrentEncoding(mime_body, ¤t_encoding); + ok_(__FILE__,line)(hres == S_OK, "GetCurrentEncoding failed: %08x\n", hres); + ok_(__FILE__,line)(current_encoding == encoding, "encoding = %d, expected %d\n", current_encoding, encoding); +} + static void test_CreateBody(void) { HRESULT hr; @@ -90,7 +193,6 @@ static void test_CreateBody(void) IStream *in; LARGE_INTEGER off; ULARGE_INTEGER pos; - ENCODINGTYPE enc; ULONG count, found_param, i; MIMEPARAMINFO *param_info; IMimeAllocator *alloc; @@ -103,19 +205,13 @@ static void test_CreateBody(void) ok(hr == MIME_E_NO_DATA, "ret %08x\n", hr); ok(handle == NULL, "handle %p\n", handle); - hr = CreateStreamOnHGlobal(NULL, TRUE, &in); - ok(hr == S_OK, "ret %08x\n", hr); - IStream_Write(in, msg1, sizeof(msg1) - 1, NULL); - off.QuadPart = 0; - IStream_Seek(in, off, STREAM_SEEK_SET, NULL); + in = create_stream_from_string(msg1); /* Need to call InitNew before Load otherwise Load crashes with native inetcomm */ hr = IMimeBody_InitNew(body); ok(hr == S_OK, "ret %08x\n", hr); - hr = IMimeBody_GetCurrentEncoding(body, &enc); - ok(hr == S_OK, "ret %08x\n", hr); - ok(enc == IET_7BIT, "encoding %d\n", enc); + test_current_encoding(body, IET_7BIT); hr = IMimeBody_Load(body, in); ok(hr == S_OK, "ret %08x\n", hr); @@ -137,9 +233,7 @@ static void test_CreateBody(void) hr = IMimeBody_IsContentType(body, "text", "plain"); todo_wine ok(hr == S_OK, "ret %08x\n", hr); - hr = IMimeBody_GetCurrentEncoding(body, &enc); - ok(hr == S_OK, "ret %08x\n", hr); - ok(enc == IET_8BIT, "encoding %d\n", enc); + test_current_encoding(body, IET_8BIT); memset(&offsets, 0xcc, sizeof(offsets)); hr = IMimeBody_GetOffsets(body, &offsets); @@ -197,6 +291,288 @@ static void test_CreateBody(void) IMimeBody_Release(body); } +typedef struct { + IStream IStream_iface; + LONG ref; + unsigned pos; +} TestStream; + +static inline TestStream *impl_from_IStream(IStream *iface) +{ + return CONTAINING_RECORD(iface, TestStream, IStream_iface); +} + +static HRESULT WINAPI Stream_QueryInterface(IStream *iface, REFIID riid, void **ppv) +{ + if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_ISequentialStream, riid) || IsEqualGUID(&IID_IStream, riid)) { + *ppv = iface; + return S_OK; + } + + ok(0, "unexpected call %s\n", wine_dbgstr_guid(riid)); + *ppv = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI Stream_AddRef(IStream *iface) +{ + TestStream *This = impl_from_IStream(iface); + return InterlockedIncrement(&This->ref); +} + +static ULONG WINAPI Stream_Release(IStream *iface) +{ + TestStream *This = impl_from_IStream(iface); + return InterlockedDecrement(&This->ref); +} + +static HRESULT WINAPI Stream_Read(IStream *iface, void *pv, ULONG cb, ULONG *pcbRead) +{ + TestStream *This = impl_from_IStream(iface); + BYTE *output = pv; + unsigned i; + + CHECK_EXPECT(Stream_Read); + + for(i = 0; i < cb; i++) + output[i] = '0' + This->pos++; + *pcbRead = i; + return S_OK; +} + +static HRESULT WINAPI Stream_Write(IStream *iface, const void *pv, ULONG cb, ULONG *pcbWritten) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static DWORD expect_seek_pos; + +static HRESULT WINAPI Stream_Seek(IStream *iface, LARGE_INTEGER dlibMove, DWORD dwOrigin, + ULARGE_INTEGER *plibNewPosition) +{ + TestStream *This = impl_from_IStream(iface); + + if(dwOrigin == STREAM_SEEK_END) { + CHECK_EXPECT(Stream_Seek_END); + ok(dlibMove.QuadPart == expect_seek_pos, "unexpected seek pos %u\n", dlibMove.u.LowPart); + if(plibNewPosition) + plibNewPosition->QuadPart = 10; + return S_OK; + } + + CHECK_EXPECT(Stream_Seek); + + ok(dlibMove.QuadPart == expect_seek_pos, "unexpected seek pos %u\n", dlibMove.u.LowPart); + ok(dwOrigin == STREAM_SEEK_SET, "dwOrigin = %d\n", dwOrigin); + This->pos = dlibMove.QuadPart; + if(plibNewPosition) + plibNewPosition->QuadPart = This->pos; + return S_OK; +} + +static HRESULT WINAPI Stream_SetSize(IStream *iface, ULARGE_INTEGER libNewSize) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI Stream_CopyTo(IStream *iface, IStream *pstm, ULARGE_INTEGER cb, + ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI Stream_Commit(IStream *iface, DWORD grfCommitFlags) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI Stream_Revert(IStream *iface) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI Stream_LockRegion(IStream *iface, ULARGE_INTEGER libOffset, + ULARGE_INTEGER cb, DWORD dwLockType) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI Stream_UnlockRegion(IStream *iface, + ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI Stream_Stat(IStream *iface, STATSTG *pstatstg, DWORD dwStatFlag) +{ + CHECK_EXPECT(Stream_Stat); + ok(dwStatFlag == STATFLAG_NONAME, "dwStatFlag = %x\n", dwStatFlag); + return E_NOTIMPL; +} + +static HRESULT WINAPI Stream_Clone(IStream *iface, IStream **ppstm) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static /* const */ IStreamVtbl StreamVtbl = { + Stream_QueryInterface, + Stream_AddRef, + Stream_Release, + Stream_Read, + Stream_Write, + Stream_Seek, + Stream_SetSize, + Stream_CopyTo, + Stream_Commit, + Stream_Revert, + Stream_LockRegion, + Stream_UnlockRegion, + Stream_Stat, + Stream_Clone +}; + +static TestStream *create_test_stream(void) +{ + TestStream *stream; + stream = HeapAlloc(GetProcessHeap(), 0, sizeof(*stream)); + stream->IStream_iface.lpVtbl = &StreamVtbl; + stream->ref = 1; + stream->pos = 0; + return stream; +} + +#define test_stream_read(a,b,c,d) _test_stream_read(__LINE__,a,b,c,d) +static void _test_stream_read(unsigned line, IStream *stream, HRESULT exhres, const char *exdata, unsigned read_size) +{ + ULONG read = 0xdeadbeed, exread = strlen(exdata); + char buf[1024]; + HRESULT hres; + + if(read_size == -1) + read_size = sizeof(buf)-1; + + hres = IStream_Read(stream, buf, read_size, &read); + ok_(__FILE__,line)(hres == exhres, "Read returned %08x, expected %08x\n", hres, exhres); + ok_(__FILE__,line)(read == exread, "unexpected read size %u, expected %u\n", read, exread); + buf[read] = 0; + ok_(__FILE__,line)(read == exread && !memcmp(buf, exdata, read), "unexpected data %s\n", buf); +} + +static void test_SetData(void) +{ + IStream *stream, *stream2; + TestStream *test_stream; + IMimeBody *body; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_IMimeBody, NULL, CLSCTX_INPROC_SERVER, &IID_IMimeBody, (void**)&body); + ok(hr == S_OK, "ret %08x\n", hr); + + /* Need to call InitNew before Load otherwise Load crashes with native inetcomm */ + hr = IMimeBody_InitNew(body); + ok(hr == S_OK, "ret %08x\n", hr); + + stream = create_stream_from_string(msg1); + hr = IMimeBody_Load(body, stream); + ok(hr == S_OK, "ret %08x\n", hr); + IStream_Release(stream); + + test_stream = create_test_stream(); + hr = IMimeBody_SetData(body, IET_BINARY, "text", "plain", &IID_IStream, &test_stream->IStream_iface); + + ok(hr == S_OK, "ret %08x\n", hr); + hr = IMimeBody_IsContentType(body, "text", "plain"); + todo_wine + ok(hr == S_OK, "ret %08x\n", hr); + + test_current_encoding(body, IET_BINARY); + + SET_EXPECT(Stream_Stat); + SET_EXPECT(Stream_Seek_END); + hr = IMimeBody_GetData(body, IET_BINARY, &stream); + CHECK_CALLED(Stream_Stat); + CHECK_CALLED(Stream_Seek_END); + ok(hr == S_OK, "GetData failed %08x\n", hr); + ok(stream != &test_stream->IStream_iface, "unexpected stream\n"); + + SET_EXPECT(Stream_Seek); + SET_EXPECT(Stream_Read); + test_stream_read(stream, S_OK, "012", 3); + CHECK_CALLED(Stream_Seek); + CHECK_CALLED(Stream_Read); + + SET_EXPECT(Stream_Stat); + SET_EXPECT(Stream_Seek_END); + hr = IMimeBody_GetData(body, IET_BINARY, &stream2); + CHECK_CALLED(Stream_Stat); + CHECK_CALLED(Stream_Seek_END); + ok(hr == S_OK, "GetData failed %08x\n", hr); + ok(stream2 != stream, "unexpected stream\n"); + + SET_EXPECT(Stream_Seek); + SET_EXPECT(Stream_Read); + test_stream_read(stream2, S_OK, "01", 2); + CHECK_CALLED(Stream_Seek); + CHECK_CALLED(Stream_Read); + + expect_seek_pos = 3; + SET_EXPECT(Stream_Seek); + SET_EXPECT(Stream_Read); + test_stream_read(stream, S_OK, "345", 3); + CHECK_CALLED(Stream_Seek); + CHECK_CALLED(Stream_Read); + + IStream_Release(stream); + IStream_Release(stream2); + IStream_Release(&test_stream->IStream_iface); + + stream = create_stream_from_string(" \t\r\n|}~YWJj ZGV|}~mZw== \t"); /* "abcdefg" in base64 obscured by invalid chars */ + hr = IMimeBody_SetData(body, IET_BASE64, "text", "plain", &IID_IStream, stream); + IStream_Release(stream); + ok(hr == S_OK, "SetData failed: %08x\n", hr); + + test_current_encoding(body, IET_BASE64); + + hr = IMimeBody_GetData(body, IET_BINARY, &stream); + ok(hr == S_OK, "GetData failed %08x\n", hr); + + test_stream_read(stream, S_OK, "abc", 3); + test_stream_read(stream, S_OK, "defg", -1); + + IStream_Release(stream); + + hr = IMimeBody_GetData(body, IET_BASE64, &stream); + ok(hr == S_OK, "GetData failed %08x\n", hr); + + test_stream_read(stream, S_OK, " \t\r", 3); + IStream_Release(stream); + + stream = create_stream_from_string(" =3d=3D\"one\" \t=\r\ntw= o=\nx3\n=34\r\n5"); + hr = IMimeBody_SetData(body, IET_QP, "text", "plain", &IID_IStream, stream); + IStream_Release(stream); + ok(hr == S_OK, "SetData failed: %08x\n", hr); + + test_current_encoding(body, IET_QP); + + hr = IMimeBody_GetData(body, IET_BINARY, &stream); + ok(hr == S_OK, "GetData failed %08x\n", hr); + + test_stream_read(stream, S_OK, " ==\"one\" \ttw=o=3\n4\r\n5", -1); + + IStream_Release(stream); + + IMimeBody_Release(body); +} + static void test_Allocator(void) { HRESULT hr; @@ -212,7 +588,6 @@ static void test_CreateMessage(void) HRESULT hr; IMimeMessage *msg; IStream *stream; - LARGE_INTEGER pos; LONG ref; HBODY hbody, hbody2; IMimeBody *body; @@ -230,10 +605,7 @@ static void test_CreateMessage(void) hr = MimeOleCreateMessage(NULL, &msg); ok(hr == S_OK, "ret %08x\n", hr); - CreateStreamOnHGlobal(NULL, TRUE, &stream); - IStream_Write(stream, msg1, sizeof(msg1) - 1, NULL); - pos.QuadPart = 0; - IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL); + stream = create_stream_from_string(msg1); hr = IMimeMessage_Load(msg, stream); ok(hr == S_OK, "ret %08x\n", hr); @@ -340,7 +712,6 @@ static void test_CreateMessage(void) ok(count == 2, "got %d\n", count); if(count == 2) { - ENCODINGTYPE encoding; IMimeBody *attachment; PROPVARIANT prop; @@ -352,9 +723,7 @@ static void test_CreateMessage(void) hr = IMimeBody_IsContentType(attachment, "multipart", NULL); ok(hr == S_FALSE, "ret %08x\n", hr); - hr = IMimeBody_GetCurrentEncoding(attachment, &encoding); - ok(hr == S_OK, "ret %08x\n", hr); - todo_wine ok(encoding == IET_8BIT, "ret %d\n", encoding); + test_current_encoding(attachment, IET_8BIT); prop.vt = VT_LPSTR; hr = IMimeBody_GetProp(attachment, "Content-Transfer-Encoding", 0, &prop); @@ -375,9 +744,7 @@ static void test_CreateMessage(void) hr = IMimeBody_IsContentType(attachment, "multipart", NULL); ok(hr == S_FALSE, "ret %08x\n", hr); - hr = IMimeBody_GetCurrentEncoding(attachment, &encoding); - ok(hr == S_OK, "ret %08x\n", hr); - todo_wine ok(encoding == IET_7BIT, "ret %d\n", encoding); + test_current_encoding(attachment, IET_7BIT); prop.vt = VT_LPSTR; hr = IMimeBody_GetProp(attachment, "Content-Transfer-Encoding", 0, &prop); @@ -411,6 +778,60 @@ static void test_CreateMessage(void) IStream_Release(stream); } +static void test_mhtml_message(void) +{ + IMimeMessage *mime_message; + IMimeBody *mime_body; + HBODY *body_list; + IStream *stream; + ULONG count; + HRESULT hres; + + hres = MimeOleCreateMessage(NULL, &mime_message); + ok(hres == S_OK, "MimeOleCreateMessage failed: %08x\n", hres); + + stream = create_stream_from_string(mhtml_page1); + hres = IMimeMessage_Load(mime_message, stream); + IStream_Release(stream); + ok(hres == S_OK, "Load failed: %08x\n", hres); + + hres = IMimeMessage_CountBodies(mime_message, HBODY_ROOT, TRUE, &count); + ok(hres == S_OK, "CountBodies failed: %08x\n", hres); + ok(count == 3, "got %d\n", count); + + hres = IMimeMessage_GetAttachments(mime_message, &count, &body_list); + ok(hres == S_OK, "GetAttachments failed: %08x\n", hres); + ok(count == 2, "count = %u\n", count); + + hres = IMimeMessage_BindToObject(mime_message, body_list[0], &IID_IMimeBody, (void**)&mime_body); + ok(hres == S_OK, "BindToObject failed: %08x\n", hres); + + hres = IMimeBody_GetData(mime_body, IET_BINARY, &stream); + ok(hres == S_OK, "GetData failed: %08x\n", hres); + test_stream_read(stream, S_OK, "", -1); + IStream_Release(stream); + + test_current_encoding(mime_body, IET_QP); + + IMimeBody_Release(mime_body); + + hres = IMimeMessage_BindToObject(mime_message, body_list[1], &IID_IMimeBody, (void**)&mime_body); + ok(hres == S_OK, "BindToObject failed: %08x\n", hres); + + test_current_encoding(mime_body, IET_BASE64); + + hres = IMimeBody_GetData(mime_body, IET_BINARY, &stream); + ok(hres == S_OK, "GetData failed: %08x\n", hres); + test_stream_read(stream, S_OK, "Test", -1); + IStream_Release(stream); + + IMimeBody_Release(mime_body); + + CoTaskMemFree(body_list); + + IMimeMessage_Release(mime_message); +} + static void test_MessageSetProp(void) { static const char topic[] = "wine topic"; @@ -760,12 +1181,475 @@ static void test_MimeOleGetPropertySchema(void) IMimePropertySchema_Release(schema); } +typedef struct { + const char *url; + const char *content; + const char *mime; + const char *data; +} mhtml_binding_test_t; + +static const mhtml_binding_test_t binding_tests[] = { + { + "mhtml:file://%s", + mhtml_page1, + "text/html", + "" + }, + { + "mhtml:file://%s!http://winehq.org/mhtmltest.html", + mhtml_page1, + "Image/Jpeg", + "Test" + } +}; + +static const mhtml_binding_test_t *current_binding_test; +static IInternetProtocol *current_binding_protocol; + +static HRESULT WINAPI BindInfo_QueryInterface(IInternetBindInfo *iface, REFIID riid, void **ppv) +{ + if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IInternetBindInfo, riid)) { + *ppv = iface; + return S_OK; + } + + *ppv = NULL; + ok(0, "unexpected riid %s\n", wine_dbgstr_guid(riid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI BindInfo_AddRef(IInternetBindInfo *iface) +{ + return 2; +} + +static ULONG WINAPI BindInfo_Release(IInternetBindInfo *iface) +{ + return 1; +} + +static HRESULT WINAPI BindInfo_GetBindInfo(IInternetBindInfo *iface, DWORD *grfBINDF, BINDINFO *pbindinfo) +{ + CHECK_EXPECT(GetBindInfo); + + ok(grfBINDF != NULL, "grfBINDF == NULL\n"); + ok(pbindinfo != NULL, "pbindinfo == NULL\n"); + ok(pbindinfo->cbSize == sizeof(BINDINFO), "wrong size of pbindinfo: %d\n", pbindinfo->cbSize); + + *grfBINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA | BINDF_FROMURLMON | BINDF_NEEDFILE; + return S_OK; +} + +static HRESULT WINAPI BindInfo_GetBindString(IInternetBindInfo *iface, ULONG ulStringType, LPOLESTR *ppwzStr, + ULONG cEl, ULONG *pcElFetched) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static IInternetBindInfoVtbl InternetBindInfoVtbl = { + BindInfo_QueryInterface, + BindInfo_AddRef, + BindInfo_Release, + BindInfo_GetBindInfo, + BindInfo_GetBindString +}; + +static IInternetBindInfo bind_info = { + &InternetBindInfoVtbl +}; + +static HRESULT WINAPI ServiceProvider_QueryInterface(IServiceProvider *iface, REFIID riid, void **ppv) +{ + ok(0, "unexpected call %s\n", wine_dbgstr_guid(riid)); + *ppv = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI ServiceProvider_AddRef(IServiceProvider *iface) +{ + return 2; +} + +static ULONG WINAPI ServiceProvider_Release(IServiceProvider *iface) +{ + return 1; +} + +static HRESULT WINAPI ServiceProvider_QueryService(IServiceProvider *iface, REFGUID guidService, + REFIID riid, void **ppv) +{ + if(IsEqualGUID(&CLSID_MimeEdit, guidService)) { + *ppv = NULL; + return E_NOINTERFACE; + } + + ok(0, "unexpected service %s\n", wine_dbgstr_guid(guidService)); + return E_FAIL; +} + +static /* const */ IServiceProviderVtbl ServiceProviderVtbl = { + ServiceProvider_QueryInterface, + ServiceProvider_AddRef, + ServiceProvider_Release, + ServiceProvider_QueryService +}; + +static IServiceProvider service_provider = { &ServiceProviderVtbl }; + +static HRESULT WINAPI ProtocolSink_QueryInterface(IInternetProtocolSink *iface, REFIID riid, void **ppv) +{ + if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IInternetProtocolSink, riid)) { + *ppv = iface; + return S_OK; + } + + if(IsEqualGUID(&IID_IServiceProvider, riid)) { + *ppv = &service_provider; + return S_OK; + } + + *ppv = NULL; + ok(0, "unexpected riid %s\n", wine_dbgstr_guid(riid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI ProtocolSink_AddRef(IInternetProtocolSink *iface) +{ + return 2; +} + +static ULONG WINAPI ProtocolSink_Release(IInternetProtocolSink *iface) +{ + return 1; +} + +static HRESULT WINAPI ProtocolSink_Switch(IInternetProtocolSink *iface, PROTOCOLDATA *pProtocolData) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI ProtocolSink_ReportProgress(IInternetProtocolSink *iface, ULONG ulStatusCode, + const WCHAR *szStatusText) +{ + switch(ulStatusCode) { + case BINDSTATUS_MIMETYPEAVAILABLE: + CHECK_EXPECT(ReportProgress_MIMETYPEAVAILABLE); + ok(!strcmp_wa(szStatusText, current_binding_test->mime), "status text %s\n", wine_dbgstr_w(szStatusText)); + return S_OK; + case BINDSTATUS_CACHEFILENAMEAVAILABLE: + CHECK_EXPECT(ReportProgress_CACHEFILENAMEAVAILABLE); + return S_OK; + default: + ok(0, "unexpected call %u %s\n", ulStatusCode, wine_dbgstr_w(szStatusText)); + } + + return E_NOTIMPL; +} + +static HRESULT WINAPI ProtocolSink_ReportData(IInternetProtocolSink *iface, DWORD grfBSCF, ULONG ulProgress, + ULONG ulProgressMax) +{ + char buf[1024]; + DWORD read; + HRESULT hres; + + CHECK_EXPECT(ReportData); + + ok(!ulProgress, "ulProgress = %u\n", ulProgress); + ok(ulProgress == ulProgressMax, "ulProgress != ulProgressMax\n"); + ok(grfBSCF == (BSCF_FIRSTDATANOTIFICATION | BSCF_INTERMEDIATEDATANOTIFICATION + | BSCF_LASTDATANOTIFICATION | BSCF_DATAFULLYAVAILABLE | BSCF_AVAILABLEDATASIZEUNKNOWN), + "grcf = %08x\n", grfBSCF); + + hres = IInternetProtocol_Read(current_binding_protocol, buf, sizeof(buf), &read); + ok(hres == S_OK, "Read failed: %08x\n", hres); + buf[read] = 0; + ok(!strcmp(buf, current_binding_test->data), "unexpected data: %s\n", buf); + + hres = IInternetProtocol_Read(current_binding_protocol, buf, sizeof(buf), &read); + ok(hres == S_FALSE, "Read failed: %08x\n", hres); + return S_OK; +} + +static HRESULT WINAPI ProtocolSink_ReportResult(IInternetProtocolSink *iface, HRESULT hrResult, DWORD dwError, + LPCWSTR szResult) +{ + CHECK_EXPECT(ReportResult); + ok(hrResult == S_OK, "hrResult = %08x\n", hrResult); + ok(!dwError, "dwError = %u\n", dwError); + ok(!szResult, "szResult = %s\n", wine_dbgstr_w(szResult)); + return S_OK; +} + +static IInternetProtocolSinkVtbl InternetProtocolSinkVtbl = { + ProtocolSink_QueryInterface, + ProtocolSink_AddRef, + ProtocolSink_Release, + ProtocolSink_Switch, + ProtocolSink_ReportProgress, + ProtocolSink_ReportData, + ProtocolSink_ReportResult +}; + +static IInternetProtocolSink protocol_sink = { &InternetProtocolSinkVtbl }; + +static void test_mhtml_protocol_binding(const mhtml_binding_test_t *test) +{ + char file_name[MAX_PATH+32], *p, urla[INTERNET_MAX_URL_LENGTH]; + WCHAR test_url[INTERNET_MAX_URL_LENGTH]; + IInternetProtocol *protocol; + IUnknown *unk; + HRESULT hres; + HANDLE file; + DWORD size; + + p = file_name + GetCurrentDirectoryA(sizeof(file_name), file_name); + *p++ = '\\'; + strcpy(p, "winetest.mht"); + + file = CreateFileA(file_name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + ok(file != INVALID_HANDLE_VALUE, "CreateFile failed\n"); + + WriteFile(file, test->content, strlen(test->content), &size, NULL); + CloseHandle(file); + + sprintf(urla, test->url, file_name); + MultiByteToWideChar(CP_ACP, 0, urla, -1, test_url, sizeof(test_url)/sizeof(WCHAR)); + + hres = CoCreateInstance(&CLSID_IMimeHtmlProtocol, NULL, CLSCTX_INPROC_SERVER, &IID_IInternetProtocol, (void**)&protocol); + ok(hres == S_OK, "Could not create protocol handler: %08x\n", hres); + + hres = IInternetProtocol_QueryInterface(protocol, &IID_IInternetProtocolEx, (void**)&unk); + ok(hres == E_NOINTERFACE, "Could get IInternetProtocolEx\n"); + + current_binding_test = test; + current_binding_protocol = protocol; + + SET_EXPECT(GetBindInfo); + SET_EXPECT(ReportProgress_MIMETYPEAVAILABLE); + SET_EXPECT(ReportProgress_CACHEFILENAMEAVAILABLE); + SET_EXPECT(ReportData); + SET_EXPECT(ReportResult); + hres = IInternetProtocol_Start(protocol, test_url, &protocol_sink, &bind_info, 0, 0); + ok(hres == S_OK, "Start failed: %08x\n", hres); + CHECK_CALLED(GetBindInfo); + CHECK_CALLED(ReportProgress_MIMETYPEAVAILABLE); + todo_wine CHECK_CALLED(ReportProgress_CACHEFILENAMEAVAILABLE); + CHECK_CALLED(ReportData); + CHECK_CALLED(ReportResult); + + IInternetProtocol_Release(protocol); + ok(DeleteFileA("winetest.mht"), "DeleteFile failed: %u\n", GetLastError()); +} + +static const struct { + const char *base_url; + const char *relative_url; + const char *expected_result; + BOOL todo; +} combine_tests[] = { + { + "mhtml:file:///c:/dir/test.mht", "http://test.org", + "mhtml:file:///c:/dir/test.mht!x-usc:http://test.org" + }, { + "mhtml:file:///c:/dir/test.mht", "3D\"http://test.org\"", + "mhtml:file:///c:/dir/test.mht!x-usc:3D\"http://test.org\"" + }, { + "mhtml:file:///c:/dir/test.mht", "123abc", + "mhtml:file:///c:/dir/test.mht!x-usc:123abc" + }, { + "mhtml:file:///c:/dir/test.mht!x-usc:http://test.org", "123abc", + "mhtml:file:///c:/dir/test.mht!x-usc:123abc" + }, { + "MhtMl:file:///c:/dir/test.mht!x-usc:http://test.org/dir/dir2/file.html", "../..", + "mhtml:file:///c:/dir/test.mht!x-usc:../.." + }, {"mhtml:file:///c:/dir/test.mht!x-usc:file:///c:/dir/dir2/file.html", "../..", + "mhtml:file:///c:/dir/test.mht!x-usc:../.." + }, { + "mhtml:file:///c:/dir/test.mht!x-usc:http://test.org", "", + "mhtml:file:///c:/dir/test.mht" + }, { + "mhtml:file:///c:/dir/test.mht!x-usc:http://test.org", "mhtml:file:///d:/file.html", + "file:///d:/file.html", TRUE + }, { + "mhtml:file:///c:/dir/test.mht!x-usc:http://test.org", "mhtml:file:///c:/dir2/test.mht!x-usc:http://test.org", + "mhtml:file:///c:/dir2/test.mht!x-usc:http://test.org", TRUE + }, { + "mhtml:file:///c:/dir/test.mht!http://test.org", "123abc", + "mhtml:file:///c:/dir/test.mht!x-usc:123abc" + }, { + "mhtml:file:///c:/dir/test.mht!http://test.org", "", + "mhtml:file:///c:/dir/test.mht" + } +}; + +static void test_mhtml_protocol_info(void) +{ + WCHAR *base_url, *relative_url, combined_url[INTERNET_MAX_URL_LENGTH]; + IInternetProtocolInfo *protocol_info; + DWORD combined_len; + unsigned i, exlen; + HRESULT hres; + + static const WCHAR http_url[] = {'h','t','t','p',':','/','/','t','e','s','t','.','o','r','g',0}; + + hres = CoCreateInstance(&CLSID_IMimeHtmlProtocol, NULL, CLSCTX_INPROC_SERVER, + &IID_IInternetProtocolInfo, (void**)&protocol_info); + ok(hres == S_OK, "Could not create protocol info: %08x\n", hres); + + for(i = 0; i < sizeof(combine_tests)/sizeof(*combine_tests); i++) { + base_url = a2w(combine_tests[i].base_url); + relative_url = a2w(combine_tests[i].relative_url); + + combined_len = 0xdeadbeef; + hres = IInternetProtocolInfo_CombineUrl(protocol_info, base_url, relative_url, ICU_BROWSER_MODE, + combined_url, sizeof(combined_url)/sizeof(WCHAR), &combined_len, 0); + todo_wine_if(combine_tests[i].todo) + ok(hres == S_OK, "[%u] CombineUrl failed: %08x\n", i, hres); + if(SUCCEEDED(hres)) { + exlen = strlen(combine_tests[i].expected_result); + ok(combined_len == exlen, "[%u] combined len is %u, expected %u\n", i, combined_len, exlen); + ok(!strcmp_wa(combined_url, combine_tests[i].expected_result), "[%u] combined URL is %s, expected %s\n", + i, wine_dbgstr_w(combined_url), combine_tests[i].expected_result); + + combined_len = 0xdeadbeef; + hres = IInternetProtocolInfo_CombineUrl(protocol_info, base_url, relative_url, ICU_BROWSER_MODE, + combined_url, exlen, &combined_len, 0); + ok(hres == E_FAIL, "[%u] CombineUrl returned: %08x\n", i, hres); + ok(!combined_len, "[%u] combined_len = %u\n", i, combined_len); + } + + HeapFree(GetProcessHeap(), 0, base_url); + HeapFree(GetProcessHeap(), 0, relative_url); + } + + hres = IInternetProtocolInfo_CombineUrl(protocol_info, http_url, http_url, ICU_BROWSER_MODE, + combined_url, sizeof(combined_url)/sizeof(WCHAR), &combined_len, 0); + ok(hres == E_FAIL, "CombineUrl failed: %08x\n", hres); + + IInternetProtocolInfo_Release(protocol_info); +} + +static HRESULT WINAPI outer_QueryInterface(IUnknown *iface, REFIID riid, void **ppv) +{ + ok(0, "unexpected call\n"); + return E_NOINTERFACE; +} + +static ULONG WINAPI outer_AddRef(IUnknown *iface) +{ + return 2; +} + +static ULONG WINAPI outer_Release(IUnknown *iface) +{ + return 1; +} + +static /* const */ IUnknownVtbl outer_vtbl = { + outer_QueryInterface, + outer_AddRef, + outer_Release +}; + +static BOOL broken_mhtml_resolver; + +static void test_mhtml_protocol(void) +{ + IUnknown outer = { &outer_vtbl }; + IClassFactory *class_factory; + IUnknown *unk, *unk2; + unsigned i; + HRESULT hres; + + /* test class factory */ + hres = CoGetClassObject(&CLSID_IMimeHtmlProtocol, CLSCTX_INPROC_SERVER, NULL, &IID_IUnknown, (void**)&unk); + ok(hres == S_OK, "CoGetClassObject failed: %08x\n", hres); + + hres = IUnknown_QueryInterface(unk, &IID_IInternetProtocolInfo, (void**)&unk2); + ok(hres == E_NOINTERFACE, "IInternetProtocolInfo supported\n"); + + hres = IUnknown_QueryInterface(unk, &IID_IClassFactory, (void**)&class_factory); + ok(hres == S_OK, "Could not get IClassFactory iface: %08x\n", hres); + IUnknown_Release(unk); + + hres = IClassFactory_CreateInstance(class_factory, &outer, &IID_IUnknown, (void**)&unk); + ok(hres == S_OK, "CreateInstance returned: %08x\n", hres); + hres = IUnknown_QueryInterface(unk, &IID_IInternetProtocol, (void**)&unk2); + ok(hres == S_OK, "Could not get IInternetProtocol iface: %08x\n", hres); + IUnknown_Release(unk2); + IUnknown_Release(unk); + + hres = IClassFactory_CreateInstance(class_factory, (IUnknown*)0xdeadbeef, &IID_IInternetProtocol, (void**)&unk2); + ok(hres == CLASS_E_NOAGGREGATION, "CreateInstance returned: %08x\n", hres); + + IClassFactory_Release(class_factory); + + if(!broken_mhtml_resolver) + test_mhtml_protocol_info(); + + for(i = 0; i < sizeof(binding_tests)/sizeof(*binding_tests); i++) + test_mhtml_protocol_binding(binding_tests + i); +} + +static void test_MimeOleObjectFromMoniker(void) +{ + IMoniker *mon, *new_mon; + WCHAR *mhtml_url, *url; + IBindCtx *bind_ctx; + IUnknown *unk; + unsigned i; + HRESULT hres; + + static const struct { + const char *url; + const char *mhtml_url; + } tests[] = { + {"file:///x:\\dir\\file.mht", "mhtml:file://x:\\dir\\file.mht"}, + {"file:///x:/dir/file.mht", "mhtml:file://x:\\dir\\file.mht"}, + {"http://www.winehq.org/index.html?query#hash", "mhtml:http://www.winehq.org/index.html?query#hash"}, + {"../test.mht", "mhtml:../test.mht"} + }; + + for(i = 0; i < sizeof(tests)/sizeof(*tests); i++) { + url = a2w(tests[i].url); + hres = CreateURLMoniker(NULL, url, &mon); + ok(hres == S_OK, "CreateURLMoniker failed: %08x\n", hres); + HeapFree(GetProcessHeap(), 0, url); + + hres = CreateBindCtx(0, &bind_ctx); + ok(hres == S_OK, "CreateBindCtx failed: %08x\n", hres); + + hres = MimeOleObjectFromMoniker(0, mon, bind_ctx, &IID_IUnknown, (void**)&unk, &new_mon); + ok(hres == S_OK || broken(!i && hres == INET_E_RESOURCE_NOT_FOUND), "MimeOleObjectFromMoniker failed: %08x\n", hres); + IBindCtx_Release(bind_ctx); + if(hres == INET_E_RESOURCE_NOT_FOUND) { /* winxp */ + win_skip("Broken MHTML behaviour found. Skipping some tests.\n"); + broken_mhtml_resolver = TRUE; + return; + } + + hres = IMoniker_GetDisplayName(new_mon, NULL, NULL, &mhtml_url); + ok(hres == S_OK, "GetDisplayName failed: %08x\n", hres); + ok(!strcmp_wa(mhtml_url, tests[i].mhtml_url), "[%d] unexpected mhtml URL: %s\n", i, wine_dbgstr_w(mhtml_url)); + CoTaskMemFree(mhtml_url); + + IUnknown_Release(unk); + IMoniker_Release(new_mon); + IMoniker_Release(mon); + } +} + START_TEST(mimeole) { OleInitialize(NULL); test_CreateVirtualStream(); test_CreateSecurity(); test_CreateBody(); + test_SetData(); test_Allocator(); test_CreateMessage(); test_MessageSetProp(); @@ -774,5 +1658,8 @@ START_TEST(mimeole) test_BindToObject(); test_BodyDeleteProp(); test_MimeOleGetPropertySchema(); + test_mhtml_message(); + test_MimeOleObjectFromMoniker(); + test_mhtml_protocol(); OleUninitialize(); } -- 2.17.1