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 <sal/config.h>
24 #include <dp_backend.h>
27 #include <rtl/ustring.hxx>
28 #include <rtl/bootstrap.hxx>
29 #include <sal/log.hxx>
30 #include <cppuhelper/exc_hlp.hxx>
31 #include <comphelper/unwrapargs.hxx>
32 #include <ucbhelper/content.hxx>
33 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
34 #include <com/sun/star/deployment/DeploymentException.hpp>
35 #include <com/sun/star/deployment/ExtensionRemovedException.hpp>
36 #include <com/sun/star/deployment/InvalidRemovedParameterException.hpp>
37 #include <com/sun/star/ucb/ContentCreationException.hpp>
38 #include <com/sun/star/ucb/CommandAbortedException.hpp>
39 #include <com/sun/star/ucb/CommandFailedException.hpp>
40 #include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp>
41 #include <com/sun/star/ucb/IOErrorCode.hpp>
42 #include <com/sun/star/beans/StringPair.hpp>
43 #include <com/sun/star/sdbc/XResultSet.hpp>
44 #include <com/sun/star/sdbc/XRow.hpp>
45 #include <comphelper/diagnose_ex.hxx>
46 #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::backend
{
58 PackageRegistryBackend::~PackageRegistryBackend()
63 void PackageRegistryBackend::disposing( lang::EventObject
const & event
)
65 Reference
<deployment::XPackage
> xPackage(
66 event
.Source
, UNO_QUERY_THROW
);
67 OUString
url( xPackage
->getURL() );
68 ::osl::MutexGuard
guard( m_aMutex
);
69 if ( m_bound
.erase( url
) != 1 )
71 SAL_WARN("desktop.deployment", "erase(" << url
<< ") != 1");
76 PackageRegistryBackend::PackageRegistryBackend(
77 Sequence
<Any
> const & args
,
78 Reference
<XComponentContext
> const & xContext
)
79 : t_BackendBase( m_aMutex
),
80 m_xComponentContext( xContext
),
81 m_eContext( Context::Unknown
)
83 assert(xContext
.is());
84 std::optional
<OUString
> cachePath
;
85 std::optional
<bool> readOnly
;
86 comphelper::unwrapArgs( args
, m_context
, cachePath
, readOnly
);
88 m_cachePath
= *cachePath
;
90 if ( m_context
== "user" )
91 m_eContext
= Context::User
;
92 else if ( m_context
== "shared" )
93 m_eContext
= Context::Shared
;
94 else if ( m_context
== "bundled" )
95 m_eContext
= Context::Bundled
;
96 else if ( m_context
== "tmp" )
97 m_eContext
= Context::Tmp
;
98 else if (m_context
.matchIgnoreAsciiCase("vnd.sun.star.tdoc:/"))
99 m_eContext
= Context::Document
;
101 m_eContext
= Context::Unknown
;
105 void PackageRegistryBackend::check()
107 ::osl::MutexGuard
guard( m_aMutex
);
108 if (rBHelper
.bInDispose
|| rBHelper
.bDisposed
) {
109 throw lang::DisposedException(
110 u
"PackageRegistryBackend instance has already been disposed!"_ustr
,
111 static_cast<OWeakObject
*>(this) );
116 void PackageRegistryBackend::disposing()
119 for (auto const& elem
: m_bound
)
120 elem
.second
->removeEventListener(this);
122 m_xComponentContext
.clear();
123 WeakComponentImplHelperBase::disposing();
125 catch (const RuntimeException
&) {
128 catch (const Exception
&) {
129 Any
exc( ::cppu::getCaughtException() );
130 throw lang::WrappedTargetRuntimeException(
131 u
"caught unexpected exception while disposing!"_ustr
,
132 static_cast<OWeakObject
*>(this), exc
);
138 Reference
<deployment::XPackage
> PackageRegistryBackend::bindPackage(
139 OUString
const & url
, OUString
const & mediaType
, sal_Bool bRemoved
,
140 OUString
const & identifier
, Reference
<XCommandEnvironment
> const & xCmdEnv
)
142 ::osl::ResettableMutexGuard
guard( m_aMutex
);
145 t_string2ref::const_iterator
const iFind( m_bound
.find( url
) );
146 if (iFind
!= m_bound
.end())
148 Reference
<deployment::XPackage
> xPackage( iFind
->second
);
151 if (!mediaType
.isEmpty() &&
152 mediaType
!= xPackage
->getPackageType()->getMediaType())
153 throw lang::IllegalArgumentException
154 (u
"XPackageRegistry::bindPackage: media type does not match"_ustr
,
155 static_cast<OWeakObject
*>(this), 1);
156 if (xPackage
->isRemoved() != bRemoved
)
157 throw deployment::InvalidRemovedParameterException(
158 u
"XPackageRegistry::bindPackage: bRemoved parameter does not match"_ustr
,
159 static_cast<OWeakObject
*>(this), xPackage
->isRemoved(), xPackage
);
166 Reference
<deployment::XPackage
> xNewPackage
;
168 xNewPackage
= bindPackage_( url
, mediaType
, bRemoved
,
169 identifier
, xCmdEnv
);
171 catch (const RuntimeException
&) {
174 catch (const CommandFailedException
&) {
177 catch (const deployment::DeploymentException
&) {
180 catch (const Exception
&) {
181 Any
exc( ::cppu::getCaughtException() );
182 throw deployment::DeploymentException(
183 "Error binding package: " + url
,
184 static_cast<OWeakObject
*>(this), exc
);
189 std::pair
< t_string2ref::iterator
, bool > insertion(
190 m_bound
.emplace( url
, xNewPackage
) );
191 if (insertion
.second
)
194 Reference
<XInterface
>(insertion
.first
->second
) != xNewPackage
,
195 "desktop.deployment", "mismatch");
198 { // found existing entry
199 Reference
<deployment::XPackage
> xPackage( insertion
.first
->second
);
202 insertion
.first
->second
= xNewPackage
;
206 xNewPackage
->addEventListener( this ); // listen for disposing events
210 OUString
PackageRegistryBackend::createFolder(
211 Reference
<ucb::XCommandEnvironment
> const & xCmdEnv
)
213 const OUString sDataFolder
= makeURL(getCachePath(), OUString());
214 //make sure the folder exist
215 ucbhelper::Content dataContent
;
216 ::dp_misc::create_folder(&dataContent
, sDataFolder
, xCmdEnv
);
218 const OUString url
= ::utl::CreateTempURL(&sDataFolder
, true);
219 return sDataFolder
+ url
.subView(url
.lastIndexOf('/'));
222 //folderURL can have the extension .tmp or .tmp_
223 //Before OOo 3.4 the created a tmp file with osl_createTempFile and
224 //then created a Folder with a same name and a trailing '_'
225 //If the folderURL has no '_' then there is no corresponding tmp file.
226 void PackageRegistryBackend::deleteTempFolder(
227 OUString
const & folderUrl
)
229 if (!folderUrl
.isEmpty())
231 erase_path( folderUrl
, Reference
<XCommandEnvironment
>(),
232 false /* no throw: ignore errors */ );
234 if (folderUrl
.endsWith("_"))
236 const OUString tempFile
= folderUrl
.copy(0, folderUrl
.getLength() - 1);
237 erase_path( tempFile
, Reference
<XCommandEnvironment
>(),
238 false /* no throw: ignore errors */ );
243 //usedFolders can contain folder names which have the extension .tmp or .tmp_
244 //Before OOo 3.4 we created a tmp file with osl_createTempFile and
245 //then created a Folder with a same name and a trailing '_'
246 //If the folderURL has no '_' then there is no corresponding tmp file.
247 void PackageRegistryBackend::deleteUnusedFolders(
248 std::vector
< OUString
> const & usedFolders
)
252 const OUString sDataFolder
= makeURL(getCachePath(), OUString());
253 ::ucbhelper::Content
tempFolder(
254 sDataFolder
, Reference
<ucb::XCommandEnvironment
>(), m_xComponentContext
);
256 Reference
<sdbc::XResultSet
> xResultSet(
257 StrTitle::createCursor( tempFolder
, ::ucbhelper::INCLUDE_FOLDERS_ONLY
) );
259 // get all temp directories:
260 std::vector
<OUString
> tempEntries
;
262 while (xResultSet
->next())
265 Reference
<sdbc::XRow
>(
266 xResultSet
, UNO_QUERY_THROW
)->getString(
269 if (title
.endsWith(".tmp"))
270 tempEntries
.push_back(
271 makeURLAppendSysPathSegment(sDataFolder
, title
));
274 for (const OUString
& tempEntrie
: tempEntries
)
276 if (std::find( usedFolders
.begin(), usedFolders
.end(), tempEntrie
) ==
279 deleteTempFolder(tempEntrie
);
283 catch (const ucb::InteractiveAugmentedIOException
& e
)
285 //In case the folder containing all the data folder does not
286 //exist yet, we ignore the exception
287 if (e
.Code
!= ucb::IOErrorCode_NOT_EXISTING
)
299 Package::Package( ::rtl::Reference
<PackageRegistryBackend
> myBackend
,
302 OUString displayName
,
303 Reference
<deployment::XPackageTypeInfo
> const & xPackageType
,
306 : t_PackageBase( m_aMutex
),
307 m_myBackend(std::move( myBackend
)),
308 m_url(std::move( url
)),
309 m_name(std::move( aName
)),
310 m_displayName(std::move( displayName
)),
311 m_xPackageType( xPackageType
),
312 m_bRemoved(bRemoved
),
313 m_identifier(std::move(identifier
))
317 //We use the last segment of the URL
319 !m_name
.isEmpty(), "desktop.deployment", "non-empty m_name");
320 OUString name
= m_url
;
321 rtl::Bootstrap::expandMacros(name
);
322 sal_Int32 index
= name
.lastIndexOf('/');
323 if (index
!= -1 && index
< name
.getLength())
324 m_name
= name
.copy(index
+ 1);
329 void Package::disposing()
332 WeakComponentImplHelperBase::disposing();
336 void Package::check() const
338 ::osl::MutexGuard
guard( m_aMutex
);
339 if (rBHelper
.bInDispose
|| rBHelper
.bDisposed
) {
340 throw lang::DisposedException(
341 u
"Package instance has already been disposed!"_ustr
,
342 static_cast<OWeakObject
*>(const_cast<Package
*>(this)));
348 void Package::dispose()
350 //Do not call check here. We must not throw an exception here if the object
351 //is being disposed or is already disposed. See com.sun.star.lang.XComponent
352 WeakComponentImplHelperBase::dispose();
356 void Package::addEventListener(
357 Reference
<lang::XEventListener
> const & xListener
)
359 //Do not call check here. We must not throw an exception here if the object
360 //is being disposed or is already disposed. See com.sun.star.lang.XComponent
361 WeakComponentImplHelperBase::addEventListener( xListener
);
365 void Package::removeEventListener(
366 Reference
<lang::XEventListener
> const & xListener
)
368 //Do not call check here. We must not throw an exception here if the object
369 //is being disposed or is already disposed. See com.sun.star.lang.XComponent
370 WeakComponentImplHelperBase::removeEventListener( xListener
);
373 // XModifyBroadcaster
375 void Package::addModifyListener(
376 Reference
<util::XModifyListener
> const & xListener
)
379 rBHelper
.addListener( cppu::UnoType
<decltype(xListener
)>::get(), xListener
);
383 void Package::removeModifyListener(
384 Reference
<util::XModifyListener
> const & xListener
)
387 rBHelper
.removeListener( cppu::UnoType
<decltype(xListener
)>::get(), xListener
);
391 void Package::checkAborted(
392 ::rtl::Reference
<AbortChannel
> const & abortChannel
)
394 if (abortChannel
.is() && abortChannel
->isAborted()) {
395 throw CommandAbortedException(
396 u
"abort!"_ustr
, static_cast<OWeakObject
*>(this) );
402 Reference
<task::XAbortChannel
> Package::createAbortChannel()
405 return new AbortChannel
;
409 sal_Bool
Package::isBundle()
411 return false; // default
415 ::sal_Int32
Package::checkPrerequisites(
416 const css::uno::Reference
< css::task::XAbortChannel
>&,
417 const css::uno::Reference
< css::ucb::XCommandEnvironment
>&,
421 throw deployment::ExtensionRemovedException();
426 sal_Bool
Package::checkDependencies(
427 const css::uno::Reference
< css::ucb::XCommandEnvironment
>& )
430 throw deployment::ExtensionRemovedException();
435 Sequence
< Reference
<deployment::XPackage
> > Package::getBundle(
436 Reference
<task::XAbortChannel
> const &,
437 Reference
<XCommandEnvironment
> const & )
439 return Sequence
< Reference
<deployment::XPackage
> >();
443 OUString
Package::getName()
448 beans::Optional
<OUString
> Package::getIdentifier()
451 return beans::Optional
<OUString
>(true, m_identifier
);
453 return beans::Optional
<OUString
>();
457 OUString
Package::getVersion()
460 throw deployment::ExtensionRemovedException();
465 OUString
Package::getURL()
471 OUString
Package::getDisplayName()
474 throw deployment::ExtensionRemovedException();
475 return m_displayName
;
479 OUString
Package::getDescription()
482 throw deployment::ExtensionRemovedException();
487 OUString
Package::getLicenseText()
490 throw deployment::ExtensionRemovedException();
495 Sequence
<OUString
> Package::getUpdateInformationURLs()
498 throw deployment::ExtensionRemovedException();
499 return Sequence
<OUString
>();
503 css::beans::StringPair
Package::getPublisherInfo()
506 throw deployment::ExtensionRemovedException();
507 css::beans::StringPair aEmptyPair
;
512 uno::Reference
< css::graphic::XGraphic
> Package::getIcon( sal_Bool
/*bHighContrast*/ )
515 throw deployment::ExtensionRemovedException();
517 uno::Reference
< css::graphic::XGraphic
> aEmpty
;
522 Reference
<deployment::XPackageTypeInfo
> Package::getPackageType()
524 return m_xPackageType
;
527 void Package::exportTo(
528 OUString
const & destFolderURL
, OUString
const & newTitle
,
529 sal_Int32 nameClashAction
, Reference
<XCommandEnvironment
> const & xCmdEnv
)
532 throw deployment::ExtensionRemovedException();
534 ::ucbhelper::Content
destFolder( destFolderURL
, xCmdEnv
, getMyBackend()->getComponentContext() );
535 ::ucbhelper::Content
sourceContent( getURL(), xCmdEnv
, getMyBackend()->getComponentContext() );
539 destFolder
.transferContent(
540 sourceContent
, ::ucbhelper::InsertOperation::Copy
,
541 newTitle
, nameClashAction
);
543 catch (const css::ucb::ContentCreationException
&)
549 throw RuntimeException( u
"UCB transferContent() failed!"_ustr
, nullptr );
552 void Package::fireModified()
554 ::cppu::OInterfaceContainerHelper
* container
= rBHelper
.getContainer(
555 cppu::UnoType
<util::XModifyListener
>::get() );
556 if (container
== nullptr)
559 const Sequence
< Reference
<XInterface
> > elements(
560 container
->getElements() );
561 lang::EventObject
evt( static_cast<OWeakObject
*>(this) );
562 for ( const Reference
<XInterface
>& x
: elements
)
564 Reference
<util::XModifyListener
> xListener( x
, UNO_QUERY
);
566 xListener
->modified( evt
);
572 beans::Optional
< beans::Ambiguous
<sal_Bool
> > Package::isRegistered(
573 Reference
<task::XAbortChannel
> const & xAbortChannel
,
574 Reference
<XCommandEnvironment
> const & xCmdEnv
)
577 ::osl::ResettableMutexGuard
guard( m_aMutex
);
578 return isRegistered_( guard
,
579 AbortChannel::get(xAbortChannel
),
582 catch (const RuntimeException
&) {
585 catch (const CommandFailedException
&) {
588 catch (const CommandAbortedException
&) {
591 catch (const deployment::DeploymentException
&) {
594 catch (const Exception
& e
) {
595 Any
exc( ::cppu::getCaughtException() );
596 throw deployment::DeploymentException(
597 "unexpected " + exc
.getValueTypeName() + ": " + e
.Message
,
598 static_cast<OWeakObject
*>(this), exc
);
603 void Package::processPackage_impl(
604 bool doRegisterPackage
,
606 Reference
<task::XAbortChannel
> const & xAbortChannel
,
607 Reference
<XCommandEnvironment
> const & xCmdEnv
)
614 ::osl::ResettableMutexGuard
guard( m_aMutex
);
615 beans::Optional
< beans::Ambiguous
<sal_Bool
> > option(
616 isRegistered_( guard
, AbortChannel::get(xAbortChannel
),
618 action
= (option
.IsPresent
&&
619 (option
.Value
.IsAmbiguous
||
620 (doRegisterPackage
? !option
.Value
.Value
621 : option
.Value
.Value
)));
624 OUString displayName
= isRemoved() ? getName() : getDisplayName();
625 ProgressLevel
progress(
628 ? PackageRegistryBackend::StrRegisteringPackage()
629 : PackageRegistryBackend::StrRevokingPackage())
631 processPackage_( guard
,
634 AbortChannel::get(xAbortChannel
),
638 catch (lang::IllegalArgumentException
&) {
639 Any
e(cppu::getCaughtException());
640 throw deployment::DeploymentException(
642 ? DpResId(RID_STR_ERROR_WHILE_REGISTERING
)
643 : DpResId(RID_STR_ERROR_WHILE_REVOKING
))
645 static_cast< OWeakObject
* >(this), e
);
647 catch (const RuntimeException
&) {
648 TOOLS_WARN_EXCEPTION("desktop.deployment", "unexpected");
651 catch (const CommandFailedException
&) {
654 catch (const CommandAbortedException
&) {
657 catch (const deployment::DeploymentException
&) {
660 catch (const Exception
& e
) {
661 Any
exc( ::cppu::getCaughtException() );
662 throw deployment::DeploymentException(
664 ? DpResId(RID_STR_ERROR_WHILE_REGISTERING
)
665 : DpResId(RID_STR_ERROR_WHILE_REVOKING
))
666 + getDisplayName() + ": " + exc
.getValueTypeName() + " \"" + e
.Message
668 static_cast<OWeakObject
*>(this), exc
);
681 void Package::registerPackage(
683 Reference
<task::XAbortChannel
> const & xAbortChannel
,
684 Reference
<XCommandEnvironment
> const & xCmdEnv
)
687 throw deployment::ExtensionRemovedException();
688 processPackage_impl( true /* register */, startup
, xAbortChannel
, xCmdEnv
);
692 void Package::revokePackage(
694 Reference
<task::XAbortChannel
> const & xAbortChannel
,
695 Reference
<XCommandEnvironment
> const & xCmdEnv
)
697 processPackage_impl( false /* revoke */, startup
, xAbortChannel
, xCmdEnv
);
701 PackageRegistryBackend
* Package::getMyBackend() const
703 PackageRegistryBackend
* pBackend
= m_myBackend
.get();
704 if (nullptr == pBackend
)
706 //May throw a DisposedException
708 //We should never get here...
709 throw RuntimeException(
710 u
"Failed to get the BackendImpl"_ustr
,
711 static_cast<OWeakObject
*>(const_cast<Package
*>(this)));
716 OUString
Package::getRepositoryName()
718 PackageRegistryBackend
* backEnd
= getMyBackend();
719 return backEnd
->getContext();
722 beans::Optional
< OUString
> Package::getRegistrationDataURL()
725 throw deployment::ExtensionRemovedException();
726 return beans::Optional
<OUString
>();
729 sal_Bool
Package::isRemoved()
734 Package::TypeInfo::~TypeInfo()
740 OUString
Package::TypeInfo::getMediaType()
746 OUString
Package::TypeInfo::getDescription()
748 return getShortDescription();
752 OUString
Package::TypeInfo::getShortDescription()
757 OUString
Package::TypeInfo::getFileFilter()
762 Any
Package::TypeInfo::getIcon( sal_Bool
/*highContrast*/, sal_Bool
/*smallIcon*/ )
769 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */