[INETCOMM] Sync with Wine Staging 4.18. CORE-16441
[reactos.git] / dll / win32 / inetcomm / mimeole.c
1 /*
2 * MIME OLE Interfaces
3 *
4 * Copyright 2006 Robert Shearman for CodeWeavers
5 * Copyright 2007 Huw Davies for CodeWeavers
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 */
21
22 #define COBJMACROS
23 #define NONAMELESSUNION
24
25 #include <stdarg.h>
26 #include <stdio.h>
27
28 #include "windef.h"
29 #include "winbase.h"
30 #include "wine/winternl.h"
31 #include "winuser.h"
32 #include "objbase.h"
33 #include "ole2.h"
34 #include "mimeole.h"
35 #ifdef __REACTOS__
36 #include <winreg.h>
37 #endif
38 #include "propvarutil.h"
39
40 #include "wine/heap.h"
41 #include "wine/list.h"
42 #include "wine/debug.h"
43
44 #include "inetcomm_private.h"
45
46 WINE_DEFAULT_DEBUG_CHANNEL(inetcomm);
47
48 typedef struct
49 {
50 LPCSTR name;
51 DWORD id;
52 DWORD flags; /* MIMEPROPFLAGS */
53 VARTYPE default_vt;
54 } property_t;
55
56 typedef struct
57 {
58 struct list entry;
59 property_t prop;
60 } property_list_entry_t;
61
62 static const property_t default_props[] =
63 {
64 {"X-Newsgroup", PID_HDR_NEWSGROUP, 0, VT_LPSTR},
65 {"Newsgroups", PID_HDR_NEWSGROUPS, 0, VT_LPSTR},
66 {"References", PID_HDR_REFS, 0, VT_LPSTR},
67 {"Subject", PID_HDR_SUBJECT, 0, VT_LPSTR},
68 {"From", PID_HDR_FROM, MPF_ADDRESS, VT_LPSTR},
69 {"Message-ID", PID_HDR_MESSAGEID, 0, VT_LPSTR},
70 {"Return-Path", PID_HDR_RETURNPATH, MPF_ADDRESS, VT_LPSTR},
71 {"Rr", PID_HDR_RR, 0, VT_LPSTR},
72 {"Return-Receipt-To", PID_HDR_RETRCPTO, MPF_ADDRESS, VT_LPSTR},
73 {"Apparently-To", PID_HDR_APPARTO, MPF_ADDRESS, VT_LPSTR},
74 {"Date", PID_HDR_DATE, 0, VT_LPSTR},
75 {"Received", PID_HDR_RECEIVED, 0, VT_LPSTR},
76 {"Reply-To", PID_HDR_REPLYTO, MPF_ADDRESS, VT_LPSTR},
77 {"X-Mailer", PID_HDR_XMAILER, 0, VT_LPSTR},
78 {"Bcc", PID_HDR_BCC, MPF_ADDRESS, VT_LPSTR},
79 {"MIME-Version", PID_HDR_MIMEVER, MPF_MIME, VT_LPSTR},
80 {"Content-Type", PID_HDR_CNTTYPE, MPF_MIME | MPF_HASPARAMS, VT_LPSTR},
81 {"Content-Transfer-Encoding", PID_HDR_CNTXFER, MPF_MIME, VT_LPSTR},
82 {"Content-ID", PID_HDR_CNTID, MPF_MIME, VT_LPSTR},
83 {"Content-Description", PID_HDR_CNTDESC, MPF_MIME, VT_LPSTR},
84 {"Content-Disposition", PID_HDR_CNTDISP, MPF_MIME | MPF_HASPARAMS, VT_LPSTR},
85 {"Content-Base", PID_HDR_CNTBASE, MPF_MIME, VT_LPSTR},
86 {"Content-Location", PID_HDR_CNTLOC, MPF_MIME, VT_LPSTR},
87 {"To", PID_HDR_TO, MPF_ADDRESS, VT_LPSTR},
88 {"Path", PID_HDR_PATH, 0, VT_LPSTR},
89 {"Followup-To", PID_HDR_FOLLOWUPTO, 0, VT_LPSTR},
90 {"Expires", PID_HDR_EXPIRES, 0, VT_LPSTR},
91 {"Cc", PID_HDR_CC, MPF_ADDRESS, VT_LPSTR},
92 {"Control", PID_HDR_CONTROL, 0, VT_LPSTR},
93 {"Distribution", PID_HDR_DISTRIB, 0, VT_LPSTR},
94 {"Keywords", PID_HDR_KEYWORDS, 0, VT_LPSTR},
95 {"Summary", PID_HDR_SUMMARY, 0, VT_LPSTR},
96 {"Approved", PID_HDR_APPROVED, 0, VT_LPSTR},
97 {"Lines", PID_HDR_LINES, 0, VT_LPSTR},
98 {"Xref", PID_HDR_XREF, 0, VT_LPSTR},
99 {"Organization", PID_HDR_ORG, 0, VT_LPSTR},
100 {"X-Newsreader", PID_HDR_XNEWSRDR, 0, VT_LPSTR},
101 {"X-Priority", PID_HDR_XPRI, 0, VT_LPSTR},
102 {"X-MSMail-Priority", PID_HDR_XMSPRI, 0, VT_LPSTR},
103 {"par:content-disposition:filename", PID_PAR_FILENAME, 0, VT_LPSTR},
104 {"par:content-type:boundary", PID_PAR_BOUNDARY, 0, VT_LPSTR},
105 {"par:content-type:charset", PID_PAR_CHARSET, 0, VT_LPSTR},
106 {"par:content-type:name", PID_PAR_NAME, 0, VT_LPSTR},
107 {"att:filename", PID_ATT_FILENAME, 0, VT_LPSTR},
108 {"att:pri-content-type", PID_ATT_PRITYPE, 0, VT_LPSTR},
109 {"att:sub-content-type", PID_ATT_SUBTYPE, 0, VT_LPSTR},
110 {"att:illegal-lines", PID_ATT_ILLEGAL, 0, VT_LPSTR},
111 {"att:rendered", PID_ATT_RENDERED, 0, VT_LPSTR},
112 {"att:sent-time", PID_ATT_SENTTIME, 0, VT_LPSTR},
113 {"att:priority", PID_ATT_PRIORITY, 0, VT_LPSTR},
114 {"Comment", PID_HDR_COMMENT, 0, VT_LPSTR},
115 {"Encoding", PID_HDR_ENCODING, 0, VT_LPSTR},
116 {"Encrypted", PID_HDR_ENCRYPTED, 0, VT_LPSTR},
117 {"X-Offsets", PID_HDR_OFFSETS, 0, VT_LPSTR},
118 {"X-Unsent", PID_HDR_XUNSENT, 0, VT_LPSTR},
119 {"X-ArticleId", PID_HDR_ARTICLEID, 0, VT_LPSTR},
120 {"Sender", PID_HDR_SENDER, MPF_ADDRESS, VT_LPSTR},
121 {"att:athena-server", PID_ATT_SERVER, 0, VT_LPSTR},
122 {"att:athena-account-id", PID_ATT_ACCOUNT, 0, VT_LPSTR},
123 {"att:athena-pop3-uidl", PID_ATT_UIDL, 0, VT_LPSTR},
124 {"att:athena-store-msgid", PID_ATT_STOREMSGID, 0, VT_LPSTR},
125 {"att:athena-user-name", PID_ATT_USERNAME, 0, VT_LPSTR},
126 {"att:athena-forward-to", PID_ATT_FORWARDTO, 0, VT_LPSTR},
127 {"att:athena-store-fdrid", PID_ATT_STOREFOLDERID,0, VT_LPSTR},
128 {"att:athena-ghosted", PID_ATT_GHOSTED, 0, VT_LPSTR},
129 {"att:athena-uncachedsize", PID_ATT_UNCACHEDSIZE, 0, VT_LPSTR},
130 {"att:athena-combined", PID_ATT_COMBINED, 0, VT_LPSTR},
131 {"att:auto-inlined", PID_ATT_AUTOINLINED, 0, VT_LPSTR},
132 {"Disposition-Notification-To", PID_HDR_DISP_NOTIFICATION_TO, 0, VT_LPSTR},
133 {"par:Content-Type:reply-type", PID_PAR_REPLYTYPE, 0, VT_LPSTR},
134 {"par:Content-Type:format", PID_PAR_FORMAT , 0, VT_LPSTR},
135 {"att:format", PID_ATT_FORMAT , 0, VT_LPSTR},
136 {"In-Reply-To", PID_HDR_INREPLYTO, 0, VT_LPSTR},
137 {"att:athena-account-name", PID_ATT_ACCOUNTNAME, 0, VT_LPSTR},
138 {NULL, 0, 0, 0}
139 };
140
141 typedef struct
142 {
143 struct list entry;
144 char *name;
145 char *value;
146 } param_t;
147
148 typedef struct
149 {
150 struct list entry;
151 const property_t *prop;
152 PROPVARIANT value;
153 struct list params;
154 } header_t;
155
156 typedef struct MimeBody
157 {
158 IMimeBody IMimeBody_iface;
159 LONG ref;
160
161 HBODY handle;
162
163 struct list headers;
164 struct list new_props; /* FIXME: This should be in a PropertySchema */
165 DWORD next_prop_id;
166 char *content_pri_type;
167 char *content_sub_type;
168 ENCODINGTYPE encoding;
169 void *data;
170 IID data_iid;
171 BODYOFFSETS body_offsets;
172 } MimeBody;
173
174 typedef struct
175 {
176 IStream IStream_iface;
177 LONG ref;
178 IStream *base;
179 ULARGE_INTEGER pos, start, length;
180 } sub_stream_t;
181
182 static inline sub_stream_t *impl_from_IStream(IStream *iface)
183 {
184 return CONTAINING_RECORD(iface, sub_stream_t, IStream_iface);
185 }
186
187 static HRESULT WINAPI sub_stream_QueryInterface(IStream *iface, REFIID riid, void **ppv)
188 {
189 sub_stream_t *This = impl_from_IStream(iface);
190
191 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
192 *ppv = NULL;
193
194 if(IsEqualIID(riid, &IID_IUnknown) ||
195 IsEqualIID(riid, &IID_ISequentialStream) ||
196 IsEqualIID(riid, &IID_IStream))
197 {
198 IStream_AddRef(iface);
199 *ppv = iface;
200 return S_OK;
201 }
202 return E_NOINTERFACE;
203 }
204
205 static ULONG WINAPI sub_stream_AddRef(IStream *iface)
206 {
207 sub_stream_t *This = impl_from_IStream(iface);
208 LONG ref = InterlockedIncrement(&This->ref);
209
210 TRACE("(%p) ref=%d\n", This, ref);
211
212 return ref;
213 }
214
215 static ULONG WINAPI sub_stream_Release(IStream *iface)
216 {
217 sub_stream_t *This = impl_from_IStream(iface);
218 LONG ref = InterlockedDecrement(&This->ref);
219
220 TRACE("(%p) ref=%d\n", This, ref);
221
222 if(!ref)
223 {
224 IStream_Release(This->base);
225 HeapFree(GetProcessHeap(), 0, This);
226 }
227 return ref;
228 }
229
230 static HRESULT WINAPI sub_stream_Read(
231 IStream* iface,
232 void *pv,
233 ULONG cb,
234 ULONG *pcbRead)
235 {
236 sub_stream_t *This = impl_from_IStream(iface);
237 HRESULT hr;
238 LARGE_INTEGER tmp_pos;
239
240 TRACE("(%p, %d, %p)\n", pv, cb, pcbRead);
241
242 tmp_pos.QuadPart = This->pos.QuadPart + This->start.QuadPart;
243 IStream_Seek(This->base, tmp_pos, STREAM_SEEK_SET, NULL);
244
245 if(This->pos.QuadPart + cb > This->length.QuadPart)
246 cb = This->length.QuadPart - This->pos.QuadPart;
247
248 hr = IStream_Read(This->base, pv, cb, pcbRead);
249
250 This->pos.QuadPart += *pcbRead;
251
252 return hr;
253 }
254
255 static HRESULT WINAPI sub_stream_Write(
256 IStream* iface,
257 const void *pv,
258 ULONG cb,
259 ULONG *pcbWritten)
260 {
261 FIXME("stub\n");
262 return E_NOTIMPL;
263 }
264
265 static HRESULT WINAPI sub_stream_Seek(
266 IStream* iface,
267 LARGE_INTEGER dlibMove,
268 DWORD dwOrigin,
269 ULARGE_INTEGER *plibNewPosition)
270 {
271 sub_stream_t *This = impl_from_IStream(iface);
272 LARGE_INTEGER new_pos;
273
274 TRACE("(%08x.%08x, %x, %p)\n", dlibMove.u.HighPart, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
275
276 switch(dwOrigin)
277 {
278 case STREAM_SEEK_SET:
279 new_pos = dlibMove;
280 break;
281 case STREAM_SEEK_CUR:
282 new_pos.QuadPart = This->pos.QuadPart + dlibMove.QuadPart;
283 break;
284 case STREAM_SEEK_END:
285 new_pos.QuadPart = This->length.QuadPart + dlibMove.QuadPart;
286 break;
287 default:
288 return STG_E_INVALIDFUNCTION;
289 }
290
291 if(new_pos.QuadPart < 0) new_pos.QuadPart = 0;
292 else if(new_pos.QuadPart > This->length.QuadPart) new_pos.QuadPart = This->length.QuadPart;
293
294 This->pos.QuadPart = new_pos.QuadPart;
295
296 if(plibNewPosition) *plibNewPosition = This->pos;
297 return S_OK;
298 }
299
300 static HRESULT WINAPI sub_stream_SetSize(
301 IStream* iface,
302 ULARGE_INTEGER libNewSize)
303 {
304 FIXME("stub\n");
305 return E_NOTIMPL;
306 }
307
308 static HRESULT WINAPI sub_stream_CopyTo(
309 IStream* iface,
310 IStream *pstm,
311 ULARGE_INTEGER cb,
312 ULARGE_INTEGER *pcbRead,
313 ULARGE_INTEGER *pcbWritten)
314 {
315 HRESULT hr = S_OK;
316 BYTE tmpBuffer[128];
317 ULONG bytesRead, bytesWritten, copySize;
318 ULARGE_INTEGER totalBytesRead;
319 ULARGE_INTEGER totalBytesWritten;
320
321 TRACE("(%p)->(%p, %d, %p, %p)\n", iface, pstm, cb.u.LowPart, pcbRead, pcbWritten);
322
323 totalBytesRead.QuadPart = 0;
324 totalBytesWritten.QuadPart = 0;
325
326 while ( cb.QuadPart > 0 )
327 {
328 if ( cb.QuadPart >= sizeof(tmpBuffer) )
329 copySize = sizeof(tmpBuffer);
330 else
331 copySize = cb.u.LowPart;
332
333 hr = IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
334 if (FAILED(hr)) break;
335
336 totalBytesRead.QuadPart += bytesRead;
337
338 if (bytesRead)
339 {
340 hr = IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
341 if (FAILED(hr)) break;
342 totalBytesWritten.QuadPart += bytesWritten;
343 }
344
345 if (bytesRead != copySize)
346 cb.QuadPart = 0;
347 else
348 cb.QuadPart -= bytesRead;
349 }
350
351 if (pcbRead) pcbRead->QuadPart = totalBytesRead.QuadPart;
352 if (pcbWritten) pcbWritten->QuadPart = totalBytesWritten.QuadPart;
353
354 return hr;
355 }
356
357 static HRESULT WINAPI sub_stream_Commit(
358 IStream* iface,
359 DWORD grfCommitFlags)
360 {
361 FIXME("stub\n");
362 return E_NOTIMPL;
363 }
364
365 static HRESULT WINAPI sub_stream_Revert(
366 IStream* iface)
367 {
368 FIXME("stub\n");
369 return E_NOTIMPL;
370 }
371
372 static HRESULT WINAPI sub_stream_LockRegion(
373 IStream* iface,
374 ULARGE_INTEGER libOffset,
375 ULARGE_INTEGER cb,
376 DWORD dwLockType)
377 {
378 FIXME("stub\n");
379 return E_NOTIMPL;
380 }
381
382 static HRESULT WINAPI sub_stream_UnlockRegion(
383 IStream* iface,
384 ULARGE_INTEGER libOffset,
385 ULARGE_INTEGER cb,
386 DWORD dwLockType)
387 {
388 FIXME("stub\n");
389 return E_NOTIMPL;
390 }
391
392 static HRESULT WINAPI sub_stream_Stat(
393 IStream* iface,
394 STATSTG *pstatstg,
395 DWORD grfStatFlag)
396 {
397 sub_stream_t *This = impl_from_IStream(iface);
398 FIXME("(%p)->(%p, %08x)\n", This, pstatstg, grfStatFlag);
399 memset(pstatstg, 0, sizeof(*pstatstg));
400 pstatstg->cbSize = This->length;
401 return S_OK;
402 }
403
404 static HRESULT WINAPI sub_stream_Clone(
405 IStream* iface,
406 IStream **ppstm)
407 {
408 FIXME("stub\n");
409 return E_NOTIMPL;
410 }
411
412 static struct IStreamVtbl sub_stream_vtbl =
413 {
414 sub_stream_QueryInterface,
415 sub_stream_AddRef,
416 sub_stream_Release,
417 sub_stream_Read,
418 sub_stream_Write,
419 sub_stream_Seek,
420 sub_stream_SetSize,
421 sub_stream_CopyTo,
422 sub_stream_Commit,
423 sub_stream_Revert,
424 sub_stream_LockRegion,
425 sub_stream_UnlockRegion,
426 sub_stream_Stat,
427 sub_stream_Clone
428 };
429
430 static HRESULT create_sub_stream(IStream *stream, ULARGE_INTEGER start, ULARGE_INTEGER length, IStream **out)
431 {
432 sub_stream_t *This;
433
434 *out = NULL;
435 This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
436 if(!This) return E_OUTOFMEMORY;
437
438 This->IStream_iface.lpVtbl = &sub_stream_vtbl;
439 This->ref = 1;
440 This->start = start;
441 This->length = length;
442 This->pos.QuadPart = 0;
443 IStream_AddRef(stream);
444 This->base = stream;
445
446 *out = &This->IStream_iface;
447 return S_OK;
448 }
449
450 static HRESULT get_stream_size(IStream *stream, ULARGE_INTEGER *size)
451 {
452 STATSTG statstg = {NULL};
453 LARGE_INTEGER zero;
454 HRESULT hres;
455
456 hres = IStream_Stat(stream, &statstg, STATFLAG_NONAME);
457 if(SUCCEEDED(hres)) {
458 *size = statstg.cbSize;
459 return S_OK;
460 }
461
462 zero.QuadPart = 0;
463 return IStream_Seek(stream, zero, STREAM_SEEK_END, size);
464 }
465
466 static inline MimeBody *impl_from_IMimeBody(IMimeBody *iface)
467 {
468 return CONTAINING_RECORD(iface, MimeBody, IMimeBody_iface);
469 }
470
471 typedef struct propschema
472 {
473 IMimePropertySchema IMimePropertySchema_iface;
474 LONG ref;
475 } propschema;
476
477 static inline propschema *impl_from_IMimePropertySchema(IMimePropertySchema *iface)
478 {
479 return CONTAINING_RECORD(iface, propschema, IMimePropertySchema_iface);
480 }
481
482 static LPSTR strdupA(LPCSTR str)
483 {
484 char *ret;
485 int len = strlen(str);
486 ret = HeapAlloc(GetProcessHeap(), 0, len + 1);
487 memcpy(ret, str, len + 1);
488 return ret;
489 }
490
491 #define PARSER_BUF_SIZE 1024
492
493 /*****************************************************
494 * copy_headers_to_buf [internal]
495 *
496 * Copies the headers into a '\0' terminated memory block and leave
497 * the stream's current position set to after the blank line.
498 */
499 static HRESULT copy_headers_to_buf(IStream *stm, char **ptr)
500 {
501 char *buf = NULL;
502 DWORD size = PARSER_BUF_SIZE, offset = 0, last_end = 0;
503 HRESULT hr;
504 BOOL done = FALSE;
505
506 *ptr = NULL;
507
508 do
509 {
510 char *end;
511 DWORD read;
512
513 if(!buf)
514 buf = HeapAlloc(GetProcessHeap(), 0, size + 1);
515 else
516 {
517 size *= 2;
518 buf = HeapReAlloc(GetProcessHeap(), 0, buf, size + 1);
519 }
520 if(!buf)
521 {
522 hr = E_OUTOFMEMORY;
523 goto fail;
524 }
525
526 hr = IStream_Read(stm, buf + offset, size - offset, &read);
527 if(FAILED(hr)) goto fail;
528
529 offset += read;
530 buf[offset] = '\0';
531
532 if(read == 0) done = TRUE;
533
534 while(!done && (end = strstr(buf + last_end, "\r\n")))
535 {
536 DWORD new_end = end - buf + 2;
537 if(new_end - last_end == 2)
538 {
539 LARGE_INTEGER off;
540 off.QuadPart = (LONGLONG)new_end - offset;
541 IStream_Seek(stm, off, STREAM_SEEK_CUR, NULL);
542 buf[new_end] = '\0';
543 done = TRUE;
544 }
545 else
546 last_end = new_end;
547 }
548 } while(!done);
549
550 *ptr = buf;
551 return S_OK;
552
553 fail:
554 HeapFree(GetProcessHeap(), 0, buf);
555 return hr;
556 }
557
558 static header_t *read_prop(MimeBody *body, char **ptr)
559 {
560 char *colon = strchr(*ptr, ':');
561 const property_t *prop;
562 header_t *ret;
563
564 if(!colon) return NULL;
565
566 *colon = '\0';
567
568 for(prop = default_props; prop->name; prop++)
569 {
570 if(!lstrcmpiA(*ptr, prop->name))
571 {
572 TRACE("%s: found match with default property id %d\n", *ptr, prop->id);
573 break;
574 }
575 }
576
577 if(!prop->name)
578 {
579 property_list_entry_t *prop_entry;
580 LIST_FOR_EACH_ENTRY(prop_entry, &body->new_props, property_list_entry_t, entry)
581 {
582 if(!lstrcmpiA(*ptr, prop_entry->prop.name))
583 {
584 TRACE("%s: found match with already added new property id %d\n", *ptr, prop_entry->prop.id);
585 prop = &prop_entry->prop;
586 break;
587 }
588 }
589 if(!prop->name)
590 {
591 prop_entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*prop_entry));
592 prop_entry->prop.name = strdupA(*ptr);
593 prop_entry->prop.id = body->next_prop_id++;
594 prop_entry->prop.flags = 0;
595 prop_entry->prop.default_vt = VT_LPSTR;
596 list_add_tail(&body->new_props, &prop_entry->entry);
597 prop = &prop_entry->prop;
598 TRACE("%s: allocating new prop id %d\n", *ptr, prop_entry->prop.id);
599 }
600 }
601
602 ret = HeapAlloc(GetProcessHeap(), 0, sizeof(*ret));
603 ret->prop = prop;
604 PropVariantInit(&ret->value);
605 list_init(&ret->params);
606 *ptr = colon + 1;
607
608 return ret;
609 }
610
611 static void unfold_header(char *header, int len)
612 {
613 char *start = header, *cp = header;
614
615 do {
616 while(*cp == ' ' || *cp == '\t')
617 {
618 cp++;
619 len--;
620 }
621 if(cp != start)
622 memmove(start, cp, len + 1);
623
624 cp = strstr(start, "\r\n");
625 len -= (cp - start);
626 start = cp;
627 *start = ' ';
628 start++;
629 len--;
630 cp += 2;
631 } while(*cp == ' ' || *cp == '\t');
632
633 *(start - 1) = '\0';
634 }
635
636 static char *unquote_string(const char *str)
637 {
638 BOOL quoted = FALSE;
639 char *ret, *cp;
640
641 while(*str == ' ' || *str == '\t') str++;
642
643 if(*str == '"')
644 {
645 quoted = TRUE;
646 str++;
647 }
648 ret = strdupA(str);
649 for(cp = ret; *cp; cp++)
650 {
651 if(*cp == '\\')
652 memmove(cp, cp + 1, strlen(cp + 1) + 1);
653 else if(*cp == '"')
654 {
655 if(!quoted)
656 {
657 WARN("quote in unquoted string\n");
658 }
659 else
660 {
661 *cp = '\0';
662 break;
663 }
664 }
665 }
666 return ret;
667 }
668
669 static void add_param(header_t *header, const char *p)
670 {
671 const char *key = p, *value, *cp = p;
672 param_t *param;
673 char *name;
674
675 TRACE("got param %s\n", p);
676
677 while (*key == ' ' || *key == '\t' ) key++;
678
679 cp = strchr(key, '=');
680 if(!cp)
681 {
682 WARN("malformed parameter - skipping\n");
683 return;
684 }
685
686 name = HeapAlloc(GetProcessHeap(), 0, cp - key + 1);
687 memcpy(name, key, cp - key);
688 name[cp - key] = '\0';
689
690 value = cp + 1;
691
692 param = HeapAlloc(GetProcessHeap(), 0, sizeof(*param));
693 param->name = name;
694 param->value = unquote_string(value);
695 list_add_tail(&header->params, &param->entry);
696 }
697
698 static void split_params(header_t *header, char *value)
699 {
700 char *cp = value, *start = value;
701 BOOL in_quotes = FALSE, done_value = FALSE;
702
703 while(*cp)
704 {
705 if(!in_quotes && *cp == ';')
706 {
707 *cp = '\0';
708 if(done_value) add_param(header, start);
709 done_value = TRUE;
710 start = cp + 1;
711 }
712 else if(*cp == '"')
713 in_quotes = !in_quotes;
714 cp++;
715 }
716 if(done_value) add_param(header, start);
717 }
718
719 static void read_value(header_t *header, char **cur)
720 {
721 char *end = *cur, *value;
722 DWORD len;
723
724 do {
725 end = strstr(end, "\r\n");
726 end += 2;
727 } while(*end == ' ' || *end == '\t');
728
729 len = end - *cur;
730 value = HeapAlloc(GetProcessHeap(), 0, len + 1);
731 memcpy(value, *cur, len);
732 value[len] = '\0';
733
734 unfold_header(value, len);
735 TRACE("value %s\n", debugstr_a(value));
736
737 if(header->prop->flags & MPF_HASPARAMS)
738 {
739 split_params(header, value);
740 TRACE("value w/o params %s\n", debugstr_a(value));
741 }
742
743 header->value.vt = VT_LPSTR;
744 header->value.u.pszVal = value;
745
746 *cur = end;
747 }
748
749 static void init_content_type(MimeBody *body, header_t *header)
750 {
751 char *slash;
752 DWORD len;
753
754 slash = strchr(header->value.u.pszVal, '/');
755 if(!slash)
756 {
757 WARN("malformed context type value\n");
758 return;
759 }
760 len = slash - header->value.u.pszVal;
761 body->content_pri_type = HeapAlloc(GetProcessHeap(), 0, len + 1);
762 memcpy(body->content_pri_type, header->value.u.pszVal, len);
763 body->content_pri_type[len] = '\0';
764 body->content_sub_type = strdupA(slash + 1);
765 }
766
767 static void init_content_encoding(MimeBody *body, header_t *header)
768 {
769 const char *encoding = header->value.u.pszVal;
770
771 if(!_strnicmp(encoding, "base64", -1))
772 body->encoding = IET_BASE64;
773 else if(!_strnicmp(encoding, "quoted-printable", -1))
774 body->encoding = IET_QP;
775 else if(!_strnicmp(encoding, "7bit", -1))
776 body->encoding = IET_7BIT;
777 else if(!_strnicmp(encoding, "8bit", -1))
778 body->encoding = IET_8BIT;
779 else
780 FIXME("unknown encoding %s\n", debugstr_a(encoding));
781 }
782
783 static HRESULT parse_headers(MimeBody *body, IStream *stm)
784 {
785 char *header_buf, *cur_header_ptr;
786 HRESULT hr;
787 header_t *header;
788
789 hr = copy_headers_to_buf(stm, &header_buf);
790 if(FAILED(hr)) return hr;
791
792 cur_header_ptr = header_buf;
793 while((header = read_prop(body, &cur_header_ptr)))
794 {
795 read_value(header, &cur_header_ptr);
796 list_add_tail(&body->headers, &header->entry);
797
798 switch(header->prop->id) {
799 case PID_HDR_CNTTYPE:
800 init_content_type(body, header);
801 break;
802 case PID_HDR_CNTXFER:
803 init_content_encoding(body, header);
804 break;
805 }
806 }
807
808 HeapFree(GetProcessHeap(), 0, header_buf);
809 return hr;
810 }
811
812 static void empty_param_list(struct list *list)
813 {
814 param_t *param, *cursor2;
815
816 LIST_FOR_EACH_ENTRY_SAFE(param, cursor2, list, param_t, entry)
817 {
818 list_remove(&param->entry);
819 HeapFree(GetProcessHeap(), 0, param->name);
820 HeapFree(GetProcessHeap(), 0, param->value);
821 HeapFree(GetProcessHeap(), 0, param);
822 }
823 }
824
825 static void free_header(header_t *header)
826 {
827 list_remove(&header->entry);
828 PropVariantClear(&header->value);
829 empty_param_list(&header->params);
830 heap_free(header);
831 }
832
833 static void empty_header_list(struct list *list)
834 {
835 header_t *header, *cursor2;
836
837 LIST_FOR_EACH_ENTRY_SAFE(header, cursor2, list, header_t, entry)
838 {
839 free_header(header);
840 }
841 }
842
843 static void empty_new_prop_list(struct list *list)
844 {
845 property_list_entry_t *prop, *cursor2;
846
847 LIST_FOR_EACH_ENTRY_SAFE(prop, cursor2, list, property_list_entry_t, entry)
848 {
849 list_remove(&prop->entry);
850 HeapFree(GetProcessHeap(), 0, (char *)prop->prop.name);
851 HeapFree(GetProcessHeap(), 0, prop);
852 }
853 }
854
855 static void release_data(REFIID riid, void *data)
856 {
857 if(!data) return;
858
859 if(IsEqualIID(riid, &IID_IStream))
860 IStream_Release((IStream *)data);
861 else
862 FIXME("Unhandled data format %s\n", debugstr_guid(riid));
863 }
864
865 static HRESULT find_prop(MimeBody *body, const char *name, header_t **prop)
866 {
867 header_t *header;
868
869 *prop = NULL;
870
871 LIST_FOR_EACH_ENTRY(header, &body->headers, header_t, entry)
872 {
873 if(ISPIDSTR(name))
874 {
875 if(STRTOPID(name) == header->prop->id)
876 {
877 *prop = header;
878 return S_OK;
879 }
880 }
881 else if(!lstrcmpiA(name, header->prop->name))
882 {
883 *prop = header;
884 return S_OK;
885 }
886 }
887
888 return MIME_E_NOT_FOUND;
889 }
890
891 static const property_t *find_default_prop(const char *name)
892 {
893 const property_t *prop_def = NULL;
894
895 for(prop_def = default_props; prop_def->name; prop_def++)
896 {
897 if(ISPIDSTR(name))
898 {
899 if(STRTOPID(name) == prop_def->id)
900 {
901 break;
902 }
903 }
904 else if(!lstrcmpiA(name, prop_def->name))
905 {
906 break;
907 }
908 }
909
910 if(prop_def->id)
911 TRACE("%s: found match with default property id %d\n", prop_def->name, prop_def->id);
912 else
913 prop_def = NULL;
914
915 return prop_def;
916 }
917
918 static HRESULT WINAPI MimeBody_QueryInterface(IMimeBody* iface,
919 REFIID riid,
920 void** ppvObject)
921 {
922 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppvObject);
923
924 *ppvObject = NULL;
925
926 if (IsEqualIID(riid, &IID_IUnknown) ||
927 IsEqualIID(riid, &IID_IPersist) ||
928 IsEqualIID(riid, &IID_IPersistStreamInit) ||
929 IsEqualIID(riid, &IID_IMimePropertySet) ||
930 IsEqualIID(riid, &IID_IMimeBody))
931 {
932 *ppvObject = iface;
933 }
934
935 if(*ppvObject)
936 {
937 IUnknown_AddRef((IUnknown*)*ppvObject);
938 return S_OK;
939 }
940
941 FIXME("no interface for %s\n", debugstr_guid(riid));
942 return E_NOINTERFACE;
943 }
944
945 static ULONG WINAPI MimeBody_AddRef(IMimeBody *iface)
946 {
947 MimeBody *This = impl_from_IMimeBody(iface);
948 LONG ref = InterlockedIncrement(&This->ref);
949
950 TRACE("(%p) ref=%d\n", This, ref);
951
952 return ref;
953 }
954
955 static ULONG WINAPI MimeBody_Release(IMimeBody *iface)
956 {
957 MimeBody *This = impl_from_IMimeBody(iface);
958 LONG ref = InterlockedDecrement(&This->ref);
959
960 TRACE("(%p) ref=%d\n", This, ref);
961
962 if (!ref)
963 {
964 empty_header_list(&This->headers);
965 empty_new_prop_list(&This->new_props);
966
967 HeapFree(GetProcessHeap(), 0, This->content_pri_type);
968 HeapFree(GetProcessHeap(), 0, This->content_sub_type);
969
970 release_data(&This->data_iid, This->data);
971
972 HeapFree(GetProcessHeap(), 0, This);
973 }
974
975 return ref;
976 }
977
978 static HRESULT WINAPI MimeBody_GetClassID(
979 IMimeBody* iface,
980 CLSID* pClassID)
981 {
982 MimeBody *This = impl_from_IMimeBody(iface);
983
984 TRACE("(%p)->(%p)\n", This, pClassID);
985
986 if(!pClassID)
987 return E_INVALIDARG;
988
989 *pClassID = IID_IMimeBody;
990 return S_OK;
991 }
992
993 static HRESULT WINAPI MimeBody_IsDirty(
994 IMimeBody* iface)
995 {
996 MimeBody *This = impl_from_IMimeBody(iface);
997 FIXME("(%p)->() stub\n", This);
998 return E_NOTIMPL;
999 }
1000
1001 static HRESULT WINAPI MimeBody_Load(IMimeBody *iface, IStream *pStm)
1002 {
1003 MimeBody *This = impl_from_IMimeBody(iface);
1004 TRACE("(%p)->(%p)\n", This, pStm);
1005 return parse_headers(This, pStm);
1006 }
1007
1008 static HRESULT WINAPI MimeBody_Save(IMimeBody *iface, IStream *pStm, BOOL fClearDirty)
1009 {
1010 MimeBody *This = impl_from_IMimeBody(iface);
1011 FIXME("(%p)->(%p, %d)\n", This, pStm, fClearDirty);
1012 return E_NOTIMPL;
1013 }
1014
1015 static HRESULT WINAPI MimeBody_GetSizeMax(
1016 IMimeBody* iface,
1017 ULARGE_INTEGER* pcbSize)
1018 {
1019 MimeBody *This = impl_from_IMimeBody(iface);
1020 FIXME("(%p)->(%p) stub\n", This, pcbSize);
1021 return E_NOTIMPL;
1022 }
1023
1024 static HRESULT WINAPI MimeBody_InitNew(
1025 IMimeBody* iface)
1026 {
1027 MimeBody *This = impl_from_IMimeBody(iface);
1028 TRACE("(%p)->()\n", This);
1029 return S_OK;
1030 }
1031
1032 static HRESULT WINAPI MimeBody_GetPropInfo(
1033 IMimeBody* iface,
1034 LPCSTR pszName,
1035 LPMIMEPROPINFO pInfo)
1036 {
1037 MimeBody *This = impl_from_IMimeBody(iface);
1038 header_t *header;
1039 HRESULT hr;
1040 DWORD supported = PIM_PROPID | PIM_VTDEFAULT;
1041
1042 TRACE("(%p)->(%s, %p) semi-stub\n", This, debugstr_a(pszName), pInfo);
1043
1044 if(!pszName || !pInfo)
1045 return E_INVALIDARG;
1046
1047 TRACE("mask 0x%04x\n", pInfo->dwMask);
1048
1049 if(pInfo->dwMask & ~supported)
1050 FIXME("Unsupported mask flags 0x%04x\n", pInfo->dwMask & ~supported);
1051
1052 hr = find_prop(This, pszName, &header);
1053 if(hr == S_OK)
1054 {
1055 if(pInfo->dwMask & PIM_CHARSET)
1056 pInfo->hCharset = 0;
1057 if(pInfo->dwMask & PIM_FLAGS)
1058 pInfo->dwFlags = 0x00000000;
1059 if(pInfo->dwMask & PIM_ROWNUMBER)
1060 pInfo->dwRowNumber = 0;
1061 if(pInfo->dwMask & PIM_ENCODINGTYPE)
1062 pInfo->ietEncoding = 0;
1063 if(pInfo->dwMask & PIM_VALUES)
1064 pInfo->cValues = 0;
1065 if(pInfo->dwMask & PIM_PROPID)
1066 pInfo->dwPropId = header->prop->id;
1067 if(pInfo->dwMask & PIM_VTDEFAULT)
1068 pInfo->vtDefault = header->prop->default_vt;
1069 if(pInfo->dwMask & PIM_VTCURRENT)
1070 pInfo->vtCurrent = 0;
1071 }
1072
1073 return hr;
1074 }
1075
1076 static HRESULT WINAPI MimeBody_SetPropInfo(
1077 IMimeBody* iface,
1078 LPCSTR pszName,
1079 LPCMIMEPROPINFO pInfo)
1080 {
1081 MimeBody *This = impl_from_IMimeBody(iface);
1082 FIXME("(%p)->(%s, %p) stub\n", This, debugstr_a(pszName), pInfo);
1083 return E_NOTIMPL;
1084 }
1085
1086 static HRESULT WINAPI MimeBody_GetProp(
1087 IMimeBody* iface,
1088 LPCSTR pszName,
1089 DWORD dwFlags,
1090 LPPROPVARIANT pValue)
1091 {
1092 MimeBody *This = impl_from_IMimeBody(iface);
1093 header_t *header;
1094 HRESULT hr;
1095
1096 TRACE("(%p)->(%s, 0x%x, %p)\n", This, debugstr_a(pszName), dwFlags, pValue);
1097
1098 if(!pszName || !pValue)
1099 return E_INVALIDARG;
1100
1101 if(!ISPIDSTR(pszName) && !lstrcmpiA(pszName, "att:pri-content-type"))
1102 {
1103 PropVariantClear(pValue);
1104 pValue->vt = VT_LPSTR;
1105 pValue->u.pszVal = strdupA(This->content_pri_type);
1106 return S_OK;
1107 }
1108
1109 hr = find_prop(This, pszName, &header);
1110 if(hr == S_OK)
1111 {
1112 TRACE("type %d->%d\n", header->value.vt, pValue->vt);
1113
1114 hr = PropVariantChangeType(pValue, &header->value, 0, pValue->vt);
1115 if(FAILED(hr))
1116 FIXME("Conversion not currently supported (%d->%d)\n", header->value.vt, pValue->vt);
1117 }
1118
1119 return hr;
1120 }
1121
1122 static HRESULT WINAPI MimeBody_SetProp(
1123 IMimeBody* iface,
1124 LPCSTR pszName,
1125 DWORD dwFlags,
1126 LPCPROPVARIANT pValue)
1127 {
1128 MimeBody *This = impl_from_IMimeBody(iface);
1129 header_t *header;
1130 HRESULT hr;
1131
1132 TRACE("(%p)->(%s, 0x%x, %p)\n", This, debugstr_a(pszName), dwFlags, pValue);
1133
1134 if(!pszName || !pValue)
1135 return E_INVALIDARG;
1136
1137 hr = find_prop(This, pszName, &header);
1138 if(hr != S_OK)
1139 {
1140 property_list_entry_t *prop_entry;
1141 const property_t *prop = NULL;
1142
1143 LIST_FOR_EACH_ENTRY(prop_entry, &This->new_props, property_list_entry_t, entry)
1144 {
1145 if(ISPIDSTR(pszName))
1146 {
1147 if(STRTOPID(pszName) == prop_entry->prop.id)
1148 {
1149 TRACE("Found match with already added new property id %d\n", prop_entry->prop.id);
1150 prop = &prop_entry->prop;
1151 break;
1152 }
1153 }
1154 else if(!lstrcmpiA(pszName, prop_entry->prop.name))
1155 {
1156 TRACE("Found match with already added new property id %d\n", prop_entry->prop.id);
1157 prop = &prop_entry->prop;
1158 break;
1159 }
1160 }
1161
1162 header = HeapAlloc(GetProcessHeap(), 0, sizeof(*header));
1163 if(!header)
1164 return E_OUTOFMEMORY;
1165
1166 if(!prop)
1167 {
1168 const property_t *prop_def = NULL;
1169 prop_entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*prop_entry));
1170 if(!prop_entry)
1171 {
1172 HeapFree(GetProcessHeap(), 0, header);
1173 return E_OUTOFMEMORY;
1174 }
1175
1176 prop_def = find_default_prop(pszName);
1177 if(prop_def)
1178 {
1179 prop_entry->prop.name = strdupA(prop_def->name);
1180 prop_entry->prop.id = prop_def->id;
1181 }
1182 else
1183 {
1184 if(ISPIDSTR(pszName))
1185 {
1186 HeapFree(GetProcessHeap(), 0, prop_entry);
1187 HeapFree(GetProcessHeap(), 0, header);
1188 return MIME_E_NOT_FOUND;
1189 }
1190
1191 prop_entry->prop.name = strdupA(pszName);
1192 prop_entry->prop.id = This->next_prop_id++;
1193 }
1194
1195 prop_entry->prop.flags = 0;
1196 prop_entry->prop.default_vt = pValue->vt;
1197 list_add_tail(&This->new_props, &prop_entry->entry);
1198 prop = &prop_entry->prop;
1199 TRACE("Allocating new prop id %d\n", prop_entry->prop.id);
1200 }
1201
1202 header->prop = prop;
1203 PropVariantInit(&header->value);
1204 list_init(&header->params);
1205 list_add_tail(&This->headers, &header->entry);
1206 }
1207
1208 PropVariantCopy(&header->value, pValue);
1209
1210 return S_OK;
1211 }
1212
1213 static HRESULT WINAPI MimeBody_AppendProp(
1214 IMimeBody* iface,
1215 LPCSTR pszName,
1216 DWORD dwFlags,
1217 LPPROPVARIANT pValue)
1218 {
1219 MimeBody *This = impl_from_IMimeBody(iface);
1220 FIXME("(%p)->(%s, 0x%x, %p) stub\n", This, debugstr_a(pszName), dwFlags, pValue);
1221 return E_NOTIMPL;
1222 }
1223
1224 static HRESULT WINAPI MimeBody_DeleteProp(
1225 IMimeBody* iface,
1226 LPCSTR pszName)
1227 {
1228 MimeBody *This = impl_from_IMimeBody(iface);
1229 header_t *cursor;
1230 BOOL found;
1231
1232 TRACE("(%p)->(%s) stub\n", This, debugstr_a(pszName));
1233
1234 LIST_FOR_EACH_ENTRY(cursor, &This->headers, header_t, entry)
1235 {
1236 if(ISPIDSTR(pszName))
1237 found = STRTOPID(pszName) == cursor->prop->id;
1238 else
1239 found = !lstrcmpiA(pszName, cursor->prop->name);
1240
1241 if(found)
1242 {
1243 free_header(cursor);
1244 return S_OK;
1245 }
1246 }
1247
1248 return MIME_E_NOT_FOUND;
1249 }
1250
1251 static HRESULT WINAPI MimeBody_CopyProps(
1252 IMimeBody* iface,
1253 ULONG cNames,
1254 LPCSTR* prgszName,
1255 IMimePropertySet* pPropertySet)
1256 {
1257 MimeBody *This = impl_from_IMimeBody(iface);
1258 FIXME("(%p)->(%d, %p, %p) stub\n", This, cNames, prgszName, pPropertySet);
1259 return E_NOTIMPL;
1260 }
1261
1262 static HRESULT WINAPI MimeBody_MoveProps(
1263 IMimeBody* iface,
1264 ULONG cNames,
1265 LPCSTR* prgszName,
1266 IMimePropertySet* pPropertySet)
1267 {
1268 MimeBody *This = impl_from_IMimeBody(iface);
1269 FIXME("(%p)->(%d, %p, %p) stub\n", This, cNames, prgszName, pPropertySet);
1270 return E_NOTIMPL;
1271 }
1272
1273 static HRESULT WINAPI MimeBody_DeleteExcept(
1274 IMimeBody* iface,
1275 ULONG cNames,
1276 LPCSTR* prgszName)
1277 {
1278 MimeBody *This = impl_from_IMimeBody(iface);
1279 FIXME("(%p)->(%d, %p) stub\n", This, cNames, prgszName);
1280 return E_NOTIMPL;
1281 }
1282
1283 static HRESULT WINAPI MimeBody_QueryProp(
1284 IMimeBody* iface,
1285 LPCSTR pszName,
1286 LPCSTR pszCriteria,
1287 boolean fSubString,
1288 boolean fCaseSensitive)
1289 {
1290 MimeBody *This = impl_from_IMimeBody(iface);
1291 FIXME("(%p)->(%s, %s, %d, %d) stub\n", This, debugstr_a(pszName), debugstr_a(pszCriteria), fSubString, fCaseSensitive);
1292 return E_NOTIMPL;
1293 }
1294
1295 static HRESULT WINAPI MimeBody_GetCharset(
1296 IMimeBody* iface,
1297 LPHCHARSET phCharset)
1298 {
1299 MimeBody *This = impl_from_IMimeBody(iface);
1300 FIXME("(%p)->(%p) stub\n", This, phCharset);
1301 *phCharset = NULL;
1302 return S_OK;
1303 }
1304
1305 static HRESULT WINAPI MimeBody_SetCharset(
1306 IMimeBody* iface,
1307 HCHARSET hCharset,
1308 CSETAPPLYTYPE applytype)
1309 {
1310 MimeBody *This = impl_from_IMimeBody(iface);
1311 FIXME("(%p)->(%p, %d) stub\n", This, hCharset, applytype);
1312 return E_NOTIMPL;
1313 }
1314
1315 static HRESULT WINAPI MimeBody_GetParameters(
1316 IMimeBody* iface,
1317 LPCSTR pszName,
1318 ULONG* pcParams,
1319 LPMIMEPARAMINFO* pprgParam)
1320 {
1321 MimeBody *This = impl_from_IMimeBody(iface);
1322 HRESULT hr;
1323 header_t *header;
1324
1325 TRACE("(%p)->(%s, %p, %p)\n", iface, debugstr_a(pszName), pcParams, pprgParam);
1326
1327 *pprgParam = NULL;
1328 *pcParams = 0;
1329
1330 hr = find_prop(This, pszName, &header);
1331 if(hr != S_OK) return hr;
1332
1333 *pcParams = list_count(&header->params);
1334 if(*pcParams)
1335 {
1336 IMimeAllocator *alloc;
1337 param_t *param;
1338 MIMEPARAMINFO *info;
1339
1340 MimeOleGetAllocator(&alloc);
1341
1342 *pprgParam = info = IMimeAllocator_Alloc(alloc, *pcParams * sizeof(**pprgParam));
1343 LIST_FOR_EACH_ENTRY(param, &header->params, param_t, entry)
1344 {
1345 int len;
1346
1347 len = strlen(param->name) + 1;
1348 info->pszName = IMimeAllocator_Alloc(alloc, len);
1349 memcpy(info->pszName, param->name, len);
1350 len = strlen(param->value) + 1;
1351 info->pszData = IMimeAllocator_Alloc(alloc, len);
1352 memcpy(info->pszData, param->value, len);
1353 info++;
1354 }
1355 IMimeAllocator_Release(alloc);
1356 }
1357 return S_OK;
1358 }
1359
1360 static HRESULT WINAPI MimeBody_IsContentType(
1361 IMimeBody* iface,
1362 LPCSTR pszPriType,
1363 LPCSTR pszSubType)
1364 {
1365 MimeBody *This = impl_from_IMimeBody(iface);
1366
1367 TRACE("(%p)->(%s, %s)\n", This, debugstr_a(pszPriType), debugstr_a(pszSubType));
1368 if(pszPriType)
1369 {
1370 const char *pri = This->content_pri_type;
1371 if(!pri) pri = "text";
1372 if(lstrcmpiA(pri, pszPriType)) return S_FALSE;
1373 }
1374
1375 if(pszSubType)
1376 {
1377 const char *sub = This->content_sub_type;
1378 if(!sub) sub = "plain";
1379 if(lstrcmpiA(sub, pszSubType)) return S_FALSE;
1380 }
1381
1382 return S_OK;
1383 }
1384
1385 static HRESULT WINAPI MimeBody_BindToObject(
1386 IMimeBody* iface,
1387 REFIID riid,
1388 void** ppvObject)
1389 {
1390 MimeBody *This = impl_from_IMimeBody(iface);
1391 FIXME("(%p)->(%s, %p) stub\n", This, debugstr_guid(riid), ppvObject);
1392 return E_NOTIMPL;
1393 }
1394
1395 static HRESULT WINAPI MimeBody_Clone(
1396 IMimeBody* iface,
1397 IMimePropertySet** ppPropertySet)
1398 {
1399 MimeBody *This = impl_from_IMimeBody(iface);
1400 FIXME("(%p)->(%p) stub\n", This, ppPropertySet);
1401 return E_NOTIMPL;
1402 }
1403
1404 static HRESULT WINAPI MimeBody_SetOption(
1405 IMimeBody* iface,
1406 const TYPEDID oid,
1407 LPCPROPVARIANT pValue)
1408 {
1409 MimeBody *This = impl_from_IMimeBody(iface);
1410 HRESULT hr = E_NOTIMPL;
1411 TRACE("(%p)->(%08x, %p)\n", This, oid, pValue);
1412
1413 if(pValue->vt != TYPEDID_TYPE(oid))
1414 {
1415 WARN("Called with vartype %04x and oid %08x\n", pValue->vt, oid);
1416 return E_INVALIDARG;
1417 }
1418
1419 switch(oid)
1420 {
1421 case OID_SECURITY_HWND_OWNER:
1422 FIXME("OID_SECURITY_HWND_OWNER (value %08x): ignoring\n", pValue->u.ulVal);
1423 hr = S_OK;
1424 break;
1425 case OID_TRANSMIT_BODY_ENCODING:
1426 FIXME("OID_TRANSMIT_BODY_ENCODING (value %08x): ignoring\n", pValue->u.ulVal);
1427 hr = S_OK;
1428 break;
1429 default:
1430 FIXME("Unhandled oid %08x\n", oid);
1431 }
1432
1433 return hr;
1434 }
1435
1436 static HRESULT WINAPI MimeBody_GetOption(
1437 IMimeBody* iface,
1438 const TYPEDID oid,
1439 LPPROPVARIANT pValue)
1440 {
1441 MimeBody *This = impl_from_IMimeBody(iface);
1442 FIXME("(%p)->(%08x, %p): stub\n", This, oid, pValue);
1443 return E_NOTIMPL;
1444 }
1445
1446 static HRESULT WINAPI MimeBody_EnumProps(
1447 IMimeBody* iface,
1448 DWORD dwFlags,
1449 IMimeEnumProperties** ppEnum)
1450 {
1451 MimeBody *This = impl_from_IMimeBody(iface);
1452 FIXME("(%p)->(0x%x, %p) stub\n", This, dwFlags, ppEnum);
1453 return E_NOTIMPL;
1454 }
1455
1456 static HRESULT WINAPI MimeBody_IsType(
1457 IMimeBody* iface,
1458 IMSGBODYTYPE bodytype)
1459 {
1460 MimeBody *This = impl_from_IMimeBody(iface);
1461
1462 TRACE("(%p)->(%d)\n", This, bodytype);
1463 switch(bodytype)
1464 {
1465 case IBT_EMPTY:
1466 return This->data ? S_FALSE : S_OK;
1467 default:
1468 FIXME("Unimplemented bodytype %d - returning S_OK\n", bodytype);
1469 }
1470 return S_OK;
1471 }
1472
1473 static HRESULT WINAPI MimeBody_SetDisplayName(
1474 IMimeBody* iface,
1475 LPCSTR pszDisplay)
1476 {
1477 MimeBody *This = impl_from_IMimeBody(iface);
1478 FIXME("(%p)->(%s) stub\n", This, debugstr_a(pszDisplay));
1479 return E_NOTIMPL;
1480 }
1481
1482 static HRESULT WINAPI MimeBody_GetDisplayName(
1483 IMimeBody* iface,
1484 LPSTR* ppszDisplay)
1485 {
1486 MimeBody *This = impl_from_IMimeBody(iface);
1487 FIXME("(%p)->(%p) stub\n", This, ppszDisplay);
1488 return E_NOTIMPL;
1489 }
1490
1491 static HRESULT WINAPI MimeBody_GetOffsets(
1492 IMimeBody* iface,
1493 LPBODYOFFSETS pOffsets)
1494 {
1495 MimeBody *This = impl_from_IMimeBody(iface);
1496 TRACE("(%p)->(%p)\n", This, pOffsets);
1497
1498 *pOffsets = This->body_offsets;
1499
1500 if(This->body_offsets.cbBodyEnd == 0) return MIME_E_NO_DATA;
1501 return S_OK;
1502 }
1503
1504 static HRESULT WINAPI MimeBody_GetCurrentEncoding(
1505 IMimeBody* iface,
1506 ENCODINGTYPE* pietEncoding)
1507 {
1508 MimeBody *This = impl_from_IMimeBody(iface);
1509
1510 TRACE("(%p)->(%p)\n", This, pietEncoding);
1511
1512 *pietEncoding = This->encoding;
1513 return S_OK;
1514 }
1515
1516 static HRESULT WINAPI MimeBody_SetCurrentEncoding(
1517 IMimeBody* iface,
1518 ENCODINGTYPE ietEncoding)
1519 {
1520 MimeBody *This = impl_from_IMimeBody(iface);
1521
1522 TRACE("(%p)->(%d)\n", This, ietEncoding);
1523
1524 This->encoding = ietEncoding;
1525 return S_OK;
1526 }
1527
1528 static HRESULT WINAPI MimeBody_GetEstimatedSize(
1529 IMimeBody* iface,
1530 ENCODINGTYPE ietEncoding,
1531 ULONG* pcbSize)
1532 {
1533 MimeBody *This = impl_from_IMimeBody(iface);
1534 FIXME("(%p)->(%d, %p) stub\n", This, ietEncoding, pcbSize);
1535 return E_NOTIMPL;
1536 }
1537
1538 static HRESULT WINAPI MimeBody_GetDataHere(
1539 IMimeBody* iface,
1540 ENCODINGTYPE ietEncoding,
1541 IStream* pStream)
1542 {
1543 MimeBody *This = impl_from_IMimeBody(iface);
1544 FIXME("(%p)->(%d, %p) stub\n", This, ietEncoding, pStream);
1545 return E_NOTIMPL;
1546 }
1547
1548 static const signed char base64_decode_table[] =
1549 {
1550 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00 */
1551 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10 */
1552 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20 */
1553 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30 */
1554 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40 */
1555 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50 */
1556 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60 */
1557 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70 */
1558 };
1559
1560 static HRESULT decode_base64(IStream *input, IStream **ret_stream)
1561 {
1562 const unsigned char *ptr, *end;
1563 unsigned char buf[1024];
1564 LARGE_INTEGER pos;
1565 unsigned char *ret;
1566 unsigned char in[4];
1567 IStream *output;
1568 DWORD size;
1569 int n = 0;
1570 HRESULT hres;
1571
1572 pos.QuadPart = 0;
1573 hres = IStream_Seek(input, pos, STREAM_SEEK_SET, NULL);
1574 if(FAILED(hres))
1575 return hres;
1576
1577 hres = CreateStreamOnHGlobal(NULL, TRUE, &output);
1578 if(FAILED(hres))
1579 return hres;
1580
1581 while(1) {
1582 hres = IStream_Read(input, buf, sizeof(buf), &size);
1583 if(FAILED(hres) || !size)
1584 break;
1585
1586 ptr = ret = buf;
1587 end = buf + size;
1588
1589 while(1) {
1590 /* skip invalid chars */
1591 while(ptr < end && (*ptr >= ARRAY_SIZE(base64_decode_table)
1592 || base64_decode_table[*ptr] == -1))
1593 ptr++;
1594 if(ptr == end)
1595 break;
1596
1597 in[n++] = base64_decode_table[*ptr++];
1598 switch(n) {
1599 case 2:
1600 *ret++ = in[0] << 2 | in[1] >> 4;
1601 continue;
1602 case 3:
1603 *ret++ = in[1] << 4 | in[2] >> 2;
1604 continue;
1605 case 4:
1606 *ret++ = ((in[2] << 6) & 0xc0) | in[3];
1607 n = 0;
1608 }
1609 }
1610
1611 if(ret > buf) {
1612 hres = IStream_Write(output, buf, ret - buf, NULL);
1613 if(FAILED(hres))
1614 break;
1615 }
1616 }
1617
1618 if(SUCCEEDED(hres))
1619 hres = IStream_Seek(output, pos, STREAM_SEEK_SET, NULL);
1620 if(FAILED(hres)) {
1621 IStream_Release(output);
1622 return hres;
1623 }
1624
1625 *ret_stream = output;
1626 return S_OK;
1627 }
1628
1629 static int hex_digit(char c)
1630 {
1631 if('0' <= c && c <= '9')
1632 return c - '0';
1633 if('A' <= c && c <= 'F')
1634 return c - 'A' + 10;
1635 if('a' <= c && c <= 'f')
1636 return c - 'a' + 10;
1637 return -1;
1638 }
1639
1640 static HRESULT decode_qp(IStream *input, IStream **ret_stream)
1641 {
1642 const unsigned char *ptr, *end;
1643 unsigned char *ret, prev = 0;
1644 unsigned char buf[1024];
1645 LARGE_INTEGER pos;
1646 IStream *output;
1647 DWORD size;
1648 int n = -1;
1649 HRESULT hres;
1650
1651 pos.QuadPart = 0;
1652 hres = IStream_Seek(input, pos, STREAM_SEEK_SET, NULL);
1653 if(FAILED(hres))
1654 return hres;
1655
1656 hres = CreateStreamOnHGlobal(NULL, TRUE, &output);
1657 if(FAILED(hres))
1658 return hres;
1659
1660 while(1) {
1661 hres = IStream_Read(input, buf, sizeof(buf), &size);
1662 if(FAILED(hres) || !size)
1663 break;
1664
1665 ptr = ret = buf;
1666 end = buf + size;
1667
1668 while(ptr < end) {
1669 unsigned char byte = *ptr++;
1670
1671 switch(n) {
1672 case -1:
1673 if(byte == '=')
1674 n = 0;
1675 else
1676 *ret++ = byte;
1677 continue;
1678 case 0:
1679 prev = byte;
1680 n = 1;
1681 continue;
1682 case 1:
1683 if(prev != '\r' || byte != '\n') {
1684 int h1 = hex_digit(prev), h2 = hex_digit(byte);
1685 if(h1 != -1 && h2 != -1)
1686 *ret++ = (h1 << 4) | h2;
1687 else
1688 *ret++ = '=';
1689 }
1690 n = -1;
1691 continue;
1692 }
1693 }
1694
1695 if(ret > buf) {
1696 hres = IStream_Write(output, buf, ret - buf, NULL);
1697 if(FAILED(hres))
1698 break;
1699 }
1700 }
1701
1702 if(SUCCEEDED(hres))
1703 hres = IStream_Seek(output, pos, STREAM_SEEK_SET, NULL);
1704 if(FAILED(hres)) {
1705 IStream_Release(output);
1706 return hres;
1707 }
1708
1709 *ret_stream = output;
1710 return S_OK;
1711 }
1712
1713 static HRESULT WINAPI MimeBody_GetData(
1714 IMimeBody* iface,
1715 ENCODINGTYPE ietEncoding,
1716 IStream** ppStream)
1717 {
1718 MimeBody *This = impl_from_IMimeBody(iface);
1719 ULARGE_INTEGER start, size;
1720 HRESULT hres;
1721
1722 TRACE("(%p)->(%d %p)\n", This, ietEncoding, ppStream);
1723
1724 if(This->encoding != ietEncoding) {
1725 switch(This->encoding) {
1726 case IET_BASE64:
1727 hres = decode_base64(This->data, ppStream);
1728 break;
1729 case IET_QP:
1730 hres = decode_qp(This->data, ppStream);
1731 break;
1732 default:
1733 FIXME("Decoding %d is not supported.\n", This->encoding);
1734 hres = S_FALSE;
1735 }
1736 if(ietEncoding != IET_BINARY)
1737 FIXME("Encoding %d is not supported.\n", ietEncoding);
1738 if(hres != S_FALSE)
1739 return hres;
1740 }
1741
1742 start.QuadPart = 0;
1743 hres = get_stream_size(This->data, &size);
1744 if(SUCCEEDED(hres))
1745 hres = create_sub_stream(This->data, start, size, ppStream);
1746 return hres;
1747 }
1748
1749 static HRESULT WINAPI MimeBody_SetData(
1750 IMimeBody* iface,
1751 ENCODINGTYPE ietEncoding,
1752 LPCSTR pszPriType,
1753 LPCSTR pszSubType,
1754 REFIID riid,
1755 LPVOID pvObject)
1756 {
1757 MimeBody *This = impl_from_IMimeBody(iface);
1758 TRACE("(%p)->(%d, %s, %s, %s %p)\n", This, ietEncoding, debugstr_a(pszPriType), debugstr_a(pszSubType),
1759 debugstr_guid(riid), pvObject);
1760
1761 if(IsEqualIID(riid, &IID_IStream))
1762 IStream_AddRef((IStream *)pvObject);
1763 else
1764 {
1765 FIXME("Unhandled object type %s\n", debugstr_guid(riid));
1766 return E_INVALIDARG;
1767 }
1768
1769 if(This->data)
1770 release_data(&This->data_iid, This->data);
1771
1772 This->data_iid = *riid;
1773 This->data = pvObject;
1774
1775 IMimeBody_SetCurrentEncoding(iface, ietEncoding);
1776
1777 /* FIXME: Update the content type.
1778 If pszPriType == NULL use 'application'
1779 If pszSubType == NULL use 'octet-stream' */
1780
1781 return S_OK;
1782 }
1783
1784 static HRESULT WINAPI MimeBody_EmptyData(
1785 IMimeBody* iface)
1786 {
1787 MimeBody *This = impl_from_IMimeBody(iface);
1788 FIXME("(%p)->() stub\n", This);
1789 return E_NOTIMPL;
1790 }
1791
1792 static HRESULT WINAPI MimeBody_CopyTo(
1793 IMimeBody* iface,
1794 IMimeBody* pBody)
1795 {
1796 MimeBody *This = impl_from_IMimeBody(iface);
1797 FIXME("(%p)->(%p) stub\n", This, pBody);
1798 return E_NOTIMPL;
1799 }
1800
1801 static HRESULT WINAPI MimeBody_GetTransmitInfo(
1802 IMimeBody* iface,
1803 LPTRANSMITINFO pTransmitInfo)
1804 {
1805 MimeBody *This = impl_from_IMimeBody(iface);
1806 FIXME("(%p)->(%p) stub\n", This, pTransmitInfo);
1807 return E_NOTIMPL;
1808 }
1809
1810 static HRESULT WINAPI MimeBody_SaveToFile(
1811 IMimeBody* iface,
1812 ENCODINGTYPE ietEncoding,
1813 LPCSTR pszFilePath)
1814 {
1815 MimeBody *This = impl_from_IMimeBody(iface);
1816 FIXME("(%p)->(%d, %s) stub\n", This, ietEncoding, debugstr_a(pszFilePath));
1817 return E_NOTIMPL;
1818 }
1819
1820 static HRESULT WINAPI MimeBody_GetHandle(
1821 IMimeBody* iface,
1822 LPHBODY phBody)
1823 {
1824 MimeBody *This = impl_from_IMimeBody(iface);
1825 TRACE("(%p)->(%p)\n", iface, phBody);
1826
1827 if(!phBody)
1828 return E_INVALIDARG;
1829
1830 *phBody = This->handle;
1831 return This->handle ? S_OK : MIME_E_NO_DATA;
1832 }
1833
1834 static IMimeBodyVtbl body_vtbl =
1835 {
1836 MimeBody_QueryInterface,
1837 MimeBody_AddRef,
1838 MimeBody_Release,
1839 MimeBody_GetClassID,
1840 MimeBody_IsDirty,
1841 MimeBody_Load,
1842 MimeBody_Save,
1843 MimeBody_GetSizeMax,
1844 MimeBody_InitNew,
1845 MimeBody_GetPropInfo,
1846 MimeBody_SetPropInfo,
1847 MimeBody_GetProp,
1848 MimeBody_SetProp,
1849 MimeBody_AppendProp,
1850 MimeBody_DeleteProp,
1851 MimeBody_CopyProps,
1852 MimeBody_MoveProps,
1853 MimeBody_DeleteExcept,
1854 MimeBody_QueryProp,
1855 MimeBody_GetCharset,
1856 MimeBody_SetCharset,
1857 MimeBody_GetParameters,
1858 MimeBody_IsContentType,
1859 MimeBody_BindToObject,
1860 MimeBody_Clone,
1861 MimeBody_SetOption,
1862 MimeBody_GetOption,
1863 MimeBody_EnumProps,
1864 MimeBody_IsType,
1865 MimeBody_SetDisplayName,
1866 MimeBody_GetDisplayName,
1867 MimeBody_GetOffsets,
1868 MimeBody_GetCurrentEncoding,
1869 MimeBody_SetCurrentEncoding,
1870 MimeBody_GetEstimatedSize,
1871 MimeBody_GetDataHere,
1872 MimeBody_GetData,
1873 MimeBody_SetData,
1874 MimeBody_EmptyData,
1875 MimeBody_CopyTo,
1876 MimeBody_GetTransmitInfo,
1877 MimeBody_SaveToFile,
1878 MimeBody_GetHandle
1879 };
1880
1881 static HRESULT MimeBody_set_offsets(MimeBody *body, const BODYOFFSETS *offsets)
1882 {
1883 TRACE("setting offsets to %d, %d, %d, %d\n", offsets->cbBoundaryStart,
1884 offsets->cbHeaderStart, offsets->cbBodyStart, offsets->cbBodyEnd);
1885
1886 body->body_offsets = *offsets;
1887 return S_OK;
1888 }
1889
1890 #define FIRST_CUSTOM_PROP_ID 0x100
1891
1892 static MimeBody *mimebody_create(void)
1893 {
1894 MimeBody *This;
1895 BODYOFFSETS body_offsets;
1896
1897 This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
1898 if (!This)
1899 return NULL;
1900
1901 This->IMimeBody_iface.lpVtbl = &body_vtbl;
1902 This->ref = 1;
1903 This->handle = NULL;
1904 list_init(&This->headers);
1905 list_init(&This->new_props);
1906 This->next_prop_id = FIRST_CUSTOM_PROP_ID;
1907 This->content_pri_type = NULL;
1908 This->content_sub_type = NULL;
1909 This->encoding = IET_7BIT;
1910 This->data = NULL;
1911 This->data_iid = IID_NULL;
1912
1913 body_offsets.cbBoundaryStart = body_offsets.cbHeaderStart = 0;
1914 body_offsets.cbBodyStart = body_offsets.cbBodyEnd = 0;
1915 MimeBody_set_offsets(This, &body_offsets);
1916
1917 return This;
1918 }
1919
1920 HRESULT MimeBody_create(IUnknown *outer, void **ppv)
1921 {
1922 MimeBody *mb;
1923
1924 if(outer)
1925 return CLASS_E_NOAGGREGATION;
1926
1927 if ((mb = mimebody_create()))
1928 {
1929 *ppv = &mb->IMimeBody_iface;
1930 return S_OK;
1931 }
1932 else
1933 {
1934 *ppv = NULL;
1935 return E_OUTOFMEMORY;
1936 }
1937 }
1938
1939 typedef struct body_t
1940 {
1941 struct list entry;
1942 DWORD index;
1943 MimeBody *mime_body;
1944
1945 struct body_t *parent;
1946 struct list children;
1947 } body_t;
1948
1949 typedef struct MimeMessage
1950 {
1951 IMimeMessage IMimeMessage_iface;
1952 LONG ref;
1953 IStream *stream;
1954
1955 struct list body_tree;
1956 DWORD next_index;
1957 } MimeMessage;
1958
1959 static inline MimeMessage *impl_from_IMimeMessage(IMimeMessage *iface)
1960 {
1961 return CONTAINING_RECORD(iface, MimeMessage, IMimeMessage_iface);
1962 }
1963
1964 static HRESULT WINAPI MimeMessage_QueryInterface(IMimeMessage *iface, REFIID riid, void **ppv)
1965 {
1966 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1967
1968 if (IsEqualIID(riid, &IID_IUnknown) ||
1969 IsEqualIID(riid, &IID_IPersist) ||
1970 IsEqualIID(riid, &IID_IPersistStreamInit) ||
1971 IsEqualIID(riid, &IID_IMimeMessageTree) ||
1972 IsEqualIID(riid, &IID_IMimeMessage))
1973 {
1974 *ppv = iface;
1975 IMimeMessage_AddRef(iface);
1976 return S_OK;
1977 }
1978
1979 FIXME("no interface for %s\n", debugstr_guid(riid));
1980 *ppv = NULL;
1981 return E_NOINTERFACE;
1982 }
1983
1984 static ULONG WINAPI MimeMessage_AddRef(IMimeMessage *iface)
1985 {
1986 MimeMessage *This = impl_from_IMimeMessage(iface);
1987 ULONG ref = InterlockedIncrement(&This->ref);
1988
1989 TRACE("(%p) ref=%d\n", This, ref);
1990
1991 return ref;
1992 }
1993
1994 static void empty_body_list(struct list *list)
1995 {
1996 body_t *body, *cursor2;
1997 LIST_FOR_EACH_ENTRY_SAFE(body, cursor2, list, body_t, entry)
1998 {
1999 empty_body_list(&body->children);
2000 list_remove(&body->entry);
2001 IMimeBody_Release(&body->mime_body->IMimeBody_iface);
2002 HeapFree(GetProcessHeap(), 0, body);
2003 }
2004 }
2005
2006 static ULONG WINAPI MimeMessage_Release(IMimeMessage *iface)
2007 {
2008 MimeMessage *This = impl_from_IMimeMessage(iface);
2009 ULONG ref = InterlockedDecrement(&This->ref);
2010
2011 TRACE("(%p) ref=%d\n", This, ref);
2012
2013 if (!ref)
2014 {
2015 empty_body_list(&This->body_tree);
2016
2017 if(This->stream) IStream_Release(This->stream);
2018 HeapFree(GetProcessHeap(), 0, This);
2019 }
2020
2021 return ref;
2022 }
2023
2024 /*** IPersist methods ***/
2025 static HRESULT WINAPI MimeMessage_GetClassID(
2026 IMimeMessage *iface,
2027 CLSID *pClassID)
2028 {
2029 FIXME("(%p)->(%p)\n", iface, pClassID);
2030 return E_NOTIMPL;
2031 }
2032
2033 /*** IPersistStreamInit methods ***/
2034 static HRESULT WINAPI MimeMessage_IsDirty(
2035 IMimeMessage *iface)
2036 {
2037 FIXME("(%p)->()\n", iface);
2038 return E_NOTIMPL;
2039 }
2040
2041 static body_t *new_body_entry(MimeBody *mime_body, DWORD index, body_t *parent)
2042 {
2043 body_t *body = HeapAlloc(GetProcessHeap(), 0, sizeof(*body));
2044 if(body)
2045 {
2046 body->mime_body = mime_body;
2047 body->index = index;
2048 list_init(&body->children);
2049 body->parent = parent;
2050
2051 mime_body->handle = UlongToHandle(body->index);
2052 }
2053 return body;
2054 }
2055
2056 typedef struct
2057 {
2058 struct list entry;
2059 BODYOFFSETS offsets;
2060 } offset_entry_t;
2061
2062 static HRESULT create_body_offset_list(IStream *stm, const char *boundary, struct list *body_offsets)
2063 {
2064 HRESULT hr;
2065 DWORD read, boundary_start;
2066 int boundary_len = strlen(boundary);
2067 char *buf, *ptr, *overlap;
2068 DWORD start = 0, overlap_no;
2069 offset_entry_t *cur_body = NULL;
2070 BOOL is_first_line = TRUE;
2071 ULARGE_INTEGER cur;
2072 LARGE_INTEGER zero;
2073
2074 list_init(body_offsets);
2075
2076 overlap_no = boundary_len + 5;
2077
2078 overlap = buf = HeapAlloc(GetProcessHeap(), 0, overlap_no + PARSER_BUF_SIZE + 1);
2079
2080 zero.QuadPart = 0;
2081 hr = IStream_Seek(stm, zero, STREAM_SEEK_CUR, &cur);
2082 start = cur.u.LowPart;
2083
2084 do {
2085 hr = IStream_Read(stm, overlap, PARSER_BUF_SIZE, &read);
2086 if(FAILED(hr)) goto end;
2087 if(read == 0) break;
2088 overlap[read] = '\0';
2089
2090 ptr = buf;
2091 while(1) {
2092 if(is_first_line) {
2093 is_first_line = FALSE;
2094 }else {
2095 ptr = strstr(ptr, "\r\n");
2096 if(!ptr)
2097 break;
2098 ptr += 2;
2099 }
2100
2101 boundary_start = start + ptr - buf;
2102
2103 if(*ptr == '-' && *(ptr + 1) == '-' && !memcmp(ptr + 2, boundary, boundary_len)) {
2104 ptr += boundary_len + 2;
2105
2106 if(*ptr == '\r' && *(ptr + 1) == '\n')
2107 {
2108 ptr += 2;
2109 if(cur_body)
2110 {
2111 cur_body->offsets.cbBodyEnd = boundary_start - 2;
2112 list_add_tail(body_offsets, &cur_body->entry);
2113 }
2114 cur_body = HeapAlloc(GetProcessHeap(), 0, sizeof(*cur_body));
2115 cur_body->offsets.cbBoundaryStart = boundary_start;
2116 cur_body->offsets.cbHeaderStart = start + ptr - buf;
2117 }
2118 else if(*ptr == '-' && *(ptr + 1) == '-')
2119 {
2120 if(cur_body)
2121 {
2122 cur_body->offsets.cbBodyEnd = boundary_start - 2;
2123 list_add_tail(body_offsets, &cur_body->entry);
2124 goto end;
2125 }
2126 }
2127 }
2128 }
2129
2130 if(overlap == buf) /* 1st iteration */
2131 {
2132 memmove(buf, buf + PARSER_BUF_SIZE - overlap_no, overlap_no);
2133 overlap = buf + overlap_no;
2134 start += read - overlap_no;
2135 }
2136 else
2137 {
2138 memmove(buf, buf + PARSER_BUF_SIZE, overlap_no);
2139 start += read;
2140 }
2141 } while(1);
2142
2143 end:
2144 HeapFree(GetProcessHeap(), 0, buf);
2145 return hr;
2146 }
2147
2148 static body_t *create_sub_body(MimeMessage *msg, IStream *pStm, BODYOFFSETS *offset, body_t *parent)
2149 {
2150 ULARGE_INTEGER start, length;
2151 MimeBody *mime_body;
2152 HRESULT hr;
2153 body_t *body;
2154 LARGE_INTEGER pos;
2155
2156 pos.QuadPart = offset->cbHeaderStart;
2157 IStream_Seek(pStm, pos, STREAM_SEEK_SET, NULL);
2158
2159 mime_body = mimebody_create();
2160 IMimeBody_Load(&mime_body->IMimeBody_iface, pStm);
2161
2162 pos.QuadPart = 0;
2163 hr = IStream_Seek(pStm, pos, STREAM_SEEK_CUR, &start);
2164 offset->cbBodyStart = start.QuadPart;
2165 if (parent) MimeBody_set_offsets(mime_body, offset);
2166
2167 length.QuadPart = offset->cbBodyEnd - offset->cbBodyStart;
2168 create_sub_stream(pStm, start, length, (IStream**)&mime_body->data);
2169 mime_body->data_iid = IID_IStream;
2170
2171 body = new_body_entry(mime_body, msg->next_index++, parent);
2172
2173 if(IMimeBody_IsContentType(&mime_body->IMimeBody_iface, "multipart", NULL) == S_OK)
2174 {
2175 MIMEPARAMINFO *param_info;
2176 ULONG count, i;
2177 IMimeAllocator *alloc;
2178
2179 hr = IMimeBody_GetParameters(&mime_body->IMimeBody_iface, "Content-Type", &count,
2180 &param_info);
2181 if(hr != S_OK || count == 0) return body;
2182
2183 MimeOleGetAllocator(&alloc);
2184
2185 for(i = 0; i < count; i++)
2186 {
2187 if(!lstrcmpiA(param_info[i].pszName, "boundary"))
2188 {
2189 struct list offset_list;
2190 offset_entry_t *cur, *cursor2;
2191 hr = create_body_offset_list(pStm, param_info[i].pszData, &offset_list);
2192 LIST_FOR_EACH_ENTRY_SAFE(cur, cursor2, &offset_list, offset_entry_t, entry)
2193 {
2194 body_t *sub_body;
2195
2196 sub_body = create_sub_body(msg, pStm, &cur->offsets, body);
2197 list_add_tail(&body->children, &sub_body->entry);
2198 list_remove(&cur->entry);
2199 HeapFree(GetProcessHeap(), 0, cur);
2200 }
2201 break;
2202 }
2203 }
2204 IMimeAllocator_FreeParamInfoArray(alloc, count, param_info, TRUE);
2205 IMimeAllocator_Release(alloc);
2206 }
2207 return body;
2208 }
2209
2210 static HRESULT WINAPI MimeMessage_Load(IMimeMessage *iface, IStream *pStm)
2211 {
2212 MimeMessage *This = impl_from_IMimeMessage(iface);
2213 body_t *root_body;
2214 BODYOFFSETS offsets;
2215 ULARGE_INTEGER cur;
2216 LARGE_INTEGER zero;
2217
2218 TRACE("(%p)->(%p)\n", iface, pStm);
2219
2220 if(This->stream)
2221 {
2222 FIXME("already loaded a message\n");
2223 return E_FAIL;
2224 }
2225
2226 empty_body_list(&This->body_tree);
2227
2228 IStream_AddRef(pStm);
2229 This->stream = pStm;
2230 offsets.cbBoundaryStart = offsets.cbHeaderStart = 0;
2231 offsets.cbBodyStart = offsets.cbBodyEnd = 0;
2232
2233 root_body = create_sub_body(This, pStm, &offsets, NULL);
2234
2235 zero.QuadPart = 0;
2236 IStream_Seek(pStm, zero, STREAM_SEEK_END, &cur);
2237 offsets.cbBodyEnd = cur.u.LowPart;
2238 MimeBody_set_offsets(root_body->mime_body, &offsets);
2239
2240 list_add_head(&This->body_tree, &root_body->entry);
2241
2242 return S_OK;
2243 }
2244
2245 static HRESULT WINAPI MimeMessage_Save(IMimeMessage *iface, IStream *pStm, BOOL fClearDirty)
2246 {
2247 FIXME("(%p)->(%p, %s)\n", iface, pStm, fClearDirty ? "TRUE" : "FALSE");
2248 return E_NOTIMPL;
2249 }
2250
2251 static HRESULT WINAPI MimeMessage_GetSizeMax(
2252 IMimeMessage *iface,
2253 ULARGE_INTEGER *pcbSize)
2254 {
2255 FIXME("(%p)->(%p)\n", iface, pcbSize);
2256 return E_NOTIMPL;
2257 }
2258
2259 static HRESULT WINAPI MimeMessage_InitNew(
2260 IMimeMessage *iface)
2261 {
2262 FIXME("(%p)->()\n", iface);
2263 return E_NOTIMPL;
2264 }
2265
2266 /*** IMimeMessageTree methods ***/
2267 static HRESULT WINAPI MimeMessage_GetMessageSource(IMimeMessage *iface, IStream **ppStream,
2268 DWORD dwFlags)
2269 {
2270 MimeMessage *This = impl_from_IMimeMessage(iface);
2271
2272 FIXME("(%p)->(%p, 0x%x)\n", iface, ppStream, dwFlags);
2273
2274 IStream_AddRef(This->stream);
2275 *ppStream = This->stream;
2276 return S_OK;
2277 }
2278
2279 static HRESULT WINAPI MimeMessage_GetMessageSize(
2280 IMimeMessage *iface,
2281 ULONG *pcbSize,
2282 DWORD dwFlags)
2283 {
2284 FIXME("(%p)->(%p, 0x%x)\n", iface, pcbSize, dwFlags);
2285 return E_NOTIMPL;
2286 }
2287
2288 static HRESULT WINAPI MimeMessage_LoadOffsetTable(
2289 IMimeMessage *iface,
2290 IStream *pStream)
2291 {
2292 FIXME("(%p)->(%p)\n", iface, pStream);
2293 return E_NOTIMPL;
2294 }
2295
2296 static HRESULT WINAPI MimeMessage_SaveOffsetTable(
2297 IMimeMessage *iface,
2298 IStream *pStream,
2299 DWORD dwFlags)
2300 {
2301 FIXME("(%p)->(%p, 0x%x)\n", iface, pStream, dwFlags);
2302 return E_NOTIMPL;
2303 }
2304
2305
2306 static HRESULT WINAPI MimeMessage_GetFlags(
2307 IMimeMessage *iface,
2308 DWORD *pdwFlags)
2309 {
2310 FIXME("(%p)->(%p)\n", iface, pdwFlags);
2311 return E_NOTIMPL;
2312 }
2313
2314 static HRESULT WINAPI MimeMessage_Commit(
2315 IMimeMessage *iface,
2316 DWORD dwFlags)
2317 {
2318 FIXME("(%p)->(0x%x)\n", iface, dwFlags);
2319 return S_OK;
2320 }
2321
2322
2323 static HRESULT WINAPI MimeMessage_HandsOffStorage(
2324 IMimeMessage *iface)
2325 {
2326 FIXME("(%p)->()\n", iface);
2327 return E_NOTIMPL;
2328 }
2329
2330 static HRESULT find_body(struct list *list, HBODY hbody, body_t **body)
2331 {
2332 body_t *cur;
2333 HRESULT hr;
2334
2335 if(hbody == HBODY_ROOT)
2336 {
2337 *body = LIST_ENTRY(list_head(list), body_t, entry);
2338 return S_OK;
2339 }
2340
2341 LIST_FOR_EACH_ENTRY(cur, list, body_t, entry)
2342 {
2343 if(cur->index == HandleToUlong(hbody))
2344 {
2345 *body = cur;
2346 return S_OK;
2347 }
2348 hr = find_body(&cur->children, hbody, body);
2349 if(hr == S_OK) return S_OK;
2350 }
2351 return S_FALSE;
2352 }
2353
2354 static HRESULT WINAPI MimeMessage_BindToObject(IMimeMessage *iface, const HBODY hBody, REFIID riid,
2355 void **ppvObject)
2356 {
2357 MimeMessage *This = impl_from_IMimeMessage(iface);
2358 HRESULT hr;
2359 body_t *body;
2360
2361 TRACE("(%p)->(%p, %s, %p)\n", iface, hBody, debugstr_guid(riid), ppvObject);
2362
2363 hr = find_body(&This->body_tree, hBody, &body);
2364
2365 if(hr != S_OK) return hr;
2366
2367 if(IsEqualIID(riid, &IID_IMimeBody))
2368 {
2369 IMimeBody_AddRef(&body->mime_body->IMimeBody_iface);
2370 *ppvObject = &body->mime_body->IMimeBody_iface;
2371 return S_OK;
2372 }
2373
2374 return E_NOINTERFACE;
2375 }
2376
2377 static HRESULT WINAPI MimeMessage_SaveBody(
2378 IMimeMessage *iface,
2379 HBODY hBody,
2380 DWORD dwFlags,
2381 IStream *pStream)
2382 {
2383 FIXME("(%p)->(%p, 0x%x, %p)\n", iface, hBody, dwFlags, pStream);
2384 return E_NOTIMPL;
2385 }
2386
2387 static HRESULT get_body(MimeMessage *msg, BODYLOCATION location, HBODY pivot, body_t **out)
2388 {
2389 body_t *root = LIST_ENTRY(list_head(&msg->body_tree), body_t, entry);
2390 body_t *body;
2391 HRESULT hr;
2392 struct list *list;
2393
2394 if(location == IBL_ROOT)
2395 {
2396 *out = root;
2397 return S_OK;
2398 }
2399
2400 hr = find_body(&msg->body_tree, pivot, &body);
2401
2402 if(hr == S_OK)
2403 {
2404 switch(location)
2405 {
2406 case IBL_PARENT:
2407 if(body->parent)
2408 *out = body->parent;
2409 else
2410 hr = MIME_E_NOT_FOUND;
2411 break;
2412
2413 case IBL_FIRST:
2414 list = list_head(&body->children);
2415 if(list)
2416 *out = LIST_ENTRY(list, body_t, entry);
2417 else
2418 hr = MIME_E_NOT_FOUND;
2419 break;
2420
2421 case IBL_LAST:
2422 list = list_tail(&body->children);
2423 if(list)
2424 *out = LIST_ENTRY(list, body_t, entry);
2425 else
2426 hr = MIME_E_NOT_FOUND;
2427 break;
2428
2429 case IBL_NEXT:
2430 list = list_next(&body->parent->children, &body->entry);
2431 if(list)
2432 *out = LIST_ENTRY(list, body_t, entry);
2433 else
2434 hr = MIME_E_NOT_FOUND;
2435 break;
2436
2437 case IBL_PREVIOUS:
2438 list = list_prev(&body->parent->children, &body->entry);
2439 if(list)
2440 *out = LIST_ENTRY(list, body_t, entry);
2441 else
2442 hr = MIME_E_NOT_FOUND;
2443 break;
2444
2445 default:
2446 hr = E_FAIL;
2447 break;
2448 }
2449 }
2450
2451 return hr;
2452 }
2453
2454
2455 static HRESULT WINAPI MimeMessage_InsertBody(
2456 IMimeMessage *iface,
2457 BODYLOCATION location,
2458 HBODY hPivot,
2459 LPHBODY phBody)
2460 {
2461 FIXME("(%p)->(%d, %p, %p)\n", iface, location, hPivot, phBody);
2462 return E_NOTIMPL;
2463 }
2464
2465 static HRESULT WINAPI MimeMessage_GetBody(IMimeMessage *iface, BODYLOCATION location, HBODY hPivot,
2466 HBODY *phBody)
2467 {
2468 MimeMessage *This = impl_from_IMimeMessage(iface);
2469 body_t *body;
2470 HRESULT hr;
2471
2472 TRACE("(%p)->(%d, %p, %p)\n", iface, location, hPivot, phBody);
2473
2474 if(!phBody)
2475 return E_INVALIDARG;
2476
2477 *phBody = NULL;
2478
2479 hr = get_body(This, location, hPivot, &body);
2480
2481 if(hr == S_OK) *phBody = UlongToHandle(body->index);
2482
2483 return hr;
2484 }
2485
2486 static HRESULT WINAPI MimeMessage_DeleteBody(
2487 IMimeMessage *iface,
2488 HBODY hBody,
2489 DWORD dwFlags)
2490 {
2491 FIXME("(%p)->(%p, %08x)\n", iface, hBody, dwFlags);
2492 return E_NOTIMPL;
2493 }
2494
2495 static HRESULT WINAPI MimeMessage_MoveBody(
2496 IMimeMessage *iface,
2497 HBODY hBody,
2498 BODYLOCATION location)
2499 {
2500 FIXME("(%p)->(%d)\n", iface, location);
2501 return E_NOTIMPL;
2502 }
2503
2504 static void count_children(body_t *body, boolean recurse, ULONG *count)
2505 {
2506 body_t *child;
2507
2508 LIST_FOR_EACH_ENTRY(child, &body->children, body_t, entry)
2509 {
2510 (*count)++;
2511 if(recurse) count_children(child, recurse, count);
2512 }
2513 }
2514
2515 static HRESULT WINAPI MimeMessage_CountBodies(IMimeMessage *iface, HBODY hParent, boolean fRecurse,
2516 ULONG *pcBodies)
2517 {
2518 HRESULT hr;
2519 MimeMessage *This = impl_from_IMimeMessage(iface);
2520 body_t *body;
2521
2522 TRACE("(%p)->(%p, %s, %p)\n", iface, hParent, fRecurse ? "TRUE" : "FALSE", pcBodies);
2523
2524 hr = find_body(&This->body_tree, hParent, &body);
2525 if(hr != S_OK) return hr;
2526
2527 *pcBodies = 1;
2528 count_children(body, fRecurse, pcBodies);
2529
2530 return S_OK;
2531 }
2532
2533 static HRESULT find_next(MimeMessage *This, body_t *body, FINDBODY *find, HBODY *out)
2534 {
2535 struct list *ptr;
2536 HBODY next;
2537
2538 for (;;)
2539 {
2540 if (!body) ptr = list_head( &This->body_tree );
2541 else
2542 {
2543 ptr = list_head( &body->children );
2544 while (!ptr)
2545 {
2546 if (!body->parent) return MIME_E_NOT_FOUND;
2547 if (!(ptr = list_next( &body->parent->children, &body->entry ))) body = body->parent;
2548 }
2549 }
2550
2551 body = LIST_ENTRY( ptr, body_t, entry );
2552 next = UlongToHandle( body->index );
2553 find->dwReserved = body->index;
2554 if (IMimeBody_IsContentType(&body->mime_body->IMimeBody_iface, find->pszPriType,
2555 find->pszSubType) == S_OK)
2556 {
2557 *out = next;
2558 return S_OK;
2559 }
2560 }
2561 return MIME_E_NOT_FOUND;
2562 }
2563
2564 static HRESULT WINAPI MimeMessage_FindFirst(IMimeMessage *iface, FINDBODY *pFindBody, HBODY *phBody)
2565 {
2566 MimeMessage *This = impl_from_IMimeMessage(iface);
2567
2568 TRACE("(%p)->(%p, %p)\n", iface, pFindBody, phBody);
2569
2570 pFindBody->dwReserved = 0;
2571 return find_next(This, NULL, pFindBody, phBody);
2572 }
2573
2574 static HRESULT WINAPI MimeMessage_FindNext(IMimeMessage *iface, FINDBODY *pFindBody, HBODY *phBody)
2575 {
2576 MimeMessage *This = impl_from_IMimeMessage(iface);
2577 body_t *body;
2578 HRESULT hr;
2579
2580 TRACE("(%p)->(%p, %p)\n", iface, pFindBody, phBody);
2581
2582 hr = find_body( &This->body_tree, UlongToHandle( pFindBody->dwReserved ), &body );
2583 if (hr != S_OK) return MIME_E_NOT_FOUND;
2584 return find_next(This, body, pFindBody, phBody);
2585 }
2586
2587 static HRESULT WINAPI MimeMessage_ResolveURL(
2588 IMimeMessage *iface,
2589 HBODY hRelated,
2590 LPCSTR pszBase,
2591 LPCSTR pszURL,
2592 DWORD dwFlags,
2593 LPHBODY phBody)
2594 {
2595 FIXME("(%p)->(%p, %s, %s, 0x%x, %p)\n", iface, hRelated, pszBase, pszURL, dwFlags, phBody);
2596 return E_NOTIMPL;
2597 }
2598
2599 static HRESULT WINAPI MimeMessage_ToMultipart(
2600 IMimeMessage *iface,
2601 HBODY hBody,
2602 LPCSTR pszSubType,
2603 LPHBODY phMultipart)
2604 {
2605 FIXME("(%p)->(%p, %s, %p)\n", iface, hBody, pszSubType, phMultipart);
2606 return E_NOTIMPL;
2607 }
2608
2609 static HRESULT WINAPI MimeMessage_GetBodyOffsets(
2610 IMimeMessage *iface,
2611 HBODY hBody,
2612 LPBODYOFFSETS pOffsets)
2613 {
2614 FIXME("(%p)->(%p, %p)\n", iface, hBody, pOffsets);
2615 return E_NOTIMPL;
2616 }
2617
2618 static HRESULT WINAPI MimeMessage_GetCharset(
2619 IMimeMessage *iface,
2620 LPHCHARSET phCharset)
2621 {
2622 FIXME("(%p)->(%p)\n", iface, phCharset);
2623 *phCharset = NULL;
2624 return S_OK;
2625 }
2626
2627 static HRESULT WINAPI MimeMessage_SetCharset(
2628 IMimeMessage *iface,
2629 HCHARSET hCharset,
2630 CSETAPPLYTYPE applytype)
2631 {
2632 FIXME("(%p)->(%p, %d)\n", iface, hCharset, applytype);
2633 return E_NOTIMPL;
2634 }
2635
2636 static HRESULT WINAPI MimeMessage_IsBodyType(
2637 IMimeMessage *iface,
2638 HBODY hBody,
2639 IMSGBODYTYPE bodytype)
2640 {
2641 HRESULT hr;
2642 IMimeBody *mime_body;
2643 TRACE("(%p)->(%p, %d)\n", iface, hBody, bodytype);
2644
2645 hr = IMimeMessage_BindToObject(iface, hBody, &IID_IMimeBody, (void**)&mime_body);
2646 if(hr != S_OK) return hr;
2647
2648 hr = IMimeBody_IsType(mime_body, bodytype);
2649 MimeBody_Release(mime_body);
2650 return hr;
2651 }
2652
2653 static HRESULT WINAPI MimeMessage_IsContentType(
2654 IMimeMessage *iface,
2655 HBODY hBody,
2656 LPCSTR pszPriType,
2657 LPCSTR pszSubType)
2658 {
2659 HRESULT hr;
2660 IMimeBody *mime_body;
2661 TRACE("(%p)->(%p, %s, %s)\n", iface, hBody, debugstr_a(pszPriType),
2662 debugstr_a(pszSubType));
2663
2664 hr = IMimeMessage_BindToObject(iface, hBody, &IID_IMimeBody, (void**)&mime_body);
2665 if(FAILED(hr)) return hr;
2666
2667 hr = IMimeBody_IsContentType(mime_body, pszPriType, pszSubType);
2668 IMimeBody_Release(mime_body);
2669 return hr;
2670 }
2671
2672 static HRESULT WINAPI MimeMessage_QueryBodyProp(
2673 IMimeMessage *iface,
2674 HBODY hBody,
2675 LPCSTR pszName,
2676 LPCSTR pszCriteria,
2677 boolean fSubString,
2678 boolean fCaseSensitive)
2679 {
2680 FIXME("(%p)->(%p, %s, %s, %s, %s)\n", iface, hBody, pszName, pszCriteria, fSubString ? "TRUE" : "FALSE", fCaseSensitive ? "TRUE" : "FALSE");
2681 return E_NOTIMPL;
2682 }
2683
2684 static HRESULT WINAPI MimeMessage_GetBodyProp(
2685 IMimeMessage *iface,
2686 HBODY hBody,
2687 LPCSTR pszName,
2688 DWORD dwFlags,
2689 LPPROPVARIANT pValue)
2690 {
2691 HRESULT hr;
2692 IMimeBody *mime_body;
2693
2694 TRACE("(%p)->(%p, %s, 0x%x, %p)\n", iface, hBody, pszName, dwFlags, pValue);
2695
2696 hr = IMimeMessage_BindToObject(iface, hBody, &IID_IMimeBody, (void**)&mime_body);
2697 if(hr != S_OK) return hr;
2698
2699 hr = IMimeBody_GetProp(mime_body, pszName, dwFlags, pValue);
2700 IMimeBody_Release(mime_body);
2701
2702 return hr;
2703 }
2704
2705 static HRESULT WINAPI MimeMessage_SetBodyProp(
2706 IMimeMessage *iface,
2707 HBODY hBody,
2708 LPCSTR pszName,
2709 DWORD dwFlags,
2710 LPCPROPVARIANT pValue)
2711 {
2712 FIXME("(%p)->(%p, %s, 0x%x, %p)\n", iface, hBody, pszName, dwFlags, pValue);
2713 return E_NOTIMPL;
2714 }
2715
2716 static HRESULT WINAPI MimeMessage_DeleteBodyProp(
2717 IMimeMessage *iface,
2718 HBODY hBody,
2719 LPCSTR pszName)
2720 {
2721 FIXME("(%p)->(%p, %s)\n", iface, hBody, pszName);
2722 return E_NOTIMPL;
2723 }
2724
2725 static HRESULT WINAPI MimeMessage_SetOption(
2726 IMimeMessage *iface,
2727 const TYPEDID oid,
2728 LPCPROPVARIANT pValue)
2729 {
2730 HRESULT hr = S_OK;
2731 TRACE("(%p)->(%08x, %p)\n", iface, oid, pValue);
2732
2733 /* Message ID is checked before type.
2734 * OID 0x4D -> 0x56 and 0x58 aren't defined but will filtered out later.
2735 */
2736 if(TYPEDID_ID(oid) < TYPEDID_ID(OID_ALLOW_8BIT_HEADER) || TYPEDID_ID(oid) > TYPEDID_ID(OID_SECURITY_2KEY_CERT_BAG_64))
2737 {
2738 WARN("oid (%08x) out of range\n", oid);
2739 return MIME_E_INVALID_OPTION_ID;
2740 }
2741
2742 if(pValue->vt != TYPEDID_TYPE(oid))
2743 {
2744 WARN("Called with vartype %04x and oid %08x\n", pValue->vt, oid);
2745 return S_OK;
2746 }
2747
2748 switch(oid)
2749 {
2750 case OID_HIDE_TNEF_ATTACHMENTS:
2751 FIXME("OID_HIDE_TNEF_ATTACHMENTS (value %d): ignoring\n", pValue->u.boolVal);
2752 break;
2753 case OID_SHOW_MACBINARY:
2754 FIXME("OID_SHOW_MACBINARY (value %d): ignoring\n", pValue->u.boolVal);
2755 break;
2756 case OID_SAVEBODY_KEEPBOUNDARY:
2757 FIXME("OID_SAVEBODY_KEEPBOUNDARY (value %d): ignoring\n", pValue->u.boolVal);
2758 break;
2759 case OID_CLEANUP_TREE_ON_SAVE:
2760 FIXME("OID_CLEANUP_TREE_ON_SAVE (value %d): ignoring\n", pValue->u.boolVal);
2761 break;
2762 default:
2763 FIXME("Unhandled oid %08x\n", oid);
2764 hr = MIME_E_INVALID_OPTION_ID;
2765 }
2766
2767 return hr;
2768 }
2769
2770 static HRESULT WINAPI MimeMessage_GetOption(
2771 IMimeMessage *iface,
2772 const TYPEDID oid,
2773 LPPROPVARIANT pValue)
2774 {
2775 FIXME("(%p)->(%08x, %p)\n", iface, oid, pValue);
2776 return E_NOTIMPL;
2777 }
2778
2779 /*** IMimeMessage methods ***/
2780 static HRESULT WINAPI MimeMessage_CreateWebPage(
2781 IMimeMessage *iface,
2782 IStream *pRootStm,
2783 LPWEBPAGEOPTIONS pOptions,
2784 IMimeMessageCallback *pCallback,
2785 IMoniker **ppMoniker)
2786 {
2787 FIXME("(%p)->(%p, %p, %p, %p)\n", iface, pRootStm, pOptions, pCallback, ppMoniker);
2788 *ppMoniker = NULL;
2789 return E_NOTIMPL;
2790 }
2791
2792 static HRESULT WINAPI MimeMessage_GetProp(
2793 IMimeMessage *iface,
2794 LPCSTR pszName,
2795 DWORD dwFlags,
2796 LPPROPVARIANT pValue)
2797 {
2798 FIXME("(%p)->(%s, 0x%x, %p)\n", iface, pszName, dwFlags, pValue);
2799 return E_NOTIMPL;
2800 }
2801
2802 static HRESULT WINAPI MimeMessage_SetProp(
2803 IMimeMessage *iface,
2804 LPCSTR pszName,
2805 DWORD dwFlags,
2806 LPCPROPVARIANT pValue)
2807 {
2808 FIXME("(%p)->(%s, 0x%x, %p)\n", iface, pszName, dwFlags, pValue);
2809 return E_NOTIMPL;
2810 }
2811
2812 static HRESULT WINAPI MimeMessage_DeleteProp(
2813 IMimeMessage *iface,
2814 LPCSTR pszName)
2815 {
2816 FIXME("(%p)->(%s)\n", iface, pszName);
2817 return E_NOTIMPL;
2818 }
2819
2820 static HRESULT WINAPI MimeMessage_QueryProp(
2821 IMimeMessage *iface,
2822 LPCSTR pszName,
2823 LPCSTR pszCriteria,
2824 boolean fSubString,
2825 boolean fCaseSensitive)
2826 {
2827 FIXME("(%p)->(%s, %s, %s, %s)\n", iface, pszName, pszCriteria, fSubString ? "TRUE" : "FALSE", fCaseSensitive ? "TRUE" : "FALSE");
2828 return E_NOTIMPL;
2829 }
2830
2831 static HRESULT WINAPI MimeMessage_GetTextBody(
2832 IMimeMessage *iface,
2833 DWORD dwTxtType,
2834 ENCODINGTYPE ietEncoding,
2835 IStream **pStream,
2836 LPHBODY phBody)
2837 {
2838 HRESULT hr;
2839 HBODY hbody;
2840 FINDBODY find_struct;
2841 IMimeBody *mime_body;
2842 static char text[] = "text";
2843 static char plain[] = "plain";
2844 static char html[] = "html";
2845
2846 TRACE("(%p)->(%d, %d, %p, %p)\n", iface, dwTxtType, ietEncoding, pStream, phBody);
2847
2848 find_struct.pszPriType = text;
2849
2850 switch(dwTxtType)
2851 {
2852 case TXT_PLAIN:
2853 find_struct.pszSubType = plain;
2854 break;
2855 case TXT_HTML:
2856 find_struct.pszSubType = html;
2857 break;
2858 default:
2859 return MIME_E_INVALID_TEXT_TYPE;
2860 }
2861
2862 hr = IMimeMessage_FindFirst(iface, &find_struct, &hbody);
2863 if(hr != S_OK)
2864 {
2865 TRACE("not found hr %08x\n", hr);
2866 *phBody = NULL;
2867 return hr;
2868 }
2869
2870 IMimeMessage_BindToObject(iface, hbody, &IID_IMimeBody, (void**)&mime_body);
2871
2872 IMimeBody_GetData(mime_body, ietEncoding, pStream);
2873 *phBody = hbody;
2874 IMimeBody_Release(mime_body);
2875 return hr;
2876 }
2877
2878 static HRESULT WINAPI MimeMessage_SetTextBody(
2879 IMimeMessage *iface,
2880 DWORD dwTxtType,
2881 ENCODINGTYPE ietEncoding,
2882 HBODY hAlternative,
2883 IStream *pStream,
2884 LPHBODY phBody)
2885 {
2886 FIXME("(%p)->(%d, %d, %p, %p, %p)\n", iface, dwTxtType, ietEncoding, hAlternative, pStream, phBody);
2887 return E_NOTIMPL;
2888 }
2889
2890 static HRESULT WINAPI MimeMessage_AttachObject(
2891 IMimeMessage *iface,
2892 REFIID riid,
2893 void *pvObject,
2894 LPHBODY phBody)
2895 {
2896 FIXME("(%p)->(%s, %p, %p)\n", iface, debugstr_guid(riid), pvObject, phBody);
2897 return E_NOTIMPL;
2898 }
2899
2900 static HRESULT WINAPI MimeMessage_AttachFile(
2901 IMimeMessage *iface,
2902 LPCSTR pszFilePath,
2903 IStream *pstmFile,
2904 LPHBODY phBody)
2905 {
2906 FIXME("(%p)->(%s, %p, %p)\n", iface, pszFilePath, pstmFile, phBody);
2907 return E_NOTIMPL;
2908 }
2909
2910 static HRESULT WINAPI MimeMessage_AttachURL(
2911 IMimeMessage *iface,
2912 LPCSTR pszBase,
2913 LPCSTR pszURL,
2914 DWORD dwFlags,
2915 IStream *pstmURL,
2916 LPSTR *ppszCIDURL,
2917 LPHBODY phBody)
2918 {
2919 FIXME("(%p)->(%s, %s, 0x%x, %p, %p, %p)\n", iface, pszBase, pszURL, dwFlags, pstmURL, ppszCIDURL, phBody);
2920 return E_NOTIMPL;
2921 }
2922
2923 static HRESULT WINAPI MimeMessage_GetAttachments(
2924 IMimeMessage *iface,
2925 ULONG *pcAttach,
2926 LPHBODY *pprghAttach)
2927 {
2928 HRESULT hr;
2929 FINDBODY find_struct;
2930 HBODY hbody;
2931 LPHBODY array;
2932 ULONG size = 10;
2933
2934 TRACE("(%p)->(%p, %p)\n", iface, pcAttach, pprghAttach);
2935
2936 *pcAttach = 0;
2937 array = CoTaskMemAlloc(size * sizeof(HBODY));
2938
2939 find_struct.pszPriType = find_struct.pszSubType = NULL;
2940 hr = IMimeMessage_FindFirst(iface, &find_struct, &hbody);
2941 while(hr == S_OK)
2942 {
2943 hr = IMimeMessage_IsContentType(iface, hbody, "multipart", NULL);
2944 TRACE("IsCT rets %08x %d\n", hr, *pcAttach);
2945 if(hr != S_OK)
2946 {
2947 if(*pcAttach + 1 > size)
2948 {
2949 size *= 2;
2950 array = CoTaskMemRealloc(array, size * sizeof(HBODY));
2951 }
2952 array[*pcAttach] = hbody;
2953 (*pcAttach)++;
2954 }
2955 hr = IMimeMessage_FindNext(iface, &find_struct, &hbody);
2956 }
2957
2958 *pprghAttach = array;
2959 return S_OK;
2960 }
2961
2962 static HRESULT WINAPI MimeMessage_GetAddressTable(
2963 IMimeMessage *iface,
2964 IMimeAddressTable **ppTable)
2965 {
2966 FIXME("(%p)->(%p)\n", iface, ppTable);
2967 return E_NOTIMPL;
2968 }
2969
2970 static HRESULT WINAPI MimeMessage_GetSender(
2971 IMimeMessage *iface,
2972 LPADDRESSPROPS pAddress)
2973 {
2974 FIXME("(%p)->(%p)\n", iface, pAddress);
2975 return E_NOTIMPL;
2976 }
2977
2978 static HRESULT WINAPI MimeMessage_GetAddressTypes(
2979 IMimeMessage *iface,
2980 DWORD dwAdrTypes,
2981 DWORD dwProps,
2982 LPADDRESSLIST pList)
2983 {
2984 FIXME("(%p)->(%d, %d, %p)\n", iface, dwAdrTypes, dwProps, pList);
2985 return E_NOTIMPL;
2986 }
2987
2988 static HRESULT WINAPI MimeMessage_GetAddressFormat(
2989 IMimeMessage *iface,
2990 DWORD dwAdrTypes,
2991 ADDRESSFORMAT format,
2992 LPSTR *ppszFormat)
2993 {
2994 FIXME("(%p)->(%d, %d, %p)\n", iface, dwAdrTypes, format, ppszFormat);
2995 return E_NOTIMPL;
2996 }
2997
2998 static HRESULT WINAPI MimeMessage_EnumAddressTypes(
2999 IMimeMessage *iface,
3000 DWORD dwAdrTypes,
3001 DWORD dwProps,
3002 IMimeEnumAddressTypes **ppEnum)
3003 {
3004 FIXME("(%p)->(%d, %d, %p)\n", iface, dwAdrTypes, dwProps, ppEnum);
3005 return E_NOTIMPL;
3006 }
3007
3008 static HRESULT WINAPI MimeMessage_SplitMessage(
3009 IMimeMessage *iface,
3010 ULONG cbMaxPart,
3011 IMimeMessageParts **ppParts)
3012 {
3013 FIXME("(%p)->(%d, %p)\n", iface, cbMaxPart, ppParts);
3014 return E_NOTIMPL;
3015 }
3016
3017 static HRESULT WINAPI MimeMessage_GetRootMoniker(
3018 IMimeMessage *iface,
3019 IMoniker **ppMoniker)
3020 {
3021 FIXME("(%p)->(%p)\n", iface, ppMoniker);
3022 return E_NOTIMPL;
3023 }
3024
3025 static const IMimeMessageVtbl MimeMessageVtbl =
3026 {
3027 MimeMessage_QueryInterface,
3028 MimeMessage_AddRef,
3029 MimeMessage_Release,
3030 MimeMessage_GetClassID,
3031 MimeMessage_IsDirty,
3032 MimeMessage_Load,
3033 MimeMessage_Save,
3034 MimeMessage_GetSizeMax,
3035 MimeMessage_InitNew,
3036 MimeMessage_GetMessageSource,
3037 MimeMessage_GetMessageSize,
3038 MimeMessage_LoadOffsetTable,
3039 MimeMessage_SaveOffsetTable,
3040 MimeMessage_GetFlags,
3041 MimeMessage_Commit,
3042 MimeMessage_HandsOffStorage,
3043 MimeMessage_BindToObject,
3044 MimeMessage_SaveBody,
3045 MimeMessage_InsertBody,
3046 MimeMessage_GetBody,
3047 MimeMessage_DeleteBody,
3048 MimeMessage_MoveBody,
3049 MimeMessage_CountBodies,
3050 MimeMessage_FindFirst,
3051 MimeMessage_FindNext,
3052 MimeMessage_ResolveURL,
3053 MimeMessage_ToMultipart,
3054 MimeMessage_GetBodyOffsets,
3055 MimeMessage_GetCharset,
3056 MimeMessage_SetCharset,
3057 MimeMessage_IsBodyType,
3058 MimeMessage_IsContentType,
3059 MimeMessage_QueryBodyProp,
3060 MimeMessage_GetBodyProp,
3061 MimeMessage_SetBodyProp,
3062 MimeMessage_DeleteBodyProp,
3063 MimeMessage_SetOption,
3064 MimeMessage_GetOption,
3065 MimeMessage_CreateWebPage,
3066 MimeMessage_GetProp,
3067 MimeMessage_SetProp,
3068 MimeMessage_DeleteProp,
3069 MimeMessage_QueryProp,
3070 MimeMessage_GetTextBody,
3071 MimeMessage_SetTextBody,
3072 MimeMessage_AttachObject,
3073 MimeMessage_AttachFile,
3074 MimeMessage_AttachURL,
3075 MimeMessage_GetAttachments,
3076 MimeMessage_GetAddressTable,
3077 MimeMessage_GetSender,
3078 MimeMessage_GetAddressTypes,
3079 MimeMessage_GetAddressFormat,
3080 MimeMessage_EnumAddressTypes,
3081 MimeMessage_SplitMessage,
3082 MimeMessage_GetRootMoniker,
3083 };
3084
3085 HRESULT MimeMessage_create(IUnknown *outer, void **obj)
3086 {
3087 MimeMessage *This;
3088 MimeBody *mime_body;
3089 body_t *root_body;
3090
3091 TRACE("(%p, %p)\n", outer, obj);
3092
3093 if (outer)
3094 {
3095 FIXME("outer unknown not supported yet\n");
3096 return E_NOTIMPL;
3097 }
3098
3099 *obj = NULL;
3100
3101 This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
3102 if (!This) return E_OUTOFMEMORY;
3103
3104 This->IMimeMessage_iface.lpVtbl = &MimeMessageVtbl;
3105 This->ref = 1;
3106 This->stream = NULL;
3107 list_init(&This->body_tree);
3108 This->next_index = 1;
3109
3110 mime_body = mimebody_create();
3111 root_body = new_body_entry(mime_body, This->next_index++, NULL);
3112 list_add_head(&This->body_tree, &root_body->entry);
3113
3114 *obj = &This->IMimeMessage_iface;
3115 return S_OK;
3116 }
3117
3118 /***********************************************************************
3119 * MimeOleCreateMessage (INETCOMM.@)
3120 */
3121 HRESULT WINAPI MimeOleCreateMessage(IUnknown *pUnkOuter, IMimeMessage **ppMessage)
3122 {
3123 TRACE("(%p, %p)\n", pUnkOuter, ppMessage);
3124 return MimeMessage_create(NULL, (void **)ppMessage);
3125 }
3126
3127 /***********************************************************************
3128 * MimeOleSetCompatMode (INETCOMM.@)
3129 */
3130 HRESULT WINAPI MimeOleSetCompatMode(DWORD dwMode)
3131 {
3132 FIXME("(0x%x)\n", dwMode);
3133 return S_OK;
3134 }
3135
3136 /***********************************************************************
3137 * MimeOleCreateVirtualStream (INETCOMM.@)
3138 */
3139 HRESULT WINAPI MimeOleCreateVirtualStream(IStream **ppStream)
3140 {
3141 HRESULT hr;
3142 FIXME("(%p)\n", ppStream);
3143
3144 hr = CreateStreamOnHGlobal(NULL, TRUE, ppStream);
3145 return hr;
3146 }
3147
3148 typedef struct MimeSecurity
3149 {
3150 IMimeSecurity IMimeSecurity_iface;
3151 LONG ref;
3152 } MimeSecurity;
3153
3154 static inline MimeSecurity *impl_from_IMimeSecurity(IMimeSecurity *iface)
3155 {
3156 return CONTAINING_RECORD(iface, MimeSecurity, IMimeSecurity_iface);
3157 }
3158
3159 static HRESULT WINAPI MimeSecurity_QueryInterface(IMimeSecurity *iface, REFIID riid, void **ppv)
3160 {
3161 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3162
3163 if (IsEqualIID(riid, &IID_IUnknown) ||
3164 IsEqualIID(riid, &IID_IMimeSecurity))
3165 {
3166 *ppv = iface;
3167 IMimeSecurity_AddRef(iface);
3168 return S_OK;
3169 }
3170
3171 FIXME("no interface for %s\n", debugstr_guid(riid));
3172 *ppv = NULL;
3173 return E_NOINTERFACE;
3174 }
3175
3176 static ULONG WINAPI MimeSecurity_AddRef(IMimeSecurity *iface)
3177 {
3178 MimeSecurity *This = impl_from_IMimeSecurity(iface);
3179 LONG ref = InterlockedIncrement(&This->ref);
3180
3181 TRACE("(%p) ref=%d\n", This, ref);
3182
3183 return ref;
3184 }
3185
3186 static ULONG WINAPI MimeSecurity_Release(IMimeSecurity *iface)
3187 {
3188 MimeSecurity *This = impl_from_IMimeSecurity(iface);
3189 LONG ref = InterlockedDecrement(&This->ref);
3190
3191 TRACE("(%p) ref=%d\n", This, ref);
3192
3193 if (!ref)
3194 HeapFree(GetProcessHeap(), 0, This);
3195
3196 return ref;
3197 }
3198
3199 static HRESULT WINAPI MimeSecurity_InitNew(
3200 IMimeSecurity* iface)
3201 {
3202 FIXME("(%p)->(): stub\n", iface);
3203 return S_OK;
3204 }
3205
3206 static HRESULT WINAPI MimeSecurity_CheckInit(
3207 IMimeSecurity* iface)
3208 {
3209 FIXME("(%p)->(): stub\n", iface);
3210 return E_NOTIMPL;
3211 }
3212
3213 static HRESULT WINAPI MimeSecurity_EncodeMessage(
3214 IMimeSecurity* iface,
3215 IMimeMessageTree* pTree,
3216 DWORD dwFlags)
3217 {