1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <config_folders.h>
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>
30 #include <o3tl/char16_t2wchar_t.hxx>
31 #include <tools/urlobj.hxx>
32 #include <unotools/pathoptions.hxx>
33 #include <unotools/syslocale.hxx>
34 #include <i18nlangtag/languagetag.hxx>
36 #define WIN32_LEAN_AND_MEAN
39 #if defined GetTempPath
46 using css::uno::UNO_QUERY
;
47 using css::uno::Reference
;
48 using css::uno::Exception
;
49 using css::uno::RuntimeException
;
50 using css::uno::Sequence
;
51 using css::lang::IllegalArgumentException
;
53 using css::system::XSimpleMailClient
;
54 using css::system::XSimpleMailMessage
;
55 using css::system::XSimpleMailMessage2
;
56 using css::system::SimpleMailClientFlags::NO_USER_INTERFACE
;
57 using css::system::SimpleMailClientFlags::NO_LOGON_DIALOG
;
59 namespace /* private */
62 look if an alternative program is configured
63 which should be used as senddoc executable */
64 OUString
getAlternativeSenddocUrl()
66 OUString altSenddocUrl
;
68 LONG lret
= RegOpenKeyW(HKEY_CURRENT_USER
, L
"Software\\LibreOffice\\SendAsEMailClient", &hkey
);
69 if (lret
== ERROR_SUCCESS
)
71 wchar_t buff
[MAX_PATH
];
72 LONG sz
= sizeof(buff
);
73 lret
= RegQueryValueW(hkey
, nullptr, buff
, &sz
);
74 if (lret
== ERROR_SUCCESS
)
76 osl::FileBase::getFileURLFromSystemPath(OUString(o3tl::toU(buff
)), altSenddocUrl
);
84 Returns the absolute file Url of the senddoc executable.
87 the absolute file Url of the senddoc executable. In case
88 of an error an empty string will be returned.
90 OUString
getSenddocUrl()
92 OUString senddocUrl
= getAlternativeSenddocUrl();
94 if (senddocUrl
.isEmpty())
96 senddocUrl
= "$BRAND_BASE_DIR/" LIBO_LIBEXEC_FOLDER
"/senddoc.exe";
97 rtl::Bootstrap::expandMacros(senddocUrl
); //TODO: detect failure
103 Execute Senddoc.exe which a MAPI wrapper.
106 [in] the arguments to be passed to Senddoc.exe
111 bool executeSenddoc(const std::vector
<OUString
>& rCommandArgs
, bool bWait
)
113 OUString senddocUrl
= getSenddocUrl();
114 if (senddocUrl
.getLength() == 0)
117 oslProcessOption nProcOption
= osl_Process_DETACHED
| (bWait
? osl_Process_WAIT
: 0);
121 /* for efficiency reasons we are using a 'bad' cast here
122 as a vector or OUStrings is nothing else than
123 an array of pointers to rtl_uString's */
124 oslProcessError err
= osl_executeProcess(
126 const_cast<rtl_uString
**>(reinterpret_cast<rtl_uString
* const *>(rCommandArgs
.data())),
135 if (err
!= osl_Process_E_None
)
141 oslProcessInfo procInfo
;
142 procInfo
.Size
= sizeof(oslProcessInfo
);
143 osl_getProcessInfo(proc
, osl_Process_EXITCODE
, &procInfo
);
144 osl_freeProcessHandle(proc
);
145 return (procInfo
.Code
== SUCCESS_SUCCESS
);
147 } // namespace private
149 Reference
<XSimpleMailMessage
> SAL_CALL
CSmplMailClient::createSimpleMailMessage()
151 return Reference
<XSimpleMailMessage
>(new CSmplMailMsg());
155 // We cannot use the session-local temporary directory for the attachment,
156 // because it will get removed upon program exit; and it must be alive for
157 // senddoc process lifetime. So we use base temppath for the attachments,
158 // and let the senddoc to do the cleanup if it was started successfully.
159 // This function works like Desktop::CreateTemporaryDirectory()
160 OUString
InitBaseTempDirURL()
162 // No need to intercept an exception here, since
163 // Desktop::CreateTemporaryDirectory() has ensured that path manager is available
165 OUString aRetURL
= aOpt
.GetTempPath();
166 if (aRetURL
.isEmpty())
168 osl::File::getTempDirURL(aRetURL
);
170 if (aRetURL
.endsWith("/"))
171 aRetURL
= aRetURL
.copy(0, aRetURL
.getLength() - 1);
176 const OUString
& GetBaseTempDirURL()
178 static const OUString
aRetURL(InitBaseTempDirURL());
183 OUString
CSmplMailClient::CopyAttachment(const OUString
& sOrigAttachURL
, OUString
& sUserVisibleName
,
186 // We do two things here:
187 // 1. Make the attachment temporary filename to not contain any fancy characters possible in
188 // original filename, that could confuse mailer, and extract the original filename to explicitly
190 // 2. Allow the copied files be outside of the session's temporary directory, and thus not be
191 // removed in Desktop::RemoveTemporaryDirectory() if soffice process gets closed before the
192 // mailer finishes using them.
194 maAttachmentFiles
.emplace_back(std::make_unique
<utl::TempFileNamed
>(&GetBaseTempDirURL()));
195 maAttachmentFiles
.back()->EnableKillingFile();
196 INetURLObject
aFilePathObj(maAttachmentFiles
.back()->GetURL());
197 OUString sNewAttachmentURL
= aFilePathObj
.GetMainURL(INetURLObject::DecodeMechanism::NONE
);
198 OUString
sCorrectedOrigAttachURL(sOrigAttachURL
);
199 // Make sure to convert to URL, if a system path was passed to XSimpleMailMessage
200 // Ignore conversion error, in which case sCorrectedOrigAttachURL is unchanged
201 osl::FileBase::getFileURLFromSystemPath(sCorrectedOrigAttachURL
, sCorrectedOrigAttachURL
);
202 if (osl::File::copy(sCorrectedOrigAttachURL
, sNewAttachmentURL
) == osl::FileBase::RC::E_None
)
204 INetURLObject
url(sCorrectedOrigAttachURL
, INetURLObject::EncodeMechanism::WasEncoded
);
205 sUserVisibleName
= url
.getName(INetURLObject::LAST_SEGMENT
, true,
206 INetURLObject::DecodeMechanism::WithCharset
);
211 // Failed to copy original; the best effort is to use original file. It is possible that
212 // the file gets deleted before used in spawned process; but let's hope... the worst thing
213 // is the absent attachment file anyway.
214 sNewAttachmentURL
= sOrigAttachURL
;
215 maAttachmentFiles
.pop_back();
216 nodelete
= true; // Do not delete a non-temporary in senddoc
218 return sNewAttachmentURL
;
221 void CSmplMailClient::ReleaseAttachments()
223 for (auto& pTempFile
: maAttachmentFiles
)
226 pTempFile
->EnableKillingFile(false);
228 maAttachmentFiles
.clear();
232 Assemble a command line for SendDoc.exe out of the members
233 of the supplied SimpleMailMessage.
235 @param xSimpleMailMessage
236 [in] the mail message.
239 [in] different flags to be used with the simple mail service.
242 [in|out] a buffer for the command line arguments. The buffer
243 is assumed to be empty.
245 @throws css::lang::IllegalArgumentException
246 if an invalid file URL has been detected in the attachment list.
248 void CSmplMailClient::assembleCommandLine(
249 const Reference
<XSimpleMailMessage
>& xSimpleMailMessage
,
250 sal_Int32 aFlag
, std::vector
<OUString
>& rCommandArgs
)
252 OSL_ENSURE(rCommandArgs
.empty(), "Provided command argument buffer not empty");
254 Reference
<XSimpleMailMessage2
> xMessage( xSimpleMailMessage
, UNO_QUERY
);
257 OUString body
= xMessage
->getBody();
258 if (body
.getLength()>0)
260 rCommandArgs
.push_back("--body");
261 rCommandArgs
.push_back(body
);
265 OUString to
= xSimpleMailMessage
->getRecipient();
266 if (to
.getLength() > 0)
268 rCommandArgs
.push_back("--to");
269 rCommandArgs
.push_back(to
);
272 const Sequence
<OUString
> ccRecipients
= xSimpleMailMessage
->getCcRecipient();
273 for (OUString
const & s
: ccRecipients
)
275 rCommandArgs
.push_back("--cc");
276 rCommandArgs
.push_back(s
);
279 const Sequence
<OUString
> bccRecipients
= xSimpleMailMessage
->getBccRecipient();
280 for (OUString
const & s
: bccRecipients
)
282 rCommandArgs
.push_back("--bcc");
283 rCommandArgs
.push_back(s
);
286 OUString from
= xSimpleMailMessage
->getOriginator();
287 if (from
.getLength() > 0)
289 rCommandArgs
.push_back("--from");
290 rCommandArgs
.push_back(from
);
293 OUString subject
= xSimpleMailMessage
->getSubject();
294 if (subject
.getLength() > 0)
296 rCommandArgs
.push_back("--subject");
297 rCommandArgs
.push_back(subject
);
300 auto const attachments
= xSimpleMailMessage
->getAttachement();
301 for (const auto& attachment
: attachments
)
303 OUString sDisplayName
;
304 bool nodelete
= false;
305 OUString
sTempFileURL(CopyAttachment(attachment
, sDisplayName
, nodelete
));
307 osl::FileBase::RC err
= osl::FileBase::getSystemPathFromFileURL(sTempFileURL
, sysPath
);
308 if (err
!= osl::FileBase::E_None
)
309 throw IllegalArgumentException(
310 "Invalid attachment file URL",
311 static_cast<XSimpleMailClient
*>(this),
314 rCommandArgs
.push_back("--attach");
315 rCommandArgs
.push_back(sysPath
);
316 if (!sDisplayName
.isEmpty())
318 rCommandArgs
.push_back("--attach-name");
319 rCommandArgs
.push_back(sDisplayName
);
322 rCommandArgs
.push_back("--nodelete");
325 if (!(aFlag
& NO_USER_INTERFACE
))
326 rCommandArgs
.push_back("--mapi-dialog");
328 if (!(aFlag
& NO_LOGON_DIALOG
))
329 rCommandArgs
.push_back("--mapi-logon-ui");
331 rCommandArgs
.push_back("--langtag");
332 rCommandArgs
.push_back(SvtSysLocale().GetUILanguageTag().getBcp47());
334 rtl::Bootstrap aBootstrap
;
335 OUString sBootstrapPath
;
336 aBootstrap
.getIniName(sBootstrapPath
);
337 if (!sBootstrapPath
.isEmpty())
339 rCommandArgs
.push_back("--bootstrap");
340 rCommandArgs
.push_back(sBootstrapPath
);
345 void SAL_CALL
CSmplMailClient::sendSimpleMailMessage(
346 const Reference
<XSimpleMailMessage
>& xSimpleMailMessage
, sal_Int32 aFlag
)
348 validateParameter(xSimpleMailMessage
, aFlag
);
350 std::vector
<OUString
> senddocParams
;
351 assembleCommandLine(xSimpleMailMessage
, aFlag
, senddocParams
);
353 const bool bWait
= aFlag
& NO_USER_INTERFACE
;
354 if (!executeSenddoc(senddocParams
, bWait
))
357 static_cast<XSimpleMailClient
*>(this));
358 // Let the launched senddoc to cleanup the attachments temporary files
360 ReleaseAttachments();
363 void CSmplMailClient::validateParameter(
364 const Reference
<XSimpleMailMessage
>& xSimpleMailMessage
, sal_Int32 aFlag
)
366 if (!xSimpleMailMessage
.is())
367 throw IllegalArgumentException(
368 "Empty mail message reference",
369 static_cast<XSimpleMailClient
*>(this),
372 OSL_ENSURE(!(aFlag
& NO_LOGON_DIALOG
), "Flag NO_LOGON_DIALOG has currently no effect");
374 // check the flags, the allowed range is 0 - (2^n - 1)
375 if (aFlag
< 0 || aFlag
> 3)
376 throw IllegalArgumentException(
377 "Invalid flag value",
378 static_cast<XSimpleMailClient
*>(this),
381 // check if a recipient is specified of the flags NO_USER_INTERFACE is specified
382 if ((aFlag
& NO_USER_INTERFACE
) && !xSimpleMailMessage
->getRecipient().getLength())
383 throw IllegalArgumentException(
384 "No recipient specified",
385 static_cast<XSimpleMailClient
*>(this),
389 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */