1 /*
2  * MimeOle tests
3  *
4  * Copyright 2007 Huw Davies
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #define COBJMACROS
22 #define NONAMELESSUNION
23 
24 #include "initguid.h"
25 #include "windows.h"
26 #include "ole2.h"
27 #include "ocidl.h"
28 
29 #include "mimeole.h"
30 #include "wininet.h"
31 
32 #include <stdio.h>
33 
34 #include "wine/test.h"
35 
36 #define DEFINE_EXPECT(func) \
37     static BOOL expect_ ## func = FALSE, called_ ## func = FALSE
38 
39 #define SET_EXPECT(func) \
40     expect_ ## func = TRUE
41 
42 #define CHECK_EXPECT(func) \
43     do { \
44         ok(expect_ ##func, "unexpected call " #func "\n"); \
45         expect_ ## func = FALSE; \
46         called_ ## func = TRUE; \
47     }while(0)
48 
49 #define CHECK_EXPECT2(func) \
50     do { \
51         ok(expect_ ##func, "unexpected call " #func  "\n"); \
52         called_ ## func = TRUE; \
53     }while(0)
54 
55 #define CHECK_CALLED(func) \
56     do { \
57         ok(called_ ## func, "expected " #func "\n"); \
58         expect_ ## func = called_ ## func = FALSE; \
59     }while(0)
60 
61 DEFINE_EXPECT(Stream_Read);
62 DEFINE_EXPECT(Stream_Stat);
63 DEFINE_EXPECT(Stream_Seek);
64 DEFINE_EXPECT(Stream_Seek_END);
65 DEFINE_EXPECT(GetBindInfo);
66 DEFINE_EXPECT(ReportProgress_MIMETYPEAVAILABLE);
67 DEFINE_EXPECT(ReportProgress_CACHEFILENAMEAVAILABLE);
68 DEFINE_EXPECT(ReportData);
69 DEFINE_EXPECT(ReportResult);
70 
71 static const char msg1[] =
72     "MIME-Version: 1.0\r\n"
73     "Content-Type: multipart/mixed;\r\n"
74     " boundary=\"------------1.5.0.6\";\r\n"
75     " stuff=\"du;nno\";\r\n"
76     " morestuff=\"so\\\\me\\\"thing\\\"\"\r\n"
77     "foo: bar\r\n"
78     "From: Huw Davies <huw@codeweavers.com>\r\n"
79     "From: Me <xxx@codeweavers.com>\r\n"
80     "To: wine-patches <wine-patches@winehq.org>\r\n"
81     "Cc: Huw Davies <huw@codeweavers.com>,\r\n"
82     "    \"Fred Bloggs\"   <fred@bloggs.com>\r\n"
83     "foo: baz\r\n"
84     "bar: fum\r\n"
85     "\r\n"
86     "This is a multi-part message in MIME format.\r\n"
87     "--------------1.5.0.6\r\n"
88     "Content-Type: text/plain; format=fixed; charset=UTF-8\r\n"
89     "Content-Transfer-Encoding: 8bit\r\n"
90     "\r\n"
91     "Stuff\r\n"
92     "--------------1.5.0.6\r\n"
93     "Content-Type: text/plain; charset=\"us-ascii\"\r\n"
94     "Content-Transfer-Encoding: 7bit\r\n"
95     "\r\n"
96     "More stuff\r\n"
97     "--------------1.5.0.6--\r\n";
98 
99 static const char mhtml_page1[] =
100     "MIME-Version: 1.0\r\n"
101     "Content-Type: multipart/related; type:=\"text/html\"; boundary=\"----=_NextPart_000_00\"\r\n"
102     "\r\n"
103     "------=_NextPart_000_00\r\n"
104     "Content-Type: text/html; charset=\"Windows-1252\"\r\n"
105     "Content-Transfer-Encoding: quoted-printable\r\n"
106     "\r\n"
107     "<HTML></HTML>\r\n"
108     "------=_NextPart_000_00\r\n"
109     "Content-Type: Image/Jpeg\r\n"
110     "Content-Transfer-Encoding: base64\r\n"
111     "Content-Location: http://winehq.org/mhtmltest.html\r\n"
112     "\r\n\t\t\t\tVGVzdA==\r\n\r\n"
113     "------=_NextPart_000_00--";
114 
115 static WCHAR *a2w(const char *str)
116 {
117     WCHAR *ret;
118     int len;
119 
120     if(!str)
121         return NULL;
122 
123     len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
124     ret = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
125     MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
126     return ret;
127 }
128 
129 static int strcmp_wa(const WCHAR *strw, const char *stra)
130 {
131     WCHAR buf[512];
132     MultiByteToWideChar(CP_ACP, 0, stra, -1, buf, sizeof(buf)/sizeof(WCHAR));
133     return lstrcmpW(strw, buf);
134 }
135 
136 static void test_CreateVirtualStream(void)
137 {
138     HRESULT hr;
139     IStream *pstm;
140 
141     hr = MimeOleCreateVirtualStream(&pstm);
142     ok(hr == S_OK, "ret %08x\n", hr);
143 
144     IStream_Release(pstm);
145 }
146 
147 static void test_CreateSecurity(void)
148 {
149     HRESULT hr;
150     IMimeSecurity *sec;
151 
152     hr = MimeOleCreateSecurity(&sec);
153     ok(hr == S_OK, "ret %08x\n", hr);
154 
155     IMimeSecurity_Release(sec);
156 }
157 
158 static IStream *create_stream_from_string(const char *data)
159 {
160     LARGE_INTEGER off;
161     IStream *stream;
162     HRESULT hr;
163 
164     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
165     ok(hr == S_OK, "ret %08x\n", hr);
166 
167     hr = IStream_Write(stream, data, strlen(data), NULL);
168     ok(hr == S_OK, "Write failed: %08x\n", hr);
169 
170     off.QuadPart = 0;
171     hr = IStream_Seek(stream, off, STREAM_SEEK_SET, NULL);
172     ok(hr == S_OK, "Seek failed: %08x\n", hr);
173 
174     return stream;
175 }
176 
177 #define test_current_encoding(a,b) _test_current_encoding(__LINE__,a,b)
178 static void _test_current_encoding(unsigned line, IMimeBody *mime_body, ENCODINGTYPE encoding)
179 {
180     ENCODINGTYPE current_encoding;
181     HRESULT hres;
182 
183     hres = IMimeBody_GetCurrentEncoding(mime_body, &current_encoding);
184     ok_(__FILE__,line)(hres == S_OK, "GetCurrentEncoding failed: %08x\n", hres);
185     ok_(__FILE__,line)(current_encoding == encoding, "encoding = %d, expected %d\n", current_encoding, encoding);
186 }
187 
188 static void test_CreateBody(void)
189 {
190     HRESULT hr;
191     IMimeBody *body;
192     HBODY handle = (void *)0xdeadbeef;
193     IStream *in;
194     LARGE_INTEGER off;
195     ULARGE_INTEGER pos;
196     ULONG count, found_param, i;
197     MIMEPARAMINFO *param_info;
198     IMimeAllocator *alloc;
199     BODYOFFSETS offsets;
200     CLSID clsid;
201 
202     hr = CoCreateInstance(&CLSID_IMimeBody, NULL, CLSCTX_INPROC_SERVER, &IID_IMimeBody, (void**)&body);
203     ok(hr == S_OK, "ret %08x\n", hr);
204 
205     hr = IMimeBody_GetClassID(body, NULL);
206     ok(hr == E_INVALIDARG, "ret %08x\n", hr);
207 
208     hr = IMimeBody_GetClassID(body, &clsid);
209     ok(hr == S_OK, "ret %08x\n", hr);
210     ok(IsEqualGUID(&clsid, &IID_IMimeBody), "got %s\n", wine_dbgstr_guid(&clsid));
211 
212     hr = IMimeBody_GetHandle(body, &handle);
213     ok(hr == MIME_E_NO_DATA, "ret %08x\n", hr);
214     ok(handle == NULL, "handle %p\n", handle);
215 
216     in = create_stream_from_string(msg1);
217 
218     /* Need to call InitNew before Load otherwise Load crashes with native inetcomm */
219     hr = IMimeBody_InitNew(body);
220     ok(hr == S_OK, "ret %08x\n", hr);
221 
222     test_current_encoding(body, IET_7BIT);
223 
224     hr = IMimeBody_Load(body, in);
225     ok(hr == S_OK, "ret %08x\n", hr);
226     off.QuadPart = 0;
227     IStream_Seek(in, off, STREAM_SEEK_CUR, &pos);
228     ok(pos.u.LowPart == 359, "pos %u\n", pos.u.LowPart);
229 
230     hr = IMimeBody_IsContentType(body, "multipart", "mixed");
231     ok(hr == S_OK, "ret %08x\n", hr);
232     hr = IMimeBody_IsContentType(body, "text", "plain");
233     ok(hr == S_FALSE, "ret %08x\n", hr);
234     hr = IMimeBody_IsContentType(body, NULL, "mixed");
235     ok(hr == S_OK, "ret %08x\n", hr);
236     hr = IMimeBody_IsType(body, IBT_EMPTY);
237     ok(hr == S_OK, "got %08x\n", hr);
238 
239     hr = IMimeBody_SetData(body, IET_8BIT, "text", "plain", &IID_IStream, in);
240     ok(hr == S_OK, "ret %08x\n", hr);
241     hr = IMimeBody_IsContentType(body, "text", "plain");
242     todo_wine
243         ok(hr == S_OK, "ret %08x\n", hr);
244     test_current_encoding(body, IET_8BIT);
245 
246     memset(&offsets, 0xcc, sizeof(offsets));
247     hr = IMimeBody_GetOffsets(body, &offsets);
248     ok(hr == MIME_E_NO_DATA, "ret %08x\n", hr);
249     ok(offsets.cbBoundaryStart == 0, "got %d\n", offsets.cbBoundaryStart);
250     ok(offsets.cbHeaderStart == 0, "got %d\n", offsets.cbHeaderStart);
251     ok(offsets.cbBodyStart == 0, "got %d\n", offsets.cbBodyStart);
252     ok(offsets.cbBodyEnd == 0, "got %d\n", offsets.cbBodyEnd);
253 
254     hr = IMimeBody_IsType(body, IBT_EMPTY);
255     ok(hr == S_FALSE, "got %08x\n", hr);
256 
257     hr = MimeOleGetAllocator(&alloc);
258     ok(hr == S_OK, "ret %08x\n", hr);
259 
260     hr = IMimeBody_GetParameters(body, "nothere", &count, &param_info);
261     ok(hr == MIME_E_NOT_FOUND, "ret %08x\n", hr);
262     ok(count == 0, "got %d\n", count);
263     ok(!param_info, "got %p\n", param_info);
264 
265     hr = IMimeBody_GetParameters(body, "bar", &count, &param_info);
266     ok(hr == S_OK, "ret %08x\n", hr);
267     ok(count == 0, "got %d\n", count);
268     ok(!param_info, "got %p\n", param_info);
269 
270     hr = IMimeBody_GetParameters(body, "Content-Type", &count, &param_info);
271     ok(hr == S_OK, "ret %08x\n", hr);
272     todo_wine  /* native adds a charset parameter */
273         ok(count == 4, "got %d\n", count);
274     ok(param_info != NULL, "got %p\n", param_info);
275 
276     found_param = 0;
277     for(i = 0; i < count; i++)
278     {
279         if(!strcmp(param_info[i].pszName, "morestuff"))
280         {
281             found_param++;
282             ok(!strcmp(param_info[i].pszData, "so\\me\"thing\""),
283                "got %s\n", param_info[i].pszData);
284         }
285         else if(!strcmp(param_info[i].pszName, "stuff"))
286         {
287             found_param++;
288             ok(!strcmp(param_info[i].pszData, "du;nno"),
289                "got %s\n", param_info[i].pszData);
290         }
291     }
292     ok(found_param == 2, "matched %d params\n", found_param);
293 
294     hr = IMimeAllocator_FreeParamInfoArray(alloc, count, param_info, TRUE);
295     ok(hr == S_OK, "ret %08x\n", hr);
296     IMimeAllocator_Release(alloc);
297 
298     IStream_Release(in);
299     IMimeBody_Release(body);
300 }
301 
302 typedef struct {
303     IStream IStream_iface;
304     LONG ref;
305     unsigned pos;
306 } TestStream;
307 
308 static inline TestStream *impl_from_IStream(IStream *iface)
309 {
310     return CONTAINING_RECORD(iface, TestStream, IStream_iface);
311 }
312 
313 static HRESULT WINAPI Stream_QueryInterface(IStream *iface, REFIID riid, void **ppv)
314 {
315     if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_ISequentialStream, riid) || IsEqualGUID(&IID_IStream, riid)) {
316         *ppv = iface;
317         return S_OK;
318     }
319 
320     ok(0, "unexpected call %s\n", wine_dbgstr_guid(riid));
321     *ppv = NULL;
322     return E_NOINTERFACE;
323 }
324 
325 static ULONG WINAPI Stream_AddRef(IStream *iface)
326 {
327     TestStream *This = impl_from_IStream(iface);
328     return InterlockedIncrement(&This->ref);
329 }
330 
331 static ULONG WINAPI Stream_Release(IStream *iface)
332 {
333     TestStream *This = impl_from_IStream(iface);
334     ULONG ref = InterlockedDecrement(&This->ref);
335 
336     if (!ref)
337         HeapFree(GetProcessHeap(), 0, This);
338 
339     return ref;
340 }
341 
342 static HRESULT WINAPI Stream_Read(IStream *iface, void *pv, ULONG cb, ULONG *pcbRead)
343 {
344     TestStream *This = impl_from_IStream(iface);
345     BYTE *output = pv;
346     unsigned i;
347 
348     CHECK_EXPECT(Stream_Read);
349 
350     for(i = 0; i < cb; i++)
351         output[i] = '0' + This->pos++;
352     *pcbRead = i;
353     return S_OK;
354 }
355 
356 static HRESULT WINAPI Stream_Write(IStream *iface, const void *pv, ULONG cb, ULONG *pcbWritten)
357 {
358     ok(0, "unexpected call\n");
359     return E_NOTIMPL;
360 }
361 
362 static DWORD expect_seek_pos;
363 
364 static HRESULT WINAPI Stream_Seek(IStream *iface, LARGE_INTEGER dlibMove, DWORD dwOrigin,
365                                   ULARGE_INTEGER *plibNewPosition)
366 {
367     TestStream *This = impl_from_IStream(iface);
368 
369     if(dwOrigin == STREAM_SEEK_END) {
370         CHECK_EXPECT(Stream_Seek_END);
371         ok(dlibMove.QuadPart == expect_seek_pos, "unexpected seek pos %u\n", dlibMove.u.LowPart);
372         if(plibNewPosition)
373             plibNewPosition->QuadPart = 10;
374         return S_OK;
375     }
376 
377     CHECK_EXPECT(Stream_Seek);
378 
379     ok(dlibMove.QuadPart == expect_seek_pos, "unexpected seek pos %u\n", dlibMove.u.LowPart);
380     ok(dwOrigin == STREAM_SEEK_SET, "dwOrigin = %d\n", dwOrigin);
381     This->pos = dlibMove.QuadPart;
382     if(plibNewPosition)
383         plibNewPosition->QuadPart = This->pos;
384     return S_OK;
385 }
386 
387 static HRESULT WINAPI Stream_SetSize(IStream *iface, ULARGE_INTEGER libNewSize)
388 {
389     ok(0, "unexpected call\n");
390     return E_NOTIMPL;
391 }
392 
393 static HRESULT WINAPI Stream_CopyTo(IStream *iface, IStream *pstm, ULARGE_INTEGER cb,
394                                     ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
395 {
396     ok(0, "unexpected call\n");
397     return E_NOTIMPL;
398 }
399 
400 static HRESULT WINAPI Stream_Commit(IStream *iface, DWORD grfCommitFlags)
401 {
402     ok(0, "unexpected call\n");
403     return E_NOTIMPL;
404 }
405 
406 static HRESULT WINAPI Stream_Revert(IStream *iface)
407 {
408     ok(0, "unexpected call\n");
409     return E_NOTIMPL;
410 }
411 
412 static HRESULT WINAPI Stream_LockRegion(IStream *iface, ULARGE_INTEGER libOffset,
413                                         ULARGE_INTEGER cb, DWORD dwLockType)
414 {
415     ok(0, "unexpected call\n");
416     return E_NOTIMPL;
417 }
418 
419 static HRESULT WINAPI Stream_UnlockRegion(IStream *iface,
420         ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
421 {
422     ok(0, "unexpected call\n");
423     return E_NOTIMPL;
424 }
425 
426 static HRESULT WINAPI Stream_Stat(IStream *iface, STATSTG *pstatstg, DWORD dwStatFlag)
427 {
428     CHECK_EXPECT(Stream_Stat);
429     ok(dwStatFlag == STATFLAG_NONAME, "dwStatFlag = %x\n", dwStatFlag);
430     return E_NOTIMPL;
431 }
432 
433 static HRESULT WINAPI Stream_Clone(IStream *iface, IStream **ppstm)
434 {
435     ok(0, "unexpected call\n");
436     return E_NOTIMPL;
437 }
438 
439 static /* const */ IStreamVtbl StreamVtbl = {
440     Stream_QueryInterface,
441     Stream_AddRef,
442     Stream_Release,
443     Stream_Read,
444     Stream_Write,
445     Stream_Seek,
446     Stream_SetSize,
447     Stream_CopyTo,
448     Stream_Commit,
449     Stream_Revert,
450     Stream_LockRegion,
451     Stream_UnlockRegion,
452     Stream_Stat,
453     Stream_Clone
454 };
455 
456 static IStream *create_test_stream(void)
457 {
458     TestStream *stream;
459     stream = HeapAlloc(GetProcessHeap(), 0, sizeof(*stream));
460     stream->IStream_iface.lpVtbl = &StreamVtbl;
461     stream->ref = 1;
462     stream->pos = 0;
463     return &stream->IStream_iface;
464 }
465 
466 #define test_stream_read(a,b,c,d) _test_stream_read(__LINE__,a,b,c,d)
467 static void _test_stream_read(unsigned line, IStream *stream, HRESULT exhres, const char *exdata, unsigned read_size)
468 {
469     ULONG read = 0xdeadbeed, exread = strlen(exdata);
470     char buf[1024];
471     HRESULT hres;
472 
473     if(read_size == -1)
474         read_size = sizeof(buf)-1;
475 
476     hres = IStream_Read(stream, buf, read_size, &read);
477     ok_(__FILE__,line)(hres == exhres, "Read returned %08x, expected %08x\n", hres, exhres);
478     ok_(__FILE__,line)(read == exread, "unexpected read size %u, expected %u\n", read, exread);
479     buf[read] = 0;
480     ok_(__FILE__,line)(read == exread && !memcmp(buf, exdata, read), "unexpected data %s\n", buf);
481 }
482 
483 static void test_SetData(void)
484 {
485     IStream *stream, *stream2, *test_stream;
486     IMimeBody *body;
487     HRESULT hr;
488 
489     hr = CoCreateInstance(&CLSID_IMimeBody, NULL, CLSCTX_INPROC_SERVER, &IID_IMimeBody, (void**)&body);
490     ok(hr == S_OK, "ret %08x\n", hr);
491 
492     /* Need to call InitNew before Load otherwise Load crashes with native inetcomm */
493     hr = IMimeBody_InitNew(body);
494     ok(hr == S_OK, "ret %08x\n", hr);
495 
496     stream = create_stream_from_string(msg1);
497     hr = IMimeBody_Load(body, stream);
498     ok(hr == S_OK, "ret %08x\n", hr);
499     IStream_Release(stream);
500 
501     test_stream = create_test_stream();
502     hr = IMimeBody_SetData(body, IET_BINARY, "text", "plain", &IID_IStream, test_stream);
503 
504     ok(hr == S_OK, "ret %08x\n", hr);
505     hr = IMimeBody_IsContentType(body, "text", "plain");
506     todo_wine
507     ok(hr == S_OK, "ret %08x\n", hr);
508 
509     test_current_encoding(body, IET_BINARY);
510 
511     SET_EXPECT(Stream_Stat);
512     SET_EXPECT(Stream_Seek_END);
513     hr = IMimeBody_GetData(body, IET_BINARY, &stream);
514     CHECK_CALLED(Stream_Stat);
515     CHECK_CALLED(Stream_Seek_END);
516     ok(hr == S_OK, "GetData failed %08x\n", hr);
517     ok(stream != test_stream, "unexpected stream\n");
518 
519     SET_EXPECT(Stream_Seek);
520     SET_EXPECT(Stream_Read);
521     test_stream_read(stream, S_OK, "012", 3);
522     CHECK_CALLED(Stream_Seek);
523     CHECK_CALLED(Stream_Read);
524 
525     SET_EXPECT(Stream_Stat);
526     SET_EXPECT(Stream_Seek_END);
527     hr = IMimeBody_GetData(body, IET_BINARY, &stream2);
528     CHECK_CALLED(Stream_Stat);
529     CHECK_CALLED(Stream_Seek_END);
530     ok(hr == S_OK, "GetData failed %08x\n", hr);
531     ok(stream2 != stream, "unexpected stream\n");
532 
533     SET_EXPECT(Stream_Seek);
534     SET_EXPECT(Stream_Read);
535     test_stream_read(stream2, S_OK, "01", 2);
536     CHECK_CALLED(Stream_Seek);
537     CHECK_CALLED(Stream_Read);
538 
539     expect_seek_pos = 3;
540     SET_EXPECT(Stream_Seek);
541     SET_EXPECT(Stream_Read);
542     test_stream_read(stream, S_OK, "345", 3);
543     CHECK_CALLED(Stream_Seek);
544     CHECK_CALLED(Stream_Read);
545 
546     IStream_Release(stream);
547     IStream_Release(stream2);
548     IStream_Release(test_stream);
549 
550     stream = create_stream_from_string(" \t\r\n|}~YWJj ZGV|}~mZw== \t"); /* "abcdefg" in base64 obscured by invalid chars */
551     hr = IMimeBody_SetData(body, IET_BASE64, "text", "plain", &IID_IStream, stream);
552     IStream_Release(stream);
553     ok(hr == S_OK, "SetData failed: %08x\n", hr);
554 
555     test_current_encoding(body, IET_BASE64);
556 
557     hr = IMimeBody_GetData(body, IET_BINARY, &stream);
558     ok(hr == S_OK, "GetData failed %08x\n", hr);
559 
560     test_stream_read(stream, S_OK, "abc", 3);
561     test_stream_read(stream, S_OK, "defg", -1);
562 
563     IStream_Release(stream);
564 
565     hr = IMimeBody_GetData(body, IET_BASE64, &stream);
566     ok(hr == S_OK, "GetData failed %08x\n", hr);
567 
568     test_stream_read(stream, S_OK, " \t\r", 3);
569     IStream_Release(stream);
570 
571     stream = create_stream_from_string(" =3d=3D\"one\" \t=\r\ntw=  o=\nx3\n=34\r\n5");
572     hr = IMimeBody_SetData(body, IET_QP, "text", "plain", &IID_IStream, stream);
573     IStream_Release(stream);
574     ok(hr == S_OK, "SetData failed: %08x\n", hr);
575 
576     test_current_encoding(body, IET_QP);
577 
578     hr = IMimeBody_GetData(body, IET_BINARY, &stream);
579     ok(hr == S_OK, "GetData failed %08x\n", hr);
580 
581     test_stream_read(stream, S_OK, " ==\"one\" \ttw=o=3\n4\r\n5", -1);
582 
583     IStream_Release(stream);
584 
585     IMimeBody_Release(body);
586 }
587 
588 static void test_Allocator(void)
589 {
590     HRESULT hr;
591     IMimeAllocator *alloc;
592 
593     hr = MimeOleGetAllocator(&alloc);
594     ok(hr == S_OK, "ret %08x\n", hr);
595     IMimeAllocator_Release(alloc);
596 }
597 
598 static void test_CreateMessage(void)
599 {
600     HRESULT hr;
601     IMimeMessage *msg;
602     IStream *stream;
603     LONG ref;
604     HBODY hbody, hbody2;
605     IMimeBody *body;
606     BODYOFFSETS offsets;
607     ULONG count;
608     FINDBODY find_struct;
609     HCHARSET hcs;
610     HBODY handle = NULL;
611 
612     char text[] = "text";
613     HBODY *body_list;
614     PROPVARIANT prop;
615     static const char att_pritype[] = "att:pri-content-type";
616 
617     hr = MimeOleCreateMessage(NULL, &msg);
618     ok(hr == S_OK, "ret %08x\n", hr);
619 
620     stream = create_stream_from_string(msg1);
621 
622     hr = IMimeMessage_Load(msg, stream);
623     ok(hr == S_OK, "ret %08x\n", hr);
624 
625     hr = IMimeMessage_CountBodies(msg, HBODY_ROOT, TRUE, &count);
626     ok(hr == S_OK, "ret %08x\n", hr);
627     ok(count == 3, "got %d\n", count);
628 
629     hr = IMimeMessage_CountBodies(msg, HBODY_ROOT, FALSE, &count);
630     ok(hr == S_OK, "ret %08x\n", hr);
631     ok(count == 3, "got %d\n", count);
632 
633     hr = IMimeMessage_BindToObject(msg, HBODY_ROOT, &IID_IMimeBody, (void**)&body);
634     ok(hr == S_OK, "ret %08x\n", hr);
635     hr = IMimeBody_GetOffsets(body, &offsets);
636     ok(hr == S_OK, "ret %08x\n", hr);
637     ok(offsets.cbBoundaryStart == 0, "got %d\n", offsets.cbBoundaryStart);
638     ok(offsets.cbHeaderStart == 0, "got %d\n", offsets.cbHeaderStart);
639     ok(offsets.cbBodyStart == 359, "got %d\n", offsets.cbBodyStart);
640     ok(offsets.cbBodyEnd == 666, "got %d\n", offsets.cbBodyEnd);
641     IMimeBody_Release(body);
642 
643     hr = IMimeMessage_GetBody(msg, IBL_ROOT, NULL, &hbody);
644     ok(hr == S_OK, "ret %08x\n", hr);
645 
646     hr = IMimeBody_GetHandle(body, NULL);
647     ok(hr == E_INVALIDARG, "ret %08x\n", hr);
648 
649     hr = IMimeBody_GetHandle(body, &handle);
650     ok(hr == S_OK, "ret %08x\n", hr);
651     ok(handle != NULL, "handle %p\n", handle);
652 
653     hr = IMimeMessage_GetBody(msg, IBL_PARENT, hbody, NULL);
654     ok(hr == E_INVALIDARG, "ret %08x\n", hr);
655 
656     hbody2 = (HBODY)0xdeadbeef;
657     hr = IMimeMessage_GetBody(msg, IBL_PARENT, hbody, &hbody2);
658     ok(hr == MIME_E_NOT_FOUND, "ret %08x\n", hr);
659     ok(hbody2 == NULL, "hbody2 %p\n", hbody2);
660 
661     PropVariantInit(&prop);
662     hr = IMimeMessage_GetBodyProp(msg, hbody, att_pritype, 0, &prop);
663     ok(hr == S_OK, "ret %08x\n", hr);
664     ok(prop.vt == VT_LPSTR, "vt %08x\n", prop.vt);
665     ok(!strcasecmp(prop.u.pszVal, "multipart"), "got %s\n", prop.u.pszVal);
666     PropVariantClear(&prop);
667 
668     hr = IMimeMessage_GetBody(msg, IBL_FIRST, hbody, &hbody);
669     ok(hr == S_OK, "ret %08x\n", hr);
670     hr = IMimeMessage_BindToObject(msg, hbody, &IID_IMimeBody, (void**)&body);
671     ok(hr == S_OK, "ret %08x\n", hr);
672 
673     hr = IMimeBody_GetHandle(body, &handle);
674     ok(hr == S_OK, "ret %08x\n", hr);
675     ok(handle == hbody, "handle %p\n", handle);
676 
677     hr = IMimeBody_GetOffsets(body, &offsets);
678     ok(hr == S_OK, "ret %08x\n", hr);
679     ok(offsets.cbBoundaryStart == 405, "got %d\n", offsets.cbBoundaryStart);
680     ok(offsets.cbHeaderStart == 428, "got %d\n", offsets.cbHeaderStart);
681     ok(offsets.cbBodyStart == 518, "got %d\n", offsets.cbBodyStart);
682     ok(offsets.cbBodyEnd == 523, "got %d\n", offsets.cbBodyEnd);
683 
684     hr = IMimeBody_GetCharset(body, &hcs);
685     ok(hr == S_OK, "ret %08x\n", hr);
686     todo_wine
687     {
688         ok(hcs != NULL, "Expected non-NULL charset\n");
689     }
690 
691     IMimeBody_Release(body);
692 
693     hr = IMimeMessage_GetBody(msg, IBL_NEXT, hbody, &hbody);
694     ok(hr == S_OK, "ret %08x\n", hr);
695     hr = IMimeMessage_BindToObject(msg, hbody, &IID_IMimeBody, (void**)&body);
696     ok(hr == S_OK, "ret %08x\n", hr);
697 
698     hr = IMimeBody_GetHandle(body, &handle);
699     ok(hr == S_OK, "ret %08x\n", hr);
700     ok(handle == hbody, "handle %p\n", handle);
701 
702     hr = IMimeBody_GetOffsets(body, &offsets);
703     ok(hr == S_OK, "ret %08x\n", hr);
704     ok(offsets.cbBoundaryStart == 525, "got %d\n", offsets.cbBoundaryStart);
705     ok(offsets.cbHeaderStart == 548, "got %d\n", offsets.cbHeaderStart);
706     ok(offsets.cbBodyStart == 629, "got %d\n", offsets.cbBodyStart);
707     ok(offsets.cbBodyEnd == 639, "got %d\n", offsets.cbBodyEnd);
708     IMimeBody_Release(body);
709 
710     find_struct.pszPriType = text;
711     find_struct.pszSubType = NULL;
712 
713     hr = IMimeMessage_FindFirst(msg, &find_struct, &hbody);
714     ok(hr == S_OK, "ret %08x\n", hr);
715 
716     hr = IMimeMessage_FindNext(msg, &find_struct, &hbody);
717     ok(hr == S_OK, "ret %08x\n", hr);
718 
719     hr = IMimeMessage_FindNext(msg, &find_struct, &hbody);
720     ok(hr == MIME_E_NOT_FOUND, "ret %08x\n", hr);
721 
722     hr = IMimeMessage_GetAttachments(msg, &count, &body_list);
723     ok(hr == S_OK, "ret %08x\n", hr);
724     ok(count == 2, "got %d\n", count);
725     if(count == 2)
726     {
727         IMimeBody *attachment;
728         PROPVARIANT prop;
729 
730         PropVariantInit(&prop);
731 
732         hr = IMimeMessage_BindToObject(msg, body_list[0], &IID_IMimeBody, (void**)&attachment);
733         ok(hr == S_OK, "ret %08x\n", hr);
734 
735         hr = IMimeBody_IsContentType(attachment, "multipart", NULL);
736         ok(hr == S_FALSE, "ret %08x\n", hr);
737 
738         test_current_encoding(attachment, IET_8BIT);
739 
740         prop.vt = VT_LPSTR;
741         hr = IMimeBody_GetProp(attachment, "Content-Transfer-Encoding", 0, &prop);
742         ok(hr == S_OK, "ret %08x\n", hr);
743 
744         ok(prop.vt == VT_LPSTR, "type %d\n", prop.vt);
745         ok(!strcmp(prop.u.pszVal, "8bit"), "got  %s\n", prop.u.pszVal);
746         PropVariantClear(&prop);
747 
748         hr = IMimeBody_IsType(attachment, IBT_ATTACHMENT);
749         todo_wine ok(hr == S_FALSE, "ret %08x\n", hr);
750 
751         IMimeBody_Release(attachment);
752 
753         hr = IMimeMessage_BindToObject(msg, body_list[1], &IID_IMimeBody, (void**)&attachment);
754         ok(hr == S_OK, "ret %08x\n", hr);
755 
756         hr = IMimeBody_IsContentType(attachment, "multipart", NULL);
757         ok(hr == S_FALSE, "ret %08x\n", hr);
758 
759         test_current_encoding(attachment, IET_7BIT);
760 
761         prop.vt = VT_LPSTR;
762         hr = IMimeBody_GetProp(attachment, "Content-Transfer-Encoding", 0, &prop);
763         ok(hr == S_OK, "ret %08x\n", hr);
764         ok(prop.vt == VT_LPSTR, "type %d\n", prop.vt);
765         ok(!strcmp(prop.u.pszVal, "7bit"), "got  %s\n", prop.u.pszVal);
766         PropVariantClear(&prop);
767 
768         hr = IMimeBody_IsType(attachment, IBT_ATTACHMENT);
769         ok(hr == S_OK, "ret %08x\n", hr);
770 
771         IMimeBody_Release(attachment);
772     }
773     CoTaskMemFree(body_list);
774 
775     hr = IMimeBody_GetCharset(body, &hcs);
776     ok(hr == S_OK, "ret %08x\n", hr);
777     todo_wine
778     {
779         ok(hcs != NULL, "Expected non-NULL charset\n");
780     }
781 
782     IMimeMessage_Release(msg);
783 
784     ref = IStream_AddRef(stream);
785     ok(ref == 2 ||
786        broken(ref == 1), /* win95 */
787        "ref %d\n", ref);
788     IStream_Release(stream);
789 
790     IStream_Release(stream);
791 }
792 
793 static void test_mhtml_message(void)
794 {
795     IMimeMessage *mime_message;
796     IMimeBody *mime_body;
797     HBODY *body_list;
798     IStream *stream;
799     ULONG count;
800     HRESULT hres;
801 
802     hres = MimeOleCreateMessage(NULL, &mime_message);
803     ok(hres == S_OK, "MimeOleCreateMessage failed: %08x\n", hres);
804 
805     stream = create_stream_from_string(mhtml_page1);
806     hres = IMimeMessage_Load(mime_message, stream);
807     IStream_Release(stream);
808     ok(hres == S_OK, "Load failed: %08x\n", hres);
809 
810     hres = IMimeMessage_CountBodies(mime_message, HBODY_ROOT, TRUE, &count);
811     ok(hres == S_OK, "CountBodies failed: %08x\n", hres);
812     ok(count == 3, "got %d\n", count);
813 
814     hres = IMimeMessage_GetAttachments(mime_message, &count, &body_list);
815     ok(hres == S_OK, "GetAttachments failed: %08x\n", hres);
816     ok(count == 2, "count = %u\n", count);
817 
818     hres = IMimeMessage_BindToObject(mime_message, body_list[0], &IID_IMimeBody, (void**)&mime_body);
819     ok(hres == S_OK, "BindToObject failed: %08x\n", hres);
820 
821     hres = IMimeBody_GetData(mime_body, IET_BINARY, &stream);
822     ok(hres == S_OK, "GetData failed: %08x\n", hres);
823     test_stream_read(stream, S_OK, "<HTML></HTML>", -1);
824     IStream_Release(stream);
825 
826     test_current_encoding(mime_body, IET_QP);
827 
828     IMimeBody_Release(mime_body);
829 
830     hres = IMimeMessage_BindToObject(mime_message, body_list[1], &IID_IMimeBody, (void**)&mime_body);
831     ok(hres == S_OK, "BindToObject failed: %08x\n", hres);
832 
833     test_current_encoding(mime_body, IET_BASE64);
834 
835     hres = IMimeBody_GetData(mime_body, IET_BINARY, &stream);
836     ok(hres == S_OK, "GetData failed: %08x\n", hres);
837     test_stream_read(stream, S_OK, "Test", -1);
838     IStream_Release(stream);
839 
840     IMimeBody_Release(mime_body);
841 
842     CoTaskMemFree(body_list);
843 
844     IMimeMessage_Release(mime_message);
845 }
846 
847 static void test_MessageSetProp(void)
848 {
849     static const char topic[] = "wine topic";
850     static const WCHAR topicW[] = {'w','i','n','e',' ','t','o','p','i','c',0};
851     HRESULT hr;
852     IMimeMessage *msg;
853     IMimeBody *body;
854     PROPVARIANT prop;
855 
856     hr = MimeOleCreateMessage(NULL, &msg);
857     ok(hr == S_OK, "ret %08x\n", hr);
858 
859     PropVariantInit(&prop);
860 
861     hr = IMimeMessage_BindToObject(msg, HBODY_ROOT, &IID_IMimeBody, (void**)&body);
862     ok(hr == S_OK, "ret %08x\n", hr);
863 
864     hr = IMimeBody_SetProp(body, NULL, 0, &prop);
865     ok(hr == E_INVALIDARG, "ret %08x\n", hr);
866 
867     hr = IMimeBody_SetProp(body, "Thread-Topic", 0, NULL);
868     ok(hr == E_INVALIDARG, "ret %08x\n", hr);
869 
870     prop.vt = VT_LPSTR;
871     prop.u.pszVal = CoTaskMemAlloc(strlen(topic)+1);
872     strcpy(prop.u.pszVal, topic);
873     hr = IMimeBody_SetProp(body, "Thread-Topic", 0, &prop);
874     ok(hr == S_OK, "ret %08x\n", hr);
875     PropVariantClear(&prop);
876 
877     hr = IMimeBody_GetProp(body, NULL, 0, &prop);
878     ok(hr == E_INVALIDARG, "ret %08x\n", hr);
879 
880     hr = IMimeBody_GetProp(body, "Thread-Topic", 0, NULL);
881     ok(hr == E_INVALIDARG, "ret %08x\n", hr);
882 
883     hr = IMimeBody_GetProp(body, "Wine-Topic", 0, &prop);
884     ok(hr == MIME_E_NOT_FOUND, "ret %08x\n", hr);
885 
886     prop.vt = VT_LPSTR;
887     hr = IMimeBody_GetProp(body, "Thread-Topic", 0, &prop);
888     ok(hr == S_OK, "ret %08x\n", hr);
889     if(hr == S_OK)
890     {
891         ok(prop.vt == VT_LPSTR, "type %d\n", prop.vt);
892         ok(!strcmp(prop.u.pszVal, topic), "got  %s\n", prop.u.pszVal);
893         PropVariantClear(&prop);
894     }
895 
896     prop.vt = VT_LPSTR;
897     prop.u.pszVal = CoTaskMemAlloc(strlen(topic)+1);
898     strcpy(prop.u.pszVal, topic);
899     hr = IMimeBody_SetProp(body, PIDTOSTR(PID_HDR_SUBJECT), 0, &prop);
900     ok(hr == S_OK, "ret %08x\n", hr);
901     PropVariantClear(&prop);
902 
903     prop.vt = VT_LPSTR;
904     hr = IMimeBody_GetProp(body, PIDTOSTR(PID_HDR_SUBJECT), 0, &prop);
905     ok(hr == S_OK, "ret %08x\n", hr);
906     if(hr == S_OK)
907     {
908         ok(prop.vt == VT_LPSTR, "type %d\n", prop.vt);
909         ok(!strcmp(prop.u.pszVal, topic), "got  %s\n", prop.u.pszVal);
910         PropVariantClear(&prop);
911     }
912 
913     /* Using the name or PID returns the same result. */
914     prop.vt = VT_LPSTR;
915     hr = IMimeBody_GetProp(body, "Subject", 0, &prop);
916     ok(hr == S_OK, "ret %08x\n", hr);
917     if(hr == S_OK)
918     {
919         ok(prop.vt == VT_LPSTR, "type %d\n", prop.vt);
920         ok(!strcmp(prop.u.pszVal, topic), "got  %s\n", prop.u.pszVal);
921         PropVariantClear(&prop);
922     }
923 
924     prop.vt = VT_LPWSTR;
925     hr = IMimeBody_GetProp(body, "Subject", 0, &prop);
926     ok(hr == S_OK, "ret %08x\n", hr);
927     if(hr == S_OK)
928     {
929         ok(prop.vt == VT_LPWSTR, "type %d\n", prop.vt);
930         ok(!lstrcmpW(prop.u.pwszVal, topicW), "got %s\n", wine_dbgstr_w(prop.u.pwszVal));
931         PropVariantClear(&prop);
932     }
933 
934     prop.vt = VT_LPSTR;
935     prop.u.pszVal = CoTaskMemAlloc(strlen(topic)+1);
936     strcpy(prop.u.pszVal, topic);
937     hr = IMimeBody_SetProp(body, PIDTOSTR(PID_HDR_TO), 0, &prop);
938     ok(hr == S_OK, "ret %08x\n", hr);
939     PropVariantClear(&prop);
940 
941     /* Out of Range PID */
942     prop.vt = VT_LPSTR;
943     prop.u.pszVal = CoTaskMemAlloc(strlen(topic)+1);
944     strcpy(prop.u.pszVal, topic);
945     hr = IMimeBody_SetProp(body, PIDTOSTR(124), 0, &prop);
946     ok(hr == MIME_E_NOT_FOUND, "ret %08x\n", hr);
947     PropVariantClear(&prop);
948 
949     IMimeBody_Release(body);
950     IMimeMessage_Release(msg);
951 }
952 
953 static void test_MessageGetPropInfo(void)
954 {
955     static const char topic[] = "wine topic";
956     static const char subject[] = "wine testing";
957     HRESULT hr;
958     IMimeMessage *msg;
959     IMimeBody *body;
960     PROPVARIANT prop;
961     MIMEPROPINFO info;
962 
963     hr = MimeOleCreateMessage(NULL, &msg);
964     ok(hr == S_OK, "ret %08x\n", hr);
965 
966     PropVariantInit(&prop);
967 
968     hr = IMimeMessage_BindToObject(msg, HBODY_ROOT, &IID_IMimeBody, (void**)&body);
969     ok(hr == S_OK, "ret %08x\n", hr);
970 
971     prop.vt = VT_LPSTR;
972     prop.u.pszVal = CoTaskMemAlloc(strlen(topic)+1);
973     strcpy(prop.u.pszVal, topic);
974     hr = IMimeBody_SetProp(body, "Thread-Topic", 0, &prop);
975     ok(hr == S_OK, "ret %08x\n", hr);
976     PropVariantClear(&prop);
977 
978     prop.vt = VT_LPSTR;
979     prop.u.pszVal = CoTaskMemAlloc(strlen(subject)+1);
980     strcpy(prop.u.pszVal, subject);
981     hr = IMimeBody_SetProp(body, PIDTOSTR(PID_HDR_SUBJECT), 0, &prop);
982     ok(hr == S_OK, "ret %08x\n", hr);
983     PropVariantClear(&prop);
984 
985     memset(&info, 0, sizeof(info));
986     info.dwMask = PIM_ENCODINGTYPE | PIM_FLAGS | PIM_PROPID;
987     hr = IMimeBody_GetPropInfo(body, NULL, &info);
988     ok(hr == E_INVALIDARG, "ret %08x\n", hr);
989 
990     memset(&info, 0, sizeof(info));
991     info.dwMask = PIM_ENCODINGTYPE | PIM_FLAGS | PIM_PROPID;
992     hr = IMimeBody_GetPropInfo(body, "Subject", NULL);
993     ok(hr == E_INVALIDARG, "ret %08x\n", hr);
994 
995     memset(&info, 0xfe, sizeof(info));
996     info.dwMask = PIM_ENCODINGTYPE | PIM_FLAGS | PIM_PROPID;
997     hr = IMimeBody_GetPropInfo(body, "Subject", &info);
998     ok(hr == S_OK, "ret %08x\n", hr);
999     if(hr == S_OK)
1000     {
1001        ok(info.dwMask & (PIM_ENCODINGTYPE | PIM_FLAGS| PIM_PROPID), "Invalid mask 0x%08x\n", info.dwFlags);
1002        todo_wine ok(info.dwFlags & 0x10000000, "Invalid flags 0x%08x\n", info.dwFlags);
1003        ok(info.ietEncoding == 0, "Invalid encoding %d\n", info.ietEncoding);
1004        ok(info.dwPropId == PID_HDR_SUBJECT, "Invalid propid %d\n", info.dwPropId);
1005        ok(info.cValues == 0xfefefefe, "Invalid cValues %d\n", info.cValues);
1006     }
1007 
1008     memset(&info, 0xfe, sizeof(info));
1009     info.dwMask = 0;
1010     hr = IMimeBody_GetPropInfo(body, "Subject", &info);
1011     ok(hr == S_OK, "ret %08x\n", hr);
1012     if(hr == S_OK)
1013     {
1014        ok(info.dwMask == 0, "Invalid mask 0x%08x\n", info.dwFlags);
1015        ok(info.dwFlags == 0xfefefefe, "Invalid flags 0x%08x\n", info.dwFlags);
1016        ok(info.ietEncoding == -16843010, "Invalid encoding %d\n", info.ietEncoding);
1017        ok(info.dwPropId == -16843010, "Invalid propid %d\n", info.dwPropId);
1018     }
1019 
1020     memset(&info, 0xfe, sizeof(info));
1021     info.dwMask = 0;
1022     info.dwPropId = 1024;
1023     info.ietEncoding = 99;
1024     hr = IMimeBody_GetPropInfo(body, "Subject", &info);
1025     ok(hr == S_OK, "ret %08x\n", hr);
1026     if(hr == S_OK)
1027     {
1028        ok(info.dwMask == 0, "Invalid mask 0x%08x\n", info.dwFlags);
1029        ok(info.dwFlags == 0xfefefefe, "Invalid flags 0x%08x\n", info.dwFlags);
1030        ok(info.ietEncoding == 99, "Invalid encoding %d\n", info.ietEncoding);
1031        ok(info.dwPropId == 1024, "Invalid propid %d\n", info.dwPropId);
1032     }
1033 
1034     memset(&info, 0, sizeof(info));
1035     info.dwMask = PIM_ENCODINGTYPE | PIM_FLAGS | PIM_PROPID;
1036     hr = IMimeBody_GetPropInfo(body, "Invalid Property", &info);
1037     ok(hr == MIME_E_NOT_FOUND, "ret %08x\n", hr);
1038 
1039     IMimeBody_Release(body);
1040     IMimeMessage_Release(msg);
1041 }
1042 
1043 static void test_MessageOptions(void)
1044 {
1045     static const char string[] = "XXXXX";
1046     static const char zero[] =   "0";
1047     HRESULT hr;
1048     IMimeMessage *msg;
1049     PROPVARIANT prop;
1050 
1051     hr = MimeOleCreateMessage(NULL, &msg);
1052     ok(hr == S_OK, "ret %08x\n", hr);
1053 
1054     PropVariantInit(&prop);
1055 
1056     prop.vt = VT_BOOL;
1057     prop.u.boolVal = TRUE;
1058     hr = IMimeMessage_SetOption(msg, OID_HIDE_TNEF_ATTACHMENTS, &prop);
1059     ok(hr == S_OK, "ret %08x\n", hr);
1060     PropVariantClear(&prop);
1061 
1062     hr = IMimeMessage_GetOption(msg, OID_HIDE_TNEF_ATTACHMENTS, &prop);
1063     todo_wine ok(hr == S_OK, "ret %08x\n", hr);
1064     todo_wine ok(prop.vt == VT_BOOL, "vt %08x\n", prop.vt);
1065     todo_wine ok(prop.u.boolVal == TRUE, "Hide Attachments got %d\n", prop.u.boolVal);
1066     PropVariantClear(&prop);
1067 
1068     prop.vt = VT_LPSTR;
1069     prop.u.pszVal = CoTaskMemAlloc(strlen(string)+1);
1070     strcpy(prop.u.pszVal, string);
1071     hr = IMimeMessage_SetOption(msg, OID_HIDE_TNEF_ATTACHMENTS, &prop);
1072     ok(hr == S_OK, "ret %08x\n", hr);
1073     PropVariantClear(&prop);
1074 
1075     hr = IMimeMessage_GetOption(msg, OID_HIDE_TNEF_ATTACHMENTS, &prop);
1076     todo_wine ok(hr == S_OK, "ret %08x\n", hr);
1077     todo_wine ok(prop.vt == VT_BOOL, "vt %08x\n", prop.vt);
1078     todo_wine ok(prop.u.boolVal == TRUE, "Hide Attachments got %d\n", prop.u.boolVal);
1079     PropVariantClear(&prop);
1080 
1081     /* Invalid property type doesn't change the value */
1082     prop.vt = VT_LPSTR;
1083     prop.u.pszVal = CoTaskMemAlloc(strlen(zero)+1);
1084     strcpy(prop.u.pszVal, zero);
1085     hr = IMimeMessage_SetOption(msg, OID_HIDE_TNEF_ATTACHMENTS, &prop);
1086     ok(hr == S_OK, "ret %08x\n", hr);
1087     PropVariantClear(&prop);
1088 
1089     hr = IMimeMessage_GetOption(msg, OID_HIDE_TNEF_ATTACHMENTS, &prop);
1090     todo_wine ok(hr == S_OK, "ret %08x\n", hr);
1091     todo_wine ok(prop.vt == VT_BOOL, "vt %08x\n", prop.vt);
1092     todo_wine ok(prop.u.boolVal == TRUE, "Hide Attachments got %d\n", prop.u.boolVal);
1093     PropVariantClear(&prop);
1094 
1095     /* Invalid OID */
1096     prop.vt = VT_BOOL;
1097     prop.u.boolVal = TRUE;
1098     hr = IMimeMessage_SetOption(msg, 0xff00000a, &prop);
1099     ok(hr == MIME_E_INVALID_OPTION_ID, "ret %08x\n", hr);
1100     PropVariantClear(&prop);
1101 
1102     /* Out of range before type. */
1103     prop.vt = VT_I4;
1104     prop.u.lVal = 1;
1105     hr = IMimeMessage_SetOption(msg, 0xff00000a, &prop);
1106     ok(hr == MIME_E_INVALID_OPTION_ID, "ret %08x\n", hr);
1107     PropVariantClear(&prop);
1108 
1109     IMimeMessage_Release(msg);
1110 }
1111 
1112 static void test_BindToObject(void)
1113 {
1114     HRESULT hr;
1115     IMimeMessage *msg;
1116     IMimeBody *body;
1117     ULONG count;
1118 
1119     hr = MimeOleCreateMessage(NULL, &msg);
1120     ok(hr == S_OK, "ret %08x\n", hr);
1121 
1122     hr = IMimeMessage_CountBodies(msg, HBODY_ROOT, TRUE, &count);
1123     ok(hr == S_OK, "ret %08x\n", hr);
1124     ok(count == 1, "got %d\n", count);
1125 
1126     hr = IMimeMessage_BindToObject(msg, HBODY_ROOT, &IID_IMimeBody, (void**)&body);
1127     ok(hr == S_OK, "ret %08x\n", hr);
1128     IMimeBody_Release(body);
1129 
1130     IMimeMessage_Release(msg);
1131 }
1132 
1133 static void test_BodyDeleteProp(void)
1134 {
1135     static const char topic[] = "wine topic";
1136     HRESULT hr;
1137     IMimeMessage *msg;
1138     IMimeBody *body;
1139     PROPVARIANT prop;
1140 
1141     hr = MimeOleCreateMessage(NULL, &msg);
1142     ok(hr == S_OK, "ret %08x\n", hr);
1143 
1144     PropVariantInit(&prop);
1145 
1146     hr = IMimeMessage_BindToObject(msg, HBODY_ROOT, &IID_IMimeBody, (void**)&body);
1147     ok(hr == S_OK, "ret %08x\n", hr);
1148 
1149     hr = IMimeBody_DeleteProp(body, "Subject");
1150     ok(hr == MIME_E_NOT_FOUND, "ret %08x\n", hr);
1151 
1152     hr = IMimeBody_DeleteProp(body, PIDTOSTR(PID_HDR_SUBJECT));
1153     ok(hr == MIME_E_NOT_FOUND, "ret %08x\n", hr);
1154 
1155     prop.vt = VT_LPSTR;
1156     prop.u.pszVal = CoTaskMemAlloc(strlen(topic)+1);
1157     strcpy(prop.u.pszVal, topic);
1158     hr = IMimeBody_SetProp(body, "Subject", 0, &prop);
1159     ok(hr == S_OK, "ret %08x\n", hr);
1160     PropVariantClear(&prop);
1161 
1162     hr = IMimeBody_DeleteProp(body, "Subject");
1163     ok(hr == S_OK, "ret %08x\n", hr);
1164 
1165     hr = IMimeBody_GetProp(body, "Subject", 0, &prop);
1166     ok(hr == MIME_E_NOT_FOUND, "ret %08x\n", hr);
1167 
1168     prop.vt = VT_LPSTR;
1169     prop.u.pszVal = CoTaskMemAlloc(strlen(topic)+1);
1170     strcpy(prop.u.pszVal, topic);
1171     hr = IMimeBody_SetProp(body, PIDTOSTR(PID_HDR_SUBJECT), 0, &prop);
1172     ok(hr == S_OK, "ret %08x\n", hr);
1173     PropVariantClear(&prop);
1174 
1175     hr = IMimeBody_DeleteProp(body, PIDTOSTR(PID_HDR_SUBJECT));
1176     ok(hr == S_OK, "ret %08x\n", hr);
1177 
1178     hr = IMimeBody_GetProp(body, PIDTOSTR(PID_HDR_SUBJECT), 0, &prop);
1179     ok(hr == MIME_E_NOT_FOUND, "ret %08x\n", hr);
1180 
1181     IMimeBody_Release(body);
1182     IMimeMessage_Release(msg);
1183 }
1184 
1185 static void test_MimeOleGetPropertySchema(void)
1186 {
1187     HRESULT hr;
1188     IMimePropertySchema *schema = NULL;
1189 
1190     hr = MimeOleGetPropertySchema(&schema);
1191     ok(hr == S_OK, "ret %08x\n", hr);
1192 
1193     IMimePropertySchema_Release(schema);
1194 }
1195 
1196 typedef struct {
1197     const char *url;
1198     const char *content;
1199     const char *mime;
1200     const char *data;
1201 } mhtml_binding_test_t;
1202 
1203 static const mhtml_binding_test_t binding_tests[] = {
1204     {
1205         "mhtml:file://%s",
1206         mhtml_page1,
1207         "text/html",
1208         "<HTML></HTML>"
1209     },
1210     {
1211         "mhtml:file://%s!http://winehq.org/mhtmltest.html",
1212         mhtml_page1,
1213         "Image/Jpeg",
1214         "Test"
1215     }
1216 };
1217 
1218 static const mhtml_binding_test_t *current_binding_test;
1219 static IInternetProtocol *current_binding_protocol;
1220 
1221 static HRESULT WINAPI BindInfo_QueryInterface(IInternetBindInfo *iface, REFIID riid, void **ppv)
1222 {
1223     if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IInternetBindInfo, riid)) {
1224         *ppv = iface;
1225         return S_OK;
1226     }
1227 
1228     *ppv = NULL;
1229     ok(0, "unexpected riid %s\n", wine_dbgstr_guid(riid));
1230     return E_NOINTERFACE;
1231 }
1232 
1233 static ULONG WINAPI BindInfo_AddRef(IInternetBindInfo *iface)
1234 {
1235     return 2;
1236 }
1237 
1238 static ULONG WINAPI BindInfo_Release(IInternetBindInfo *iface)
1239 {
1240     return 1;
1241 }
1242 
1243 static HRESULT WINAPI BindInfo_GetBindInfo(IInternetBindInfo *iface, DWORD *grfBINDF, BINDINFO *pbindinfo)
1244 {
1245     CHECK_EXPECT(GetBindInfo);
1246 
1247     ok(grfBINDF != NULL, "grfBINDF == NULL\n");
1248     ok(pbindinfo != NULL, "pbindinfo == NULL\n");
1249     ok(pbindinfo->cbSize == sizeof(BINDINFO), "wrong size of pbindinfo: %d\n", pbindinfo->cbSize);
1250 
1251     *grfBINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA | BINDF_FROMURLMON | BINDF_NEEDFILE;
1252     return S_OK;
1253 }
1254 
1255 static HRESULT WINAPI BindInfo_GetBindString(IInternetBindInfo *iface, ULONG ulStringType, LPOLESTR *ppwzStr,
1256         ULONG cEl, ULONG *pcElFetched)
1257 {
1258     ok(0, "unexpected call\n");
1259     return E_NOTIMPL;
1260 }
1261 
1262 static IInternetBindInfoVtbl InternetBindInfoVtbl = {
1263     BindInfo_QueryInterface,
1264     BindInfo_AddRef,
1265     BindInfo_Release,
1266     BindInfo_GetBindInfo,
1267     BindInfo_GetBindString
1268 };
1269 
1270 static IInternetBindInfo bind_info = {
1271     &InternetBindInfoVtbl
1272 };
1273 
1274 static HRESULT WINAPI ServiceProvider_QueryInterface(IServiceProvider *iface, REFIID riid, void **ppv)
1275 {
1276     ok(0, "unexpected call %s\n", wine_dbgstr_guid(riid));
1277     *ppv = NULL;
1278     return E_NOINTERFACE;
1279 }
1280 
1281 static ULONG WINAPI ServiceProvider_AddRef(IServiceProvider *iface)
1282 {
1283     return 2;
1284 }
1285 
1286 static ULONG WINAPI ServiceProvider_Release(IServiceProvider *iface)
1287 {
1288     return 1;
1289 }
1290 
1291 static HRESULT WINAPI ServiceProvider_QueryService(IServiceProvider *iface, REFGUID guidService,
1292         REFIID riid, void **ppv)
1293 {
1294     if(IsEqualGUID(&CLSID_MimeEdit, guidService)) {
1295         *ppv = NULL;
1296         return E_NOINTERFACE;
1297     }
1298 
1299     ok(0, "unexpected service %s\n", wine_dbgstr_guid(guidService));
1300     return E_FAIL;
1301 }
1302 
1303 static /* const */ IServiceProviderVtbl ServiceProviderVtbl = {
1304     ServiceProvider_QueryInterface,
1305     ServiceProvider_AddRef,
1306     ServiceProvider_Release,
1307     ServiceProvider_QueryService
1308 };
1309 
1310 static IServiceProvider service_provider = { &ServiceProviderVtbl };
1311 
1312 static HRESULT WINAPI ProtocolSink_QueryInterface(IInternetProtocolSink *iface, REFIID riid, void **ppv)
1313 {
1314     if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IInternetProtocolSink, riid)) {
1315         *ppv = iface;
1316         return S_OK;
1317     }
1318 
1319     if(IsEqualGUID(&IID_IServiceProvider, riid)) {
1320         *ppv = &service_provider;
1321         return S_OK;
1322     }
1323 
1324     *ppv = NULL;
1325     ok(0, "unexpected riid %s\n", wine_dbgstr_guid(riid));
1326     return E_NOINTERFACE;
1327 }
1328 
1329 static ULONG WINAPI ProtocolSink_AddRef(IInternetProtocolSink *iface)
1330 {
1331     return 2;
1332 }
1333 
1334 static ULONG WINAPI ProtocolSink_Release(IInternetProtocolSink *iface)
1335 {
1336     return 1;
1337 }
1338 
1339 static HRESULT WINAPI ProtocolSink_Switch(IInternetProtocolSink *iface, PROTOCOLDATA *pProtocolData)
1340 {
1341     ok(0, "unexpected call\n");
1342     return E_NOTIMPL;
1343 }
1344 
1345 static HRESULT WINAPI ProtocolSink_ReportProgress(IInternetProtocolSink *iface, ULONG ulStatusCode,
1346         const WCHAR *szStatusText)
1347 {
1348     switch(ulStatusCode) {
1349     case BINDSTATUS_MIMETYPEAVAILABLE:
1350         CHECK_EXPECT(ReportProgress_MIMETYPEAVAILABLE);
1351         ok(!strcmp_wa(szStatusText, current_binding_test->mime), "status text %s\n", wine_dbgstr_w(szStatusText));
1352         return S_OK;
1353     case BINDSTATUS_CACHEFILENAMEAVAILABLE:
1354         CHECK_EXPECT(ReportProgress_CACHEFILENAMEAVAILABLE);
1355         return S_OK;
1356     default:
1357         ok(0, "unexpected call %u %s\n", ulStatusCode, wine_dbgstr_w(szStatusText));
1358     }
1359 
1360     return E_NOTIMPL;
1361 }
1362 
1363 static HRESULT WINAPI ProtocolSink_ReportData(IInternetProtocolSink *iface, DWORD grfBSCF, ULONG ulProgress,
1364         ULONG ulProgressMax)
1365 {
1366     char buf[1024];
1367     DWORD read;
1368     HRESULT hres;
1369 
1370     CHECK_EXPECT(ReportData);
1371 
1372     ok(!ulProgress, "ulProgress = %u\n", ulProgress);
1373     ok(ulProgress == ulProgressMax, "ulProgress != ulProgressMax\n");
1374     ok(grfBSCF == (BSCF_FIRSTDATANOTIFICATION | BSCF_INTERMEDIATEDATANOTIFICATION
1375                    | BSCF_LASTDATANOTIFICATION | BSCF_DATAFULLYAVAILABLE | BSCF_AVAILABLEDATASIZEUNKNOWN),
1376             "grcf = %08x\n", grfBSCF);
1377 
1378     hres = IInternetProtocol_Read(current_binding_protocol, buf, sizeof(buf), &read);
1379     ok(hres == S_OK, "Read failed: %08x\n", hres);
1380     buf[read] = 0;
1381     ok(!strcmp(buf, current_binding_test->data), "unexpected data: %s\n", buf);
1382 
1383     hres = IInternetProtocol_Read(current_binding_protocol, buf, sizeof(buf), &read);
1384     ok(hres == S_FALSE, "Read failed: %08x\n", hres);
1385     return S_OK;
1386 }
1387 
1388 static HRESULT WINAPI ProtocolSink_ReportResult(IInternetProtocolSink *iface, HRESULT hrResult, DWORD dwError,
1389         LPCWSTR szResult)
1390 {
1391     CHECK_EXPECT(ReportResult);
1392     ok(hrResult == S_OK, "hrResult = %08x\n", hrResult);
1393     ok(!dwError, "dwError = %u\n", dwError);
1394     ok(!szResult, "szResult = %s\n", wine_dbgstr_w(szResult));
1395     return S_OK;
1396 }
1397 
1398 static IInternetProtocolSinkVtbl InternetProtocolSinkVtbl = {
1399     ProtocolSink_QueryInterface,
1400     ProtocolSink_AddRef,
1401     ProtocolSink_Release,
1402     ProtocolSink_Switch,
1403     ProtocolSink_ReportProgress,
1404     ProtocolSink_ReportData,
1405     ProtocolSink_ReportResult
1406 };
1407 
1408 static IInternetProtocolSink protocol_sink = { &InternetProtocolSinkVtbl };
1409 
1410 static void test_mhtml_protocol_binding(const mhtml_binding_test_t *test)
1411 {
1412     char file_name[MAX_PATH+32], *p, urla[INTERNET_MAX_URL_LENGTH];
1413     WCHAR test_url[INTERNET_MAX_URL_LENGTH];
1414     IInternetProtocol *protocol;
1415     IUnknown *unk;
1416     HRESULT hres;
1417     HANDLE file;
1418     DWORD size;
1419     BOOL ret;
1420 
1421     p = file_name + GetCurrentDirectoryA(sizeof(file_name), file_name);
1422     *p++ = '\\';
1423     strcpy(p, "winetest.mht");
1424 
1425     file = CreateFileA(file_name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
1426                        FILE_ATTRIBUTE_NORMAL, NULL);
1427     ok(file != INVALID_HANDLE_VALUE, "CreateFile failed\n");
1428 
1429     WriteFile(file, test->content, strlen(test->content), &size, NULL);
1430     CloseHandle(file);
1431 
1432     sprintf(urla, test->url, file_name);
1433     MultiByteToWideChar(CP_ACP, 0, urla, -1, test_url, sizeof(test_url)/sizeof(WCHAR));
1434 
1435     hres = CoCreateInstance(&CLSID_IMimeHtmlProtocol, NULL, CLSCTX_INPROC_SERVER, &IID_IInternetProtocol, (void**)&protocol);
1436     ok(hres == S_OK, "Could not create protocol handler: %08x\n", hres);
1437 
1438     hres = IInternetProtocol_QueryInterface(protocol, &IID_IInternetProtocolEx, (void**)&unk);
1439     ok(hres == E_NOINTERFACE, "Could get IInternetProtocolEx\n");
1440 
1441     current_binding_test = test;
1442     current_binding_protocol = protocol;
1443 
1444     SET_EXPECT(GetBindInfo);
1445     SET_EXPECT(ReportProgress_MIMETYPEAVAILABLE);
1446     SET_EXPECT(ReportProgress_CACHEFILENAMEAVAILABLE);
1447     SET_EXPECT(ReportData);
1448     SET_EXPECT(ReportResult);
1449     hres = IInternetProtocol_Start(protocol, test_url, &protocol_sink, &bind_info, 0, 0);
1450     ok(hres == S_OK, "Start failed: %08x\n", hres);
1451     CHECK_CALLED(GetBindInfo);
1452     CHECK_CALLED(ReportProgress_MIMETYPEAVAILABLE);
1453     todo_wine CHECK_CALLED(ReportProgress_CACHEFILENAMEAVAILABLE);
1454     CHECK_CALLED(ReportData);
1455     CHECK_CALLED(ReportResult);
1456 
1457     IInternetProtocol_Release(protocol);
1458     ret = DeleteFileA("winetest.mht");
1459     ok(ret, "DeleteFile failed: %u\n", GetLastError());
1460 }
1461 
1462 static const struct {
1463     const char *base_url;
1464     const char *relative_url;
1465     const char *expected_result;
1466     BOOL todo;
1467 } combine_tests[] = {
1468     {
1469         "mhtml:file:///c:/dir/test.mht", "http://test.org",
1470         "mhtml:file:///c:/dir/test.mht!x-usc:http://test.org"
1471     }, {
1472         "mhtml:file:///c:/dir/test.mht", "3D\"http://test.org\"",
1473         "mhtml:file:///c:/dir/test.mht!x-usc:3D\"http://test.org\""
1474     }, {
1475         "mhtml:file:///c:/dir/test.mht", "123abc",
1476         "mhtml:file:///c:/dir/test.mht!x-usc:123abc"
1477     }, {
1478         "mhtml:file:///c:/dir/test.mht!x-usc:http://test.org", "123abc",
1479         "mhtml:file:///c:/dir/test.mht!x-usc:123abc"
1480     }, {
1481         "MhtMl:file:///c:/dir/test.mht!x-usc:http://test.org/dir/dir2/file.html", "../..",
1482         "mhtml:file:///c:/dir/test.mht!x-usc:../.."
1483     }, {"mhtml:file:///c:/dir/test.mht!x-usc:file:///c:/dir/dir2/file.html", "../..",
1484         "mhtml:file:///c:/dir/test.mht!x-usc:../.."
1485     }, {
1486         "mhtml:file:///c:/dir/test.mht!x-usc:http://test.org", "",
1487         "mhtml:file:///c:/dir/test.mht"
1488     }, {
1489         "mhtml:file:///c:/dir/test.mht!x-usc:http://test.org", "mhtml:file:///d:/file.html",
1490         "file:///d:/file.html", TRUE
1491     }, {
1492         "mhtml:file:///c:/dir/test.mht!x-usc:http://test.org", "mhtml:file:///c:/dir2/test.mht!x-usc:http://test.org",
1493         "mhtml:file:///c:/dir2/test.mht!x-usc:http://test.org", TRUE
1494     }, {
1495         "mhtml:file:///c:/dir/test.mht!http://test.org", "123abc",
1496         "mhtml:file:///c:/dir/test.mht!x-usc:123abc"
1497     }, {
1498         "mhtml:file:///c:/dir/test.mht!http://test.org", "",
1499         "mhtml:file:///c:/dir/test.mht"
1500     }
1501 };
1502 
1503 static void test_mhtml_protocol_info(void)
1504 {
1505     WCHAR *base_url, *relative_url, combined_url[INTERNET_MAX_URL_LENGTH];
1506     IInternetProtocolInfo *protocol_info;
1507     DWORD combined_len;
1508     unsigned i, exlen;
1509     HRESULT hres;
1510 
1511     static const WCHAR http_url[] = {'h','t','t','p',':','/','/','t','e','s','t','.','o','r','g',0};
1512 
1513     hres = CoCreateInstance(&CLSID_IMimeHtmlProtocol, NULL, CLSCTX_INPROC_SERVER,
1514                             &IID_IInternetProtocolInfo, (void**)&protocol_info);
1515     ok(hres == S_OK, "Could not create protocol info: %08x\n", hres);
1516 
1517     for(i = 0; i < sizeof(combine_tests)/sizeof(*combine_tests); i++) {
1518         base_url = a2w(combine_tests[i].base_url);
1519         relative_url = a2w(combine_tests[i].relative_url);
1520 
1521         combined_len = 0xdeadbeef;
1522         hres = IInternetProtocolInfo_CombineUrl(protocol_info, base_url, relative_url, ICU_BROWSER_MODE,
1523                                                 combined_url, sizeof(combined_url)/sizeof(WCHAR), &combined_len, 0);
1524         todo_wine_if(combine_tests[i].todo)
1525         ok(hres == S_OK, "[%u] CombineUrl failed: %08x\n", i, hres);
1526         if(SUCCEEDED(hres)) {
1527             exlen = strlen(combine_tests[i].expected_result);
1528             ok(combined_len == exlen, "[%u] combined len is %u, expected %u\n", i, combined_len, exlen);
1529             ok(!strcmp_wa(combined_url, combine_tests[i].expected_result), "[%u] combined URL is %s, expected %s\n",
1530                i, wine_dbgstr_w(combined_url), combine_tests[i].expected_result);
1531 
1532             combined_len = 0xdeadbeef;
1533             hres = IInternetProtocolInfo_CombineUrl(protocol_info, base_url, relative_url, ICU_BROWSER_MODE,
1534                                                     combined_url, exlen, &combined_len, 0);
1535             ok(hres == E_FAIL, "[%u] CombineUrl returned: %08x\n", i, hres);
1536             ok(!combined_len, "[%u] combined_len = %u\n", i, combined_len);
1537         }
1538 
1539         HeapFree(GetProcessHeap(), 0, base_url);
1540         HeapFree(GetProcessHeap(), 0, relative_url);
1541     }
1542 
1543     hres = IInternetProtocolInfo_CombineUrl(protocol_info, http_url, http_url, ICU_BROWSER_MODE,
1544                                             combined_url, sizeof(combined_url)/sizeof(WCHAR), &combined_len, 0);
1545     ok(hres == E_FAIL, "CombineUrl failed: %08x\n", hres);
1546 
1547     IInternetProtocolInfo_Release(protocol_info);
1548 }
1549 
1550 static HRESULT WINAPI outer_QueryInterface(IUnknown *iface, REFIID riid, void **ppv)
1551 {
1552     ok(0, "unexpected call\n");
1553     return E_NOINTERFACE;
1554 }
1555 
1556 static ULONG WINAPI outer_AddRef(IUnknown *iface)
1557 {
1558     return 2;
1559 }
1560 
1561 static ULONG WINAPI outer_Release(IUnknown *iface)
1562 {
1563     return 1;
1564 }
1565 
1566 static /* const */ IUnknownVtbl outer_vtbl = {
1567     outer_QueryInterface,
1568     outer_AddRef,
1569     outer_Release
1570 };
1571 
1572 static BOOL broken_mhtml_resolver;
1573 
1574 static void test_mhtml_protocol(void)
1575 {
1576     IUnknown outer = { &outer_vtbl };
1577     IClassFactory *class_factory;
1578     IUnknown *unk, *unk2;
1579     unsigned i;
1580     HRESULT hres;
1581 
1582     /* test class factory */
1583     hres = CoGetClassObject(&CLSID_IMimeHtmlProtocol, CLSCTX_INPROC_SERVER, NULL, &IID_IUnknown, (void**)&unk);
1584     ok(hres == S_OK, "CoGetClassObject failed: %08x\n", hres);
1585 
1586     hres = IUnknown_QueryInterface(unk, &IID_IInternetProtocolInfo, (void**)&unk2);
1587     ok(hres == E_NOINTERFACE, "IInternetProtocolInfo supported\n");
1588 
1589     hres = IUnknown_QueryInterface(unk, &IID_IClassFactory, (void**)&class_factory);
1590     ok(hres == S_OK, "Could not get IClassFactory iface: %08x\n", hres);
1591     IUnknown_Release(unk);
1592 
1593     hres = IClassFactory_CreateInstance(class_factory, &outer, &IID_IUnknown, (void**)&unk);
1594     ok(hres == S_OK, "CreateInstance returned: %08x\n", hres);
1595     hres = IUnknown_QueryInterface(unk, &IID_IInternetProtocol, (void**)&unk2);
1596     ok(hres == S_OK, "Could not get IInternetProtocol iface: %08x\n", hres);
1597     IUnknown_Release(unk2);
1598     IUnknown_Release(unk);
1599 
1600     hres = IClassFactory_CreateInstance(class_factory, (IUnknown*)0xdeadbeef, &IID_IInternetProtocol, (void**)&unk2);
1601     ok(hres == CLASS_E_NOAGGREGATION, "CreateInstance returned: %08x\n", hres);
1602 
1603     IClassFactory_Release(class_factory);
1604 
1605     if(!broken_mhtml_resolver)
1606         test_mhtml_protocol_info();
1607 
1608     for(i = 0; i < sizeof(binding_tests)/sizeof(*binding_tests); i++)
1609         test_mhtml_protocol_binding(binding_tests + i);
1610 }
1611 
1612 static void test_MimeOleObjectFromMoniker(void)
1613 {
1614     IMoniker *mon, *new_mon;
1615     WCHAR *mhtml_url, *url;
1616     IBindCtx *bind_ctx;
1617     IUnknown *unk;
1618     unsigned i;
1619     HRESULT hres;
1620 
1621     static const struct {
1622         const char *url;
1623         const char *mhtml_url;
1624     } tests[] = {
1625         {"file:///x:\\dir\\file.mht", "mhtml:file://x:\\dir\\file.mht"},
1626         {"file:///x:/dir/file.mht", "mhtml:file://x:\\dir\\file.mht"},
1627         {"http://www.winehq.org/index.html?query#hash", "mhtml:http://www.winehq.org/index.html?query#hash"},
1628         {"../test.mht", "mhtml:../test.mht"}
1629     };
1630 
1631     for(i = 0; i < sizeof(tests)/sizeof(*tests); i++) {
1632         url = a2w(tests[i].url);
1633         hres = CreateURLMoniker(NULL, url, &mon);
1634         ok(hres == S_OK, "CreateURLMoniker failed: %08x\n", hres);
1635         HeapFree(GetProcessHeap(), 0, url);
1636 
1637         hres = CreateBindCtx(0, &bind_ctx);
1638         ok(hres == S_OK, "CreateBindCtx failed: %08x\n", hres);
1639 
1640         hres = MimeOleObjectFromMoniker(0, mon, bind_ctx, &IID_IUnknown, (void**)&unk, &new_mon);
1641         ok(hres == S_OK || broken(!i && hres == INET_E_RESOURCE_NOT_FOUND), "MimeOleObjectFromMoniker failed: %08x\n", hres);
1642         IBindCtx_Release(bind_ctx);
1643         if(hres == INET_E_RESOURCE_NOT_FOUND) { /* winxp */
1644             win_skip("Broken MHTML behaviour found. Skipping some tests.\n");
1645             broken_mhtml_resolver = TRUE;
1646             return;
1647         }
1648 
1649         hres = IMoniker_GetDisplayName(new_mon, NULL, NULL, &mhtml_url);
1650         ok(hres == S_OK, "GetDisplayName failed: %08x\n", hres);
1651         ok(!strcmp_wa(mhtml_url, tests[i].mhtml_url), "[%d] unexpected mhtml URL: %s\n", i, wine_dbgstr_w(mhtml_url));
1652         CoTaskMemFree(mhtml_url);
1653 
1654         IUnknown_Release(unk);
1655         IMoniker_Release(new_mon);
1656         IMoniker_Release(mon);
1657     }
1658 }
1659 
1660 START_TEST(mimeole)
1661 {
1662     OleInitialize(NULL);
1663     test_CreateVirtualStream();
1664     test_CreateSecurity();
1665     test_CreateBody();
1666     test_SetData();
1667     test_Allocator();
1668     test_CreateMessage();
1669     test_MessageSetProp();
1670     test_MessageGetPropInfo();
1671     test_MessageOptions();
1672     test_BindToObject();
1673     test_BodyDeleteProp();
1674     test_MimeOleGetPropertySchema();
1675     test_mhtml_message();
1676     test_MimeOleObjectFromMoniker();
1677     test_mhtml_protocol();
1678     OleUninitialize();
1679 }
1680