calc: on editing invalidation of view with different zoom is wrong
[LibreOffice.git] / shell / source / win32 / simplemail / senddoc.cxx
blobe34412cbfae10f8afe83a452b16cb129c55f57c8
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, displayed name, and "do not delete" flag
62 std::vector<std::tuple<std::wstring, std::wstring, bool>> 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& [filepath, attachname, nodelete] : gAttachments)
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);
144 /** @internal */
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());
154 /** @internal */
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[] =
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",
188 /** @internal */
189 static bool isKnownParameter(const wchar_t* aParameterName)
191 for (const wchar_t* KnownParameter : KnownParameters)
192 if (_wcsicmp(aParameterName, KnownParameter) == 0)
193 return true;
195 return false;
198 /** @internal */
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");
206 continue;
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)
232 gBody = argv[i+1];
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
237 std::wstring sName;
238 if ((i + 3) < argc && _wcsicmp(argv[i+2], L"--attach-name") == 0)
240 sName = argv[i+3];
241 i += 2;
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)
247 nodelete = true;
248 ++i;
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]);
257 i++;
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);
269 OUString sErrorId;
270 switch (nMAPIResult)
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);
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);
358 Main.
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
362 will be ignored.
364 int wmain(int argc, wchar_t* argv[])
367 initParameter(argc, argv);
369 #if OSL_DEBUG_LEVEL > 0
370 dumpParameter();
371 #endif
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)
408 OSL_FAIL(ex.what());
411 // Now cleanup the temporary attachment files
412 for (const auto& [filepath, attachname, nodelete] : gAttachments)
414 (void)attachname;
415 if (!nodelete)
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)))
421 ShowError(ulRet);
423 return ulRet;
426 #if OSL_DEBUG_LEVEL > 0
427 void dumpParameter()
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;
454 if (nodelete)
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);
472 #endif
474 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */