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