/* * MIME OLE Interfaces * * Copyright 2006 Robert Shearman for CodeWeavers * Copyright 2007 Huw Davies for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #define COBJMACROS #define NONAMELESSUNION #include #include #include "windef.h" #include "winbase.h" #include "winuser.h" #include "objbase.h" #include "ole2.h" #include "mimeole.h" #include "propvarutil.h" #include "wine/list.h" #include "wine/debug.h" #include "inetcomm_private.h" WINE_DEFAULT_DEBUG_CHANNEL(inetcomm); typedef struct { LPCSTR name; DWORD id; DWORD flags; /* MIMEPROPFLAGS */ VARTYPE default_vt; } property_t; typedef struct { struct list entry; property_t prop; } property_list_entry_t; static const property_t default_props[] = { {"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 { struct list entry; char *name; char *value; } param_t; typedef struct { struct list entry; const property_t *prop; PROPVARIANT value; struct list params; } header_t; typedef struct MimeBody { IMimeBody IMimeBody_iface; LONG ref; HBODY handle; struct list headers; struct list new_props; /* FIXME: This should be in a PropertySchema */ DWORD next_prop_id; char *content_pri_type; char *content_sub_type; ENCODINGTYPE encoding; void *data; IID data_iid; BODYOFFSETS body_offsets; } MimeBody; typedef struct { 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 HRESULT WINAPI sub_stream_QueryInterface(IStream *iface, REFIID riid, void **ppv) { 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; } static ULONG WINAPI sub_stream_AddRef(IStream *iface) { sub_stream_t *This = impl_from_IStream(iface); LONG ref = InterlockedIncrement(&This->ref); TRACE("(%p) ref=%d\n", This, ref); return ref; } static ULONG WINAPI sub_stream_Release(IStream *iface) { sub_stream_t *This = impl_from_IStream(iface); LONG ref = InterlockedDecrement(&This->ref); TRACE("(%p) ref=%d\n", This, ref); if(!ref) { IStream_Release(This->base); HeapFree(GetProcessHeap(), 0, This); } return ref; } 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; TRACE("(%p, %d, %p)\n", pv, cb, pcbRead); 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; 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) { 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_quotes = !in_quotes; cp++; } if(done_value) add_param(header, start); } static void read_value(header_t *header, char **cur) { char *end = *cur, *value; DWORD len; do { end = strstr(end, "\r\n"); end += 2; } while(*end == ' ' || *end == '\t'); len = end - *cur; value = HeapAlloc(GetProcessHeap(), 0, len + 1); memcpy(value, *cur, len); value[len] = '\0'; unfold_header(value, len); TRACE("value %s\n", debugstr_a(value)); if(header->prop->flags & MPF_HASPARAMS) { split_params(header, value); TRACE("value w/o params %s\n", debugstr_a(value)); } header->value.vt = VT_LPSTR; header->value.u.pszVal = value; *cur = end; } 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) { WARN("malformed context type value\n"); return; } len = slash - header->value.u.pszVal; body->content_pri_type = HeapAlloc(GetProcessHeap(), 0, len + 1); memcpy(body->content_pri_type, header->value.u.pszVal, len); body->content_pri_type[len] = '\0'; body->content_sub_type = strdupA(slash + 1); } static HRESULT parse_headers(MimeBody *body, IStream *stm) { char *header_buf, *cur_header_ptr; HRESULT hr; header_t *header; hr = copy_headers_to_buf(stm, &header_buf); if(FAILED(hr)) return hr; cur_header_ptr = header_buf; while((header = read_prop(body, &cur_header_ptr))) { read_value(header, &cur_header_ptr); list_add_tail(&body->headers, &header->entry); if(header->prop->id == PID_HDR_CNTTYPE) init_content_type(body, header); } HeapFree(GetProcessHeap(), 0, header_buf); return hr; } static void empty_param_list(struct list *list) { param_t *param, *cursor2; LIST_FOR_EACH_ENTRY_SAFE(param, cursor2, list, param_t, entry) { list_remove(¶m->entry); HeapFree(GetProcessHeap(), 0, param->name); HeapFree(GetProcessHeap(), 0, param->value); HeapFree(GetProcessHeap(), 0, param); } } 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); } } static void empty_new_prop_list(struct list *list) { property_list_entry_t *prop, *cursor2; LIST_FOR_EACH_ENTRY_SAFE(prop, cursor2, list, property_list_entry_t, entry) { list_remove(&prop->entry); HeapFree(GetProcessHeap(), 0, (char *)prop->prop.name); HeapFree(GetProcessHeap(), 0, prop); } } static void release_data(REFIID riid, void *data) { if(!data) return; if(IsEqualIID(riid, &IID_IStream)) IStream_Release((IStream *)data); else FIXME("Unhandled data format %s\n", debugstr_guid(riid)); } static HRESULT find_prop(MimeBody *body, const char *name, header_t **prop) { header_t *header; *prop = NULL; LIST_FOR_EACH_ENTRY(header, &body->headers, header_t, entry) { 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) { TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppvObject); *ppvObject = NULL; if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IPersist) || IsEqualIID(riid, &IID_IPersistStreamInit) || IsEqualIID(riid, &IID_IMimePropertySet) || IsEqualIID(riid, &IID_IMimeBody)) { *ppvObject = iface; } if(*ppvObject) { IUnknown_AddRef((IUnknown*)*ppvObject); return S_OK; } FIXME("no interface for %s\n", debugstr_guid(riid)); return E_NOINTERFACE; } static ULONG WINAPI MimeBody_AddRef(IMimeBody *iface) { MimeBody *This = impl_from_IMimeBody(iface); LONG ref = InterlockedIncrement(&This->ref); TRACE("(%p) ref=%d\n", This, ref); return ref; } static ULONG WINAPI MimeBody_Release(IMimeBody *iface) { MimeBody *This = impl_from_IMimeBody(iface); LONG ref = InterlockedDecrement(&This->ref); TRACE("(%p) ref=%d\n", This, ref); if (!ref) { empty_header_list(&This->headers); empty_new_prop_list(&This->new_props); HeapFree(GetProcessHeap(), 0, This->content_pri_type); HeapFree(GetProcessHeap(), 0, This->content_sub_type); release_data(&This->data_iid, This->data); HeapFree(GetProcessHeap(), 0, This); } return ref; } static HRESULT WINAPI MimeBody_GetClassID( IMimeBody* iface, CLSID* pClassID) { MimeBody *This = impl_from_IMimeBody(iface); FIXME("(%p)->(%p) stub\n", This, pClassID); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_IsDirty( IMimeBody* iface) { 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", This, pStm); return parse_headers(This, pStm); } static HRESULT WINAPI MimeBody_Save(IMimeBody *iface, IStream *pStm, BOOL fClearDirty) { MimeBody *This = impl_from_IMimeBody(iface); FIXME("(%p)->(%p, %d)\n", This, pStm, fClearDirty); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_GetSizeMax( IMimeBody* iface, ULARGE_INTEGER* pcbSize) { MimeBody *This = impl_from_IMimeBody(iface); FIXME("(%p)->(%p) stub\n", This, pcbSize); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_InitNew( IMimeBody* iface) { MimeBody *This = impl_from_IMimeBody(iface); TRACE("(%p)->()\n", This); return S_OK; } static HRESULT WINAPI MimeBody_GetPropInfo( IMimeBody* iface, LPCSTR pszName, LPMIMEPROPINFO pInfo) { 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( IMimeBody* iface, LPCSTR pszName, LPCMIMEPROPINFO pInfo) { 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); 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); } 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( IMimeBody* iface, LPCSTR pszName, DWORD dwFlags, LPPROPVARIANT pValue) { MimeBody *This = impl_from_IMimeBody(iface); FIXME("(%p)->(%s, 0x%x, %p) stub\n", This, debugstr_a(pszName), dwFlags, pValue); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_DeleteProp( IMimeBody* iface, LPCSTR pszName) { 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) { list_remove(&cursor->entry); HeapFree(GetProcessHeap(), 0, cursor); return S_OK; } } return MIME_E_NOT_FOUND; } static HRESULT WINAPI MimeBody_CopyProps( IMimeBody* iface, ULONG cNames, LPCSTR* prgszName, IMimePropertySet* pPropertySet) { MimeBody *This = impl_from_IMimeBody(iface); FIXME("(%p)->(%d, %p, %p) stub\n", This, cNames, prgszName, pPropertySet); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_MoveProps( IMimeBody* iface, ULONG cNames, LPCSTR* prgszName, IMimePropertySet* pPropertySet) { MimeBody *This = impl_from_IMimeBody(iface); FIXME("(%p)->(%d, %p, %p) stub\n", This, cNames, prgszName, pPropertySet); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_DeleteExcept( IMimeBody* iface, ULONG cNames, LPCSTR* prgszName) { MimeBody *This = impl_from_IMimeBody(iface); FIXME("(%p)->(%d, %p) stub\n", This, cNames, prgszName); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_QueryProp( IMimeBody* iface, LPCSTR pszName, LPCSTR pszCriteria, boolean fSubString, boolean fCaseSensitive) { 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; } static HRESULT WINAPI MimeBody_GetCharset( IMimeBody* iface, LPHCHARSET phCharset) { MimeBody *This = impl_from_IMimeBody(iface); FIXME("(%p)->(%p) stub\n", This, phCharset); *phCharset = NULL; return S_OK; } static HRESULT WINAPI MimeBody_SetCharset( IMimeBody* iface, HCHARSET hCharset, CSETAPPLYTYPE applytype) { MimeBody *This = impl_from_IMimeBody(iface); FIXME("(%p)->(%p, %d) stub\n", This, hCharset, applytype); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_GetParameters( IMimeBody* iface, LPCSTR pszName, ULONG* pcParams, LPMIMEPARAMINFO* pprgParam) { MimeBody *This = impl_from_IMimeBody(iface); HRESULT hr; header_t *header; TRACE("(%p)->(%s, %p, %p)\n", iface, debugstr_a(pszName), pcParams, pprgParam); *pprgParam = NULL; *pcParams = 0; hr = find_prop(This, pszName, &header); if(hr != S_OK) return hr; *pcParams = list_count(&header->params); if(*pcParams) { IMimeAllocator *alloc; param_t *param; MIMEPARAMINFO *info; MimeOleGetAllocator(&alloc); *pprgParam = info = IMimeAllocator_Alloc(alloc, *pcParams * sizeof(**pprgParam)); LIST_FOR_EACH_ENTRY(param, &header->params, param_t, entry) { int len; len = strlen(param->name) + 1; info->pszName = IMimeAllocator_Alloc(alloc, len); memcpy(info->pszName, param->name, len); len = strlen(param->value) + 1; info->pszData = IMimeAllocator_Alloc(alloc, len); memcpy(info->pszData, param->value, len); info++; } IMimeAllocator_Release(alloc); } 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(lstrcmpiA(pri, pszPriType)) return S_FALSE; } 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_BindToObject( IMimeBody* iface, REFIID riid, void** ppvObject) { MimeBody *This = impl_from_IMimeBody(iface); FIXME("(%p)->(%s, %p) stub\n", This, debugstr_guid(riid), ppvObject); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_Clone( IMimeBody* iface, IMimePropertySet** ppPropertySet) { MimeBody *This = impl_from_IMimeBody(iface); FIXME("(%p)->(%p) stub\n", This, ppPropertySet); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_SetOption( IMimeBody* iface, const TYPEDID oid, LPCPROPVARIANT pValue) { 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_GetOption( IMimeBody* iface, const TYPEDID oid, LPPROPVARIANT pValue) { MimeBody *This = impl_from_IMimeBody(iface); FIXME("(%p)->(%08x, %p): stub\n", This, oid, pValue); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_EnumProps( IMimeBody* iface, DWORD dwFlags, IMimeEnumProperties** ppEnum) { MimeBody *This = impl_from_IMimeBody(iface); FIXME("(%p)->(0x%x, %p) stub\n", This, dwFlags, ppEnum); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_IsType( IMimeBody* iface, IMSGBODYTYPE bodytype) { MimeBody *This = impl_from_IMimeBody(iface); 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; } static HRESULT WINAPI MimeBody_SetDisplayName( IMimeBody* iface, LPCSTR pszDisplay) { MimeBody *This = impl_from_IMimeBody(iface); FIXME("(%p)->(%s) stub\n", This, debugstr_a(pszDisplay)); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_GetDisplayName( IMimeBody* iface, LPSTR* ppszDisplay) { MimeBody *This = impl_from_IMimeBody(iface); FIXME("(%p)->(%p) stub\n", This, ppszDisplay); 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) { MimeBody *This = impl_from_IMimeBody(iface); FIXME("(%p)->(%d, %p) stub\n", This, ietEncoding, pcbSize); return E_NOTIMPL; } 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 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 >= sizeof(base64_decode_table)/sizeof(*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 HRESULT WINAPI MimeBody_GetData( IMimeBody* iface, ENCODINGTYPE ietEncoding, IStream** ppStream) { MimeBody *This = impl_from_IMimeBody(iface); ULARGE_INTEGER start, size; HRESULT hres; TRACE("(%p)->(%d %p)\n", This, ietEncoding, ppStream); if(This->encoding != ietEncoding) { switch(This->encoding) { case IET_BASE64: if(ietEncoding != IET_BINARY) FIXME("Encofing %d is not supported.\n", ietEncoding); return decode_base64(This->data, ppStream); default: FIXME("Decoding %d is not supported.\n", This->encoding); } } 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( 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; IMimeBody_SetCurrentEncoding(iface, ietEncoding); /* FIXME: Update the content type. If pszPriType == NULL use 'application' If pszSubType == NULL use 'octet-stream' */ return S_OK; } static HRESULT WINAPI MimeBody_EmptyData( IMimeBody* iface) { MimeBody *This = impl_from_IMimeBody(iface); FIXME("(%p)->() stub\n", This); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_CopyTo( IMimeBody* iface, IMimeBody* pBody) { MimeBody *This = impl_from_IMimeBody(iface); FIXME("(%p)->(%p) stub\n", This, pBody); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_GetTransmitInfo( IMimeBody* iface, LPTRANSMITINFO pTransmitInfo) { MimeBody *This = impl_from_IMimeBody(iface); FIXME("(%p)->(%p) stub\n", This, pTransmitInfo); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_SaveToFile( IMimeBody* iface, ENCODINGTYPE ietEncoding, LPCSTR pszFilePath) { MimeBody *This = impl_from_IMimeBody(iface); FIXME("(%p)->(%d, %s) stub\n", This, ietEncoding, debugstr_a(pszFilePath)); return E_NOTIMPL; } 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) { 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 static MimeBody *mimebody_create(void) { 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; } 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; MimeBody *mime_body; struct body_t *parent; struct list children; } body_t; typedef struct MimeMessage { 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); if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IPersist) || IsEqualIID(riid, &IID_IPersistStreamInit) || IsEqualIID(riid, &IID_IMimeMessageTree) || IsEqualIID(riid, &IID_IMimeMessage)) { *ppv = iface; IMimeMessage_AddRef(iface); return S_OK; } FIXME("no interface for %s\n", debugstr_guid(riid)); *ppv = NULL; return E_NOINTERFACE; } static ULONG WINAPI MimeMessage_AddRef(IMimeMessage *iface) { 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) { body_t *body, *cursor2; LIST_FOR_EACH_ENTRY_SAFE(body, cursor2, list, body_t, entry) { empty_body_list(&body->children); list_remove(&body->entry); IMimeBody_Release(&body->mime_body->IMimeBody_iface); HeapFree(GetProcessHeap(), 0, body); } } static ULONG WINAPI MimeMessage_Release(IMimeMessage *iface) { MimeMessage *This = impl_from_IMimeMessage(iface); ULONG ref = InterlockedDecrement(&This->ref); TRACE("(%p) ref=%d\n", This, ref); if (!ref) { empty_body_list(&This->body_tree); if(This->stream) IStream_Release(This->stream); HeapFree(GetProcessHeap(), 0, This); } return ref; } /*** IPersist methods ***/ static HRESULT WINAPI MimeMessage_GetClassID( IMimeMessage *iface, CLSID *pClassID) { FIXME("(%p)->(%p)\n", iface, pClassID); return E_NOTIMPL; } /*** IPersistStreamInit methods ***/ static HRESULT WINAPI MimeMessage_IsDirty( IMimeMessage *iface) { FIXME("(%p)->()\n", iface); return E_NOTIMPL; } 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->mime_body = mime_body; body->index = index; list_init(&body->children); body->parent = parent; mime_body->handle = UlongToHandle(body->index); } return body; } typedef struct { struct list entry; BODYOFFSETS offsets; } offset_entry_t; static HRESULT create_body_offset_list(IStream *stm, const char *boundary, struct list *body_offsets) { HRESULT hr; DWORD read; int boundary_len = strlen(boundary); char *buf, *nl_boundary, *ptr, *overlap; DWORD start = 0, overlap_no; offset_entry_t *cur_body = NULL; 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 = buf = HeapAlloc(GetProcessHeap(), 0, overlap_no + PARSER_BUF_SIZE + 1); zero.QuadPart = 0; hr = IStream_Seek(stm, zero, STREAM_SEEK_CUR, &cur); start = cur.u.LowPart; do { hr = IStream_Read(stm, overlap, PARSER_BUF_SIZE, &read); if(FAILED(hr)) goto end; if(read == 0) break; 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') break; if(*end == '\r' && *(end + 1) == '\n') { if(cur_body) { cur_body->offsets.cbBodyEnd = boundary_start; 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; } else if(*end == '-' && *(end + 1) == '-') { if(cur_body) { cur_body->offsets.cbBodyEnd = boundary_start; list_add_tail(body_offsets, &cur_body->entry); goto end; } } ptr = end + 2; } } while(ptr); if(overlap == buf) /* 1st iteration */ { memmove(buf, buf + PARSER_BUF_SIZE - overlap_no, overlap_no); overlap = buf + overlap_no; start += read - overlap_no; } else { 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) { MimeBody *mime_body; HRESULT hr; body_t *body; ULARGE_INTEGER cur; LARGE_INTEGER zero; 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; if (parent) MimeBody_set_offsets(mime_body, offset); IMimeBody_SetData(&mime_body->IMimeBody_iface, IET_BINARY, NULL, NULL, &IID_IStream, pStm); body = new_body_entry(mime_body, msg->next_index++, parent); if(IMimeBody_IsContentType(&mime_body->IMimeBody_iface, "multipart", NULL) == S_OK) { MIMEPARAMINFO *param_info; ULONG count, i; IMimeAllocator *alloc; 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(!lstrcmpiA(param_info[i].pszName, "boundary")) { struct list offset_list; offset_entry_t *cur, *cursor2; hr = create_body_offset_list(pStm, param_info[i].pszData, &offset_list); 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); list_add_tail(&body->children, &sub_body->entry); list_remove(&cur->entry); HeapFree(GetProcessHeap(), 0, cur); } break; } } IMimeAllocator_FreeParamInfoArray(alloc, count, param_info, TRUE); IMimeAllocator_Release(alloc); } return body; } static HRESULT WINAPI MimeMessage_Load(IMimeMessage *iface, IStream *pStm) { MimeMessage *This = impl_from_IMimeMessage(iface); body_t *root_body; BODYOFFSETS offsets; ULARGE_INTEGER cur; LARGE_INTEGER zero; TRACE("(%p)->(%p)\n", iface, pStm); if(This->stream) { FIXME("already loaded a message\n"); return E_FAIL; } empty_body_list(&This->body_tree); IStream_AddRef(pStm); This->stream = pStm; offsets.cbBoundaryStart = offsets.cbHeaderStart = 0; offsets.cbBodyStart = offsets.cbBodyEnd = 0; root_body = create_sub_body(This, pStm, &offsets, NULL); zero.QuadPart = 0; IStream_Seek(pStm, zero, STREAM_SEEK_END, &cur); offsets.cbBodyEnd = cur.u.LowPart; 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, IStream *pStm, BOOL fClearDirty) { FIXME("(%p)->(%p, %s)\n", iface, pStm, fClearDirty ? "TRUE" : "FALSE"); return E_NOTIMPL; } static HRESULT WINAPI MimeMessage_GetSizeMax( IMimeMessage *iface, ULARGE_INTEGER *pcbSize) { FIXME("(%p)->(%p)\n", iface, pcbSize); return E_NOTIMPL; } static HRESULT WINAPI MimeMessage_InitNew( IMimeMessage *iface) { FIXME("(%p)->()\n", iface); return E_NOTIMPL; } /*** IMimeMessageTree methods ***/ static HRESULT WINAPI MimeMessage_GetMessageSource(IMimeMessage *iface, IStream **ppStream, DWORD dwFlags) { MimeMessage *This = impl_from_IMimeMessage(iface); FIXME("(%p)->(%p, 0x%x)\n", iface, ppStream, dwFlags); IStream_AddRef(This->stream); *ppStream = This->stream; return S_OK; } static HRESULT WINAPI MimeMessage_GetMessageSize( IMimeMessage *iface, ULONG *pcbSize, DWORD dwFlags) { FIXME("(%p)->(%p, 0x%x)\n", iface, pcbSize, dwFlags); return E_NOTIMPL; } static HRESULT WINAPI MimeMessage_LoadOffsetTable( IMimeMessage *iface, IStream *pStream) { FIXME("(%p)->(%p)\n", iface, pStream); return E_NOTIMPL; } static HRESULT WINAPI MimeMessage_SaveOffsetTable( IMimeMessage *iface, IStream *pStream, DWORD dwFlags) { FIXME("(%p)->(%p, 0x%x)\n", iface, pStream, dwFlags); return E_NOTIMPL; } static HRESULT WINAPI MimeMessage_GetFlags( IMimeMessage *iface, DWORD *pdwFlags) { FIXME("(%p)->(%p)\n", iface, pdwFlags); return E_NOTIMPL; } static HRESULT WINAPI MimeMessage_Commit( IMimeMessage *iface, DWORD dwFlags) { FIXME("(%p)->(0x%x)\n", iface, dwFlags); return S_OK; } static HRESULT WINAPI MimeMessage_HandsOffStorage( IMimeMessage *iface) { FIXME("(%p)->()\n", iface); return E_NOTIMPL; } static HRESULT find_body(struct list *list, HBODY hbody, body_t **body) { body_t *cur; HRESULT hr; if(hbody == HBODY_ROOT) { *body = LIST_ENTRY(list_head(list), body_t, entry); return S_OK; } LIST_FOR_EACH_ENTRY(cur, list, body_t, entry) { if(cur->index == HandleToUlong(hbody)) { *body = cur; return S_OK; } hr = find_body(&cur->children, hbody, body); if(hr == S_OK) return S_OK; } return S_FALSE; } static HRESULT WINAPI MimeMessage_BindToObject(IMimeMessage *iface, const HBODY hBody, REFIID riid, void **ppvObject) { MimeMessage *This = impl_from_IMimeMessage(iface); HRESULT hr; body_t *body; TRACE("(%p)->(%p, %s, %p)\n", iface, hBody, debugstr_guid(riid), ppvObject); hr = find_body(&This->body_tree, hBody, &body); if(hr != S_OK) return hr; if(IsEqualIID(riid, &IID_IMimeBody)) { IMimeBody_AddRef(&body->mime_body->IMimeBody_iface); *ppvObject = &body->mime_body->IMimeBody_iface; return S_OK; } return E_NOINTERFACE; } static HRESULT WINAPI MimeMessage_SaveBody( IMimeMessage *iface, HBODY hBody, DWORD dwFlags, IStream *pStream) { FIXME("(%p)->(%p, 0x%x, %p)\n", iface, hBody, dwFlags, pStream); return E_NOTIMPL; } static HRESULT get_body(MimeMessage *msg, BODYLOCATION location, HBODY pivot, body_t **out) { body_t *root = LIST_ENTRY(list_head(&msg->body_tree), body_t, entry); body_t *body; HRESULT hr; struct list *list; if(location == IBL_ROOT) { *out = root; return S_OK; } hr = find_body(&msg->body_tree, pivot, &body); if(hr == S_OK) { switch(location) { case IBL_PARENT: if(body->parent) *out = body->parent; else hr = MIME_E_NOT_FOUND; break; case IBL_FIRST: list = list_head(&body->children); if(list) *out = LIST_ENTRY(list, body_t, entry); else hr = MIME_E_NOT_FOUND; break; case IBL_LAST: list = list_tail(&body->children); if(list) *out = LIST_ENTRY(list, body_t, entry); else hr = MIME_E_NOT_FOUND; break; case IBL_NEXT: list = list_next(&body->parent->children, &body->entry); if(list) *out = LIST_ENTRY(list, body_t, entry); else hr = MIME_E_NOT_FOUND; break; case IBL_PREVIOUS: list = list_prev(&body->parent->children, &body->entry); if(list) *out = LIST_ENTRY(list, body_t, entry); else hr = MIME_E_NOT_FOUND; break; default: hr = E_FAIL; break; } } return hr; } static HRESULT WINAPI MimeMessage_InsertBody( IMimeMessage *iface, BODYLOCATION location, HBODY hPivot, LPHBODY phBody) { FIXME("(%p)->(%d, %p, %p)\n", iface, location, hPivot, phBody); return E_NOTIMPL; } static HRESULT WINAPI MimeMessage_GetBody(IMimeMessage *iface, BODYLOCATION location, HBODY hPivot, HBODY *phBody) { 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); return hr; } static HRESULT WINAPI MimeMessage_DeleteBody( IMimeMessage *iface, HBODY hBody, DWORD dwFlags) { FIXME("(%p)->(%p, %08x)\n", iface, hBody, dwFlags); return E_NOTIMPL; } static HRESULT WINAPI MimeMessage_MoveBody( IMimeMessage *iface, HBODY hBody, BODYLOCATION location) { FIXME("(%p)->(%d)\n", iface, location); return E_NOTIMPL; } static void count_children(body_t *body, boolean recurse, ULONG *count) { body_t *child; LIST_FOR_EACH_ENTRY(child, &body->children, body_t, entry) { (*count)++; if(recurse) count_children(child, recurse, count); } } static HRESULT WINAPI MimeMessage_CountBodies(IMimeMessage *iface, HBODY hParent, boolean fRecurse, ULONG *pcBodies) { HRESULT hr; MimeMessage *This = impl_from_IMimeMessage(iface); body_t *body; TRACE("(%p)->(%p, %s, %p)\n", iface, hParent, fRecurse ? "TRUE" : "FALSE", pcBodies); hr = find_body(&This->body_tree, hParent, &body); if(hr != S_OK) return hr; *pcBodies = 1; count_children(body, fRecurse, pcBodies); return S_OK; } static HRESULT find_next(MimeMessage *This, body_t *body, FINDBODY *find, HBODY *out) { struct list *ptr; HBODY next; for (;;) { if (!body) ptr = list_head( &This->body_tree ); else { ptr = list_head( &body->children ); while (!ptr) { if (!body->parent) return MIME_E_NOT_FOUND; if (!(ptr = list_next( &body->parent->children, &body->entry ))) body = body->parent; } } body = LIST_ENTRY( ptr, body_t, entry ); next = UlongToHandle( body->index ); find->dwReserved = body->index; 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, FINDBODY *pFindBody, HBODY *phBody) { MimeMessage *This = impl_from_IMimeMessage(iface); TRACE("(%p)->(%p, %p)\n", iface, pFindBody, phBody); pFindBody->dwReserved = 0; return find_next(This, NULL, pFindBody, phBody); } static HRESULT WINAPI MimeMessage_FindNext(IMimeMessage *iface, FINDBODY *pFindBody, HBODY *phBody) { MimeMessage *This = impl_from_IMimeMessage(iface); body_t *body; HRESULT hr; TRACE("(%p)->(%p, %p)\n", iface, pFindBody, phBody); hr = find_body( &This->body_tree, UlongToHandle( pFindBody->dwReserved ), &body ); if (hr != S_OK) return MIME_E_NOT_FOUND; return find_next(This, body, pFindBody, phBody); } static HRESULT WINAPI MimeMessage_ResolveURL( IMimeMessage *iface, HBODY hRelated, LPCSTR pszBase, LPCSTR pszURL, DWORD dwFlags, LPHBODY phBody) { FIXME("(%p)->(%p, %s, %s, 0x%x, %p)\n", iface, hRelated, pszBase, pszURL, dwFlags, phBody); return E_NOTIMPL; } static HRESULT WINAPI MimeMessage_ToMultipart( IMimeMessage *iface, HBODY hBody, LPCSTR pszSubType, LPHBODY phMultipart) { FIXME("(%p)->(%p, %s, %p)\n", iface, hBody, pszSubType, phMultipart); return E_NOTIMPL; } static HRESULT WINAPI MimeMessage_GetBodyOffsets( IMimeMessage *iface, HBODY hBody, LPBODYOFFSETS pOffsets) { FIXME("(%p)->(%p, %p)\n", iface, hBody, pOffsets); return E_NOTIMPL; } static HRESULT WINAPI MimeMessage_GetCharset( IMimeMessage *iface, LPHCHARSET phCharset) { FIXME("(%p)->(%p)\n", iface, phCharset); *phCharset = NULL; return S_OK; } static HRESULT WINAPI MimeMessage_SetCharset( IMimeMessage *iface, HCHARSET hCharset, CSETAPPLYTYPE applytype) { FIXME("(%p)->(%p, %d)\n", iface, hCharset, applytype); return E_NOTIMPL; } static HRESULT WINAPI MimeMessage_IsBodyType( IMimeMessage *iface, HBODY hBody, IMSGBODYTYPE bodytype) { HRESULT hr; IMimeBody *mime_body; TRACE("(%p)->(%p, %d)\n", iface, hBody, bodytype); hr = IMimeMessage_BindToObject(iface, hBody, &IID_IMimeBody, (void**)&mime_body); if(hr != S_OK) return hr; hr = IMimeBody_IsType(mime_body, bodytype); MimeBody_Release(mime_body); return hr; } static HRESULT WINAPI MimeMessage_IsContentType( IMimeMessage *iface, HBODY hBody, LPCSTR pszPriType, LPCSTR pszSubType) { HRESULT hr; IMimeBody *mime_body; TRACE("(%p)->(%p, %s, %s)\n", iface, hBody, debugstr_a(pszPriType), debugstr_a(pszSubType)); hr = IMimeMessage_BindToObject(iface, hBody, &IID_IMimeBody, (void**)&mime_body); if(FAILED(hr)) return hr; hr = IMimeBody_IsContentType(mime_body, pszPriType, pszSubType); IMimeBody_Release(mime_body); return hr; } static HRESULT WINAPI MimeMessage_QueryBodyProp( IMimeMessage *iface, HBODY hBody, LPCSTR pszName, LPCSTR pszCriteria, boolean fSubString, boolean fCaseSensitive) { FIXME("(%p)->(%p, %s, %s, %s, %s)\n", iface, hBody, pszName, pszCriteria, fSubString ? "TRUE" : "FALSE", fCaseSensitive ? "TRUE" : "FALSE"); return E_NOTIMPL; } static HRESULT WINAPI MimeMessage_GetBodyProp( IMimeMessage *iface, HBODY hBody, LPCSTR pszName, DWORD dwFlags, LPPROPVARIANT pValue) { HRESULT hr; IMimeBody *mime_body; TRACE("(%p)->(%p, %s, 0x%x, %p)\n", iface, hBody, pszName, dwFlags, pValue); hr = IMimeMessage_BindToObject(iface, hBody, &IID_IMimeBody, (void**)&mime_body); if(hr != S_OK) return hr; hr = IMimeBody_GetProp(mime_body, pszName, dwFlags, pValue); IMimeBody_Release(mime_body); return hr; } static HRESULT WINAPI MimeMessage_SetBodyProp( IMimeMessage *iface, HBODY hBody, LPCSTR pszName, DWORD dwFlags, LPCPROPVARIANT pValue) { FIXME("(%p)->(%p, %s, 0x%x, %p)\n", iface, hBody, pszName, dwFlags, pValue); return E_NOTIMPL; } static HRESULT WINAPI MimeMessage_DeleteBodyProp( IMimeMessage *iface, HBODY hBody, LPCSTR pszName) { FIXME("(%p)->(%p, %s)\n", iface, hBody, pszName); return E_NOTIMPL; } static HRESULT WINAPI MimeMessage_SetOption( IMimeMessage *iface, const TYPEDID oid, LPCPROPVARIANT pValue) { 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 S_OK; } switch(oid) { case OID_HIDE_TNEF_ATTACHMENTS: FIXME("OID_HIDE_TNEF_ATTACHMENTS (value %d): ignoring\n", pValue->u.boolVal); break; case OID_SHOW_MACBINARY: FIXME("OID_SHOW_MACBINARY (value %d): ignoring\n", pValue->u.boolVal); 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; } static HRESULT WINAPI MimeMessage_GetOption( IMimeMessage *iface, const TYPEDID oid, LPPROPVARIANT pValue) { FIXME("(%p)->(%08x, %p)\n", iface, oid, pValue); return E_NOTIMPL; } /*** IMimeMessage methods ***/ static HRESULT WINAPI MimeMessage_CreateWebPage( IMimeMessage *iface, IStream *pRootStm, LPWEBPAGEOPTIONS pOptions, IMimeMessageCallback *pCallback, IMoniker **ppMoniker) { FIXME("(%p)->(%p, %p, %p, %p)\n", iface, pRootStm, pOptions, pCallback, ppMoniker); *ppMoniker = NULL; return E_NOTIMPL; } static HRESULT WINAPI MimeMessage_GetProp( IMimeMessage *iface, LPCSTR pszName, DWORD dwFlags, LPPROPVARIANT pValue) { FIXME("(%p)->(%s, 0x%x, %p)\n", iface, pszName, dwFlags, pValue); return E_NOTIMPL; } static HRESULT WINAPI MimeMessage_SetProp( IMimeMessage *iface, LPCSTR pszName, DWORD dwFlags, LPCPROPVARIANT pValue) { FIXME("(%p)->(%s, 0x%x, %p)\n", iface, pszName, dwFlags, pValue); return E_NOTIMPL; } static HRESULT WINAPI MimeMessage_DeleteProp( IMimeMessage *iface, LPCSTR pszName) { FIXME("(%p)->(%s)\n", iface, pszName); return E_NOTIMPL; } static HRESULT WINAPI MimeMessage_QueryProp( IMimeMessage *iface, LPCSTR pszName, LPCSTR pszCriteria, boolean fSubString, boolean fCaseSensitive) { FIXME("(%p)->(%s, %s, %s, %s)\n", iface, pszName, pszCriteria, fSubString ? "TRUE" : "FALSE", fCaseSensitive ? "TRUE" : "FALSE"); return E_NOTIMPL; } static HRESULT WINAPI MimeMessage_GetTextBody( IMimeMessage *iface, DWORD dwTxtType, ENCODINGTYPE ietEncoding, IStream **pStream, LPHBODY phBody) { HRESULT hr; HBODY hbody; FINDBODY find_struct; IMimeBody *mime_body; static char text[] = "text"; static char plain[] = "plain"; static char html[] = "html"; TRACE("(%p)->(%d, %d, %p, %p)\n", iface, dwTxtType, ietEncoding, pStream, phBody); find_struct.pszPriType = text; switch(dwTxtType) { case TXT_PLAIN: find_struct.pszSubType = plain; break; case TXT_HTML: find_struct.pszSubType = html; break; default: return MIME_E_INVALID_TEXT_TYPE; } hr = IMimeMessage_FindFirst(iface, &find_struct, &hbody); if(hr != S_OK) { TRACE("not found hr %08x\n", hr); *phBody = NULL; return hr; } IMimeMessage_BindToObject(iface, hbody, &IID_IMimeBody, (void**)&mime_body); IMimeBody_GetData(mime_body, ietEncoding, pStream); *phBody = hbody; IMimeBody_Release(mime_body); return hr; } static HRESULT WINAPI MimeMessage_SetTextBody( IMimeMessage *iface, DWORD dwTxtType, ENCODINGTYPE ietEncoding, HBODY hAlternative, IStream *pStream, LPHBODY phBody) { FIXME("(%p)->(%d, %d, %p, %p, %p)\n", iface, dwTxtType, ietEncoding, hAlternative, pStream, phBody); return E_NOTIMPL; } static HRESULT WINAPI MimeMessage_AttachObject( IMimeMessage *iface, REFIID riid, void *pvObject, LPHBODY phBody) { FIXME("(%p)->(%s, %p, %p)\n", iface, debugstr_guid(riid), pvObject, phBody); return E_NOTIMPL; } static HRESULT WINAPI MimeMessage_AttachFile( IMimeMessage *iface, LPCSTR pszFilePath, IStream *pstmFile, LPHBODY phBody) { FIXME("(%p)->(%s, %p, %p)\n", iface, pszFilePath, pstmFile, phBody); return E_NOTIMPL; } static HRESULT WINAPI MimeMessage_AttachURL( IMimeMessage *iface, LPCSTR pszBase, LPCSTR pszURL, DWORD dwFlags, IStream *pstmURL, LPSTR *ppszCIDURL, LPHBODY phBody) { FIXME("(%p)->(%s, %s, 0x%x, %p, %p, %p)\n", iface, pszBase, pszURL, dwFlags, pstmURL, ppszCIDURL, phBody); return E_NOTIMPL; } static HRESULT WINAPI MimeMessage_GetAttachments( IMimeMessage *iface, ULONG *pcAttach, LPHBODY *pprghAttach) { HRESULT hr; FINDBODY find_struct; HBODY hbody; LPHBODY array; ULONG size = 10; TRACE("(%p)->(%p, %p)\n", iface, pcAttach, pprghAttach); *pcAttach = 0; array = CoTaskMemAlloc(size * sizeof(HBODY)); find_struct.pszPriType = find_struct.pszSubType = NULL; hr = IMimeMessage_FindFirst(iface, &find_struct, &hbody); while(hr == S_OK) { hr = IMimeMessage_IsContentType(iface, hbody, "multipart", NULL); TRACE("IsCT rets %08x %d\n", hr, *pcAttach); if(hr != S_OK) { if(*pcAttach + 1 > size) { size *= 2; array = CoTaskMemRealloc(array, size * sizeof(HBODY)); } array[*pcAttach] = hbody; (*pcAttach)++; } hr = IMimeMessage_FindNext(iface, &find_struct, &hbody); } *pprghAttach = array; return S_OK; } static HRESULT WINAPI MimeMessage_GetAddressTable( IMimeMessage *iface, IMimeAddressTable **ppTable) { FIXME("(%p)->(%p)\n", iface, ppTable); return E_NOTIMPL; } static HRESULT WINAPI MimeMessage_GetSender( IMimeMessage *iface, LPADDRESSPROPS pAddress) { FIXME("(%p)->(%p)\n", iface, pAddress); return E_NOTIMPL; } static HRESULT WINAPI MimeMessage_GetAddressTypes( IMimeMessage *iface, DWORD dwAdrTypes, DWORD dwProps, LPADDRESSLIST pList) { FIXME("(%p)->(%d, %d, %p)\n", iface, dwAdrTypes, dwProps, pList); return E_NOTIMPL; } static HRESULT WINAPI MimeMessage_GetAddressFormat( IMimeMessage *iface, DWORD dwAdrTypes, ADDRESSFORMAT format, LPSTR *ppszFormat) { FIXME("(%p)->(%d, %d, %p)\n", iface, dwAdrTypes, format, ppszFormat); return E_NOTIMPL; } static HRESULT WINAPI MimeMessage_EnumAddressTypes( IMimeMessage *iface, DWORD dwAdrTypes, DWORD dwProps, IMimeEnumAddressTypes **ppEnum) { FIXME("(%p)->(%d, %d, %p)\n", iface, dwAdrTypes, dwProps, ppEnum); return E_NOTIMPL; } static HRESULT WINAPI MimeMessage_SplitMessage( IMimeMessage *iface, ULONG cbMaxPart, IMimeMessageParts **ppParts) { FIXME("(%p)->(%d, %p)\n", iface, cbMaxPart, ppParts); return E_NOTIMPL; } static HRESULT WINAPI MimeMessage_GetRootMoniker( IMimeMessage *iface, IMoniker **ppMoniker) { FIXME("(%p)->(%p)\n", iface, ppMoniker); return E_NOTIMPL; } static const IMimeMessageVtbl MimeMessageVtbl = { MimeMessage_QueryInterface, MimeMessage_AddRef, MimeMessage_Release, MimeMessage_GetClassID, MimeMessage_IsDirty, MimeMessage_Load, MimeMessage_Save, MimeMessage_GetSizeMax, MimeMessage_InitNew, MimeMessage_GetMessageSource, MimeMessage_GetMessageSize, MimeMessage_LoadOffsetTable, MimeMessage_SaveOffsetTable, MimeMessage_GetFlags, MimeMessage_Commit, MimeMessage_HandsOffStorage, MimeMessage_BindToObject, MimeMessage_SaveBody, MimeMessage_InsertBody, MimeMessage_GetBody, MimeMessage_DeleteBody, MimeMessage_MoveBody, MimeMessage_CountBodies, MimeMessage_FindFirst, MimeMessage_FindNext, MimeMessage_ResolveURL, MimeMessage_ToMultipart, MimeMessage_GetBodyOffsets, MimeMessage_GetCharset, MimeMessage_SetCharset, MimeMessage_IsBodyType, MimeMessage_IsContentType, MimeMessage_QueryBodyProp, MimeMessage_GetBodyProp, MimeMessage_SetBodyProp, MimeMessage_DeleteBodyProp, MimeMessage_SetOption, MimeMessage_GetOption, MimeMessage_CreateWebPage, MimeMessage_GetProp, MimeMessage_SetProp, MimeMessage_DeleteProp, MimeMessage_QueryProp, MimeMessage_GetTextBody, MimeMessage_SetTextBody, MimeMessage_AttachObject, MimeMessage_AttachFile, MimeMessage_AttachURL, MimeMessage_GetAttachments, MimeMessage_GetAddressTable, MimeMessage_GetSender, MimeMessage_GetAddressTypes, MimeMessage_GetAddressFormat, MimeMessage_EnumAddressTypes, MimeMessage_SplitMessage, MimeMessage_GetRootMoniker, }; HRESULT MimeMessage_create(IUnknown *outer, void **obj) { MimeMessage *This; MimeBody *mime_body; body_t *root_body; TRACE("(%p, %p)\n", outer, obj); if (outer) { FIXME("outer unknown not supported yet\n"); return E_NOTIMPL; } *obj = NULL; This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); if (!This) return E_OUTOFMEMORY; This->IMimeMessage_iface.lpVtbl = &MimeMessageVtbl; This->ref = 1; This->stream = NULL; 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; } /*********************************************************************** * MimeOleCreateMessage (INETCOMM.@) */ HRESULT WINAPI MimeOleCreateMessage(IUnknown *pUnkOuter, IMimeMessage **ppMessage) { TRACE("(%p, %p)\n", pUnkOuter, ppMessage); return MimeMessage_create(NULL, (void **)ppMessage); } /*********************************************************************** * MimeOleSetCompatMode (INETCOMM.@) */ HRESULT WINAPI MimeOleSetCompatMode(DWORD dwMode) { FIXME("(0x%x)\n", dwMode); return S_OK; } /*********************************************************************** * MimeOleCreateVirtualStream (INETCOMM.@) */ HRESULT WINAPI MimeOleCreateVirtualStream(IStream **ppStream) { HRESULT hr; FIXME("(%p)\n", ppStream); hr = CreateStreamOnHGlobal(NULL, TRUE, ppStream); return hr; } typedef struct MimeSecurity { IMimeSecurity IMimeSecurity_iface; LONG ref; } MimeSecurity; static inline MimeSecurity *impl_from_IMimeSecurity(IMimeSecurity *iface) { 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)) { *ppv = iface; IMimeSecurity_AddRef(iface); return S_OK; } FIXME("no interface for %s\n", debugstr_guid(riid)); *ppv = NULL; return E_NOINTERFACE; } static ULONG WINAPI MimeSecurity_AddRef(IMimeSecurity *iface) { 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) { MimeSecurity *This = impl_from_IMimeSecurity(iface); LONG ref = InterlockedDecrement(&This->ref); TRACE("(%p) ref=%d\n", This, ref); if (!ref) HeapFree(GetProcessHeap(), 0, This); return ref; } static HRESULT WINAPI MimeSecurity_InitNew( IMimeSecurity* iface) { FIXME("(%p)->(): stub\n", iface); return S_OK; } static HRESULT WINAPI MimeSecurity_CheckInit( IMimeSecurity* iface) { FIXME("(%p)->(): stub\n", iface); return E_NOTIMPL; } static HRESULT WINAPI MimeSecurity_EncodeMessage( IMimeSecurity* iface, IMimeMessageTree* pTree, DWORD dwFlags) { FIXME("(%p)->(%p, %08x): stub\n", iface, pTree, dwFlags); return E_NOTIMPL; } static HRESULT WINAPI MimeSecurity_EncodeBody( IMimeSecurity* iface, IMimeMessageTree* pTree, HBODY hEncodeRoot, DWORD dwFlags) { FIXME("(%p)->(%p, %p, %08x): stub\n", iface, pTree, hEncodeRoot, dwFlags); return E_NOTIMPL; } static HRESULT WINAPI MimeSecurity_DecodeMessage( IMimeSecurity* iface, IMimeMessageTree* pTree, DWORD dwFlags) { FIXME("(%p)->(%p, %08x): stub\n", iface, pTree, dwFlags); return E_NOTIMPL; } static HRESULT WINAPI MimeSecurity_DecodeBody( IMimeSecurity* iface, IMimeMessageTree* pTree, HBODY hDecodeRoot, DWORD dwFlags) { FIXME("(%p)->(%p, %p, %08x): stub\n", iface, pTree, hDecodeRoot, dwFlags); return E_NOTIMPL; } static HRESULT WINAPI MimeSecurity_EnumCertificates( IMimeSecurity* iface, HCAPICERTSTORE hc, DWORD dwUsage, PCX509CERT pPrev, PCX509CERT* ppCert) { FIXME("(%p)->(%p, %08x, %p, %p): stub\n", iface, hc, dwUsage, pPrev, ppCert); return E_NOTIMPL; } static HRESULT WINAPI MimeSecurity_GetCertificateName( IMimeSecurity* iface, const PCX509CERT pX509Cert, const CERTNAMETYPE cn, LPSTR* ppszName) { FIXME("(%p)->(%p, %08x, %p): stub\n", iface, pX509Cert, cn, ppszName); return E_NOTIMPL; } static HRESULT WINAPI MimeSecurity_GetMessageType( IMimeSecurity* iface, const HWND hwndParent, IMimeBody* pBody, DWORD* pdwSecType) { FIXME("(%p)->(%p, %p, %p): stub\n", iface, hwndParent, pBody, pdwSecType); return E_NOTIMPL; } static HRESULT WINAPI MimeSecurity_GetCertData( IMimeSecurity* iface, const PCX509CERT pX509Cert, const CERTDATAID dataid, LPPROPVARIANT pValue) { FIXME("(%p)->(%p, %x, %p): stub\n", iface, pX509Cert, dataid, pValue); return E_NOTIMPL; } static const IMimeSecurityVtbl MimeSecurityVtbl = { MimeSecurity_QueryInterface, MimeSecurity_AddRef, MimeSecurity_Release, MimeSecurity_InitNew, MimeSecurity_CheckInit, MimeSecurity_EncodeMessage, MimeSecurity_EncodeBody, MimeSecurity_DecodeMessage, MimeSecurity_DecodeBody, MimeSecurity_EnumCertificates, MimeSecurity_GetCertificateName, MimeSecurity_GetMessageType, MimeSecurity_GetCertData }; HRESULT MimeSecurity_create(IUnknown *outer, void **obj) { MimeSecurity *This; *obj = NULL; if (outer) return CLASS_E_NOAGGREGATION; This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); if (!This) return E_OUTOFMEMORY; This->IMimeSecurity_iface.lpVtbl = &MimeSecurityVtbl; This->ref = 1; *obj = &This->IMimeSecurity_iface; return S_OK; } /*********************************************************************** * MimeOleCreateSecurity (INETCOMM.@) */ HRESULT WINAPI MimeOleCreateSecurity(IMimeSecurity **ppSecurity) { return MimeSecurity_create(NULL, (void **)ppSecurity); } static HRESULT WINAPI MimeAlloc_QueryInterface( IMimeAllocator* iface, REFIID riid, void **obj) { TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), obj); if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IMalloc) || IsEqualIID(riid, &IID_IMimeAllocator)) { *obj = iface; IMimeAllocator_AddRef(iface); return S_OK; } FIXME("no interface for %s\n", debugstr_guid(riid)); *obj = NULL; return E_NOINTERFACE; } static ULONG WINAPI MimeAlloc_AddRef( IMimeAllocator* iface) { return 2; } static ULONG WINAPI MimeAlloc_Release( IMimeAllocator* iface) { return 1; } static LPVOID WINAPI MimeAlloc_Alloc( IMimeAllocator* iface, SIZE_T cb) { return CoTaskMemAlloc(cb); } static LPVOID WINAPI MimeAlloc_Realloc( IMimeAllocator* iface, LPVOID pv, SIZE_T cb) { return CoTaskMemRealloc(pv, cb); } static void WINAPI MimeAlloc_Free( IMimeAllocator* iface, LPVOID pv) { CoTaskMemFree(pv); } static SIZE_T WINAPI MimeAlloc_GetSize( IMimeAllocator* iface, LPVOID pv) { FIXME("stub\n"); return 0; } static int WINAPI MimeAlloc_DidAlloc( IMimeAllocator* iface, LPVOID pv) { FIXME("stub\n"); return 0; } static void WINAPI MimeAlloc_HeapMinimize( IMimeAllocator* iface) { FIXME("stub\n"); return; } static HRESULT WINAPI MimeAlloc_FreeParamInfoArray( IMimeAllocator* iface, ULONG cParams, LPMIMEPARAMINFO prgParam, boolean fFreeArray) { ULONG i; TRACE("(%p)->(%d, %p, %d)\n", iface, cParams, prgParam, fFreeArray); for(i = 0; i < cParams; i++) { IMimeAllocator_Free(iface, prgParam[i].pszName); IMimeAllocator_Free(iface, prgParam[i].pszData); } if(fFreeArray) IMimeAllocator_Free(iface, prgParam); return S_OK; } static HRESULT WINAPI MimeAlloc_FreeAddressList( IMimeAllocator* iface, LPADDRESSLIST pList) { FIXME("stub\n"); return E_NOTIMPL; } static HRESULT WINAPI MimeAlloc_FreeAddressProps( IMimeAllocator* iface, LPADDRESSPROPS pAddress) { FIXME("stub\n"); return E_NOTIMPL; } static HRESULT WINAPI MimeAlloc_ReleaseObjects( IMimeAllocator* iface, ULONG cObjects, IUnknown **prgpUnknown, boolean fFreeArray) { FIXME("stub\n"); return E_NOTIMPL; } static HRESULT WINAPI MimeAlloc_FreeEnumHeaderRowArray( IMimeAllocator* iface, ULONG cRows, LPENUMHEADERROW prgRow, boolean fFreeArray) { FIXME("stub\n"); return E_NOTIMPL; } static HRESULT WINAPI MimeAlloc_FreeEnumPropertyArray( IMimeAllocator* iface, ULONG cProps, LPENUMPROPERTY prgProp, boolean fFreeArray) { FIXME("stub\n"); return E_NOTIMPL; } static HRESULT WINAPI MimeAlloc_FreeThumbprint( IMimeAllocator* iface, THUMBBLOB *pthumbprint) { FIXME("stub\n"); return E_NOTIMPL; } static HRESULT WINAPI MimeAlloc_PropVariantClear( IMimeAllocator* iface, LPPROPVARIANT pProp) { FIXME("stub\n"); return E_NOTIMPL; } static IMimeAllocatorVtbl mime_alloc_vtbl = { MimeAlloc_QueryInterface, MimeAlloc_AddRef, MimeAlloc_Release, MimeAlloc_Alloc, MimeAlloc_Realloc, MimeAlloc_Free, MimeAlloc_GetSize, MimeAlloc_DidAlloc, MimeAlloc_HeapMinimize, MimeAlloc_FreeParamInfoArray, MimeAlloc_FreeAddressList, MimeAlloc_FreeAddressProps, MimeAlloc_ReleaseObjects, MimeAlloc_FreeEnumHeaderRowArray, MimeAlloc_FreeEnumPropertyArray, MimeAlloc_FreeThumbprint, MimeAlloc_PropVariantClear }; static IMimeAllocator mime_allocator = { &mime_alloc_vtbl }; HRESULT MimeAllocator_create(IUnknown *outer, void **obj) { if(outer) return CLASS_E_NOAGGREGATION; *obj = &mime_allocator; return S_OK; } HRESULT WINAPI MimeOleGetAllocator(IMimeAllocator **alloc) { return MimeAllocator_create(NULL, (void**)alloc); } HRESULT VirtualStream_create(IUnknown *outer, void **obj) { FIXME("(%p, %p)\n", outer, obj); *obj = NULL; if (outer) return CLASS_E_NOAGGREGATION; 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; } HRESULT WINAPI MimeOleObjectFromMoniker(BINDF bindf, IMoniker *moniker, IBindCtx *binding, REFIID riid, void **out, IMoniker **moniker_new) { FIXME("(0x%08x, %p, %p, %s, %p, %p) stub\n", bindf, moniker, binding, debugstr_guid(riid), out, moniker_new); return E_NOTIMPL; }