1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
30 #define WIN32_LEAN_AND_MEAN
33 #include <MapiUnicodeHelp.h>
37 #if OSL_DEBUG_LEVEL > 0
42 #if OSL_DEBUG_LEVEL > 0
43 static void dumpParameter();
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 */
56 std::wstring gSubject
;
58 std::vector
<std::wstring
> gTo
;
59 std::vector
<std::wstring
> gCc
;
60 std::vector
<std::wstring
> gBcc
;
61 // Keep temp filepath, displayed name, and "do not delete" flag
62 std::vector
<std::tuple
<std::wstring
, std::wstring
, bool>> gAttachments
;
67 Add a prefix to an email address. MAPI requires that
68 email addresses have an 'SMTP:' prefix.
71 [in] the email address.
74 [in] the prefix to be added to the email address.
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
);
87 static void addRecipient(
89 const std::wstring
& recipAddress
,
90 MapiRecipientList_t
* pMapiRecipientList
)
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
);
102 static void initRecipientList(MapiRecipientList_t
* pMapiRecipientList
)
104 OSL_ASSERT(pMapiRecipientList
->empty());
107 for (const auto& address
: gTo
)
108 addRecipient(MAPI_TO
, address
, pMapiRecipientList
);
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
);
120 static void initAttachmentList(MapiAttachmentList_t
* pMapiAttachmentList
)
122 OSL_ASSERT(pMapiAttachmentList
->empty());
124 for (const auto& [filepath
, attachname
, nodelete
] : gAttachments
)
128 ZeroMemory(&mfd
, sizeof(mfd
));
129 mfd
.lpszPathName
= const_cast<wchar_t*>(filepath
.c_str());
130 // MapiFileDesc documentation (https://msdn.microsoft.com/en-us/library/hh707272)
131 // allows using here either nullptr, or a pointer to empty string. However,
132 // for Outlook 2013, we cannot use nullptr here, and must point to a (possibly
133 // empty) string: otherwise using MAPI_DIALOG_MODELESS results in MAPI_E_FAILURE.
134 // See http://peach.ease.lsoft.com/scripts/wa-PEACH.exe?A2=MAPI-L;d2bf3060.1604
135 // Since C++11, c_str() must return a pointer to single null character when the
136 // string is empty, so we are OK here in case when there's no explicit file name
138 mfd
.lpszFileName
= const_cast<wchar_t*>(attachname
.c_str());
139 mfd
.nPosition
= sal::static_int_cast
<ULONG
>(-1);
140 pMapiAttachmentList
->push_back(mfd
);
145 static void initMapiOriginator(MapiRecipDescW
* pMapiOriginator
)
147 ZeroMemory(pMapiOriginator
, sizeof(*pMapiOriginator
));
149 pMapiOriginator
->ulRecipClass
= MAPI_ORIG
;
150 pMapiOriginator
->lpszName
= const_cast<wchar_t*>(L
"");
151 pMapiOriginator
->lpszAddress
= const_cast<wchar_t*>(gFrom
.c_str());
155 static void initMapiMessage(
156 MapiRecipDescW
* aMapiOriginator
,
157 MapiRecipientList_t
& aMapiRecipientList
,
158 MapiAttachmentList_t
& aMapiAttachmentList
,
159 MapiMessageW
* pMapiMessage
)
161 ZeroMemory(pMapiMessage
, sizeof(*pMapiMessage
));
163 pMapiMessage
->lpszSubject
= const_cast<wchar_t*>(gSubject
.c_str());
164 pMapiMessage
->lpszNoteText
= (gBody
.length() ? const_cast<wchar_t*>(gBody
.c_str()) : nullptr);
165 pMapiMessage
->lpOriginator
= aMapiOriginator
;
166 pMapiMessage
->lpRecips
= aMapiRecipientList
.size() ? aMapiRecipientList
.data() : nullptr;
167 pMapiMessage
->nRecipCount
= aMapiRecipientList
.size();
168 if (!aMapiAttachmentList
.empty())
169 pMapiMessage
->lpFiles
= aMapiAttachmentList
.data();
170 pMapiMessage
->nFileCount
= aMapiAttachmentList
.size();
173 const wchar_t* const KnownParameters
[] =
189 static bool isKnownParameter(const wchar_t* aParameterName
)
191 for (const wchar_t* KnownParameter
: KnownParameters
)
192 if (_wcsicmp(aParameterName
, KnownParameter
) == 0)
199 static void initParameter(int argc
, wchar_t* argv
[])
201 for (int i
= 1; i
< argc
; i
++)
203 if (!isKnownParameter(argv
[i
]))
205 OSL_FAIL("Wrong parameter received");
209 if (_wcsicmp(argv
[i
], L
"--mapi-dialog") == 0)
211 // MAPI_DIALOG_MODELESS has many problems and crashes Outlook 2016.
212 // see the commit message for a lengthy description.
213 gMapiFlags
|= MAPI_DIALOG
;
215 else if (_wcsicmp(argv
[i
], L
"--mapi-logon-ui") == 0)
217 gMapiFlags
|= MAPI_LOGON_UI
;
219 else if ((i
+1) < argc
) // is the value of a parameter available too?
221 if (_wcsicmp(argv
[i
], L
"--to") == 0)
222 gTo
.push_back(prefixEmailAddress(argv
[i
+1]));
223 else if (_wcsicmp(argv
[i
], L
"--cc") == 0)
224 gCc
.push_back(prefixEmailAddress(argv
[i
+1]));
225 else if (_wcsicmp(argv
[i
], L
"--bcc") == 0)
226 gBcc
.push_back(prefixEmailAddress(argv
[i
+1]));
227 else if (_wcsicmp(argv
[i
], L
"--from") == 0)
228 gFrom
= prefixEmailAddress(argv
[i
+1]);
229 else if (_wcsicmp(argv
[i
], L
"--subject") == 0)
230 gSubject
= argv
[i
+1];
231 else if (_wcsicmp(argv
[i
], L
"--body") == 0)
233 else if (_wcsicmp(argv
[i
], L
"--attach") == 0)
235 std::wstring
sPath(argv
[i
+ 1]);
236 // An attachment may optionally be immediately followed by --attach-name and user-visible name
238 if ((i
+ 3) < argc
&& _wcsicmp(argv
[i
+2], L
"--attach-name") == 0)
243 // Also there may be --nodelete to keep the attachment on exit
244 bool nodelete
= false;
245 if ((i
+ 2) < argc
&& _wcsicmp(argv
[i
+2], L
"--nodelete") == 0)
250 gAttachments
.emplace_back(sPath
, sName
, nodelete
);
252 else if (_wcsicmp(argv
[i
], L
"--langtag") == 0)
253 gLangTag
= o3tl::toU(argv
[i
+1]);
254 else if (_wcsicmp(argv
[i
], L
"--bootstrap") == 0)
255 gBootstrap
= o3tl::toU(argv
[i
+1]);
262 static void ShowError(ULONG nMAPIResult
)
264 if (!gBootstrap
.isEmpty())
265 rtl::Bootstrap::setIniFilename(gBootstrap
);
266 LanguageTag
aLangTag(gLangTag
);
267 std::locale aLocale
= Translate::Create("sfx", aLangTag
);
268 OUString sMessage
= Translate::get(STR_ERROR_SEND_MAIL_CODE
, aLocale
);
273 sErrorId
= "MAPI_E_FAILURE";
275 case MAPI_E_LOGON_FAILURE
:
276 sErrorId
= "MAPI_E_LOGON_FAILURE";
278 case MAPI_E_DISK_FULL
:
279 sErrorId
= "MAPI_E_DISK_FULL";
281 case MAPI_E_INSUFFICIENT_MEMORY
:
282 sErrorId
= "MAPI_E_INSUFFICIENT_MEMORY";
284 case MAPI_E_ACCESS_DENIED
:
285 sErrorId
= "MAPI_E_ACCESS_DENIED";
287 case MAPI_E_TOO_MANY_SESSIONS
:
288 sErrorId
= "MAPI_E_ACCESS_DENIED";
290 case MAPI_E_TOO_MANY_FILES
:
291 sErrorId
= "MAPI_E_TOO_MANY_FILES";
293 case MAPI_E_TOO_MANY_RECIPIENTS
:
294 sErrorId
= "MAPI_E_TOO_MANY_RECIPIENTS";
296 case MAPI_E_ATTACHMENT_NOT_FOUND
:
297 sErrorId
= "MAPI_E_ATTACHMENT_NOT_FOUND";
299 case MAPI_E_ATTACHMENT_OPEN_FAILURE
:
300 sErrorId
= "MAPI_E_ATTACHMENT_OPEN_FAILURE";
302 case MAPI_E_ATTACHMENT_WRITE_FAILURE
:
303 sErrorId
= "MAPI_E_ATTACHMENT_WRITE_FAILURE";
305 case MAPI_E_UNKNOWN_RECIPIENT
:
306 sErrorId
= "MAPI_E_UNKNOWN_RECIPIENT";
308 case MAPI_E_BAD_RECIPTYPE
:
309 sErrorId
= "MAPI_E_BAD_RECIPTYPE";
311 case MAPI_E_NO_MESSAGES
:
312 sErrorId
= "MAPI_E_NO_MESSAGES";
314 case MAPI_E_INVALID_MESSAGE
:
315 sErrorId
= "MAPI_E_INVALID_MESSAGE";
317 case MAPI_E_TEXT_TOO_LARGE
:
318 sErrorId
= "MAPI_E_TEXT_TOO_LARGE";
320 case MAPI_E_INVALID_SESSION
:
321 sErrorId
= "MAPI_E_INVALID_SESSION";
323 case MAPI_E_TYPE_NOT_SUPPORTED
:
324 sErrorId
= "MAPI_E_TYPE_NOT_SUPPORTED";
326 case MAPI_E_AMBIGUOUS_RECIPIENT
:
327 sErrorId
= "MAPI_E_AMBIGUOUS_RECIPIENT";
329 case MAPI_E_MESSAGE_IN_USE
:
330 sErrorId
= "MAPI_E_MESSAGE_IN_USE";
332 case MAPI_E_NETWORK_FAILURE
:
333 sErrorId
= "MAPI_E_NETWORK_FAILURE";
335 case MAPI_E_INVALID_EDITFIELDS
:
336 sErrorId
= "MAPI_E_INVALID_EDITFIELDS";
338 case MAPI_E_INVALID_RECIPS
:
339 sErrorId
= "MAPI_E_INVALID_RECIPS";
341 case MAPI_E_NOT_SUPPORTED
:
342 sErrorId
= "MAPI_E_NOT_SUPPORTED";
344 case MAPI_E_UNICODE_NOT_SUPPORTED
:
345 sErrorId
= "MAPI_E_UNICODE_NOT_SUPPORTED";
348 sErrorId
= OUString::number(nMAPIResult
);
350 sMessage
= sMessage
.replaceAll("$1", sErrorId
);
351 OUString
sTitle(Translate::get(STR_ERROR_SEND_MAIL_HEADER
, aLocale
));
353 MessageBoxW(nullptr, o3tl::toW(sMessage
.getStr()), o3tl::toW(sTitle
.getStr()),
354 MB_OK
| MB_ICONINFORMATION
);
359 NOTE: Because this is program only serves implementation
360 purposes and should not be used by any end user the
361 parameter checking is very limited. Every unknown parameter
364 int wmain(int argc
, wchar_t* argv
[])
367 initParameter(argc
, argv
);
369 #if OSL_DEBUG_LEVEL > 0
373 ULONG ulRet
= MAPI_E_FAILURE
;
377 LHANDLE
const hSession
= 0;
379 MapiRecipDescW mapiOriginator
;
380 MapiRecipientList_t mapiRecipientList
;
381 MapiAttachmentList_t mapiAttachmentList
;
382 MapiMessageW mapiMsg
;
384 initMapiOriginator(&mapiOriginator
);
385 initRecipientList(&mapiRecipientList
);
386 initAttachmentList(&mapiAttachmentList
);
387 initMapiMessage((gFrom
.length() ? &mapiOriginator
: nullptr), mapiRecipientList
, mapiAttachmentList
, &mapiMsg
);
389 ulRet
= MAPISendMailHelper(hSession
, 0, &mapiMsg
, gMapiFlags
, 0);
391 // There is no point in treating an aborted mail sending
392 // dialog as an error to be returned as our exit
393 // status. If the user decided to abort sending a document
394 // as mail, OK, that is not an error.
396 // Also, it seems that GroupWise makes MAPISendMail()
397 // return MAPI_E_USER_ABORT even if the mail sending
398 // dialog was not aborted by the user, and the mail was
399 // actually sent just fine. See bnc#660241 (visible to
400 // Novell people only, sorry).
402 if (ulRet
== MAPI_E_USER_ABORT
)
403 ulRet
= SUCCESS_SUCCESS
;
406 catch (const std::runtime_error
& ex
)
411 // Now cleanup the temporary attachment files
412 for (const auto& [filepath
, attachname
, nodelete
] : gAttachments
)
416 DeleteFileW(filepath
.c_str());
419 // Only show the error message if UI was requested
420 if ((ulRet
!= SUCCESS_SUCCESS
) && (gMapiFlags
& (MAPI_DIALOG
| MAPI_LOGON_UI
)))
426 #if OSL_DEBUG_LEVEL > 0
429 std::wostringstream oss
;
431 if (gFrom
.length() > 0)
432 oss
<< "--from " << gFrom
<< std::endl
;
434 if (gSubject
.length() > 0)
435 oss
<< "--subject " << gSubject
<< std::endl
;
437 if (gBody
.length() > 0)
438 oss
<< "--body " << gBody
<< std::endl
;
440 for (const auto& address
: gTo
)
441 oss
<< "--to " << address
<< std::endl
;
443 for (const auto& address
: gCc
)
444 oss
<< "--cc " << address
<< std::endl
;
446 for (const auto& address
: gBcc
)
447 oss
<< "--bcc " << address
<< std::endl
;
449 for (const auto& [filepath
, attachname
, nodelete
] : gAttachments
)
451 oss
<< "--attach " << filepath
<< std::endl
;
452 if (!attachname
.empty())
453 oss
<< "--attach-name " << attachname
<< std::endl
;
455 oss
<< "--nodelete" << std::endl
;
458 if (gMapiFlags
& MAPI_DIALOG
)
459 oss
<< "--mapi-dialog" << std::endl
;
461 if (gMapiFlags
& MAPI_LOGON_UI
)
462 oss
<< "--mapi-logon-ui" << std::endl
;
464 if (!gLangTag
.isEmpty())
465 oss
<< "--langtag " << gLangTag
<< std::endl
;
467 if (!gBootstrap
.isEmpty())
468 oss
<< "--bootstrap " << gBootstrap
<< std::endl
;
470 MessageBoxW(nullptr, oss
.str().c_str(), L
"Arguments", MB_OK
| MB_ICONINFORMATION
);
474 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */