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>
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
{
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
);
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
;
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()
121 for (auto const& elem
: m_bound
)
122 elem
.second
->removeEventListener(this);
124 m_xComponentContext
.clear();
125 WeakComponentImplHelperBase::disposing();
127 catch (const RuntimeException
&) {
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
);
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() );
147 t_string2ref::const_iterator
const iFind( m_bound
.find( url
) );
148 if (iFind
!= m_bound
.end())
150 Reference
<deployment::XPackage
> xPackage( iFind
->second
);
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
);
168 Reference
<deployment::XPackage
> xNewPackage
;
170 xNewPackage
= bindPackage_( url
, mediaType
, bRemoved
,
171 identifier
, xCmdEnv
);
173 catch (const RuntimeException
&) {
176 catch (const CommandFailedException
&) {
179 catch (const deployment::DeploymentException
&) {
182 catch (const Exception
&) {
183 Any
exc( ::cppu::getCaughtException() );
184 throw deployment::DeploymentException(
185 "Error binding package: " + url
,
186 static_cast<OWeakObject
*>(this), exc
);
191 std::pair
< t_string2ref::iterator
, bool > insertion(
192 m_bound
.emplace( url
, xNewPackage
) );
193 if (insertion
.second
)
196 Reference
<XInterface
>(insertion
.first
->second
) != xNewPackage
,
197 "desktop.deployment", "mismatch");
200 { // found existing entry
201 Reference
<deployment::XPackage
> xPackage( insertion
.first
->second
);
204 insertion
.first
->second
= xNewPackage
;
208 xNewPackage
->addEventListener( this ); // listen for disposing events
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())
269 Reference
<sdbc::XRow
>(
270 xResultSet
, UNO_QUERY_THROW
)->getString(
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
) ==
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
)
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
,
309 OUString
const & identifier
)
310 : t_PackageBase( getMutex() ),
311 m_myBackend( myBackend
),
314 m_displayName( displayName
),
315 m_xPackageType( xPackageType
),
316 m_bRemoved(bRemoved
),
317 m_identifier(identifier
)
321 //We use the last segment of the URL
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()
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)));
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
)
383 rBHelper
.addListener( cppu::UnoType
<decltype(xListener
)>::get(), xListener
);
387 void Package::removeModifyListener(
388 Reference
<util::XModifyListener
> const & xListener
)
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) );
406 Reference
<task::XAbortChannel
> Package::createAbortChannel()
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
>&,
425 throw deployment::ExtensionRemovedException();
430 sal_Bool
Package::checkDependencies(
431 const css::uno::Reference
< css::ucb::XCommandEnvironment
>& )
434 throw deployment::ExtensionRemovedException();
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()
452 beans::Optional
<OUString
> Package::getIdentifier()
455 return beans::Optional
<OUString
>(true, m_identifier
);
457 return beans::Optional
<OUString
>();
461 OUString
Package::getVersion()
464 throw deployment::ExtensionRemovedException();
469 OUString
Package::getURL()
475 OUString
Package::getDisplayName()
478 throw deployment::ExtensionRemovedException();
479 return m_displayName
;
483 OUString
Package::getDescription()
486 throw deployment::ExtensionRemovedException();
491 OUString
Package::getLicenseText()
494 throw deployment::ExtensionRemovedException();
499 Sequence
<OUString
> Package::getUpdateInformationURLs()
502 throw deployment::ExtensionRemovedException();
503 return Sequence
<OUString
>();
507 css::beans::StringPair
Package::getPublisherInfo()
510 throw deployment::ExtensionRemovedException();
511 css::beans::StringPair aEmptyPair
;
516 uno::Reference
< css::graphic::XGraphic
> Package::getIcon( sal_Bool
/*bHighContrast*/ )
519 throw deployment::ExtensionRemovedException();
521 uno::Reference
< css::graphic::XGraphic
> 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
)
536 throw deployment::ExtensionRemovedException();
538 ::ucbhelper::Content
destFolder( destFolderURL
, xCmdEnv
, getMyBackend()->getComponentContext() );
539 ::ucbhelper::Content
sourceContent( getURL(), xCmdEnv
, getMyBackend()->getComponentContext() );
543 destFolder
.transferContent(
544 sourceContent
, ::ucbhelper::InsertOperation::Copy
,
545 newTitle
, nameClashAction
);
547 catch (const css::ucb::ContentCreationException
&)
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
);
569 xListener
->modified( evt
);
576 beans::Optional
< beans::Ambiguous
<sal_Bool
> > Package::isRegistered(
577 Reference
<task::XAbortChannel
> const & xAbortChannel
,
578 Reference
<XCommandEnvironment
> const & xCmdEnv
)
581 ::osl::ResettableMutexGuard
guard( getMutex() );
582 return isRegistered_( guard
,
583 AbortChannel::get(xAbortChannel
),
586 catch (const RuntimeException
&) {
589 catch (const CommandFailedException
&) {
592 catch (const CommandAbortedException
&) {
595 catch (const deployment::DeploymentException
&) {
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
,
610 Reference
<task::XAbortChannel
> const & xAbortChannel
,
611 Reference
<XCommandEnvironment
> const & xCmdEnv
)
618 ::osl::ResettableMutexGuard
guard( getMutex() );
619 beans::Optional
< beans::Ambiguous
<sal_Bool
> > option(
620 isRegistered_( guard
, AbortChannel::get(xAbortChannel
),
622 action
= (option
.IsPresent
&&
623 (option
.Value
.IsAmbiguous
||
624 (doRegisterPackage
? !option
.Value
.Value
625 : option
.Value
.Value
)));
628 OUString displayName
= isRemoved() ? getName() : getDisplayName();
629 ProgressLevel
progress(
632 ? PackageRegistryBackend::StrRegisteringPackage()
633 : PackageRegistryBackend::StrRevokingPackage())
635 processPackage_( guard
,
638 AbortChannel::get(xAbortChannel
),
642 catch (lang::IllegalArgumentException
&) {
643 Any
e(cppu::getCaughtException());
644 throw deployment::DeploymentException(
646 ? DpResId(RID_STR_ERROR_WHILE_REGISTERING
)
647 : DpResId(RID_STR_ERROR_WHILE_REVOKING
))
649 static_cast< OWeakObject
* >(this), e
);
651 catch (const RuntimeException
&) {
652 TOOLS_WARN_EXCEPTION("desktop.deployment", "unexpected");
655 catch (const CommandFailedException
&) {
658 catch (const CommandAbortedException
&) {
661 catch (const deployment::DeploymentException
&) {
664 catch (const Exception
&) {
665 Any
exc( ::cppu::getCaughtException() );
666 throw deployment::DeploymentException(
668 ? DpResId(RID_STR_ERROR_WHILE_REGISTERING
)
669 : DpResId(RID_STR_ERROR_WHILE_REVOKING
))
670 + getDisplayName(), static_cast<OWeakObject
*>(this), exc
);
683 void Package::registerPackage(
685 Reference
<task::XAbortChannel
> const & xAbortChannel
,
686 Reference
<XCommandEnvironment
> const & xCmdEnv
)
689 throw deployment::ExtensionRemovedException();
690 processPackage_impl( true /* register */, startup
, xAbortChannel
, xCmdEnv
);
694 void Package::revokePackage(
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
710 //We should never get here...
711 throw RuntimeException(
712 "Failed to get the BackendImpl",
713 static_cast<OWeakObject
*>(const_cast<Package
*>(this)));
718 OUString
Package::getRepositoryName()
720 PackageRegistryBackend
* backEnd
= getMyBackend();
721 return backEnd
->getContext();
724 beans::Optional
< OUString
> Package::getRegistrationDataURL()
727 throw deployment::ExtensionRemovedException();
728 return beans::Optional
<OUString
>();
731 sal_Bool
Package::isRemoved()
736 Package::TypeInfo::~TypeInfo()
742 OUString
Package::TypeInfo::getMediaType()
748 OUString
Package::TypeInfo::getDescription()
750 return getShortDescription();
754 OUString
Package::TypeInfo::getShortDescription()
759 OUString
Package::TypeInfo::getFileFilter()
764 Any
Package::TypeInfo::getIcon( sal_Bool
/*highContrast*/, sal_Bool
/*smallIcon*/ )
772 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */