summaryrefslogtreecommitdiff
path: root/shell/source/win32/simplemail/smplmailclient.cxx
diff options
context:
space:
mode:
authorMike Kaganski <mike.kaganski@collabora.com>2018-02-27 23:40:15 +0300
committerMike Kaganski <mike.kaganski@collabora.com>2018-03-07 04:30:15 +0100
commit2f061dad7f875f704e3744fc5780c1d145b22e9f (patch)
treedb50657571c682aaa520a87d522fce158c567ea3 /shell/source/win32/simplemail/smplmailclient.cxx
parentbec51fdb04bf25b9d670982dccab728c55c83fdf (diff)
tdf#116074: Don't block on sending email interactively
When sending e-mail using a MAPI mail client that doesn't recognize MAPI_DIALOG_MODELESS flag, and doesn't return from MAPISendMail until message compose dialog is closed (like MS Outlook 2010 and older), waiting for the senddoc process blocks UI, which is unexpected and prevents users from copying stuff from documents to the mail body. Waiting for senddoc process completion is used for two things: 1. To serialize sending multiple mails (e.g., using mailmerge); 2. To show error in case when it failed. This patch allows to avoid blocking the UI in case when compose UI is requested - i.e., user interaction with the mail client is expected, and serialization is not required. In this case, the senddoc process will show the error message itself -> no need for main application to wait for its return. The error message now includes actual error code. To avoid cases when closing main program would remove temporary attachment files before they were used by mail client, they are copied to base temporary directory (instead of default session temporary directory that gets deleted upon program shutdown). senddoc cleans up its temporaries itself. The temporary attachment files are copied to files with ASCII-only filenames, and their original filenames are passed to mail clients using MAPI. This allows to avoid cases when the filenames contain characters outside of current Windows codepage, and the mail client does not support Unicode MAPI, thus receiving wrong filename and erroring out from the send. Change-Id: I4a517bd7a797e76e4c0b7ea48bb1a7b652741a81 Reviewed-on: https://gerrit.libreoffice.org/50826 Tested-by: Jenkins <ci@libreoffice.org> Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
Diffstat (limited to 'shell/source/win32/simplemail/smplmailclient.cxx')
-rw-r--r--shell/source/win32/simplemail/smplmailclient.cxx120
1 files changed, 114 insertions, 6 deletions
diff --git a/shell/source/win32/simplemail/smplmailclient.cxx b/shell/source/win32/simplemail/smplmailclient.cxx
index f83932902e7b..3a659548bbd1 100644
--- a/shell/source/win32/simplemail/smplmailclient.cxx
+++ b/shell/source/win32/simplemail/smplmailclient.cxx
@@ -28,10 +28,17 @@
#include <com/sun/star/system/XSimpleMailMessage2.hpp>
#include <osl/file.hxx>
#include <o3tl/char16_t2wchar_t.hxx>
+#include <o3tl/make_unique.hxx>
+#include <tools/urlobj.hxx>
+#include <unotools/pathoptions.hxx>
+#include <unotools/syslocale.hxx>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <mapi.h>
+#if defined GetTempPath
+#undef GetTempPath
+#endif
#include <process.h>
#include <vector>
@@ -56,8 +63,11 @@ const OUString FROM("--from");
const OUString SUBJECT("--subject");
const OUString BODY("--body");
const OUString ATTACH("--attach");
+const OUString ATTACH_NAME("--attach-name");
const OUString FLAG_MAPI_DIALOG("--mapi-dialog");
const OUString FLAG_MAPI_LOGON_UI("--mapi-logon-ui");
+const OUString FLAG_LANGTAG("--langtag");
+const OUString FLAG_BOOTSTRAP("--bootstrap");
namespace /* private */
{
@@ -111,12 +121,14 @@ namespace /* private */
@returns
<TRUE/> on success.
*/
- bool executeSenddoc(const std::vector<OUString>& rCommandArgs)
+ bool executeSenddoc(const std::vector<OUString>& rCommandArgs, bool bWait)
{
OUString senddocUrl = getSenddocUrl();
if (senddocUrl.getLength() == 0)
return false;
+ oslProcessOption nProcOption = osl_Process_DETACHED | (bWait ? osl_Process_WAIT : 0);
+
oslProcess proc;
/* for efficiency reasons we are using a 'bad' cast here
@@ -126,7 +138,7 @@ namespace /* private */
senddocUrl.pData,
const_cast<rtl_uString**>(reinterpret_cast<rtl_uString * const *>(&rCommandArgs[0])),
rCommandArgs.size(),
- osl_Process_WAIT | osl_Process_DETACHED,
+ nProcOption,
nullptr,
nullptr,
nullptr,
@@ -136,6 +148,9 @@ namespace /* private */
if (err != osl_Process_E_None)
return false;
+ if (!bWait)
+ return true;
+
oslProcessInfo procInfo;
procInfo.Size = sizeof(oslProcessInfo);
osl_getProcessInfo(proc, osl_Process_EXITCODE, &procInfo);
@@ -149,6 +164,76 @@ Reference<XSimpleMailMessage> SAL_CALL CSmplMailClient::createSimpleMailMessage(
return Reference<XSimpleMailMessage>(new CSmplMailMsg());
}
+namespace {
+// We cannot use the session-local temporary directory for the attachment,
+// because it will get removed upon program exit; and it must be alive for
+// senddoc process lifetime. So we use base temppath for the attachments,
+// and let the senddoc to do the cleanup if it was started successfully.
+// This function works like Desktop::CreateTemporaryDirectory()
+OUString&& InitBaseTempDirURL()
+{
+ // No need to intercept an exception here, since
+ // Desktop::CreateTemporaryDirectory() has ensured that path manager is available
+ SvtPathOptions aOpt;
+ OUString aRetURL = aOpt.GetTempPath();
+ if (aRetURL.isEmpty())
+ {
+ osl::File::getTempDirURL(aRetURL);
+ }
+ if (aRetURL.endsWith("/"))
+ aRetURL = aRetURL.copy(0, aRetURL.getLength() - 1);
+
+ return std::move(aRetURL);
+}
+
+const OUString& GetBaseTempDirURL()
+{
+ static const OUString aRetURL(InitBaseTempDirURL());
+ return aRetURL;
+}
+}
+
+OUString CSmplMailClient::CopyAttachment(const OUString& sOrigAttachURL, OUString& sUserVisibleName)
+{
+ // We do two things here:
+ // 1. Make the attachment temporary filename to not contain any fancy characters possible in
+ // original filename, that could confuse mailer, and extract the original filename to explicitly
+ // define it;
+ // 2. Allow the copied files be outside of the session's temporary directory, and thus not be
+ // removed in Desktop::RemoveTemporaryDirectory() if soffice process gets closed before the
+ // mailer finishes using them.
+
+ maAttachmentFiles.emplace_back(o3tl::make_unique<utl::TempFile>(&GetBaseTempDirURL()));
+ maAttachmentFiles.back()->EnableKillingFile();
+ INetURLObject aFilePathObj(maAttachmentFiles.back()->GetURL());
+ OUString sNewAttachmentURL = aFilePathObj.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+ if (osl::File::copy(sOrigAttachURL, sNewAttachmentURL) == osl::FileBase::RC::E_None)
+ {
+ INetURLObject url(sOrigAttachURL, INetURLObject::EncodeMechanism::WasEncoded);
+ sUserVisibleName = url.getName(INetURLObject::LAST_SEGMENT, true,
+ INetURLObject::DecodeMechanism::WithCharset);
+ }
+ else
+ {
+ // Failed to copy original; the best effort is to use original file. It is possible that
+ // the file gets deleted before used in spawned process; but let's hope... the worst thing
+ // is the absent attachment file anyway.
+ sNewAttachmentURL = sOrigAttachURL;
+ maAttachmentFiles.pop_back();
+ }
+ return sNewAttachmentURL;
+}
+
+void CSmplMailClient::ReleaseAttachments()
+{
+ for (auto& pTempFile : maAttachmentFiles)
+ {
+ if (pTempFile)
+ pTempFile->EnableKillingFile(false);
+ }
+ maAttachmentFiles.clear();
+}
+
/**
Assemble a command line for SendDoc.exe out of the members
of the supplied SimpleMailMessage.
@@ -218,11 +303,12 @@ void CSmplMailClient::assembleCommandLine(
rCommandArgs.push_back(subject);
}
- Sequence<OUString> attachments = xSimpleMailMessage->getAttachement();
- for (int i = 0; i < attachments.getLength(); i++)
+ for (const auto& attachment : xSimpleMailMessage->getAttachement())
{
+ OUString sDisplayName;
+ OUString sTempFileURL(CopyAttachment(attachment, sDisplayName));
OUString sysPath;
- osl::FileBase::RC err = osl::FileBase::getSystemPathFromFileURL(attachments[i], sysPath);
+ osl::FileBase::RC err = osl::FileBase::getSystemPathFromFileURL(sTempFileURL, sysPath);
if (err != osl::FileBase::E_None)
throw IllegalArgumentException(
"Invalid attachment file URL",
@@ -231,6 +317,11 @@ void CSmplMailClient::assembleCommandLine(
rCommandArgs.push_back(ATTACH);
rCommandArgs.push_back(sysPath);
+ if (!sDisplayName.isEmpty())
+ {
+ rCommandArgs.push_back(ATTACH_NAME);
+ rCommandArgs.push_back(sDisplayName);
+ }
}
if (!(aFlag & NO_USER_INTERFACE))
@@ -238,6 +329,19 @@ void CSmplMailClient::assembleCommandLine(
if (!(aFlag & NO_LOGON_DIALOG))
rCommandArgs.push_back(FLAG_MAPI_LOGON_UI);
+
+ rCommandArgs.push_back(FLAG_LANGTAG);
+ rCommandArgs.push_back(SvtSysLocale().GetUILanguageTag().getBcp47());
+
+ rtl::Bootstrap aBootstrap;
+ OUString sBootstrapPath;
+ aBootstrap.getIniName(sBootstrapPath);
+ if (!sBootstrapPath.isEmpty())
+ {
+ rCommandArgs.push_back(FLAG_BOOTSTRAP);
+ rCommandArgs.push_back(sBootstrapPath);
+ }
+
}
void SAL_CALL CSmplMailClient::sendSimpleMailMessage(
@@ -248,10 +352,14 @@ void SAL_CALL CSmplMailClient::sendSimpleMailMessage(
std::vector<OUString> senddocParams;
assembleCommandLine(xSimpleMailMessage, aFlag, senddocParams);
- if (!executeSenddoc(senddocParams))
+ const bool bWait = aFlag & NO_USER_INTERFACE;
+ if (!executeSenddoc(senddocParams, bWait))
throw Exception(
"Send email failed",
static_cast<XSimpleMailClient*>(this));
+ // Let the launched senddoc to cleanup the attachments temporary files
+ if (!bWait)
+ ReleaseAttachments();
}
void CSmplMailClient::validateParameter(