inetcomm: Add an implementation of IPOP3Transport::CommandRETR.
[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 = (IMimeBody *)&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, buf);
1614     return hr;
1615 }
1616
1617 static body_t *create_sub_body(MimeMessage *msg, IStream *pStm, BODYOFFSETS *offset, body_t *parent)
1618 {
1619     IMimeBody *mime_body;
1620     HRESULT hr;
1621     body_t *body;
1622     ULARGE_INTEGER cur;
1623     LARGE_INTEGER zero;
1624
1625     MimeBody_create(NULL, (void**)&mime_body);
1626     IMimeBody_Load(mime_body, pStm);
1627     zero.QuadPart = 0;
1628     hr = IStream_Seek(pStm, zero, STREAM_SEEK_CUR, &cur);
1629     offset->cbBodyStart = cur.u.LowPart + offset->cbHeaderStart;
1630     if(parent) MimeBody_set_offsets(impl_from_IMimeBody(mime_body), offset);
1631     IMimeBody_SetData(mime_body, IET_BINARY, NULL, NULL, &IID_IStream, pStm);
1632     body = new_body_entry(mime_body, msg->next_hbody, parent);
1633     msg->next_hbody = (HBODY)((DWORD)msg->next_hbody + 1);
1634
1635     if(IMimeBody_IsContentType(mime_body, "multipart", NULL) == S_OK)
1636     {
1637         MIMEPARAMINFO *param_info;
1638         ULONG count, i;
1639         IMimeAllocator *alloc;
1640
1641         hr = IMimeBody_GetParameters(mime_body, "Content-Type", &count, &param_info);
1642         if(hr != S_OK || count == 0) return body;
1643
1644         MimeOleGetAllocator(&alloc);
1645
1646         for(i = 0; i < count; i++)
1647         {
1648             if(!strcasecmp(param_info[i].pszName, "boundary"))
1649             {
1650                 struct list offset_list;
1651                 offset_entry_t *cur, *cursor2;
1652                 hr = create_body_offset_list(pStm, param_info[i].pszData, &offset_list);
1653                 LIST_FOR_EACH_ENTRY_SAFE(cur, cursor2, &offset_list, offset_entry_t, entry)
1654                 {
1655                     body_t *sub_body;
1656                     IStream *sub_stream;
1657                     ULARGE_INTEGER start, length;
1658
1659                     start.QuadPart = cur->offsets.cbHeaderStart;
1660                     length.QuadPart = cur->offsets.cbBodyEnd - cur->offsets.cbHeaderStart;
1661                     create_sub_stream(pStm, start, length, &sub_stream);
1662                     sub_body = create_sub_body(msg, sub_stream, &cur->offsets, body);
1663                     IStream_Release(sub_stream);
1664                     list_add_tail(&body->children, &sub_body->entry);
1665                     list_remove(&cur->entry);
1666                     HeapFree(GetProcessHeap(), 0, cur);
1667                 }
1668                 break;
1669             }
1670         }
1671         IMimeAllocator_FreeParamInfoArray(alloc, count, param_info, TRUE);
1672         IMimeAllocator_Release(alloc);
1673     }
1674     return body;
1675 }
1676
1677 static HRESULT WINAPI MimeMessage_Load(
1678     IMimeMessage *iface,
1679     LPSTREAM pStm)
1680 {
1681     MimeMessage *This = (MimeMessage *)iface;
1682     body_t *root_body;
1683     BODYOFFSETS offsets;
1684     ULARGE_INTEGER cur;
1685     LARGE_INTEGER zero;
1686
1687     TRACE("(%p)->(%p)\n", iface, pStm);
1688
1689     if(This->stream)
1690     {
1691         FIXME("already loaded a message\n");
1692         return E_FAIL;
1693     }
1694
1695     IStream_AddRef(pStm);
1696     This->stream = pStm;
1697     offsets.cbBoundaryStart = offsets.cbHeaderStart = 0;
1698     offsets.cbBodyStart = offsets.cbBodyEnd = 0;
1699
1700     root_body = create_sub_body(This, pStm, &offsets, NULL);
1701
1702     zero.QuadPart = 0;
1703     IStream_Seek(pStm, zero, STREAM_SEEK_END, &cur);
1704     offsets.cbBodyEnd = cur.u.LowPart;
1705     MimeBody_set_offsets(impl_from_IMimeBody(root_body->mime_body), &offsets);
1706
1707     list_add_head(&This->body_tree, &root_body->entry);
1708
1709     return S_OK;
1710 }
1711
1712 static HRESULT WINAPI MimeMessage_Save(
1713     IMimeMessage *iface,
1714     LPSTREAM pStm,
1715     BOOL fClearDirty)
1716 {
1717     FIXME("(%p)->(%p, %s)\n", iface, pStm, fClearDirty ? "TRUE" : "FALSE");
1718     return E_NOTIMPL;
1719 }
1720
1721 static HRESULT WINAPI MimeMessage_GetSizeMax(
1722     IMimeMessage *iface,
1723     ULARGE_INTEGER *pcbSize)
1724 {
1725     FIXME("(%p)->(%p)\n", iface, pcbSize);
1726     return E_NOTIMPL;
1727 }
1728
1729 static HRESULT WINAPI MimeMessage_InitNew(
1730     IMimeMessage *iface)
1731 {
1732     FIXME("(%p)->()\n", iface);
1733     return E_NOTIMPL;
1734 }
1735
1736 /*** IMimeMessageTree methods ***/
1737 static HRESULT WINAPI MimeMessage_GetMessageSource(
1738     IMimeMessage *iface,
1739     IStream **ppStream,
1740     DWORD dwFlags)
1741 {
1742     MimeMessage *This = (MimeMessage *)iface;
1743     FIXME("(%p)->(%p, 0x%x)\n", iface, ppStream, dwFlags);
1744
1745     IStream_AddRef(This->stream);
1746     *ppStream = This->stream;
1747     return S_OK;
1748 }
1749
1750 static HRESULT WINAPI MimeMessage_GetMessageSize(
1751     IMimeMessage *iface,
1752     ULONG *pcbSize,
1753     DWORD dwFlags)
1754 {
1755     FIXME("(%p)->(%p, 0x%x)\n", iface, pcbSize, dwFlags);
1756     return E_NOTIMPL;
1757 }
1758
1759 static HRESULT WINAPI MimeMessage_LoadOffsetTable(
1760     IMimeMessage *iface,
1761     IStream *pStream)
1762 {
1763     FIXME("(%p)->(%p)\n", iface, pStream);
1764     return E_NOTIMPL;
1765 }
1766
1767 static HRESULT WINAPI MimeMessage_SaveOffsetTable(
1768     IMimeMessage *iface,
1769     IStream *pStream,
1770     DWORD dwFlags)
1771 {
1772     FIXME("(%p)->(%p, 0x%x)\n", iface, pStream, dwFlags);
1773     return E_NOTIMPL;
1774 }
1775
1776
1777 static HRESULT WINAPI MimeMessage_GetFlags(
1778     IMimeMessage *iface,
1779     DWORD *pdwFlags)
1780 {
1781     FIXME("(%p)->(%p)\n", iface, pdwFlags);
1782     return E_NOTIMPL;
1783 }
1784
1785 static HRESULT WINAPI MimeMessage_Commit(
1786     IMimeMessage *iface,
1787     DWORD dwFlags)
1788 {
1789     FIXME("(%p)->(0x%x)\n", iface, dwFlags);
1790     return E_NOTIMPL;
1791 }
1792
1793
1794 static HRESULT WINAPI MimeMessage_HandsOffStorage(
1795     IMimeMessage *iface)
1796 {
1797     FIXME("(%p)->()\n", iface);
1798     return E_NOTIMPL;
1799 }
1800
1801 static HRESULT find_body(struct list *list, HBODY hbody, body_t **body)
1802 {
1803     body_t *cur;
1804     HRESULT hr;
1805
1806     if(hbody == HBODY_ROOT)
1807     {
1808         *body = LIST_ENTRY(list_head(list), body_t, entry);
1809         return S_OK;
1810     }
1811
1812     LIST_FOR_EACH_ENTRY(cur, list, body_t, entry)
1813     {
1814         if(cur->hbody == hbody)
1815         {
1816             *body = cur;
1817             return S_OK;
1818         }
1819         hr = find_body(&cur->children, hbody, body);
1820         if(hr == S_OK) return S_OK;
1821     }
1822     return S_FALSE;
1823 }
1824
1825 static HRESULT WINAPI MimeMessage_BindToObject(
1826     IMimeMessage *iface,
1827     const HBODY hBody,
1828     REFIID riid,
1829     void **ppvObject)
1830 {
1831     MimeMessage *This = (MimeMessage *)iface;
1832     HRESULT hr;
1833     body_t *body;
1834
1835     TRACE("(%p)->(%p, %s, %p)\n", iface, hBody, debugstr_guid(riid), ppvObject);
1836
1837     hr = find_body(&This->body_tree, hBody, &body);
1838
1839     if(hr != S_OK) return hr;
1840
1841     if(IsEqualIID(riid, &IID_IMimeBody))
1842     {
1843         IMimeBody_AddRef(body->mime_body);
1844         *ppvObject = body->mime_body;
1845         return S_OK;
1846     }
1847
1848     return E_NOINTERFACE;
1849 }
1850
1851 static HRESULT WINAPI MimeMessage_SaveBody(
1852     IMimeMessage *iface,
1853     HBODY hBody,
1854     DWORD dwFlags,
1855     IStream *pStream)
1856 {
1857     FIXME("(%p)->(%p, 0x%x, %p)\n", iface, hBody, dwFlags, pStream);
1858     return E_NOTIMPL;
1859 }
1860
1861 static HRESULT get_body(MimeMessage *msg, BODYLOCATION location, HBODY pivot, body_t **out)
1862 {
1863     body_t *root = LIST_ENTRY(list_head(&msg->body_tree), body_t, entry);
1864     body_t *body;
1865     HRESULT hr;
1866     struct list *list;
1867
1868     if(location == IBL_ROOT)
1869     {
1870         *out = root;
1871         return S_OK;
1872     }
1873
1874     hr = find_body(&msg->body_tree, pivot, &body);
1875
1876     if(hr == S_OK)
1877     {
1878         switch(location)
1879         {
1880         case IBL_PARENT:
1881             *out = body->parent;
1882             break;
1883
1884         case IBL_FIRST:
1885             list = list_head(&body->children);
1886             if(list)
1887                 *out = LIST_ENTRY(list, body_t, entry);
1888             else
1889                 hr = MIME_E_NOT_FOUND;
1890             break;
1891
1892         case IBL_LAST:
1893             list = list_tail(&body->children);
1894             if(list)
1895                 *out = LIST_ENTRY(list, body_t, entry);
1896             else
1897                 hr = MIME_E_NOT_FOUND;
1898             break;
1899
1900         case IBL_NEXT:
1901             list = list_next(&body->parent->children, &body->entry);
1902             if(list)
1903                 *out = LIST_ENTRY(list, body_t, entry);
1904             else
1905                 hr = MIME_E_NOT_FOUND;
1906             break;
1907
1908         case IBL_PREVIOUS:
1909             list = list_prev(&body->parent->children, &body->entry);
1910             if(list)
1911                 *out = LIST_ENTRY(list, body_t, entry);
1912             else
1913                 hr = MIME_E_NOT_FOUND;
1914             break;
1915
1916         default:
1917             hr = E_FAIL;
1918             break;
1919         }
1920     }
1921
1922     return hr;
1923 }
1924
1925
1926 static HRESULT WINAPI MimeMessage_InsertBody(
1927     IMimeMessage *iface,
1928     BODYLOCATION location,
1929     HBODY hPivot,
1930     LPHBODY phBody)
1931 {
1932     FIXME("(%p)->(%d, %p, %p)\n", iface, location, hPivot, phBody);
1933     return E_NOTIMPL;
1934 }
1935
1936 static HRESULT WINAPI MimeMessage_GetBody(
1937     IMimeMessage *iface,
1938     BODYLOCATION location,
1939     HBODY hPivot,
1940     LPHBODY phBody)
1941 {
1942     MimeMessage *This = (MimeMessage *)iface;
1943     body_t *body;
1944     HRESULT hr;
1945
1946     TRACE("(%p)->(%d, %p, %p)\n", iface, location, hPivot, phBody);
1947
1948     hr = get_body(This, location, hPivot, &body);
1949
1950     if(hr == S_OK) *phBody = body->hbody;
1951
1952     return hr;
1953 }
1954
1955 static HRESULT WINAPI MimeMessage_DeleteBody(
1956     IMimeMessage *iface,
1957     HBODY hBody,
1958     DWORD dwFlags)
1959 {
1960     FIXME("(%p)->(%p, %08x)\n", iface, hBody, dwFlags);
1961     return E_NOTIMPL;
1962 }
1963
1964 static HRESULT WINAPI MimeMessage_MoveBody(
1965     IMimeMessage *iface,
1966     HBODY hBody,
1967     BODYLOCATION location)
1968 {
1969     FIXME("(%p)->(%d)\n", iface, location);
1970     return E_NOTIMPL;
1971 }
1972
1973 static void count_children(body_t *body, boolean recurse, ULONG *count)
1974 {
1975     body_t *child;
1976
1977     LIST_FOR_EACH_ENTRY(child, &body->children, body_t, entry)
1978     {
1979         (*count)++;
1980         if(recurse) count_children(child, recurse, count);
1981     }
1982 }
1983
1984 static HRESULT WINAPI MimeMessage_CountBodies(
1985     IMimeMessage *iface,
1986     HBODY hParent,
1987     boolean fRecurse,
1988     ULONG *pcBodies)
1989 {
1990     HRESULT hr;
1991     MimeMessage *This = (MimeMessage *)iface;
1992     body_t *body;
1993
1994     TRACE("(%p)->(%p, %s, %p)\n", iface, hParent, fRecurse ? "TRUE" : "FALSE", pcBodies);
1995
1996     hr = find_body(&This->body_tree, hParent, &body);
1997     if(hr != S_OK) return hr;
1998
1999     *pcBodies = 1;
2000     count_children(body, fRecurse, pcBodies);
2001
2002     return S_OK;
2003 }
2004
2005 static HRESULT find_next(IMimeMessage *msg, LPFINDBODY find_body, HBODY *out)
2006 {
2007     HRESULT hr;
2008     IMimeBody *mime_body;
2009     HBODY next;
2010
2011     if(find_body->dwReserved == 0)
2012         find_body->dwReserved = (DWORD)HBODY_ROOT;
2013     else
2014     {
2015         hr = IMimeMessage_GetBody(msg, IBL_FIRST, (HBODY)find_body->dwReserved, &next);
2016         if(hr == S_OK)
2017             find_body->dwReserved = (DWORD)next;
2018         else
2019         {
2020             hr = IMimeMessage_GetBody(msg, IBL_NEXT, (HBODY)find_body->dwReserved, &next);
2021             if(hr == S_OK)
2022                 find_body->dwReserved = (DWORD)next;
2023             else
2024                 return MIME_E_NOT_FOUND;
2025         }
2026     }
2027
2028     hr = IMimeMessage_BindToObject(msg, (HBODY)find_body->dwReserved, &IID_IMimeBody, (void**)&mime_body);
2029     if(IMimeBody_IsContentType(mime_body, find_body->pszPriType, find_body->pszSubType) == S_OK)
2030     {
2031         IMimeBody_Release(mime_body);
2032         *out = (HBODY)find_body->dwReserved;
2033         return S_OK;
2034     }
2035     IMimeBody_Release(mime_body);
2036     return find_next(msg, find_body, out);
2037 }
2038
2039 static HRESULT WINAPI MimeMessage_FindFirst(
2040     IMimeMessage *iface,
2041     LPFINDBODY pFindBody,
2042     LPHBODY phBody)
2043 {
2044     TRACE("(%p)->(%p, %p)\n", iface, pFindBody, phBody);
2045
2046     pFindBody->dwReserved = 0;
2047     return find_next(iface, pFindBody, phBody);
2048 }
2049
2050 static HRESULT WINAPI MimeMessage_FindNext(
2051     IMimeMessage *iface,
2052     LPFINDBODY pFindBody,
2053     LPHBODY phBody)
2054 {
2055     TRACE("(%p)->(%p, %p)\n", iface, pFindBody, phBody);
2056
2057     return find_next(iface, pFindBody, phBody);
2058 }
2059
2060 static HRESULT WINAPI MimeMessage_ResolveURL(
2061     IMimeMessage *iface,
2062     HBODY hRelated,
2063     LPCSTR pszBase,
2064     LPCSTR pszURL,
2065     DWORD dwFlags,
2066     LPHBODY phBody)
2067 {
2068     FIXME("(%p)->(%p, %s, %s, 0x%x, %p)\n", iface, hRelated, pszBase, pszURL, dwFlags, phBody);
2069     return E_NOTIMPL;
2070 }
2071
2072 static HRESULT WINAPI MimeMessage_ToMultipart(
2073     IMimeMessage *iface,
2074     HBODY hBody,
2075     LPCSTR pszSubType,
2076     LPHBODY phMultipart)
2077 {
2078     FIXME("(%p)->(%p, %s, %p)\n", iface, hBody, pszSubType, phMultipart);
2079     return E_NOTIMPL;
2080 }
2081
2082 static HRESULT WINAPI MimeMessage_GetBodyOffsets(
2083     IMimeMessage *iface,
2084     HBODY hBody,
2085     LPBODYOFFSETS pOffsets)
2086 {
2087     FIXME("(%p)->(%p, %p)\n", iface, hBody, pOffsets);
2088     return E_NOTIMPL;
2089 }
2090
2091 static HRESULT WINAPI MimeMessage_GetCharset(
2092     IMimeMessage *iface,
2093     LPHCHARSET phCharset)
2094 {
2095     FIXME("(%p)->(%p)\n", iface, phCharset);
2096     *phCharset = NULL;
2097     return S_OK;
2098 }
2099
2100 static HRESULT WINAPI MimeMessage_SetCharset(
2101     IMimeMessage *iface,
2102     HCHARSET hCharset,
2103     CSETAPPLYTYPE applytype)
2104 {
2105     FIXME("(%p)->(%p, %d)\n", iface, hCharset, applytype);
2106     return E_NOTIMPL;
2107 }
2108
2109 static HRESULT WINAPI MimeMessage_IsBodyType(
2110     IMimeMessage *iface,
2111     HBODY hBody,
2112     IMSGBODYTYPE bodytype)
2113 {
2114     HRESULT hr;
2115     IMimeBody *mime_body;
2116     TRACE("(%p)->(%p, %d)\n", iface, hBody, bodytype);
2117
2118     hr = IMimeMessage_BindToObject(iface, hBody, &IID_IMimeBody, (void**)&mime_body);
2119     if(hr != S_OK) return hr;
2120
2121     hr = IMimeBody_IsType(mime_body, bodytype);
2122     MimeBody_Release(mime_body);
2123     return hr;
2124 }
2125
2126 static HRESULT WINAPI MimeMessage_IsContentType(
2127     IMimeMessage *iface,
2128     HBODY hBody,
2129     LPCSTR pszPriType,
2130     LPCSTR pszSubType)
2131 {
2132     HRESULT hr;
2133     IMimeBody *mime_body;
2134     TRACE("(%p)->(%p, %s, %s)\n", iface, hBody, pszPriType, pszSubType);
2135
2136     hr = IMimeMessage_BindToObject(iface, hBody, &IID_IMimeBody, (void**)&mime_body);
2137     if(FAILED(hr)) return hr;
2138
2139     hr = IMimeBody_IsContentType(mime_body, pszPriType, pszSubType);
2140     IMimeBody_Release(mime_body);
2141     return hr;
2142 }
2143
2144 static HRESULT WINAPI MimeMessage_QueryBodyProp(
2145     IMimeMessage *iface,
2146     HBODY hBody,
2147     LPCSTR pszName,
2148     LPCSTR pszCriteria,
2149     boolean fSubString,
2150     boolean fCaseSensitive)
2151 {
2152     FIXME("(%p)->(%p, %s, %s, %s, %s)\n", iface, hBody, pszName, pszCriteria, fSubString ? "TRUE" : "FALSE", fCaseSensitive ? "TRUE" : "FALSE");
2153     return E_NOTIMPL;
2154 }
2155
2156 static HRESULT WINAPI MimeMessage_GetBodyProp(
2157     IMimeMessage *iface,
2158     HBODY hBody,
2159     LPCSTR pszName,
2160     DWORD dwFlags,
2161     LPPROPVARIANT pValue)
2162 {
2163     HRESULT hr;
2164     IMimeBody *mime_body;
2165
2166     TRACE("(%p)->(%p, %s, 0x%x, %p)\n", iface, hBody, pszName, dwFlags, pValue);
2167
2168     hr = IMimeMessage_BindToObject(iface, hBody, &IID_IMimeBody, (void**)&mime_body);
2169     if(hr != S_OK) return hr;
2170
2171     hr = IMimeBody_GetProp(mime_body, pszName, dwFlags, pValue);
2172     IMimeBody_Release(mime_body);
2173
2174     return hr;
2175 }
2176
2177 static HRESULT WINAPI MimeMessage_SetBodyProp(
2178     IMimeMessage *iface,
2179     HBODY hBody,
2180     LPCSTR pszName,
2181     DWORD dwFlags,
2182     LPCPROPVARIANT pValue)
2183 {
2184     FIXME("(%p)->(%p, %s, 0x%x, %p)\n", iface, hBody, pszName, dwFlags, pValue);
2185     return E_NOTIMPL;
2186 }
2187
2188 static HRESULT WINAPI MimeMessage_DeleteBodyProp(
2189     IMimeMessage *iface,
2190     HBODY hBody,
2191     LPCSTR pszName)
2192 {
2193     FIXME("(%p)->(%p, %s)\n", iface, hBody, pszName);
2194     return E_NOTIMPL;
2195 }
2196
2197 static HRESULT WINAPI MimeMessage_SetOption(
2198     IMimeMessage *iface,
2199     const TYPEDID oid,
2200     LPCPROPVARIANT pValue)
2201 {
2202     HRESULT hr = E_NOTIMPL;
2203     TRACE("(%p)->(%08x, %p)\n", iface, oid, pValue);
2204
2205     if(pValue->vt != TYPEDID_TYPE(oid))
2206     {
2207         WARN("Called with vartype %04x and oid %08x\n", pValue->vt, oid);
2208         return E_INVALIDARG;
2209     }
2210
2211     switch(oid)
2212     {
2213     case OID_HIDE_TNEF_ATTACHMENTS:
2214         FIXME("OID_HIDE_TNEF_ATTACHMENTS (value %d): ignoring\n", pValue->u.boolVal);
2215         hr = S_OK;
2216         break;
2217     case OID_SHOW_MACBINARY:
2218         FIXME("OID_SHOW_MACBINARY (value %d): ignoring\n", pValue->u.boolVal);
2219         hr = S_OK;
2220         break;
2221     default:
2222         FIXME("Unhandled oid %08x\n", oid);
2223     }
2224
2225     return hr;
2226 }
2227
2228 static HRESULT WINAPI MimeMessage_GetOption(
2229     IMimeMessage *iface,
2230     const TYPEDID oid,
2231     LPPROPVARIANT pValue)
2232 {
2233     FIXME("(%p)->(%08x, %p)\n", iface, oid, pValue);
2234     return E_NOTIMPL;
2235 }
2236
2237 /*** IMimeMessage methods ***/
2238 static HRESULT WINAPI MimeMessage_CreateWebPage(
2239     IMimeMessage *iface,
2240     IStream *pRootStm,
2241     LPWEBPAGEOPTIONS pOptions,
2242     IMimeMessageCallback *pCallback,
2243     IMoniker **ppMoniker)
2244 {
2245     FIXME("(%p)->(%p, %p, %p, %p)\n", iface, pRootStm, pOptions, pCallback, ppMoniker);
2246     *ppMoniker = NULL;
2247     return E_NOTIMPL;
2248 }
2249
2250 static HRESULT WINAPI MimeMessage_GetProp(
2251     IMimeMessage *iface,
2252     LPCSTR pszName,
2253     DWORD dwFlags,
2254     LPPROPVARIANT pValue)
2255 {
2256     FIXME("(%p)->(%s, 0x%x, %p)\n", iface, pszName, dwFlags, pValue);
2257     return E_NOTIMPL;
2258 }
2259
2260 static HRESULT WINAPI MimeMessage_SetProp(
2261     IMimeMessage *iface,
2262     LPCSTR pszName,
2263     DWORD dwFlags,
2264     LPCPROPVARIANT pValue)
2265 {
2266     FIXME("(%p)->(%s, 0x%x, %p)\n", iface, pszName, dwFlags, pValue);
2267     return E_NOTIMPL;
2268 }
2269
2270 static HRESULT WINAPI MimeMessage_DeleteProp(
2271     IMimeMessage *iface,
2272     LPCSTR pszName)
2273 {
2274     FIXME("(%p)->(%s)\n", iface, pszName);
2275     return E_NOTIMPL;
2276 }
2277
2278 static HRESULT WINAPI MimeMessage_QueryProp(
2279     IMimeMessage *iface,
2280     LPCSTR pszName,
2281     LPCSTR pszCriteria,
2282     boolean fSubString,
2283     boolean fCaseSensitive)
2284 {
2285     FIXME("(%p)->(%s, %s, %s, %s)\n", iface, pszName, pszCriteria, fSubString ? "TRUE" : "FALSE", fCaseSensitive ? "TRUE" : "FALSE");
2286     return E_NOTIMPL;
2287 }
2288
2289 static HRESULT WINAPI MimeMessage_GetTextBody(
2290     IMimeMessage *iface,
2291     DWORD dwTxtType,
2292     ENCODINGTYPE ietEncoding,
2293     IStream **pStream,
2294     LPHBODY phBody)
2295 {
2296     HRESULT hr;
2297     HBODY hbody;
2298     FINDBODY find_struct;
2299     IMimeBody *mime_body;
2300     static char text[] = "text";
2301     static char plain[] = "plain";
2302     static char html[] = "html";
2303
2304     TRACE("(%p)->(%d, %d, %p, %p)\n", iface, dwTxtType, ietEncoding, pStream, phBody);
2305
2306     find_struct.pszPriType = text;
2307
2308     switch(dwTxtType)
2309     {
2310     case TXT_PLAIN:
2311         find_struct.pszSubType = plain;
2312         break;
2313     case TXT_HTML:
2314         find_struct.pszSubType = html;
2315         break;
2316     default:
2317         return MIME_E_INVALID_TEXT_TYPE;
2318     }
2319
2320     hr = IMimeMessage_FindFirst(iface, &find_struct, &hbody);
2321     if(hr != S_OK)
2322     {
2323         TRACE("not found hr %08x\n", hr);
2324         *phBody = NULL;
2325         return hr;
2326     }
2327
2328     IMimeMessage_BindToObject(iface, hbody, &IID_IMimeBody, (void**)&mime_body);
2329
2330     IMimeBody_GetData(mime_body, ietEncoding, pStream);
2331     *phBody = hbody;
2332     IMimeBody_Release(mime_body);
2333     return hr;
2334 }
2335
2336 static HRESULT WINAPI MimeMessage_SetTextBody(
2337     IMimeMessage *iface,
2338     DWORD dwTxtType,
2339     ENCODINGTYPE ietEncoding,
2340     HBODY hAlternative,
2341     IStream *pStream,
2342     LPHBODY phBody)
2343 {
2344     FIXME("(%p)->(%d, %d, %p, %p, %p)\n", iface, dwTxtType, ietEncoding, hAlternative, pStream, phBody);
2345     return E_NOTIMPL;
2346 }
2347
2348 static HRESULT WINAPI MimeMessage_AttachObject(
2349     IMimeMessage *iface,
2350     REFIID riid,
2351     void *pvObject,
2352     LPHBODY phBody)
2353 {
2354     FIXME("(%p)->(%s, %p, %p)\n", iface, debugstr_guid(riid), pvObject, phBody);
2355     return E_NOTIMPL;
2356 }
2357
2358 static HRESULT WINAPI MimeMessage_AttachFile(
2359     IMimeMessage *iface,
2360     LPCSTR pszFilePath,
2361     IStream *pstmFile,
2362     LPHBODY phBody)
2363 {
2364     FIXME("(%p)->(%s, %p, %p)\n", iface, pszFilePath, pstmFile, phBody);
2365     return E_NOTIMPL;
2366 }
2367
2368 static HRESULT WINAPI MimeMessage_AttachURL(
2369     IMimeMessage *iface,
2370     LPCSTR pszBase,
2371     LPCSTR pszURL,
2372     DWORD dwFlags,
2373     IStream *pstmURL,
2374     LPSTR *ppszCIDURL,
2375     LPHBODY phBody)
2376 {
2377     FIXME("(%p)->(%s, %s, 0x%x, %p, %p, %p)\n", iface, pszBase, pszURL, dwFlags, pstmURL, ppszCIDURL, phBody);
2378     return E_NOTIMPL;
2379 }
2380
2381 static HRESULT WINAPI MimeMessage_GetAttachments(
2382     IMimeMessage *iface,
2383     ULONG *pcAttach,
2384     LPHBODY *pprghAttach)
2385 {
2386     HRESULT hr;
2387     FINDBODY find_struct;
2388     HBODY hbody;
2389     LPHBODY array;
2390     ULONG size = 10;
2391
2392     TRACE("(%p)->(%p, %p)\n", iface, pcAttach, pprghAttach);
2393
2394     *pcAttach = 0;
2395     array = CoTaskMemAlloc(size * sizeof(HBODY));
2396
2397     find_struct.pszPriType = find_struct.pszSubType = NULL;
2398     hr = IMimeMessage_FindFirst(iface, &find_struct, &hbody);
2399     while(hr == S_OK)
2400     {
2401         hr = IMimeMessage_IsContentType(iface, hbody, "multipart", NULL);
2402         TRACE("IsCT rets %08x %d\n", hr, *pcAttach);
2403         if(hr != S_OK)
2404         {
2405             if(*pcAttach + 1 > size)
2406             {
2407                 size *= 2;
2408                 array = CoTaskMemRealloc(array, size * sizeof(HBODY));
2409             }
2410             array[*pcAttach] = hbody;
2411             (*pcAttach)++;
2412         }
2413         hr = IMimeMessage_FindNext(iface, &find_struct, &hbody);
2414     }
2415
2416     *pprghAttach = array;
2417     return S_OK;
2418 }
2419
2420 static HRESULT WINAPI MimeMessage_GetAddressTable(
2421     IMimeMessage *iface,
2422     IMimeAddressTable **ppTable)
2423 {
2424     FIXME("(%p)->(%p)\n", iface, ppTable);
2425     return E_NOTIMPL;
2426 }
2427
2428 static HRESULT WINAPI MimeMessage_GetSender(
2429     IMimeMessage *iface,
2430     LPADDRESSPROPS pAddress)
2431 {
2432     FIXME("(%p)->(%p)\n", iface, pAddress);
2433     return E_NOTIMPL;
2434 }
2435
2436 static HRESULT WINAPI MimeMessage_GetAddressTypes(
2437     IMimeMessage *iface,
2438     DWORD dwAdrTypes,
2439     DWORD dwProps,
2440     LPADDRESSLIST pList)
2441 {
2442     FIXME("(%p)->(%d, %d, %p)\n", iface, dwAdrTypes, dwProps, pList);
2443     return E_NOTIMPL;
2444 }
2445
2446 static HRESULT WINAPI MimeMessage_GetAddressFormat(
2447     IMimeMessage *iface,
2448     DWORD dwAdrTypes,
2449     ADDRESSFORMAT format,
2450     LPSTR *ppszFormat)
2451 {
2452     FIXME("(%p)->(%d, %d, %p)\n", iface, dwAdrTypes, format, ppszFormat);
2453     return E_NOTIMPL;
2454 }
2455
2456 static HRESULT WINAPI MimeMessage_EnumAddressTypes(
2457     IMimeMessage *iface,
2458     DWORD dwAdrTypes,
2459     DWORD dwProps,
2460     IMimeEnumAddressTypes **ppEnum)
2461 {
2462     FIXME("(%p)->(%d, %d, %p)\n", iface, dwAdrTypes, dwProps, ppEnum);
2463     return E_NOTIMPL;
2464 }
2465
2466 static HRESULT WINAPI MimeMessage_SplitMessage(
2467     IMimeMessage *iface,
2468     ULONG cbMaxPart,
2469     IMimeMessageParts **ppParts)
2470 {
2471     FIXME("(%p)->(%d, %p)\n", iface, cbMaxPart, ppParts);
2472     return E_NOTIMPL;
2473 }
2474
2475 static HRESULT WINAPI MimeMessage_GetRootMoniker(
2476     IMimeMessage *iface,
2477     IMoniker **ppMoniker)
2478 {
2479     FIXME("(%p)->(%p)\n", iface, ppMoniker);
2480     return E_NOTIMPL;
2481 }
2482
2483 static const IMimeMessageVtbl MimeMessageVtbl =
2484 {
2485     MimeMessage_QueryInterface,
2486     MimeMessage_AddRef,
2487     MimeMessage_Release,
2488     MimeMessage_GetClassID,
2489     MimeMessage_IsDirty,
2490     MimeMessage_Load,
2491     MimeMessage_Save,
2492     MimeMessage_GetSizeMax,
2493     MimeMessage_InitNew,
2494     MimeMessage_GetMessageSource,
2495     MimeMessage_GetMessageSize,
2496     MimeMessage_LoadOffsetTable,
2497     MimeMessage_SaveOffsetTable,
2498     MimeMessage_GetFlags,
2499     MimeMessage_Commit,
2500     MimeMessage_HandsOffStorage,
2501     MimeMessage_BindToObject,
2502     MimeMessage_SaveBody,
2503     MimeMessage_InsertBody,
2504     MimeMessage_GetBody,
2505     MimeMessage_DeleteBody,
2506     MimeMessage_MoveBody,
2507     MimeMessage_CountBodies,
2508     MimeMessage_FindFirst,
2509     MimeMessage_FindNext,
2510     MimeMessage_ResolveURL,
2511     MimeMessage_ToMultipart,
2512     MimeMessage_GetBodyOffsets,
2513     MimeMessage_GetCharset,
2514     MimeMessage_SetCharset,
2515     MimeMessage_IsBodyType,
2516     MimeMessage_IsContentType,
2517     MimeMessage_QueryBodyProp,
2518     MimeMessage_GetBodyProp,
2519     MimeMessage_SetBodyProp,
2520     MimeMessage_DeleteBodyProp,
2521     MimeMessage_SetOption,
2522     MimeMessage_GetOption,
2523     MimeMessage_CreateWebPage,
2524     MimeMessage_GetProp,
2525     MimeMessage_SetProp,
2526     MimeMessage_DeleteProp,
2527     MimeMessage_QueryProp,
2528     MimeMessage_GetTextBody,
2529     MimeMessage_SetTextBody,
2530     MimeMessage_AttachObject,
2531     MimeMessage_AttachFile,
2532     MimeMessage_AttachURL,
2533     MimeMessage_GetAttachments,
2534     MimeMessage_GetAddressTable,
2535     MimeMessage_GetSender,
2536     MimeMessage_GetAddressTypes,
2537     MimeMessage_GetAddressFormat,
2538     MimeMessage_EnumAddressTypes,
2539     MimeMessage_SplitMessage,
2540     MimeMessage_GetRootMoniker,
2541 };
2542
2543 HRESULT MimeMessage_create(IUnknown *outer, void **obj)
2544 {
2545     MimeMessage *This;
2546
2547     TRACE("(%p, %p)\n", outer, obj);
2548
2549     if (outer)
2550     {
2551         FIXME("outer unknown not supported yet\n");
2552         return E_NOTIMPL;
2553     }
2554
2555     *obj = NULL;
2556
2557     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
2558     if (!This) return E_OUTOFMEMORY;
2559
2560     This->lpVtbl = &MimeMessageVtbl;
2561     This->refs = 1;
2562     This->stream = NULL;
2563     list_init(&This->body_tree);
2564     This->next_hbody = (HBODY)1;
2565
2566     *obj = (IMimeMessage *)&This->lpVtbl;
2567     return S_OK;
2568 }
2569
2570 /***********************************************************************
2571  *              MimeOleCreateMessage (INETCOMM.@)
2572  */
2573 HRESULT WINAPI MimeOleCreateMessage(IUnknown *pUnkOuter, IMimeMessage **ppMessage)
2574 {
2575     TRACE("(%p, %p)\n", pUnkOuter, ppMessage);
2576     return MimeMessage_create(NULL, (void **)ppMessage);
2577 }
2578
2579 /***********************************************************************
2580  *              MimeOleSetCompatMode (INETCOMM.@)
2581  */
2582 HRESULT WINAPI MimeOleSetCompatMode(DWORD dwMode)
2583 {
2584     FIXME("(0x%x)\n", dwMode);
2585     return S_OK;
2586 }
2587
2588 /***********************************************************************
2589  *              MimeOleCreateVirtualStream (INETCOMM.@)
2590  */
2591 HRESULT WINAPI MimeOleCreateVirtualStream(IStream **ppStream)
2592 {
2593     HRESULT hr;
2594     FIXME("(%p)\n", ppStream);
2595
2596     hr = CreateStreamOnHGlobal(NULL, TRUE, ppStream);
2597     return hr;
2598 }
2599
2600 typedef struct MimeSecurity
2601 {
2602     const IMimeSecurityVtbl *lpVtbl;
2603
2604     LONG refs;
2605 } MimeSecurity;
2606
2607 static HRESULT WINAPI MimeSecurity_QueryInterface(
2608         IMimeSecurity* iface,
2609         REFIID riid,
2610         void** obj)
2611 {
2612     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), obj);
2613
2614     if (IsEqualIID(riid, &IID_IUnknown) ||
2615         IsEqualIID(riid, &IID_IMimeSecurity))
2616     {
2617         *obj = iface;
2618         IUnknown_AddRef(iface);
2619         return S_OK;
2620     }
2621
2622     FIXME("no interface for %s\n", debugstr_guid(riid));
2623     *obj = NULL;
2624     return E_NOINTERFACE;
2625 }
2626
2627 static ULONG WINAPI MimeSecurity_AddRef(
2628         IMimeSecurity* iface)
2629 {
2630     MimeSecurity *This = (MimeSecurity *)iface;
2631     TRACE("(%p)->()\n", iface);
2632     return InterlockedIncrement(&This->refs);
2633 }
2634
2635 static ULONG WINAPI MimeSecurity_Release(
2636         IMimeSecurity* iface)
2637 {
2638     MimeSecurity *This = (MimeSecurity *)iface;
2639     ULONG refs;
2640
2641     TRACE("(%p)->()\n", iface);
2642
2643     refs = InterlockedDecrement(&This->refs);
2644     if (!refs)
2645     {
2646         HeapFree(GetProcessHeap(), 0, This);
2647     }
2648
2649     return refs;
2650 }
2651
2652 static HRESULT WINAPI MimeSecurity_InitNew(
2653         IMimeSecurity* iface)
2654 {
2655     FIXME("(%p)->(): stub\n", iface);
2656     return S_OK;
2657 }
2658
2659 static HRESULT WINAPI MimeSecurity_CheckInit(
2660         IMimeSecurity* iface)
2661 {
2662     FIXME("(%p)->(): stub\n", iface);
2663     return E_NOTIMPL;
2664 }
2665
2666 static HRESULT WINAPI MimeSecurity_EncodeMessage(
2667         IMimeSecurity* iface,
2668         IMimeMessageTree* pTree,
2669         DWORD dwFlags)
2670 {
2671     FIXME("(%p)->(%p, %08x): stub\n", iface, pTree, dwFlags);
2672     return E_NOTIMPL;
2673 }
2674
2675 static HRESULT WINAPI MimeSecurity_EncodeBody(
2676         IMimeSecurity* iface,
2677         IMimeMessageTree* pTree,
2678         HBODY hEncodeRoot,
2679         DWORD dwFlags)
2680 {
2681     FIXME("(%p)->(%p, %p, %08x): stub\n", iface, pTree, hEncodeRoot, dwFlags);
2682     return E_NOTIMPL;
2683 }
2684
2685 static HRESULT WINAPI MimeSecurity_DecodeMessage(
2686         IMimeSecurity* iface,
2687         IMimeMessageTree* pTree,
2688         DWORD dwFlags)
2689 {
2690     FIXME("(%p)->(%p, %08x): stub\n", iface, pTree, dwFlags);
2691     return E_NOTIMPL;
2692 }
2693
2694 static HRESULT WINAPI MimeSecurity_DecodeBody(
2695         IMimeSecurity* iface,
2696         IMimeMessageTree* pTree,
2697         HBODY hDecodeRoot,
2698         DWORD dwFlags)
2699 {
2700     FIXME("(%p)->(%p, %p, %08x): stub\n", iface, pTree, hDecodeRoot, dwFlags);
2701     return E_NOTIMPL;
2702 }
2703
2704 static HRESULT WINAPI MimeSecurity_EnumCertificates(
2705         IMimeSecurity* iface,
2706         HCAPICERTSTORE hc,
2707         DWORD dwUsage,
2708         PCX509CERT pPrev,
2709         PCX509CERT* ppCert)
2710 {
2711     FIXME("(%p)->(%p, %08x, %p, %p): stub\n", iface, hc, dwUsage, pPrev, ppCert);
2712     return E_NOTIMPL;
2713 }
2714
2715 static HRESULT WINAPI MimeSecurity_GetCertificateName(
2716         IMimeSecurity* iface,
2717         const PCX509CERT pX509Cert,
2718         const CERTNAMETYPE cn,
2719         LPSTR* ppszName)
2720 {
2721     FIXME("(%p)->(%p, %08x, %p): stub\n", iface, pX509Cert, cn, ppszName);
2722     return E_NOTIMPL;
2723 }
2724
2725 static HRESULT WINAPI MimeSecurity_GetMessageType(
2726         IMimeSecurity* iface,
2727         const HWND hwndParent,
2728         IMimeBody* pBody,
2729         DWORD* pdwSecType)
2730 {
2731     FIXME("(%p)->(%p, %p, %p): stub\n", iface, hwndParent, pBody, pdwSecType);
2732     return E_NOTIMPL;
2733 }
2734
2735 static HRESULT WINAPI MimeSecurity_GetCertData(
2736         IMimeSecurity* iface,
2737         const PCX509CERT pX509Cert,
2738         const CERTDATAID dataid,
2739         LPPROPVARIANT pValue)
2740 {
2741     FIXME("(%p)->(%p, %x, %p): stub\n", iface, pX509Cert, dataid, pValue);
2742     return E_NOTIMPL;
2743 }
2744
2745
2746 static const IMimeSecurityVtbl MimeSecurityVtbl =
2747 {
2748     MimeSecurity_QueryInterface,
2749     MimeSecurity_AddRef,
2750     MimeSecurity_Release,
2751     MimeSecurity_InitNew,
2752     MimeSecurity_CheckInit,
2753     MimeSecurity_EncodeMessage,
2754     MimeSecurity_EncodeBody,
2755     MimeSecurity_DecodeMessage,
2756     MimeSecurity_DecodeBody,
2757     MimeSecurity_EnumCertificates,
2758     MimeSecurity_GetCertificateName,
2759     MimeSecurity_GetMessageType,
2760     MimeSecurity_GetCertData
2761 };
2762
2763 HRESULT MimeSecurity_create(IUnknown *outer, void **obj)
2764 {
2765     MimeSecurity *This;
2766
2767     *obj = NULL;
2768
2769     if (outer) return CLASS_E_NOAGGREGATION;
2770
2771     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
2772     if (!This) return E_OUTOFMEMORY;
2773
2774     This->lpVtbl = &MimeSecurityVtbl;
2775     This->refs = 1;
2776
2777     *obj = (IMimeSecurity *)&This->lpVtbl;
2778     return S_OK;
2779 }
2780
2781 /***********************************************************************
2782  *              MimeOleCreateSecurity (INETCOMM.@)
2783  */
2784 HRESULT WINAPI MimeOleCreateSecurity(IMimeSecurity **ppSecurity)
2785 {
2786     return MimeSecurity_create(NULL, (void **)ppSecurity);
2787 }
2788
2789 typedef struct
2790 {
2791     IMimeAllocatorVtbl *lpVtbl;
2792 } MimeAllocator;
2793
2794 static HRESULT WINAPI MimeAlloc_QueryInterface(
2795         IMimeAllocator* iface,
2796         REFIID riid,
2797         void **obj)
2798 {
2799     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), obj);
2800
2801     if (IsEqualIID(riid, &IID_IUnknown) ||
2802         IsEqualIID(riid, &IID_IMalloc) ||
2803         IsEqualIID(riid, &IID_IMimeAllocator))
2804     {
2805         *obj = iface;
2806         IUnknown_AddRef(iface);
2807         return S_OK;
2808     }
2809
2810     FIXME("no interface for %s\n", debugstr_guid(riid));
2811     *obj = NULL;
2812     return E_NOINTERFACE;
2813 }
2814
2815 static ULONG WINAPI MimeAlloc_AddRef(
2816         IMimeAllocator* iface)
2817 {
2818     return 2;
2819 }
2820
2821 static ULONG WINAPI MimeAlloc_Release(
2822         IMimeAllocator* iface)
2823 {
2824     return 1;
2825 }
2826
2827 static LPVOID WINAPI MimeAlloc_Alloc(
2828         IMimeAllocator* iface,
2829         ULONG cb)
2830 {
2831     return CoTaskMemAlloc(cb);
2832 }
2833
2834 static LPVOID WINAPI MimeAlloc_Realloc(
2835         IMimeAllocator* iface,
2836         LPVOID pv,
2837         ULONG cb)
2838 {
2839     return CoTaskMemRealloc(pv, cb);
2840 }
2841
2842 static void WINAPI MimeAlloc_Free(
2843         IMimeAllocator* iface,
2844         LPVOID pv)
2845 {
2846     CoTaskMemFree(pv);
2847 }
2848
2849 static ULONG WINAPI MimeAlloc_GetSize(
2850         IMimeAllocator* iface,
2851         LPVOID pv)
2852 {
2853     FIXME("stub\n");
2854     return 0;
2855 }
2856
2857 static int WINAPI MimeAlloc_DidAlloc(
2858         IMimeAllocator* iface,
2859         LPVOID pv)
2860 {
2861     FIXME("stub\n");
2862     return 0;
2863 }
2864
2865 static void WINAPI MimeAlloc_HeapMinimize(
2866         IMimeAllocator* iface)
2867 {
2868     FIXME("stub\n");
2869     return;
2870 }
2871
2872 static HRESULT WINAPI MimeAlloc_FreeParamInfoArray(
2873         IMimeAllocator* iface,
2874         ULONG cParams,
2875         LPMIMEPARAMINFO prgParam,
2876         boolean fFreeArray)
2877 {
2878     ULONG i;
2879     TRACE("(%p)->(%d, %p, %d)\n", iface, cParams, prgParam, fFreeArray);
2880
2881     for(i = 0; i < cParams; i++)
2882     {
2883         IMimeAllocator_Free(iface, prgParam[i].pszName);
2884         IMimeAllocator_Free(iface, prgParam[i].pszData);
2885     }
2886     if(fFreeArray) IMimeAllocator_Free(iface, prgParam);
2887     return S_OK;
2888 }
2889
2890 static HRESULT WINAPI MimeAlloc_FreeAddressList(
2891         IMimeAllocator* iface,
2892         LPADDRESSLIST pList)
2893 {
2894     FIXME("stub\n");
2895     return E_NOTIMPL;
2896 }
2897
2898 static HRESULT WINAPI MimeAlloc_FreeAddressProps(
2899         IMimeAllocator* iface,
2900         LPADDRESSPROPS pAddress)
2901 {
2902     FIXME("stub\n");
2903     return E_NOTIMPL;
2904 }
2905
2906 static HRESULT WINAPI MimeAlloc_ReleaseObjects(
2907         IMimeAllocator* iface,
2908         ULONG cObjects,
2909         IUnknown **prgpUnknown,
2910         boolean fFreeArray)
2911 {
2912     FIXME("stub\n");
2913     return E_NOTIMPL;
2914 }
2915
2916
2917 static HRESULT WINAPI MimeAlloc_FreeEnumHeaderRowArray(
2918         IMimeAllocator* iface,
2919         ULONG cRows,
2920         LPENUMHEADERROW prgRow,
2921         boolean fFreeArray)
2922 {
2923     FIXME("stub\n");
2924     return E_NOTIMPL;
2925 }
2926
2927 static HRESULT WINAPI MimeAlloc_FreeEnumPropertyArray(
2928         IMimeAllocator* iface,
2929         ULONG cProps,
2930         LPENUMPROPERTY prgProp,
2931         boolean fFreeArray)
2932 {
2933     FIXME("stub\n");
2934     return E_NOTIMPL;
2935 }
2936
2937 static HRESULT WINAPI MimeAlloc_FreeThumbprint(
2938         IMimeAllocator* iface,
2939         THUMBBLOB *pthumbprint)
2940 {
2941     FIXME("stub\n");
2942     return E_NOTIMPL;
2943 }
2944
2945
2946 static HRESULT WINAPI MimeAlloc_PropVariantClear(
2947         IMimeAllocator* iface,
2948         LPPROPVARIANT pProp)
2949 {
2950     FIXME("stub\n");
2951     return E_NOTIMPL;
2952 }
2953
2954 static IMimeAllocatorVtbl mime_alloc_vtbl =
2955 {
2956     MimeAlloc_QueryInterface,
2957     MimeAlloc_AddRef,
2958     MimeAlloc_Release,
2959     MimeAlloc_Alloc,
2960     MimeAlloc_Realloc,
2961     MimeAlloc_Free,
2962     MimeAlloc_GetSize,
2963     MimeAlloc_DidAlloc,
2964     MimeAlloc_HeapMinimize,
2965     MimeAlloc_FreeParamInfoArray,
2966     MimeAlloc_FreeAddressList,
2967     MimeAlloc_FreeAddressProps,
2968     MimeAlloc_ReleaseObjects,
2969     MimeAlloc_FreeEnumHeaderRowArray,
2970     MimeAlloc_FreeEnumPropertyArray,
2971     MimeAlloc_FreeThumbprint,
2972     MimeAlloc_PropVariantClear
2973 };
2974
2975 static MimeAllocator mime_allocator =
2976 {
2977     &mime_alloc_vtbl
2978 };
2979
2980 HRESULT MimeAllocator_create(IUnknown *outer, void **obj)
2981 {
2982     if(outer) return CLASS_E_NOAGGREGATION;
2983
2984     *obj = &mime_allocator;
2985     return S_OK;
2986 }
2987
2988 HRESULT WINAPI MimeOleGetAllocator(IMimeAllocator **alloc)
2989 {
2990     return MimeAllocator_create(NULL, (void**)alloc);
2991 }