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