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