* Sync to trunk r63845.
[reactos.git] / dll / win32 / mapi32 / sendmail.c
1 /*
2 * MAPISendMail implementation
3 *
4 * Copyright 2005 Hans Leidekker
5 * Copyright 2009 Owen Rudge 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 #include "precomp.h"
23
24 #include <winuser.h>
25
26 #include "res.h"
27
28 #define READ_BUF_SIZE 4096
29
30 #define STORE_UNICODE_OK 0x00040000
31
32 static LPSTR convert_from_unicode(LPCWSTR wstr)
33 {
34 LPSTR str;
35 DWORD len;
36
37 if (!wstr)
38 return NULL;
39
40 len = WideCharToMultiByte(CP_ACP, 0, wstr, -1, NULL, 0, NULL, NULL);
41 str = HeapAlloc(GetProcessHeap(), 0, len);
42 WideCharToMultiByte(CP_ACP, 0, wstr, -1, str, len, NULL, NULL);
43
44 return str;
45 }
46
47 static LPWSTR convert_to_unicode(LPSTR str)
48 {
49 LPWSTR wstr;
50 DWORD len;
51
52 if (!str)
53 return NULL;
54
55 len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
56 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
57 MultiByteToWideChar(CP_ACP, 0, str, -1, wstr, len);
58
59 return wstr;
60 }
61
62 /*
63 Internal function to send a message via Extended MAPI. Wrapper around the Simple
64 MAPI function MAPISendMail.
65 */
66 static ULONG sendmail_extended_mapi(LHANDLE mapi_session, ULONG_PTR uiparam, lpMapiMessageW message,
67 FLAGS flags)
68 {
69 ULONG tags[] = {1, 0};
70 char *subjectA = NULL, *bodyA = NULL;
71 ULONG retval = MAPI_E_FAILURE;
72 IMAPISession *session = NULL;
73 BOOL unicode_aware = FALSE;
74 IMAPITable* msg_table;
75 LPSRowSet rows = NULL;
76 IMsgStore* msg_store;
77 IMAPIFolder* folder = NULL, *draft_folder = NULL;
78 LPENTRYID entry_id;
79 LPSPropValue props;
80 ULONG entry_len;
81 DWORD obj_type;
82 IMessage* msg;
83 ULONG values;
84 HRESULT ret;
85
86 TRACE("Using Extended MAPI wrapper for MAPISendMail\n");
87
88 /* Attempt to log on via Extended MAPI */
89
90 ret = MAPILogonEx(0, NULL, NULL, MAPI_EXTENDED | MAPI_USE_DEFAULT | MAPI_NEW_SESSION, &session);
91 TRACE("MAPILogonEx: %x\n", ret);
92
93 if (ret != S_OK)
94 {
95 retval = MAPI_E_LOGIN_FAILURE;
96 goto cleanup;
97 }
98
99 /* Open the default message store */
100
101 if (IMAPISession_GetMsgStoresTable(session, 0, &msg_table) == S_OK)
102 {
103 /* We want the default store */
104 SizedSPropTagArray(2, columns) = {2, {PR_ENTRYID, PR_DEFAULT_STORE}};
105
106 /* Set the columns we want */
107 if (IMAPITable_SetColumns(msg_table, (LPSPropTagArray) &columns, 0) == S_OK)
108 {
109 while (1)
110 {
111 if (IMAPITable_QueryRows(msg_table, 1, 0, &rows) != S_OK)
112 {
113 MAPIFreeBuffer(rows);
114 rows = NULL;
115 }
116 else if (rows->cRows != 1)
117 {
118 FreeProws(rows);
119 rows = NULL;
120 }
121 else
122 {
123 /* If it's not the default store, try the next row */
124 if (!rows->aRow[0].lpProps[1].Value.b)
125 {
126 FreeProws(rows);
127 continue;
128 }
129 }
130
131 break;
132 }
133 }
134
135 IMAPITable_Release(msg_table);
136 }
137
138 /* Did we manage to get the right store? */
139 if (!rows)
140 goto logoff;
141
142 /* Open the message store */
143 IMAPISession_OpenMsgStore(session, 0, rows->aRow[0].lpProps[0].Value.bin.cb,
144 (ENTRYID *) rows->aRow[0].lpProps[0].Value.bin.lpb, NULL,
145 MDB_NO_DIALOG | MAPI_BEST_ACCESS, &msg_store);
146
147 /* We don't need this any more */
148 FreeProws(rows);
149
150 /* Check if the message store supports Unicode */
151 tags[1] = PR_STORE_SUPPORT_MASK;
152 ret = IMsgStore_GetProps(msg_store, (LPSPropTagArray) tags, 0, &values, &props);
153
154 if ((ret == S_OK) && (props[0].Value.l & STORE_UNICODE_OK))
155 unicode_aware = TRUE;
156 else
157 {
158 /* Don't convert to ANSI */
159 if (flags & MAPI_FORCE_UNICODE)
160 {
161 WARN("No Unicode-capable mail client, and MAPI_FORCE_UNICODE is specified. MAPISendMail failed.\n");
162 retval = MAPI_E_UNICODE_NOT_SUPPORTED;
163 IMsgStore_Release(msg_store);
164 goto logoff;
165 }
166 }
167
168 /* First open the inbox, from which the drafts folder can be opened */
169 if (IMsgStore_GetReceiveFolder(msg_store, NULL, 0, &entry_len, &entry_id, NULL) == S_OK)
170 {
171 IMsgStore_OpenEntry(msg_store, entry_len, entry_id, NULL, 0, &obj_type, (LPUNKNOWN*) &folder);
172 MAPIFreeBuffer(entry_id);
173 }
174
175 tags[1] = PR_IPM_DRAFTS_ENTRYID;
176
177 /* Open the drafts folder, or failing that, try asking the message store for the outbox */
178 if ((folder == NULL) || ((ret = IMAPIFolder_GetProps(folder, (LPSPropTagArray) tags, 0, &values, &props)) != S_OK))
179 {
180 TRACE("Unable to open Drafts folder; opening Outbox instead\n");
181 tags[1] = PR_IPM_OUTBOX_ENTRYID;
182 ret = IMsgStore_GetProps(msg_store, (LPSPropTagArray) tags, 0, &values, &props);
183 }
184
185 if (ret != S_OK)
186 goto logoff;
187
188 IMsgStore_OpenEntry(msg_store, props[0].Value.bin.cb, (LPENTRYID) props[0].Value.bin.lpb,
189 NULL, MAPI_MODIFY, &obj_type, (LPUNKNOWN *) &draft_folder);
190
191 /* Create a new message */
192 if (IMAPIFolder_CreateMessage(draft_folder, NULL, 0, &msg) == S_OK)
193 {
194 ULONG token;
195 SPropValue p;
196
197 /* Define message properties */
198 p.ulPropTag = PR_MESSAGE_FLAGS;
199 p.Value.l = MSGFLAG_FROMME | MSGFLAG_UNSENT;
200
201 IMessage_SetProps(msg, 1, &p, NULL);
202
203 p.ulPropTag = PR_SENTMAIL_ENTRYID;
204 p.Value.bin.cb = props[0].Value.bin.cb;
205 p.Value.bin.lpb = props[0].Value.bin.lpb;
206 IMessage_SetProps(msg, 1,&p, NULL);
207
208 /* Set message subject */
209 if (message->lpszSubject)
210 {
211 if (unicode_aware)
212 {
213 p.ulPropTag = PR_SUBJECT_W;
214 p.Value.lpszW = message->lpszSubject;
215 }
216 else
217 {
218 subjectA = convert_from_unicode(message->lpszSubject);
219
220 p.ulPropTag = PR_SUBJECT_A;
221 p.Value.lpszA = subjectA;
222 }
223
224 IMessage_SetProps(msg, 1, &p, NULL);
225 }
226
227 /* Set message body */
228 if (message->lpszNoteText)
229 {
230 LPSTREAM stream = NULL;
231
232 if (IMessage_OpenProperty(msg, unicode_aware ? PR_BODY_W : PR_BODY_A, &IID_IStream, 0,
233 MAPI_MODIFY | MAPI_CREATE, (LPUNKNOWN*) &stream) == S_OK)
234 {
235 if (unicode_aware)
236 IStream_Write(stream, message->lpszNoteText, (lstrlenW(message->lpszNoteText)+1) * sizeof(WCHAR), NULL);
237 else
238 {
239 bodyA = convert_from_unicode(message->lpszNoteText);
240 IStream_Write(stream, bodyA, strlen(bodyA)+1, NULL);
241 }
242
243 IStream_Release(stream);
244 }
245 }
246
247 /* Add message attachments */
248 if (message->nFileCount > 0)
249 {
250 ULONG num_attach = 0;
251 unsigned int i;
252
253 for (i = 0; i < message->nFileCount; i++)
254 {
255 IAttach* attachment = NULL;
256 char *filenameA = NULL;
257 SPropValue prop[4];
258 LPCWSTR filename;
259 HANDLE file;
260
261 if (!message->lpFiles[i].lpszPathName)
262 continue;
263
264 /* Open the attachment for reading */
265 file = CreateFileW(message->lpFiles[i].lpszPathName, GENERIC_READ, FILE_SHARE_READ,
266 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
267
268 if (file == INVALID_HANDLE_VALUE)
269 continue;
270
271 /* Check if a display filename has been given; if not, get one ourselves from path name */
272 filename = message->lpFiles[i].lpszFileName;
273
274 if (!filename)
275 {
276 int j;
277
278 filename = message->lpFiles[i].lpszPathName;
279
280 for (j = lstrlenW(message->lpFiles[i].lpszPathName)-1; j >= 0; j--)
281 {
282 if (message->lpFiles[i].lpszPathName[i] == '\\' ||
283 message->lpFiles[i].lpszPathName[i] == '/')
284 {
285 filename = &message->lpFiles[i].lpszPathName[i+1];
286 break;
287 }
288 }
289 }
290
291 TRACE("Attachment %u path: '%s'; filename: '%s'\n", i, debugstr_w(message->lpFiles[i].lpszPathName),
292 debugstr_w(filename));
293
294 /* Create the attachment */
295 if (IMessage_CreateAttach(msg, NULL, 0, &num_attach, &attachment) != S_OK)
296 {
297 TRACE("Unable to create attachment\n");
298 CloseHandle(file);
299 continue;
300 }
301
302 /* Set the attachment properties */
303 ZeroMemory(prop, sizeof(prop));
304
305 prop[0].ulPropTag = PR_ATTACH_METHOD;
306 prop[0].Value.ul = ATTACH_BY_VALUE;
307
308 if (unicode_aware)
309 {
310 prop[1].ulPropTag = PR_ATTACH_LONG_FILENAME_W;
311 prop[1].Value.lpszW = (LPWSTR) filename;
312 prop[2].ulPropTag = PR_ATTACH_FILENAME_W;
313 prop[2].Value.lpszW = (LPWSTR) filename;
314 }
315 else
316 {
317 filenameA = convert_from_unicode(filename);
318
319 prop[1].ulPropTag = PR_ATTACH_LONG_FILENAME_A;
320 prop[1].Value.lpszA = (LPSTR) filenameA;
321 prop[2].ulPropTag = PR_ATTACH_FILENAME_A;
322 prop[2].Value.lpszA = (LPSTR) filenameA;
323
324 }
325
326 prop[3].ulPropTag = PR_RENDERING_POSITION;
327 prop[3].Value.l = -1;
328
329 if (IAttach_SetProps(attachment, 4, prop, NULL) == S_OK)
330 {
331 LPSTREAM stream = NULL;
332
333 if (IAttach_OpenProperty(attachment, PR_ATTACH_DATA_BIN, &IID_IStream, 0,
334 MAPI_MODIFY | MAPI_CREATE, (LPUNKNOWN*) &stream) == S_OK)
335 {
336 BYTE data[READ_BUF_SIZE];
337 DWORD size = 0, read, written;
338
339 while (ReadFile(file, data, READ_BUF_SIZE, &read, NULL) && (read != 0))
340 {
341 IStream_Write(stream, data, read, &written);
342 size += read;
343 }
344
345 TRACE("%d bytes written of attachment\n", size);
346
347 IStream_Commit(stream, STGC_DEFAULT);
348 IStream_Release(stream);
349
350 prop[0].ulPropTag = PR_ATTACH_SIZE;
351 prop[0].Value.ul = size;
352 IAttach_SetProps(attachment, 1, prop, NULL);
353
354 IAttach_SaveChanges(attachment, KEEP_OPEN_READONLY);
355 num_attach++;
356 }
357 }
358
359 CloseHandle(file);
360 IAttach_Release(attachment);
361
362 HeapFree(GetProcessHeap(), 0, filenameA);
363 }
364 }
365
366 IMessage_SaveChanges(msg, KEEP_OPEN_READWRITE);
367
368 /* Prepare the message form */
369
370 if (IMAPISession_PrepareForm(session, NULL, msg, &token) == S_OK)
371 {
372 ULONG access = 0, status = 0, message_flags = 0, pc = 0;
373 ULONG pT[2] = {1, PR_MSG_STATUS};
374
375 /* Retrieve message status, flags, access rights and class */
376
377 if (IMessage_GetProps(msg, (LPSPropTagArray) pT, 0, &pc, &props) == S_OK)
378 {
379 status = props->Value.ul;
380 MAPIFreeBuffer(props);
381 }
382
383 pT[1] = PR_MESSAGE_FLAGS;
384
385 if (IMessage_GetProps(msg, (LPSPropTagArray) pT, 0, &pc, &props) == S_OK)
386 {
387 message_flags = props->Value.ul;
388 MAPIFreeBuffer(props);
389 }
390
391 pT[1] = PR_ACCESS;
392
393 if (IMessage_GetProps(msg, (LPSPropTagArray) pT, 0, &pc, &props) == S_OK)
394 {
395 access = props->Value.ul;
396 MAPIFreeBuffer(props);
397 }
398
399 pT[1] = PR_MESSAGE_CLASS_A;
400
401 if (IMessage_GetProps(msg, (LPSPropTagArray) pT, 0, &pc, &props) == S_OK)
402 {
403 /* Show the message form (edit window) */
404
405 ret = IMAPISession_ShowForm(session, 0, msg_store, draft_folder, NULL,
406 token, NULL, 0, status, message_flags, access,
407 props->Value.lpszA);
408
409 switch (ret)
410 {
411 case S_OK:
412 retval = SUCCESS_SUCCESS;
413 break;
414
415 case MAPI_E_USER_CANCEL:
416 retval = MAPI_E_USER_ABORT;
417 break;
418
419 default:
420 TRACE("ShowForm failure: %x\n", ret);
421 break;
422 }
423 }
424 }
425
426 IMessage_Release(msg);
427 }
428
429 /* Free up the resources we've used */
430 IMAPIFolder_Release(draft_folder);
431 if (folder) IMAPIFolder_Release(folder);
432 IMsgStore_Release(msg_store);
433
434 HeapFree(GetProcessHeap(), 0, subjectA);
435 HeapFree(GetProcessHeap(), 0, bodyA);
436
437 logoff: ;
438 IMAPISession_Logoff(session, 0, 0, 0);
439 IMAPISession_Release(session);
440
441 cleanup: ;
442 MAPIUninitialize();
443 return retval;
444 }
445
446 /**************************************************************************
447 * MAPISendMail (MAPI32.211)
448 *
449 * Send a mail.
450 *
451 * PARAMS
452 * session [I] Handle to a MAPI session.
453 * uiparam [I] Parent window handle.
454 * message [I] Pointer to a MAPIMessage structure.
455 * flags [I] Flags.
456 * reserved [I] Reserved, pass 0.
457 *
458 * RETURNS
459 * Success: SUCCESS_SUCCESS
460 * Failure: MAPI_E_FAILURE
461 *
462 */
463 ULONG WINAPI MAPISendMail( LHANDLE session, ULONG_PTR uiparam,
464 lpMapiMessage message, FLAGS flags, ULONG reserved )
465 {
466 WCHAR msg_title[READ_BUF_SIZE], error_msg[READ_BUF_SIZE];
467
468 /* Check to see if we have a Simple MAPI provider loaded */
469 if (mapiFunctions.MAPISendMail)
470 return mapiFunctions.MAPISendMail(session, uiparam, message, flags, reserved);
471
472 /* Check if we have an Extended MAPI provider - if so, use our wrapper */
473 if (MAPIInitialize(NULL) == S_OK)
474 {
475 MapiMessageW messageW;
476 ULONG ret;
477
478 ZeroMemory(&messageW, sizeof(MapiMessageW));
479
480 /* Convert the entries we need to Unicode */
481 messageW.lpszSubject = convert_to_unicode(message->lpszSubject);
482 messageW.lpszNoteText = convert_to_unicode(message->lpszNoteText);
483 messageW.nFileCount = message->nFileCount;
484
485 if (message->nFileCount && message->lpFiles)
486 {
487 lpMapiFileDescW filesW;
488 unsigned int i;
489
490 filesW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MapiFileDescW) * message->nFileCount);
491
492 for (i = 0; i < message->nFileCount; i++)
493 {
494 filesW[i].lpszPathName = convert_to_unicode(message->lpFiles[i].lpszPathName);
495 filesW[i].lpszFileName = convert_to_unicode(message->lpFiles[i].lpszFileName);
496 }
497
498 messageW.lpFiles = filesW;
499 }
500
501 ret = sendmail_extended_mapi(session, uiparam, &messageW, flags);
502
503 /* Now free everything we allocated */
504 if (message->nFileCount && message->lpFiles)
505 {
506 unsigned int i;
507
508 for (i = 0; i < message->nFileCount; i++)
509 {
510 HeapFree(GetProcessHeap(), 0, messageW.lpFiles[i].lpszPathName);
511 HeapFree(GetProcessHeap(), 0, messageW.lpFiles[i].lpszFileName);
512 }
513
514 HeapFree(GetProcessHeap(), 0, messageW.lpFiles);
515 }
516
517 HeapFree(GetProcessHeap(), 0, messageW.lpszSubject);
518 HeapFree(GetProcessHeap(), 0, messageW.lpszNoteText);
519
520 return ret;
521 }
522
523 /* Display an error message since we apparently have no mail clients */
524 LoadStringW(hInstMAPI32, IDS_NO_MAPI_CLIENT, error_msg, sizeof(error_msg) / sizeof(WCHAR));
525 LoadStringW(hInstMAPI32, IDS_SEND_MAIL, msg_title, sizeof(msg_title) / sizeof(WCHAR));
526
527 MessageBoxW((HWND) uiparam, error_msg, msg_title, MB_ICONEXCLAMATION);
528
529 return MAPI_E_NOT_SUPPORTED;
530 }
531
532 static lpMapiRecipDesc convert_recipient_from_unicode(lpMapiRecipDescW recipW, lpMapiRecipDesc dest)
533 {
534 lpMapiRecipDesc ret;
535
536 if (!recipW)
537 return NULL;
538
539 if (dest)
540 ret = dest;
541 else
542 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MapiRecipDesc));
543
544 ret->ulRecipClass = recipW->ulRecipClass;
545 ret->lpszName = convert_from_unicode(recipW->lpszName);
546 ret->lpszAddress = convert_from_unicode(recipW->lpszAddress);
547 ret->ulEIDSize = recipW->ulEIDSize;
548 ret->lpEntryID = recipW->lpEntryID;
549
550 return ret;
551 }
552
553 /**************************************************************************
554 * MAPISendMailW (MAPI32.256)
555 *
556 * Send a mail.
557 *
558 * PARAMS
559 * session [I] Handle to a MAPI session.
560 * uiparam [I] Parent window handle.
561 * message [I] Pointer to a MAPIMessageW structure.
562 * flags [I] Flags.
563 * reserved [I] Reserved, pass 0.
564 *
565 * RETURNS
566 * Success: SUCCESS_SUCCESS
567 * Failure: MAPI_E_FAILURE
568 *
569 */
570 ULONG WINAPI MAPISendMailW(LHANDLE session, ULONG_PTR uiparam,
571 lpMapiMessageW message, FLAGS flags, ULONG reserved)
572 {
573 WCHAR msg_title[READ_BUF_SIZE], error_msg[READ_BUF_SIZE];
574
575 /* Check to see if we have a Simple MAPI provider loaded */
576 if (mapiFunctions.MAPISendMailW)
577 return mapiFunctions.MAPISendMailW(session, uiparam, message, flags, reserved);
578
579 /* Check if we have an Extended MAPI provider - if so, use our wrapper */
580 if (MAPIInitialize(NULL) == S_OK)
581 return sendmail_extended_mapi(session, uiparam, message, flags);
582
583 if (mapiFunctions.MAPISendMail)
584 {
585 MapiMessage messageA;
586 ULONG ret;
587
588 if (flags & MAPI_FORCE_UNICODE)
589 return MAPI_E_UNICODE_NOT_SUPPORTED;
590
591 /* Convert to ANSI and send to MAPISendMail */
592 ZeroMemory(&messageA, sizeof(MapiMessage));
593
594 messageA.lpszSubject = convert_from_unicode(message->lpszSubject);
595 messageA.lpszNoteText = convert_from_unicode(message->lpszNoteText);
596 messageA.lpszMessageType = convert_from_unicode(message->lpszMessageType);
597 messageA.lpszDateReceived = convert_from_unicode(message->lpszDateReceived);
598 messageA.lpszConversationID = convert_from_unicode(message->lpszConversationID);
599 messageA.flFlags = message->flFlags;
600 messageA.lpOriginator = convert_recipient_from_unicode(message->lpOriginator, NULL);
601 messageA.nRecipCount = message->nRecipCount;
602 messageA.nFileCount = message->nFileCount;
603
604 if (message->nRecipCount && message->lpRecips)
605 {
606 lpMapiRecipDesc recipsA;
607 unsigned int i;
608
609 recipsA = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MapiRecipDesc) * message->nRecipCount);
610
611 for (i = 0; i < message->nRecipCount; i++)
612 {
613 convert_recipient_from_unicode(&message->lpRecips[i], &recipsA[i]);
614 }
615
616 messageA.lpRecips = recipsA;
617 }
618
619 if (message->nFileCount && message->lpFiles)
620 {
621 lpMapiFileDesc filesA;
622 unsigned int i;
623
624 filesA = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MapiFileDesc) * message->nFileCount);
625
626 for (i = 0; i < message->nFileCount; i++)
627 {
628 filesA[i].flFlags = message->lpFiles[i].flFlags;
629 filesA[i].nPosition = message->lpFiles[i].nPosition;
630 filesA[i].lpszPathName = convert_from_unicode(message->lpFiles[i].lpszPathName);
631 filesA[i].lpszFileName = convert_from_unicode(message->lpFiles[i].lpszFileName);
632 filesA[i].lpFileType = message->lpFiles[i].lpFileType;
633 }
634
635 messageA.lpFiles = filesA;
636 }
637
638 ret = mapiFunctions.MAPISendMail(session, uiparam, &messageA, flags, reserved);
639
640 /* Now free everything we allocated */
641 if (message->lpOriginator)
642 {
643 HeapFree(GetProcessHeap(), 0, messageA.lpOriginator->lpszName);
644 HeapFree(GetProcessHeap(), 0, messageA.lpOriginator->lpszAddress);
645 HeapFree(GetProcessHeap(), 0, messageA.lpOriginator);
646 }
647
648 if (message->nRecipCount && message->lpRecips)
649 {
650 unsigned int i;
651
652 for (i = 0; i < message->nRecipCount; i++)
653 {
654 HeapFree(GetProcessHeap(), 0, messageA.lpRecips[i].lpszName);
655 HeapFree(GetProcessHeap(), 0, messageA.lpRecips[i].lpszAddress);
656 }
657
658 HeapFree(GetProcessHeap(), 0, messageA.lpRecips);
659 }
660
661 if (message->nFileCount && message->lpFiles)
662 {
663 unsigned int i;
664
665 for (i = 0; i < message->nFileCount; i++)
666 {
667 HeapFree(GetProcessHeap(), 0, messageA.lpFiles[i].lpszPathName);
668 HeapFree(GetProcessHeap(), 0, messageA.lpFiles[i].lpszFileName);
669 }
670
671 HeapFree(GetProcessHeap(), 0, messageA.lpFiles);
672 }
673
674 HeapFree(GetProcessHeap(), 0, messageA.lpszSubject);
675 HeapFree(GetProcessHeap(), 0, messageA.lpszNoteText);
676 HeapFree(GetProcessHeap(), 0, messageA.lpszDateReceived);
677 HeapFree(GetProcessHeap(), 0, messageA.lpszConversationID);
678
679 return ret;
680 }
681
682 /* Display an error message since we apparently have no mail clients */
683 LoadStringW(hInstMAPI32, IDS_NO_MAPI_CLIENT, error_msg, sizeof(error_msg) / sizeof(WCHAR));
684 LoadStringW(hInstMAPI32, IDS_SEND_MAIL, msg_title, sizeof(msg_title) / sizeof(WCHAR));
685
686 MessageBoxW((HWND) uiparam, error_msg, msg_title, MB_ICONEXCLAMATION);
687
688 return MAPI_E_NOT_SUPPORTED;
689 }
690
691 ULONG WINAPI MAPISendDocuments(ULONG_PTR uiparam, LPSTR delim, LPSTR paths,
692 LPSTR filenames, ULONG reserved)
693 {
694 if (mapiFunctions.MAPISendDocuments)
695 return mapiFunctions.MAPISendDocuments(uiparam, delim, paths, filenames, reserved);
696
697 return MAPI_E_NOT_SUPPORTED;
698 }