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/.
10 #include <unotest/macros_test.hxx>
14 #include <com/sun/star/document/MacroExecMode.hpp>
15 #include <com/sun/star/uno/XComponentContext.hpp>
16 #include <com/sun/star/frame/DispatchHelper.hpp>
17 #include <com/sun/star/packages/zip/ZipFileAccess.hpp>
18 #include <com/sun/star/security/CertificateValidity.hpp>
19 #include <com/sun/star/security/XCertificate.hpp>
20 #include <com/sun/star/util/URLTransformer.hpp>
21 #include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp>
23 #include <basic/basrdll.hxx>
24 #include <cppunit/TestAssert.h>
25 #include <comphelper/sequence.hxx>
26 #include <comphelper/processfactory.hxx>
27 #include <unotest/directories.hxx>
28 #include <osl/file.hxx>
29 #include <osl/process.h>
30 #include <osl/thread.h>
31 #include <tools/datetime.hxx>
32 #include <unotools/tempfile.hxx>
33 #include <unotools/ucbstreamhelper.hxx>
34 #include <vcl/scheduler.hxx>
40 MacrosTest::MacrosTest()
41 : mpDll(std::make_unique
<BasicDLL
>())
45 MacrosTest::~MacrosTest() = default;
47 uno::Reference
<css::lang::XComponent
>
48 MacrosTest::loadFromDesktop(const OUString
& rURL
, const OUString
& rDocService
,
49 const uno::Sequence
<beans::PropertyValue
>& rExtraArgs
)
51 CPPUNIT_ASSERT_MESSAGE("no desktop", mxDesktop
.is());
52 std::vector
<beans::PropertyValue
> args
;
53 beans::PropertyValue aMacroValue
;
54 aMacroValue
.Name
= "MacroExecutionMode";
55 aMacroValue
.Handle
= -1;
56 aMacroValue
.Value
<<= document::MacroExecMode::ALWAYS_EXECUTE_NO_WARN
;
57 aMacroValue
.State
= beans::PropertyState_DIRECT_VALUE
;
58 args
.push_back(aMacroValue
);
60 if (!rDocService
.isEmpty())
62 beans::PropertyValue aValue
;
63 aValue
.Name
= "DocumentService";
65 aValue
.Value
<<= rDocService
;
66 aValue
.State
= beans::PropertyState_DIRECT_VALUE
;
67 args
.push_back(aValue
);
70 args
.insert(args
.end(), rExtraArgs
.begin(), rExtraArgs
.end());
72 uno::Reference
<lang::XComponent
> xComponent
= mxDesktop
->loadComponentFromURL(
73 rURL
, u
"_default"_ustr
, 0, comphelper::containerToSequence(args
));
74 OUString sMessage
= "loading failed: " + rURL
;
75 CPPUNIT_ASSERT_MESSAGE(OUStringToOString(sMessage
, RTL_TEXTENCODING_UTF8
).getStr(),
81 MacrosTest::dispatchCommand(const uno::Reference
<lang::XComponent
>& xComponent
,
82 const OUString
& rCommand
,
83 const uno::Sequence
<beans::PropertyValue
>& rPropertyValues
)
85 uno::Reference
<frame::XController
> xController
86 = uno::Reference
<frame::XModel
>(xComponent
, uno::UNO_QUERY_THROW
)->getCurrentController();
87 CPPUNIT_ASSERT(xController
.is());
88 uno::Reference
<frame::XDispatchProvider
> xFrame(xController
->getFrame(), uno::UNO_QUERY
);
89 CPPUNIT_ASSERT(xFrame
.is());
91 const uno::Reference
<uno::XComponentContext
>& xContext
92 = ::comphelper::getProcessComponentContext();
93 uno::Reference
<frame::XDispatchHelper
> xDispatchHelper(frame::DispatchHelper::create(xContext
));
94 CPPUNIT_ASSERT(xDispatchHelper
.is());
96 auto ret
= xDispatchHelper
->executeDispatch(xFrame
, rCommand
, OUString(), 0, rPropertyValues
);
97 Scheduler::ProcessEventsToIdle();
104 class StateGetter
: public ::cppu::WeakImplHelper
<frame::XStatusListener
>
107 uno::Any
& m_rOldValue
;
108 bool m_Received
{ false };
109 StateGetter(uno::Any
& rOldValue
)
110 : m_rOldValue(rOldValue
)
114 virtual void SAL_CALL
disposing(lang::EventObject
const&) override
116 CPPUNIT_ASSERT(m_Received
);
118 virtual void SAL_CALL
statusChanged(frame::FeatureStateEvent
const& rEvent
) override
122 m_rOldValue
= rEvent
.State
;
130 uno::Any
MacrosTest::queryDispatchStatus(uno::Reference
<lang::XComponent
> const& xComponent
,
131 uno::Reference
<uno::XComponentContext
> const& xContext
,
132 OUString
const& rURL
)
139 uno::Reference
<css::util::XURLTransformer
> const xParser(
140 css::util::URLTransformer::create(xContext
));
141 CPPUNIT_ASSERT(xParser
.is());
142 xParser
->parseStrict(url
);
145 uno::Reference
<frame::XController
> const xController
146 = uno::Reference
<frame::XModel
>(xComponent
, uno::UNO_QUERY_THROW
)->getCurrentController();
147 uno::Reference
<frame::XDispatchProvider
> const xFrame(xController
->getFrame(), uno::UNO_QUERY
);
148 CPPUNIT_ASSERT(xFrame
.is());
149 uno::Reference
<frame::XDispatch
> const xDisp(xFrame
->queryDispatch(url
, "", 0));
150 CPPUNIT_ASSERT(xDisp
.is());
152 uno::Reference
<frame::XStatusListener
> const xListener
{ new StateGetter(ret
) };
153 xDisp
->addStatusListener(xListener
, url
);
158 std::unique_ptr
<SvStream
> MacrosTest::parseExportStream(const OUString
& url
,
159 const OUString
& rStreamName
)
161 const uno::Reference
<uno::XComponentContext
>& xComponentContext
162 = comphelper::getProcessComponentContext();
163 uno::Reference
<packages::zip::XZipFileAccess2
> const xZipNames(
164 packages::zip::ZipFileAccess::createWithURL(xComponentContext
, url
));
165 uno::Reference
<io::XInputStream
> const xInputStream(xZipNames
->getByName(rStreamName
),
167 std::unique_ptr
<SvStream
> pStream(utl::UcbStreamHelper::CreateStream(xInputStream
, true));
171 void MacrosTest::setUpX509(const test::Directories
& rDirectories
, const OUString
& rTestName
)
173 static bool isDone
{ false };
174 if (isDone
) // must only be done once on MacOSX - see below!
180 OUString aSourceDir
= rDirectories
.getURLFromSrc(u
"/test/signing-keys/");
182 = rDirectories
.getURLFromWorkdir(Concat2View("CppunitTest/" + rTestName
+ ".test.user"));
184 OUString aTargetPath
;
185 osl::FileBase::getSystemPathFromFileURL(aTargetDir
, aTargetPath
);
188 // CryptoAPI test certificates
189 osl::File::copy(aSourceDir
+ "test.p7b", aTargetDir
+ "/test.p7b");
190 OUString
caVar("LIBO_TEST_CRYPTOAPI_PKCS7");
191 osl_setEnvironment(caVar
.pData
, aTargetPath
.pData
);
193 // Set up NSS database in workdir/CppunitTest/
194 // WARNING: on MacOSX, this *must only be done once* - once NSS has opened
195 // the files, SQLite will *stop using them* if they are overwritten or renamed!
196 osl::File::copy(aSourceDir
+ "cert9.db", aTargetDir
+ "/cert9.db");
197 osl::File::copy(aSourceDir
+ "key4.db", aTargetDir
+ "/key4.db");
198 osl::File::copy(aSourceDir
+ "pkcs11.txt", aTargetDir
+ "/pkcs11.txt");
200 OUString
mozCertVar(u
"MOZILLA_CERTIFICATE_FOLDER"_ustr
);
201 // explicit prefix with "sql:" needed for CentOS7 system NSS 3.67
202 osl_setEnvironment(mozCertVar
.pData
, OUString("sql:" + aTargetPath
).pData
);
206 #if HAVE_GPGCONF_SOCKETDIR
207 // mutable global should be tolerable in test lib
208 static OString g_gpgconfCommandPrefix
;
214 void test_init_gpg(OUString
const& rTargetDir
)
216 const char* pSrcRoot
= getenv("SRC_ROOT");
221 OUString
const srcRootPath(OUString(pSrcRoot
, strlen(pSrcRoot
), osl_getThreadTextEncoding()));
222 OUString
const sourcePath(srcRootPath
+ "/test/signing-keys/");
224 osl::FileBase::RC e
= osl::FileBase::getFileURLFromSystemPath(sourcePath
, aSourceDir
);
225 if (osl::FileBase::E_None
!= e
)
230 OUString aTargetPath
;
231 osl::FileBase::getSystemPathFromFileURL(rTargetDir
, aTargetPath
);
233 auto const rc
= osl::Directory::create(rTargetDir
);
234 if (osl::FileBase::E_None
!= rc
&& osl::FileBase::E_EXIST
!= rc
)
236 SAL_WARN("test", "creating target dir failed, aborting");
240 // Make gpg use our own defined setup & keys
241 if (osl::FileBase::E_None
242 != osl::File::copy(aSourceDir
+ "pubring.gpg", rTargetDir
+ "/pubring.gpg")
243 || osl::FileBase::E_None
244 != osl::File::copy(aSourceDir
+ "random_seed", rTargetDir
+ "/random_seed")
245 || osl::FileBase::E_None
246 != osl::File::copy(aSourceDir
+ "secring.gpg", rTargetDir
+ "/secring.gpg")
247 || osl::FileBase::E_None
248 != osl::File::copy(aSourceDir
+ "trustdb.gpg", rTargetDir
+ "/trustdb.gpg"))
250 SAL_WARN("test", "copying files failed, aborting");
254 // note: this doesn't work for UITest because "os.environ" is a copy :(
255 OUString
gpgHomeVar(u
"GNUPGHOME"_ustr
);
256 osl_setEnvironment(gpgHomeVar
.pData
, aTargetPath
.pData
);
258 #if HAVE_GPGCONF_SOCKETDIR
259 auto const ldPath
= std::getenv("LIBO_LD_PATH");
260 g_gpgconfCommandPrefix
261 = ldPath
== nullptr ? OString() : OString::Concat("LD_LIBRARY_PATH=") + ldPath
+ " ";
263 bool ok
= aTargetPath
.convertToString(&path
, osl_getThreadTextEncoding(),
264 RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
265 | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR
);
266 // if conversion fails, at least provide a best-effort conversion in the message here, for
270 SAL_WARN("test", "converting path failed, aborting: " << aTargetPath
);
273 g_gpgconfCommandPrefix
+= "GNUPGHOME=" + path
+ " " GPGME_GPGCONF
;
274 // HAVE_GPGCONF_SOCKETDIR is only defined in configure.ac for Linux for now, so (a) std::system
275 // behavior will conform to POSIX (and the relevant env var to set is named LD_LIBRARY_PATH), and
276 // (b) gpgconf --create-socketdir should return zero:
277 OString cmd
= g_gpgconfCommandPrefix
+ " --create-socketdir";
278 int res
= std::system(cmd
.getStr());
281 SAL_WARN("test", "invoking gpgconf failed, aborting: " << cmd
);
289 SAL_DLLPUBLIC_EXPORT
void test_deinit_gpg()
291 #if HAVE_GPGCONF_SOCKETDIR
292 // HAVE_GPGCONF_SOCKETDIR is only defined in configure.ac for Linux for now, so (a) std::system
293 // behavior will conform to POSIX, and (b) gpgconf --remove-socketdir should return zero:
294 CPPUNIT_ASSERT(!g_gpgconfCommandPrefix
.isEmpty());
295 OString cmd
= g_gpgconfCommandPrefix
+ " --remove-socketdir";
296 int res
= std::system(cmd
.getStr());
297 CPPUNIT_ASSERT_EQUAL_MESSAGE(cmd
.getStr(), 0, res
);
298 g_gpgconfCommandPrefix
.clear();
304 void MacrosTest::setUpGpg(const test::Directories
& rDirectories
,
305 std::u16string_view
const rTestName
)
307 OUString aTargetDir
= rDirectories
.getURLFromWorkdir(
308 Concat2View("CppunitTest/" + OUString(rTestName
.data(), rTestName
.size()) + ".test.user"));
310 return test_init_gpg(aTargetDir
);
313 void MacrosTest::tearDownGpg() { return test_deinit_gpg(); }
320 OUString subjectName
;
321 const css::uno::Reference
<css::xml::crypto::XSecurityEnvironment
>& env
;
322 Valid(const css::uno::Sequence
<css::beans::PropertyValue
>& rFilterData
,
323 const css::uno::Reference
<css::xml::crypto::XSecurityEnvironment
>& rEnv
)
324 : now(DateTime::SYSTEM
)
327 for (const auto& propVal
: rFilterData
)
329 if (propVal
.Name
== "SignCertificateSubjectName")
330 propVal
.Value
>>= subjectName
;
333 bool operator()(const css::uno::Reference
<css::security::XCertificate
>& cert
) const
335 if (!now
.IsBetween(DateTime(cert
->getNotValidBefore()), DateTime(cert
->getNotValidAfter())))
337 if (!subjectName
.isEmpty() && subjectName
!= cert
->getSubjectName())
339 if (env
->verifyCertificate(cert
, {}) != css::security::CertificateValidity::VALID
)
346 bool MacrosTest::IsValid(const css::uno::Reference
<css::security::XCertificate
>& cert
,
347 const css::uno::Reference
<css::xml::crypto::XSecurityEnvironment
>& env
)
349 const Valid
test({}, env
);
353 css::uno::Reference
<css::security::XCertificate
> MacrosTest::GetValidCertificate(
354 const css::uno::Sequence
<css::uno::Reference
<css::security::XCertificate
>>& certs
,
355 const css::uno::Reference
<css::xml::crypto::XSecurityEnvironment
>& env
,
356 const css::uno::Sequence
<css::beans::PropertyValue
>& rFilterData
)
358 if (auto it
= std::find_if(certs
.begin(), certs
.end(), Valid(rFilterData
, env
));
365 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */