winex11: add owned windows to taskbar if owner is not mapped
[wine] / dlls / inetcomm / mimeole.c
1 /*
2  * MIME OLE Interfaces
3  *
4  * Copyright 2006 Robert Shearman for CodeWeavers
5  * Copyright 2007 Huw Davies for CodeWeavers
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #define COBJMACROS
23 #define NONAMELESSUNION
24
25 #include <stdarg.h>
26 #include <stdio.h>
27
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winuser.h"
31 #include "objbase.h"
32 #include "ole2.h"
33 #include "mimeole.h"
34
35 #include "wine/list.h"
36 #include "wine/debug.h"
37
38 #include "inetcomm_private.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(inetcomm);
41
42 typedef struct
43 {
44     LPCSTR     name;
45     DWORD      id;
46     DWORD      flags; /* MIMEPROPFLAGS */
47     VARTYPE    default_vt;
48 } property_t;
49
50 typedef struct
51 {
52     struct list entry;
53     property_t prop;
54 } property_list_entry_t;
55
56 static const property_t default_props[] =
57 {
58     {"References",                   PID_HDR_REFS,       0,                               VT_LPSTR},
59     {"Subject",                      PID_HDR_SUBJECT,    0,                               VT_LPSTR},
60     {"From",                         PID_HDR_FROM,       MPF_ADDRESS,                     VT_LPSTR},
61     {"Message-ID",                   PID_HDR_MESSAGEID,  0,                               VT_LPSTR},
62     {"Return-Path",                  PID_HDR_RETURNPATH, MPF_ADDRESS,                     VT_LPSTR},
63     {"Date",                         PID_HDR_DATE,       0,                               VT_LPSTR},
64     {"Received",                     PID_HDR_RECEIVED,   0,                               VT_LPSTR},
65     {"Reply-To",                     PID_HDR_REPLYTO,    MPF_ADDRESS,                     VT_LPSTR},
66     {"X-Mailer",                     PID_HDR_XMAILER,    0,                               VT_LPSTR},
67     {"Bcc",                          PID_HDR_BCC,        MPF_ADDRESS,                     VT_LPSTR},
68     {"MIME-Version",                 PID_HDR_MIMEVER,    MPF_MIME,                        VT_LPSTR},
69     {"Content-Type",                 PID_HDR_CNTTYPE,    MPF_MIME | MPF_HASPARAMS,        VT_LPSTR},
70     {"Content-Transfer-Encoding",    PID_HDR_CNTXFER,    MPF_MIME,                        VT_LPSTR},
71     {"Content-ID",                   PID_HDR_CNTID,      MPF_MIME,                        VT_LPSTR},
72     {"Content-Disposition",          PID_HDR_CNTDISP,    MPF_MIME,                        VT_LPSTR},
73     {"To",                           PID_HDR_TO,         MPF_ADDRESS,                     VT_LPSTR},
74     {"Cc",                           PID_HDR_CC,         MPF_ADDRESS,                     VT_LPSTR},
75     {"Sender",                       PID_HDR_SENDER,     MPF_ADDRESS,                     VT_LPSTR},
76     {"In-Reply-To",                  PID_HDR_INREPLYTO,  0,                               VT_LPSTR},
77     {NULL,                           0,                  0,                               0}
78 };
79
80 typedef struct
81 {
82     struct list entry;
83     char *name;
84     char *value;
85 } param_t;
86
87 typedef struct
88 {
89     struct list entry;
90     const property_t *prop;
91     PROPVARIANT value;
92     struct list params;
93 } header_t;
94
95 typedef struct MimeBody
96 {
97     const IMimeBodyVtbl *lpVtbl;
98     LONG refs;
99
100     HBODY handle;
101
102     struct list headers;
103     struct list new_props; /* FIXME: This should be in a PropertySchema */
104     DWORD next_prop_id;
105     char *content_pri_type;
106     char *content_sub_type;
107     ENCODINGTYPE encoding;
108     void *data;
109     IID data_iid;
110     BODYOFFSETS body_offsets;
111 } MimeBody;
112
113 static inline MimeBody *impl_from_IMimeBody( IMimeBody *iface )
114 {
115     return (MimeBody *)((char*)iface - FIELD_OFFSET(MimeBody, lpVtbl));
116 }
117
118 static LPSTR strdupA(LPCSTR str)
119 {
120     char *ret;
121     int len = strlen(str);
122     ret = HeapAlloc(GetProcessHeap(), 0, len + 1);
123     memcpy(ret, str, len + 1);
124     return ret;
125 }
126
127 #define PARSER_BUF_SIZE 1024
128
129 /*****************************************************
130  *        copy_headers_to_buf [internal]
131  *
132  * Copies the headers into a '\0' terminated memory block and leave
133  * the stream's current position set to after the blank line.
134  */
135 static HRESULT copy_headers_to_buf(IStream *stm, char **ptr)
136 {
137     char *buf = NULL;
138     DWORD size = PARSER_BUF_SIZE, offset = 0, last_end = 0;
139     HRESULT hr;
140     int done = 0;
141
142     *ptr = NULL;
143
144     do
145     {
146         char *end;
147         DWORD read;
148
149         if(!buf)
150             buf = HeapAlloc(GetProcessHeap(), 0, size + 1);
151         else
152         {
153             size *= 2;
154             buf = HeapReAlloc(GetProcessHeap(), 0, buf, size + 1);
155         }
156         if(!buf)
157         {
158             hr = E_OUTOFMEMORY;
159             goto fail;
160         }
161
162         hr = IStream_Read(stm, buf + offset, size - offset, &read);
163         if(FAILED(hr)) goto fail;
164
165         offset += read;
166         buf[offset] = '\0';
167
168         if(read == 0) done = 1;
169
170         while(!done && (end = strstr(buf + last_end, "\r\n")))
171         {
172             DWORD new_end = end - buf + 2;
173             if(new_end - last_end == 2)
174             {
175                 LARGE_INTEGER off;
176                 off.QuadPart = new_end;
177                 IStream_Seek(stm, off, STREAM_SEEK_SET, NULL);
178                 buf[new_end] = '\0';
179                 done = 1;
180             }
181             else
182                 last_end = new_end;
183         }
184     } while(!done);
185
186     *ptr = buf;
187     return S_OK;
188
189 fail:
190     HeapFree(GetProcessHeap(), 0, buf);
191     return hr;
192 }
193
194 static header_t *read_prop(MimeBody *body, char **ptr)
195 {
196     char *colon = strchr(*ptr, ':');
197     const property_t *prop;
198     header_t *ret;
199
200     if(!colon) return NULL;
201
202     *colon = '\0';
203
204     for(prop = default_props; prop->name; prop++)
205     {
206         if(!strcasecmp(*ptr, prop->name))
207         {
208             TRACE("%s: found match with default property id %d\n", *ptr, prop->id);
209             break;
210         }
211     }
212
213     if(!prop->name)
214     {
215         property_list_entry_t *prop_entry;
216         LIST_FOR_EACH_ENTRY(prop_entry, &body->new_props, property_list_entry_t, entry)
217         {
218             if(!strcasecmp(*ptr, prop_entry->prop.name))
219             {
220                 TRACE("%s: found match with already added new property id %d\n", *ptr, prop_entry->prop.id);
221                 prop = &prop_entry->prop;
222                 break;
223             }
224         }
225         if(!prop->name)
226         {
227             prop_entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*prop_entry));
228             prop_entry->prop.name = strdupA(*ptr);
229             prop_entry->prop.id = body->next_prop_id++;
230             prop_entry->prop.flags = 0;
231             prop_entry->prop.default_vt = VT_LPSTR;
232             list_add_tail(&body->new_props, &prop_entry->entry);
233             prop = &prop_entry->prop;
234             TRACE("%s: allocating new prop id %d\n", *ptr, prop_entry->prop.id);
235         }
236     }
237
238     ret = HeapAlloc(GetProcessHeap(), 0, sizeof(*ret));
239     ret->prop = prop;
240     PropVariantInit(&ret->value);
241     list_init(&ret->params);
242     *ptr = colon + 1;
243
244     return ret;
245 }
246
247 static void unfold_header(char *header, int len)
248 {
249     char *start = header, *cp = header;
250
251     do {
252         while(*cp == ' ' || *cp == '\t')
253         {
254             cp++;
255             len--;
256         }
257         if(cp != start)
258             memmove(start, cp, len + 1);
259
260         cp = strstr(start, "\r\n");
261         len -= (cp - start);
262         start = cp;
263         *start = ' ';
264         start++;
265         len--;
266         cp += 2;
267     } while(*cp == ' ' || *cp == '\t');
268
269     *(start - 1) = '\0';
270 }
271
272 static char *unquote_string(const char *str)
273 {
274     int quoted = 0;
275     char *ret, *cp;
276
277     while(*str == ' ' || *str == '\t') str++;
278
279     if(*str == '"')
280     {
281         quoted = 1;
282         str++;
283     }
284     ret = strdupA(str);
285     for(cp = ret; *cp; cp++)
286     {
287         if(*cp == '\\')
288             memmove(cp, cp + 1, strlen(cp + 1) + 1);
289         else if(*cp == '"')
290         {
291             if(!quoted)
292             {
293                 WARN("quote in unquoted string\n");
294             }
295             else
296             {
297                 *cp = '\0';
298                 break;
299             }
300         }
301     }
302     return ret;
303 }
304
305 static void add_param(header_t *header, const char *p)
306 {
307     const char *key = p, *value, *cp = p;
308     param_t *param;
309     char *name;
310
311     TRACE("got param %s\n", p);
312
313     while (*key == ' ' || *key == '\t' ) key++;
314
315     cp = strchr(key, '=');
316     if(!cp)
317     {
318         WARN("malformed parameter - skipping\n");
319         return;
320     }
321
322     name = HeapAlloc(GetProcessHeap(), 0, cp - key + 1);
323     memcpy(name, key, cp - key);
324     name[cp - key] = '\0';
325
326     value = cp + 1;
327
328     param = HeapAlloc(GetProcessHeap(), 0, sizeof(*param));
329     param->name = name;
330     param->value = unquote_string(value);
331     list_add_tail(&header->params, &param->entry);
332 }
333
334 static void split_params(header_t *header, char *value)
335 {
336     char *cp = value, *start = value;
337     int in_quote = 0;
338     int done_value = 0;
339
340     while(*cp)
341     {
342         if(!in_quote && *cp == ';')
343         {
344             *cp = '\0';
345             if(done_value) add_param(header, start);
346             done_value = 1;
347             start = cp + 1;
348         }
349         else if(*cp == '"')
350             in_quote = !in_quote;
351         cp++;
352     }
353     if(done_value) add_param(header, start);
354 }
355
356 static void read_value(header_t *header, char **cur)
357 {
358     char *end = *cur, *value;
359     DWORD len;
360
361     do {
362         end = strstr(end, "\r\n");
363         end += 2;
364     } while(*end == ' ' || *end == '\t');
365
366     len = end - *cur;
367     value = HeapAlloc(GetProcessHeap(), 0, len + 1);
368     memcpy(value, *cur, len);
369     value[len] = '\0';
370
371     unfold_header(value, len);
372     TRACE("value %s\n", debugstr_a(value));
373
374     if(header->prop->flags & MPF_HASPARAMS)
375     {
376         split_params(header, value);
377         TRACE("value w/o params %s\n", debugstr_a(value));
378     }
379
380     header->value.vt = VT_LPSTR;
381     header->value.u.pszVal = value;
382
383     *cur = end;
384 }
385
386 static void init_content_type(MimeBody *body, header_t *header)
387 {
388     char *slash;
389     DWORD len;
390
391     if(header->prop->id != PID_HDR_CNTTYPE)
392     {
393         ERR("called with header %s\n", header->prop->name);
394         return;
395     }
396
397     slash = strchr(header->value.u.pszVal, '/');
398     if(!slash)
399     {
400         WARN("malformed context type value\n");
401         return;
402     }
403     len = slash - header->value.u.pszVal;
404     body->content_pri_type = HeapAlloc(GetProcessHeap(), 0, len + 1);
405     memcpy(body->content_pri_type, header->value.u.pszVal, len);
406     body->content_pri_type[len] = '\0';
407     body->content_sub_type = strdupA(slash + 1);
408 }
409
410 static HRESULT parse_headers(MimeBody *body, IStream *stm)
411 {
412     char *header_buf, *cur_header_ptr;
413     HRESULT hr;
414     header_t *header;
415
416     hr = copy_headers_to_buf(stm, &header_buf);
417     if(FAILED(hr)) return hr;
418
419     cur_header_ptr = header_buf;
420     while((header = read_prop(body, &cur_header_ptr)))
421     {
422         read_value(header, &cur_header_ptr);
423         list_add_tail(&body->headers, &header->entry);
424
425         if(header->prop->id == PID_HDR_CNTTYPE)
426             init_content_type(body, header);
427     }
428
429     HeapFree(GetProcessHeap(), 0, header_buf);
430     return hr;
431 }
432
433 static void empty_param_list(struct list *list)
434 {
435     param_t *param, *cursor2;
436
437     LIST_FOR_EACH_ENTRY_SAFE(param, cursor2, list, param_t, entry)
438     {
439         list_remove(&param->entry);
440         HeapFree(GetProcessHeap(), 0, param->name);
441         HeapFree(GetProcessHeap(), 0, param->value);
442         HeapFree(GetProcessHeap(), 0, param);
443     }
444 }
445
446 static void empty_header_list(struct list *list)
447 {
448     header_t *header, *cursor2;
449
450     LIST_FOR_EACH_ENTRY_SAFE(header, cursor2, list, header_t, entry)
451     {
452         list_remove(&header->entry);
453         PropVariantClear(&header->value);
454         empty_param_list(&header->params);
455         HeapFree(GetProcessHeap(), 0, header);
456     }
457 }
458
459 static void empty_new_prop_list(struct list *list)
460 {
461     property_list_entry_t *prop, *cursor2;
462
463     LIST_FOR_EACH_ENTRY_SAFE(prop, cursor2, list, property_list_entry_t, entry)
464     {
465         list_remove(&prop->entry);
466         HeapFree(GetProcessHeap(), 0, (char *)prop->prop.name);
467         HeapFree(GetProcessHeap(), 0, prop);
468     }
469 }
470
471 static void release_data(REFIID riid, void *data)
472 {
473     if(!data) return;
474
475     if(IsEqualIID(riid, &IID_IStream))
476         IStream_Release((IStream *)data);
477     else
478         FIXME("Unhandled data format %s\n", debugstr_guid(riid));
479 }
480
481 static HRESULT find_prop(MimeBody *body, const char *name, header_t **prop)
482 {
483     header_t *header;
484
485     *prop = NULL;
486
487     LIST_FOR_EACH_ENTRY(header, &body->headers, header_t, entry)
488     {
489         if(!strcasecmp(name, header->prop->name))
490         {
491             *prop = header;
492             return S_OK;
493         }
494     }
495
496     return MIME_E_NOT_FOUND;
497 }
498
499 static HRESULT WINAPI MimeBody_QueryInterface(IMimeBody* iface,
500                                      REFIID riid,
501                                      void** ppvObject)
502 {
503     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppvObject);
504
505     *ppvObject = NULL;
506
507     if (IsEqualIID(riid, &IID_IUnknown) ||
508         IsEqualIID(riid, &IID_IPersist) ||
509         IsEqualIID(riid, &IID_IPersistStreamInit) ||
510         IsEqualIID(riid, &IID_IMimePropertySet) ||
511         IsEqualIID(riid, &IID_IMimeBody))
512     {
513         *ppvObject = iface;
514     }
515
516     if(*ppvObject)
517     {
518         IUnknown_AddRef((IUnknown*)*ppvObject);
519         return S_OK;
520     }
521
522     FIXME("no interface for %s\n", debugstr_guid(riid));
523     return E_NOINTERFACE;
524 }
525
526 static ULONG WINAPI MimeBody_AddRef(IMimeBody* iface)
527 {
528     MimeBody *This = impl_from_IMimeBody(iface);
529     TRACE("(%p)->()\n", iface);
530     return InterlockedIncrement(&This->refs);
531 }
532
533 static ULONG WINAPI MimeBody_Release(IMimeBody* iface)
534 {
535     MimeBody *This = impl_from_IMimeBody(iface);
536     ULONG refs;
537
538     TRACE("(%p)->()\n", iface);
539
540     refs = InterlockedDecrement(&This->refs);
541     if (!refs)
542     {
543         empty_header_list(&This->headers);
544         empty_new_prop_list(&This->new_props);
545
546         HeapFree(GetProcessHeap(), 0, This->content_pri_type);
547         HeapFree(GetProcessHeap(), 0, This->content_sub_type);
548
549         release_data(&This->data_iid, This->data);
550
551         HeapFree(GetProcessHeap(), 0, This);
552     }
553
554     return refs;
555 }
556
557 static HRESULT WINAPI MimeBody_GetClassID(
558                                  IMimeBody* iface,
559                                  CLSID* pClassID)
560 {
561     FIXME("stub\n");
562     return E_NOTIMPL;
563 }
564
565
566 static HRESULT WINAPI MimeBody_IsDirty(
567                               IMimeBody* iface)
568 {
569     FIXME("stub\n");
570     return E_NOTIMPL;
571 }
572
573 static HRESULT WINAPI MimeBody_Load(
574                            IMimeBody* iface,
575                            LPSTREAM pStm)
576 {
577     MimeBody *This = impl_from_IMimeBody(iface);
578     TRACE("(%p)->(%p)\n", iface, pStm);
579     return parse_headers(This, pStm);
580 }
581
582 static HRESULT WINAPI MimeBody_Save(
583                            IMimeBody* iface,
584                            LPSTREAM pStm,
585                            BOOL fClearDirty)
586 {
587     FIXME("stub\n");
588     return E_NOTIMPL;
589 }
590
591 static HRESULT WINAPI MimeBody_GetSizeMax(
592                                  IMimeBody* iface,
593                                  ULARGE_INTEGER* pcbSize)
594 {
595     FIXME("stub\n");
596     return E_NOTIMPL;
597 }
598
599 static HRESULT WINAPI MimeBody_InitNew(
600                               IMimeBody* iface)
601 {
602     TRACE("%p->()\n", iface);
603     return S_OK;
604 }
605
606 static HRESULT WINAPI MimeBody_GetPropInfo(
607                                   IMimeBody* iface,
608                                   LPCSTR pszName,
609                                   LPMIMEPROPINFO pInfo)
610 {
611     FIXME("stub\n");
612     return E_NOTIMPL;
613 }
614
615 static HRESULT WINAPI MimeBody_SetPropInfo(
616                                   IMimeBody* iface,
617                                   LPCSTR pszName,
618                                   LPCMIMEPROPINFO pInfo)
619 {
620     FIXME("stub\n");
621     return E_NOTIMPL;
622 }
623
624 static HRESULT WINAPI MimeBody_GetProp(
625                               IMimeBody* iface,
626                               LPCSTR pszName,
627                               DWORD dwFlags,
628                               LPPROPVARIANT pValue)
629 {
630     MimeBody *This = impl_from_IMimeBody(iface);
631     TRACE("(%p)->(%s, %d, %p)\n", This, pszName, dwFlags, pValue);
632
633     if(!strcasecmp(pszName, "att:pri-content-type"))
634     {
635         PropVariantClear(pValue);
636         pValue->vt = VT_LPSTR;
637         pValue->u.pszVal = strdupA(This->content_pri_type);
638         return S_OK;
639     }
640
641     FIXME("stub!\n");
642     return E_FAIL;
643 }
644
645 static HRESULT WINAPI MimeBody_SetProp(
646                               IMimeBody* iface,
647                               LPCSTR pszName,
648                               DWORD dwFlags,
649                               LPCPROPVARIANT pValue)
650 {
651     FIXME("stub\n");
652     return E_NOTIMPL;
653 }
654
655 static HRESULT WINAPI MimeBody_AppendProp(
656                                  IMimeBody* iface,
657                                  LPCSTR pszName,
658                                  DWORD dwFlags,
659                                  LPPROPVARIANT pValue)
660 {
661     FIXME("stub\n");
662     return E_NOTIMPL;
663 }
664
665 static HRESULT WINAPI MimeBody_DeleteProp(
666                                  IMimeBody* iface,
667                                  LPCSTR pszName)
668 {
669     FIXME("stub\n");
670     return E_NOTIMPL;
671 }
672
673 static HRESULT WINAPI MimeBody_CopyProps(
674                                 IMimeBody* iface,
675                                 ULONG cNames,
676                                 LPCSTR* prgszName,
677                                 IMimePropertySet* pPropertySet)
678 {
679     FIXME("stub\n");
680     return E_NOTIMPL;
681 }
682
683 static HRESULT WINAPI MimeBody_MoveProps(
684                                 IMimeBody* iface,
685                                 ULONG cNames,
686                                 LPCSTR* prgszName,
687                                 IMimePropertySet* pPropertySet)
688 {
689     FIXME("stub\n");
690     return E_NOTIMPL;
691 }
692
693 static HRESULT WINAPI MimeBody_DeleteExcept(
694                                    IMimeBody* iface,
695                                    ULONG cNames,
696                                    LPCSTR* prgszName)
697 {
698     FIXME("stub\n");
699     return E_NOTIMPL;
700 }
701
702 static HRESULT WINAPI MimeBody_QueryProp(
703                                 IMimeBody* iface,
704                                 LPCSTR pszName,
705                                 LPCSTR pszCriteria,
706                                 boolean fSubString,
707                                 boolean fCaseSensitive)
708 {
709     FIXME("stub\n");
710     return E_NOTIMPL;
711 }
712
713 static HRESULT WINAPI MimeBody_GetCharset(
714                                  IMimeBody* iface,
715                                  LPHCHARSET phCharset)
716 {
717     FIXME("stub\n");
718     *phCharset = NULL;
719     return S_OK;
720 }
721
722 static HRESULT WINAPI MimeBody_SetCharset(
723                                  IMimeBody* iface,
724                                  HCHARSET hCharset,
725                                  CSETAPPLYTYPE applytype)
726 {
727     FIXME("stub\n");
728     return E_NOTIMPL;
729 }
730
731 static HRESULT WINAPI MimeBody_GetParameters(
732                                     IMimeBody* iface,
733                                     LPCSTR pszName,
734                                     ULONG* pcParams,
735                                     LPMIMEPARAMINFO* pprgParam)
736 {
737     MimeBody *This = impl_from_IMimeBody(iface);
738     HRESULT hr;
739     header_t *header;
740
741     TRACE("(%p)->(%s, %p, %p)\n", iface, debugstr_a(pszName), pcParams, pprgParam);
742
743     *pprgParam = NULL;
744     *pcParams = 0;
745
746     hr = find_prop(This, pszName, &header);
747     if(hr != S_OK) return hr;
748
749     *pcParams = list_count(&header->params);
750     if(*pcParams)
751     {
752         IMimeAllocator *alloc;
753         param_t *param;
754         MIMEPARAMINFO *info;
755
756         MimeOleGetAllocator(&alloc);
757
758         *pprgParam = info = IMimeAllocator_Alloc(alloc, *pcParams * sizeof(**pprgParam));
759         LIST_FOR_EACH_ENTRY(param, &header->params, param_t, entry)
760         {
761             int len;
762
763             len = strlen(param->name) + 1;
764             info->pszName = IMimeAllocator_Alloc(alloc, len);
765             memcpy(info->pszName, param->name, len);
766             len = strlen(param->value) + 1;
767             info->pszData = IMimeAllocator_Alloc(alloc, len);
768             memcpy(info->pszData, param->value, len);
769             info++;
770         }
771         IMimeAllocator_Release(alloc);
772     }
773     return S_OK;
774 }
775
776 static HRESULT WINAPI MimeBody_IsContentType(
777                                     IMimeBody* iface,
778                                     LPCSTR pszPriType,
779                                     LPCSTR pszSubType)
780 {
781     MimeBody *This = impl_from_IMimeBody(iface);
782
783     TRACE("(%p)->(%s, %s)\n", This, debugstr_a(pszPriType), debugstr_a(pszSubType));
784     if(pszPriType)
785     {
786         const char *pri = This->content_pri_type;
787         if(!pri) pri = "text";
788         if(strcasecmp(pri, pszPriType)) return S_FALSE;
789     }
790
791     if(pszSubType)
792     {
793         const char *sub = This->content_sub_type;
794         if(!sub) sub = "plain";
795         if(strcasecmp(sub, pszSubType)) return S_FALSE;
796     }
797
798     return S_OK;
799 }
800
801 static HRESULT WINAPI MimeBody_BindToObject(
802                                    IMimeBody* iface,
803                                    REFIID riid,
804                                    void** ppvObject)
805 {
806     FIXME("stub\n");
807     return E_NOTIMPL;
808 }
809
810 static HRESULT WINAPI MimeBody_Clone(
811                             IMimeBody* iface,
812                             IMimePropertySet** ppPropertySet)
813 {
814     FIXME("stub\n");
815     return E_NOTIMPL;
816 }
817
818 static HRESULT WINAPI MimeBody_SetOption(
819                                 IMimeBody* iface,
820                                 const TYPEDID oid,
821                                 LPCPROPVARIANT pValue)
822 {
823     FIXME("(%p)->(%08x, %p): stub\n", iface, oid, pValue);
824     return E_NOTIMPL;
825 }
826
827 static HRESULT WINAPI MimeBody_GetOption(
828                                 IMimeBody* iface,
829                                 const TYPEDID oid,
830                                 LPPROPVARIANT pValue)
831 {
832     FIXME("(%p)->(%08x, %p): stub\n", iface, oid, pValue);
833     return E_NOTIMPL;
834 }
835
836 static HRESULT WINAPI MimeBody_EnumProps(
837                                 IMimeBody* iface,
838                                 DWORD dwFlags,
839                                 IMimeEnumProperties** ppEnum)
840 {
841     FIXME("stub\n");
842     return E_NOTIMPL;
843 }
844
845 static HRESULT WINAPI MimeBody_IsType(
846                              IMimeBody* iface,
847                              IMSGBODYTYPE bodytype)
848 {
849     FIXME("(%p)->(%d): stub\n", iface, bodytype);
850     return E_NOTIMPL;
851 }
852
853 static HRESULT WINAPI MimeBody_SetDisplayName(
854                                      IMimeBody* iface,
855                                      LPCSTR pszDisplay)
856 {
857     FIXME("stub\n");
858     return E_NOTIMPL;
859 }
860
861 static HRESULT WINAPI MimeBody_GetDisplayName(
862                                      IMimeBody* iface,
863                                      LPSTR* ppszDisplay)
864 {
865     FIXME("stub\n");
866     return E_NOTIMPL;
867 }
868
869 static HRESULT WINAPI MimeBody_GetOffsets(
870                                  IMimeBody* iface,
871                                  LPBODYOFFSETS pOffsets)
872 {
873     MimeBody *This = impl_from_IMimeBody(iface);
874     TRACE("(%p)->(%p)\n", This, pOffsets);
875
876     *pOffsets = This->body_offsets;
877
878     if(This->body_offsets.cbBodyEnd == 0) return MIME_E_NO_DATA;
879     return S_OK;
880 }
881
882 static HRESULT WINAPI MimeBody_GetCurrentEncoding(
883                                          IMimeBody* iface,
884                                          ENCODINGTYPE* pietEncoding)
885 {
886     MimeBody *This = impl_from_IMimeBody(iface);
887
888     TRACE("(%p)->(%p)\n", This, pietEncoding);
889
890     *pietEncoding = This->encoding;
891     return S_OK;
892 }
893
894 static HRESULT WINAPI MimeBody_SetCurrentEncoding(
895                                          IMimeBody* iface,
896                                          ENCODINGTYPE ietEncoding)
897 {
898     MimeBody *This = impl_from_IMimeBody(iface);
899
900     TRACE("(%p)->(%d)\n", This, ietEncoding);
901
902     This->encoding = ietEncoding;
903     return S_OK;
904 }
905
906 static HRESULT WINAPI MimeBody_GetEstimatedSize(
907                                        IMimeBody* iface,
908                                        ENCODINGTYPE ietEncoding,
909                                        ULONG* pcbSize)
910 {
911     FIXME("stub\n");
912     return E_NOTIMPL;
913 }
914
915 static HRESULT WINAPI MimeBody_GetDataHere(
916                                   IMimeBody* iface,
917                                   ENCODINGTYPE ietEncoding,
918                                   IStream* pStream)
919 {
920     FIXME("stub\n");
921     return E_NOTIMPL;
922 }
923
924 static HRESULT WINAPI MimeBody_GetData(
925                               IMimeBody* iface,
926                               ENCODINGTYPE ietEncoding,
927                               IStream** ppStream)
928 {
929     MimeBody *This = impl_from_IMimeBody(iface);
930     FIXME("(%p)->(%d, %p). Ignoring encoding type.\n", This, ietEncoding, ppStream);
931
932     *ppStream = This->data;
933     IStream_AddRef(*ppStream);
934     return S_OK;
935 }
936
937 static HRESULT WINAPI MimeBody_SetData(
938                               IMimeBody* iface,
939                               ENCODINGTYPE ietEncoding,
940                               LPCSTR pszPriType,
941                               LPCSTR pszSubType,
942                               REFIID riid,
943                               LPVOID pvObject)
944 {
945     MimeBody *This = impl_from_IMimeBody(iface);
946     TRACE("(%p)->(%d, %s, %s, %s %p)\n", This, ietEncoding, debugstr_a(pszPriType), debugstr_a(pszSubType),
947           debugstr_guid(riid), pvObject);
948
949     if(IsEqualIID(riid, &IID_IStream))
950         IStream_AddRef((IStream *)pvObject);
951     else
952     {
953         FIXME("Unhandled object type %s\n", debugstr_guid(riid));
954         return E_INVALIDARG;
955     }
956
957     if(This->data)
958         FIXME("release old data\n");
959
960     This->data_iid = *riid;
961     This->data = pvObject;
962
963     IMimeBody_SetCurrentEncoding(iface, ietEncoding);
964
965     /* FIXME: Update the content type.
966        If pszPriType == NULL use 'application'
967        If pszSubType == NULL use 'octet-stream' */
968
969     return S_OK;
970 }
971
972 static HRESULT WINAPI MimeBody_EmptyData(
973                                 IMimeBody* iface)
974 {
975     FIXME("stub\n");
976     return E_NOTIMPL;
977 }
978
979 static HRESULT WINAPI MimeBody_CopyTo(
980                              IMimeBody* iface,
981                              IMimeBody* pBody)
982 {
983     FIXME("stub\n");
984     return E_NOTIMPL;
985 }
986
987 static HRESULT WINAPI MimeBody_GetTransmitInfo(
988                                       IMimeBody* iface,
989                                       LPTRANSMITINFO pTransmitInfo)
990 {
991     FIXME("stub\n");
992     return E_NOTIMPL;
993 }
994
995 static HRESULT WINAPI MimeBody_SaveToFile(
996                                  IMimeBody* iface,
997                                  ENCODINGTYPE ietEncoding,
998                                  LPCSTR pszFilePath)
999 {
1000     FIXME("stub\n");
1001     return E_NOTIMPL;
1002 }
1003
1004 static HRESULT WINAPI MimeBody_GetHandle(
1005                                 IMimeBody* iface,
1006                                 LPHBODY phBody)
1007 {
1008     MimeBody *This = impl_from_IMimeBody(iface);
1009     TRACE("(%p)->(%p)\n", iface, phBody);
1010
1011     *phBody = This->handle;
1012     return This->handle ? S_OK : MIME_E_NO_DATA;
1013 }
1014
1015 static IMimeBodyVtbl body_vtbl =
1016 {
1017     MimeBody_QueryInterface,
1018     MimeBody_AddRef,
1019     MimeBody_Release,
1020     MimeBody_GetClassID,
1021     MimeBody_IsDirty,
1022     MimeBody_Load,
1023     MimeBody_Save,
1024     MimeBody_GetSizeMax,
1025     MimeBody_InitNew,
1026     MimeBody_GetPropInfo,
1027     MimeBody_SetPropInfo,
1028     MimeBody_GetProp,
1029     MimeBody_SetProp,
1030     MimeBody_AppendProp,
1031     MimeBody_DeleteProp,
1032     MimeBody_CopyProps,
1033     MimeBody_MoveProps,
1034     MimeBody_DeleteExcept,
1035     MimeBody_QueryProp,
1036     MimeBody_GetCharset,
1037     MimeBody_SetCharset,
1038     MimeBody_GetParameters,
1039     MimeBody_IsContentType,
1040     MimeBody_BindToObject,
1041     MimeBody_Clone,
1042     MimeBody_SetOption,
1043     MimeBody_GetOption,
1044     MimeBody_EnumProps,
1045     MimeBody_IsType,
1046     MimeBody_SetDisplayName,
1047     MimeBody_GetDisplayName,
1048     MimeBody_GetOffsets,
1049     MimeBody_GetCurrentEncoding,
1050     MimeBody_SetCurrentEncoding,
1051     MimeBody_GetEstimatedSize,
1052     MimeBody_GetDataHere,
1053     MimeBody_GetData,
1054     MimeBody_SetData,
1055     MimeBody_EmptyData,
1056     MimeBody_CopyTo,
1057     MimeBody_GetTransmitInfo,
1058     MimeBody_SaveToFile,
1059     MimeBody_GetHandle
1060 };
1061
1062 static HRESULT MimeBody_set_offsets(MimeBody *body, const BODYOFFSETS *offsets)
1063 {
1064     TRACE("setting offsets to %d, %d, %d, %d\n", offsets->cbBoundaryStart,
1065           offsets->cbHeaderStart, offsets->cbBodyStart, offsets->cbBodyEnd);
1066
1067     body->body_offsets = *offsets;
1068     return S_OK;
1069 }
1070
1071 #define FIRST_CUSTOM_PROP_ID 0x100
1072
1073 HRESULT MimeBody_create(IUnknown *outer, void **obj)
1074 {
1075     MimeBody *This;
1076     BODYOFFSETS body_offsets;
1077
1078     *obj = NULL;
1079
1080     if(outer) return CLASS_E_NOAGGREGATION;
1081
1082     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
1083     if (!This) return E_OUTOFMEMORY;
1084
1085     This->lpVtbl = &body_vtbl;
1086     This->refs = 1;
1087     This->handle = NULL;
1088     list_init(&This->headers);
1089     list_init(&This->new_props);
1090     This->next_prop_id = FIRST_CUSTOM_PROP_ID;
1091     This->content_pri_type = NULL;
1092     This->content_sub_type = NULL;
1093     This->encoding = IET_7BIT;
1094     This->data = NULL;
1095     This->data_iid = IID_NULL;
1096
1097     body_offsets.cbBoundaryStart = body_offsets.cbHeaderStart = 0;
1098     body_offsets.cbBodyStart     = body_offsets.cbBodyEnd     = 0;
1099     MimeBody_set_offsets(This, &body_offsets);
1100
1101     *obj = (IMimeBody *)&This->lpVtbl;
1102     return S_OK;
1103 }
1104
1105 typedef struct
1106 {
1107     IStreamVtbl *lpVtbl;
1108     LONG refs;
1109
1110     IStream *base;
1111     ULARGE_INTEGER pos, start, length;
1112 } sub_stream_t;
1113
1114 static inline sub_stream_t *impl_from_IStream( IStream *iface )
1115 {
1116     return (sub_stream_t *)((char*)iface - FIELD_OFFSET(sub_stream_t, lpVtbl));
1117 }
1118
1119 static HRESULT WINAPI sub_stream_QueryInterface(
1120         IStream* iface,
1121         REFIID riid,
1122         void **ppvObject)
1123 {
1124     sub_stream_t *This = impl_from_IStream(iface);
1125
1126     TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppvObject);
1127     *ppvObject = NULL;
1128
1129     if(IsEqualIID(riid, &IID_IUnknown) ||
1130        IsEqualIID(riid, &IID_ISequentialStream) ||
1131        IsEqualIID(riid, &IID_IStream))
1132     {
1133         IStream_AddRef(iface);
1134         *ppvObject = iface;
1135         return S_OK;
1136     }
1137     return E_NOINTERFACE;
1138 }
1139
1140 static ULONG WINAPI sub_stream_AddRef(
1141          IStream* iface)
1142 {
1143     sub_stream_t *This = impl_from_IStream(iface);
1144
1145     TRACE("(%p)\n", This);
1146     return InterlockedIncrement(&This->refs);
1147 }
1148
1149 static ULONG WINAPI  sub_stream_Release(
1150         IStream* iface)
1151 {
1152     sub_stream_t *This = impl_from_IStream(iface);
1153     LONG refs;
1154
1155     TRACE("(%p)\n", This);
1156     refs = InterlockedDecrement(&This->refs);
1157     if(!refs)
1158     {
1159         IStream_Release(This->base);
1160         HeapFree(GetProcessHeap(), 0, This);
1161     }
1162     return refs;
1163 }
1164
1165 static HRESULT WINAPI sub_stream_Read(
1166         IStream* iface,
1167         void *pv,
1168         ULONG cb,
1169         ULONG *pcbRead)
1170 {
1171     sub_stream_t *This = impl_from_IStream(iface);
1172     HRESULT hr;
1173     ULARGE_INTEGER base_pos;
1174     LARGE_INTEGER tmp_pos;
1175
1176     TRACE("(%p, %d, %p)\n", pv, cb, pcbRead);
1177
1178     tmp_pos.QuadPart = 0;
1179     IStream_Seek(This->base, tmp_pos, STREAM_SEEK_CUR, &base_pos);
1180     tmp_pos.QuadPart = This->pos.QuadPart + This->start.QuadPart;
1181     IStream_Seek(This->base, tmp_pos, STREAM_SEEK_SET, NULL);
1182
1183     if(This->pos.QuadPart + cb > This->length.QuadPart)
1184         cb = This->length.QuadPart - This->pos.QuadPart;
1185
1186     hr = IStream_Read(This->base, pv, cb, pcbRead);
1187
1188     This->pos.QuadPart += *pcbRead;
1189
1190     tmp_pos.QuadPart = base_pos.QuadPart;
1191     IStream_Seek(This->base, tmp_pos, STREAM_SEEK_SET, NULL);
1192
1193     return hr;
1194 }
1195
1196 static HRESULT WINAPI sub_stream_Write(
1197         IStream* iface,
1198         const void *pv,
1199         ULONG cb,
1200         ULONG *pcbWritten)
1201 {
1202     FIXME("stub\n");
1203     return E_NOTIMPL;
1204 }
1205
1206 static HRESULT WINAPI sub_stream_Seek(
1207         IStream* iface,
1208         LARGE_INTEGER dlibMove,
1209         DWORD dwOrigin,
1210         ULARGE_INTEGER *plibNewPosition)
1211 {
1212     sub_stream_t *This = impl_from_IStream(iface);
1213     LARGE_INTEGER new_pos;
1214
1215     TRACE("(%08x.%08x, %x, %p)\n", dlibMove.u.HighPart, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
1216
1217     switch(dwOrigin)
1218     {
1219     case STREAM_SEEK_SET:
1220         new_pos = dlibMove;
1221         break;
1222     case STREAM_SEEK_CUR:
1223         new_pos.QuadPart = This->pos.QuadPart + dlibMove.QuadPart;
1224         break;
1225     case STREAM_SEEK_END:
1226         new_pos.QuadPart = This->length.QuadPart + dlibMove.QuadPart;
1227         break;
1228     default:
1229         return STG_E_INVALIDFUNCTION;
1230     }
1231
1232     if(new_pos.QuadPart < 0) new_pos.QuadPart = 0;
1233     else if(new_pos.QuadPart > This->length.QuadPart) new_pos.QuadPart = This->length.QuadPart;
1234
1235     This->pos.QuadPart = new_pos.QuadPart;
1236
1237     if(plibNewPosition) *plibNewPosition = This->pos;
1238     return S_OK;
1239 }
1240
1241 static HRESULT WINAPI sub_stream_SetSize(
1242         IStream* iface,
1243         ULARGE_INTEGER libNewSize)
1244 {
1245     FIXME("stub\n");
1246     return E_NOTIMPL;
1247 }
1248
1249 static HRESULT WINAPI sub_stream_CopyTo(
1250         IStream* iface,
1251         IStream *pstm,
1252         ULARGE_INTEGER cb,
1253         ULARGE_INTEGER *pcbRead,
1254         ULARGE_INTEGER *pcbWritten)
1255 {
1256     HRESULT        hr = S_OK;
1257     BYTE           tmpBuffer[128];
1258     ULONG          bytesRead, bytesWritten, copySize;
1259     ULARGE_INTEGER totalBytesRead;
1260     ULARGE_INTEGER totalBytesWritten;
1261
1262     TRACE("(%p)->(%p, %d, %p, %p)\n", iface, pstm, cb.u.LowPart, pcbRead, pcbWritten);
1263
1264     totalBytesRead.QuadPart = 0;
1265     totalBytesWritten.QuadPart = 0;
1266
1267     while ( cb.QuadPart > 0 )
1268     {
1269         if ( cb.QuadPart >= sizeof(tmpBuffer) )
1270             copySize = sizeof(tmpBuffer);
1271         else
1272             copySize = cb.u.LowPart;
1273
1274         hr = IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
1275         if (FAILED(hr)) break;
1276
1277         totalBytesRead.QuadPart += bytesRead;
1278
1279         if (bytesRead)
1280         {
1281             hr = IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
1282             if (FAILED(hr)) break;
1283             totalBytesWritten.QuadPart += bytesWritten;
1284         }
1285
1286         if (bytesRead != copySize)
1287             cb.QuadPart = 0;
1288         else
1289             cb.QuadPart -= bytesRead;
1290     }
1291
1292     if (pcbRead) pcbRead->QuadPart = totalBytesRead.QuadPart;
1293     if (pcbWritten) pcbWritten->QuadPart = totalBytesWritten.QuadPart;
1294
1295     return hr;
1296 }
1297
1298 static HRESULT WINAPI sub_stream_Commit(
1299         IStream* iface,
1300         DWORD grfCommitFlags)
1301 {
1302     FIXME("stub\n");
1303     return E_NOTIMPL;
1304 }
1305
1306 static HRESULT WINAPI sub_stream_Revert(
1307         IStream* iface)
1308 {
1309     FIXME("stub\n");
1310     return E_NOTIMPL;
1311 }
1312
1313 static HRESULT WINAPI sub_stream_LockRegion(
1314         IStream* iface,
1315         ULARGE_INTEGER libOffset,
1316         ULARGE_INTEGER cb,
1317         DWORD dwLockType)
1318 {
1319     FIXME("stub\n");
1320     return E_NOTIMPL;
1321 }
1322
1323 static HRESULT WINAPI sub_stream_UnlockRegion(
1324         IStream* iface,
1325         ULARGE_INTEGER libOffset,
1326         ULARGE_INTEGER cb,
1327         DWORD dwLockType)
1328 {
1329     FIXME("stub\n");
1330     return E_NOTIMPL;
1331 }
1332
1333 static HRESULT WINAPI sub_stream_Stat(
1334         IStream* iface,
1335         STATSTG *pstatstg,
1336         DWORD grfStatFlag)
1337 {
1338     sub_stream_t *This = impl_from_IStream(iface);
1339     FIXME("(%p)->(%p, %08x)\n", This, pstatstg, grfStatFlag);
1340     memset(pstatstg, 0, sizeof(*pstatstg));
1341     pstatstg->cbSize = This->length;
1342     return S_OK;
1343 }
1344
1345 static HRESULT WINAPI sub_stream_Clone(
1346         IStream* iface,
1347         IStream **ppstm)
1348 {
1349     FIXME("stub\n");
1350     return E_NOTIMPL;
1351 }
1352
1353 static struct IStreamVtbl sub_stream_vtbl =
1354 {
1355     sub_stream_QueryInterface,
1356     sub_stream_AddRef,
1357     sub_stream_Release,
1358     sub_stream_Read,
1359     sub_stream_Write,
1360     sub_stream_Seek,
1361     sub_stream_SetSize,
1362     sub_stream_CopyTo,
1363     sub_stream_Commit,
1364     sub_stream_Revert,
1365     sub_stream_LockRegion,
1366     sub_stream_UnlockRegion,
1367     sub_stream_Stat,
1368     sub_stream_Clone
1369 };
1370
1371 static HRESULT create_sub_stream(IStream *stream, ULARGE_INTEGER start, ULARGE_INTEGER length, IStream **out)
1372 {
1373     sub_stream_t *This;
1374
1375     *out = NULL;
1376     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
1377     if(!This) return E_OUTOFMEMORY;
1378
1379     This->lpVtbl = &sub_stream_vtbl;
1380     This->refs = 1;
1381     This->start = start;
1382     This->length = length;
1383     This->pos.QuadPart = 0;
1384     IStream_AddRef(stream);
1385     This->base = stream;
1386
1387     *out = (IStream*)&This->lpVtbl;
1388     return S_OK;
1389 }
1390
1391
1392 typedef struct body_t
1393 {
1394     struct list entry;
1395     HBODY hbody;
1396     IMimeBody *mime_body;
1397
1398     struct body_t *parent;
1399     struct list children;
1400 } body_t;
1401
1402 typedef struct MimeMessage
1403 {
1404     const IMimeMessageVtbl *lpVtbl;
1405
1406     LONG refs;
1407     IStream *stream;
1408
1409     struct list body_tree;
1410     HBODY next_hbody;
1411 } MimeMessage;
1412
1413 static HRESULT WINAPI MimeMessage_QueryInterface(IMimeMessage *iface, REFIID riid, void **ppv)
1414 {
1415     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1416
1417     if (IsEqualIID(riid, &IID_IUnknown) ||
1418         IsEqualIID(riid, &IID_IPersist) ||
1419         IsEqualIID(riid, &IID_IPersistStreamInit) ||
1420         IsEqualIID(riid, &IID_IMimeMessageTree) ||
1421         IsEqualIID(riid, &IID_IMimeMessage))
1422     {
1423         *ppv = iface;
1424         IUnknown_AddRef(iface);
1425         return S_OK;
1426     }
1427
1428     FIXME("no interface for %s\n", debugstr_guid(riid));
1429     *ppv = NULL;
1430     return E_NOINTERFACE;
1431 }
1432
1433 static ULONG WINAPI MimeMessage_AddRef(IMimeMessage *iface)
1434 {
1435     MimeMessage *This = (MimeMessage *)iface;
1436     TRACE("(%p)->()\n", iface);
1437     return InterlockedIncrement(&This->refs);
1438 }
1439
1440 static void empty_body_list(struct list *list)
1441 {
1442     body_t *body, *cursor2;
1443     LIST_FOR_EACH_ENTRY_SAFE(body, cursor2, list, body_t, entry)
1444     {
1445         empty_body_list(&body->children);
1446         list_remove(&body->entry);
1447         IMimeBody_Release(body->mime_body);
1448         HeapFree(GetProcessHeap(), 0, body);
1449     }
1450 }
1451
1452 static ULONG WINAPI MimeMessage_Release(IMimeMessage *iface)
1453 {
1454     MimeMessage *This = (MimeMessage *)iface;
1455     ULONG refs;
1456
1457     TRACE("(%p)->()\n", iface);
1458
1459     refs = InterlockedDecrement(&This->refs);
1460     if (!refs)
1461     {
1462         empty_body_list(&This->body_tree);
1463
1464         if(This->stream) IStream_Release(This->stream);
1465         HeapFree(GetProcessHeap(), 0, This);
1466     }
1467
1468     return refs;
1469 }
1470
1471 /*** IPersist methods ***/
1472 static HRESULT WINAPI MimeMessage_GetClassID(
1473     IMimeMessage *iface,
1474     CLSID *pClassID)
1475 {
1476     FIXME("(%p)->(%p)\n", iface, pClassID);
1477     return E_NOTIMPL;
1478 }
1479
1480 /*** IPersistStreamInit methods ***/
1481 static HRESULT WINAPI MimeMessage_IsDirty(
1482     IMimeMessage *iface)
1483 {
1484     FIXME("(%p)->()\n", iface);
1485     return E_NOTIMPL;
1486 }
1487
1488 static body_t *new_body_entry(IMimeBody *mime_body, HBODY hbody, body_t *parent)
1489 {
1490     body_t *body = HeapAlloc(GetProcessHeap(), 0, sizeof(*body));
1491     if(body)
1492     {
1493         body->mime_body = mime_body;
1494         body->hbody = hbody;
1495         list_init(&body->children);
1496         body->parent = parent;
1497     }
1498     return body;
1499 }
1500
1501 typedef struct
1502 {
1503     struct list entry;
1504     BODYOFFSETS offsets;
1505 } offset_entry_t;
1506
1507 static HRESULT create_body_offset_list(IStream *stm, const char *boundary, struct list *body_offsets)
1508 {
1509     HRESULT hr;
1510     DWORD read;
1511     int boundary_len = strlen(boundary);
1512     char *buf, *nl_boundary, *ptr, *overlap;
1513     DWORD total_read = 0;
1514     DWORD start = 0, overlap_no;
1515     offset_entry_t *cur_body = NULL;
1516     ULARGE_INTEGER cur;
1517     LARGE_INTEGER zero;
1518
1519     list_init(body_offsets);
1520     nl_boundary = HeapAlloc(GetProcessHeap(), 0, 4 + boundary_len + 1);
1521     memcpy(nl_boundary, "\r\n--", 4);
1522     memcpy(nl_boundary + 4, boundary, boundary_len + 1);
1523
1524     overlap_no = boundary_len + 5;
1525
1526     overlap = buf = HeapAlloc(GetProcessHeap(), 0, overlap_no + PARSER_BUF_SIZE + 1);
1527
1528     zero.QuadPart = 0;
1529     hr = IStream_Seek(stm, zero, STREAM_SEEK_CUR, &cur);
1530     start = cur.u.LowPart;
1531
1532     do {
1533         hr = IStream_Read(stm, overlap, PARSER_BUF_SIZE, &read);
1534         if(FAILED(hr)) goto end;
1535         if(read == 0) break;
1536         total_read += read;
1537         overlap[read] = '\0';
1538
1539         ptr = buf;
1540         do {
1541             ptr = strstr(ptr, nl_boundary);
1542             if(ptr)
1543             {
1544                 DWORD boundary_start = start + ptr - buf;
1545                 char *end = ptr + boundary_len + 4;
1546
1547                 if(*end == '\0' || *(end + 1) == '\0')
1548                     break;
1549
1550                 if(*end == '\r' && *(end + 1) == '\n')
1551                 {
1552                     if(cur_body)
1553                     {
1554                         cur_body->offsets.cbBodyEnd = boundary_start;
1555                         list_add_tail(body_offsets, &cur_body->entry);
1556                     }
1557                     cur_body = HeapAlloc(GetProcessHeap(), 0, sizeof(*cur_body));
1558                     cur_body->offsets.cbBoundaryStart = boundary_start + 2; /* doesn't including the leading \r\n */
1559                     cur_body->offsets.cbHeaderStart = boundary_start + boundary_len + 6;
1560                 }
1561                 else if(*end == '-' && *(end + 1) == '-')
1562                 {
1563                     if(cur_body)
1564                     {
1565                         cur_body->offsets.cbBodyEnd = boundary_start;
1566                         list_add_tail(body_offsets, &cur_body->entry);
1567                         goto end;
1568                     }
1569                 }
1570                 ptr = end + 2;
1571             }
1572         } while(ptr);
1573
1574         if(overlap == buf) /* 1st iteration */
1575         {
1576             memcpy(buf, buf + PARSER_BUF_SIZE - overlap_no, overlap_no);
1577             overlap = buf + overlap_no;
1578             start += read - overlap_no;
1579         }
1580         else
1581         {
1582             memcpy(buf, buf + PARSER_BUF_SIZE, overlap_no);
1583             start += read;
1584         }
1585     } while(1);
1586
1587 end:
1588     HeapFree(GetProcessHeap(), 0, buf);
1589     return hr;
1590 }
1591
1592 static body_t *create_sub_body(MimeMessage *msg, IStream *pStm, BODYOFFSETS *offset, body_t *parent)
1593 {
1594     IMimeBody *mime_body;
1595     HRESULT hr;
1596     body_t *body;
1597     ULARGE_INTEGER cur;
1598     LARGE_INTEGER zero;
1599
1600     MimeBody_create(NULL, (void**)&mime_body);
1601     IMimeBody_Load(mime_body, pStm);
1602     zero.QuadPart = 0;
1603     hr = IStream_Seek(pStm, zero, STREAM_SEEK_CUR, &cur);
1604     offset->cbBodyStart = cur.u.LowPart + offset->cbHeaderStart;
1605     if(parent) MimeBody_set_offsets(impl_from_IMimeBody(mime_body), offset);
1606     IMimeBody_SetData(mime_body, IET_BINARY, NULL, NULL, &IID_IStream, pStm);
1607     body = new_body_entry(mime_body, msg->next_hbody, parent);
1608     msg->next_hbody = (HBODY)((DWORD)msg->next_hbody + 1);
1609
1610     if(IMimeBody_IsContentType(mime_body, "multipart", NULL) == S_OK)
1611     {
1612         MIMEPARAMINFO *param_info;
1613         ULONG count, i;
1614         IMimeAllocator *alloc;
1615
1616         hr = IMimeBody_GetParameters(mime_body, "Content-Type", &count, &param_info);
1617         if(hr != S_OK || count == 0) return body;
1618
1619         MimeOleGetAllocator(&alloc);
1620
1621         for(i = 0; i < count; i++)
1622         {
1623             if(!strcasecmp(param_info[i].pszName, "boundary"))
1624             {
1625                 struct list offset_list;
1626                 offset_entry_t *cur, *cursor2;
1627                 hr = create_body_offset_list(pStm, param_info[i].pszData, &offset_list);
1628                 LIST_FOR_EACH_ENTRY_SAFE(cur, cursor2, &offset_list, offset_entry_t, entry)
1629                 {
1630                     body_t *sub_body;
1631                     IStream *sub_stream;
1632                     ULARGE_INTEGER start, length;
1633
1634                     start.u.LowPart = cur->offsets.cbHeaderStart;
1635                     length.u.LowPart = cur->offsets.cbBodyEnd - cur->offsets.cbHeaderStart;
1636                     create_sub_stream(pStm, start, length, &sub_stream);
1637                     sub_body = create_sub_body(msg, sub_stream, &cur->offsets, body);
1638                     IStream_Release(sub_stream);
1639                     list_add_tail(&body->children, &sub_body->entry);
1640                     list_remove(&cur->entry);
1641                     HeapFree(GetProcessHeap(), 0, cur);
1642                 }
1643                 break;
1644             }
1645         }
1646         IMimeAllocator_FreeParamInfoArray(alloc, count, param_info, TRUE);
1647         IMimeAllocator_Release(alloc);
1648     }
1649     return body;
1650 }
1651
1652 static HRESULT WINAPI MimeMessage_Load(
1653     IMimeMessage *iface,
1654     LPSTREAM pStm)
1655 {
1656     MimeMessage *This = (MimeMessage *)iface;
1657     body_t *root_body;
1658     BODYOFFSETS offsets;
1659     ULARGE_INTEGER cur;
1660     LARGE_INTEGER zero;
1661
1662     TRACE("(%p)->(%p)\n", iface, pStm);
1663
1664     if(This->stream)
1665     {
1666         FIXME("already loaded a message\n");
1667         return E_FAIL;
1668     }
1669
1670     IStream_AddRef(pStm);
1671     This->stream = pStm;
1672     offsets.cbBoundaryStart = offsets.cbHeaderStart = 0;
1673     offsets.cbBodyStart = offsets.cbBodyEnd = 0;
1674
1675     root_body = create_sub_body(This, pStm, &offsets, NULL);
1676
1677     zero.QuadPart = 0;
1678     IStream_Seek(pStm, zero, STREAM_SEEK_END, &cur);
1679     offsets.cbBodyEnd = cur.u.LowPart;
1680     MimeBody_set_offsets(impl_from_IMimeBody(root_body->mime_body), &offsets);
1681
1682     list_add_head(&This->body_tree, &root_body->entry);
1683
1684     return S_OK;
1685 }
1686
1687 static HRESULT WINAPI MimeMessage_Save(
1688     IMimeMessage *iface,
1689     LPSTREAM pStm,
1690     BOOL fClearDirty)
1691 {
1692     FIXME("(%p)->(%p, %s)\n", iface, pStm, fClearDirty ? "TRUE" : "FALSE");
1693     return E_NOTIMPL;
1694 }
1695
1696 static HRESULT WINAPI MimeMessage_GetSizeMax(
1697     IMimeMessage *iface,
1698     ULARGE_INTEGER *pcbSize)
1699 {
1700     FIXME("(%p)->(%p)\n", iface, pcbSize);
1701     return E_NOTIMPL;
1702 }
1703
1704 static HRESULT WINAPI MimeMessage_InitNew(
1705     IMimeMessage *iface)
1706 {
1707     FIXME("(%p)->()\n", iface);
1708     return E_NOTIMPL;
1709 }
1710
1711 /*** IMimeMessageTree methods ***/
1712 static HRESULT WINAPI MimeMessage_GetMessageSource(
1713     IMimeMessage *iface,
1714     IStream **ppStream,
1715     DWORD dwFlags)
1716 {
1717     MimeMessage *This = (MimeMessage *)iface;
1718     FIXME("(%p)->(%p, 0x%x)\n", iface, ppStream, dwFlags);
1719
1720     IStream_AddRef(This->stream);
1721     *ppStream = This->stream;
1722     return S_OK;
1723 }
1724
1725 static HRESULT WINAPI MimeMessage_GetMessageSize(
1726     IMimeMessage *iface,
1727     ULONG *pcbSize,
1728     DWORD dwFlags)
1729 {
1730     FIXME("(%p)->(%p, 0x%x)\n", iface, pcbSize, dwFlags);
1731     return E_NOTIMPL;
1732 }
1733
1734 static HRESULT WINAPI MimeMessage_LoadOffsetTable(
1735     IMimeMessage *iface,
1736     IStream *pStream)
1737 {
1738     FIXME("(%p)->(%p)\n", iface, pStream);
1739     return E_NOTIMPL;
1740 }
1741
1742 static HRESULT WINAPI MimeMessage_SaveOffsetTable(
1743     IMimeMessage *iface,
1744     IStream *pStream,
1745     DWORD dwFlags)
1746 {
1747     FIXME("(%p)->(%p, 0x%x)\n", iface, pStream, dwFlags);
1748     return E_NOTIMPL;
1749 }
1750
1751
1752 static HRESULT WINAPI MimeMessage_GetFlags(
1753     IMimeMessage *iface,
1754     DWORD *pdwFlags)
1755 {
1756     FIXME("(%p)->(%p)\n", iface, pdwFlags);
1757     return E_NOTIMPL;
1758 }
1759
1760 static HRESULT WINAPI MimeMessage_Commit(
1761     IMimeMessage *iface,
1762     DWORD dwFlags)
1763 {
1764     FIXME("(%p)->(0x%x)\n", iface, dwFlags);
1765     return E_NOTIMPL;
1766 }
1767
1768
1769 static HRESULT WINAPI MimeMessage_HandsOffStorage(
1770     IMimeMessage *iface)
1771 {
1772     FIXME("(%p)->()\n", iface);
1773     return E_NOTIMPL;
1774 }
1775
1776 static HRESULT find_body(struct list *list, HBODY hbody, body_t **body)
1777 {
1778     body_t *cur;
1779     HRESULT hr;
1780
1781     if(hbody == HBODY_ROOT)
1782     {
1783         *body = LIST_ENTRY(list_head(list), body_t, entry);
1784         return S_OK;
1785     }
1786
1787     LIST_FOR_EACH_ENTRY(cur, list, body_t, entry)
1788     {
1789         if(cur->hbody == hbody)
1790         {
1791             *body = cur;
1792             return S_OK;
1793         }
1794         hr = find_body(&cur->children, hbody, body);
1795         if(hr == S_OK) return S_OK;
1796     }
1797     return S_FALSE;
1798 }
1799
1800 static HRESULT WINAPI MimeMessage_BindToObject(
1801     IMimeMessage *iface,
1802     const HBODY hBody,
1803     REFIID riid,
1804     void **ppvObject)
1805 {
1806     MimeMessage *This = (MimeMessage *)iface;
1807     HRESULT hr;
1808     body_t *body;
1809
1810     TRACE("(%p)->(%p, %s, %p)\n", iface, hBody, debugstr_guid(riid), ppvObject);
1811
1812     hr = find_body(&This->body_tree, hBody, &body);
1813
1814     if(hr != S_OK) return hr;
1815
1816     if(IsEqualIID(riid, &IID_IMimeBody))
1817     {
1818         IMimeBody_AddRef(body->mime_body);
1819         *ppvObject = body->mime_body;
1820         return S_OK;
1821     }
1822
1823     return E_NOINTERFACE;
1824 }
1825
1826 static HRESULT WINAPI MimeMessage_SaveBody(
1827     IMimeMessage *iface,
1828     HBODY hBody,
1829     DWORD dwFlags,
1830     IStream *pStream)
1831 {
1832     FIXME("(%p)->(%p, 0x%x, %p)\n", iface, hBody, dwFlags, pStream);
1833     return E_NOTIMPL;
1834 }
1835
1836 static HRESULT get_body(MimeMessage *msg, BODYLOCATION location, HBODY pivot, body_t **out)
1837 {
1838     body_t *root = LIST_ENTRY(list_head(&msg->body_tree), body_t, entry);
1839     body_t *body;
1840     HRESULT hr;
1841     struct list *list;
1842
1843     if(location == IBL_ROOT)
1844     {
1845         *out = root;
1846         return S_OK;
1847     }
1848
1849     hr = find_body(&msg->body_tree, pivot, &body);
1850
1851     if(hr == S_OK)
1852     {
1853         switch(location)
1854         {
1855         case IBL_PARENT:
1856             *out = body->parent;
1857             break;
1858
1859         case IBL_FIRST:
1860             list = list_head(&body->children);
1861             if(list)
1862                 *out = LIST_ENTRY(list, body_t, entry);
1863             else
1864                 hr = MIME_E_NOT_FOUND;
1865             break;
1866
1867         case IBL_LAST:
1868             list = list_tail(&body->children);
1869             if(list)
1870                 *out = LIST_ENTRY(list, body_t, entry);
1871             else
1872                 hr = MIME_E_NOT_FOUND;
1873             break;
1874
1875         case IBL_NEXT:
1876             list = list_next(&body->parent->children, &body->entry);
1877             if(list)
1878                 *out = LIST_ENTRY(list, body_t, entry);
1879             else
1880                 hr = MIME_E_NOT_FOUND;
1881             break;
1882
1883         case IBL_PREVIOUS:
1884             list = list_prev(&body->parent->children, &body->entry);
1885             if(list)
1886                 *out = LIST_ENTRY(list, body_t, entry);
1887             else
1888                 hr = MIME_E_NOT_FOUND;
1889             break;
1890
1891         default:
1892             hr = E_FAIL;
1893             break;
1894         }
1895     }
1896
1897     return hr;
1898 }
1899
1900
1901 static HRESULT WINAPI MimeMessage_InsertBody(
1902     IMimeMessage *iface,
1903     BODYLOCATION location,
1904     HBODY hPivot,
1905     LPHBODY phBody)
1906 {
1907     FIXME("(%p)->(%d, %p, %p)\n", iface, location, hPivot, phBody);
1908     return E_NOTIMPL;
1909 }
1910
1911 static HRESULT WINAPI MimeMessage_GetBody(
1912     IMimeMessage *iface,
1913     BODYLOCATION location,
1914     HBODY hPivot,
1915     LPHBODY phBody)
1916 {
1917     MimeMessage *This = (MimeMessage *)iface;
1918     body_t *body;
1919     HRESULT hr;
1920
1921     TRACE("(%p)->(%d, %p, %p)\n", iface, location, hPivot, phBody);
1922
1923     hr = get_body(This, location, hPivot, &body);
1924
1925     if(hr == S_OK) *phBody = body->hbody;
1926
1927     return hr;
1928 }
1929
1930 static HRESULT WINAPI MimeMessage_DeleteBody(
1931     IMimeMessage *iface,
1932     HBODY hBody,
1933     DWORD dwFlags)
1934 {
1935     FIXME("(%p)->(%p, %08x)\n", iface, hBody, dwFlags);
1936     return E_NOTIMPL;
1937 }
1938
1939 static HRESULT WINAPI MimeMessage_MoveBody(
1940     IMimeMessage *iface,
1941     HBODY hBody,
1942     BODYLOCATION location)
1943 {
1944     FIXME("(%p)->(%d)\n", iface, location);
1945     return E_NOTIMPL;
1946 }
1947
1948 static void count_children(body_t *body, boolean recurse, ULONG *count)
1949 {
1950     body_t *child;
1951
1952     LIST_FOR_EACH_ENTRY(child, &body->children, body_t, entry)
1953     {
1954         (*count)++;
1955         if(recurse) count_children(child, recurse, count);
1956     }
1957 }
1958
1959 static HRESULT WINAPI MimeMessage_CountBodies(
1960     IMimeMessage *iface,
1961     HBODY hParent,
1962     boolean fRecurse,
1963     ULONG *pcBodies)
1964 {
1965     HRESULT hr;
1966     MimeMessage *This = (MimeMessage *)iface;
1967     body_t *body;
1968
1969     TRACE("(%p)->(%p, %s, %p)\n", iface, hParent, fRecurse ? "TRUE" : "FALSE", pcBodies);
1970
1971     hr = find_body(&This->body_tree, hParent, &body);
1972     if(hr != S_OK) return hr;
1973
1974     *pcBodies = 1;
1975     count_children(body, fRecurse, pcBodies);
1976
1977     return S_OK;
1978 }
1979
1980 static HRESULT find_next(IMimeMessage *msg, LPFINDBODY find_body, HBODY *out)
1981 {
1982     HRESULT hr;
1983     IMimeBody *mime_body;
1984     HBODY next;
1985
1986     if(find_body->dwReserved == 0)
1987         find_body->dwReserved = (DWORD)HBODY_ROOT;
1988     else
1989     {
1990         hr = IMimeMessage_GetBody(msg, IBL_FIRST, (HBODY)find_body->dwReserved, &next);
1991         if(hr == S_OK)
1992             find_body->dwReserved = (DWORD)next;
1993         else
1994         {
1995             hr = IMimeMessage_GetBody(msg, IBL_NEXT, (HBODY)find_body->dwReserved, &next);
1996             if(hr == S_OK)
1997                 find_body->dwReserved = (DWORD)next;
1998             else
1999                 return MIME_E_NOT_FOUND;
2000         }
2001     }
2002
2003     hr = IMimeMessage_BindToObject(msg, (HBODY)find_body->dwReserved, &IID_IMimeBody, (void**)&mime_body);
2004     if(IMimeBody_IsContentType(mime_body, find_body->pszPriType, find_body->pszSubType) == S_OK)
2005     {
2006         IMimeBody_Release(mime_body);
2007         *out = (HBODY)find_body->dwReserved;
2008         return S_OK;
2009     }
2010     IMimeBody_Release(mime_body);
2011     return find_next(msg, find_body, out);
2012 }
2013
2014 static HRESULT WINAPI MimeMessage_FindFirst(
2015     IMimeMessage *iface,
2016     LPFINDBODY pFindBody,
2017     LPHBODY phBody)
2018 {
2019     TRACE("(%p)->(%p, %p)\n", iface, pFindBody, phBody);
2020
2021     pFindBody->dwReserved = 0;
2022     return find_next(iface, pFindBody, phBody);
2023 }
2024
2025 static HRESULT WINAPI MimeMessage_FindNext(
2026     IMimeMessage *iface,
2027     LPFINDBODY pFindBody,
2028     LPHBODY phBody)
2029 {
2030     TRACE("(%p)->(%p, %p)\n", iface, pFindBody, phBody);
2031
2032     return find_next(iface, pFindBody, phBody);
2033 }
2034
2035 static HRESULT WINAPI MimeMessage_ResolveURL(
2036     IMimeMessage *iface,
2037     HBODY hRelated,
2038     LPCSTR pszBase,
2039     LPCSTR pszURL,
2040     DWORD dwFlags,
2041     LPHBODY phBody)
2042 {
2043     FIXME("(%p)->(%p, %s, %s, 0x%x, %p)\n", iface, hRelated, pszBase, pszURL, dwFlags, phBody);
2044     return E_NOTIMPL;
2045 }
2046
2047 static HRESULT WINAPI MimeMessage_ToMultipart(
2048     IMimeMessage *iface,
2049     HBODY hBody,
2050     LPCSTR pszSubType,
2051     LPHBODY phMultipart)
2052 {
2053     FIXME("(%p)->(%p, %s, %p)\n", iface, hBody, pszSubType, phMultipart);
2054     return E_NOTIMPL;
2055 }
2056
2057 static HRESULT WINAPI MimeMessage_GetBodyOffsets(
2058     IMimeMessage *iface,
2059     HBODY hBody,
2060     LPBODYOFFSETS pOffsets)
2061 {
2062     FIXME("(%p)->(%p, %p)\n", iface, hBody, pOffsets);
2063     return E_NOTIMPL;
2064 }
2065
2066 static HRESULT WINAPI MimeMessage_GetCharset(
2067     IMimeMessage *iface,
2068     LPHCHARSET phCharset)
2069 {
2070     FIXME("(%p)->(%p)\n", iface, phCharset);
2071     *phCharset = NULL;
2072     return S_OK;
2073 }
2074
2075 static HRESULT WINAPI MimeMessage_SetCharset(
2076     IMimeMessage *iface,
2077     HCHARSET hCharset,
2078     CSETAPPLYTYPE applytype)
2079 {
2080     FIXME("(%p)->(%p, %d)\n", iface, hCharset, applytype);
2081     return E_NOTIMPL;
2082 }
2083
2084 static HRESULT WINAPI MimeMessage_IsBodyType(
2085     IMimeMessage *iface,
2086     HBODY hBody,
2087     IMSGBODYTYPE bodytype)
2088 {
2089     HRESULT hr;
2090     IMimeBody *mime_body;
2091     TRACE("(%p)->(%p, %d)\n", iface, hBody, bodytype);
2092
2093     hr = IMimeMessage_BindToObject(iface, hBody, &IID_IMimeBody, (void**)&mime_body);
2094     if(hr != S_OK) return hr;
2095
2096     hr = IMimeBody_IsType(mime_body, bodytype);
2097     MimeBody_Release(mime_body);
2098     return hr;
2099 }
2100
2101 static HRESULT WINAPI MimeMessage_IsContentType(
2102     IMimeMessage *iface,
2103     HBODY hBody,
2104     LPCSTR pszPriType,
2105     LPCSTR pszSubType)
2106 {
2107     HRESULT hr;
2108     IMimeBody *mime_body;
2109     TRACE("(%p)->(%p, %s, %s)\n", iface, hBody, pszPriType, pszSubType);
2110
2111     hr = IMimeMessage_BindToObject(iface, hBody, &IID_IMimeBody, (void**)&mime_body);
2112     if(FAILED(hr)) return hr;
2113
2114     hr = IMimeBody_IsContentType(mime_body, pszPriType, pszSubType);
2115     IMimeBody_Release(mime_body);
2116     return hr;
2117 }
2118
2119 static HRESULT WINAPI MimeMessage_QueryBodyProp(
2120     IMimeMessage *iface,
2121     HBODY hBody,
2122     LPCSTR pszName,
2123     LPCSTR pszCriteria,
2124     boolean fSubString,
2125     boolean fCaseSensitive)
2126 {
2127     FIXME("(%p)->(%p, %s, %s, %s, %s)\n", iface, hBody, pszName, pszCriteria, fSubString ? "TRUE" : "FALSE", fCaseSensitive ? "TRUE" : "FALSE");
2128     return E_NOTIMPL;
2129 }
2130
2131 static HRESULT WINAPI MimeMessage_GetBodyProp(
2132     IMimeMessage *iface,
2133     HBODY hBody,
2134     LPCSTR pszName,
2135     DWORD dwFlags,
2136     LPPROPVARIANT pValue)
2137 {
2138     HRESULT hr;
2139     IMimeBody *mime_body;
2140
2141     TRACE("(%p)->(%p, %s, 0x%x, %p)\n", iface, hBody, pszName, dwFlags, pValue);
2142
2143     hr = IMimeMessage_BindToObject(iface, hBody, &IID_IMimeBody, (void**)&mime_body);
2144     if(hr != S_OK) return hr;
2145
2146     hr = IMimeBody_GetProp(mime_body, pszName, dwFlags, pValue);
2147     IMimeBody_Release(mime_body);
2148
2149     return hr;
2150 }
2151
2152 static HRESULT WINAPI MimeMessage_SetBodyProp(
2153     IMimeMessage *iface,
2154     HBODY hBody,
2155     LPCSTR pszName,
2156     DWORD dwFlags,
2157     LPCPROPVARIANT pValue)
2158 {
2159     FIXME("(%p)->(%p, %s, 0x%x, %p)\n", iface, hBody, pszName, dwFlags, pValue);
2160     return E_NOTIMPL;
2161 }
2162
2163 static HRESULT WINAPI MimeMessage_DeleteBodyProp(
2164     IMimeMessage *iface,
2165     HBODY hBody,
2166     LPCSTR pszName)
2167 {
2168     FIXME("(%p)->(%p, %s)\n", iface, hBody, pszName);
2169     return E_NOTIMPL;
2170 }
2171
2172 static HRESULT WINAPI MimeMessage_SetOption(
2173     IMimeMessage *iface,
2174     const TYPEDID oid,
2175     LPCPROPVARIANT pValue)
2176 {
2177     FIXME("(%p)->(%08x, %p)\n", iface, oid, pValue);
2178     return E_NOTIMPL;
2179 }
2180
2181 static HRESULT WINAPI MimeMessage_GetOption(
2182     IMimeMessage *iface,
2183     const TYPEDID oid,
2184     LPPROPVARIANT pValue)
2185 {
2186     FIXME("(%p)->(%08x, %p)\n", iface, oid, pValue);
2187     return E_NOTIMPL;
2188 }
2189
2190 /*** IMimeMessage methods ***/
2191 static HRESULT WINAPI MimeMessage_CreateWebPage(
2192     IMimeMessage *iface,
2193     IStream *pRootStm,
2194     LPWEBPAGEOPTIONS pOptions,
2195     IMimeMessageCallback *pCallback,
2196     IMoniker **ppMoniker)
2197 {
2198     FIXME("(%p)->(%p, %p, %p, %p)\n", iface, pRootStm, pOptions, pCallback, ppMoniker);
2199     *ppMoniker = NULL;
2200     return E_NOTIMPL;
2201 }
2202
2203 static HRESULT WINAPI MimeMessage_GetProp(
2204     IMimeMessage *iface,
2205     LPCSTR pszName,
2206     DWORD dwFlags,
2207     LPPROPVARIANT pValue)
2208 {
2209     FIXME("(%p)->(%s, 0x%x, %p)\n", iface, pszName, dwFlags, pValue);
2210     return E_NOTIMPL;
2211 }
2212
2213 static HRESULT WINAPI MimeMessage_SetProp(
2214     IMimeMessage *iface,
2215     LPCSTR pszName,
2216     DWORD dwFlags,
2217     LPCPROPVARIANT pValue)
2218 {
2219     FIXME("(%p)->(%s, 0x%x, %p)\n", iface, pszName, dwFlags, pValue);
2220     return E_NOTIMPL;
2221 }
2222
2223 static HRESULT WINAPI MimeMessage_DeleteProp(
2224     IMimeMessage *iface,
2225     LPCSTR pszName)
2226 {
2227     FIXME("(%p)->(%s)\n", iface, pszName);
2228     return E_NOTIMPL;
2229 }
2230
2231 static HRESULT WINAPI MimeMessage_QueryProp(
2232     IMimeMessage *iface,
2233     LPCSTR pszName,
2234     LPCSTR pszCriteria,
2235     boolean fSubString,
2236     boolean fCaseSensitive)
2237 {
2238     FIXME("(%p)->(%s, %s, %s, %s)\n", iface, pszName, pszCriteria, fSubString ? "TRUE" : "FALSE", fCaseSensitive ? "TRUE" : "FALSE");
2239     return E_NOTIMPL;
2240 }
2241
2242 static HRESULT WINAPI MimeMessage_GetTextBody(
2243     IMimeMessage *iface,
2244     DWORD dwTxtType,
2245     ENCODINGTYPE ietEncoding,
2246     IStream **pStream,
2247     LPHBODY phBody)
2248 {
2249     HRESULT hr;
2250     HBODY hbody;
2251     FINDBODY find_struct;
2252     IMimeBody *mime_body;
2253     static char text[] = "text";
2254     static char plain[] = "plain";
2255     static char html[] = "html";
2256
2257     TRACE("(%p)->(%d, %d, %p, %p)\n", iface, dwTxtType, ietEncoding, pStream, phBody);
2258
2259     find_struct.pszPriType = text;
2260
2261     switch(dwTxtType)
2262     {
2263     case TXT_PLAIN:
2264         find_struct.pszSubType = plain;
2265         break;
2266     case TXT_HTML:
2267         find_struct.pszSubType = html;
2268         break;
2269     default:
2270         return MIME_E_INVALID_TEXT_TYPE;
2271     }
2272
2273     hr = IMimeMessage_FindFirst(iface, &find_struct, &hbody);
2274     if(hr != S_OK)
2275     {
2276         TRACE("not found hr %08x\n", hr);
2277         *phBody = NULL;
2278         return hr;
2279     }
2280
2281     IMimeMessage_BindToObject(iface, hbody, &IID_IMimeBody, (void**)&mime_body);
2282
2283     IMimeBody_GetData(mime_body, ietEncoding, pStream);
2284     *phBody = hbody;
2285     IMimeBody_Release(mime_body);
2286     return hr;
2287 }
2288
2289 static HRESULT WINAPI MimeMessage_SetTextBody(
2290     IMimeMessage *iface,
2291     DWORD dwTxtType,
2292     ENCODINGTYPE ietEncoding,
2293     HBODY hAlternative,
2294     IStream *pStream,
2295     LPHBODY phBody)
2296 {
2297     FIXME("(%p)->(%d, %d, %p, %p, %p)\n", iface, dwTxtType, ietEncoding, hAlternative, pStream, phBody);
2298     return E_NOTIMPL;
2299 }
2300
2301 static HRESULT WINAPI MimeMessage_AttachObject(
2302     IMimeMessage *iface,
2303     REFIID riid,
2304     void *pvObject,
2305     LPHBODY phBody)
2306 {
2307     FIXME("(%p)->(%s, %p, %p)\n", iface, debugstr_guid(riid), pvObject, phBody);
2308     return E_NOTIMPL;
2309 }
2310
2311 static HRESULT WINAPI MimeMessage_AttachFile(
2312     IMimeMessage *iface,
2313     LPCSTR pszFilePath,
2314     IStream *pstmFile,
2315     LPHBODY phBody)
2316 {
2317     FIXME("(%p)->(%s, %p, %p)\n", iface, pszFilePath, pstmFile, phBody);
2318     return E_NOTIMPL;
2319 }
2320
2321 static HRESULT WINAPI MimeMessage_AttachURL(
2322     IMimeMessage *iface,
2323     LPCSTR pszBase,
2324     LPCSTR pszURL,
2325     DWORD dwFlags,
2326     IStream *pstmURL,
2327     LPSTR *ppszCIDURL,
2328     LPHBODY phBody)
2329 {
2330     FIXME("(%p)->(%s, %s, 0x%x, %p, %p, %p)\n", iface, pszBase, pszURL, dwFlags, pstmURL, ppszCIDURL, phBody);
2331     return E_NOTIMPL;
2332 }
2333
2334 static HRESULT WINAPI MimeMessage_GetAttachments(
2335     IMimeMessage *iface,
2336     ULONG *pcAttach,
2337     LPHBODY *pprghAttach)
2338 {
2339     HRESULT hr;
2340     FINDBODY find_struct;
2341     HBODY hbody;
2342     LPHBODY array;
2343     ULONG size = 10;
2344
2345     TRACE("(%p)->(%p, %p)\n", iface, pcAttach, pprghAttach);
2346
2347     *pcAttach = 0;
2348     array = CoTaskMemAlloc(size * sizeof(HBODY));
2349
2350     find_struct.pszPriType = find_struct.pszSubType = NULL;
2351     hr = IMimeMessage_FindFirst(iface, &find_struct, &hbody);
2352     while(hr == S_OK)
2353     {
2354         hr = IMimeMessage_IsContentType(iface, hbody, "multipart", NULL);
2355         TRACE("IsCT rets %08x %d\n", hr, *pcAttach);
2356         if(hr != S_OK)
2357         {
2358             if(*pcAttach + 1 > size)
2359             {
2360                 size *= 2;
2361                 array = CoTaskMemRealloc(array, size * sizeof(HBODY));
2362             }
2363             array[*pcAttach] = hbody;
2364             (*pcAttach)++;
2365         }
2366         hr = IMimeMessage_FindNext(iface, &find_struct, &hbody);
2367     }
2368
2369     *pprghAttach = array;
2370     return S_OK;
2371 }
2372
2373 static HRESULT WINAPI MimeMessage_GetAddressTable(
2374     IMimeMessage *iface,
2375     IMimeAddressTable **ppTable)
2376 {
2377     FIXME("(%p)->(%p)\n", iface, ppTable);
2378     return E_NOTIMPL;
2379 }
2380
2381 static HRESULT WINAPI MimeMessage_GetSender(
2382     IMimeMessage *iface,
2383     LPADDRESSPROPS pAddress)
2384 {
2385     FIXME("(%p)->(%p)\n", iface, pAddress);
2386     return E_NOTIMPL;
2387 }
2388
2389 static HRESULT WINAPI MimeMessage_GetAddressTypes(
2390     IMimeMessage *iface,
2391     DWORD dwAdrTypes,
2392     DWORD dwProps,
2393     LPADDRESSLIST pList)
2394 {
2395     FIXME("(%p)->(%d, %d, %p)\n", iface, dwAdrTypes, dwProps, pList);
2396     return E_NOTIMPL;
2397 }
2398
2399 static HRESULT WINAPI MimeMessage_GetAddressFormat(
2400     IMimeMessage *iface,
2401     DWORD dwAdrTypes,
2402     ADDRESSFORMAT format,
2403     LPSTR *ppszFormat)
2404 {
2405     FIXME("(%p)->(%d, %d, %p)\n", iface, dwAdrTypes, format, ppszFormat);
2406     return E_NOTIMPL;
2407 }
2408
2409 static HRESULT WINAPI MimeMessage_EnumAddressTypes(
2410     IMimeMessage *iface,
2411     DWORD dwAdrTypes,
2412     DWORD dwProps,
2413     IMimeEnumAddressTypes **ppEnum)
2414 {
2415     FIXME("(%p)->(%d, %d, %p)\n", iface, dwAdrTypes, dwProps, ppEnum);
2416     return E_NOTIMPL;
2417 }
2418
2419 static HRESULT WINAPI MimeMessage_SplitMessage(
2420     IMimeMessage *iface,
2421     ULONG cbMaxPart,
2422     IMimeMessageParts **ppParts)
2423 {
2424     FIXME("(%p)->(%d, %p)\n", iface, cbMaxPart, ppParts);
2425     return E_NOTIMPL;
2426 }
2427
2428 static HRESULT WINAPI MimeMessage_GetRootMoniker(
2429     IMimeMessage *iface,
2430     IMoniker **ppMoniker)
2431 {
2432     FIXME("(%p)->(%p)\n", iface, ppMoniker);
2433     return E_NOTIMPL;
2434 }
2435
2436 static const IMimeMessageVtbl MimeMessageVtbl =
2437 {
2438     MimeMessage_QueryInterface,
2439     MimeMessage_AddRef,
2440     MimeMessage_Release,
2441     MimeMessage_GetClassID,
2442     MimeMessage_IsDirty,
2443     MimeMessage_Load,
2444     MimeMessage_Save,
2445     MimeMessage_GetSizeMax,
2446     MimeMessage_InitNew,
2447     MimeMessage_GetMessageSource,
2448     MimeMessage_GetMessageSize,
2449     MimeMessage_LoadOffsetTable,
2450     MimeMessage_SaveOffsetTable,
2451     MimeMessage_GetFlags,
2452     MimeMessage_Commit,
2453     MimeMessage_HandsOffStorage,
2454     MimeMessage_BindToObject,
2455     MimeMessage_SaveBody,
2456     MimeMessage_InsertBody,
2457     MimeMessage_GetBody,
2458     MimeMessage_DeleteBody,
2459     MimeMessage_MoveBody,
2460     MimeMessage_CountBodies,
2461     MimeMessage_FindFirst,
2462     MimeMessage_FindNext,
2463     MimeMessage_ResolveURL,
2464     MimeMessage_ToMultipart,
2465     MimeMessage_GetBodyOffsets,
2466     MimeMessage_GetCharset,
2467     MimeMessage_SetCharset,
2468     MimeMessage_IsBodyType,
2469     MimeMessage_IsContentType,
2470     MimeMessage_QueryBodyProp,
2471     MimeMessage_GetBodyProp,
2472     MimeMessage_SetBodyProp,
2473     MimeMessage_DeleteBodyProp,
2474     MimeMessage_SetOption,
2475     MimeMessage_GetOption,
2476     MimeMessage_CreateWebPage,
2477     MimeMessage_GetProp,
2478     MimeMessage_SetProp,
2479     MimeMessage_DeleteProp,
2480     MimeMessage_QueryProp,
2481     MimeMessage_GetTextBody,
2482     MimeMessage_SetTextBody,
2483     MimeMessage_AttachObject,
2484     MimeMessage_AttachFile,
2485     MimeMessage_AttachURL,
2486     MimeMessage_GetAttachments,
2487     MimeMessage_GetAddressTable,
2488     MimeMessage_GetSender,
2489     MimeMessage_GetAddressTypes,
2490     MimeMessage_GetAddressFormat,
2491     MimeMessage_EnumAddressTypes,
2492     MimeMessage_SplitMessage,
2493     MimeMessage_GetRootMoniker,
2494 };
2495
2496 /***********************************************************************
2497  *              MimeOleCreateMessage (INETCOMM.@)
2498  */
2499 HRESULT WINAPI MimeOleCreateMessage(IUnknown *pUnkOuter, IMimeMessage **ppMessage)
2500 {
2501     MimeMessage *This;
2502
2503     TRACE("(%p, %p)\n", pUnkOuter, ppMessage);
2504
2505     if (pUnkOuter)
2506     {
2507         FIXME("outer unknown not supported yet\n");
2508         return E_NOTIMPL;
2509     }
2510
2511     *ppMessage = NULL;
2512
2513     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
2514     if (!This) return E_OUTOFMEMORY;
2515
2516     This->lpVtbl = &MimeMessageVtbl;
2517     This->refs = 1;
2518     This->stream = NULL;
2519     list_init(&This->body_tree);
2520     This->next_hbody = (HBODY)1;
2521
2522     *ppMessage = (IMimeMessage *)&This->lpVtbl;
2523     return S_OK;
2524 }
2525
2526 /***********************************************************************
2527  *              MimeOleSetCompatMode (INETCOMM.@)
2528  */
2529 HRESULT WINAPI MimeOleSetCompatMode(DWORD dwMode)
2530 {
2531     FIXME("(0x%x)\n", dwMode);
2532     return S_OK;
2533 }
2534
2535 /***********************************************************************
2536  *              MimeOleCreateVirtualStream (INETCOMM.@)
2537  */
2538 HRESULT WINAPI MimeOleCreateVirtualStream(IStream **ppStream)
2539 {
2540     HRESULT hr;
2541     FIXME("(%p)\n", ppStream);
2542
2543     hr = CreateStreamOnHGlobal(NULL, TRUE, ppStream);
2544     return hr;
2545 }
2546
2547 typedef struct MimeSecurity
2548 {
2549     const IMimeSecurityVtbl *lpVtbl;
2550
2551     LONG refs;
2552 } MimeSecurity;
2553
2554 static HRESULT WINAPI MimeSecurity_QueryInterface(
2555         IMimeSecurity* iface,
2556         REFIID riid,
2557         void** obj)
2558 {
2559     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), obj);
2560
2561     if (IsEqualIID(riid, &IID_IUnknown) ||
2562         IsEqualIID(riid, &IID_IMimeSecurity))
2563     {
2564         *obj = iface;
2565         IUnknown_AddRef(iface);
2566         return S_OK;
2567     }
2568
2569     FIXME("no interface for %s\n", debugstr_guid(riid));
2570     *obj = NULL;
2571     return E_NOINTERFACE;
2572 }
2573
2574 static ULONG WINAPI MimeSecurity_AddRef(
2575         IMimeSecurity* iface)
2576 {
2577     MimeSecurity *This = (MimeSecurity *)iface;
2578     TRACE("(%p)->()\n", iface);
2579     return InterlockedIncrement(&This->refs);
2580 }
2581
2582 static ULONG WINAPI MimeSecurity_Release(
2583         IMimeSecurity* iface)
2584 {
2585     MimeSecurity *This = (MimeSecurity *)iface;
2586     ULONG refs;
2587
2588     TRACE("(%p)->()\n", iface);
2589
2590     refs = InterlockedDecrement(&This->refs);
2591     if (!refs)
2592     {
2593         HeapFree(GetProcessHeap(), 0, This);
2594     }
2595
2596     return refs;
2597 }
2598
2599 static HRESULT WINAPI MimeSecurity_InitNew(
2600         IMimeSecurity* iface)
2601 {
2602     FIXME("(%p)->(): stub\n", iface);
2603     return S_OK;
2604 }
2605
2606 static HRESULT WINAPI MimeSecurity_CheckInit(
2607         IMimeSecurity* iface)
2608 {
2609     FIXME("(%p)->(): stub\n", iface);
2610     return E_NOTIMPL;
2611 }
2612
2613 static HRESULT WINAPI MimeSecurity_EncodeMessage(
2614         IMimeSecurity* iface,
2615         IMimeMessageTree* pTree,
2616         DWORD dwFlags)
2617 {
2618     FIXME("(%p)->(%p, %08x): stub\n", iface, pTree, dwFlags);
2619     return E_NOTIMPL;
2620 }
2621
2622 static HRESULT WINAPI MimeSecurity_EncodeBody(
2623         IMimeSecurity* iface,
2624         IMimeMessageTree* pTree,
2625         HBODY hEncodeRoot,
2626         DWORD dwFlags)
2627 {
2628     FIXME("(%p)->(%p, %p, %08x): stub\n", iface, pTree, hEncodeRoot, dwFlags);
2629     return E_NOTIMPL;
2630 }
2631
2632 static HRESULT WINAPI MimeSecurity_DecodeMessage(
2633         IMimeSecurity* iface,
2634         IMimeMessageTree* pTree,
2635         DWORD dwFlags)
2636 {
2637     FIXME("(%p)->(%p, %08x): stub\n", iface, pTree, dwFlags);
2638     return E_NOTIMPL;
2639 }
2640
2641 static HRESULT WINAPI MimeSecurity_DecodeBody(
2642         IMimeSecurity* iface,
2643         IMimeMessageTree* pTree,
2644         HBODY hDecodeRoot,
2645         DWORD dwFlags)
2646 {
2647     FIXME("(%p)->(%p, %p, %08x): stub\n", iface, pTree, hDecodeRoot, dwFlags);
2648     return E_NOTIMPL;
2649 }
2650
2651 static HRESULT WINAPI MimeSecurity_EnumCertificates(
2652         IMimeSecurity* iface,
2653         HCAPICERTSTORE hc,
2654         DWORD dwUsage,
2655         PCX509CERT pPrev,
2656         PCX509CERT* ppCert)
2657 {
2658     FIXME("(%p)->(%p, %08x, %p, %p): stub\n", iface, hc, dwUsage, pPrev, ppCert);
2659     return E_NOTIMPL;
2660 }
2661
2662 static HRESULT WINAPI MimeSecurity_GetCertificateName(
2663         IMimeSecurity* iface,
2664         const PCX509CERT pX509Cert,
2665         const CERTNAMETYPE cn,
2666         LPSTR* ppszName)
2667 {
2668     FIXME("(%p)->(%p, %08x, %p): stub\n", iface, pX509Cert, cn, ppszName);
2669     return E_NOTIMPL;
2670 }
2671
2672 static HRESULT WINAPI MimeSecurity_GetMessageType(
2673         IMimeSecurity* iface,
2674         const HWND hwndParent,
2675         IMimeBody* pBody,
2676         DWORD* pdwSecType)
2677 {
2678     FIXME("(%p)->(%p, %p, %p): stub\n", iface, hwndParent, pBody, pdwSecType);
2679     return E_NOTIMPL;
2680 }
2681
2682 static HRESULT WINAPI MimeSecurity_GetCertData(
2683         IMimeSecurity* iface,
2684         const PCX509CERT pX509Cert,
2685         const CERTDATAID dataid,
2686         LPPROPVARIANT pValue)
2687 {
2688     FIXME("(%p)->(%p, %x, %p): stub\n", iface, pX509Cert, dataid, pValue);
2689     return E_NOTIMPL;
2690 }
2691
2692
2693 static const IMimeSecurityVtbl MimeSecurityVtbl =
2694 {
2695     MimeSecurity_QueryInterface,
2696     MimeSecurity_AddRef,
2697     MimeSecurity_Release,
2698     MimeSecurity_InitNew,
2699     MimeSecurity_CheckInit,
2700     MimeSecurity_EncodeMessage,
2701     MimeSecurity_EncodeBody,
2702     MimeSecurity_DecodeMessage,
2703     MimeSecurity_DecodeBody,
2704     MimeSecurity_EnumCertificates,
2705     MimeSecurity_GetCertificateName,
2706     MimeSecurity_GetMessageType,
2707     MimeSecurity_GetCertData
2708 };
2709
2710 /***********************************************************************
2711  *              MimeOleCreateSecurity (INETCOMM.@)
2712  */
2713 HRESULT WINAPI MimeOleCreateSecurity(IMimeSecurity **ppSecurity)
2714 {
2715     MimeSecurity *This;
2716
2717     TRACE("(%p)\n", ppSecurity);
2718
2719     *ppSecurity = NULL;
2720
2721     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
2722     if (!This) return E_OUTOFMEMORY;
2723
2724     This->lpVtbl = &MimeSecurityVtbl;
2725     This->refs = 1;
2726
2727     *ppSecurity = (IMimeSecurity *)&This->lpVtbl;
2728     return S_OK;
2729 }
2730
2731
2732 typedef struct
2733 {
2734     IMimeAllocatorVtbl *lpVtbl;
2735 } MimeAllocator;
2736
2737 static HRESULT WINAPI MimeAlloc_QueryInterface(
2738         IMimeAllocator* iface,
2739         REFIID riid,
2740         void **obj)
2741 {
2742     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), obj);
2743
2744     if (IsEqualIID(riid, &IID_IUnknown) ||
2745         IsEqualIID(riid, &IID_IMalloc) ||
2746         IsEqualIID(riid, &IID_IMimeAllocator))
2747     {
2748         *obj = iface;
2749         IUnknown_AddRef(iface);
2750         return S_OK;
2751     }
2752
2753     FIXME("no interface for %s\n", debugstr_guid(riid));
2754     *obj = NULL;
2755     return E_NOINTERFACE;
2756 }
2757
2758 static ULONG WINAPI MimeAlloc_AddRef(
2759         IMimeAllocator* iface)
2760 {
2761     return 2;
2762 }
2763
2764 static ULONG WINAPI MimeAlloc_Release(
2765         IMimeAllocator* iface)
2766 {
2767     return 1;
2768 }
2769
2770 static LPVOID WINAPI MimeAlloc_Alloc(
2771         IMimeAllocator* iface,
2772         ULONG cb)
2773 {
2774     return CoTaskMemAlloc(cb);
2775 }
2776
2777 static LPVOID WINAPI MimeAlloc_Realloc(
2778         IMimeAllocator* iface,
2779         LPVOID pv,
2780         ULONG cb)
2781 {
2782     return CoTaskMemRealloc(pv, cb);
2783 }
2784
2785 static void WINAPI MimeAlloc_Free(
2786         IMimeAllocator* iface,
2787         LPVOID pv)
2788 {
2789     CoTaskMemFree(pv);
2790 }
2791
2792 static ULONG WINAPI MimeAlloc_GetSize(
2793         IMimeAllocator* iface,
2794         LPVOID pv)
2795 {
2796     FIXME("stub\n");
2797     return 0;
2798 }
2799
2800 static int WINAPI MimeAlloc_DidAlloc(
2801         IMimeAllocator* iface,
2802         LPVOID pv)
2803 {
2804     FIXME("stub\n");
2805     return 0;
2806 }
2807
2808 static void WINAPI MimeAlloc_HeapMinimize(
2809         IMimeAllocator* iface)
2810 {
2811     FIXME("stub\n");
2812     return;
2813 }
2814
2815 static HRESULT WINAPI MimeAlloc_FreeParamInfoArray(
2816         IMimeAllocator* iface,
2817         ULONG cParams,
2818         LPMIMEPARAMINFO prgParam,
2819         boolean fFreeArray)
2820 {
2821     ULONG i;
2822     TRACE("(%p)->(%d, %p, %d)\n", iface, cParams, prgParam, fFreeArray);
2823
2824     for(i = 0; i < cParams; i++)
2825     {
2826         IMimeAllocator_Free(iface, prgParam[i].pszName);
2827         IMimeAllocator_Free(iface, prgParam[i].pszData);
2828     }
2829     if(fFreeArray) IMimeAllocator_Free(iface, prgParam);
2830     return S_OK;
2831 }
2832
2833 static HRESULT WINAPI MimeAlloc_FreeAddressList(
2834         IMimeAllocator* iface,
2835         LPADDRESSLIST pList)
2836 {
2837     FIXME("stub\n");
2838     return E_NOTIMPL;
2839 }
2840
2841 static HRESULT WINAPI MimeAlloc_FreeAddressProps(
2842         IMimeAllocator* iface,
2843         LPADDRESSPROPS pAddress)
2844 {
2845     FIXME("stub\n");
2846     return E_NOTIMPL;
2847 }
2848
2849 static HRESULT WINAPI MimeAlloc_ReleaseObjects(
2850         IMimeAllocator* iface,
2851         ULONG cObjects,
2852         IUnknown **prgpUnknown,
2853         boolean fFreeArray)
2854 {
2855     FIXME("stub\n");
2856     return E_NOTIMPL;
2857 }
2858
2859
2860 static HRESULT WINAPI MimeAlloc_FreeEnumHeaderRowArray(
2861         IMimeAllocator* iface,
2862         ULONG cRows,
2863         LPENUMHEADERROW prgRow,
2864         boolean fFreeArray)
2865 {
2866     FIXME("stub\n");
2867     return E_NOTIMPL;
2868 }
2869
2870 static HRESULT WINAPI MimeAlloc_FreeEnumPropertyArray(
2871         IMimeAllocator* iface,
2872         ULONG cProps,
2873         LPENUMPROPERTY prgProp,
2874         boolean fFreeArray)
2875 {
2876     FIXME("stub\n");
2877     return E_NOTIMPL;
2878 }
2879
2880 static HRESULT WINAPI MimeAlloc_FreeThumbprint(
2881         IMimeAllocator* iface,
2882         THUMBBLOB *pthumbprint)
2883 {
2884     FIXME("stub\n");
2885     return E_NOTIMPL;
2886 }
2887
2888
2889 static HRESULT WINAPI MimeAlloc_PropVariantClear(
2890         IMimeAllocator* iface,
2891         LPPROPVARIANT pProp)
2892 {
2893     FIXME("stub\n");
2894     return E_NOTIMPL;
2895 }
2896
2897 static IMimeAllocatorVtbl mime_alloc_vtbl =
2898 {
2899     MimeAlloc_QueryInterface,
2900     MimeAlloc_AddRef,
2901     MimeAlloc_Release,
2902     MimeAlloc_Alloc,
2903     MimeAlloc_Realloc,
2904     MimeAlloc_Free,
2905     MimeAlloc_GetSize,
2906     MimeAlloc_DidAlloc,
2907     MimeAlloc_HeapMinimize,
2908     MimeAlloc_FreeParamInfoArray,
2909     MimeAlloc_FreeAddressList,
2910     MimeAlloc_FreeAddressProps,
2911     MimeAlloc_ReleaseObjects,
2912     MimeAlloc_FreeEnumHeaderRowArray,
2913     MimeAlloc_FreeEnumPropertyArray,
2914     MimeAlloc_FreeThumbprint,
2915     MimeAlloc_PropVariantClear
2916 };
2917
2918 static MimeAllocator mime_allocator =
2919 {
2920     &mime_alloc_vtbl
2921 };
2922
2923 HRESULT MimeAllocator_create(IUnknown *outer, void **obj)
2924 {
2925     if(outer) return CLASS_E_NOAGGREGATION;
2926
2927     *obj = &mime_allocator;
2928     return S_OK;
2929 }
2930
2931 HRESULT WINAPI MimeOleGetAllocator(IMimeAllocator **alloc)
2932 {
2933     return MimeAllocator_create(NULL, (void**)alloc);
2934 }
2935
2936 HRESULT WINAPI MimeOleGetCharsetInfo(HCHARSET hCharset, LPINETCSETINFO pCsetInfo)
2937 {
2938     FIXME("(%p, %p)\n", hCharset, pCsetInfo);
2939     if(!hCharset) return E_INVALIDARG;
2940     return E_FAIL;
2941 }