[INETCOMM] Sync with Wine Staging 4.18. CORE-16441
[reactos.git] / dll / win32 / inetcomm / mimeole.c
index 9e25ed5..d69c40c 100644 (file)
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#define COBJMACROS
+#define NONAMELESSUNION
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wine/winternl.h"
+#include "winuser.h"
+#include "objbase.h"
+#include "ole2.h"
+#include "mimeole.h"
+#ifdef __REACTOS__
+#include <winreg.h>
+#endif
+#include "propvarutil.h"
+
+#include "wine/heap.h"
+#include "wine/list.h"
+#include "wine/debug.h"
+
 #include "inetcomm_private.h"
 
+WINE_DEFAULT_DEBUG_CHANNEL(inetcomm);
+
 typedef struct
 {
     LPCSTR     name;
@@ -37,26 +61,81 @@ typedef struct
 
 static const property_t default_props[] =
 {
-    {"References",                   PID_HDR_REFS,       0,                               VT_LPSTR},
-    {"Subject",                      PID_HDR_SUBJECT,    0,                               VT_LPSTR},
-    {"From",                         PID_HDR_FROM,       MPF_ADDRESS,                     VT_LPSTR},
-    {"Message-ID",                   PID_HDR_MESSAGEID,  0,                               VT_LPSTR},
-    {"Return-Path",                  PID_HDR_RETURNPATH, MPF_ADDRESS,                     VT_LPSTR},
-    {"Date",                         PID_HDR_DATE,       0,                               VT_LPSTR},
-    {"Received",                     PID_HDR_RECEIVED,   0,                               VT_LPSTR},
-    {"Reply-To",                     PID_HDR_REPLYTO,    MPF_ADDRESS,                     VT_LPSTR},
-    {"X-Mailer",                     PID_HDR_XMAILER,    0,                               VT_LPSTR},
-    {"Bcc",                          PID_HDR_BCC,        MPF_ADDRESS,                     VT_LPSTR},
-    {"MIME-Version",                 PID_HDR_MIMEVER,    MPF_MIME,                        VT_LPSTR},
-    {"Content-Type",                 PID_HDR_CNTTYPE,    MPF_MIME | MPF_HASPARAMS,        VT_LPSTR},
-    {"Content-Transfer-Encoding",    PID_HDR_CNTXFER,    MPF_MIME,                        VT_LPSTR},
-    {"Content-ID",                   PID_HDR_CNTID,      MPF_MIME,                        VT_LPSTR},
-    {"Content-Disposition",          PID_HDR_CNTDISP,    MPF_MIME | MPF_HASPARAMS,        VT_LPSTR},
-    {"To",                           PID_HDR_TO,         MPF_ADDRESS,                     VT_LPSTR},
-    {"Cc",                           PID_HDR_CC,         MPF_ADDRESS,                     VT_LPSTR},
-    {"Sender",                       PID_HDR_SENDER,     MPF_ADDRESS,                     VT_LPSTR},
-    {"In-Reply-To",                  PID_HDR_INREPLYTO,  0,                               VT_LPSTR},
-    {NULL,                           0,                  0,                               0}
+    {"X-Newsgroup",                  PID_HDR_NEWSGROUP,    0,                               VT_LPSTR},
+    {"Newsgroups",                   PID_HDR_NEWSGROUPS,   0,                               VT_LPSTR},
+    {"References",                   PID_HDR_REFS,         0,                               VT_LPSTR},
+    {"Subject",                      PID_HDR_SUBJECT,      0,                               VT_LPSTR},
+    {"From",                         PID_HDR_FROM,         MPF_ADDRESS,                     VT_LPSTR},
+    {"Message-ID",                   PID_HDR_MESSAGEID,    0,                               VT_LPSTR},
+    {"Return-Path",                  PID_HDR_RETURNPATH,   MPF_ADDRESS,                     VT_LPSTR},
+    {"Rr",                           PID_HDR_RR,           0,                               VT_LPSTR},
+    {"Return-Receipt-To",            PID_HDR_RETRCPTO,     MPF_ADDRESS,                     VT_LPSTR},
+    {"Apparently-To",                PID_HDR_APPARTO,      MPF_ADDRESS,                     VT_LPSTR},
+    {"Date",                         PID_HDR_DATE,         0,                               VT_LPSTR},
+    {"Received",                     PID_HDR_RECEIVED,     0,                               VT_LPSTR},
+    {"Reply-To",                     PID_HDR_REPLYTO,      MPF_ADDRESS,                     VT_LPSTR},
+    {"X-Mailer",                     PID_HDR_XMAILER,      0,                               VT_LPSTR},
+    {"Bcc",                          PID_HDR_BCC,          MPF_ADDRESS,                     VT_LPSTR},
+    {"MIME-Version",                 PID_HDR_MIMEVER,      MPF_MIME,                        VT_LPSTR},
+    {"Content-Type",                 PID_HDR_CNTTYPE,      MPF_MIME | MPF_HASPARAMS,        VT_LPSTR},
+    {"Content-Transfer-Encoding",    PID_HDR_CNTXFER,      MPF_MIME,                        VT_LPSTR},
+    {"Content-ID",                   PID_HDR_CNTID,        MPF_MIME,                        VT_LPSTR},
+    {"Content-Description",          PID_HDR_CNTDESC,      MPF_MIME,                        VT_LPSTR},
+    {"Content-Disposition",          PID_HDR_CNTDISP,      MPF_MIME | MPF_HASPARAMS,        VT_LPSTR},
+    {"Content-Base",                 PID_HDR_CNTBASE,      MPF_MIME,                        VT_LPSTR},
+    {"Content-Location",             PID_HDR_CNTLOC,       MPF_MIME,                        VT_LPSTR},
+    {"To",                           PID_HDR_TO,           MPF_ADDRESS,                     VT_LPSTR},
+    {"Path",                         PID_HDR_PATH,         0,                               VT_LPSTR},
+    {"Followup-To",                  PID_HDR_FOLLOWUPTO,   0,                               VT_LPSTR},
+    {"Expires",                      PID_HDR_EXPIRES,      0,                               VT_LPSTR},
+    {"Cc",                           PID_HDR_CC,           MPF_ADDRESS,                     VT_LPSTR},
+    {"Control",                      PID_HDR_CONTROL,      0,                               VT_LPSTR},
+    {"Distribution",                 PID_HDR_DISTRIB,      0,                               VT_LPSTR},
+    {"Keywords",                     PID_HDR_KEYWORDS,     0,                               VT_LPSTR},
+    {"Summary",                      PID_HDR_SUMMARY,      0,                               VT_LPSTR},
+    {"Approved",                     PID_HDR_APPROVED,     0,                               VT_LPSTR},
+    {"Lines",                        PID_HDR_LINES,        0,                               VT_LPSTR},
+    {"Xref",                         PID_HDR_XREF,         0,                               VT_LPSTR},
+    {"Organization",                 PID_HDR_ORG,          0,                               VT_LPSTR},
+    {"X-Newsreader",                 PID_HDR_XNEWSRDR,     0,                               VT_LPSTR},
+    {"X-Priority",                   PID_HDR_XPRI,         0,                               VT_LPSTR},
+    {"X-MSMail-Priority",            PID_HDR_XMSPRI,       0,                               VT_LPSTR},
+    {"par:content-disposition:filename", PID_PAR_FILENAME, 0,                               VT_LPSTR},
+    {"par:content-type:boundary",    PID_PAR_BOUNDARY,     0,                               VT_LPSTR},
+    {"par:content-type:charset",     PID_PAR_CHARSET,      0,                               VT_LPSTR},
+    {"par:content-type:name",        PID_PAR_NAME,         0,                               VT_LPSTR},
+    {"att:filename",                 PID_ATT_FILENAME,     0,                               VT_LPSTR},
+    {"att:pri-content-type",         PID_ATT_PRITYPE,      0,                               VT_LPSTR},
+    {"att:sub-content-type",         PID_ATT_SUBTYPE,      0,                               VT_LPSTR},
+    {"att:illegal-lines",            PID_ATT_ILLEGAL,      0,                               VT_LPSTR},
+    {"att:rendered",                 PID_ATT_RENDERED,     0,                               VT_LPSTR},
+    {"att:sent-time",                PID_ATT_SENTTIME,     0,                               VT_LPSTR},
+    {"att:priority",                 PID_ATT_PRIORITY,     0,                               VT_LPSTR},
+    {"Comment",                      PID_HDR_COMMENT,      0,                               VT_LPSTR},
+    {"Encoding",                     PID_HDR_ENCODING,     0,                               VT_LPSTR},
+    {"Encrypted",                    PID_HDR_ENCRYPTED,    0,                               VT_LPSTR},
+    {"X-Offsets",                    PID_HDR_OFFSETS,      0,                               VT_LPSTR},
+    {"X-Unsent",                     PID_HDR_XUNSENT,      0,                               VT_LPSTR},
+    {"X-ArticleId",                  PID_HDR_ARTICLEID,    0,                               VT_LPSTR},
+    {"Sender",                       PID_HDR_SENDER,       MPF_ADDRESS,                     VT_LPSTR},
+    {"att:athena-server",            PID_ATT_SERVER,       0,                               VT_LPSTR},
+    {"att:athena-account-id",        PID_ATT_ACCOUNT,      0,                               VT_LPSTR},
+    {"att:athena-pop3-uidl",         PID_ATT_UIDL,         0,                               VT_LPSTR},
+    {"att:athena-store-msgid",       PID_ATT_STOREMSGID,   0,                               VT_LPSTR},
+    {"att:athena-user-name",         PID_ATT_USERNAME,     0,                               VT_LPSTR},
+    {"att:athena-forward-to",        PID_ATT_FORWARDTO,    0,                               VT_LPSTR},
+    {"att:athena-store-fdrid",       PID_ATT_STOREFOLDERID,0,                               VT_LPSTR},
+    {"att:athena-ghosted",           PID_ATT_GHOSTED,      0,                               VT_LPSTR},
+    {"att:athena-uncachedsize",      PID_ATT_UNCACHEDSIZE, 0,                               VT_LPSTR},
+    {"att:athena-combined",          PID_ATT_COMBINED,     0,                               VT_LPSTR},
+    {"att:auto-inlined",             PID_ATT_AUTOINLINED,  0,                               VT_LPSTR},
+    {"Disposition-Notification-To",  PID_HDR_DISP_NOTIFICATION_TO,  0,                      VT_LPSTR},
+    {"par:Content-Type:reply-type",  PID_PAR_REPLYTYPE,    0,                               VT_LPSTR},
+    {"par:Content-Type:format",      PID_PAR_FORMAT ,      0,                               VT_LPSTR},
+    {"att:format",                   PID_ATT_FORMAT ,      0,                               VT_LPSTR},
+    {"In-Reply-To",                  PID_HDR_INREPLYTO,    0,                               VT_LPSTR},
+    {"att:athena-account-name",      PID_ATT_ACCOUNTNAME,  0,                               VT_LPSTR},
+    {NULL,                           0,                    0,                               0}
 };
 
 typedef struct
@@ -92,188 +171,491 @@ typedef struct MimeBody
     BODYOFFSETS body_offsets;
 } MimeBody;
 
-static inline MimeBody *impl_from_IMimeBody(IMimeBody *iface)
+typedef struct
 {
-    return CONTAINING_RECORD(iface, MimeBody, IMimeBody_iface);
+    IStream IStream_iface;
+    LONG ref;
+    IStream *base;
+    ULARGE_INTEGER pos, start, length;
+} sub_stream_t;
+
+static inline sub_stream_t *impl_from_IStream(IStream *iface)
+{
+    return CONTAINING_RECORD(iface, sub_stream_t, IStream_iface);
 }
 
-static LPSTR strdupA(LPCSTR str)
+static HRESULT WINAPI sub_stream_QueryInterface(IStream *iface, REFIID riid, void **ppv)
 {
-    char *ret;
-    int len = strlen(str);
-    ret = HeapAlloc(GetProcessHeap(), 0, len + 1);
-    memcpy(ret, str, len + 1);
-    return ret;
+    sub_stream_t *This = impl_from_IStream(iface);
+
+    TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
+    *ppv = NULL;
+
+    if(IsEqualIID(riid, &IID_IUnknown) ||
+       IsEqualIID(riid, &IID_ISequentialStream) ||
+       IsEqualIID(riid, &IID_IStream))
+    {
+        IStream_AddRef(iface);
+        *ppv = iface;
+        return S_OK;
+    }
+    return E_NOINTERFACE;
 }
 
-#define PARSER_BUF_SIZE 1024
+static ULONG WINAPI sub_stream_AddRef(IStream *iface)
+{
+    sub_stream_t *This = impl_from_IStream(iface);
+    LONG ref = InterlockedIncrement(&This->ref);
 
-/*****************************************************
- *        copy_headers_to_buf [internal]
- *
- * Copies the headers into a '\0' terminated memory block and leave
- * the stream's current position set to after the blank line.
- */
-static HRESULT copy_headers_to_buf(IStream *stm, char **ptr)
+    TRACE("(%p) ref=%d\n", This, ref);
+
+    return ref;
+}
+
+static ULONG WINAPI sub_stream_Release(IStream *iface)
 {
-    char *buf = NULL;
-    DWORD size = PARSER_BUF_SIZE, offset = 0, last_end = 0;
-    HRESULT hr;
-    BOOL done = FALSE;
+    sub_stream_t *This = impl_from_IStream(iface);
+    LONG ref = InterlockedDecrement(&This->ref);
 
-    *ptr = NULL;
+    TRACE("(%p) ref=%d\n", This, ref);
 
-    do
+    if(!ref)
     {
-        char *end;
-        DWORD read;
+        IStream_Release(This->base);
+        HeapFree(GetProcessHeap(), 0, This);
+    }
+    return ref;
+}
 
