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