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 and displayed name
62 std::vector
<std::pair
<std::wstring
, std::wstring
>> 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& attachment
: gAttachments
)
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
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
);
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());
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
[] =
188 static bool isKnownParameter(const wchar_t* aParameterName
)
190 for (const wchar_t* KnownParameter
: KnownParameters
)
191 if (_wcsicmp(aParameterName
, KnownParameter
) == 0)
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");
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)
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
237 if ((i
+ 3) < argc
&& _wcsicmp(argv
[i
+2], L
"--attach-name") == 0)
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]);
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
);
265 sErrorId
= "MAPI_E_FAILURE";
267 case MAPI_E_LOGON_FAILURE
:
268 sErrorId
= "MAPI_E_LOGON_FAILURE";
270 case MAPI_E_DISK_FULL
:
271 sErrorId
= "MAPI_E_DISK_FULL";
273 case MAPI_E_INSUFFICIENT_MEMORY
:
274 sErrorId
= "MAPI_E_INSUFFICIENT_MEMORY";
276 case MAPI_E_ACCESS_DENIED
:
277 sErrorId
= "MAPI_E_ACCESS_DENIED";
279 case MAPI_E_TOO_MANY_SESSIONS
:
280 sErrorId
= "MAPI_E_ACCESS_DENIED";
282 case MAPI_E_TOO_MANY_FILES
:
283 sErrorId
= "MAPI_E_TOO_MANY_FILES";
285 case MAPI_E_TOO_MANY_RECIPIENTS
:
286 sErrorId
= "MAPI_E_TOO_MANY_RECIPIENTS";
288 case MAPI_E_ATTACHMENT_NOT_FOUND
:
289 sErrorId
= "MAPI_E_ATTACHMENT_NOT_FOUND";
291 case MAPI_E_ATTACHMENT_OPEN_FAILURE
:
292 sErrorId
= "MAPI_E_ATTACHMENT_OPEN_FAILURE";
294 case MAPI_E_ATTACHMENT_WRITE_FAILURE
:
295 sErrorId
= "MAPI_E_ATTACHMENT_WRITE_FAILURE";
297 case MAPI_E_UNKNOWN_RECIPIENT
:
298 sErrorId
= "MAPI_E_UNKNOWN_RECIPIENT";
300 case MAPI_E_BAD_RECIPTYPE
:
301 sErrorId
= "MAPI_E_BAD_RECIPTYPE";
303 case MAPI_E_NO_MESSAGES
:
304 sErrorId
= "MAPI_E_NO_MESSAGES";
306 case MAPI_E_INVALID_MESSAGE
:
307 sErrorId
= "MAPI_E_INVALID_MESSAGE";
309 case MAPI_E_TEXT_TOO_LARGE
:
310 sErrorId
= "MAPI_E_TEXT_TOO_LARGE";
312 case MAPI_E_INVALID_SESSION
:
313 sErrorId
= "MAPI_E_INVALID_SESSION";
315 case MAPI_E_TYPE_NOT_SUPPORTED
:
316 sErrorId
= "MAPI_E_TYPE_NOT_SUPPORTED";
318 case MAPI_E_AMBIGUOUS_RECIPIENT
:
319 sErrorId
= "MAPI_E_AMBIGUOUS_RECIPIENT";
321 case MAPI_E_MESSAGE_IN_USE
:
322 sErrorId
= "MAPI_E_MESSAGE_IN_USE";
324 case MAPI_E_NETWORK_FAILURE
:
325 sErrorId
= "MAPI_E_NETWORK_FAILURE";
327 case MAPI_E_INVALID_EDITFIELDS
:
328 sErrorId
= "MAPI_E_INVALID_EDITFIELDS";
330 case MAPI_E_INVALID_RECIPS
:
331 sErrorId
= "MAPI_E_INVALID_RECIPS";
333 case MAPI_E_NOT_SUPPORTED
:
334 sErrorId
= "MAPI_E_NOT_SUPPORTED";
336 case MAPI_E_UNICODE_NOT_SUPPORTED
:
337 sErrorId
= "MAPI_E_UNICODE_NOT_SUPPORTED";
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
);
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
356 int wmain(int argc
, wchar_t* argv
[])
359 initParameter(argc
, argv
);
361 #if OSL_DEBUG_LEVEL > 0
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
)
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
)))
414 #if OSL_DEBUG_LEVEL > 0
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
);
460 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */