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 const OUString
TO("--to");
60 const OUString
CC("--cc");
61 const OUString
BCC("--bcc");
62 const OUString
FROM("--from");
63 const OUString
SUBJECT("--subject");
64 const OUString
BODY("--body");
65 const OUString
ATTACH("--attach");
66 const OUString
ATTACH_NAME("--attach-name");
67 const OUString
FLAG_MAPI_DIALOG("--mapi-dialog");
68 const OUString
FLAG_MAPI_LOGON_UI("--mapi-logon-ui");
69 const OUString
FLAG_LANGTAG("--langtag");
70 const OUString
FLAG_BOOTSTRAP("--bootstrap");
72 namespace /* private */
75 look if an alternative program is configured
76 which should be used as senddoc executable */
77 OUString
getAlternativeSenddocUrl()
79 OUString altSenddocUrl
;
81 LONG lret
= RegOpenKeyW(HKEY_CURRENT_USER
, L
"Software\\LibreOffice\\SendAsEMailClient", &hkey
);
82 if (lret
== ERROR_SUCCESS
)
84 wchar_t buff
[MAX_PATH
];
85 LONG sz
= sizeof(buff
);
86 lret
= RegQueryValueW(hkey
, nullptr, buff
, &sz
);
87 if (lret
== ERROR_SUCCESS
)
89 osl::FileBase::getFileURLFromSystemPath(o3tl::toU(buff
), altSenddocUrl
);
97 Returns the absolute file Url of the senddoc executable.
100 the absolute file Url of the senddoc executable. In case
101 of an error an empty string will be returned.
103 OUString
getSenddocUrl()
105 OUString senddocUrl
= getAlternativeSenddocUrl();
107 if (senddocUrl
.isEmpty())
109 senddocUrl
= "$BRAND_BASE_DIR/" LIBO_LIBEXEC_FOLDER
"/senddoc.exe";
110 rtl::Bootstrap::expandMacros(senddocUrl
); //TODO: detect failure
116 Execute Senddoc.exe which a MAPI wrapper.
119 [in] the arguments to be passed to Senddoc.exe
124 bool executeSenddoc(const std::vector
<OUString
>& rCommandArgs
, bool bWait
)
126 OUString senddocUrl
= getSenddocUrl();
127 if (senddocUrl
.getLength() == 0)
130 oslProcessOption nProcOption
= osl_Process_DETACHED
| (bWait
? osl_Process_WAIT
: 0);
134 /* for efficiency reasons we are using a 'bad' cast here
135 as a vector or OUStrings is nothing else than
136 an array of pointers to rtl_uString's */
137 oslProcessError err
= osl_executeProcess(
139 const_cast<rtl_uString
**>(reinterpret_cast<rtl_uString
* const *>(rCommandArgs
.data())),
148 if (err
!= osl_Process_E_None
)
154 oslProcessInfo procInfo
;
155 procInfo
.Size
= sizeof(oslProcessInfo
);
156 osl_getProcessInfo(proc
, osl_Process_EXITCODE
, &procInfo
);
157 osl_freeProcessHandle(proc
);
158 return (procInfo
.Code
== SUCCESS_SUCCESS
);
160 } // namespace private
162 Reference
<XSimpleMailMessage
> SAL_CALL
CSmplMailClient::createSimpleMailMessage()
164 return Reference
<XSimpleMailMessage
>(new CSmplMailMsg());
168 // We cannot use the session-local temporary directory for the attachment,
169 // because it will get removed upon program exit; and it must be alive for
170 // senddoc process lifetime. So we use base temppath for the attachments,
171 // and let the senddoc to do the cleanup if it was started successfully.
172 // This function works like Desktop::CreateTemporaryDirectory()
173 OUString
InitBaseTempDirURL()
175 // No need to intercept an exception here, since
176 // Desktop::CreateTemporaryDirectory() has ensured that path manager is available
178 OUString aRetURL
= aOpt
.GetTempPath();
179 if (aRetURL
.isEmpty())
181 osl::File::getTempDirURL(aRetURL
);
183 if (aRetURL
.endsWith("/"))
184 aRetURL
= aRetURL
.copy(0, aRetURL
.getLength() - 1);
189 const OUString
& GetBaseTempDirURL()
191 static const OUString
aRetURL(InitBaseTempDirURL());
196 OUString
CSmplMailClient::CopyAttachment(const OUString
& sOrigAttachURL
, OUString
& sUserVisibleName
)
198 // We do two things here:
199 // 1. Make the attachment temporary filename to not contain any fancy characters possible in
200 // original filename, that could confuse mailer, and extract the original filename to explicitly
202 // 2. Allow the copied files be outside of the session's temporary directory, and thus not be
203 // removed in Desktop::RemoveTemporaryDirectory() if soffice process gets closed before the
204 // mailer finishes using them.
206 maAttachmentFiles
.emplace_back(std::make_unique
<utl::TempFile
>(&GetBaseTempDirURL()));
207 maAttachmentFiles
.back()->EnableKillingFile();
208 INetURLObject
aFilePathObj(maAttachmentFiles
.back()->GetURL());
209 OUString sNewAttachmentURL
= aFilePathObj
.GetMainURL(INetURLObject::DecodeMechanism::NONE
);
210 if (osl::File::copy(sOrigAttachURL
, sNewAttachmentURL
) == osl::FileBase::RC::E_None
)
212 INetURLObject
url(sOrigAttachURL
, INetURLObject::EncodeMechanism::WasEncoded
);
213 sUserVisibleName
= url
.getName(INetURLObject::LAST_SEGMENT
, true,
214 INetURLObject::DecodeMechanism::WithCharset
);
218 // Failed to copy original; the best effort is to use original file. It is possible that
219 // the file gets deleted before used in spawned process; but let's hope... the worst thing
220 // is the absent attachment file anyway.
221 sNewAttachmentURL
= sOrigAttachURL
;
222 maAttachmentFiles
.pop_back();
224 return sNewAttachmentURL
;
227 void CSmplMailClient::ReleaseAttachments()
229 for (auto& pTempFile
: maAttachmentFiles
)
232 pTempFile
->EnableKillingFile(false);
234 maAttachmentFiles
.clear();
238 Assemble a command line for SendDoc.exe out of the members
239 of the supplied SimpleMailMessage.
241 @param xSimpleMailMessage
242 [in] the mail message.
245 [in] different flags to be used with the simple mail service.
248 [in|out] a buffer for the command line arguments. The buffer
249 is assumed to be empty.
251 @throws css::lang::IllegalArgumentException
252 if an invalid file URL has been detected in the attachment list.
254 void CSmplMailClient::assembleCommandLine(
255 const Reference
<XSimpleMailMessage
>& xSimpleMailMessage
,
256 sal_Int32 aFlag
, std::vector
<OUString
>& rCommandArgs
)
258 OSL_ENSURE(rCommandArgs
.empty(), "Provided command argument buffer not empty");
260 Reference
<XSimpleMailMessage2
> xMessage( xSimpleMailMessage
, UNO_QUERY
);
263 OUString body
= xMessage
->getBody();
264 if (body
.getLength()>0)
266 rCommandArgs
.push_back(BODY
);
267 rCommandArgs
.push_back(body
);
271 OUString to
= xSimpleMailMessage
->getRecipient();
272 if (to
.getLength() > 0)
274 rCommandArgs
.push_back(TO
);
275 rCommandArgs
.push_back(to
);
278 Sequence
<OUString
> ccRecipients
= xSimpleMailMessage
->getCcRecipient();
279 for (int i
= 0; i
< ccRecipients
.getLength(); i
++)
281 rCommandArgs
.push_back(CC
);
282 rCommandArgs
.push_back(ccRecipients
[i
]);
285 Sequence
<OUString
> bccRecipients
= xSimpleMailMessage
->getBccRecipient();
286 for (int i
= 0; i
< bccRecipients
.getLength(); i
++)
288 rCommandArgs
.push_back(BCC
);
289 rCommandArgs
.push_back(bccRecipients
[i
]);
292 OUString from
= xSimpleMailMessage
->getOriginator();
293 if (from
.getLength() > 0)
295 rCommandArgs
.push_back(FROM
);
296 rCommandArgs
.push_back(from
);
299 OUString subject
= xSimpleMailMessage
->getSubject();
300 if (subject
.getLength() > 0)
302 rCommandArgs
.push_back(SUBJECT
);
303 rCommandArgs
.push_back(subject
);
306 auto const attachments
= xSimpleMailMessage
->getAttachement();
307 for (const auto& attachment
: attachments
)
309 OUString sDisplayName
;
310 OUString
sTempFileURL(CopyAttachment(attachment
, sDisplayName
));
312 osl::FileBase::RC err
= osl::FileBase::getSystemPathFromFileURL(sTempFileURL
, sysPath
);
313 if (err
!= osl::FileBase::E_None
)
314 throw IllegalArgumentException(
315 "Invalid attachment file URL",
316 static_cast<XSimpleMailClient
*>(this),
319 rCommandArgs
.push_back(ATTACH
);
320 rCommandArgs
.push_back(sysPath
);
321 if (!sDisplayName
.isEmpty())
323 rCommandArgs
.push_back(ATTACH_NAME
);
324 rCommandArgs
.push_back(sDisplayName
);
328 if (!(aFlag
& NO_USER_INTERFACE
))
329 rCommandArgs
.push_back(FLAG_MAPI_DIALOG
);
331 if (!(aFlag
& NO_LOGON_DIALOG
))
332 rCommandArgs
.push_back(FLAG_MAPI_LOGON_UI
);
334 rCommandArgs
.push_back(FLAG_LANGTAG
);
335 rCommandArgs
.push_back(SvtSysLocale().GetUILanguageTag().getBcp47());
337 rtl::Bootstrap aBootstrap
;
338 OUString sBootstrapPath
;
339 aBootstrap
.getIniName(sBootstrapPath
);
340 if (!sBootstrapPath
.isEmpty())
342 rCommandArgs
.push_back(FLAG_BOOTSTRAP
);
343 rCommandArgs
.push_back(sBootstrapPath
);
348 void SAL_CALL
CSmplMailClient::sendSimpleMailMessage(
349 const Reference
<XSimpleMailMessage
>& xSimpleMailMessage
, sal_Int32 aFlag
)
351 validateParameter(xSimpleMailMessage
, aFlag
);
353 std::vector
<OUString
> senddocParams
;
354 assembleCommandLine(xSimpleMailMessage
, aFlag
, senddocParams
);
356 const bool bWait
= aFlag
& NO_USER_INTERFACE
;
357 if (!executeSenddoc(senddocParams
, bWait
))
360 static_cast<XSimpleMailClient
*>(this));
361 // Let the launched senddoc to cleanup the attachments temporary files
363 ReleaseAttachments();
366 void CSmplMailClient::validateParameter(
367 const Reference
<XSimpleMailMessage
>& xSimpleMailMessage
, sal_Int32 aFlag
)
369 if (!xSimpleMailMessage
.is())
370 throw IllegalArgumentException(
371 "Empty mail message reference",
372 static_cast<XSimpleMailClient
*>(this),
375 OSL_ENSURE(!(aFlag
& NO_LOGON_DIALOG
), "Flag NO_LOGON_DIALOG has currently no effect");
377 // check the flags, the allowed range is 0 - (2^n - 1)
378 if (aFlag
< 0 || aFlag
> 3)
379 throw IllegalArgumentException(
380 "Invalid flag value",
381 static_cast<XSimpleMailClient
*>(this),
384 // check if a recipient is specified of the flags NO_USER_INTERFACE is specified
385 if ((aFlag
& NO_USER_INTERFACE
) && !xSimpleMailMessage
->getRecipient().getLength())
386 throw IllegalArgumentException(
387 "No recipient specified",
388 static_cast<XSimpleMailClient
*>(this),
392 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */