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