xref: /reactos/dll/win32/inetcomm/mimeole.c (revision 19b18ce2)
1 /*
2  * MIME OLE Interfaces
3  *
4  * Copyright 2006 Robert Shearman for CodeWeavers
5  * Copyright 2007 Huw Davies for CodeWeavers
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 
22 #define COBJMACROS
23 #define NONAMELESSUNION
24 
25 #include <stdarg.h>
26 #include <stdio.h>
27 
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winuser.h"
31 #include "objbase.h"
32 #include "ole2.h"
33 #include "mimeole.h"
34 #ifdef __REACTOS__
35 #include <winreg.h>
36 #endif
37 #include "propvarutil.h"
38 
39 #include "wine/heap.h"
40 #include "wine/list.h"
41 #include "wine/debug.h"
42 #include "wine/unicode.h"
43 
44 #include "inetcomm_private.h"
45 
46 WINE_DEFAULT_DEBUG_CHANNEL(inetcomm);
47 
48 typedef struct
49 {
50     LPCSTR     name;
51     DWORD      id;
52     DWORD      flags; /* MIMEPROPFLAGS */
53     VARTYPE    default_vt;
54 } property_t;
55 
56 typedef struct
57 {
58     struct list entry;
59     property_t prop;
60 } property_list_entry_t;
61 
62 static const property_t default_props[] =
63 {
64     {"X-Newsgroup",                  PID_HDR_NEWSGROUP,    0,                               VT_LPSTR},
65     {"Newsgroups",                   PID_HDR_NEWSGROUPS,   0,                               VT_LPSTR},
66     {"References",                   PID_HDR_REFS,         0,                               VT_LPSTR},
67     {"Subject",                      PID_HDR_SUBJECT,      0,                               VT_LPSTR},
68     {"From",                         PID_HDR_FROM,         MPF_ADDRESS,                     VT_LPSTR},
69     {"Message-ID",                   PID_HDR_MESSAGEID,    0,                               VT_LPSTR},
70     {"Return-Path",                  PID_HDR_RETURNPATH,   MPF_ADDRESS,                     VT_LPSTR},
71     {"Rr",                           PID_HDR_RR,           0,                               VT_LPSTR},
72     {"Return-Receipt-To",            PID_HDR_RETRCPTO,     MPF_ADDRESS,                     VT_LPSTR},
73     {"Apparently-To",                PID_HDR_APPARTO,      MPF_ADDRESS,                     VT_LPSTR},
74     {"Date",                         PID_HDR_DATE,         0,                               VT_LPSTR},
75     {"Received",                     PID_HDR_RECEIVED,     0,                               VT_LPSTR},
76     {"Reply-To",                     PID_HDR_REPLYTO,      MPF_ADDRESS,                     VT_LPSTR},
77     {"X-Mailer",                     PID_HDR_XMAILER,      0,                               VT_LPSTR},
78     {"Bcc",                          PID_HDR_BCC,          MPF_ADDRESS,                     VT_LPSTR},
79     {"MIME-Version",                 PID_HDR_MIMEVER,      MPF_MIME,                        VT_LPSTR},
80     {"Content-Type",                 PID_HDR_CNTTYPE,      MPF_MIME | MPF_HASPARAMS,        VT_LPSTR},
81     {"Content-Transfer-Encoding",    PID_HDR_CNTXFER,      MPF_MIME,                        VT_LPSTR},
82     {"Content-ID",                   PID_HDR_CNTID,        MPF_MIME,                        VT_LPSTR},
83     {"Content-Description",          PID_HDR_CNTDESC,      MPF_MIME,                        VT_LPSTR},
84     {"Content-Disposition",          PID_HDR_CNTDISP,      MPF_MIME | MPF_HASPARAMS,        VT_LPSTR},
85     {"Content-Base",                 PID_HDR_CNTBASE,      MPF_MIME,                        VT_LPSTR},
86     {"Content-Location",             PID_HDR_CNTLOC,       MPF_MIME,                        VT_LPSTR},
87     {"To",                           PID_HDR_TO,           MPF_ADDRESS,                     VT_LPSTR},
88     {"Path",                         PID_HDR_PATH,         0,                               VT_LPSTR},
89     {"Followup-To",                  PID_HDR_FOLLOWUPTO,   0,                               VT_LPSTR},
90     {"Expires",                      PID_HDR_EXPIRES,      0,                               VT_LPSTR},
91     {"Cc",                           PID_HDR_CC,           MPF_ADDRESS,                     VT_LPSTR},
92     {"Control",                      PID_HDR_CONTROL,      0,                               VT_LPSTR},
93     {"Distribution",                 PID_HDR_DISTRIB,      0,                               VT_LPSTR},
94     {"Keywords",                     PID_HDR_KEYWORDS,     0,                               VT_LPSTR},
95     {"Summary",                      PID_HDR_SUMMARY,      0,                               VT_LPSTR},
96     {"Approved",                     PID_HDR_APPROVED,     0,                               VT_LPSTR},
97     {"Lines",                        PID_HDR_LINES,        0,                               VT_LPSTR},
98     {"Xref",                         PID_HDR_XREF,         0,                               VT_LPSTR},
99     {"Organization",                 PID_HDR_ORG,          0,                               VT_LPSTR},
100     {"X-Newsreader",                 PID_HDR_XNEWSRDR,     0,                               VT_LPSTR},
101     {"X-Priority",                   PID_HDR_XPRI,         0,                               VT_LPSTR},
102     {"X-MSMail-Priority",            PID_HDR_XMSPRI,       0,                               VT_LPSTR},
103     {"par:content-disposition:filename", PID_PAR_FILENAME, 0,                               VT_LPSTR},
104     {"par:content-type:boundary",    PID_PAR_BOUNDARY,     0,                               VT_LPSTR},
105     {"par:content-type:charset",     PID_PAR_CHARSET,      0,                               VT_LPSTR},
106     {"par:content-type:name",        PID_PAR_NAME,         0,                               VT_LPSTR},
107     {"att:filename",                 PID_ATT_FILENAME,     0,                               VT_LPSTR},
108     {"att:pri-content-type",         PID_ATT_PRITYPE,      0,                               VT_LPSTR},
109     {"att:sub-content-type",         PID_ATT_SUBTYPE,      0,                               VT_LPSTR},
110     {"att:illegal-lines",            PID_ATT_ILLEGAL,      0,                               VT_LPSTR},
111     {"att:rendered",                 PID_ATT_RENDERED,     0,                               VT_LPSTR},
112     {"att:sent-time",                PID_ATT_SENTTIME,     0,                               VT_LPSTR},
113     {"att:priority",                 PID_ATT_PRIORITY,     0,                               VT_LPSTR},
114     {"Comment",                      PID_HDR_COMMENT,      0,                               VT_LPSTR},
115     {"Encoding",                     PID_HDR_ENCODING,     0,                               VT_LPSTR},
116     {"Encrypted",                    PID_HDR_ENCRYPTED,    0,                               VT_LPSTR},
117     {"X-Offsets",                    PID_HDR_OFFSETS,      0,                               VT_LPSTR},
118     {"X-Unsent",                     PID_HDR_XUNSENT,      0,                               VT_LPSTR},
119     {"X-ArticleId",                  PID_HDR_ARTICLEID,    0,                               VT_LPSTR},
120     {"Sender",                       PID_HDR_SENDER,       MPF_ADDRESS,                     VT_LPSTR},
121     {"att:athena-server",            PID_ATT_SERVER,       0,                               VT_LPSTR},
122     {"att:athena-account-id",        PID_ATT_ACCOUNT,      0,                               VT_LPSTR},
123     {"att:athena-pop3-uidl",         PID_ATT_UIDL,         0,                               VT_LPSTR},
124     {"att:athena-store-msgid",       PID_ATT_STOREMSGID,   0,                               VT_LPSTR},
125     {"att:athena-user-name",         PID_ATT_USERNAME,     0,                               VT_LPSTR},
126     {"att:athena-forward-to",        PID_ATT_FORWARDTO,    0,                               VT_LPSTR},
127     {"att:athena-store-fdrid",       PID_ATT_STOREFOLDERID,0,                               VT_LPSTR},
128     {"att:athena-ghosted",           PID_ATT_GHOSTED,      0,                               VT_LPSTR},
129     {"att:athena-uncachedsize",      PID_ATT_UNCACHEDSIZE, 0,                               VT_LPSTR},
130     {"att:athena-combined",          PID_ATT_COMBINED,     0,                               VT_LPSTR},
131     {"att:auto-inlined",             PID_ATT_AUTOINLINED,  0,                               VT_LPSTR},
132     {"Disposition-Notification-To",  PID_HDR_DISP_NOTIFICATION_TO,  0,                      VT_LPSTR},
133     {"par:Content-Type:reply-type",  PID_PAR_REPLYTYPE,    0,                               VT_LPSTR},
134     {"par:Content-Type:format",      PID_PAR_FORMAT ,      0,                               VT_LPSTR},
135     {"att:format",                   PID_ATT_FORMAT ,      0,                               VT_LPSTR},
136     {"In-Reply-To",                  PID_HDR_INREPLYTO,    0,                               VT_LPSTR},
137     {"att:athena-account-name",      PID_ATT_ACCOUNTNAME,  0,                               VT_LPSTR},
138     {NULL,                           0,                    0,                               0}
139 };
140 
141 typedef struct
142 {
143     struct list entry;
144     char *name;
145     char *value;
146 } param_t;
147 
148 typedef struct
149 {
150     struct list entry;
151     const property_t *prop;
152     PROPVARIANT value;
153     struct list params;
154 } header_t;
155 
156 typedef struct MimeBody
157 {
158     IMimeBody IMimeBody_iface;
159     LONG ref;
160 
161     HBODY handle;
162 
163     struct list headers;
164     struct list new_props; /* FIXME: This should be in a PropertySchema */
165     DWORD next_prop_id;
166     char *content_pri_type;
167     char *content_sub_type;
168     ENCODINGTYPE encoding;
169     void *data;
170     IID data_iid;
171     BODYOFFSETS body_offsets;
172 } MimeBody;
173 
174 typedef struct
175 {
176     IStream IStream_iface;
177     LONG ref;
178     IStream *base;
179     ULARGE_INTEGER pos, start, length;
180 } sub_stream_t;
181 
182 static inline sub_stream_t *impl_from_IStream(IStream *iface)
183 {
184     return CONTAINING_RECORD(iface, sub_stream_t, IStream_iface);
185 }
186 
187 static HRESULT WINAPI sub_stream_QueryInterface(IStream *iface, REFIID riid, void **ppv)
188 {
189     sub_stream_t *This = impl_from_IStream(iface);
190 
191     TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
192     *ppv = NULL;
193 
194     if(IsEqualIID(riid, &IID_IUnknown) ||
195        IsEqualIID(riid, &IID_ISequentialStream) ||
196        IsEqualIID(riid, &IID_IStream))
197     {
198         IStream_AddRef(iface);
199         *ppv = iface;
200         return S_OK;
201     }
202     return E_NOINTERFACE;
203 }
204 
205 static ULONG WINAPI sub_stream_AddRef(IStream *iface)
206 {
207     sub_stream_t *This = impl_from_IStream(iface);
208     LONG ref = InterlockedIncrement(&This->ref);
209 
210     TRACE("(%p) ref=%d\n", This, ref);
211 
212     return ref;
213 }
214 
215 static ULONG WINAPI sub_stream_Release(IStream *iface)
216 {
217     sub_stream_t *This = impl_from_IStream(iface);
218     LONG ref = InterlockedDecrement(&This->ref);
219 
220     TRACE("(%p) ref=%d\n", This, ref);
221 
222     if(!ref)
223     {
224         IStream_Release(This->base);
225         HeapFree(GetProcessHeap(), 0, This);
226     }
227     return ref;
228 }
229 
230 static HRESULT WINAPI sub_stream_Read(
231         IStream* iface,
232         void *pv,
233         ULONG cb,
234         ULONG *pcbRead)
235 {
236     sub_stream_t *This = impl_from_IStream(iface);
237     HRESULT hr;
238     LARGE_INTEGER tmp_pos;
239 
240     TRACE("(%p, %d, %p)\n", pv, cb, pcbRead);
241 
242     tmp_pos.QuadPart = This->pos.QuadPart + This->start.QuadPart;
243     IStream_Seek(This->base, tmp_pos, STREAM_SEEK_SET, NULL);
244 
245     if(This->pos.QuadPart + cb > This->length.QuadPart)
246         cb = This->length.QuadPart - This->pos.QuadPart;
247 
248     hr = IStream_Read(This->base, pv, cb, pcbRead);
249 
250     This->pos.QuadPart += *pcbRead;
251 
252     return hr;
253 }
254 
255 static HRESULT WINAPI sub_stream_Write(
256         IStream* iface,
257         const void *pv,
258         ULONG cb,
259         ULONG *pcbWritten)
260 {
261     FIXME("stub\n");
262     return E_NOTIMPL;
263 }
264 
265 static HRESULT WINAPI sub_stream_Seek(
266         IStream* iface,
267         LARGE_INTEGER dlibMove,
268         DWORD dwOrigin,
269         ULARGE_INTEGER *plibNewPosition)
270 {
271     sub_stream_t *This = impl_from_IStream(iface);
272     LARGE_INTEGER new_pos;
273 
274     TRACE("(%08x.%08x, %x, %p)\n", dlibMove.u.HighPart, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
275 
276     switch(dwOrigin)
277     {
278     case STREAM_SEEK_SET:
279         new_pos = dlibMove;
280         break;
281     case STREAM_SEEK_CUR:
282         new_pos.QuadPart = This->pos.QuadPart + dlibMove.QuadPart;
283         break;
284     case STREAM_SEEK_END:
285         new_pos.QuadPart = This->length.QuadPart + dlibMove.QuadPart;
286         break;
287     default:
288         return STG_E_INVALIDFUNCTION;
289     }
290 
291     if(new_pos.QuadPart < 0) new_pos.QuadPart = 0;
292     else if(new_pos.QuadPart > This->length.QuadPart) new_pos.QuadPart = This->length.QuadPart;
293 
294     This->pos.QuadPart = new_pos.QuadPart;
295 
296     if(plibNewPosition) *plibNewPosition = This->pos;
297     return S_OK;
298 }
299 
300 static HRESULT WINAPI sub_stream_SetSize(
301         IStream* iface,
302         ULARGE_INTEGER libNewSize)
303 {
304     FIXME("stub\n");
305     return E_NOTIMPL;
306 }
307 
308 static HRESULT WINAPI sub_stream_CopyTo(
309         IStream* iface,
310         IStream *pstm,
311         ULARGE_INTEGER cb,
312         ULARGE_INTEGER *pcbRead,
313         ULARGE_INTEGER *pcbWritten)
314 {
315     HRESULT        hr = S_OK;
316     BYTE           tmpBuffer[128];
317     ULONG          bytesRead, bytesWritten, copySize;
318     ULARGE_INTEGER totalBytesRead;
319     ULARGE_INTEGER totalBytesWritten;
320 
321     TRACE("(%p)->(%p, %d, %p, %p)\n", iface, pstm, cb.u.LowPart, pcbRead, pcbWritten);
322 
323     totalBytesRead.QuadPart = 0;
324     totalBytesWritten.QuadPart = 0;
325 
326     while ( cb.QuadPart > 0 )
327     {
328         if ( cb.QuadPart >= sizeof(tmpBuffer) )
329             copySize = sizeof(tmpBuffer);
330         else
331             copySize = cb.u.LowPart;
332 
333         hr = IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
334         if (FAILED(hr)) break;
335 
336         totalBytesRead.QuadPart += bytesRead;
337 
338         if (bytesRead)
339         {
340             hr = IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
341             if (FAILED(hr)) break;
342             totalBytesWritten.QuadPart += bytesWritten;
343         }
344 
345         if (bytesRead != copySize)
346             cb.QuadPart = 0;
347         else
348             cb.QuadPart -= bytesRead;
349     }
350 
351     if (pcbRead) pcbRead->QuadPart = totalBytesRead.QuadPart;
352     if (pcbWritten) pcbWritten->QuadPart = totalBytesWritten.QuadPart;
353 
354     return hr;
355 }
356 
357 static HRESULT WINAPI sub_stream_Commit(
358         IStream* iface,
359         DWORD grfCommitFlags)
360 {
361     FIXME("stub\n");
362     return E_NOTIMPL;
363 }
364 
365 static HRESULT WINAPI sub_stream_Revert(
366         IStream* iface)
367 {
368     FIXME("stub\n");
369     return E_NOTIMPL;
370 }
371 
372 static HRESULT WINAPI sub_stream_LockRegion(
373         IStream* iface,
374         ULARGE_INTEGER libOffset,
375         ULARGE_INTEGER cb,
376         DWORD dwLockType)
377 {
378     FIXME("stub\n");
379     return E_NOTIMPL;
380 }
381 
382 static HRESULT WINAPI sub_stream_UnlockRegion(
383         IStream* iface,
384         ULARGE_INTEGER libOffset,
385         ULARGE_INTEGER cb,
386         DWORD dwLockType)
387 {
388     FIXME("stub\n");
389     return E_NOTIMPL;
390 }
391 
392 static HRESULT WINAPI sub_stream_Stat(
393         IStream* iface,
394         STATSTG *pstatstg,
395         DWORD grfStatFlag)
396 {
397     sub_stream_t *This = impl_from_IStream(iface);
398     FIXME("(%p)->(%p, %08x)\n", This, pstatstg, grfStatFlag);
399     memset(pstatstg, 0, sizeof(*pstatstg));
400     pstatstg->cbSize = This->length;
401     return S_OK;
402 }
403 
404 static HRESULT WINAPI sub_stream_Clone(
405         IStream* iface,
406         IStream **ppstm)
407 {
408     FIXME("stub\n");
409     return E_NOTIMPL;
410 }
411 
412 static struct IStreamVtbl sub_stream_vtbl =
413 {
414     sub_stream_QueryInterface,
415     sub_stream_AddRef,
416     sub_stream_Release,
417     sub_stream_Read,
418     sub_stream_Write,
419     sub_stream_Seek,
420     sub_stream_SetSize,
421     sub_stream_CopyTo,
422     sub_stream_Commit,
423     sub_stream_Revert,
424     sub_stream_LockRegion,
425     sub_stream_UnlockRegion,
426     sub_stream_Stat,
427     sub_stream_Clone
428 };
429 
430 static HRESULT create_sub_stream(IStream *stream, ULARGE_INTEGER start, ULARGE_INTEGER length, IStream **out)
431 {
432     sub_stream_t *This;
433 
434     *out = NULL;
435     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
436     if(!This) return E_OUTOFMEMORY;
437 
438     This->IStream_iface.lpVtbl = &sub_stream_vtbl;
439     This->ref = 1;
440     This->start = start;
441     This->length = length;
442     This->pos.QuadPart = 0;
443     IStream_AddRef(stream);
444     This->base = stream;
445 
446     *out = &This->IStream_iface;
447     return S_OK;
448 }
449 
450 static HRESULT get_stream_size(IStream *stream, ULARGE_INTEGER *size)
451 {
452     STATSTG statstg = {NULL};
453     LARGE_INTEGER zero;
454     HRESULT hres;
455 
456     hres = IStream_Stat(stream, &statstg, STATFLAG_NONAME);
457     if(SUCCEEDED(hres)) {
458         *size = statstg.cbSize;
459         return S_OK;
460     }
461 
462     zero.QuadPart = 0;
463     return IStream_Seek(stream, zero, STREAM_SEEK_END, size);
464 }
465 
466 static inline MimeBody *impl_from_IMimeBody(IMimeBody *iface)
467 {
468     return CONTAINING_RECORD(iface, MimeBody, IMimeBody_iface);
469 }
470 
471 typedef struct propschema
472 {
473     IMimePropertySchema IMimePropertySchema_iface;
474     LONG ref;
475 } propschema;
476 
477 static inline propschema *impl_from_IMimePropertySchema(IMimePropertySchema *iface)
478 {
479     return CONTAINING_RECORD(iface, propschema, IMimePropertySchema_iface);
480 }
481 
482 static LPSTR strdupA(LPCSTR str)
483 {
484     char *ret;
485     int len = strlen(str);
486     ret = HeapAlloc(GetProcessHeap(), 0, len + 1);
487     memcpy(ret, str, len + 1);
488     return ret;
489 }
490 
491 #define PARSER_BUF_SIZE 1024
492 
493 /*****************************************************
494  *        copy_headers_to_buf [internal]
495  *
496  * Copies the headers into a '\0' terminated memory block and leave
497  * the stream's current position set to after the blank line.
498  */
499 static HRESULT copy_headers_to_buf(IStream *stm, char **ptr)
500 {
501     char *buf = NULL;
502     DWORD size = PARSER_BUF_SIZE, offset = 0, last_end = 0;
503     HRESULT hr;
504     BOOL done = FALSE;
505 
506     *ptr = NULL;
507 
508     do
509     {
510         char *end;
511         DWORD read;
512 
513         if(!buf)
514             buf = HeapAlloc(GetProcessHeap(), 0, size + 1);
515         else
516         {
517             size *= 2;
518             buf = HeapReAlloc(GetProcessHeap(), 0, buf, size + 1);
519         }
520         if(!buf)
521         {
522             hr = E_OUTOFMEMORY;
523             goto fail;
524         }
525 
526         hr = IStream_Read(stm, buf + offset, size - offset, &read);
527         if(FAILED(hr)) goto fail;
528 
529         offset += read;
530         buf[offset] = '\0';
531 
532         if(read == 0) done = TRUE;
533 
534         while(!done && (end = strstr(buf + last_end, "\r\n")))
535         {
536             DWORD new_end = end - buf + 2;
537             if(new_end - last_end == 2)
538             {
539                 LARGE_INTEGER off;
540                 off.QuadPart = (LONGLONG)new_end - offset;
541                 IStream_Seek(stm, off, STREAM_SEEK_CUR, NULL);
542                 buf[new_end] = '\0';
543                 done = TRUE;
544             }
545             else
546                 last_end = new_end;
547         }
548     } while(!done);
549 
550     *ptr = buf;
551     return S_OK;
552 
553 fail:
554     HeapFree(GetProcessHeap(), 0, buf);
555     return hr;
556 }
557 
558 static header_t *read_prop(MimeBody *body, char **ptr)
559 {
560     char *colon = strchr(*ptr, ':');
561     const property_t *prop;
562     header_t *ret;
563 
564     if(!colon) return NULL;
565 
566     *colon = '\0';
567 
568     for(prop = default_props; prop->name; prop++)
569     {
570         if(!lstrcmpiA(*ptr, prop->name))
571         {
572             TRACE("%s: found match with default property id %d\n", *ptr, prop->id);
573             break;
574         }
575     }
576 
577     if(!prop->name)
578     {
579         property_list_entry_t *prop_entry;
580         LIST_FOR_EACH_ENTRY(prop_entry, &body->new_props, property_list_entry_t, entry)
581         {
582             if(!lstrcmpiA(*ptr, prop_entry->prop.name))
583             {
584                 TRACE("%s: found match with already added new property id %d\n", *ptr, prop_entry->prop.id);
585                 prop = &prop_entry->prop;
586                 break;
587             }
588         }
589         if(!prop->name)
590         {
591             prop_entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*prop_entry));
592             prop_entry->prop.name = strdupA(*ptr);
593             prop_entry->prop.id = body->next_prop_id++;
594             prop_entry->prop.flags = 0;
595             prop_entry->prop.default_vt = VT_LPSTR;
596             list_add_tail(&body->new_props, &prop_entry->entry);
597             prop = &prop_entry->prop;
598             TRACE("%s: allocating new prop id %d\n", *ptr, prop_entry->prop.id);
599         }
600     }
601 
602     ret = HeapAlloc(GetProcessHeap(), 0, sizeof(*ret));
603     ret->prop = prop;
604     PropVariantInit(&ret->value);
605     list_init(&ret->params);
606     *ptr = colon + 1;
607 
608     return ret;
609 }
610 
611 static void unfold_header(char *header, int len)
612 {
613     char *start = header, *cp = header;
614 
615     do {
616         while(*cp == ' ' || *cp == '\t')
617         {
618             cp++;
619             len--;
620         }
621         if(cp != start)
622             memmove(start, cp, len + 1);
623 
624         cp = strstr(start, "\r\n");
625         len -= (cp - start);
626         start = cp;
627         *start = ' ';
628         start++;
629         len--;
630         cp += 2;
631     } while(*cp == ' ' || *cp == '\t');
632 
633     *(start - 1) = '\0';
634 }
635 
636 static char *unquote_string(const char *str)
637 {
638     BOOL quoted = FALSE;
639     char *ret, *cp;
640 
641     while(*str == ' ' || *str == '\t') str++;
642 
643     if(*str == '"')
644     {
645         quoted = TRUE;
646         str++;
647     }
648     ret = strdupA(str);
649     for(cp = ret; *cp; cp++)
650     {
651         if(*cp == '\\')
652             memmove(cp, cp + 1, strlen(cp + 1) + 1);
653         else if(*cp == '"')
654         {
655             if(!quoted)
656             {
657                 WARN("quote in unquoted string\n");
658             }
659             else
660             {
661                 *cp = '\0';
662                 break;
663             }
664         }
665     }
666     return ret;
667 }
668 
669 static void add_param(header_t *header, const char *p)
670 {
671     const char *key = p, *value, *cp = p;
672     param_t *param;
673     char *name;
674 
675     TRACE("got param %s\n", p);
676 
677     while (*key == ' ' || *key == '\t' ) key++;
678 
679     cp = strchr(key, '=');
680     if(!cp)
681     {
682         WARN("malformed parameter - skipping\n");
683         return;
684     }
685 
686     name = HeapAlloc(GetProcessHeap(), 0, cp - key + 1);
687     memcpy(name, key, cp - key);
688     name[cp - key] = '\0';
689 
690     value = cp + 1;
691 
692     param = HeapAlloc(GetProcessHeap(), 0, sizeof(*param));
693     param->name = name;
694     param->value = unquote_string(value);
695     list_add_tail(&header->params, &param->entry);
696 }
697 
698 static void split_params(header_t *header, char *value)
699 {
700     char *cp = value, *start = value;
701     BOOL in_quotes = FALSE, done_value = FALSE;
702 
703     while(*cp)
704     {
705         if(!in_quotes && *cp == ';')
706         {
707             *cp = '\0';
708             if(done_value) add_param(header, start);
709             done_value = TRUE;
710             start = cp + 1;
711         }
712         else if(*cp == '"')
713             in_quotes = !in_quotes;
714         cp++;
715     }
716     if(done_value) add_param(header, start);
717 }
718 
719 static void read_value(header_t *header, char **cur)
720 {
721     char *end = *cur, *value;
722     DWORD len;
723 
724     do {
725         end = strstr(end, "\r\n");
726         end += 2;
727     } while(*end == ' ' || *end == '\t');
728 
729     len = end - *cur;
730     value = HeapAlloc(GetProcessHeap(), 0, len + 1);
731     memcpy(value, *cur, len);
732     value[len] = '\0';
733 
734     unfold_header(value, len);
735     TRACE("value %s\n", debugstr_a(value));
736 
737     if(header->prop->flags & MPF_HASPARAMS)
738     {
739         split_params(header, value);
740         TRACE("value w/o params %s\n", debugstr_a(value));
741     }
742 
743     header->value.vt = VT_LPSTR;
744     header->value.u.pszVal = value;
745 
746     *cur = end;
747 }
748 
749 static void init_content_type(MimeBody *body, header_t *header)
750 {
751     char *slash;
752     DWORD len;
753 
754     slash = strchr(header->value.u.pszVal, '/');
755     if(!slash)
756     {
757         WARN("malformed context type value\n");
758         return;
759     }
760     len = slash - header->value.u.pszVal;
761     body->content_pri_type = HeapAlloc(GetProcessHeap(), 0, len + 1);
762     memcpy(body->content_pri_type, header->value.u.pszVal, len);
763     body->content_pri_type[len] = '\0';
764     body->content_sub_type = strdupA(slash + 1);
765 }
766 
767 static void init_content_encoding(MimeBody *body, header_t *header)
768 {
769     const char *encoding = header->value.u.pszVal;
770 
771     if(!strcasecmp(encoding, "base64"))
772         body->encoding = IET_BASE64;
773     else if(!strcasecmp(encoding, "quoted-printable"))
774         body->encoding = IET_QP;
775     else if(!strcasecmp(encoding, "7bit"))
776         body->encoding = IET_7BIT;
777     else if(!strcasecmp(encoding, "8bit"))
778         body->encoding = IET_8BIT;
779     else
780         FIXME("unknown encoding %s\n", debugstr_a(encoding));
781 }
782 
783 static HRESULT parse_headers(MimeBody *body, IStream *stm)
784 {
785     char *header_buf, *cur_header_ptr;
786     HRESULT hr;
787     header_t *header;
788 
789     hr = copy_headers_to_buf(stm, &header_buf);
790     if(FAILED(hr)) return hr;
791 
792     cur_header_ptr = header_buf;
793     while((header = read_prop(body, &cur_header_ptr)))
794     {
795         read_value(header, &cur_header_ptr);
796         list_add_tail(&body->headers, &header->entry);
797 
798         switch(header->prop->id) {
799         case PID_HDR_CNTTYPE:
800             init_content_type(body, header);
801             break;
802         case PID_HDR_CNTXFER:
803             init_content_encoding(body, header);
804             break;
805         }
806     }
807 
808     HeapFree(GetProcessHeap(), 0, header_buf);
809     return hr;
810 }
811 
812 static void empty_param_list(struct list *list)
813 {
814     param_t *param, *cursor2;
815 
816     LIST_FOR_EACH_ENTRY_SAFE(param, cursor2, list, param_t, entry)
817     {
818         list_remove(&param->entry);
819         HeapFree(GetProcessHeap(), 0, param->name);
820         HeapFree(GetProcessHeap(), 0, param->value);
821         HeapFree(GetProcessHeap(), 0, param);
822     }
823 }
824 
825 static void empty_header_list(struct list *list)
826 {
827     header_t *header, *cursor2;
828 
829     LIST_FOR_EACH_ENTRY_SAFE(header, cursor2, list, header_t, entry)
830     {
831         list_remove(&header->entry);
832         PropVariantClear(&header->value);
833         empty_param_list(&header->params);
834         HeapFree(GetProcessHeap(), 0, header);
835     }
836 }
837 
838 static void empty_new_prop_list(struct list *list)
839 {
840     property_list_entry_t *prop, *cursor2;
841 
842     LIST_FOR_EACH_ENTRY_SAFE(prop, cursor2, list, property_list_entry_t, entry)
843     {
844         list_remove(&prop->entry);
845         HeapFree(GetProcessHeap(), 0, (char *)prop->prop.name);
846         HeapFree(GetProcessHeap(), 0, prop);
847     }
848 }
849 
850 static void release_data(REFIID riid, void *data)
851 {
852     if(!data) return;
853 
854     if(IsEqualIID(riid, &IID_IStream))
855         IStream_Release((IStream *)data);
856     else
857         FIXME("Unhandled data format %s\n", debugstr_guid(riid));
858 }
859 
860 static HRESULT find_prop(MimeBody *body, const char *name, header_t **prop)
861 {
862     header_t *header;
863 
864     *prop = NULL;
865 
866     LIST_FOR_EACH_ENTRY(header, &body->headers, header_t, entry)
867     {
868         if(ISPIDSTR(name))
869         {
870             if(STRTOPID(name) == header->prop->id)
871             {
872                 *prop = header;
873                 return S_OK;
874             }
875         }
876         else if(!lstrcmpiA(name, header->prop->name))
877         {
878             *prop = header;
879             return S_OK;
880         }
881     }
882 
883     return MIME_E_NOT_FOUND;
884 }
885 
886 static const property_t *find_default_prop(const char *name)
887 {
888     const property_t *prop_def = NULL;
889 
890     for(prop_def = default_props; prop_def->name; prop_def++)
891     {
892         if(ISPIDSTR(name))
893         {
894             if(STRTOPID(name) == prop_def->id)
895             {
896                 break;
897             }
898         }
899         else if(!lstrcmpiA(name, prop_def->name))
900         {
901             break;
902         }
903     }
904 
905     if(prop_def->id)
906        TRACE("%s: found match with default property id %d\n", prop_def->name, prop_def->id);
907     else
908        prop_def = NULL;
909 
910     return prop_def;
911 }
912 
913 static HRESULT WINAPI MimeBody_QueryInterface(IMimeBody* iface,
914                                      REFIID riid,
915                                      void** ppvObject)
916 {
917     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppvObject);
918 
919     *ppvObject = NULL;
920 
921     if (IsEqualIID(riid, &IID_IUnknown) ||
922         IsEqualIID(riid, &IID_IPersist) ||
923         IsEqualIID(riid, &IID_IPersistStreamInit) ||
924         IsEqualIID(riid, &IID_IMimePropertySet) ||
925         IsEqualIID(riid, &IID_IMimeBody))
926     {
927         *ppvObject = iface;
928     }
929 
930     if(*ppvObject)
931     {
932         IUnknown_AddRef((IUnknown*)*ppvObject);
933         return S_OK;
934     }
935 
936     FIXME("no interface for %s\n", debugstr_guid(riid));
937     return E_NOINTERFACE;
938 }
939 
940 static ULONG WINAPI MimeBody_AddRef(IMimeBody *iface)
941 {
942     MimeBody *This = impl_from_IMimeBody(iface);
943     LONG ref = InterlockedIncrement(&This->ref);
944 
945     TRACE("(%p) ref=%d\n", This, ref);
946 
947     return ref;
948 }
949 
950 static ULONG WINAPI MimeBody_Release(IMimeBody *iface)
951 {
952     MimeBody *This = impl_from_IMimeBody(iface);
953     LONG ref = InterlockedDecrement(&This->ref);
954 
955     TRACE("(%p) ref=%d\n", This, ref);
956 
957     if (!ref)
958     {
959         empty_header_list(&This->headers);
960         empty_new_prop_list(&This->new_props);
961 
962         HeapFree(GetProcessHeap(), 0, This->content_pri_type);
963         HeapFree(GetProcessHeap(), 0, This->content_sub_type);
964 
965         release_data(&This->data_iid, This->data);
966 
967         HeapFree(GetProcessHeap(), 0, This);
968     }
969 
970     return ref;
971 }
972 
973 static HRESULT WINAPI MimeBody_GetClassID(
974                                  IMimeBody* iface,
975                                  CLSID* pClassID)
976 {
977     MimeBody *This = impl_from_IMimeBody(iface);
978 
979     TRACE("(%p)->(%p)\n", This, pClassID);
980 
981     if(!pClassID)
982         return E_INVALIDARG;
983 
984     *pClassID = IID_IMimeBody;
985     return S_OK;
986 }
987 
988 static HRESULT WINAPI MimeBody_IsDirty(
989                               IMimeBody* iface)
990 {
991     MimeBody *This = impl_from_IMimeBody(iface);
992     FIXME("(%p)->() stub\n", This);
993     return E_NOTIMPL;
994 }
995 
996 static HRESULT WINAPI MimeBody_Load(IMimeBody *iface, IStream *pStm)
997 {
998     MimeBody *This = impl_from_IMimeBody(iface);
999     TRACE("(%p)->(%p)\n", This, pStm);
1000     return parse_headers(This, pStm);
1001 }
1002 
1003 static HRESULT WINAPI MimeBody_Save(IMimeBody *iface, IStream *pStm, BOOL fClearDirty)
1004 {
1005     MimeBody *This = impl_from_IMimeBody(iface);
1006     FIXME("(%p)->(%p, %d)\n", This, pStm, fClearDirty);
1007     return E_NOTIMPL;
1008 }
1009 
1010 static HRESULT WINAPI MimeBody_GetSizeMax(
1011                                  IMimeBody* iface,
1012                                  ULARGE_INTEGER* pcbSize)
1013 {
1014     MimeBody *This = impl_from_IMimeBody(iface);
1015     FIXME("(%p)->(%p) stub\n", This, pcbSize);
1016     return E_NOTIMPL;
1017 }
1018 
1019 static HRESULT WINAPI MimeBody_InitNew(
1020                               IMimeBody* iface)
1021 {
1022     MimeBody *This = impl_from_IMimeBody(iface);
1023     TRACE("(%p)->()\n", This);
1024     return S_OK;
1025 }
1026 
1027 static HRESULT WINAPI MimeBody_GetPropInfo(
1028                                   IMimeBody* iface,
1029                                   LPCSTR pszName,
1030                                   LPMIMEPROPINFO pInfo)
1031 {
1032     MimeBody *This = impl_from_IMimeBody(iface);
1033     header_t *header;
1034     HRESULT hr;
1035     DWORD supported = PIM_PROPID | PIM_VTDEFAULT;
1036 
1037     TRACE("(%p)->(%s, %p) semi-stub\n", This, debugstr_a(pszName), pInfo);
1038 
1039     if(!pszName || !pInfo)
1040         return E_INVALIDARG;
1041 
1042     TRACE("mask 0x%04x\n", pInfo->dwMask);
1043 
1044     if(pInfo->dwMask & ~supported)
1045          FIXME("Unsupported mask flags 0x%04x\n", pInfo->dwMask & ~supported);
1046 
1047     hr = find_prop(This, pszName, &header);
1048     if(hr == S_OK)
1049     {
1050         if(pInfo->dwMask & PIM_CHARSET)
1051             pInfo->hCharset = 0;
1052         if(pInfo->dwMask & PIM_FLAGS)
1053             pInfo->dwFlags = 0x00000000;
1054         if(pInfo->dwMask & PIM_ROWNUMBER)
1055             pInfo->dwRowNumber = 0;
1056         if(pInfo->dwMask & PIM_ENCODINGTYPE)
1057             pInfo->ietEncoding = 0;
1058         if(pInfo->dwMask & PIM_VALUES)
1059             pInfo->cValues = 0;
1060         if(pInfo->dwMask & PIM_PROPID)
1061             pInfo->dwPropId = header->prop->id;
1062         if(pInfo->dwMask & PIM_VTDEFAULT)
1063             pInfo->vtDefault = header->prop->default_vt;
1064         if(pInfo->dwMask & PIM_VTCURRENT)
1065             pInfo->vtCurrent = 0;
1066     }
1067 
1068     return hr;
1069 }
1070 
1071 static HRESULT WINAPI MimeBody_SetPropInfo(
1072                                   IMimeBody* iface,
1073                                   LPCSTR pszName,
1074                                   LPCMIMEPROPINFO pInfo)
1075 {
1076     MimeBody *This = impl_from_IMimeBody(iface);
1077     FIXME("(%p)->(%s, %p) stub\n", This, debugstr_a(pszName), pInfo);
1078     return E_NOTIMPL;
1079 }
1080 
1081 static HRESULT WINAPI MimeBody_GetProp(
1082                               IMimeBody* iface,
1083                               LPCSTR pszName,
1084                               DWORD dwFlags,
1085                               LPPROPVARIANT pValue)
1086 {
1087     MimeBody *This = impl_from_IMimeBody(iface);
1088     header_t *header;
1089     HRESULT hr;
1090 
1091     TRACE("(%p)->(%s, 0x%x, %p)\n", This, debugstr_a(pszName), dwFlags, pValue);
1092 
1093     if(!pszName || !pValue)
1094         return E_INVALIDARG;
1095 
1096     if(!ISPIDSTR(pszName) && !lstrcmpiA(pszName, "att:pri-content-type"))
1097     {
1098         PropVariantClear(pValue);
1099         pValue->vt = VT_LPSTR;
1100         pValue->u.pszVal = strdupA(This->content_pri_type);
1101         return S_OK;
1102     }
1103 
1104     hr = find_prop(This, pszName, &header);
1105     if(hr == S_OK)
1106     {
1107         TRACE("type %d->%d\n", header->value.vt, pValue->vt);
1108 
1109         hr = PropVariantChangeType(pValue, &header->value, 0,  pValue->vt);
1110         if(FAILED(hr))
1111             FIXME("Conversion not currently supported (%d->%d)\n", header->value.vt, pValue->vt);
1112     }
1113 
1114     return hr;
1115 }
1116 
1117 static HRESULT WINAPI MimeBody_SetProp(
1118                               IMimeBody* iface,
1119                               LPCSTR pszName,
1120                               DWORD dwFlags,
1121                               LPCPROPVARIANT pValue)
1122 {
1123     MimeBody *This = impl_from_IMimeBody(iface);
1124     header_t *header;
1125     HRESULT hr;
1126 
1127     TRACE("(%p)->(%s, 0x%x, %p)\n", This, debugstr_a(pszName), dwFlags, pValue);
1128 
1129     if(!pszName || !pValue)
1130         return E_INVALIDARG;
1131 
1132     hr = find_prop(This, pszName, &header);
1133     if(hr != S_OK)
1134     {
1135         property_list_entry_t *prop_entry;
1136         const property_t *prop = NULL;
1137 
1138         LIST_FOR_EACH_ENTRY(prop_entry, &This->new_props, property_list_entry_t, entry)
1139         {
1140             if(ISPIDSTR(pszName))
1141             {
1142                 if(STRTOPID(pszName) == prop_entry->prop.id)
1143                 {
1144                     TRACE("Found match with already added new property id %d\n", prop_entry->prop.id);
1145                     prop = &prop_entry->prop;
1146                     break;
1147                 }
1148             }
1149             else if(!lstrcmpiA(pszName, prop_entry->prop.name))
1150             {
1151                 TRACE("Found match with already added new property id %d\n", prop_entry->prop.id);
1152                 prop = &prop_entry->prop;
1153                 break;
1154             }
1155         }
1156 
1157         header = HeapAlloc(GetProcessHeap(), 0, sizeof(*header));
1158         if(!header)
1159             return E_OUTOFMEMORY;
1160 
1161         if(!prop)
1162         {
1163             const property_t *prop_def = NULL;
1164             prop_entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*prop_entry));
1165             if(!prop_entry)
1166             {
1167                 HeapFree(GetProcessHeap(), 0, header);
1168                 return E_OUTOFMEMORY;
1169             }
1170 
1171             prop_def = find_default_prop(pszName);
1172             if(prop_def)
1173             {
1174                 prop_entry->prop.name = strdupA(prop_def->name);
1175                 prop_entry->prop.id =  prop_def->id;
1176             }
1177             else
1178             {
1179                 if(ISPIDSTR(pszName))
1180                 {
1181                     HeapFree(GetProcessHeap(), 0, prop_entry);
1182                     HeapFree(GetProcessHeap(), 0, header);
1183                     return MIME_E_NOT_FOUND;
1184                 }
1185 
1186                 prop_entry->prop.name = strdupA(pszName);
1187                 prop_entry->prop.id = This->next_prop_id++;
1188             }
1189 
1190             prop_entry->prop.flags = 0;
1191             prop_entry->prop.default_vt = pValue->vt;
1192             list_add_tail(&This->new_props, &prop_entry->entry);
1193             prop = &prop_entry->prop;
1194             TRACE("Allocating new prop id %d\n", prop_entry->prop.id);
1195         }
1196 
1197         header->prop = prop;
1198         PropVariantInit(&header->value);
1199         list_init(&header->params);
1200         list_add_tail(&This->headers, &header->entry);
1201     }
1202 
1203     PropVariantCopy(&header->value, pValue);
1204 
1205     return S_OK;
1206 }
1207 
1208 static HRESULT WINAPI MimeBody_AppendProp(
1209                                  IMimeBody* iface,
1210                                  LPCSTR pszName,
1211                                  DWORD dwFlags,
1212                                  LPPROPVARIANT pValue)
1213 {
1214     MimeBody *This = impl_from_IMimeBody(iface);
1215     FIXME("(%p)->(%s, 0x%x, %p) stub\n", This, debugstr_a(pszName), dwFlags, pValue);
1216     return E_NOTIMPL;
1217 }
1218 
1219 static HRESULT WINAPI MimeBody_DeleteProp(
1220                                  IMimeBody* iface,
1221                                  LPCSTR pszName)
1222 {
1223     MimeBody *This = impl_from_IMimeBody(iface);
1224     header_t *cursor;
1225     BOOL found;
1226 
1227     TRACE("(%p)->(%s) stub\n", This, debugstr_a(pszName));
1228 
1229     LIST_FOR_EACH_ENTRY(cursor, &This->headers, header_t, entry)
1230     {
1231         if(ISPIDSTR(pszName))
1232             found = STRTOPID(pszName) == cursor->prop->id;
1233         else
1234             found = !lstrcmpiA(pszName, cursor->prop->name);
1235 
1236         if(found)
1237         {
1238              list_remove(&cursor->entry);
1239              HeapFree(GetProcessHeap(), 0, cursor);
1240              return S_OK;
1241         }
1242     }
1243 
1244     return MIME_E_NOT_FOUND;
1245 }
1246 
1247 static HRESULT WINAPI MimeBody_CopyProps(
1248                                 IMimeBody* iface,
1249                                 ULONG cNames,
1250                                 LPCSTR* prgszName,
1251                                 IMimePropertySet* pPropertySet)
1252 {
1253     MimeBody *This = impl_from_IMimeBody(iface);
1254     FIXME("(%p)->(%d, %p, %p) stub\n", This, cNames, prgszName, pPropertySet);
1255     return E_NOTIMPL;
1256 }
1257 
1258 static HRESULT WINAPI MimeBody_MoveProps(
1259                                 IMimeBody* iface,
1260                                 ULONG cNames,
1261                                 LPCSTR* prgszName,
1262                                 IMimePropertySet* pPropertySet)
1263 {
1264     MimeBody *This = impl_from_IMimeBody(iface);
1265     FIXME("(%p)->(%d, %p, %p) stub\n", This, cNames, prgszName, pPropertySet);
1266     return E_NOTIMPL;
1267 }
1268 
1269 static HRESULT WINAPI MimeBody_DeleteExcept(
1270                                    IMimeBody* iface,
1271                                    ULONG cNames,
1272                                    LPCSTR* prgszName)
1273 {
1274     MimeBody *This = impl_from_IMimeBody(iface);
1275     FIXME("(%p)->(%d, %p) stub\n", This, cNames, prgszName);
1276     return E_NOTIMPL;
1277 }
1278 
1279 static HRESULT WINAPI MimeBody_QueryProp(
1280                                 IMimeBody* iface,
1281                                 LPCSTR pszName,
1282                                 LPCSTR pszCriteria,
1283                                 boolean fSubString,
1284                                 boolean fCaseSensitive)
1285 {
1286     MimeBody *This = impl_from_IMimeBody(iface);
1287     FIXME("(%p)->(%s, %s, %d, %d) stub\n", This, debugstr_a(pszName), debugstr_a(pszCriteria), fSubString, fCaseSensitive);
1288     return E_NOTIMPL;
1289 }
1290 
1291 static HRESULT WINAPI MimeBody_GetCharset(
1292                                  IMimeBody* iface,
1293                                  LPHCHARSET phCharset)
1294 {
1295     MimeBody *This = impl_from_IMimeBody(iface);
1296     FIXME("(%p)->(%p) stub\n", This, phCharset);
1297     *phCharset = NULL;
1298     return S_OK;
1299 }
1300 
1301 static HRESULT WINAPI MimeBody_SetCharset(
1302                                  IMimeBody* iface,
1303                                  HCHARSET hCharset,
1304                                  CSETAPPLYTYPE applytype)
1305 {
1306     MimeBody *This = impl_from_IMimeBody(iface);
1307     FIXME("(%p)->(%p, %d) stub\n", This, hCharset, applytype);
1308     return E_NOTIMPL;
1309 }
1310 
1311 static HRESULT WINAPI MimeBody_GetParameters(
1312                                     IMimeBody* iface,
1313                                     LPCSTR pszName,
1314                                     ULONG* pcParams,
1315                                     LPMIMEPARAMINFO* pprgParam)
1316 {
1317     MimeBody *This = impl_from_IMimeBody(iface);
1318     HRESULT hr;
1319     header_t *header;
1320 
1321     TRACE("(%p)->(%s, %p, %p)\n", iface, debugstr_a(pszName), pcParams, pprgParam);
1322 
1323     *pprgParam = NULL;
1324     *pcParams = 0;
1325 
1326     hr = find_prop(This, pszName, &header);
1327     if(hr != S_OK) return hr;
1328 
1329     *pcParams = list_count(&header->params);
1330     if(*pcParams)
1331     {
1332         IMimeAllocator *alloc;
1333         param_t *param;
1334         MIMEPARAMINFO *info;
1335 
1336         MimeOleGetAllocator(&alloc);
1337 
1338         *pprgParam = info = IMimeAllocator_Alloc(alloc, *pcParams * sizeof(**pprgParam));
1339         LIST_FOR_EACH_ENTRY(param, &header->params, param_t, entry)
1340         {
1341             int len;
1342 
1343             len = strlen(param->name) + 1;
1344             info->pszName = IMimeAllocator_Alloc(alloc, len);
1345             memcpy(info->pszName, param->name, len);
1346             len = strlen(param->value) + 1;
1347             info->pszData = IMimeAllocator_Alloc(alloc, len);
1348             memcpy(info->pszData, param->value, len);
1349             info++;
1350         }
1351         IMimeAllocator_Release(alloc);
1352     }
1353     return S_OK;
1354 }
1355 
1356 static HRESULT WINAPI MimeBody_IsContentType(
1357                                     IMimeBody* iface,
1358                                     LPCSTR pszPriType,
1359                                     LPCSTR pszSubType)
1360 {
1361     MimeBody *This = impl_from_IMimeBody(iface);
1362 
1363     TRACE("(%p)->(%s, %s)\n", This, debugstr_a(pszPriType), debugstr_a(pszSubType));
1364     if(pszPriType)
1365     {
1366         const char *pri = This->content_pri_type;
1367         if(!pri) pri = "text";
1368         if(lstrcmpiA(pri, pszPriType)) return S_FALSE;
1369     }
1370 
1371     if(pszSubType)
1372     {
1373         const char *sub = This->content_sub_type;
1374         if(!sub) sub = "plain";
1375         if(lstrcmpiA(sub, pszSubType)) return S_FALSE;
1376     }
1377 
1378     return S_OK;
1379 }
1380 
1381 static HRESULT WINAPI MimeBody_BindToObject(
1382                                    IMimeBody* iface,
1383                                    REFIID riid,
1384                                    void** ppvObject)
1385 {
1386     MimeBody *This = impl_from_IMimeBody(iface);
1387     FIXME("(%p)->(%s, %p) stub\n", This, debugstr_guid(riid), ppvObject);
1388     return E_NOTIMPL;
1389 }
1390 
1391 static HRESULT WINAPI MimeBody_Clone(
1392                             IMimeBody* iface,
1393                             IMimePropertySet** ppPropertySet)
1394 {
1395     MimeBody *This = impl_from_IMimeBody(iface);
1396     FIXME("(%p)->(%p) stub\n", This, ppPropertySet);
1397     return E_NOTIMPL;
1398 }
1399 
1400 static HRESULT WINAPI MimeBody_SetOption(
1401                                 IMimeBody* iface,
1402                                 const TYPEDID oid,
1403                                 LPCPROPVARIANT pValue)
1404 {
1405     MimeBody *This = impl_from_IMimeBody(iface);
1406     HRESULT hr = E_NOTIMPL;
1407     TRACE("(%p)->(%08x, %p)\n", This, oid, pValue);
1408 
1409     if(pValue->vt != TYPEDID_TYPE(oid))
1410     {
1411         WARN("Called with vartype %04x and oid %08x\n", pValue->vt, oid);
1412         return E_INVALIDARG;
1413     }
1414 
1415     switch(oid)
1416     {
1417     case OID_SECURITY_HWND_OWNER:
1418         FIXME("OID_SECURITY_HWND_OWNER (value %08x): ignoring\n", pValue->u.ulVal);
1419         hr = S_OK;
1420         break;
1421     case OID_TRANSMIT_BODY_ENCODING:
1422         FIXME("OID_TRANSMIT_BODY_ENCODING (value %08x): ignoring\n", pValue->u.ulVal);
1423         hr = S_OK;
1424         break;
1425     default:
1426         FIXME("Unhandled oid %08x\n", oid);
1427     }
1428 
1429     return hr;
1430 }
1431 
1432 static HRESULT WINAPI MimeBody_GetOption(
1433                                 IMimeBody* iface,
1434                                 const TYPEDID oid,
1435                                 LPPROPVARIANT pValue)
1436 {
1437     MimeBody *This = impl_from_IMimeBody(iface);
1438     FIXME("(%p)->(%08x, %p): stub\n", This, oid, pValue);
1439     return E_NOTIMPL;
1440 }
1441 
1442 static HRESULT WINAPI MimeBody_EnumProps(
1443                                 IMimeBody* iface,
1444                                 DWORD dwFlags,
1445                                 IMimeEnumProperties** ppEnum)
1446 {
1447     MimeBody *This = impl_from_IMimeBody(iface);
1448     FIXME("(%p)->(0x%x, %p) stub\n", This, dwFlags, ppEnum);
1449     return E_NOTIMPL;
1450 }
1451 
1452 static HRESULT WINAPI MimeBody_IsType(
1453                              IMimeBody* iface,
1454                              IMSGBODYTYPE bodytype)
1455 {
1456     MimeBody *This = impl_from_IMimeBody(iface);
1457 
1458     TRACE("(%p)->(%d)\n", This, bodytype);
1459     switch(bodytype)
1460     {
1461     case IBT_EMPTY:
1462         return This->data ? S_FALSE : S_OK;
1463     default:
1464         FIXME("Unimplemented bodytype %d - returning S_OK\n", bodytype);
1465     }
1466     return S_OK;
1467 }
1468 
1469 static HRESULT WINAPI MimeBody_SetDisplayName(
1470                                      IMimeBody* iface,
1471                                      LPCSTR pszDisplay)
1472 {
1473     MimeBody *This = impl_from_IMimeBody(iface);
1474     FIXME("(%p)->(%s) stub\n", This, debugstr_a(pszDisplay));
1475     return E_NOTIMPL;
1476 }
1477 
1478 static HRESULT WINAPI MimeBody_GetDisplayName(
1479                                      IMimeBody* iface,
1480                                      LPSTR* ppszDisplay)
1481 {
1482     MimeBody *This = impl_from_IMimeBody(iface);
1483     FIXME("(%p)->(%p) stub\n", This, ppszDisplay);
1484     return E_NOTIMPL;
1485 }
1486 
1487 static HRESULT WINAPI MimeBody_GetOffsets(
1488                                  IMimeBody* iface,
1489                                  LPBODYOFFSETS pOffsets)
1490 {
1491     MimeBody *This = impl_from_IMimeBody(iface);
1492     TRACE("(%p)->(%p)\n", This, pOffsets);
1493 
1494     *pOffsets = This->body_offsets;
1495 
1496     if(This->body_offsets.cbBodyEnd == 0) return MIME_E_NO_DATA;
1497     return S_OK;
1498 }
1499 
1500 static HRESULT WINAPI MimeBody_GetCurrentEncoding(
1501                                          IMimeBody* iface,
1502                                          ENCODINGTYPE* pietEncoding)
1503 {
1504     MimeBody *This = impl_from_IMimeBody(iface);
1505 
1506     TRACE("(%p)->(%p)\n", This, pietEncoding);
1507 
1508     *pietEncoding = This->encoding;
1509     return S_OK;
1510 }
1511 
1512 static HRESULT WINAPI MimeBody_SetCurrentEncoding(
1513                                          IMimeBody* iface,
1514                                          ENCODINGTYPE ietEncoding)
1515 {
1516     MimeBody *This = impl_from_IMimeBody(iface);
1517 
1518     TRACE("(%p)->(%d)\n", This, ietEncoding);
1519 
1520     This->encoding = ietEncoding;
1521     return S_OK;
1522 }
1523 
1524 static HRESULT WINAPI MimeBody_GetEstimatedSize(
1525                                        IMimeBody* iface,
1526                                        ENCODINGTYPE ietEncoding,
1527                                        ULONG* pcbSize)
1528 {
1529     MimeBody *This = impl_from_IMimeBody(iface);
1530     FIXME("(%p)->(%d, %p) stub\n", This, ietEncoding, pcbSize);
1531     return E_NOTIMPL;
1532 }
1533 
1534 static HRESULT WINAPI MimeBody_GetDataHere(
1535                                   IMimeBody* iface,
1536                                   ENCODINGTYPE ietEncoding,
1537                                   IStream* pStream)
1538 {
1539     MimeBody *This = impl_from_IMimeBody(iface);
1540     FIXME("(%p)->(%d, %p) stub\n", This, ietEncoding, pStream);
1541     return E_NOTIMPL;
1542 }
1543 
1544 static const signed char base64_decode_table[] =
1545 {
1546     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00 */
1547     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10 */
1548     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20 */
1549     52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30 */
1550     -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, /* 0x40 */
1551     15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50 */
1552     -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60 */
1553     41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1  /* 0x70 */
1554 };
1555 
1556 static HRESULT decode_base64(IStream *input, IStream **ret_stream)
1557 {
1558     const unsigned char *ptr, *end;
1559     unsigned char buf[1024];
1560     LARGE_INTEGER pos;
1561     unsigned char *ret;
1562     unsigned char in[4];
1563     IStream *output;
1564     DWORD size;
1565     int n = 0;
1566     HRESULT hres;
1567 
1568     pos.QuadPart = 0;
1569     hres = IStream_Seek(input, pos, STREAM_SEEK_SET, NULL);
1570     if(FAILED(hres))
1571         return hres;
1572 
1573     hres = CreateStreamOnHGlobal(NULL, TRUE, &output);
1574     if(FAILED(hres))
1575         return hres;
1576 
1577     while(1) {
1578         hres = IStream_Read(input, buf, sizeof(buf), &size);
1579         if(FAILED(hres) || !size)
1580             break;
1581 
1582         ptr = ret = buf;
1583         end = buf + size;
1584 
1585         while(1) {
1586             /* skip invalid chars */
1587             while(ptr < end &&
1588                   (*ptr >= sizeof(base64_decode_table)/sizeof(*base64_decode_table)
1589                    || base64_decode_table[*ptr] == -1))
1590                 ptr++;
1591             if(ptr == end)
1592                 break;
1593 
1594             in[n++] = base64_decode_table[*ptr++];
1595             switch(n) {
1596             case 2:
1597                 *ret++ = in[0] << 2 | in[1] >> 4;
1598                 continue;
1599             case 3:
1600                 *ret++ = in[1] << 4 | in[2] >> 2;
1601                 continue;
1602             case 4:
1603                 *ret++ = ((in[2] << 6) & 0xc0) | in[3];
1604                 n = 0;
1605             }
1606         }
1607 
1608         if(ret > buf) {
1609             hres = IStream_Write(output, buf, ret - buf, NULL);
1610             if(FAILED(hres))
1611                 break;
1612         }
1613     }
1614 
1615     if(SUCCEEDED(hres))
1616         hres = IStream_Seek(output, pos, STREAM_SEEK_SET, NULL);
1617     if(FAILED(hres)) {
1618         IStream_Release(output);
1619         return hres;
1620     }
1621 
1622     *ret_stream = output;
1623     return S_OK;
1624 }
1625 
1626 static int hex_digit(char c)
1627 {
1628     if('0' <= c && c <= '9')
1629         return c - '0';
1630     if('A' <= c && c <= 'F')
1631         return c - 'A' + 10;
1632     if('a' <= c && c <= 'f')
1633         return c - 'a' + 10;
1634     return -1;
1635 }
1636 
1637 static HRESULT decode_qp(IStream *input, IStream **ret_stream)
1638 {
1639     const unsigned char *ptr, *end;
1640     unsigned char *ret, prev = 0;
1641     unsigned char buf[1024];
1642     LARGE_INTEGER pos;
1643     IStream *output;
1644     DWORD size;
1645     int n = -1;
1646     HRESULT hres;
1647 
1648     pos.QuadPart = 0;
1649     hres = IStream_Seek(input, pos, STREAM_SEEK_SET, NULL);
1650     if(FAILED(hres))
1651         return hres;
1652 
1653     hres = CreateStreamOnHGlobal(NULL, TRUE, &output);
1654     if(FAILED(hres))
1655         return hres;
1656 
1657     while(1) {
1658         hres = IStream_Read(input, buf, sizeof(buf), &size);
1659         if(FAILED(hres) || !size)
1660             break;
1661 
1662         ptr = ret = buf;
1663         end = buf + size;
1664 
1665         while(ptr < end) {
1666             unsigned char byte = *ptr++;
1667 
1668             switch(n) {
1669             case -1:
1670                 if(byte == '=')
1671                     n = 0;
1672                 else
1673                     *ret++ = byte;
1674                 continue;
1675             case 0:
1676                 prev = byte;
1677                 n = 1;
1678                 continue;
1679             case 1:
1680                 if(prev != '\r' || byte != '\n') {
1681                     int h1 = hex_digit(prev), h2 = hex_digit(byte);
1682                     if(h1 != -1 && h2 != -1)
1683                         *ret++ = (h1 << 4) | h2;
1684                     else
1685                         *ret++ = '=';
1686                 }
1687                 n = -1;
1688                 continue;
1689             }
1690         }
1691 
1692         if(ret > buf) {
1693             hres = IStream_Write(output, buf, ret - buf, NULL);
1694             if(FAILED(hres))
1695                 break;
1696         }
1697     }
1698 
1699     if(SUCCEEDED(hres))
1700         hres = IStream_Seek(output, pos, STREAM_SEEK_SET, NULL);
1701     if(FAILED(hres)) {
1702         IStream_Release(output);
1703         return hres;
1704     }
1705 
1706     *ret_stream = output;
1707     return S_OK;
1708 }
1709 
1710 static HRESULT WINAPI MimeBody_GetData(
1711                               IMimeBody* iface,
1712                               ENCODINGTYPE ietEncoding,
1713                               IStream** ppStream)
1714 {
1715     MimeBody *This = impl_from_IMimeBody(iface);
1716     ULARGE_INTEGER start, size;
1717     HRESULT hres;
1718 
1719     TRACE("(%p)->(%d %p)\n", This, ietEncoding, ppStream);
1720 
1721     if(This->encoding != ietEncoding) {
1722         switch(This->encoding) {
1723         case IET_BASE64:
1724             hres = decode_base64(This->data, ppStream);
1725             break;
1726         case IET_QP:
1727             hres = decode_qp(This->data, ppStream);
1728             break;
1729         default:
1730             FIXME("Decoding %d is not supported.\n", This->encoding);
1731             hres = S_FALSE;
1732         }
1733         if(ietEncoding != IET_BINARY)
1734             FIXME("Encoding %d is not supported.\n", ietEncoding);
1735         if(hres != S_FALSE)
1736             return hres;
1737     }
1738 
1739     start.QuadPart = 0;
1740     hres = get_stream_size(This->data, &size);
1741     if(SUCCEEDED(hres))
1742         hres = create_sub_stream(This->data, start, size, ppStream);
1743     return hres;
1744 }
1745 
1746 static HRESULT WINAPI MimeBody_SetData(
1747                               IMimeBody* iface,
1748                               ENCODINGTYPE ietEncoding,
1749                               LPCSTR pszPriType,
1750                               LPCSTR pszSubType,
1751                               REFIID riid,
1752                               LPVOID pvObject)
1753 {
1754     MimeBody *This = impl_from_IMimeBody(iface);
1755     TRACE("(%p)->(%d, %s, %s, %s %p)\n", This, ietEncoding, debugstr_a(pszPriType), debugstr_a(pszSubType),
1756           debugstr_guid(riid), pvObject);
1757 
1758     if(IsEqualIID(riid, &IID_IStream))
1759         IStream_AddRef((IStream *)pvObject);
1760     else
1761     {
1762         FIXME("Unhandled object type %s\n", debugstr_guid(riid));
1763         return E_INVALIDARG;
1764     }
1765 
1766     if(This->data)
1767         release_data(&This->data_iid, This->data);
1768 
1769     This->data_iid = *riid;
1770     This->data = pvObject;
1771 
1772     IMimeBody_SetCurrentEncoding(iface, ietEncoding);
1773 
1774     /* FIXME: Update the content type.
1775        If pszPriType == NULL use 'application'
1776        If pszSubType == NULL use 'octet-stream' */
1777 
1778     return S_OK;
1779 }
1780 
1781 static HRESULT WINAPI MimeBody_EmptyData(
1782                                 IMimeBody* iface)
1783 {
1784     MimeBody *This = impl_from_IMimeBody(iface);
1785     FIXME("(%p)->() stub\n", This);
1786     return E_NOTIMPL;
1787 }
1788 
1789 static HRESULT WINAPI MimeBody_CopyTo(
1790                              IMimeBody* iface,
1791                              IMimeBody* pBody)
1792 {
1793     MimeBody *This = impl_from_IMimeBody(iface);
1794     FIXME("(%p)->(%p) stub\n", This, pBody);
1795     return E_NOTIMPL;
1796 }
1797 
1798 static HRESULT WINAPI MimeBody_GetTransmitInfo(
1799                                       IMimeBody* iface,
1800                                       LPTRANSMITINFO pTransmitInfo)
1801 {
1802     MimeBody *This = impl_from_IMimeBody(iface);
1803     FIXME("(%p)->(%p) stub\n", This, pTransmitInfo);
1804     return E_NOTIMPL;
1805 }
1806 
1807 static HRESULT WINAPI MimeBody_SaveToFile(
1808                                  IMimeBody* iface,
1809                                  ENCODINGTYPE ietEncoding,
1810                                  LPCSTR pszFilePath)
1811 {
1812     MimeBody *This = impl_from_IMimeBody(iface);
1813     FIXME("(%p)->(%d, %s) stub\n", This, ietEncoding, debugstr_a(pszFilePath));
1814     return E_NOTIMPL;
1815 }
1816 
1817 static HRESULT WINAPI MimeBody_GetHandle(
1818                                 IMimeBody* iface,
1819                                 LPHBODY phBody)
1820 {
1821     MimeBody *This = impl_from_IMimeBody(iface);
1822     TRACE("(%p)->(%p)\n", iface, phBody);
1823 
1824     if(!phBody)
1825         return E_INVALIDARG;
1826 
1827     *phBody = This->handle;
1828     return This->handle ? S_OK : MIME_E_NO_DATA;
1829 }
1830 
1831 static IMimeBodyVtbl body_vtbl =
1832 {
1833     MimeBody_QueryInterface,
1834     MimeBody_AddRef,
1835     MimeBody_Release,
1836     MimeBody_GetClassID,
1837     MimeBody_IsDirty,
1838     MimeBody_Load,
1839     MimeBody_Save,
1840     MimeBody_GetSizeMax,
1841     MimeBody_InitNew,
1842     MimeBody_GetPropInfo,
1843     MimeBody_SetPropInfo,
1844     MimeBody_GetProp,
1845     MimeBody_SetProp,
1846     MimeBody_AppendProp,
1847     MimeBody_DeleteProp,
1848     MimeBody_CopyProps,
1849     MimeBody_MoveProps,
1850     MimeBody_DeleteExcept,
1851     MimeBody_QueryProp,
1852     MimeBody_GetCharset,
1853     MimeBody_SetCharset,
1854     MimeBody_GetParameters,
1855     MimeBody_IsContentType,
1856     MimeBody_BindToObject,
1857     MimeBody_Clone,
1858     MimeBody_SetOption,
1859     MimeBody_GetOption,
1860     MimeBody_EnumProps,
1861     MimeBody_IsType,
1862     MimeBody_SetDisplayName,
1863     MimeBody_GetDisplayName,
1864     MimeBody_GetOffsets,
1865     MimeBody_GetCurrentEncoding,
1866     MimeBody_SetCurrentEncoding,
1867     MimeBody_GetEstimatedSize,
1868     MimeBody_GetDataHere,
1869     MimeBody_GetData,
1870     MimeBody_SetData,
1871     MimeBody_EmptyData,
1872     MimeBody_CopyTo,
1873     MimeBody_GetTransmitInfo,
1874     MimeBody_SaveToFile,
1875     MimeBody_GetHandle
1876 };
1877 
1878 static HRESULT MimeBody_set_offsets(MimeBody *body, const BODYOFFSETS *offsets)
1879 {
1880     TRACE("setting offsets to %d, %d, %d, %d\n", offsets->cbBoundaryStart,
1881           offsets->cbHeaderStart, offsets->cbBodyStart, offsets->cbBodyEnd);
1882 
1883     body->body_offsets = *offsets;
1884     return S_OK;
1885 }
1886 
1887 #define FIRST_CUSTOM_PROP_ID 0x100
1888 
1889 static MimeBody *mimebody_create(void)
1890 {
1891     MimeBody *This;
1892     BODYOFFSETS body_offsets;
1893 
1894     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
1895     if (!This)
1896         return NULL;
1897 
1898     This->IMimeBody_iface.lpVtbl = &body_vtbl;
1899     This->ref = 1;
1900     This->handle = NULL;
1901     list_init(&This->headers);
1902     list_init(&This->new_props);
1903     This->next_prop_id = FIRST_CUSTOM_PROP_ID;
1904     This->content_pri_type = NULL;
1905     This->content_sub_type = NULL;
1906     This->encoding = IET_7BIT;
1907     This->data = NULL;
1908     This->data_iid = IID_NULL;
1909 
1910     body_offsets.cbBoundaryStart = body_offsets.cbHeaderStart = 0;
1911     body_offsets.cbBodyStart     = body_offsets.cbBodyEnd     = 0;
1912     MimeBody_set_offsets(This, &body_offsets);
1913 
1914     return This;
1915 }
1916 
1917 HRESULT MimeBody_create(IUnknown *outer, void **ppv)
1918 {
1919     MimeBody *mb;
1920 
1921     if(outer)
1922         return CLASS_E_NOAGGREGATION;
1923 
1924     if ((mb = mimebody_create()))
1925     {
1926         *ppv = &mb->IMimeBody_iface;
1927         return S_OK;
1928     }
1929     else
1930     {
1931         *ppv = NULL;
1932         return E_OUTOFMEMORY;
1933     }
1934 }
1935 
1936 typedef struct body_t
1937 {
1938     struct list entry;
1939     DWORD index;
1940     MimeBody *mime_body;
1941 
1942     struct body_t *parent;
1943     struct list children;
1944 } body_t;
1945 
1946 typedef struct MimeMessage
1947 {
1948     IMimeMessage IMimeMessage_iface;
1949     LONG ref;
1950     IStream *stream;
1951 
1952     struct list body_tree;
1953     DWORD next_index;
1954 } MimeMessage;
1955 
1956 static inline MimeMessage *impl_from_IMimeMessage(IMimeMessage *iface)
1957 {
1958     return CONTAINING_RECORD(iface, MimeMessage, IMimeMessage_iface);
1959 }
1960 
1961 static HRESULT WINAPI MimeMessage_QueryInterface(IMimeMessage *iface, REFIID riid, void **ppv)
1962 {
1963     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1964 
1965     if (IsEqualIID(riid, &IID_IUnknown) ||
1966         IsEqualIID(riid, &IID_IPersist) ||
1967         IsEqualIID(riid, &IID_IPersistStreamInit) ||
1968         IsEqualIID(riid, &IID_IMimeMessageTree) ||
1969         IsEqualIID(riid, &IID_IMimeMessage))
1970     {
1971         *ppv = iface;
1972         IMimeMessage_AddRef(iface);
1973         return S_OK;
1974     }
1975 
1976     FIXME("no interface for %s\n", debugstr_guid(riid));
1977     *ppv = NULL;
1978     return E_NOINTERFACE;
1979 }
1980 
1981 static ULONG WINAPI MimeMessage_AddRef(IMimeMessage *iface)
1982 {
1983     MimeMessage *This = impl_from_IMimeMessage(iface);
1984     ULONG ref = InterlockedIncrement(&This->ref);
1985 
1986     TRACE("(%p) ref=%d\n", This, ref);
1987 
1988     return ref;
1989 }
1990 
1991 static void empty_body_list(struct list *list)
1992 {
1993     body_t *body, *cursor2;
1994     LIST_FOR_EACH_ENTRY_SAFE(body, cursor2, list, body_t, entry)
1995     {
1996         empty_body_list(&body->children);
1997         list_remove(&body->entry);
1998         IMimeBody_Release(&body->mime_body->IMimeBody_iface);
1999         HeapFree(GetProcessHeap(), 0, body);
2000     }
2001 }
2002 
2003 static ULONG WINAPI MimeMessage_Release(IMimeMessage *iface)
2004 {
2005     MimeMessage *This = impl_from_IMimeMessage(iface);
2006     ULONG ref = InterlockedDecrement(&This->ref);
2007 
2008     TRACE("(%p) ref=%d\n", This, ref);
2009 
2010     if (!ref)
2011     {
2012         empty_body_list(&This->body_tree);
2013 
2014         if(This->stream) IStream_Release(This->stream);
2015         HeapFree(GetProcessHeap(), 0, This);
2016     }
2017 
2018     return ref;
2019 }
2020 
2021 /*** IPersist methods ***/
2022 static HRESULT WINAPI MimeMessage_GetClassID(
2023     IMimeMessage *iface,
2024     CLSID *pClassID)
2025 {
2026     FIXME("(%p)->(%p)\n", iface, pClassID);
2027     return E_NOTIMPL;
2028 }
2029 
2030 /*** IPersistStreamInit methods ***/
2031 static HRESULT WINAPI MimeMessage_IsDirty(
2032     IMimeMessage *iface)
2033 {
2034     FIXME("(%p)->()\n", iface);
2035     return E_NOTIMPL;
2036 }
2037 
2038 static body_t *new_body_entry(MimeBody *mime_body, DWORD index, body_t *parent)
2039 {
2040     body_t *body = HeapAlloc(GetProcessHeap(), 0, sizeof(*body));
2041     if(body)
2042     {
2043         body->mime_body = mime_body;
2044         body->index = index;
2045         list_init(&body->children);
2046         body->parent = parent;
2047 
2048         mime_body->handle = UlongToHandle(body->index);
2049     }
2050     return body;
2051 }
2052 
2053 typedef struct
2054 {
2055     struct list entry;
2056     BODYOFFSETS offsets;
2057 } offset_entry_t;
2058 
2059 static HRESULT create_body_offset_list(IStream *stm, const char *boundary, struct list *body_offsets)
2060 {
2061     HRESULT hr;
2062     DWORD read, boundary_start;
2063     int boundary_len = strlen(boundary);
2064     char *buf, *ptr, *overlap;
2065     DWORD start = 0, overlap_no;
2066     offset_entry_t *cur_body = NULL;
2067     BOOL is_first_line = TRUE;
2068     ULARGE_INTEGER cur;
2069     LARGE_INTEGER zero;
2070 
2071     list_init(body_offsets);
2072 
2073     overlap_no = boundary_len + 5;
2074 
2075     overlap = buf = HeapAlloc(GetProcessHeap(), 0, overlap_no + PARSER_BUF_SIZE + 1);
2076 
2077     zero.QuadPart = 0;
2078     hr = IStream_Seek(stm, zero, STREAM_SEEK_CUR, &cur);
2079     start = cur.u.LowPart;
2080 
2081     do {
2082         hr = IStream_Read(stm, overlap, PARSER_BUF_SIZE, &read);
2083         if(FAILED(hr)) goto end;
2084         if(read == 0) break;
2085         overlap[read] = '\0';
2086 
2087         ptr = buf;
2088         while(1) {
2089             if(is_first_line) {
2090                 is_first_line = FALSE;
2091             }else {
2092                 ptr = strstr(ptr, "\r\n");
2093                 if(!ptr)
2094                     break;
2095                 ptr += 2;
2096             }
2097 
2098             boundary_start = start + ptr - buf;
2099 
2100             if(*ptr == '-' && *(ptr + 1) == '-' && !memcmp(ptr + 2, boundary, boundary_len)) {
2101                 ptr += boundary_len + 2;
2102 
2103                 if(*ptr == '\r' && *(ptr + 1) == '\n')
2104                 {
2105                     ptr += 2;
2106                     if(cur_body)
2107                     {
2108                         cur_body->offsets.cbBodyEnd = boundary_start - 2;
2109                         list_add_tail(body_offsets, &cur_body->entry);
2110                     }
2111                     cur_body = HeapAlloc(GetProcessHeap(), 0, sizeof(*cur_body));
2112                     cur_body->offsets.cbBoundaryStart = boundary_start;
2113                     cur_body->offsets.cbHeaderStart = start + ptr - buf;
2114                 }
2115                 else if(*ptr == '-' && *(ptr + 1) == '-')
2116                 {
2117                     if(cur_body)
2118                     {
2119                         cur_body->offsets.cbBodyEnd = boundary_start - 2;
2120                         list_add_tail(body_offsets, &cur_body->entry);
2121                         goto end;
2122                     }
2123                 }
2124             }
2125         }
2126 
2127         if(overlap == buf) /* 1st iteration */
2128         {
2129             memmove(buf, buf + PARSER_BUF_SIZE - overlap_no, overlap_no);
2130             overlap = buf + overlap_no;
2131             start += read - overlap_no;
2132         }
2133         else
2134         {
2135             memmove(buf, buf + PARSER_BUF_SIZE, overlap_no);
2136             start += read;
2137         }
2138     } while(1);
2139 
2140 end:
2141     HeapFree(GetProcessHeap(), 0, buf);
2142     return hr;
2143 }
2144 
2145 static body_t *create_sub_body(MimeMessage *msg, IStream *pStm, BODYOFFSETS *offset, body_t *parent)
2146 {
2147     ULARGE_INTEGER start, length;
2148     MimeBody *mime_body;
2149     HRESULT hr;
2150     body_t *body;
2151     LARGE_INTEGER pos;
2152 
2153     pos.QuadPart = offset->cbHeaderStart;
2154     IStream_Seek(pStm, pos, STREAM_SEEK_SET, NULL);
2155 
2156     mime_body = mimebody_create();
2157     IMimeBody_Load(&mime_body->IMimeBody_iface, pStm);
2158 
2159     pos.QuadPart = 0;
2160     hr = IStream_Seek(pStm, pos, STREAM_SEEK_CUR, &start);
2161     offset->cbBodyStart = start.QuadPart;
2162     if (parent) MimeBody_set_offsets(mime_body, offset);
2163 
2164     length.QuadPart = offset->cbBodyEnd - offset->cbBodyStart;
2165     create_sub_stream(pStm, start, length, (IStream**)&mime_body->data);
2166     mime_body->data_iid = IID_IStream;
2167 
2168     body = new_body_entry(mime_body, msg->next_index++, parent);
2169 
2170     if(IMimeBody_IsContentType(&mime_body->IMimeBody_iface, "multipart", NULL) == S_OK)
2171     {
2172         MIMEPARAMINFO *param_info;
2173         ULONG count, i;
2174         IMimeAllocator *alloc;
2175 
2176         hr = IMimeBody_GetParameters(&mime_body->IMimeBody_iface, "Content-Type", &count,
2177                 &param_info);
2178         if(hr != S_OK || count == 0) return body;
2179 
2180         MimeOleGetAllocator(&alloc);
2181 
2182         for(i = 0; i < count; i++)
2183         {
2184             if(!lstrcmpiA(param_info[i].pszName, "boundary"))
2185             {
2186                 struct list offset_list;
2187                 offset_entry_t *cur, *cursor2;
2188                 hr = create_body_offset_list(pStm, param_info[i].pszData, &offset_list);
2189                 LIST_FOR_EACH_ENTRY_SAFE(cur, cursor2, &offset_list, offset_entry_t, entry)
2190                 {
2191                     body_t *sub_body;
2192 
2193                     sub_body = create_sub_body(msg, pStm, &cur->offsets, body);
2194                     list_add_tail(&body->children, &sub_body->entry);
2195                     list_remove(&cur->entry);
2196                     HeapFree(GetProcessHeap(), 0, cur);
2197                 }
2198                 break;
2199             }
2200         }
2201         IMimeAllocator_FreeParamInfoArray(alloc, count, param_info, TRUE);
2202         IMimeAllocator_Release(alloc);
2203     }
2204     return body;
2205 }
2206 
2207 static HRESULT WINAPI MimeMessage_Load(IMimeMessage *iface, IStream *pStm)
2208 {
2209     MimeMessage *This = impl_from_IMimeMessage(iface);
2210     body_t *root_body;
2211     BODYOFFSETS offsets;
2212     ULARGE_INTEGER cur;
2213     LARGE_INTEGER zero;
2214 
2215     TRACE("(%p)->(%p)\n", iface, pStm);
2216 
2217     if(This->stream)
2218     {
2219         FIXME("already loaded a message\n");
2220         return E_FAIL;
2221     }
2222 
2223     empty_body_list(&This->body_tree);
2224 
2225     IStream_AddRef(pStm);
2226     This->stream = pStm;
2227     offsets.cbBoundaryStart = offsets.cbHeaderStart = 0;
2228     offsets.cbBodyStart = offsets.cbBodyEnd = 0;
2229 
2230     root_body = create_sub_body(This, pStm, &offsets, NULL);
2231 
2232     zero.QuadPart = 0;
2233     IStream_Seek(pStm, zero, STREAM_SEEK_END, &cur);
2234     offsets.cbBodyEnd = cur.u.LowPart;
2235     MimeBody_set_offsets(root_body->mime_body, &offsets);
2236 
2237     list_add_head(&This->body_tree, &root_body->entry);
2238 
2239     return S_OK;
2240 }
2241 
2242 static HRESULT WINAPI MimeMessage_Save(IMimeMessage *iface, IStream *pStm, BOOL fClearDirty)
2243 {
2244     FIXME("(%p)->(%p, %s)\n", iface, pStm, fClearDirty ? "TRUE" : "FALSE");
2245     return E_NOTIMPL;
2246 }
2247 
2248 static HRESULT WINAPI MimeMessage_GetSizeMax(
2249     IMimeMessage *iface,
2250     ULARGE_INTEGER *pcbSize)
2251 {
2252     FIXME("(%p)->(%p)\n", iface, pcbSize);
2253     return E_NOTIMPL;
2254 }
2255 
2256 static HRESULT WINAPI MimeMessage_InitNew(
2257     IMimeMessage *iface)
2258 {
2259     FIXME("(%p)->()\n", iface);
2260     return E_NOTIMPL;
2261 }
2262 
2263 /*** IMimeMessageTree methods ***/
2264 static HRESULT WINAPI MimeMessage_GetMessageSource(IMimeMessage *iface, IStream **ppStream,
2265         DWORD dwFlags)
2266 {
2267     MimeMessage *This = impl_from_IMimeMessage(iface);
2268 
2269     FIXME("(%p)->(%p, 0x%x)\n", iface, ppStream, dwFlags);
2270 
2271     IStream_AddRef(This->stream);
2272     *ppStream = This->stream;
2273     return S_OK;
2274 }
2275 
2276 static HRESULT WINAPI MimeMessage_GetMessageSize(
2277     IMimeMessage *iface,
2278     ULONG *pcbSize,
2279     DWORD dwFlags)
2280 {
2281     FIXME("(%p)->(%p, 0x%x)\n", iface, pcbSize, dwFlags);
2282     return E_NOTIMPL;
2283 }
2284 
2285 static HRESULT WINAPI MimeMessage_LoadOffsetTable(
2286     IMimeMessage *iface,
2287     IStream *pStream)
2288 {
2289     FIXME("(%p)->(%p)\n", iface, pStream);
2290     return E_NOTIMPL;
2291 }
2292 
2293 static HRESULT WINAPI MimeMessage_SaveOffsetTable(
2294     IMimeMessage *iface,
2295     IStream *pStream,
2296     DWORD dwFlags)
2297 {
2298     FIXME("(%p)->(%p, 0x%x)\n", iface, pStream, dwFlags);
2299     return E_NOTIMPL;
2300 }
2301 
2302 
2303 static HRESULT WINAPI MimeMessage_GetFlags(
2304     IMimeMessage *iface,
2305     DWORD *pdwFlags)
2306 {
2307     FIXME("(%p)->(%p)\n", iface, pdwFlags);
2308     return E_NOTIMPL;
2309 }
2310 
2311 static HRESULT WINAPI MimeMessage_Commit(
2312     IMimeMessage *iface,
2313     DWORD dwFlags)
2314 {
2315     FIXME("(%p)->(0x%x)\n", iface, dwFlags);
2316     return S_OK;
2317 }
2318 
2319 
2320 static HRESULT WINAPI MimeMessage_HandsOffStorage(
2321     IMimeMessage *iface)
2322 {
2323     FIXME("(%p)->()\n", iface);
2324     return E_NOTIMPL;
2325 }
2326 
2327 static HRESULT find_body(struct list *list, HBODY hbody, body_t **body)
2328 {
2329     body_t *cur;
2330     HRESULT hr;
2331 
2332     if(hbody == HBODY_ROOT)
2333     {
2334         *body = LIST_ENTRY(list_head(list), body_t, entry);
2335         return S_OK;
2336     }
2337 
2338     LIST_FOR_EACH_ENTRY(cur, list, body_t, entry)
2339     {
2340         if(cur->index == HandleToUlong(hbody))
2341         {
2342             *body = cur;
2343             return S_OK;
2344         }
2345         hr = find_body(&cur->children, hbody, body);
2346         if(hr == S_OK) return S_OK;
2347     }
2348     return S_FALSE;
2349 }
2350 
2351 static HRESULT WINAPI MimeMessage_BindToObject(IMimeMessage *iface, const HBODY hBody, REFIID riid,
2352         void **ppvObject)
2353 {
2354     MimeMessage *This = impl_from_IMimeMessage(iface);
2355     HRESULT hr;
2356     body_t *body;
2357 
2358     TRACE("(%p)->(%p, %s, %p)\n", iface, hBody, debugstr_guid(riid), ppvObject);
2359 
2360     hr = find_body(&This->body_tree, hBody, &body);
2361 
2362     if(hr != S_OK) return hr;
2363 
2364     if(IsEqualIID(riid, &IID_IMimeBody))
2365     {
2366         IMimeBody_AddRef(&body->mime_body->IMimeBody_iface);
2367         *ppvObject = &body->mime_body->IMimeBody_iface;
2368         return S_OK;
2369     }
2370 
2371     return E_NOINTERFACE;
2372 }
2373 
2374 static HRESULT WINAPI MimeMessage_SaveBody(
2375     IMimeMessage *iface,
2376     HBODY hBody,
2377     DWORD dwFlags,
2378     IStream *pStream)
2379 {
2380     FIXME("(%p)->(%p, 0x%x, %p)\n", iface, hBody, dwFlags, pStream);
2381     return E_NOTIMPL;
2382 }
2383 
2384 static HRESULT get_body(MimeMessage *msg, BODYLOCATION location, HBODY pivot, body_t **out)
2385 {
2386     body_t *root = LIST_ENTRY(list_head(&msg->body_tree), body_t, entry);
2387     body_t *body;
2388     HRESULT hr;
2389     struct list *list;
2390 
2391     if(location == IBL_ROOT)
2392     {
2393         *out = root;
2394         return S_OK;
2395     }
2396 
2397     hr = find_body(&msg->body_tree, pivot, &body);
2398 
2399     if(hr == S_OK)
2400     {
2401         switch(location)
2402         {
2403         case IBL_PARENT:
2404             if(body->parent)
2405                 *out = body->parent;
2406             else
2407                 hr = MIME_E_NOT_FOUND;
2408             break;
2409 
2410         case IBL_FIRST:
2411             list = list_head(&body->children);
2412             if(list)
2413                 *out = LIST_ENTRY(list, body_t, entry);
2414             else
2415                 hr = MIME_E_NOT_FOUND;
2416             break;
2417 
2418         case IBL_LAST:
2419             list = list_tail(&body->children);
2420             if(list)
2421                 *out = LIST_ENTRY(list, body_t, entry);
2422             else
2423                 hr = MIME_E_NOT_FOUND;
2424             break;
2425 
2426         case IBL_NEXT:
2427             list = list_next(&body->parent->children, &body->entry);
2428             if(list)
2429                 *out = LIST_ENTRY(list, body_t, entry);
2430             else
2431                 hr = MIME_E_NOT_FOUND;
2432             break;
2433 
2434         case IBL_PREVIOUS:
2435             list = list_prev(&body->parent->children, &body->entry);
2436             if(list)
2437                 *out = LIST_ENTRY(list, body_t, entry);
2438             else
2439                 hr = MIME_E_NOT_FOUND;
2440             break;
2441 
2442         default:
2443             hr = E_FAIL;
2444             break;
2445         }
2446     }
2447 
2448     return hr;
2449 }
2450 
2451 
2452 static HRESULT WINAPI MimeMessage_InsertBody(
2453     IMimeMessage *iface,
2454     BODYLOCATION location,
2455     HBODY hPivot,
2456     LPHBODY phBody)
2457 {
2458     FIXME("(%p)->(%d, %p, %p)\n", iface, location, hPivot, phBody);
2459     return E_NOTIMPL;
2460 }
2461 
2462 static HRESULT WINAPI MimeMessage_GetBody(IMimeMessage *iface, BODYLOCATION location, HBODY hPivot,
2463         HBODY *phBody)
2464 {
2465     MimeMessage *This = impl_from_IMimeMessage(iface);
2466     body_t *body;
2467     HRESULT hr;
2468 
2469     TRACE("(%p)->(%d, %p, %p)\n", iface, location, hPivot, phBody);
2470 
2471     if(!phBody)
2472         return E_INVALIDARG;
2473 
2474     *phBody = NULL;
2475 
2476     hr = get_body(This, location, hPivot, &body);
2477 
2478     if(hr == S_OK) *phBody = UlongToHandle(body->index);
2479 
2480     return hr;
2481 }
2482 
2483 static HRESULT WINAPI MimeMessage_DeleteBody(
2484     IMimeMessage *iface,
2485     HBODY hBody,
2486     DWORD dwFlags)
2487 {
2488     FIXME("(%p)->(%p, %08x)\n", iface, hBody, dwFlags);
2489     return E_NOTIMPL;
2490 }
2491 
2492 static HRESULT WINAPI MimeMessage_MoveBody(
2493     IMimeMessage *iface,
2494     HBODY hBody,
2495     BODYLOCATION location)
2496 {
2497     FIXME("(%p)->(%d)\n", iface, location);
2498     return E_NOTIMPL;
2499 }
2500 
2501 static void count_children(body_t *body, boolean recurse, ULONG *count)
2502 {
2503     body_t *child;
2504 
2505     LIST_FOR_EACH_ENTRY(child, &body->children, body_t, entry)
2506     {
2507         (*count)++;
2508         if(recurse) count_children(child, recurse, count);
2509     }
2510 }
2511 
2512 static HRESULT WINAPI MimeMessage_CountBodies(IMimeMessage *iface, HBODY hParent, boolean fRecurse,
2513         ULONG *pcBodies)
2514 {
2515     HRESULT hr;
2516     MimeMessage *This = impl_from_IMimeMessage(iface);
2517     body_t *body;
2518 
2519     TRACE("(%p)->(%p, %s, %p)\n", iface, hParent, fRecurse ? "TRUE" : "FALSE", pcBodies);
2520 
2521     hr = find_body(&This->body_tree, hParent, &body);
2522     if(hr != S_OK) return hr;
2523 
2524     *pcBodies = 1;
2525     count_children(body, fRecurse, pcBodies);
2526 
2527     return S_OK;
2528 }
2529 
2530 static HRESULT find_next(MimeMessage *This, body_t *body, FINDBODY *find, HBODY *out)
2531 {
2532     struct list *ptr;
2533     HBODY next;
2534 
2535     for (;;)
2536     {
2537         if (!body) ptr = list_head( &This->body_tree );
2538         else
2539         {
2540             ptr = list_head( &body->children );
2541             while (!ptr)
2542             {
2543                 if (!body->parent) return MIME_E_NOT_FOUND;
2544                 if (!(ptr = list_next( &body->parent->children, &body->entry ))) body = body->parent;
2545             }
2546         }
2547 
2548         body = LIST_ENTRY( ptr, body_t, entry );
2549         next = UlongToHandle( body->index );
2550         find->dwReserved = body->index;
2551         if (IMimeBody_IsContentType(&body->mime_body->IMimeBody_iface, find->pszPriType,
2552                     find->pszSubType) == S_OK)
2553         {
2554             *out = next;
2555             return S_OK;
2556         }
2557     }
2558     return MIME_E_NOT_FOUND;
2559 }
2560 
2561 static HRESULT WINAPI MimeMessage_FindFirst(IMimeMessage *iface, FINDBODY *pFindBody, HBODY *phBody)
2562 {
2563     MimeMessage *This = impl_from_IMimeMessage(iface);
2564 
2565     TRACE("(%p)->(%p, %p)\n", iface, pFindBody, phBody);
2566 
2567     pFindBody->dwReserved = 0;
2568     return find_next(This, NULL, pFindBody, phBody);
2569 }
2570 
2571 static HRESULT WINAPI MimeMessage_FindNext(IMimeMessage *iface, FINDBODY *pFindBody, HBODY *phBody)
2572 {
2573     MimeMessage *This = impl_from_IMimeMessage(iface);
2574     body_t *body;
2575     HRESULT hr;
2576 
2577     TRACE("(%p)->(%p, %p)\n", iface, pFindBody, phBody);
2578 
2579     hr = find_body( &This->body_tree, UlongToHandle( pFindBody->dwReserved ), &body );
2580     if (hr != S_OK) return MIME_E_NOT_FOUND;
2581     return find_next(This, body, pFindBody, phBody);
2582 }
2583 
2584 static HRESULT WINAPI MimeMessage_ResolveURL(
2585     IMimeMessage *iface,
2586     HBODY hRelated,
2587     LPCSTR pszBase,
2588     LPCSTR pszURL,
2589     DWORD dwFlags,
2590     LPHBODY phBody)
2591 {
2592     FIXME("(%p)->(%p, %s, %s, 0x%x, %p)\n", iface, hRelated, pszBase, pszURL, dwFlags, phBody);
2593     return E_NOTIMPL;
2594 }
2595 
2596 static HRESULT WINAPI MimeMessage_ToMultipart(
2597     IMimeMessage *iface,
2598     HBODY hBody,
2599     LPCSTR pszSubType,
2600     LPHBODY phMultipart)
2601 {
2602     FIXME("(%p)->(%p, %s, %p)\n", iface, hBody, pszSubType, phMultipart);
2603     return E_NOTIMPL;
2604 }
2605 
2606 static HRESULT WINAPI MimeMessage_GetBodyOffsets(
2607     IMimeMessage *iface,
2608     HBODY hBody,
2609     LPBODYOFFSETS pOffsets)
2610 {
2611     FIXME("(%p)->(%p, %p)\n", iface, hBody, pOffsets);
2612     return E_NOTIMPL;
2613 }
2614 
2615 static HRESULT WINAPI MimeMessage_GetCharset(
2616     IMimeMessage *iface,
2617     LPHCHARSET phCharset)
2618 {
2619     FIXME("(%p)->(%p)\n", iface, phCharset);
2620     *phCharset = NULL;
2621     return S_OK;
2622 }
2623 
2624 static HRESULT WINAPI MimeMessage_SetCharset(
2625     IMimeMessage *iface,
2626     HCHARSET hCharset,
2627     CSETAPPLYTYPE applytype)
2628 {
2629     FIXME("(%p)->(%p, %d)\n", iface, hCharset, applytype);
2630     return E_NOTIMPL;
2631 }
2632 
2633 static HRESULT WINAPI MimeMessage_IsBodyType(
2634     IMimeMessage *iface,
2635     HBODY hBody,
2636     IMSGBODYTYPE bodytype)
2637 {
2638     HRESULT hr;
2639     IMimeBody *mime_body;
2640     TRACE("(%p)->(%p, %d)\n", iface, hBody, bodytype);
2641 
2642     hr = IMimeMessage_BindToObject(iface, hBody, &IID_IMimeBody, (void**)&mime_body);
2643     if(hr != S_OK) return hr;
2644 
2645     hr = IMimeBody_IsType(mime_body, bodytype);
2646     MimeBody_Release(mime_body);
2647     return hr;
2648 }
2649 
2650 static HRESULT WINAPI MimeMessage_IsContentType(
2651     IMimeMessage *iface,
2652     HBODY hBody,
2653     LPCSTR pszPriType,
2654     LPCSTR pszSubType)
2655 {
2656     HRESULT hr;
2657     IMimeBody *mime_body;
2658     TRACE("(%p)->(%p, %s, %s)\n", iface, hBody, debugstr_a(pszPriType),
2659           debugstr_a(pszSubType));
2660 
2661     hr = IMimeMessage_BindToObject(iface, hBody, &IID_IMimeBody, (void**)&mime_body);
2662     if(FAILED(hr)) return hr;
2663 
2664     hr = IMimeBody_IsContentType(mime_body, pszPriType, pszSubType);
2665     IMimeBody_Release(mime_body);
2666     return hr;
2667 }
2668 
2669 static HRESULT WINAPI MimeMessage_QueryBodyProp(
2670     IMimeMessage *iface,
2671     HBODY hBody,
2672     LPCSTR pszName,
2673     LPCSTR pszCriteria,
2674     boolean fSubString,
2675     boolean fCaseSensitive)
2676 {
2677     FIXME("(%p)->(%p, %s, %s, %s, %s)\n", iface, hBody, pszName, pszCriteria, fSubString ? "TRUE" : "FALSE", fCaseSensitive ? "TRUE" : "FALSE");
2678     return E_NOTIMPL;
2679 }
2680 
2681 static HRESULT WINAPI MimeMessage_GetBodyProp(
2682     IMimeMessage *iface,
2683     HBODY hBody,
2684     LPCSTR pszName,
2685     DWORD dwFlags,
2686     LPPROPVARIANT pValue)
2687 {
2688     HRESULT hr;
2689     IMimeBody *mime_body;
2690 
2691     TRACE("(%p)->(%p, %s, 0x%x, %p)\n", iface, hBody, pszName, dwFlags, pValue);
2692 
2693     hr = IMimeMessage_BindToObject(iface, hBody, &IID_IMimeBody, (void**)&mime_body);
2694     if(hr != S_OK) return hr;
2695 
2696     hr = IMimeBody_GetProp(mime_body, pszName, dwFlags, pValue);
2697     IMimeBody_Release(mime_body);
2698 
2699     return hr;
2700 }
2701 
2702 static HRESULT WINAPI MimeMessage_SetBodyProp(
2703     IMimeMessage *iface,
2704     HBODY hBody,
2705     LPCSTR pszName,
2706     DWORD dwFlags,
2707     LPCPROPVARIANT pValue)
2708 {
2709     FIXME("(%p)->(%p, %s, 0x%x, %p)\n", iface, hBody, pszName, dwFlags, pValue);
2710     return E_NOTIMPL;
2711 }
2712 
2713 static HRESULT WINAPI MimeMessage_DeleteBodyProp(
2714     IMimeMessage *iface,
2715     HBODY hBody,
2716     LPCSTR pszName)
2717 {
2718     FIXME("(%p)->(%p, %s)\n", iface, hBody, pszName);
2719     return E_NOTIMPL;
2720 }
2721 
2722 static HRESULT WINAPI MimeMessage_SetOption(
2723     IMimeMessage *iface,
2724     const TYPEDID oid,
2725     LPCPROPVARIANT pValue)
2726 {
2727     HRESULT hr = S_OK;
2728     TRACE("(%p)->(%08x, %p)\n", iface, oid, pValue);
2729 
2730     /* Message ID is checked before type.
2731      *  OID 0x4D -> 0x56 and 0x58 aren't defined but will filtered out later.
2732      */
2733     if(TYPEDID_ID(oid) < TYPEDID_ID(OID_ALLOW_8BIT_HEADER) || TYPEDID_ID(oid) > TYPEDID_ID(OID_SECURITY_2KEY_CERT_BAG_64))
2734     {
2735         WARN("oid (%08x) out of range\n", oid);
2736         return MIME_E_INVALID_OPTION_ID;
2737     }
2738 
2739     if(pValue->vt != TYPEDID_TYPE(oid))
2740     {
2741         WARN("Called with vartype %04x and oid %08x\n", pValue->vt, oid);
2742         return S_OK;
2743     }
2744 
2745     switch(oid)
2746     {
2747     case OID_HIDE_TNEF_ATTACHMENTS:
2748         FIXME("OID_HIDE_TNEF_ATTACHMENTS (value %d): ignoring\n", pValue->u.boolVal);
2749         break;
2750     case OID_SHOW_MACBINARY:
2751         FIXME("OID_SHOW_MACBINARY (value %d): ignoring\n", pValue->u.boolVal);
2752         break;
2753     case OID_SAVEBODY_KEEPBOUNDARY:
2754         FIXME("OID_SAVEBODY_KEEPBOUNDARY (value %d): ignoring\n", pValue->u.boolVal);
2755         break;
2756     case OID_CLEANUP_TREE_ON_SAVE:
2757         FIXME("OID_CLEANUP_TREE_ON_SAVE (value %d): ignoring\n", pValue->u.boolVal);
2758         break;
2759     default:
2760         FIXME("Unhandled oid %08x\n", oid);
2761         hr = MIME_E_INVALID_OPTION_ID;
2762     }
2763 
2764     return hr;
2765 }
2766 
2767 static HRESULT WINAPI MimeMessage_GetOption(
2768     IMimeMessage *iface,
2769     const TYPEDID oid,
2770     LPPROPVARIANT pValue)
2771 {
2772     FIXME("(%p)->(%08x, %p)\n", iface, oid, pValue);
2773     return E_NOTIMPL;
2774 }
2775 
2776 /*** IMimeMessage methods ***/
2777 static HRESULT WINAPI MimeMessage_CreateWebPage(
2778     IMimeMessage *iface,
2779     IStream *pRootStm,
2780     LPWEBPAGEOPTIONS pOptions,
2781     IMimeMessageCallback *pCallback,
2782     IMoniker **ppMoniker)
2783 {
2784     FIXME("(%p)->(%p, %p, %p, %p)\n", iface, pRootStm, pOptions, pCallback, ppMoniker);
2785     *ppMoniker = NULL;
2786     return E_NOTIMPL;
2787 }
2788 
2789 static HRESULT WINAPI MimeMessage_GetProp(
2790     IMimeMessage *iface,
2791     LPCSTR pszName,
2792     DWORD dwFlags,
2793     LPPROPVARIANT pValue)
2794 {
2795     FIXME("(%p)->(%s, 0x%x, %p)\n", iface, pszName, dwFlags, pValue);
2796     return E_NOTIMPL;
2797 }
2798 
2799 static HRESULT WINAPI MimeMessage_SetProp(
2800     IMimeMessage *iface,
2801     LPCSTR pszName,
2802     DWORD dwFlags,
2803     LPCPROPVARIANT pValue)
2804 {
2805     FIXME("(%p)->(%s, 0x%x, %p)\n", iface, pszName, dwFlags, pValue);
2806     return E_NOTIMPL;
2807 }
2808 
2809 static HRESULT WINAPI MimeMessage_DeleteProp(
2810     IMimeMessage *iface,
2811     LPCSTR pszName)
2812 {
2813     FIXME("(%p)->(%s)\n", iface, pszName);
2814     return E_NOTIMPL;
2815 }
2816 
2817 static HRESULT WINAPI MimeMessage_QueryProp(
2818     IMimeMessage *iface,
2819     LPCSTR pszName,
2820     LPCSTR pszCriteria,
2821     boolean fSubString,
2822     boolean fCaseSensitive)
2823 {
2824     FIXME("(%p)->(%s, %s, %s, %s)\n", iface, pszName, pszCriteria, fSubString ? "TRUE" : "FALSE", fCaseSensitive ? "TRUE" : "FALSE");
2825     return E_NOTIMPL;
2826 }
2827 
2828 static HRESULT WINAPI MimeMessage_GetTextBody(
2829     IMimeMessage *iface,
2830     DWORD dwTxtType,
2831     ENCODINGTYPE ietEncoding,
2832     IStream **pStream,
2833     LPHBODY phBody)
2834 {
2835     HRESULT hr;
2836     HBODY hbody;
2837     FINDBODY find_struct;
2838     IMimeBody *mime_body;
2839     static char text[] = "text";
2840     static char plain[] = "plain";
2841     static char html[] = "html";
2842 
2843     TRACE("(%p)->(%d, %d, %p, %p)\n", iface, dwTxtType, ietEncoding, pStream, phBody);
2844 
2845     find_struct.pszPriType = text;
2846 
2847     switch(dwTxtType)
2848     {
2849     case TXT_PLAIN:
2850         find_struct.pszSubType = plain;
2851         break;
2852     case TXT_HTML:
2853         find_struct.pszSubType = html;
2854         break;
2855     default:
2856         return MIME_E_INVALID_TEXT_TYPE;
2857     }
2858 
2859     hr = IMimeMessage_FindFirst(iface, &find_struct, &hbody);
2860     if(hr != S_OK)
2861     {
2862         TRACE("not found hr %08x\n", hr);
2863         *phBody = NULL;
2864         return hr;
2865     }
2866 
2867     IMimeMessage_BindToObject(iface, hbody, &IID_IMimeBody, (void**)&mime_body);
2868 
2869     IMimeBody_GetData(mime_body, ietEncoding, pStream);
2870     *phBody = hbody;
2871     IMimeBody_Release(mime_body);
2872     return hr;
2873 }
2874 
2875 static HRESULT WINAPI MimeMessage_SetTextBody(
2876     IMimeMessage *iface,
2877     DWORD dwTxtType,
2878     ENCODINGTYPE ietEncoding,
2879     HBODY hAlternative,
2880     IStream *pStream,
2881     LPHBODY phBody)
2882 {
2883     FIXME("(%p)->(%d, %d, %p, %p, %p)\n", iface, dwTxtType, ietEncoding, hAlternative, pStream, phBody);
2884     return E_NOTIMPL;
2885 }
2886 
2887 static HRESULT WINAPI MimeMessage_AttachObject(
2888     IMimeMessage *iface,
2889     REFIID riid,
2890     void *pvObject,
2891     LPHBODY phBody)
2892 {
2893     FIXME("(%p)->(%s, %p, %p)\n", iface, debugstr_guid(riid), pvObject, phBody);
2894     return E_NOTIMPL;
2895 }
2896 
2897 static HRESULT WINAPI MimeMessage_AttachFile(
2898     IMimeMessage *iface,
2899     LPCSTR pszFilePath,
2900     IStream *pstmFile,
2901     LPHBODY phBody)
2902 {
2903     FIXME("(%p)->(%s, %p, %p)\n", iface, pszFilePath, pstmFile, phBody);
2904     return E_NOTIMPL;
2905 }
2906 
2907 static HRESULT WINAPI MimeMessage_AttachURL(
2908     IMimeMessage *iface,
2909     LPCSTR pszBase,
2910     LPCSTR pszURL,
2911     DWORD dwFlags,
2912     IStream *pstmURL,
2913     LPSTR *ppszCIDURL,
2914     LPHBODY phBody)
2915 {
2916     FIXME("(%p)->(%s, %s, 0x%x, %p, %p, %p)\n", iface, pszBase, pszURL, dwFlags, pstmURL, ppszCIDURL, phBody);
2917     return E_NOTIMPL;
2918 }
2919 
2920 static HRESULT WINAPI MimeMessage_GetAttachments(
2921     IMimeMessage *iface,
2922     ULONG *pcAttach,
2923     LPHBODY *pprghAttach)
2924 {
2925     HRESULT hr;
2926     FINDBODY find_struct;
2927     HBODY hbody;
2928     LPHBODY array;
2929     ULONG size = 10;
2930 
2931     TRACE("(%p)->(%p, %p)\n", iface, pcAttach, pprghAttach);
2932 
2933     *pcAttach = 0;
2934     array = CoTaskMemAlloc(size * sizeof(HBODY));
2935 
2936     find_struct.pszPriType = find_struct.pszSubType = NULL;
2937     hr = IMimeMessage_FindFirst(iface, &find_struct, &hbody);
2938     while(hr == S_OK)
2939     {
2940         hr = IMimeMessage_IsContentType(iface, hbody, "multipart", NULL);
2941         TRACE("IsCT rets %08x %d\n", hr, *pcAttach);
2942         if(hr != S_OK)
2943         {
2944             if(*pcAttach + 1 > size)
2945             {
2946                 size *= 2;
2947                 array = CoTaskMemRealloc(array, size * sizeof(HBODY));
2948             }
2949             array[*pcAttach] = hbody;
2950             (*pcAttach)++;
2951         }
2952         hr = IMimeMessage_FindNext(iface, &find_struct, &hbody);
2953     }
2954 
2955     *pprghAttach = array;
2956     return S_OK;
2957 }
2958 
2959 static HRESULT WINAPI MimeMessage_GetAddressTable(
2960     IMimeMessage *iface,
2961     IMimeAddressTable **ppTable)
2962 {
2963     FIXME("(%p)->(%p)\n", iface, ppTable);
2964     return E_NOTIMPL;
2965 }
2966 
2967 static HRESULT WINAPI MimeMessage_GetSender(
2968     IMimeMessage *iface,
2969     LPADDRESSPROPS pAddress)
2970 {
2971     FIXME("(%p)->(%p)\n", iface, pAddress);
2972     return E_NOTIMPL;
2973 }
2974 
2975 static HRESULT WINAPI MimeMessage_GetAddressTypes(
2976     IMimeMessage *iface,
2977     DWORD dwAdrTypes,
2978     DWORD dwProps,
2979     LPADDRESSLIST pList)
2980 {
2981     FIXME("(%p)->(%d, %d, %p)\n", iface, dwAdrTypes, dwProps, pList);
2982     return E_NOTIMPL;
2983 }
2984 
2985 static HRESULT WINAPI MimeMessage_GetAddressFormat(
2986     IMimeMessage *iface,
2987     DWORD dwAdrTypes,
2988     ADDRESSFORMAT format,
2989     LPSTR *ppszFormat)
2990 {
2991     FIXME("(%p)->(%d, %d, %p)\n", iface, dwAdrTypes, format, ppszFormat);
2992     return E_NOTIMPL;
2993 }
2994 
2995 static HRESULT WINAPI MimeMessage_EnumAddressTypes(
2996     IMimeMessage *iface,
2997     DWORD dwAdrTypes,
2998     DWORD dwProps,
2999     IMimeEnumAddressTypes **ppEnum)
3000 {
3001     FIXME("(%p)->(%d, %d, %p)\n", iface, dwAdrTypes, dwProps, ppEnum);
3002     return E_NOTIMPL;
3003 }
3004 
3005 static HRESULT WINAPI MimeMessage_SplitMessage(
3006     IMimeMessage *iface,
3007     ULONG cbMaxPart,
3008     IMimeMessageParts **ppParts)
3009 {
3010     FIXME("(%p)->(%d, %p)\n", iface, cbMaxPart, ppParts);
3011     return E_NOTIMPL;
3012 }
3013 
3014 static HRESULT WINAPI MimeMessage_GetRootMoniker(
3015     IMimeMessage *iface,
3016     IMoniker **ppMoniker)
3017 {
3018     FIXME("(%p)->(%p)\n", iface, ppMoniker);
3019     return E_NOTIMPL;
3020 }
3021 
3022 static const IMimeMessageVtbl MimeMessageVtbl =
3023 {
3024     MimeMessage_QueryInterface,
3025     MimeMessage_AddRef,
3026     MimeMessage_Release,
3027     MimeMessage_GetClassID,
3028     MimeMessage_IsDirty,
3029     MimeMessage_Load,
3030     MimeMessage_Save,
3031     MimeMessage_GetSizeMax,
3032     MimeMessage_InitNew,
3033     MimeMessage_GetMessageSource,
3034     MimeMessage_GetMessageSize,
3035     MimeMessage_LoadOffsetTable,
3036     MimeMessage_SaveOffsetTable,
3037     MimeMessage_GetFlags,
3038     MimeMessage_Commit,
3039     MimeMessage_HandsOffStorage,
3040     MimeMessage_BindToObject,
3041     MimeMessage_SaveBody,
3042     MimeMessage_InsertBody,
3043     MimeMessage_GetBody,
3044     MimeMessage_DeleteBody,
3045     MimeMessage_MoveBody,
3046     MimeMessage_CountBodies,
3047     MimeMessage_FindFirst,
3048     MimeMessage_FindNext,
3049     MimeMessage_ResolveURL,
3050     MimeMessage_ToMultipart,
3051     MimeMessage_GetBodyOffsets,
3052     MimeMessage_GetCharset,
3053     MimeMessage_SetCharset,
3054     MimeMessage_IsBodyType,
3055     MimeMessage_IsContentType,
3056     MimeMessage_QueryBodyProp,
3057     MimeMessage_GetBodyProp,
3058     MimeMessage_SetBodyProp,
3059     MimeMessage_DeleteBodyProp,
3060     MimeMessage_SetOption,
3061     MimeMessage_GetOption,
3062     MimeMessage_CreateWebPage,
3063     MimeMessage_GetProp,
3064     MimeMessage_SetProp,
3065     MimeMessage_DeleteProp,
3066     MimeMessage_QueryProp,
3067     MimeMessage_GetTextBody,
3068     MimeMessage_SetTextBody,
3069     MimeMessage_AttachObject,
3070     MimeMessage_AttachFile,
3071     MimeMessage_AttachURL,
3072     MimeMessage_GetAttachments,
3073     MimeMessage_GetAddressTable,
3074     MimeMessage_GetSender,
3075     MimeMessage_GetAddressTypes,
3076     MimeMessage_GetAddressFormat,
3077     MimeMessage_EnumAddressTypes,
3078     MimeMessage_SplitMessage,
3079     MimeMessage_GetRootMoniker,
3080 };
3081 
3082 HRESULT MimeMessage_create(IUnknown *outer, void **obj)
3083 {
3084     MimeMessage *This;
3085     MimeBody *mime_body;
3086     body_t *root_body;
3087 
3088     TRACE("(%p, %p)\n", outer, obj);
3089 
3090     if (outer)
3091     {
3092         FIXME("outer unknown not supported yet\n");
3093         return E_NOTIMPL;
3094     }
3095 
3096     *obj = NULL;
3097 
3098     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
3099     if (!This) return E_OUTOFMEMORY;
3100 
3101     This->IMimeMessage_iface.lpVtbl = &MimeMessageVtbl;
3102     This->ref = 1;
3103     This->stream = NULL;
3104     list_init(&This->body_tree);
3105     This->next_index = 1;
3106 
3107     mime_body = mimebody_create();
3108     root_body = new_body_entry(mime_body, This->next_index++, NULL);
3109     list_add_head(&This->body_tree, &root_body->entry);
3110 
3111     *obj = &This->IMimeMessage_iface;
3112     return S_OK;
3113 }
3114 
3115 /***********************************************************************
3116  *              MimeOleCreateMessage (INETCOMM.@)
3117  */
3118 HRESULT WINAPI MimeOleCreateMessage(IUnknown *pUnkOuter, IMimeMessage **ppMessage)
3119 {
3120     TRACE("(%p, %p)\n", pUnkOuter, ppMessage);
3121     return MimeMessage_create(NULL, (void **)ppMessage);
3122 }
3123 
3124 /***********************************************************************
3125  *              MimeOleSetCompatMode (INETCOMM.@)
3126  */
3127 HRESULT WINAPI MimeOleSetCompatMode(DWORD dwMode)
3128 {
3129     FIXME("(0x%x)\n", dwMode);
3130     return S_OK;
3131 }
3132 
3133 /***********************************************************************
3134  *              MimeOleCreateVirtualStream (INETCOMM.@)
3135  */
3136 HRESULT WINAPI MimeOleCreateVirtualStream(IStream **ppStream)
3137 {
3138     HRESULT hr;
3139     FIXME("(%p)\n", ppStream);
3140 
3141     hr = CreateStreamOnHGlobal(NULL, TRUE, ppStream);
3142     return hr;
3143 }
3144 
3145 typedef struct MimeSecurity
3146 {
3147     IMimeSecurity IMimeSecurity_iface;
3148     LONG ref;
3149 } MimeSecurity;
3150 
3151 static inline MimeSecurity *impl_from_IMimeSecurity(IMimeSecurity *iface)
3152 {
3153     return CONTAINING_RECORD(iface, MimeSecurity, IMimeSecurity_iface);
3154 }
3155 
3156 static HRESULT WINAPI MimeSecurity_QueryInterface(IMimeSecurity *iface, REFIID riid, void **ppv)
3157 {
3158     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3159 
3160     if (IsEqualIID(riid, &IID_IUnknown) ||
3161         IsEqualIID(riid, &IID_IMimeSecurity))
3162     {
3163         *ppv = iface;
3164         IMimeSecurity_AddRef(iface);
3165         return S_OK;
3166     }
3167 
3168     FIXME("no interface for %s\n", debugstr_guid(riid));
3169     *ppv = NULL;
3170     return E_NOINTERFACE;
3171 }
3172 
3173 static ULONG WINAPI MimeSecurity_AddRef(IMimeSecurity *iface)
3174 {
3175     MimeSecurity *This = impl_from_IMimeSecurity(iface);
3176     LONG ref = InterlockedIncrement(&This->ref);
3177 
3178     TRACE("(%p) ref=%d\n", This, ref);
3179 
3180     return ref;
3181 }
3182 
3183 static ULONG WINAPI MimeSecurity_Release(IMimeSecurity *iface)
3184 {
3185     MimeSecurity *This = impl_from_IMimeSecurity(iface);
3186     LONG ref = InterlockedDecrement(&This->ref);
3187 
3188     TRACE("(%p) ref=%d\n", This, ref);
3189 
3190     if (!ref)
3191         HeapFree(GetProcessHeap(), 0, This);
3192 
3193     return ref;
3194 }
3195 
3196 static HRESULT WINAPI MimeSecurity_InitNew(
3197         IMimeSecurity* iface)
3198 {
3199     FIXME("(%p)->(): stub\n", iface);
3200     return S_OK;
3201 }
3202 
3203 static HRESULT WINAPI MimeSecurity_CheckInit(
3204         IMimeSecurity* iface)
3205 {
3206     FIXME("(%p)->(): stub\n", iface);
3207     return E_NOTIMPL;
3208 }
3209 
3210 static HRESULT WINAPI MimeSecurity_EncodeMessage(
3211         IMimeSecurity* iface,
3212         IMimeMessageTree* pTree,
3213         DWORD dwFlags)
3214 {
3215     FIXME("(%p)->(%p, %08x): stub\n", iface, pTree, dwFlags);
3216     return E_NOTIMPL;
3217 }
3218 
3219 static HRESULT WINAPI MimeSecurity_EncodeBody(
3220         IMimeSecurity* iface,
3221         IMimeMessageTree* pTree,
3222         HBODY hEncodeRoot,
3223         DWORD dwFlags)
3224 {
3225     FIXME("(%p)->(%p, %p, %08x): stub\n", iface, pTree, hEncodeRoot, dwFlags);
3226     return E_NOTIMPL;
3227 }
3228 
3229 static HRESULT WINAPI MimeSecurity_DecodeMessage(
3230         IMimeSecurity* iface,
3231         IMimeMessageTree* pTree,
3232         DWORD dwFlags)
3233 {
3234     FIXME("(%p)->(%p, %08x): stub\n", iface, pTree, dwFlags);
3235     return E_NOTIMPL;
3236 }
3237 
3238 static HRESULT WINAPI MimeSecurity_DecodeBody(
3239         IMimeSecurity* iface,
3240         IMimeMessageTree* pTree,
3241         HBODY hDecodeRoot,
3242         DWORD dwFlags)
3243 {
3244     FIXME("(%p)->(%p, %p, %08x): stub\n", iface, pTree, hDecodeRoot, dwFlags);
3245     return E_NOTIMPL;
3246 }
3247 
3248 static HRESULT WINAPI MimeSecurity_EnumCertificates(
3249         IMimeSecurity* iface,
3250         HCAPICERTSTORE hc,
3251         DWORD dwUsage,
3252         PCX509CERT pPrev,
3253         PCX509CERT* ppCert)
3254 {
3255     FIXME("(%p)->(%p, %08x, %p, %p): stub\n", iface, hc, dwUsage, pPrev, ppCert);
3256     return E_NOTIMPL;
3257 }
3258 
3259 static HRESULT WINAPI MimeSecurity_GetCertificateName(
3260         IMimeSecurity* iface,
3261         const PCX509CERT pX509Cert,
3262         const CERTNAMETYPE cn,
3263         LPSTR* ppszName)
3264 {
3265     FIXME("(%p)->(%p, %08x, %p): stub\n", iface, pX509Cert, cn, ppszName);
3266     return E_NOTIMPL;
3267 }
3268 
3269 static HRESULT WINAPI MimeSecurity_GetMessageType(
3270         IMimeSecurity* iface,
3271         const HWND hwndParent,
3272         IMimeBody* pBody,
3273         DWORD* pdwSecType)
3274 {
3275     FIXME("(%p)->(%p, %p, %p): stub\n", iface, hwndParent, pBody, pdwSecType);
3276     return E_NOTIMPL;
3277 }
3278 
3279 static HRESULT WINAPI MimeSecurity_GetCertData(
3280         IMimeSecurity* iface,
3281         const PCX509CERT pX509Cert,
3282         const CERTDATAID dataid,
3283         LPPROPVARIANT pValue)
3284 {
3285     FIXME("(%p)->(%p, %x, %p): stub\n", iface, pX509Cert, dataid, pValue);
3286     return E_NOTIMPL;
3287 }
3288 
3289 
3290 static const IMimeSecurityVtbl MimeSecurityVtbl =
3291 {
3292     MimeSecurity_QueryInterface,
3293     MimeSecurity_AddRef,
3294     MimeSecurity_Release,
3295     MimeSecurity_InitNew,
3296     MimeSecurity_CheckInit,
3297     MimeSecurity_EncodeMessage,
3298     MimeSecurity_EncodeBody,
3299     MimeSecurity_DecodeMessage,
3300     MimeSecurity_DecodeBody,
3301     MimeSecurity_EnumCertificates,
3302     MimeSecurity_GetCertificateName,
3303     MimeSecurity_GetMessageType,
3304     MimeSecurity_GetCertData
3305 };
3306 
3307 HRESULT MimeSecurity_create(IUnknown *outer, void **obj)
3308 {
3309     MimeSecurity *This;
3310 
3311     *obj = NULL;
3312 
3313     if (outer) return CLASS_E_NOAGGREGATION;
3314 
3315     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
3316     if (!This) return E_OUTOFMEMORY;
3317 
3318     This->IMimeSecurity_iface.lpVtbl = &MimeSecurityVtbl;
3319     This->ref = 1;
3320 
3321     *obj = &This->IMimeSecurity_iface;
3322     return S_OK;
3323 }
3324 
3325 /***********************************************************************
3326  *              MimeOleCreateSecurity (INETCOMM.@)
3327  */
3328 HRESULT WINAPI MimeOleCreateSecurity(IMimeSecurity **ppSecurity)
3329 {
3330     return MimeSecurity_create(NULL, (void **)ppSecurity);
3331 }
3332 
3333 static HRESULT WINAPI MimeAlloc_QueryInterface(
3334         IMimeAllocator* iface,
3335         REFIID riid,
3336         void **obj)
3337 {
3338     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), obj);
3339 
3340     if (IsEqualIID(riid, &IID_IUnknown) ||
3341         IsEqualIID(riid, &IID_IMalloc) ||
3342         IsEqualIID(riid, &IID_IMimeAllocator))
3343     {
3344         *obj = iface;
3345         IMimeAllocator_AddRef(iface);
3346         return S_OK;
3347     }
3348 
3349     FIXME("no interface for %s\n", debugstr_guid(riid));
3350     *obj = NULL;
3351     return E_NOINTERFACE;
3352 }
3353 
3354 static ULONG WINAPI MimeAlloc_AddRef(
3355         IMimeAllocator* iface)
3356 {
3357     return 2;
3358 }
3359 
3360 static ULONG WINAPI MimeAlloc_Release(
3361         IMimeAllocator* iface)
3362 {
3363     return 1;
3364 }
3365 
3366 static LPVOID WINAPI MimeAlloc_Alloc(
3367         IMimeAllocator* iface,
3368         SIZE_T cb)
3369 {
3370     return CoTaskMemAlloc(cb);
3371 }
3372 
3373 static LPVOID WINAPI MimeAlloc_Realloc(
3374         IMimeAllocator* iface,
3375         LPVOID pv,
3376         SIZE_T cb)
3377 {
3378     return CoTaskMemRealloc(pv, cb);
3379 }
3380 
3381 static void WINAPI MimeAlloc_Free(
3382         IMimeAllocator* iface,
3383         LPVOID pv)
3384 {
3385     CoTaskMemFree(pv);
3386 }
3387 
3388 static SIZE_T WINAPI MimeAlloc_GetSize(
3389         IMimeAllocator* iface,
3390         LPVOID pv)
3391 {
3392     FIXME("stub\n");
3393     return 0;
3394 }
3395 
3396 static int WINAPI MimeAlloc_DidAlloc(
3397         IMimeAllocator* iface,
3398         LPVOID pv)
3399 {
3400     FIXME("stub\n");
3401     return 0;
3402 }
3403 
3404 static void WINAPI MimeAlloc_HeapMinimize(
3405         IMimeAllocator* iface)
3406 {
3407     FIXME("stub\n");
3408     return;
3409 }
3410 
3411 static HRESULT WINAPI MimeAlloc_FreeParamInfoArray(
3412         IMimeAllocator* iface,
3413         ULONG cParams,
3414         LPMIMEPARAMINFO prgParam,
3415         boolean fFreeArray)
3416 {
3417     ULONG i;
3418     TRACE("(%p)->(%d, %p, %d)\n", iface, cParams, prgParam, fFreeArray);
3419 
3420     for(i = 0; i < cParams; i++)
3421     {
3422         IMimeAllocator_Free(iface, prgParam[i].pszName);
3423         IMimeAllocator_Free(iface, prgParam[i].pszData);
3424     }
3425     if(fFreeArray) IMimeAllocator_Free(iface, prgParam);
3426     return S_OK;
3427 }
3428 
3429 static HRESULT WINAPI MimeAlloc_FreeAddressList(
3430         IMimeAllocator* iface,
3431         LPADDRESSLIST pList)
3432 {
3433     FIXME("stub\n");
3434     return E_NOTIMPL;
3435 }
3436 
3437 static HRESULT WINAPI MimeAlloc_FreeAddressProps(
3438         IMimeAllocator* iface,
3439         LPADDRESSPROPS pAddress)
3440 {
3441     FIXME("stub\n");
3442     return E_NOTIMPL;
3443 }
3444 
3445 static HRESULT WINAPI MimeAlloc_ReleaseObjects(
3446         IMimeAllocator* iface,
3447         ULONG cObjects,
3448         IUnknown **prgpUnknown,
3449         boolean fFreeArray)
3450 {
3451     FIXME("stub\n");
3452     return E_NOTIMPL;
3453 }
3454 
3455 
3456 static HRESULT WINAPI MimeAlloc_FreeEnumHeaderRowArray(
3457         IMimeAllocator* iface,
3458         ULONG cRows,
3459         LPENUMHEADERROW prgRow,
3460         boolean fFreeArray)
3461 {
3462     FIXME("stub\n");
3463     return E_NOTIMPL;
3464 }
3465 
3466 static HRESULT WINAPI MimeAlloc_FreeEnumPropertyArray(
3467         IMimeAllocator* iface,
3468         ULONG cProps,
3469         LPENUMPROPERTY prgProp,
3470         boolean fFreeArray)
3471 {
3472     FIXME("stub\n");
3473     return E_NOTIMPL;
3474 }
3475 
3476 static HRESULT WINAPI MimeAlloc_FreeThumbprint(
3477         IMimeAllocator* iface,
3478         THUMBBLOB *pthumbprint)
3479 {
3480     FIXME("stub\n");
3481     return E_NOTIMPL;
3482 }
3483 
3484 
3485 static HRESULT WINAPI MimeAlloc_PropVariantClear(
3486         IMimeAllocator* iface,
3487         LPPROPVARIANT pProp)
3488 {
3489     FIXME("stub\n");
3490     return E_NOTIMPL;
3491 }
3492 
3493 static IMimeAllocatorVtbl mime_alloc_vtbl =
3494 {
3495     MimeAlloc_QueryInterface,
3496     MimeAlloc_AddRef,
3497     MimeAlloc_Release,
3498     MimeAlloc_Alloc,
3499     MimeAlloc_Realloc,
3500     MimeAlloc_Free,
3501     MimeAlloc_GetSize,
3502     MimeAlloc_DidAlloc,
3503     MimeAlloc_HeapMinimize,
3504     MimeAlloc_FreeParamInfoArray,
3505     MimeAlloc_FreeAddressList,
3506     MimeAlloc_FreeAddressProps,
3507     MimeAlloc_ReleaseObjects,
3508     MimeAlloc_FreeEnumHeaderRowArray,
3509     MimeAlloc_FreeEnumPropertyArray,
3510     MimeAlloc_FreeThumbprint,
3511     MimeAlloc_PropVariantClear
3512 };
3513 
3514 static IMimeAllocator mime_allocator =
3515 {
3516     &mime_alloc_vtbl
3517 };
3518 
3519 HRESULT MimeAllocator_create(IUnknown *outer, void **obj)
3520 {
3521     if(outer) return CLASS_E_NOAGGREGATION;
3522 
3523     *obj = &mime_allocator;
3524     return S_OK;
3525 }
3526 
3527 HRESULT WINAPI MimeOleGetAllocator(IMimeAllocator **alloc)
3528 {
3529     return MimeAllocator_create(NULL, (void**)alloc);
3530 }
3531 
3532 HRESULT VirtualStream_create(IUnknown *outer, void **obj)
3533 {
3534     FIXME("(%p, %p)\n", outer, obj);
3535 
3536     *obj = NULL;
3537     if (outer) return CLASS_E_NOAGGREGATION;
3538 
3539     return MimeOleCreateVirtualStream((IStream **)obj);
3540 }
3541 
3542 /* IMimePropertySchema Interface */
3543 static HRESULT WINAPI propschema_QueryInterface(IMimePropertySchema *iface, REFIID riid, void **out)
3544 {
3545     propschema *This = impl_from_IMimePropertySchema(iface);
3546     TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), out);
3547 
3548     *out = NULL;
3549 
3550     if (IsEqualIID(riid, &IID_IUnknown) ||
3551         IsEqualIID(riid, &IID_IMimePropertySchema))
3552     {
3553         *out = iface;
3554     }
3555     else
3556     {
3557         FIXME("no interface for %s\n", debugstr_guid(riid));
3558         return E_NOINTERFACE;
3559     }
3560 
3561     IMimePropertySchema_AddRef(iface);
3562     return S_OK;
3563 }
3564 
3565 static ULONG WINAPI propschema_AddRef(IMimePropertySchema *iface)
3566 {
3567     propschema *This = impl_from_IMimePropertySchema(iface);
3568     LONG ref = InterlockedIncrement(&This->ref);
3569 
3570     TRACE("(%p) ref=%d\n", This, ref);
3571 
3572     return ref;
3573 }
3574 
3575 static ULONG WINAPI propschema_Release(IMimePropertySchema *iface)
3576 {
3577     propschema *This = impl_from_IMimePropertySchema(iface);
3578     LONG ref = InterlockedDecrement(&This->ref);
3579 
3580     TRACE("(%p) ref=%d\n", This, ref);
3581 
3582     if (!ref)
3583     {
3584         HeapFree(GetProcessHeap(), 0, This);
3585     }
3586 
3587     return ref;
3588 }
3589 
3590 static HRESULT WINAPI propschema_RegisterProperty(IMimePropertySchema *iface, const char *name, DWORD flags,
3591         DWORD rownumber, VARTYPE vtdefault, DWORD *propid)
3592 {
3593     propschema *This = impl_from_IMimePropertySchema(iface);
3594     FIXME("(%p)->(%s, %x, %d, %d, %p) stub\n", This, debugstr_a(name), flags, rownumber, vtdefault, propid);
3595     return E_NOTIMPL;
3596 }
3597 
3598 static HRESULT WINAPI propschema_ModifyProperty(IMimePropertySchema *iface, const char *name, DWORD flags,
3599         DWORD rownumber, VARTYPE vtdefault)
3600 {
3601     propschema *This = impl_from_IMimePropertySchema(iface);
3602     FIXME("(%p)->(%s, %x, %d, %d) stub\n", This, debugstr_a(name), flags, rownumber, vtdefault);
3603     return S_OK;
3604 }
3605 
3606 static HRESULT WINAPI propschema_GetPropertyId(IMimePropertySchema *iface, const char *name, DWORD *propid)
3607 {
3608     propschema *This = impl_from_IMimePropertySchema(iface);
3609     FIXME("(%p)->(%s, %p) stub\n", This, debugstr_a(name), propid);
3610     return E_NOTIMPL;
3611 }
3612 
3613 static HRESULT WINAPI propschema_GetPropertyName(IMimePropertySchema *iface, DWORD propid, char **name)
3614 {
3615     propschema *This = impl_from_IMimePropertySchema(iface);
3616     FIXME("(%p)->(%d, %p) stub\n", This, propid, name);
3617     return E_NOTIMPL;
3618 }
3619 
3620 static HRESULT WINAPI propschema_RegisterAddressType(IMimePropertySchema *iface, const char *name, DWORD *adrtype)
3621 {
3622     propschema *This = impl_from_IMimePropertySchema(iface);
3623     FIXME("(%p)->(%s, %p) stub\n", This, debugstr_a(name), adrtype);
3624     return E_NOTIMPL;
3625 }
3626 
3627 static IMimePropertySchemaVtbl prop_schema_vtbl =
3628 {
3629     propschema_QueryInterface,
3630     propschema_AddRef,
3631     propschema_Release,
3632     propschema_RegisterProperty,
3633     propschema_ModifyProperty,
3634     propschema_GetPropertyId,
3635     propschema_GetPropertyName,
3636     propschema_RegisterAddressType
3637 };
3638 
3639 
3640 HRESULT WINAPI MimeOleGetPropertySchema(IMimePropertySchema **schema)
3641 {
3642     propschema *This;
3643 
3644     TRACE("(%p) stub\n", schema);
3645 
3646     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
3647     if (!This)
3648         return E_OUTOFMEMORY;
3649 
3650     This->IMimePropertySchema_iface.lpVtbl = &prop_schema_vtbl;
3651     This->ref = 1;
3652 
3653     *schema = &This->IMimePropertySchema_iface;
3654 
3655     return S_OK;
3656 }
3657 
3658 HRESULT WINAPI MimeGetAddressFormatW(REFIID riid, void *object, DWORD addr_type,
3659        ADDRESSFORMAT addr_format, WCHAR **address)
3660 {
3661     FIXME("(%s, %p, %d, %d, %p) stub\n", debugstr_guid(riid), object, addr_type, addr_format, address);
3662 
3663     return E_NOTIMPL;
3664 }
3665 
3666 static HRESULT WINAPI mime_obj_QueryInterface(IUnknown *iface, REFIID riid, void **ppv)
3667 {
3668     FIXME("(%s %p)\n", debugstr_guid(riid), ppv);
3669     *ppv = NULL;
3670     return E_NOINTERFACE;
3671 }
3672 
3673 static ULONG WINAPI mime_obj_AddRef(IUnknown *iface)
3674 {
3675     TRACE("\n");
3676     return 2;
3677 }
3678 
3679 static ULONG WINAPI mime_obj_Release(IUnknown *iface)
3680 {
3681     TRACE("\n");
3682     return 1;
3683 }
3684 
3685 static const IUnknownVtbl mime_obj_vtbl = {
3686     mime_obj_QueryInterface,
3687     mime_obj_AddRef,
3688     mime_obj_Release
3689 };
3690 
3691 static IUnknown mime_obj = { &mime_obj_vtbl };
3692 
3693 HRESULT WINAPI MimeOleObjectFromMoniker(BINDF bindf, IMoniker *moniker, IBindCtx *binding,
3694        REFIID riid, void **out, IMoniker **moniker_new)
3695 {
3696     WCHAR *display_name, *mhtml_url;
3697     size_t len;
3698     HRESULT hres;
3699 
3700     static const WCHAR mhtml_prefixW[] = {'m','h','t','m','l',':'};
3701 
3702     WARN("(0x%08x, %p, %p, %s, %p, %p) semi-stub\n", bindf, moniker, binding, debugstr_guid(riid), out, moniker_new);
3703 
3704     if(!IsEqualGUID(&IID_IUnknown, riid)) {
3705         FIXME("Unsupported riid %s\n", debugstr_guid(riid));
3706         return E_NOINTERFACE;
3707     }
3708 
3709     hres = IMoniker_GetDisplayName(moniker, NULL, NULL, &display_name);
3710     if(FAILED(hres))
3711         return hres;
3712 
3713     TRACE("display name %s\n", debugstr_w(display_name));
3714 
3715     len = strlenW(display_name);
3716     mhtml_url = heap_alloc((len+1)*sizeof(WCHAR) + sizeof(mhtml_prefixW));
3717     if(!mhtml_url)
3718         return E_OUTOFMEMORY;
3719 
3720     memcpy(mhtml_url, mhtml_prefixW, sizeof(mhtml_prefixW));
3721     strcpyW(mhtml_url + sizeof(mhtml_prefixW)/sizeof(WCHAR), display_name);
3722     HeapFree(GetProcessHeap(), 0, display_name);
3723 
3724     hres = CreateURLMoniker(NULL, mhtml_url, moniker_new);
3725     heap_free(mhtml_url);
3726     if(FAILED(hres))
3727         return hres;
3728 
3729     /* FIXME: We most likely should start binding here and return something more meaningful as mime object. */
3730     *out = &mime_obj;
3731     return S_OK;
3732 }
3733