cid#1607171 Data race condition
[LibreOffice.git] / desktop / source / deployment / registry / package / dp_package.cxx
blob21b2fba254528aeb0a46eee29e549200d6ffef31
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 .
21 #include <strings.hrc>
22 #include <dp_package.hxx>
23 #include <dp_backend.h>
24 #include <dp_misc.h>
25 #include <dp_ucb.h>
26 #include <dp_interact.h>
27 #include <dp_dependencies.hxx>
28 #include <dp_platform.hxx>
29 #include <dp_descriptioninfoset.hxx>
30 #include <dp_identifier.hxx>
31 #include <dp_resource.h>
32 #include <rtl/uri.hxx>
33 #include <rtl/ustrbuf.hxx>
34 #include <sal/log.hxx>
35 #include <o3tl/string_view.hxx>
36 #include <cppuhelper/exc_hlp.hxx>
37 #include <cppuhelper/implbase.hxx>
38 #include <cppuhelper/supportsservice.hxx>
39 #include <ucbhelper/content.hxx>
40 #include <svl/inettype.hxx>
41 #include <comphelper/propertyvalue.hxx>
42 #include <comphelper/sequence.hxx>
43 #include <com/sun/star/lang/WrappedTargetException.hpp>
44 #include <com/sun/star/graphic/XGraphic.hpp>
45 #include <com/sun/star/graphic/GraphicProvider.hpp>
46 #include <com/sun/star/graphic/XGraphicProvider.hpp>
47 #include <com/sun/star/io/Pipe.hpp>
48 #include <com/sun/star/io/XOutputStream.hpp>
49 #include <com/sun/star/io/XInputStream.hpp>
50 #include <com/sun/star/task/InteractionClassification.hpp>
51 #include <com/sun/star/task/XInteractionApprove.hpp>
52 #include <com/sun/star/ucb/CommandAbortedException.hpp>
53 #include <com/sun/star/ucb/CommandFailedException.hpp>
54 #include <com/sun/star/ucb/ContentCreationException.hpp>
55 #include <com/sun/star/ucb/XInteractionReplaceExistingData.hpp>
56 #include <com/sun/star/ucb/NameClashResolveRequest.hpp>
57 #include <com/sun/star/ucb/XContentAccess.hpp>
58 #include <com/sun/star/ucb/NameClash.hpp>
59 #include <com/sun/star/ucb/UnsupportedCommandException.hpp>
60 #include <com/sun/star/sdbc/XResultSet.hpp>
61 #include <com/sun/star/sdbc/XRow.hpp>
62 #include <com/sun/star/packages/manifest/ManifestReader.hpp>
63 #include <com/sun/star/packages/manifest/ManifestWriter.hpp>
64 #include <com/sun/star/deployment/DependencyException.hpp>
65 #include <com/sun/star/deployment/DeploymentException.hpp>
66 #include <com/sun/star/deployment/ExtensionRemovedException.hpp>
67 #include <com/sun/star/deployment/LicenseException.hpp>
68 #include <com/sun/star/deployment/PlatformException.hpp>
69 #include <com/sun/star/deployment/Prerequisites.hpp>
70 #include <optional>
71 #include <comphelper/diagnose_ex.hxx>
73 #include <algorithm>
74 #include <memory>
75 #include <string_view>
76 #include <utility>
77 #include <vector>
79 #include "dp_extbackenddb.hxx"
80 using namespace ::dp_misc;
81 using namespace ::com::sun::star;
82 using namespace ::com::sun::star::uno;
85 namespace dp_registry::backend::bundle {
86 namespace {
88 typedef cppu::ImplInheritanceHelper<PackageRegistryBackend> ImplBaseT;
91 class BackendImpl : public ImplBaseT
93 class PackageImpl : public ::dp_registry::backend::Package
95 BackendImpl * getMyBackend() const;
96 /** contains the old tooltip description for the Extension Manager GUI in OOo v.2.x
97 We keep it for backward compatibility.
99 OUString m_oldDescription;
100 OUString m_url_expanded;
101 const bool m_legacyBundle;
102 Sequence< Reference<deployment::XPackage> > m_bundle;
103 Sequence< Reference<deployment::XPackage> > * m_pBundle;
105 ExtensionBackendDb::Data m_dbData;
107 Reference<deployment::XPackage> bindBundleItem(
108 OUString const & url, OUString const & mediaType,
109 bool bRemoved, //that is, using data base information
110 OUString const & identifier,
111 Reference<ucb::XCommandEnvironment> const & xCmdEnv,
112 bool notifyDetectionError = true );
114 typedef std::vector< Reference<deployment::XPackage> > t_packagevec;
115 void scanBundle(
116 t_packagevec & bundle,
117 ::rtl::Reference<AbortChannel> const & abortChannel,
118 Reference<ucb::XCommandEnvironment> const & xCmdEnv );
119 void scanLegacyBundle(
120 t_packagevec & bundle,
121 OUString const & url,
122 ::rtl::Reference<AbortChannel> const & abortChannel,
123 Reference<ucb::XCommandEnvironment> const & xCmdEnv,
124 bool skip_registration = false );
125 std::vector<Reference<deployment::XPackage> > getPackagesFromDb(
126 Reference<ucb::XCommandEnvironment> const & xCmdEnv);
127 bool checkPlatform(
128 Reference<ucb::XCommandEnvironment > const & environment);
130 bool checkDependencies(
131 Reference<ucb::XCommandEnvironment > const &
132 environment,
133 DescriptionInfoset const & description);
134 // throws css::uno::RuntimeException,
135 // css::deployment::DeploymentException
137 /// @throws deployment::DeploymentException
138 /// @throws ucb::CommandFailedException
139 /// @throws ucb::CommandAbortedException
140 /// @throws RuntimeException
141 bool checkLicense(
142 Reference< ucb::XCommandEnvironment > const & xCmdEnv,
143 DescriptionInfoset const & description, bool bNoLicenseChecking);
144 // @throws DeploymentException
145 OUString getTextFromURL(
146 const Reference< ucb::XCommandEnvironment >& xCmdEnv,
147 const OUString& licenseUrl);
149 DescriptionInfoset getDescriptionInfoset() const;
151 // Package
152 virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
153 ::osl::ResettableMutexGuard & guard,
154 ::rtl::Reference<AbortChannel> const & abortChannel,
155 Reference<ucb::XCommandEnvironment> const & xCmdEnv ) override;
156 virtual void processPackage_(
157 ::osl::ResettableMutexGuard & guard,
158 bool registerPackage,
159 bool startup,
160 ::rtl::Reference<AbortChannel> const & abortChannel,
161 Reference<ucb::XCommandEnvironment> const & xCmdEnv ) override;
163 virtual void SAL_CALL disposing() override;
166 public:
167 PackageImpl(
168 ::rtl::Reference<PackageRegistryBackend> const & myBackend,
169 OUString const & url,
170 OUString const & name,
171 Reference<deployment::XPackageTypeInfo> const & xPackageType,
172 bool legacyBundle,
173 bool bRemoved,
174 OUString const & identifier);
176 // XPackage
177 virtual sal_Bool SAL_CALL isBundle() override;
179 virtual Sequence< Reference<deployment::XPackage> > SAL_CALL getBundle(
180 Reference<task::XAbortChannel> const & xAbortChannel,
181 Reference<ucb::XCommandEnvironment> const & xCmdEnv ) override;
182 virtual OUString SAL_CALL getDescription() override;
184 virtual OUString SAL_CALL getLicenseText() override;
186 virtual void SAL_CALL exportTo(
187 OUString const & destFolderURL, OUString const & newTitle,
188 sal_Int32 nameClashAction,
189 Reference<ucb::XCommandEnvironment> const & xCmdEnv ) override;
191 virtual ::sal_Int32 SAL_CALL checkPrerequisites(
192 const Reference< task::XAbortChannel >& xAbortChannel,
193 const Reference< ucb::XCommandEnvironment >& xCmdEnv,
194 sal_Bool noLicenseChecking) override;
196 virtual sal_Bool SAL_CALL checkDependencies(
197 const Reference< ucb::XCommandEnvironment >& xCmdEnv ) override;
199 virtual beans::Optional<OUString> SAL_CALL getIdentifier() override;
201 virtual OUString SAL_CALL getVersion() override;
203 virtual Sequence<OUString> SAL_CALL getUpdateInformationURLs() override;
205 virtual beans::StringPair SAL_CALL getPublisherInfo() override;
207 virtual OUString SAL_CALL getDisplayName() override;
209 virtual Reference< graphic::XGraphic > SAL_CALL
210 getIcon( sal_Bool bHighContrast ) override;
212 friend class PackageImpl;
214 Reference<deployment::XPackageRegistry> m_xRootRegistry;
215 const Reference<deployment::XPackageTypeInfo> m_xBundleTypeInfo;
216 const Reference<deployment::XPackageTypeInfo> m_xLegacyBundleTypeInfo;
217 Sequence< Reference<deployment::XPackageTypeInfo> > m_typeInfos;
219 std::unique_ptr<ExtensionBackendDb> m_backendDb;
221 void addDataToDb(OUString const & url, ExtensionBackendDb::Data const & data);
222 ExtensionBackendDb::Data readDataFromDb(std::u16string_view url);
223 void revokeEntryFromDb(std::u16string_view url);
225 // PackageRegistryBackend
226 virtual Reference<deployment::XPackage> bindPackage_(
227 OUString const & url, OUString const & mediaType,
228 bool bRemoved, OUString const & identifier,
229 Reference<ucb::XCommandEnvironment> const & xCmdEnv ) override;
231 virtual void SAL_CALL disposing() override;
233 public:
234 BackendImpl(
235 Sequence<Any> const & args,
236 Reference<XComponentContext> const & xComponentContext,
237 Reference<deployment::XPackageRegistry> const & xRootRegistry );
239 // XServiceInfo
240 virtual OUString SAL_CALL getImplementationName() override;
241 virtual sal_Bool SAL_CALL supportsService( OUString const& name ) override;
242 virtual Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
244 // XPackageRegistry
245 virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
246 getSupportedPackageTypes() override;
247 virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
249 using ImplBaseT::disposing;
252 //Used to find a XPackage with a particular URL
253 class XPackage_eq
255 OUString m_URL;
256 public:
257 explicit XPackage_eq(OUString s) : m_URL(std::move(s)) {}
258 bool operator() (const Reference<deployment::XPackage> & p) const
260 return m_URL == p->getURL();
265 BackendImpl::BackendImpl(
266 Sequence<Any> const & args,
267 Reference<XComponentContext> const & xComponentContext,
268 Reference<deployment::XPackageRegistry> const & xRootRegistry )
269 : ImplBaseT( args, xComponentContext ),
270 m_xRootRegistry( xRootRegistry ),
271 m_xBundleTypeInfo( new Package::TypeInfo(
272 u"application/vnd.sun.star.package-bundle"_ustr,
273 u"*.oxt;*.uno.pkg"_ustr,
274 DpResId(RID_STR_PACKAGE_BUNDLE)
275 ) ),
276 m_xLegacyBundleTypeInfo( new Package::TypeInfo(
277 u"application/vnd.sun.star.legacy-package-bundle"_ustr,
278 u"*.zip"_ustr,
279 m_xBundleTypeInfo->getShortDescription()
280 ) ),
281 m_typeInfos{ m_xBundleTypeInfo, m_xLegacyBundleTypeInfo }
283 if (!transientMode())
285 OUString dbFile = makeURL(getCachePath(), getImplementationName());
286 dbFile = makeURL(dbFile, u"backenddb.xml"_ustr);
287 m_backendDb.reset(
288 new ExtensionBackendDb(getComponentContext(), dbFile));
293 void BackendImpl::disposing()
295 m_xRootRegistry.clear();
296 PackageRegistryBackend::disposing();
299 // XServiceInfo
300 OUString BackendImpl::getImplementationName()
302 return u"com.sun.star.comp.deployment.bundle.PackageRegistryBackend"_ustr;
305 sal_Bool BackendImpl::supportsService(OUString const & ServiceName)
307 return cppu::supportsService(this, ServiceName);
310 Sequence<OUString> BackendImpl::getSupportedServiceNames()
312 return { BACKEND_SERVICE_NAME };
315 // XPackageRegistry
317 Sequence< Reference<deployment::XPackageTypeInfo> >
318 BackendImpl::getSupportedPackageTypes()
320 return m_typeInfos;
323 void BackendImpl::packageRemoved(OUString const & url, OUString const & /*mediaType*/)
325 //Notify the backend responsible for processing the different media
326 //types that this extension was removed.
327 ExtensionBackendDb::Data data = readDataFromDb(url);
328 for (auto const& item : data.items)
330 m_xRootRegistry->packageRemoved(item.first, item.second);
333 if (m_backendDb)
334 m_backendDb->removeEntry(url);
338 // PackageRegistryBackend
340 Reference<deployment::XPackage> BackendImpl::bindPackage_(
341 OUString const & url, OUString const & mediaType_,
342 bool bRemoved, OUString const & identifier,
343 Reference<ucb::XCommandEnvironment> const & xCmdEnv )
345 OUString mediaType( mediaType_ );
346 if (mediaType.isEmpty())
348 // detect media-type:
349 ::ucbhelper::Content ucbContent;
350 if (create_ucb_content( &ucbContent, url, xCmdEnv ))
352 if (ucbContent.isFolder())
354 //Every .oxt, uno.pkg file must contain a META-INF folder
355 ::ucbhelper::Content metaInfContent;
356 if (create_ucb_content(
357 &metaInfContent, makeURL( url, u"META-INF"_ustr ),
358 xCmdEnv, false /* no throw */ ))
360 mediaType = "application/vnd.sun.star.package-bundle";
362 //No support of legacy bundles, because every folder could be one.
364 else
366 const OUString title( StrTitle::getTitle( ucbContent ) );
367 if (title.endsWithIgnoreAsciiCase(".oxt") ||
368 title.endsWithIgnoreAsciiCase(".uno.pkg"))
369 mediaType = "application/vnd.sun.star.package-bundle";
370 else if (title.endsWithIgnoreAsciiCase(".zip"))
371 mediaType = "application/vnd.sun.star.legacy-package-bundle";
374 if (mediaType.isEmpty())
375 throw lang::IllegalArgumentException(
376 StrCannotDetectMediaType() + url,
377 static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
380 OUString type, subType;
381 INetContentTypeParameterList params;
382 if (INetContentTypes::parse( mediaType, type, subType, &params ))
384 if (type.equalsIgnoreAsciiCase("application"))
387 //In case a XPackage is created for a removed extension, we cannot
388 //obtain the name
389 OUString name;
390 if (!bRemoved)
392 ::ucbhelper::Content ucbContent(
393 url, xCmdEnv, getComponentContext() );
394 name = StrTitle::getTitle( ucbContent );
396 if (subType.equalsIgnoreAsciiCase("vnd.sun.star.package-bundle"))
398 return new PackageImpl(
399 this, url, name, m_xBundleTypeInfo, false, bRemoved,
400 identifier);
402 else if (subType.equalsIgnoreAsciiCase( "vnd.sun.star.legacy-package-bundle"))
404 return new PackageImpl(
405 this, url, name, m_xLegacyBundleTypeInfo, true, bRemoved,
406 identifier);
410 throw lang::IllegalArgumentException(
411 StrUnsupportedMediaType() + mediaType,
412 static_cast<OWeakObject *>(this),
413 static_cast<sal_Int16>(-1) );
416 void BackendImpl::addDataToDb(
417 OUString const & url, ExtensionBackendDb::Data const & data)
419 if (m_backendDb)
420 m_backendDb->addEntry(url, data);
423 ExtensionBackendDb::Data BackendImpl::readDataFromDb(
424 std::u16string_view url)
426 ExtensionBackendDb::Data data;
427 if (m_backendDb)
428 data = m_backendDb->getEntry(url);
429 return data;
432 void BackendImpl::revokeEntryFromDb(std::u16string_view url)
434 if (m_backendDb)
435 m_backendDb->revokeEntry(url);
439 BackendImpl::PackageImpl::PackageImpl(
440 ::rtl::Reference<PackageRegistryBackend> const & myBackend,
441 OUString const & url,
442 OUString const & name,
443 Reference<deployment::XPackageTypeInfo> const & xPackageType,
444 bool legacyBundle, bool bRemoved, OUString const & identifier)
445 : Package( myBackend, url, name, name /* display-name */,
446 xPackageType, bRemoved, identifier),
447 m_url_expanded( expandUnoRcUrl( url ) ),
448 m_legacyBundle( legacyBundle ),
449 m_pBundle( nullptr )
451 if (bRemoved)
452 m_dbData = getMyBackend()->readDataFromDb(url);
455 BackendImpl * BackendImpl::PackageImpl::getMyBackend() const
457 BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get());
458 if (nullptr == pBackend)
460 //May throw a DisposedException
461 check();
462 //We should never get here...
463 throw RuntimeException(u"Failed to get the BackendImpl"_ustr,
464 static_cast<OWeakObject*>(const_cast<PackageImpl *>(this)));
466 return pBackend;
469 void BackendImpl::PackageImpl::disposing()
471 for (auto& xPackage : m_bundle)
472 try_dispose(xPackage);
473 m_bundle.realloc( 0 );
475 Package::disposing();
478 // Package
480 beans::Optional< beans::Ambiguous<sal_Bool> >
481 BackendImpl::PackageImpl::isRegistered_(
482 ::osl::ResettableMutexGuard &,
483 ::rtl::Reference<AbortChannel> const & abortChannel,
484 Reference<ucb::XCommandEnvironment> const & xCmdEnv )
486 //In case the object was created for a removed extension (m_bRemoved = true)
487 //but the extension is not registered, then bundle will be empty. Then
488 //the return value will be Optional<...>.IsPresent= false. Although this is
489 //not true, this does not matter. Then registerPackage or revokePackage
490 //would never be called for the items. But since the extension is removed
491 //and not registered anyway, this does not matter.
492 const Sequence< Reference<deployment::XPackage> > bundle(
493 getBundle( abortChannel, xCmdEnv ) );
495 bool reg = false;
496 bool present = false;
497 bool ambig = false;
498 for ( sal_Int32 pos = bundle.getLength(); pos--; )
500 Reference<deployment::XPackage> const & xPackage = bundle[ pos ];
501 Reference<task::XAbortChannel> xSubAbortChannel(
502 xPackage->createAbortChannel() );
503 AbortChannel::Chain chain( abortChannel, xSubAbortChannel );
504 beans::Optional< beans::Ambiguous<sal_Bool> > option(
505 xPackage->isRegistered( xSubAbortChannel, xCmdEnv ) );
507 //present = true if at least one bundle item has this value.
508 //reg = true if all bundle items have an option value (option.IsPresent == 1)
509 //and all have value of true (option.Value.Value == true)
510 //If not, then the bundle has the status of not registered and ambiguous.
511 if (option.IsPresent)
513 beans::Ambiguous<sal_Bool> const & status = option.Value;
514 if (present)
516 //we never come here in the first iteration
517 if (reg != bool(status.Value)) {
519 ambig = true;
520 reg = false;
521 break;
524 else
526 //we always come here in the first iteration
527 reg = status.Value;
528 present = true;
532 return beans::Optional< beans::Ambiguous<sal_Bool> >(
533 present, beans::Ambiguous<sal_Bool>(reg, ambig) );
536 OUString BackendImpl::PackageImpl::getTextFromURL(
537 const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv,
538 const OUString& licenseUrl)
542 ::ucbhelper::Content descContent(
543 licenseUrl, xCmdEnv, getMyBackend()->getComponentContext());
544 std::vector<sal_Int8> seq = dp_misc::readFile(descContent);
545 return OUString( reinterpret_cast<char const *>(
546 seq.data()), seq.size(), RTL_TEXTENCODING_UTF8);
548 catch (const css::uno::Exception&)
550 Any exc( ::cppu::getCaughtException() );
551 throw css::deployment::DeploymentException(
552 "Could not read file " + licenseUrl, nullptr, exc);
557 DescriptionInfoset BackendImpl::PackageImpl::getDescriptionInfoset() const
559 return dp_misc::getDescriptionInfoset(m_url_expanded);
562 bool BackendImpl::PackageImpl::checkPlatform(
563 css::uno::Reference< css::ucb::XCommandEnvironment > const & environment)
565 bool ret = false;
566 DescriptionInfoset info(getDescriptionInfoset());
567 Sequence<OUString> platforms(info.getSupportedPlatforms());
568 if (hasValidPlatform(platforms))
570 ret = true;
572 else
574 ret = false;
575 OUString msg(
576 u"unsupported platform"_ustr);
577 Any e(
578 css::deployment::PlatformException(
579 msg, static_cast<OWeakObject *>(this), this));
580 if (!interactContinuation(
581 e, cppu::UnoType< css::task::XInteractionApprove >::get(),
582 environment, nullptr, nullptr))
584 throw css::deployment::DeploymentException(
585 msg, static_cast<OWeakObject *>(this), e);
588 return ret;
592 bool BackendImpl::PackageImpl::checkDependencies(
593 css::uno::Reference< css::ucb::XCommandEnvironment > const & environment,
594 DescriptionInfoset const & description)
596 css::uno::Sequence< css::uno::Reference< css::xml::dom::XElement > >
597 unsatisfied(dp_misc::Dependencies::check(description));
599 if (!unsatisfied.hasElements()) {
600 return true;
601 } else {
602 OUString msg(
603 u"unsatisfied dependencies"_ustr);
604 Any e(
605 css::deployment::DependencyException(
606 msg, static_cast<OWeakObject *>(this), unsatisfied));
607 if (!interactContinuation(
608 e, cppu::UnoType< css::task::XInteractionApprove >::get(),
609 environment, nullptr, nullptr))
611 throw css::deployment::DeploymentException(
612 msg, static_cast<OWeakObject *>(this), e);
614 return false;
618 bool BackendImpl::PackageImpl::checkLicense(
619 css::uno::Reference< css::ucb::XCommandEnvironment > const & xCmdEnv,
620 DescriptionInfoset const & info, bool alreadyInstalled)
624 ::std::optional<SimpleLicenseAttributes> simplLicAttr
625 = info.getSimpleLicenseAttributes();
626 if (! simplLicAttr)
627 return true;
628 OUString sLic = info.getLocalizedLicenseURL();
629 //If we do not get a localized licence then there is an error in the description.xml
630 //This should be handled by using a validating parser. Therefore we assume that no
631 //license is available.
632 if (sLic.isEmpty())
633 throw css::deployment::DeploymentException(
634 u"Could not obtain path to license. Possible error in description.xml"_ustr, nullptr, Any());
635 OUString sHref = m_url_expanded + "/" + sLic;
636 OUString sLicense = getTextFromURL(xCmdEnv, sHref);
637 ////determine who has to agree to the license
638 //check correct value for attribute
639 if ( simplLicAttr->acceptBy != "user" && simplLicAttr->acceptBy != "admin")
640 throw css::deployment::DeploymentException(
641 u"Could not obtain attribute simple-license@accept-by or it has no valid value"_ustr, nullptr, Any());
644 //Only use interaction if there is no version of this extension already installed
645 //and the suppress-on-update flag is not set for the new extension
646 // alreadyInstalled | bSuppressOnUpdate | show license
648 // 0 | 0 | 1
649 // 0 | 1 | 1
650 // 1 | 0 | 1
651 // 1 | 1 | 0
653 if ( !(alreadyInstalled && simplLicAttr->suppressOnUpdate))
655 css::deployment::LicenseException licExc(
656 OUString(), nullptr, getDisplayName(), sLicense,
657 simplLicAttr->acceptBy);
658 bool approve = false;
659 bool abort = false;
660 if (! interactContinuation(
661 Any(licExc), cppu::UnoType<task::XInteractionApprove>::get(), xCmdEnv, &approve, &abort ))
662 throw css::deployment::DeploymentException(
663 u"Could not interact with user."_ustr, nullptr, Any());
665 return approve;
667 return true;
668 } catch (const css::ucb::CommandFailedException&) {
669 throw;
670 } catch (const css::ucb::CommandAbortedException&) {
671 throw;
672 } catch (const css::deployment::DeploymentException&) {
673 throw;
674 } catch (const css::uno::RuntimeException&) {
675 throw;
676 } catch (const css::uno::Exception&) {
677 Any anyExc = cppu::getCaughtException();
678 throw css::deployment::DeploymentException(u"Unexpected exception"_ustr, nullptr, anyExc);
682 ::sal_Int32 BackendImpl::PackageImpl::checkPrerequisites(
683 const css::uno::Reference< css::task::XAbortChannel >&,
684 const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv,
685 sal_Bool alreadyInstalled)
687 if (m_bRemoved)
688 throw deployment::ExtensionRemovedException();
689 DescriptionInfoset info = getDescriptionInfoset();
690 if (!info.hasDescription())
691 return 0;
693 //always return LICENSE as long as the user did not accept the license
694 //so that XExtensionManager::checkPrerequisitesAndEnable will again
695 //check the license
696 if (!checkPlatform(xCmdEnv))
697 return deployment::Prerequisites::PLATFORM |
698 deployment::Prerequisites::LICENSE;
699 else if(!checkDependencies(xCmdEnv, info))
700 return deployment::Prerequisites::DEPENDENCIES |
701 deployment::Prerequisites::LICENSE;
702 else if(!checkLicense(xCmdEnv, info, alreadyInstalled))
703 return deployment::Prerequisites::LICENSE;
704 else
705 return 0;
708 sal_Bool BackendImpl::PackageImpl::checkDependencies(
709 const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv )
711 if (m_bRemoved)
712 throw deployment::ExtensionRemovedException();
713 DescriptionInfoset info = getDescriptionInfoset();
714 if (!info.hasDescription())
715 return true;
717 return checkDependencies(xCmdEnv, info);
720 beans::Optional<OUString> BackendImpl::PackageImpl::getIdentifier()
722 OUString identifier;
723 if (m_bRemoved)
724 identifier = m_identifier;
725 else
726 identifier = dp_misc::generateIdentifier(
727 getDescriptionInfoset().getIdentifier(), m_name);
729 return beans::Optional<OUString>(
730 true, identifier);
733 OUString BackendImpl::PackageImpl::getVersion()
735 if (m_bRemoved)
736 throw deployment::ExtensionRemovedException();
737 return getDescriptionInfoset().getVersion();
740 Sequence<OUString> BackendImpl::PackageImpl::getUpdateInformationURLs()
742 if (m_bRemoved)
743 throw deployment::ExtensionRemovedException();
744 return getDescriptionInfoset().getUpdateInformationUrls();
747 beans::StringPair BackendImpl::PackageImpl::getPublisherInfo()
749 if (m_bRemoved)
750 throw deployment::ExtensionRemovedException();
751 std::pair< OUString, OUString > aInfo = getDescriptionInfoset().getLocalizedPublisherNameAndURL();
752 beans::StringPair aStrPair( aInfo.first, aInfo.second );
753 return aStrPair;
757 uno::Reference< graphic::XGraphic > BackendImpl::PackageImpl::getIcon( sal_Bool bHighContrast )
759 if (m_bRemoved)
760 throw deployment::ExtensionRemovedException();
762 uno::Reference< graphic::XGraphic > xGraphic;
764 OUString aIconURL = getDescriptionInfoset().getIconURL( bHighContrast );
765 if ( !aIconURL.isEmpty() )
767 OUString aFullIconURL = m_url_expanded + "/" + aIconURL;
769 uno::Reference< XComponentContext > xContext( getMyBackend()->getComponentContext() );
770 uno::Reference< graphic::XGraphicProvider > xGraphProvider( graphic::GraphicProvider::create(xContext) );
772 uno::Sequence< beans::PropertyValue > aMediaProps{ comphelper::makePropertyValue(
773 u"URL"_ustr, aFullIconURL) };
774 xGraphic = xGraphProvider->queryGraphic( aMediaProps );
777 return xGraphic;
781 void BackendImpl::PackageImpl::processPackage_(
782 ::osl::ResettableMutexGuard & guard,
783 bool doRegisterPackage,
784 bool startup,
785 ::rtl::Reference<AbortChannel> const & abortChannel,
786 Reference<ucb::XCommandEnvironment> const & xCmdEnv )
788 const Sequence< Reference<deployment::XPackage> > bundle(
789 getBundle( abortChannel, xCmdEnv ) );
791 if (doRegisterPackage)
793 ExtensionBackendDb::Data data;
794 const sal_Int32 len = bundle.getLength();
795 for ( sal_Int32 pos = 0; pos < len; ++pos )
797 checkAborted(abortChannel);
798 Reference<deployment::XPackage> const & xPackage = bundle[ pos ];
799 Reference<task::XAbortChannel> xSubAbortChannel(
800 xPackage->createAbortChannel() );
801 AbortChannel::Chain chain( abortChannel, xSubAbortChannel );
802 try {
803 // tdf#159790 temporarily release mutex for child packages
804 // This code is normally run on a separate thread so if a
805 // child package tries to acquire the solar mutex, a deadlock
806 // can occur if the main thread calls isRegistered() on this
807 // package or any of its parents. So, temporarily release
808 // this package's mutex while registering the child package.
809 osl::ResettableMutexGuardScopedReleaser releaser(guard);
810 xPackage->registerPackage( startup, xSubAbortChannel, xCmdEnv );
812 catch (const Exception &)
814 //We even try a rollback if the user cancelled the action (CommandAbortedException)
815 //in order to prevent invalid database entries.
816 Any exc( ::cppu::getCaughtException() );
817 // try to handle exception, notify:
818 bool approve = false, abort = false;
819 if (! interactContinuation(
820 Any( lang::WrappedTargetException(
821 u"bundle item registration error!"_ustr,
822 static_cast<OWeakObject *>(this), exc ) ),
823 cppu::UnoType<task::XInteractionApprove>::get(), xCmdEnv,
824 &approve, &abort )) {
825 OSL_ASSERT( !approve && !abort );
826 if (m_legacyBundle) // default for legacy packages: ignore
827 continue;
828 // no selection at all, so rethrow;
829 // no C++ rethrow after getCaughtException(),
830 // see cppuhelper/exc_hlp.hxx:
831 ::cppu::throwException(exc);
833 if (approve && !abort) // ignore error, just continue
834 continue;
837 ProgressLevel progress( xCmdEnv, u"rollback..."_ustr );
838 // try rollback
839 for ( ; pos--; )
841 try {
842 bundle[ pos ]->revokePackage(
843 startup, xSubAbortChannel, xCmdEnv );
845 catch (const Exception &)
847 TOOLS_WARN_EXCEPTION( "desktop", "" );
848 // ignore any errors of rollback
851 progress.update( u"rollback finished."_ustr );
854 deployment::DeploymentException dpExc;
855 if (exc >>= dpExc) {
856 throw ucb::CommandFailedException(
857 dpExc.Message, dpExc.Context, dpExc.Cause );
859 else {
860 // rethrow CommandFailedException
861 ::cppu::throwException(exc);
865 data.items.emplace_back(xPackage->getURL(),
866 xPackage->getPackageType()->getMediaType());
868 getMyBackend()->addDataToDb(getURL(), data);
870 else
872 // revoke in reverse order:
873 for ( sal_Int32 pos = bundle.getLength(); pos--; )
875 checkAborted(abortChannel);
876 Reference<deployment::XPackage> const & xPackage = bundle[ pos ];
877 Reference<task::XAbortChannel> xSubAbortChannel(
878 xPackage->createAbortChannel() );
879 AbortChannel::Chain chain( abortChannel, xSubAbortChannel );
880 try {
881 bundle[ pos ]->revokePackage(
882 startup, xSubAbortChannel, xCmdEnv );
884 catch (const RuntimeException &) {
885 throw;
887 catch (const ucb::CommandAbortedException &) {
888 throw;
890 catch (const Exception &) {
891 // CommandFailedException, DeploymentException:
892 Any exc( ::cppu::getCaughtException() );
893 // try to handle exception, notify:
894 bool approve = false, abort = false;
895 if (! interactContinuation(
896 Any( lang::WrappedTargetException(
897 u"bundle item revocation error!"_ustr,
898 static_cast<OWeakObject *>(this), exc ) ),
899 cppu::UnoType<task::XInteractionApprove>::get(), xCmdEnv,
900 &approve, &abort )) {
901 OSL_ASSERT( !approve && !abort );
902 if (m_legacyBundle) // default for legacy packages: ignore
903 continue;
904 // no selection at all, so rethrow
905 // no C++ rethrow after getCaughtException(),
906 // see cppuhelper/exc_hlp.hxx:
907 ::cppu::throwException(exc);
909 // ignore errors when revoking, although abort may have been
910 // selected
913 getMyBackend()->revokeEntryFromDb(getURL());
918 OUString BackendImpl::PackageImpl::getDescription()
920 if (m_bRemoved)
921 throw deployment::ExtensionRemovedException();
923 const OUString sRelativeURL(getDescriptionInfoset().getLocalizedDescriptionURL());
924 OUString sDescription;
925 if (!sRelativeURL.isEmpty())
927 OUString sURL = m_url_expanded + "/" + sRelativeURL;
931 sDescription = getTextFromURL( css::uno::Reference< css::ucb::XCommandEnvironment >(), sURL );
933 catch ( const css::deployment::DeploymentException& )
935 TOOLS_WARN_EXCEPTION( "desktop", "" );
939 if (!sDescription.isEmpty())
940 return sDescription;
941 return m_oldDescription;
945 OUString BackendImpl::PackageImpl::getLicenseText()
947 if (m_bRemoved)
948 throw deployment::ExtensionRemovedException();
950 OUString sLicense;
951 DescriptionInfoset aInfo = getDescriptionInfoset();
953 ::std::optional< SimpleLicenseAttributes > aSimplLicAttr = aInfo.getSimpleLicenseAttributes();
954 if ( aSimplLicAttr )
956 OUString aLicenseURL = aInfo.getLocalizedLicenseURL();
958 if ( !aLicenseURL.isEmpty() )
960 OUString aFullURL = m_url_expanded + "/" + aLicenseURL;
961 sLicense = getTextFromURL( Reference< ucb::XCommandEnvironment >(), aFullURL);
965 return sLicense;
969 void BackendImpl::PackageImpl::exportTo(
970 OUString const & destFolderURL, OUString const & newTitle,
971 sal_Int32 nameClashAction, Reference<ucb::XCommandEnvironment> const & xCmdEnv )
973 if (m_bRemoved)
974 throw deployment::ExtensionRemovedException();
976 ::ucbhelper::Content sourceContent(
977 m_url_expanded, xCmdEnv, getMyBackend()->getComponentContext() );
978 OUString title(newTitle);
979 if (title.isEmpty())
980 sourceContent.getPropertyValue( u"Title"_ustr ) >>= title;
981 OUString destURL( makeURL( destFolderURL, ::rtl::Uri::encode(
982 title, rtl_UriCharClassPchar,
983 rtl_UriEncodeIgnoreEscapes,
984 RTL_TEXTENCODING_UTF8 ) ) );
986 if (nameClashAction == ucb::NameClash::ASK)
988 if (create_ucb_content(
989 nullptr, destURL, xCmdEnv, false /* no throw */ )) {
990 bool replace = false, abort = false;
991 if (! interactContinuation(
992 Any( ucb::NameClashResolveRequest(
993 "file already exists: " + title,
994 static_cast<OWeakObject *>(this),
995 task::InteractionClassification_QUERY,
996 destFolderURL, title, OUString() ) ),
997 cppu::UnoType<ucb::XInteractionReplaceExistingData>::get(), xCmdEnv,
998 &replace, &abort ) || !replace) {
999 return;
1003 else if (nameClashAction != ucb::NameClash::OVERWRITE) {
1004 throw ucb::CommandFailedException(u"unsupported nameClashAction!"_ustr,
1005 static_cast<OWeakObject *>(this), Any() );
1007 erase_path( destURL, xCmdEnv );
1009 OUString destFolder =
1010 "vnd.sun.star.zip://" +
1011 ::rtl::Uri::encode( destURL,
1012 rtl_UriCharClassRegName,
1013 rtl_UriEncodeIgnoreEscapes,
1014 RTL_TEXTENCODING_UTF8 ) +
1015 "/";
1017 ::ucbhelper::Content destFolderContent(
1018 destFolder, xCmdEnv, getMyBackend()->getComponentContext() );
1020 // transfer every item of folder into zip:
1021 Reference<sdbc::XResultSet> xResultSet(
1022 sourceContent.createCursor( Sequence<OUString>() ) );
1023 ProgressLevel progress( xCmdEnv, OUString() );
1024 while (xResultSet->next())
1026 ::ucbhelper::Content subContent(
1027 Reference<ucb::XContentAccess>(
1028 xResultSet, UNO_QUERY_THROW )->queryContent(),
1029 xCmdEnv, getMyBackend()->getComponentContext() );
1030 destFolderContent.transferContent(
1031 subContent, ::ucbhelper::InsertOperation::Copy,
1032 OUString(), ucb::NameClash::OVERWRITE );
1033 progress.update( Any() ); // animating progress bar
1037 // assure META-INF folder:
1038 ::ucbhelper::Content metainfFolderContent;
1039 create_folder( &metainfFolderContent,
1040 makeURL( destFolderContent.getURL(), u"META-INF"_ustr ),
1041 xCmdEnv );
1043 if (m_legacyBundle)
1045 // easy to migrate legacy bundles to new format:
1046 // just export them once using a .oxt name!
1047 // set detected media-types of any bundle item:
1049 // collect all manifest entries:
1050 Sequence< Reference<deployment::XPackage> > bundle;
1051 try {
1052 bundle = getBundle( Reference<task::XAbortChannel>(), xCmdEnv );
1054 // xxx todo: think about exception specs:
1055 catch (const deployment::DeploymentException &) {
1056 TOOLS_WARN_EXCEPTION( "desktop", "" );
1058 catch (const lang::IllegalArgumentException &) {
1059 TOOLS_WARN_EXCEPTION( "desktop", "" );
1062 std::vector< Sequence<beans::PropertyValue> > manifest;
1063 manifest.reserve( bundle.getLength() );
1064 sal_Int32 baseURLlen = m_url_expanded.getLength();
1065 static constexpr OUStringLiteral strMediaType( u"MediaType" );
1066 static constexpr OUStringLiteral strFullPath( u"FullPath" );
1067 static constexpr OUStringLiteral strIsFolder( u"IsFolder" );
1068 for ( sal_Int32 pos = bundle.getLength(); pos--; )
1070 Reference<deployment::XPackage> const& xPackage = bundle[pos];
1071 OUString url_( expandUnoRcUrl( xPackage->getURL() ) );
1072 OSL_ASSERT( url_.getLength() >= baseURLlen );
1073 OUString fullPath;
1074 if (url_.getLength() > baseURLlen)
1075 fullPath = url_.copy( baseURLlen + 1 );
1076 ::ucbhelper::Content ucbContent(
1077 url_, xCmdEnv, getMyBackend()->getComponentContext() );
1078 if (ucbContent.getPropertyValue(strIsFolder).get<bool>())
1079 fullPath += "/";
1080 Sequence<beans::PropertyValue> attribs( 2 );
1081 beans::PropertyValue * pattribs = attribs.getArray();
1082 pattribs[ 0 ].Name = strFullPath;
1083 pattribs[ 0 ].Value <<= fullPath;
1084 pattribs[ 1 ].Name = strMediaType;
1085 const Reference<deployment::XPackageTypeInfo> xPackageType(
1086 xPackage->getPackageType() );
1087 OUString mediaType;
1088 OSL_ASSERT( xPackageType.is() );
1089 if (xPackageType.is())
1090 mediaType = xPackageType->getMediaType();
1091 else
1092 mediaType = "unknown";
1093 pattribs[ 1 ].Value <<= mediaType;
1094 manifest.push_back( attribs );
1097 // write into pipe:
1098 Reference<XComponentContext> xContext(
1099 getMyBackend()->getComponentContext() );
1100 Reference<packages::manifest::XManifestWriter> xManifestWriter =
1101 packages::manifest::ManifestWriter::create( xContext );
1102 Reference<io::XOutputStream> xPipe( io::Pipe::create(xContext), UNO_QUERY_THROW );
1103 xManifestWriter->writeManifestSequence(
1104 xPipe, comphelper::containerToSequence(manifest) );
1106 // write buffered pipe data to content:
1107 ::ucbhelper::Content manifestContent(
1108 makeURL( metainfFolderContent.getURL(), u"manifest.xml"_ustr ),
1109 xCmdEnv, getMyBackend()->getComponentContext() );
1110 manifestContent.writeStream(
1111 Reference<io::XInputStream>( xPipe, UNO_QUERY_THROW ),
1112 true /* replace existing */ );
1114 else
1116 bool bSuccess = false;
1119 // overwrite manifest.xml:
1120 ::ucbhelper::Content manifestContent;
1121 if ( ! create_ucb_content(
1122 &manifestContent,
1123 makeURL( m_url_expanded, u"META-INF/manifest.xml"_ustr ),
1124 xCmdEnv, false ) )
1126 OSL_FAIL( "### missing META-INF/manifest.xml file!" );
1127 return;
1130 metainfFolderContent.transferContent(
1131 manifestContent, ::ucbhelper::InsertOperation::Copy,
1132 OUString(), ucb::NameClash::OVERWRITE );
1133 bSuccess = true;
1135 catch (const css::ucb::ContentCreationException &)
1137 TOOLS_WARN_EXCEPTION("desktop.deployment", "exception on overwriting manifest");
1140 if (!bSuccess)
1141 throw RuntimeException( u"UCB transferContent() failed!"_ustr,
1142 static_cast<OWeakObject *>(this) );
1145 // xxx todo: maybe obsolete in the future
1146 try {
1147 destFolderContent.executeCommand( u"flush"_ustr, Any() );
1149 catch (const ucb::UnsupportedCommandException &) {
1154 sal_Bool BackendImpl::PackageImpl::isBundle()
1156 return true;
1160 Sequence< Reference<deployment::XPackage> > BackendImpl::PackageImpl::getBundle(
1161 Reference<task::XAbortChannel> const & xAbortChannel,
1162 Reference<ucb::XCommandEnvironment> const & xCmdEnv )
1164 Sequence< Reference<deployment::XPackage> > * pBundle = m_pBundle;
1165 if (pBundle == nullptr)
1167 t_packagevec bundle;
1168 if (m_bRemoved)
1170 bundle = getPackagesFromDb(xCmdEnv);
1172 else
1174 try {
1175 if (m_legacyBundle)
1177 // .zip legacy packages allow script.xlb, dialog.xlb in bundle
1178 // root folder:
1179 OUString mediaType;
1180 // probe for script.xlb:
1181 if (create_ucb_content(
1182 nullptr, makeURL( m_url_expanded, u"script.xlb"_ustr ),
1183 xCmdEnv, false /* no throw */ )) {
1184 mediaType = "application/vnd.sun.star.basic-library";
1186 // probe for dialog.xlb:
1187 else if (create_ucb_content(
1188 nullptr, makeURL( m_url_expanded, u"dialog.xlb"_ustr ),
1189 xCmdEnv, false /* no throw */ ))
1190 mediaType = "application/vnd.sun.star.dialog-library";
1192 if (!mediaType.isEmpty()) {
1193 const Reference<deployment::XPackage> xPackage(
1194 bindBundleItem( getURL(), mediaType, false, OUString(),
1195 xCmdEnv ) );
1196 if (xPackage.is())
1197 bundle.push_back( xPackage );
1198 // continue scanning:
1200 scanLegacyBundle( bundle, getURL(),
1201 AbortChannel::get(xAbortChannel), xCmdEnv );
1203 else
1205 // .oxt:
1206 scanBundle( bundle, AbortChannel::get(xAbortChannel), xCmdEnv );
1210 catch (const RuntimeException &) {
1211 throw;
1213 catch (const ucb::CommandFailedException &) {
1214 throw;
1216 catch (const ucb::CommandAbortedException &) {
1217 throw;
1219 catch (const deployment::DeploymentException &) {
1220 throw;
1222 catch (const Exception &) {
1223 Any exc( ::cppu::getCaughtException() );
1224 throw deployment::DeploymentException(
1225 "error scanning bundle: " + getURL(),
1226 static_cast<OWeakObject *>(this), exc );
1230 // sort: schema before config data, typelibs before components:
1231 Sequence< Reference<deployment::XPackage> > ret( bundle.size() );
1232 Reference<deployment::XPackage> * pret = ret.getArray();
1233 sal_Int32 lower_end = 0;
1234 sal_Int32 upper_end = ret.getLength();
1235 for (auto const& elem : bundle)
1237 const Reference<deployment::XPackageTypeInfo> xPackageType(
1238 elem->getPackageType() );
1239 OSL_ASSERT( xPackageType.is() );
1240 if (xPackageType.is())
1242 const OUString mediaType( xPackageType->getMediaType() );
1243 OUString type, subType;
1244 INetContentTypeParameterList params;
1245 if (INetContentTypes::parse( mediaType, type, subType, &params ) &&
1246 type.equalsIgnoreAsciiCase("application") &&
1247 (subType.equalsIgnoreAsciiCase( "vnd.sun.star.uno-component") ||
1248 subType.equalsIgnoreAsciiCase( "vnd.sun.star.configuration-data")))
1250 --upper_end;
1251 pret[ upper_end ] = elem;
1252 continue;
1255 pret[ lower_end ] = elem;
1256 ++lower_end;
1258 OSL_ASSERT( lower_end == upper_end );
1260 const ::osl::MutexGuard guard( m_aMutex );
1261 pBundle = m_pBundle;
1262 if (pBundle == nullptr) {
1263 m_bundle = std::move(ret);
1264 pBundle = &m_bundle;
1265 OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
1266 m_pBundle = pBundle;
1269 else {
1270 OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
1272 return *pBundle;
1275 bool isBundle_( std::u16string_view mediaType )
1277 // xxx todo: additional parsing?
1278 return !mediaType.empty() &&
1279 (o3tl::matchIgnoreAsciiCase( mediaType, u"application/vnd.sun.star.package-bundle") ||
1280 o3tl::matchIgnoreAsciiCase( mediaType, u"application/vnd.sun.star.legacy-package-bundle"));
1284 Reference<deployment::XPackage> BackendImpl::PackageImpl::bindBundleItem(
1285 OUString const & url, OUString const & mediaType,
1286 bool bRemoved, OUString const & identifier,
1287 Reference<ucb::XCommandEnvironment> const & xCmdEnv,
1288 bool notifyDetectionError )
1290 // ignore any nested bundles:
1291 if (isBundle_(mediaType))
1292 return Reference<deployment::XPackage>();
1294 Reference<deployment::XPackage>xPackage;
1295 try {
1296 try {
1297 xPackage.set( getMyBackend()->m_xRootRegistry->bindPackage(
1298 url, mediaType, bRemoved, identifier, xCmdEnv ) );
1299 OSL_ASSERT( xPackage.is() );
1300 } catch (css::lang::IllegalArgumentException & e) {
1301 css::uno::Any exc(cppu::getCaughtException());
1302 throw css::lang::WrappedTargetException(
1303 "wrapped: " + e.Message, e.Context, exc);
1306 catch (const RuntimeException &) {
1307 throw;
1309 catch (const ucb::CommandFailedException &) {
1310 // ignore already handled error
1312 catch (const Exception &) {
1313 const Any exc( ::cppu::getCaughtException() );
1314 if (notifyDetectionError ||
1315 !exc.isExtractableTo( cppu::UnoType<lang::IllegalArgumentException>::get()) )
1317 (void)interactContinuation(
1318 Any( lang::WrappedTargetException(u"bundle item error!"_ustr,
1319 static_cast<OWeakObject *>(this), exc ) ),
1320 cppu::UnoType<task::XInteractionApprove>::get(), xCmdEnv, nullptr, nullptr );
1324 if (xPackage.is()) {
1325 const Reference<deployment::XPackageTypeInfo> xPackageType(
1326 xPackage->getPackageType() );
1327 OSL_ASSERT( xPackageType.is() );
1328 // ignore any nested bundles:
1329 if (xPackageType.is() && isBundle_( xPackageType->getMediaType() ))
1330 xPackage.clear();
1332 return xPackage;
1336 void BackendImpl::PackageImpl::scanBundle(
1337 t_packagevec & bundle,
1338 ::rtl::Reference<AbortChannel> const & abortChannel,
1339 Reference<ucb::XCommandEnvironment> const & xCmdEnv )
1341 OSL_ASSERT( !m_legacyBundle );
1343 OUString mfUrl( makeURL( m_url_expanded, u"META-INF/manifest.xml"_ustr ) );
1344 ::ucbhelper::Content manifestContent;
1345 if (! create_ucb_content(
1346 &manifestContent, mfUrl, xCmdEnv, false /* no throw */ ))
1348 SAL_WARN(
1349 "desktop.deployment",
1350 "cannot create UCB Content for <" << mfUrl << ">" );
1351 return;
1355 const LanguageTag& officeLocale = getOfficeLanguageTag();
1356 const std::vector< OUString > officeFallbacks( officeLocale.getFallbackStrings( true));
1357 const size_t nPenaltyMax = std::numeric_limits<size_t>::max();
1358 size_t descrPenalty = nPenaltyMax;
1359 OUString descrFile;
1361 const Reference<XComponentContext> xContext(
1362 getMyBackend()->getComponentContext() );
1363 Reference<packages::manifest::XManifestReader> xManifestReader =
1364 packages::manifest::ManifestReader::create( xContext );
1365 const Sequence< Sequence<beans::PropertyValue> > manifestSeq(
1366 xManifestReader->readManifestSequence( manifestContent.openStream() ) );
1367 const OUString packageRootURL( getURL() );
1368 for ( sal_Int32 pos = manifestSeq.getLength(); pos--; )
1370 OUString fullPath, mediaType;
1371 Sequence<beans::PropertyValue> const & attribs = manifestSeq[ pos ];
1372 for ( sal_Int32 i = attribs.getLength(); i--; )
1374 if (!(fullPath.isEmpty() || mediaType.isEmpty()))
1375 break;
1376 if ( attribs[i].Name == "FullPath" )
1377 attribs[i].Value >>= fullPath;
1378 else if ( attribs[i].Name == "MediaType" )
1379 attribs[i].Value >>= mediaType;
1382 if ( fullPath.isEmpty() || mediaType.isEmpty() || mediaType == "text/xml" )// opt: exclude common text/xml
1383 continue;
1385 OUString type, subType;
1386 INetContentTypeParameterList params;
1387 if (! INetContentTypes::parse( mediaType, type, subType, &params ))
1388 continue;
1391 auto const iter = params.find("platform"_ostr);
1392 if (iter != params.end() && !platform_fits(iter->second.m_sValue))
1393 continue;
1395 const OUString url( makeURL( packageRootURL, fullPath ) );
1397 // check for bundle description:
1398 if (type.equalsIgnoreAsciiCase("application") &&
1399 subType.equalsIgnoreAsciiCase( "vnd.sun.star.package-bundle-description"))
1401 // check locale:
1402 auto const iter = params.find("locale"_ostr);
1403 if (iter == params.end())
1405 if (descrFile.isEmpty())
1406 descrFile = url;
1408 else {
1409 // match best locale:
1410 LanguageTag descrTag(iter->second.m_sValue);
1411 if (officeLocale.getLanguage() == descrTag.getLanguage())
1413 size_t nPenalty = nPenaltyMax;
1414 const std::vector< OUString > descrFallbacks( descrTag.getFallbackStrings( true));
1415 for (size_t o=0; o < officeFallbacks.size() && nPenalty == nPenaltyMax; ++o)
1417 for (size_t d=0; d < descrFallbacks.size() && nPenalty == nPenaltyMax; ++d)
1419 if (officeFallbacks[o] == descrFallbacks[d])
1421 // The last fallbacks are always language-only
1422 // fallbacks, so we _will_ have _some_ match if
1423 // we ever entered the overall if() condition.
1424 nPenalty = o * 1000 + d;
1425 if (descrPenalty > nPenalty)
1427 descrPenalty = nPenalty;
1428 descrFile = url;
1434 // TODO: we could break here if descrPenalty==0 for an exact
1435 // match of officeLocale, but the previous code didn't; are
1436 // there side effects?
1438 continue;
1441 checkAborted( abortChannel );
1443 //We make sure that we only create one XPackage for a particular URL.
1444 //Sometime programmers insert the same URL several times in the manifest
1445 //which may lead to DisposedExceptions.
1446 if (std::none_of(bundle.begin(), bundle.end(), XPackage_eq(url)))
1448 const Reference<deployment::XPackage> xPackage(
1449 bindBundleItem( url, mediaType, false, OUString(), xCmdEnv ) );
1450 if (xPackage.is())
1451 bundle.push_back( xPackage );
1453 else
1455 SAL_WARN("desktop.deployment", "manifest.xml contains a duplicate entry (from " << url << ")");
1459 if (descrFile.isEmpty())
1460 return;
1462 ::ucbhelper::Content descrFileContent;
1463 if (!create_ucb_content( &descrFileContent, descrFile,
1464 xCmdEnv, false /* no throw */ ))
1465 return;
1467 // patch description:
1468 std::vector<sal_Int8> bytes( readFile( descrFileContent ) );
1469 OUStringBuffer buf;
1470 if ( !bytes.empty() )
1472 buf.append( OUString( reinterpret_cast<char const *>(
1473 bytes.data() ),
1474 bytes.size(), RTL_TEXTENCODING_UTF8 ) );
1476 else
1478 buf.append( Package::getDescription() );
1480 m_oldDescription = buf.makeStringAndClear();
1484 void BackendImpl::PackageImpl::scanLegacyBundle(
1485 t_packagevec & bundle,
1486 OUString const & url,
1487 ::rtl::Reference<AbortChannel> const & abortChannel,
1488 Reference<ucb::XCommandEnvironment> const & xCmdEnv,
1489 bool skip_registration )
1491 ::ucbhelper::Content ucbContent(
1492 url, xCmdEnv, getMyBackend()->getComponentContext() );
1494 // check for platform paths:
1495 const OUString title( StrTitle::getTitle( ucbContent ) );
1496 if (title.endsWithIgnoreAsciiCase( ".plt" ) &&
1497 !platform_fits( title.subView( 0, title.getLength() - 4 ) )) {
1498 return;
1500 if (title.endsWithIgnoreAsciiCase("skip_registration") )
1501 skip_registration = true;
1503 Sequence<OUString> ar { u"Title"_ustr, u"IsFolder"_ustr };
1504 Reference<sdbc::XResultSet> xResultSet( ucbContent.createCursor( ar ) );
1505 while (xResultSet->next())
1507 checkAborted( abortChannel );
1509 const Reference<sdbc::XRow> xRow( xResultSet, UNO_QUERY_THROW );
1510 const OUString title_enc( ::rtl::Uri::encode(
1511 xRow->getString( 1 /* Title */ ),
1512 rtl_UriCharClassPchar,
1513 rtl_UriEncodeIgnoreEscapes,
1514 RTL_TEXTENCODING_UTF8 ) );
1515 const OUString path( makeURL( url, title_enc ) );
1517 OUString mediaType;
1518 const Reference<deployment::XPackage> xPackage(
1519 bindBundleItem( path, OUString() /* detect */, false, OUString(),
1520 xCmdEnv, false /* ignore detection errors */ ) );
1521 if (xPackage.is()) {
1522 const Reference<deployment::XPackageTypeInfo> xPackageType(
1523 xPackage->getPackageType() );
1524 OSL_ASSERT( xPackageType.is() );
1525 if (xPackageType.is())
1526 mediaType = xPackageType->getMediaType();
1528 if (skip_registration &&
1529 // xxx todo: additional parsing?
1530 mediaType.matchIgnoreAsciiCase("application/vnd.sun.star.uno-component"))
1531 continue;
1533 bundle.push_back( xPackage );
1536 if (mediaType.isEmpty() ||
1537 // script.xlb, dialog.xlb can be met everywhere:
1538 mediaType.matchIgnoreAsciiCase("application/vnd.sun.star.basic-library") ||
1539 mediaType.matchIgnoreAsciiCase("application/vnd.sun.star.dialog-library"))
1541 if (xRow->getBoolean( 2 /* IsFolder */ )) { // recurse into folder:
1542 scanLegacyBundle(
1543 bundle, path, abortChannel, xCmdEnv, skip_registration );
1549 OUString BackendImpl::PackageImpl::getDisplayName()
1551 if (m_bRemoved)
1552 throw deployment::ExtensionRemovedException();
1554 OUString sName = getDescriptionInfoset().getLocalizedDisplayName();
1555 if (sName.isEmpty())
1556 return m_displayName;
1557 else
1558 return sName;
1561 std::vector<Reference<deployment::XPackage> >
1562 BackendImpl::PackageImpl::getPackagesFromDb(
1563 Reference<ucb::XCommandEnvironment> const & xCmdEnv)
1565 std::vector<Reference<deployment::XPackage> > retVector;
1567 for (auto const& item : m_dbData.items)
1569 Reference<deployment::XPackage> xExtension =
1570 bindBundleItem(item.first, item.second, true, m_identifier, xCmdEnv);
1571 OSL_ASSERT(xExtension.is());
1572 if (xExtension.is())
1573 retVector.push_back(xExtension);
1576 return retVector;
1579 } // anon namespace
1582 Reference<deployment::XPackageRegistry> create(
1583 Reference<deployment::XPackageRegistry> const & xRootRegistry,
1584 OUString const & context, OUString const & cachePath,
1585 Reference<XComponentContext> const & xComponentContext )
1587 Sequence<Any> args(cachePath.isEmpty() ? 1 : 3 );
1588 auto pArgs = args.getArray();
1589 pArgs[ 0 ] <<= context;
1590 if (!cachePath.isEmpty()) {
1591 pArgs[ 1 ] <<= cachePath;
1592 pArgs[ 2 ] <<= false; // readOnly
1594 return new BackendImpl( args, xComponentContext, xRootRegistry );
1597 } // namespace dp_registry
1599 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */