inetcomm: Implement IMimeInternational_ConvertBuffer.
[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     MimeBody *This = impl_from_IMimeBody(iface);
631     TRACE("(%p)->(%s, %d, %p)\n", This, pszName, dwFlags, pValue);
632
633     if(!strcasecmp(pszName, "att:pri-content-type"))
634     {
635         PropVariantClear(pValue);
636         pValue->vt = VT_LPSTR;
637         pValue->u.pszVal = strdupA(This->content_pri_type);
638         return S_OK;
639     }
640
641     FIXME("stub!\n");
642     return E_FAIL;
643 }
644
645 static HRESULT WINAPI MimeBody_SetProp(
646                               IMimeBody* iface,
647                               LPCSTR pszName,
648                               DWORD dwFlags,
649                               LPCPROPVARIANT pValue)
650 {
651     FIXME("stub\n");
652     return E_NOTIMPL;
653 }
654
655 static HRESULT WINAPI MimeBody_AppendProp(
656                                  IMimeBody* iface,
657                                  LPCSTR pszName,
658                                  DWORD dwFlags,
659                                  LPPROPVARIANT pValue)
660 {
661     FIXME("stub\n");
662     return E_NOTIMPL;
663 }
664
665 static HRESULT WINAPI MimeBody_DeleteProp(
666                                  IMimeBody* iface,
667                                  LPCSTR pszName)
668 {
669     FIXME("stub\n");
670     return E_NOTIMPL;
671 }
672
673 static HRESULT WINAPI MimeBody_CopyProps(
674                                 IMimeBody* iface,
675                                 ULONG cNames,
676                                 LPCSTR* prgszName,
677                                 IMimePropertySet* pPropertySet)
678 {
679     FIXME("stub\n");
680     return E_NOTIMPL;
681 }
682
683 static HRESULT WINAPI MimeBody_MoveProps(
684                                 IMimeBody* iface,
685                                 ULONG cNames,
686                                 LPCSTR* prgszName,
687                                 IMimePropertySet* pPropertySet)
688 {
689     FIXME("stub\n");
690     return E_NOTIMPL;
691 }
692
693 static HRESULT WINAPI MimeBody_DeleteExcept(
694                                    IMimeBody* iface,
695                                    ULONG cNames,
696                                    LPCSTR* prgszName)
697 {
698     FIXME("stub\n");
699     return E_NOTIMPL;
700 }
701
702 static HRESULT WINAPI MimeBody_QueryProp(
703                                 IMimeBody* iface,
704                                 LPCSTR pszName,
705                                 LPCSTR pszCriteria,
706                                 boolean fSubString,
707                                 boolean fCaseSensitive)
708 {
709     FIXME("stub\n");
710     return E_NOTIMPL;
711 }
712
713 static HRESULT WINAPI MimeBody_GetCharset(
714                                  IMimeBody* iface,
715                                  LPHCHARSET phCharset)
716 {
717     FIXME("stub\n");
718     *phCharset = NULL;
719     return S_OK;
720 }
721
722 static HRESULT WINAPI MimeBody_SetCharset(
723                                  IMimeBody* iface,
724                                  HCHARSET hCharset,
725                                  CSETAPPLYTYPE applytype)
726 {
727     FIXME("stub\n");
728     return E_NOTIMPL;
729 }
730
731 static HRESULT WINAPI MimeBody_GetParameters(
732                                     IMimeBody* iface,
733                                     LPCSTR pszName,
734                                     ULONG* pcParams,
735                                     LPMIMEPARAMINFO* pprgParam)
736 {
737     MimeBody *This = impl_from_IMimeBody(iface);
738     HRESULT hr;
739     header_t *header;
740
741     TRACE("(%p)->(%s, %p, %p)\n", iface, debugstr_a(pszName), pcParams, pprgParam);
742
743     *pprgParam = NULL;
744     *pcParams = 0;
745
746     hr = find_prop(This, pszName, &header);
747     if(hr != S_OK) return hr;
748
749     *pcParams = list_count(&header->params);
750     if(*pcParams)
751     {
752         IMimeAllocator *alloc;
753         param_t *param;
754         MIMEPARAMINFO *info;
755
756         MimeOleGetAllocator(&alloc);
757
758         *pprgParam = info = IMimeAllocator_Alloc(alloc, *pcParams * sizeof(**pprgParam));
759         LIST_FOR_EACH_ENTRY(param, &header->params, param_t, entry)
760         {
761             int len;
762
763             len = strlen(param->name) + 1;
764             info->pszName = IMimeAllocator_Alloc(alloc, len);
765             memcpy(info->pszName, param->name, len);
766             len = strlen(param->value) + 1;
767             info->pszData = IMimeAllocator_Alloc(alloc, len);
768             memcpy(info->pszData, param->value, len);
769             info++;
770         }
771         IMimeAllocator_Release(alloc);
772     }
773     return S_OK;
774 }
775
776 static HRESULT WINAPI MimeBody_IsContentType(
777                                     IMimeBody* iface,
778                                     LPCSTR pszPriType,
779                                     LPCSTR pszSubType)
780 {
781     MimeBody *This = impl_from_IMimeBody(iface);
782
783     TRACE("(%p)->(%s, %s)\n", This, debugstr_a(pszPriType), debugstr_a(pszSubType));
784     if(pszPriType)
785     {
786         const char *pri = This->content_pri_type;
787         if(!pri) pri = "text";
788         if(strcasecmp(pri, pszPriType)) return S_FALSE;
789     }
790
791     if(pszSubType)
792     {
793         const char *sub = This->content_sub_type;
794         if(!sub) sub = "plain";
795         if(strcasecmp(sub, pszSubType)) return S_FALSE;
796     }
797
798     return S_OK;
799 }
800
801 static HRESULT WINAPI MimeBody_BindToObject(
802                                    IMimeBody* iface,
803                                    REFIID riid,
804                                    void** ppvObject)
805 {
806     FIXME("stub\n");
807     return E_NOTIMPL;
808 }
809
810 static HRESULT WINAPI MimeBody_Clone(
811                             IMimeBody* iface,
812                             IMimePropertySet** ppPropertySet)
813 {
814     FIXME("stub\n");
815     return E_NOTIMPL;
816 }
817
818 static HRESULT WINAPI MimeBody_SetOption(
819                                 IMimeBody* iface,
820                                 const TYPEDID oid,
821                                 LPCPROPVARIANT pValue)
822 {
823     FIXME("(%p)->(%08x, %p): stub\n", iface, oid, pValue);
824     return E_NOTIMPL;
825 }
826
827 static HRESULT WINAPI MimeBody_GetOption(
828                                 IMimeBody* iface,
829                                 const TYPEDID oid,
830                                 LPPROPVARIANT pValue)
831 {
832     FIXME("(%p)->(%08x, %p): stub\n", iface, oid, pValue);
833     return E_NOTIMPL;
834 }
835
836 static HRESULT WINAPI MimeBody_EnumProps(
837                                 IMimeBody* iface,
838                                 DWORD dwFlags,
839                                 IMimeEnumProperties** ppEnum)
840 {
841     FIXME("stub\n");
842     return E_NOTIMPL;
843 }
844
845 static HRESULT WINAPI MimeBody_IsType(
846                              IMimeBody* iface,
847                              IMSGBODYTYPE bodytype)
848 {
849     FIXME("(%p)->(%d): stub\n", iface, bodytype);
850     return E_NOTIMPL;
851 }
852
853 static HRESULT WINAPI MimeBody_SetDisplayName(
854                                      IMimeBody* iface,
855                                      LPCSTR pszDisplay)
856 {
857     FIXME("stub\n");
858     return E_NOTIMPL;
859 }
860
861 static HRESULT WINAPI MimeBody_GetDisplayName(
862                                      IMimeBody* iface,
863                                      LPSTR* ppszDisplay)
864 {
865     FIXME("stub\n");
866     return E_NOTIMPL;
867 }
868
869 static HRESULT WINAPI MimeBody_GetOffsets(
870                                  IMimeBody* iface,
871                                  LPBODYOFFSETS pOffsets)
872 {
873     MimeBody *This = impl_from_IMimeBody(iface);
874     TRACE("(%p)->(%p)\n", This, pOffsets);
875
876     *pOffsets = This->body_offsets;
877
878     if(This->body_offsets.cbBodyEnd == 0) return MIME_E_NO_DATA;
879     return S_OK;
880 }
881
882 static HRESULT WINAPI MimeBody_GetCurrentEncoding(
883                                          IMimeBody* iface,
884                                          ENCODINGTYPE* pietEncoding)
885 {
886     MimeBody *This = impl_from_IMimeBody(iface);
887
888     TRACE("(%p)->(%p)\n", This, pietEncoding);
889
890     *pietEncoding = This->encoding;
891     return S_OK;
892 }
893
894 static HRESULT WINAPI MimeBody_SetCurrentEncoding(
895                                          IMimeBody* iface,
896                                          ENCODINGTYPE ietEncoding)
897 {
898     MimeBody *This = impl_from_IMimeBody(iface);
899
900     TRACE("(%p)->(%d)\n", This, ietEncoding);
901
902     This->encoding = ietEncoding;
903     return S_OK;
904 }
905
906 static HRESULT WINAPI MimeBody_GetEstimatedSize(
907                                        IMimeBody* iface,
908                                        ENCODINGTYPE ietEncoding,
909                                        ULONG* pcbSize)
910 {
911     FIXME("stub\n");
912     return E_NOTIMPL;
913 }
914
915 static HRESULT WINAPI MimeBody_GetDataHere(
916                                   IMimeBody* iface,
917                                   ENCODINGTYPE ietEncoding,
918                                   IStream* pStream)
919 {
920     FIXME("stub\n");
921     return E_NOTIMPL;
922 }
923
924 static HRESULT WINAPI MimeBody_GetData(
925                               IMimeBody* iface,
926                               ENCODINGTYPE ietEncoding,
927                               IStream** ppStream)
928 {
929     MimeBody *This = impl_from_IMimeBody(iface);
930     FIXME("(%p)->(%d, %p). Ignoring encoding type.\n", This, ietEncoding, ppStream);
931
932     *ppStream = This->data;
933     IStream_AddRef(*ppStream);
934     return S_OK;
935 }
936
937 static HRESULT WINAPI MimeBody_SetData(
938                               IMimeBody* iface,
939                               ENCODINGTYPE ietEncoding,
940                               LPCSTR pszPriType,
941                               LPCSTR pszSubType,
942                               REFIID riid,
943                               LPVOID pvObject)
944 {
945     MimeBody *This = impl_from_IMimeBody(iface);
946     TRACE("(%p)->(%d, %s, %s, %s %p)\n", This, ietEncoding, debugstr_a(pszPriType), debugstr_a(pszSubType),
947           debugstr_guid(riid), pvObject);
948
949     if(IsEqualIID(riid, &IID_IStream))
950         IStream_AddRef((IStream *)pvObject);
951     else
952     {
953         FIXME("Unhandled object type %s\n", debugstr_guid(riid));
954         return E_INVALIDARG;
955     }
956
957     if(This->data)
958         FIXME("release old data\n");
959
960     This->data_iid = *riid;
961     This->data = pvObject;
962
963     IMimeBody_SetCurrentEncoding(iface, ietEncoding);
964
965     /* FIXME: Update the content type.
966        If pszPriType == NULL use 'application'
967        If pszSubType == NULL use 'octet-stream' */
968
969     return S_OK;
970 }
971
972 static HRESULT WINAPI MimeBody_EmptyData(
973                                 IMimeBody* iface)
974 {
975     FIXME("stub\n");
976     return E_NOTIMPL;
977 }
978
979 static HRESULT WINAPI MimeBody_CopyTo(
980                              IMimeBody* iface,
981                              IMimeBody* pBody)
982 {
983     FIXME("stub\n");
984     return E_NOTIMPL;
985 }
986
987 static HRESULT WINAPI MimeBody_GetTransmitInfo(
988                                       IMimeBody* iface,
989                                       LPTRANSMITINFO pTransmitInfo)
990 {
991     FIXME("stub\n");
992     return E_NOTIMPL;
993 }
994
995 static HRESULT WINAPI MimeBody_SaveToFile(
996                                  IMimeBody* iface,
997                                  ENCODINGTYPE ietEncoding,
998                                  LPCSTR pszFilePath)
999 {
1000     FIXME("stub\n");
1001     return E_NOTIMPL;
1002 }
1003
1004 static HRESULT WINAPI MimeBody_GetHandle(
1005                                 IMimeBody* iface,
1006                                 LPHBODY phBody)
1007 {
1008     MimeBody *This = impl_from_IMimeBody(iface);
1009     TRACE("(%p)->(%p)\n", iface, phBody);
1010
1011     *phBody = This->handle;
1012     return This->handle ? S_OK : MIME_E_NO_DATA;
1013 }
1014
1015 static IMimeBodyVtbl body_vtbl =
1016 {
1017     MimeBody_QueryInterface,
1018     MimeBody_AddRef,
1019     MimeBody_Release,
1020     MimeBody_GetClassID,
1021     MimeBody_IsDirty,
1022     MimeBody_Load,
1023     MimeBody_Save,
1024     MimeBody_GetSizeMax,
1025     MimeBody_InitNew,
1026     MimeBody_GetPropInfo,
1027     MimeBody_SetPropInfo,
1028     MimeBody_GetProp,
1029     MimeBody_SetProp,
1030     MimeBody_AppendProp,
1031     MimeBody_DeleteProp,
1032     MimeBody_CopyProps,
1033     MimeBody_MoveProps,
1034     MimeBody_DeleteExcept,
1035     MimeBody_QueryProp,
1036     MimeBody_GetCharset,
1037     MimeBody_SetCharset,
1038     MimeBody_GetParameters,
1039     MimeBody_IsContentType,
1040     MimeBody_BindToObject,
1041     MimeBody_Clone,
1042     MimeBody_SetOption,
1043     MimeBody_GetOption,
1044     MimeBody_EnumProps,
1045     MimeBody_IsType,
1046     MimeBody_SetDisplayName,
1047     MimeBody_GetDisplayName,
1048     MimeBody_GetOffsets,
1049     MimeBody_GetCurrentEncoding,
1050     MimeBody_SetCurrentEncoding,
1051     MimeBody_GetEstimatedSize,
1052     MimeBody_GetDataHere,
1053     MimeBody_GetData,
1054     MimeBody_SetData,
1055     MimeBody_EmptyData,
1056     MimeBody_CopyTo,
1057     MimeBody_GetTransmitInfo,
1058     MimeBody_SaveToFile,
1059     MimeBody_GetHandle
1060 };
1061
1062 static HRESULT MimeBody_set_offsets(MimeBody *body, const BODYOFFSETS *offsets)
1063 {
1064     TRACE("setting offsets to %d, %d, %d, %d\n", offsets->cbBoundaryStart,
1065           offsets->cbHeaderStart, offsets->cbBodyStart, offsets->cbBodyEnd);
1066
1067     body->body_offsets = *offsets;
1068     return S_OK;
1069 }
1070
1071 #define FIRST_CUSTOM_PROP_ID 0x100
1072
1073 HRESULT MimeBody_create(IUnknown *outer, void **obj)
1074 {
1075     MimeBody *This;
1076     BODYOFFSETS body_offsets;
1077
1078     *obj = NULL;
1079
1080     if(outer) return CLASS_E_NOAGGREGATION;
1081
1082     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
1083     if (!This) return E_OUTOFMEMORY;
1084
1085     This->lpVtbl = &body_vtbl;
1086     This->refs = 1;
1087     This->handle = NULL;
1088     list_init(&This->headers);
1089     list_init(&This->new_props);
1090     This->next_prop_id = FIRST_CUSTOM_PROP_ID;
1091     This->content_pri_type = NULL;
1092     This->content_sub_type = NULL;
1093     This->encoding = IET_7BIT;
1094     This->data = NULL;
1095     This->data_iid = IID_NULL;
1096
1097     body_offsets.cbBoundaryStart = body_offsets.cbHeaderStart = 0;
1098     body_offsets.cbBodyStart     = body_offsets.cbBodyEnd     = 0;
1099     MimeBody_set_offsets(This, &body_offsets);
1100
1101     *obj = (IMimeBody *)&This->lpVtbl;
1102     return S_OK;
1103 }
1104
1105 typedef struct
1106 {
1107     IStreamVtbl *lpVtbl;
1108     LONG refs;
1109
1110     IStream *base;
1111     ULARGE_INTEGER pos, start, length;
1112 } sub_stream_t;
1113
1114 static inline sub_stream_t *impl_from_IStream( IStream *iface )
1115 {
1116     return (sub_stream_t *)((char*)iface - FIELD_OFFSET(sub_stream_t, lpVtbl));
1117 }
1118
1119 static HRESULT WINAPI sub_stream_QueryInterface(
1120         IStream* iface,
1121         REFIID riid,
1122         void **ppvObject)
1123 {
1124     sub_stream_t *This = impl_from_IStream(iface);
1125
1126     TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppvObject);
1127     *ppvObject = NULL;
1128
1129     if(IsEqualIID(riid, &IID_IUnknown) ||
1130        IsEqualIID(riid, &IID_ISequentialStream) ||
1131        IsEqualIID(riid, &IID_IStream))
1132     {
1133         IStream_AddRef(iface);
1134         *ppvObject = iface;
1135         return S_OK;
1136     }
1137     return E_NOINTERFACE;
1138 }
1139
1140 static ULONG WINAPI sub_stream_AddRef(
1141          IStream* iface)
1142 {
1143     sub_stream_t *This = impl_from_IStream(iface);
1144
1145     TRACE("(%p)\n", This);
1146     return InterlockedIncrement(&This->refs);
1147 }
1148
1149 static ULONG WINAPI  sub_stream_Release(
1150         IStream* iface)
1151 {
1152     sub_stream_t *This = impl_from_IStream(iface);
1153     LONG refs;
1154
1155     TRACE("(%p)\n", This);
1156     refs = InterlockedDecrement(&This->refs);
1157     if(!refs)
1158     {
1159         IStream_Release(This->base);
1160         HeapFree(GetProcessHeap(), 0, This);
1161     }
1162     return refs;
1163 }
1164
1165 static HRESULT WINAPI sub_stream_Read(
1166         IStream* iface,
1167         void *pv,
1168         ULONG cb,
1169         ULONG *pcbRead)
1170 {
1171     sub_stream_t *This = impl_from_IStream(iface);
1172     HRESULT hr;
1173     ULARGE_INTEGER base_pos;
1174     LARGE_INTEGER tmp_pos;
1175
1176     TRACE("(%p, %d, %p)\n", pv, cb, pcbRead);
1177
1178     tmp_pos.QuadPart = 0;
1179     IStream_Seek(This->base, tmp_pos, STREAM_SEEK_CUR, &base_pos);
1180     tmp_pos.QuadPart = This->pos.QuadPart + This->start.QuadPart;
1181     IStream_Seek(This->base, tmp_pos, STREAM_SEEK_SET, NULL);
1182
1183     if(This->pos.QuadPart + cb > This->length.QuadPart)
1184         cb = This->length.QuadPart - This->pos.QuadPart;
1185
1186     hr = IStream_Read(This->base, pv, cb, pcbRead);
1187
1188     This->pos.QuadPart += *pcbRead;
1189
1190     tmp_pos.QuadPart = base_pos.QuadPart;
1191     IStream_Seek(This->base, tmp_pos, STREAM_SEEK_SET, NULL);
1192
1193     return hr;
1194 }
1195
1196 static HRESULT WINAPI sub_stream_Write(
1197         IStream* iface,
1198         const void *pv,
1199         ULONG cb,
1200         ULONG *pcbWritten)
1201 {
1202     FIXME("stub\n");
1203     return E_NOTIMPL;
1204 }
1205
1206 static HRESULT WINAPI sub_stream_Seek(
1207         IStream* iface,
1208         LARGE_INTEGER dlibMove,
1209         DWORD dwOrigin,
1210         ULARGE_INTEGER *plibNewPosition)
1211 {
1212     sub_stream_t *This = impl_from_IStream(iface);
1213     LARGE_INTEGER new_pos;
1214
1215     TRACE("(%08x.%08x, %x, %p)\n", dlibMove.u.HighPart, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
1216
1217     switch(dwOrigin)
1218     {
1219     case STREAM_SEEK_SET:
1220         new_pos = dlibMove;
1221         break;
1222     case STREAM_SEEK_CUR:
1223         new_pos.QuadPart = This->pos.QuadPart + dlibMove.QuadPart;
1224         break;
1225     case STREAM_SEEK_END:
1226         new_pos.QuadPart = This->length.QuadPart + dlibMove.QuadPart;
1227         break;
1228     default:
1229         return STG_E_INVALIDFUNCTION;
1230     }
1231
1232     if(new_pos.QuadPart < 0) new_pos.QuadPart = 0;
1233     else if(new_pos.QuadPart > This->length.QuadPart) new_pos.QuadPart = This->length.QuadPart;
1234
1235     This->pos.QuadPart = new_pos.QuadPart;
1236
1237     if(plibNewPosition) *plibNewPosition = This->pos;
1238     return S_OK;
1239 }
1240
1241 static HRESULT WINAPI sub_stream_SetSize(
1242         IStream* iface,
1243         ULARGE_INTEGER libNewSize)
1244 {
1245     FIXME("stub\n");
1246     return E_NOTIMPL;
1247 }
1248
1249 static HRESULT WINAPI sub_stream_CopyTo(
1250         IStream* iface,
1251         IStream *pstm,
1252         ULARGE_INTEGER cb,
1253         ULARGE_INTEGER *pcbRead,
1254         ULARGE_INTEGER *pcbWritten)
1255 {
1256     HRESULT        hr = S_OK;
1257     BYTE           tmpBuffer[128];
1258     ULONG          bytesRead, bytesWritten, copySize;
1259     ULARGE_INTEGER totalBytesRead;
1260     ULARGE_INTEGER totalBytesWritten;
1261
1262     TRACE("(%p)->(%p, %d, %p, %p)\n", iface, pstm, cb.u.LowPart, pcbRead, pcbWritten);
1263
1264     totalBytesRead.QuadPart = 0;
1265     totalBytesWritten.QuadPart = 0;
1266
1267     while ( cb.QuadPart > 0 )
1268     {
1269         if ( cb.QuadPart >= sizeof(tmpBuffer) )
1270             copySize = sizeof(tmpBuffer);
1271         else
1272             copySize = cb.u.LowPart;
1273
1274         hr = IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
1275         if (FAILED(hr)) break;
1276
1277         totalBytesRead.QuadPart += bytesRead;
1278
1279         if (bytesRead)
1280         {
1281             hr = IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
1282             if (FAILED(hr)) break;
1283             totalBytesWritten.QuadPart += bytesWritten;
1284         }
1285
1286         if (bytesRead != copySize)
1287             cb.QuadPart = 0;
1288         else
1289             cb.QuadPart -= bytesRead;
1290     }
1291
1292     if (pcbRead) pcbRead->QuadPart = totalBytesRead.QuadPart;
1293     if (pcbWritten) pcbWritten->QuadPart = totalBytesWritten.QuadPart;
1294
1295     return hr;
1296 }
1297
1298 static HRESULT WINAPI sub_stream_Commit(
1299         IStream* iface,
1300         DWORD grfCommitFlags)
1301 {
1302     FIXME("stub\n");
1303     return E_NOTIMPL;
1304 }
1305
1306 static HRESULT WINAPI sub_stream_Revert(
1307         IStream* iface)
1308 {
1309     FIXME("stub\n");
1310     return E_NOTIMPL;
1311 }
1312
1313 static HRESULT WINAPI sub_stream_LockRegion(
1314         IStream* iface,
1315         ULARGE_INTEGER libOffset,
1316         ULARGE_INTEGER cb,
1317         DWORD dwLockType)
1318 {
1319     FIXME("stub\n");
1320     return E_NOTIMPL;
1321 }
1322
1323 static HRESULT WINAPI sub_stream_UnlockRegion(
1324         IStream* iface,
1325         ULARGE_INTEGER libOffset,
1326         ULARGE_INTEGER cb,
1327         DWORD dwLockType)
1328 {
1329     FIXME("stub\n");
1330     return E_NOTIMPL;
1331 }
1332
1333 static HRESULT WINAPI sub_stream_Stat(
1334         IStream* iface,
1335         STATSTG *pstatstg,
1336         DWORD grfStatFlag)
1337 {
1338     sub_stream_t *This = impl_from_IStream(iface);
1339     FIXME("(%p)->(%p, %08x)\n", This, pstatstg, grfStatFlag);
1340     memset(pstatstg, 0, sizeof(*pstatstg));
1341     pstatstg->cbSize = This->length;
1342     return S_OK;
1343 }
1344
1345 static HRESULT WINAPI sub_stream_Clone(
1346         IStream* iface,
1347         IStream **ppstm)
1348 {
1349     FIXME("stub\n");
1350     return E_NOTIMPL;
1351 }
1352
1353 static struct IStreamVtbl sub_stream_vtbl =
1354 {
1355     sub_stream_QueryInterface,
1356     sub_stream_AddRef,
1357     sub_stream_Release,
1358     sub_stream_Read,
1359     sub_stream_Write,
1360     sub_stream_Seek,
1361     sub_stream_SetSize,
1362     sub_stream_CopyTo,
1363     sub_stream_Commit,
1364     sub_stream_Revert,
1365     sub_stream_LockRegion,
1366     sub_stream_UnlockRegion,
1367     sub_stream_Stat,
1368     sub_stream_Clone
1369 };
1370
1371 static HRESULT create_sub_stream(IStream *stream, ULARGE_INTEGER start, ULARGE_INTEGER length, IStream **out)
1372 {
1373     sub_stream_t *This;
1374
1375     *out = NULL;
1376     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
1377     if(!This) return E_OUTOFMEMORY;
1378
1379     This->lpVtbl = &sub_stream_vtbl;
1380     This->refs = 1;
1381     This->start = start;
1382     This->length = length;
1383     This->pos.QuadPart = 0;
1384     IStream_AddRef(stream);
1385     This->base = stream;
1386
1387     *out = (IStream*)&This->lpVtbl;
1388     return S_OK;
1389 }
1390
1391
1392 typedef struct body_t
1393 {
1394     struct list entry;
1395     HBODY hbody;
1396     IMimeBody *mime_body;
1397
1398     struct body_t *parent;
1399     struct list children;
1400 } body_t;
1401
1402 typedef struct MimeMessage
1403 {
1404     const IMimeMessageVtbl *lpVtbl;
1405
1406     LONG refs;
1407     IStream *stream;
1408
1409     struct list body_tree;
1410     HBODY next_hbody;
1411 } MimeMessage;
1412
1413 static HRESULT WINAPI MimeMessage_QueryInterface(IMimeMessage *iface, REFIID riid, void **ppv)
1414 {
1415     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1416
1417     if (IsEqualIID(riid, &IID_IUnknown) ||
1418         IsEqualIID(riid, &IID_IPersist) ||
1419         IsEqualIID(riid, &IID_IPersistStreamInit) ||
1420         IsEqualIID(riid, &IID_IMimeMessageTree) ||
1421         IsEqualIID(riid, &IID_IMimeMessage))
1422     {
1423         *ppv = iface;
1424         IUnknown_AddRef(iface);
1425         return S_OK;
1426     }
1427
1428     FIXME("no interface for %s\n", debugstr_guid(riid));
1429     *ppv = NULL;
1430     return E_NOINTERFACE;
1431 }
1432
1433 static ULONG WINAPI MimeMessage_AddRef(IMimeMessage *iface)
1434 {
1435     MimeMessage *This = (MimeMessage *)iface;
1436     TRACE("(%p)->()\n", iface);
1437     return InterlockedIncrement(&This->refs);
1438 }
1439
1440 static void empty_body_list(struct list *list)
1441 {
1442     body_t *body, *cursor2;
1443     LIST_FOR_EACH_ENTRY_SAFE(body, cursor2, list, body_t, entry)
1444     {
1445         empty_body_list(&body->children);
1446         list_remove(&body->entry);
1447         IMimeBody_Release(body->mime_body);
1448         HeapFree(GetProcessHeap(), 0, body);
1449     }
1450 }
1451
1452 static ULONG WINAPI MimeMessage_Release(IMimeMessage *iface)
1453 {
1454     MimeMessage *This = (MimeMessage *)iface;
1455     ULONG refs;
1456
1457     TRACE("(%p)->()\n", iface);
1458
1459     refs = InterlockedDecrement(&This->refs);
1460     if (!refs)
1461     {
1462         empty_body_list(&This->body_tree);
1463
1464         if(This->stream) IStream_Release(This->stream);
1465         HeapFree(GetProcessHeap(), 0, This);
1466     }
1467
1468     return refs;
1469 }
1470
1471 /*** IPersist methods ***/
1472 static HRESULT WINAPI MimeMessage_GetClassID(
1473     IMimeMessage *iface,
1474     CLSID *pClassID)
1475 {
1476     FIXME("(%p)->(%p)\n", iface, pClassID);
1477     return E_NOTIMPL;
1478 }
1479
1480 /*** IPersistStreamInit methods ***/
1481 static HRESULT WINAPI MimeMessage_IsDirty(
1482     IMimeMessage *iface)
1483 {
1484     FIXME("(%p)->()\n", iface);
1485     return E_NOTIMPL;
1486 }
1487
1488 static body_t *new_body_entry(IMimeBody *mime_body, HBODY hbody, body_t *parent)
1489 {
1490     body_t *body = HeapAlloc(GetProcessHeap(), 0, sizeof(*body));
1491     if(body)
1492     {
1493         body->mime_body = mime_body;
1494         body->hbody = hbody;
1495         list_init(&body->children);
1496         body->parent = parent;
1497     }
1498     return body;
1499 }
1500
1501 typedef struct
1502 {
1503     struct list entry;
1504     BODYOFFSETS offsets;
1505 } offset_entry_t;
1506
1507 static HRESULT create_body_offset_list(IStream *stm, const char *boundary, struct list *body_offsets)
1508 {
1509     HRESULT hr;
1510     DWORD read;
1511     int boundary_len = strlen(boundary);
1512     char *buf, *nl_boundary, *ptr, *overlap;
1513     DWORD start = 0, overlap_no;
1514     offset_entry_t *cur_body = NULL;
1515     ULARGE_INTEGER cur;
1516     LARGE_INTEGER zero;
1517
1518     list_init(body_offsets);
1519     nl_boundary = HeapAlloc(GetProcessHeap(), 0, 4 + boundary_len + 1);
1520     memcpy(nl_boundary, "\r\n--", 4);
1521     memcpy(nl_boundary + 4, boundary, boundary_len + 1);
1522
1523     overlap_no = boundary_len + 5;
1524
1525     overlap = buf = HeapAlloc(GetProcessHeap(), 0, overlap_no + PARSER_BUF_SIZE + 1);
1526
1527     zero.QuadPart = 0;
1528     hr = IStream_Seek(stm, zero, STREAM_SEEK_CUR, &cur);
1529     start = cur.u.LowPart;
1530
1531     do {
1532         hr = IStream_Read(stm, overlap, PARSER_BUF_SIZE, &read);
1533         if(FAILED(hr)) goto end;
1534         if(read == 0) break;
1535         overlap[read] = '\0';
1536
1537         ptr = buf;
1538         do {
1539             ptr = strstr(ptr, nl_boundary);
1540             if(ptr)
1541             {
1542                 DWORD boundary_start = start + ptr - buf;
1543                 char *end = ptr + boundary_len + 4;
1544
1545                 if(*end == '\0' || *(end + 1) == '\0')
1546                     break;
1547
1548                 if(*end == '\r' && *(end + 1) == '\n')
1549                 {
1550                     if(cur_body)
1551                     {
1552                         cur_body->offsets.cbBodyEnd = boundary_start;
1553                         list_add_tail(body_offsets, &cur_body->entry);
1554                     }
1555                     cur_body = HeapAlloc(GetProcessHeap(), 0, sizeof(*cur_body));
1556                     cur_body->offsets.cbBoundaryStart = boundary_start + 2; /* doesn't including the leading \r\n */
1557                     cur_body->offsets.cbHeaderStart = boundary_start + boundary_len + 6;
1558                 }
1559                 else if(*end == '-' && *(end + 1) == '-')
1560                 {
1561                     if(cur_body)
1562                     {
1563                         cur_body->offsets.cbBodyEnd = boundary_start;
1564                         list_add_tail(body_offsets, &cur_body->entry);
1565                         goto end;
1566                     }
1567                 }
1568                 ptr = end + 2;
1569             }
1570         } while(ptr);
1571
1572         if(overlap == buf) /* 1st iteration */
1573         {
1574             memcpy(buf, buf + PARSER_BUF_SIZE - overlap_no, overlap_no);
1575             overlap = buf + overlap_no;
1576             start += read - overlap_no;
1577         }
1578         else
1579         {
1580             memcpy(buf, buf + PARSER_BUF_SIZE, overlap_no);
1581             start += read;
1582         }
1583     } while(1);
1584
1585 end:
1586     HeapFree(GetProcessHeap(), 0, buf);
1587     return hr;
1588 }
1589
1590 static body_t *create_sub_body(MimeMessage *msg, IStream *pStm, BODYOFFSETS *offset, body_t *parent)
1591 {
1592     IMimeBody *mime_body;
1593     HRESULT hr;
1594     body_t *body;
1595     ULARGE_INTEGER cur;
1596     LARGE_INTEGER zero;
1597
1598     MimeBody_create(NULL, (void**)&mime_body);
1599     IMimeBody_Load(mime_body, pStm);
1600     zero.QuadPart = 0;
1601     hr = IStream_Seek(pStm, zero, STREAM_SEEK_CUR, &cur);
1602     offset->cbBodyStart = cur.u.LowPart + offset->cbHeaderStart;
1603     if(parent) MimeBody_set_offsets(impl_from_IMimeBody(mime_body), offset);
1604     IMimeBody_SetData(mime_body, IET_BINARY, NULL, NULL, &IID_IStream, pStm);
1605     body = new_body_entry(mime_body, msg->next_hbody, parent);
1606     msg->next_hbody = (HBODY)((DWORD)msg->next_hbody + 1);
1607
1608     if(IMimeBody_IsContentType(mime_body, "multipart", NULL) == S_OK)
1609     {
1610         MIMEPARAMINFO *param_info;
1611         ULONG count, i;
1612         IMimeAllocator *alloc;
1613
1614         hr = IMimeBody_GetParameters(mime_body, "Content-Type", &count, &param_info);
1615         if(hr != S_OK || count == 0) return body;
1616
1617         MimeOleGetAllocator(&alloc);
1618
1619         for(i = 0; i < count; i++)
1620         {
1621             if(!strcasecmp(param_info[i].pszName, "boundary"))
1622             {
1623                 struct list offset_list;
1624                 offset_entry_t *cur, *cursor2;
1625                 hr = create_body_offset_list(pStm, param_info[i].pszData, &offset_list);
1626                 LIST_FOR_EACH_ENTRY_SAFE(cur, cursor2, &offset_list, offset_entry_t, entry)
1627                 {
1628                     body_t *sub_body;
1629                     IStream *sub_stream;
1630                     ULARGE_INTEGER start, length;
1631
1632                     start.QuadPart = cur->offsets.cbHeaderStart;
1633                     length.QuadPart = cur->offsets.cbBodyEnd - cur->offsets.cbHeaderStart;
1634                     create_sub_stream(pStm, start, length, &sub_stream);
1635                     sub_body = create_sub_body(msg, sub_stream, &cur->offsets, body);
1636                     IStream_Release(sub_stream);
1637                     list_add_tail(&body->children, &sub_body->entry);
1638                     list_remove(&cur->entry);
1639                     HeapFree(GetProcessHeap(), 0, cur);
1640                 }
1641                 break;
1642             }
1643         }
1644         IMimeAllocator_FreeParamInfoArray(alloc, count, param_info, TRUE);
1645         IMimeAllocator_Release(alloc);
1646     }
1647     return body;
1648 }
1649
1650 static HRESULT WINAPI MimeMessage_Load(
1651     IMimeMessage *iface,
1652     LPSTREAM pStm)
1653 {
1654     MimeMessage *This = (MimeMessage *)iface;
1655     body_t *root_body;
1656     BODYOFFSETS offsets;
1657     ULARGE_INTEGER cur;
1658     LARGE_INTEGER zero;
1659
1660     TRACE("(%p)->(%p)\n", iface, pStm);
1661
1662     if(This->stream)
1663     {
1664         FIXME("already loaded a message\n");
1665         return E_FAIL;
1666     }
1667
1668     IStream_AddRef(pStm);
1669     This->stream = pStm;
1670     offsets.cbBoundaryStart = offsets.cbHeaderStart = 0;
1671     offsets.cbBodyStart = offsets.cbBodyEnd = 0;
1672
1673     root_body = create_sub_body(This, pStm, &offsets, NULL);
1674
1675     zero.QuadPart = 0;
1676     IStream_Seek(pStm, zero, STREAM_SEEK_END, &cur);
1677     offsets.cbBodyEnd = cur.u.LowPart;
1678     MimeBody_set_offsets(impl_from_IMimeBody(root_body->mime_body), &offsets);
1679
1680     list_add_head(&This->body_tree, &root_body->entry);
1681
1682     return S_OK;
1683 }
1684
1685 static HRESULT WINAPI MimeMessage_Save(
1686     IMimeMessage *iface,
1687     LPSTREAM pStm,
1688     BOOL fClearDirty)
1689 {
1690     FIXME("(%p)->(%p, %s)\n", iface, pStm, fClearDirty ? "TRUE" : "FALSE");
1691     return E_NOTIMPL;
1692 }
1693
1694 static HRESULT WINAPI MimeMessage_GetSizeMax(
1695     IMimeMessage *iface,
1696     ULARGE_INTEGER *pcbSize)
1697 {
1698     FIXME("(%p)->(%p)\n", iface, pcbSize);
1699     return E_NOTIMPL;
1700 }
1701
1702 static HRESULT WINAPI MimeMessage_InitNew(
1703     IMimeMessage *iface)
1704 {
1705     FIXME("(%p)->()\n", iface);
1706     return E_NOTIMPL;
1707 }
1708
1709 /*** IMimeMessageTree methods ***/
1710 static HRESULT WINAPI MimeMessage_GetMessageSource(
1711     IMimeMessage *iface,
1712     IStream **ppStream,
1713     DWORD dwFlags)
1714 {
1715     MimeMessage *This = (MimeMessage *)iface;
1716     FIXME("(%p)->(%p, 0x%x)\n", iface, ppStream, dwFlags);
1717
1718     IStream_AddRef(This->stream);
1719     *ppStream = This->stream;
1720     return S_OK;
1721 }
1722
1723 static HRESULT WINAPI MimeMessage_GetMessageSize(
1724     IMimeMessage *iface,
1725     ULONG *pcbSize,
1726     DWORD dwFlags)
1727 {
1728     FIXME("(%p)->(%p, 0x%x)\n", iface, pcbSize, dwFlags);
1729     return E_NOTIMPL;
1730 }
1731
1732 static HRESULT WINAPI MimeMessage_LoadOffsetTable(
1733     IMimeMessage *iface,
1734     IStream *pStream)
1735 {
1736     FIXME("(%p)->(%p)\n", iface, pStream);
1737     return E_NOTIMPL;
1738 }
1739
1740 static HRESULT WINAPI MimeMessage_SaveOffsetTable(
1741     IMimeMessage *iface,
1742     IStream *pStream,
1743     DWORD dwFlags)
1744 {
1745     FIXME("(%p)->(%p, 0x%x)\n", iface, pStream, dwFlags);
1746     return E_NOTIMPL;
1747 }
1748
1749
1750 static HRESULT WINAPI MimeMessage_GetFlags(
1751     IMimeMessage *iface,
1752     DWORD *pdwFlags)
1753 {
1754     FIXME("(%p)->(%p)\n", iface, pdwFlags);
1755     return E_NOTIMPL;
1756 }
1757
1758 static HRESULT WINAPI MimeMessage_Commit(
1759     IMimeMessage *iface,
1760     DWORD dwFlags)
1761 {
1762     FIXME("(%p)->(0x%x)\n", iface, dwFlags);
1763     return E_NOTIMPL;
1764 }
1765
1766
1767 static HRESULT WINAPI MimeMessage_HandsOffStorage(
1768     IMimeMessage *iface)
1769 {
1770     FIXME("(%p)->()\n", iface);
1771     return E_NOTIMPL;
1772 }
1773
1774 static HRESULT find_body(struct list *list, HBODY hbody, body_t **body)
1775 {
1776     body_t *cur;
1777     HRESULT hr;
1778
1779     if(hbody == HBODY_ROOT)
1780     {
1781         *body = LIST_ENTRY(list_head(list), body_t, entry);
1782         return S_OK;
1783     }
1784
1785     LIST_FOR_EACH_ENTRY(cur, list, body_t, entry)
1786     {
1787         if(cur->hbody == hbody)
1788         {
1789             *body = cur;
1790             return S_OK;
1791         }
1792         hr = find_body(&cur->children, hbody, body);
1793         if(hr == S_OK) return S_OK;
1794     }
1795     return S_FALSE;
1796 }
1797
1798 static HRESULT WINAPI MimeMessage_BindToObject(
1799     IMimeMessage *iface,
1800     const HBODY hBody,
1801     REFIID riid,
1802     void **ppvObject)
1803 {
1804     MimeMessage *This = (MimeMessage *)iface;
1805     HRESULT hr;
1806     body_t *body;
1807
1808     TRACE("(%p)->(%p, %s, %p)\n", iface, hBody, debugstr_guid(riid), ppvObject);
1809
1810     hr = find_body(&This->body_tree, hBody, &body);
1811
1812     if(hr != S_OK) return hr;
1813
1814     if(IsEqualIID(riid, &IID_IMimeBody))
1815     {
1816         IMimeBody_AddRef(body->mime_body);
1817         *ppvObject = body->mime_body;
1818         return S_OK;
1819     }
1820
1821     return E_NOINTERFACE;
1822 }
1823
1824 static HRESULT WINAPI MimeMessage_SaveBody(
1825     IMimeMessage *iface,
1826     HBODY hBody,
1827     DWORD dwFlags,
1828     IStream *pStream)
1829 {
1830     FIXME("(%p)->(%p, 0x%x, %p)\n", iface, hBody, dwFlags, pStream);
1831     return E_NOTIMPL;
1832 }
1833
1834 static HRESULT get_body(MimeMessage *msg, BODYLOCATION location, HBODY pivot, body_t **out)
1835 {
1836     body_t *root = LIST_ENTRY(list_head(&msg->body_tree), body_t, entry);
1837     body_t *body;
1838     HRESULT hr;
1839     struct list *list;
1840
1841     if(location == IBL_ROOT)
1842     {
1843         *out = root;
1844         return S_OK;
1845     }
1846
1847     hr = find_body(&msg->body_tree, pivot, &body);
1848
1849     if(hr == S_OK)
1850     {
1851         switch(location)
1852         {
1853         case IBL_PARENT:
1854             *out = body->parent;
1855             break;
1856
1857         case IBL_FIRST:
1858             list = list_head(&body->children);
1859             if(list)
1860                 *out = LIST_ENTRY(list, body_t, entry);
1861             else
1862                 hr = MIME_E_NOT_FOUND;
1863             break;
1864
1865         case IBL_LAST:
1866             list = list_tail(&body->children);
1867             if(list)
1868                 *out = LIST_ENTRY(list, body_t, entry);
1869             else
1870                 hr = MIME_E_NOT_FOUND;
1871             break;
1872
1873         case IBL_NEXT:
1874             list = list_next(&body->parent->children, &body->entry);
1875             if(list)
1876                 *out = LIST_ENTRY(list, body_t, entry);
1877             else
1878                 hr = MIME_E_NOT_FOUND;
1879             break;
1880
1881         case IBL_PREVIOUS:
1882             list = list_prev(&body->parent->children, &body->entry);
1883             if(list)
1884                 *out = LIST_ENTRY(list, body_t, entry);
1885             else
1886                 hr = MIME_E_NOT_FOUND;
1887             break;
1888
1889         default:
1890             hr = E_FAIL;
1891             break;
1892         }
1893     }
1894
1895     return hr;
1896 }
1897
1898
1899 static HRESULT WINAPI MimeMessage_InsertBody(
1900     IMimeMessage *iface,
1901     BODYLOCATION location,
1902     HBODY hPivot,
1903     LPHBODY phBody)
1904 {
1905     FIXME("(%p)->(%d, %p, %p)\n", iface, location, hPivot, phBody);
1906     return E_NOTIMPL;
1907 }
1908
1909 static HRESULT WINAPI MimeMessage_GetBody(
1910     IMimeMessage *iface,
1911     BODYLOCATION location,
1912     HBODY hPivot,
1913     LPHBODY phBody)
1914 {
1915     MimeMessage *This = (MimeMessage *)iface;
1916     body_t *body;
1917     HRESULT hr;
1918
1919     TRACE("(%p)->(%d, %p, %p)\n", iface, location, hPivot, phBody);
1920
1921     hr = get_body(This, location, hPivot, &body);
1922
1923     if(hr == S_OK) *phBody = body->hbody;
1924
1925     return hr;
1926 }
1927
1928 static HRESULT WINAPI MimeMessage_DeleteBody(
1929     IMimeMessage *iface,
1930     HBODY hBody,
1931     DWORD dwFlags)
1932 {
1933     FIXME("(%p)->(%p, %08x)\n", iface, hBody, dwFlags);
1934     return E_NOTIMPL;
1935 }
1936
1937 static HRESULT WINAPI MimeMessage_MoveBody(
1938     IMimeMessage *iface,
1939     HBODY hBody,
1940     BODYLOCATION location)
1941 {
1942     FIXME("(%p)->(%d)\n", iface, location);
1943     return E_NOTIMPL;
1944 }
1945
1946 static void count_children(body_t *body, boolean recurse, ULONG *count)
1947 {
1948     body_t *child;
1949
1950     LIST_FOR_EACH_ENTRY(child, &body->children, body_t, entry)
1951     {
1952         (*count)++;
1953         if(recurse) count_children(child, recurse, count);
1954     }
1955 }
1956
1957 static HRESULT WINAPI MimeMessage_CountBodies(
1958     IMimeMessage *iface,
1959     HBODY hParent,
1960     boolean fRecurse,
1961     ULONG *pcBodies)
1962 {
1963     HRESULT hr;
1964     MimeMessage *This = (MimeMessage *)iface;
1965     body_t *body;
1966
1967     TRACE("(%p)->(%p, %s, %p)\n", iface, hParent, fRecurse ? "TRUE" : "FALSE", pcBodies);
1968
1969     hr = find_body(&This->body_tree, hParent, &body);
1970     if(hr != S_OK) return hr;
1971
1972     *pcBodies = 1;
1973     count_children(body, fRecurse, pcBodies);
1974
1975     return S_OK;
1976 }
1977
1978 static HRESULT find_next(IMimeMessage *msg, LPFINDBODY find_body, HBODY *out)
1979 {
1980     HRESULT hr;
1981     IMimeBody *mime_body;
1982     HBODY next;
1983
1984     if(find_body->dwReserved == 0)
1985         find_body->dwReserved = (DWORD)HBODY_ROOT;
1986     else
1987     {
1988         hr = IMimeMessage_GetBody(msg, IBL_FIRST, (HBODY)find_body->dwReserved, &next);
1989         if(hr == S_OK)
1990             find_body->dwReserved = (DWORD)next;
1991         else
1992         {
1993             hr = IMimeMessage_GetBody(msg, IBL_NEXT, (HBODY)find_body->dwReserved, &next);
1994             if(hr == S_OK)
1995                 find_body->dwReserved = (DWORD)next;
1996             else
1997                 return MIME_E_NOT_FOUND;
1998         }
1999     }
2000
2001     hr = IMimeMessage_BindToObject(msg, (HBODY)find_body->dwReserved, &IID_IMimeBody, (void**)&mime_body);
2002     if(IMimeBody_IsContentType(mime_body, find_body->pszPriType, find_body->pszSubType) == S_OK)
2003     {
2004         IMimeBody_Release(mime_body);
2005         *out = (HBODY)find_body->dwReserved;
2006         return S_OK;
2007     }
2008     IMimeBody_Release(mime_body);
2009     return find_next(msg, find_body, out);
2010 }
2011
2012 static HRESULT WINAPI MimeMessage_FindFirst(
2013     IMimeMessage *iface,
2014     LPFINDBODY pFindBody,
2015     LPHBODY phBody)
2016 {
2017     TRACE("(%p)->(%p, %p)\n", iface, pFindBody, phBody);
2018
2019     pFindBody->dwReserved = 0;
2020     return find_next(iface, pFindBody, phBody);
2021 }
2022
2023 static HRESULT WINAPI MimeMessage_FindNext(
2024     IMimeMessage *iface,
2025     LPFINDBODY pFindBody,
2026     LPHBODY phBody)
2027 {
2028     TRACE("(%p)->(%p, %p)\n", iface, pFindBody, phBody);
2029
2030     return find_next(iface, pFindBody, phBody);
2031 }
2032
2033 static HRESULT WINAPI MimeMessage_ResolveURL(
2034     IMimeMessage *iface,
2035     HBODY hRelated,
2036     LPCSTR pszBase,
2037     LPCSTR pszURL,
2038     DWORD dwFlags,
2039     LPHBODY phBody)
2040 {
2041     FIXME("(%p)->(%p, %s, %s, 0x%x, %p)\n", iface, hRelated, pszBase, pszURL, dwFlags, phBody);
2042     return E_NOTIMPL;
2043 }
2044
2045 static HRESULT WINAPI MimeMessage_ToMultipart(
2046     IMimeMessage *iface,
2047     HBODY hBody,
2048     LPCSTR pszSubType,
2049     LPHBODY phMultipart)
2050 {
2051     FIXME("(%p)->(%p, %s, %p)\n", iface, hBody, pszSubType, phMultipart);
2052     return E_NOTIMPL;
2053 }
2054
2055 static HRESULT WINAPI MimeMessage_GetBodyOffsets(
2056     IMimeMessage *iface,
2057     HBODY hBody,
2058     LPBODYOFFSETS pOffsets)
2059 {
2060     FIXME("(%p)->(%p, %p)\n", iface, hBody, pOffsets);
2061     return E_NOTIMPL;
2062 }
2063
2064 static HRESULT WINAPI MimeMessage_GetCharset(
2065     IMimeMessage *iface,
2066     LPHCHARSET phCharset)
2067 {
2068     FIXME("(%p)->(%p)\n", iface, phCharset);
2069     *phCharset = NULL;
2070     return S_OK;
2071 }
2072
2073 static HRESULT WINAPI MimeMessage_SetCharset(
2074     IMimeMessage *iface,
2075     HCHARSET hCharset,
2076     CSETAPPLYTYPE applytype)
2077 {
2078     FIXME("(%p)->(%p, %d)\n", iface, hCharset, applytype);
2079     return E_NOTIMPL;
2080 }
2081
2082 static HRESULT WINAPI MimeMessage_IsBodyType(
2083     IMimeMessage *iface,
2084     HBODY hBody,
2085     IMSGBODYTYPE bodytype)
2086 {
2087     HRESULT hr;
2088     IMimeBody *mime_body;
2089     TRACE("(%p)->(%p, %d)\n", iface, hBody, bodytype);
2090
2091     hr = IMimeMessage_BindToObject(iface, hBody, &IID_IMimeBody, (void**)&mime_body);
2092     if(hr != S_OK) return hr;
2093
2094     hr = IMimeBody_IsType(mime_body, bodytype);
2095     MimeBody_Release(mime_body);
2096     return hr;
2097 }
2098
2099 static HRESULT WINAPI MimeMessage_IsContentType(
2100     IMimeMessage *iface,
2101     HBODY hBody,
2102     LPCSTR pszPriType,
2103     LPCSTR pszSubType)
2104 {
2105     HRESULT hr;
2106     IMimeBody *mime_body;
2107     TRACE("(%p)->(%p, %s, %s)\n", iface, hBody, pszPriType, pszSubType);
2108
2109     hr = IMimeMessage_BindToObject(iface, hBody, &IID_IMimeBody, (void**)&mime_body);
2110     if(FAILED(hr)) return hr;
2111
2112     hr = IMimeBody_IsContentType(mime_body, pszPriType, pszSubType);
2113     IMimeBody_Release(mime_body);
2114     return hr;
2115 }
2116
2117 static HRESULT WINAPI MimeMessage_QueryBodyProp(
2118     IMimeMessage *iface,
2119     HBODY hBody,
2120     LPCSTR pszName,
2121     LPCSTR pszCriteria,
2122     boolean fSubString,
2123     boolean fCaseSensitive)
2124 {
2125     FIXME("(%p)->(%p, %s, %s, %s, %s)\n", iface, hBody, pszName, pszCriteria, fSubString ? "TRUE" : "FALSE", fCaseSensitive ? "TRUE" : "FALSE");
2126     return E_NOTIMPL;
2127 }
2128
2129 static HRESULT WINAPI MimeMessage_GetBodyProp(
2130     IMimeMessage *iface,
2131     HBODY hBody,
2132     LPCSTR pszName,
2133     DWORD dwFlags,
2134     LPPROPVARIANT pValue)
2135 {
2136     HRESULT hr;
2137     IMimeBody *mime_body;
2138
2139     TRACE("(%p)->(%p, %s, 0x%x, %p)\n", iface, hBody, pszName, dwFlags, pValue);
2140
2141     hr = IMimeMessage_BindToObject(iface, hBody, &IID_IMimeBody, (void**)&mime_body);
2142     if(hr != S_OK) return hr;
2143
2144     hr = IMimeBody_GetProp(mime_body, pszName, dwFlags, pValue);
2145     IMimeBody_Release(mime_body);
2146
2147     return hr;
2148 }
2149
2150 static HRESULT WINAPI MimeMessage_SetBodyProp(
2151     IMimeMessage *iface,
2152     HBODY hBody,
2153     LPCSTR pszName,
2154     DWORD dwFlags,
2155     LPCPROPVARIANT pValue)
2156 {
2157     FIXME("(%p)->(%p, %s, 0x%x, %p)\n", iface, hBody, pszName, dwFlags, pValue);
2158     return E_NOTIMPL;
2159 }
2160
2161 static HRESULT WINAPI MimeMessage_DeleteBodyProp(
2162     IMimeMessage *iface,
2163     HBODY hBody,
2164     LPCSTR pszName)
2165 {
2166     FIXME("(%p)->(%p, %s)\n", iface, hBody, pszName);
2167     return E_NOTIMPL;
2168 }
2169
2170 static HRESULT WINAPI MimeMessage_SetOption(
2171     IMimeMessage *iface,
2172     const TYPEDID oid,
2173     LPCPROPVARIANT pValue)
2174 {
2175     FIXME("(%p)->(%08x, %p)\n", iface, oid, pValue);
2176     return E_NOTIMPL;
2177 }
2178
2179 static HRESULT WINAPI MimeMessage_GetOption(
2180     IMimeMessage *iface,
2181     const TYPEDID oid,
2182     LPPROPVARIANT pValue)
2183 {
2184     FIXME("(%p)->(%08x, %p)\n", iface, oid, pValue);
2185     return E_NOTIMPL;
2186 }
2187
2188 /*** IMimeMessage methods ***/
2189 static HRESULT WINAPI MimeMessage_CreateWebPage(
2190     IMimeMessage *iface,
2191     IStream *pRootStm,
2192     LPWEBPAGEOPTIONS pOptions,
2193     IMimeMessageCallback *pCallback,
2194     IMoniker **ppMoniker)
2195 {
2196     FIXME("(%p)->(%p, %p, %p, %p)\n", iface, pRootStm, pOptions, pCallback, ppMoniker);
2197     *ppMoniker = NULL;
2198     return E_NOTIMPL;
2199 }
2200
2201 static HRESULT WINAPI MimeMessage_GetProp(
2202     IMimeMessage *iface,
2203     LPCSTR pszName,
2204     DWORD dwFlags,
2205     LPPROPVARIANT pValue)
2206 {
2207     FIXME("(%p)->(%s, 0x%x, %p)\n", iface, pszName, dwFlags, pValue);
2208     return E_NOTIMPL;
2209 }
2210
2211 static HRESULT WINAPI MimeMessage_SetProp(
2212     IMimeMessage *iface,
2213     LPCSTR pszName,
2214     DWORD dwFlags,
2215     LPCPROPVARIANT pValue)
2216 {
2217     FIXME("(%p)->(%s, 0x%x, %p)\n", iface, pszName, dwFlags, pValue);
2218     return E_NOTIMPL;
2219 }
2220
2221 static HRESULT WINAPI MimeMessage_DeleteProp(
2222     IMimeMessage *iface,
2223     LPCSTR pszName)
2224 {
2225     FIXME("(%p)->(%s)\n", iface, pszName);
2226     return E_NOTIMPL;
2227 }
2228
2229 static HRESULT WINAPI MimeMessage_QueryProp(
2230     IMimeMessage *iface,
2231     LPCSTR pszName,
2232     LPCSTR pszCriteria,
2233     boolean fSubString,
2234     boolean fCaseSensitive)
2235 {
2236     FIXME("(%p)->(%s, %s, %s, %s)\n", iface, pszName, pszCriteria, fSubString ? "TRUE" : "FALSE", fCaseSensitive ? "TRUE" : "FALSE");
2237     return E_NOTIMPL;
2238 }
2239
2240 static HRESULT WINAPI MimeMessage_GetTextBody(
2241     IMimeMessage *iface,
2242     DWORD dwTxtType,
2243     ENCODINGTYPE ietEncoding,
2244     IStream **pStream,
2245     LPHBODY phBody)
2246 {
2247     HRESULT hr;
2248     HBODY hbody;
2249     FINDBODY find_struct;
2250     IMimeBody *mime_body;
2251     static char text[] = "text";
2252     static char plain[] = "plain";
2253     static char html[] = "html";
2254
2255     TRACE("(%p)->(%d, %d, %p, %p)\n", iface, dwTxtType, ietEncoding, pStream, phBody);
2256
2257     find_struct.pszPriType = text;
2258
2259     switch(dwTxtType)
2260     {
2261     case TXT_PLAIN:
2262         find_struct.pszSubType = plain;
2263         break;
2264     case TXT_HTML:
2265         find_struct.pszSubType = html;
2266         break;
2267     default:
2268         return MIME_E_INVALID_TEXT_TYPE;
2269     }
2270
2271     hr = IMimeMessage_FindFirst(iface, &find_struct, &hbody);
2272     if(hr != S_OK)
2273     {
2274         TRACE("not found hr %08x\n", hr);
2275         *phBody = NULL;
2276         return hr;
2277     }
2278
2279     IMimeMessage_BindToObject(iface, hbody, &IID_IMimeBody, (void**)&mime_body);
2280
2281     IMimeBody_GetData(mime_body, ietEncoding, pStream);
2282     *phBody = hbody;
2283     IMimeBody_Release(mime_body);
2284     return hr;
2285 }
2286
2287 static HRESULT WINAPI MimeMessage_SetTextBody(
2288     IMimeMessage *iface,
2289     DWORD dwTxtType,
2290     ENCODINGTYPE ietEncoding,
2291     HBODY hAlternative,
2292     IStream *pStream,
2293     LPHBODY phBody)
2294 {
2295     FIXME("(%p)->(%d, %d, %p, %p, %p)\n", iface, dwTxtType, ietEncoding, hAlternative, pStream, phBody);
2296     return E_NOTIMPL;
2297 }
2298
2299 static HRESULT WINAPI MimeMessage_AttachObject(
2300     IMimeMessage *iface,
2301     REFIID riid,
2302     void *pvObject,
2303     LPHBODY phBody)
2304 {
2305     FIXME("(%p)->(%s, %p, %p)\n", iface, debugstr_guid(riid), pvObject, phBody);
2306     return E_NOTIMPL;
2307 }
2308
2309 static HRESULT WINAPI MimeMessage_AttachFile(
2310     IMimeMessage *iface,
2311     LPCSTR pszFilePath,
2312     IStream *pstmFile,
2313     LPHBODY phBody)
2314 {
2315     FIXME("(%p)->(%s, %p, %p)\n", iface, pszFilePath, pstmFile, phBody);
2316     return E_NOTIMPL;
2317 }
2318
2319 static HRESULT WINAPI MimeMessage_AttachURL(
2320     IMimeMessage *iface,
2321     LPCSTR pszBase,
2322     LPCSTR pszURL,
2323     DWORD dwFlags,
2324     IStream *pstmURL,
2325     LPSTR *ppszCIDURL,
2326     LPHBODY phBody)
2327 {
2328     FIXME("(%p)->(%s, %s, 0x%x, %p, %p, %p)\n", iface, pszBase, pszURL, dwFlags, pstmURL, ppszCIDURL, phBody);
2329     return E_NOTIMPL;
2330 }
2331
2332 static HRESULT WINAPI MimeMessage_GetAttachments(
2333     IMimeMessage *iface,
2334     ULONG *pcAttach,
2335     LPHBODY *pprghAttach)
2336 {
2337     HRESULT hr;
2338     FINDBODY find_struct;
2339     HBODY hbody;
2340     LPHBODY array;
2341     ULONG size = 10;
2342
2343     TRACE("(%p)->(%p, %p)\n", iface, pcAttach, pprghAttach);
2344
2345     *pcAttach = 0;
2346     array = CoTaskMemAlloc(size * sizeof(HBODY));
2347
2348     find_struct.pszPriType = find_struct.pszSubType = NULL;
2349     hr = IMimeMessage_FindFirst(iface, &find_struct, &hbody);
2350     while(hr == S_OK)
2351     {
2352         hr = IMimeMessage_IsContentType(iface, hbody, "multipart", NULL);
2353         TRACE("IsCT rets %08x %d\n", hr, *pcAttach);
2354         if(hr != S_OK)
2355         {
2356             if(*pcAttach + 1 > size)
2357             {
2358                 size *= 2;
2359                 array = CoTaskMemRealloc(array, size * sizeof(HBODY));
2360             }
2361             array[*pcAttach] = hbody;
2362             (*pcAttach)++;
2363         }
2364         hr = IMimeMessage_FindNext(iface, &find_struct, &hbody);
2365     }
2366
2367     *pprghAttach = array;
2368     return S_OK;
2369 }
2370
2371 static HRESULT WINAPI MimeMessage_GetAddressTable(
2372     IMimeMessage *iface,
2373     IMimeAddressTable **ppTable)
2374 {
2375     FIXME("(%p)->(%p)\n", iface, ppTable);
2376     return E_NOTIMPL;
2377 }
2378
2379 static HRESULT WINAPI MimeMessage_GetSender(
2380     IMimeMessage *iface,
2381     LPADDRESSPROPS pAddress)
2382 {
2383     FIXME("(%p)->(%p)\n", iface, pAddress);
2384     return E_NOTIMPL;
2385 }
2386
2387 static HRESULT WINAPI MimeMessage_GetAddressTypes(
2388     IMimeMessage *iface,
2389     DWORD dwAdrTypes,
2390     DWORD dwProps,
2391     LPADDRESSLIST pList)
2392 {
2393     FIXME("(%p)->(%d, %d, %p)\n", iface, dwAdrTypes, dwProps, pList);
2394     return E_NOTIMPL;
2395 }
2396
2397 static HRESULT WINAPI MimeMessage_GetAddressFormat(
2398     IMimeMessage *iface,
2399     DWORD dwAdrTypes,
2400     ADDRESSFORMAT format,
2401     LPSTR *ppszFormat)
2402 {
2403     FIXME("(%p)->(%d, %d, %p)\n", iface, dwAdrTypes, format, ppszFormat);
2404     return E_NOTIMPL;
2405 }
2406
2407 static HRESULT WINAPI MimeMessage_EnumAddressTypes(
2408     IMimeMessage *iface,
2409     DWORD dwAdrTypes,
2410     DWORD dwProps,
2411     IMimeEnumAddressTypes **ppEnum)
2412 {
2413     FIXME("(%p)->(%d, %d, %p)\n", iface, dwAdrTypes, dwProps, ppEnum);
2414     return E_NOTIMPL;
2415 }
2416
2417 static HRESULT WINAPI MimeMessage_SplitMessage(
2418     IMimeMessage *iface,
2419     ULONG cbMaxPart,
2420     IMimeMessageParts **ppParts)
2421 {
2422     FIXME("(%p)->(%d, %p)\n", iface, cbMaxPart, ppParts);
2423     return E_NOTIMPL;
2424 }
2425
2426 static HRESULT WINAPI MimeMessage_GetRootMoniker(
2427     IMimeMessage *iface,
2428     IMoniker **ppMoniker)
2429 {
2430     FIXME("(%p)->(%p)\n", iface, ppMoniker);
2431     return E_NOTIMPL;
2432 }
2433
2434 static const IMimeMessageVtbl MimeMessageVtbl =
2435 {
2436     MimeMessage_QueryInterface,
2437     MimeMessage_AddRef,
2438     MimeMessage_Release,
2439     MimeMessage_GetClassID,
2440     MimeMessage_IsDirty,
2441     MimeMessage_Load,
2442     MimeMessage_Save,
2443     MimeMessage_GetSizeMax,
2444     MimeMessage_InitNew,
2445     MimeMessage_GetMessageSource,
2446     MimeMessage_GetMessageSize,
2447     MimeMessage_LoadOffsetTable,
2448     MimeMessage_SaveOffsetTable,
2449     MimeMessage_GetFlags,
2450     MimeMessage_Commit,
2451     MimeMessage_HandsOffStorage,
2452     MimeMessage_BindToObject,
2453     MimeMessage_SaveBody,
2454     MimeMessage_InsertBody,
2455     MimeMessage_GetBody,
2456     MimeMessage_DeleteBody,
2457     MimeMessage_MoveBody,
2458     MimeMessage_CountBodies,
2459     MimeMessage_FindFirst,
2460     MimeMessage_FindNext,
2461     MimeMessage_ResolveURL,
2462     MimeMessage_ToMultipart,
2463     MimeMessage_GetBodyOffsets,
2464     MimeMessage_GetCharset,
2465     MimeMessage_SetCharset,
2466     MimeMessage_IsBodyType,
2467     MimeMessage_IsContentType,
2468     MimeMessage_QueryBodyProp,
2469     MimeMessage_GetBodyProp,
2470     MimeMessage_SetBodyProp,
2471     MimeMessage_DeleteBodyProp,
2472     MimeMessage_SetOption,
2473     MimeMessage_GetOption,
2474     MimeMessage_CreateWebPage,
2475     MimeMessage_GetProp,
2476     MimeMessage_SetProp,
2477     MimeMessage_DeleteProp,
2478     MimeMessage_QueryProp,
2479     MimeMessage_GetTextBody,
2480     MimeMessage_SetTextBody,
2481     MimeMessage_AttachObject,
2482     MimeMessage_AttachFile,
2483     MimeMessage_AttachURL,
2484     MimeMessage_GetAttachments,
2485     MimeMessage_GetAddressTable,
2486     MimeMessage_GetSender,
2487     MimeMessage_GetAddressTypes,
2488     MimeMessage_GetAddressFormat,
2489     MimeMessage_EnumAddressTypes,
2490     MimeMessage_SplitMessage,
2491     MimeMessage_GetRootMoniker,
2492 };
2493
2494 /***********************************************************************
2495  *              MimeOleCreateMessage (INETCOMM.@)
2496  */
2497 HRESULT WINAPI MimeOleCreateMessage(IUnknown *pUnkOuter, IMimeMessage **ppMessage)
2498 {
2499     MimeMessage *This;
2500
2501     TRACE("(%p, %p)\n", pUnkOuter, ppMessage);
2502
2503     if (pUnkOuter)
2504     {
2505         FIXME("outer unknown not supported yet\n");
2506         return E_NOTIMPL;
2507     }
2508
2509     *ppMessage = NULL;
2510
2511     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
2512     if (!This) return E_OUTOFMEMORY;
2513
2514     This->lpVtbl = &MimeMessageVtbl;
2515     This->refs = 1;
2516     This->stream = NULL;
2517     list_init(&This->body_tree);
2518     This->next_hbody = (HBODY)1;
2519
2520     *ppMessage = (IMimeMessage *)&This->lpVtbl;
2521     return S_OK;
2522 }
2523
2524 /***********************************************************************
2525  *              MimeOleSetCompatMode (INETCOMM.@)
2526  */
2527 HRESULT WINAPI MimeOleSetCompatMode(DWORD dwMode)
2528 {
2529     FIXME("(0x%x)\n", dwMode);
2530     return S_OK;
2531 }
2532
2533 /***********************************************************************
2534  *              MimeOleCreateVirtualStream (INETCOMM.@)
2535  */
2536 HRESULT WINAPI MimeOleCreateVirtualStream(IStream **ppStream)
2537 {
2538     HRESULT hr;
2539     FIXME("(%p)\n", ppStream);
2540
2541     hr = CreateStreamOnHGlobal(NULL, TRUE, ppStream);
2542     return hr;
2543 }
2544
2545 typedef struct MimeSecurity
2546 {
2547     const IMimeSecurityVtbl *lpVtbl;
2548
2549     LONG refs;
2550 } MimeSecurity;
2551
2552 static HRESULT WINAPI MimeSecurity_QueryInterface(
2553         IMimeSecurity* iface,
2554         REFIID riid,
2555         void** obj)
2556 {
2557     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), obj);
2558
2559     if (IsEqualIID(riid, &IID_IUnknown) ||
2560         IsEqualIID(riid, &IID_IMimeSecurity))
2561     {
2562         *obj = iface;
2563         IUnknown_AddRef(iface);
2564         return S_OK;
2565     }
2566
2567     FIXME("no interface for %s\n", debugstr_guid(riid));
2568     *obj = NULL;
2569     return E_NOINTERFACE;
2570 }
2571
2572 static ULONG WINAPI MimeSecurity_AddRef(
2573         IMimeSecurity* iface)
2574 {
2575     MimeSecurity *This = (MimeSecurity *)iface;
2576     TRACE("(%p)->()\n", iface);
2577     return InterlockedIncrement(&This->refs);
2578 }
2579
2580 static ULONG WINAPI MimeSecurity_Release(
2581         IMimeSecurity* iface)
2582 {
2583     MimeSecurity *This = (MimeSecurity *)iface;
2584     ULONG refs;
2585
2586     TRACE("(%p)->()\n", iface);
2587
2588     refs = InterlockedDecrement(&This->refs);
2589     if (!refs)
2590     {
2591         HeapFree(GetProcessHeap(), 0, This);
2592     }
2593
2594     return refs;
2595 }
2596
2597 static HRESULT WINAPI MimeSecurity_InitNew(
2598         IMimeSecurity* iface)
2599 {
2600     FIXME("(%p)->(): stub\n", iface);
2601     return S_OK;
2602 }
2603
2604 static HRESULT WINAPI MimeSecurity_CheckInit(
2605         IMimeSecurity* iface)
2606 {
2607     FIXME("(%p)->(): stub\n", iface);
2608     return E_NOTIMPL;
2609 }
2610
2611 static HRESULT WINAPI MimeSecurity_EncodeMessage(
2612         IMimeSecurity* iface,
2613         IMimeMessageTree* pTree,
2614         DWORD dwFlags)
2615 {
2616     FIXME("(%p)->(%p, %08x): stub\n", iface, pTree, dwFlags);
2617     return E_NOTIMPL;
2618 }
2619
2620 static HRESULT WINAPI MimeSecurity_EncodeBody(
2621         IMimeSecurity* iface,
2622         IMimeMessageTree* pTree,
2623         HBODY hEncodeRoot,
2624         DWORD dwFlags)
2625 {
2626     FIXME("(%p)->(%p, %p, %08x): stub\n", iface, pTree, hEncodeRoot, dwFlags);
2627     return E_NOTIMPL;
2628 }
2629
2630 static HRESULT WINAPI MimeSecurity_DecodeMessage(
2631         IMimeSecurity* iface,
2632         IMimeMessageTree* pTree,
2633         DWORD dwFlags)
2634 {
2635     FIXME("(%p)->(%p, %08x): stub\n", iface, pTree, dwFlags);
2636     return E_NOTIMPL;
2637 }
2638
2639 static HRESULT WINAPI MimeSecurity_DecodeBody(
2640         IMimeSecurity* iface,
2641         IMimeMessageTree* pTree,
2642         HBODY hDecodeRoot,
2643         DWORD dwFlags)
2644 {
2645     FIXME("(%p)->(%p, %p, %08x): stub\n", iface, pTree, hDecodeRoot, dwFlags);
2646     return E_NOTIMPL;
2647 }
2648
2649 static HRESULT WINAPI MimeSecurity_EnumCertificates(
2650         IMimeSecurity* iface,
2651         HCAPICERTSTORE hc,
2652         DWORD dwUsage,
2653         PCX509CERT pPrev,
2654         PCX509CERT* ppCert)
2655 {
2656     FIXME("(%p)->(%p, %08x, %p, %p): stub\n", iface, hc, dwUsage, pPrev, ppCert);
2657     return E_NOTIMPL;
2658 }
2659
2660 static HRESULT WINAPI MimeSecurity_GetCertificateName(
2661         IMimeSecurity* iface,
2662         const PCX509CERT pX509Cert,
2663         const CERTNAMETYPE cn,
2664         LPSTR* ppszName)
2665 {
2666     FIXME("(%p)->(%p, %08x, %p): stub\n", iface, pX509Cert, cn, ppszName);
2667     return E_NOTIMPL;
2668 }
2669
2670 static HRESULT WINAPI MimeSecurity_GetMessageType(
2671         IMimeSecurity* iface,
2672         const HWND hwndParent,
2673         IMimeBody* pBody,
2674         DWORD* pdwSecType)
2675 {
2676     FIXME("(%p)->(%p, %p, %p): stub\n", iface, hwndParent, pBody, pdwSecType);
2677     return E_NOTIMPL;
2678 }
2679
2680 static HRESULT WINAPI MimeSecurity_GetCertData(
2681         IMimeSecurity* iface,
2682         const PCX509CERT pX509Cert,
2683         const CERTDATAID dataid,
2684         LPPROPVARIANT pValue)
2685 {
2686     FIXME("(%p)->(%p, %x, %p): stub\n", iface, pX509Cert, dataid, pValue);
2687     return E_NOTIMPL;
2688 }
2689
2690
2691 static const IMimeSecurityVtbl MimeSecurityVtbl =
2692 {
2693     MimeSecurity_QueryInterface,
2694     MimeSecurity_AddRef,
2695     MimeSecurity_Release,
2696     MimeSecurity_InitNew,
2697     MimeSecurity_CheckInit,
2698     MimeSecurity_EncodeMessage,
2699     MimeSecurity_EncodeBody,
2700     MimeSecurity_DecodeMessage,
2701     MimeSecurity_DecodeBody,
2702     MimeSecurity_EnumCertificates,
2703     MimeSecurity_GetCertificateName,
2704     MimeSecurity_GetMessageType,
2705     MimeSecurity_GetCertData
2706 };
2707
2708 /***********************************************************************
2709  *              MimeOleCreateSecurity (INETCOMM.@)
2710  */
2711 HRESULT WINAPI MimeOleCreateSecurity(IMimeSecurity **ppSecurity)
2712 {
2713     MimeSecurity *This;
2714
2715     TRACE("(%p)\n", ppSecurity);
2716
2717     *ppSecurity = NULL;
2718
2719     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
2720     if (!This) return E_OUTOFMEMORY;
2721
2722     This->lpVtbl = &MimeSecurityVtbl;
2723     This->refs = 1;
2724
2725     *ppSecurity = (IMimeSecurity *)&This->lpVtbl;
2726     return S_OK;
2727 }
2728
2729
2730 typedef struct
2731 {
2732     IMimeAllocatorVtbl *lpVtbl;
2733 } MimeAllocator;
2734
2735 static HRESULT WINAPI MimeAlloc_QueryInterface(
2736         IMimeAllocator* iface,
2737         REFIID riid,
2738         void **obj)
2739 {
2740     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), obj);
2741
2742     if (IsEqualIID(riid, &IID_IUnknown) ||
2743         IsEqualIID(riid, &IID_IMalloc) ||
2744         IsEqualIID(riid, &IID_IMimeAllocator))
2745     {
2746         *obj = iface;
2747         IUnknown_AddRef(iface);
2748         return S_OK;
2749     }
2750
2751     FIXME("no interface for %s\n", debugstr_guid(riid));
2752     *obj = NULL;
2753     return E_NOINTERFACE;
2754 }
2755
2756 static ULONG WINAPI MimeAlloc_AddRef(
2757         IMimeAllocator* iface)
2758 {
2759     return 2;
2760 }
2761
2762 static ULONG WINAPI MimeAlloc_Release(
2763         IMimeAllocator* iface)
2764 {
2765     return 1;
2766 }
2767
2768 static LPVOID WINAPI MimeAlloc_Alloc(
2769         IMimeAllocator* iface,
2770         ULONG cb)
2771 {
2772     return CoTaskMemAlloc(cb);
2773 }
2774
2775 static LPVOID WINAPI MimeAlloc_Realloc(
2776         IMimeAllocator* iface,
2777         LPVOID pv,
2778         ULONG cb)
2779 {
2780     return CoTaskMemRealloc(pv, cb);
2781 }
2782
2783 static void WINAPI MimeAlloc_Free(
2784         IMimeAllocator* iface,
2785         LPVOID pv)
2786 {
2787     CoTaskMemFree(pv);
2788 }
2789
2790 static ULONG WINAPI MimeAlloc_GetSize(
2791         IMimeAllocator* iface,
2792         LPVOID pv)
2793 {
2794     FIXME("stub\n");
2795     return 0;
2796 }
2797
2798 static int WINAPI MimeAlloc_DidAlloc(
2799         IMimeAllocator* iface,
2800         LPVOID pv)
2801 {
2802     FIXME("stub\n");
2803     return 0;
2804 }
2805
2806 static void WINAPI MimeAlloc_HeapMinimize(
2807         IMimeAllocator* iface)
2808 {
2809     FIXME("stub\n");
2810     return;
2811 }
2812
2813 static HRESULT WINAPI MimeAlloc_FreeParamInfoArray(
2814         IMimeAllocator* iface,
2815         ULONG cParams,
2816         LPMIMEPARAMINFO prgParam,
2817         boolean fFreeArray)
2818 {
2819     ULONG i;
2820     TRACE("(%p)->(%d, %p, %d)\n", iface, cParams, prgParam, fFreeArray);
2821
2822     for(i = 0; i < cParams; i++)
2823     {
2824         IMimeAllocator_Free(iface, prgParam[i].pszName);
2825         IMimeAllocator_Free(iface, prgParam[i].pszData);
2826     }
2827     if(fFreeArray) IMimeAllocator_Free(iface, prgParam);
2828     return S_OK;
2829 }
2830
2831 static HRESULT WINAPI MimeAlloc_FreeAddressList(
2832         IMimeAllocator* iface,
2833         LPADDRESSLIST pList)
2834 {
2835     FIXME("stub\n");
2836     return E_NOTIMPL;
2837 }
2838
2839 static HRESULT WINAPI MimeAlloc_FreeAddressProps(
2840         IMimeAllocator* iface,
2841         LPADDRESSPROPS pAddress)
2842 {
2843     FIXME("stub\n");
2844     return E_NOTIMPL;
2845 }
2846
2847 static HRESULT WINAPI MimeAlloc_ReleaseObjects(
2848         IMimeAllocator* iface,
2849         ULONG cObjects,
2850         IUnknown **prgpUnknown,
2851         boolean fFreeArray)
2852 {
2853     FIXME("stub\n");
2854     return E_NOTIMPL;
2855 }
2856
2857
2858 static HRESULT WINAPI MimeAlloc_FreeEnumHeaderRowArray(
2859         IMimeAllocator* iface,
2860         ULONG cRows,
2861         LPENUMHEADERROW prgRow,
2862         boolean fFreeArray)
2863 {
2864     FIXME("stub\n");
2865     return E_NOTIMPL;
2866 }
2867
2868 static HRESULT WINAPI MimeAlloc_FreeEnumPropertyArray(
2869         IMimeAllocator* iface,
2870         ULONG cProps,
2871         LPENUMPROPERTY prgProp,
2872         boolean fFreeArray)
2873 {
2874     FIXME("stub\n");
2875     return E_NOTIMPL;
2876 }
2877
2878 static HRESULT WINAPI MimeAlloc_FreeThumbprint(
2879         IMimeAllocator* iface,
2880         THUMBBLOB *pthumbprint)
2881 {
2882     FIXME("stub\n");
2883     return E_NOTIMPL;
2884 }
2885
2886
2887 static HRESULT WINAPI MimeAlloc_PropVariantClear(
2888         IMimeAllocator* iface,
2889         LPPROPVARIANT pProp)
2890 {
2891     FIXME("stub\n");
2892     return E_NOTIMPL;
2893 }
2894
2895 static IMimeAllocatorVtbl mime_alloc_vtbl =
2896 {
2897     MimeAlloc_QueryInterface,
2898     MimeAlloc_AddRef,
2899     MimeAlloc_Release,
2900     MimeAlloc_Alloc,
2901     MimeAlloc_Realloc,
2902     MimeAlloc_Free,
2903     MimeAlloc_GetSize,
2904     MimeAlloc_DidAlloc,
2905     MimeAlloc_HeapMinimize,
2906     MimeAlloc_FreeParamInfoArray,
2907     MimeAlloc_FreeAddressList,
2908     MimeAlloc_FreeAddressProps,
2909     MimeAlloc_ReleaseObjects,
2910     MimeAlloc_FreeEnumHeaderRowArray,
2911     MimeAlloc_FreeEnumPropertyArray,
2912     MimeAlloc_FreeThumbprint,
2913     MimeAlloc_PropVariantClear
2914 };
2915
2916 static MimeAllocator mime_allocator =
2917 {
2918     &mime_alloc_vtbl
2919 };
2920
2921 HRESULT MimeAllocator_create(IUnknown *outer, void **obj)
2922 {
2923     if(outer) return CLASS_E_NOAGGREGATION;
2924
2925     *obj = &mime_allocator;
2926     return S_OK;
2927 }
2928
2929 HRESULT WINAPI MimeOleGetAllocator(IMimeAllocator **alloc)
2930 {
2931     return MimeAllocator_create(NULL, (void**)alloc);
2932 }
2933
2934 HRESULT WINAPI MimeOleGetCharsetInfo(HCHARSET hCharset, LPINETCSETINFO pCsetInfo)
2935 {
2936     FIXME("(%p, %p)\n", hCharset, pCsetInfo);
2937     if(!hCharset) return E_INVALIDARG;
2938     return E_FAIL;
2939 }