/* * 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 "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[] = { {"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, 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} }; 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 { const IMimeBodyVtbl *lpVtbl; LONG refs; 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; static inline MimeBody *impl_from_IMimeBody( IMimeBody *iface ) { return (MimeBody *)((char*)iface - FIELD_OFFSET(MimeBody, lpVtbl)); } 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; int done = 0; *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 = 1; 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); *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(!strcasecmp(*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(!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); } } 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) { int quoted = 0; char *ret, *cp; while(*str == ' ' || *str == '\t') str++; 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; } 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; int in_quote = 0; int done_value = 0; while(*cp) { if(!in_quote && *cp == ';') { *cp = '\0'; if(done_value) add_param(header, start); done_value = 1; start = cp + 1; } else if(*cp == '"') in_quote = !in_quote; 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(!strcasecmp(name, header->prop->name)) { *prop = header; return S_OK; } } return MIME_E_NOT_FOUND; } 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); TRACE("(%p)->()\n", iface); return InterlockedIncrement(&This->refs); } static ULONG WINAPI MimeBody_Release(IMimeBody* iface) { MimeBody *This = impl_from_IMimeBody(iface); ULONG refs; TRACE("(%p)->()\n", iface); refs = InterlockedDecrement(&This->refs); if (!refs) { 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 refs; } static HRESULT WINAPI MimeBody_GetClassID( IMimeBody* iface, CLSID* pClassID) { FIXME("stub\n"); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_IsDirty( IMimeBody* iface) { FIXME("stub\n"); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_Load( IMimeBody* iface, LPSTREAM pStm) { MimeBody *This = impl_from_IMimeBody(iface); TRACE("(%p)->(%p)\n", iface, pStm); return parse_headers(This, pStm); } static HRESULT WINAPI MimeBody_Save( IMimeBody* iface, LPSTREAM pStm, BOOL fClearDirty) { FIXME("stub\n"); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_GetSizeMax( IMimeBody* iface, ULARGE_INTEGER* pcbSize) { FIXME("stub\n"); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_InitNew( IMimeBody* iface) { TRACE("%p->()\n", iface); return S_OK; } static HRESULT WINAPI MimeBody_GetPropInfo( IMimeBody* iface, LPCSTR pszName, LPMIMEPROPINFO pInfo) { FIXME("stub\n"); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_SetPropInfo( IMimeBody* iface, LPCSTR pszName, LPCMIMEPROPINFO pInfo) { FIXME("stub\n"); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_GetProp( IMimeBody* iface, LPCSTR pszName, DWORD dwFlags, LPPROPVARIANT pValue) { MimeBody *This = impl_from_IMimeBody(iface); TRACE("(%p)->(%s, %d, %p)\n", This, pszName, dwFlags, pValue); if(!strcasecmp(pszName, "att:pri-content-type")) { PropVariantClear(pValue); pValue->vt = VT_LPSTR; pValue->u.pszVal = strdupA(This->content_pri_type); return S_OK; } FIXME("stub!\n"); return E_FAIL; } static HRESULT WINAPI MimeBody_SetProp( IMimeBody* iface, LPCSTR pszName, DWORD dwFlags, LPCPROPVARIANT pValue) { FIXME("stub\n"); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_AppendProp( IMimeBody* iface, LPCSTR pszName, DWORD dwFlags, LPPROPVARIANT pValue) { FIXME("stub\n"); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_DeleteProp( IMimeBody* iface, LPCSTR pszName) { FIXME("stub\n"); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_CopyProps( IMimeBody* iface, ULONG cNames, LPCSTR* prgszName, IMimePropertySet* pPropertySet) { FIXME("stub\n"); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_MoveProps( IMimeBody* iface, ULONG cNames, LPCSTR* prgszName, IMimePropertySet* pPropertySet) { FIXME("stub\n"); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_DeleteExcept( IMimeBody* iface, ULONG cNames, LPCSTR* prgszName) { FIXME("stub\n"); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_QueryProp( IMimeBody* iface, LPCSTR pszName, LPCSTR pszCriteria, boolean fSubString, boolean fCaseSensitive) { FIXME("stub\n"); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_GetCharset( IMimeBody* iface, LPHCHARSET phCharset) { FIXME("stub\n"); *phCharset = NULL; return S_OK; } static HRESULT WINAPI MimeBody_SetCharset( IMimeBody* iface, HCHARSET hCharset, CSETAPPLYTYPE applytype) { FIXME("stub\n"); 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(strcasecmp(pri, pszPriType)) return S_FALSE; } if(pszSubType) { const char *sub = This->content_sub_type; if(!sub) sub = "plain"; if(strcasecmp(sub, pszSubType)) return S_FALSE; } return S_OK; } static HRESULT WINAPI MimeBody_BindToObject( IMimeBody* iface, REFIID riid, void** ppvObject) { FIXME("stub\n"); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_Clone( IMimeBody* iface, IMimePropertySet** ppPropertySet) { FIXME("stub\n"); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_SetOption( IMimeBody* iface, const TYPEDID oid, LPCPROPVARIANT pValue) { FIXME("(%p)->(%08x, %p): stub\n", iface, oid, pValue); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_GetOption( IMimeBody* iface, const TYPEDID oid, LPPROPVARIANT pValue) { FIXME("(%p)->(%08x, %p): stub\n", iface, oid, pValue); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_EnumProps( IMimeBody* iface, DWORD dwFlags, IMimeEnumProperties** ppEnum) { FIXME("stub\n"); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_IsType( IMimeBody* iface, IMSGBODYTYPE bodytype) { FIXME("(%p)->(%d): stub\n", iface, bodytype); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_SetDisplayName( IMimeBody* iface, LPCSTR pszDisplay) { FIXME("stub\n"); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_GetDisplayName( IMimeBody* iface, LPSTR* ppszDisplay) { FIXME("stub\n"); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_GetOffsets( IMimeBody* iface, LPBODYOFFSETS pOffsets) { MimeBody *This = impl_from_IMimeBody(iface); TRACE("(%p)->(%p)\n", This, pOffsets); *pOffsets = This->body_offsets; if(This->body_offsets.cbBodyEnd == 0) return MIME_E_NO_DATA; return S_OK; } static HRESULT WINAPI MimeBody_GetCurrentEncoding( IMimeBody* iface, ENCODINGTYPE* pietEncoding) { MimeBody *This = impl_from_IMimeBody(iface); TRACE("(%p)->(%p)\n", This, pietEncoding); *pietEncoding = This->encoding; return S_OK; } static HRESULT WINAPI MimeBody_SetCurrentEncoding( IMimeBody* iface, ENCODINGTYPE ietEncoding) { MimeBody *This = impl_from_IMimeBody(iface); TRACE("(%p)->(%d)\n", This, ietEncoding); This->encoding = ietEncoding; return S_OK; } static HRESULT WINAPI MimeBody_GetEstimatedSize( IMimeBody* iface, ENCODINGTYPE ietEncoding, ULONG* pcbSize) { FIXME("stub\n"); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_GetDataHere( IMimeBody* iface, ENCODINGTYPE ietEncoding, IStream* pStream) { FIXME("stub\n"); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_GetData( IMimeBody* iface, ENCODINGTYPE ietEncoding, IStream** ppStream) { MimeBody *This = impl_from_IMimeBody(iface); FIXME("(%p)->(%d, %p). Ignoring encoding type.\n", This, ietEncoding, ppStream); *ppStream = This->data; IStream_AddRef(*ppStream); return S_OK; } static HRESULT WINAPI MimeBody_SetData( IMimeBody* iface, ENCODINGTYPE ietEncoding, LPCSTR pszPriType, LPCSTR pszSubType, REFIID riid, LPVOID pvObject) { MimeBody *This = impl_from_IMimeBody(iface); TRACE("(%p)->(%d, %s, %s, %s %p)\n", This, ietEncoding, debugstr_a(pszPriType), debugstr_a(pszSubType), debugstr_guid(riid), pvObject); if(IsEqualIID(riid, &IID_IStream)) IStream_AddRef((IStream *)pvObject); else { FIXME("Unhandled object type %s\n", debugstr_guid(riid)); return E_INVALIDARG; } if(This->data) FIXME("release old data\n"); This->data_iid = *riid; This->data = pvObject; 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) { FIXME("stub\n"); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_CopyTo( IMimeBody* iface, IMimeBody* pBody) { FIXME("stub\n"); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_GetTransmitInfo( IMimeBody* iface, LPTRANSMITINFO pTransmitInfo) { FIXME("stub\n"); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_SaveToFile( IMimeBody* iface, ENCODINGTYPE ietEncoding, LPCSTR pszFilePath) { FIXME("stub\n"); return E_NOTIMPL; } static HRESULT WINAPI MimeBody_GetHandle( IMimeBody* iface, LPHBODY phBody) { MimeBody *This = impl_from_IMimeBody(iface); TRACE("(%p)->(%p)\n", iface, phBody); *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 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 = (IMimeBody *)&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; } 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.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->lpVtbl = &sub_stream_vtbl; This->refs = 1; This->start = start; This->length = length; This->pos.QuadPart = 0; IStream_AddRef(stream); This->base = stream; *out = (IStream*)&This->lpVtbl; return S_OK; } typedef struct body_t { struct list entry; HBODY hbody; IMimeBody *mime_body; struct body_t *parent; struct list children; } body_t; typedef struct MimeMessage { const IMimeMessageVtbl *lpVtbl; LONG refs; IStream *stream; struct list body_tree; HBODY next_hbody; } MimeMessage; 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; IUnknown_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 = (MimeMessage *)iface; TRACE("(%p)->()\n", iface); return InterlockedIncrement(&This->refs); } 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); HeapFree(GetProcessHeap(), 0, body); } } static ULONG WINAPI MimeMessage_Release(IMimeMessage *iface) { MimeMessage *This = (MimeMessage *)iface; ULONG refs; TRACE("(%p)->()\n", iface); refs = InterlockedDecrement(&This->refs); if (!refs) { empty_body_list(&This->body_tree); if(This->stream) IStream_Release(This->stream); HeapFree(GetProcessHeap(), 0, This); } return refs; } /*** 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(IMimeBody *mime_body, HBODY hbody, body_t *parent) { body_t *body = HeapAlloc(GetProcessHeap(), 0, sizeof(*body)); if(body) { body->mime_body = mime_body; body->hbody = hbody; list_init(&body->children); body->parent = parent; } 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 total_read = 0; 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.LowPart; do { hr = IStream_Read(stm, overlap, PARSER_BUF_SIZE, &read); if(FAILED(hr)) goto end; if(read == 0) break; total_read += read; 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 */ { memcpy(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); start += read; } } while(1); end: HeapFree(GetProcessHeap(), 0, buf); return hr; } static body_t *create_sub_body(MimeMessage *msg, IStream *pStm, BODYOFFSETS *offset, body_t *parent) { IMimeBody *mime_body; HRESULT hr; body_t *body; ULARGE_INTEGER cur; LARGE_INTEGER zero; 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.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_hbody, parent); msg->next_hbody = (HBODY)((DWORD)msg->next_hbody + 1); if(IMimeBody_IsContentType(mime_body, "multipart", NULL) == S_OK) { MIMEPARAMINFO *param_info; ULONG count, i; IMimeAllocator *alloc; hr = IMimeBody_GetParameters(mime_body, "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")) { 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.LowPart = cur->offsets.cbHeaderStart; length.LowPart = 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, LPSTREAM pStm) { MimeMessage *This = (MimeMessage *)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; } 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.LowPart; MimeBody_set_offsets(impl_from_IMimeBody(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) { 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 = (MimeMessage *)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 E_NOTIMPL; } 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->hbody == 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 = (MimeMessage *)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); *ppvObject = body->mime_body; 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: *out = body->parent; 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, LPHBODY phBody) { MimeMessage *This = (MimeMessage *)iface; body_t *body; HRESULT hr; TRACE("(%p)->(%d, %p, %p)\n", iface, location, hPivot, phBody); hr = get_body(This, location, hPivot, &body); if(hr == S_OK) *phBody = body->hbody; 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 = (MimeMessage *)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(IMimeMessage *msg, LPFINDBODY find_body, HBODY *out) { HRESULT hr; IMimeBody *mime_body; HBODY next; if(find_body->dwReserved == 0) find_body->dwReserved = (DWORD)HBODY_ROOT; else { hr = IMimeMessage_GetBody(msg, IBL_FIRST, (HBODY)find_body->dwReserved, &next); if(hr == S_OK) find_body->dwReserved = (DWORD)next; else { hr = IMimeMessage_GetBody(msg, IBL_NEXT, (HBODY)find_body->dwReserved, &next); if(hr == S_OK) find_body->dwReserved = (DWORD)next; else return MIME_E_NOT_FOUND; } } hr = IMimeMessage_BindToObject(msg, (HBODY)find_body->dwReserved, &IID_IMimeBody, (void**)&mime_body); if(IMimeBody_IsContentType(mime_body, find_body->pszPriType, find_body->pszSubType) == S_OK) { IMimeBody_Release(mime_body); *out = (HBODY)find_body->dwReserved; return S_OK; } IMimeBody_Release(mime_body); return find_next(msg, find_body, out); } static HRESULT WINAPI MimeMessage_FindFirst( IMimeMessage *iface, LPFINDBODY pFindBody, LPHBODY phBody) { TRACE("(%p)->(%p, %p)\n", iface, pFindBody, phBody); pFindBody->dwReserved = 0; return find_next(iface, pFindBody, phBody); } static HRESULT WINAPI MimeMessage_FindNext( IMimeMessage *iface, LPFINDBODY pFindBody, LPHBODY phBody) { TRACE("(%p)->(%p, %p)\n", iface, pFindBody, phBody); return find_next(iface, 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, pszPriType, 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) { FIXME("(%p)->(%08x, %p)\n", iface, oid, pValue); return E_NOTIMPL; } 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, }; /*********************************************************************** * MimeOleCreateMessage (INETCOMM.@) */ HRESULT WINAPI MimeOleCreateMessage(IUnknown *pUnkOuter, IMimeMessage **ppMessage) { MimeMessage *This; TRACE("(%p, %p)\n", pUnkOuter, ppMessage); if (pUnkOuter) { FIXME("outer unknown not supported yet\n"); return E_NOTIMPL; } *ppMessage = NULL; This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); if (!This) return E_OUTOFMEMORY; This->lpVtbl = &MimeMessageVtbl; This->refs = 1; This->stream = NULL; list_init(&This->body_tree); This->next_hbody = (HBODY)1; *ppMessage = (IMimeMessage *)&This->lpVtbl; return S_OK; } /*********************************************************************** * 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 { const IMimeSecurityVtbl *lpVtbl; LONG refs; } MimeSecurity; static HRESULT WINAPI MimeSecurity_QueryInterface( IMimeSecurity* iface, REFIID riid, void** obj) { TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), obj); if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IMimeSecurity)) { *obj = iface; IUnknown_AddRef(iface); return S_OK; } FIXME("no interface for %s\n", debugstr_guid(riid)); *obj = NULL; return E_NOINTERFACE; } static ULONG WINAPI MimeSecurity_AddRef( IMimeSecurity* iface) { MimeSecurity *This = (MimeSecurity *)iface; TRACE("(%p)->()\n", iface); return InterlockedIncrement(&This->refs); } static ULONG WINAPI MimeSecurity_Release( IMimeSecurity* iface) { MimeSecurity *This = (MimeSecurity *)iface; ULONG refs; TRACE("(%p)->()\n", iface); refs = InterlockedDecrement(&This->refs); if (!refs) { HeapFree(GetProcessHeap(), 0, This); } return refs; } 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 }; /*********************************************************************** * MimeOleCreateSecurity (INETCOMM.@) */ HRESULT WINAPI MimeOleCreateSecurity(IMimeSecurity **ppSecurity) { MimeSecurity *This; TRACE("(%p)\n", ppSecurity); *ppSecurity = NULL; This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); if (!This) return E_OUTOFMEMORY; This->lpVtbl = &MimeSecurityVtbl; This->refs = 1; *ppSecurity = (IMimeSecurity *)&This->lpVtbl; return S_OK; } typedef struct { IMimeAllocatorVtbl *lpVtbl; } MimeAllocator; 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; IUnknown_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, ULONG cb) { return CoTaskMemAlloc(cb); } static LPVOID WINAPI MimeAlloc_Realloc( IMimeAllocator* iface, LPVOID pv, ULONG cb) { return CoTaskMemRealloc(pv, cb); } static void WINAPI MimeAlloc_Free( IMimeAllocator* iface, LPVOID pv) { return CoTaskMemFree(pv); } static ULONG 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 MimeAllocator 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 WINAPI MimeOleGetCharsetInfo(HCHARSET hCharset, LPINETCSETINFO pCsetInfo) { FIXME("(%p, %p)\n", hCharset, pCsetInfo); if(!hCharset) return E_INVALIDARG; return E_FAIL; }