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