| /* | |
| * 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 <stdarg.h> | |
| #include <stdio.h> | |
| #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 "wine/unicode.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; | |
| 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 void init_content_encoding(MimeBody *body, header_t *header) | |
| { | |
| const char *encoding = header->value.u.pszVal; | |
| if(!strcasecmp(encoding, "base64")) | |
| body->encoding = IET_BASE64; | |
| else if(!strcasecmp(encoding, "quoted-printable")) | |
| body->encoding = IET_QP; | |
| else if(!strcasecmp(encoding, "7bit")) | |
| body->encoding = IET_7BIT; | |
| else if(!strcasecmp(encoding, "8bit")) | |
| body->encoding = IET_8BIT; | |
| else | |
| FIXME("unknown encoding %s\n", debugstr_a(encoding)); | |
| } | |
| static HRESULT parse_headers(MimeBody *body, IStream *stm) | |
| { | |
| char *header_buf, *cur_header_ptr; | |
| 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); | |
| switch(header->prop->id) { | |
| case PID_HDR_CNTTYPE: | |
| init_content_type(body, header); | |
| break; | |
| case PID_HDR_CNTXFER: | |
| init_content_encoding(body, header); | |
| break; | |
| } | |
| } | |
| HeapFree(GetProcessHeap(), 0, header_buf); | |
| 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); | |
| TRACE("(%p)->(%p)\n", This, pClassID); | |
| if(!pClassID) | |
| return E_INVALIDARG; | |
| *pClassID = IID_IMimeBody; | |
| return S_OK; | |
| } | |
| 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 int hex_digit(char c) | |
| { | |
| if('0' <= c && c <= '9') | |
| return c - '0'; | |
| if('A' <= c && c <= 'F') | |
| return c - 'A' + 10; | |
| if('a' <= c && c <= 'f') | |
| return c - 'a' + 10; | |
| return -1; | |
| } | |
| static HRESULT decode_qp(IStream *input, IStream **ret_stream) | |
| { | |
| const unsigned char *ptr, *end; | |
| unsigned char *ret, prev = 0; | |
| unsigned char buf[1024]; | |
| LARGE_INTEGER pos; | |
| IStream *output; | |
| DWORD size; | |
| int n = -1; | |
| HRESULT hres; | |
| pos.QuadPart = 0; | |
| hres = IStream_Seek(input, pos, STREAM_SEEK_SET, NULL); | |
| if(FAILED(hres)) | |
| return hres; | |
| hres = CreateStreamOnHGlobal(NULL, TRUE, &output); | |
| if(FAILED(hres)) | |
| return hres; | |
| while(1) { | |
| hres = IStream_Read(input, buf, sizeof(buf), &size); | |
| if(FAILED(hres) || !size) | |
| break; | |
| ptr = ret = buf; | |
| end = buf + size; | |
| while(ptr < end) { | |
| unsigned char byte = *ptr++; | |
| switch(n) { | |
| case -1: | |
| if(byte == '=') | |
| n = 0; | |
| else | |
| *ret++ = byte; | |
| continue; | |
| case 0: | |
| prev = byte; | |
| n = 1; | |
| continue; | |
| case 1: | |
| if(prev != '\r' || byte != '\n') { | |
| int h1 = hex_digit(prev), h2 = hex_digit(byte); | |
| if(h1 != -1 && h2 != -1) | |
| *ret++ = (h1 << 4) | h2; | |
| else | |
| *ret++ = '='; | |
| } | |
| n = -1; | |
| continue; | |
| } | |
| } | |
| if(ret > buf) { | |
| hres = IStream_Write(output, buf, ret - buf, NULL); | |
| if(FAILED(hres)) | |
| break; | |
| } | |
| } | |
| if(SUCCEEDED(hres)) | |
| hres = IStream_Seek(output, pos, STREAM_SEEK_SET, NULL); | |
| if(FAILED(hres)) { | |
| IStream_Release(output); | |
| return hres; | |
| } | |
| *ret_stream = output; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI MimeBody_GetData( | |
| IMimeBody* iface, | |
| ENCODINGTYPE ietEncoding, | |
| IStream** ppStream) | |
| { | |
| MimeBody *This = impl_from_IMimeBody(iface); | |
| ULARGE_INTEGER start, size; | |
| HRESULT hres; | |
| TRACE("(%p)->(%d %p)\n", This, ietEncoding, ppStream); | |
| if(This->encoding != ietEncoding) { | |
| switch(This->encoding) { | |
| case IET_BASE64: | |
| hres = decode_base64(This->data, ppStream); | |
| break; | |
| case IET_QP: | |
| hres = decode_qp(This->data, ppStream); | |
| break; | |
| default: | |
| FIXME("Decoding %d is not supported.\n", This->encoding); | |
| hres = S_FALSE; | |
| } | |
| if(ietEncoding != IET_BINARY) | |
| FIXME("Encoding %d is not supported.\n", ietEncoding); | |
| if(hres != S_FALSE) | |
| return hres; | |
| } | |
| start.QuadPart = 0; | |
| hres = get_stream_size(This->data, &size); | |
| if(SUCCEEDED(hres)) | |
| hres = create_sub_stream(This->data, start, size, ppStream); | |
| return hres; | |
| } | |
| static HRESULT WINAPI MimeBody_SetData( | |
| 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) | |
| release_data(&This->data_iid, This->data); | |
| 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, boundary_start; | |
| int boundary_len = strlen(boundary); | |
| char *buf, *ptr, *overlap; | |
| DWORD start = 0, overlap_no; | |
| offset_entry_t *cur_body = NULL; | |
| BOOL is_first_line = TRUE; | |
| ULARGE_INTEGER cur; | |
| LARGE_INTEGER zero; | |
| list_init(body_offsets); | |
| 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; | |
| while(1) { | |
| if(is_first_line) { | |
| is_first_line = FALSE; | |
| }else { | |
| ptr = strstr(ptr, "\r\n"); | |
| if(!ptr) | |
| break; | |
| ptr += 2; | |
| } | |
| boundary_start = start + ptr - buf; | |
| if(*ptr == '-' && *(ptr + 1) == '-' && !memcmp(ptr + 2, boundary, boundary_len)) { | |
| ptr += boundary_len + 2; | |
| if(*ptr == '\r' && *(ptr + 1) == '\n') | |
| { | |
| ptr += 2; | |
| if(cur_body) | |
| { | |
| cur_body->offsets.cbBodyEnd = boundary_start - 2; | |
| list_add_tail(body_offsets, &cur_body->entry); | |
| } | |
| cur_body = HeapAlloc(GetProcessHeap(), 0, sizeof(*cur_body)); | |
| cur_body->offsets.cbBoundaryStart = boundary_start; | |
| cur_body->offsets.cbHeaderStart = start + ptr - buf; | |
| } | |
| else if(*ptr == '-' && *(ptr + 1) == '-') | |
| { | |
| if(cur_body) | |
| { | |
| cur_body->offsets.cbBodyEnd = boundary_start - 2; | |
| list_add_tail(body_offsets, &cur_body->entry); | |
| goto end; | |
| } | |
| } | |
| } | |
| } | |
| 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, buf); | |
| return hr; | |
| } | |
| static body_t *create_sub_body(MimeMessage *msg, IStream *pStm, BODYOFFSETS *offset, body_t *parent) | |
| { | |
| ULARGE_INTEGER start, length; | |
| MimeBody *mime_body; | |
| HRESULT hr; | |
| body_t *body; | |
| LARGE_INTEGER pos; | |
| pos.QuadPart = offset->cbHeaderStart; | |
| IStream_Seek(pStm, pos, STREAM_SEEK_SET, NULL); | |
| mime_body = mimebody_create(); | |
| IMimeBody_Load(&mime_body->IMimeBody_iface, pStm); | |
| pos.QuadPart = 0; | |
| hr = IStream_Seek(pStm, pos, STREAM_SEEK_CUR, &start); | |
| offset->cbBodyStart = start.QuadPart; | |
| if (parent) MimeBody_set_offsets(mime_body, offset); | |
| length.QuadPart = offset->cbBodyEnd - offset->cbBodyStart; | |
| create_sub_stream(pStm, start, length, (IStream**)&mime_body->data); | |
| mime_body->data_iid = IID_IStream; | |
| 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; | |
| sub_body = create_sub_body(msg, pStm, &cur->offsets, body); | |
| list_add_tail(&body->children, &sub_body->entry); | |
| list_remove(&cur->entry); | |
| HeapFree(GetProcessHeap(), 0, cur); | |
| } | |
| 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; | |
| } | |
| static HRESULT WINAPI mime_obj_QueryInterface(IUnknown *iface, REFIID riid, void **ppv) | |
| { | |
| FIXME("(%s %p)\n", debugstr_guid(riid), ppv); | |
| *ppv = NULL; | |
| return E_NOINTERFACE; | |
| } | |
| static ULONG WINAPI mime_obj_AddRef(IUnknown *iface) | |
| { | |
| TRACE("\n"); | |
| return 2; | |
| } | |
| static ULONG WINAPI mime_obj_Release(IUnknown *iface) | |
| { | |
| TRACE("\n"); | |
| return 1; | |
| } | |
| static const IUnknownVtbl mime_obj_vtbl = { | |
| mime_obj_QueryInterface, | |
| mime_obj_AddRef, | |
| mime_obj_Release | |
| }; | |
| static IUnknown mime_obj = { &mime_obj_vtbl }; | |
| HRESULT WINAPI MimeOleObjectFromMoniker(BINDF bindf, IMoniker *moniker, IBindCtx *binding, | |
| REFIID riid, void **out, IMoniker **moniker_new) | |
| { | |
| WCHAR *display_name, *mhtml_url; | |
| size_t len; | |
| HRESULT hres; | |
| static const WCHAR mhtml_prefixW[] = {'m','h','t','m','l',':'}; | |
| WARN("(0x%08x, %p, %p, %s, %p, %p) semi-stub\n", bindf, moniker, binding, debugstr_guid(riid), out, moniker_new); | |
| if(!IsEqualGUID(&IID_IUnknown, riid)) { | |
| FIXME("Unsupported riid %s\n", debugstr_guid(riid)); | |
| return E_NOINTERFACE; | |
| } | |
| hres = IMoniker_GetDisplayName(moniker, NULL, NULL, &display_name); | |
| if(FAILED(hres)) | |
| return hres; | |
| TRACE("display name %s\n", debugstr_w(display_name)); | |
| len = strlenW(display_name); | |
| mhtml_url = heap_alloc((len+1)*sizeof(WCHAR) + sizeof(mhtml_prefixW)); | |
| if(!mhtml_url) | |
| return E_OUTOFMEMORY; | |
| memcpy(mhtml_url, mhtml_prefixW, sizeof(mhtml_prefixW)); | |
| strcpyW(mhtml_url + sizeof(mhtml_prefixW)/sizeof(WCHAR), display_name); | |
| HeapFree(GetProcessHeap(), 0, display_name); | |
| hres = CreateURLMoniker(NULL, mhtml_url, moniker_new); | |
| heap_free(mhtml_url); | |
| if(FAILED(hres)) | |
| return hres; | |
| /* FIXME: We most likely should start binding here and return something more meaningful as mime object. */ | |
| *out = &mime_obj; | |
| return S_OK; | |
| } |