-        if(!buf)
-            buf = HeapAlloc(GetProcessHeap(), 0, size + 1);
-        else
-        {
-            size *= 2;
-            buf = HeapReAlloc(GetProcessHeap(), 0, buf, size + 1);
-        }
-        if(!buf)
-        {
-            hr = E_OUTOFMEMORY;
-            goto fail;
-        }
+static HRESULT WINAPI sub_stream_Read(
+        IStream* iface,
+        void *pv,
+        ULONG cb,
+        ULONG *pcbRead)
+{
+    sub_stream_t *This = impl_from_IStream(iface);
+    HRESULT hr;
+    LARGE_INTEGER tmp_pos;
 
-        hr = IStream_Read(stm, buf + offset, size - offset, &read);
-        if(FAILED(hr)) goto fail;
+    TRACE("(%p, %d, %p)\n", pv, cb, pcbRead);
 
-        offset += read;
-        buf[offset] = '\0';
+    tmp_pos.QuadPart = This->pos.QuadPart + This->start.QuadPart;
+    IStream_Seek(This->base, tmp_pos, STREAM_SEEK_SET, NULL);
 
-        if(read == 0) done = TRUE;
+    if(This->pos.QuadPart + cb > This->length.QuadPart)
+        cb = This->length.QuadPart - This->pos.QuadPart;
 
-        while(!done && (end = strstr(buf + last_end, "\r\n")))
-        {
-            DWORD new_end = end - buf + 2;
-            if(new_end - last_end == 2)
-            {
-                LARGE_INTEGER off;
-                off.QuadPart = new_end;
-                IStream_Seek(stm, off, STREAM_SEEK_SET, NULL);
-                buf[new_end] = '\0';
-                done = TRUE;
-            }
-            else
-                last_end = new_end;
-        }
-    } while(!done);
+    hr = IStream_Read(This->base, pv, cb, pcbRead);
 
-    *ptr = buf;
-    return S_OK;
+    This->pos.QuadPart += *pcbRead;
 
-fail:
-    HeapFree(GetProcessHeap(), 0, buf);
     return hr;
 }
 
-static header_t *read_prop(MimeBody *body, char **ptr)
+static HRESULT WINAPI sub_stream_Write(
+        IStream* iface,
+        const void *pv,
+        ULONG cb,
+        ULONG *pcbWritten)
 {
-    char *colon = strchr(*ptr, ':');
-    const property_t *prop;
-    header_t *ret;
+    FIXME("stub\n");
+    return E_NOTIMPL;
+}
 
-    if(!colon) return NULL;
+static HRESULT WINAPI sub_stream_Seek(
+        IStream* iface,
+        LARGE_INTEGER dlibMove,
+        DWORD dwOrigin,
+        ULARGE_INTEGER *plibNewPosition)
+{
+    sub_stream_t *This = impl_from_IStream(iface);
+    LARGE_INTEGER new_pos;
 
-    *colon = '\0';
+    TRACE("(%08x.%08x, %x, %p)\n", dlibMove.u.HighPart, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
 
-    for(prop = default_props; prop->name; prop++)
+    switch(dwOrigin)
     {
-        if(!strcasecmp(*ptr, prop->name))
-        {
-            TRACE("%s: found match with default property id %d\n", *ptr, prop->id);
-            break;
-        }
+    case STREAM_SEEK_SET:
+        new_pos = dlibMove;
+        break;
+    case STREAM_SEEK_CUR:
+        new_pos.QuadPart = This->pos.QuadPart + dlibMove.QuadPart;
+        break;
+    case STREAM_SEEK_END:
+        new_pos.QuadPart = This->length.QuadPart + dlibMove.QuadPart;
+        break;
+    default:
+        return STG_E_INVALIDFUNCTION;
     }
 
-    if(!prop->name)
-    {
-        property_list_entry_t *prop_entry;
-        LIST_FOR_EACH_ENTRY(prop_entry, &body->new_props, property_list_entry_t, entry)
-        {
-            if(!strcasecmp(*ptr, prop_entry->prop.name))
-            {
-                TRACE("%s: found match with already added new property id %d\n", *ptr, prop_entry->prop.id);
-                prop = &prop_entry->prop;
-                break;
-            }
-        }
-        if(!prop->name)
-        {
-            prop_entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*prop_entry));
-            prop_entry->prop.name = strdupA(*ptr);
-            prop_entry->prop.id = body->next_prop_id++;
-            prop_entry->prop.flags = 0;
-            prop_entry->prop.default_vt = VT_LPSTR;
-            list_add_tail(&body->new_props, &prop_entry->entry);
-            prop = &prop_entry->prop;
-            TRACE("%s: allocating new prop id %d\n", *ptr, prop_entry->prop.id);
-        }
-    }
+    if(new_pos.QuadPart < 0) new_pos.QuadPart = 0;
+    else if(new_pos.QuadPart > This->length.QuadPart) new_pos.QuadPart = This->length.QuadPart;
 
-    ret = HeapAlloc(GetProcessHeap(), 0, sizeof(*ret));
-    ret->prop = prop;
-    PropVariantInit(&ret->value);
-    list_init(&ret->params);
-    *ptr = colon + 1;
+    This->pos.QuadPart = new_pos.QuadPart;
 
-    return ret;
+    if(plibNewPosition) *plibNewPosition = This->pos;
+    return S_OK;
 }
 
-static void unfold_header(char *header, int len)
+static HRESULT WINAPI sub_stream_SetSize(
+        IStream* iface,
+        ULARGE_INTEGER libNewSize)
 {
-    char *start = header, *cp = header;
-
-    do {
-        while(*cp == ' ' || *cp == '\t')
-        {
-            cp++;
-            len--;
-        }
-        if(cp != start)
-            memmove(start, cp, len + 1);
-
-        cp = strstr(start, "\r\n");
-        len -= (cp - start);
-        start = cp;
-        *start = ' ';
-        start++;
-        len--;
-        cp += 2;
-    } while(*cp == ' ' || *cp == '\t');
-
-    *(start - 1) = '\0';
+    FIXME("stub\n");
+    return E_NOTIMPL;
 }
 
