Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / shell / source / win32 / simplemail / senddoc.cxx
blob3b57684fe8219bbb91254f5398a678259188bde6
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <osl/diagnose.h>
21 #include <sal/macros.h>
23 #include <o3tl/char16_t2wchar_t.hxx>
24 #include <rtl/bootstrap.hxx>
25 #include <sfx2/strings.hrc>
26 #include <unotools/resmgr.hxx>
27 #include <i18nlangtag/languagetag.hxx>
29 #include <wchar.h>
30 #define WIN32_LEAN_AND_MEAN
31 #include <windows.h>
32 #include <mapi.h>
33 #include <MapiUnicodeHelp.h>
35 #include <string>
36 #include <vector>
37 #if OSL_DEBUG_LEVEL > 0
38 #include <sstream>
39 #endif
40 #include <stdexcept>
42 #if OSL_DEBUG_LEVEL > 0
43 static void dumpParameter();
44 #endif
46 typedef std::vector<MapiRecipDescW> MapiRecipientList_t;
47 typedef std::vector<MapiFileDescW> MapiAttachmentList_t;
49 const int LEN_SMTP_PREFIX = 5; // "SMTP:"
51 namespace /* private */
53 OUString gLangTag;
54 OUString gBootstrap;
55 std::wstring gFrom;
56 std::wstring gSubject;
57 std::wstring gBody;
58 std::vector<std::wstring> gTo;
59 std::vector<std::wstring> gCc;
60 std::vector<std::wstring> gBcc;
61 // Keep temp filepath and displayed name
62 std::vector<std::pair<std::wstring, std::wstring>> gAttachments;
63 int gMapiFlags = 0;
66 /**
67 Add a prefix to an email address. MAPI requires that
68 email addresses have an 'SMTP:' prefix.
70 @param aEmailAddress
71 [in] the email address.
73 @param aPrefix
74 [in] the prefix to be added to the email address.
76 @returns
77 the email address prefixed with the specified prefix.
79 static std::wstring prefixEmailAddress(
80 const std::wstring& aEmailAddress,
81 const std::wstring& aPrefix = L"SMTP:")
83 return (aPrefix + aEmailAddress);
86 /** @internal */
87 static void addRecipient(
88 ULONG recipClass,
89 const std::wstring& recipAddress,
90 MapiRecipientList_t* pMapiRecipientList)
92 MapiRecipDescW mrd;
93 ZeroMemory(&mrd, sizeof(mrd));
95 mrd.ulRecipClass = recipClass;
96 mrd.lpszName = const_cast<wchar_t*>(recipAddress.c_str()) + LEN_SMTP_PREFIX;
97 mrd.lpszAddress = const_cast<wchar_t*>(recipAddress.c_str());
98 pMapiRecipientList->push_back(mrd);
101 /** @internal */
102 static void initRecipientList(MapiRecipientList_t* pMapiRecipientList)
104 OSL_ASSERT(pMapiRecipientList->empty());
106 // add to recipients
107 for (const auto& address : gTo)
108 addRecipient(MAPI_TO, address, pMapiRecipientList);
110 // add cc recipients
111 for (const auto& address : gCc)
112 addRecipient(MAPI_CC, address, pMapiRecipientList);
114 // add bcc recipients
115 for (const auto& address : gBcc)
116 addRecipient(MAPI_BCC, address, pMapiRecipientList);
119 /** @internal */
120 static void initAttachmentList(MapiAttachmentList_t* pMapiAttachmentList)
122 OSL_ASSERT(pMapiAttachmentList->empty());
124 for (const auto& attachment : gAttachments)
126 MapiFileDescW mfd;
127 ZeroMemory(&mfd, sizeof(mfd));
128 mfd.lpszPathName = const_cast<wchar_t*>(attachment.first.c_str());
129 // MapiFileDesc documentation (https://msdn.microsoft.com/en-us/library/hh707272)
130 // allows using here either nullptr, or a pointer to empty string. However,
131 // for Outlook 2013, we cannot use nullptr here, and must point to a (possibly
132 // empty) string: otherwise using MAPI_DIALOG_MODELESS results in MAPI_E_FAILURE.
133 // See http://peach.ease.lsoft.com/scripts/wa-PEACH.exe?A2=MAPI-L;d2bf3060.1604
134 // Since C++11, c_str() must return a pointer to single null character when the
135 // string is empty, so we are OK here in case when there's no explicit file name
136 // passed
137 mfd.lpszFileName = const_cast<wchar_t*>(attachment.second.c_str());
138 mfd.nPosition = sal::static_int_cast<ULONG>(-1);
139 pMapiAttachmentList->push_back(mfd);
143 /** @internal */
144 static void initMapiOriginator(MapiRecipDescW* pMapiOriginator)
146 ZeroMemory(pMapiOriginator, sizeof(*pMapiOriginator));
148 pMapiOriginator->ulRecipClass = MAPI_ORIG;
149 pMapiOriginator->lpszName = const_cast<wchar_t*>(L"");
150 pMapiOriginator->lpszAddress = const_cast<wchar_t*>(gFrom.c_str());
153 /** @internal */
154 static void initMapiMessage(
155 MapiRecipDescW* aMapiOriginator,
156 MapiRecipientList_t& aMapiRecipientList,
157 MapiAttachmentList_t& aMapiAttachmentList,
158 MapiMessageW* pMapiMessage)
160 ZeroMemory(pMapiMessage, sizeof(*pMapiMessage));
162 pMapiMessage->lpszSubject = const_cast<wchar_t*>(gSubject.c_str());
163 pMapiMessage->lpszNoteText = (gBody.length() ? const_cast<wchar_t*>(gBody.c_str()) : nullptr);
164 pMapiMessage->lpOriginator = aMapiOriginator;
165 pMapiMessage->lpRecips = aMapiRecipientList.size() ? aMapiRecipientList.data() : nullptr;
166 pMapiMessage->nRecipCount = aMapiRecipientList.size();
167 if (!aMapiAttachmentList.empty())
168 pMapiMessage->lpFiles = aMapiAttachmentList.data();
169 pMapiMessage->nFileCount = aMapiAttachmentList.size();
172 const wchar_t* const KnownParameters[] =
174 L"--to",
175 L"--cc",
176 L"--bcc",
177 L"--from",
178 L"--subject",
179 L"--body",
180 L"--attach",
181 L"--mapi-dialog",
182 L"--mapi-logon-ui",
183 L"--langtag",
184 L"--bootstrap",
187 /** @internal */
188 static bool isKnownParameter(const wchar_t* aParameterName)
190 for (const wchar_t* KnownParameter : KnownParameters)
191 if (_wcsicmp(aParameterName, KnownParameter) == 0)
192 return true;
194 return false;
197 /** @internal */
198 static void initParameter(int argc, wchar_t* argv[])
200 for (int i = 1; i < argc; i++)
202 if (!isKnownParameter(argv[i]))
204 OSL_FAIL("Wrong parameter received");
205 continue;
208 if (_wcsicmp(argv[i], L"--mapi-dialog") == 0)
210 // MAPI_DIALOG_MODELESS has many problems and crashes Outlook 2016.
211 // see the commit message for a lengthy description.
212 gMapiFlags |= MAPI_DIALOG;
214 else if (_wcsicmp(argv[i], L"--mapi-logon-ui") == 0)
216 gMapiFlags |= MAPI_LOGON_UI;
218 else if ((i+1) < argc) // is the value of a parameter available too?
220 if (_wcsicmp(argv[i], L"--to") == 0)
221 gTo.push_back(prefixEmailAddress(argv[i+1]));
222 else if (_wcsicmp(argv[i], L"--cc") == 0)
223 gCc.push_back(prefixEmailAddress(argv[i+1]));
224 else if (_wcsicmp(argv[i], L"--bcc") == 0)
225 gBcc.push_back(prefixEmailAddress(argv[i+1]));
226 else if (_wcsicmp(argv[i], L"--from") == 0)
227 gFrom = prefixEmailAddress(argv[i+1]);
228 else if (_wcsicmp(argv[i], L"--subject") == 0)
229 gSubject = argv[i+1];
230 else if (_wcsicmp(argv[i], L"--body") == 0)
231 gBody = argv[i+1];
232 else if (_wcsicmp(argv[i], L"--attach") == 0)
234 std::wstring sPath(argv[i + 1]);
235 // An attachment may optionally be immediately followed by --attach-name and user-visible name
236 std::wstring sName;
237 if ((i + 3) < argc && _wcsicmp(argv[i+2], L"--attach-name") == 0)
239 sName = argv[i+3];
240 i += 2;
242 gAttachments.emplace_back(sPath, sName);
244 else if (_wcsicmp(argv[i], L"--langtag") == 0)
245 gLangTag = o3tl::toU(argv[i+1]);
246 else if (_wcsicmp(argv[i], L"--bootstrap") == 0)
247 gBootstrap = o3tl::toU(argv[i+1]);
249 i++;
254 static void ShowError(ULONG nMAPIResult)
256 if (!gBootstrap.isEmpty())
257 rtl::Bootstrap::setIniFilename(gBootstrap);
258 LanguageTag aLangTag(gLangTag);
259 std::locale aLocale = Translate::Create("sfx", aLangTag);
260 OUString sMessage = Translate::get(STR_ERROR_SEND_MAIL_CODE, aLocale);
261 OUString sErrorId;
262 switch (nMAPIResult)
264 case MAPI_E_FAILURE:
265 sErrorId = "MAPI_E_FAILURE";
266 break;
267 case MAPI_E_LOGON_FAILURE:
268 sErrorId = "MAPI_E_LOGON_FAILURE";
269 break;
270 case MAPI_E_DISK_FULL:
271 sErrorId = "MAPI_E_DISK_FULL";
272 break;
273 case MAPI_E_INSUFFICIENT_MEMORY:
274 sErrorId = "MAPI_E_INSUFFICIENT_MEMORY";
275 break;
276 case MAPI_E_ACCESS_DENIED:
277 sErrorId = "MAPI_E_ACCESS_DENIED";
278 break;
279 case MAPI_E_TOO_MANY_SESSIONS:
280 sErrorId = "MAPI_E_ACCESS_DENIED";
281 break;
282 case MAPI_E_TOO_MANY_FILES:
283 sErrorId = "MAPI_E_TOO_MANY_FILES";
284 break;
285 case MAPI_E_TOO_MANY_RECIPIENTS:
286 sErrorId = "MAPI_E_TOO_MANY_RECIPIENTS";
287 break;
288 case MAPI_E_ATTACHMENT_NOT_FOUND:
289 sErrorId = "MAPI_E_ATTACHMENT_NOT_FOUND";
290 break;
291 case MAPI_E_ATTACHMENT_OPEN_FAILURE:
292 sErrorId = "MAPI_E_ATTACHMENT_OPEN_FAILURE";
293 break;
294 case MAPI_E_ATTACHMENT_WRITE_FAILURE:
295 sErrorId = "MAPI_E_ATTACHMENT_WRITE_FAILURE";
296 break;
297 case MAPI_E_UNKNOWN_RECIPIENT:
298 sErrorId = "MAPI_E_UNKNOWN_RECIPIENT";
299 break;
300 case MAPI_E_BAD_RECIPTYPE:
301 sErrorId = "MAPI_E_BAD_RECIPTYPE";
302 break;
303 case MAPI_E_NO_MESSAGES:
304 sErrorId = "MAPI_E_NO_MESSAGES";
305 break;
306 case MAPI_E_INVALID_MESSAGE:
307 sErrorId = "MAPI_E_INVALID_MESSAGE";
308 break;
309 case MAPI_E_TEXT_TOO_LARGE:
310 sErrorId = "MAPI_E_TEXT_TOO_LARGE";
311 break;
312 case MAPI_E_INVALID_SESSION:
313 sErrorId = "MAPI_E_INVALID_SESSION";
314 break;
315 case MAPI_E_TYPE_NOT_SUPPORTED:
316 sErrorId = "MAPI_E_TYPE_NOT_SUPPORTED";
317 break;
318 case MAPI_E_AMBIGUOUS_RECIPIENT:
319 sErrorId = "MAPI_E_AMBIGUOUS_RECIPIENT";
320 break;
321 case MAPI_E_MESSAGE_IN_USE:
322 sErrorId = "MAPI_E_MESSAGE_IN_USE";
323 break;
324 case MAPI_E_NETWORK_FAILURE:
325 sErrorId = "MAPI_E_NETWORK_FAILURE";
326 break;
327 case MAPI_E_INVALID_EDITFIELDS:
328 sErrorId = "MAPI_E_INVALID_EDITFIELDS";
329 break;
330 case MAPI_E_INVALID_RECIPS:
331 sErrorId = "MAPI_E_INVALID_RECIPS";
332 break;
333 case MAPI_E_NOT_SUPPORTED:
334 sErrorId = "MAPI_E_NOT_SUPPORTED";
335 break;
336 case MAPI_E_UNICODE_NOT_SUPPORTED:
337 sErrorId = "MAPI_E_UNICODE_NOT_SUPPORTED";
338 break;
339 default:
340 sErrorId = OUString::number(nMAPIResult);
342 sMessage = sMessage.replaceAll("$1", sErrorId);
343 OUString sTitle(Translate::get(STR_ERROR_SEND_MAIL_HEADER, aLocale));
345 MessageBoxW(nullptr, o3tl::toW(sMessage.getStr()), o3tl::toW(sTitle.getStr()),
346 MB_OK | MB_ICONINFORMATION);
350 Main.
351 NOTE: Because this is program only serves implementation
352 purposes and should not be used by any end user the
353 parameter checking is very limited. Every unknown parameter
354 will be ignored.
356 int wmain(int argc, wchar_t* argv[])
359 initParameter(argc, argv);
361 #if OSL_DEBUG_LEVEL > 0
362 dumpParameter();
363 #endif
365 ULONG ulRet = MAPI_E_FAILURE;
369 LHANDLE const hSession = 0;
371 MapiRecipDescW mapiOriginator;
372 MapiRecipientList_t mapiRecipientList;
373 MapiAttachmentList_t mapiAttachmentList;
374 MapiMessageW mapiMsg;
376 initMapiOriginator(&mapiOriginator);
377 initRecipientList(&mapiRecipientList);
378 initAttachmentList(&mapiAttachmentList);
379 initMapiMessage((gFrom.length() ? &mapiOriginator : nullptr), mapiRecipientList, mapiAttachmentList, &mapiMsg);
381 ulRet = MAPISendMailHelper(hSession, 0, &mapiMsg, gMapiFlags, 0);
383 // There is no point in treating an aborted mail sending
384 // dialog as an error to be returned as our exit
385 // status. If the user decided to abort sending a document
386 // as mail, OK, that is not an error.
388 // Also, it seems that GroupWise makes MAPISendMail()
389 // return MAPI_E_USER_ABORT even if the mail sending
390 // dialog was not aborted by the user, and the mail was
391 // actually sent just fine. See bnc#660241 (visible to
392 // Novell people only, sorry).
394 if (ulRet == MAPI_E_USER_ABORT)
395 ulRet = SUCCESS_SUCCESS;
398 catch (const std::runtime_error& ex)
400 OSL_FAIL(ex.what());
403 // Now cleanup the temporary attachment files
404 for (const auto& rAttachment : gAttachments)
405 DeleteFileW(rAttachment.first.c_str());
407 // Only show the error message if UI was requested
408 if ((ulRet != SUCCESS_SUCCESS) && (gMapiFlags & (MAPI_DIALOG | MAPI_LOGON_UI)))
409 ShowError(ulRet);
411 return ulRet;
414 #if OSL_DEBUG_LEVEL > 0
415 void dumpParameter()
417 std::wostringstream oss;
419 if (gFrom.length() > 0)
420 oss << "--from " << gFrom << std::endl;
422 if (gSubject.length() > 0)
423 oss << "--subject " << gSubject << std::endl;
425 if (gBody.length() > 0)
426 oss << "--body " << gBody << std::endl;
428 for (const auto& address : gTo)
429 oss << "--to " << address << std::endl;
431 for (const auto& address : gCc)
432 oss << "--cc " << address << std::endl;
434 for (const auto& address : gBcc)
435 oss << "--bcc " << address << std::endl;
437 for (const auto& attachment : gAttachments)
439 oss << "--attach " << attachment.first << std::endl;
440 if (!attachment.second.empty())
441 oss << "--attach-name " << attachment.second << std::endl;
444 if (gMapiFlags & MAPI_DIALOG)
445 oss << "--mapi-dialog" << std::endl;
447 if (gMapiFlags & MAPI_LOGON_UI)
448 oss << "--mapi-logon-ui" << std::endl;
450 if (!gLangTag.isEmpty())
451 oss << "--langtag " << gLangTag << std::endl;
453 if (!gBootstrap.isEmpty())
454 oss << "--bootstrap " << gBootstrap << std::endl;
456 MessageBoxW(nullptr, oss.str().c_str(), L"Arguments", MB_OK | MB_ICONINFORMATION);
458 #endif
460 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */