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