-static char *unquote_string(const char *str)
+static HRESULT WINAPI sub_stream_CopyTo(
+        IStream* iface,
+        IStream *pstm,
+        ULARGE_INTEGER cb,
+        ULARGE_INTEGER *pcbRead,
+        ULARGE_INTEGER *pcbWritten)
 {
-    BOOL quoted = FALSE;
-    char *ret, *cp;
+    HRESULT        hr = S_OK;
+    BYTE           tmpBuffer[128];
+    ULONG          bytesRead, bytesWritten, copySize;
+    ULARGE_INTEGER totalBytesRead;
+    ULARGE_INTEGER totalBytesWritten;
 
-    while(*str == ' ' || *str == '\t') str++;
+    TRACE("(%p)->(%p, %d, %p, %p)\n", iface, pstm, cb.u.LowPart, pcbRead, pcbWritten);
 
-    if(*str == '"')
-    {
-        quoted = TRUE;
-        str++;
-    }
-    ret = strdupA(str);
-    for(cp = ret; *cp; cp++)
+    totalBytesRead.QuadPart = 0;
+    totalBytesWritten.QuadPart = 0;
+
+    while ( cb.QuadPart > 0 )
     {
-        if(*cp == '\\')
-            memmove(cp, cp + 1, strlen(cp + 1) + 1);
-        else if(*cp == '"')
-        {
-            if(!quoted)
-            {
-                WARN("quote in unquoted string\n");
-            }
+        if ( cb.QuadPart >= sizeof(tmpBuffer) )
+            copySize = sizeof(tmpBuffer);
+        else
+            copySize = cb.u.LowPart;
+
+        hr = IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
+        if (FAILED(hr)) break;
+
+        totalBytesRead.QuadPart += bytesRead;
+
+        if (bytesRead)
+        {
+            hr = IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
+            if (FAILED(hr)) break;
+            totalBytesWritten.QuadPart += bytesWritten;
+        }
+
+        if (bytesRead != copySize)
+            cb.QuadPart = 0;
+        else
+            cb.QuadPart -= bytesRead;
+    }
+
+    if (pcbRead) pcbRead->QuadPart = totalBytesRead.QuadPart;
+    if (pcbWritten) pcbWritten->QuadPart = totalBytesWritten.QuadPart;
+
+    return hr;
+}
+
+static HRESULT WINAPI sub_stream_Commit(
+        IStream* iface,
+        DWORD grfCommitFlags)
+{
+    FIXME("stub\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI sub_stream_Revert(
+        IStream* iface)
+{
+    FIXME("stub\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI sub_stream_LockRegion(
+        IStream* iface,
+        ULARGE_INTEGER libOffset,
+        ULARGE_INTEGER cb,
+        DWORD dwLockType)
+{
+    FIXME("stub\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI sub_stream_UnlockRegion(
+        IStream* iface,
+        ULARGE_INTEGER libOffset,
+        ULARGE_INTEGER cb,
+        DWORD dwLockType)
+{
+    FIXME("stub\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI sub_stream_Stat(
+        IStream* iface,
+        STATSTG *pstatstg,
+        DWORD grfStatFlag)
+{
+    sub_stream_t *This = impl_from_IStream(iface);
+    FIXME("(%p)->(%p, %08x)\n", This, pstatstg, grfStatFlag);
+    memset(pstatstg, 0, sizeof(*pstatstg));
+    pstatstg->cbSize = This->length;
+    return S_OK;
+}
+
+static HRESULT WINAPI sub_stream_Clone(
+        IStream* iface,
+        IStream **ppstm)
+{
+    FIXME("stub\n");
+    return E_NOTIMPL;
+}
+
+static struct IStreamVtbl sub_stream_vtbl =
+{
+    sub_stream_QueryInterface,
+    sub_stream_AddRef,
+    sub_stream_Release,
+    sub_stream_Read,
+    sub_stream_Write,
+    sub_stream_Seek,
+    sub_stream_SetSize,
+    sub_stream_CopyTo,
+    sub_stream_Commit,
+    sub_stream_Revert,
+    sub_stream_LockRegion,
+    sub_stream_UnlockRegion,
+    sub_stream_Stat,
+    sub_stream_Clone
+};
+
+static HRESULT create_sub_stream(IStream *stream, ULARGE_INTEGER start, ULARGE_INTEGER length, IStream **out)
+{
+    sub_stream_t *This;
+
+    *out = NULL;
+    This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
+    if(!This) return E_OUTOFMEMORY;
+
+    This->IStream_iface.lpVtbl = &sub_stream_vtbl;
+    This->ref = 1;
+    This->start = start;
+    This->length = length;
+    This->pos.QuadPart = 0;
+    IStream_AddRef(stream);
+    This->base = stream;
+
+    *out = &This->IStream_iface;
+    return S_OK;
+}
+
+static HRESULT get_stream_size(IStream *stream, ULARGE_INTEGER *size)
+{
+    STATSTG statstg = {NULL};
+    LARGE_INTEGER zero;
+    HRESULT hres;
+
+    hres = IStream_Stat(stream, &statstg, STATFLAG_NONAME);
+    if(SUCCEEDED(hres)) {
+        *size = statstg.cbSize;
+        return S_OK;
+    }
+
+    zero.QuadPart = 0;
+    return IStream_Seek(stream, zero, STREAM_SEEK_END, size);
+}
+
+static inline MimeBody *impl_from_IMimeBody(IMimeBody *iface)
+{
+    return CONTAINING_RECORD(iface, MimeBody, IMimeBody_iface);
+}
+
+typedef struct propschema
+{
+    IMimePropertySchema IMimePropertySchema_iface;
+    LONG ref;
+} propschema;
+
+static inline propschema *impl_from_IMimePropertySchema(IMimePropertySchema *iface)
+{
+    return CONTAINING_RECORD(iface, propschema, IMimePropertySchema_iface);
+}
+
+static LPSTR strdupA(LPCSTR str)
+{
+    char *ret;
+    int len = strlen(str);
+    ret = HeapAlloc(GetProcessHeap(), 0, len + 1);
+    memcpy(ret, str, len + 1);
+    return ret;
+}
+
+#define PARSER_BUF_SIZE 1024
+
+/*****************************************************
+ *        copy_headers_to_buf [internal]
+ *
+ * Copies the headers into a '\0' terminated memory block and leave
+ * the stream's current position set to after the blank line.
+ */
+static HRESULT copy_headers_to_buf(IStream *stm, char **ptr)
+{
+    char *buf = NULL;
+    DWORD size = PARSER_BUF_SIZE, offset = 0, last_end = 0;
+    HRESULT hr;
+    BOOL done = FALSE;
+
+    *ptr = NULL;
+
+    do
+    {
+        char *end;
+        DWORD read;
+
+        if(!buf)
+            buf = HeapAlloc(GetProcessHeap(), 0, size + 1);
+        else
+        {
+            size *= 2;
+            buf = HeapReAlloc(GetProcessHeap(), 0, buf, size + 1);
+        }
+        if(!buf)
+        {
+            hr = E_OUTOFMEMORY;
+            goto fail;
+        }
+
+        hr = IStream_Read(stm, buf + offset, size - offset, &read);
+        if(FAILED(hr)) goto fail;
+
+        offset += read;
+        buf[offset] = '\0';
+
+        if(read == 0) done = TRUE;
+
+        while(!done && (end = strstr(buf + last_end, "\r\n")))
+        {
+            DWORD new_end = end - buf + 2;
+            if(new_end - last_end == 2)
+            {
+                LARGE_INTEGER off;
+                off.QuadPart = (LONGLONG)new_end - offset;
+                IStream_Seek(stm, off, STREAM_SEEK_CUR, NULL);
+                buf[new_end] = '\0';
+                done = TRUE;
+            }
+            else
+                last_end = new_end;
+        }
+    } while(!done);
+
+    *ptr = buf;
+    return S_OK;
+
+fail:
+    HeapFree(GetProcessHeap(), 0, buf);
+    return hr;
+}
+
+static header_t *read_prop(MimeBody *body, char **ptr)
+{
+    char *colon = strchr(*ptr, ':');
+    const property_t *prop;
+    header_t *ret;
+
+    if(!colon) return NULL;
+
+    *colon = '\0';
+
+    for(prop = default_props; prop->name; prop++)
+    {
+        if(!lstrcmpiA(*ptr, prop->name))
+        {
+            TRACE("%s: found match with default property id %d\n", *ptr, prop->id);
+            break;
+        }
+    }
+
+    if(!prop->name)
+    {
+        property_list_entry_t *prop_entry;
+        LIST_FOR_EACH_ENTRY(prop_entry, &body->new_props, property_list_entry_t, entry)
+        {
+            if(!lstrcmpiA(*ptr, prop_entry->prop.name))
+            {
+                TRACE("%s: found match with already added new property id %d\n", *ptr, prop_entry->prop.id);
+                prop = &prop_entry->prop;
+                break;
+            }
+        }
+        if(!prop->name)
+        {
+            prop_entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*prop_entry));
+            prop_entry->prop.name = strdupA(*ptr);
+            prop_entry->prop.id = body->next_prop_id++;
+            prop_entry->prop.flags = 0;
+            prop_entry->prop.default_vt = VT_LPSTR;
+            list_add_tail(&body->new_props, &prop_entry->entry);
+            prop = &prop_entry->prop;
+            TRACE("%s: allocating new prop id %d\n", *ptr, prop_entry->prop.id);
+        }
+    }
+
+    ret = HeapAlloc(GetProcessHeap(), 0, sizeof(*ret));
+    ret->prop = prop;
+    PropVariantInit(&ret->value);
+    list_init(&ret->params);
+    *ptr = colon + 1;
+
+    return ret;
+}
+
+static void unfold_header(char *header, int len)
+{
+    char *start = header, *cp = header;
+
+    do {
+        while(*cp == ' ' || *cp == '\t')
+        {
+            cp++;
+            len--;
+        }
+        if(cp != start)
+            memmove(start, cp, len + 1);
+
+        cp = strstr(start, "\r\n");
+        len -= (cp - start);
+        start = cp;
+        *start = ' ';
+        start++;
+        len--;
+        cp += 2;
+    } while(*cp == ' ' || *cp == '\t');
+
+    *(start - 1) = '\0';
+}
+
+static char *unquote_string(const char *str)
+{
+    BOOL quoted = FALSE;
+    char *ret, *cp;
+
+    while(*str == ' ' || *str == '\t') str++;
+
+    if(*str == '"')
+    {
+        quoted = TRUE;
+        str++;
+    }
+    ret = strdupA(str);
+    for(cp = ret; *cp; cp++)
+    {
+        if(*cp == '\\')
+            memmove(cp, cp + 1, strlen(cp + 1) + 1);
+        else if(*cp == '"')
+        {
+            if(!quoted)
+            {
+                WARN("quote in unquoted string\n");
+            }
             else
             {
                 *cp = '\0';
@@ -369,12 +751,6 @@ static void init_content_type(MimeBody *body, header_t *header)
     char *slash;
     DWORD len;
 
-    if(header->prop->id != PID_HDR_CNTTYPE)
-    {
-        ERR("called with header %s\n", header->prop->name);
-        return;
-    }
-
     slash = strchr(header->value.u.pszVal, '/');
     if(!slash)
     {
@@ -388,6 +764,22 @@ static void init_content_type(MimeBody *body, header_t *header)
     body->content_sub_type = strdupA(slash + 1);
 }
 
+static void init_content_encoding(MimeBody *body, header_t *header)
+{
+    const char *encoding = header->value.u.pszVal;
+
+    if(!_strnicmp(encoding, "base64", -1))
+        body->encoding = IET_BASE64;
+    else if(!_strnicmp(encoding, "quoted-printable", -1))
+        body->encoding = IET_QP;
+    else if(!_strnicmp(encoding, "7bit", -1))
+        body->encoding = IET_7BIT;
+    else if(!_strnicmp(encoding, "8bit", -1))
+        body->encoding = IET_8BIT;
+    else
+        FIXME("unknown encoding %s\n", debugstr_a(encoding));
+}
+
 static HRESULT parse_headers(MimeBody *body, IStream *stm)
 {
     char *header_buf, *cur_header_ptr;
@@ -403,8 +795,14 @@ static HRESULT parse_headers(MimeBody *body, IStream *stm)
         read_value(header, &cur_header_ptr);
         list_add_tail(&body->headers, &header->entry);
 
-        if(header->prop->id == PID_HDR_CNTTYPE)
+        switch(header->prop->id) {
+        case PID_HDR_CNTTYPE:
             init_content_type(body, header);
+            break;
+        case PID_HDR_CNTXFER:
+            init_content_encoding(body, header);
+            break;
+        }
     }
 
     HeapFree(GetProcessHeap(), 0, header_buf);
@@ -424,16 +822,21 @@ static void empty_param_list(struct list *list)
     }
 }
 
+static void free_header(header_t *header)
+{
+    list_remove(&header->entry);
+    PropVariantClear(&header->value);
+    empty_param_list(&header->params);
+    heap_free(header);
+}
+
 static void empty_header_list(struct list *list)
 {
     header_t *header, *cursor2;
 
     LIST_FOR_EACH_ENTRY_SAFE(header, cursor2, list, header_t, entry)
     {
-        list_remove(&header->entry);
-        PropVariantClear(&header->value);
-        empty_param_list(&header->params);
-        HeapFree(GetProcessHeap(), 0, header);
+        free_header(header);
     }
 }
 
@@ -467,7 +870,15 @@ static HRESULT find_prop(MimeBody *body, const char *name, header_t **prop)
 
     LIST_FOR_EACH_ENTRY(header, &body->headers, header_t, entry)
     {
-        if(!strcasecmp(name, header->prop->name))
+        if(ISPIDSTR(name))
+        {
+            if(STRTOPID(name) == header->prop->id)
+            {
+                *prop = header;
+                return S_OK;
+            }
+        }
+        else if(!lstrcmpiA(name, header->prop->name))
         {
             *prop = header;
             return S_OK;
@@ -477,6 +888,33 @@ static HRESULT find_prop(MimeBody *body, const char *name, header_t **prop)
     return MIME_E_NOT_FOUND;
 }
 
+static const property_t *find_default_prop(const char *name)
+{
+    const property_t *prop_def = NULL;
+
+    for(prop_def = default_props; prop_def->name; prop_def++)
+    {
+        if(ISPIDSTR(name))
+        {
+            if(STRTOPID(name) == prop_def->id)
+            {
+                break;
+            }
+        }
+        else if(!lstrcmpiA(name, prop_def->name))
+        {
+            break;
+        }
+    }
+
+    if(prop_def->id)
+       TRACE("%s: found match with default property id %d\n", prop_def->name, prop_def->id);
+    else
+       prop_def = NULL;
+
+    return prop_def;
+}
+
 static HRESULT WINAPI MimeBody_QueryInterface(IMimeBody* iface,
                                      REFIID riid,
                                      void** ppvObject)
@@ -541,28 +979,36 @@ static HRESULT WINAPI MimeBody_GetClassID(
                                  IMimeBody* iface,
                                  CLSID* pClassID)
 {
-    FIXME("stub\n");
-    return E_NOTIMPL;
-}
+    MimeBody *This = impl_from_IMimeBody(iface);
 
+    TRACE("(%p)->(%p)\n", This, pClassID);
+
+    if(!pClassID)
+        return E_INVALIDARG;
+
+    *pClassID = IID_IMimeBody;
+    return S_OK;
+}
 
 static HRESULT WINAPI MimeBody_IsDirty(
                               IMimeBody* iface)
 {
-    FIXME("stub\n");
+    MimeBody *This = impl_from_IMimeBody(iface);
+    FIXME("(%p)->() stub\n", This);
     return E_NOTIMPL;
 }
 
 static HRESULT WINAPI MimeBody_Load(IMimeBody *iface, IStream *pStm)
 {
     MimeBody *This = impl_from_IMimeBody(iface);
-    TRACE("(%p)->(%p)\n", iface, pStm);
+    TRACE("(%p)->(%p)\n", This, pStm);
     return parse_headers(This, pStm);
 }
 
 static HRESULT WINAPI MimeBody_Save(IMimeBody *iface, IStream *pStm, BOOL fClearDirty)
 {
-    FIXME("stub\n");
+    MimeBody *This = impl_from_IMimeBody(iface);
+    FIXME("(%p)->(%p, %d)\n", This, pStm, fClearDirty);
     return E_NOTIMPL;
 }
 
@@ -570,14 +1016,16 @@ static HRESULT WINAPI MimeBody_GetSizeMax(
                                  IMimeBody* iface,
                                  ULARGE_INTEGER* pcbSize)
 {
-    FIXME("stub\n");
+    MimeBody *This = impl_from_IMimeBody(iface);
+    FIXME("(%p)->(%p) stub\n", This, pcbSize);
     return E_NOTIMPL;
 }
 
 static HRESULT WINAPI MimeBody_InitNew(
                               IMimeBody* iface)
 {
-    TRACE("%p->()\n", iface);
+    MimeBody *This = impl_from_IMimeBody(iface);
+    TRACE("(%p)->()\n", This);
     return S_OK;
 }
 
@@ -586,8 +1034,43 @@ static HRESULT WINAPI MimeBody_GetPropInfo(
                                   LPCSTR pszName,
                                   LPMIMEPROPINFO pInfo)
 {
-    FIXME("stub\n");
-    return E_NOTIMPL;
+    MimeBody *This = impl_from_IMimeBody(iface);
+    header_t *header;
+    HRESULT hr;
+    DWORD supported = PIM_PROPID | PIM_VTDEFAULT;
+
+    TRACE("(%p)->(%s, %p) semi-stub\n", This, debugstr_a(pszName), pInfo);
+
+    if(!pszName || !pInfo)
+        return E_INVALIDARG;
+
+    TRACE("mask 0x%04x\n", pInfo->dwMask);
+
+    if(pInfo->dwMask & ~supported)
+         FIXME("Unsupported mask flags 0x%04x\n", pInfo->dwMask & ~supported);
+
+    hr = find_prop(This, pszName, &header);
+    if(hr == S_OK)
+    {
+        if(pInfo->dwMask & PIM_CHARSET)
+            pInfo->hCharset = 0;
+        if(pInfo->dwMask & PIM_FLAGS)
+            pInfo->dwFlags = 0x00000000;
+        if(pInfo->dwMask & PIM_ROWNUMBER)
+            pInfo->dwRowNumber = 0;
+        if(pInfo->dwMask & PIM_ENCODINGTYPE)
+            pInfo->ietEncoding = 0;
+        if(pInfo->dwMask & PIM_VALUES)
+            pInfo->cValues = 0;
+        if(pInfo->dwMask & PIM_PROPID)
+            pInfo->dwPropId = header->prop->id;
+        if(pInfo->dwMask & PIM_VTDEFAULT)
+            pInfo->vtDefault = header->prop->default_vt;
+        if(pInfo->dwMask & PIM_VTCURRENT)
+            pInfo->vtCurrent = 0;
+    }
+
+    return hr;
 }
 
 static HRESULT WINAPI MimeBody_SetPropInfo(
@@ -595,39 +1078,136 @@ static HRESULT WINAPI MimeBody_SetPropInfo(
                                   LPCSTR pszName,
                                   LPCMIMEPROPINFO pInfo)
 {
-    FIXME("stub\n");
+    MimeBody *This = impl_from_IMimeBody(iface);
+    FIXME("(%p)->(%s, %p) stub\n", This, debugstr_a(pszName), pInfo);
     return E_NOTIMPL;
 }
 
-static HRESULT WINAPI MimeBody_GetProp(
-                              IMimeBody* iface,
-                              LPCSTR pszName,
-                              DWORD dwFlags,
-                              LPPROPVARIANT pValue)
-{
-    MimeBody *This = impl_from_IMimeBody(iface);
-    TRACE("(%p)->(%s, %d, %p)\n", This, pszName, dwFlags, pValue);
+static HRESULT WINAPI MimeBody_GetProp(
+                              IMimeBody* iface,
+                              LPCSTR pszName,
+                              DWORD dwFlags,
+                              LPPROPVARIANT pValue)
+{
+    MimeBody *This = impl_from_IMimeBody(iface);
+    header_t *header;
+    HRESULT hr;
+
+    TRACE("(%p)->(%s, 0x%x, %p)\n", This, debugstr_a(pszName), dwFlags, pValue);
+
+    if(!pszName || !pValue)
+        return E_INVALIDARG;
+
+    if(!ISPIDSTR(pszName) && !lstrcmpiA(pszName, "att:pri-content-type"))
+    {
+        PropVariantClear(pValue);
+        pValue->vt = VT_LPSTR;
+        pValue->u.pszVal = strdupA(This->content_pri_type);
+        return S_OK;
+    }
+
+    hr = find_prop(This, pszName, &header);
+    if(hr == S_OK)
+    {
+        TRACE("type %d->%d\n", header->value.vt, pValue->vt);
+
+        hr = PropVariantChangeType(pValue, &header->value, 0,  pValue->vt);
+        if(FAILED(hr))
+            FIXME("Conversion not currently supported (%d->%d)\n", header->value.vt, pValue->vt);
+    }
+
+    return hr;
+}
+
+static HRESULT WINAPI MimeBody_SetProp(
+                              IMimeBody* iface,
+                              LPCSTR pszName,
+                              DWORD dwFlags,
+                              LPCPROPVARIANT pValue)
+{
+    MimeBody *This = impl_from_IMimeBody(iface);
+    header_t *header;
+    HRESULT hr;
+
+    TRACE("(%p)->(%s, 0x%x, %p)\n", This, debugstr_a(pszName), dwFlags, pValue);
+
+    if(!pszName || !pValue)
+        return E_INVALIDARG;
+
+    hr = find_prop(This, pszName, &header);
+    if(hr != S_OK)
+    {
+        property_list_entry_t *prop_entry;
+        const property_t *prop = NULL;
+
+        LIST_FOR_EACH_ENTRY(prop_entry, &This->new_props, property_list_entry_t, entry)
+        {
+            if(ISPIDSTR(pszName))
+            {
+                if(STRTOPID(pszName) == prop_entry->prop.id)
+                {
+                    TRACE("Found match with already added new property id %d\n", prop_entry->prop.id);
+                    prop = &prop_entry->prop;
+                    break;
+                }
+            }
+            else if(!lstrcmpiA(pszName, prop_entry->prop.name))
+            {
+                TRACE("Found match with already added new property id %d\n", prop_entry->prop.id);
+                prop = &prop_entry->prop;
+                break;
+            }
+        }
+
+        header = HeapAlloc(GetProcessHeap(), 0, sizeof(*header));
+        if(!header)
+            return E_OUTOFMEMORY;
+
+        if(!prop)
+        {
+            const property_t *prop_def = NULL;
+            prop_entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*prop_entry));
+            if(!prop_entry)
+            {
+                HeapFree(GetProcessHeap(), 0, header);
+                return E_OUTOFMEMORY;
+            }
+
+            prop_def = find_default_prop(pszName);
+            if(prop_def)
+            {
+                prop_entry->prop.name = strdupA(prop_def->name);
+                prop_entry->prop.id =  prop_def->id;
+            }
+            else
+            {
+                if(ISPIDSTR(pszName))
+                {
+                    HeapFree(GetProcessHeap(), 0, prop_entry);
+                    HeapFree(GetProcessHeap(), 0, header);
+                    return MIME_E_NOT_FOUND;
+                }
+
+                prop_entry->prop.name = strdupA(pszName);
+                prop_entry->prop.id = This->next_prop_id++;
+            }
+
+            prop_entry->prop.flags = 0;
+            prop_entry->prop.default_vt = pValue->vt;
+            list_add_tail(&This->new_props, &prop_entry->entry);
+            prop = &prop_entry->prop;
+            TRACE("Allocating new prop id %d\n", prop_entry->prop.id);
+        }
 
-    if(!strcasecmp(pszName, "att:pri-content-type"))
-    {
-        PropVariantClear(pValue);
-        pValue->vt = VT_LPSTR;
-        pValue->u.pszVal = strdupA(This->content_pri_type);
-        return S_OK;
+        header->prop = prop;
+        PropVariantInit(&header->value);
+        list_init(&header->params);
+        list_add_tail(&This->headers, &header->entry);
     }
 
-    FIXME("stub!\n");
-    return E_FAIL;
-}
+    PropVariantCopy(&header->value, pValue);
 
-static HRESULT WINAPI MimeBody_SetProp(
-                              IMimeBody* iface,
-                              LPCSTR pszName,
-                              DWORD dwFlags,
-                              LPCPROPVARIANT pValue)
-{
-    FIXME("stub\n");
-    return E_NOTIMPL;
+    return S_OK;
 }
 
 static HRESULT WINAPI MimeBody_AppendProp(
@@ -636,7 +1216,8 @@ static HRESULT WINAPI MimeBody_AppendProp(
                                  DWORD dwFlags,
                                  LPPROPVARIANT pValue)
 {
-    FIXME("stub\n");
+    MimeBody *This = impl_from_IMimeBody(iface);
+    FIXME("(%p)->(%s, 0x%x, %p) stub\n", This, debugstr_a(pszName), dwFlags, pValue);
     return E_NOTIMPL;
 }
 
@@ -644,8 +1225,27 @@ static HRESULT WINAPI MimeBody_DeleteProp(
                                  IMimeBody* iface,
                                  LPCSTR pszName)
 {
-    FIXME("stub\n");
-    return E_NOTIMPL;
+    MimeBody *This = impl_from_IMimeBody(iface);
+    header_t *cursor;
+    BOOL found;
+
+    TRACE("(%p)->(%s) stub\n", This, debugstr_a(pszName));
+
+    LIST_FOR_EACH_ENTRY(cursor, &This->headers, header_t, entry)
+    {
+        if(ISPIDSTR(pszName))
+            found = STRTOPID(pszName) == cursor->prop->id;
+        else
+            found = !lstrcmpiA(pszName, cursor->prop->name);
+
+        if(found)
+        {
+             free_header(cursor);
+             return S_OK;
+        }
+    }
+
+    return MIME_E_NOT_FOUND;
 }
 
 static HRESULT WINAPI MimeBody_CopyProps(
@@ -654,7 +1254,8 @@ static HRESULT WINAPI MimeBody_CopyProps(
                                 LPCSTR* prgszName,
                                 IMimePropertySet* pPropertySet)
 {
-    FIXME("stub\n");
+    MimeBody *This = impl_from_IMimeBody(iface);
+    FIXME("(%p)->(%d, %p, %p) stub\n", This, cNames, prgszName, pPropertySet);
     return E_NOTIMPL;
 }
 
@@ -664,7 +1265,8 @@ static HRESULT WINAPI MimeBody_MoveProps(
                                 LPCSTR* prgszName,
                                 IMimePropertySet* pPropertySet)
 {
-    FIXME("stub\n");
+    MimeBody *This = impl_from_IMimeBody(iface);
+    FIXME("(%p)->(%d, %p, %p) stub\n", This, cNames, prgszName, pPropertySet);
     return E_NOTIMPL;
 }
 
@@ -673,7 +1275,8 @@ static HRESULT WINAPI MimeBody_DeleteExcept(
                                    ULONG cNames,
                                    LPCSTR* prgszName)
 {
-    FIXME("stub\n");
+    MimeBody *This = impl_from_IMimeBody(iface);
+    FIXME("(%p)->(%d, %p) stub\n", This, cNames, prgszName);
     return E_NOTIMPL;
 }
 
@@ -684,7 +1287,8 @@ static HRESULT WINAPI MimeBody_QueryProp(
                                 boolean fSubString,
                                 boolean fCaseSensitive)
 {
-    FIXME("stub\n");
+    MimeBody *This = impl_from_IMimeBody(iface);
+    FIXME("(%p)->(%s, %s, %d, %d) stub\n", This, debugstr_a(pszName), debugstr_a(pszCriteria), fSubString, fCaseSensitive);
     return E_NOTIMPL;
 }
 
@@ -692,7 +1296,8 @@ static HRESULT WINAPI MimeBody_GetCharset(
                                  IMimeBody* iface,
                                  LPHCHARSET phCharset)
 {
-    FIXME("stub\n");
+    MimeBody *This = impl_from_IMimeBody(iface);
+    FIXME("(%p)->(%p) stub\n", This, phCharset);
     *phCharset = NULL;
     return S_OK;
 }
@@ -702,7 +1307,8 @@ static HRESULT WINAPI MimeBody_SetCharset(
                                  HCHARSET hCharset,
                                  CSETAPPLYTYPE applytype)
 {
-    FIXME("stub\n");
+    MimeBody *This = impl_from_IMimeBody(iface);
+    FIXME("(%p)->(%p, %d) stub\n", This, hCharset, applytype);
     return E_NOTIMPL;
 }
 
@@ -751,661 +1357,584 @@ static HRESULT WINAPI MimeBody_GetParameters(
     return S_OK;
 }
 
-static HRESULT WINAPI MimeBody_IsContentType(
-                                    IMimeBody* iface,
-                                    LPCSTR pszPriType,
-                                    LPCSTR pszSubType)
-{
-    MimeBody *This = impl_from_IMimeBody(iface);
-
-    TRACE("(%p)->(%s, %s)\n", This, debugstr_a(pszPriType), debugstr_a(pszSubType));
-    if(pszPriType)
-    {
-        const char *pri = This->content_pri_type;
-        if(!pri) pri = "text";
-        if(strcasecmp(pri, pszPriType)) return S_FALSE;
-    }
-
-    if(pszSubType)
-    {
-        const char *sub = This->content_sub_type;
-        if(!sub) sub = "plain";
-        if(strcasecmp(sub, pszSubType)) return S_FALSE;
-    }
-
-    return S_OK;
-}
-
-static HRESULT WINAPI MimeBody_BindToObject(
-                                   IMimeBody* iface,
-                                   REFIID riid,
-                                   void** ppvObject)
-{
-    FIXME("stub\n");
-    return E_NOTIMPL;
-}
-
-static HRESULT WINAPI MimeBody_Clone(
-                            IMimeBody* iface,
-                            IMimePropertySet** ppPropertySet)
-{
-    FIXME("stub\n");
-    return E_NOTIMPL;
-}
-
-static HRESULT WINAPI MimeBody_SetOption(
-                                IMimeBody* iface,
-                                const TYPEDID oid,
-                                LPCPROPVARIANT pValue)
-{
-    HRESULT hr = E_NOTIMPL;
-    TRACE("(%p)->(%08x, %p)\n", iface, oid, pValue);
-
-    if(pValue->vt != TYPEDID_TYPE(oid))
-    {
-        WARN("Called with vartype %04x and oid %08x\n", pValue->vt, oid);
-        return E_INVALIDARG;
-    }
-
-    switch(oid)
-    {
-    case OID_SECURITY_HWND_OWNER:
-        FIXME("OID_SECURITY_HWND_OWNER (value %08x): ignoring\n", pValue->u.ulVal);
-        hr = S_OK;
-        break;
-    default:
-        FIXME("Unhandled oid %08x\n", oid);
-    }
-
-    return hr;
-}
-
-static HRESULT WINAPI MimeBody_GetOption(
-                                IMimeBody* iface,
-                                const TYPEDID oid,
-                                LPPROPVARIANT pValue)
-{
-    FIXME("(%p)->(%08x, %p): stub\n", iface, oid, pValue);
-    return E_NOTIMPL;
-}
-
-static HRESULT WINAPI MimeBody_EnumProps(
-                                IMimeBody* iface,
-                                DWORD dwFlags,
-                                IMimeEnumProperties** ppEnum)
-{
-    FIXME("stub\n");
-    return E_NOTIMPL;
-}
-
-static HRESULT WINAPI MimeBody_IsType(
-                             IMimeBody* iface,
-                             IMSGBODYTYPE bodytype)
-{
-    MimeBody *This = impl_from_IMimeBody(iface);
-
-    TRACE("(%p)->(%d)\n", iface, bodytype);
-    switch(bodytype)
-    {
-    case IBT_EMPTY:
-        return This->data ? S_FALSE : S_OK;
-    default:
-        FIXME("Unimplemented bodytype %d - returning S_OK\n", bodytype);
-    }
-    return S_OK;
-}
-
-static HRESULT WINAPI MimeBody_SetDisplayName(
-                                     IMimeBody* iface,
-                                     LPCSTR pszDisplay)
-{
-    FIXME("stub\n");
-    return E_NOTIMPL;
-}
-
-static HRESULT WINAPI MimeBody_GetDisplayName(
-                                     IMimeBody* iface,
-                                     LPSTR* ppszDisplay)
-{
-    FIXME("stub\n");
-    return E_NOTIMPL;
-}
-
-static HRESULT WINAPI MimeBody_GetOffsets(
-                                 IMimeBody* iface,
-                                 LPBODYOFFSETS pOffsets)
-{
-    MimeBody *This = impl_from_IMimeBody(iface);
-    TRACE("(%p)->(%p)\n", This, pOffsets);
-
-    *pOffsets = This->body_offsets;
-
-    if(This->body_offsets.cbBodyEnd == 0) return MIME_E_NO_DATA;
-    return S_OK;
-}
-
-static HRESULT WINAPI MimeBody_GetCurrentEncoding(
-                                         IMimeBody* iface,
-                                         ENCODINGTYPE* pietEncoding)
-{
-    MimeBody *This = impl_from_IMimeBody(iface);
-
-    TRACE("(%p)->(%p)\n", This, pietEncoding);
-
-    *pietEncoding = This->encoding;
-    return S_OK;
-}
-
-static HRESULT WINAPI MimeBody_SetCurrentEncoding(
-                                         IMimeBody* iface,
-                                         ENCODINGTYPE ietEncoding)
-{
-    MimeBody *This = impl_from_IMimeBody(iface);
-
-    TRACE("(%p)->(%d)\n", This, ietEncoding);
-
-    This->encoding = ietEncoding;
-    return S_OK;
-}
-
-static HRESULT WINAPI MimeBody_GetEstimatedSize(
-                                       IMimeBody* iface,
-                                       ENCODINGTYPE ietEncoding,
-                                       ULONG* pcbSize)
-{
-    FIXME("stub\n");
-    return E_NOTIMPL;
-}
-
-static HRESULT WINAPI MimeBody_GetDataHere(
-                                  IMimeBody* iface,
-                                  ENCODINGTYPE ietEncoding,
-                                  IStream* pStream)
-{
-    FIXME("stub\n");
-    return E_NOTIMPL;
-}
-
-static HRESULT WINAPI MimeBody_GetData(
-                              IMimeBody* iface,
-                              ENCODINGTYPE ietEncoding,
-                              IStream** ppStream)
-{
-    MimeBody *This = impl_from_IMimeBody(iface);
-    FIXME("(%p)->(%d, %p). Ignoring encoding type.\n", This, ietEncoding, ppStream);
-
-    *ppStream = This->data;
-    IStream_AddRef(*ppStream);
-    return S_OK;
-}
-
-static HRESULT WINAPI MimeBody_SetData(
-                              IMimeBody* iface,
-                              ENCODINGTYPE ietEncoding,
-                              LPCSTR pszPriType,
-                              LPCSTR pszSubType,
-                              REFIID riid,
-                              LPVOID pvObject)
-{
-    MimeBody *This = impl_from_IMimeBody(iface);
-    TRACE("(%p)->(%d, %s, %s, %s %p)\n", This, ietEncoding, debugstr_a(pszPriType), debugstr_a(pszSubType),
-          debugstr_guid(riid), pvObject);
-
-    if(IsEqualIID(riid, &IID_IStream))
-        IStream_AddRef((IStream *)pvObject);
-    else
-    {
-        FIXME("Unhandled object type %s\n", debugstr_guid(riid));
-        return E_INVALIDARG;
-    }
-
-    if(This->data)
-        FIXME("release old data\n");
-
-    This->data_iid = *riid;
-    This->data = pvObject;
+static HRESULT WINAPI MimeBody_IsContentType(
+                                    IMimeBody* iface,
+                                    LPCSTR pszPriType,
+                                    LPCSTR pszSubType)
+{
+    MimeBody *This = impl_from_IMimeBody(iface);
 
-    IMimeBody_SetCurrentEncoding(iface, ietEncoding);
+    TRACE("(%p)->(%s, %s)\n", This, debugstr_a(pszPriType), debugstr_a(pszSubType));
+    if(pszPriType)
+    {
+        const char *pri = This->content_pri_type;
+        if(!pri) pri = "text";
+        if(lstrcmpiA(pri, pszPriType)) return S_FALSE;
+    }
 
-    /* FIXME: Update the content type.
-       If pszPriType == NULL use 'application'
-       If pszSubType == NULL use 'octet-stream' */
+    if(pszSubType)
+    {
+        const char *sub = This->content_sub_type;
+        if(!sub) sub = "plain";
+        if(lstrcmpiA(sub, pszSubType)) return S_FALSE;
+    }
 
     return S_OK;
 }
 
-static HRESULT WINAPI MimeBody_EmptyData(
-                                IMimeBody* iface)
+static HRESULT WINAPI MimeBody_BindToObject(
+                                   IMimeBody* iface,
+                                   REFIID riid,
+                                   void** ppvObject)
 {
-    FIXME("stub\n");
+    MimeBody *This = impl_from_IMimeBody(iface);
+    FIXME("(%p)->(%s, %p) stub\n", This, debugstr_guid(riid), ppvObject);
     return E_NOTIMPL;
 }
 
-static HRESULT WINAPI MimeBody_CopyTo(
-                             IMimeBody* iface,
-                             IMimeBody* pBody)
+static HRESULT WINAPI MimeBody_Clone(
+                            IMimeBody* iface,
+                            IMimePropertySet** ppPropertySet)
 {
-    FIXME("stub\n");
+    MimeBody *This = impl_from_IMimeBody(iface);
+    FIXME("(%p)->(%p) stub\n", This, ppPropertySet);
     return E_NOTIMPL;
 }
 
-static HRESULT WINAPI MimeBody_GetTransmitInfo(
-                                      IMimeBody* iface,
-                                      LPTRANSMITINFO pTransmitInfo)
+static HRESULT WINAPI MimeBody_SetOption(
+                                IMimeBody* iface,
+                                const TYPEDID oid,
+                                LPCPROPVARIANT pValue)
 {
-    FIXME("stub\n");
-    return E_NOTIMPL;
+    MimeBody *This = impl_from_IMimeBody(iface);
+    HRESULT hr = E_NOTIMPL;
+    TRACE("(%p)->(%08x, %p)\n", This, oid, pValue);
+
+    if(pValue->vt != TYPEDID_TYPE(oid))
+    {
+        WARN("Called with vartype %04x and oid %08x\n", pValue->vt, oid);
+        return E_INVALIDARG;
+    }
+
+    switch(oid)
+    {
+    case OID_SECURITY_HWND_OWNER:
+        FIXME("OID_SECURITY_HWND_OWNER (value %08x): ignoring\n", pValue->u.ulVal);
+        hr = S_OK;
+        break;
+    case OID_TRANSMIT_BODY_ENCODING:
+        FIXME("OID_TRANSMIT_BODY_ENCODING (value %08x): ignoring\n", pValue->u.ulVal);
+        hr = S_OK;
+        break;
+    default:
+        FIXME("Unhandled oid %08x\n", oid);
+    }
+
+    return hr;
 }
 
-static HRESULT WINAPI MimeBody_SaveToFile(
-                                 IMimeBody* iface,
-                                 ENCODINGTYPE ietEncoding,
-                                 LPCSTR pszFilePath)
+static HRESULT WINAPI MimeBody_GetOption(
+                                IMimeBody* iface,
+                                const TYPEDID oid,
+                                LPPROPVARIANT pValue)
 {
-    FIXME("stub\n");
+    MimeBody *This = impl_from_IMimeBody(iface);
+    FIXME("(%p)->(%08x, %p): stub\n", This, oid, pValue);
     return E_NOTIMPL;
 }
 
-static HRESULT WINAPI MimeBody_GetHandle(
+static HRESULT WINAPI MimeBody_EnumProps(
                                 IMimeBody* iface,
-                                LPHBODY phBody)
+                                DWORD dwFlags,
+                                IMimeEnumProperties** ppEnum)
 {
     MimeBody *This = impl_from_IMimeBody(iface);
-    TRACE("(%p)->(%p)\n", iface, phBody);
-
-    *phBody = This->handle;
-    return This->handle ? S_OK : MIME_E_NO_DATA;
+    FIXME("(%p)->(0x%x, %p) stub\n", This, dwFlags, ppEnum);
+    return E_NOTIMPL;
 }
 
-static IMimeBodyVtbl body_vtbl =
-{
-    MimeBody_QueryInterface,
-    MimeBody_AddRef,
-    MimeBody_Release,
-    MimeBody_GetClassID,
-    MimeBody_IsDirty,
-    MimeBody_Load,
-    MimeBody_Save,
-    MimeBody_GetSizeMax,
-    MimeBody_InitNew,
-    MimeBody_GetPropInfo,
-    MimeBody_SetPropInfo,
-    MimeBody_GetProp,
-    MimeBody_SetProp,
-    MimeBody_AppendProp,
-    MimeBody_DeleteProp,
-    MimeBody_CopyProps,
-    MimeBody_MoveProps,
-    MimeBody_DeleteExcept,
-    MimeBody_QueryProp,
-    MimeBody_GetCharset,
-    MimeBody_SetCharset,
-    MimeBody_GetParameters,
-    MimeBody_IsContentType,
-    MimeBody_BindToObject,
-    MimeBody_Clone,
-    MimeBody_SetOption,
-    MimeBody_GetOption,
-    MimeBody_EnumProps,
-    MimeBody_IsType,
-    MimeBody_SetDisplayName,
-    MimeBody_GetDisplayName,
-    MimeBody_GetOffsets,
-    MimeBody_GetCurrentEncoding,
-    MimeBody_SetCurrentEncoding,
-    MimeBody_GetEstimatedSize,
-    MimeBody_GetDataHere,
-    MimeBody_GetData,
-    MimeBody_SetData,
-    MimeBody_EmptyData,
-    MimeBody_CopyTo,
-    MimeBody_GetTransmitInfo,
-    MimeBody_SaveToFile,
-    MimeBody_GetHandle
-};
-
-static HRESULT MimeBody_set_offsets(MimeBody *body, const BODYOFFSETS *offsets)
+static HRESULT WINAPI MimeBody_IsType(
+                             IMimeBody* iface,
+                             IMSGBODYTYPE bodytype)
 {
-    TRACE("setting offsets to %d, %d, %d, %d\n", offsets->cbBoundaryStart,
-          offsets->cbHeaderStart, offsets->cbBodyStart, offsets->cbBodyEnd);
+    MimeBody *This = impl_from_IMimeBody(iface);
 
-    body->body_offsets = *offsets;
+    TRACE("(%p)->(%d)\n", This, bodytype);
+    switch(bodytype)
+    {
+    case IBT_EMPTY:
+        return This->data ? S_FALSE : S_OK;
+    default:
+        FIXME("Unimplemented bodytype %d - returning S_OK\n", bodytype);
+    }
     return S_OK;
 }
 
-#define FIRST_CUSTOM_PROP_ID 0x100
-
-static MimeBody *mimebody_create(void)
+static HRESULT WINAPI MimeBody_SetDisplayName(
+                                     IMimeBody* iface,
+                                     LPCSTR pszDisplay)
 {
-    MimeBody *This;
-    BODYOFFSETS body_offsets;
-
-    This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
-    if (!This)
-        return NULL;
-
-    This->IMimeBody_iface.lpVtbl = &body_vtbl;
-    This->ref = 1;
-    This->handle = NULL;
-    list_init(&This->headers);
-    list_init(&This->new_props);
-    This->next_prop_id = FIRST_CUSTOM_PROP_ID;
-    This->content_pri_type = NULL;
-    This->content_sub_type = NULL;
-    This->encoding = IET_7BIT;
-    This->data = NULL;
-    This->data_iid = IID_NULL;
-
-    body_offsets.cbBoundaryStart = body_offsets.cbHeaderStart = 0;
-    body_offsets.cbBodyStart     = body_offsets.cbBodyEnd     = 0;
-    MimeBody_set_offsets(This, &body_offsets);
-
-    return This;
+    MimeBody *This = impl_from_IMimeBody(iface);
+    FIXME("(%p)->(%s) stub\n", This, debugstr_a(pszDisplay));
+    return E_NOTIMPL;
 }
 
-HRESULT MimeBody_create(IUnknown *outer, void **ppv)
+static HRESULT WINAPI MimeBody_GetDisplayName(
+                                     IMimeBody* iface,
+                                     LPSTR* ppszDisplay)
 {
-    MimeBody *mb;
-
-    if(outer)
-        return CLASS_E_NOAGGREGATION;
-
-    if ((mb = mimebody_create()))
-    {
-        *ppv = &mb->IMimeBody_iface;
-        return S_OK;
-    }
-    else
-    {
-        *ppv = NULL;
-        return E_OUTOFMEMORY;
-    }
+    MimeBody *This = impl_from_IMimeBody(iface);
+    FIXME("(%p)->(%p) stub\n", This, ppszDisplay);
+    return E_NOTIMPL;
 }
 
-
-
-typedef struct
+static HRESULT WINAPI MimeBody_GetOffsets(
+                                 IMimeBody* iface,
+                                 LPBODYOFFSETS pOffsets)
 {
-    IStream IStream_iface;
-    LONG ref;
-    IStream *base;
-    ULARGE_INTEGER pos, start, length;
-} sub_stream_t;
+    MimeBody *This = impl_from_IMimeBody(iface);
+    TRACE("(%p)->(%p)\n", This, pOffsets);
 
-static inline sub_stream_t *impl_from_IStream(IStream *iface)
-{
-    return CONTAINING_RECORD(iface, sub_stream_t, IStream_iface);
+    *pOffsets = This->body_offsets;
+
+    if(This->body_offsets.cbBodyEnd == 0) return MIME_E_NO_DATA;
+    return S_OK;
 }
 
-static HRESULT WINAPI sub_stream_QueryInterface(IStream *iface, REFIID riid, void **ppv)
+static HRESULT WINAPI MimeBody_GetCurrentEncoding(
+                                         IMimeBody* iface,
+                                         ENCODINGTYPE* pietEncoding)
 {
-    sub_stream_t *This = impl_from_IStream(iface);
+    MimeBody *This = impl_from_IMimeBody(iface);
 
-    TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
-    *ppv = NULL;
+    TRACE("(%p)->(%p)\n", This, pietEncoding);
 
-    if(IsEqualIID(riid, &IID_IUnknown) ||
-       IsEqualIID(riid, &IID_ISequentialStream) ||
-       IsEqualIID(riid, &IID_IStream))
-    {
-        IStream_AddRef(iface);
-        *ppv = iface;
-        return S_OK;
-    }
-    return E_NOINTERFACE;
+    *pietEncoding = This->encoding;
+    return S_OK;
 }
 
-static ULONG WINAPI sub_stream_AddRef(IStream *iface)
+static HRESULT WINAPI MimeBody_SetCurrentEncoding(
+                                         IMimeBody* iface,
+                                         ENCODINGTYPE ietEncoding)
 {
-    sub_stream_t *This = impl_from_IStream(iface);
-    LONG ref = InterlockedIncrement(&This->ref);
+    MimeBody *This = impl_from_IMimeBody(iface);
 
-    TRACE("(%p) ref=%d\n", This, ref);
+    TRACE("(%p)->(%d)\n", This, ietEncoding);
 
-    return ref;
+    This->encoding = ietEncoding;
+    return S_OK;
 }
 
-static ULONG WINAPI sub_stream_Release(IStream *iface)
+static HRESULT WINAPI MimeBody_GetEstimatedSize(
+                                       IMimeBody* iface,
+                                       ENCODINGTYPE ietEncoding,
+                                       ULONG* pcbSize)
 {
-    sub_stream_t *This = impl_from_IStream(iface);
-    LONG ref = InterlockedDecrement(&This->ref);
-
-    TRACE("(%p) ref=%d\n", This, ref);
+    MimeBody *This = impl_from_IMimeBody(iface);
+    FIXME("(%p)->(%d, %p) stub\n", This, ietEncoding, pcbSize);
+    return E_NOTIMPL;
+}
 
-    if(!ref)
-    {
-        IStream_Release(This->base);
-        HeapFree(GetProcessHeap(), 0, This);
-    }
-    return ref;
+static HRESULT WINAPI MimeBody_GetDataHere(
+                                  IMimeBody* iface,
+                                  ENCODINGTYPE ietEncoding,
+                                  IStream* pStream)
+{
+    MimeBody *This = impl_from_IMimeBody(iface);
+    FIXME("(%p)->(%d, %p) stub\n", This, ietEncoding, pStream);
+    return E_NOTIMPL;
 }
 
-static HRESULT WINAPI sub_stream_Read(
-        IStream* iface,
-        void *pv,
-        ULONG cb,
-        ULONG *pcbRead)
+static const signed char base64_decode_table[] =
 {
-    sub_stream_t *This = impl_from_IStream(iface);
-    HRESULT hr;
-    ULARGE_INTEGER base_pos;
-    LARGE_INTEGER tmp_pos;
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00 */
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10 */
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20 */
+    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30 */
+    -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, /* 0x40 */
+    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50 */
+    -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60 */
+    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1  /* 0x70 */
+};
 
-    TRACE("(%p, %d, %p)\n", pv, cb, pcbRead);
+static HRESULT decode_base64(IStream *input, IStream **ret_stream)
+{
+    const unsigned char *ptr, *end;
+    unsigned char buf[1024];
+    LARGE_INTEGER pos;
+    unsigned char *ret;
+    unsigned char in[4];
+    IStream *output;
+    DWORD size;
+    int n = 0;
+    HRESULT hres;
+
+    pos.QuadPart = 0;
+    hres = IStream_Seek(input, pos, STREAM_SEEK_SET, NULL);
+    if(FAILED(hres))
+        return hres;
+
+    hres = CreateStreamOnHGlobal(NULL, TRUE, &output);
+    if(FAILED(hres))
+        return hres;
+
+    while(1) {
+        hres = IStream_Read(input, buf, sizeof(buf), &size);
+        if(FAILED(hres) || !size)
+            break;
 
-    tmp_pos.QuadPart = 0;
-    IStream_Seek(This->base, tmp_pos, STREAM_SEEK_CUR, &base_pos);
-    tmp_pos.QuadPart = This->pos.QuadPart + This->start.QuadPart;
-    IStream_Seek(This->base, tmp_pos, STREAM_SEEK_SET, NULL);
+        ptr = ret = buf;
+        end = buf + size;
 
-    if(This->pos.QuadPart + cb > This->length.QuadPart)
-        cb = This->length.QuadPart - This->pos.QuadPart;
+        while(1) {
+            /* skip invalid chars */
+            while(ptr < end && (*ptr >= ARRAY_SIZE(base64_decode_table)
+                                || base64_decode_table[*ptr] == -1))
+                ptr++;
+            if(ptr == end)
+                break;
 
-    hr = IStream_Read(This->base, pv, cb, pcbRead);
+            in[n++] = base64_decode_table[*ptr++];
+            switch(n) {
+            case 2:
+                *ret++ = in[0] << 2 | in[1] >> 4;
+                continue;
+            case 3:
+                *ret++ = in[1] << 4 | in[2] >> 2;
+                continue;
+            case 4:
+                *ret++ = ((in[2] << 6) & 0xc0) | in[3];
+                n = 0;
+            }
+        }
 
-    This->pos.QuadPart += *pcbRead;
+        if(ret > buf) {
+            hres = IStream_Write(output, buf, ret - buf, NULL);
+            if(FAILED(hres))
+                break;
+        }
+    }
 
-    tmp_pos.QuadPart = base_pos.QuadPart;
-    IStream_Seek(This->base, tmp_pos, STREAM_SEEK_SET, NULL);
+    if(SUCCEEDED(hres))
+        hres = IStream_Seek(output, pos, STREAM_SEEK_SET, NULL);
+    if(FAILED(hres)) {
+        IStream_Release(output);
+        return hres;
+    }
 
-    return hr;
+    *ret_stream = output;
+    return S_OK;
 }
 
-static HRESULT WINAPI sub_stream_Write(
-        IStream* iface,
-        const void *pv,
-        ULONG cb,
-        ULONG *pcbWritten)
+static int hex_digit(char c)
 {
-    FIXME("stub\n");
-    return E_NOTIMPL;
+    if('0' <= c && c <= '9')
+        return c - '0';
+    if('A' <= c && c <= 'F')
+        return c - 'A' + 10;
+    if('a' <= c && c <= 'f')
+        return c - 'a' + 10;
+    return -1;
 }
 
-static HRESULT WINAPI sub_stream_Seek(
-        IStream* iface,
-        LARGE_INTEGER dlibMove,
-        DWORD dwOrigin,
-        ULARGE_INTEGER *plibNewPosition)
+static HRESULT decode_qp(IStream *input, IStream **ret_stream)
 {
-    sub_stream_t *This = impl_from_IStream(iface);
-    LARGE_INTEGER new_pos;
+    const unsigned char *ptr, *end;
+    unsigned char *ret, prev = 0;
+    unsigned char buf[1024];
+    LARGE_INTEGER pos;
+    IStream *output;
+    DWORD size;
+    int n = -1;
+    HRESULT hres;
 
-    TRACE("(%08x.%08x, %x, %p)\n", dlibMove.u.HighPart, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
+    pos.QuadPart = 0;
+    hres = IStream_Seek(input, pos, STREAM_SEEK_SET, NULL);
+    if(FAILED(hres))
+        return hres;
 
-    switch(dwOrigin)
-    {
-    case STREAM_SEEK_SET:
-        new_pos = dlibMove;
-        break;
-    case STREAM_SEEK_CUR:
-        new_pos.QuadPart = This->pos.QuadPart + dlibMove.QuadPart;
-        break;
-    case STREAM_SEEK_END:
-        new_pos.QuadPart = This->length.QuadPart + dlibMove.QuadPart;
-        break;
-    default:
-        return STG_E_INVALIDFUNCTION;
-    }
+    hres = CreateStreamOnHGlobal(NULL, TRUE, &output);
+    if(FAILED(hres))
+        return hres;
 
-    if(new_pos.QuadPart < 0) new_pos.QuadPart = 0;
-    else if(new_pos.QuadPart > This->length.QuadPart) new_pos.QuadPart = This->length.QuadPart;
+    while(1) {
+        hres = IStream_Read(input, buf, sizeof(buf), &size);
+        if(FAILED(hres) || !size)
+            break;
 
-    This->pos.QuadPart = new_pos.QuadPart;
+        ptr = ret = buf;
+        end = buf + size;
+
+        while(ptr < end) {
+            unsigned char byte = *ptr++;
+
+            switch(n) {
+            case -1:
+                if(byte == '=')
+                    n = 0;
+                else
+                    *ret++ = byte;
+                continue;
+            case 0:
+                prev = byte;
+                n = 1;
+                continue;
+            case 1:
+                if(prev != '\r' || byte != '\n') {
+                    int h1 = hex_digit(prev), h2 = hex_digit(byte);
+                    if(h1 != -1 && h2 != -1)
+                        *ret++ = (h1 << 4) | h2;
+                    else
+                        *ret++ = '=';
+                }
+                n = -1;
+                continue;
+            }
+        }
 
-    if(plibNewPosition) *plibNewPosition = This->pos;
+        if(ret > buf) {
+            hres = IStream_Write(output, buf, ret - buf, NULL);
+            if(FAILED(hres))
+                break;
+        }
+    }
+
+    if(SUCCEEDED(hres))
+        hres = IStream_Seek(output, pos, STREAM_SEEK_SET, NULL);
+    if(FAILED(hres)) {
+        IStream_Release(output);
+        return hres;
+    }
+
+    *ret_stream = output;
     return S_OK;
 }
 
-static HRESULT WINAPI sub_stream_SetSize(
-        IStream* iface,
-        ULARGE_INTEGER libNewSize)
+static HRESULT WINAPI MimeBody_GetData(
+                              IMimeBody* iface,
+                              ENCODINGTYPE ietEncoding,
+                              IStream** ppStream)
 {
-    FIXME("stub\n");
-    return E_NOTIMPL;
-}
+    MimeBody *This = impl_from_IMimeBody(iface);
+    ULARGE_INTEGER start, size;
+    HRESULT hres;
 
-static HRESULT WINAPI sub_stream_CopyTo(
-        IStream* iface,
-        IStream *pstm,
-        ULARGE_INTEGER cb,
-        ULARGE_INTEGER *pcbRead,
-        ULARGE_INTEGER *pcbWritten)
-{
-    HRESULT        hr = S_OK;
-    BYTE           tmpBuffer[128];
-    ULONG          bytesRead, bytesWritten, copySize;
-    ULARGE_INTEGER totalBytesRead;
-    ULARGE_INTEGER totalBytesWritten;
+    TRACE("(%p)->(%d %p)\n", This, ietEncoding, ppStream);
 
-    TRACE("(%p)->(%p, %d, %p, %p)\n", iface, pstm, cb.u.LowPart, pcbRead, pcbWritten);
+    if(This->encoding != ietEncoding) {
+        switch(This->encoding) {
+        case IET_BASE64:
+            hres = decode_base64(This->data, ppStream);
+            break;
+        case IET_QP:
+            hres = decode_qp(This->data, ppStream);
+            break;
+        default:
+            FIXME("Decoding %d is not supported.\n", This->encoding);
+            hres = S_FALSE;
+        }
+        if(ietEncoding != IET_BINARY)
+            FIXME("Encoding %d is not supported.\n", ietEncoding);
+        if(hres != S_FALSE)
+            return hres;
+    }
 
-    totalBytesRead.QuadPart = 0;
-    totalBytesWritten.QuadPart = 0;
+    start.QuadPart = 0;
+    hres = get_stream_size(This->data, &size);
+    if(SUCCEEDED(hres))
+        hres = create_sub_stream(This->data, start, size, ppStream);
+    return hres;
+}
 
-    while ( cb.QuadPart > 0 )
+static HRESULT WINAPI MimeBody_SetData(
+                              IMimeBody* iface,
+                              ENCODINGTYPE ietEncoding,
+                              LPCSTR pszPriType,
+                              LPCSTR pszSubType,
+                              REFIID riid,
+                              LPVOID pvObject)
+{
+    MimeBody *This = impl_from_IMimeBody(iface);
+    TRACE("(%p)->(%d, %s, %s, %s %p)\n", This, ietEncoding, debugstr_a(pszPriType), debugstr_a(pszSubType),
+          debugstr_guid(riid), pvObject);
+
+    if(IsEqualIID(riid, &IID_IStream))
+        IStream_AddRef((IStream *)pvObject);
+    else
     {
-        if ( cb.QuadPart >= sizeof(tmpBuffer) )
-            copySize = sizeof(tmpBuffer);
-        else
-            copySize = cb.u.LowPart;
+        FIXME("Unhandled object type %s\n", debugstr_guid(riid));
+        return E_INVALIDARG;
+    }
 
-        hr = IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
-        if (FAILED(hr)) break;
+    if(This->data)
+        release_data(&This->data_iid, This->data);
 
-        totalBytesRead.QuadPart += bytesRead;
+    This->data_iid = *riid;
+    This->data = pvObject;
 
-        if (bytesRead)
-        {
-            hr = IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
-            if (FAILED(hr)) break;
-            totalBytesWritten.QuadPart += bytesWritten;
-        }
+    IMimeBody_SetCurrentEncoding(iface, ietEncoding);
 
-        if (bytesRead != copySize)
-            cb.QuadPart = 0;
-        else
-            cb.QuadPart -= bytesRead;
-    }
+    /* FIXME: Update the content type.
+       If pszPriType == NULL use 'application'
+       If pszSubType == NULL use 'octet-stream' */
 
-    if (pcbRead) pcbRead->QuadPart = totalBytesRead.QuadPart;
-    if (pcbWritten) pcbWritten->QuadPart = totalBytesWritten.QuadPart;
+    return S_OK;
+}
 
-    return hr;
+static HRESULT WINAPI MimeBody_EmptyData(
+                                IMimeBody* iface)
+{
+    MimeBody *This = impl_from_IMimeBody(iface);
+    FIXME("(%p)->() stub\n", This);
+    return E_NOTIMPL;
 }
 
-static HRESULT WINAPI sub_stream_Commit(
-        IStream* iface,
-        DWORD grfCommitFlags)
+static HRESULT WINAPI MimeBody_CopyTo(
+                             IMimeBody* iface,
+                             IMimeBody* pBody)
 {
-    FIXME("stub\n");
+    MimeBody *This = impl_from_IMimeBody(iface);
+    FIXME("(%p)->(%p) stub\n", This, pBody);
     return E_NOTIMPL;
 }
 
-static HRESULT WINAPI sub_stream_Revert(
-        IStream* iface)
+static HRESULT WINAPI MimeBody_GetTransmitInfo(
+                                      IMimeBody* iface,
+                                      LPTRANSMITINFO pTransmitInfo)
 {
-    FIXME("stub\n");
+    MimeBody *This = impl_from_IMimeBody(iface);
+    FIXME("(%p)->(%p) stub\n", This, pTransmitInfo);
     return E_NOTIMPL;
 }
 
-static HRESULT WINAPI sub_stream_LockRegion(
-        IStream* iface,
-        ULARGE_INTEGER libOffset,
-        ULARGE_INTEGER cb,
-        DWORD dwLockType)
+static HRESULT WINAPI MimeBody_SaveToFile(
+                                 IMimeBody* iface,
+                                 ENCODINGTYPE ietEncoding,
+                                 LPCSTR pszFilePath)
 {
-    FIXME("stub\n");
+    MimeBody *This = impl_from_IMimeBody(iface);
+    FIXME("(%p)->(%d, %s) stub\n", This, ietEncoding, debugstr_a(pszFilePath));
     return E_NOTIMPL;
 }
 
-static HRESULT WINAPI sub_stream_UnlockRegion(
-        IStream* iface,
-        ULARGE_INTEGER libOffset,
-        ULARGE_INTEGER cb,
-        DWORD dwLockType)
+static HRESULT WINAPI MimeBody_GetHandle(
+                                IMimeBody* iface,
+                                LPHBODY phBody)
+{
+    MimeBody *This = impl_from_IMimeBody(iface);
+    TRACE("(%p)->(%p)\n", iface, phBody);
+
+    if(!phBody)
+        return E_INVALIDARG;
+
+    *phBody = This->handle;
+    return This->handle ? S_OK : MIME_E_NO_DATA;
+}
+
+static IMimeBodyVtbl body_vtbl =
+{
+    MimeBody_QueryInterface,
+    MimeBody_AddRef,
+    MimeBody_Release,
+    MimeBody_GetClassID,
+    MimeBody_IsDirty,
+    MimeBody_Load,
+    MimeBody_Save,
+    MimeBody_GetSizeMax,
+    MimeBody_InitNew,
+    MimeBody_GetPropInfo,
+    MimeBody_SetPropInfo,
+    MimeBody_GetProp,
+    MimeBody_SetProp,
+    MimeBody_AppendProp,
+    MimeBody_DeleteProp,
+    MimeBody_CopyProps,
+    MimeBody_MoveProps,
+    MimeBody_DeleteExcept,
+    MimeBody_QueryProp,
+    MimeBody_GetCharset,
+    MimeBody_SetCharset,
+    MimeBody_GetParameters,
+    MimeBody_IsContentType,
+    MimeBody_BindToObject,
+    MimeBody_Clone,
+    MimeBody_SetOption,
+    MimeBody_GetOption,
+    MimeBody_EnumProps,
+    MimeBody_IsType,
+    MimeBody_SetDisplayName,
+    MimeBody_GetDisplayName,
+    MimeBody_GetOffsets,
+    MimeBody_GetCurrentEncoding,
+    MimeBody_SetCurrentEncoding,
+    MimeBody_GetEstimatedSize,
+    MimeBody_GetDataHere,
+    MimeBody_GetData,
+    MimeBody_SetData,
+    MimeBody_EmptyData,
+    MimeBody_CopyTo,
+    MimeBody_GetTransmitInfo,
+    MimeBody_SaveToFile,
+    MimeBody_GetHandle
+};
+
+static HRESULT MimeBody_set_offsets(MimeBody *body, const BODYOFFSETS *offsets)
 {
-    FIXME("stub\n");
-    return E_NOTIMPL;
-}
+    TRACE("setting offsets to %d, %d, %d, %d\n", offsets->cbBoundaryStart,
+          offsets->cbHeaderStart, offsets->cbBodyStart, offsets->cbBodyEnd);
 
-static HRESULT WINAPI sub_stream_Stat(
-        IStream* iface,
-        STATSTG *pstatstg,
-        DWORD grfStatFlag)
-{
-    sub_stream_t *This = impl_from_IStream(iface);
-    FIXME("(%p)->(%p, %08x)\n", This, pstatstg, grfStatFlag);
-    memset(pstatstg, 0, sizeof(*pstatstg));
-    pstatstg->cbSize = This->length;
+    body->body_offsets = *offsets;
     return S_OK;
 }
 
-static HRESULT WINAPI sub_stream_Clone(
-        IStream* iface,
-        IStream **ppstm)
-{
-    FIXME("stub\n");
-    return E_NOTIMPL;
-}
-
-static struct IStreamVtbl sub_stream_vtbl =
-{
-    sub_stream_QueryInterface,
-    sub_stream_AddRef,
-    sub_stream_Release,
-    sub_stream_Read,
-    sub_stream_Write,
-    sub_stream_Seek,
-    sub_stream_SetSize,
-    sub_stream_CopyTo,
-    sub_stream_Commit,
-    sub_stream_Revert,
-    sub_stream_LockRegion,
-    sub_stream_UnlockRegion,
-    sub_stream_Stat,
-    sub_stream_Clone
-};
+#define FIRST_CUSTOM_PROP_ID 0x100
 
-static HRESULT create_sub_stream(IStream *stream, ULARGE_INTEGER start, ULARGE_INTEGER length, IStream **out)
+static MimeBody *mimebody_create(void)
 {
-    sub_stream_t *This;
+    MimeBody *This;
+    BODYOFFSETS body_offsets;
 
-    *out = NULL;
     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
-    if(!This) return E_OUTOFMEMORY;
+    if (!This)
+        return NULL;
 
-    This->IStream_iface.lpVtbl = &sub_stream_vtbl;
+    This->IMimeBody_iface.lpVtbl = &body_vtbl;
     This->ref = 1;
-    This->start = start;
-    This->length = length;
-    This->pos.QuadPart = 0;
-    IStream_AddRef(stream);
-    This->base = stream;
+    This->handle = NULL;
+    list_init(&This->headers);
+    list_init(&This->new_props);
+    This->next_prop_id = FIRST_CUSTOM_PROP_ID;
+    This->content_pri_type = NULL;
+    This->content_sub_type = NULL;
+    This->encoding = IET_7BIT;
+    This->data = NULL;
+    This->data_iid = IID_NULL;
 
-    *out = &This->IStream_iface;
-    return S_OK;
+    body_offsets.cbBoundaryStart = body_offsets.cbHeaderStart = 0;
+    body_offsets.cbBodyStart     = body_offsets.cbBodyEnd     = 0;
+    MimeBody_set_offsets(This, &body_offsets);
+
+    return This;
 }
 
+HRESULT MimeBody_create(IUnknown *outer, void **ppv)
+{
+    MimeBody *mb;
+
+    if(outer)
+        return CLASS_E_NOAGGREGATION;
+
+    if ((mb = mimebody_create()))
+    {
+        *ppv = &mb->IMimeBody_iface;
+        return S_OK;
+    }
+    else
+    {
+        *ppv = NULL;
+        return E_OUTOFMEMORY;
+    }
+}
 
 typedef struct body_t
 {
@@ -1518,6 +2047,8 @@ static body_t *new_body_entry(MimeBody *mime_body, DWORD index, body_t *parent)
         body->index = index;
         list_init(&body->children);
         body->parent = parent;
+
+        mime_body->handle = UlongToHandle(body->index);
     }
     return body;
 }
@@ -1531,18 +2062,16 @@ typedef struct
 static HRESULT create_body_offset_list(IStream *stm, const char *boundary, struct list *body_offsets)
 {
     HRESULT hr;
-    DWORD read;
+    DWORD read, boundary_start;
     int boundary_len = strlen(boundary);
-    char *buf, *nl_boundary, *ptr, *overlap;
+    char *buf, *ptr, *overlap;
     DWORD start = 0, overlap_no;
     offset_entry_t *cur_body = NULL;
+    BOOL is_first_line = TRUE;
     ULARGE_INTEGER cur;
     LARGE_INTEGER zero;
 
     list_init(body_offsets);
-    nl_boundary = HeapAlloc(GetProcessHeap(), 0, 4 + boundary_len + 1);
-    memcpy(nl_boundary, "\r\n--", 4);
-    memcpy(nl_boundary + 4, boundary, boundary_len + 1);
 
     overlap_no = boundary_len + 5;
 
@@ -1559,74 +2088,86 @@ static HRESULT create_body_offset_list(IStream *stm, const char *boundary, struc
         overlap[read] = '\0';
 
         ptr = buf;
-        do {
-            ptr = strstr(ptr, nl_boundary);
-            if(ptr)
-            {
-                DWORD boundary_start = start + ptr - buf;
-                char *end = ptr + boundary_len + 4;
-
-                if(*end == '\0' || *(end + 1) == '\0')
+        while(1) {
+            if(is_first_line) {
+                is_first_line = FALSE;
+            }else {
+                ptr = strstr(ptr, "\r\n");
+                if(!ptr)
                     break;
+                ptr += 2;
+            }
+
+            boundary_start = start + ptr - buf;
+
+            if(*ptr == '-' && *(ptr + 1) == '-' && !memcmp(ptr + 2, boundary, boundary_len)) {
+                ptr += boundary_len + 2;
 
-                if(*end == '\r' && *(end + 1) == '\n')
+                if(*ptr == '\r' && *(ptr + 1) == '\n')
                 {
+                    ptr += 2;
                     if(cur_body)
                     {
-                        cur_body->offsets.cbBodyEnd = boundary_start;
+                        cur_body->offsets.cbBodyEnd = boundary_start - 2;
                         list_add_tail(body_offsets, &cur_body->entry);
                     }
                     cur_body = HeapAlloc(GetProcessHeap(), 0, sizeof(*cur_body));
-                    cur_body->offsets.cbBoundaryStart = boundary_start + 2; /* doesn't including the leading \r\n */
-                    cur_body->offsets.cbHeaderStart = boundary_start + boundary_len + 6;
+                    cur_body->offsets.cbBoundaryStart = boundary_start;
+                    cur_body->offsets.cbHeaderStart = start + ptr - buf;
                 }
-                else if(*end == '-' && *(end + 1) == '-')
+                else if(*ptr == '-' && *(ptr + 1) == '-')
                 {
                     if(cur_body)
                     {
-                        cur_body->offsets.cbBodyEnd = boundary_start;
+                        cur_body->offsets.cbBodyEnd = boundary_start - 2;
                         list_add_tail(body_offsets, &cur_body->entry);
                         goto end;
                     }
                 }
-                ptr = end + 2;
             }
-        } while(ptr);
+        }
 
         if(overlap == buf) /* 1st iteration */
         {
-            memcpy(buf, buf + PARSER_BUF_SIZE - overlap_no, overlap_no);
+            memmove(buf, buf + PARSER_BUF_SIZE - overlap_no, overlap_no);
             overlap = buf + overlap_no;
             start += read - overlap_no;
         }
         else
         {
-            memcpy(buf, buf + PARSER_BUF_SIZE, overlap_no);
+            memmove(buf, buf + PARSER_BUF_SIZE, overlap_no);
             start += read;
         }
     } while(1);
 
 end:
-    HeapFree(GetProcessHeap(), 0, nl_boundary);
     HeapFree(GetProcessHeap(), 0, buf);
     return hr;
 }
 
 static body_t *create_sub_body(MimeMessage *msg, IStream *pStm, BODYOFFSETS *offset, body_t *parent)
 {
+    ULARGE_INTEGER start, length;
     MimeBody *mime_body;
     HRESULT hr;
     body_t *body;
-    ULARGE_INTEGER cur;
-    LARGE_INTEGER zero;
+    LARGE_INTEGER pos;
+
+    pos.QuadPart = offset->cbHeaderStart;
+    IStream_Seek(pStm, pos, STREAM_SEEK_SET, NULL);
 
     mime_body = mimebody_create();
     IMimeBody_Load(&mime_body->IMimeBody_iface, pStm);
-    zero.QuadPart = 0;
-    hr = IStream_Seek(pStm, zero, STREAM_SEEK_CUR, &cur);
-    offset->cbBodyStart = cur.u.LowPart + offset->cbHeaderStart;
+
+    pos.QuadPart = 0;
+    hr = IStream_Seek(pStm, pos, STREAM_SEEK_CUR, &start);
+    offset->cbBodyStart = start.QuadPart;
     if (parent) MimeBody_set_offsets(mime_body, offset);
-    IMimeBody_SetData(&mime_body->IMimeBody_iface, IET_BINARY, NULL, NULL, &IID_IStream, pStm);
+
+    length.QuadPart = offset->cbBodyEnd - offset->cbBodyStart;
+    create_sub_stream(pStm, start, length, (IStream**)&mime_body->data);
+    mime_body->data_iid = IID_IStream;
+
     body = new_body_entry(mime_body, msg->next_index++, parent);
 
     if(IMimeBody_IsContentType(&mime_body->IMimeBody_iface, "multipart", NULL) == S_OK)
@@ -1643,7 +2184,7 @@ static body_t *create_sub_body(MimeMessage *msg, IStream *pStm, BODYOFFSETS *off
 
         for(i = 0; i < count; i++)
         {
-            if(!strcasecmp(param_info[i].pszName, "boundary"))
+            if(!lstrcmpiA(param_info[i].pszName, "boundary"))
             {
                 struct list offset_list;
                 offset_entry_t *cur, *cursor2;
@@ -1651,14 +2192,8 @@ static body_t *create_sub_body(MimeMessage *msg, IStream *pStm, BODYOFFSETS *off
                 LIST_FOR_EACH_ENTRY_SAFE(cur, cursor2, &offset_list, offset_entry_t, entry)
                 {
                     body_t *sub_body;
-                    IStream *sub_stream;
-                    ULARGE_INTEGER start, length;
-
-                    start.QuadPart = cur->offsets.cbHeaderStart;
-                    length.QuadPart = cur->offsets.cbBodyEnd - cur->offsets.cbHeaderStart;
-                    create_sub_stream(pStm, start, length, &sub_stream);
-                    sub_body = create_sub_body(msg, sub_stream, &cur->offsets, body);
-                    IStream_Release(sub_stream);
+
+                    sub_body = create_sub_body(msg, pStm, &cur->offsets, body);
                     list_add_tail(&body->children, &sub_body->entry);
                     list_remove(&cur->entry);
                     HeapFree(GetProcessHeap(), 0, cur);
@@ -1688,6 +2223,8 @@ static HRESULT WINAPI MimeMessage_Load(IMimeMessage *iface, IStream *pStm)
         return E_FAIL;
     }
 
+    empty_body_list(&This->body_tree);
+
     IStream_AddRef(pStm);
     This->stream = pStm;
     offsets.cbBoundaryStart = offsets.cbHeaderStart = 0;
@@ -1779,7 +2316,7 @@ static HRESULT WINAPI MimeMessage_Commit(
     DWORD dwFlags)
 {
     FIXME("(%p)->(0x%x)\n", iface, dwFlags);
-    return E_NOTIMPL;
+    return S_OK;
 }
 
 
@@ -1867,7 +2404,10 @@ static HRESULT get_body(MimeMessage *msg, BODYLOCATION location, HBODY pivot, bo
         switch(location)
         {
         case IBL_PARENT:
-            *out = body->parent;
+            if(body->parent)
+                *out = body->parent;
+            else
+                hr = MIME_E_NOT_FOUND;
             break;
 
         case IBL_FIRST:
@@ -1931,6 +2471,11 @@ static HRESULT WINAPI MimeMessage_GetBody(IMimeMessage *iface, BODYLOCATION loca
 
     TRACE("(%p)->(%d, %p, %p)\n", iface, location, hPivot, phBody);
 
+    if(!phBody)
+        return E_INVALIDARG;
+
+    *phBody = NULL;
+
     hr = get_body(This, location, hPivot, &body);
 
     if(hr == S_OK) *phBody = UlongToHandle(body->index);
@@ -2182,27 +2727,41 @@ static HRESULT WINAPI MimeMessage_SetOption(
     const TYPEDID oid,
     LPCPROPVARIANT pValue)
 {
-    HRESULT hr = E_NOTIMPL;
+    HRESULT hr = S_OK;
     TRACE("(%p)->(%08x, %p)\n", iface, oid, pValue);
 
+    /* Message ID is checked before type.
+     *  OID 0x4D -> 0x56 and 0x58 aren't defined but will filtered out later.
+     */
+    if(TYPEDID_ID(oid) < TYPEDID_ID(OID_ALLOW_8BIT_HEADER) || TYPEDID_ID(oid) > TYPEDID_ID(OID_SECURITY_2KEY_CERT_BAG_64))
+    {
+        WARN("oid (%08x) out of range\n", oid);
+        return MIME_E_INVALID_OPTION_ID;
+    }
+
     if(pValue->vt != TYPEDID_TYPE(oid))
     {
         WARN("Called with vartype %04x and oid %08x\n", pValue->vt, oid);
-        return E_INVALIDARG;
+        return S_OK;
     }
 
     switch(oid)
     {
     case OID_HIDE_TNEF_ATTACHMENTS:
         FIXME("OID_HIDE_TNEF_ATTACHMENTS (value %d): ignoring\n", pValue->u.boolVal);
-        hr = S_OK;
         break;
     case OID_SHOW_MACBINARY:
         FIXME("OID_SHOW_MACBINARY (value %d): ignoring\n", pValue->u.boolVal);
-        hr = S_OK;
+        break;
+    case OID_SAVEBODY_KEEPBOUNDARY:
+        FIXME("OID_SAVEBODY_KEEPBOUNDARY (value %d): ignoring\n", pValue->u.boolVal);
+        break;
+    case OID_CLEANUP_TREE_ON_SAVE:
+        FIXME("OID_CLEANUP_TREE_ON_SAVE (value %d): ignoring\n", pValue->u.boolVal);
         break;
     default:
         FIXME("Unhandled oid %08x\n", oid);
+        hr = MIME_E_INVALID_OPTION_ID;
     }
 
     return hr;
@@ -2526,6 +3085,8 @@ static const IMimeMessageVtbl MimeMessageVtbl =
 HRESULT MimeMessage_create(IUnknown *outer, void **obj)
 {
     MimeMessage *This;
+    MimeBody *mime_body;
+    body_t *root_body;
 
     TRACE("(%p, %p)\n", outer, obj);
 
@@ -2546,6 +3107,10 @@ HRESULT MimeMessage_create(IUnknown *outer, void **obj)
     list_init(&This->body_tree);
     This->next_index = 1;
 
+    mime_body = mimebody_create();
+    root_body = new_body_entry(mime_body, This->next_index++, NULL);
+    list_add_head(&This->body_tree, &root_body->entry);
+
     *obj = &This->IMimeMessage_iface;
     return S_OK;
 }
@@ -2803,7 +3368,7 @@ static ULONG WINAPI MimeAlloc_Release(
 
 static LPVOID WINAPI MimeAlloc_Alloc(
         IMimeAllocator* iface,
-        ULONG cb)
+        SIZE_T cb)
 {
     return CoTaskMemAlloc(cb);
 }
@@ -2811,7 +3376,7 @@ static LPVOID WINAPI MimeAlloc_Alloc(
 static LPVOID WINAPI MimeAlloc_Realloc(
         IMimeAllocator* iface,
         LPVOID pv,
-        ULONG cb)
+        SIZE_T cb)
 {
     return CoTaskMemRealloc(pv, cb);
 }
@@ -2823,7 +3388,7 @@ static void WINAPI MimeAlloc_Free(
     CoTaskMemFree(pv);
 }
 
-static ULONG WINAPI MimeAlloc_GetSize(
+static SIZE_T WINAPI MimeAlloc_GetSize(
         IMimeAllocator* iface,
         LPVOID pv)
 {
@@ -2976,3 +3541,195 @@ HRESULT VirtualStream_create(IUnknown *outer, void **obj)
 
     return MimeOleCreateVirtualStream((IStream **)obj);
 }
+
+/* IMimePropertySchema Interface */
+static HRESULT WINAPI propschema_QueryInterface(IMimePropertySchema *iface, REFIID riid, void **out)
+{
+    propschema *This = impl_from_IMimePropertySchema(iface);
+    TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), out);
+
+    *out = NULL;
+
+    if (IsEqualIID(riid, &IID_IUnknown) ||
+        IsEqualIID(riid, &IID_IMimePropertySchema))
+    {
+        *out = iface;
+    }
+    else
+    {
+        FIXME("no interface for %s\n", debugstr_guid(riid));
+        return E_NOINTERFACE;
+    }
+
+    IMimePropertySchema_AddRef(iface);
+    return S_OK;
+}
+
+static ULONG WINAPI propschema_AddRef(IMimePropertySchema *iface)
+{
+    propschema *This = impl_from_IMimePropertySchema(iface);
+    LONG ref = InterlockedIncrement(&This->ref);
+
+    TRACE("(%p) ref=%d\n", This, ref);
+
+    return ref;
+}
+
+static ULONG WINAPI propschema_Release(IMimePropertySchema *iface)
+{
+    propschema *This = impl_from_IMimePropertySchema(iface);
+    LONG ref = InterlockedDecrement(&This->ref);
+
+    TRACE("(%p) ref=%d\n", This, ref);
+
+    if (!ref)
+    {
+        HeapFree(GetProcessHeap(), 0, This);
+    }
+
+    return ref;
+}
+
+static HRESULT WINAPI propschema_RegisterProperty(IMimePropertySchema *iface, const char *name, DWORD flags,
+        DWORD rownumber, VARTYPE vtdefault, DWORD *propid)
+{
+    propschema *This = impl_from_IMimePropertySchema(iface);
+    FIXME("(%p)->(%s, %x, %d, %d, %p) stub\n", This, debugstr_a(name), flags, rownumber, vtdefault, propid);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI propschema_ModifyProperty(IMimePropertySchema *iface, const char *name, DWORD flags,
+        DWORD rownumber, VARTYPE vtdefault)
+{
+    propschema *This = impl_from_IMimePropertySchema(iface);
+    FIXME("(%p)->(%s, %x, %d, %d) stub\n", This, debugstr_a(name), flags, rownumber, vtdefault);
+    return S_OK;
+}
+
+static HRESULT WINAPI propschema_GetPropertyId(IMimePropertySchema *iface, const char *name, DWORD *propid)
+{
+    propschema *This = impl_from_IMimePropertySchema(iface);
+    FIXME("(%p)->(%s, %p) stub\n", This, debugstr_a(name), propid);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI propschema_GetPropertyName(IMimePropertySchema *iface, DWORD propid, char **name)
+{
+    propschema *This = impl_from_IMimePropertySchema(iface);
+    FIXME("(%p)->(%d, %p) stub\n", This, propid, name);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI propschema_RegisterAddressType(IMimePropertySchema *iface, const char *name, DWORD *adrtype)
+{
+    propschema *This = impl_from_IMimePropertySchema(iface);
+    FIXME("(%p)->(%s, %p) stub\n", This, debugstr_a(name), adrtype);
+    return E_NOTIMPL;
+}
+
+static IMimePropertySchemaVtbl prop_schema_vtbl =
+{
+    propschema_QueryInterface,
+    propschema_AddRef,
+    propschema_Release,
+    propschema_RegisterProperty,
+    propschema_ModifyProperty,
+    propschema_GetPropertyId,
+    propschema_GetPropertyName,
+    propschema_RegisterAddressType
+};
+
+
+HRESULT WINAPI MimeOleGetPropertySchema(IMimePropertySchema **schema)
+{
+    propschema *This;
+
+    TRACE("(%p) stub\n", schema);
+
+    This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
+    if (!This)
+        return E_OUTOFMEMORY;
+
+    This->IMimePropertySchema_iface.lpVtbl = &prop_schema_vtbl;
+    This->ref = 1;
+
+    *schema = &This->IMimePropertySchema_iface;
+
+    return S_OK;
+}
+
+HRESULT WINAPI MimeGetAddressFormatW(REFIID riid, void *object, DWORD addr_type,
+       ADDRESSFORMAT addr_format, WCHAR **address)
+{
+    FIXME("(%s, %p, %d, %d, %p) stub\n", debugstr_guid(riid), object, addr_type, addr_format, address);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI mime_obj_QueryInterface(IUnknown *iface, REFIID riid, void **ppv)
+{
+    FIXME("(%s %p)\n", debugstr_guid(riid), ppv);
+    *ppv = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI mime_obj_AddRef(IUnknown *iface)
+{
+    TRACE("\n");
+    return 2;
+}
+
+static ULONG WINAPI mime_obj_Release(IUnknown *iface)
+{
+    TRACE("\n");
+    return 1;
+}
+
+static const IUnknownVtbl mime_obj_vtbl = {
+    mime_obj_QueryInterface,
+    mime_obj_AddRef,
+    mime_obj_Release
+};
+
+static IUnknown mime_obj = { &mime_obj_vtbl };
+
+HRESULT WINAPI MimeOleObjectFromMoniker(BINDF bindf, IMoniker *moniker, IBindCtx *binding,
+       REFIID riid, void **out, IMoniker **moniker_new)
+{
+    WCHAR *display_name, *mhtml_url;
+    size_t len;
+    HRESULT hres;
+
+    static const WCHAR mhtml_prefixW[] = {'m','h','t','m','l',':'};
+
+    WARN("(0x%08x, %p, %p, %s, %p, %p) semi-stub\n", bindf, moniker, binding, debugstr_guid(riid), out, moniker_new);
+
+    if(!IsEqualGUID(&IID_IUnknown, riid)) {
+        FIXME("Unsupported riid %s\n", debugstr_guid(riid));
+        return E_NOINTERFACE;
+    }
+
+    hres = IMoniker_GetDisplayName(moniker, NULL, NULL, &display_name);
+    if(FAILED(hres))
+        return hres;
+
+    TRACE("display name %s\n", debugstr_w(display_name));
+
+    len = lstrlenW(display_name);
+    mhtml_url = heap_alloc((len+1)*sizeof(WCHAR) + sizeof(mhtml_prefixW));
+    if(!mhtml_url)
+        return E_OUTOFMEMORY;
+
+    memcpy(mhtml_url, mhtml_prefixW, sizeof(mhtml_prefixW));
+    lstrcpyW(mhtml_url + ARRAY_SIZE(mhtml_prefixW), display_name);
+    HeapFree(GetProcessHeap(), 0, display_name);
+
+    hres = CreateURLMoniker(NULL, mhtml_url, moniker_new);
+    heap_free(mhtml_url);
+    if(FAILED(hres))
+        return hres;
+
+    /* FIXME: We most likely should start binding here and return something more meaningful as mime object. */
+    *out = &mime_obj;
+    return S_OK;
+}