Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / desktop / source / deployment / manager / dp_manager.cxx
blob206b44d85bd60770ca32a0367e78115af0827cc7
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 <config_features.h>
22 #include <dp_interact.h>
23 #include <dp_misc.h>
24 #include <dp_registry.hxx>
25 #include <dp_shared.hxx>
26 #include <strings.hrc>
27 #include <dp_ucb.h>
28 #include <dp_platform.hxx>
29 #include "dp_manager.h"
30 #include <dp_identifier.hxx>
31 #include <rtl/ustrbuf.hxx>
32 #include <rtl/string.hxx>
33 #include <rtl/uri.hxx>
34 #include <rtl/bootstrap.hxx>
35 #include <sal/log.hxx>
36 #include <tools/urlobj.hxx>
37 #include <comphelper/diagnose_ex.hxx>
38 #include <osl/diagnose.h>
39 #include <osl/file.hxx>
40 #include <osl/security.hxx>
41 #include <cppuhelper/exc_hlp.hxx>
42 #include <comphelper/logging.hxx>
43 #include <comphelper/sequence.hxx>
44 #include <utility>
45 #include <xmlscript/xml_helper.hxx>
46 #include <svl/inettype.hxx>
47 #include <com/sun/star/lang/IllegalArgumentException.hpp>
48 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
49 #include <com/sun/star/beans/UnknownPropertyException.hpp>
50 #include <com/sun/star/logging/LogLevel.hpp>
51 #include <com/sun/star/logging/FileHandler.hpp>
52 #include <com/sun/star/logging/SimpleTextFormatter.hpp>
53 #include <com/sun/star/logging/XLogger.hpp>
54 #include <com/sun/star/util/XUpdatable.hpp>
55 #include <com/sun/star/sdbc/XResultSet.hpp>
56 #include <com/sun/star/sdbc/XRow.hpp>
57 #include <com/sun/star/ucb/CommandAbortedException.hpp>
58 #include <com/sun/star/ucb/CommandFailedException.hpp>
59 #include <com/sun/star/ucb/ContentCreationException.hpp>
60 #include <com/sun/star/ucb/XContentAccess.hpp>
61 #include <com/sun/star/ucb/NameClash.hpp>
62 #include <com/sun/star/deployment/DeploymentException.hpp>
63 #include <com/sun/star/deployment/InvalidRemovedParameterException.hpp>
64 #include <com/sun/star/deployment/Prerequisites.hpp>
65 #include <com/sun/star/ucb/UnsupportedCommandException.hpp>
66 #include <unotools/tempfile.hxx>
68 #include <dp_descriptioninfoset.hxx>
69 #include "dp_commandenvironments.hxx"
70 #include "dp_properties.hxx"
72 #include <vector>
73 #include <algorithm>
75 using namespace ::dp_misc;
76 using namespace ::com::sun::star;
77 using namespace ::com::sun::star::uno;
78 using namespace ::com::sun::star::ucb;
79 using namespace ::com::sun::star::logging;
82 namespace dp_manager {
84 namespace {
86 struct MatchTempDir
88 OUString m_str;
89 explicit MatchTempDir( OUString str ) : m_str(std::move( str )) {}
90 bool operator () ( ActivePackages::Entries::value_type const & v ) const {
91 return v.second.temporaryName.equalsIgnoreAsciiCase( m_str );
95 OUString getExtensionFolder(OUString const & parentFolder,
96 Reference<ucb::XCommandEnvironment> const & xCmdEnv,
97 Reference<uno::XComponentContext> const & xContext)
99 ::ucbhelper::Content tempFolder( parentFolder, xCmdEnv, xContext );
100 Reference<sdbc::XResultSet> xResultSet(
101 StrTitle::createCursor (tempFolder, ::ucbhelper::INCLUDE_FOLDERS_ONLY ) );
103 OUString title;
104 if (xResultSet->next())
106 title = Reference<sdbc::XRow>(
107 xResultSet, UNO_QUERY_THROW )->getString(1 /* Title */ ) ;
109 return title;
113 void PackageManagerImpl::initActivationLayer(
114 Reference<XCommandEnvironment> const & xCmdEnv )
116 if (m_activePackages.isEmpty())
118 OSL_ASSERT( m_registryCache.isEmpty() );
119 // documents temp activation:
120 m_activePackagesDB.reset( new ActivePackages );
121 ::ucbhelper::Content ucbContent;
122 if (create_ucb_content( &ucbContent, m_context, xCmdEnv,
123 false /* no throw */ ))
125 // scan for all entries in m_packagesDir:
126 Reference<sdbc::XResultSet> xResultSet(
127 StrTitle::createCursor (ucbContent, ::ucbhelper::INCLUDE_FOLDERS_AND_DOCUMENTS ) );
129 while (xResultSet->next())
131 Reference<sdbc::XRow> xRow( xResultSet, UNO_QUERY_THROW );
132 OUString title( xRow->getString( 1 /* Title */ ) );
133 // xxx todo: remove workaround for tdoc
134 if ( title == "this_is_a_dummy_stream_just_there_as_a_workaround_for_a_temporary_limitation_of_the_storage_api_implementation" )
135 continue;
136 if ( title == "META-INF" )
137 continue;
139 ::ucbhelper::Content sourceContent(
140 Reference<XContentAccess>(
141 xResultSet, UNO_QUERY_THROW )->queryContent(),
142 xCmdEnv, m_xComponentContext );
144 OUString mediaType( detectMediaType( sourceContent,
145 false /* no throw */) );
146 if (!mediaType.isEmpty())
148 ActivePackages::Data dbData;
149 insertToActivationLayer(
150 Sequence<css::beans::NamedValue>(),mediaType, sourceContent,
151 title, &dbData );
153 insertToActivationLayerDB( title, dbData );
154 //TODO #i73136#: insertToActivationLayerDB needs id not
155 // title, but the whole m_activePackages.getLength()==0
156 // case (i.e., document-relative deployment) currently
157 // does not work, anyway.
162 else
164 // user|share:
165 OSL_ASSERT( !m_activePackages.isEmpty() );
166 m_activePackages_expanded = expandUnoRcUrl( m_activePackages );
167 m_registrationData_expanded = expandUnoRcUrl(m_registrationData);
168 if (!m_readOnly)
169 create_folder( nullptr, m_activePackages_expanded, xCmdEnv);
171 OUString dbName;
172 if (m_context == "user")
173 dbName = m_activePackages_expanded + ".pmap";
174 else
176 // Create the extension data base in the user installation
177 create_folder( nullptr, m_registrationData_expanded, xCmdEnv);
178 dbName = m_registrationData_expanded + "/extensions.pmap";
180 // The data base can always be written because it is always in the user installation
181 m_activePackagesDB.reset( new ActivePackages( dbName ) );
183 if (! m_readOnly && m_context != "bundled")
185 // clean up activation layer, scan for zombie temp dirs:
186 ActivePackages::Entries id2temp( m_activePackagesDB->getEntries() );
188 ::ucbhelper::Content tempFolder( m_activePackages_expanded, xCmdEnv, m_xComponentContext );
189 Reference<sdbc::XResultSet> xResultSet(
190 StrTitle::createCursor (tempFolder,
191 ::ucbhelper::INCLUDE_DOCUMENTS_ONLY ) );
193 // get all temp directories:
194 std::vector<OUString> tempEntries;
195 std::vector<OUString> removedEntries;
196 while (xResultSet->next())
198 OUString title(
199 Reference<sdbc::XRow>(
200 xResultSet, UNO_QUERY_THROW )->getString(
201 1 /* Title */ ) );
202 if (title.endsWith("removed", &title))
204 //save the file name without the "removed" part
205 removedEntries.push_back(::rtl::Uri::encode(
206 title, rtl_UriCharClassPchar,
207 rtl_UriEncodeIgnoreEscapes,
208 RTL_TEXTENCODING_UTF8 ) );
210 else
212 tempEntries.push_back( ::rtl::Uri::encode(
213 title, rtl_UriCharClassPchar,
214 rtl_UriEncodeIgnoreEscapes,
215 RTL_TEXTENCODING_UTF8 ) );
219 bool bShared = (m_context == "shared");
220 for (const OUString & tempEntry : tempEntries)
222 const MatchTempDir match( tempEntry );
223 if (std::none_of( id2temp.begin(), id2temp.end(), match ))
225 const OUString url(
226 makeURL(m_activePackages_expanded, tempEntry ) );
228 //In case of shared extensions, new entries are regarded as
229 //added extensions if there is no xxx.tmpremoved file.
230 if (bShared)
232 if (std::find(removedEntries.begin(), removedEntries.end(), tempEntry) ==
233 removedEntries.end())
235 continue;
237 else
239 //Make sure only the same user removes the extension, who
240 //previously unregistered it. This is avoid races if multiple instances
241 //of OOo are running which all have write access to the shared installation.
242 //For example, a user removes the extension, but keeps OOo
243 //running. Parts of the extension may still be loaded and used by OOo.
244 //Therefore the extension is only deleted the next time the extension manager is
245 //run after restarting OOo. While OOo is still running, another user starts OOo
246 //which would deleted the extension files. If the same user starts another
247 //instance of OOo then the lock file will prevent this.
248 OUString aUserName;
249 ::osl::Security aSecurity;
250 aSecurity.getUserName( aUserName );
251 ucbhelper::Content remFileContent(
252 url + "removed", Reference<XCommandEnvironment>(), m_xComponentContext);
253 std::vector<sal_Int8> data = dp_misc::readFile(remFileContent);
254 std::string_view osData(reinterpret_cast<const char*>(data.data()),
255 data.size());
256 OUString sData = OStringToOUString(
257 osData, RTL_TEXTENCODING_UTF8);
258 if (sData != aUserName)
259 continue;
262 // temp entry not needed anymore:
263 erase_path( url + "_",
264 Reference<XCommandEnvironment>(),
265 false /* no throw: ignore errors */ );
266 erase_path( url, Reference<XCommandEnvironment>(),
267 false /* no throw: ignore errors */ );
268 //delete the xxx.tmpremoved file
269 erase_path(url + "removed",
270 Reference<XCommandEnvironment>(), false);
278 void PackageManagerImpl::initRegistryBackends()
280 if (!m_registryCache.isEmpty())
281 create_folder( nullptr, m_registryCache,
282 Reference<XCommandEnvironment>(), false);
283 m_xRegistry.set( ::dp_registry::create(
284 m_context, m_registryCache,
285 m_xComponentContext ) );
288 namespace {
290 osl::FileBase::RC createDirectory(OUString const & url) {
291 auto e = osl::Directory::create(url);
292 if (e != osl::FileBase::E_NOENT) {
293 return e;
295 INetURLObject o(url);
296 if (!o.removeSegment()) {
297 return osl::FileBase::E_INVAL; // anything but E_None/E_EXIST
299 e = createDirectory(o.GetMainURL(INetURLObject::DecodeMechanism::NONE));
300 if (e != osl::FileBase::E_None && e != osl::FileBase::E_EXIST) {
301 return e;
303 return osl::Directory::create(url);
306 bool isMacroURLReadOnly( const OUString &rMacro )
308 OUString aDirURL( rMacro );
309 ::rtl::Bootstrap::expandMacros( aDirURL );
311 ::osl::FileBase::RC aErr = createDirectory( aDirURL );
312 if ( aErr == ::osl::FileBase::E_None )
313 return false; // it will be writeable
314 if ( aErr != ::osl::FileBase::E_EXIST )
315 return true; // some serious problem creating it
317 bool bError;
318 sal_uInt64 nWritten = 0;
319 OUString aFileURL( aDirURL + "/stamp.sys" );
320 ::osl::File aFile( aFileURL );
322 bError = aFile.open( osl_File_OpenFlag_Read |
323 osl_File_OpenFlag_Write |
324 osl_File_OpenFlag_Create ) != ::osl::FileBase::E_None;
325 if (!bError)
326 bError = aFile.write( "1", 1, nWritten ) != ::osl::FileBase::E_None;
327 if (aFile.close() != ::osl::FileBase::E_None)
328 bError = true;
329 if (osl::File::remove( aFileURL ) != ::osl::FileBase::E_None)
330 bError = true;
332 SAL_INFO(
333 "desktop.deployment",
334 "local url '" << rMacro << "' -> '" << aFileURL << "' "
335 << (bError ? "is" : "is not") << " readonly\n");
336 return bError;
341 Reference<deployment::XPackageManager> PackageManagerImpl::create(
342 Reference<XComponentContext> const & xComponentContext,
343 OUString const & context )
345 rtl::Reference<PackageManagerImpl> that = new PackageManagerImpl(
346 xComponentContext, context );
348 OUString logFile, stamp;
349 if ( context == "user" ) {
350 that->m_activePackages = "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE/uno_packages";
351 that->m_registrationData = "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE";
352 that->m_registryCache = "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE/registry";
353 logFile = "$UNO_USER_PACKAGES_CACHE/log.txt";
354 //We use the extension .sys for the file because on Windows Vista a sys
355 //(as well as exe and dll) file
356 //will not be written in the VirtualStore. For example if the process has no
357 //admin right once cannot write to the %programfiles% folder. However, when
358 //virtualization is used, the file will be written into the VirtualStore and
359 //it appears as if one could write to %programfiles%. When we test for write
360 //access to the office/shared folder for shared extensions then this typically
361 //fails because a normal user typically cannot write to this folder. However,
362 //using virtualization it appears that he/she can. Then a shared extension can
363 //be installed but is only visible for the user (because the extension is in
364 //the virtual store).
365 stamp = "$UNO_USER_PACKAGES_CACHE";
367 else if ( context == "shared" ) {
368 that->m_activePackages = "vnd.sun.star.expand:$UNO_SHARED_PACKAGES_CACHE/uno_packages";
369 that->m_registrationData = "vnd.sun.star.expand:$SHARED_EXTENSIONS_USER";
370 that->m_registryCache = "vnd.sun.star.expand:$SHARED_EXTENSIONS_USER/registry";
371 logFile = "$SHARED_EXTENSIONS_USER/log.txt";
372 #if !HAVE_FEATURE_READONLY_INSTALLSET
373 // The "shared" extensions are read-only when we have a
374 // read-only installset.
375 stamp = "$UNO_SHARED_PACKAGES_CACHE";
376 #endif
378 else if ( context == "bundled" ) {
379 that->m_activePackages = "vnd.sun.star.expand:$BUNDLED_EXTENSIONS";
380 that->m_registrationData = "vnd.sun.star.expand:$BUNDLED_EXTENSIONS_USER";
381 that->m_registryCache = "vnd.sun.star.expand:$BUNDLED_EXTENSIONS_USER/registry";
382 logFile = "$BUNDLED_EXTENSIONS_USER/log.txt";
383 //No stamp file. We assume that bundled is always readonly. It must not be
384 //modified from ExtensionManager but only by the installer
386 else if ( context == "tmp" ) {
387 that->m_activePackages = "vnd.sun.star.expand:$TMP_EXTENSIONS/extensions";
388 that->m_registrationData = "vnd.sun.star.expand:$TMP_EXTENSIONS";
389 that->m_registryCache = "vnd.sun.star.expand:$TMP_EXTENSIONS/registry";
390 stamp = "$TMP_EXTENSIONS";
392 else if (context == "bak") {
393 that->m_activePackages = "vnd.sun.star.expand:$BAK_EXTENSIONS/extensions";
394 that->m_registrationData = "vnd.sun.star.expand:$BAK_EXTENSIONS";
395 that->m_registryCache = "vnd.sun.star.expand:$BAK_EXTENSIONS/registry";
396 stamp = "$BAK_EXTENSIONS";
399 else if (! context.match("vnd.sun.star.tdoc:/")) {
400 throw lang::IllegalArgumentException(
401 "invalid context given: " + context,
402 Reference<XInterface>(), static_cast<sal_Int16>(-1) );
405 Reference<XCommandEnvironment> xCmdEnv;
407 try {
408 // There is no stamp for the bundled folder:
409 if (!stamp.isEmpty())
410 that->m_readOnly = isMacroURLReadOnly( stamp );
412 if (!that->m_readOnly && !logFile.isEmpty())
414 // Initialize logger which will be used in ProgressLogImpl (created below)
415 rtl::Bootstrap::expandMacros(logFile);
416 comphelper::EventLogger logger(xComponentContext, "unopkg");
417 const Reference<XLogger> xLogger(logger.getLogger());
418 Reference<XLogFormatter> xLogFormatter(SimpleTextFormatter::create(xComponentContext));
419 Sequence < beans::NamedValue > aSeq2 { { "Formatter", Any(xLogFormatter) }, {"FileURL", Any(logFile)} };
420 Reference<XLogHandler> xFileHandler(css::logging::FileHandler::createWithSettings(xComponentContext, aSeq2));
421 xFileHandler->setLevel(LogLevel::WARNING);
422 xLogger->addLogHandler(xFileHandler);
424 that->m_xLogFile.set(
425 that->m_xComponentContext->getServiceManager()
426 ->createInstanceWithArgumentsAndContext(
427 "com.sun.star.comp.deployment.ProgressLog",
428 Sequence<Any>(),
429 that->m_xComponentContext ),
430 UNO_QUERY_THROW );
431 xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv, that->m_xLogFile ) );
434 that->initRegistryBackends();
435 that->initActivationLayer( xCmdEnv );
437 return that;
440 catch (const RuntimeException &) {
441 throw;
443 catch (const Exception & e) {
444 Any exc( ::cppu::getCaughtException() );
445 throw lang::WrappedTargetRuntimeException(
446 ("[context=\"" + context + "\"] caught unexpected "
447 + exc.getValueType().getTypeName() + ": " + e.Message),
448 Reference<XInterface>(), exc );
453 PackageManagerImpl::~PackageManagerImpl()
458 void PackageManagerImpl::fireModified()
460 ::cppu::OInterfaceContainerHelper * pContainer = rBHelper.getContainer(
461 cppu::UnoType<util::XModifyListener>::get() );
462 if (pContainer != nullptr) {
463 pContainer->forEach<util::XModifyListener>(
464 [this] (uno::Reference<util::XModifyListener> const& xListener)
465 { return xListener->modified(lang::EventObject(static_cast<OWeakObject *>(this))); });
470 void PackageManagerImpl::disposing()
472 try {
473 // // xxx todo: guarding?
474 // ::osl::MutexGuard guard( getMutex() );
475 try_dispose( m_xLogFile );
476 m_xLogFile.clear();
477 try_dispose( m_xRegistry );
478 m_xRegistry.clear();
479 m_activePackagesDB.reset();
480 m_xComponentContext.clear();
482 t_pm_helper::disposing();
485 catch (const RuntimeException &) {
486 throw;
488 catch (const Exception &) {
489 Any exc( ::cppu::getCaughtException() );
490 throw lang::WrappedTargetRuntimeException(
491 "caught unexpected exception while disposing...",
492 static_cast<OWeakObject *>(this), exc );
496 // XComponent
498 void PackageManagerImpl::dispose()
500 //Do not call check here. We must not throw an exception here if the object
501 //is being disposed or is already disposed. See com.sun.star.lang.XComponent
502 WeakComponentImplHelperBase::dispose();
506 void PackageManagerImpl::addEventListener(
507 Reference<lang::XEventListener> const & xListener )
509 //Do not call check here. We must not throw an exception here if the object
510 //is being disposed or is already disposed. See com.sun.star.lang.XComponent
511 WeakComponentImplHelperBase::addEventListener( xListener );
515 void PackageManagerImpl::removeEventListener(
516 Reference<lang::XEventListener> const & xListener )
518 //Do not call check here. We must not throw an exception here if the object
519 //is being disposed or is already disposed. See com.sun.star.lang.XComponent
520 WeakComponentImplHelperBase::removeEventListener( xListener );
523 // XPackageManager
525 OUString PackageManagerImpl::getContext()
527 check();
528 return m_context;
532 Sequence< Reference<deployment::XPackageTypeInfo> >
533 PackageManagerImpl::getSupportedPackageTypes()
535 OSL_ASSERT( m_xRegistry.is() );
536 return m_xRegistry->getSupportedPackageTypes();
540 Reference<task::XAbortChannel> PackageManagerImpl::createAbortChannel()
542 check();
543 return new AbortChannel;
546 // XModifyBroadcaster
548 void PackageManagerImpl::addModifyListener(
549 Reference<util::XModifyListener> const & xListener )
551 check();
552 rBHelper.addListener( cppu::UnoType<decltype(xListener)>::get(), xListener );
556 void PackageManagerImpl::removeModifyListener(
557 Reference<util::XModifyListener> const & xListener )
559 check();
560 rBHelper.removeListener( cppu::UnoType<decltype(xListener)>::get(), xListener );
564 OUString PackageManagerImpl::detectMediaType(
565 ::ucbhelper::Content const & ucbContent_, bool throw_exc )
567 ::ucbhelper::Content ucbContent(ucbContent_);
568 OUString url( ucbContent.getURL() );
569 OUString mediaType;
570 if (url.match( "vnd.sun.star.tdoc:" ) || url.match( "vnd.sun.star.pkg:" ))
572 try {
573 ucbContent.getPropertyValue( "MediaType" ) >>= mediaType;
575 catch (const beans::UnknownPropertyException &) {
577 OSL_ENSURE( !mediaType.isEmpty(), "### no media-type?!" );
579 if (mediaType.isEmpty())
581 try {
582 Reference<deployment::XPackage> xPackage(
583 m_xRegistry->bindPackage(
584 url, OUString(), false, OUString(), ucbContent.getCommandEnvironment() ) );
585 const Reference<deployment::XPackageTypeInfo> xPackageType(
586 xPackage->getPackageType() );
587 OSL_ASSERT( xPackageType.is() );
588 if (xPackageType.is())
589 mediaType = xPackageType->getMediaType();
591 catch (const lang::IllegalArgumentException &) {
592 if (throw_exc)
593 throw;
594 css::uno::Any ex( cppu::getCaughtException() );
595 SAL_WARN( "desktop", exceptionToString(ex) );
598 return mediaType;
602 OUString PackageManagerImpl::insertToActivationLayer(
603 Sequence<beans::NamedValue> const & properties,
604 OUString const & mediaType, ::ucbhelper::Content const & sourceContent_,
605 OUString const & title, ActivePackages::Data * dbData )
607 ::ucbhelper::Content sourceContent(sourceContent_);
608 Reference<XCommandEnvironment> xCmdEnv(
609 sourceContent.getCommandEnvironment() );
611 OUString tempEntry = ::utl::CreateTempURL(&m_activePackages_expanded, false);
612 tempEntry = tempEntry.copy(tempEntry.lastIndexOf('/') + 1);
613 OUString destFolder = makeURL( m_activePackages, tempEntry) + "_";
615 // prepare activation folder:
616 ::ucbhelper::Content destFolderContent;
617 create_folder( &destFolderContent, destFolder, xCmdEnv );
619 // copy content into activation temp dir:
620 if (mediaType.matchIgnoreAsciiCase("application/vnd.sun.star.package-bundle") ||
621 // xxx todo: more sophisticated parsing
622 mediaType.matchIgnoreAsciiCase("application/vnd.sun.star.legacy-package-bundle"))
624 // inflate content:
625 OUStringBuffer buf;
626 if (!sourceContent.isFolder())
628 buf.append( "vnd.sun.star.zip://" );
629 buf.append( ::rtl::Uri::encode( sourceContent.getURL(),
630 rtl_UriCharClassRegName,
631 rtl_UriEncodeIgnoreEscapes,
632 RTL_TEXTENCODING_UTF8 ) );
634 else
636 //Folder. No need to unzip, just copy
637 buf.append(sourceContent.getURL());
639 buf.append( '/' );
640 sourceContent = ::ucbhelper::Content(
641 buf.makeStringAndClear(), xCmdEnv, m_xComponentContext );
643 destFolderContent.transferContent(
644 sourceContent, ::ucbhelper::InsertOperation::Copy,
645 title, NameClash::OVERWRITE );
648 // write to DB:
649 //bundled extensions should only be added by the synchronizeAddedExtensions
650 //functions. Moreover, there is no "temporary folder" for bundled extensions.
651 OSL_ASSERT(!(m_context == "bundled"));
652 OUString sFolderUrl = makeURLAppendSysPathSegment(destFolderContent.getURL(), title);
653 DescriptionInfoset info =
654 dp_misc::getDescriptionInfoset(sFolderUrl);
655 dbData->temporaryName = tempEntry;
656 dbData->fileName = title;
657 dbData->mediaType = mediaType;
658 dbData->version = info.getVersion();
660 //No write the properties file next to the extension
661 ExtensionProperties props(sFolderUrl, properties, xCmdEnv, m_xComponentContext);
662 props.write();
663 return destFolder;
667 void PackageManagerImpl::insertToActivationLayerDB(
668 OUString const & id, ActivePackages::Data const & dbData )
670 //access to the database must be guarded. See removePackage
671 const ::osl::MutexGuard guard( m_aMutex );
672 m_activePackagesDB->put( id, dbData );
676 /* The function returns true if there is an extension with the same id already
677 installed which needs to be uninstalled, before the new extension can be installed.
679 bool PackageManagerImpl::isInstalled(
680 Reference<deployment::XPackage> const & package)
682 OUString id(dp_misc::getIdentifier(package));
683 OUString fn(package->getName());
684 bool bInstalled = false;
685 if (m_activePackagesDB->has( id, fn ))
687 bInstalled = true;
689 return bInstalled;
692 // XPackageManager
694 Reference<deployment::XPackage> PackageManagerImpl::importExtension(
695 Reference<deployment::XPackage> const & extension,
696 Reference<task::XAbortChannel> const & xAbortChannel,
697 Reference<XCommandEnvironment> const & xCmdEnv_ )
699 return addPackage(extension->getURL(), Sequence<beans::NamedValue>(),
700 OUString(), xAbortChannel, xCmdEnv_);
703 /* The function adds an extension but does not register it!!!
704 It may not do any user interaction. This is done in XExtensionManager::addExtension
706 Reference<deployment::XPackage> PackageManagerImpl::addPackage(
707 OUString const & url,
708 css::uno::Sequence<css::beans::NamedValue> const & properties,
709 OUString const & mediaType_,
710 Reference<task::XAbortChannel> const & xAbortChannel,
711 Reference<XCommandEnvironment> const & xCmdEnv_ )
713 check();
714 if (m_readOnly)
716 OUString message;
717 if (m_context == "shared")
718 message = "You need write permissions to install a shared extension!";
719 else
720 message = "You need write permissions to install this extension!";
721 throw deployment::DeploymentException(
722 message, static_cast<OWeakObject *>(this), Any() );
724 Reference<XCommandEnvironment> xCmdEnv;
725 if (m_xLogFile.is())
726 xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv_, m_xLogFile ) );
727 else
728 xCmdEnv.set( xCmdEnv_ );
730 try {
731 ::ucbhelper::Content sourceContent;
732 (void)create_ucb_content( &sourceContent, url, xCmdEnv ); // throws exc
733 const OUString title( StrTitle::getTitle( sourceContent ) );
734 const OUString title_enc( ::rtl::Uri::encode(
735 title, rtl_UriCharClassPchar,
736 rtl_UriEncodeIgnoreEscapes,
737 RTL_TEXTENCODING_UTF8 ) );
738 OUString destFolder;
740 OUString mediaType(mediaType_);
741 if (mediaType.isEmpty())
742 mediaType = detectMediaType( sourceContent );
744 Reference<deployment::XPackage> xPackage;
745 // copy file:
746 progressUpdate(
747 DpResId(RID_STR_COPYING_PACKAGE) + title, xCmdEnv );
748 if (m_activePackages.isEmpty())
750 ::ucbhelper::Content docFolderContent;
751 create_folder( &docFolderContent, m_context, xCmdEnv );
752 // copy into document, first:
753 docFolderContent.transferContent(
754 sourceContent, ::ucbhelper::InsertOperation::Copy,
755 OUString(),
756 NameClash::ASK /* xxx todo: ASK not needed? */);
757 // set media-type:
758 ::ucbhelper::Content docContent(
759 makeURL( m_context, title_enc ), xCmdEnv, m_xComponentContext );
760 //TODO #i73136#: using title instead of id can lead to
761 // clashes, but the whole m_activePackages.getLength()==0
762 // case (i.e., document-relative deployment) currently does
763 // not work, anyway.
764 docContent.setPropertyValue("MediaType", Any(mediaType) );
766 // xxx todo: obsolete in the future
767 try {
768 docFolderContent.executeCommand( "flush", Any() );
770 catch (const UnsupportedCommandException &) {
773 ActivePackages::Data dbData;
774 destFolder = insertToActivationLayer(
775 properties, mediaType, sourceContent, title, &dbData );
778 // bind activation package:
779 //Because every shared/user extension will be unpacked in a folder,
780 //which was created with a unique name we will always have two different
781 //XPackage objects, even if the second extension is the same.
782 //Therefore bindPackage does not need a guard here.
783 xPackage = m_xRegistry->bindPackage(
784 makeURL( destFolder, title_enc ), mediaType, false, OUString(), xCmdEnv );
786 OSL_ASSERT( xPackage.is() );
787 if (xPackage.is())
789 bool install = false;
792 OUString const id = dp_misc::getIdentifier( xPackage );
794 std::unique_lock g(m_addMutex);
795 if (isInstalled(xPackage))
797 //Do not guard the complete function with the getMutex
798 removePackage(id, xPackage->getName(), xAbortChannel,
799 xCmdEnv);
801 install = true;
802 insertToActivationLayerDB(id, dbData);
804 catch (...)
806 deletePackageFromCache( xPackage, destFolder );
807 throw;
809 if (!install)
811 deletePackageFromCache( xPackage, destFolder );
813 //ToDo: We should notify only if the extension is registered
814 fireModified();
816 return xPackage;
818 catch (const RuntimeException &) {
819 throw;
821 catch (const CommandFailedException & exc) {
822 logIntern( Any(exc) );
823 throw;
825 catch (const CommandAbortedException & exc) {
826 logIntern( Any(exc) );
827 throw;
829 catch (const deployment::DeploymentException & exc) {
830 logIntern( Any(exc) );
831 throw;
833 catch (const Exception &) {
834 Any exc( ::cppu::getCaughtException() );
835 logIntern( exc );
836 throw deployment::DeploymentException(
837 DpResId(RID_STR_ERROR_WHILE_ADDING) + url,
838 static_cast<OWeakObject *>(this), exc );
841 void PackageManagerImpl::deletePackageFromCache(
842 Reference<deployment::XPackage> const & xPackage,
843 OUString const & destFolder)
845 try_dispose( xPackage );
847 //we remove the package from the uno cache
848 //no service from the package may be loaded at this time!!!
849 erase_path( destFolder, Reference<XCommandEnvironment>(),
850 false /* no throw: ignore errors */ );
851 //rm last character '_'
852 OUString url = destFolder.copy(0, destFolder.getLength() - 1);
853 erase_path( url, Reference<XCommandEnvironment>(),
854 false /* no throw: ignore errors */ );
858 void PackageManagerImpl::removePackage(
859 OUString const & id, OUString const & fileName,
860 Reference<task::XAbortChannel> const & /*xAbortChannel*/,
861 Reference<XCommandEnvironment> const & xCmdEnv_ )
863 check();
865 Reference<XCommandEnvironment> xCmdEnv;
866 if (m_xLogFile.is())
867 xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv_, m_xLogFile ) );
868 else
869 xCmdEnv.set( xCmdEnv_ );
871 try {
872 Reference<deployment::XPackage> xPackage;
874 const ::osl::MutexGuard guard(m_aMutex);
875 //Check if this extension exist and throw an IllegalArgumentException
876 //if it does not
877 //If the files of the extension are already removed, or there is a
878 //different extension at the same place, for example after updating the
879 //extension, then the returned object is that which uses the database data.
880 xPackage = getDeployedPackage_(id, fileName, xCmdEnv );
883 //Because the extension is only removed the next time the extension
884 //manager runs after restarting OOo, we need to indicate that a
885 //shared extension was "deleted". When a user starts OOo, then it
886 //will check if something changed in the shared repository. Based on
887 //the flag file it will then recognize, that the extension was
888 //deleted and can then update the extension database of the shared
889 //extensions in the user installation.
890 if ( xPackage.is() && !m_readOnly && !xPackage->isRemoved() && (m_context == "shared"))
892 ActivePackages::Data val;
893 m_activePackagesDB->get( & val, id, fileName);
894 OSL_ASSERT(!val.temporaryName.isEmpty());
895 OUString url(makeURL(m_activePackages_expanded,
896 val.temporaryName + "removed"));
897 ::ucbhelper::Content contentRemoved(url, xCmdEnv, m_xComponentContext);
898 OUString aUserName;
899 ::osl::Security aSecurity;
900 aSecurity.getUserName( aUserName );
902 OString stamp = OUStringToOString(aUserName, RTL_TEXTENCODING_UTF8);
903 Reference<css::io::XInputStream> xData(
904 ::xmlscript::createInputStream(
905 reinterpret_cast<sal_Int8 const *>(stamp.getStr()),
906 stamp.getLength() ) );
907 contentRemoved.writeStream( xData, true /* replace existing */ );
909 m_activePackagesDB->erase( id, fileName ); // to be removed upon next start
910 //remove any cached data hold by the backend
911 m_xRegistry->packageRemoved(xPackage->getURL(), xPackage->getPackageType()->getMediaType());
913 try_dispose( xPackage );
915 fireModified();
917 catch (const RuntimeException &) {
918 throw;
920 catch (const CommandFailedException & exc) {
921 logIntern( Any(exc) );
922 throw;
924 catch (const CommandAbortedException & exc) {
925 logIntern( Any(exc) );
926 throw;
928 catch (const deployment::DeploymentException & exc) {
929 logIntern( Any(exc) );
930 throw;
932 catch (const Exception &) {
933 Any exc( ::cppu::getCaughtException() );
934 logIntern( exc );
935 throw deployment::DeploymentException(
936 DpResId(RID_STR_ERROR_WHILE_REMOVING) + id,
937 static_cast<OWeakObject *>(this), exc );
942 OUString PackageManagerImpl::getDeployPath( ActivePackages::Data const & data )
944 OUStringBuffer buf( data.temporaryName );
945 //The bundled extensions are not contained in an additional folder
946 //with a unique name. data.temporaryName contains already the
947 //UTF8 encoded folder name. See PackageManagerImpl::synchronize
948 if (m_context != "bundled")
950 buf.append( "_/"
951 + ::rtl::Uri::encode( data.fileName, rtl_UriCharClassPchar,
952 rtl_UriEncodeIgnoreEscapes,
953 RTL_TEXTENCODING_UTF8 ) );
955 return makeURL( m_activePackages, buf.makeStringAndClear() );
959 Reference<deployment::XPackage> PackageManagerImpl::getDeployedPackage_(
960 OUString const & id, OUString const & fileName,
961 Reference<XCommandEnvironment> const & xCmdEnv )
963 ActivePackages::Data val;
964 if (m_activePackagesDB->get( &val, id, fileName ))
966 return getDeployedPackage_( id, val, xCmdEnv );
968 throw lang::IllegalArgumentException(
969 DpResId(RID_STR_NO_SUCH_PACKAGE) + id,
970 static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
974 Reference<deployment::XPackage> PackageManagerImpl::getDeployedPackage_(
975 std::u16string_view id, ActivePackages::Data const & data,
976 Reference<XCommandEnvironment> const & xCmdEnv, bool ignoreAlienPlatforms )
978 if (ignoreAlienPlatforms)
980 OUString type, subType;
981 INetContentTypeParameterList params;
982 if (INetContentTypes::parse( data.mediaType, type, subType, &params ))
984 auto const iter = params.find(OString("platform"));
985 if (iter != params.end() && !platform_fits(iter->second.m_sValue))
986 throw lang::IllegalArgumentException(
987 DpResId(RID_STR_NO_SUCH_PACKAGE) + id,
988 static_cast<OWeakObject *>(this),
989 static_cast<sal_Int16>(-1) );
992 Reference<deployment::XPackage> xExtension;
995 //Ignore extensions where XPackage::checkPrerequisites failed.
996 //They must not be usable for this user.
997 if (data.failedPrerequisites == "0")
999 xExtension = m_xRegistry->bindPackage(
1000 getDeployPath( data ), data.mediaType, false, OUString(), xCmdEnv );
1003 catch (const deployment::InvalidRemovedParameterException& e)
1005 xExtension = e.Extension;
1007 return xExtension;
1011 Sequence< Reference<deployment::XPackage> >
1012 PackageManagerImpl::getDeployedPackages_(
1013 Reference<XCommandEnvironment> const & xCmdEnv )
1015 std::vector< Reference<deployment::XPackage> > packages;
1016 ActivePackages::Entries id2temp( m_activePackagesDB->getEntries() );
1017 for (auto const& elem : id2temp)
1019 if (elem.second.failedPrerequisites != "0")
1020 continue;
1021 try {
1022 packages.push_back(
1023 getDeployedPackage_(
1024 elem.first, elem.second, xCmdEnv,
1025 true /* xxx todo: think of GUI:
1026 ignore other platforms than the current one */ ) );
1028 catch (const lang::IllegalArgumentException &) {
1029 // ignore
1030 TOOLS_WARN_EXCEPTION( "desktop", "" );
1032 catch (const deployment::DeploymentException&) {
1033 // ignore
1034 TOOLS_WARN_EXCEPTION( "desktop", "" );
1037 return comphelper::containerToSequence(packages);
1041 Reference<deployment::XPackage> PackageManagerImpl::getDeployedPackage(
1042 OUString const & id, OUString const & fileName,
1043 Reference<XCommandEnvironment> const & xCmdEnv_ )
1045 check();
1046 Reference<XCommandEnvironment> xCmdEnv;
1047 if (m_xLogFile.is())
1048 xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv_, m_xLogFile ) );
1049 else
1050 xCmdEnv.set( xCmdEnv_ );
1052 try {
1053 const ::osl::MutexGuard guard( m_aMutex );
1054 return getDeployedPackage_( id, fileName, xCmdEnv );
1056 catch (const RuntimeException &) {
1057 throw;
1059 catch (const CommandFailedException & exc) {
1060 logIntern( Any(exc) );
1061 throw;
1063 catch (const deployment::DeploymentException & exc) {
1064 logIntern( Any(exc) );
1065 throw;
1067 catch (const Exception &) {
1068 Any exc( ::cppu::getCaughtException() );
1069 logIntern( exc );
1070 throw deployment::DeploymentException(
1071 // ought never occur...
1072 "error while accessing deployed package: " + id,
1073 static_cast<OWeakObject *>(this), exc );
1078 Sequence< Reference<deployment::XPackage> >
1079 PackageManagerImpl::getDeployedPackages(
1080 Reference<task::XAbortChannel> const &,
1081 Reference<XCommandEnvironment> const & xCmdEnv_ )
1083 check();
1084 Reference<XCommandEnvironment> xCmdEnv;
1085 if (m_xLogFile.is())
1086 xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv_, m_xLogFile ) );
1087 else
1088 xCmdEnv.set( xCmdEnv_ );
1090 try {
1091 const ::osl::MutexGuard guard( m_aMutex );
1092 return getDeployedPackages_( xCmdEnv );
1094 catch (const RuntimeException &) {
1095 throw;
1097 catch (const CommandFailedException & exc) {
1098 logIntern( Any(exc) );
1099 throw;
1101 catch (const CommandAbortedException & exc) {
1102 logIntern( Any(exc) );
1103 throw;
1105 catch (const deployment::DeploymentException & exc) {
1106 logIntern( Any(exc) );
1107 throw;
1109 catch (const Exception &) {
1110 Any exc( ::cppu::getCaughtException() );
1111 logIntern( exc );
1112 throw deployment::DeploymentException(
1113 // ought never occur...
1114 "error while getting all deployed packages: " + m_context,
1115 static_cast<OWeakObject *>(this), exc );
1120 //ToDo: the function must not call registerPackage, do this in
1121 //XExtensionManager.reinstallDeployedExtensions
1122 void PackageManagerImpl::reinstallDeployedPackages(
1123 sal_Bool force, Reference<task::XAbortChannel> const & /*xAbortChannel*/,
1124 Reference<XCommandEnvironment> const & xCmdEnv_ )
1126 check();
1127 if (!force && office_is_running())
1128 throw RuntimeException(
1129 "You must close any running Office process before reinstalling packages!",
1130 static_cast<OWeakObject *>(this) );
1132 Reference<XCommandEnvironment> xCmdEnv;
1133 if (m_xLogFile.is())
1134 xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv_, m_xLogFile ) );
1135 else
1136 xCmdEnv.set( xCmdEnv_ );
1138 try {
1139 ProgressLevel progress(
1140 xCmdEnv, "Reinstalling all deployed packages..." );
1142 try_dispose( m_xRegistry );
1143 m_xRegistry.clear();
1144 if (!m_registryCache.isEmpty())
1145 erase_path( m_registryCache, xCmdEnv );
1146 initRegistryBackends();
1147 Reference<util::XUpdatable> xUpdatable( m_xRegistry, UNO_QUERY );
1148 if (xUpdatable.is())
1149 xUpdatable->update();
1151 //registering is done by the ExtensionManager service.
1153 catch (const RuntimeException &) {
1154 throw;
1156 catch (const CommandFailedException & exc) {
1157 logIntern( Any(exc) );
1158 throw;
1160 catch (const CommandAbortedException & exc) {
1161 logIntern( Any(exc) );
1162 throw;
1164 catch (const deployment::DeploymentException & exc) {
1165 logIntern( Any(exc) );
1166 throw;
1168 catch (const Exception &) {
1169 Any exc( ::cppu::getCaughtException() );
1170 logIntern( exc );
1171 throw deployment::DeploymentException(
1172 "Error while reinstalling all previously deployed packages of context " + m_context,
1173 static_cast<OWeakObject *>(this), exc );
1178 sal_Bool SAL_CALL PackageManagerImpl::isReadOnly( )
1180 return m_readOnly;
1182 bool PackageManagerImpl::synchronizeRemovedExtensions(
1183 Reference<task::XAbortChannel> const & xAbortChannel,
1184 Reference<css::ucb::XCommandEnvironment> const & xCmdEnv)
1187 //find all which are in the extension data base but which
1188 //are removed already.
1189 OSL_ASSERT(!(m_context == "user"));
1190 bool bModified = false;
1191 ActivePackages::Entries id2temp( m_activePackagesDB->getEntries() );
1193 bool bShared = (m_context == "shared");
1195 for (auto const& elem : id2temp)
1199 //Get the URL to the extensions folder, first make the url for the
1200 //shared repository including the temporary name
1201 OUString url = makeURL(m_activePackages, elem.second.temporaryName);
1202 if (bShared)
1203 url = makeURLAppendSysPathSegment( Concat2View(url + "_"), elem.second.fileName);
1205 bool bRemoved = false;
1206 //Check if the URL to the extension is still the same
1207 ::ucbhelper::Content contentExtension;
1209 if (!create_ucb_content(
1210 &contentExtension, url,
1211 Reference<XCommandEnvironment>(), false))
1213 bRemoved = true;
1216 //The folder is in the extension database, but it can still be deleted.
1217 //look for the xxx.tmpremoved file
1218 //There can also be the case that a different extension was installed
1219 //in a "temp" folder with name that is already used.
1220 if (!bRemoved && bShared)
1222 ::ucbhelper::Content contentRemoved;
1224 if (create_ucb_content(
1225 &contentRemoved,
1226 m_activePackages_expanded + "/" +
1227 elem.second.temporaryName + "removed",
1228 Reference<XCommandEnvironment>(), false))
1230 bRemoved = true;
1234 if (!bRemoved)
1236 //There may be another extensions at the same place
1237 dp_misc::DescriptionInfoset infoset =
1238 dp_misc::getDescriptionInfoset(url);
1239 OSL_ENSURE(infoset.hasDescription() && infoset.getIdentifier(),
1240 "Extension Manager: bundled and shared extensions "
1241 "must have an identifier and a version");
1242 if (infoset.hasDescription() &&
1243 infoset.getIdentifier() &&
1244 ( elem.first != *(infoset.getIdentifier())
1245 || elem.second.version != infoset.getVersion()))
1247 bRemoved = true;
1251 if (bRemoved)
1253 Reference<deployment::XPackage> xPackage = m_xRegistry->bindPackage(
1254 url, elem.second.mediaType, true, elem.first, xCmdEnv );
1255 OSL_ASSERT(xPackage.is()); //Even if the files are removed, we must get the object.
1256 xPackage->revokePackage(true, xAbortChannel, xCmdEnv);
1257 removePackage(xPackage->getIdentifier().Value, xPackage->getName(),
1258 xAbortChannel, xCmdEnv);
1259 bModified = true;
1262 catch( const uno::Exception & )
1264 TOOLS_WARN_EXCEPTION("desktop.deployment", "");
1267 return bModified;
1271 bool PackageManagerImpl::synchronizeAddedExtensions(
1272 Reference<task::XAbortChannel> const & xAbortChannel,
1273 Reference<css::ucb::XCommandEnvironment> const & xCmdEnv)
1275 bool bModified = false;
1276 OSL_ASSERT(!(m_context == "user"));
1278 ActivePackages::Entries id2temp( m_activePackagesDB->getEntries() );
1279 //check if the folder exist at all. The shared extension folder
1280 //may not exist for a normal user.
1281 bool bOk=true;
1284 bOk = create_ucb_content(
1285 nullptr, m_activePackages_expanded, Reference<css::ucb::XCommandEnvironment>(), false);
1287 catch (const css::ucb::ContentCreationException&)
1289 bOk = false;
1292 if (!bOk)
1293 return bModified;
1295 ::ucbhelper::Content tempFolder( m_activePackages_expanded, xCmdEnv, m_xComponentContext );
1296 Reference<sdbc::XResultSet> xResultSet(
1297 StrTitle::createCursor( tempFolder,
1298 ::ucbhelper::INCLUDE_FOLDERS_ONLY ) );
1300 while (xResultSet->next())
1304 OUString title(
1305 Reference<sdbc::XRow>(
1306 xResultSet, UNO_QUERY_THROW )->getString(
1307 1 /* Title */ ) );
1308 //The temporary folders of user and shared have an '_' at then end.
1309 //But the name in ActivePackages.temporaryName is saved without.
1310 OUString title2 = title;
1311 bool bShared = (m_context == "shared");
1312 if (bShared)
1314 OSL_ASSERT(title2.endsWith("_"));
1315 title2 = title2.copy(0, title2.getLength() -1);
1317 OUString titleEncoded = ::rtl::Uri::encode(
1318 title2, rtl_UriCharClassPchar,
1319 rtl_UriEncodeIgnoreEscapes,
1320 RTL_TEXTENCODING_UTF8);
1322 //It is sufficient to check for the folder name, because when the administrator
1323 //installed the extension it was already checked if there is one with the
1324 //same identifier.
1325 const MatchTempDir match(titleEncoded);
1326 if (std::none_of( id2temp.begin(), id2temp.end(), match ))
1329 // The folder was not found in the data base, so it must be
1330 // an added extension
1331 OUString url(m_activePackages_expanded + "/" + titleEncoded);
1332 OUString sExtFolder;
1333 if (bShared) //that is, shared
1335 //Check if the extension was not "deleted" already which is indicated
1336 //by a xxx.tmpremoved file
1337 ::ucbhelper::Content contentRemoved;
1338 if (create_ucb_content(&contentRemoved, url + "removed",
1339 Reference<XCommandEnvironment>(), false))
1340 continue;
1341 sExtFolder = getExtensionFolder(
1342 m_activePackages_expanded + "/" + titleEncoded + "_",
1343 xCmdEnv, m_xComponentContext);
1344 url = makeURLAppendSysPathSegment(m_activePackages_expanded, title);
1345 url = makeURLAppendSysPathSegment(url, sExtFolder);
1347 Reference<deployment::XPackage> xPackage = m_xRegistry->bindPackage(
1348 url, OUString(), false, OUString(), xCmdEnv );
1349 if (xPackage.is())
1351 OUString id = dp_misc::getIdentifier( xPackage );
1353 //Prepare the database entry
1354 ActivePackages::Data dbData;
1356 dbData.temporaryName = titleEncoded;
1357 if (bShared)
1358 dbData.fileName = sExtFolder;
1359 else
1360 dbData.fileName = title;
1361 dbData.mediaType = xPackage->getPackageType()->getMediaType();
1362 dbData.version = xPackage->getVersion();
1363 SAL_WARN_IF(
1364 dbData.version.isEmpty(), "desktop.deployment",
1365 "bundled/shared extension " << id << " at <" << url
1366 << "> has no explicit version");
1368 //We provide a special command environment that will prevent
1369 //showing a license if simple-license/@accept-by = "admin"
1370 //It will also prevent showing the license for bundled extensions
1371 //which is not supported.
1372 OSL_ASSERT(!(m_context == "user"));
1374 // shall the license be suppressed?
1375 DescriptionInfoset info =
1376 dp_misc::getDescriptionInfoset(url);
1377 ::std::optional<dp_misc::SimpleLicenseAttributes>
1378 attr = info.getSimpleLicenseAttributes();
1379 ExtensionProperties props(url, xCmdEnv, m_xComponentContext);
1380 bool bNoLicense = false;
1381 if (attr && attr->suppressIfRequired && props.isSuppressedLicense())
1382 bNoLicense = true;
1384 Reference<ucb::XCommandEnvironment> licCmdEnv(
1385 new LicenseCommandEnv(xCmdEnv->getInteractionHandler(),
1386 bNoLicense, m_context));
1387 sal_Int32 failedPrereq = xPackage->checkPrerequisites(
1388 xAbortChannel, licCmdEnv, false);
1389 //Remember that this failed. For example, the user
1390 //could have declined the license. Then the next time the
1391 //extension folder is investigated we do not want to
1392 //try to install the extension again.
1393 dbData.failedPrerequisites = OUString::number(failedPrereq);
1394 insertToActivationLayerDB(id, dbData);
1395 bModified = true;
1399 catch (const uno::Exception &)
1401 // Looks like exceptions being caught here is not an uncommon case.
1402 TOOLS_WARN_EXCEPTION("desktop.deployment", "");
1405 return bModified;
1408 sal_Bool PackageManagerImpl::synchronize(
1409 Reference<task::XAbortChannel> const & xAbortChannel,
1410 Reference<css::ucb::XCommandEnvironment> const & xCmdEnv)
1412 check();
1413 bool bModified = false;
1414 if (m_context == "user")
1415 return bModified;
1416 bModified |=
1417 synchronizeRemovedExtensions(xAbortChannel, xCmdEnv);
1418 bModified |= synchronizeAddedExtensions(xAbortChannel, xCmdEnv);
1420 return bModified;
1423 Sequence< Reference<deployment::XPackage> > PackageManagerImpl::getExtensionsWithUnacceptedLicenses(
1424 Reference<ucb::XCommandEnvironment> const & xCmdEnv)
1426 std::vector<Reference<deployment::XPackage> > vec;
1430 const ::osl::MutexGuard guard( m_aMutex );
1431 // clean up activation layer, scan for zombie temp dirs:
1432 ActivePackages::Entries id2temp( m_activePackagesDB->getEntries() );
1434 bool bShared = (m_context == "shared");
1436 for (auto const& elem : id2temp)
1438 //Get the database entry
1439 ActivePackages::Data const & dbData = elem.second;
1440 sal_Int32 failedPrereq = dbData.failedPrerequisites.toInt32();
1441 //If the installation failed for other reason then the license then we
1442 //ignore it.
1443 if (failedPrereq ^ deployment::Prerequisites::LICENSE)
1444 continue;
1446 //Prepare the URL to the extension
1447 OUString url = makeURL(m_activePackages, elem.second.temporaryName);
1448 if (bShared)
1449 url = makeURLAppendSysPathSegment( Concat2View(url + "_"), elem.second.fileName);
1451 Reference<deployment::XPackage> p = m_xRegistry->bindPackage(
1452 url, OUString(), false, OUString(), xCmdEnv );
1454 if (p.is())
1455 vec.push_back(p);
1458 return ::comphelper::containerToSequence(vec);
1460 catch (const deployment::DeploymentException &)
1462 throw;
1464 catch (const RuntimeException&)
1466 throw;
1468 catch (...)
1470 Any exc = ::cppu::getCaughtException();
1471 deployment::DeploymentException de(
1472 "PackageManagerImpl::getExtensionsWithUnacceptedLicenses",
1473 static_cast<OWeakObject*>(this), exc);
1474 exc <<= de;
1475 ::cppu::throwException(exc);
1478 return ::comphelper::containerToSequence(vec);
1481 sal_Int32 PackageManagerImpl::checkPrerequisites(
1482 css::uno::Reference<css::deployment::XPackage> const & extension,
1483 css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
1484 css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv )
1488 if (!extension.is())
1489 return 0;
1490 if (m_context != extension->getRepositoryName())
1491 throw lang::IllegalArgumentException(
1492 "PackageManagerImpl::checkPrerequisites: extension is not from this repository.",
1493 nullptr, 0);
1495 ActivePackages::Data dbData;
1496 OUString id = dp_misc::getIdentifier(extension);
1497 if (!m_activePackagesDB->get( &dbData, id, OUString()))
1499 throw lang::IllegalArgumentException(
1500 "PackageManagerImpl::checkPrerequisites: unknown extension",
1501 nullptr, 0);
1504 //If the license was already displayed, then do not show it again
1505 Reference<ucb::XCommandEnvironment> _xCmdEnv = xCmdEnv;
1506 sal_Int32 prereq = dbData.failedPrerequisites.toInt32();
1507 if ( !(prereq & deployment::Prerequisites::LICENSE))
1508 _xCmdEnv = new NoLicenseCommandEnv(xCmdEnv->getInteractionHandler());
1510 sal_Int32 failedPrereq = extension->checkPrerequisites(
1511 xAbortChannel, _xCmdEnv, false);
1512 dbData.failedPrerequisites = OUString::number(failedPrereq);
1513 insertToActivationLayerDB(id, dbData);
1514 return 0;
1516 catch ( const deployment::DeploymentException& ) {
1517 throw;
1518 } catch ( const ucb::CommandFailedException & ) {
1519 throw;
1520 } catch ( const ucb::CommandAbortedException & ) {
1521 throw;
1522 } catch (const lang::IllegalArgumentException &) {
1523 throw;
1524 } catch (const uno::RuntimeException &) {
1525 throw;
1526 } catch (...) {
1527 uno::Any excOccurred = ::cppu::getCaughtException();
1528 deployment::DeploymentException exc(
1529 "PackageManagerImpl::checkPrerequisites: exception ",
1530 static_cast<OWeakObject*>(this), excOccurred);
1531 throw exc;
1536 PackageManagerImpl::CmdEnvWrapperImpl::~CmdEnvWrapperImpl()
1541 PackageManagerImpl::CmdEnvWrapperImpl::CmdEnvWrapperImpl(
1542 Reference<XCommandEnvironment> const & xUserCmdEnv,
1543 Reference<XProgressHandler> const & xLogFile )
1544 : m_xLogFile( xLogFile )
1546 if (xUserCmdEnv.is()) {
1547 m_xUserProgress.set( xUserCmdEnv->getProgressHandler() );
1548 m_xUserInteractionHandler.set( xUserCmdEnv->getInteractionHandler() );
1552 // XCommandEnvironment
1554 Reference<task::XInteractionHandler>
1555 PackageManagerImpl::CmdEnvWrapperImpl::getInteractionHandler()
1557 return m_xUserInteractionHandler;
1561 Reference<XProgressHandler>
1562 PackageManagerImpl::CmdEnvWrapperImpl::getProgressHandler()
1564 return this;
1567 // XProgressHandler
1569 void PackageManagerImpl::CmdEnvWrapperImpl::push( Any const & Status )
1571 if (m_xLogFile.is())
1572 m_xLogFile->push( Status );
1573 if (m_xUserProgress.is())
1574 m_xUserProgress->push( Status );
1578 void PackageManagerImpl::CmdEnvWrapperImpl::update( Any const & Status )
1580 if (m_xLogFile.is())
1581 m_xLogFile->update( Status );
1582 if (m_xUserProgress.is())
1583 m_xUserProgress->update( Status );
1587 void PackageManagerImpl::CmdEnvWrapperImpl::pop()
1589 if (m_xLogFile.is())
1590 m_xLogFile->pop();
1591 if (m_xUserProgress.is())
1592 m_xUserProgress->pop();
1595 } // namespace dp_manager
1597 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */