LibreOffice Module desktop (master)  1
dp_registry.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 #include <sal/config.h>
21 #include <sal/log.hxx>
22 
23 #include <dp_shared.hxx>
24 #include <dp_package.hxx>
25 #include <strings.hrc>
26 #include <dp_registry.hxx>
27 #include <dp_misc.h>
28 #include <dp_ucb.h>
29 #include <osl/diagnose.h>
30 #include <rtl/ustrbuf.hxx>
31 #include <rtl/uri.hxx>
32 #include <cppuhelper/basemutex.hxx>
33 #include <cppuhelper/compbase.hxx>
34 #include <comphelper/sequence.hxx>
35 #include <ucbhelper/content.hxx>
36 #include <o3tl/string_view.hxx>
37 #include <com/sun/star/ucb/ContentCreationException.hpp>
38 #include <com/sun/star/uno/DeploymentException.hpp>
39 #include <com/sun/star/lang/DisposedException.hpp>
40 #include <com/sun/star/lang/IllegalArgumentException.hpp>
41 #include <com/sun/star/lang/XServiceInfo.hpp>
42 #include <com/sun/star/lang/XSingleComponentFactory.hpp>
43 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
44 #include <com/sun/star/util/XUpdatable.hpp>
45 #include <com/sun/star/container/XContentEnumerationAccess.hpp>
46 #include <com/sun/star/deployment/XPackageTypeInfo.hpp>
47 #include <com/sun/star/deployment/XPackageRegistry.hpp>
48 #include <set>
49 #include <string_view>
50 #include <unordered_map>
51 #include <unordered_set>
52 
53 using namespace ::dp_misc;
54 using namespace ::com::sun::star;
55 using namespace ::com::sun::star::uno;
56 using namespace ::com::sun::star::ucb;
57 
58 
59 namespace dp_registry {
60 
61 namespace {
62 
63 typedef ::cppu::WeakComponentImplHelper<
64  deployment::XPackageRegistry, util::XUpdatable > t_helper;
65 
66 
67 class PackageRegistryImpl : private cppu::BaseMutex, public t_helper
68 {
69  struct ci_string_hash {
70  std::size_t operator () ( OUString const & str ) const {
71  return str.toAsciiLowerCase().hashCode();
72  }
73  };
74  struct ci_string_equals {
75  bool operator () ( std::u16string_view str1, std::u16string_view str2 ) const{
76  return o3tl::equalsIgnoreAsciiCase( str1, str2 );
77  }
78  };
79  typedef std::unordered_map<
80  OUString, Reference<deployment::XPackageRegistry>,
81  ci_string_hash, ci_string_equals > t_string2registry;
82  typedef std::unordered_map<
83  OUString, OUString,
84  ci_string_hash, ci_string_equals > t_string2string;
85  typedef std::set<
86  Reference<deployment::XPackageRegistry> > t_registryset;
87 
88  t_string2registry m_mediaType2backend;
89  t_string2string m_filter2mediaType;
90  t_registryset m_ambiguousBackends;
91  t_registryset m_allBackends;
92  std::vector< Reference<deployment::XPackageTypeInfo> > m_typesInfos;
93 
94  void insertBackend(
95  Reference<deployment::XPackageRegistry> const & xBackend );
96 
97 protected:
98  void check();
99  virtual void SAL_CALL disposing() override;
100 
101  virtual ~PackageRegistryImpl() override;
102  PackageRegistryImpl() : t_helper( m_aMutex ) {}
103 
104 
105 public:
106  static Reference<deployment::XPackageRegistry> create(
107  OUString const & context,
108  OUString const & cachePath,
109  Reference<XComponentContext> const & xComponentContext );
110 
111  // XUpdatable
112  virtual void SAL_CALL update() override;
113 
114  // XPackageRegistry
115  virtual Reference<deployment::XPackage> SAL_CALL bindPackage(
116  OUString const & url, OUString const & mediaType, sal_Bool bRemoved,
117  OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv ) override;
118  virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
119  getSupportedPackageTypes() override;
120  virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
121 
122 };
123 
124 
125 void PackageRegistryImpl::check()
126 {
127  ::osl::MutexGuard guard( m_aMutex );
128  if (rBHelper.bInDispose || rBHelper.bDisposed) {
129  throw lang::DisposedException(
130  "PackageRegistry instance has already been disposed!",
131  static_cast<OWeakObject *>(this) );
132  }
133 }
134 
135 
136 void PackageRegistryImpl::disposing()
137 {
138  // dispose all backends:
139  for (auto const& backend : m_allBackends)
140  {
141  try_dispose(backend);
142  }
143  m_mediaType2backend = t_string2registry();
144  m_ambiguousBackends = t_registryset();
145  m_allBackends = t_registryset();
146 
147  t_helper::disposing();
148 }
149 
150 
151 PackageRegistryImpl::~PackageRegistryImpl()
152 {
153 }
154 
155 
156 OUString normalizeMediaType( std::u16string_view mediaType )
157 {
158  OUStringBuffer buf;
159  sal_Int32 index = 0;
160  for (;;) {
161  buf.append( o3tl::trim(o3tl::getToken(mediaType, 0, '/', index )) );
162  if (index < 0)
163  break;
164  buf.append( '/' );
165  }
166  return buf.makeStringAndClear();
167 }
168 
169 
170 void PackageRegistryImpl::packageRemoved(
171  OUString const & url, OUString const & mediaType)
172 {
173  const t_string2registry::const_iterator i =
174  m_mediaType2backend.find(mediaType);
175 
176  if (i != m_mediaType2backend.end())
177  {
178  i->second->packageRemoved(url, mediaType);
179  }
180 }
181 
182 void PackageRegistryImpl::insertBackend(
183  Reference<deployment::XPackageRegistry> const & xBackend )
184 {
185  m_allBackends.insert( xBackend );
186  std::unordered_set<OUString> ambiguousFilters;
187 
188  const Sequence< Reference<deployment::XPackageTypeInfo> > packageTypes(
189  xBackend->getSupportedPackageTypes() );
190  for ( Reference<deployment::XPackageTypeInfo> const & xPackageType : packageTypes )
191  {
192  m_typesInfos.push_back( xPackageType );
193 
194  const OUString mediaType( normalizeMediaType(
195  xPackageType->getMediaType() ) );
196  std::pair<t_string2registry::iterator, bool> a_insertion(
197  m_mediaType2backend.emplace( mediaType, xBackend ) );
198  if (a_insertion.second)
199  {
200  // add parameterless media-type, too:
201  sal_Int32 semi = mediaType.indexOf( ';' );
202  if (semi >= 0) {
203  m_mediaType2backend.emplace( mediaType.copy( 0, semi ), xBackend );
204  }
205  const OUString fileFilter( xPackageType->getFileFilter() );
206  //The package backend shall also be called to determine the mediatype
207  //(XPackageRegistry.bindPackage) when the URL points to a directory.
208  const bool bExtension = (mediaType == "application/vnd.sun.star.package-bundle");
209  if (fileFilter.isEmpty() || fileFilter == "*.*" || fileFilter == "*" || bExtension)
210  {
211  m_ambiguousBackends.insert( xBackend );
212  }
213  else
214  {
215  sal_Int32 nIndex = 0;
216  do {
217  OUString token( fileFilter.getToken( 0, ';', nIndex ) );
218  if (token.match( "*." ))
219  token = token.copy( 1 );
220  if (token.isEmpty())
221  continue;
222  // mark any further wildcards ambig:
223  bool ambig = (token.indexOf('*') >= 0 ||
224  token.indexOf('?') >= 0);
225  if (! ambig) {
226  std::pair<t_string2string::iterator, bool> ins(
227  m_filter2mediaType.emplace(
228  token, mediaType ) );
229  ambig = !ins.second;
230  if (ambig) {
231  // filter has already been in: add previously
232  // added backend to ambig set
233  const t_string2registry::const_iterator iFind(
234  m_mediaType2backend.find(
235  /* media-type of pr. added backend */
236  ins.first->second ) );
237  OSL_ASSERT(
238  iFind != m_mediaType2backend.end() );
239  if (iFind != m_mediaType2backend.end())
240  m_ambiguousBackends.insert( iFind->second );
241  }
242  }
243  if (ambig) {
244  m_ambiguousBackends.insert( xBackend );
245  // mark filter to be removed later from filters map:
246  ambiguousFilters.insert( token );
247  }
248  }
249  while (nIndex >= 0);
250  }
251  }
252 #if OSL_DEBUG_LEVEL > 0
253  else
254  {
255  SAL_WARN( "desktop", "more than one PackageRegistryBackend for media-type=\""
256  << mediaType
257  << "\" => "
258  << Reference<lang::XServiceInfo>(
259  xBackend, UNO_QUERY_THROW )->getImplementationName()
260  << "\"!" );
261  }
262 #endif
263  }
264 
265  // cut out ambiguous filters:
266  for (auto const& ambiguousFilter : ambiguousFilters)
267  {
268  m_filter2mediaType.erase(ambiguousFilter);
269  }
270 }
271 
272 
273 Reference<deployment::XPackageRegistry> PackageRegistryImpl::create(
274  OUString const & context,
275  OUString const & cachePath,
276  Reference<XComponentContext> const & xComponentContext )
277 {
278  rtl::Reference<PackageRegistryImpl> that = new PackageRegistryImpl;
279 
280  // auto-detect all registered package registries:
281  Reference<container::XEnumeration> xEnum(
282  Reference<container::XContentEnumerationAccess>(
283  xComponentContext->getServiceManager(),
284  UNO_QUERY_THROW )->createContentEnumeration(
285  "com.sun.star.deployment.PackageRegistryBackend" ) );
286  if (xEnum.is())
287  {
288  while (xEnum->hasMoreElements())
289  {
290  Any element( xEnum->nextElement() );
291  Sequence<Any> registryArgs(cachePath.isEmpty() ? 1 : 3 );
292  auto pregistryArgs = registryArgs.getArray();
293  pregistryArgs[ 0 ] <<= context;
294  if (!cachePath.isEmpty())
295  {
296  Reference<lang::XServiceInfo> xServiceInfo(
297  element, UNO_QUERY_THROW );
298  OUString registryCachePath(
299  makeURL( cachePath,
300  ::rtl::Uri::encode(
301  xServiceInfo->getImplementationName(),
302  rtl_UriCharClassPchar,
303  rtl_UriEncodeIgnoreEscapes,
304  RTL_TEXTENCODING_UTF8 ) ) );
305  pregistryArgs[ 1 ] <<= registryCachePath;
306  pregistryArgs[ 2 ] <<= false; // readOnly;
307  create_folder( nullptr, registryCachePath,
309  }
310 
311  Reference<deployment::XPackageRegistry> xBackend;
312  Reference<lang::XSingleComponentFactory> xFac( element, UNO_QUERY );
313  if (xFac.is()) {
314  xBackend.set(
315  xFac->createInstanceWithArgumentsAndContext(
316  registryArgs, xComponentContext ), UNO_QUERY );
317  }
318  else {
319  Reference<lang::XSingleServiceFactory> xSingleServiceFac(
320  element, UNO_QUERY_THROW );
321  xBackend.set(
322  xSingleServiceFac->createInstanceWithArguments(
323  registryArgs ), UNO_QUERY );
324  }
325  if (! xBackend.is()) {
326  throw DeploymentException(
327  "cannot instantiate PackageRegistryBackend service: "
328  + Reference<lang::XServiceInfo>(
329  element, UNO_QUERY_THROW )->getImplementationName(),
330  static_cast<OWeakObject *>(that.get()) );
331  }
332 
333  that->insertBackend( xBackend );
334  }
335  }
336 
337  // Insert bundle back-end.
338  // Always register as last, because we want to add extensions also as folders
339  // and as a default we accept every folder, which was not recognized by the other
340  // backends.
341  Reference<deployment::XPackageRegistry> extensionBackend =
343  that, context, cachePath, xComponentContext);
344  that->insertBackend(extensionBackend);
345 
346  Reference<lang::XServiceInfo> xServiceInfo(
347  extensionBackend, UNO_QUERY_THROW );
348 
349  OSL_ASSERT(xServiceInfo.is());
350  OUString registryCachePath(
351  makeURL( cachePath,
352  ::rtl::Uri::encode(
353  xServiceInfo->getImplementationName(),
354  rtl_UriCharClassPchar,
355  rtl_UriEncodeIgnoreEscapes,
356  RTL_TEXTENCODING_UTF8 ) ) );
357  create_folder( nullptr, registryCachePath, Reference<XCommandEnvironment>());
358 
359 
360 #if OSL_DEBUG_LEVEL > 0
361  // dump tables:
362  {
363  t_registryset allBackends;
364  dp_misc::TRACE("> [dp_registry.cxx] media-type detection:\n\n" );
365  for (auto const& elem : that->m_filter2mediaType)
366  {
367  const Reference<deployment::XPackageRegistry> xBackend(
368  that->m_mediaType2backend.find( elem.second )->second );
369  allBackends.insert( xBackend );
371  "extension \"" + elem.first + "\" maps to media-type \"" + elem.second
372  + "\" maps to backend "
373  + Reference<lang::XServiceInfo>(
374  xBackend, UNO_QUERY_THROW )
375  ->getImplementationName()
376  + "\n");
377  }
378  dp_misc::TRACE( "> [dp_registry.cxx] ambiguous backends:\n\n" );
379  for (auto const& ambiguousBackend : that->m_ambiguousBackends)
380  {
381  OUStringBuffer buf;
382  buf.append(
383  Reference<lang::XServiceInfo>(
384  ambiguousBackend, UNO_QUERY_THROW )->getImplementationName() );
385  buf.append( ": " );
386  const Sequence< Reference<deployment::XPackageTypeInfo> > types(
387  ambiguousBackend->getSupportedPackageTypes() );
388  for ( sal_Int32 pos = 0; pos < types.getLength(); ++pos ) {
389  Reference<deployment::XPackageTypeInfo> const & xInfo =
390  types[ pos ];
391  buf.append( xInfo->getMediaType() );
392  const OUString filter( xInfo->getFileFilter() );
393  if (!filter.isEmpty()) {
394  buf.append( " (" );
395  buf.append( filter );
396  buf.append( ")" );
397  }
398  if (pos < (types.getLength() - 1))
399  buf.append( ", " );
400  }
401  dp_misc::TRACE(buf.makeStringAndClear() + "\n\n");
402  }
403  allBackends.insert( that->m_ambiguousBackends.begin(),
404  that->m_ambiguousBackends.end() );
405  OSL_ASSERT( allBackends == that->m_allBackends );
406  }
407 #endif
408 
409  return that;
410 }
411 
412 // XUpdatable: broadcast to backends
413 
415 {
416  check();
417  for (auto const& backend : m_allBackends)
418  {
419  const Reference<util::XUpdatable> xUpdatable(backend, UNO_QUERY);
420  if (xUpdatable.is())
421  xUpdatable->update();
422  }
423 }
424 
425 // XPackageRegistry
426 
427 Reference<deployment::XPackage> PackageRegistryImpl::bindPackage(
428  OUString const & url, OUString const & mediaType_, sal_Bool bRemoved,
429  OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv )
430 {
431  check();
432  OUString mediaType(mediaType_);
433  if (mediaType.isEmpty())
434  {
435  ::ucbhelper::Content ucbContent;
436  bool bOk=true;
437 
438  try
439  {
440  bOk = create_ucb_content(
441  &ucbContent, url, xCmdEnv, false /* no throw */ )
442  && !ucbContent.isFolder();
443  }
444  catch (const css::ucb::ContentCreationException&)
445  {
446  bOk = false;
447  }
448 
449  if (bOk)
450  {
451  OUString title( StrTitle::getTitle( ucbContent ) );
452  for (;;)
453  {
454  const t_string2string::const_iterator iFind(
455  m_filter2mediaType.find(title) );
456  if (iFind != m_filter2mediaType.end()) {
457  mediaType = iFind->second;
458  break;
459  }
460  sal_Int32 point = title.indexOf( '.', 1 /* consume . */ );
461  if (point < 0)
462  break;
463  title = title.copy(point);
464  }
465  }
466  }
467  if (mediaType.isEmpty())
468  {
469  // try ambiguous backends:
470  for (auto const& ambiguousBackend : m_ambiguousBackends)
471  {
472  try {
473  return ambiguousBackend->bindPackage( url, mediaType, bRemoved,
474  identifier, xCmdEnv );
475  }
476  catch (const lang::IllegalArgumentException &) {
477  }
478  }
479  throw lang::IllegalArgumentException(
480  DpResId(RID_STR_CANNOT_DETECT_MEDIA_TYPE) + url,
481  static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
482  }
483  else
484  {
485  // get backend by media-type:
486  t_string2registry::const_iterator iFind(
487  m_mediaType2backend.find( normalizeMediaType(mediaType) ) );
488  if (iFind == m_mediaType2backend.end()) {
489  // xxx todo: more sophisticated media-type argument parsing...
490  sal_Int32 q = mediaType.indexOf( ';' );
491  if (q >= 0) {
492  iFind = m_mediaType2backend.find(
493  normalizeMediaType(
494  // cut parameters:
495  mediaType.subView( 0, q ) ) );
496  }
497  }
498  if (iFind == m_mediaType2backend.end()) {
499  throw lang::IllegalArgumentException(
500  DpResId(RID_STR_UNSUPPORTED_MEDIA_TYPE) + mediaType,
501  static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
502  }
503  return iFind->second->bindPackage( url, mediaType, bRemoved,
504  identifier, xCmdEnv );
505  }
506 }
507 
508 
509 Sequence< Reference<deployment::XPackageTypeInfo> >
510 PackageRegistryImpl::getSupportedPackageTypes()
511 {
513 }
514 } // anon namespace
515 
516 
517 Reference<deployment::XPackageRegistry> create(
518  OUString const & context,
519  OUString const & cachePath,
520  Reference<XComponentContext> const & xComponentContext )
521 {
523  context, cachePath, xComponentContext );
524 }
525 
526 } // namespace dp_registry
527 
528 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
sal_Int32 nIndex
t_registryset m_allBackends
Definition: dp_registry.cxx:91
bool update()
Definition: updater.cxx:286
OUString getImplementationName()
std::basic_string_view< charT, traits > getToken(std::basic_string_view< charT, traits > sv, charT delimiter, std::size_t &position)
def point()
OUString makeURL(OUString const &baseURL, OUString const &relPath_)
appends a relative path to a url.
Definition: dp_misc.cxx:250
size_t pos
std::mutex m_aMutex
def check(model)
int i
std::vector< Reference< deployment::XPackageTypeInfo > > m_typesInfos
Definition: dp_registry.cxx:92
::cppu::WeakImplHelper< css::script::provider::XScriptProvider, css::script::browse::XBrowseNode, css::lang::XServiceInfo, css::lang::XInitialization, css::container::XNameContainer > t_helper
unsigned char sal_Bool
t_string2string m_filter2mediaType
Definition: dp_registry.cxx:89
tuple index
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)
void TRACE(OUString const &sText)
print the text to the console in a debug build.
Definition: dp_misc.cxx:487
void try_dispose(css::uno::Reference< css::uno::XInterface > const &x)
Definition: dp_misc.h:38
bool equalsIgnoreAsciiCase(std::u16string_view s1, std::u16string_view s2)
css::uno::Sequence< DstElementType > containerToSequence(const SrcType &i_Container)
std::u16string_view trim(std::u16string_view str)
#define SAL_WARN(area, stream)
t_string2registry m_mediaType2backend
Definition: dp_registry.cxx:88
OUString DpResId(TranslateId aId)
Definition: dp_misc.cxx:554
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)
t_registryset m_ambiguousBackends
Definition: dp_registry.cxx:90
Reference< deployment::XPackageRegistry > create(OUString const &context, OUString const &cachePath, Reference< XComponentContext > const &xComponentContext)
css::uno::Reference< css::deployment::XPackageRegistry > create(OUString const &context, OUString const &cachePath, css::uno::Reference< css::uno::XComponentContext > const &xComponentContext)