Bump version to 6.0-36
[LibreOffice.git] / desktop / source / deployment / registry / dp_backend.cxx
blob46be07058768cd50b483376a9f985116f80ad079
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/servicedecl.hxx>
33 #include <comphelper/unwrapargs.hxx>
34 #include <ucbhelper/content.hxx>
35 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
36 #include <com/sun/star/deployment/DeploymentException.hpp>
37 #include <com/sun/star/deployment/ExtensionRemovedException.hpp>
38 #include <com/sun/star/deployment/InvalidRemovedParameterException.hpp>
39 #include <com/sun/star/deployment/thePackageManagerFactory.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 <unotools/tempfile.hxx>
50 using namespace ::dp_misc;
51 using namespace ::com::sun::star;
52 using namespace ::com::sun::star::uno;
53 using namespace ::com::sun::star::ucb;
55 namespace dp_registry {
56 namespace backend {
59 PackageRegistryBackend::~PackageRegistryBackend()
64 void PackageRegistryBackend::disposing( lang::EventObject const & event )
66 Reference<deployment::XPackage> xPackage(
67 event.Source, UNO_QUERY_THROW );
68 OUString url( xPackage->getURL() );
69 ::osl::MutexGuard guard( getMutex() );
70 if ( m_bound.erase( url ) != 1 )
72 SAL_WARN("desktop.deployment", "erase(" << url << ") != 1");
77 PackageRegistryBackend::PackageRegistryBackend(
78 Sequence<Any> const & args,
79 Reference<XComponentContext> const & xContext )
80 : t_BackendBase( getMutex() ),
81 m_xComponentContext( xContext ),
82 m_eContext( Context::Unknown )
84 assert(xContext.is());
85 boost::optional<OUString> cachePath;
86 boost::optional<bool> readOnly;
87 comphelper::unwrapArgs( args, m_context, cachePath, readOnly );
88 if (cachePath)
89 m_cachePath = *cachePath;
91 if ( m_context == "user" )
92 m_eContext = Context::User;
93 else if ( m_context == "shared" )
94 m_eContext = Context::Shared;
95 else if ( m_context == "bundled" )
96 m_eContext = Context::Bundled;
97 else if ( m_context == "tmp" )
98 m_eContext = Context::Tmp;
99 else if (m_context.matchIgnoreAsciiCase("vnd.sun.star.tdoc:/"))
100 m_eContext = Context::Document;
101 else
102 m_eContext = Context::Unknown;
106 void PackageRegistryBackend::check()
108 ::osl::MutexGuard guard( getMutex() );
109 if (rBHelper.bInDispose || rBHelper.bDisposed) {
110 throw lang::DisposedException(
111 "PackageRegistryBackend instance has already been disposed!",
112 static_cast<OWeakObject *>(this) );
117 void PackageRegistryBackend::disposing()
119 try {
120 for ( t_string2ref::const_iterator i = m_bound.begin(); i != m_bound.end(); ++i)
121 i->second->removeEventListener(this);
122 m_bound.clear();
123 m_xComponentContext.clear();
124 WeakComponentImplHelperBase::disposing();
126 catch (const RuntimeException &) {
127 throw;
129 catch (const Exception &) {
130 Any exc( ::cppu::getCaughtException() );
131 throw lang::WrappedTargetRuntimeException(
132 "caught unexpected exception while disposing!",
133 static_cast<OWeakObject *>(this), exc );
137 // XPackageRegistry
139 Reference<deployment::XPackage> PackageRegistryBackend::bindPackage(
140 OUString const & url, OUString const & mediaType, sal_Bool bRemoved,
141 OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv )
143 ::osl::ResettableMutexGuard guard( getMutex() );
144 check();
146 t_string2ref::const_iterator const iFind( m_bound.find( url ) );
147 if (iFind != m_bound.end())
149 Reference<deployment::XPackage> xPackage( iFind->second );
150 if (xPackage.is())
152 if (!mediaType.isEmpty() &&
153 mediaType != xPackage->getPackageType()->getMediaType())
154 throw lang::IllegalArgumentException
155 ("XPackageRegistry::bindPackage: media type does not match",
156 static_cast<OWeakObject*>(this), 1);
157 if (xPackage->isRemoved() != bRemoved)
158 throw deployment::InvalidRemovedParameterException(
159 "XPackageRegistry::bindPackage: bRemoved parameter does not match",
160 static_cast<OWeakObject*>(this), xPackage->isRemoved(), xPackage);
161 return xPackage;
165 guard.clear();
167 Reference<deployment::XPackage> xNewPackage;
168 try {
169 xNewPackage = bindPackage_( url, mediaType, bRemoved,
170 identifier, xCmdEnv );
172 catch (const RuntimeException &) {
173 throw;
175 catch (const CommandFailedException &) {
176 throw;
178 catch (const deployment::DeploymentException &) {
179 throw;
181 catch (const Exception &) {
182 Any exc( ::cppu::getCaughtException() );
183 throw deployment::DeploymentException(
184 "Error binding package: " + url,
185 static_cast<OWeakObject *>(this), exc );
188 guard.reset();
190 std::pair< t_string2ref::iterator, bool > insertion(
191 m_bound.emplace( url, xNewPackage ) );
192 if (insertion.second)
193 { // first insertion
194 SAL_WARN_IF(
195 Reference<XInterface>(insertion.first->second) != xNewPackage,
196 "desktop.deployment", "mismatch");
198 else
199 { // found existing entry
200 Reference<deployment::XPackage> xPackage( insertion.first->second );
201 if (xPackage.is())
202 return xPackage;
203 insertion.first->second = xNewPackage;
206 guard.clear();
207 xNewPackage->addEventListener( this ); // listen for disposing events
208 return xNewPackage;
211 OUString PackageRegistryBackend::createFolder(
212 Reference<ucb::XCommandEnvironment> const & xCmdEnv)
214 const OUString sDataFolder = makeURL(getCachePath(), OUString());
215 //make sure the folder exist
216 ucbhelper::Content dataContent;
217 ::dp_misc::create_folder(&dataContent, sDataFolder, xCmdEnv);
219 const OUString baseDir(sDataFolder);
220 ::utl::TempFile aTemp(&baseDir, true);
221 const OUString url = aTemp.GetURL();
222 return sDataFolder + url.copy(url.lastIndexOf('/'));
225 //folderURL can have the extension .tmp or .tmp_
226 //Before OOo 3.4 the created a tmp file with osl_createTempFile and
227 //then created a Folder with a same name and a trailing '_'
228 //If the folderURL has no '_' then there is no corresponding tmp file.
229 void PackageRegistryBackend::deleteTempFolder(
230 OUString const & folderUrl)
232 if (!folderUrl.isEmpty())
234 erase_path( folderUrl, Reference<XCommandEnvironment>(),
235 false /* no throw: ignore errors */ );
237 if (folderUrl.endsWith("_"))
239 const OUString tempFile = folderUrl.copy(0, folderUrl.getLength() - 1);
240 erase_path( tempFile, Reference<XCommandEnvironment>(),
241 false /* no throw: ignore errors */ );
246 //usedFolders can contain folder names which have the extension .tmp or .tmp_
247 //Before OOo 3.4 we created a tmp file with osl_createTempFile and
248 //then created a Folder with a same name and a trailing '_'
249 //If the folderURL has no '_' then there is no corresponding tmp file.
250 void PackageRegistryBackend::deleteUnusedFolders(
251 std::vector< OUString> const & usedFolders)
255 const OUString sDataFolder = makeURL(getCachePath(), OUString());
256 ::ucbhelper::Content tempFolder(
257 sDataFolder, Reference<ucb::XCommandEnvironment>(), m_xComponentContext);
259 Reference<sdbc::XResultSet> xResultSet(
260 StrTitle::createCursor( tempFolder, ::ucbhelper::INCLUDE_FOLDERS_ONLY ) );
262 // get all temp directories:
263 std::vector<OUString> tempEntries;
265 while (xResultSet->next())
267 OUString title(
268 Reference<sdbc::XRow>(
269 xResultSet, UNO_QUERY_THROW )->getString(
270 1 /* Title */ ) );
272 if (title.endsWith(".tmp"))
273 tempEntries.push_back(
274 makeURLAppendSysPathSegment(sDataFolder, title));
277 for (OUString & tempEntrie : tempEntries)
279 if (std::find( usedFolders.begin(), usedFolders.end(), tempEntrie ) ==
280 usedFolders.end())
282 deleteTempFolder(tempEntrie);
286 catch (const ucb::InteractiveAugmentedIOException& e)
288 //In case the folder containing all the data folder does not
289 //exist yet, we ignore the exception
290 if (e.Code != ucb::IOErrorCode_NOT_EXISTING)
291 throw;
297 Package::~Package()
302 Package::Package( ::rtl::Reference<PackageRegistryBackend> const & myBackend,
303 OUString const & url,
304 OUString const & rName,
305 OUString const & displayName,
306 Reference<deployment::XPackageTypeInfo> const & xPackageType,
307 bool bRemoved,
308 OUString const & identifier)
309 : t_PackageBase( getMutex() ),
310 m_myBackend( myBackend ),
311 m_url( url ),
312 m_name( rName ),
313 m_displayName( displayName ),
314 m_xPackageType( xPackageType ),
315 m_bRemoved(bRemoved),
316 m_identifier(identifier)
318 if (m_bRemoved)
320 //We use the last segment of the URL
321 SAL_WARN_IF(
322 !m_name.isEmpty(), "desktop.deployment", "non-empty m_name");
323 OUString name = m_url;
324 rtl::Bootstrap::expandMacros(name);
325 sal_Int32 index = name.lastIndexOf('/');
326 if (index != -1 && index < name.getLength())
327 m_name = name.copy(index + 1);
332 void Package::disposing()
334 m_myBackend.clear();
335 WeakComponentImplHelperBase::disposing();
339 void Package::check() const
341 ::osl::MutexGuard guard( getMutex() );
342 if (rBHelper.bInDispose || rBHelper.bDisposed) {
343 throw lang::DisposedException(
344 "Package instance has already been disposed!",
345 static_cast<OWeakObject *>(const_cast<Package *>(this)));
349 // XComponent
351 void Package::dispose()
353 //Do not call check here. We must not throw an exception here if the object
354 //is being disposed or is already disposed. See com.sun.star.lang.XComponent
355 WeakComponentImplHelperBase::dispose();
359 void Package::addEventListener(
360 Reference<lang::XEventListener> const & xListener )
362 //Do not call check here. We must not throw an exception here if the object
363 //is being disposed or is already disposed. See com.sun.star.lang.XComponent
364 WeakComponentImplHelperBase::addEventListener( xListener );
368 void Package::removeEventListener(
369 Reference<lang::XEventListener> const & xListener )
371 //Do not call check here. We must not throw an exception here if the object
372 //is being disposed or is already disposed. See com.sun.star.lang.XComponent
373 WeakComponentImplHelperBase::removeEventListener( xListener );
376 // XModifyBroadcaster
378 void Package::addModifyListener(
379 Reference<util::XModifyListener> const & xListener )
381 check();
382 rBHelper.addListener( cppu::UnoType<decltype(xListener)>::get(), xListener );
386 void Package::removeModifyListener(
387 Reference<util::XModifyListener> const & xListener )
389 check();
390 rBHelper.removeListener( cppu::UnoType<decltype(xListener)>::get(), xListener );
394 void Package::checkAborted(
395 ::rtl::Reference<AbortChannel> const & abortChannel )
397 if (abortChannel.is() && abortChannel->isAborted()) {
398 throw CommandAbortedException(
399 "abort!", static_cast<OWeakObject *>(this) );
403 // XPackage
405 Reference<task::XAbortChannel> Package::createAbortChannel()
407 check();
408 return new AbortChannel;
412 sal_Bool Package::isBundle()
414 return false; // default
418 ::sal_Int32 Package::checkPrerequisites(
419 const css::uno::Reference< css::task::XAbortChannel >&,
420 const css::uno::Reference< css::ucb::XCommandEnvironment >&,
421 sal_Bool)
423 if (m_bRemoved)
424 throw deployment::ExtensionRemovedException();
425 return 0;
429 sal_Bool Package::checkDependencies(
430 const css::uno::Reference< css::ucb::XCommandEnvironment >& )
432 if (m_bRemoved)
433 throw deployment::ExtensionRemovedException();
434 return true;
438 Sequence< Reference<deployment::XPackage> > Package::getBundle(
439 Reference<task::XAbortChannel> const &,
440 Reference<XCommandEnvironment> const & )
442 return Sequence< Reference<deployment::XPackage> >();
446 OUString Package::getName()
448 return m_name;
451 beans::Optional<OUString> Package::getIdentifier()
453 if (m_bRemoved)
454 return beans::Optional<OUString>(true, m_identifier);
456 return beans::Optional<OUString>();
460 OUString Package::getVersion()
462 if (m_bRemoved)
463 throw deployment::ExtensionRemovedException();
464 return OUString();
468 OUString Package::getURL()
470 return m_url;
474 OUString Package::getDisplayName()
476 if (m_bRemoved)
477 throw deployment::ExtensionRemovedException();
478 return m_displayName;
482 OUString Package::getDescription()
484 if (m_bRemoved)
485 throw deployment::ExtensionRemovedException();
486 return OUString();
490 OUString Package::getLicenseText()
492 if (m_bRemoved)
493 throw deployment::ExtensionRemovedException();
494 return OUString();
498 Sequence<OUString> Package::getUpdateInformationURLs()
500 if (m_bRemoved)
501 throw deployment::ExtensionRemovedException();
502 return Sequence<OUString>();
506 css::beans::StringPair Package::getPublisherInfo()
508 if (m_bRemoved)
509 throw deployment::ExtensionRemovedException();
510 css::beans::StringPair aEmptyPair;
511 return aEmptyPair;
515 uno::Reference< css::graphic::XGraphic > Package::getIcon( sal_Bool /*bHighContrast*/ )
517 if (m_bRemoved)
518 throw deployment::ExtensionRemovedException();
520 uno::Reference< css::graphic::XGraphic > aEmpty;
521 return aEmpty;
525 Reference<deployment::XPackageTypeInfo> Package::getPackageType()
527 return m_xPackageType;
530 void Package::exportTo(
531 OUString const & destFolderURL, OUString const & newTitle,
532 sal_Int32 nameClashAction, Reference<XCommandEnvironment> const & xCmdEnv )
534 if (m_bRemoved)
535 throw deployment::ExtensionRemovedException();
537 ::ucbhelper::Content destFolder( destFolderURL, xCmdEnv, getMyBackend()->getComponentContext() );
538 ::ucbhelper::Content sourceContent( getURL(), xCmdEnv, getMyBackend()->getComponentContext() );
539 bool bOk=true;
542 bOk = destFolder.transferContent(
543 sourceContent, ::ucbhelper::InsertOperation::Copy,
544 newTitle, nameClashAction);
546 catch (const css::ucb::ContentCreationException&)
548 bOk = false;
551 if (!bOk)
552 throw RuntimeException( "UCB transferContent() failed!", nullptr );
555 void Package::fireModified()
557 ::cppu::OInterfaceContainerHelper * container = rBHelper.getContainer(
558 cppu::UnoType<util::XModifyListener>::get() );
559 if (container != nullptr) {
560 Sequence< Reference<XInterface> > elements(
561 container->getElements() );
562 lang::EventObject evt( static_cast<OWeakObject *>(this) );
563 for ( sal_Int32 pos = 0; pos < elements.getLength(); ++pos )
565 Reference<util::XModifyListener> xListener(
566 elements[ pos ], UNO_QUERY );
567 if (xListener.is())
568 xListener->modified( evt );
573 // XPackage
575 beans::Optional< beans::Ambiguous<sal_Bool> > Package::isRegistered(
576 Reference<task::XAbortChannel> const & xAbortChannel,
577 Reference<XCommandEnvironment> const & xCmdEnv )
579 try {
580 ::osl::ResettableMutexGuard guard( getMutex() );
581 return isRegistered_( guard,
582 AbortChannel::get(xAbortChannel),
583 xCmdEnv );
585 catch (const RuntimeException &) {
586 throw;
588 catch (const CommandFailedException &) {
589 throw;
591 catch (const CommandAbortedException &) {
592 throw;
594 catch (const deployment::DeploymentException &) {
595 throw;
597 catch (const Exception & e) {
598 Any exc( ::cppu::getCaughtException() );
599 throw deployment::DeploymentException(
600 "unexpected " + exc.getValueTypeName() + ": " + e.Message,
601 static_cast<OWeakObject *>(this), exc );
606 void Package::processPackage_impl(
607 bool doRegisterPackage,
608 bool startup,
609 Reference<task::XAbortChannel> const & xAbortChannel,
610 Reference<XCommandEnvironment> const & xCmdEnv )
612 check();
613 bool action = false;
615 try {
616 try {
617 ::osl::ResettableMutexGuard guard( getMutex() );
618 beans::Optional< beans::Ambiguous<sal_Bool> > option(
619 isRegistered_( guard, AbortChannel::get(xAbortChannel),
620 xCmdEnv ) );
621 action = (option.IsPresent &&
622 (option.Value.IsAmbiguous ||
623 (doRegisterPackage ? !option.Value.Value
624 : option.Value.Value)));
625 if (action) {
627 OUString displayName = isRemoved() ? getName() : getDisplayName();
628 ProgressLevel progress(
629 xCmdEnv,
630 (doRegisterPackage
631 ? PackageRegistryBackend::StrRegisteringPackage()
632 : PackageRegistryBackend::StrRevokingPackage())
633 + displayName );
634 processPackage_( guard,
635 doRegisterPackage,
636 startup,
637 AbortChannel::get(xAbortChannel),
638 xCmdEnv );
641 catch (lang::IllegalArgumentException &) {
642 Any e(cppu::getCaughtException());
643 throw deployment::DeploymentException(
644 ((doRegisterPackage
645 ? DpResId(RID_STR_ERROR_WHILE_REGISTERING)
646 : DpResId(RID_STR_ERROR_WHILE_REVOKING))
647 + getDisplayName()),
648 static_cast< OWeakObject * >(this), e);
650 catch (const RuntimeException &e) {
651 SAL_WARN("desktop.deployment", "unexpected " << e);
652 throw;
654 catch (const CommandFailedException &) {
655 throw;
657 catch (const CommandAbortedException &) {
658 throw;
660 catch (const deployment::DeploymentException &) {
661 throw;
663 catch (const Exception &) {
664 Any exc( ::cppu::getCaughtException() );
665 throw deployment::DeploymentException(
666 (doRegisterPackage
667 ? DpResId(RID_STR_ERROR_WHILE_REGISTERING)
668 : DpResId(RID_STR_ERROR_WHILE_REVOKING))
669 + getDisplayName(), static_cast<OWeakObject *>(this), exc );
672 catch (...) {
673 if (action)
674 fireModified();
675 throw;
677 if (action)
678 fireModified();
682 void Package::registerPackage(
683 sal_Bool startup,
684 Reference<task::XAbortChannel> const & xAbortChannel,
685 Reference<XCommandEnvironment> const & xCmdEnv )
687 if (m_bRemoved)
688 throw deployment::ExtensionRemovedException();
689 processPackage_impl( true /* register */, startup, xAbortChannel, xCmdEnv );
693 void Package::revokePackage(
694 sal_Bool startup,
695 Reference<task::XAbortChannel> const & xAbortChannel,
696 Reference<XCommandEnvironment> const & xCmdEnv )
698 processPackage_impl( false /* revoke */, startup, xAbortChannel, xCmdEnv );
702 PackageRegistryBackend * Package::getMyBackend() const
704 PackageRegistryBackend * pBackend = m_myBackend.get();
705 if (nullptr == pBackend)
707 //May throw a DisposedException
708 check();
709 //We should never get here...
710 throw RuntimeException(
711 "Failed to get the BackendImpl",
712 static_cast<OWeakObject*>(const_cast<Package *>(this)));
714 return pBackend;
717 OUString Package::getRepositoryName()
719 PackageRegistryBackend * backEnd = getMyBackend();
720 return backEnd->getContext();
723 beans::Optional< OUString > Package::getRegistrationDataURL()
725 if (m_bRemoved)
726 throw deployment::ExtensionRemovedException();
727 return beans::Optional<OUString>();
730 sal_Bool Package::isRemoved()
732 return m_bRemoved;
735 Package::TypeInfo::~TypeInfo()
739 // XPackageTypeInfo
741 OUString Package::TypeInfo::getMediaType()
743 return m_mediaType;
747 OUString Package::TypeInfo::getDescription()
749 return getShortDescription();
753 OUString Package::TypeInfo::getShortDescription()
755 return m_shortDescr;
758 OUString Package::TypeInfo::getFileFilter()
760 return m_fileFilter;
763 Any Package::TypeInfo::getIcon( sal_Bool /*highContrast*/, sal_Bool /*smallIcon*/ )
765 return Any();
771 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */