* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
-#define WIN32_NO_STATUS
-#define _INC_WINDOWS
-#define COM_NO_WINDOWS_H
-
#define COBJMACROS
#define NONAMELESSUNION
#include <stdarg.h>
-//#include <stdio.h>
-
-#include <windef.h>
-#include <winbase.h>
-//#include "winuser.h"
-//#include "objbase.h"
-#include <ole2.h>
-#include <mimeole.h>
-
-#include <wine/list.h>
-#include <wine/debug.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"
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
typedef struct MimeBody
{
- const IMimeBodyVtbl *lpVtbl;
- LONG refs;
+ IMimeBody IMimeBody_iface;
+ LONG ref;
HBODY handle;
BODYOFFSETS body_offsets;
} MimeBody;
-static inline MimeBody *impl_from_IMimeBody( IMimeBody *iface )
+typedef struct
{
- return (MimeBody *)((char*)iface - FIELD_OFFSET(MimeBody, lpVtbl));
-}
+ IStream IStream_iface;
+ LONG ref;
+ IStream *base;
+ ULARGE_INTEGER pos, start, length;
+} sub_stream_t;
-static LPSTR strdupA(LPCSTR str)
+static inline sub_stream_t *impl_from_IStream(IStream *iface)
{
- char *ret;
- int len = strlen(str);
- ret = HeapAlloc(GetProcessHeap(), 0, len + 1);
- memcpy(ret, str, len + 1);
- return ret;
+ return CONTAINING_RECORD(iface, sub_stream_t, IStream_iface);
}
-#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)
+static HRESULT WINAPI sub_stream_QueryInterface(IStream *iface, REFIID riid, void **ppv)
{
- char *buf = NULL;
- DWORD size = PARSER_BUF_SIZE, offset = 0, last_end = 0;
- HRESULT hr;
- int done = 0;
+ sub_stream_t *This = impl_from_IStream(iface);
- *ptr = NULL;
+ TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
+ *ppv = NULL;
- do
+ if(IsEqualIID(riid, &IID_IUnknown) ||
+ IsEqualIID(riid, &IID_ISequentialStream) ||
+ IsEqualIID(riid, &IID_IStream))
{
- 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 = 1;
+ IStream_AddRef(iface);
+ *ppv = iface;
+ return S_OK;
+ }
+ return E_NOINTERFACE;
+}
- 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 = 1;
- }
- else
- last_end = new_end;
- }
- } while(!done);
+static ULONG WINAPI sub_stream_AddRef(IStream *iface)
+{
+ sub_stream_t *This = impl_from_IStream(iface);
+ LONG ref = InterlockedIncrement(&This->ref);
- *ptr = buf;
- return S_OK;
+ TRACE("(%p) ref=%d\n", This, ref);
-fail:
- HeapFree(GetProcessHeap(), 0, buf);
- return hr;
+ return ref;
}
-static header_t *read_prop(MimeBody *body, char **ptr)
+static ULONG WINAPI sub_stream_Release(IStream *iface)
{
- char *colon = strchr(*ptr, ':');
- const property_t *prop;
- header_t *ret;
-
- if(!colon) return NULL;
-
- *colon = '\0';
+ sub_stream_t *This = impl_from_IStream(iface);
+ LONG ref = InterlockedDecrement(&This->ref);
- for(prop = default_props; prop->name; prop++)
- {
- if(!strcasecmp(*ptr, prop->name))
- {
- TRACE("%s: found match with default property id %d\n", *ptr, prop->id);
- break;
- }
- }
+ TRACE("(%p) ref=%d\n", This, ref);
- if(!prop->name)
+ if(!ref)
{
- 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);
- }
+ IStream_Release(This->base);
+ HeapFree(GetProcessHeap(), 0, This);
}
-
- ret = HeapAlloc(GetProcessHeap(), 0, sizeof(*ret));
- ret->prop = prop;
- PropVariantInit(&ret->value);
- list_init(&ret->params);
- *ptr = colon + 1;
-
- return ret;
+ return ref;
}
-static void unfold_header(char *header, int len)
+static HRESULT WINAPI sub_stream_Read(
+ IStream* iface,
+ void *pv,
+ ULONG cb,
+ ULONG *pcbRead)
{
- char *start = header, *cp = header;
+ sub_stream_t *This = impl_from_IStream(iface);
+ HRESULT hr;
+ LARGE_INTEGER tmp_pos;
- do {
- while(*cp == ' ' || *cp == '\t')
- {
- cp++;
- len--;
- }
- if(cp != start)
- memmove(start, cp, len + 1);
+ TRACE("(%p, %d, %p)\n", pv, cb, pcbRead);
- cp = strstr(start, "\r\n");
- len -= (cp - start);
- start = cp;
- *start = ' ';
- start++;
- len--;
- cp += 2;
- } while(*cp == ' ' || *cp == '\t');
+ tmp_pos.QuadPart = This->pos.QuadPart + This->start.QuadPart;
+ IStream_Seek(This->base, tmp_pos, STREAM_SEEK_SET, NULL);
- *(start - 1) = '\0';
-}
+ if(This->pos.QuadPart + cb > This->length.QuadPart)
+ cb = This->length.QuadPart - This->pos.QuadPart;
-static char *unquote_string(const char *str)
-{
- int quoted = 0;
- char *ret, *cp;
+ hr = IStream_Read(This->base, pv, cb, pcbRead);
- while(*str == ' ' || *str == '\t') str++;
+ This->pos.QuadPart += *pcbRead;
- if(*str == '"')
- {
- quoted = 1;
- 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';
- break;
- }
- }
- }
- return ret;
+ return hr;
}
-static void add_param(header_t *header, const char *p)
+static HRESULT WINAPI sub_stream_Write(
+ IStream* iface,
+ const void *pv,
+ ULONG cb,
+ ULONG *pcbWritten)
{
- const char *key = p, *value, *cp = p;
- param_t *param;
- char *name;
+ FIXME("stub\n");
+ return E_NOTIMPL;
+}
- TRACE("got param %s\n", p);
+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;
- while (*key == ' ' || *key == '\t' ) key++;
+ TRACE("(%08x.%08x, %x, %p)\n", dlibMove.u.HighPart, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
- cp = strchr(key, '=');
- if(!cp)
+ switch(dwOrigin)
{
- WARN("malformed parameter - skipping\n");
- return;
+ 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;
}
- name = HeapAlloc(GetProcessHeap(), 0, cp - key + 1);
- memcpy(name, key, cp - key);
- name[cp - key] = '\0';
+ if(new_pos.QuadPart < 0) new_pos.QuadPart = 0;
+ else if(new_pos.QuadPart > This->length.QuadPart) new_pos.QuadPart = This->length.QuadPart;
- value = cp + 1;
+ This->pos.QuadPart = new_pos.QuadPart;
- param = HeapAlloc(GetProcessHeap(), 0, sizeof(*param));
- param->name = name;
- param->value = unquote_string(value);
- list_add_tail(&header->params, ¶m->entry);
+ if(plibNewPosition) *plibNewPosition = This->pos;
+ return S_OK;
}
-static void split_params(header_t *header, char *value)
+static HRESULT WINAPI sub_stream_SetSize(
+ IStream* iface,
+ ULARGE_INTEGER libNewSize)
{
- char *cp = value, *start = value;
- int in_quote = 0;
- int done_value = 0;
+ FIXME("stub\n");
+ return E_NOTIMPL;
+}
- while(*cp)
- {
- if(!in_quote && *cp == ';')
- {
- *cp = '\0';
- if(done_value) add_param(header, start);
- done_value = 1;
- start = cp + 1;
+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)->(%p, %d, %p, %p)\n", iface, pstm, cb.u.LowPart, pcbRead, pcbWritten);
+
+ totalBytesRead.QuadPart = 0;
+ totalBytesWritten.QuadPart = 0;
+
+ while ( cb.QuadPart > 0 )
+ {
+ 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';
+ break;
+ }
+ }
+ }
+ return ret;
+}
+
+static void add_param(header_t *header, const char *p)
+{
+ const char *key = p, *value, *cp = p;
+ param_t *param;
+ char *name;
+
+ TRACE("got param %s\n", p);
+
+ while (*key == ' ' || *key == '\t' ) key++;
+
+ cp = strchr(key, '=');
+ if(!cp)
+ {
+ WARN("malformed parameter - skipping\n");
+ return;
+ }
+
+ name = HeapAlloc(GetProcessHeap(), 0, cp - key + 1);
+ memcpy(name, key, cp - key);
+ name[cp - key] = '\0';
+
+ value = cp + 1;
+
+ param = HeapAlloc(GetProcessHeap(), 0, sizeof(*param));
+ param->name = name;
+ param->value = unquote_string(value);
+ list_add_tail(&header->params, ¶m->entry);
+}
+
+static void split_params(header_t *header, char *value)
+{
+ char *cp = value, *start = value;
+ BOOL in_quotes = FALSE, done_value = FALSE;
+
+ while(*cp)
+ {
+ if(!in_quotes && *cp == ';')
+ {
+ *cp = '\0';
+ if(done_value) add_param(header, start);
+ done_value = TRUE;
+ start = cp + 1;
}
else if(*cp == '"')
- in_quote = !in_quote;
+ in_quotes = !in_quotes;
cp++;
}
if(done_value) add_param(header, start);
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)
{
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;
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);
}
}
+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);
}
}
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;
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)
return E_NOINTERFACE;
}
-static ULONG WINAPI MimeBody_AddRef(IMimeBody* iface)
+static ULONG WINAPI MimeBody_AddRef(IMimeBody *iface)
{
MimeBody *This = impl_from_IMimeBody(iface);
- TRACE("(%p)->()\n", iface);
- return InterlockedIncrement(&This->refs);
+ LONG ref = InterlockedIncrement(&This->ref);
+
+ TRACE("(%p) ref=%d\n", This, ref);
+
+ return ref;
}
-static ULONG WINAPI MimeBody_Release(IMimeBody* iface)
+static ULONG WINAPI MimeBody_Release(IMimeBody *iface)
{
MimeBody *This = impl_from_IMimeBody(iface);
- ULONG refs;
+ LONG ref = InterlockedDecrement(&This->ref);
- TRACE("(%p)->()\n", iface);
+ TRACE("(%p) ref=%d\n", This, ref);
- refs = InterlockedDecrement(&This->refs);
- if (!refs)
+ if (!ref)
{
empty_header_list(&This->headers);
empty_new_prop_list(&This->new_props);
HeapFree(GetProcessHeap(), 0, This);
}
- return refs;
+ return ref;
}
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,
- LPSTREAM pStm)
+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,
- LPSTREAM pStm,
- BOOL fClearDirty)
+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;
}
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;
}
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(
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;
}
LPPROPVARIANT pValue)
{
MimeBody *This = impl_from_IMimeBody(iface);
- TRACE("(%p)->(%s, %d, %p)\n", This, pszName, dwFlags, pValue);
+ 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(!strcasecmp(pszName, "att:pri-content-type"))
+ if(!ISPIDSTR(pszName) && !lstrcmpiA(pszName, "att:pri-content-type"))
{
PropVariantClear(pValue);
pValue->vt = VT_LPSTR;
return S_OK;
}
- FIXME("stub!\n");
- return E_FAIL;
+ 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(
DWORD dwFlags,
LPCPROPVARIANT pValue)
{
- FIXME("stub\n");
- return E_NOTIMPL;
+ 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);
+ }
+
+ header->prop = prop;
+ PropVariantInit(&header->value);
+ list_init(&header->params);
+ list_add_tail(&This->headers, &header->entry);
+ }
+
+ PropVariantCopy(&header->value, pValue);
+
+ return S_OK;
}
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;
}
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(
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
{
const char *pri = This->content_pri_type;
if(!pri) pri = "text";
- if(strcasecmp(pri, pszPriType)) return S_FALSE;
+ if(lstrcmpiA(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;
+ if(lstrcmpiA(sub, pszSubType)) return S_FALSE;
}
return S_OK;
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;
}
IMimeBody* iface,
IMimePropertySet** ppPropertySet)
{
- FIXME("stub\n");
+ MimeBody *This = impl_from_IMimeBody(iface);
+ FIXME("(%p)->(%p) stub\n", This, ppPropertySet);
return E_NOTIMPL;
}
const TYPEDID oid,
LPCPROPVARIANT pValue)
{
+ MimeBody *This = impl_from_IMimeBody(iface);
HRESULT hr = E_NOTIMPL;
- TRACE("(%p)->(%08x, %p)\n", iface, oid, pValue);
+ TRACE("(%p)->(%08x, %p)\n", This, oid, pValue);
if(pValue->vt != TYPEDID_TYPE(oid))
{
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);
}
const TYPEDID oid,
LPPROPVARIANT pValue)
{
- FIXME("(%p)->(%08x, %p): stub\n", iface, oid, pValue);
+ MimeBody *This = impl_from_IMimeBody(iface);
+ FIXME("(%p)->(%08x, %p): stub\n", This, oid, pValue);
return E_NOTIMPL;
}
DWORD dwFlags,
IMimeEnumProperties** ppEnum)
{
- FIXME("stub\n");
+ MimeBody *This = impl_from_IMimeBody(iface);
+ FIXME("(%p)->(0x%x, %p) stub\n", This, dwFlags, ppEnum);
return E_NOTIMPL;
}
{
MimeBody *This = impl_from_IMimeBody(iface);
- TRACE("(%p)->(%d)\n", iface, bodytype);
+ TRACE("(%p)->(%d)\n", This, bodytype);
switch(bodytype)
{
case IBT_EMPTY:
IMimeBody* iface,
LPCSTR pszDisplay)
{
- FIXME("stub\n");
+ MimeBody *This = impl_from_IMimeBody(iface);
+ FIXME("(%p)->(%s) stub\n", This, debugstr_a(pszDisplay));
return E_NOTIMPL;
}
IMimeBody* iface,
LPSTR* ppszDisplay)
{
- FIXME("stub\n");
+ MimeBody *This = impl_from_IMimeBody(iface);
+ FIXME("(%p)->(%p) stub\n", This, ppszDisplay);
return E_NOTIMPL;
}
ENCODINGTYPE ietEncoding,
ULONG* pcbSize)
{
- FIXME("stub\n");
+ MimeBody *This = impl_from_IMimeBody(iface);
+ FIXME("(%p)->(%d, %p) stub\n", This, ietEncoding, pcbSize);
return E_NOTIMPL;
}
ENCODINGTYPE ietEncoding,
IStream* pStream)
{
- FIXME("stub\n");
+ MimeBody *This = impl_from_IMimeBody(iface);
+ FIXME("(%p)->(%d, %p) stub\n", This, ietEncoding, pStream);
return E_NOTIMPL;
}
+static const signed char base64_decode_table[] =
+{
+ -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 */
+};
+
+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;
+
+ ptr = ret = buf;
+ end = buf + size;
+
+ while(1) {
+ /* skip invalid chars */
+ while(ptr < end && (*ptr >= ARRAY_SIZE(base64_decode_table)
+ || base64_decode_table[*ptr] == -1))
+ ptr++;
+ if(ptr == end)
+ break;
+
+ 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;
+ }
+ }
+
+ 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 int hex_digit(char c)
+{
+ 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 decode_qp(IStream *input, IStream **ret_stream)
+{
+ 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;
+
+ 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;
+
+ 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(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 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);
+ ULARGE_INTEGER start, size;
+ HRESULT hres;
- *ppStream = This->data;
- IStream_AddRef(*ppStream);
- return S_OK;
+ TRACE("(%p)->(%d %p)\n", This, ietEncoding, ppStream);
+
+ 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;
+ }
+
+ start.QuadPart = 0;
+ hres = get_stream_size(This->data, &size);
+ if(SUCCEEDED(hres))
+ hres = create_sub_stream(This->data, start, size, ppStream);
+ return hres;
}
static HRESULT WINAPI MimeBody_SetData(
}
if(This->data)
- FIXME("release old data\n");
+ release_data(&This->data_iid, This->data);
This->data_iid = *riid;
This->data = pvObject;
static HRESULT WINAPI MimeBody_EmptyData(
IMimeBody* iface)
{
- FIXME("stub\n");
+ MimeBody *This = impl_from_IMimeBody(iface);
+ FIXME("(%p)->() stub\n", This);
return E_NOTIMPL;
}
IMimeBody* iface,
IMimeBody* pBody)
{
- FIXME("stub\n");
+ MimeBody *This = impl_from_IMimeBody(iface);
+ FIXME("(%p)->(%p) stub\n", This, pBody);
return E_NOTIMPL;
}
IMimeBody* iface,
LPTRANSMITINFO pTransmitInfo)
{
- FIXME("stub\n");
+ MimeBody *This = impl_from_IMimeBody(iface);
+ FIXME("(%p)->(%p) stub\n", This, pTransmitInfo);
return E_NOTIMPL;
}
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;
}
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;
}
MimeBody_GetTransmitInfo,
MimeBody_SaveToFile,
MimeBody_GetHandle
-};
-
-static HRESULT MimeBody_set_offsets(MimeBody *body, const BODYOFFSETS *offsets)
-{
- TRACE("setting offsets to %d, %d, %d, %d\n", offsets->cbBoundaryStart,
- offsets->cbHeaderStart, offsets->cbBodyStart, offsets->cbBodyEnd);
-
- body->body_offsets = *offsets;
- return S_OK;
-}
-
-#define FIRST_CUSTOM_PROP_ID 0x100
-
-HRESULT MimeBody_create(IUnknown *outer, void **obj)
-{
- MimeBody *This;
- BODYOFFSETS body_offsets;
-
- *obj = NULL;
-
- if(outer) return CLASS_E_NOAGGREGATION;
-
- This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
- if (!This) return E_OUTOFMEMORY;
-
- This->lpVtbl = &body_vtbl;
- This->refs = 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);
-
- *obj = &This->lpVtbl;
- return S_OK;
-}
-
-typedef struct
-{
- IStreamVtbl *lpVtbl;
- LONG refs;
-
- IStream *base;
- ULARGE_INTEGER pos, start, length;
-} sub_stream_t;
-
-static inline sub_stream_t *impl_from_IStream( IStream *iface )
-{
- return (sub_stream_t *)((char*)iface - FIELD_OFFSET(sub_stream_t, lpVtbl));
-}
-
-static HRESULT WINAPI sub_stream_QueryInterface(
- IStream* iface,
- REFIID riid,
- void **ppvObject)
-{
- sub_stream_t *This = impl_from_IStream(iface);
-
- TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppvObject);
- *ppvObject = NULL;
-
- if(IsEqualIID(riid, &IID_IUnknown) ||
- IsEqualIID(riid, &IID_ISequentialStream) ||
- IsEqualIID(riid, &IID_IStream))
- {
- IStream_AddRef(iface);
- *ppvObject = iface;
- return S_OK;
- }
- return E_NOINTERFACE;
-}
-
-static ULONG WINAPI sub_stream_AddRef(
- IStream* iface)
-{
- sub_stream_t *This = impl_from_IStream(iface);
-
- TRACE("(%p)\n", This);
- return InterlockedIncrement(&This->refs);
-}
-
-static ULONG WINAPI sub_stream_Release(
- IStream* iface)
-{
- sub_stream_t *This = impl_from_IStream(iface);
- LONG refs;
-
- TRACE("(%p)\n", This);
- refs = InterlockedDecrement(&This->refs);
- if(!refs)
- {
- IStream_Release(This->base);
- HeapFree(GetProcessHeap(), 0, This);
- }
- return refs;
-}
-
-static HRESULT WINAPI sub_stream_Read(
- IStream* iface,
- void *pv,
- ULONG cb,
- ULONG *pcbRead)
-{
- sub_stream_t *This = impl_from_IStream(iface);
- HRESULT hr;
- ULARGE_INTEGER base_pos;
- LARGE_INTEGER tmp_pos;
-
- TRACE("(%p, %d, %p)\n", pv, cb, pcbRead);
-
- 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);
-
- if(This->pos.QuadPart + cb > This->length.QuadPart)
- cb = This->length.QuadPart - This->pos.QuadPart;
-
- hr = IStream_Read(This->base, pv, cb, pcbRead);
-
- This->pos.QuadPart += *pcbRead;
-
- tmp_pos.QuadPart = base_pos.QuadPart;
- IStream_Seek(This->base, tmp_pos, STREAM_SEEK_SET, NULL);
-
- return hr;
-}
-
-static HRESULT WINAPI sub_stream_Write(
- IStream* iface,
- const void *pv,
- ULONG cb,
- ULONG *pcbWritten)
-{
- FIXME("stub\n");
- return E_NOTIMPL;
-}
-
-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;
-
- TRACE("(%08x.%08x, %x, %p)\n", dlibMove.u.HighPart, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
-
- 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;
- }
-
- if(new_pos.QuadPart < 0) new_pos.QuadPart = 0;
- else if(new_pos.QuadPart > This->length.QuadPart) new_pos.QuadPart = This->length.QuadPart;
-
- This->pos.QuadPart = new_pos.QuadPart;
-
- if(plibNewPosition) *plibNewPosition = This->pos;
- return S_OK;
-}
-
-static HRESULT WINAPI sub_stream_SetSize(
- IStream* iface,
- ULARGE_INTEGER libNewSize)
-{
- FIXME("stub\n");
- return E_NOTIMPL;
-}
-
-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)->(%p, %d, %p, %p)\n", iface, pstm, cb.u.LowPart, pcbRead, pcbWritten);
-
- totalBytesRead.QuadPart = 0;
- totalBytesWritten.QuadPart = 0;
-
- while ( cb.QuadPart > 0 )
- {
- 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)
+static HRESULT MimeBody_set_offsets(MimeBody *body, const BODYOFFSETS *offsets)
{
- 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;
-}
+ TRACE("setting offsets to %d, %d, %d, %d\n", offsets->cbBoundaryStart,
+ offsets->cbHeaderStart, offsets->cbBodyStart, offsets->cbBodyEnd);
-static HRESULT WINAPI sub_stream_Clone(
- IStream* iface,
- IStream **ppstm)
-{
- FIXME("stub\n");
- return E_NOTIMPL;
+ body->body_offsets = *offsets;
+ return S_OK;
}
-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->lpVtbl = &sub_stream_vtbl;
- This->refs = 1;
- This->start = start;
- This->length = length;
- This->pos.QuadPart = 0;
- IStream_AddRef(stream);
- This->base = stream;
+ 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;
- *out = (IStream*)&This->lpVtbl;
- 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
{
struct list entry;
DWORD index;
- IMimeBody *mime_body;
+ MimeBody *mime_body;
struct body_t *parent;
struct list children;
typedef struct MimeMessage
{
- const IMimeMessageVtbl *lpVtbl;
-
- LONG refs;
+ IMimeMessage IMimeMessage_iface;
+ LONG ref;
IStream *stream;
struct list body_tree;
DWORD next_index;
} MimeMessage;
+static inline MimeMessage *impl_from_IMimeMessage(IMimeMessage *iface)
+{
+ return CONTAINING_RECORD(iface, MimeMessage, IMimeMessage_iface);
+}
+
static HRESULT WINAPI MimeMessage_QueryInterface(IMimeMessage *iface, REFIID riid, void **ppv)
{
TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
IsEqualIID(riid, &IID_IMimeMessage))
{
*ppv = iface;
- IUnknown_AddRef(iface);
+ IMimeMessage_AddRef(iface);
return S_OK;
}
static ULONG WINAPI MimeMessage_AddRef(IMimeMessage *iface)
{
- MimeMessage *This = (MimeMessage *)iface;
- TRACE("(%p)->()\n", iface);
- return InterlockedIncrement(&This->refs);
+ MimeMessage *This = impl_from_IMimeMessage(iface);
+ ULONG ref = InterlockedIncrement(&This->ref);
+
+ TRACE("(%p) ref=%d\n", This, ref);
+
+ return ref;
}
static void empty_body_list(struct list *list)
{
empty_body_list(&body->children);
list_remove(&body->entry);
- IMimeBody_Release(body->mime_body);
+ IMimeBody_Release(&body->mime_body->IMimeBody_iface);
HeapFree(GetProcessHeap(), 0, body);
}
}
static ULONG WINAPI MimeMessage_Release(IMimeMessage *iface)
{
- MimeMessage *This = (MimeMessage *)iface;
- ULONG refs;
+ MimeMessage *This = impl_from_IMimeMessage(iface);
+ ULONG ref = InterlockedDecrement(&This->ref);
- TRACE("(%p)->()\n", iface);
+ TRACE("(%p) ref=%d\n", This, ref);
- refs = InterlockedDecrement(&This->refs);
- if (!refs)
+ if (!ref)
{
empty_body_list(&This->body_tree);
HeapFree(GetProcessHeap(), 0, This);
}
- return refs;
+ return ref;
}
/*** IPersist methods ***/
return E_NOTIMPL;
}
-static body_t *new_body_entry(IMimeBody *mime_body, DWORD index, body_t *parent)
+static body_t *new_body_entry(MimeBody *mime_body, DWORD index, body_t *parent)
{
body_t *body = HeapAlloc(GetProcessHeap(), 0, sizeof(*body));
if(body)
body->index = index;
list_init(&body->children);
body->parent = parent;
+
+ mime_body->handle = UlongToHandle(body->index);
}
return body;
}
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;
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)
{
- IMimeBody *mime_body;
+ 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);
+
+ pos.QuadPart = 0;
+ hr = IStream_Seek(pStm, pos, STREAM_SEEK_CUR, &start);
+ offset->cbBodyStart = start.QuadPart;
+ if (parent) MimeBody_set_offsets(mime_body, offset);
+
+ length.QuadPart = offset->cbBodyEnd - offset->cbBodyStart;
+ create_sub_stream(pStm, start, length, (IStream**)&mime_body->data);
+ mime_body->data_iid = IID_IStream;
- MimeBody_create(NULL, (void**)&mime_body);
- IMimeBody_Load(mime_body, pStm);
- zero.QuadPart = 0;
- hr = IStream_Seek(pStm, zero, STREAM_SEEK_CUR, &cur);
- offset->cbBodyStart = cur.u.LowPart + offset->cbHeaderStart;
- if(parent) MimeBody_set_offsets(impl_from_IMimeBody(mime_body), offset);
- IMimeBody_SetData(mime_body, IET_BINARY, NULL, NULL, &IID_IStream, pStm);
body = new_body_entry(mime_body, msg->next_index++, parent);
- if(IMimeBody_IsContentType(mime_body, "multipart", NULL) == S_OK)
+ if(IMimeBody_IsContentType(&mime_body->IMimeBody_iface, "multipart", NULL) == S_OK)
{
MIMEPARAMINFO *param_info;
ULONG count, i;
IMimeAllocator *alloc;
- hr = IMimeBody_GetParameters(mime_body, "Content-Type", &count, ¶m_info);
+ hr = IMimeBody_GetParameters(&mime_body->IMimeBody_iface, "Content-Type", &count,
+ ¶m_info);
if(hr != S_OK || count == 0) return body;
MimeOleGetAllocator(&alloc);
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;
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);
return body;
}
-static HRESULT WINAPI MimeMessage_Load(
- IMimeMessage *iface,
- LPSTREAM pStm)
+static HRESULT WINAPI MimeMessage_Load(IMimeMessage *iface, IStream *pStm)
{
- MimeMessage *This = (MimeMessage *)iface;
+ MimeMessage *This = impl_from_IMimeMessage(iface);
body_t *root_body;
BODYOFFSETS offsets;
ULARGE_INTEGER cur;
return E_FAIL;
}
+ empty_body_list(&This->body_tree);
+
IStream_AddRef(pStm);
This->stream = pStm;
offsets.cbBoundaryStart = offsets.cbHeaderStart = 0;
zero.QuadPart = 0;
IStream_Seek(pStm, zero, STREAM_SEEK_END, &cur);
offsets.cbBodyEnd = cur.u.LowPart;
- MimeBody_set_offsets(impl_from_IMimeBody(root_body->mime_body), &offsets);
+ MimeBody_set_offsets(root_body->mime_body, &offsets);
list_add_head(&This->body_tree, &root_body->entry);
return S_OK;
}
-static HRESULT WINAPI MimeMessage_Save(
- IMimeMessage *iface,
- LPSTREAM pStm,
- BOOL fClearDirty)
+static HRESULT WINAPI MimeMessage_Save(IMimeMessage *iface, IStream *pStm, BOOL fClearDirty)
{
FIXME("(%p)->(%p, %s)\n", iface, pStm, fClearDirty ? "TRUE" : "FALSE");
return E_NOTIMPL;
}
/*** IMimeMessageTree methods ***/
-static HRESULT WINAPI MimeMessage_GetMessageSource(
- IMimeMessage *iface,
- IStream **ppStream,
- DWORD dwFlags)
+static HRESULT WINAPI MimeMessage_GetMessageSource(IMimeMessage *iface, IStream **ppStream,
+ DWORD dwFlags)
{
- MimeMessage *This = (MimeMessage *)iface;
+ MimeMessage *This = impl_from_IMimeMessage(iface);
+
FIXME("(%p)->(%p, 0x%x)\n", iface, ppStream, dwFlags);
IStream_AddRef(This->stream);
DWORD dwFlags)
{
FIXME("(%p)->(0x%x)\n", iface, dwFlags);
- return E_NOTIMPL;
+ return S_OK;
}
return S_FALSE;
}
-static HRESULT WINAPI MimeMessage_BindToObject(
- IMimeMessage *iface,
- const HBODY hBody,
- REFIID riid,
- void **ppvObject)
+static HRESULT WINAPI MimeMessage_BindToObject(IMimeMessage *iface, const HBODY hBody, REFIID riid,
+ void **ppvObject)
{
- MimeMessage *This = (MimeMessage *)iface;
+ MimeMessage *This = impl_from_IMimeMessage(iface);
HRESULT hr;
body_t *body;
if(IsEqualIID(riid, &IID_IMimeBody))
{
- IMimeBody_AddRef(body->mime_body);
- *ppvObject = body->mime_body;
+ IMimeBody_AddRef(&body->mime_body->IMimeBody_iface);
+ *ppvObject = &body->mime_body->IMimeBody_iface;
return S_OK;
}
switch(location)
{
case IBL_PARENT:
- *out = body->parent;
+ if(body->parent)
+ *out = body->parent;
+ else
+ hr = MIME_E_NOT_FOUND;
break;
case IBL_FIRST:
return E_NOTIMPL;
}
-static HRESULT WINAPI MimeMessage_GetBody(
- IMimeMessage *iface,
- BODYLOCATION location,
- HBODY hPivot,
- LPHBODY phBody)
+static HRESULT WINAPI MimeMessage_GetBody(IMimeMessage *iface, BODYLOCATION location, HBODY hPivot,
+ HBODY *phBody)
{
- MimeMessage *This = (MimeMessage *)iface;
+ MimeMessage *This = impl_from_IMimeMessage(iface);
body_t *body;
HRESULT hr;
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);
}
}
-static HRESULT WINAPI MimeMessage_CountBodies(
- IMimeMessage *iface,
- HBODY hParent,
- boolean fRecurse,
- ULONG *pcBodies)
+static HRESULT WINAPI MimeMessage_CountBodies(IMimeMessage *iface, HBODY hParent, boolean fRecurse,
+ ULONG *pcBodies)
{
HRESULT hr;
- MimeMessage *This = (MimeMessage *)iface;
+ MimeMessage *This = impl_from_IMimeMessage(iface);
body_t *body;
TRACE("(%p)->(%p, %s, %p)\n", iface, hParent, fRecurse ? "TRUE" : "FALSE", pcBodies);
return S_OK;
}
-static HRESULT find_next(IMimeMessage *msg, body_t *body, LPFINDBODY find, HBODY *out)
+static HRESULT find_next(MimeMessage *This, body_t *body, FINDBODY *find, HBODY *out)
{
- MimeMessage *This = (MimeMessage *)msg;
struct list *ptr;
HBODY next;
body = LIST_ENTRY( ptr, body_t, entry );
next = UlongToHandle( body->index );
find->dwReserved = body->index;
- if (IMimeBody_IsContentType(body->mime_body, find->pszPriType, find->pszSubType) == S_OK)
+ if (IMimeBody_IsContentType(&body->mime_body->IMimeBody_iface, find->pszPriType,
+ find->pszSubType) == S_OK)
{
*out = next;
return S_OK;
return MIME_E_NOT_FOUND;
}
-static HRESULT WINAPI MimeMessage_FindFirst(
- IMimeMessage *iface,
- LPFINDBODY pFindBody,
- LPHBODY phBody)
+static HRESULT WINAPI MimeMessage_FindFirst(IMimeMessage *iface, FINDBODY *pFindBody, HBODY *phBody)
{
+ MimeMessage *This = impl_from_IMimeMessage(iface);
+
TRACE("(%p)->(%p, %p)\n", iface, pFindBody, phBody);
pFindBody->dwReserved = 0;
- return find_next( iface, NULL, pFindBody, phBody );
+ return find_next(This, NULL, pFindBody, phBody);
}
-static HRESULT WINAPI MimeMessage_FindNext(
- IMimeMessage *iface,
- LPFINDBODY pFindBody,
- LPHBODY phBody)
+static HRESULT WINAPI MimeMessage_FindNext(IMimeMessage *iface, FINDBODY *pFindBody, HBODY *phBody)
{
- MimeMessage *This = (MimeMessage *)iface;
+ MimeMessage *This = impl_from_IMimeMessage(iface);
body_t *body;
HRESULT hr;
hr = find_body( &This->body_tree, UlongToHandle( pFindBody->dwReserved ), &body );
if (hr != S_OK) return MIME_E_NOT_FOUND;
- return find_next( iface, body, pFindBody, phBody );
+ return find_next(This, body, pFindBody, phBody);
}
static HRESULT WINAPI MimeMessage_ResolveURL(
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;
HRESULT MimeMessage_create(IUnknown *outer, void **obj)
{
MimeMessage *This;
+ MimeBody *mime_body;
+ body_t *root_body;
TRACE("(%p, %p)\n", outer, obj);
This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
if (!This) return E_OUTOFMEMORY;
- This->lpVtbl = &MimeMessageVtbl;
- This->refs = 1;
+ This->IMimeMessage_iface.lpVtbl = &MimeMessageVtbl;
+ This->ref = 1;
This->stream = NULL;
list_init(&This->body_tree);
This->next_index = 1;
- *obj = &This->lpVtbl;
+ 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;
}
typedef struct MimeSecurity
{
- const IMimeSecurityVtbl *lpVtbl;
-
- LONG refs;
+ IMimeSecurity IMimeSecurity_iface;
+ LONG ref;
} MimeSecurity;
-static HRESULT WINAPI MimeSecurity_QueryInterface(
- IMimeSecurity* iface,
- REFIID riid,
- void** obj)
+static inline MimeSecurity *impl_from_IMimeSecurity(IMimeSecurity *iface)
{
- TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), obj);
+ return CONTAINING_RECORD(iface, MimeSecurity, IMimeSecurity_iface);
+}
+
+static HRESULT WINAPI MimeSecurity_QueryInterface(IMimeSecurity *iface, REFIID riid, void **ppv)
+{
+ TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
if (IsEqualIID(riid, &IID_IUnknown) ||
IsEqualIID(riid, &IID_IMimeSecurity))
{
- *obj = iface;
- IUnknown_AddRef(iface);
+ *ppv = iface;
+ IMimeSecurity_AddRef(iface);
return S_OK;
}
FIXME("no interface for %s\n", debugstr_guid(riid));
- *obj = NULL;
+ *ppv = NULL;
return E_NOINTERFACE;
}
-static ULONG WINAPI MimeSecurity_AddRef(
- IMimeSecurity* iface)
+static ULONG WINAPI MimeSecurity_AddRef(IMimeSecurity *iface)
{
- MimeSecurity *This = (MimeSecurity *)iface;
- TRACE("(%p)->()\n", iface);
- return InterlockedIncrement(&This->refs);
+ MimeSecurity *This = impl_from_IMimeSecurity(iface);
+ LONG ref = InterlockedIncrement(&This->ref);
+
+ TRACE("(%p) ref=%d\n", This, ref);
+
+ return ref;
}
-static ULONG WINAPI MimeSecurity_Release(
- IMimeSecurity* iface)
+static ULONG WINAPI MimeSecurity_Release(IMimeSecurity *iface)
{
- MimeSecurity *This = (MimeSecurity *)iface;
- ULONG refs;
+ MimeSecurity *This = impl_from_IMimeSecurity(iface);
+ LONG ref = InterlockedDecrement(&This->ref);
- TRACE("(%p)->()\n", iface);
+ TRACE("(%p) ref=%d\n", This, ref);
- refs = InterlockedDecrement(&This->refs);
- if (!refs)
- {
+ if (!ref)
HeapFree(GetProcessHeap(), 0, This);
- }
- return refs;
+ return ref;
}
static HRESULT WINAPI MimeSecurity_InitNew(
This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
if (!This) return E_OUTOFMEMORY;
- This->lpVtbl = &MimeSecurityVtbl;
- This->refs = 1;
+ This->IMimeSecurity_iface.lpVtbl = &MimeSecurityVtbl;
+ This->ref = 1;
- *obj = &This->lpVtbl;
+ *obj = &This->IMimeSecurity_iface;
return S_OK;
}
return MimeSecurity_create(NULL, (void **)ppSecurity);
}
-typedef struct
-{
- IMimeAllocatorVtbl *lpVtbl;
-} MimeAllocator;
-
static HRESULT WINAPI MimeAlloc_QueryInterface(
IMimeAllocator* iface,
REFIID riid,
IsEqualIID(riid, &IID_IMimeAllocator))
{
*obj = iface;
- IUnknown_AddRef(iface);
+ IMimeAllocator_AddRef(iface);
return S_OK;
}
static LPVOID WINAPI MimeAlloc_Alloc(
IMimeAllocator* iface,
- ULONG cb)
+ SIZE_T cb)
{
return CoTaskMemAlloc(cb);
}
static LPVOID WINAPI MimeAlloc_Realloc(
IMimeAllocator* iface,
LPVOID pv,
- ULONG cb)
+ SIZE_T cb)
{
return CoTaskMemRealloc(pv, cb);
}
CoTaskMemFree(pv);
}
-static ULONG WINAPI MimeAlloc_GetSize(
+static SIZE_T WINAPI MimeAlloc_GetSize(
IMimeAllocator* iface,
LPVOID pv)
{
MimeAlloc_PropVariantClear
};
-static MimeAllocator mime_allocator =
+static IMimeAllocator mime_allocator =
{
&mime_alloc_vtbl
};
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;
+}