advapi32/tests: Restore default cryptographic provider after the tests.
[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     HBODY hbody;
1423     IMimeBody *mime_body;
1424
1425     struct body_t *parent;
1426     struct list children;
1427 } body_t;
1428
1429 typedef struct MimeMessage
1430 {
1431     const IMimeMessageVtbl *lpVtbl;
1432
1433     LONG refs;
1434     IStream *stream;
1435
1436     struct list body_tree;
1437     HBODY next_hbody;
1438 } MimeMessage;
1439
1440 static HRESULT WINAPI MimeMessage_QueryInterface(IMimeMessage *iface, REFIID riid, void **ppv)
1441 {
1442     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1443
1444     if (IsEqualIID(riid, &IID_IUnknown) ||
1445         IsEqualIID(riid, &IID_IPersist) ||
1446         IsEqualIID(riid, &IID_IPersistStreamInit) ||
1447         IsEqualIID(riid, &IID_IMimeMessageTree) ||
1448         IsEqualIID(riid, &IID_IMimeMessage))
1449     {
1450         *ppv = iface;
1451         IUnknown_AddRef(iface);
1452         return S_OK;
1453     }
1454
1455     FIXME("no interface for %s\n", debugstr_guid(riid));
1456     *ppv = NULL;
1457     return E_NOINTERFACE;
1458 }
1459
1460 static ULONG WINAPI MimeMessage_AddRef(IMimeMessage *iface)
1461 {
1462     MimeMessage *This = (MimeMessage *)iface;
1463     TRACE("(%p)->()\n", iface);
1464     return InterlockedIncrement(&This->refs);
1465 }
1466
1467 static void empty_body_list(struct list *list)
1468 {
1469     body_t *body, *cursor2;
1470     LIST_FOR_EACH_ENTRY_SAFE(body, cursor2, list, body_t, entry)
1471     {
1472         empty_body_list(&body->children);
1473         list_remove(&body->entry);
1474         IMimeBody_Release(body->mime_body);
1475         HeapFree(GetProcessHeap(), 0, body);
1476     }
1477 }
1478
1479 static ULONG WINAPI MimeMessage_Release(IMimeMessage *iface)
1480 {
1481     MimeMessage *This = (MimeMessage *)iface;
1482     ULONG refs;
1483
1484     TRACE("(%p)->()\n", iface);
1485
1486     refs = InterlockedDecrement(&This->refs);
1487     if (!refs)
1488     {
1489         empty_body_list(&This->body_tree);
1490
1491         if(This->stream) IStream_Release(This->stream);
1492         HeapFree(GetProcessHeap(), 0, This);
1493     }
1494
1495     return refs;
1496 }
1497
1498 /*** IPersist methods ***/
1499 static HRESULT WINAPI MimeMessage_GetClassID(
1500     IMimeMessage *iface,
1501     CLSID *pClassID)
1502 {
1503     FIXME("(%p)->(%p)\n", iface, pClassID);
1504     return E_NOTIMPL;
1505 }
1506
1507 /*** IPersistStreamInit methods ***/
1508 static HRESULT WINAPI MimeMessage_IsDirty(
1509     IMimeMessage *iface)
1510 {
1511     FIXME("(%p)->()\n", iface);
1512     return E_NOTIMPL;
1513 }
1514
1515 static body_t *new_body_entry(IMimeBody *mime_body, HBODY hbody, body_t *parent)
1516 {
1517     body_t *body = HeapAlloc(GetProcessHeap(), 0, sizeof(*body));
1518     if(body)
1519     {
1520         body->mime_body = mime_body;
1521         body->hbody = hbody;
1522         list_init(&body->children);
1523         body->parent = parent;
1524     }
1525     return body;
1526 }
1527
1528 typedef struct
1529 {
1530     struct list entry;
1531     BODYOFFSETS offsets;
1532 } offset_entry_t;
1533
1534 static HRESULT create_body_offset_list(IStream *stm, const char *boundary, struct list *body_offsets)
1535 {
1536     HRESULT hr;
1537     DWORD read;
1538     int boundary_len = strlen(boundary);
1539     char *buf, *nl_boundary, *ptr, *overlap;
1540     DWORD start = 0, overlap_no;
1541     offset_entry_t *cur_body = NULL;
1542     ULARGE_INTEGER cur;
1543     LARGE_INTEGER zero;
1544
1545     list_init(body_offsets);
1546     nl_boundary = HeapAlloc(GetProcessHeap(), 0, 4 + boundary_len + 1);
1547     memcpy(nl_boundary, "\r\n--", 4);
1548     memcpy(nl_boundary + 4, boundary, boundary_len + 1);
1549
1550     overlap_no = boundary_len + 5;
1551
1552     overlap = buf = HeapAlloc(GetProcessHeap(), 0, overlap_no + PARSER_BUF_SIZE + 1);
1553
1554     zero.QuadPart = 0;
1555     hr = IStream_Seek(stm, zero, STREAM_SEEK_CUR, &cur);
1556     start = cur.u.LowPart;
1557
1558     do {
1559         hr = IStream_Read(stm, overlap, PARSER_BUF_SIZE, &read);
1560         if(FAILED(hr)) goto end;
1561         if(read == 0) break;
1562         overlap[read] = '\0';
1563
1564         ptr = buf;
1565         do {
1566             ptr = strstr(ptr, nl_boundary);
1567             if(ptr)
1568             {
1569                 DWORD boundary_start = start + ptr - buf;
1570                 char *end = ptr + boundary_len + 4;
1571
1572                 if(*end == '\0' || *(end + 1) == '\0')
1573                     break;
1574
1575                 if(*end == '\r' && *(end + 1) == '\n')
1576                 {
1577                     if(cur_body)
1578                     {
1579                         cur_body->offsets.cbBodyEnd = boundary_start;
1580                         list_add_tail(body_offsets, &cur_body->entry);
1581                     }
1582                     cur_body = HeapAlloc(GetProcessHeap(), 0, sizeof(*cur_body));
1583                     cur_body->offsets.cbBoundaryStart = boundary_start + 2; /* doesn't including the leading \r\n */
1584                     cur_body->offsets.cbHeaderStart = boundary_start + boundary_len + 6;
1585                 }
1586                 else if(*end == '-' && *(end + 1) == '-')
1587                 {
1588                     if(cur_body)
1589                     {
1590                         cur_body->offsets.cbBodyEnd = boundary_start;
1591                         list_add_tail(body_offsets, &cur_body->entry);
1592                         goto end;
1593                     }
1594                 }
1595                 ptr = end + 2;
1596             }
1597         } while(ptr);
1598
1599         if(overlap == buf) /* 1st iteration */
1600         {
1601             memcpy(buf, buf + PARSER_BUF_SIZE - overlap_no, overlap_no);
1602             overlap = buf + overlap_no;
1603             start += read - overlap_no;
1604         }
1605         else
1606         {
1607             memcpy(buf, buf + PARSER_BUF_SIZE, overlap_no);
1608             start += read;
1609         }
1610     } while(1);
1611
1612 end:
1613     HeapFree(GetProcessHeap(), 0, nl_boundary);
1614     HeapFree(GetProcessHeap(), 0, buf);
1615     return hr;
1616 }
1617
1618 static body_t *create_sub_body(MimeMessage *msg, IStream *pStm, BODYOFFSETS *offset, body_t *parent)
1619 {
1620     IMimeBody *mime_body;
1621     HRESULT hr;
1622     body_t *body;
1623     ULARGE_INTEGER cur;
1624     LARGE_INTEGER zero;
1625
1626     MimeBody_create(NULL, (void**)&mime_body);
1627     IMimeBody_Load(mime_body, pStm);
1628     zero.QuadPart = 0;
1629     hr = IStream_Seek(pStm, zero, STREAM_SEEK_CUR, &cur);
1630     offset->cbBodyStart = cur.u.LowPart + offset->cbHeaderStart;
1631     if(parent) MimeBody_set_offsets(impl_from_IMimeBody(mime_body), offset);
1632     IMimeBody_SetData(mime_body, IET_BINARY, NULL, NULL, &IID_IStream, pStm);
1633     body = new_body_entry(mime_body, msg->next_hbody, parent);
1634     msg->next_hbody = (HBODY)((DWORD)msg->next_hbody + 1);
1635
1636     if(IMimeBody_IsContentType(mime_body, "multipart", NULL) == S_OK)
1637     {
1638         MIMEPARAMINFO *param_info;
1639         ULONG count, i;
1640         IMimeAllocator *alloc;
1641
1642         hr = IMimeBody_GetParameters(mime_body, "Content-Type", &count, &param_info);
1643         if(hr != S_OK || count == 0) return body;
1644
1645         MimeOleGetAllocator(&alloc);
1646
1647         for(i = 0; i < count; i++)
1648         {
1649             if(!strcasecmp(param_info[i].pszName, "boundary"))
1650             {
1651                 struct list offset_list;
1652                 offset_entry_t *cur, *cursor2;
1653                 hr = create_body_offset_list(pStm, param_info[i].pszData, &offset_list);
1654                 LIST_FOR_EACH_ENTRY_SAFE(cur, cursor2, &offset_list, offset_entry_t, entry)
1655                 {
1656                     body_t *sub_body;
1657                     IStream *sub_stream;
1658                     ULARGE_INTEGER start, length;
1659
1660                     start.QuadPart = cur->offsets.cbHeaderStart;
1661                     length.QuadPart = cur->offsets.cbBodyEnd - cur->offsets.cbHeaderStart;
1662                     create_sub_stream(pStm, start, length, &sub_stream);
1663                     sub_body = create_sub_body(msg, sub_stream, &cur->offsets, body);
1664                     IStream_Release(sub_stream);
1665                     list_add_tail(&body->children, &sub_body->entry);
1666                     list_remove(&cur->entry);
1667                     HeapFree(GetProcessHeap(), 0, cur);
1668                 }
1669                 break;
1670             }
1671         }
1672         IMimeAllocator_FreeParamInfoArray(alloc, count, param_info, TRUE);
1673         IMimeAllocator_Release(alloc);
1674     }
1675     return body;
1676 }
1677
1678 static HRESULT WINAPI MimeMessage_Load(
1679     IMimeMessage *iface,
1680     LPSTREAM pStm)
1681 {
1682     MimeMessage *This = (MimeMessage *)iface;
1683     body_t *root_body;
1684     BODYOFFSETS offsets;
1685     ULARGE_INTEGER cur;
1686     LARGE_INTEGER zero;
1687
1688     TRACE("(%p)->(%p)\n", iface, pStm);
1689
1690     if(This->stream)
1691     {
1692         FIXME("already loaded a message\n");
1693         return E_FAIL;
1694     }
1695
1696     IStream_AddRef(pStm);
1697     This->stream = pStm;
1698     offsets.cbBoundaryStart = offsets.cbHeaderStart = 0;
1699     offsets.cbBodyStart = offsets.cbBodyEnd = 0;
1700
1701     root_body = create_sub_body(This, pStm, &offsets, NULL);
1702
1703     zero.QuadPart = 0;
1704     IStream_Seek(pStm, zero, STREAM_SEEK_END, &cur);
1705     offsets.cbBodyEnd = cur.u.LowPart;
1706     MimeBody_set_offsets(impl_from_IMimeBody(root_body->mime_body), &offsets);
1707
1708     list_add_head(&This->body_tree, &root_body->entry);
1709
1710     return S_OK;
1711 }
1712
1713 static HRESULT WINAPI MimeMessage_Save(
1714     IMimeMessage *iface,
1715     LPSTREAM pStm,
1716     BOOL fClearDirty)
1717 {
1718     FIXME("(%p)->(%p, %s)\n", iface, pStm, fClearDirty ? "TRUE" : "FALSE");
1719     return E_NOTIMPL;
1720 }
1721
1722 static HRESULT WINAPI MimeMessage_GetSizeMax(
1723     IMimeMessage *iface,
1724     ULARGE_INTEGER *pcbSize)
1725 {
1726     FIXME("(%p)->(%p)\n", iface, pcbSize);
1727     return E_NOTIMPL;
1728 }
1729
1730 static HRESULT WINAPI MimeMessage_InitNew(
1731     IMimeMessage *iface)
1732 {
1733     FIXME("(%p)->()\n", iface);
1734     return E_NOTIMPL;
1735 }
1736
1737 /*** IMimeMessageTree methods ***/
1738 static HRESULT WINAPI MimeMessage_GetMessageSource(
1739     IMimeMessage *iface,
1740     IStream **ppStream,
1741     DWORD dwFlags)
1742 {
1743     MimeMessage *This = (MimeMessage *)iface;
1744     FIXME("(%p)->(%p, 0x%x)\n", iface, ppStream, dwFlags);
1745
1746     IStream_AddRef(This->stream);
1747     *ppStream = This->stream;
1748     return S_OK;
1749 }
1750
1751 static HRESULT WINAPI MimeMessage_GetMessageSize(
1752     IMimeMessage *iface,
1753     ULONG *pcbSize,
1754     DWORD dwFlags)
1755 {
1756     FIXME("(%p)->(%p, 0x%x)\n", iface, pcbSize, dwFlags);
1757     return E_NOTIMPL;
1758 }
1759
1760 static HRESULT WINAPI MimeMessage_LoadOffsetTable(
1761     IMimeMessage *iface,
1762     IStream *pStream)
1763 {
1764     FIXME("(%p)->(%p)\n", iface, pStream);
1765     return E_NOTIMPL;
1766 }
1767
1768 static HRESULT WINAPI MimeMessage_SaveOffsetTable(
1769     IMimeMessage *iface,
1770     IStream *pStream,
1771     DWORD dwFlags)
1772 {
1773     FIXME("(%p)->(%p, 0x%x)\n", iface, pStream, dwFlags);
1774     return E_NOTIMPL;
1775 }
1776
1777
1778 static HRESULT WINAPI MimeMessage_GetFlags(
1779     IMimeMessage *iface,
1780     DWORD *pdwFlags)
1781 {
1782     FIXME("(%p)->(%p)\n", iface, pdwFlags);
1783     return E_NOTIMPL;
1784 }
1785
1786 static HRESULT WINAPI MimeMessage_Commit(
1787     IMimeMessage *iface,
1788     DWORD dwFlags)
1789 {
1790     FIXME("(%p)->(0x%x)\n", iface, dwFlags);
1791     return E_NOTIMPL;
1792 }
1793
1794
1795 static HRESULT WINAPI MimeMessage_HandsOffStorage(
1796     IMimeMessage *iface)
1797 {
1798     FIXME("(%p)->()\n", iface);
1799     return E_NOTIMPL;
1800 }
1801
1802 static HRESULT find_body(struct list *list, HBODY hbody, body_t **body)
1803 {
1804     body_t *cur;
1805     HRESULT hr;
1806
1807     if(hbody == HBODY_ROOT)
1808     {
1809         *body = LIST_ENTRY(list_head(list), body_t, entry);
1810         return S_OK;
1811     }
1812
1813     LIST_FOR_EACH_ENTRY(cur, list, body_t, entry)
1814     {
1815         if(cur->hbody == hbody)
1816         {
1817             *body = cur;
1818             return S_OK;
1819         }
1820         hr = find_body(&cur->children, hbody, body);
1821         if(hr == S_OK) return S_OK;
1822     }
1823     return S_FALSE;
1824 }
1825
1826 static HRESULT WINAPI MimeMessage_BindToObject(
1827     IMimeMessage *iface,
1828     const HBODY hBody,
1829     REFIID riid,
1830     void **ppvObject)
1831 {
1832     MimeMessage *This = (MimeMessage *)iface;
1833     HRESULT hr;
1834     body_t *body;
1835
1836     TRACE("(%p)->(%p, %s, %p)\n", iface, hBody, debugstr_guid(riid), ppvObject);
1837
1838     hr = find_body(&This->body_tree, hBody, &body);
1839
1840     if(hr != S_OK) return hr;
1841
1842     if(IsEqualIID(riid, &IID_IMimeBody))
1843     {
1844         IMimeBody_AddRef(body->mime_body);
1845         *ppvObject = body->mime_body;
1846         return S_OK;
1847     }
1848
1849     return E_NOINTERFACE;
1850 }
1851
1852 static HRESULT WINAPI MimeMessage_SaveBody(
1853     IMimeMessage *iface,
1854     HBODY hBody,
1855     DWORD dwFlags,
1856     IStream *pStream)
1857 {
1858     FIXME("(%p)->(%p, 0x%x, %p)\n", iface, hBody, dwFlags, pStream);
1859     return E_NOTIMPL;
1860 }
1861
1862 static HRESULT get_body(MimeMessage *msg, BODYLOCATION location, HBODY pivot, body_t **out)
1863 {
1864     body_t *root = LIST_ENTRY(list_head(&msg->body_tree), body_t, entry);
1865     body_t *body;
1866     HRESULT hr;
1867     struct list *list;
1868
1869     if(location == IBL_ROOT)
1870     {
1871         *out = root;
1872         return S_OK;
1873     }
1874
1875     hr = find_body(&msg->body_tree, pivot, &body);
1876
1877     if(hr == S_OK)
1878     {
1879         switch(location)
1880         {
1881         case IBL_PARENT:
1882             *out = body->parent;
1883             break;
1884
1885         case IBL_FIRST:
1886             list = list_head(&body->children);
1887             if(list)
1888                 *out = LIST_ENTRY(list, body_t, entry);
1889             else
1890                 hr = MIME_E_NOT_FOUND;
1891             break;
1892
1893         case IBL_LAST:
1894             list = list_tail(&body->children);
1895             if(list)
1896                 *out = LIST_ENTRY(list, body_t, entry);
1897             else
1898                 hr = MIME_E_NOT_FOUND;
1899             break;
1900
1901         case IBL_NEXT:
1902             list = list_next(&body->parent->children, &body->entry);
1903             if(list)
1904                 *out = LIST_ENTRY(list, body_t, entry);
1905             else
1906                 hr = MIME_E_NOT_FOUND;
1907             break;
1908
1909         case IBL_PREVIOUS:
1910             list = list_prev(&body->parent->children, &body->entry);
1911             if(list)
1912                 *out = LIST_ENTRY(list, body_t, entry);
1913             else
1914                 hr = MIME_E_NOT_FOUND;
1915             break;
1916
1917         default:
1918             hr = E_FAIL;
1919             break;
1920         }
1921     }
1922
1923     return hr;
1924 }
1925
1926
1927 static HRESULT WINAPI MimeMessage_InsertBody(
1928     IMimeMessage *iface,
1929     BODYLOCATION location,
1930     HBODY hPivot,
1931     LPHBODY phBody)
1932 {
1933     FIXME("(%p)->(%d, %p, %p)\n", iface, location, hPivot, phBody);
1934     return E_NOTIMPL;
1935 }
1936
1937 static HRESULT WINAPI MimeMessage_GetBody(
1938     IMimeMessage *iface,
1939     BODYLOCATION location,
1940     HBODY hPivot,
1941     LPHBODY phBody)
1942 {
1943     MimeMessage *This = (MimeMessage *)iface;
1944     body_t *body;
1945     HRESULT hr;
1946
1947     TRACE("(%p)->(%d, %p, %p)\n", iface, location, hPivot, phBody);
1948
1949     hr = get_body(This, location, hPivot, &body);
1950
1951     if(hr == S_OK) *phBody = body->hbody;
1952
1953     return hr;
1954 }
1955
1956 static HRESULT WINAPI MimeMessage_DeleteBody(
1957     IMimeMessage *iface,
1958     HBODY hBody,
1959     DWORD dwFlags)
1960 {
1961     FIXME("(%p)->(%p, %08x)\n", iface, hBody, dwFlags);
1962     return E_NOTIMPL;
1963 }
1964
1965 static HRESULT WINAPI MimeMessage_MoveBody(
1966     IMimeMessage *iface,
1967     HBODY hBody,
1968     BODYLOCATION location)
1969 {
1970     FIXME("(%p)->(%d)\n", iface, location);
1971     return E_NOTIMPL;
1972 }
1973
1974 static void count_children(body_t *body, boolean recurse, ULONG *count)
1975 {
1976     body_t *child;
1977
1978     LIST_FOR_EACH_ENTRY(child, &body->children, body_t, entry)
1979     {
1980         (*count)++;
1981         if(recurse) count_children(child, recurse, count);
1982     }
1983 }
1984
1985 static HRESULT WINAPI MimeMessage_CountBodies(
1986     IMimeMessage *iface,
1987     HBODY hParent,
1988     boolean fRecurse,
1989     ULONG *pcBodies)
1990 {
1991     HRESULT hr;
1992     MimeMessage *This = (MimeMessage *)iface;
1993     body_t *body;
1994
1995     TRACE("(%p)->(%p, %s, %p)\n", iface, hParent, fRecurse ? "TRUE" : "FALSE", pcBodies);
1996
1997     hr = find_body(&This->body_tree, hParent, &body);
1998     if(hr != S_OK) return hr;
1999
2000     *pcBodies = 1;
2001     count_children(body, fRecurse, pcBodies);
2002
2003     return S_OK;
2004 }
2005
2006 static HRESULT find_next(IMimeMessage *msg, LPFINDBODY find_body, HBODY *out)
2007 {
2008     HRESULT hr;
2009     IMimeBody *mime_body;
2010     HBODY next;
2011
2012     if(find_body->dwReserved == 0)
2013         find_body->dwReserved = (DWORD)HBODY_ROOT;
2014     else
2015     {
2016         hr = IMimeMessage_GetBody(msg, IBL_FIRST, (HBODY)find_body->dwReserved, &next);
2017         if(hr == S_OK)
2018             find_body->dwReserved = (DWORD)next;
2019         else
2020         {
2021             hr = IMimeMessage_GetBody(msg, IBL_NEXT, (HBODY)find_body->dwReserved, &next);
2022             if(hr == S_OK)
2023                 find_body->dwReserved = (DWORD)next;
2024             else
2025                 return MIME_E_NOT_FOUND;
2026         }
2027     }
2028
2029     hr = IMimeMessage_BindToObject(msg, (HBODY)find_body->dwReserved, &IID_IMimeBody, (void**)&mime_body);
2030     if(IMimeBody_IsContentType(mime_body, find_body->pszPriType, find_body->pszSubType) == S_OK)
2031     {
2032         IMimeBody_Release(mime_body);
2033         *out = (HBODY)find_body->dwReserved;
2034         return S_OK;
2035     }
2036     IMimeBody_Release(mime_body);
2037     return find_next(msg, find_body, out);
2038 }
2039
2040 static HRESULT WINAPI MimeMessage_FindFirst(
2041     IMimeMessage *iface,
2042     LPFINDBODY pFindBody,
2043     LPHBODY phBody)
2044 {
2045     TRACE("(%p)->(%p, %p)\n", iface, pFindBody, phBody);
2046
2047     pFindBody->dwReserved = 0;
2048     return find_next(iface, pFindBody, phBody);
2049 }
2050
2051 static HRESULT WINAPI MimeMessage_FindNext(
2052     IMimeMessage *iface,
2053     LPFINDBODY pFindBody,
2054     LPHBODY phBody)
2055 {
2056     TRACE("(%p)->(%p, %p)\n", iface, pFindBody, phBody);
2057
2058     return find_next(iface, 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, pszPriType, pszSubType);
2136
2137     hr = IMimeMessage_BindToObject(iface, hBody, &IID_IMimeBody, (void**)&mime_body);
2138     if(FAILED(hr)) return hr;
2139
2140     hr = IMimeBody_IsContentType(mime_body, pszPriType, pszSubType);
2141     IMimeBody_Release(mime_body);
2142     return hr;
2143 }
2144
2145 static HRESULT WINAPI MimeMessage_QueryBodyProp(
2146     IMimeMessage *iface,
2147     HBODY hBody,
2148     LPCSTR pszName,
2149     LPCSTR pszCriteria,
2150     boolean fSubString,
2151     boolean fCaseSensitive)
2152 {
2153     FIXME("(%p)->(%p, %s, %s, %s, %s)\n", iface, hBody, pszName, pszCriteria, fSubString ? "TRUE" : "FALSE", fCaseSensitive ? "TRUE" : "FALSE");
2154     return E_NOTIMPL;
2155 }
2156
2157 static HRESULT WINAPI MimeMessage_GetBodyProp(
2158     IMimeMessage *iface,
2159     HBODY hBody,
2160     LPCSTR pszName,
2161     DWORD dwFlags,
2162     LPPROPVARIANT pValue)
2163 {
2164     HRESULT hr;
2165     IMimeBody *mime_body;
2166
2167     TRACE("(%p)->(%p, %s, 0x%x, %p)\n", iface, hBody, pszName, dwFlags, pValue);
2168
2169     hr = IMimeMessage_BindToObject(iface, hBody, &IID_IMimeBody, (void**)&mime_body);
2170     if(hr != S_OK) return hr;
2171
2172     hr = IMimeBody_GetProp(mime_body, pszName, dwFlags, pValue);
2173     IMimeBody_Release(mime_body);
2174
2175     return hr;
2176 }
2177
2178 static HRESULT WINAPI MimeMessage_SetBodyProp(
2179     IMimeMessage *iface,
2180     HBODY hBody,
2181     LPCSTR pszName,
2182     DWORD dwFlags,
2183     LPCPROPVARIANT pValue)
2184 {
2185     FIXME("(%p)->(%p, %s, 0x%x, %p)\n", iface, hBody, pszName, dwFlags, pValue);
2186     return E_NOTIMPL;
2187 }
2188
2189 static HRESULT WINAPI MimeMessage_DeleteBodyProp(
2190     IMimeMessage *iface,
2191     HBODY hBody,
2192     LPCSTR pszName)
2193 {
2194     FIXME("(%p)->(%p, %s)\n", iface, hBody, pszName);
2195     return E_NOTIMPL;
2196 }
2197
2198 static HRESULT WINAPI MimeMessage_SetOption(
2199     IMimeMessage *iface,
2200     const TYPEDID oid,
2201     LPCPROPVARIANT pValue)
2202 {
2203     HRESULT hr = E_NOTIMPL;
2204     TRACE("(%p)->(%08x, %p)\n", iface, oid, pValue);
2205
2206     if(pValue->vt != TYPEDID_TYPE(oid))
2207     {
2208         WARN("Called with vartype %04x and oid %08x\n", pValue->vt, oid);
2209         return E_INVALIDARG;
2210     }
2211
2212     switch(oid)
2213     {
2214     case OID_HIDE_TNEF_ATTACHMENTS:
2215         FIXME("OID_HIDE_TNEF_ATTACHMENTS (value %d): ignoring\n", pValue->u.boolVal);
2216         hr = S_OK;
2217         break;
2218     case OID_SHOW_MACBINARY:
2219         FIXME("OID_SHOW_MACBINARY (value %d): ignoring\n", pValue->u.boolVal);
2220         hr = S_OK;
2221         break;
2222     default:
2223         FIXME("Unhandled oid %08x\n", oid);
2224     }
2225
2226     return hr;
2227 }
2228
2229 static HRESULT WINAPI MimeMessage_GetOption(
2230     IMimeMessage *iface,
2231     const TYPEDID oid,
2232     LPPROPVARIANT pValue)
2233 {
2234     FIXME("(%p)->(%08x, %p)\n", iface, oid, pValue);
2235     return E_NOTIMPL;
2236 }
2237
2238 /*** IMimeMessage methods ***/
2239 static HRESULT WINAPI MimeMessage_CreateWebPage(
2240     IMimeMessage *iface,
2241     IStream *pRootStm,
2242     LPWEBPAGEOPTIONS pOptions,
2243     IMimeMessageCallback *pCallback,
2244     IMoniker **ppMoniker)
2245 {
2246     FIXME("(%p)->(%p, %p, %p, %p)\n", iface, pRootStm, pOptions, pCallback, ppMoniker);
2247     *ppMoniker = NULL;
2248     return E_NOTIMPL;
2249 }
2250
2251 static HRESULT WINAPI MimeMessage_GetProp(
2252     IMimeMessage *iface,
2253     LPCSTR pszName,
2254     DWORD dwFlags,
2255     LPPROPVARIANT pValue)
2256 {
2257     FIXME("(%p)->(%s, 0x%x, %p)\n", iface, pszName, dwFlags, pValue);
2258     return E_NOTIMPL;
2259 }
2260
2261 static HRESULT WINAPI MimeMessage_SetProp(
2262     IMimeMessage *iface,
2263     LPCSTR pszName,
2264     DWORD dwFlags,
2265     LPCPROPVARIANT pValue)
2266 {
2267     FIXME("(%p)->(%s, 0x%x, %p)\n", iface, pszName, dwFlags, pValue);
2268     return E_NOTIMPL;
2269 }
2270
2271 static HRESULT WINAPI MimeMessage_DeleteProp(
2272     IMimeMessage *iface,
2273     LPCSTR pszName)
2274 {
2275     FIXME("(%p)->(%s)\n", iface, pszName);
2276     return E_NOTIMPL;
2277 }
2278
2279 static HRESULT WINAPI MimeMessage_QueryProp(
2280     IMimeMessage *iface,
2281     LPCSTR pszName,
2282     LPCSTR pszCriteria,
2283     boolean fSubString,
2284     boolean fCaseSensitive)
2285 {
2286     FIXME("(%p)->(%s, %s, %s, %s)\n", iface, pszName, pszCriteria, fSubString ? "TRUE" : "FALSE", fCaseSensitive ? "TRUE" : "FALSE");
2287     return E_NOTIMPL;
2288 }
2289
2290 static HRESULT WINAPI MimeMessage_GetTextBody(
2291     IMimeMessage *iface,
2292     DWORD dwTxtType,
2293     ENCODINGTYPE ietEncoding,
2294     IStream **pStream,
2295     LPHBODY phBody)
2296 {
2297     HRESULT hr;
2298     HBODY hbody;
2299     FINDBODY find_struct;
2300     IMimeBody *mime_body;
2301     static char text[] = "text";
2302     static char plain[] = "plain";
2303     static char html[] = "html";
2304
2305     TRACE("(%p)->(%d, %d, %p, %p)\n", iface, dwTxtType, ietEncoding, pStream, phBody);
2306
2307     find_struct.pszPriType = text;
2308
2309     switch(dwTxtType)
2310     {
2311     case TXT_PLAIN:
2312         find_struct.pszSubType = plain;
2313         break;
2314     case TXT_HTML:
2315         find_struct.pszSubType = html;
2316         break;
2317     default:
2318         return MIME_E_INVALID_TEXT_TYPE;
2319     }
2320
2321     hr = IMimeMessage_FindFirst(iface, &find_struct, &hbody);
2322     if(hr != S_OK)
2323     {
2324         TRACE("not found hr %08x\n", hr);
2325         *phBody = NULL;
2326         return hr;
2327     }
2328
2329     IMimeMessage_BindToObject(iface, hbody, &IID_IMimeBody, (void**)&mime_body);
2330
2331     IMimeBody_GetData(mime_body, ietEncoding, pStream);
2332     *phBody = hbody;
2333     IMimeBody_Release(mime_body);
2334     return hr;
2335 }
2336
2337 static HRESULT WINAPI MimeMessage_SetTextBody(
2338     IMimeMessage *iface,
2339     DWORD dwTxtType,
2340     ENCODINGTYPE ietEncoding,
2341     HBODY hAlternative,
2342     IStream *pStream,
2343     LPHBODY phBody)
2344 {
2345     FIXME("(%p)->(%d, %d, %p, %p, %p)\n", iface, dwTxtType, ietEncoding, hAlternative, pStream, phBody);
2346     return E_NOTIMPL;
2347 }
2348
2349 static HRESULT WINAPI MimeMessage_AttachObject(
2350     IMimeMessage *iface,
2351     REFIID riid,
2352     void *pvObject,
2353     LPHBODY phBody)
2354 {
2355     FIXME("(%p)->(%s, %p, %p)\n", iface, debugstr_guid(riid), pvObject, phBody);
2356     return E_NOTIMPL;
2357 }
2358
2359 static HRESULT WINAPI MimeMessage_AttachFile(
2360     IMimeMessage *iface,
2361     LPCSTR pszFilePath,
2362     IStream *pstmFile,
2363     LPHBODY phBody)
2364 {
2365     FIXME("(%p)->(%s, %p, %p)\n", iface, pszFilePath, pstmFile, phBody);
2366     return E_NOTIMPL;
2367 }
2368
2369 static HRESULT WINAPI MimeMessage_AttachURL(
2370     IMimeMessage *iface,
2371     LPCSTR pszBase,
2372     LPCSTR pszURL,
2373     DWORD dwFlags,
2374     IStream *pstmURL,
2375     LPSTR *ppszCIDURL,
2376     LPHBODY phBody)
2377 {
2378     FIXME("(%p)->(%s, %s, 0x%x, %p, %p, %p)\n", iface, pszBase, pszURL, dwFlags, pstmURL, ppszCIDURL, phBody);
2379     return E_NOTIMPL;
2380 }
2381
2382 static HRESULT WINAPI MimeMessage_GetAttachments(
2383     IMimeMessage *iface,
2384     ULONG *pcAttach,
2385     LPHBODY *pprghAttach)
2386 {
2387     HRESULT hr;
2388     FINDBODY find_struct;
2389     HBODY hbody;
2390     LPHBODY array;
2391     ULONG size = 10;
2392
2393     TRACE("(%p)->(%p, %p)\n", iface, pcAttach, pprghAttach);
2394
2395     *pcAttach = 0;
2396     array = CoTaskMemAlloc(size * sizeof(HBODY));
2397
2398     find_struct.pszPriType = find_struct.pszSubType = NULL;
2399     hr = IMimeMessage_FindFirst(iface, &find_struct, &hbody);
2400     while(hr == S_OK)
2401     {
2402         hr = IMimeMessage_IsContentType(iface, hbody, "multipart", NULL);
2403         TRACE("IsCT rets %08x %d\n", hr, *pcAttach);
2404         if(hr != S_OK)
2405         {
2406             if(*pcAttach + 1 > size)
2407             {
2408                 size *= 2;
2409                 array = CoTaskMemRealloc(array, size * sizeof(HBODY));
2410             }
2411             array[*pcAttach] = hbody;
2412             (*pcAttach)++;
2413         }
2414         hr = IMimeMessage_FindNext(iface, &find_struct, &hbody);
2415     }
2416
2417     *pprghAttach = array;
2418     return S_OK;
2419 }
2420
2421 static HRESULT WINAPI MimeMessage_GetAddressTable(
2422     IMimeMessage *iface,
2423     IMimeAddressTable **ppTable)
2424 {
2425     FIXME("(%p)->(%p)\n", iface, ppTable);
2426     return E_NOTIMPL;
2427 }
2428
2429 static HRESULT WINAPI MimeMessage_GetSender(
2430     IMimeMessage *iface,
2431     LPADDRESSPROPS pAddress)
2432 {
2433     FIXME("(%p)->(%p)\n", iface, pAddress);
2434     return E_NOTIMPL;
2435 }
2436
2437 static HRESULT WINAPI MimeMessage_GetAddressTypes(
2438     IMimeMessage *iface,
2439     DWORD dwAdrTypes,
2440     DWORD dwProps,
2441     LPADDRESSLIST pList)
2442 {
2443     FIXME("(%p)->(%d, %d, %p)\n", iface, dwAdrTypes, dwProps, pList);
2444     return E_NOTIMPL;
2445 }
2446
2447 static HRESULT WINAPI MimeMessage_GetAddressFormat(
2448     IMimeMessage *iface,
2449     DWORD dwAdrTypes,
2450     ADDRESSFORMAT format,
2451     LPSTR *ppszFormat)
2452 {
2453     FIXME("(%p)->(%d, %d, %p)\n", iface, dwAdrTypes, format, ppszFormat);
2454     return E_NOTIMPL;
2455 }
2456
2457 static HRESULT WINAPI MimeMessage_EnumAddressTypes(
2458     IMimeMessage *iface,
2459     DWORD dwAdrTypes,
2460     DWORD dwProps,
2461     IMimeEnumAddressTypes **ppEnum)
2462 {
2463     FIXME("(%p)->(%d, %d, %p)\n", iface, dwAdrTypes, dwProps, ppEnum);
2464     return E_NOTIMPL;
2465 }
2466
2467 static HRESULT WINAPI MimeMessage_SplitMessage(
2468     IMimeMessage *iface,
2469     ULONG cbMaxPart,
2470     IMimeMessageParts **ppParts)
2471 {
2472     FIXME("(%p)->(%d, %p)\n", iface, cbMaxPart, ppParts);
2473     return E_NOTIMPL;
2474 }
2475
2476 static HRESULT WINAPI MimeMessage_GetRootMoniker(
2477     IMimeMessage *iface,
2478     IMoniker **ppMoniker)
2479 {
2480     FIXME("(%p)->(%p)\n", iface, ppMoniker);
2481     return E_NOTIMPL;
2482 }
2483
2484 static const IMimeMessageVtbl MimeMessageVtbl =
2485 {
2486     MimeMessage_QueryInterface,
2487     MimeMessage_AddRef,
2488     MimeMessage_Release,
2489     MimeMessage_GetClassID,
2490     MimeMessage_IsDirty,
2491     MimeMessage_Load,
2492     MimeMessage_Save,
2493     MimeMessage_GetSizeMax,
2494     MimeMessage_InitNew,
2495     MimeMessage_GetMessageSource,
2496     MimeMessage_GetMessageSize,
2497     MimeMessage_LoadOffsetTable,
2498     MimeMessage_SaveOffsetTable,
2499     MimeMessage_GetFlags,
2500     MimeMessage_Commit,
2501     MimeMessage_HandsOffStorage,
2502     MimeMessage_BindToObject,
2503     MimeMessage_SaveBody,
2504     MimeMessage_InsertBody,
2505     MimeMessage_GetBody,
2506     MimeMessage_DeleteBody,
2507     MimeMessage_MoveBody,
2508     MimeMessage_CountBodies,
2509     MimeMessage_FindFirst,
2510     MimeMessage_FindNext,
2511     MimeMessage_ResolveURL,
2512     MimeMessage_ToMultipart,
2513     MimeMessage_GetBodyOffsets,
2514     MimeMessage_GetCharset,
2515     MimeMessage_SetCharset,
2516     MimeMessage_IsBodyType,
2517     MimeMessage_IsContentType,
2518     MimeMessage_QueryBodyProp,
2519     MimeMessage_GetBodyProp,
2520     MimeMessage_SetBodyProp,
2521     MimeMessage_DeleteBodyProp,
2522     MimeMessage_SetOption,
2523     MimeMessage_GetOption,
2524     MimeMessage_CreateWebPage,
2525     MimeMessage_GetProp,
2526     MimeMessage_SetProp,
2527     MimeMessage_DeleteProp,
2528     MimeMessage_QueryProp,
2529     MimeMessage_GetTextBody,
2530     MimeMessage_SetTextBody,
2531     MimeMessage_AttachObject,
2532     MimeMessage_AttachFile,
2533     MimeMessage_AttachURL,
2534     MimeMessage_GetAttachments,
2535     MimeMessage_GetAddressTable,
2536     MimeMessage_GetSender,
2537     MimeMessage_GetAddressTypes,
2538     MimeMessage_GetAddressFormat,
2539     MimeMessage_EnumAddressTypes,
2540     MimeMessage_SplitMessage,
2541     MimeMessage_GetRootMoniker,
2542 };
2543
2544 HRESULT MimeMessage_create(IUnknown *outer, void **obj)
2545 {
2546     MimeMessage *This;
2547
2548     TRACE("(%p, %p)\n", outer, obj);
2549
2550     if (outer)
2551     {
2552         FIXME("outer unknown not supported yet\n");
2553         return E_NOTIMPL;
2554     }
2555
2556     *obj = NULL;
2557
2558     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
2559     if (!This) return E_OUTOFMEMORY;
2560
2561     This->lpVtbl = &MimeMessageVtbl;
2562     This->refs = 1;
2563     This->stream = NULL;
2564     list_init(&This->body_tree);
2565     This->next_hbody = (HBODY)1;
2566
2567     *obj = &This->lpVtbl;
2568     return S_OK;
2569 }
2570
2571 /***********************************************************************
2572  *              MimeOleCreateMessage (INETCOMM.@)
2573  */
2574 HRESULT WINAPI MimeOleCreateMessage(IUnknown *pUnkOuter, IMimeMessage **ppMessage)
2575 {
2576     TRACE("(%p, %p)\n", pUnkOuter, ppMessage);
2577     return MimeMessage_create(NULL, (void **)ppMessage);
2578 }
2579
2580 /***********************************************************************
2581  *              MimeOleSetCompatMode (INETCOMM.@)
2582  */
2583 HRESULT WINAPI MimeOleSetCompatMode(DWORD dwMode)
2584 {
2585     FIXME("(0x%x)\n", dwMode);
2586     return S_OK;
2587 }
2588
2589 /***********************************************************************
2590  *              MimeOleCreateVirtualStream (INETCOMM.@)
2591  */
2592 HRESULT WINAPI MimeOleCreateVirtualStream(IStream **ppStream)
2593 {
2594     HRESULT hr;
2595     FIXME("(%p)\n", ppStream);
2596
2597     hr = CreateStreamOnHGlobal(NULL, TRUE, ppStream);
2598     return hr;
2599 }
2600
2601 typedef struct MimeSecurity
2602 {
2603     const IMimeSecurityVtbl *lpVtbl;
2604
2605     LONG refs;
2606 } MimeSecurity;
2607
2608 static HRESULT WINAPI MimeSecurity_QueryInterface(
2609         IMimeSecurity* iface,
2610         REFIID riid,
2611         void** obj)
2612 {
2613     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), obj);
2614
2615     if (IsEqualIID(riid, &IID_IUnknown) ||
2616         IsEqualIID(riid, &IID_IMimeSecurity))
2617     {
2618         *obj = iface;
2619         IUnknown_AddRef(iface);
2620         return S_OK;
2621     }
2622
2623     FIXME("no interface for %s\n", debugstr_guid(riid));
2624     *obj = NULL;
2625     return E_NOINTERFACE;
2626 }
2627
2628 static ULONG WINAPI MimeSecurity_AddRef(
2629         IMimeSecurity* iface)
2630 {
2631     MimeSecurity *This = (MimeSecurity *)iface;
2632     TRACE("(%p)->()\n", iface);
2633     return InterlockedIncrement(&This->refs);
2634 }
2635
2636 static ULONG WINAPI MimeSecurity_Release(
2637         IMimeSecurity* iface)
2638 {
2639     MimeSecurity *This = (MimeSecurity *)iface;
2640     ULONG refs;
2641
2642     TRACE("(%p)->()\n", iface);
2643
2644     refs = InterlockedDecrement(&This->refs);
2645     if (!refs)
2646     {
2647         HeapFree(GetProcessHeap(), 0, This);
2648     }
2649
2650     return refs;
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->lpVtbl = &MimeSecurityVtbl;
2776     This->refs = 1;
2777
2778     *obj = &This->lpVtbl;
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 typedef struct
2791 {
2792     IMimeAllocatorVtbl *lpVtbl;
2793 } MimeAllocator;
2794
2795 static HRESULT WINAPI MimeAlloc_QueryInterface(
2796         IMimeAllocator* iface,
2797         REFIID riid,
2798         void **obj)
2799 {
2800     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), obj);
2801
2802     if (IsEqualIID(riid, &IID_IUnknown) ||
2803         IsEqualIID(riid, &IID_IMalloc) ||
2804         IsEqualIID(riid, &IID_IMimeAllocator))
2805     {
2806         *obj = iface;
2807         IUnknown_AddRef(iface);
2808         return S_OK;
2809     }
2810
2811     FIXME("no interface for %s\n", debugstr_guid(riid));
2812     *obj = NULL;
2813     return E_NOINTERFACE;
2814 }
2815
2816 static ULONG WINAPI MimeAlloc_AddRef(
2817         IMimeAllocator* iface)
2818 {
2819     return 2;
2820 }
2821
2822 static ULONG WINAPI MimeAlloc_Release(
2823         IMimeAllocator* iface)
2824 {
2825     return 1;
2826 }
2827
2828 static LPVOID WINAPI MimeAlloc_Alloc(
2829         IMimeAllocator* iface,
2830         ULONG cb)
2831 {
2832     return CoTaskMemAlloc(cb);
2833 }
2834
2835 static LPVOID WINAPI MimeAlloc_Realloc(
2836         IMimeAllocator* iface,
2837         LPVOID pv,
2838         ULONG cb)
2839 {
2840     return CoTaskMemRealloc(pv, cb);
2841 }
2842
2843 static void WINAPI MimeAlloc_Free(
2844         IMimeAllocator* iface,
2845         LPVOID pv)
2846 {
2847     CoTaskMemFree(pv);
2848 }
2849
2850 static ULONG WINAPI MimeAlloc_GetSize(
2851         IMimeAllocator* iface,
2852         LPVOID pv)
2853 {
2854     FIXME("stub\n");
2855     return 0;
2856 }
2857
2858 static int WINAPI MimeAlloc_DidAlloc(
2859         IMimeAllocator* iface,
2860         LPVOID pv)
2861 {
2862     FIXME("stub\n");
2863     return 0;
2864 }
2865
2866 static void WINAPI MimeAlloc_HeapMinimize(
2867         IMimeAllocator* iface)
2868 {
2869     FIXME("stub\n");
2870     return;
2871 }
2872
2873 static HRESULT WINAPI MimeAlloc_FreeParamInfoArray(
2874         IMimeAllocator* iface,
2875         ULONG cParams,
2876         LPMIMEPARAMINFO prgParam,
2877         boolean fFreeArray)
2878 {
2879     ULONG i;
2880     TRACE("(%p)->(%d, %p, %d)\n", iface, cParams, prgParam, fFreeArray);
2881
2882     for(i = 0; i < cParams; i++)
2883     {
2884         IMimeAllocator_Free(iface, prgParam[i].pszName);
2885         IMimeAllocator_Free(iface, prgParam[i].pszData);
2886     }
2887     if(fFreeArray) IMimeAllocator_Free(iface, prgParam);
2888     return S_OK;
2889 }
2890
2891 static HRESULT WINAPI MimeAlloc_FreeAddressList(
2892         IMimeAllocator* iface,
2893         LPADDRESSLIST pList)
2894 {
2895     FIXME("stub\n");
2896     return E_NOTIMPL;
2897 }
2898
2899 static HRESULT WINAPI MimeAlloc_FreeAddressProps(
2900         IMimeAllocator* iface,
2901         LPADDRESSPROPS pAddress)
2902 {
2903     FIXME("stub\n");
2904     return E_NOTIMPL;
2905 }
2906
2907 static HRESULT WINAPI MimeAlloc_ReleaseObjects(
2908         IMimeAllocator* iface,
2909         ULONG cObjects,
2910         IUnknown **prgpUnknown,
2911         boolean fFreeArray)
2912 {
2913     FIXME("stub\n");
2914     return E_NOTIMPL;
2915 }
2916
2917
2918 static HRESULT WINAPI MimeAlloc_FreeEnumHeaderRowArray(
2919         IMimeAllocator* iface,
2920         ULONG cRows,
2921         LPENUMHEADERROW prgRow,
2922         boolean fFreeArray)
2923 {
2924     FIXME("stub\n");
2925     return E_NOTIMPL;
2926 }
2927
2928 static HRESULT WINAPI MimeAlloc_FreeEnumPropertyArray(
2929         IMimeAllocator* iface,
2930         ULONG cProps,
2931         LPENUMPROPERTY prgProp,
2932         boolean fFreeArray)
2933 {
2934     FIXME("stub\n");
2935     return E_NOTIMPL;
2936 }
2937
2938 static HRESULT WINAPI MimeAlloc_FreeThumbprint(
2939         IMimeAllocator* iface,
2940         THUMBBLOB *pthumbprint)
2941 {
2942     FIXME("stub\n");
2943     return E_NOTIMPL;
2944 }
2945
2946
2947 static HRESULT WINAPI MimeAlloc_PropVariantClear(
2948         IMimeAllocator* iface,
2949         LPPROPVARIANT pProp)
2950 {
2951     FIXME("stub\n");
2952     return E_NOTIMPL;
2953 }
2954
2955 static IMimeAllocatorVtbl mime_alloc_vtbl =
2956 {
2957     MimeAlloc_QueryInterface,
2958     MimeAlloc_AddRef,
2959     MimeAlloc_Release,
2960     MimeAlloc_Alloc,
2961     MimeAlloc_Realloc,
2962     MimeAlloc_Free,
2963     MimeAlloc_GetSize,
2964     MimeAlloc_DidAlloc,
2965     MimeAlloc_HeapMinimize,
2966     MimeAlloc_FreeParamInfoArray,
2967     MimeAlloc_FreeAddressList,
2968     MimeAlloc_FreeAddressProps,
2969     MimeAlloc_ReleaseObjects,
2970     MimeAlloc_FreeEnumHeaderRowArray,
2971     MimeAlloc_FreeEnumPropertyArray,
2972     MimeAlloc_FreeThumbprint,
2973     MimeAlloc_PropVariantClear
2974 };
2975
2976 static MimeAllocator mime_allocator =
2977 {
2978     &mime_alloc_vtbl
2979 };
2980
2981 HRESULT MimeAllocator_create(IUnknown *outer, void **obj)
2982 {
2983     if(outer) return CLASS_E_NOAGGREGATION;
2984
2985     *obj = &mime_allocator;
2986     return S_OK;
2987 }
2988
2989 HRESULT WINAPI MimeOleGetAllocator(IMimeAllocator **alloc)
2990 {
2991     return MimeAllocator_create(NULL, (void**)alloc);
2992 }
2993
2994 HRESULT VirtualStream_create(IUnknown *outer, void **obj)
2995 {
2996     FIXME("(%p, %p)\n", outer, obj);
2997
2998     *obj = NULL;
2999     if (outer) return CLASS_E_NOAGGREGATION;
3000
3001     return MimeOleCreateVirtualStream((IStream **)obj);
3002 }