Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / desktop / source / deployment / registry / dp_backend.cxx
blob5c7b9bd1f2e3b8f5da044b76840d4004017e310e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 <sal/config.h>
22 #include <cassert>
24 #include <dp_backend.h>
25 #include <dp_ucb.h>
26 #include <rtl/ustring.hxx>
27 #include <rtl/uri.hxx>
28 #include <rtl/bootstrap.hxx>
29 #include <sal/log.hxx>
30 #include <osl/file.hxx>
31 #include <cppuhelper/exc_hlp.hxx>
32 #include <comphelper/unwrapargs.hxx>
33 #include <ucbhelper/content.hxx>
34 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
35 #include <com/sun/star/deployment/DeploymentException.hpp>
36 #include <com/sun/star/deployment/ExtensionRemovedException.hpp>
37 #include <com/sun/star/deployment/InvalidRemovedParameterException.hpp>
38 #include <com/sun/star/deployment/thePackageManagerFactory.hpp>
39 #include <com/sun/star/ucb/ContentCreationException.hpp>
40 #include <com/sun/star/ucb/CommandAbortedException.hpp>
41 #include <com/sun/star/ucb/CommandFailedException.hpp>
42 #include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp>
43 #include <com/sun/star/ucb/IOErrorCode.hpp>
44 #include <com/sun/star/beans/StringPair.hpp>
45 #include <com/sun/star/sdbc/XResultSet.hpp>
46 #include <com/sun/star/sdbc/XRow.hpp>
47 #include <tools/diagnose_ex.h>
48 #include <unotools/tempfile.hxx>
49 #include <boost/optional.hpp>
51 using namespace ::dp_misc;
52 using namespace ::com::sun::star;
53 using namespace ::com::sun::star::uno;
54 using namespace ::com::sun::star::ucb;
56 namespace dp_registry {
57 namespace backend {
60 PackageRegistryBackend::~PackageRegistryBackend()
65 void PackageRegistryBackend::disposing( lang::EventObject const & event )
67 Reference<deployment::XPackage> xPackage(
68 event.Source, UNO_QUERY_THROW );
69 OUString url( xPackage->getURL() );
70 ::osl::MutexGuard guard( getMutex() );
71 if ( m_bound.erase( url ) != 1 )
73 SAL_WARN("desktop.deployment", "erase(" << url << ") != 1");
78 PackageRegistryBackend::PackageRegistryBackend(
79 Sequence<Any> const & args,
80 Reference<XComponentContext> const & xContext )
81 : t_BackendBase( getMutex() ),
82 m_xComponentContext( xContext ),
83 m_eContext( Context::Unknown )
85 assert(xContext.is());
86 boost::optional<OUString> cachePath;
87 boost::optional<bool> readOnly;
88 comphelper::unwrapArgs( args, m_context, cachePath, readOnly );
89 if (cachePath)
90 m_cachePath = *cachePath;
92 if ( m_context == "user" )
93 m_eContext = Context::User;
94 else if ( m_context == "shared" )
95 m_eContext = Context::Shared;
96 else if ( m_context == "bundled" )
97 m_eContext = Context::Bundled;
98 else if ( m_context == "tmp" )
99 m_eContext = Context::Tmp;
100 else if (m_context.matchIgnoreAsciiCase("vnd.sun.star.tdoc:/"))
101 m_eContext = Context::Document;
102 else
103 m_eContext = Context::Unknown;
107 void PackageRegistryBackend::check()
109 ::osl::MutexGuard guard( getMutex() );
110 if (rBHelper.bInDispose || rBHelper.bDisposed) {
111 throw lang::DisposedException(
112 "PackageRegistryBackend instance has already been disposed!",
113 static_cast<OWeakObject *>(this) );
118 void PackageRegistryBackend::disposing()
120 try {
121 for (auto const& elem : m_bound)
122 elem.second->removeEventListener(this);
123 m_bound.clear();
124 m_xComponentContext.clear();
125 WeakComponentImplHelperBase::disposing();
127 catch (const RuntimeException &) {
128 throw;
130 catch (const Exception &) {
131 Any exc( ::cppu::getCaughtException() );
132 throw lang::WrappedTargetRuntimeException(
133 "caught unexpected exception while disposing!",
134 static_cast<OWeakObject *>(this), exc );
138 // XPackageRegistry
140 Reference<deployment::XPackage> PackageRegistryBackend::bindPackage(
141 OUString const & url, OUString const & mediaType, sal_Bool bRemoved,
142 OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv )
144 ::osl::ResettableMutexGuard guard( getMutex() );
145 check();
147 t_string2ref::const_iterator const iFind( m_bound.find( url ) );
148 if (iFind != m_bound.end())
150 Reference<deployment::XPackage> xPackage( iFind->second );
151 if (xPackage.is())
153 if (!mediaType.isEmpty() &&
154 mediaType != xPackage->getPackageType()->getMediaType())
155 throw lang::IllegalArgumentException
156 ("XPackageRegistry::bindPackage: media type does not match",
157 static_cast<OWeakObject*>(this), 1);
158 if (xPackage->isRemoved() != bRemoved)
159 throw deployment::InvalidRemovedParameterException(
160 "XPackageRegistry::bindPackage: bRemoved parameter does not match",
161 static_cast<OWeakObject*>(this), xPackage->isRemoved(), xPackage);
162 return xPackage;
166 guard.clear();
168 Reference<deployment::XPackage> xNewPackage;
169 try {
170 xNewPackage = bindPackage_( url, mediaType, bRemoved,
171 identifier, xCmdEnv );
173 catch (const RuntimeException &) {
174 throw;
176 catch (const CommandFailedException &) {
177 throw;
179 catch (const deployment::DeploymentException &) {
180 throw;
182 catch (const Exception &) {
183 Any exc( ::cppu::getCaughtException() );
184 throw deployment::DeploymentException(
185 "Error binding package: " + url,
186 static_cast<OWeakObject *>(this), exc );
189 guard.reset();
191 std::pair< t_string2ref::iterator, bool > insertion(
192 m_bound.emplace( url, xNewPackage ) );
193 if (insertion.second)
194 { // first insertion
195 SAL_WARN_IF(
196 Reference<XInterface>(insertion.first->second) != xNewPackage,
197 "desktop.deployment", "mismatch");
199 else
200 { // found existing entry
201 Reference<deployment::XPackage> xPackage( insertion.first->second );
202 if (xPackage.is())
203 return xPackage;
204 insertion.first->second = xNewPackage;
207 guard.clear();
208 xNewPackage->addEventListener( this ); // listen for disposing events
209 return xNewPackage;
212 OUString PackageRegistryBackend::createFolder(
213 Reference<ucb::XCommandEnvironment> const & xCmdEnv)
215 const OUString sDataFolder = makeURL(getCachePath(), OUString());
216 //make sure the folder exist
217 ucbhelper::Content dataContent;
218 ::dp_misc::create_folder(&dataContent, sDataFolder, xCmdEnv);
220 const OUString baseDir(sDataFolder);
221 ::utl::TempFile aTemp(&baseDir, true);
222 const OUString& url = aTemp.GetURL();
223 return sDataFolder + url.copy(url.lastIndexOf('/'));
226 //folderURL can have the extension .tmp or .tmp_
227 //Before OOo 3.4 the created a tmp file with osl_createTempFile and
228 //then created a Folder with a same name and a trailing '_'
229 //If the folderURL has no '_' then there is no corresponding tmp file.
230 void PackageRegistryBackend::deleteTempFolder(
231 OUString const & folderUrl)
233 if (!folderUrl.isEmpty())
235 erase_path( folderUrl, Reference<XCommandEnvironment>(),
236 false /* no throw: ignore errors */ );
238 if (folderUrl.endsWith("_"))
240 const OUString tempFile = folderUrl.copy(0, folderUrl.getLength() - 1);
241 erase_path( tempFile, Reference<XCommandEnvironment>(),
242 false /* no throw: ignore errors */ );
247 //usedFolders can contain folder names which have the extension .tmp or .tmp_
248 //Before OOo 3.4 we created a tmp file with osl_createTempFile and
249 //then created a Folder with a same name and a trailing '_'
250 //If the folderURL has no '_' then there is no corresponding tmp file.
251 void PackageRegistryBackend::deleteUnusedFolders(
252 std::vector< OUString> const & usedFolders)
256 const OUString sDataFolder = makeURL(getCachePath(), OUString());
257 ::ucbhelper::Content tempFolder(
258 sDataFolder, Reference<ucb::XCommandEnvironment>(), m_xComponentContext);
260 Reference<sdbc::XResultSet> xResultSet(
261 StrTitle::createCursor( tempFolder, ::ucbhelper::INCLUDE_FOLDERS_ONLY ) );
263 // get all temp directories:
264 std::vector<OUString> tempEntries;
266 while (xResultSet->next())
268 OUString title(
269 Reference<sdbc::XRow>(
270 xResultSet, UNO_QUERY_THROW )->getString(
271 1 /* Title */ ) );
273 if (title.endsWith(".tmp"))
274 tempEntries.push_back(
275 makeURLAppendSysPathSegment(sDataFolder, title));
278 for (const OUString & tempEntrie : tempEntries)
280 if (std::find( usedFolders.begin(), usedFolders.end(), tempEntrie ) ==
281 usedFolders.end())
283 deleteTempFolder(tempEntrie);
287 catch (const ucb::InteractiveAugmentedIOException& e)
289 //In case the folder containing all the data folder does not
290 //exist yet, we ignore the exception
291 if (e.Code != ucb::IOErrorCode_NOT_EXISTING)
292 throw;
298 Package::~Package()
303 Package::Package( ::rtl::Reference<PackageRegistryBackend> const & myBackend,
304 OUString const & url,
305 OUString const & rName,
306 OUString const & displayName,
307 Reference<deployment::XPackageTypeInfo> const & xPackageType,
308 bool bRemoved,
309 OUString const & identifier)
310 : t_PackageBase( getMutex() ),
311 m_myBackend( myBackend ),
312 m_url( url ),
313 m_name( rName ),
314 m_displayName( displayName ),
315 m_xPackageType( xPackageType ),
316 m_bRemoved(bRemoved),
317 m_identifier(identifier)
319 if (m_bRemoved)
321 //We use the last segment of the URL
322 SAL_WARN_IF(
323 !m_name.isEmpty(), "desktop.deployment", "non-empty m_name");
324 OUString name = m_url;
325 rtl::Bootstrap::expandMacros(name);
326 sal_Int32 index = name.lastIndexOf('/');
327 if (index != -1 && index < name.getLength())
328 m_name = name.copy(index + 1);
333 void Package::disposing()
335 m_myBackend.clear();
336 WeakComponentImplHelperBase::disposing();
340 void Package::check() const
342 ::osl::MutexGuard guard( getMutex() );
343 if (rBHelper.bInDispose || rBHelper.bDisposed) {
344 throw lang::DisposedException(
345 "Package instance has already been disposed!",
346 static_cast<OWeakObject *>(const_cast<Package *>(this)));
350 // XComponent
352 void Package::dispose()
354 //Do not call check here. We must not throw an exception here if the object
355 //is being disposed or is already disposed. See com.sun.star.lang.XComponent
356 WeakComponentImplHelperBase::dispose();
360 void Package::addEventListener(
361 Reference<lang::XEventListener> const & xListener )
363 //Do not call check here. We must not throw an exception here if the object
364 //is being disposed or is already disposed. See com.sun.star.lang.XComponent
365 WeakComponentImplHelperBase::addEventListener( xListener );
369 void Package::removeEventListener(
370 Reference<lang::XEventListener> const & xListener )
372 //Do not call check here. We must not throw an exception here if the object
373 //is being disposed or is already disposed. See com.sun.star.lang.XComponent
374 WeakComponentImplHelperBase::removeEventListener( xListener );
377 // XModifyBroadcaster
379 void Package::addModifyListener(
380 Reference<util::XModifyListener> const & xListener )
382 check();
383 rBHelper.addListener( cppu::UnoType<decltype(xListener)>::get(), xListener );
387 void Package::removeModifyListener(
388 Reference<util::XModifyListener> const & xListener )
390 check();
391 rBHelper.removeListener( cppu::UnoType<decltype(xListener)>::get(), xListener );
395 void Package::checkAborted(
396 ::rtl::Reference<AbortChannel> const & abortChannel )
398 if (abortChannel.is() && abortChannel->isAborted()) {
399 throw CommandAbortedException(
400 "abort!", static_cast<OWeakObject *>(this) );
404 // XPackage
406 Reference<task::XAbortChannel> Package::createAbortChannel()
408 check();
409 return new AbortChannel;
413 sal_Bool Package::isBundle()
415 return false; // default
419 ::sal_Int32 Package::checkPrerequisites(
420 const css::uno::Reference< css::task::XAbortChannel >&,
421 const css::uno::Reference< css::ucb::XCommandEnvironment >&,
422 sal_Bool)
424 if (m_bRemoved)
425 throw deployment::ExtensionRemovedException();
426 return 0;
430 sal_Bool Package::checkDependencies(
431 const css::uno::Reference< css::ucb::XCommandEnvironment >& )
433 if (m_bRemoved)
434 throw deployment::ExtensionRemovedException();
435 return true;
439 Sequence< Reference<deployment::XPackage> > Package::getBundle(
440 Reference<task::XAbortChannel> const &,
441 Reference<XCommandEnvironment> const & )
443 return Sequence< Reference<deployment::XPackage> >();
447 OUString Package::getName()
449 return m_name;
452 beans::Optional<OUString> Package::getIdentifier()
454 if (m_bRemoved)
455 return beans::Optional<OUString>(true, m_identifier);
457 return beans::Optional<OUString>();
461 OUString Package::getVersion()
463 if (m_bRemoved)
464 throw deployment::ExtensionRemovedException();
465 return OUString();
469 OUString Package::getURL()
471 return m_url;
475 OUString Package::getDisplayName()
477 if (m_bRemoved)
478 throw deployment::ExtensionRemovedException();
479 return m_displayName;
483 OUString Package::getDescription()
485 if (m_bRemoved)
486 throw deployment::ExtensionRemovedException();
487 return OUString();
491 OUString Package::getLicenseText()
493 if (m_bRemoved)
494 throw deployment::ExtensionRemovedException();
495 return OUString();
499 Sequence<OUString> Package::getUpdateInformationURLs()
501 if (m_bRemoved)
502 throw deployment::ExtensionRemovedException();
503 return Sequence<OUString>();
507 css::beans::StringPair Package::getPublisherInfo()
509 if (m_bRemoved)
510 throw deployment::ExtensionRemovedException();
511 css::beans::StringPair aEmptyPair;
512 return aEmptyPair;
516 uno::Reference< css::graphic::XGraphic > Package::getIcon( sal_Bool /*bHighContrast*/ )
518 if (m_bRemoved)
519 throw deployment::ExtensionRemovedException();
521 uno::Reference< css::graphic::XGraphic > aEmpty;
522 return aEmpty;
526 Reference<deployment::XPackageTypeInfo> Package::getPackageType()
528 return m_xPackageType;
531 void Package::exportTo(
532 OUString const & destFolderURL, OUString const & newTitle,
533 sal_Int32 nameClashAction, Reference<XCommandEnvironment> const & xCmdEnv )
535 if (m_bRemoved)
536 throw deployment::ExtensionRemovedException();
538 ::ucbhelper::Content destFolder( destFolderURL, xCmdEnv, getMyBackend()->getComponentContext() );
539 ::ucbhelper::Content sourceContent( getURL(), xCmdEnv, getMyBackend()->getComponentContext() );
540 bool bOk=true;
543 destFolder.transferContent(
544 sourceContent, ::ucbhelper::InsertOperation::Copy,
545 newTitle, nameClashAction);
547 catch (const css::ucb::ContentCreationException&)
549 bOk = false;
552 if (!bOk)
553 throw RuntimeException( "UCB transferContent() failed!", nullptr );
556 void Package::fireModified()
558 ::cppu::OInterfaceContainerHelper * container = rBHelper.getContainer(
559 cppu::UnoType<util::XModifyListener>::get() );
560 if (container != nullptr) {
561 Sequence< Reference<XInterface> > elements(
562 container->getElements() );
563 lang::EventObject evt( static_cast<OWeakObject *>(this) );
564 for ( sal_Int32 pos = 0; pos < elements.getLength(); ++pos )
566 Reference<util::XModifyListener> xListener(
567 elements[ pos ], UNO_QUERY );
568 if (xListener.is())
569 xListener->modified( evt );
574 // XPackage
576 beans::Optional< beans::Ambiguous<sal_Bool> > Package::isRegistered(
577 Reference<task::XAbortChannel> const & xAbortChannel,
578 Reference<XCommandEnvironment> const & xCmdEnv )
580 try {
581 ::osl::ResettableMutexGuard guard( getMutex() );
582 return isRegistered_( guard,
583 AbortChannel::get(xAbortChannel),
584 xCmdEnv );
586 catch (const RuntimeException &) {
587 throw;
589 catch (const CommandFailedException &) {
590 throw;
592 catch (const CommandAbortedException &) {
593 throw;
595 catch (const deployment::DeploymentException &) {
596 throw;
598 catch (const Exception & e) {
599 Any exc( ::cppu::getCaughtException() );
600 throw deployment::DeploymentException(
601 "unexpected " + exc.getValueTypeName() + ": " + e.Message,
602 static_cast<OWeakObject *>(this), exc );
607 void Package::processPackage_impl(
608 bool doRegisterPackage,
609 bool startup,
610 Reference<task::XAbortChannel> const & xAbortChannel,
611 Reference<XCommandEnvironment> const & xCmdEnv )
613 check();
614 bool action = false;
616 try {
617 try {
618 ::osl::ResettableMutexGuard guard( getMutex() );
619 beans::Optional< beans::Ambiguous<sal_Bool> > option(
620 isRegistered_( guard, AbortChannel::get(xAbortChannel),
621 xCmdEnv ) );
622 action = (option.IsPresent &&
623 (option.Value.IsAmbiguous ||
624 (doRegisterPackage ? !option.Value.Value
625 : option.Value.Value)));
626 if (action) {
628 OUString displayName = isRemoved() ? getName() : getDisplayName();
629 ProgressLevel progress(
630 xCmdEnv,
631 (doRegisterPackage
632 ? PackageRegistryBackend::StrRegisteringPackage()
633 : PackageRegistryBackend::StrRevokingPackage())
634 + displayName );
635 processPackage_( guard,
636 doRegisterPackage,
637 startup,
638 AbortChannel::get(xAbortChannel),
639 xCmdEnv );
642 catch (lang::IllegalArgumentException &) {
643 Any e(cppu::getCaughtException());
644 throw deployment::DeploymentException(
645 ((doRegisterPackage
646 ? DpResId(RID_STR_ERROR_WHILE_REGISTERING)
647 : DpResId(RID_STR_ERROR_WHILE_REVOKING))
648 + getDisplayName()),
649 static_cast< OWeakObject * >(this), e);
651 catch (const RuntimeException &) {
652 TOOLS_WARN_EXCEPTION("desktop.deployment", "unexpected");
653 throw;
655 catch (const CommandFailedException &) {
656 throw;
658 catch (const CommandAbortedException &) {
659 throw;
661 catch (const deployment::DeploymentException &) {
662 throw;
664 catch (const Exception &) {
665 Any exc( ::cppu::getCaughtException() );
666 throw deployment::DeploymentException(
667 (doRegisterPackage
668 ? DpResId(RID_STR_ERROR_WHILE_REGISTERING)
669 : DpResId(RID_STR_ERROR_WHILE_REVOKING))
670 + getDisplayName(), static_cast<OWeakObject *>(this), exc );
673 catch (...) {
674 if (action)
675 fireModified();
676 throw;
678 if (action)
679 fireModified();
683 void Package::registerPackage(
684 sal_Bool startup,
685 Reference<task::XAbortChannel> const & xAbortChannel,
686 Reference<XCommandEnvironment> const & xCmdEnv )
688 if (m_bRemoved)
689 throw deployment::ExtensionRemovedException();
690 processPackage_impl( true /* register */, startup, xAbortChannel, xCmdEnv );
694 void Package::revokePackage(
695 sal_Bool startup,
696 Reference<task::XAbortChannel> const & xAbortChannel,
697 Reference<XCommandEnvironment> const & xCmdEnv )
699 processPackage_impl( false /* revoke */, startup, xAbortChannel, xCmdEnv );
703 PackageRegistryBackend * Package::getMyBackend() const
705 PackageRegistryBackend * pBackend = m_myBackend.get();
706 if (nullptr == pBackend)
708 //May throw a DisposedException
709 check();
710 //We should never get here...
711 throw RuntimeException(
712 "Failed to get the BackendImpl",
713 static_cast<OWeakObject*>(const_cast<Package *>(this)));
715 return pBackend;
718 OUString Package::getRepositoryName()
720 PackageRegistryBackend * backEnd = getMyBackend();
721 return backEnd->getContext();
724 beans::Optional< OUString > Package::getRegistrationDataURL()
726 if (m_bRemoved)
727 throw deployment::ExtensionRemovedException();
728 return beans::Optional<OUString>();
731 sal_Bool Package::isRemoved()
733 return m_bRemoved;
736 Package::TypeInfo::~TypeInfo()
740 // XPackageTypeInfo
742 OUString Package::TypeInfo::getMediaType()
744 return m_mediaType;
748 OUString Package::TypeInfo::getDescription()
750 return getShortDescription();
754 OUString Package::TypeInfo::getShortDescription()
756 return m_shortDescr;
759 OUString Package::TypeInfo::getFileFilter()
761 return m_fileFilter;
764 Any Package::TypeInfo::getIcon( sal_Bool /*highContrast*/, sal_Bool /*smallIcon*/ )
766 return Any();
772 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */