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