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