Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / desktop / source / deployment / registry / package / dp_package.cxx
blob712742ed93c50e2989a687dbfbafcfe27a0212fb
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_ucb.h>
25 #include <dp_interact.h>
26 #include <dp_dependencies.hxx>
27 #include <dp_platform.hxx>
28 #include <dp_descriptioninfoset.hxx>
29 #include <dp_identifier.hxx>
30 #include <rtl/uri.hxx>
31 #include <sal/log.hxx>
32 #include <cppuhelper/exc_hlp.hxx>
33 #include <cppuhelper/implbase.hxx>
34 #include <cppuhelper/supportsservice.hxx>
35 #include <ucbhelper/content.hxx>
36 #include <svl/inettype.hxx>
37 #include <comphelper/anytostring.hxx>
38 #include <comphelper/sequence.hxx>
39 #include <com/sun/star/lang/WrappedTargetException.hpp>
40 #include <com/sun/star/lang/XServiceInfo.hpp>
41 #include <com/sun/star/beans/UnknownPropertyException.hpp>
42 #include <com/sun/star/graphic/XGraphic.hpp>
43 #include <com/sun/star/graphic/GraphicProvider.hpp>
44 #include <com/sun/star/graphic/XGraphicProvider.hpp>
45 #include <com/sun/star/io/Pipe.hpp>
46 #include <com/sun/star/io/XOutputStream.hpp>
47 #include <com/sun/star/io/XInputStream.hpp>
48 #include <com/sun/star/task/InteractionClassification.hpp>
49 #include <com/sun/star/task/XInteractionApprove.hpp>
50 #include <com/sun/star/ucb/CommandAbortedException.hpp>
51 #include <com/sun/star/ucb/CommandFailedException.hpp>
52 #include <com/sun/star/ucb/ContentCreationException.hpp>
53 #include <com/sun/star/ucb/XInteractionReplaceExistingData.hpp>
54 #include <com/sun/star/ucb/NameClashResolveRequest.hpp>
55 #include <com/sun/star/ucb/XContentAccess.hpp>
56 #include <com/sun/star/ucb/NameClash.hpp>
57 #include <com/sun/star/ucb/UnsupportedCommandException.hpp>
58 #include <com/sun/star/sdbc/XResultSet.hpp>
59 #include <com/sun/star/sdbc/XRow.hpp>
60 #include <com/sun/star/packages/manifest/ManifestReader.hpp>
61 #include <com/sun/star/packages/manifest/ManifestWriter.hpp>
62 #include <com/sun/star/deployment/DependencyException.hpp>
63 #include <com/sun/star/deployment/DeploymentException.hpp>
64 #include <com/sun/star/deployment/ExtensionRemovedException.hpp>
65 #include <com/sun/star/deployment/LicenseException.hpp>
66 #include <com/sun/star/deployment/PlatformException.hpp>
67 #include <com/sun/star/deployment/Prerequisites.hpp>
68 #include <com/sun/star/xml/dom/XDocumentBuilder.hpp>
69 #include <com/sun/star/xml/xpath/XXPathAPI.hpp>
70 #include <com/sun/star/deployment/XPackageManager.hpp>
71 #include <boost/optional.hpp>
72 #include <tools/diagnose_ex.h>
74 #include <algorithm>
75 #include <memory>
76 #include <vector>
78 #include "dp_extbackenddb.hxx"
79 using namespace ::dp_misc;
80 using namespace ::com::sun::star;
81 using namespace ::com::sun::star::uno;
84 namespace dp_registry {
85 namespace backend {
86 namespace bundle {
87 namespace {
89 typedef cppu::ImplInheritanceHelper<PackageRegistryBackend,
90 lang::XServiceInfo> ImplBaseT;
93 class BackendImpl : public ImplBaseT
95 class PackageImpl : public ::dp_registry::backend::Package
97 BackendImpl * getMyBackend() const;
98 /** contains the old tooltip description for the Extension Manager GUI in OOo v.2.x
99 We keep it for backward compatibility.
101 OUString m_oldDescription;
102 OUString m_url_expanded;
103 const bool m_legacyBundle;
104 Sequence< Reference<deployment::XPackage> > m_bundle;
105 Sequence< Reference<deployment::XPackage> > * m_pBundle;
107 ExtensionBackendDb::Data m_dbData;
109 Reference<deployment::XPackage> bindBundleItem(
110 OUString const & url, OUString const & mediaType,
111 bool bRemoved, //that is, using data base information
112 OUString const & identifier,
113 Reference<ucb::XCommandEnvironment> const & xCmdEnv,
114 bool notifyDetectionError = true );
116 typedef std::vector< Reference<deployment::XPackage> > t_packagevec;
117 void scanBundle(
118 t_packagevec & bundle,
119 ::rtl::Reference<AbortChannel> const & abortChannel,
120 Reference<ucb::XCommandEnvironment> const & xCmdEnv );
121 void scanLegacyBundle(
122 t_packagevec & bundle,
123 OUString const & url,
124 ::rtl::Reference<AbortChannel> const & abortChannel,
125 Reference<ucb::XCommandEnvironment> const & xCmdEnv,
126 bool skip_registration = false );
127 std::vector<Reference<deployment::XPackage> > getPackagesFromDb(
128 Reference<ucb::XCommandEnvironment> const & xCmdEnv);
129 bool checkPlatform(
130 Reference<ucb::XCommandEnvironment > const & environment);
132 bool checkDependencies(
133 Reference<ucb::XCommandEnvironment > const &
134 environment,
135 DescriptionInfoset const & description);
136 // throws css::uno::RuntimeException,
137 // css::deployment::DeploymentException
139 /// @throws deployment::DeploymentException
140 /// @throws ucb::CommandFailedException
141 /// @throws ucb::CommandAbortedException
142 /// @throws RuntimeException
143 bool checkLicense(
144 Reference< ucb::XCommandEnvironment > const & xCmdEnv,
145 DescriptionInfoset const & description, bool bNoLicenseChecking);
146 // @throws DeploymentException
147 OUString getTextFromURL(
148 const Reference< ucb::XCommandEnvironment >& xCmdEnv,
149 const OUString& licenseUrl);
151 DescriptionInfoset getDescriptionInfoset() const;
153 // Package
154 virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
155 ::osl::ResettableMutexGuard & guard,
156 ::rtl::Reference<AbortChannel> const & abortChannel,
157 Reference<ucb::XCommandEnvironment> const & xCmdEnv ) override;
158 virtual void processPackage_(
159 ::osl::ResettableMutexGuard & guard,
160 bool registerPackage,
161 bool startup,
162 ::rtl::Reference<AbortChannel> const & abortChannel,
163 Reference<ucb::XCommandEnvironment> const & xCmdEnv ) override;
165 virtual void SAL_CALL disposing() override;
168 public:
169 PackageImpl(
170 ::rtl::Reference<PackageRegistryBackend> const & myBackend,
171 OUString const & url,
172 OUString const & name,
173 Reference<deployment::XPackageTypeInfo> const & xPackageType,
174 bool legacyBundle,
175 bool bRemoved,
176 OUString const & identifier);
178 // XPackage
179 virtual sal_Bool SAL_CALL isBundle() override;
181 virtual Sequence< Reference<deployment::XPackage> > SAL_CALL getBundle(
182 Reference<task::XAbortChannel> const & xAbortChannel,
183 Reference<ucb::XCommandEnvironment> const & xCmdEnv ) override;
184 virtual OUString SAL_CALL getDescription() override;
186 virtual OUString SAL_CALL getLicenseText() override;
188 virtual void SAL_CALL exportTo(
189 OUString const & destFolderURL, OUString const & newTitle,
190 sal_Int32 nameClashAction,
191 Reference<ucb::XCommandEnvironment> const & xCmdEnv ) override;
193 virtual ::sal_Int32 SAL_CALL checkPrerequisites(
194 const Reference< task::XAbortChannel >& xAbortChannel,
195 const Reference< ucb::XCommandEnvironment >& xCmdEnv,
196 sal_Bool noLicenseChecking) override;
198 virtual sal_Bool SAL_CALL checkDependencies(
199 const Reference< ucb::XCommandEnvironment >& xCmdEnv ) override;
201 virtual beans::Optional<OUString> SAL_CALL getIdentifier() override;
203 virtual OUString SAL_CALL getVersion() override;
205 virtual Sequence<OUString> SAL_CALL getUpdateInformationURLs() override;
207 virtual beans::StringPair SAL_CALL getPublisherInfo() override;
209 virtual OUString SAL_CALL getDisplayName() override;
211 virtual Reference< graphic::XGraphic > SAL_CALL
212 getIcon( sal_Bool bHighContrast ) override;
214 friend class PackageImpl;
216 Reference<deployment::XPackageRegistry> m_xRootRegistry;
217 const Reference<deployment::XPackageTypeInfo> m_xBundleTypeInfo;
218 const Reference<deployment::XPackageTypeInfo> m_xLegacyBundleTypeInfo;
219 Sequence< Reference<deployment::XPackageTypeInfo> > m_typeInfos;
221 std::unique_ptr<ExtensionBackendDb> m_backendDb;
223 void addDataToDb(OUString const & url, ExtensionBackendDb::Data const & data);
224 ExtensionBackendDb::Data readDataFromDb(OUString const & url);
225 void revokeEntryFromDb(OUString const & url);
227 // PackageRegistryBackend
228 virtual Reference<deployment::XPackage> bindPackage_(
229 OUString const & url, OUString const & mediaType,
230 bool bRemoved, OUString const & identifier,
231 Reference<ucb::XCommandEnvironment> const & xCmdEnv ) override;
233 virtual void SAL_CALL disposing() override;
235 public:
236 BackendImpl(
237 Sequence<Any> const & args,
238 Reference<XComponentContext> const & xComponentContext,
239 Reference<deployment::XPackageRegistry> const & xRootRegistry );
241 // XServiceInfo
242 virtual OUString SAL_CALL getImplementationName() override;
243 virtual sal_Bool SAL_CALL supportsService( OUString const& name ) override;
244 virtual Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
246 // XPackageRegistry
247 virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
248 getSupportedPackageTypes() override;
249 virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
251 using ImplBaseT::disposing;
254 //Used to find a XPackage with a particular URL
255 class XPackage_eq
257 OUString m_URL;
258 public:
259 explicit XPackage_eq(const OUString & s) : m_URL(s) {}
260 bool operator() (const Reference<deployment::XPackage> & p) const
262 return m_URL == p->getURL();
267 BackendImpl::BackendImpl(
268 Sequence<Any> const & args,
269 Reference<XComponentContext> const & xComponentContext,
270 Reference<deployment::XPackageRegistry> const & xRootRegistry )
271 : ImplBaseT( args, xComponentContext ),
272 m_xRootRegistry( xRootRegistry ),
273 m_xBundleTypeInfo( new Package::TypeInfo(
274 "application/vnd.sun.star.package-bundle",
275 "*.oxt;*.uno.pkg",
276 DpResId(RID_STR_PACKAGE_BUNDLE)
277 ) ),
278 m_xLegacyBundleTypeInfo( new Package::TypeInfo(
279 "application/vnd.sun.star.legacy-package-bundle",
280 "*.zip",
281 m_xBundleTypeInfo->getShortDescription()
282 ) ),
283 m_typeInfos(2)
285 m_typeInfos[ 0 ] = m_xBundleTypeInfo;
286 m_typeInfos[ 1 ] = m_xLegacyBundleTypeInfo;
288 if (!transientMode())
290 OUString dbFile = makeURL(getCachePath(), getImplementationName());
291 dbFile = makeURL(dbFile, "backenddb.xml");
292 m_backendDb.reset(
293 new ExtensionBackendDb(getComponentContext(), dbFile));
298 void BackendImpl::disposing()
300 m_xRootRegistry.clear();
301 PackageRegistryBackend::disposing();
304 // XServiceInfo
305 OUString BackendImpl::getImplementationName()
307 return "com.sun.star.comp.deployment.bundle.PackageRegistryBackend";
310 sal_Bool BackendImpl::supportsService(OUString const & ServiceName)
312 return cppu::supportsService(this, ServiceName);
315 Sequence<OUString> BackendImpl::getSupportedServiceNames()
317 return { OUString(BACKEND_SERVICE_NAME) };
320 // XPackageRegistry
322 Sequence< Reference<deployment::XPackageTypeInfo> >
323 BackendImpl::getSupportedPackageTypes()
325 return m_typeInfos;
328 void BackendImpl::packageRemoved(OUString const & url, OUString const & /*mediaType*/)
330 //Notify the backend responsible for processing the different media
331 //types that this extension was removed.
332 ExtensionBackendDb::Data data = readDataFromDb(url);
333 for (auto const& item : data.items)
335 m_xRootRegistry->packageRemoved(item.first, item.second);
338 if (m_backendDb)
339 m_backendDb->removeEntry(url);
343 // PackageRegistryBackend
345 Reference<deployment::XPackage> BackendImpl::bindPackage_(
346 OUString const & url, OUString const & mediaType_,
347 bool bRemoved, OUString const & identifier,
348 Reference<ucb::XCommandEnvironment> const & xCmdEnv )
350 OUString mediaType( mediaType_ );
351 if (mediaType.isEmpty())
353 // detect media-type:
354 ::ucbhelper::Content ucbContent;
355 if (create_ucb_content( &ucbContent, url, xCmdEnv ))
357 if (ucbContent.isFolder())
359 //Every .oxt, uno.pkg file must contain a META-INF folder
360 ::ucbhelper::Content metaInfContent;
361 if (create_ucb_content(
362 &metaInfContent, makeURL( url, "META-INF" ),
363 xCmdEnv, false /* no throw */ ))
365 mediaType = "application/vnd.sun.star.package-bundle";
367 //No support of legacy bundles, because every folder could be one.
369 else
371 const OUString title( StrTitle::getTitle( ucbContent ) );
372 if (title.endsWithIgnoreAsciiCase(".oxt") ||
373 title.endsWithIgnoreAsciiCase(".uno.pkg"))
374 mediaType = "application/vnd.sun.star.package-bundle";
375 else if (title.endsWithIgnoreAsciiCase(".zip"))
376 mediaType = "application/vnd.sun.star.legacy-package-bundle";
379 if (mediaType.isEmpty())
380 throw lang::IllegalArgumentException(
381 StrCannotDetectMediaType() + url,
382 static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
385 OUString type, subType;
386 INetContentTypeParameterList params;
387 if (INetContentTypes::parse( mediaType, type, subType, &params ))
389 if (type.equalsIgnoreAsciiCase("application"))
392 //In case a XPackage is created for a removed extension, we cannot
393 //obtain the name
394 OUString name;
395 if (!bRemoved)
397 ::ucbhelper::Content ucbContent(
398 url, xCmdEnv, getComponentContext() );
399 name = StrTitle::getTitle( ucbContent );
401 if (subType.equalsIgnoreAsciiCase("vnd.sun.star.package-bundle"))
403 return new PackageImpl(
404 this, url, name, m_xBundleTypeInfo, false, bRemoved,
405 identifier);
407 else if (subType.equalsIgnoreAsciiCase( "vnd.sun.star.legacy-package-bundle"))
409 return new PackageImpl(
410 this, url, name, m_xLegacyBundleTypeInfo, true, bRemoved,
411 identifier);
415 throw lang::IllegalArgumentException(
416 StrUnsupportedMediaType() + mediaType,
417 static_cast<OWeakObject *>(this),
418 static_cast<sal_Int16>(-1) );
421 void BackendImpl::addDataToDb(
422 OUString const & url, ExtensionBackendDb::Data const & data)
424 if (m_backendDb)
425 m_backendDb->addEntry(url, data);
428 ExtensionBackendDb::Data BackendImpl::readDataFromDb(
429 OUString const & url)
431 ExtensionBackendDb::Data data;
432 if (m_backendDb)
433 data = m_backendDb->getEntry(url);
434 return data;
437 void BackendImpl::revokeEntryFromDb(OUString const & url)
439 if (m_backendDb)
440 m_backendDb->revokeEntry(url);
444 BackendImpl::PackageImpl::PackageImpl(
445 ::rtl::Reference<PackageRegistryBackend> const & myBackend,
446 OUString const & url,
447 OUString const & name,
448 Reference<deployment::XPackageTypeInfo> const & xPackageType,
449 bool legacyBundle, bool bRemoved, OUString const & identifier)
450 : Package( myBackend, url, name, name /* display-name */,
451 xPackageType, bRemoved, identifier),
452 m_url_expanded( expandUnoRcUrl( url ) ),
453 m_legacyBundle( legacyBundle ),
454 m_pBundle( nullptr )
456 if (bRemoved)
457 m_dbData = getMyBackend()->readDataFromDb(url);
460 BackendImpl * BackendImpl::PackageImpl::getMyBackend() const
462 BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get());
463 if (nullptr == pBackend)
465 //May throw a DisposedException
466 check();
467 //We should never get here...
468 throw RuntimeException("Failed to get the BackendImpl",
469 static_cast<OWeakObject*>(const_cast<PackageImpl *>(this)));
471 return pBackend;
474 void BackendImpl::PackageImpl::disposing()
476 sal_Int32 len = m_bundle.getLength();
477 Reference<deployment::XPackage> const * p = m_bundle.getConstArray();
478 for ( sal_Int32 pos = 0; pos < len; ++pos )
479 try_dispose( p[ pos ] );
480 m_bundle.realloc( 0 );
482 Package::disposing();
485 // Package
487 beans::Optional< beans::Ambiguous<sal_Bool> >
488 BackendImpl::PackageImpl::isRegistered_(
489 ::osl::ResettableMutexGuard &,
490 ::rtl::Reference<AbortChannel> const & abortChannel,
491 Reference<ucb::XCommandEnvironment> const & xCmdEnv )
493 //In case the object was created for a removed extension (m_bRemoved = true)
494 //but the extension is not registered, then bundle will be empty. Then
495 //the return value will be Optional<...>.IsPresent= false. Although this is
496 //not true, this does not matter. Then registerPackage or revokePackage
497 //would never be called for the items. But since the extension is removed
498 //and not registered anyway, this does not matter.
499 const Sequence< Reference<deployment::XPackage> > bundle(
500 getBundle( abortChannel.get(), xCmdEnv ) );
502 bool reg = false;
503 bool present = false;
504 bool ambig = false;
505 for ( sal_Int32 pos = bundle.getLength(); pos--; )
507 Reference<deployment::XPackage> const & xPackage = bundle[ pos ];
508 Reference<task::XAbortChannel> xSubAbortChannel(
509 xPackage->createAbortChannel() );
510 AbortChannel::Chain chain( abortChannel, xSubAbortChannel );
511 beans::Optional< beans::Ambiguous<sal_Bool> > option(
512 xPackage->isRegistered( xSubAbortChannel, xCmdEnv ) );
514 //present = true if at least one bundle item has this value.
515 //reg = true if all bundle items have an option value (option.IsPresent == 1)
516 //and all have value of true (option.Value.Value == true)
517 //If not, then the bundle has the status of not registered and ambiguous.
518 if (option.IsPresent)
520 beans::Ambiguous<sal_Bool> const & status = option.Value;
521 if (present)
523 //we never come here in the first iteration
524 if (reg != bool(status.Value)) {
526 ambig = true;
527 reg = false;
528 break;
531 else
533 //we always come here in the first iteration
534 reg = status.Value;
535 present = true;
539 return beans::Optional< beans::Ambiguous<sal_Bool> >(
540 present, beans::Ambiguous<sal_Bool>(reg, ambig) );
543 OUString BackendImpl::PackageImpl::getTextFromURL(
544 const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv,
545 const OUString& licenseUrl)
549 ::ucbhelper::Content descContent(
550 licenseUrl, xCmdEnv, getMyBackend()->getComponentContext());
551 std::vector<sal_Int8> seq = dp_misc::readFile(descContent);
552 return OUString( reinterpret_cast<sal_Char const *>(
553 seq.data()), seq.size(), RTL_TEXTENCODING_UTF8);
555 catch (const css::uno::Exception&)
557 Any exc( ::cppu::getCaughtException() );
558 throw css::deployment::DeploymentException(
559 "Could not read file " + licenseUrl, nullptr, exc);
564 DescriptionInfoset BackendImpl::PackageImpl::getDescriptionInfoset() const
566 return dp_misc::getDescriptionInfoset(m_url_expanded);
569 bool BackendImpl::PackageImpl::checkPlatform(
570 css::uno::Reference< css::ucb::XCommandEnvironment > const & environment)
572 bool ret = false;
573 DescriptionInfoset info(getDescriptionInfoset());
574 Sequence<OUString> platforms(info.getSupportedPlatforms());
575 if (hasValidPlatform(platforms))
577 ret = true;
579 else
581 ret = false;
582 OUString msg(
583 "unsupported platform");
584 Any e(
585 css::deployment::PlatformException(
586 msg, static_cast<OWeakObject *>(this), this));
587 if (!interactContinuation(
588 e, cppu::UnoType< css::task::XInteractionApprove >::get(),
589 environment, nullptr, nullptr))
591 throw css::deployment::DeploymentException(
592 msg, static_cast<OWeakObject *>(this), e);
595 return ret;
599 bool BackendImpl::PackageImpl::checkDependencies(
600 css::uno::Reference< css::ucb::XCommandEnvironment > const & environment,
601 DescriptionInfoset const & description)
603 css::uno::Sequence< css::uno::Reference< css::xml::dom::XElement > >
604 unsatisfied(dp_misc::Dependencies::check(description));
606 if (!unsatisfied.hasElements()) {
607 return true;
608 } else {
609 OUString msg(
610 "unsatisfied dependencies");
611 Any e(
612 css::deployment::DependencyException(
613 msg, static_cast<OWeakObject *>(this), unsatisfied));
614 if (!interactContinuation(
615 e, cppu::UnoType< css::task::XInteractionApprove >::get(),
616 environment, nullptr, nullptr))
618 throw css::deployment::DeploymentException(
619 msg, static_cast<OWeakObject *>(this), e);
621 return false;
625 bool BackendImpl::PackageImpl::checkLicense(
626 css::uno::Reference< css::ucb::XCommandEnvironment > const & xCmdEnv,
627 DescriptionInfoset const & info, bool alreadyInstalled)
631 ::boost::optional<SimpleLicenseAttributes> simplLicAttr
632 = info.getSimpleLicenseAttributes();
633 if (! simplLicAttr)
634 return true;
635 OUString sLic = info.getLocalizedLicenseURL();
636 //If we do not get a localized licence then there is an error in the description.xml
637 //This should be handled by using a validating parser. Therefore we assume that no
638 //license is available.
639 if (sLic.isEmpty())
640 throw css::deployment::DeploymentException(
641 "Could not obtain path to license. Possible error in description.xml", nullptr, Any());
642 OUString sHref = m_url_expanded + "/" + sLic;
643 OUString sLicense = getTextFromURL(xCmdEnv, sHref);
644 ////determine who has to agree to the license
645 //check correct value for attribute
646 if ( ! (simplLicAttr->acceptBy == "user" || simplLicAttr->acceptBy == "admin"))
647 throw css::deployment::DeploymentException(
648 "Could not obtain attribute simple-license@accept-by or it has no valid value", nullptr, Any());
651 //Only use interaction if there is no version of this extension already installed
652 //and the suppress-on-update flag is not set for the new extension
653 // alreadyInstalled | bSuppressOnUpdate | show license
655 // 0 | 0 | 1
656 // 0 | 1 | 1
657 // 1 | 0 | 1
658 // 1 | 1 | 0
660 if ( !(alreadyInstalled && simplLicAttr->suppressOnUpdate))
662 css::deployment::LicenseException licExc(
663 OUString(), nullptr, getDisplayName(), sLicense,
664 simplLicAttr->acceptBy);
665 bool approve = false;
666 bool abort = false;
667 if (! interactContinuation(
668 Any(licExc), cppu::UnoType<task::XInteractionApprove>::get(), xCmdEnv, &approve, &abort ))
669 throw css::deployment::DeploymentException(
670 "Could not interact with user.", nullptr, Any());
672 return approve;
674 return true;
675 } catch (const css::ucb::CommandFailedException&) {
676 throw;
677 } catch (const css::ucb::CommandAbortedException&) {
678 throw;
679 } catch (const css::deployment::DeploymentException&) {
680 throw;
681 } catch (const css::uno::RuntimeException&) {
682 throw;
683 } catch (const css::uno::Exception&) {
684 Any anyExc = cppu::getCaughtException();
685 throw css::deployment::DeploymentException("Unexpected exception", nullptr, anyExc);
689 ::sal_Int32 BackendImpl::PackageImpl::checkPrerequisites(
690 const css::uno::Reference< css::task::XAbortChannel >&,
691 const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv,
692 sal_Bool alreadyInstalled)
694 if (m_bRemoved)
695 throw deployment::ExtensionRemovedException();
696 DescriptionInfoset info = getDescriptionInfoset();
697 if (!info.hasDescription())
698 return 0;
700 //always return LICENSE as long as the user did not accept the license
701 //so that XExtensonManager::checkPrerequisitesAndEnable will again
702 //check the license
703 if (!checkPlatform(xCmdEnv))
704 return deployment::Prerequisites::PLATFORM |
705 deployment::Prerequisites::LICENSE;
706 else if(!checkDependencies(xCmdEnv, info))
707 return deployment::Prerequisites::DEPENDENCIES |
708 deployment::Prerequisites::LICENSE;
709 else if(!checkLicense(xCmdEnv, info, alreadyInstalled))
710 return deployment::Prerequisites::LICENSE;
711 else
712 return 0;
715 sal_Bool BackendImpl::PackageImpl::checkDependencies(
716 const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv )
718 if (m_bRemoved)
719 throw deployment::ExtensionRemovedException();
720 DescriptionInfoset info = getDescriptionInfoset();
721 if (!info.hasDescription())
722 return true;
724 return checkDependencies(xCmdEnv, info);
727 beans::Optional<OUString> BackendImpl::PackageImpl::getIdentifier()
729 OUString identifier;
730 if (m_bRemoved)
731 identifier = m_identifier;
732 else
733 identifier = dp_misc::generateIdentifier(
734 getDescriptionInfoset().getIdentifier(), m_name);
736 return beans::Optional<OUString>(
737 true, identifier);
740 OUString BackendImpl::PackageImpl::getVersion()
742 if (m_bRemoved)
743 throw deployment::ExtensionRemovedException();
744 return getDescriptionInfoset().getVersion();
747 Sequence<OUString> BackendImpl::PackageImpl::getUpdateInformationURLs()
749 if (m_bRemoved)
750 throw deployment::ExtensionRemovedException();
751 return getDescriptionInfoset().getUpdateInformationUrls();
754 beans::StringPair BackendImpl::PackageImpl::getPublisherInfo()
756 if (m_bRemoved)
757 throw deployment::ExtensionRemovedException();
758 std::pair< OUString, OUString > aInfo = getDescriptionInfoset().getLocalizedPublisherNameAndURL();
759 beans::StringPair aStrPair( aInfo.first, aInfo.second );
760 return aStrPair;
764 uno::Reference< graphic::XGraphic > BackendImpl::PackageImpl::getIcon( sal_Bool bHighContrast )
766 if (m_bRemoved)
767 throw deployment::ExtensionRemovedException();
769 uno::Reference< graphic::XGraphic > xGraphic;
771 OUString aIconURL = getDescriptionInfoset().getIconURL( bHighContrast );
772 if ( !aIconURL.isEmpty() )
774 OUString aFullIconURL = m_url_expanded + "/" + aIconURL;
776 uno::Reference< XComponentContext > xContext( getMyBackend()->getComponentContext() );
777 uno::Reference< graphic::XGraphicProvider > xGraphProvider( graphic::GraphicProvider::create(xContext) );
779 uno::Sequence< beans::PropertyValue > aMediaProps( 1 );
780 aMediaProps[0].Name = "URL";
781 aMediaProps[0].Value <<= aFullIconURL;
783 xGraphic = xGraphProvider->queryGraphic( aMediaProps );
786 return xGraphic;
790 void BackendImpl::PackageImpl::processPackage_(
791 ::osl::ResettableMutexGuard &,
792 bool doRegisterPackage,
793 bool startup,
794 ::rtl::Reference<AbortChannel> const & abortChannel,
795 Reference<ucb::XCommandEnvironment> const & xCmdEnv )
797 const Sequence< Reference<deployment::XPackage> > bundle(
798 getBundle( abortChannel.get(), xCmdEnv ) );
800 if (doRegisterPackage)
802 ExtensionBackendDb::Data data;
803 const sal_Int32 len = bundle.getLength();
804 for ( sal_Int32 pos = 0; pos < len; ++pos )
806 checkAborted(abortChannel);
807 Reference<deployment::XPackage> const & xPackage = bundle[ pos ];
808 Reference<task::XAbortChannel> xSubAbortChannel(
809 xPackage->createAbortChannel() );
810 AbortChannel::Chain chain( abortChannel, xSubAbortChannel );
811 try {
812 xPackage->registerPackage( startup, xSubAbortChannel, xCmdEnv );
814 catch (const Exception &)
816 //We even try a rollback if the user cancelled the action (CommandAbortedException)
817 //in order to prevent invalid database entries.
818 Any exc( ::cppu::getCaughtException() );
819 // try to handle exception, notify:
820 bool approve = false, abort = false;
821 if (! interactContinuation(
822 Any( lang::WrappedTargetException(
823 "bundle item registration error!",
824 static_cast<OWeakObject *>(this), exc ) ),
825 cppu::UnoType<task::XInteractionApprove>::get(), xCmdEnv,
826 &approve, &abort )) {
827 OSL_ASSERT( !approve && !abort );
828 if (m_legacyBundle) // default for legacy packages: ignore
829 continue;
830 // no selection at all, so rethrow;
831 // no C++ rethrow after getCaughtException(),
832 // see cppuhelper/exc_hlp.hxx:
833 ::cppu::throwException(exc);
835 if (approve && !abort) // ignore error, just continue
836 continue;
839 ProgressLevel progress( xCmdEnv, "rollback..." );
840 // try rollback
841 for ( ; pos--; )
843 try {
844 bundle[ pos ]->revokePackage(
845 startup, xSubAbortChannel, xCmdEnv );
847 catch (const Exception &)
849 TOOLS_WARN_EXCEPTION( "desktop", "" );
850 // ignore any errors of rollback
853 progress.update( "rollback finished." );
856 deployment::DeploymentException dpExc;
857 if (exc >>= dpExc) {
858 throw ucb::CommandFailedException(
859 dpExc.Message, dpExc.Context, dpExc.Cause );
861 else {
862 // rethrow CommandFailedException
863 ::cppu::throwException(exc);
866 data.items.emplace_back(xPackage->getURL(),
867 xPackage->getPackageType()->getMediaType());
869 getMyBackend()->addDataToDb(getURL(), data);
871 else
873 // revoke in reverse order:
874 for ( sal_Int32 pos = bundle.getLength(); pos--; )
876 checkAborted(abortChannel);
877 Reference<deployment::XPackage> const & xPackage = bundle[ pos ];
878 Reference<task::XAbortChannel> xSubAbortChannel(
879 xPackage->createAbortChannel() );
880 AbortChannel::Chain chain( abortChannel, xSubAbortChannel );
881 try {
882 bundle[ pos ]->revokePackage(
883 startup, xSubAbortChannel, xCmdEnv );
885 catch (const RuntimeException &) {
886 throw;
888 catch (const ucb::CommandAbortedException &) {
889 throw;
891 catch (const Exception &) {
892 // CommandFailedException, DeploymentException:
893 Any exc( ::cppu::getCaughtException() );
894 // try to handle exception, notify:
895 bool approve = false, abort = false;
896 if (! interactContinuation(
897 Any( lang::WrappedTargetException(
898 "bundle item revocation error!",
899 static_cast<OWeakObject *>(this), exc ) ),
900 cppu::UnoType<task::XInteractionApprove>::get(), xCmdEnv,
901 &approve, &abort )) {
902 OSL_ASSERT( !approve && !abort );
903 if (m_legacyBundle) // default for legacy packages: ignore
904 continue;
905 // no selection at all, so rethrow
906 // no C++ rethrow after getCaughtException(),
907 // see cppuhelper/exc_hlp.hxx:
908 ::cppu::throwException(exc);
910 // ignore errors when revoking, although abort may have been
911 // selected
914 getMyBackend()->revokeEntryFromDb(getURL());
919 OUString BackendImpl::PackageImpl::getDescription()
921 if (m_bRemoved)
922 throw deployment::ExtensionRemovedException();
924 const OUString sRelativeURL(getDescriptionInfoset().getLocalizedDescriptionURL());
925 OUString sDescription;
926 if (!sRelativeURL.isEmpty())
928 OUString sURL = m_url_expanded + "/" + sRelativeURL;
932 sDescription = getTextFromURL( css::uno::Reference< css::ucb::XCommandEnvironment >(), sURL );
934 catch ( const css::deployment::DeploymentException& )
936 TOOLS_WARN_EXCEPTION( "desktop", "" );
940 if (!sDescription.isEmpty())
941 return sDescription;
942 return m_oldDescription;
946 OUString BackendImpl::PackageImpl::getLicenseText()
948 if (m_bRemoved)
949 throw deployment::ExtensionRemovedException();
951 OUString sLicense;
952 DescriptionInfoset aInfo = getDescriptionInfoset();
954 ::boost::optional< SimpleLicenseAttributes > aSimplLicAttr = aInfo.getSimpleLicenseAttributes();
955 if ( aSimplLicAttr )
957 OUString aLicenseURL = aInfo.getLocalizedLicenseURL();
959 if ( !aLicenseURL.isEmpty() )
961 OUString aFullURL = m_url_expanded + "/" + aLicenseURL;
962 sLicense = getTextFromURL( Reference< ucb::XCommandEnvironment >(), aFullURL);
966 return sLicense;
970 void BackendImpl::PackageImpl::exportTo(
971 OUString const & destFolderURL, OUString const & newTitle,
972 sal_Int32 nameClashAction, Reference<ucb::XCommandEnvironment> const & xCmdEnv )
974 if (m_bRemoved)
975 throw deployment::ExtensionRemovedException();
977 ::ucbhelper::Content sourceContent(
978 m_url_expanded, xCmdEnv, getMyBackend()->getComponentContext() );
979 OUString title(newTitle);
980 if (title.isEmpty())
981 sourceContent.getPropertyValue( "Title" ) >>= title;
982 OUString destURL( makeURL( destFolderURL, ::rtl::Uri::encode(
983 title, rtl_UriCharClassPchar,
984 rtl_UriEncodeIgnoreEscapes,
985 RTL_TEXTENCODING_UTF8 ) ) );
987 if (nameClashAction == ucb::NameClash::ASK)
989 if (create_ucb_content(
990 nullptr, destURL, xCmdEnv, false /* no throw */ )) {
991 bool replace = false, abort = false;
992 if (! interactContinuation(
993 Any( ucb::NameClashResolveRequest(
994 "file already exists: " + title,
995 static_cast<OWeakObject *>(this),
996 task::InteractionClassification_QUERY,
997 destFolderURL, title, OUString() ) ),
998 cppu::UnoType<ucb::XInteractionReplaceExistingData>::get(), xCmdEnv,
999 &replace, &abort ) || !replace) {
1000 return;
1004 else if (nameClashAction != ucb::NameClash::OVERWRITE) {
1005 throw ucb::CommandFailedException("unsupported nameClashAction!",
1006 static_cast<OWeakObject *>(this), Any() );
1008 erase_path( destURL, xCmdEnv );
1010 OUStringBuffer buf;
1011 buf.append( "vnd.sun.star.zip://" );
1012 buf.append( ::rtl::Uri::encode( destURL,
1013 rtl_UriCharClassRegName,
1014 rtl_UriEncodeIgnoreEscapes,
1015 RTL_TEXTENCODING_UTF8 ) );
1016 buf.append( '/' );
1017 OUString destFolder( buf.makeStringAndClear() );
1019 ::ucbhelper::Content destFolderContent(
1020 destFolder, xCmdEnv, getMyBackend()->getComponentContext() );
1022 // transfer every item of folder into zip:
1023 Reference<sdbc::XResultSet> xResultSet(
1024 sourceContent.createCursor( Sequence<OUString>() ) );
1025 ProgressLevel progress( xCmdEnv, OUString() );
1026 while (xResultSet->next())
1028 ::ucbhelper::Content subContent(
1029 Reference<ucb::XContentAccess>(
1030 xResultSet, UNO_QUERY_THROW )->queryContent(),
1031 xCmdEnv, getMyBackend()->getComponentContext() );
1032 destFolderContent.transferContent(
1033 subContent, ::ucbhelper::InsertOperation::Copy,
1034 OUString(), ucb::NameClash::OVERWRITE );
1035 progress.update( Any() ); // animating progress bar
1039 // assure META-INF folder:
1040 ::ucbhelper::Content metainfFolderContent;
1041 create_folder( &metainfFolderContent,
1042 makeURL( destFolderContent.getURL(), "META-INF" ),
1043 xCmdEnv );
1045 if (m_legacyBundle)
1047 // easy to migrate legacy bundles to new format:
1048 // just export them once using a .oxt name!
1049 // set detected media-types of any bundle item:
1051 // collect all manifest entries:
1052 Sequence< Reference<deployment::XPackage> > bundle;
1053 try {
1054 bundle = getBundle( Reference<task::XAbortChannel>(), xCmdEnv );
1056 // xxx todo: think about exception specs:
1057 catch (const deployment::DeploymentException &) {
1058 TOOLS_WARN_EXCEPTION( "desktop", "" );
1060 catch (const lang::IllegalArgumentException &) {
1061 TOOLS_WARN_EXCEPTION( "desktop", "" );
1064 std::vector< Sequence<beans::PropertyValue> > manifest;
1065 manifest.reserve( bundle.getLength() );
1066 sal_Int32 baseURLlen = m_url_expanded.getLength();
1067 Reference<deployment::XPackage> const *pbundle = bundle.getConstArray();
1068 const OUString strMediaType( "MediaType" );
1069 const OUString strFullPath( "FullPath" );
1070 const OUString strIsFolder( "IsFolder" );
1071 for ( sal_Int32 pos = bundle.getLength(); pos--; )
1073 Reference<deployment::XPackage> const & xPackage = pbundle[ pos ];
1074 OUString url_( expandUnoRcUrl( xPackage->getURL() ) );
1075 OSL_ASSERT( url_.getLength() >= baseURLlen );
1076 OUString fullPath;
1077 if (url_.getLength() > baseURLlen)
1078 fullPath = url_.copy( baseURLlen + 1 );
1079 ::ucbhelper::Content ucbContent(
1080 url_, xCmdEnv, getMyBackend()->getComponentContext() );
1081 if (ucbContent.getPropertyValue(strIsFolder).get<bool>())
1082 fullPath += "/";
1083 Sequence<beans::PropertyValue> attribs( 2 );
1084 beans::PropertyValue * pattribs = attribs.getArray();
1085 pattribs[ 0 ].Name = strFullPath;
1086 pattribs[ 0 ].Value <<= fullPath;
1087 pattribs[ 1 ].Name = strMediaType;
1088 const Reference<deployment::XPackageTypeInfo> xPackageType(
1089 xPackage->getPackageType() );
1090 OUString mediaType;
1091 OSL_ASSERT( xPackageType.is() );
1092 if (xPackageType.is())
1093 mediaType = xPackageType->getMediaType();
1094 else
1095 mediaType = "unknown";
1096 pattribs[ 1 ].Value <<= mediaType;
1097 manifest.push_back( attribs );
1100 // write into pipe:
1101 Reference<XComponentContext> xContext(
1102 getMyBackend()->getComponentContext() );
1103 Reference<packages::manifest::XManifestWriter> xManifestWriter =
1104 packages::manifest::ManifestWriter::create( xContext );
1105 Reference<io::XOutputStream> xPipe( io::Pipe::create(xContext), UNO_QUERY_THROW );
1106 xManifestWriter->writeManifestSequence(
1107 xPipe, comphelper::containerToSequence(manifest) );
1109 // write buffered pipe data to content:
1110 ::ucbhelper::Content manifestContent(
1111 makeURL( metainfFolderContent.getURL(), "manifest.xml" ),
1112 xCmdEnv, getMyBackend()->getComponentContext() );
1113 manifestContent.writeStream(
1114 Reference<io::XInputStream>( xPipe, UNO_QUERY_THROW ),
1115 true /* replace existing */ );
1117 else
1119 bool bSuccess = false;
1122 // overwrite manifest.xml:
1123 ::ucbhelper::Content manifestContent;
1124 if ( ! create_ucb_content(
1125 &manifestContent,
1126 makeURL( m_url_expanded, "META-INF/manifest.xml" ),
1127 xCmdEnv, false ) )
1129 OSL_FAIL( "### missing META-INF/manifest.xml file!" );
1130 return;
1133 metainfFolderContent.transferContent(
1134 manifestContent, ::ucbhelper::InsertOperation::Copy,
1135 OUString(), ucb::NameClash::OVERWRITE );
1136 bSuccess = true;
1138 catch (const css::ucb::ContentCreationException &)
1140 TOOLS_WARN_EXCEPTION("desktop.deployment", "exception on overwriting manifest");
1143 if (!bSuccess)
1144 throw RuntimeException( "UCB transferContent() failed!",
1145 static_cast<OWeakObject *>(this) );
1148 // xxx todo: maybe obsolete in the future
1149 try {
1150 destFolderContent.executeCommand( "flush", Any() );
1152 catch (const ucb::UnsupportedCommandException &) {
1157 sal_Bool BackendImpl::PackageImpl::isBundle()
1159 return true;
1163 Sequence< Reference<deployment::XPackage> > BackendImpl::PackageImpl::getBundle(
1164 Reference<task::XAbortChannel> const & xAbortChannel,
1165 Reference<ucb::XCommandEnvironment> const & xCmdEnv )
1167 Sequence< Reference<deployment::XPackage> > * pBundle = m_pBundle;
1168 if (pBundle == nullptr)
1170 t_packagevec bundle;
1171 if (m_bRemoved)
1173 bundle = getPackagesFromDb(xCmdEnv);
1175 else
1177 try {
1178 if (m_legacyBundle)
1180 // .zip legacy packages allow script.xlb, dialog.xlb in bundle
1181 // root folder:
1182 OUString mediaType;
1183 // probe for script.xlb:
1184 if (create_ucb_content(
1185 nullptr, makeURL( m_url_expanded, "script.xlb" ),
1186 xCmdEnv, false /* no throw */ )) {
1187 mediaType = "application/vnd.sun.star.basic-library";
1189 // probe for dialog.xlb:
1190 else if (create_ucb_content(
1191 nullptr, makeURL( m_url_expanded, "dialog.xlb" ),
1192 xCmdEnv, false /* no throw */ ))
1193 mediaType = "application/vnd.sun.star.dialog-library";
1195 if (!mediaType.isEmpty()) {
1196 const Reference<deployment::XPackage> xPackage(
1197 bindBundleItem( getURL(), mediaType, false, OUString(),
1198 xCmdEnv ) );
1199 if (xPackage.is())
1200 bundle.push_back( xPackage );
1201 // continue scanning:
1203 scanLegacyBundle( bundle, getURL(),
1204 AbortChannel::get(xAbortChannel), xCmdEnv );
1206 else
1208 // .oxt:
1209 scanBundle( bundle, AbortChannel::get(xAbortChannel), xCmdEnv );
1213 catch (const RuntimeException &) {
1214 throw;
1216 catch (const ucb::CommandFailedException &) {
1217 throw;
1219 catch (const ucb::CommandAbortedException &) {
1220 throw;
1222 catch (const deployment::DeploymentException &) {
1223 throw;
1225 catch (const Exception &) {
1226 Any exc( ::cppu::getCaughtException() );
1227 throw deployment::DeploymentException(
1228 "error scanning bundle: " + getURL(),
1229 static_cast<OWeakObject *>(this), exc );
1233 // sort: schema before config data, typelibs before components:
1234 Sequence< Reference<deployment::XPackage> > ret( bundle.size() );
1235 Reference<deployment::XPackage> * pret = ret.getArray();
1236 sal_Int32 lower_end = 0;
1237 sal_Int32 upper_end = ret.getLength();
1238 for (auto const& elem : bundle)
1240 const Reference<deployment::XPackageTypeInfo> xPackageType(
1241 elem->getPackageType() );
1242 OSL_ASSERT( xPackageType.is() );
1243 if (xPackageType.is())
1245 const OUString mediaType( xPackageType->getMediaType() );
1246 OUString type, subType;
1247 INetContentTypeParameterList params;
1248 if (INetContentTypes::parse( mediaType, type, subType, &params ) &&
1249 type.equalsIgnoreAsciiCase("application") &&
1250 (subType.equalsIgnoreAsciiCase( "vnd.sun.star.uno-component") ||
1251 subType.equalsIgnoreAsciiCase( "vnd.sun.star.configuration-data")))
1253 --upper_end;
1254 pret[ upper_end ] = elem;
1255 continue;
1258 pret[ lower_end ] = elem;
1259 ++lower_end;
1261 OSL_ASSERT( lower_end == upper_end );
1263 const ::osl::MutexGuard guard( getMutex() );
1264 pBundle = m_pBundle;
1265 if (pBundle == nullptr) {
1266 m_bundle = ret;
1267 pBundle = &m_bundle;
1268 OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
1269 m_pBundle = pBundle;
1272 else {
1273 OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
1275 return *pBundle;
1278 bool isBundle_( OUString const & mediaType )
1280 // xxx todo: additional parsing?
1281 return !mediaType.isEmpty() &&
1282 (mediaType.matchIgnoreAsciiCase( "application/vnd.sun.star.package-bundle") ||
1283 mediaType.matchIgnoreAsciiCase( "application/vnd.sun.star.legacy-package-bundle"));
1287 Reference<deployment::XPackage> BackendImpl::PackageImpl::bindBundleItem(
1288 OUString const & url, OUString const & mediaType,
1289 bool bRemoved, OUString const & identifier,
1290 Reference<ucb::XCommandEnvironment> const & xCmdEnv,
1291 bool notifyDetectionError )
1293 // ignore any nested bundles:
1294 if (isBundle_(mediaType))
1295 return Reference<deployment::XPackage>();
1297 Reference<deployment::XPackage>xPackage;
1298 try {
1299 try {
1300 xPackage.set( getMyBackend()->m_xRootRegistry->bindPackage(
1301 url, mediaType, bRemoved, identifier, xCmdEnv ) );
1302 OSL_ASSERT( xPackage.is() );
1303 } catch (css::lang::IllegalArgumentException & e) {
1304 css::uno::Any exc(cppu::getCaughtException());
1305 throw css::lang::WrappedTargetException(
1306 "wrapped: " + e.Message, e.Context, exc);
1309 catch (const RuntimeException &) {
1310 throw;
1312 catch (const ucb::CommandFailedException &) {
1313 // ignore already handled error
1315 catch (const Exception &) {
1316 const Any exc( ::cppu::getCaughtException() );
1317 if (notifyDetectionError ||
1318 !exc.isExtractableTo( cppu::UnoType<lang::IllegalArgumentException>::get()) )
1320 (void)interactContinuation(
1321 Any( lang::WrappedTargetException("bundle item error!",
1322 static_cast<OWeakObject *>(this), exc ) ),
1323 cppu::UnoType<task::XInteractionApprove>::get(), xCmdEnv, nullptr, nullptr );
1327 if (xPackage.is()) {
1328 const Reference<deployment::XPackageTypeInfo> xPackageType(
1329 xPackage->getPackageType() );
1330 OSL_ASSERT( xPackageType.is() );
1331 // ignore any nested bundles:
1332 if (xPackageType.is() && isBundle_( xPackageType->getMediaType() ))
1333 xPackage.clear();
1335 return xPackage;
1339 void BackendImpl::PackageImpl::scanBundle(
1340 t_packagevec & bundle,
1341 ::rtl::Reference<AbortChannel> const & abortChannel,
1342 Reference<ucb::XCommandEnvironment> const & xCmdEnv )
1344 OSL_ASSERT( !m_legacyBundle );
1346 OUString mfUrl( makeURL( m_url_expanded, "META-INF/manifest.xml" ) );
1347 ::ucbhelper::Content manifestContent;
1348 if (! create_ucb_content(
1349 &manifestContent, mfUrl, xCmdEnv, false /* no throw */ ))
1351 SAL_WARN(
1352 "desktop.deployment",
1353 "cannot create UCB Content for <" << mfUrl << ">" );
1354 return;
1358 const LanguageTag& officeLocale = getOfficeLanguageTag();
1359 const std::vector< OUString > officeFallbacks( officeLocale.getFallbackStrings( true));
1360 const size_t nPenaltyMax = std::numeric_limits<size_t>::max();
1361 size_t descrPenalty = nPenaltyMax;
1362 OUString descrFile;
1364 const Reference<XComponentContext> xContext(
1365 getMyBackend()->getComponentContext() );
1366 Reference<packages::manifest::XManifestReader> xManifestReader =
1367 packages::manifest::ManifestReader::create( xContext );
1368 const Sequence< Sequence<beans::PropertyValue> > manifestSeq(
1369 xManifestReader->readManifestSequence( manifestContent.openStream() ) );
1370 const OUString packageRootURL( getURL() );
1371 for ( sal_Int32 pos = manifestSeq.getLength(); pos--; )
1373 OUString fullPath, mediaType;
1374 Sequence<beans::PropertyValue> const & attribs = manifestSeq[ pos ];
1375 for ( sal_Int32 i = attribs.getLength(); i--; )
1377 if (!(fullPath.isEmpty() || mediaType.isEmpty()))
1378 break;
1379 if ( attribs[i].Name == "FullPath" )
1380 attribs[i].Value >>= fullPath;
1381 else if ( attribs[i].Name == "MediaType" )
1382 attribs[i].Value >>= mediaType;
1385 if ( fullPath.isEmpty() || mediaType.isEmpty() || mediaType == "text/xml" )// opt: exclude common text/xml
1386 continue;
1388 OUString type, subType;
1389 INetContentTypeParameterList params;
1390 if (! INetContentTypes::parse( mediaType, type, subType, &params ))
1391 continue;
1394 auto const iter = params.find("platform");
1395 if (iter != params.end() && !platform_fits(iter->second.m_sValue))
1396 continue;
1398 const OUString url( makeURL( packageRootURL, fullPath ) );
1400 // check for bundle description:
1401 if (type.equalsIgnoreAsciiCase("application") &&
1402 subType.equalsIgnoreAsciiCase( "vnd.sun.star.package-bundle-description"))
1404 // check locale:
1405 auto const iter = params.find("locale");
1406 if (iter == params.end())
1408 if (descrFile.isEmpty())
1409 descrFile = url;
1411 else {
1412 // match best locale:
1413 LanguageTag descrTag(iter->second.m_sValue);
1414 if (officeLocale.getLanguage() == descrTag.getLanguage())
1416 size_t nPenalty = nPenaltyMax;
1417 const std::vector< OUString > descrFallbacks( descrTag.getFallbackStrings( true));
1418 for (size_t o=0; o < officeFallbacks.size() && nPenalty == nPenaltyMax; ++o)
1420 for (size_t d=0; d < descrFallbacks.size() && nPenalty == nPenaltyMax; ++d)
1422 if (officeFallbacks[o] == descrFallbacks[d])
1424 // The last fallbacks are always language-only
1425 // fallbacks, so we _will_ have _some_ match if
1426 // we ever entered the overall if() condition.
1427 nPenalty = o * 1000 + d;
1428 if (descrPenalty > nPenalty)
1430 descrPenalty = nPenalty;
1431 descrFile = url;
1437 // TODO: we could break here if descrPenalty==0 for an exact
1438 // match of officeLocale, but the previous code didn't; are
1439 // there side effects?
1441 continue;
1444 checkAborted( abortChannel );
1446 //We make sure that we only create one XPackage for a particular URL.
1447 //Sometime programmers insert the same URL several times in the manifest
1448 //which may lead to DisposedExceptions.
1449 if (std::none_of(bundle.begin(), bundle.end(), XPackage_eq(url)))
1451 const Reference<deployment::XPackage> xPackage(
1452 bindBundleItem( url, mediaType, false, OUString(), xCmdEnv ) );
1453 if (xPackage.is())
1454 bundle.push_back( xPackage );
1456 else
1458 SAL_WARN("desktop.deployment", "manifest.xml contains a duplicate entry (from " << url << ")");
1462 if (!descrFile.isEmpty())
1464 ::ucbhelper::Content descrFileContent;
1465 if (create_ucb_content( &descrFileContent, descrFile,
1466 xCmdEnv, false /* no throw */ ))
1468 // patch description:
1469 std::vector<sal_Int8> bytes( readFile( descrFileContent ) );
1470 OUStringBuffer buf;
1471 if ( !bytes.empty() )
1473 buf.append( OUString( reinterpret_cast<sal_Char const *>(
1474 bytes.data() ),
1475 bytes.size(), RTL_TEXTENCODING_UTF8 ) );
1477 else
1479 buf.append( Package::getDescription() );
1481 m_oldDescription = buf.makeStringAndClear();
1487 void BackendImpl::PackageImpl::scanLegacyBundle(
1488 t_packagevec & bundle,
1489 OUString const & url,
1490 ::rtl::Reference<AbortChannel> const & abortChannel,
1491 Reference<ucb::XCommandEnvironment> const & xCmdEnv,
1492 bool skip_registration )
1494 ::ucbhelper::Content ucbContent(
1495 url, xCmdEnv, getMyBackend()->getComponentContext() );
1497 // check for platform paths:
1498 const OUString title( StrTitle::getTitle( ucbContent ) );
1499 if (title.endsWithIgnoreAsciiCase( ".plt" ) &&
1500 !platform_fits( title.copy( 0, title.getLength() - 4 ) )) {
1501 return;
1503 if (title.endsWithIgnoreAsciiCase("skip_registration") )
1504 skip_registration = true;
1506 Sequence<OUString> ar { OUString("Title"), OUString("IsFolder") };
1507 Reference<sdbc::XResultSet> xResultSet( ucbContent.createCursor( ar ) );
1508 while (xResultSet->next())
1510 checkAborted( abortChannel );
1512 const Reference<sdbc::XRow> xRow( xResultSet, UNO_QUERY_THROW );
1513 const OUString title_enc( ::rtl::Uri::encode(
1514 xRow->getString( 1 /* Title */ ),
1515 rtl_UriCharClassPchar,
1516 rtl_UriEncodeIgnoreEscapes,
1517 RTL_TEXTENCODING_UTF8 ) );
1518 const OUString path( makeURL( url, title_enc ) );
1520 OUString mediaType;
1521 const Reference<deployment::XPackage> xPackage(
1522 bindBundleItem( path, OUString() /* detect */, false, OUString(),
1523 xCmdEnv, false /* ignore detection errors */ ) );
1524 if (xPackage.is()) {
1525 const Reference<deployment::XPackageTypeInfo> xPackageType(
1526 xPackage->getPackageType() );
1527 OSL_ASSERT( xPackageType.is() );
1528 if (xPackageType.is())
1529 mediaType = xPackageType->getMediaType();
1531 if (skip_registration &&
1532 // xxx todo: additional parsing?
1533 mediaType.matchIgnoreAsciiCase("application/vnd.sun.star.uno-component"))
1534 continue;
1536 bundle.push_back( xPackage );
1539 if (mediaType.isEmpty() ||
1540 // script.xlb, dialog.xlb can be met everywhere:
1541 mediaType.matchIgnoreAsciiCase("application/vnd.sun.star.basic-library") ||
1542 mediaType.matchIgnoreAsciiCase("application/vnd.sun.star.dialog-library"))
1544 if (xRow->getBoolean( 2 /* IsFolder */ )) { // recurse into folder:
1545 scanLegacyBundle(
1546 bundle, path, abortChannel, xCmdEnv, skip_registration );
1552 OUString BackendImpl::PackageImpl::getDisplayName()
1554 if (m_bRemoved)
1555 throw deployment::ExtensionRemovedException();
1557 OUString sName = getDescriptionInfoset().getLocalizedDisplayName();
1558 if (sName.isEmpty())
1559 return m_displayName;
1560 else
1561 return sName;
1564 std::vector<Reference<deployment::XPackage> >
1565 BackendImpl::PackageImpl::getPackagesFromDb(
1566 Reference<ucb::XCommandEnvironment> const & xCmdEnv)
1568 std::vector<Reference<deployment::XPackage> > retVector;
1570 for (auto const& item : m_dbData.items)
1572 Reference<deployment::XPackage> xExtension =
1573 bindBundleItem(item.first, item.second, true, m_identifier, xCmdEnv);
1574 OSL_ASSERT(xExtension.is());
1575 if (xExtension.is())
1576 retVector.push_back(xExtension);
1579 return retVector;
1582 } // anon namespace
1585 Reference<deployment::XPackageRegistry> create(
1586 Reference<deployment::XPackageRegistry> const & xRootRegistry,
1587 OUString const & context, OUString const & cachePath,
1588 Reference<XComponentContext> const & xComponentContext )
1590 Sequence<Any> args(cachePath.isEmpty() ? 1 : 3 );
1591 args[ 0 ] <<= context;
1592 if (!cachePath.isEmpty()) {
1593 args[ 1 ] <<= cachePath;
1594 args[ 2 ] <<= false; // readOnly
1596 return new BackendImpl( args, xComponentContext, xRootRegistry );
1599 } // namespace bundle
1600 } // namespace backend
1601 } // namespace dp_registry
1603 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */