LibreOffice Module desktop (master) 1
dp_package.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20
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>
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>
39#include <ucbhelper/content.hxx>
40#include <svl/inettype.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>
72
73#include <algorithm>
74#include <memory>
75#include <string_view>
76#include <utility>
77#include <vector>
78
79#include "dp_extbackenddb.hxx"
80using namespace ::dp_misc;
81using namespace ::com::sun::star;
82using namespace ::com::sun::star::uno;
83
84
86namespace {
87
88typedef cppu::ImplInheritanceHelper<PackageRegistryBackend> ImplBaseT;
89
90
91class BackendImpl : public ImplBaseT
92{
93 class PackageImpl : public ::dp_registry::backend::Package
94 {
95 BackendImpl * getMyBackend() const;
101 const bool m_legacyBundle;
102 Sequence< Reference<deployment::XPackage> > m_bundle;
103 Sequence< Reference<deployment::XPackage> > * m_pBundle;
104
105 ExtensionBackendDb::Data m_dbData;
106
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 );
113
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);
129
130 bool checkDependencies(
131 Reference<ucb::XCommandEnvironment > const &
132 environment,
133 DescriptionInfoset const & description);
134 // throws css::uno::RuntimeException,
135 // css::deployment::DeploymentException
136
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);
148
149 DescriptionInfoset getDescriptionInfoset() const;
150
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;
162
163 virtual void SAL_CALL disposing() override;
164
165
166 public:
167 PackageImpl(
169 OUString const & url,
170 OUString const & name,
171 Reference<deployment::XPackageTypeInfo> const & xPackageType,
172 bool legacyBundle,
173 bool bRemoved,
174 OUString const & identifier);
175
176 // XPackage
177 virtual sal_Bool SAL_CALL isBundle() override;
178
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;
183
184 virtual OUString SAL_CALL getLicenseText() override;
185
186 virtual void SAL_CALL exportTo(
187 OUString const & destFolderURL, OUString const & newTitle,
188 sal_Int32 nameClashAction,
189 Reference<ucb::XCommandEnvironment> const & xCmdEnv ) override;
190
191 virtual ::sal_Int32 SAL_CALL checkPrerequisites(
192 const Reference< task::XAbortChannel >& xAbortChannel,
193 const Reference< ucb::XCommandEnvironment >& xCmdEnv,
194 sal_Bool noLicenseChecking) override;
195
196 virtual sal_Bool SAL_CALL checkDependencies(
197 const Reference< ucb::XCommandEnvironment >& xCmdEnv ) override;
198
199 virtual beans::Optional<OUString> SAL_CALL getIdentifier() override;
200
201 virtual OUString SAL_CALL getVersion() override;
202
203 virtual Sequence<OUString> SAL_CALL getUpdateInformationURLs() override;
204
205 virtual beans::StringPair SAL_CALL getPublisherInfo() override;
206
207 virtual OUString SAL_CALL getDisplayName() override;
208
209 virtual Reference< graphic::XGraphic > SAL_CALL
210 getIcon( sal_Bool bHighContrast ) override;
211 };
212 friend class PackageImpl;
213
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;
218
219 std::unique_ptr<ExtensionBackendDb> m_backendDb;
220
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);
224
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;
230
231 virtual void SAL_CALL disposing() override;
232
233public:
234 BackendImpl(
235 Sequence<Any> const & args,
236 Reference<XComponentContext> const & xComponentContext,
237 Reference<deployment::XPackageRegistry> const & xRootRegistry );
238
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;
243
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;
248
249 using ImplBaseT::disposing;
250};
251
252//Used to find a XPackage with a particular URL
253class XPackage_eq
254{
255 OUString m_URL;
256public:
257 explicit XPackage_eq(OUString s) : m_URL(std::move(s)) {}
258 bool operator() (const Reference<deployment::XPackage> & p) const
259 {
260 return m_URL == p->getURL();
261 }
262};
263
264
265BackendImpl::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 "application/vnd.sun.star.package-bundle",
273 "*.oxt;*.uno.pkg",
274 DpResId(RID_STR_PACKAGE_BUNDLE)
275 ) ),
276 m_xLegacyBundleTypeInfo( new Package::TypeInfo(
277 "application/vnd.sun.star.legacy-package-bundle",
278 "*.zip",
279 m_xBundleTypeInfo->getShortDescription()
280 ) ),
282{
283 if (!transientMode())
284 {
285 OUString dbFile = makeURL(getCachePath(), getImplementationName());
286 dbFile = makeURL(dbFile, "backenddb.xml");
287 m_backendDb.reset(
288 new ExtensionBackendDb(getComponentContext(), dbFile));
289 }
290}
291
292
293void BackendImpl::disposing()
294{
295 m_xRootRegistry.clear();
296 PackageRegistryBackend::disposing();
297}
298
299// XServiceInfo
300OUString BackendImpl::getImplementationName()
301{
302 return "com.sun.star.comp.deployment.bundle.PackageRegistryBackend";
303}
304
305sal_Bool BackendImpl::supportsService(OUString const & ServiceName)
306{
307 return cppu::supportsService(this, ServiceName);
308}
309
310Sequence<OUString> BackendImpl::getSupportedServiceNames()
311{
312 return { OUString(BACKEND_SERVICE_NAME) };
313}
314
315// XPackageRegistry
316
317Sequence< Reference<deployment::XPackageTypeInfo> >
318BackendImpl::getSupportedPackageTypes()
319{
320 return m_typeInfos;
321}
322
323void BackendImpl::packageRemoved(OUString const & url, OUString const & /*mediaType*/)
324{
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)
329 {
330 m_xRootRegistry->packageRemoved(item.first, item.second);
331 }
332
333 if (m_backendDb)
334 m_backendDb->removeEntry(url);
335}
336
337
338// PackageRegistryBackend
339
340Reference<deployment::XPackage> BackendImpl::bindPackage_(
341 OUString const & url, OUString const & mediaType_,
342 bool bRemoved, OUString const & identifier,
343 Reference<ucb::XCommandEnvironment> const & xCmdEnv )
344{
345 OUString mediaType( mediaType_ );
346 if (mediaType.isEmpty())
347 {
348 // detect media-type:
349 ::ucbhelper::Content ucbContent;
350 if (create_ucb_content( &ucbContent, url, xCmdEnv ))
351 {
352 if (ucbContent.isFolder())
353 {
354 //Every .oxt, uno.pkg file must contain a META-INF folder
355 ::ucbhelper::Content metaInfContent;
357 &metaInfContent, makeURL( url, "META-INF" ),
358 xCmdEnv, false /* no throw */ ))
359 {
360 mediaType = "application/vnd.sun.star.package-bundle";
361 }
362 //No support of legacy bundles, because every folder could be one.
363 }
364 else
365 {
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";
372 }
373 }
374 if (mediaType.isEmpty())
375 throw lang::IllegalArgumentException(
376 StrCannotDetectMediaType() + url,
377 static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
378 }
379
380 OUString type, subType;
382 if (INetContentTypes::parse( mediaType, type, subType, &params ))
383 {
384 if (type.equalsIgnoreAsciiCase("application"))
385 {
386
387 //In case a XPackage is created for a removed extension, we cannot
388 //obtain the name
389 OUString name;
390 if (!bRemoved)
391 {
392 ::ucbhelper::Content ucbContent(
393 url, xCmdEnv, getComponentContext() );
394 name = StrTitle::getTitle( ucbContent );
395 }
396 if (subType.equalsIgnoreAsciiCase("vnd.sun.star.package-bundle"))
397 {
398 return new PackageImpl(
399 this, url, name, m_xBundleTypeInfo, false, bRemoved,
400 identifier);
401 }
402 else if (subType.equalsIgnoreAsciiCase( "vnd.sun.star.legacy-package-bundle"))
403 {
404 return new PackageImpl(
405 this, url, name, m_xLegacyBundleTypeInfo, true, bRemoved,
406 identifier);
407 }
408 }
409 }
410 throw lang::IllegalArgumentException(
411 StrUnsupportedMediaType() + mediaType,
412 static_cast<OWeakObject *>(this),
413 static_cast<sal_Int16>(-1) );
414}
415
416void BackendImpl::addDataToDb(
417 OUString const & url, ExtensionBackendDb::Data const & data)
418{
419 if (m_backendDb)
420 m_backendDb->addEntry(url, data);
421}
422
423ExtensionBackendDb::Data BackendImpl::readDataFromDb(
424 std::u16string_view url)
425{
426 ExtensionBackendDb::Data data;
427 if (m_backendDb)
428 data = m_backendDb->getEntry(url);
429 return data;
430}
431
432void BackendImpl::revokeEntryFromDb(std::u16string_view url)
433{
434 if (m_backendDb)
435 m_backendDb->revokeEntry(url);
436}
437
438
439BackendImpl::PackageImpl::PackageImpl(
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),
448 m_legacyBundle( legacyBundle ),
449 m_pBundle( nullptr )
450{
451 if (bRemoved)
452 m_dbData = getMyBackend()->readDataFromDb(url);
453}
454
455BackendImpl * BackendImpl::PackageImpl::getMyBackend() const
456{
457 BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get());
458 if (nullptr == pBackend)
459 {
460 //May throw a DisposedException
461 check();
462 //We should never get here...
463 throw RuntimeException("Failed to get the BackendImpl",
464 static_cast<OWeakObject*>(const_cast<PackageImpl *>(this)));
465 }
466 return pBackend;
467}
468
469void BackendImpl::PackageImpl::disposing()
470{
471 sal_Int32 len = m_bundle.getLength();
472 Reference<deployment::XPackage> const * p = m_bundle.getConstArray();
473 for ( sal_Int32 pos = 0; pos < len; ++pos )
474 try_dispose( p[ pos ] );
475 m_bundle.realloc( 0 );
476
477 Package::disposing();
478}
479
480// Package
481
482beans::Optional< beans::Ambiguous<sal_Bool> >
483BackendImpl::PackageImpl::isRegistered_(
484 ::osl::ResettableMutexGuard &,
485 ::rtl::Reference<AbortChannel> const & abortChannel,
486 Reference<ucb::XCommandEnvironment> const & xCmdEnv )
487{
488 //In case the object was created for a removed extension (m_bRemoved = true)
489 //but the extension is not registered, then bundle will be empty. Then
490 //the return value will be Optional<...>.IsPresent= false. Although this is
491 //not true, this does not matter. Then registerPackage or revokePackage
492 //would never be called for the items. But since the extension is removed
493 //and not registered anyway, this does not matter.
494 const Sequence< Reference<deployment::XPackage> > bundle(
495 getBundle( abortChannel, xCmdEnv ) );
496
497 bool reg = false;
498 bool present = false;
499 bool ambig = false;
500 for ( sal_Int32 pos = bundle.getLength(); pos--; )
501 {
502 Reference<deployment::XPackage> const & xPackage = bundle[ pos ];
503 Reference<task::XAbortChannel> xSubAbortChannel(
504 xPackage->createAbortChannel() );
505 AbortChannel::Chain chain( abortChannel, xSubAbortChannel );
506 beans::Optional< beans::Ambiguous<sal_Bool> > option(
507 xPackage->isRegistered( xSubAbortChannel, xCmdEnv ) );
508
509 //present = true if at least one bundle item has this value.
510 //reg = true if all bundle items have an option value (option.IsPresent == 1)
511 //and all have value of true (option.Value.Value == true)
512 //If not, then the bundle has the status of not registered and ambiguous.
513 if (option.IsPresent)
514 {
515 beans::Ambiguous<sal_Bool> const & status = option.Value;
516 if (present)
517 {
518 //we never come here in the first iteration
519 if (reg != bool(status.Value)) {
520
521 ambig = true;
522 reg = false;
523 break;
524 }
525 }
526 else
527 {
528 //we always come here in the first iteration
529 reg = status.Value;
530 present = true;
531 }
532 }
533 }
534 return beans::Optional< beans::Ambiguous<sal_Bool> >(
535 present, beans::Ambiguous<sal_Bool>(reg, ambig) );
536}
537
538OUString BackendImpl::PackageImpl::getTextFromURL(
539 const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv,
540 const OUString& licenseUrl)
541{
542 try
543 {
544 ::ucbhelper::Content descContent(
545 licenseUrl, xCmdEnv, getMyBackend()->getComponentContext());
546 std::vector<sal_Int8> seq = dp_misc::readFile(descContent);
547 return OUString( reinterpret_cast<char const *>(
548 seq.data()), seq.size(), RTL_TEXTENCODING_UTF8);
549 }
550 catch (const css::uno::Exception&)
551 {
552 Any exc( ::cppu::getCaughtException() );
553 throw css::deployment::DeploymentException(
554 "Could not read file " + licenseUrl, nullptr, exc);
555 }
556
557}
558
560{
562}
563
564bool BackendImpl::PackageImpl::checkPlatform(
565 css::uno::Reference< css::ucb::XCommandEnvironment > const & environment)
566{
567 bool ret = false;
568 DescriptionInfoset info(getDescriptionInfoset());
569 Sequence<OUString> platforms(info.getSupportedPlatforms());
570 if (hasValidPlatform(platforms))
571 {
572 ret = true;
573 }
574 else
575 {
576 ret = false;
577 OUString msg(
578 "unsupported platform");
579 Any e(
580 css::deployment::PlatformException(
581 msg, static_cast<OWeakObject *>(this), this));
584 environment, nullptr, nullptr))
585 {
586 throw css::deployment::DeploymentException(
587 msg, static_cast<OWeakObject *>(this), e);
588 }
589 }
590 return ret;
591}
592
593
594bool BackendImpl::PackageImpl::checkDependencies(
595 css::uno::Reference< css::ucb::XCommandEnvironment > const & environment,
596 DescriptionInfoset const & description)
597{
598 css::uno::Sequence< css::uno::Reference< css::xml::dom::XElement > >
599 unsatisfied(dp_misc::Dependencies::check(description));
600
601 if (!unsatisfied.hasElements()) {
602 return true;
603 } else {
604 OUString msg(
605 "unsatisfied dependencies");
606 Any e(
607 css::deployment::DependencyException(
608 msg, static_cast<OWeakObject *>(this), unsatisfied));
611 environment, nullptr, nullptr))
612 {
613 throw css::deployment::DeploymentException(
614 msg, static_cast<OWeakObject *>(this), e);
615 }
616 return false;
617 }
618}
619
620bool BackendImpl::PackageImpl::checkLicense(
621 css::uno::Reference< css::ucb::XCommandEnvironment > const & xCmdEnv,
622 DescriptionInfoset const & info, bool alreadyInstalled)
623{
624 try
625 {
626 ::std::optional<SimpleLicenseAttributes> simplLicAttr
627 = info.getSimpleLicenseAttributes();
628 if (! simplLicAttr)
629 return true;
630 OUString sLic = info.getLocalizedLicenseURL();
631 //If we do not get a localized licence then there is an error in the description.xml
632 //This should be handled by using a validating parser. Therefore we assume that no
633 //license is available.
634 if (sLic.isEmpty())
635 throw css::deployment::DeploymentException(
636 "Could not obtain path to license. Possible error in description.xml", nullptr, Any());
637 OUString sHref = m_url_expanded + "/" + sLic;
638 OUString sLicense = getTextFromURL(xCmdEnv, sHref);
640 //check correct value for attribute
641 if ( simplLicAttr->acceptBy != "user" && simplLicAttr->acceptBy != "admin")
642 throw css::deployment::DeploymentException(
643 "Could not obtain attribute simple-license@accept-by or it has no valid value", nullptr, Any());
644
645
646 //Only use interaction if there is no version of this extension already installed
647 //and the suppress-on-update flag is not set for the new extension
648 // alreadyInstalled | bSuppressOnUpdate | show license
649
650 // 0 | 0 | 1
651 // 0 | 1 | 1
652 // 1 | 0 | 1
653 // 1 | 1 | 0
654
655 if ( !(alreadyInstalled && simplLicAttr->suppressOnUpdate))
656 {
657 css::deployment::LicenseException licExc(
658 OUString(), nullptr, getDisplayName(), sLicense,
659 simplLicAttr->acceptBy);
660 bool approve = false;
661 bool abort = false;
663 Any(licExc), cppu::UnoType<task::XInteractionApprove>::get(), xCmdEnv, &approve, &abort ))
664 throw css::deployment::DeploymentException(
665 "Could not interact with user.", nullptr, Any());
666
667 return approve;
668 }
669 return true;
670 } catch (const css::ucb::CommandFailedException&) {
671 throw;
672 } catch (const css::ucb::CommandAbortedException&) {
673 throw;
674 } catch (const css::deployment::DeploymentException&) {
675 throw;
676 } catch (const css::uno::RuntimeException&) {
677 throw;
678 } catch (const css::uno::Exception&) {
679 Any anyExc = cppu::getCaughtException();
680 throw css::deployment::DeploymentException("Unexpected exception", nullptr, anyExc);
681 }
682}
683
684::sal_Int32 BackendImpl::PackageImpl::checkPrerequisites(
685 const css::uno::Reference< css::task::XAbortChannel >&,
686 const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv,
687 sal_Bool alreadyInstalled)
688{
689 if (m_bRemoved)
690 throw deployment::ExtensionRemovedException();
691 DescriptionInfoset info = getDescriptionInfoset();
692 if (!info.hasDescription())
693 return 0;
694
695 //always return LICENSE as long as the user did not accept the license
696 //so that XExtensionManager::checkPrerequisitesAndEnable will again
697 //check the license
698 if (!checkPlatform(xCmdEnv))
699 return deployment::Prerequisites::PLATFORM |
700 deployment::Prerequisites::LICENSE;
701 else if(!checkDependencies(xCmdEnv, info))
702 return deployment::Prerequisites::DEPENDENCIES |
703 deployment::Prerequisites::LICENSE;
704 else if(!checkLicense(xCmdEnv, info, alreadyInstalled))
705 return deployment::Prerequisites::LICENSE;
706 else
707 return 0;
708}
709
710sal_Bool BackendImpl::PackageImpl::checkDependencies(
711 const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv )
712{
713 if (m_bRemoved)
714 throw deployment::ExtensionRemovedException();
715 DescriptionInfoset info = getDescriptionInfoset();
716 if (!info.hasDescription())
717 return true;
718
719 return checkDependencies(xCmdEnv, info);
720}
721
722beans::Optional<OUString> BackendImpl::PackageImpl::getIdentifier()
723{
724 OUString identifier;
725 if (m_bRemoved)
726 identifier = m_identifier;
727 else
730
731 return beans::Optional<OUString>(
732 true, identifier);
733}
734
735OUString BackendImpl::PackageImpl::getVersion()
736{
737 if (m_bRemoved)
738 throw deployment::ExtensionRemovedException();
740}
741
742Sequence<OUString> BackendImpl::PackageImpl::getUpdateInformationURLs()
743{
744 if (m_bRemoved)
745 throw deployment::ExtensionRemovedException();
747}
748
749beans::StringPair BackendImpl::PackageImpl::getPublisherInfo()
750{
751 if (m_bRemoved)
752 throw deployment::ExtensionRemovedException();
753 std::pair< OUString, OUString > aInfo = getDescriptionInfoset().getLocalizedPublisherNameAndURL();
754 beans::StringPair aStrPair( aInfo.first, aInfo.second );
755 return aStrPair;
756}
757
758
759uno::Reference< graphic::XGraphic > BackendImpl::PackageImpl::getIcon( sal_Bool bHighContrast )
760{
761 if (m_bRemoved)
762 throw deployment::ExtensionRemovedException();
763
765
766 OUString aIconURL = getDescriptionInfoset().getIconURL( bHighContrast );
767 if ( !aIconURL.isEmpty() )
768 {
769 OUString aFullIconURL = m_url_expanded + "/" + aIconURL;
770
773
775 "URL", aFullIconURL) };
776 xGraphic = xGraphProvider->queryGraphic( aMediaProps );
777 }
778
779 return xGraphic;
780}
781
782
783void BackendImpl::PackageImpl::processPackage_(
784 ::osl::ResettableMutexGuard &,
785 bool doRegisterPackage,
786 bool startup,
787 ::rtl::Reference<AbortChannel> const & abortChannel,
788 Reference<ucb::XCommandEnvironment> const & xCmdEnv )
789{
790 const Sequence< Reference<deployment::XPackage> > bundle(
791 getBundle( abortChannel, xCmdEnv ) );
792
793 if (doRegisterPackage)
794 {
795 ExtensionBackendDb::Data data;
796 const sal_Int32 len = bundle.getLength();
797 for ( sal_Int32 pos = 0; pos < len; ++pos )
798 {
799 checkAborted(abortChannel);
800 Reference<deployment::XPackage> const & xPackage = bundle[ pos ];
801 Reference<task::XAbortChannel> xSubAbortChannel(
802 xPackage->createAbortChannel() );
803 AbortChannel::Chain chain( abortChannel, xSubAbortChannel );
804 try {
805 xPackage->registerPackage( startup, xSubAbortChannel, xCmdEnv );
806 }
807 catch (const Exception &)
808 {
809 //We even try a rollback if the user cancelled the action (CommandAbortedException)
810 //in order to prevent invalid database entries.
811 Any exc( ::cppu::getCaughtException() );
812 // try to handle exception, notify:
813 bool approve = false, abort = false;
815 Any( lang::WrappedTargetException(
816 "bundle item registration error!",
817 static_cast<OWeakObject *>(this), exc ) ),
819 &approve, &abort )) {
820 OSL_ASSERT( !approve && !abort );
821 if (m_legacyBundle) // default for legacy packages: ignore
822 continue;
823 // no selection at all, so rethrow;
824 // no C++ rethrow after getCaughtException(),
825 // see cppuhelper/exc_hlp.hxx:
826 ::cppu::throwException(exc);
827 }
828 if (approve && !abort) // ignore error, just continue
829 continue;
830
831 {
832 ProgressLevel progress( xCmdEnv, "rollback..." );
833 // try rollback
834 for ( ; pos--; )
835 {
836 try {
837 bundle[ pos ]->revokePackage(
838 startup, xSubAbortChannel, xCmdEnv );
839 }
840 catch (const Exception &)
841 {
842 TOOLS_WARN_EXCEPTION( "desktop", "" );
843 // ignore any errors of rollback
844 }
845 }
846 progress.update( "rollback finished." );
847 }
848
849 deployment::DeploymentException dpExc;
850 if (exc >>= dpExc) {
851 throw ucb::CommandFailedException(
852 dpExc.Message, dpExc.Context, dpExc.Cause );
853 }
854 else {
855 // rethrow CommandFailedException
856 ::cppu::throwException(exc);
857 }
858 }
859 data.items.emplace_back(xPackage->getURL(),
860 xPackage->getPackageType()->getMediaType());
861 }
862 getMyBackend()->addDataToDb(getURL(), data);
863 }
864 else
865 {
866 // revoke in reverse order:
867 for ( sal_Int32 pos = bundle.getLength(); pos--; )
868 {
869 checkAborted(abortChannel);
870 Reference<deployment::XPackage> const & xPackage = bundle[ pos ];
871 Reference<task::XAbortChannel> xSubAbortChannel(
872 xPackage->createAbortChannel() );
873 AbortChannel::Chain chain( abortChannel, xSubAbortChannel );
874 try {
875 bundle[ pos ]->revokePackage(
876 startup, xSubAbortChannel, xCmdEnv );
877 }
878 catch (const RuntimeException &) {
879 throw;
880 }
881 catch (const ucb::CommandAbortedException &) {
882 throw;
883 }
884 catch (const Exception &) {
885 // CommandFailedException, DeploymentException:
886 Any exc( ::cppu::getCaughtException() );
887 // try to handle exception, notify:
888 bool approve = false, abort = false;
890 Any( lang::WrappedTargetException(
891 "bundle item revocation error!",
892 static_cast<OWeakObject *>(this), exc ) ),
894 &approve, &abort )) {
895 OSL_ASSERT( !approve && !abort );
896 if (m_legacyBundle) // default for legacy packages: ignore
897 continue;
898 // no selection at all, so rethrow
899 // no C++ rethrow after getCaughtException(),
900 // see cppuhelper/exc_hlp.hxx:
901 ::cppu::throwException(exc);
902 }
903 // ignore errors when revoking, although abort may have been
904 // selected
905 }
906 }
907 getMyBackend()->revokeEntryFromDb(getURL());
908 }
909}
910
911
912OUString BackendImpl::PackageImpl::getDescription()
913{
914 if (m_bRemoved)
915 throw deployment::ExtensionRemovedException();
916
917 const OUString sRelativeURL(getDescriptionInfoset().getLocalizedDescriptionURL());
918 OUString sDescription;
919 if (!sRelativeURL.isEmpty())
920 {
921 OUString sURL = m_url_expanded + "/" + sRelativeURL;
922
923 try
924 {
925 sDescription = getTextFromURL( css::uno::Reference< css::ucb::XCommandEnvironment >(), sURL );
926 }
927 catch ( const css::deployment::DeploymentException& )
928 {
929 TOOLS_WARN_EXCEPTION( "desktop", "" );
930 }
931 }
932
933 if (!sDescription.isEmpty())
934 return sDescription;
935 return m_oldDescription;
936}
937
938
939OUString BackendImpl::PackageImpl::getLicenseText()
940{
941 if (m_bRemoved)
942 throw deployment::ExtensionRemovedException();
943
944 OUString sLicense;
945 DescriptionInfoset aInfo = getDescriptionInfoset();
946
947 ::std::optional< SimpleLicenseAttributes > aSimplLicAttr = aInfo.getSimpleLicenseAttributes();
948 if ( aSimplLicAttr )
949 {
950 OUString aLicenseURL = aInfo.getLocalizedLicenseURL();
951
952 if ( !aLicenseURL.isEmpty() )
953 {
954 OUString aFullURL = m_url_expanded + "/" + aLicenseURL;
955 sLicense = getTextFromURL( Reference< ucb::XCommandEnvironment >(), aFullURL);
956 }
957 }
958
959 return sLicense;
960}
961
962
963void BackendImpl::PackageImpl::exportTo(
964 OUString const & destFolderURL, OUString const & newTitle,
965 sal_Int32 nameClashAction, Reference<ucb::XCommandEnvironment> const & xCmdEnv )
966{
967 if (m_bRemoved)
968 throw deployment::ExtensionRemovedException();
969
970 ::ucbhelper::Content sourceContent(
971 m_url_expanded, xCmdEnv, getMyBackend()->getComponentContext() );
972 OUString title(newTitle);
973 if (title.isEmpty())
974 sourceContent.getPropertyValue( "Title" ) >>= title;
975 OUString destURL( makeURL( destFolderURL, ::rtl::Uri::encode(
976 title, rtl_UriCharClassPchar,
977 rtl_UriEncodeIgnoreEscapes,
978 RTL_TEXTENCODING_UTF8 ) ) );
979
980 if (nameClashAction == ucb::NameClash::ASK)
981 {
983 nullptr, destURL, xCmdEnv, false /* no throw */ )) {
984 bool replace = false, abort = false;
986 Any( ucb::NameClashResolveRequest(
987 "file already exists: " + title,
988 static_cast<OWeakObject *>(this),
989 task::InteractionClassification_QUERY,
990 destFolderURL, title, OUString() ) ),
992 &replace, &abort ) || !replace) {
993 return;
994 }
995 }
996 }
997 else if (nameClashAction != ucb::NameClash::OVERWRITE) {
998 throw ucb::CommandFailedException("unsupported nameClashAction!",
999 static_cast<OWeakObject *>(this), Any() );
1000 }
1001 erase_path( destURL, xCmdEnv );
1002
1003 OUString destFolder =
1004 "vnd.sun.star.zip://" +
1005 ::rtl::Uri::encode( destURL,
1006 rtl_UriCharClassRegName,
1007 rtl_UriEncodeIgnoreEscapes,
1008 RTL_TEXTENCODING_UTF8 ) +
1009 "/";
1010
1011 ::ucbhelper::Content destFolderContent(
1012 destFolder, xCmdEnv, getMyBackend()->getComponentContext() );
1013 {
1014 // transfer every item of folder into zip:
1015 Reference<sdbc::XResultSet> xResultSet(
1016 sourceContent.createCursor( Sequence<OUString>() ) );
1017 ProgressLevel progress( xCmdEnv, OUString() );
1018 while (xResultSet->next())
1019 {
1020 ::ucbhelper::Content subContent(
1021 Reference<ucb::XContentAccess>(
1022 xResultSet, UNO_QUERY_THROW )->queryContent(),
1023 xCmdEnv, getMyBackend()->getComponentContext() );
1024 destFolderContent.transferContent(
1026 OUString(), ucb::NameClash::OVERWRITE );
1027 progress.update( Any() ); // animating progress bar
1028 }
1029 }
1030
1031 // assure META-INF folder:
1032 ::ucbhelper::Content metainfFolderContent;
1033 create_folder( &metainfFolderContent,
1034 makeURL( destFolderContent.getURL(), "META-INF" ),
1035 xCmdEnv );
1036
1037 if (m_legacyBundle)
1038 {
1039 // easy to migrate legacy bundles to new format:
1040 // just export them once using a .oxt name!
1041 // set detected media-types of any bundle item:
1042
1043 // collect all manifest entries:
1044 Sequence< Reference<deployment::XPackage> > bundle;
1045 try {
1046 bundle = getBundle( Reference<task::XAbortChannel>(), xCmdEnv );
1047 }
1048 // xxx todo: think about exception specs:
1049 catch (const deployment::DeploymentException &) {
1050 TOOLS_WARN_EXCEPTION( "desktop", "" );
1051 }
1052 catch (const lang::IllegalArgumentException &) {
1053 TOOLS_WARN_EXCEPTION( "desktop", "" );
1054 }
1055
1056 std::vector< Sequence<beans::PropertyValue> > manifest;
1057 manifest.reserve( bundle.getLength() );
1058 sal_Int32 baseURLlen = m_url_expanded.getLength();
1059 Reference<deployment::XPackage> const *pbundle = bundle.getConstArray();
1060 static constexpr OUStringLiteral strMediaType( u"MediaType" );
1061 static constexpr OUStringLiteral strFullPath( u"FullPath" );
1062 static constexpr OUStringLiteral strIsFolder( u"IsFolder" );
1063 for ( sal_Int32 pos = bundle.getLength(); pos--; )
1064 {
1065 Reference<deployment::XPackage> const & xPackage = pbundle[ pos ];
1066 OUString url_( expandUnoRcUrl( xPackage->getURL() ) );
1067 OSL_ASSERT( url_.getLength() >= baseURLlen );
1068 OUString fullPath;
1069 if (url_.getLength() > baseURLlen)
1070 fullPath = url_.copy( baseURLlen + 1 );
1071 ::ucbhelper::Content ucbContent(
1072 url_, xCmdEnv, getMyBackend()->getComponentContext() );
1073 if (ucbContent.getPropertyValue(strIsFolder).get<bool>())
1074 fullPath += "/";
1075 Sequence<beans::PropertyValue> attribs( 2 );
1076 beans::PropertyValue * pattribs = attribs.getArray();
1077 pattribs[ 0 ].Name = strFullPath;
1078 pattribs[ 0 ].Value <<= fullPath;
1079 pattribs[ 1 ].Name = strMediaType;
1080 const Reference<deployment::XPackageTypeInfo> xPackageType(
1081 xPackage->getPackageType() );
1082 OUString mediaType;
1083 OSL_ASSERT( xPackageType.is() );
1084 if (xPackageType.is())
1085 mediaType = xPackageType->getMediaType();
1086 else
1087 mediaType = "unknown";
1088 pattribs[ 1 ].Value <<= mediaType;
1089 manifest.push_back( attribs );
1090 }
1091
1092 // write into pipe:
1093 Reference<XComponentContext> xContext(
1094 getMyBackend()->getComponentContext() );
1095 Reference<packages::manifest::XManifestWriter> xManifestWriter =
1097 Reference<io::XOutputStream> xPipe( io::Pipe::create(xContext), UNO_QUERY_THROW );
1098 xManifestWriter->writeManifestSequence(
1099 xPipe, comphelper::containerToSequence(manifest) );
1100
1101 // write buffered pipe data to content:
1102 ::ucbhelper::Content manifestContent(
1103 makeURL( metainfFolderContent.getURL(), "manifest.xml" ),
1104 xCmdEnv, getMyBackend()->getComponentContext() );
1105 manifestContent.writeStream(
1106 Reference<io::XInputStream>( xPipe, UNO_QUERY_THROW ),
1107 true /* replace existing */ );
1108 }
1109 else
1110 {
1111 bool bSuccess = false;
1112 try
1113 {
1114 // overwrite manifest.xml:
1115 ::ucbhelper::Content manifestContent;
1116 if ( ! create_ucb_content(
1117 &manifestContent,
1118 makeURL( m_url_expanded, "META-INF/manifest.xml" ),
1119 xCmdEnv, false ) )
1120 {
1121 OSL_FAIL( "### missing META-INF/manifest.xml file!" );
1122 return;
1123 }
1124
1125 metainfFolderContent.transferContent(
1126 manifestContent, ::ucbhelper::InsertOperation::Copy,
1127 OUString(), ucb::NameClash::OVERWRITE );
1128 bSuccess = true;
1129 }
1130 catch (const css::ucb::ContentCreationException &)
1131 {
1132 TOOLS_WARN_EXCEPTION("desktop.deployment", "exception on overwriting manifest");
1133 }
1134
1135 if (!bSuccess)
1136 throw RuntimeException( "UCB transferContent() failed!",
1137 static_cast<OWeakObject *>(this) );
1138 }
1139
1140 // xxx todo: maybe obsolete in the future
1141 try {
1142 destFolderContent.executeCommand( "flush", Any() );
1143 }
1144 catch (const ucb::UnsupportedCommandException &) {
1145 }
1146}
1147
1148
1149sal_Bool BackendImpl::PackageImpl::isBundle()
1150{
1151 return true;
1152}
1153
1154
1155Sequence< Reference<deployment::XPackage> > BackendImpl::PackageImpl::getBundle(
1156 Reference<task::XAbortChannel> const & xAbortChannel,
1157 Reference<ucb::XCommandEnvironment> const & xCmdEnv )
1158{
1159 Sequence< Reference<deployment::XPackage> > * pBundle = m_pBundle;
1160 if (pBundle == nullptr)
1161 {
1162 t_packagevec bundle;
1163 if (m_bRemoved)
1164 {
1165 bundle = getPackagesFromDb(xCmdEnv);
1166 }
1167 else
1168 {
1169 try {
1170 if (m_legacyBundle)
1171 {
1172 // .zip legacy packages allow script.xlb, dialog.xlb in bundle
1173 // root folder:
1174 OUString mediaType;
1175 // probe for script.xlb:
1177 nullptr, makeURL( m_url_expanded, "script.xlb" ),
1178 xCmdEnv, false /* no throw */ )) {
1179 mediaType = "application/vnd.sun.star.basic-library";
1180 }
1181 // probe for dialog.xlb:
1182 else if (create_ucb_content(
1183 nullptr, makeURL( m_url_expanded, "dialog.xlb" ),
1184 xCmdEnv, false /* no throw */ ))
1185 mediaType = "application/vnd.sun.star.dialog-library";
1186
1187 if (!mediaType.isEmpty()) {
1188 const Reference<deployment::XPackage> xPackage(
1189 bindBundleItem( getURL(), mediaType, false, OUString(),
1190 xCmdEnv ) );
1191 if (xPackage.is())
1192 bundle.push_back( xPackage );
1193 // continue scanning:
1194 }
1195 scanLegacyBundle( bundle, getURL(),
1196 AbortChannel::get(xAbortChannel), xCmdEnv );
1197 }
1198 else
1199 {
1200 // .oxt:
1201 scanBundle( bundle, AbortChannel::get(xAbortChannel), xCmdEnv );
1202 }
1203
1204 }
1205 catch (const RuntimeException &) {
1206 throw;
1207 }
1208 catch (const ucb::CommandFailedException &) {
1209 throw;
1210 }
1211 catch (const ucb::CommandAbortedException &) {
1212 throw;
1213 }
1214 catch (const deployment::DeploymentException &) {
1215 throw;
1216 }
1217 catch (const Exception &) {
1218 Any exc( ::cppu::getCaughtException() );
1219 throw deployment::DeploymentException(
1220 "error scanning bundle: " + getURL(),
1221 static_cast<OWeakObject *>(this), exc );
1222 }
1223 }
1224
1225 // sort: schema before config data, typelibs before components:
1226 Sequence< Reference<deployment::XPackage> > ret( bundle.size() );
1227 Reference<deployment::XPackage> * pret = ret.getArray();
1228 sal_Int32 lower_end = 0;
1229 sal_Int32 upper_end = ret.getLength();
1230 for (auto const& elem : bundle)
1231 {
1232 const Reference<deployment::XPackageTypeInfo> xPackageType(
1233 elem->getPackageType() );
1234 OSL_ASSERT( xPackageType.is() );
1235 if (xPackageType.is())
1236 {
1237 const OUString mediaType( xPackageType->getMediaType() );
1238 OUString type, subType;
1240 if (INetContentTypes::parse( mediaType, type, subType, &params ) &&
1241 type.equalsIgnoreAsciiCase("application") &&
1242 (subType.equalsIgnoreAsciiCase( "vnd.sun.star.uno-component") ||
1243 subType.equalsIgnoreAsciiCase( "vnd.sun.star.configuration-data")))
1244 {
1245 --upper_end;
1246 pret[ upper_end ] = elem;
1247 continue;
1248 }
1249 }
1250 pret[ lower_end ] = elem;
1251 ++lower_end;
1252 }
1253 OSL_ASSERT( lower_end == upper_end );
1254
1255 const ::osl::MutexGuard guard( m_aMutex );
1256 pBundle = m_pBundle;
1257 if (pBundle == nullptr) {
1258 m_bundle = ret;
1259 pBundle = &m_bundle;
1260 OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
1261 m_pBundle = pBundle;
1262 }
1263 }
1264 else {
1265 OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
1266 }
1267 return *pBundle;
1268}
1269
1270bool isBundle_( std::u16string_view mediaType )
1271{
1272 // xxx todo: additional parsing?
1273 return !mediaType.empty() &&
1274 (o3tl::matchIgnoreAsciiCase( mediaType, u"application/vnd.sun.star.package-bundle") ||
1275 o3tl::matchIgnoreAsciiCase( mediaType, u"application/vnd.sun.star.legacy-package-bundle"));
1276}
1277
1278
1279Reference<deployment::XPackage> BackendImpl::PackageImpl::bindBundleItem(
1280 OUString const & url, OUString const & mediaType,
1281 bool bRemoved, OUString const & identifier,
1282 Reference<ucb::XCommandEnvironment> const & xCmdEnv,
1283 bool notifyDetectionError )
1284{
1285 // ignore any nested bundles:
1286 if (isBundle_(mediaType))
1287 return Reference<deployment::XPackage>();
1288
1289 Reference<deployment::XPackage>xPackage;
1290 try {
1291 try {
1292 xPackage.set( getMyBackend()->m_xRootRegistry->bindPackage(
1293 url, mediaType, bRemoved, identifier, xCmdEnv ) );
1294 OSL_ASSERT( xPackage.is() );
1295 } catch (css::lang::IllegalArgumentException & e) {
1296 css::uno::Any exc(cppu::getCaughtException());
1297 throw css::lang::WrappedTargetException(
1298 "wrapped: " + e.Message, e.Context, exc);
1299 }
1300 }
1301 catch (const RuntimeException &) {
1302 throw;
1303 }
1304 catch (const ucb::CommandFailedException &) {
1305 // ignore already handled error
1306 }
1307 catch (const Exception &) {
1308 const Any exc( ::cppu::getCaughtException() );
1309 if (notifyDetectionError ||
1311 {
1313 Any( lang::WrappedTargetException("bundle item error!",
1314 static_cast<OWeakObject *>(this), exc ) ),
1315 cppu::UnoType<task::XInteractionApprove>::get(), xCmdEnv, nullptr, nullptr );
1316 }
1317 }
1318
1319 if (xPackage.is()) {
1320 const Reference<deployment::XPackageTypeInfo> xPackageType(
1321 xPackage->getPackageType() );
1322 OSL_ASSERT( xPackageType.is() );
1323 // ignore any nested bundles:
1324 if (xPackageType.is() && isBundle_( xPackageType->getMediaType() ))
1325 xPackage.clear();
1326 }
1327 return xPackage;
1328}
1329
1330
1331void BackendImpl::PackageImpl::scanBundle(
1332 t_packagevec & bundle,
1333 ::rtl::Reference<AbortChannel> const & abortChannel,
1334 Reference<ucb::XCommandEnvironment> const & xCmdEnv )
1335{
1336 OSL_ASSERT( !m_legacyBundle );
1337
1338 OUString mfUrl( makeURL( m_url_expanded, "META-INF/manifest.xml" ) );
1339 ::ucbhelper::Content manifestContent;
1340 if (! create_ucb_content(
1341 &manifestContent, mfUrl, xCmdEnv, false /* no throw */ ))
1342 {
1343 SAL_WARN(
1344 "desktop.deployment",
1345 "cannot create UCB Content for <" << mfUrl << ">" );
1346 return;
1347 }
1348
1349
1350 const LanguageTag& officeLocale = getOfficeLanguageTag();
1351 const std::vector< OUString > officeFallbacks( officeLocale.getFallbackStrings( true));
1352 const size_t nPenaltyMax = std::numeric_limits<size_t>::max();
1353 size_t descrPenalty = nPenaltyMax;
1354 OUString descrFile;
1355
1356 const Reference<XComponentContext> xContext(
1357 getMyBackend()->getComponentContext() );
1358 Reference<packages::manifest::XManifestReader> xManifestReader =
1360 const Sequence< Sequence<beans::PropertyValue> > manifestSeq(
1361 xManifestReader->readManifestSequence( manifestContent.openStream() ) );
1362 const OUString packageRootURL( getURL() );
1363 for ( sal_Int32 pos = manifestSeq.getLength(); pos--; )
1364 {
1365 OUString fullPath, mediaType;
1366 Sequence<beans::PropertyValue> const & attribs = manifestSeq[ pos ];
1367 for ( sal_Int32 i = attribs.getLength(); i--; )
1368 {
1369 if (!(fullPath.isEmpty() || mediaType.isEmpty()))
1370 break;
1371 if ( attribs[i].Name == "FullPath" )
1372 attribs[i].Value >>= fullPath;
1373 else if ( attribs[i].Name == "MediaType" )
1374 attribs[i].Value >>= mediaType;
1375 }
1376
1377 if ( fullPath.isEmpty() || mediaType.isEmpty() || mediaType == "text/xml" )// opt: exclude common text/xml
1378 continue;
1379
1380 OUString type, subType;
1382 if (! INetContentTypes::parse( mediaType, type, subType, &params ))
1383 continue;
1384
1385 {
1386 auto const iter = params.find("platform");
1387 if (iter != params.end() && !platform_fits(iter->second.m_sValue))
1388 continue;
1389 }
1390 const OUString url( makeURL( packageRootURL, fullPath ) );
1391
1392 // check for bundle description:
1393 if (type.equalsIgnoreAsciiCase("application") &&
1394 subType.equalsIgnoreAsciiCase( "vnd.sun.star.package-bundle-description"))
1395 {
1396 // check locale:
1397 auto const iter = params.find("locale");
1398 if (iter == params.end())
1399 {
1400 if (descrFile.isEmpty())
1401 descrFile = url;
1402 }
1403 else {
1404 // match best locale:
1405 LanguageTag descrTag(iter->second.m_sValue);
1406 if (officeLocale.getLanguage() == descrTag.getLanguage())
1407 {
1408 size_t nPenalty = nPenaltyMax;
1409 const std::vector< OUString > descrFallbacks( descrTag.getFallbackStrings( true));
1410 for (size_t o=0; o < officeFallbacks.size() && nPenalty == nPenaltyMax; ++o)
1411 {
1412 for (size_t d=0; d < descrFallbacks.size() && nPenalty == nPenaltyMax; ++d)
1413 {
1414 if (officeFallbacks[o] == descrFallbacks[d])
1415 {
1416 // The last fallbacks are always language-only
1417 // fallbacks, so we _will_ have _some_ match if
1418 // we ever entered the overall if() condition.
1419 nPenalty = o * 1000 + d;
1420 if (descrPenalty > nPenalty)
1421 {
1422 descrPenalty = nPenalty;
1423 descrFile = url;
1424 }
1425 }
1426 }
1427 }
1428 }
1429 // TODO: we could break here if descrPenalty==0 for an exact
1430 // match of officeLocale, but the previous code didn't; are
1431 // there side effects?
1432 }
1433 continue;
1434 }
1435
1436 checkAborted( abortChannel );
1437
1438 //We make sure that we only create one XPackage for a particular URL.
1439 //Sometime programmers insert the same URL several times in the manifest
1440 //which may lead to DisposedExceptions.
1441 if (std::none_of(bundle.begin(), bundle.end(), XPackage_eq(url)))
1442 {
1443 const Reference<deployment::XPackage> xPackage(
1444 bindBundleItem( url, mediaType, false, OUString(), xCmdEnv ) );
1445 if (xPackage.is())
1446 bundle.push_back( xPackage );
1447 }
1448 else
1449 {
1450 SAL_WARN("desktop.deployment", "manifest.xml contains a duplicate entry (from " << url << ")");
1451 }
1452 }
1453
1454 if (descrFile.isEmpty())
1455 return;
1456
1457 ::ucbhelper::Content descrFileContent;
1458 if (!create_ucb_content( &descrFileContent, descrFile,
1459 xCmdEnv, false /* no throw */ ))
1460 return;
1461
1462 // patch description:
1463 std::vector<sal_Int8> bytes( readFile( descrFileContent ) );
1464 OUStringBuffer buf;
1465 if ( !bytes.empty() )
1466 {
1467 buf.append( OUString( reinterpret_cast<char const *>(
1468 bytes.data() ),
1469 bytes.size(), RTL_TEXTENCODING_UTF8 ) );
1470 }
1471 else
1472 {
1473 buf.append( Package::getDescription() );
1474 }
1475 m_oldDescription = buf.makeStringAndClear();
1476}
1477
1478
1479void BackendImpl::PackageImpl::scanLegacyBundle(
1480 t_packagevec & bundle,
1481 OUString const & url,
1482 ::rtl::Reference<AbortChannel> const & abortChannel,
1483 Reference<ucb::XCommandEnvironment> const & xCmdEnv,
1484 bool skip_registration )
1485{
1486 ::ucbhelper::Content ucbContent(
1487 url, xCmdEnv, getMyBackend()->getComponentContext() );
1488
1489 // check for platform paths:
1490 const OUString title( StrTitle::getTitle( ucbContent ) );
1491 if (title.endsWithIgnoreAsciiCase( ".plt" ) &&
1492 !platform_fits( title.subView( 0, title.getLength() - 4 ) )) {
1493 return;
1494 }
1495 if (title.endsWithIgnoreAsciiCase("skip_registration") )
1496 skip_registration = true;
1497
1498 Sequence<OUString> ar { OUString("Title"), OUString("IsFolder") };
1499 Reference<sdbc::XResultSet> xResultSet( ucbContent.createCursor( ar ) );
1500 while (xResultSet->next())
1501 {
1502 checkAborted( abortChannel );
1503
1504 const Reference<sdbc::XRow> xRow( xResultSet, UNO_QUERY_THROW );
1505 const OUString title_enc( ::rtl::Uri::encode(
1506 xRow->getString( 1 /* Title */ ),
1507 rtl_UriCharClassPchar,
1508 rtl_UriEncodeIgnoreEscapes,
1509 RTL_TEXTENCODING_UTF8 ) );
1510 const OUString path( makeURL( url, title_enc ) );
1511
1512 OUString mediaType;
1513 const Reference<deployment::XPackage> xPackage(
1514 bindBundleItem( path, OUString() /* detect */, false, OUString(),
1515 xCmdEnv, false /* ignore detection errors */ ) );
1516 if (xPackage.is()) {
1517 const Reference<deployment::XPackageTypeInfo> xPackageType(
1518 xPackage->getPackageType() );
1519 OSL_ASSERT( xPackageType.is() );
1520 if (xPackageType.is())
1521 mediaType = xPackageType->getMediaType();
1522
1523 if (skip_registration &&
1524 // xxx todo: additional parsing?
1525 mediaType.matchIgnoreAsciiCase("application/vnd.sun.star.uno-component"))
1526 continue;
1527
1528 bundle.push_back( xPackage );
1529 }
1530
1531 if (mediaType.isEmpty() ||
1532 // script.xlb, dialog.xlb can be met everywhere:
1533 mediaType.matchIgnoreAsciiCase("application/vnd.sun.star.basic-library") ||
1534 mediaType.matchIgnoreAsciiCase("application/vnd.sun.star.dialog-library"))
1535 {
1536 if (xRow->getBoolean( 2 /* IsFolder */ )) { // recurse into folder:
1537 scanLegacyBundle(
1538 bundle, path, abortChannel, xCmdEnv, skip_registration );
1539 }
1540 }
1541 }
1542}
1543
1544OUString BackendImpl::PackageImpl::getDisplayName()
1545{
1546 if (m_bRemoved)
1547 throw deployment::ExtensionRemovedException();
1548
1550 if (sName.isEmpty())
1551 return m_displayName;
1552 else
1553 return sName;
1554}
1555
1556std::vector<Reference<deployment::XPackage> >
1557BackendImpl::PackageImpl::getPackagesFromDb(
1558 Reference<ucb::XCommandEnvironment> const & xCmdEnv)
1559{
1560 std::vector<Reference<deployment::XPackage> > retVector;
1561
1562 for (auto const& item : m_dbData.items)
1563 {
1564 Reference<deployment::XPackage> xExtension =
1565 bindBundleItem(item.first, item.second, true, m_identifier, xCmdEnv);
1566 OSL_ASSERT(xExtension.is());
1567 if (xExtension.is())
1568 retVector.push_back(xExtension);
1569 }
1570
1571 return retVector;
1572}
1573
1574} // anon namespace
1575
1576
1577Reference<deployment::XPackageRegistry> create(
1578 Reference<deployment::XPackageRegistry> const & xRootRegistry,
1579 OUString const & context, OUString const & cachePath,
1580 Reference<XComponentContext> const & xComponentContext )
1581{
1582 Sequence<Any> args(cachePath.isEmpty() ? 1 : 3 );
1583 auto pArgs = args.getArray();
1584 pArgs[ 0 ] <<= context;
1585 if (!cachePath.isEmpty()) {
1586 pArgs[ 1 ] <<= cachePath;
1587 pArgs[ 2 ] <<= false; // readOnly
1588 }
1589 return new BackendImpl( args, xComponentContext, xRootRegistry );
1590}
1591
1592} // namespace dp_registry
1593
1594/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
double d
static bool parse(OUString const &rMediaType, OUString &rType, OUString &rSubType, INetContentTypeParameterList *pParameters=nullptr)
OUString getLanguage() const
::std::vector< OUString > getFallbackStrings(bool bIncludeFullBcp47) const
css::uno::Type const & get()
OUString getLocalizedDisplayName() const
returns the localized display name of the extensions.
css::uno::Sequence< OUString > getUpdateInformationUrls() const
Return the update information URLs.
std::pair< OUString, OUString > getLocalizedPublisherNameAndURL() const
Returns the localized publisher name and the corresponding URL.
OUString getVersion() const
Return the textual version representation.
OUString getIconURL(bool bHighContrast) const
Returns the URL for the icon image.
const OUString & getURL() const
void transferContent(const Content &rSourceContent, InsertOperation eOperation, const OUString &rTitle, const sal_Int32 nNameClashAction, const OUString &rMimeType=OUString(), bool bMajorVersion=false, const OUString &rCommentVersion=OUString(), OUString *pResultURL=nullptr, const OUString &rDocumentId=OUString()) const
css::uno::Any getPropertyValue(const OUString &rPropertyName)
css::uno::Reference< css::io::XInputStream > openStream()
css::uno::Reference< css::sdbc::XResultSet > createCursor(const css::uno::Sequence< OUString > &rPropertyNames, ResultSetInclude eMode=INCLUDE_FOLDERS_AND_DOCUMENTS)
OUString url_
#define TOOLS_WARN_EXCEPTION(area, stream)
OUString DpResId(TranslateId aId)
Definition: dp_misc.cxx:555
ExtensionBackendDb::Data m_dbData
Definition: dp_package.cxx:105
const Reference< deployment::XPackageTypeInfo > m_xBundleTypeInfo
Definition: dp_package.cxx:215
std::unique_ptr< ExtensionBackendDb > m_backendDb
Definition: dp_package.cxx:219
Sequence< Reference< deployment::XPackageTypeInfo > > m_typeInfos
Definition: dp_package.cxx:217
const bool m_legacyBundle
Definition: dp_package.cxx:101
const Reference< deployment::XPackageTypeInfo > m_xLegacyBundleTypeInfo
Definition: dp_package.cxx:216
Reference< deployment::XPackageRegistry > m_xRootRegistry
Definition: dp_package.cxx:214
OUString m_oldDescription
contains the old tooltip description for the Extension Manager GUI in OOo v.2.x We keep it for backwa...
Definition: dp_package.cxx:99
Sequence< Reference< deployment::XPackage > > * m_pBundle
Definition: dp_package.cxx:103
OUString m_url_expanded
Definition: dp_package.cxx:100
Sequence< Reference< deployment::XPackage > > m_bundle
Definition: dp_package.cxx:102
OUString m_URL
Definition: dp_package.cxx:255
OUString sName
const char * name
std::unordered_map< OString, INetContentTypeParameter > INetContentTypeParameterList
static uno::Reference< css::uno::XComponentContext > xContext
Definition: init.cxx:2642
void * p
#define SAL_WARN(area, stream)
css::uno::Sequence< DstElementType > containerToSequence(const SrcType &i_Container)
Reference< XComponentContext > getComponentContext(Reference< XMultiServiceFactory > const &factory)
css::beans::PropertyValue makePropertyValue(const OUString &rName, T &&rValue)
css::uno::Sequence< OUString > getSupportedServiceNames()
OUString getImplementationName()
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
Any SAL_CALL getCaughtException()
DESKTOP_DEPLOYMENTMISC_DLLPUBLIC css::uno::Sequence< css::uno::Reference< css::xml::dom::XElement > > check(dp_misc::DescriptionInfoset const &infoset)
Check for unsatisfied dependencies.
OUString makeURL(std::u16string_view baseURL, OUString const &relPath_)
appends a relative path to a url.
Definition: dp_misc.cxx:253
DESKTOP_DEPLOYMENTMISC_DLLPUBLIC bool create_folder(::ucbhelper::Content *ucb_content, OUString const &url, css::uno::Reference< css::ucb::XCommandEnvironment > const &xCmdEnv, bool throw_exc=true)
DESKTOP_DEPLOYMENTMISC_DLLPUBLIC bool interactContinuation(css::uno::Any const &request, css::uno::Type const &continuation, css::uno::Reference< css::ucb::XCommandEnvironment > const &xCmdEnv, bool *pcont, bool *pabort)
OUString expandUnoRcUrl(OUString const &url)
Definition: dp_misc.cxx:315
DESKTOP_DEPLOYMENTMISC_DLLPUBLIC const LanguageTag & getOfficeLanguageTag()
Definition: dp_resource.cxx:29
DESKTOP_DEPLOYMENTMISC_DLLPUBLIC OUString generateIdentifier(::std::optional< OUString > const &optional, std::u16string_view fileName)
Generates an identifier from an optional identifier.
DESKTOP_DEPLOYMENTMISC_DLLPUBLIC bool erase_path(OUString const &url, css::uno::Reference< css::ucb::XCommandEnvironment > const &xCmdEnv, bool throw_exc=true)
void try_dispose(css::uno::Reference< css::uno::XInterface > const &x)
Definition: dp_misc.h:38
DESKTOP_DEPLOYMENTMISC_DLLPUBLIC bool create_ucb_content(::ucbhelper::Content *ucb_content, OUString const &url, css::uno::Reference< css::ucb::XCommandEnvironment > const &xCmdEnv, bool throw_exc=true)
DESKTOP_DEPLOYMENTMISC_DLLPUBLIC std::vector< sal_Int8 > readFile(::ucbhelper::Content &ucb_content)
Definition: dp_ucb.cxx:187
DESKTOP_DEPLOYMENTMISC_DLLPUBLIC bool platform_fits(std::u16string_view platform_string)
DESKTOP_DEPLOYMENTMISC_DLLPUBLIC bool hasValidPlatform(css::uno::Sequence< OUString > const &platformStrings)
determines if the current platform corresponds to one of the platform strings.
DESKTOP_DEPLOYMENTMISC_DLLPUBLIC DescriptionInfoset getDescriptionInfoset(std::u16string_view sExtensionFolderURL)
creates a DescriptionInfoset object.
DESKTOP_DEPLOYMENTMISC_DLLPUBLIC OUString getIdentifier(css::uno::Reference< css::deployment::XPackage > const &package)
Gets the identifier of a package.
Reference< deployment::XPackageRegistry > create(Reference< deployment::XPackageRegistry > const &xRootRegistry, OUString const &context, OUString const &cachePath, Reference< XComponentContext > const &xComponentContext)
constexpr OUStringLiteral BACKEND_SERVICE_NAME
Definition: dp_backend.h:41
int i
def check(model)
bool matchIgnoreAsciiCase(std::u16string_view s1, std::u16string_view s2, sal_Int32 fromIndex=0)
args
std::vector< sal_uInt8 > bytes
::boost::spirit::classic::rule< ScannerT > identifier
JCOPY_OPTION option
unsigned char sal_Bool
ResultType type
size_t pos