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