inetcomm: Implement IMimeMessage_GetTextBody.
[wine] / dlls / inetcomm / mimeole.c
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
35 #include "wine/list.h"
36 #include "wine/debug.h"
37
38 #include "inetcomm_private.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(inetcomm);
41
42 typedef struct
43 {
44     LPCSTR     name;
45     DWORD      id;
46     DWORD      flags; /* MIMEPROPFLAGS */
47     VARTYPE    default_vt;
48 } property_t;
49
50 typedef struct
51 {
52     struct list entry;
53     property_t prop;
54 } property_list_entry_t;
55
56 static const property_t default_props[] =
57 {
58     {"References",                   PID_HDR_REFS,       0,                               VT_LPSTR},
59     {"Subject",                      PID_HDR_SUBJECT,    0,                               VT_LPSTR},
60     {"From",                         PID_HDR_FROM,       MPF_ADDRESS,                     VT_LPSTR},
61     {"Message-ID",                   PID_HDR_MESSAGEID,  0,                               VT_LPSTR},
62     {"Return-Path",                  PID_HDR_RETURNPATH, MPF_ADDRESS,                     VT_LPSTR},
63     {"Date",                         PID_HDR_DATE,       0,                               VT_LPSTR},
64     {"Received",                     PID_HDR_RECEIVED,   0,                               VT_LPSTR},
65     {"Reply-To",                     PID_HDR_REPLYTO,    MPF_ADDRESS,                     VT_LPSTR},
66     {"X-Mailer",                     PID_HDR_XMAILER,    0,                               VT_LPSTR},
67     {"Bcc",                          PID_HDR_BCC,        MPF_ADDRESS,                     VT_LPSTR},
68     {"MIME-Version",                 PID_HDR_MIMEVER,    MPF_MIME,                        VT_LPSTR},
69     {"Content-Type",                 PID_HDR_CNTTYPE,    MPF_MIME | MPF_HASPARAMS,        VT_LPSTR},
70     {"Content-Transfer-Encoding",    PID_HDR_CNTXFER,    MPF_MIME,                        VT_LPSTR},
71     {"Content-ID",                   PID_HDR_CNTID,      MPF_MIME,                        VT_LPSTR},
72     {"Content-Disposition",          PID_HDR_CNTDISP,    MPF_MIME,                        VT_LPSTR},
73     {"To",                           PID_HDR_TO,         MPF_ADDRESS,                     VT_LPSTR},
74     {"Cc",                           PID_HDR_CC,         MPF_ADDRESS,                     VT_LPSTR},
75     {"Sender",                       PID_HDR_SENDER,     MPF_ADDRESS,                     VT_LPSTR},
76     {"In-Reply-To",                  PID_HDR_INREPLYTO,  0,                               VT_LPSTR},
77     {NULL,                           0,                  0,                               0}
78 };
79
80 typedef struct
81 {
82     struct list entry;
83     char *name;
84     char *value;
85 } param_t;
86
87 typedef struct
88 {
89     struct list entry;
90     const property_t *prop;
91     PROPVARIANT value;
92     struct list params;
93 } header_t;
94
95 typedef struct MimeBody
96 {
97     const IMimeBodyVtbl *lpVtbl;
98     LONG refs;
99
100     HBODY handle;
101
102     struct list headers;
103     struct list new_props; /* FIXME: This should be in a PropertySchema */
104     DWORD next_prop_id;
105     char *content_pri_type;
106     char *content_sub_type;
107     ENCODINGTYPE encoding;
108     void *data;
109     IID data_iid;
110     BODYOFFSETS body_offsets;
111 } MimeBody;
112
113 static inline MimeBody *impl_from_IMimeBody( IMimeBody *iface )
114 {
115     return (MimeBody *)((char*)iface - FIELD_OFFSET(MimeBody, lpVtbl));
116 }
117
118 static LPSTR strdupA(LPCSTR str)
119 {
120     char *ret;
121     int len = strlen(str);
122     ret = HeapAlloc(GetProcessHeap(), 0, len + 1);
123     memcpy(ret, str, len + 1);
124     return ret;
125 }
126
127 #define PARSER_BUF_SIZE 1024
128
129 /*****************************************************
130  *        copy_headers_to_buf [internal]
131  *
132  * Copies the headers into a '\0' terminated memory block and leave
133  * the stream's current position set to after the blank line.
134  */
135 static HRESULT copy_headers_to_buf(IStream *stm, char **ptr)
136 {
137     char *buf = NULL;
138     DWORD size = PARSER_BUF_SIZE, offset = 0, last_end = 0;
139     HRESULT hr;
140     int done = 0;
141
142     *ptr = NULL;
143
144     do
145     {
146         char *end;
147         DWORD read;
148
149         if(!buf)
150             buf = HeapAlloc(GetProcessHeap(), 0, size + 1);
151         else
152         {
153             size *= 2;
154             buf = HeapReAlloc(GetProcessHeap(), 0, buf, size + 1);
155         }
156         if(!buf)
157         {
158             hr = E_OUTOFMEMORY;
159             goto fail;
160         }
161
162         hr = IStream_Read(stm, buf + offset, size - offset, &read);
163         if(FAILED(hr)) goto fail;
164
165         offset += read;
166         buf[offset] = '\0';
167
168         if(read == 0) done = 1;
169
170         while(!done && (end = strstr(buf + last_end, "\r\n")))
171         {
172             DWORD new_end = end - buf + 2;
173             if(new_end - last_end == 2)
174             {
175                 LARGE_INTEGER off;
176                 off.QuadPart = new_end;
177                 IStream_Seek(stm, off, STREAM_SEEK_SET, NULL);
178                 buf[new_end] = '\0';
179                 done = 1;
180             }
181             else
182                 last_end = new_end;
183         }
184     } while(!done);
185
186     *ptr = buf;
187     return S_OK;
188
189 fail:
190     HeapFree(GetProcessHeap(), 0, buf);
191     return hr;
192 }
193
194 static header_t *read_prop(MimeBody *body, char **ptr)
195 {
196     char *colon = strchr(*ptr, ':');
197     const property_t *prop;
198     header_t *ret;
199
200     if(!colon) return NULL;
201
202     *colon = '\0';
203
204     for(prop = default_props; prop->name; prop++)
205     {
206         if(!strcasecmp(*ptr, prop->name))
207         {
208             TRACE("%s: found match with default property id %d\n", *ptr, prop->id);
209             break;
210         }
211     }
212
213     if(!prop->name)
214     {
215         property_list_entry_t *prop_entry;
216         LIST_FOR_EACH_ENTRY(prop_entry, &body->new_props, property_list_entry_t, entry)
217         {
218             if(!strcasecmp(*ptr, prop_entry->prop.name))
219             {
220                 TRACE("%s: found match with already added new property id %d\n", *ptr, prop_entry->prop.id);
221                 prop = &prop_entry->prop;
222                 break;
223             }
224         }
225         if(!prop->name)
226         {
227             prop_entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*prop_entry));
228             prop_entry->prop.name = strdupA(*ptr);
229             prop_entry->prop.id = body->next_prop_id++;
230             prop_entry->prop.flags = 0;
231             prop_entry->prop.default_vt = VT_LPSTR;
232             list_add_tail(&body->new_props, &prop_entry->entry);
233             prop = &prop_entry->prop;
234             TRACE("%s: allocating new prop id %d\n", *ptr, prop_entry->prop.id);
235         }
236     }
237
238     ret = HeapAlloc(GetProcessHeap(), 0, sizeof(*ret));
239     ret->prop = prop;
240     PropVariantInit(&ret->value);
241     list_init(&ret->params);
242     *ptr = colon + 1;
243
244     return ret;
245 }
246
247 static void unfold_header(char *header, int len)
248 {
249     char *start = header, *cp = header;
250
251     do {
252         while(*cp == ' ' || *cp == '\t')
253         {
254             cp++;
255             len--;
256         }
257         if(cp != start)
258             memmove(start, cp, len + 1);
259
260         cp = strstr(start, "\r\n");
261         len -= (cp - start);
262         start = cp;
263         *start = ' ';
264         start++;
265         len--;
266         cp += 2;
267     } while(*cp == ' ' || *cp == '\t');
268
269     *(start - 1) = '\0';
270 }
271
272 static char *unquote_string(const char *str)
273 {
274     int quoted = 0;
275     char *ret, *cp;
276
277     while(*str == ' ' || *str == '\t') str++;
278
279     if(*str == '"')
280     {
281         quoted = 1;
282         str++;
283     }
284     ret = strdupA(str);
285     for(cp = ret; *cp; cp++)
286     {
287         if(*cp == '\\')
288             memmove(cp, cp + 1, strlen(cp + 1) + 1);
289         else if(*cp == '"')
290         {
291             if(!quoted)
292             {
293                 WARN("quote in unquoted string\n");
294             }
295             else
296             {
297                 *cp = '\0';
298                 break;
299             }
300         }
301     }
302     return ret;
303 }
304
305 static void add_param(header_t *header, const char *p)
306 {
307     const char *key = p, *value, *cp = p;
308     param_t *param;
309     char *name;
310
311     TRACE("got param %s\n", p);
312
313     while (*key == ' ' || *key == '\t' ) key++;
314
315     cp = strchr(key, '=');
316     if(!cp)
317     {
318         WARN("malformed parameter - skipping\n");
319         return;
320     }
321
322     name = HeapAlloc(GetProcessHeap(), 0, cp - key + 1);
323     memcpy(name, key, cp - key);
324     name[cp - key] = '\0';
325
326     value = cp + 1;
327
328     param = HeapAlloc(GetProcessHeap(), 0, sizeof(*param));
329     param->name = name;
330     param->value = unquote_string(value);
331     list_add_tail(&header->params, &param->entry);
332 }
333
334 static void split_params(header_t *header, char *value)
335 {
336     char *cp = value, *start = value;
337     int in_quote = 0;
338     int done_value = 0;
339
340     while(*cp)
341     {
342         if(!in_quote && *cp == ';')
343         {
344             *cp = '\0';
345             if(done_value) add_param(header, start);
346             done_value = 1;
347             start = cp + 1;
348         }
349         else if(*cp == '"')
350             in_quote = !in_quote;
351         cp++;
352     }
353     if(done_value) add_param(header, start);
354 }
355
356 static void read_value(header_t *header, char **cur)
357 {
358     char *end = *cur, *value;
359     DWORD len;
360
361     do {
362         end = strstr(end, "\r\n");
363         end += 2;
364     } while(*end == ' ' || *end == '\t');
365
366     len = end - *cur;
367     value = HeapAlloc(GetProcessHeap(), 0, len + 1);
368     memcpy(value, *cur, len);
369     value[len] = '\0';
370
371     unfold_header(value, len);
372     TRACE("value %s\n", debugstr_a(value));
373
374     if(header->prop->flags & MPF_HASPARAMS)
375     {
376         split_params(header, value);
377         TRACE("value w/o params %s\n", debugstr_a(value));
378     }
379
380     header->value.vt = VT_LPSTR;
381     header->value.u.pszVal = value;
382
383     *cur = end;
384 }
385
386 static void init_content_type(MimeBody *body, header_t *header)
387 {
388     char *slash;
389     DWORD len;
390
391     if(header->prop->id != PID_HDR_CNTTYPE)
392     {
393         ERR("called with header %s\n", header->prop->name);
394         return;
395     }
396
397     slash = strchr(header->value.u.pszVal, '/');
398     if(!slash)
399     {
400         WARN("malformed context type value\n");
401         return;
402     }
403     len = slash - header->value.u.pszVal;
404     body->content_pri_type = HeapAlloc(GetProcessHeap(), 0, len + 1);
405     memcpy(body->content_pri_type, header->value.u.pszVal, len);
406     body->content_pri_type[len] = '\0';
407     body->content_sub_type = strdupA(slash + 1);
408 }
409
410 static HRESULT parse_headers(MimeBody *body, IStream *stm)
411 {
412     char *header_buf, *cur_header_ptr;
413     HRESULT hr;
414     header_t *header;
415
416     hr = copy_headers_to_buf(stm, &header_buf);
417     if(FAILED(hr)) return hr;
418
419     cur_header_ptr = header_buf;
420     while((header = read_prop(body, &cur_header_ptr)))
421     {
422         read_value(header, &cur_header_ptr);
423         list_add_tail(&body->headers, &header->entry);
424
425         if(header->prop->id == PID_HDR_CNTTYPE)
426             init_content_type(body, header);
427     }
428
429     HeapFree(GetProcessHeap(), 0, header_buf);
430     return hr;
431 }
432
433 static void empty_param_list(struct list *list)
434 {
435     param_t *param, *cursor2;
436
437     LIST_FOR_EACH_ENTRY_SAFE(param, cursor2, list, param_t, entry)
438     {
439         list_remove(&param->entry);
440         HeapFree(GetProcessHeap(), 0, param->name);
441         HeapFree(GetProcessHeap(), 0, param->value);
442         HeapFree(GetProcessHeap(), 0, param);
443     }
444 }
445
446 static void empty_header_list(struct list *list)
447 {
448     header_t *header, *cursor2;
449
450     LIST_FOR_EACH_ENTRY_SAFE(header, cursor2, list, header_t, entry)
451     {
452         list_remove(&header->entry);
453         PropVariantClear(&header->value);
454         empty_param_list(&header->params);
455         HeapFree(GetProcessHeap(), 0, header);
456     }
457 }
458
459 static void empty_new_prop_list(struct list *list)
460 {
461     property_list_entry_t *prop, *cursor2;
462
463     LIST_FOR_EACH_ENTRY_SAFE(prop, cursor2, list, property_list_entry_t, entry)
464     {
465         list_remove(&prop->entry);
466         HeapFree(GetProcessHeap(), 0, (char *)prop->prop.name);
467         HeapFree(GetProcessHeap(), 0, prop);
468     }
469 }
470
471 static void release_data(REFIID riid, void *data)
472 {
473     if(!data) return;
474
475     if(IsEqualIID(riid, &IID_IStream))
476         IStream_Release((IStream *)data);
477     else
478         FIXME("Unhandled data format %s\n", debugstr_guid(riid));
479 }
480
481 static HRESULT find_prop(MimeBody *body, const char *name, header_t **prop)
482 {
483     header_t *header;
484
485     *prop = NULL;
486
487     LIST_FOR_EACH_ENTRY(header, &body->headers, header_t, entry)
488     {
489         if(!strcasecmp(name, header->prop->name))
490         {
491             *prop = header;
492             return S_OK;
493         }
494     }
495
496     return MIME_E_NOT_FOUND;
497 }
498
499 static HRESULT WINAPI MimeBody_QueryInterface(IMimeBody* iface,
500                                      REFIID riid,
501                                      void** ppvObject)
502 {
503     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppvObject);
504
505     *ppvObject = NULL;
506
507     if (IsEqualIID(riid, &IID_IUnknown) ||
508         IsEqualIID(riid, &IID_IPersist) ||
509         IsEqualIID(riid, &IID_IPersistStreamInit) ||
510         IsEqualIID(riid, &IID_IMimePropertySet) ||
511         IsEqualIID(riid, &IID_IMimeBody))
512     {
513         *ppvObject = iface;
514     }
515
516     if(*ppvObject)
517     {
518         IUnknown_AddRef((IUnknown*)*ppvObject);
519         return S_OK;
520     }
521
522     FIXME("no interface for %s\n", debugstr_guid(riid));
523     return E_NOINTERFACE;
524 }
525
526 static ULONG WINAPI MimeBody_AddRef(IMimeBody* iface)
527 {
528     MimeBody *This = impl_from_IMimeBody(iface);
529     TRACE("(%p)->()\n", iface);
530     return InterlockedIncrement(&This->refs);
531 }
532
533 static ULONG WINAPI MimeBody_Release(IMimeBody* iface)
534 {
535     MimeBody *This = impl_from_IMimeBody(iface);
536     ULONG refs;
537
538     TRACE("(%p)->()\n", iface);
539
540     refs = InterlockedDecrement(&This->refs);
541     if (!refs)
542     {
543         empty_header_list(&This->headers);
544         empty_new_prop_list(&This->new_props);
545
546         HeapFree(GetProcessHeap(), 0, This->content_pri_type);
547         HeapFree(GetProcessHeap(), 0, This->content_sub_type);
548
549         release_data(&This->data_iid, This->data);
550
551         HeapFree(GetProcessHeap(), 0, This);
552     }
553
554     return refs;
555 }
556
557 static HRESULT WINAPI MimeBody_GetClassID(
558                                  IMimeBody* iface,
559                                  CLSID* pClassID)
560 {
561     FIXME("stub\n");
562     return E_NOTIMPL;
563 }
564
565
566 static HRESULT WINAPI MimeBody_IsDirty(
567                               IMimeBody* iface)
568 {
569     FIXME("stub\n");
570     return E_NOTIMPL;
571 }
572
573 static HRESULT WINAPI MimeBody_Load(
574                            IMimeBody* iface,
575                            LPSTREAM pStm)
576 {
577     MimeBody *This = impl_from_IMimeBody(iface);
578     TRACE("(%p)->(%p)\n", iface, pStm);
579     return parse_headers(This, pStm);
580 }
581
582 static HRESULT WINAPI MimeBody_Save(
583                            IMimeBody* iface,
584                            LPSTREAM pStm,
585                            BOOL fClearDirty)
586 {
587     FIXME("stub\n");
588     return E_NOTIMPL;
589 }
590
591 static HRESULT WINAPI MimeBody_GetSizeMax(
592                                  IMimeBody* iface,
593                                  ULARGE_INTEGER* pcbSize)
594 {
595     FIXME("stub\n");
596     return E_NOTIMPL;
597 }
598
599 static HRESULT WINAPI MimeBody_InitNew(
600                               IMimeBody* iface)
601 {
602     TRACE("%p->()\n", iface);
603     return S_OK;
604 }
605
606 static HRESULT WINAPI MimeBody_GetPropInfo(
607                                   IMimeBody* iface,
608                                   LPCSTR pszName,
609                                   LPMIMEPROPINFO pInfo)
610 {
611     FIXME("stub\n");
612     return E_NOTIMPL;
613 }
614
615 static HRESULT WINAPI MimeBody_SetPropInfo(
616                                   IMimeBody* iface,
617                                   LPCSTR pszName,
618                                   LPCMIMEPROPINFO pInfo)
619 {
620     FIXME("stub\n");
621     return E_NOTIMPL;
622 }
623
624 static HRESULT WINAPI MimeBody_GetProp(
625                               IMimeBody* iface,
626                               LPCSTR pszName,
627                               DWORD dwFlags,
628                               LPPROPVARIANT pValue)
629 {
630     FIXME("stub\n");
631     return E_NOTIMPL;
632 }
633
634 static HRESULT WINAPI MimeBody_SetProp(
635                               IMimeBody* iface,
636                               LPCSTR pszName,
637                               DWORD dwFlags,
638                               LPCPROPVARIANT pValue)
639 {
640     FIXME("stub\n");
641     return E_NOTIMPL;
642 }
643
644 static HRESULT WINAPI MimeBody_AppendProp(
645                                  IMimeBody* iface,
646                                  LPCSTR pszName,
647                                  DWORD dwFlags,
648                                  LPPROPVARIANT pValue)
649 {
650     FIXME("stub\n");
651     return E_NOTIMPL;
652 }
653
654 static HRESULT WINAPI MimeBody_DeleteProp(
655                                  IMimeBody* iface,
656                                  LPCSTR pszName)
657 {
658     FIXME("stub\n");
659     return E_NOTIMPL;
660 }
661
662 static HRESULT WINAPI MimeBody_CopyProps(
663                                 IMimeBody* iface,
664                                 ULONG cNames,
665                                 LPCSTR* prgszName,
666                                 IMimePropertySet* pPropertySet)
667 {
668     FIXME("stub\n");
669     return E_NOTIMPL;
670 }
671
672 static HRESULT WINAPI MimeBody_MoveProps(
673                                 IMimeBody* iface,
674                                 ULONG cNames,
675                                 LPCSTR* prgszName,
676                                 IMimePropertySet* pPropertySet)
677 {
678     FIXME("stub\n");
679     return E_NOTIMPL;
680 }
681
682 static HRESULT WINAPI MimeBody_DeleteExcept(
683                                    IMimeBody* iface,
684                                    ULONG cNames,
685                                    LPCSTR* prgszName)
686 {
687     FIXME("stub\n");
688     return E_NOTIMPL;
689 }
690
691 static HRESULT WINAPI MimeBody_QueryProp(
692                                 IMimeBody* iface,
693                                 LPCSTR pszName,
694                                 LPCSTR pszCriteria,
695                                 boolean fSubString,
696                                 boolean fCaseSensitive)
697 {
698     FIXME("stub\n");
699     return E_NOTIMPL;
700 }
701
702 static HRESULT WINAPI MimeBody_GetCharset(
703                                  IMimeBody* iface,
704                                  LPHCHARSET phCharset)
705 {
706     FIXME("stub\n");
707     return E_NOTIMPL;
708 }
709
710 static HRESULT WINAPI MimeBody_SetCharset(
711                                  IMimeBody* iface,
712                                  HCHARSET hCharset,
713                                  CSETAPPLYTYPE applytype)
714 {
715     FIXME("stub\n");
716     return E_NOTIMPL;
717 }
718
719 static HRESULT WINAPI MimeBody_GetParameters(
720                                     IMimeBody* iface,
721                                     LPCSTR pszName,
722                                     ULONG* pcParams,
723                                     LPMIMEPARAMINFO* pprgParam)
724 {
725     MimeBody *This = impl_from_IMimeBody(iface);
726     HRESULT hr;
727     header_t *header;
728
729     TRACE("(%p)->(%s, %p, %p)\n", iface, debugstr_a(pszName), pcParams, pprgParam);
730
731     *pprgParam = NULL;
732     *pcParams = 0;
733
734     hr = find_prop(This, pszName, &header);
735     if(hr != S_OK) return hr;
736
737     *pcParams = list_count(&header->params);
738     if(*pcParams)
739     {
740         IMimeAllocator *alloc;
741         param_t *param;
742         MIMEPARAMINFO *info;
743
744         MimeOleGetAllocator(&alloc);
745
746         *pprgParam = info = IMimeAllocator_Alloc(alloc, *pcParams * sizeof(**pprgParam));
747         LIST_FOR_EACH_ENTRY(param, &header->params, param_t, entry)
748         {
749             int len;
750
751             len = strlen(param->name) + 1;
752             info->pszName = IMimeAllocator_Alloc(alloc, len);
753             memcpy(info->pszName, param->name, len);
754             len = strlen(param->value) + 1;
755             info->pszData = IMimeAllocator_Alloc(alloc, len);
756             memcpy(info->pszData, param->value, len);
757             info++;
758         }
759         IMimeAllocator_Release(alloc);
760     }
761     return S_OK;
762 }
763
764 static HRESULT WINAPI MimeBody_IsContentType(
765                                     IMimeBody* iface,
766                                     LPCSTR pszPriType,
767                                     LPCSTR pszSubType)
768 {
769     MimeBody *This = impl_from_IMimeBody(iface);
770
771     TRACE("(%p)->(%s, %s)\n", This, debugstr_a(pszPriType), debugstr_a(pszSubType));
772     if(pszPriType)
773     {
774         const char *pri = This->content_pri_type;
775         if(!pri) pri = "text";
776         if(strcasecmp(pri, pszPriType)) return S_FALSE;
777     }
778
779     if(pszSubType)
780     {
781         const char *sub = This->content_sub_type;
782         if(!sub) sub = "plain";
783         if(strcasecmp(sub, pszSubType)) return S_FALSE;
784     }
785
786     return S_OK;
787 }
788
789 static HRESULT WINAPI MimeBody_BindToObject(
790                                    IMimeBody* iface,
791                                    REFIID riid,
792                                    void** ppvObject)
793 {
794     FIXME("stub\n");
795     return E_NOTIMPL;
796 }
797
798 static HRESULT WINAPI MimeBody_Clone(
799                             IMimeBody* iface,
800                             IMimePropertySet** ppPropertySet)
801 {
802     FIXME("stub\n");
803     return E_NOTIMPL;
804 }
805
806 static HRESULT WINAPI MimeBody_SetOption(
807                                 IMimeBody* iface,
808                                 const TYPEDID oid,
809                                 LPCPROPVARIANT pValue)
810 {
811     FIXME("stub\n");
812     return E_NOTIMPL;
813 }
814
815 static HRESULT WINAPI MimeBody_GetOption(
816                                 IMimeBody* iface,
817                                 const TYPEDID oid,
818                                 LPPROPVARIANT pValue)
819 {
820     FIXME("stub\n");
821     return E_NOTIMPL;
822 }
823
824 static HRESULT WINAPI MimeBody_EnumProps(
825                                 IMimeBody* iface,
826                                 DWORD dwFlags,
827                                 IMimeEnumProperties** ppEnum)
828 {
829     FIXME("stub\n");
830     return E_NOTIMPL;
831 }
832
833 static HRESULT WINAPI MimeBody_IsType(
834                              IMimeBody* iface,
835                              IMSGBODYTYPE bodytype)
836 {
837     FIXME("stub\n");
838     return E_NOTIMPL;
839 }
840
841 static HRESULT WINAPI MimeBody_SetDisplayName(
842                                      IMimeBody* iface,
843                                      LPCSTR pszDisplay)
844 {
845     FIXME("stub\n");
846     return E_NOTIMPL;
847 }
848
849 static HRESULT WINAPI MimeBody_GetDisplayName(
850                                      IMimeBody* iface,
851                                      LPSTR* ppszDisplay)
852 {
853     FIXME("stub\n");
854     return E_NOTIMPL;
855 }
856
857 static HRESULT WINAPI MimeBody_GetOffsets(
858                                  IMimeBody* iface,
859                                  LPBODYOFFSETS pOffsets)
860 {
861     MimeBody *This = impl_from_IMimeBody(iface);
862     TRACE("(%p)->(%p)\n", This, pOffsets);
863
864     *pOffsets = This->body_offsets;
865
866     if(This->body_offsets.cbBodyEnd == 0) return MIME_E_NO_DATA;
867     return S_OK;
868 }
869
870 static HRESULT WINAPI MimeBody_GetCurrentEncoding(
871                                          IMimeBody* iface,
872                                          ENCODINGTYPE* pietEncoding)
873 {
874     MimeBody *This = impl_from_IMimeBody(iface);
875
876     TRACE("(%p)->(%p)\n", This, pietEncoding);
877
878     *pietEncoding = This->encoding;
879     return S_OK;
880 }
881
882 static HRESULT WINAPI MimeBody_SetCurrentEncoding(
883                                          IMimeBody* iface,
884                                          ENCODINGTYPE ietEncoding)
885 {
886     MimeBody *This = impl_from_IMimeBody(iface);
887
888     TRACE("(%p)->(%d)\n", This, ietEncoding);
889
890     This->encoding = ietEncoding;
891     return S_OK;
892 }
893
894 static HRESULT WINAPI MimeBody_GetEstimatedSize(
895                                        IMimeBody* iface,
896                                        ENCODINGTYPE ietEncoding,
897                                        ULONG* pcbSize)
898 {
899     FIXME("stub\n");
900     return E_NOTIMPL;
901 }
902
903 static HRESULT WINAPI MimeBody_GetDataHere(
904                                   IMimeBody* iface,
905                                   ENCODINGTYPE ietEncoding,
906                                   IStream* pStream)
907 {
908     FIXME("stub\n");
909     return E_NOTIMPL;
910 }
911
912 static HRESULT WINAPI MimeBody_GetData(
913                               IMimeBody* iface,
914                               ENCODINGTYPE ietEncoding,
915                               IStream** ppStream)
916 {
917     MimeBody *This = impl_from_IMimeBody(iface);
918     FIXME("(%p)->(%d, %p). Ignoring encoding type.\n", This, ietEncoding, ppStream);
919
920     *ppStream = This->data;
921     IStream_AddRef(*ppStream);
922     return S_OK;
923 }
924
925 static HRESULT WINAPI MimeBody_SetData(
926                               IMimeBody* iface,
927                               ENCODINGTYPE ietEncoding,
928                               LPCSTR pszPriType,
929                               LPCSTR pszSubType,
930                               REFIID riid,
931                               LPVOID pvObject)
932 {
933     MimeBody *This = impl_from_IMimeBody(iface);
934     TRACE("(%p)->(%d, %s, %s, %s %p)\n", This, ietEncoding, debugstr_a(pszPriType), debugstr_a(pszSubType),
935           debugstr_guid(riid), pvObject);
936
937     if(IsEqualIID(riid, &IID_IStream))
938         IStream_AddRef((IStream *)pvObject);
939     else
940     {
941         FIXME("Unhandled object type %s\n", debugstr_guid(riid));
942         return E_INVALIDARG;
943     }
944
945     if(This->data)
946         FIXME("release old data\n");
947
948     This->data_iid = *riid;
949     This->data = pvObject;
950
951     IMimeBody_SetCurrentEncoding(iface, ietEncoding);
952
953     /* FIXME: Update the content type.
954        If pszPriType == NULL use 'application'
955        If pszSubType == NULL use 'octet-stream' */
956
957     return S_OK;
958 }
959
960 static HRESULT WINAPI MimeBody_EmptyData(
961                                 IMimeBody* iface)
962 {
963     FIXME("stub\n");
964     return E_NOTIMPL;
965 }
966
967 static HRESULT WINAPI MimeBody_CopyTo(
968                              IMimeBody* iface,
969                              IMimeBody* pBody)
970 {
971     FIXME("stub\n");
972     return E_NOTIMPL;
973 }
974
975 static HRESULT WINAPI MimeBody_GetTransmitInfo(
976                                       IMimeBody* iface,
977                                       LPTRANSMITINFO pTransmitInfo)
978 {
979     FIXME("stub\n");
980     return E_NOTIMPL;
981 }
982
983 static HRESULT WINAPI MimeBody_SaveToFile(
984                                  IMimeBody* iface,
985                                  ENCODINGTYPE ietEncoding,
986                                  LPCSTR pszFilePath)
987 {
988     FIXME("stub\n");
989     return E_NOTIMPL;
990 }
991
992 static HRESULT WINAPI MimeBody_GetHandle(
993                                 IMimeBody* iface,
994                                 LPHBODY phBody)
995 {
996     MimeBody *This = impl_from_IMimeBody(iface);
997     TRACE("(%p)->(%p)\n", iface, phBody);
998
999     *phBody = This->handle;
1000     return This->handle ? S_OK : MIME_E_NO_DATA;
1001 }
1002
1003 static IMimeBodyVtbl body_vtbl =
1004 {
1005     MimeBody_QueryInterface,
1006     MimeBody_AddRef,
1007     MimeBody_Release,
1008     MimeBody_GetClassID,
1009     MimeBody_IsDirty,
1010     MimeBody_Load,
1011     MimeBody_Save,
1012     MimeBody_GetSizeMax,
1013     MimeBody_InitNew,
1014     MimeBody_GetPropInfo,
1015     MimeBody_SetPropInfo,
1016     MimeBody_GetProp,
1017     MimeBody_SetProp,
1018     MimeBody_AppendProp,
1019     MimeBody_DeleteProp,
1020     MimeBody_CopyProps,
1021     MimeBody_MoveProps,
1022     MimeBody_DeleteExcept,
1023     MimeBody_QueryProp,
1024     MimeBody_GetCharset,
1025     MimeBody_SetCharset,
1026     MimeBody_GetParameters,
1027     MimeBody_IsContentType,
1028     MimeBody_BindToObject,
1029     MimeBody_Clone,
1030     MimeBody_SetOption,
1031     MimeBody_GetOption,
1032     MimeBody_EnumProps,
1033     MimeBody_IsType,
1034     MimeBody_SetDisplayName,
1035     MimeBody_GetDisplayName,
1036     MimeBody_GetOffsets,
1037     MimeBody_GetCurrentEncoding,
1038     MimeBody_SetCurrentEncoding,
1039     MimeBody_GetEstimatedSize,
1040     MimeBody_GetDataHere,
1041     MimeBody_GetData,
1042     MimeBody_SetData,
1043     MimeBody_EmptyData,
1044     MimeBody_CopyTo,
1045     MimeBody_GetTransmitInfo,
1046     MimeBody_SaveToFile,
1047     MimeBody_GetHandle
1048 };
1049
1050 static HRESULT MimeBody_set_offsets(MimeBody *body, const BODYOFFSETS *offsets)
1051 {
1052     TRACE("setting offsets to %d, %d, %d, %d\n", offsets->cbBoundaryStart,
1053           offsets->cbHeaderStart, offsets->cbBodyStart, offsets->cbBodyEnd);
1054
1055     body->body_offsets = *offsets;
1056     return S_OK;
1057 }
1058
1059 #define FIRST_CUSTOM_PROP_ID 0x100
1060
1061 HRESULT MimeBody_create(IUnknown *outer, void **obj)
1062 {
1063     MimeBody *This;
1064     BODYOFFSETS body_offsets;
1065
1066     *obj = NULL;
1067
1068     if(outer) return CLASS_E_NOAGGREGATION;
1069
1070     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
1071     if (!This) return E_OUTOFMEMORY;
1072
1073     This->lpVtbl = &body_vtbl;
1074     This->refs = 1;
1075     This->handle = NULL;
1076     list_init(&This->headers);
1077     list_init(&This->new_props);
1078     This->next_prop_id = FIRST_CUSTOM_PROP_ID;
1079     This->content_pri_type = NULL;
1080     This->content_sub_type = NULL;
1081     This->encoding = IET_7BIT;
1082     This->data = NULL;
1083     This->data_iid = IID_NULL;
1084
1085     body_offsets.cbBoundaryStart = body_offsets.cbHeaderStart = 0;
1086     body_offsets.cbBodyStart     = body_offsets.cbBodyEnd     = 0;
1087     MimeBody_set_offsets(This, &body_offsets);
1088
1089     *obj = (IMimeBody *)&This->lpVtbl;
1090     return S_OK;
1091 }
1092
1093 typedef struct
1094 {
1095     IStreamVtbl *lpVtbl;
1096     LONG refs;
1097
1098     IStream *base;
1099     ULARGE_INTEGER pos, start, length;
1100 } sub_stream_t;
1101
1102 static inline sub_stream_t *impl_from_IStream( IStream *iface )
1103 {
1104     return (sub_stream_t *)((char*)iface - FIELD_OFFSET(sub_stream_t, lpVtbl));
1105 }
1106
1107 static HRESULT WINAPI sub_stream_QueryInterface(
1108         IStream* iface,
1109         REFIID riid,
1110         void **ppvObject)
1111 {
1112     sub_stream_t *This = impl_from_IStream(iface);
1113
1114     TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppvObject);
1115     *ppvObject = NULL;
1116
1117     if(IsEqualIID(riid, &IID_IUnknown) ||
1118        IsEqualIID(riid, &IID_ISequentialStream) ||
1119        IsEqualIID(riid, &IID_IStream))
1120     {
1121         IStream_AddRef(iface);
1122         *ppvObject = iface;
1123         return S_OK;
1124     }
1125     return E_NOINTERFACE;
1126 }
1127
1128 static ULONG WINAPI sub_stream_AddRef(
1129          IStream* iface)
1130 {
1131     sub_stream_t *This = impl_from_IStream(iface);
1132
1133     TRACE("(%p)\n", This);
1134     return InterlockedIncrement(&This->refs);
1135 }
1136
1137 static ULONG WINAPI  sub_stream_Release(
1138         IStream* iface)
1139 {
1140     sub_stream_t *This = impl_from_IStream(iface);
1141     LONG refs;
1142
1143     TRACE("(%p)\n", This);
1144     refs = InterlockedDecrement(&This->refs);
1145     if(!refs)
1146     {
1147         IStream_Release(This->base);
1148         HeapFree(GetProcessHeap(), 0, This);
1149     }
1150     return refs;
1151 }
1152
1153 static HRESULT WINAPI sub_stream_Read(
1154         IStream* iface,
1155         void *pv,
1156         ULONG cb,
1157         ULONG *pcbRead)
1158 {
1159     sub_stream_t *This = impl_from_IStream(iface);
1160     HRESULT hr;
1161     ULARGE_INTEGER base_pos;
1162     LARGE_INTEGER tmp_pos;
1163
1164     TRACE("(%p, %d, %p)\n", pv, cb, pcbRead);
1165
1166     tmp_pos.QuadPart = 0;
1167     IStream_Seek(This->base, tmp_pos, STREAM_SEEK_CUR, &base_pos);
1168     tmp_pos.QuadPart = This->pos.QuadPart + This->start.QuadPart;
1169     IStream_Seek(This->base, tmp_pos, STREAM_SEEK_SET, NULL);
1170
1171     if(This->pos.QuadPart + cb > This->length.QuadPart)
1172         cb = This->length.QuadPart - This->pos.QuadPart;
1173
1174     hr = IStream_Read(This->base, pv, cb, pcbRead);
1175
1176     This->pos.QuadPart += *pcbRead;
1177
1178     tmp_pos.QuadPart = base_pos.QuadPart;
1179     IStream_Seek(This->base, tmp_pos, STREAM_SEEK_SET, NULL);
1180
1181     return hr;
1182 }
1183
1184 static HRESULT WINAPI sub_stream_Write(
1185         IStream* iface,
1186         const void *pv,
1187         ULONG cb,
1188         ULONG *pcbWritten)
1189 {
1190     FIXME("stub\n");
1191     return E_NOTIMPL;
1192 }
1193
1194 static HRESULT WINAPI sub_stream_Seek(
1195         IStream* iface,
1196         LARGE_INTEGER dlibMove,
1197         DWORD dwOrigin,
1198         ULARGE_INTEGER *plibNewPosition)
1199 {
1200     sub_stream_t *This = impl_from_IStream(iface);
1201     LARGE_INTEGER new_pos;
1202
1203     TRACE("(%08x.%08x, %x, %p)\n", dlibMove.u.HighPart, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
1204
1205     switch(dwOrigin)
1206     {
1207     case STREAM_SEEK_SET:
1208         new_pos = dlibMove;
1209         break;
1210     case STREAM_SEEK_CUR:
1211         new_pos.QuadPart = This->pos.QuadPart + dlibMove.QuadPart;
1212         break;
1213     case STREAM_SEEK_END:
1214         new_pos.QuadPart = This->length.QuadPart + dlibMove.QuadPart;
1215         break;
1216     }
1217
1218     if(new_pos.QuadPart < 0) new_pos.QuadPart = 0;
1219     else if(new_pos.QuadPart > This->length.QuadPart) new_pos.QuadPart = This->length.QuadPart;
1220
1221     This->pos.QuadPart = new_pos.QuadPart;
1222
1223     if(plibNewPosition) *plibNewPosition = This->pos;
1224     return S_OK;
1225 }
1226
1227 static HRESULT WINAPI sub_stream_SetSize(
1228         IStream* iface,
1229         ULARGE_INTEGER libNewSize)
1230 {
1231     FIXME("stub\n");
1232     return E_NOTIMPL;
1233 }
1234
1235 static HRESULT WINAPI sub_stream_CopyTo(
1236         IStream* iface,
1237         IStream *pstm,
1238         ULARGE_INTEGER cb,
1239         ULARGE_INTEGER *pcbRead,
1240         ULARGE_INTEGER *pcbWritten)
1241 {
1242     FIXME("stub\n");
1243     return E_NOTIMPL;
1244 }
1245
1246 static HRESULT WINAPI sub_stream_Commit(
1247         IStream* iface,
1248         DWORD grfCommitFlags)
1249 {
1250     FIXME("stub\n");
1251     return E_NOTIMPL;
1252 }
1253
1254 static HRESULT WINAPI sub_stream_Revert(
1255         IStream* iface)
1256 {
1257     FIXME("stub\n");
1258     return E_NOTIMPL;
1259 }
1260
1261 static HRESULT WINAPI sub_stream_LockRegion(
1262         IStream* iface,
1263         ULARGE_INTEGER libOffset,
1264         ULARGE_INTEGER cb,
1265         DWORD dwLockType)
1266 {
1267     FIXME("stub\n");
1268     return E_NOTIMPL;
1269 }
1270
1271 static HRESULT WINAPI sub_stream_UnlockRegion(
1272         IStream* iface,
1273         ULARGE_INTEGER libOffset,
1274         ULARGE_INTEGER cb,
1275         DWORD dwLockType)
1276 {
1277     FIXME("stub\n");
1278     return E_NOTIMPL;
1279 }
1280
1281 static HRESULT WINAPI sub_stream_Stat(
1282         IStream* iface,
1283         STATSTG *pstatstg,
1284         DWORD grfStatFlag)
1285 {
1286     sub_stream_t *This = impl_from_IStream(iface);
1287     FIXME("(%p)->(%p, %08x)\n", This, pstatstg, grfStatFlag);
1288     memset(pstatstg, 0, sizeof(*pstatstg));
1289     pstatstg->cbSize = This->length;
1290     return S_OK;
1291 }
1292
1293 static HRESULT WINAPI sub_stream_Clone(
1294         IStream* iface,
1295         IStream **ppstm)
1296 {
1297     FIXME("stub\n");
1298     return E_NOTIMPL;
1299 }
1300
1301 static struct IStreamVtbl sub_stream_vtbl =
1302 {
1303     sub_stream_QueryInterface,
1304     sub_stream_AddRef,
1305     sub_stream_Release,
1306     sub_stream_Read,
1307     sub_stream_Write,
1308     sub_stream_Seek,
1309     sub_stream_SetSize,
1310     sub_stream_CopyTo,
1311     sub_stream_Commit,
1312     sub_stream_Revert,
1313     sub_stream_LockRegion,
1314     sub_stream_UnlockRegion,
1315     sub_stream_Stat,
1316     sub_stream_Clone
1317 };
1318
1319 static HRESULT create_sub_stream(IStream *stream, ULARGE_INTEGER start, ULARGE_INTEGER length, IStream **out)
1320 {
1321     sub_stream_t *This;
1322
1323     *out = NULL;
1324     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
1325     if(!This) return E_OUTOFMEMORY;
1326
1327     This->lpVtbl = &sub_stream_vtbl;
1328     This->refs = 1;
1329     This->start = start;
1330     This->length = length;
1331     This->pos.QuadPart = 0;
1332     IStream_AddRef(stream);
1333     This->base = stream;
1334
1335     *out = (IStream*)&This->lpVtbl;
1336     return S_OK;
1337 }
1338
1339
1340 typedef struct body_t
1341 {
1342     struct list entry;
1343     HBODY hbody;
1344     IMimeBody *mime_body;
1345
1346     struct body_t *parent;
1347     struct list children;
1348 } body_t;
1349
1350 typedef struct MimeMessage
1351 {
1352     const IMimeMessageVtbl *lpVtbl;
1353
1354     LONG refs;
1355     IStream *stream;
1356
1357     struct list body_tree;
1358     HBODY next_hbody;
1359 } MimeMessage;
1360
1361 static HRESULT WINAPI MimeMessage_QueryInterface(IMimeMessage *iface, REFIID riid, void **ppv)
1362 {
1363     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1364
1365     if (IsEqualIID(riid, &IID_IUnknown) ||
1366         IsEqualIID(riid, &IID_IPersist) ||
1367         IsEqualIID(riid, &IID_IPersistStreamInit) ||
1368         IsEqualIID(riid, &IID_IMimeMessageTree) ||
1369         IsEqualIID(riid, &IID_IMimeMessage))
1370     {
1371         *ppv = iface;
1372         IUnknown_AddRef(iface);
1373         return S_OK;
1374     }
1375
1376     FIXME("no interface for %s\n", debugstr_guid(riid));
1377     *ppv = NULL;
1378     return E_NOINTERFACE;
1379 }
1380
1381 static ULONG WINAPI MimeMessage_AddRef(IMimeMessage *iface)
1382 {
1383     MimeMessage *This = (MimeMessage *)iface;
1384     TRACE("(%p)->()\n", iface);
1385     return InterlockedIncrement(&This->refs);
1386 }
1387
1388 static void empty_body_list(struct list *list)
1389 {
1390     body_t *body, *cursor2;
1391     LIST_FOR_EACH_ENTRY_SAFE(body, cursor2, list, body_t, entry)
1392     {
1393         empty_body_list(&body->children);
1394         list_remove(&body->entry);
1395         IMimeBody_Release(body->mime_body);
1396         HeapFree(GetProcessHeap(), 0, body);
1397     }
1398 }
1399
1400 static ULONG WINAPI MimeMessage_Release(IMimeMessage *iface)
1401 {
1402     MimeMessage *This = (MimeMessage *)iface;
1403     ULONG refs;
1404
1405     TRACE("(%p)->()\n", iface);
1406
1407     refs = InterlockedDecrement(&This->refs);
1408     if (!refs)
1409     {
1410         empty_body_list(&This->body_tree);
1411
1412         if(This->stream) IStream_Release(This->stream);
1413         HeapFree(GetProcessHeap(), 0, This);
1414     }
1415
1416     return refs;
1417 }
1418
1419 /*** IPersist methods ***/
1420 static HRESULT WINAPI MimeMessage_GetClassID(
1421     IMimeMessage *iface,
1422     CLSID *pClassID)
1423 {
1424     FIXME("(%p)->(%p)\n", iface, pClassID);
1425     return E_NOTIMPL;
1426 }
1427
1428 /*** IPersistStreamInit methods ***/
1429 static HRESULT WINAPI MimeMessage_IsDirty(
1430     IMimeMessage *iface)
1431 {
1432     FIXME("(%p)->()\n", iface);
1433     return E_NOTIMPL;
1434 }
1435
1436 static body_t *new_body_entry(IMimeBody *mime_body, HBODY hbody, body_t *parent)
1437 {
1438     body_t *body = HeapAlloc(GetProcessHeap(), 0, sizeof(*body));
1439     if(body)
1440     {
1441         body->mime_body = mime_body;
1442         body->hbody = hbody;
1443         list_init(&body->children);
1444         body->parent = parent;
1445     }
1446     return body;
1447 }
1448
1449 typedef struct
1450 {
1451     struct list entry;
1452     BODYOFFSETS offsets;
1453 } offset_entry_t;
1454
1455 static HRESULT create_body_offset_list(IStream *stm, const char *boundary, struct list *body_offsets)
1456 {
1457     HRESULT hr;
1458     DWORD read;
1459     int boundary_len = strlen(boundary);
1460     char *buf, *nl_boundary, *ptr, *overlap;
1461     DWORD total_read = 0;
1462     DWORD start = 0, overlap_no;
1463     offset_entry_t *cur_body = NULL;
1464     ULARGE_INTEGER cur;
1465     LARGE_INTEGER zero;
1466
1467     list_init(body_offsets);
1468     nl_boundary = HeapAlloc(GetProcessHeap(), 0, 4 + boundary_len + 1);
1469     memcpy(nl_boundary, "\r\n--", 4);
1470     memcpy(nl_boundary + 4, boundary, boundary_len + 1);
1471
1472     overlap_no = boundary_len + 5;
1473
1474     overlap = buf = HeapAlloc(GetProcessHeap(), 0, overlap_no + PARSER_BUF_SIZE + 1);
1475
1476     zero.QuadPart = 0;
1477     hr = IStream_Seek(stm, zero, STREAM_SEEK_CUR, &cur);
1478     start = cur.LowPart;
1479
1480     do {
1481         hr = IStream_Read(stm, overlap, PARSER_BUF_SIZE, &read);
1482         if(FAILED(hr)) goto end;
1483         if(read == 0) break;
1484         total_read += read;
1485         overlap[read] = '\0';
1486
1487         ptr = buf;
1488         do {
1489             ptr = strstr(ptr, nl_boundary);
1490             if(ptr)
1491             {
1492                 DWORD boundary_start = start + ptr - buf;
1493                 char *end = ptr + boundary_len + 4;
1494
1495                 if(*end == '\0' || *(end + 1) == '\0')
1496                     break;
1497
1498                 if(*end == '\r' && *(end + 1) == '\n')
1499                 {
1500                     if(cur_body)
1501                     {
1502                         cur_body->offsets.cbBodyEnd = boundary_start;
1503                         list_add_tail(body_offsets, &cur_body->entry);
1504                     }
1505                     cur_body = HeapAlloc(GetProcessHeap(), 0, sizeof(*cur_body));
1506                     cur_body->offsets.cbBoundaryStart = boundary_start + 2; /* doesn't including the leading \r\n */
1507                     cur_body->offsets.cbHeaderStart = boundary_start + boundary_len + 6;
1508                 }
1509                 else if(*end == '-' && *(end + 1) == '-')
1510                 {
1511                     if(cur_body)
1512                     {
1513                         cur_body->offsets.cbBodyEnd = boundary_start;
1514                         list_add_tail(body_offsets, &cur_body->entry);
1515                         goto end;
1516                     }
1517                 }
1518                 ptr = end + 2;
1519             }
1520         } while(ptr);
1521
1522         if(overlap == buf) /* 1st iteration */
1523         {
1524             memcpy(buf, buf + PARSER_BUF_SIZE - overlap_no, overlap_no);
1525             overlap = buf + overlap_no;
1526             start += read - overlap_no;
1527         }
1528         else
1529         {
1530             memcpy(buf, buf + PARSER_BUF_SIZE, overlap_no);
1531             start += read;
1532         }
1533     } while(1);
1534
1535 end:
1536     HeapFree(GetProcessHeap(), 0, buf);
1537     return hr;
1538 }
1539
1540 static body_t *create_sub_body(MimeMessage *msg, IStream *pStm, BODYOFFSETS *offset, body_t *parent)
1541 {
1542     IMimeBody *mime_body;
1543     HRESULT hr;
1544     body_t *body;
1545     ULARGE_INTEGER cur;
1546     LARGE_INTEGER zero;
1547
1548     MimeBody_create(NULL, (void**)&mime_body);
1549     IMimeBody_Load(mime_body, pStm);
1550     zero.QuadPart = 0;
1551     hr = IStream_Seek(pStm, zero, STREAM_SEEK_CUR, &cur);
1552     offset->cbBodyStart = cur.LowPart + offset->cbHeaderStart;
1553     if(parent) MimeBody_set_offsets(impl_from_IMimeBody(mime_body), offset);
1554     IMimeBody_SetData(mime_body, IET_BINARY, NULL, NULL, &IID_IStream, pStm);
1555     body = new_body_entry(mime_body, msg->next_hbody, parent);
1556     msg->next_hbody = (HBODY)((DWORD)msg->next_hbody + 1);
1557
1558     if(IMimeBody_IsContentType(mime_body, "multipart", NULL) == S_OK)
1559     {
1560         MIMEPARAMINFO *param_info;
1561         ULONG count, i;
1562         IMimeAllocator *alloc;
1563
1564         hr = IMimeBody_GetParameters(mime_body, "Content-Type", &count, &param_info);
1565         if(hr != S_OK || count == 0) return body;
1566
1567         MimeOleGetAllocator(&alloc);
1568
1569         for(i = 0; i < count; i++)
1570         {
1571             if(!strcasecmp(param_info[i].pszName, "boundary"))
1572             {
1573                 struct list offset_list;
1574                 offset_entry_t *cur, *cursor2;
1575                 hr = create_body_offset_list(pStm, param_info[i].pszData, &offset_list);
1576                 LIST_FOR_EACH_ENTRY_SAFE(cur, cursor2, &offset_list, offset_entry_t, entry)
1577                 {
1578                     body_t *sub_body;
1579                     IStream *sub_stream;
1580                     ULARGE_INTEGER start, length;
1581
1582                     start.LowPart = cur->offsets.cbHeaderStart;
1583                     length.LowPart = cur->offsets.cbBodyEnd - cur->offsets.cbHeaderStart;
1584                     create_sub_stream(pStm, start, length, &sub_stream);
1585                     sub_body = create_sub_body(msg, sub_stream, &cur->offsets, body);
1586                     IStream_Release(sub_stream);
1587                     list_add_tail(&body->children, &sub_body->entry);
1588                     list_remove(&cur->entry);
1589                     HeapFree(GetProcessHeap(), 0, cur);
1590                 }
1591                 break;
1592             }
1593         }
1594         IMimeAllocator_FreeParamInfoArray(alloc, count, param_info, TRUE);
1595         IMimeAllocator_Release(alloc);
1596     }
1597     return body;
1598 }
1599
1600 static HRESULT WINAPI MimeMessage_Load(
1601     IMimeMessage *iface,
1602     LPSTREAM pStm)
1603 {
1604     MimeMessage *This = (MimeMessage *)iface;
1605     body_t *root_body;
1606     BODYOFFSETS offsets;
1607     ULARGE_INTEGER cur;
1608     LARGE_INTEGER zero;
1609
1610     TRACE("(%p)->(%p)\n", iface, pStm);
1611
1612     if(This->stream)
1613     {
1614         FIXME("already loaded a message\n");
1615         return E_FAIL;
1616     }
1617
1618     IStream_AddRef(pStm);
1619     This->stream = pStm;
1620     offsets.cbBoundaryStart = offsets.cbHeaderStart = 0;
1621     offsets.cbBodyStart = offsets.cbBodyEnd = 0;
1622
1623     root_body = create_sub_body(This, pStm, &offsets, NULL);
1624
1625     zero.QuadPart = 0;
1626     IStream_Seek(pStm, zero, STREAM_SEEK_END, &cur);
1627     offsets.cbBodyEnd = cur.LowPart;
1628     MimeBody_set_offsets(impl_from_IMimeBody(root_body->mime_body), &offsets);
1629
1630     list_add_head(&This->body_tree, &root_body->entry);
1631
1632     return S_OK;
1633 }
1634
1635 static HRESULT WINAPI MimeMessage_Save(
1636     IMimeMessage *iface,
1637     LPSTREAM pStm,
1638     BOOL fClearDirty)
1639 {
1640     FIXME("(%p)->(%p, %s)\n", iface, pStm, fClearDirty ? "TRUE" : "FALSE");
1641     return E_NOTIMPL;
1642 }
1643
1644 static HRESULT WINAPI MimeMessage_GetSizeMax(
1645     IMimeMessage *iface,
1646     ULARGE_INTEGER *pcbSize)
1647 {
1648     FIXME("(%p)->(%p)\n", iface, pcbSize);
1649     return E_NOTIMPL;
1650 }
1651
1652 static HRESULT WINAPI MimeMessage_InitNew(
1653     IMimeMessage *iface)
1654 {
1655     FIXME("(%p)->()\n", iface);
1656     return E_NOTIMPL;
1657 }
1658
1659 /*** IMimeMessageTree methods ***/
1660 static HRESULT WINAPI MimeMessage_GetMessageSource(
1661     IMimeMessage *iface,
1662     IStream **ppStream,
1663     DWORD dwFlags)
1664 {
1665     FIXME("(%p)->(%p, 0x%x)\n", iface, ppStream, dwFlags);
1666     return E_NOTIMPL;
1667 }
1668
1669 static HRESULT WINAPI MimeMessage_GetMessageSize(
1670     IMimeMessage *iface,
1671     ULONG *pcbSize,
1672     DWORD dwFlags)
1673 {
1674     FIXME("(%p)->(%p, 0x%x)\n", iface, pcbSize, dwFlags);
1675     return E_NOTIMPL;
1676 }
1677
1678 static HRESULT WINAPI MimeMessage_LoadOffsetTable(
1679     IMimeMessage *iface,
1680     IStream *pStream)
1681 {
1682     FIXME("(%p)->(%p)\n", iface, pStream);
1683     return E_NOTIMPL;
1684 }
1685
1686 static HRESULT WINAPI MimeMessage_SaveOffsetTable(
1687     IMimeMessage *iface,
1688     IStream *pStream,
1689     DWORD dwFlags)
1690 {
1691     FIXME("(%p)->(%p, 0x%x)\n", iface, pStream, dwFlags);
1692     return E_NOTIMPL;
1693 }
1694
1695
1696 static HRESULT WINAPI MimeMessage_GetFlags(
1697     IMimeMessage *iface,
1698     DWORD *pdwFlags)
1699 {
1700     FIXME("(%p)->(%p)\n", iface, pdwFlags);
1701     return E_NOTIMPL;
1702 }
1703
1704 static HRESULT WINAPI MimeMessage_Commit(
1705     IMimeMessage *iface,
1706     DWORD dwFlags)
1707 {
1708     FIXME("(%p)->(0x%x)\n", iface, dwFlags);
1709     return E_NOTIMPL;
1710 }
1711
1712
1713 static HRESULT WINAPI MimeMessage_HandsOffStorage(
1714     IMimeMessage *iface)
1715 {
1716     FIXME("(%p)->()\n", iface);
1717     return E_NOTIMPL;
1718 }
1719
1720 static HRESULT find_body(struct list *list, HBODY hbody, body_t **body)
1721 {
1722     body_t *cur;
1723     HRESULT hr;
1724
1725     if(hbody == HBODY_ROOT)
1726     {
1727         *body = LIST_ENTRY(list_head(list), body_t, entry);
1728         return S_OK;
1729     }
1730
1731     LIST_FOR_EACH_ENTRY(cur, list, body_t, entry)
1732     {
1733         if(cur->hbody == hbody)
1734         {
1735             *body = cur;
1736             return S_OK;
1737         }
1738         hr = find_body(&cur->children, hbody, body);
1739         if(hr == S_OK) return S_OK;
1740     }
1741     return S_FALSE;
1742 }
1743
1744 static HRESULT WINAPI MimeMessage_BindToObject(
1745     IMimeMessage *iface,
1746     const HBODY hBody,
1747     REFIID riid,
1748     void **ppvObject)
1749 {
1750     MimeMessage *This = (MimeMessage *)iface;
1751     HRESULT hr;
1752     body_t *body;
1753
1754     TRACE("(%p)->(%p, %s, %p)\n", iface, hBody, debugstr_guid(riid), ppvObject);
1755
1756     hr = find_body(&This->body_tree, hBody, &body);
1757
1758     if(hr != S_OK) return hr;
1759
1760     if(IsEqualIID(riid, &IID_IMimeBody))
1761     {
1762         IMimeBody_AddRef(body->mime_body);
1763         *ppvObject = body->mime_body;
1764         return S_OK;
1765     }
1766
1767     return E_NOINTERFACE;
1768 }
1769
1770 static HRESULT WINAPI MimeMessage_SaveBody(
1771     IMimeMessage *iface,
1772     HBODY hBody,
1773     DWORD dwFlags,
1774     IStream *pStream)
1775 {
1776     FIXME("(%p)->(%p, 0x%x, %p)\n", iface, hBody, dwFlags, pStream);
1777     return E_NOTIMPL;
1778 }
1779
1780 static HRESULT get_body(MimeMessage *msg, BODYLOCATION location, HBODY pivot, body_t **out)
1781 {
1782     body_t *root = LIST_ENTRY(list_head(&msg->body_tree), body_t, entry);
1783     body_t *body;
1784     HRESULT hr;
1785     struct list *list;
1786
1787     if(location == IBL_ROOT)
1788     {
1789         *out = root;
1790         return S_OK;
1791     }
1792
1793     hr = find_body(&msg->body_tree, pivot, &body);
1794
1795     if(hr == S_OK)
1796     {
1797         switch(location)
1798         {
1799         case IBL_PARENT:
1800             *out = body->parent;
1801             break;
1802
1803         case IBL_FIRST:
1804             list = list_head(&body->children);
1805             if(list)
1806                 *out = LIST_ENTRY(list, body_t, entry);
1807             else
1808                 hr = MIME_E_NOT_FOUND;
1809             break;
1810
1811         case IBL_LAST:
1812             list = list_tail(&body->children);
1813             if(list)
1814                 *out = LIST_ENTRY(list, body_t, entry);
1815             else
1816                 hr = MIME_E_NOT_FOUND;
1817             break;
1818
1819         case IBL_NEXT:
1820             list = list_next(&body->parent->children, &body->entry);
1821             if(list)
1822                 *out = LIST_ENTRY(list, body_t, entry);
1823             else
1824                 hr = MIME_E_NOT_FOUND;
1825             break;
1826
1827         case IBL_PREVIOUS:
1828             list = list_prev(&body->parent->children, &body->entry);
1829             if(list)
1830                 *out = LIST_ENTRY(list, body_t, entry);
1831             else
1832                 hr = MIME_E_NOT_FOUND;
1833             break;
1834
1835         default:
1836             hr = E_FAIL;
1837             break;
1838         }
1839     }
1840
1841     return hr;
1842 }
1843
1844
1845 static HRESULT WINAPI MimeMessage_InsertBody(
1846     IMimeMessage *iface,
1847     BODYLOCATION location,
1848     HBODY hPivot,
1849     LPHBODY phBody)
1850 {
1851     FIXME("(%p)->(%d, %p, %p)\n", iface, location, hPivot, phBody);
1852     return E_NOTIMPL;
1853 }
1854
1855 static HRESULT WINAPI MimeMessage_GetBody(
1856     IMimeMessage *iface,
1857     BODYLOCATION location,
1858     HBODY hPivot,
1859     LPHBODY phBody)
1860 {
1861     MimeMessage *This = (MimeMessage *)iface;
1862     body_t *body;
1863     HRESULT hr;
1864
1865     TRACE("(%p)->(%d, %p, %p)\n", iface, location, hPivot, phBody);
1866
1867     hr = get_body(This, location, hPivot, &body);
1868
1869     if(hr == S_OK) *phBody = body->hbody;
1870
1871     return hr;
1872 }
1873
1874 static HRESULT WINAPI MimeMessage_DeleteBody(
1875     IMimeMessage *iface,
1876     HBODY hBody,
1877     DWORD dwFlags)
1878 {
1879     FIXME("(%p)->(%p, %08x)\n", iface, hBody, dwFlags);
1880     return E_NOTIMPL;
1881 }
1882
1883 static HRESULT WINAPI MimeMessage_MoveBody(
1884     IMimeMessage *iface,
1885     HBODY hBody,
1886     BODYLOCATION location)
1887 {
1888     FIXME("(%p)->(%d)\n", iface, location);
1889     return E_NOTIMPL;
1890 }
1891
1892 static void count_children(body_t *body, boolean recurse, ULONG *count)
1893 {
1894     body_t *child;
1895
1896     LIST_FOR_EACH_ENTRY(child, &body->children, body_t, entry)
1897     {
1898         (*count)++;
1899         if(recurse) count_children(child, recurse, count);
1900     }
1901 }
1902
1903 static HRESULT WINAPI MimeMessage_CountBodies(
1904     IMimeMessage *iface,
1905     HBODY hParent,
1906     boolean fRecurse,
1907     ULONG *pcBodies)
1908 {
1909     HRESULT hr;
1910     MimeMessage *This = (MimeMessage *)iface;
1911     body_t *body;
1912
1913     TRACE("(%p)->(%p, %s, %p)\n", iface, hParent, fRecurse ? "TRUE" : "FALSE", pcBodies);
1914
1915     hr = find_body(&This->body_tree, hParent, &body);
1916     if(hr != S_OK) return hr;
1917
1918     *pcBodies = 1;
1919     count_children(body, fRecurse, pcBodies);
1920
1921     return S_OK;
1922 }
1923
1924 static HRESULT find_next(IMimeMessage *msg, LPFINDBODY find_body, HBODY *out)
1925 {
1926     HRESULT hr;
1927     IMimeBody *mime_body;
1928     HBODY next;
1929
1930     if(find_body->dwReserved == 0)
1931         find_body->dwReserved = (DWORD)HBODY_ROOT;
1932     else
1933     {
1934         hr = IMimeMessage_GetBody(msg, IBL_FIRST, (HBODY)find_body->dwReserved, &next);
1935         if(hr == S_OK)
1936             find_body->dwReserved = (DWORD)next;
1937         else
1938         {
1939             hr = IMimeMessage_GetBody(msg, IBL_NEXT, (HBODY)find_body->dwReserved, &next);
1940             if(hr == S_OK)
1941                 find_body->dwReserved = (DWORD)next;
1942             else
1943                 return MIME_E_NOT_FOUND;
1944         }
1945     }
1946
1947     hr = IMimeMessage_BindToObject(msg, (HBODY)find_body->dwReserved, &IID_IMimeBody, (void**)&mime_body);
1948     if(IMimeBody_IsContentType(mime_body, find_body->pszPriType, find_body->pszSubType) == S_OK)
1949     {
1950         IMimeBody_Release(mime_body);
1951         *out = (HBODY)find_body->dwReserved;
1952         return S_OK;
1953     }
1954     IMimeBody_Release(mime_body);
1955     return find_next(msg, find_body, out);
1956 }
1957
1958 static HRESULT WINAPI MimeMessage_FindFirst(
1959     IMimeMessage *iface,
1960     LPFINDBODY pFindBody,
1961     LPHBODY phBody)
1962 {
1963     TRACE("(%p)->(%p, %p)\n", iface, pFindBody, phBody);
1964
1965     pFindBody->dwReserved = 0;
1966     return find_next(iface, pFindBody, phBody);
1967 }
1968
1969 static HRESULT WINAPI MimeMessage_FindNext(
1970     IMimeMessage *iface,
1971     LPFINDBODY pFindBody,
1972     LPHBODY phBody)
1973 {
1974     TRACE("(%p)->(%p, %p)\n", iface, pFindBody, phBody);
1975
1976     return find_next(iface, pFindBody, phBody);
1977 }
1978
1979 static HRESULT WINAPI MimeMessage_ResolveURL(
1980     IMimeMessage *iface,
1981     HBODY hRelated,
1982     LPCSTR pszBase,
1983     LPCSTR pszURL,
1984     DWORD dwFlags,
1985     LPHBODY phBody)
1986 {
1987     FIXME("(%p)->(%p, %s, %s, 0x%x, %p)\n", iface, hRelated, pszBase, pszURL, dwFlags, phBody);
1988     return E_NOTIMPL;
1989 }
1990
1991 static HRESULT WINAPI MimeMessage_ToMultipart(
1992     IMimeMessage *iface,
1993     HBODY hBody,
1994     LPCSTR pszSubType,
1995     LPHBODY phMultipart)
1996 {
1997     FIXME("(%p)->(%p, %s, %p)\n", iface, hBody, pszSubType, phMultipart);
1998     return E_NOTIMPL;
1999 }
2000
2001 static HRESULT WINAPI MimeMessage_GetBodyOffsets(
2002     IMimeMessage *iface,
2003     HBODY hBody,
2004     LPBODYOFFSETS pOffsets)
2005 {
2006     FIXME("(%p)->(%p, %p)\n", iface, hBody, pOffsets);
2007     return E_NOTIMPL;
2008 }
2009
2010 static HRESULT WINAPI MimeMessage_GetCharset(
2011     IMimeMessage *iface,
2012     LPHCHARSET phCharset)
2013 {
2014     FIXME("(%p)->(%p)\n", iface, phCharset);
2015     return E_NOTIMPL;
2016 }
2017
2018 static HRESULT WINAPI MimeMessage_SetCharset(
2019     IMimeMessage *iface,
2020     HCHARSET hCharset,
2021     CSETAPPLYTYPE applytype)
2022 {
2023     FIXME("(%p)->(%p, %d)\n", iface, hCharset, applytype);
2024     return E_NOTIMPL;
2025 }
2026
2027 static HRESULT WINAPI MimeMessage_IsBodyType(
2028     IMimeMessage *iface,
2029     HBODY hBody,
2030     IMSGBODYTYPE bodytype)
2031 {
2032     HRESULT hr;
2033     IMimeBody *mime_body;
2034     TRACE("(%p)->(%p, %d)\n", iface, hBody, bodytype);
2035
2036     hr = IMimeMessage_BindToObject(iface, hBody, &IID_IMimeBody, (void**)&mime_body);
2037     if(hr != S_OK) return hr;
2038
2039     hr = IMimeBody_IsType(mime_body, bodytype);
2040     MimeBody_Release(mime_body);
2041     return hr;
2042 }
2043
2044 static HRESULT WINAPI MimeMessage_IsContentType(
2045     IMimeMessage *iface,
2046     HBODY hBody,
2047     LPCSTR pszPriType,
2048     LPCSTR pszSubType)
2049 {
2050     HRESULT hr;
2051     IMimeBody *mime_body;
2052     TRACE("(%p)->(%p, %s, %s)\n", iface, hBody, pszPriType, pszSubType);
2053
2054     hr = IMimeMessage_BindToObject(iface, hBody, &IID_IMimeBody, (void**)&mime_body);
2055     if(FAILED(hr)) return hr;
2056
2057     hr = IMimeBody_IsContentType(mime_body, pszPriType, pszSubType);
2058     IMimeBody_Release(mime_body);
2059     return hr;
2060 }
2061
2062 static HRESULT WINAPI MimeMessage_QueryBodyProp(
2063     IMimeMessage *iface,
2064     HBODY hBody,
2065     LPCSTR pszName,
2066     LPCSTR pszCriteria,
2067     boolean fSubString,
2068     boolean fCaseSensitive)
2069 {
2070     FIXME("(%p)->(%p, %s, %s, %s, %s)\n", iface, hBody, pszName, pszCriteria, fSubString ? "TRUE" : "FALSE", fCaseSensitive ? "TRUE" : "FALSE");
2071     return E_NOTIMPL;
2072 }
2073
2074 static HRESULT WINAPI MimeMessage_GetBodyProp(
2075     IMimeMessage *iface,
2076     HBODY hBody,
2077     LPCSTR pszName,
2078     DWORD dwFlags,
2079     LPPROPVARIANT pValue)
2080 {
2081     HRESULT hr;
2082     IMimeBody *mime_body;
2083
2084     TRACE("(%p)->(%p, %s, 0x%x, %p)\n", iface, hBody, pszName, dwFlags, pValue);
2085
2086     hr = IMimeMessage_BindToObject(iface, hBody, &IID_IMimeBody, (void**)&mime_body);
2087     if(hr != S_OK) return hr;
2088
2089     hr = IMimeBody_GetProp(mime_body, pszName, dwFlags, pValue);
2090     IMimeBody_Release(mime_body);
2091
2092     return hr;
2093 }
2094
2095 static HRESULT WINAPI MimeMessage_SetBodyProp(
2096     IMimeMessage *iface,
2097     HBODY hBody,
2098     LPCSTR pszName,
2099     DWORD dwFlags,
2100     LPCPROPVARIANT pValue)
2101 {
2102     FIXME("(%p)->(%p, %s, 0x%x, %p)\n", iface, hBody, pszName, dwFlags, pValue);
2103     return E_NOTIMPL;
2104 }
2105
2106 static HRESULT WINAPI MimeMessage_DeleteBodyProp(
2107     IMimeMessage *iface,
2108     HBODY hBody,
2109     LPCSTR pszName)
2110 {
2111     FIXME("(%p)->(%p, %s)\n", iface, hBody, pszName);
2112     return E_NOTIMPL;
2113 }
2114
2115 static HRESULT WINAPI MimeMessage_SetOption(
2116     IMimeMessage *iface,
2117     const TYPEDID oid,
2118     LPCPROPVARIANT pValue)
2119 {
2120     FIXME("(%p)->(%d, %p)\n", iface, oid, pValue);
2121     return E_NOTIMPL;
2122 }
2123
2124 static HRESULT WINAPI MimeMessage_GetOption(
2125     IMimeMessage *iface,
2126     const TYPEDID oid,
2127     LPPROPVARIANT pValue)
2128 {
2129     FIXME("(%p)->(%d, %p)\n", iface, oid, pValue);
2130     return E_NOTIMPL;
2131 }
2132
2133 /*** IMimeMessage methods ***/
2134 static HRESULT WINAPI MimeMessage_CreateWebPage(
2135     IMimeMessage *iface,
2136     IStream *pRootStm,
2137     LPWEBPAGEOPTIONS pOptions,
2138     IMimeMessageCallback *pCallback,
2139     IMoniker **ppMoniker)
2140 {
2141     FIXME("(%p)->(%p, %p, %p, %p)\n", iface, pRootStm, pOptions, pCallback, ppMoniker);
2142     *ppMoniker = NULL;
2143     return E_NOTIMPL;
2144 }
2145
2146 static HRESULT WINAPI MimeMessage_GetProp(
2147     IMimeMessage *iface,
2148     LPCSTR pszName,
2149     DWORD dwFlags,
2150     LPPROPVARIANT pValue)
2151 {
2152     FIXME("(%p)->(%s, 0x%x, %p)\n", iface, pszName, dwFlags, pValue);
2153     return E_NOTIMPL;
2154 }
2155
2156 static HRESULT WINAPI MimeMessage_SetProp(
2157     IMimeMessage *iface,
2158     LPCSTR pszName,
2159     DWORD dwFlags,
2160     LPCPROPVARIANT pValue)
2161 {
2162     FIXME("(%p)->(%s, 0x%x, %p)\n", iface, pszName, dwFlags, pValue);
2163     return E_NOTIMPL;
2164 }
2165
2166 static HRESULT WINAPI MimeMessage_DeleteProp(
2167     IMimeMessage *iface,
2168     LPCSTR pszName)
2169 {
2170     FIXME("(%p)->(%s)\n", iface, pszName);
2171     return E_NOTIMPL;
2172 }
2173
2174 static HRESULT WINAPI MimeMessage_QueryProp(
2175     IMimeMessage *iface,
2176     LPCSTR pszName,
2177     LPCSTR pszCriteria,
2178     boolean fSubString,
2179     boolean fCaseSensitive)
2180 {
2181     FIXME("(%p)->(%s, %s, %s, %s)\n", iface, pszName, pszCriteria, fSubString ? "TRUE" : "FALSE", fCaseSensitive ? "TRUE" : "FALSE");
2182     return E_NOTIMPL;
2183 }
2184
2185 static HRESULT WINAPI MimeMessage_GetTextBody(
2186     IMimeMessage *iface,
2187     DWORD dwTxtType,
2188     ENCODINGTYPE ietEncoding,
2189     IStream **pStream,
2190     LPHBODY phBody)
2191 {
2192     HRESULT hr;
2193     HBODY hbody;
2194     FINDBODY find_struct;
2195     IMimeBody *mime_body;
2196     static char text[] = "text";
2197     static char plain[] = "plain";
2198     static char html[] = "html";
2199
2200     TRACE("(%p)->(%d, %d, %p, %p)\n", iface, dwTxtType, ietEncoding, pStream, phBody);
2201
2202     find_struct.pszPriType = text;
2203
2204     switch(dwTxtType)
2205     {
2206     case TXT_PLAIN:
2207         find_struct.pszSubType = plain;
2208         break;
2209     case TXT_HTML:
2210         find_struct.pszSubType = html;
2211         break;
2212     default:
2213         return MIME_E_INVALID_TEXT_TYPE;
2214     }
2215
2216     hr = IMimeMessage_FindFirst(iface, &find_struct, &hbody);
2217     if(hr != S_OK)
2218     {
2219         TRACE("not found hr %08x\n", hr);
2220         *phBody = NULL;
2221         return hr;
2222     }
2223
2224     IMimeMessage_BindToObject(iface, hbody, &IID_IMimeBody, (void**)&mime_body);
2225
2226     IMimeBody_GetData(mime_body, ietEncoding, pStream);
2227     *phBody = hbody;
2228     IMimeBody_Release(mime_body);
2229     return hr;
2230 }
2231
2232 static HRESULT WINAPI MimeMessage_SetTextBody(
2233     IMimeMessage *iface,
2234     DWORD dwTxtType,
2235     ENCODINGTYPE ietEncoding,
2236     HBODY hAlternative,
2237     IStream *pStream,
2238     LPHBODY phBody)
2239 {
2240     FIXME("(%p)->(%d, %d, %p, %p, %p)\n", iface, dwTxtType, ietEncoding, hAlternative, pStream, phBody);
2241     return E_NOTIMPL;
2242 }
2243
2244 static HRESULT WINAPI MimeMessage_AttachObject(
2245     IMimeMessage *iface,
2246     REFIID riid,
2247     void *pvObject,
2248     LPHBODY phBody)
2249 {
2250     FIXME("(%p)->(%s, %p, %p)\n", iface, debugstr_guid(riid), pvObject, phBody);
2251     return E_NOTIMPL;
2252 }
2253
2254 static HRESULT WINAPI MimeMessage_AttachFile(
2255     IMimeMessage *iface,
2256     LPCSTR pszFilePath,
2257     IStream *pstmFile,
2258     LPHBODY phBody)
2259 {
2260     FIXME("(%p)->(%s, %p, %p)\n", iface, pszFilePath, pstmFile, phBody);
2261     return E_NOTIMPL;
2262 }
2263
2264 static HRESULT WINAPI MimeMessage_AttachURL(
2265     IMimeMessage *iface,
2266     LPCSTR pszBase,
2267     LPCSTR pszURL,
2268     DWORD dwFlags,
2269     IStream *pstmURL,
2270     LPSTR *ppszCIDURL,
2271     LPHBODY phBody)
2272 {
2273     FIXME("(%p)->(%s, %s, 0x%x, %p, %p, %p)\n", iface, pszBase, pszURL, dwFlags, pstmURL, ppszCIDURL, phBody);
2274     return E_NOTIMPL;
2275 }
2276
2277 static HRESULT WINAPI MimeMessage_GetAttachments(
2278     IMimeMessage *iface,
2279     ULONG *pcAttach,
2280     LPHBODY *pprghAttach)
2281 {
2282     FIXME("(%p)->(%p, %p)\n", iface, pcAttach, pprghAttach);
2283     return E_NOTIMPL;
2284 }
2285
2286 static HRESULT WINAPI MimeMessage_GetAddressTable(
2287     IMimeMessage *iface,
2288     IMimeAddressTable **ppTable)
2289 {
2290     FIXME("(%p)->(%p)\n", iface, ppTable);
2291     return E_NOTIMPL;
2292 }
2293
2294 static HRESULT WINAPI MimeMessage_GetSender(
2295     IMimeMessage *iface,
2296     LPADDRESSPROPS pAddress)
2297 {
2298     FIXME("(%p)->(%p)\n", iface, pAddress);
2299     return E_NOTIMPL;
2300 }
2301
2302 static HRESULT WINAPI MimeMessage_GetAddressTypes(
2303     IMimeMessage *iface,
2304     DWORD dwAdrTypes,
2305     DWORD dwProps,
2306     LPADDRESSLIST pList)
2307 {
2308     FIXME("(%p)->(%d, %d, %p)\n", iface, dwAdrTypes, dwProps, pList);
2309     return E_NOTIMPL;
2310 }
2311
2312 static HRESULT WINAPI MimeMessage_GetAddressFormat(
2313     IMimeMessage *iface,
2314     DWORD dwAdrTypes,
2315     ADDRESSFORMAT format,
2316     LPSTR *ppszFormat)
2317 {
2318     FIXME("(%p)->(%d, %d, %p)\n", iface, dwAdrTypes, format, ppszFormat);
2319     return E_NOTIMPL;
2320 }
2321
2322 static HRESULT WINAPI MimeMessage_EnumAddressTypes(
2323     IMimeMessage *iface,
2324     DWORD dwAdrTypes,
2325     DWORD dwProps,
2326     IMimeEnumAddressTypes **ppEnum)
2327 {
2328     FIXME("(%p)->(%d, %d, %p)\n", iface, dwAdrTypes, dwProps, ppEnum);
2329     return E_NOTIMPL;
2330 }
2331
2332 static HRESULT WINAPI MimeMessage_SplitMessage(
2333     IMimeMessage *iface,
2334     ULONG cbMaxPart,
2335     IMimeMessageParts **ppParts)
2336 {
2337     FIXME("(%p)->(%d, %p)\n", iface, cbMaxPart, ppParts);
2338     return E_NOTIMPL;
2339 }
2340
2341 static HRESULT WINAPI MimeMessage_GetRootMoniker(
2342     IMimeMessage *iface,
2343     IMoniker **ppMoniker)
2344 {
2345     FIXME("(%p)->(%p)\n", iface, ppMoniker);
2346     return E_NOTIMPL;
2347 }
2348
2349 static const IMimeMessageVtbl MimeMessageVtbl =
2350 {
2351     MimeMessage_QueryInterface,
2352     MimeMessage_AddRef,
2353     MimeMessage_Release,
2354     MimeMessage_GetClassID,
2355     MimeMessage_IsDirty,
2356     MimeMessage_Load,
2357     MimeMessage_Save,
2358     MimeMessage_GetSizeMax,
2359     MimeMessage_InitNew,
2360     MimeMessage_GetMessageSource,
2361     MimeMessage_GetMessageSize,
2362     MimeMessage_LoadOffsetTable,
2363     MimeMessage_SaveOffsetTable,
2364     MimeMessage_GetFlags,
2365     MimeMessage_Commit,
2366     MimeMessage_HandsOffStorage,
2367     MimeMessage_BindToObject,
2368     MimeMessage_SaveBody,
2369     MimeMessage_InsertBody,
2370     MimeMessage_GetBody,
2371     MimeMessage_DeleteBody,
2372     MimeMessage_MoveBody,
2373     MimeMessage_CountBodies,
2374     MimeMessage_FindFirst,
2375     MimeMessage_FindNext,
2376     MimeMessage_ResolveURL,
2377     MimeMessage_ToMultipart,
2378     MimeMessage_GetBodyOffsets,
2379     MimeMessage_GetCharset,
2380     MimeMessage_SetCharset,
2381     MimeMessage_IsBodyType,
2382     MimeMessage_IsContentType,
2383     MimeMessage_QueryBodyProp,
2384     MimeMessage_GetBodyProp,
2385     MimeMessage_SetBodyProp,
2386     MimeMessage_DeleteBodyProp,
2387     MimeMessage_SetOption,
2388     MimeMessage_GetOption,
2389     MimeMessage_CreateWebPage,
2390     MimeMessage_GetProp,
2391     MimeMessage_SetProp,
2392     MimeMessage_DeleteProp,
2393     MimeMessage_QueryProp,
2394     MimeMessage_GetTextBody,
2395     MimeMessage_SetTextBody,
2396     MimeMessage_AttachObject,
2397     MimeMessage_AttachFile,
2398     MimeMessage_AttachURL,
2399     MimeMessage_GetAttachments,
2400     MimeMessage_GetAddressTable,
2401     MimeMessage_GetSender,
2402     MimeMessage_GetAddressTypes,
2403     MimeMessage_GetAddressFormat,
2404     MimeMessage_EnumAddressTypes,
2405     MimeMessage_SplitMessage,
2406     MimeMessage_GetRootMoniker,
2407 };
2408
2409 /***********************************************************************
2410  *              MimeOleCreateMessage (INETCOMM.@)
2411  */
2412 HRESULT WINAPI MimeOleCreateMessage(IUnknown *pUnkOuter, IMimeMessage **ppMessage)
2413 {
2414     MimeMessage *This;
2415
2416     TRACE("(%p, %p)\n", pUnkOuter, ppMessage);
2417
2418     if (pUnkOuter)
2419     {
2420         FIXME("outer unknown not supported yet\n");
2421         return E_NOTIMPL;
2422     }
2423
2424     *ppMessage = NULL;
2425
2426     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
2427     if (!This) return E_OUTOFMEMORY;
2428
2429     This->lpVtbl = &MimeMessageVtbl;
2430     This->refs = 1;
2431     This->stream = NULL;
2432     list_init(&This->body_tree);
2433     This->next_hbody = (HBODY)1;
2434
2435     *ppMessage = (IMimeMessage *)&This->lpVtbl;
2436     return S_OK;
2437 }
2438
2439 /***********************************************************************
2440  *              MimeOleSetCompatMode (INETCOMM.@)
2441  */
2442 HRESULT WINAPI MimeOleSetCompatMode(DWORD dwMode)
2443 {
2444     FIXME("(0x%x)\n", dwMode);
2445     return S_OK;
2446 }
2447
2448 /***********************************************************************
2449  *              MimeOleCreateVirtualStream (INETCOMM.@)
2450  */
2451 HRESULT WINAPI MimeOleCreateVirtualStream(IStream **ppStream)
2452 {
2453     HRESULT hr;
2454     FIXME("(%p)\n", ppStream);
2455
2456     hr = CreateStreamOnHGlobal(NULL, TRUE, ppStream);
2457     return hr;
2458 }
2459
2460 typedef struct MimeSecurity
2461 {
2462     const IMimeSecurityVtbl *lpVtbl;
2463
2464     LONG refs;
2465 } MimeSecurity;
2466
2467 static HRESULT WINAPI MimeSecurity_QueryInterface(
2468         IMimeSecurity* iface,
2469         REFIID riid,
2470         void** obj)
2471 {
2472     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), obj);
2473
2474     if (IsEqualIID(riid, &IID_IUnknown) ||
2475         IsEqualIID(riid, &IID_IMimeSecurity))
2476     {
2477         *obj = iface;
2478         IUnknown_AddRef(iface);
2479         return S_OK;
2480     }
2481
2482     FIXME("no interface for %s\n", debugstr_guid(riid));
2483     *obj = NULL;
2484     return E_NOINTERFACE;
2485 }
2486
2487 static ULONG WINAPI MimeSecurity_AddRef(
2488         IMimeSecurity* iface)
2489 {
2490     MimeSecurity *This = (MimeSecurity *)iface;
2491     TRACE("(%p)->()\n", iface);
2492     return InterlockedIncrement(&This->refs);
2493 }
2494
2495 static ULONG WINAPI MimeSecurity_Release(
2496         IMimeSecurity* iface)
2497 {
2498     MimeSecurity *This = (MimeSecurity *)iface;
2499     ULONG refs;
2500
2501     TRACE("(%p)->()\n", iface);
2502
2503     refs = InterlockedDecrement(&This->refs);
2504     if (!refs)
2505     {
2506         HeapFree(GetProcessHeap(), 0, This);
2507     }
2508
2509     return refs;
2510 }
2511
2512 static HRESULT WINAPI MimeSecurity_InitNew(
2513         IMimeSecurity* iface)
2514 {
2515     FIXME("(%p)->(): stub\n", iface);
2516     return S_OK;
2517 }
2518
2519 static HRESULT WINAPI MimeSecurity_CheckInit(
2520         IMimeSecurity* iface)
2521 {
2522     FIXME("(%p)->(): stub\n", iface);
2523     return E_NOTIMPL;
2524 }
2525
2526 static HRESULT WINAPI MimeSecurity_EncodeMessage(
2527         IMimeSecurity* iface,
2528         IMimeMessageTree* pTree,
2529         DWORD dwFlags)
2530 {
2531     FIXME("(%p)->(%p, %08x): stub\n", iface, pTree, dwFlags);
2532     return E_NOTIMPL;
2533 }
2534
2535 static HRESULT WINAPI MimeSecurity_EncodeBody(
2536         IMimeSecurity* iface,
2537         IMimeMessageTree* pTree,
2538         HBODY hEncodeRoot,
2539         DWORD dwFlags)
2540 {
2541     FIXME("(%p)->(%p, %p, %08x): stub\n", iface, pTree, hEncodeRoot, dwFlags);
2542     return E_NOTIMPL;
2543 }
2544
2545 static HRESULT WINAPI MimeSecurity_DecodeMessage(
2546         IMimeSecurity* iface,
2547         IMimeMessageTree* pTree,
2548         DWORD dwFlags)
2549 {
2550     FIXME("(%p)->(%p, %08x): stub\n", iface, pTree, dwFlags);
2551     return E_NOTIMPL;
2552 }
2553
2554 static HRESULT WINAPI MimeSecurity_DecodeBody(
2555         IMimeSecurity* iface,
2556         IMimeMessageTree* pTree,
2557         HBODY hDecodeRoot,
2558         DWORD dwFlags)
2559 {
2560     FIXME("(%p)->(%p, %p, %08x): stub\n", iface, pTree, hDecodeRoot, dwFlags);
2561     return E_NOTIMPL;
2562 }
2563
2564 static HRESULT WINAPI MimeSecurity_EnumCertificates(
2565         IMimeSecurity* iface,
2566         HCAPICERTSTORE hc,
2567         DWORD dwUsage,
2568         PCX509CERT pPrev,
2569         PCX509CERT* ppCert)
2570 {
2571     FIXME("(%p)->(%p, %08x, %p, %p): stub\n", iface, hc, dwUsage, pPrev, ppCert);
2572     return E_NOTIMPL;
2573 }
2574
2575 static HRESULT WINAPI MimeSecurity_GetCertificateName(
2576         IMimeSecurity* iface,
2577         const PCX509CERT pX509Cert,
2578         const CERTNAMETYPE cn,
2579         LPSTR* ppszName)
2580 {
2581     FIXME("(%p)->(%p, %08x, %p): stub\n", iface, pX509Cert, cn, ppszName);
2582     return E_NOTIMPL;
2583 }
2584
2585 static HRESULT WINAPI MimeSecurity_GetMessageType(
2586         IMimeSecurity* iface,
2587         const HWND hwndParent,
2588         IMimeBody* pBody,
2589         DWORD* pdwSecType)
2590 {
2591     FIXME("(%p)->(%p, %p, %p): stub\n", iface, hwndParent, pBody, pdwSecType);
2592     return E_NOTIMPL;
2593 }
2594
2595 static HRESULT WINAPI MimeSecurity_GetCertData(
2596         IMimeSecurity* iface,
2597         const PCX509CERT pX509Cert,
2598         const CERTDATAID dataid,
2599         LPPROPVARIANT pValue)
2600 {
2601     FIXME("(%p)->(%p, %x, %p): stub\n", iface, pX509Cert, dataid, pValue);
2602     return E_NOTIMPL;
2603 }
2604
2605
2606 static const IMimeSecurityVtbl MimeSecurityVtbl =
2607 {
2608     MimeSecurity_QueryInterface,
2609     MimeSecurity_AddRef,
2610     MimeSecurity_Release,
2611     MimeSecurity_InitNew,
2612     MimeSecurity_CheckInit,
2613     MimeSecurity_EncodeMessage,
2614     MimeSecurity_EncodeBody,
2615     MimeSecurity_DecodeMessage,
2616     MimeSecurity_DecodeBody,
2617     MimeSecurity_EnumCertificates,
2618     MimeSecurity_GetCertificateName,
2619     MimeSecurity_GetMessageType,
2620     MimeSecurity_GetCertData
2621 };
2622
2623 /***********************************************************************
2624  *              MimeOleCreateSecurity (INETCOMM.@)
2625  */
2626 HRESULT WINAPI MimeOleCreateSecurity(IMimeSecurity **ppSecurity)
2627 {
2628     MimeSecurity *This;
2629
2630     TRACE("(%p)\n", ppSecurity);
2631
2632     *ppSecurity = NULL;
2633
2634     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
2635     if (!This) return E_OUTOFMEMORY;
2636
2637     This->lpVtbl = &MimeSecurityVtbl;
2638     This->refs = 1;
2639
2640     *ppSecurity = (IMimeSecurity *)&This->lpVtbl;
2641     return S_OK;
2642 }
2643
2644
2645 typedef struct
2646 {
2647     IMimeAllocatorVtbl *lpVtbl;
2648 } MimeAllocator;
2649
2650 static HRESULT WINAPI MimeAlloc_QueryInterface(
2651         IMimeAllocator* iface,
2652         REFIID riid,
2653         void **obj)
2654 {
2655     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), obj);
2656
2657     if (IsEqualIID(riid, &IID_IUnknown) ||
2658         IsEqualIID(riid, &IID_IMalloc) ||
2659         IsEqualIID(riid, &IID_IMimeAllocator))
2660     {
2661         *obj = iface;
2662         IUnknown_AddRef(iface);
2663         return S_OK;
2664     }
2665
2666     FIXME("no interface for %s\n", debugstr_guid(riid));
2667     *obj = NULL;
2668     return E_NOINTERFACE;
2669 }
2670
2671 static ULONG WINAPI MimeAlloc_AddRef(
2672         IMimeAllocator* iface)
2673 {
2674     return 2;
2675 }
2676
2677 static ULONG WINAPI MimeAlloc_Release(
2678         IMimeAllocator* iface)
2679 {
2680     return 1;
2681 }
2682
2683 static LPVOID WINAPI MimeAlloc_Alloc(
2684         IMimeAllocator* iface,
2685         ULONG cb)
2686 {
2687     return CoTaskMemAlloc(cb);
2688 }
2689
2690 static LPVOID WINAPI MimeAlloc_Realloc(
2691         IMimeAllocator* iface,
2692         LPVOID pv,
2693         ULONG cb)
2694 {
2695     return CoTaskMemRealloc(pv, cb);
2696 }
2697
2698 static void WINAPI MimeAlloc_Free(
2699         IMimeAllocator* iface,
2700         LPVOID pv)
2701 {
2702     return CoTaskMemFree(pv);
2703 }
2704
2705 static ULONG WINAPI MimeAlloc_GetSize(
2706         IMimeAllocator* iface,
2707         LPVOID pv)
2708 {
2709     FIXME("stub\n");
2710     return 0;
2711 }
2712
2713 static int WINAPI MimeAlloc_DidAlloc(
2714         IMimeAllocator* iface,
2715         LPVOID pv)
2716 {
2717     FIXME("stub\n");
2718     return 0;
2719 }
2720
2721 static void WINAPI MimeAlloc_HeapMinimize(
2722         IMimeAllocator* iface)
2723 {
2724     FIXME("stub\n");
2725     return;
2726 }
2727
2728 static HRESULT WINAPI MimeAlloc_FreeParamInfoArray(
2729         IMimeAllocator* iface,
2730         ULONG cParams,
2731         LPMIMEPARAMINFO prgParam,
2732         boolean fFreeArray)
2733 {
2734     ULONG i;
2735     TRACE("(%p)->(%d, %p, %d)\n", iface, cParams, prgParam, fFreeArray);
2736
2737     for(i = 0; i < cParams; i++)
2738     {
2739         IMimeAllocator_Free(iface, prgParam[i].pszName);
2740         IMimeAllocator_Free(iface, prgParam[i].pszData);
2741     }
2742     if(fFreeArray) IMimeAllocator_Free(iface, prgParam);
2743     return S_OK;
2744 }
2745
2746 static HRESULT WINAPI MimeAlloc_FreeAddressList(
2747         IMimeAllocator* iface,
2748         LPADDRESSLIST pList)
2749 {
2750     FIXME("stub\n");
2751     return E_NOTIMPL;
2752 }
2753
2754 static HRESULT WINAPI MimeAlloc_FreeAddressProps(
2755         IMimeAllocator* iface,
2756         LPADDRESSPROPS pAddress)
2757 {
2758     FIXME("stub\n");
2759     return E_NOTIMPL;
2760 }
2761
2762 static HRESULT WINAPI MimeAlloc_ReleaseObjects(
2763         IMimeAllocator* iface,
2764         ULONG cObjects,
2765         IUnknown **prgpUnknown,
2766         boolean fFreeArray)
2767 {
2768     FIXME("stub\n");
2769     return E_NOTIMPL;
2770 }
2771
2772
2773 static HRESULT WINAPI MimeAlloc_FreeEnumHeaderRowArray(
2774         IMimeAllocator* iface,
2775         ULONG cRows,
2776         LPENUMHEADERROW prgRow,
2777         boolean fFreeArray)
2778 {
2779     FIXME("stub\n");
2780     return E_NOTIMPL;
2781 }
2782
2783 static HRESULT WINAPI MimeAlloc_FreeEnumPropertyArray(
2784         IMimeAllocator* iface,
2785         ULONG cProps,
2786         LPENUMPROPERTY prgProp,
2787         boolean fFreeArray)
2788 {
2789     FIXME("stub\n");
2790     return E_NOTIMPL;
2791 }
2792
2793 static HRESULT WINAPI MimeAlloc_FreeThumbprint(
2794         IMimeAllocator* iface,
2795         THUMBBLOB *pthumbprint)
2796 {
2797     FIXME("stub\n");
2798     return E_NOTIMPL;
2799 }
2800
2801
2802 static HRESULT WINAPI MimeAlloc_PropVariantClear(
2803         IMimeAllocator* iface,
2804         LPPROPVARIANT pProp)
2805 {
2806     FIXME("stub\n");
2807     return E_NOTIMPL;
2808 }
2809
2810 static IMimeAllocatorVtbl mime_alloc_vtbl =
2811 {
2812     MimeAlloc_QueryInterface,
2813     MimeAlloc_AddRef,
2814     MimeAlloc_Release,
2815     MimeAlloc_Alloc,
2816     MimeAlloc_Realloc,
2817     MimeAlloc_Free,
2818     MimeAlloc_GetSize,
2819     MimeAlloc_DidAlloc,
2820     MimeAlloc_HeapMinimize,
2821     MimeAlloc_FreeParamInfoArray,
2822     MimeAlloc_FreeAddressList,
2823     MimeAlloc_FreeAddressProps,
2824     MimeAlloc_ReleaseObjects,
2825     MimeAlloc_FreeEnumHeaderRowArray,
2826     MimeAlloc_FreeEnumPropertyArray,
2827     MimeAlloc_FreeThumbprint,
2828     MimeAlloc_PropVariantClear
2829 };
2830
2831 static MimeAllocator mime_allocator =
2832 {
2833     &mime_alloc_vtbl
2834 };
2835
2836 HRESULT MimeAllocator_create(IUnknown *outer, void **obj)
2837 {
2838     if(outer) return CLASS_E_NOAGGREGATION;
2839
2840     *obj = &mime_allocator;
2841     return S_OK;
2842 }
2843
2844 HRESULT WINAPI MimeOleGetAllocator(IMimeAllocator **alloc)
2845 {
2846     return MimeAllocator_create(NULL, (void**)alloc);
2847 }