LibreOffice Module shell (master) 1
smplmailclient.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 <config_folders.h>
21
22#include <osl/diagnose.h>
23#include <osl/process.h>
24#include <rtl/bootstrap.hxx>
25#include "smplmailclient.hxx"
26#include "smplmailmsg.hxx"
27#include <com/sun/star/system/SimpleMailClientFlags.hpp>
28#include <com/sun/star/system/XSimpleMailMessage2.hpp>
29#include <osl/file.hxx>
31#include <tools/urlobj.hxx>
35
36#define WIN32_LEAN_AND_MEAN
37#include <windows.h>
38#include <mapi.h>
39#if defined GetTempPath
40#undef GetTempPath
41#endif
42
43#include <process.h>
44#include <vector>
45
46using css::uno::UNO_QUERY;
47using css::uno::Reference;
48using css::uno::Exception;
49using css::uno::Sequence;
50using css::lang::IllegalArgumentException;
51
52using css::system::XSimpleMailClient;
53using css::system::XSimpleMailMessage;
54using css::system::XSimpleMailMessage2;
55using css::system::SimpleMailClientFlags::NO_USER_INTERFACE;
56using css::system::SimpleMailClientFlags::NO_LOGON_DIALOG;
57
58namespace /* private */
59{
63 OUString getAlternativeSenddocUrl()
64 {
65 OUString altSenddocUrl;
66 HKEY hkey;
67 LONG lret = RegOpenKeyW(HKEY_CURRENT_USER, L"Software\\LibreOffice\\SendAsEMailClient", &hkey);
68 if (lret == ERROR_SUCCESS)
69 {
70 wchar_t buff[MAX_PATH];
71 LONG sz = sizeof(buff);
72 lret = RegQueryValueW(hkey, nullptr, buff, &sz);
73 if (lret == ERROR_SUCCESS)
74 {
75 osl::FileBase::getFileURLFromSystemPath(OUString(o3tl::toU(buff)), altSenddocUrl);
76 }
77 RegCloseKey(hkey);
78 }
79 return altSenddocUrl;
80 }
81
89 OUString getSenddocUrl()
90 {
91 OUString senddocUrl = getAlternativeSenddocUrl();
92
93 if (senddocUrl.isEmpty())
94 {
95 senddocUrl = "$BRAND_BASE_DIR/" LIBO_LIBEXEC_FOLDER "/senddoc.exe";
96 rtl::Bootstrap::expandMacros(senddocUrl); //TODO: detect failure
97 }
98 return senddocUrl;
99 }
100
110 bool executeSenddoc(const std::vector<OUString>& rCommandArgs, bool bWait)
111 {
112 OUString senddocUrl = getSenddocUrl();
113 if (senddocUrl.getLength() == 0)
114 return false;
115
116 oslProcessOption nProcOption = osl_Process_DETACHED | (bWait ? osl_Process_WAIT : 0);
117
118 oslProcess proc;
119
120 /* for efficiency reasons we are using a 'bad' cast here
121 as a vector or OUStrings is nothing else than
122 an array of pointers to rtl_uString's */
123 oslProcessError err = osl_executeProcess(
124 senddocUrl.pData,
125 const_cast<rtl_uString**>(reinterpret_cast<rtl_uString * const *>(rCommandArgs.data())),
126 rCommandArgs.size(),
127 nProcOption,
128 nullptr,
129 nullptr,
130 nullptr,
131 0,
132 &proc);
133
134 if (err != osl_Process_E_None)
135 return false;
136
137 if (!bWait)
138 return true;
139
140 oslProcessInfo procInfo;
141 procInfo.Size = sizeof(oslProcessInfo);
142 osl_getProcessInfo(proc, osl_Process_EXITCODE, &procInfo);
143 osl_freeProcessHandle(proc);
144 return (procInfo.Code == SUCCESS_SUCCESS);
145 }
146} // namespace private
147
148Reference<XSimpleMailMessage> SAL_CALL CSmplMailClient::createSimpleMailMessage()
149{
150 return Reference<XSimpleMailMessage>(new CSmplMailMsg());
151}
152
153namespace {
154// We cannot use the session-local temporary directory for the attachment,
155// because it will get removed upon program exit; and it must be alive for
156// senddoc process lifetime. So we use base temppath for the attachments,
157// and let the senddoc to do the cleanup if it was started successfully.
158// This function works like Desktop::CreateTemporaryDirectory()
159OUString InitBaseTempDirURL()
160{
161 // No need to intercept an exception here, since
162 // Desktop::CreateTemporaryDirectory() has ensured that path manager is available
163 SvtPathOptions aOpt;
164 OUString aRetURL = aOpt.GetTempPath();
165 if (aRetURL.isEmpty())
166 {
167 osl::File::getTempDirURL(aRetURL);
168 }
169 if (aRetURL.endsWith("/"))
170 aRetURL = aRetURL.copy(0, aRetURL.getLength() - 1);
171
172 return aRetURL;
173}
174
175const OUString& GetBaseTempDirURL()
176{
177 static const OUString aRetURL(InitBaseTempDirURL());
178 return aRetURL;
179}
180}
181
182OUString CSmplMailClient::CopyAttachment(const OUString& sOrigAttachURL, OUString& sUserVisibleName,
183 bool& nodelete)
184{
185 // We do two things here:
186 // 1. Make the attachment temporary filename to not contain any fancy characters possible in
187 // original filename, that could confuse mailer, and extract the original filename to explicitly
188 // define it;
189 // 2. Allow the copied files be outside of the session's temporary directory, and thus not be
190 // removed in Desktop::RemoveTemporaryDirectory() if soffice process gets closed before the
191 // mailer finishes using them.
192
193 maAttachmentFiles.emplace_back(std::make_unique<utl::TempFileNamed>(&GetBaseTempDirURL()));
194 maAttachmentFiles.back()->EnableKillingFile();
195 INetURLObject aFilePathObj(maAttachmentFiles.back()->GetURL());
196 OUString sNewAttachmentURL = aFilePathObj.GetMainURL(INetURLObject::DecodeMechanism::NONE);
197 OUString sCorrectedOrigAttachURL(sOrigAttachURL);
198 // Make sure to convert to URL, if a system path was passed to XSimpleMailMessage
199 // Ignore conversion error, in which case sCorrectedOrigAttachURL is unchanged
200 osl::FileBase::getFileURLFromSystemPath(sCorrectedOrigAttachURL, sCorrectedOrigAttachURL);
201 if (osl::File::copy(sCorrectedOrigAttachURL, sNewAttachmentURL) == osl::FileBase::RC::E_None)
202 {
203 INetURLObject url(sCorrectedOrigAttachURL, INetURLObject::EncodeMechanism::WasEncoded);
204 sUserVisibleName = url.getName(INetURLObject::LAST_SEGMENT, true,
206 nodelete = false;
207 }
208 else
209 {
210 // Failed to copy original; the best effort is to use original file. It is possible that
211 // the file gets deleted before used in spawned process; but let's hope... the worst thing
212 // is the absent attachment file anyway.
213 sNewAttachmentURL = sOrigAttachURL;
214 maAttachmentFiles.pop_back();
215 nodelete = true; // Do not delete a non-temporary in senddoc
216 }
217 return sNewAttachmentURL;
218}
219
221{
222 for (auto& pTempFile : maAttachmentFiles)
223 {
224 if (pTempFile)
225 pTempFile->EnableKillingFile(false);
226 }
227 maAttachmentFiles.clear();
228}
229
248 const Reference<XSimpleMailMessage>& xSimpleMailMessage,
249 sal_Int32 aFlag, std::vector<OUString>& rCommandArgs)
250{
251 OSL_ENSURE(rCommandArgs.empty(), "Provided command argument buffer not empty");
252
253 Reference<XSimpleMailMessage2> xMessage( xSimpleMailMessage, UNO_QUERY );
254 if (xMessage.is())
255 {
256 OUString body = xMessage->getBody();
257 if (body.getLength()>0)
258 {
259 rCommandArgs.push_back("--body");
260 rCommandArgs.push_back(body);
261 }
262 }
263
264 OUString to = xSimpleMailMessage->getRecipient();
265 if (to.getLength() > 0)
266 {
267 rCommandArgs.push_back("--to");
268 rCommandArgs.push_back(to);
269 }
270
271 const Sequence<OUString> ccRecipients = xSimpleMailMessage->getCcRecipient();
272 for (OUString const & s : ccRecipients)
273 {
274 rCommandArgs.push_back("--cc");
275 rCommandArgs.push_back(s);
276 }
277
278 const Sequence<OUString> bccRecipients = xSimpleMailMessage->getBccRecipient();
279 for (OUString const & s : bccRecipients)
280 {
281 rCommandArgs.push_back("--bcc");
282 rCommandArgs.push_back(s);
283 }
284
285 OUString from = xSimpleMailMessage->getOriginator();
286 if (from.getLength() > 0)
287 {
288 rCommandArgs.push_back("--from");
289 rCommandArgs.push_back(from);
290 }
291
292 OUString subject = xSimpleMailMessage->getSubject();
293 if (subject.getLength() > 0)
294 {
295 rCommandArgs.push_back("--subject");
296 rCommandArgs.push_back(subject);
297 }
298
299 auto const attachments = xSimpleMailMessage->getAttachement();
300 for (const auto& attachment : attachments)
301 {
302 OUString sDisplayName;
303 bool nodelete = false;
304 OUString sTempFileURL(CopyAttachment(attachment, sDisplayName, nodelete));
305 OUString sysPath;
306 osl::FileBase::RC err = osl::FileBase::getSystemPathFromFileURL(sTempFileURL, sysPath);
307 if (err != osl::FileBase::E_None)
308 throw IllegalArgumentException(
309 "Invalid attachment file URL",
310 static_cast<XSimpleMailClient*>(this),
311 1);
312
313 rCommandArgs.push_back("--attach");
314 rCommandArgs.push_back(sysPath);
315 if (!sDisplayName.isEmpty())
316 {
317 rCommandArgs.push_back("--attach-name");
318 rCommandArgs.push_back(sDisplayName);
319 }
320 if (nodelete)
321 rCommandArgs.push_back("--nodelete");
322 }
323
324 if (!(aFlag & NO_USER_INTERFACE))
325 rCommandArgs.push_back("--mapi-dialog");
326
327 if (!(aFlag & NO_LOGON_DIALOG))
328 rCommandArgs.push_back("--mapi-logon-ui");
329
330 rCommandArgs.push_back("--langtag");
331 rCommandArgs.push_back(SvtSysLocale().GetUILanguageTag().getBcp47());
332
333 rtl::Bootstrap aBootstrap;
334 OUString sBootstrapPath;
335 aBootstrap.getIniName(sBootstrapPath);
336 if (!sBootstrapPath.isEmpty())
337 {
338 rCommandArgs.push_back("--bootstrap");
339 rCommandArgs.push_back(sBootstrapPath);
340 }
341
342}
343
345 const Reference<XSimpleMailMessage>& xSimpleMailMessage, sal_Int32 aFlag)
346{
347 validateParameter(xSimpleMailMessage, aFlag);
348
349 std::vector<OUString> senddocParams;
350 assembleCommandLine(xSimpleMailMessage, aFlag, senddocParams);
351
352 const bool bWait = aFlag & NO_USER_INTERFACE;
353 if (!executeSenddoc(senddocParams, bWait))
354 throw Exception(
355 "Send email failed",
356 static_cast<XSimpleMailClient*>(this));
357 // Let the launched senddoc to cleanup the attachments temporary files
358 if (!bWait)
360}
361
363 const Reference<XSimpleMailMessage>& xSimpleMailMessage, sal_Int32 aFlag )
364{
365 if (!xSimpleMailMessage.is())
366 throw IllegalArgumentException(
367 "Empty mail message reference",
368 static_cast<XSimpleMailClient*>(this),
369 1);
370
371 OSL_ENSURE(!(aFlag & NO_LOGON_DIALOG), "Flag NO_LOGON_DIALOG has currently no effect");
372
373 // check the flags, the allowed range is 0 - (2^n - 1)
374 if (aFlag < 0 || aFlag > 3)
375 throw IllegalArgumentException(
376 "Invalid flag value",
377 static_cast<XSimpleMailClient*>(this),
378 2);
379
380 // check if a recipient is specified of the flags NO_USER_INTERFACE is specified
381 if ((aFlag & NO_USER_INTERFACE) && !xSimpleMailMessage->getRecipient().getLength())
382 throw IllegalArgumentException(
383 "No recipient specified",
384 static_cast<XSimpleMailClient*>(this),
385 1);
386}
387
388/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
UBlockCode from
UBlockCode to
OUString CopyAttachment(const OUString &sOrigAttachURL, OUString &sUserVisibleName, bool &nodelete)
std::vector< std::unique_ptr< utl::TempFileNamed > > maAttachmentFiles
virtual void SAL_CALL sendSimpleMailMessage(const css::uno::Reference< css::system::XSimpleMailMessage > &xSimpleMailMessage, sal_Int32 aFlag) override
virtual css::uno::Reference< css::system::XSimpleMailMessage > SAL_CALL createSimpleMailMessage() override
void assembleCommandLine(const css::uno::Reference< css::system::XSimpleMailMessage > &xSimpleMailMessage, sal_Int32 aFlag, std::vector< OUString > &rCommandArgs)
Assemble a command line for SendDoc.exe out of the members of the supplied SimpleMailMessage.
void validateParameter(const css::uno::Reference< css::system::XSimpleMailMessage > &xSimpleMailMessage, sal_Int32 aFlag)
OUString getName(sal_Int32 nIndex=LAST_SEGMENT, bool bIgnoreFinalSlash=true, DecodeMechanism eMechanism=DecodeMechanism::ToIUri, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
OUString GetMainURL(DecodeMechanism eMechanism, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
const OUString & GetTempPath() const
OUString sDisplayName
#define MAX_PATH
err
@ Exception
LONG