diff options
Diffstat (limited to 'shell')
-rw-r--r-- | shell/Executable_senddoc.mk | 4 | ||||
-rw-r--r-- | shell/Library_smplmail.mk | 3 | ||||
-rw-r--r-- | shell/source/win32/simplemail/senddoc.cxx | 170 | ||||
-rw-r--r-- | shell/source/win32/simplemail/smplmailclient.cxx | 124 | ||||
-rw-r--r-- | shell/source/win32/simplemail/smplmailclient.hxx | 7 |
5 files changed, 283 insertions, 25 deletions
diff --git a/shell/Executable_senddoc.mk b/shell/Executable_senddoc.mk index f51c8441aada..4b8c5e3d18bb 100644 --- a/shell/Executable_senddoc.mk +++ b/shell/Executable_senddoc.mk @@ -13,8 +13,12 @@ $(eval $(call gb_Executable_use_system_win32_libs,senddoc,\ kernel32 \ )) +$(eval $(call gb_Executable_use_sdk_api,senddoc)) + $(eval $(call gb_Executable_use_libraries,senddoc,\ + i18nlangtag \ sal \ + utl \ )) $(eval $(call gb_Executable_add_exception_objects,senddoc,\ diff --git a/shell/Library_smplmail.mk b/shell/Library_smplmail.mk index d68f0a65e8ec..fa8c222943ed 100644 --- a/shell/Library_smplmail.mk +++ b/shell/Library_smplmail.mk @@ -18,7 +18,10 @@ $(eval $(call gb_Library_use_system_win32_libs,smplmail,\ $(eval $(call gb_Library_use_libraries,smplmail,\ cppu \ cppuhelper \ + i18nlangtag \ sal \ + tl \ + utl \ )) $(eval $(call gb_Library_set_componentfile,smplmail,shell/source/win32/simplemail/smplmail)) diff --git a/shell/source/win32/simplemail/senddoc.cxx b/shell/source/win32/simplemail/senddoc.cxx index d972bad5f71e..636a0d862f4e 100644 --- a/shell/source/win32/simplemail/senddoc.cxx +++ b/shell/source/win32/simplemail/senddoc.cxx @@ -20,6 +20,11 @@ #include <osl/diagnose.h> #include <sal/macros.h> +#include <o3tl/char16_t2wchar_t.hxx> +#include <rtl/bootstrap.hxx> +#include <sfx2/strings.hrc> +#include <unotools/resmgr.hxx> + #include <wchar.h> #define WIN32_LEAN_AND_MEAN #include <windows.h> @@ -38,7 +43,6 @@ #endif typedef std::vector<std::wstring> StringList_t; -typedef StringList_t::const_iterator StringListIterator_t; typedef std::vector<MapiRecipDescW> MapiRecipientList_t; typedef std::vector<MapiFileDescW> MapiAttachmentList_t; @@ -46,13 +50,16 @@ const int LEN_SMTP_PREFIX = 5; // "SMTP:" namespace /* private */ { + OUString gLangTag; + OUString gBootstrap; std::wstring gFrom; std::wstring gSubject; std::wstring gBody; StringList_t gTo; StringList_t gCc; StringList_t gBcc; - StringList_t gAttachments; + // Keep temp filepath and displayed name + std::vector<std::pair<std::wstring, std::wstring>> gAttachments; int gMapiFlags = 0; } @@ -118,10 +125,16 @@ void initAttachmentList(MapiAttachmentList_t* pMapiAttachmentList) { MapiFileDescW mfd; ZeroMemory(&mfd, sizeof(mfd)); - mfd.lpszPathName = const_cast<wchar_t*>(attachment.c_str()); - // This is required for Outlook 2013 - otherwise using MAPI_DIALOG_MODELESS results in MAPI_E_FAILURE + mfd.lpszPathName = const_cast<wchar_t*>(attachment.first.c_str()); + // MapiFileDesc documentation (https://msdn.microsoft.com/en-us/library/hh707272) + // allows using here either nullptr, or a pointer to empty string. However, + // for Outlook 2013, we cannot use nullptr here, and must point to a (possibly + // empty) string: otherwise using MAPI_DIALOG_MODELESS results in MAPI_E_FAILURE. // See http://peach.ease.lsoft.com/scripts/wa-PEACH.exe?A2=MAPI-L;d2bf3060.1604 - mfd.lpszFileName = L""; + // Since C++11, c_str() must return a pointer to single null character when the + // string is empty, so we are OK here in case when there's no explicit file name + // passed + mfd.lpszFileName = const_cast<wchar_t*>(attachment.second.c_str()); mfd.nPosition = sal::static_int_cast<ULONG>(-1); pMapiAttachmentList->push_back(mfd); } @@ -151,11 +164,12 @@ void initMapiMessage( pMapiMessage->lpOriginator = aMapiOriginator; pMapiMessage->lpRecips = aMapiRecipientList.size() ? &aMapiRecipientList[0] : nullptr; pMapiMessage->nRecipCount = aMapiRecipientList.size(); - pMapiMessage->lpFiles = &aMapiAttachmentList[0]; + if (!aMapiAttachmentList.empty()) + pMapiMessage->lpFiles = &aMapiAttachmentList[0]; pMapiMessage->nFileCount = aMapiAttachmentList.size(); } -const wchar_t* const KnownParameter[] = +const wchar_t* const KnownParameters[] = { L"--to", L"--cc", @@ -165,16 +179,16 @@ const wchar_t* const KnownParameter[] = L"--body", L"--attach", L"--mapi-dialog", - L"--mapi-logon-ui" + L"--mapi-logon-ui", + L"--langtag", + L"--bootstrap", }; -const size_t nKnownParameter = SAL_N_ELEMENTS(KnownParameter); - /** @internal */ bool isKnownParameter(const wchar_t* aParameterName) { - for (size_t i = 0; i < nKnownParameter; i++) - if (_wcsicmp(aParameterName, KnownParameter[i]) == 0) + for (const wchar_t* KnownParameter : KnownParameters) + if (_wcsicmp(aParameterName, KnownParameter) == 0) return true; return false; @@ -215,13 +229,122 @@ void initParameter(int argc, wchar_t* argv[]) else if (_wcsicmp(argv[i], L"--body") == 0) gBody = argv[i+1]; else if (_wcsicmp(argv[i], L"--attach") == 0) - gAttachments.push_back(argv[i+1]); + { + std::wstring sPath(argv[i + 1]); + // An attachment may optionally be immediately followed by --attach-name and user-visible name + std::wstring sName; + if ((i + 3) < argc && _wcsicmp(argv[i+2], L"--attach-name") == 0) + { + sName = argv[i+3]; + i += 2; + } + gAttachments.emplace_back(sPath, sName); + } + else if (_wcsicmp(argv[i], L"--langtag") == 0) + gLangTag = o3tl::toU(argv[i+1]); + else if (_wcsicmp(argv[i], L"--bootstrap") == 0) + gBootstrap = o3tl::toU(argv[i+1]); i++; } } } +void ShowError(ULONG nMAPIResult) +{ + if (!gBootstrap.isEmpty()) + rtl::Bootstrap::setIniFilename(gBootstrap); + LanguageTag aLangTag(gLangTag); + std::locale aLocale = Translate::Create("sfx", aLangTag); + OUString sMessage = Translate::get(STR_ERROR_SEND_MAIL_CODE, aLocale); + OUString sErrorId; + switch (nMAPIResult) + { + case MAPI_E_FAILURE: + sErrorId = "MAPI_E_FAILURE"; + break; + case MAPI_E_LOGON_FAILURE: + sErrorId = "MAPI_E_LOGON_FAILURE"; + break; + case MAPI_E_DISK_FULL: + sErrorId = "MAPI_E_DISK_FULL"; + break; + case MAPI_E_INSUFFICIENT_MEMORY: + sErrorId = "MAPI_E_INSUFFICIENT_MEMORY"; + break; + case MAPI_E_ACCESS_DENIED: + sErrorId = "MAPI_E_ACCESS_DENIED"; + break; + case MAPI_E_TOO_MANY_SESSIONS: + sErrorId = "MAPI_E_ACCESS_DENIED"; + break; + case MAPI_E_TOO_MANY_FILES: + sErrorId = "MAPI_E_TOO_MANY_FILES"; + break; + case MAPI_E_TOO_MANY_RECIPIENTS: + sErrorId = "MAPI_E_TOO_MANY_RECIPIENTS"; + break; + case MAPI_E_ATTACHMENT_NOT_FOUND: + sErrorId = "MAPI_E_ATTACHMENT_NOT_FOUND"; + break; + case MAPI_E_ATTACHMENT_OPEN_FAILURE: + sErrorId = "MAPI_E_ATTACHMENT_OPEN_FAILURE"; + break; + case MAPI_E_ATTACHMENT_WRITE_FAILURE: + sErrorId = "MAPI_E_ATTACHMENT_WRITE_FAILURE"; + break; + case MAPI_E_UNKNOWN_RECIPIENT: + sErrorId = "MAPI_E_UNKNOWN_RECIPIENT"; + break; + case MAPI_E_BAD_RECIPTYPE: + sErrorId = "MAPI_E_BAD_RECIPTYPE"; + break; + case MAPI_E_NO_MESSAGES: + sErrorId = "MAPI_E_NO_MESSAGES"; + break; + case MAPI_E_INVALID_MESSAGE: + sErrorId = "MAPI_E_INVALID_MESSAGE"; + break; + case MAPI_E_TEXT_TOO_LARGE: + sErrorId = "MAPI_E_TEXT_TOO_LARGE"; + break; + case MAPI_E_INVALID_SESSION: + sErrorId = "MAPI_E_INVALID_SESSION"; + break; + case MAPI_E_TYPE_NOT_SUPPORTED: + sErrorId = "MAPI_E_TYPE_NOT_SUPPORTED"; + break; + case MAPI_E_AMBIGUOUS_RECIPIENT: + sErrorId = "MAPI_E_AMBIGUOUS_RECIPIENT"; + break; + case MAPI_E_MESSAGE_IN_USE: + sErrorId = "MAPI_E_MESSAGE_IN_USE"; + break; + case MAPI_E_NETWORK_FAILURE: + sErrorId = "MAPI_E_NETWORK_FAILURE"; + break; + case MAPI_E_INVALID_EDITFIELDS: + sErrorId = "MAPI_E_INVALID_EDITFIELDS"; + break; + case MAPI_E_INVALID_RECIPS: + sErrorId = "MAPI_E_INVALID_RECIPS"; + break; + case MAPI_E_NOT_SUPPORTED: + sErrorId = "MAPI_E_NOT_SUPPORTED"; + break; + case MAPI_E_UNICODE_NOT_SUPPORTED: + sErrorId = "MAPI_E_UNICODE_NOT_SUPPORTED"; + break; + default: + sErrorId = OUString::number(nMAPIResult); + } + sMessage = sMessage.replaceAll("$1", sErrorId); + OUString sTitle(Translate::get(STR_ERROR_SEND_MAIL_HEADER, aLocale)); + + MessageBoxW(nullptr, o3tl::toW(sMessage.getStr()), o3tl::toW(sTitle.getStr()), + MB_OK | MB_ICONINFORMATION); +} + /** Main. NOTE: Because this is program only serves implementation @@ -275,6 +398,15 @@ int wmain(int argc, wchar_t* argv[]) { OSL_FAIL(ex.what()); } + + // Now cleanup the temporary attachment files + for (const auto& rAttachment : gAttachments) + DeleteFileW(rAttachment.first.c_str()); + + // Only show the error message if UI was requested + if ((ulRet != SUCCESS_SUCCESS) && (gMapiFlags & (MAPI_DIALOG | MAPI_LOGON_UI))) + ShowError(ulRet); + return ulRet; } @@ -302,7 +434,11 @@ int wmain(int argc, wchar_t* argv[]) oss << "--bcc " << address << std::endl; for (const auto& attachment : gAttachments) - oss << "--attach " << attachment << std::endl; + { + oss << "--attach " << attachment.first << std::endl; + if (!attachment.second.empty()) + oss << "--attach-name " << attachment.second << std::endl; + } if (gMapiFlags & MAPI_DIALOG) oss << "--mapi-dialog" << std::endl; @@ -310,6 +446,12 @@ int wmain(int argc, wchar_t* argv[]) if (gMapiFlags & MAPI_LOGON_UI) oss << "--mapi-logon-ui" << std::endl; + if (!gLangTag.isEmpty()) + oss << "--langtag " << gLangTag << std::endl; + + if (!gBootstrap.isEmpty()) + oss << "--bootstrap " << gBootstrap << std::endl; + MessageBoxW(nullptr, oss.str().c_str(), L"Arguments", MB_OK | MB_ICONINFORMATION); } #endif diff --git a/shell/source/win32/simplemail/smplmailclient.cxx b/shell/source/win32/simplemail/smplmailclient.cxx index b38061c37037..8568ae87a908 100644 --- a/shell/source/win32/simplemail/smplmailclient.cxx +++ b/shell/source/win32/simplemail/smplmailclient.cxx @@ -28,15 +28,16 @@ #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 -#if defined _MSC_VER -#pragma warning(push, 1) -#endif #include <windows.h> #include <mapi.h> -#if defined _MSC_VER -#pragma warning(pop) +#if defined GetTempPath +#undef GetTempPath #endif #include <process.h> @@ -62,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 */ { @@ -117,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 @@ -132,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, @@ -142,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); @@ -155,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. @@ -224,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", @@ -237,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)) @@ -244,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( @@ -254,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( diff --git a/shell/source/win32/simplemail/smplmailclient.hxx b/shell/source/win32/simplemail/smplmailclient.hxx index 4a744c289eec..5844e99147e5 100644 --- a/shell/source/win32/simplemail/smplmailclient.hxx +++ b/shell/source/win32/simplemail/smplmailclient.hxx @@ -24,7 +24,9 @@ #include <com/sun/star/lang/XServiceInfo.hpp> #include <com/sun/star/system/XSimpleMailClient.hpp> +#include <unotools/tempfile.hxx> #include <vector> +#include <memory> class CSmplMailClient : public cppu::WeakImplHelper<css::system::XSimpleMailClient> { @@ -36,6 +38,11 @@ public: private: void validateParameter(const css::uno::Reference<css::system::XSimpleMailMessage>& xSimpleMailMessage, sal_Int32 aFlag); void assembleCommandLine(const css::uno::Reference<css::system::XSimpleMailMessage>& xSimpleMailMessage, sal_Int32 aFlag, std::vector<OUString>& rCommandArgs); + OUString CopyAttachment(const OUString& sOrigAttachURL, OUString& sUserVisibleName); + // Don't try to delete the copied attachment files; let the spawned process cleanup them + void ReleaseAttachments(); + + std::vector< std::unique_ptr<utl::TempFile> > maAttachmentFiles; }; #endif |