LibreOffice Module shell (master) 1
senddoc.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <osl/diagnose.h>
21#include <sal/macros.h>
22
24#include <rtl/bootstrap.hxx>
25#include <sfx2/strings.hrc>
26#include <unotools/resmgr.hxx>
28
29#include <wchar.h>
30#define WIN32_LEAN_AND_MEAN
31#include <windows.h>
32#include <mapi.h>
33#include <MapiUnicodeHelp.h>
34
35#include <string>
36#include <vector>
37#if OSL_DEBUG_LEVEL > 0
38#include <sstream>
39#endif
40#include <stdexcept>
41
42#if OSL_DEBUG_LEVEL > 0
43 static void dumpParameter();
44#endif
45
46typedef std::vector<MapiRecipDescW> MapiRecipientList_t;
47typedef std::vector<MapiFileDescW> MapiAttachmentList_t;
48
49const int LEN_SMTP_PREFIX = 5; // "SMTP:"
50
51namespace /* private */
52{
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, displayed name, and "do not delete" flag
62 std::vector<std::tuple<std::wstring, std::wstring, bool>> gAttachments;
63 int gMapiFlags = 0;
64}
65
79static std::wstring prefixEmailAddress(
80 const std::wstring& aEmailAddress,
81 const std::wstring& aPrefix = L"SMTP:")
82{
83 return (aPrefix + aEmailAddress);
84}
85
87static void addRecipient(
88 ULONG recipClass,
89 const std::wstring& recipAddress,
90 MapiRecipientList_t* pMapiRecipientList)
91{
92 MapiRecipDescW mrd;
93 ZeroMemory(&mrd, sizeof(mrd));
94
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);
99}
100
102static void initRecipientList(MapiRecipientList_t* pMapiRecipientList)
103{
104 OSL_ASSERT(pMapiRecipientList->empty());
105
106 // add to recipients
107 for (const auto& address : gTo)
108 addRecipient(MAPI_TO, address, pMapiRecipientList);
109
110 // add cc recipients
111 for (const auto& address : gCc)
112 addRecipient(MAPI_CC, address, pMapiRecipientList);
113
114 // add bcc recipients
115 for (const auto& address : gBcc)
116 addRecipient(MAPI_BCC, address, pMapiRecipientList);
117}
118
120static void initAttachmentList(MapiAttachmentList_t* pMapiAttachmentList)
121{
122 OSL_ASSERT(pMapiAttachmentList->empty());
123
124 for (const auto& [filepath, attachname, nodelete] : gAttachments)
125 {
126 (void)nodelete;
127 MapiFileDescW mfd;
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
137 // passed
138 mfd.lpszFileName = const_cast<wchar_t*>(attachname.c_str());
139 mfd.nPosition = sal::static_int_cast<ULONG>(-1);
140 pMapiAttachmentList->push_back(mfd);
141 }
142}
143
145static void initMapiOriginator(MapiRecipDescW* pMapiOriginator)
146{
147 ZeroMemory(pMapiOriginator, sizeof(*pMapiOriginator));
148
149 pMapiOriginator->ulRecipClass = MAPI_ORIG;
150 pMapiOriginator->lpszName = const_cast<wchar_t*>(L"");
151 pMapiOriginator->lpszAddress = const_cast<wchar_t*>(gFrom.c_str());
152}
153
155static void initMapiMessage(
156 MapiRecipDescW* aMapiOriginator,
157 MapiRecipientList_t& aMapiRecipientList,
158 MapiAttachmentList_t& aMapiAttachmentList,
159 MapiMessageW* pMapiMessage)
160{
161 ZeroMemory(pMapiMessage, sizeof(*pMapiMessage));
162
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();
171}
172
173const wchar_t* const KnownParameters[] =
174{
175 L"--to",
176 L"--cc",
177 L"--bcc",
178 L"--from",
179 L"--subject",
180 L"--body",
181 L"--attach",
182 L"--mapi-dialog",
183 L"--mapi-logon-ui",
184 L"--langtag",
185 L"--bootstrap",
186};
187
189static bool isKnownParameter(const wchar_t* aParameterName)
190{
191 for (const wchar_t* KnownParameter : KnownParameters)
192 if (_wcsicmp(aParameterName, KnownParameter) == 0)
193 return true;
194
195 return false;
196}
197
199static void initParameter(int argc, wchar_t* argv[])
200{
201 for (int i = 1; i < argc; i++)
202 {
203 if (!isKnownParameter(argv[i]))
204 {
205 OSL_FAIL("Wrong parameter received");
206 continue;
207 }
208
209 if (_wcsicmp(argv[i], L"--mapi-dialog") == 0)
210 {
211 // MAPI_DIALOG_MODELESS has many problems and crashes Outlook 2016.
212 // see the commit message for a lengthy description.
213 gMapiFlags |= MAPI_DIALOG;
214 }
215 else if (_wcsicmp(argv[i], L"--mapi-logon-ui") == 0)
216 {
217 gMapiFlags |= MAPI_LOGON_UI;
218 }
219 else if ((i+1) < argc) // is the value of a parameter available too?
220 {
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)
232 gBody = argv[i+1];
233 else if (_wcsicmp(argv[i], L"--attach") == 0)
234 {
235 std::wstring sPath(argv[i + 1]);
236 // An attachment may optionally be immediately followed by --attach-name and user-visible name
237 std::wstring sName;
238 if ((i + 3) < argc && _wcsicmp(argv[i+2], L"--attach-name") == 0)
239 {
240 sName = argv[i+3];
241 i += 2;
242 }
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)
246 {
247 nodelete = true;
248 ++i;
249 }
250 gAttachments.emplace_back(sPath, sName, nodelete);
251 }
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]);
256
257 i++;
258 }
259 }
260}
261
262static void ShowError(ULONG nMAPIResult)
263{
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);
269 OUString sErrorId;
270 switch (nMAPIResult)
271 {
272 case MAPI_E_FAILURE:
273 sErrorId = "MAPI_E_FAILURE";
274 break;
275 case MAPI_E_LOGON_FAILURE:
276 sErrorId = "MAPI_E_LOGON_FAILURE";
277 break;
278 case MAPI_E_DISK_FULL:
279 sErrorId = "MAPI_E_DISK_FULL";
280 break;
281 case MAPI_E_INSUFFICIENT_MEMORY:
282 sErrorId = "MAPI_E_INSUFFICIENT_MEMORY";
283 break;
284 case MAPI_E_ACCESS_DENIED:
285 sErrorId = "MAPI_E_ACCESS_DENIED";
286 break;
287 case MAPI_E_TOO_MANY_SESSIONS:
288 sErrorId = "MAPI_E_ACCESS_DENIED";
289 break;
290 case MAPI_E_TOO_MANY_FILES:
291 sErrorId = "MAPI_E_TOO_MANY_FILES";
292 break;
293 case MAPI_E_TOO_MANY_RECIPIENTS:
294 sErrorId = "MAPI_E_TOO_MANY_RECIPIENTS";
295 break;
296 case MAPI_E_ATTACHMENT_NOT_FOUND:
297 sErrorId = "MAPI_E_ATTACHMENT_NOT_FOUND";
298 break;
299 case MAPI_E_ATTACHMENT_OPEN_FAILURE:
300 sErrorId = "MAPI_E_ATTACHMENT_OPEN_FAILURE";
301 break;
302 case MAPI_E_ATTACHMENT_WRITE_FAILURE:
303 sErrorId = "MAPI_E_ATTACHMENT_WRITE_FAILURE";
304 break;
305 case MAPI_E_UNKNOWN_RECIPIENT:
306 sErrorId = "MAPI_E_UNKNOWN_RECIPIENT";
307 break;
308 case MAPI_E_BAD_RECIPTYPE:
309 sErrorId = "MAPI_E_BAD_RECIPTYPE";
310 break;
311 case MAPI_E_NO_MESSAGES:
312 sErrorId = "MAPI_E_NO_MESSAGES";
313 break;
314 case MAPI_E_INVALID_MESSAGE:
315 sErrorId = "MAPI_E_INVALID_MESSAGE";
316 break;
317 case MAPI_E_TEXT_TOO_LARGE:
318 sErrorId = "MAPI_E_TEXT_TOO_LARGE";
319 break;
320 case MAPI_E_INVALID_SESSION:
321 sErrorId = "MAPI_E_INVALID_SESSION";
322 break;
323 case MAPI_E_TYPE_NOT_SUPPORTED:
324 sErrorId = "MAPI_E_TYPE_NOT_SUPPORTED";
325 break;
326 case MAPI_E_AMBIGUOUS_RECIPIENT:
327 sErrorId = "MAPI_E_AMBIGUOUS_RECIPIENT";
328 break;
329 case MAPI_E_MESSAGE_IN_USE:
330 sErrorId = "MAPI_E_MESSAGE_IN_USE";
331 break;
332 case MAPI_E_NETWORK_FAILURE:
333 sErrorId = "MAPI_E_NETWORK_FAILURE";
334 break;
335 case MAPI_E_INVALID_EDITFIELDS:
336 sErrorId = "MAPI_E_INVALID_EDITFIELDS";
337 break;
338 case MAPI_E_INVALID_RECIPS:
339 sErrorId = "MAPI_E_INVALID_RECIPS";
340 break;
341 case MAPI_E_NOT_SUPPORTED:
342 sErrorId = "MAPI_E_NOT_SUPPORTED";
343 break;
344 case MAPI_E_UNICODE_NOT_SUPPORTED:
345 sErrorId = "MAPI_E_UNICODE_NOT_SUPPORTED";
346 break;
347 default:
348 sErrorId = OUString::number(nMAPIResult);
349 }
350 sMessage = sMessage.replaceAll("$1", sErrorId);
351 OUString sTitle(Translate::get(STR_ERROR_SEND_MAIL_HEADER, aLocale));
352
353 MessageBoxW(nullptr, o3tl::toW(sMessage.getStr()), o3tl::toW(sTitle.getStr()),
354 MB_OK | MB_ICONINFORMATION);
355}
356
364int wmain(int argc, wchar_t* argv[])
365{
366
367 initParameter(argc, argv);
368
369#if OSL_DEBUG_LEVEL > 0
371#endif
372
373 ULONG ulRet = MAPI_E_FAILURE;
374
375 try
376 {
377 LHANDLE const hSession = 0;
378
379 MapiRecipDescW mapiOriginator;
380 MapiRecipientList_t mapiRecipientList;
381 MapiAttachmentList_t mapiAttachmentList;
382 MapiMessageW mapiMsg;
383
384 initMapiOriginator(&mapiOriginator);
385 initRecipientList(&mapiRecipientList);
386 initAttachmentList(&mapiAttachmentList);
387 initMapiMessage((gFrom.length() ? &mapiOriginator : nullptr), mapiRecipientList, mapiAttachmentList, &mapiMsg);
388
389 ulRet = MAPISendMailHelper(hSession, 0, &mapiMsg, gMapiFlags, 0);
390
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.
395
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).
401
402 if (ulRet == MAPI_E_USER_ABORT)
403 ulRet = SUCCESS_SUCCESS;
404
405 }
406 catch (const std::runtime_error& ex)
407 {
408 OSL_FAIL(ex.what());
409 }
410
411 // Now cleanup the temporary attachment files
412 for (const auto& [filepath, attachname, nodelete] : gAttachments)
413 {
414 (void)attachname;
415 if (!nodelete)
416 DeleteFileW(filepath.c_str());
417 }
418
419 // Only show the error message if UI was requested
420 if ((ulRet != SUCCESS_SUCCESS) && (gMapiFlags & (MAPI_DIALOG | MAPI_LOGON_UI)))
421 ShowError(ulRet);
422
423 return ulRet;
424}
425
426#if OSL_DEBUG_LEVEL > 0
428 {
429 std::wostringstream oss;
430
431 if (gFrom.length() > 0)
432 oss << "--from " << gFrom << std::endl;
433
434 if (gSubject.length() > 0)
435 oss << "--subject " << gSubject << std::endl;
436
437 if (gBody.length() > 0)
438 oss << "--body " << gBody << std::endl;
439
440 for (const auto& address : gTo)
441 oss << "--to " << address << std::endl;
442
443 for (const auto& address : gCc)
444 oss << "--cc " << address << std::endl;
445
446 for (const auto& address : gBcc)
447 oss << "--bcc " << address << std::endl;
448
449 for (const auto& [filepath, attachname, nodelete] : gAttachments)
450 {
451 oss << "--attach " << filepath << std::endl;
452 if (!attachname.empty())
453 oss << "--attach-name " << attachname << std::endl;
454 if (nodelete)
455 oss << "--nodelete" << std::endl;
456 }
457
458 if (gMapiFlags & MAPI_DIALOG)
459 oss << "--mapi-dialog" << std::endl;
460
461 if (gMapiFlags & MAPI_LOGON_UI)
462 oss << "--mapi-logon-ui" << std::endl;
463
464 if (!gLangTag.isEmpty())
465 oss << "--langtag " << gLangTag << std::endl;
466
467 if (!gBootstrap.isEmpty())
468 oss << "--bootstrap " << gBootstrap << std::endl;
469
470 MessageBoxW(nullptr, oss.str().c_str(), L"Arguments", MB_OK | MB_ICONINFORMATION);
471 }
472#endif
473
474/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
OUString sName
std::locale Create(std::string_view aPrefixName, const LanguageTag &rLocale)
OUString get(TranslateId sContextAndId, const std::locale &loc)
int i
static void ShowError(ULONG nMAPIResult)
Definition: senddoc.cxx:262
static bool isKnownParameter(const wchar_t *aParameterName)
Definition: senddoc.cxx:189
std::vector< MapiFileDescW > MapiAttachmentList_t
Definition: senddoc.cxx:47
static void addRecipient(ULONG recipClass, const std::wstring &recipAddress, MapiRecipientList_t *pMapiRecipientList)
Definition: senddoc.cxx:87
static std::wstring prefixEmailAddress(const std::wstring &aEmailAddress, const std::wstring &aPrefix=L"SMTP:")
Add a prefix to an email address.
Definition: senddoc.cxx:79
static void initRecipientList(MapiRecipientList_t *pMapiRecipientList)
Definition: senddoc.cxx:102
static void initMapiOriginator(MapiRecipDescW *pMapiOriginator)
Definition: senddoc.cxx:145
static void initMapiMessage(MapiRecipDescW *aMapiOriginator, MapiRecipientList_t &aMapiRecipientList, MapiAttachmentList_t &aMapiAttachmentList, MapiMessageW *pMapiMessage)
Definition: senddoc.cxx:155
std::vector< MapiRecipDescW > MapiRecipientList_t
Definition: senddoc.cxx:46
const wchar_t *const KnownParameters[]
Definition: senddoc.cxx:173
static void dumpParameter()
Definition: senddoc.cxx:427
static void initAttachmentList(MapiAttachmentList_t *pMapiAttachmentList)
Definition: senddoc.cxx:120
const int LEN_SMTP_PREFIX
Definition: senddoc.cxx:49
int wmain(int argc, wchar_t *argv[])
Main.
Definition: senddoc.cxx:364
static void initParameter(int argc, wchar_t *argv[])
Definition: senddoc.cxx:199
OUString sMessage