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/compbase.hxx>
33 #include <comphelper/sequence.hxx>
34 #include <ucbhelper/content.hxx>
35 #include <com/sun/star/ucb/ContentCreationException.hpp>
36 #include <com/sun/star/uno/DeploymentException.hpp>
37 #include <com/sun/star/lang/DisposedException.hpp>
38 #include <com/sun/star/lang/IllegalArgumentException.hpp>
39 #include <com/sun/star/lang/XServiceInfo.hpp>
40 #include <com/sun/star/lang/XSingleComponentFactory.hpp>
41 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
42 #include <com/sun/star/util/XUpdatable.hpp>
43 #include <com/sun/star/container/XContentEnumerationAccess.hpp>
44 #include <com/sun/star/deployment/XPackageTypeInfo.hpp>
45 #include <com/sun/star/deployment/XPackageRegistry.hpp>
46 #include <set>
47 #include <unordered_map>
48 #include <unordered_set>
49 
50 using namespace ::dp_misc;
51 using namespace ::com::sun::star;
52 using namespace ::com::sun::star::uno;
53 using namespace ::com::sun::star::ucb;
54 
55 
56 namespace dp_registry {
57 
58 namespace {
59 
60 typedef ::cppu::WeakComponentImplHelper<
61  deployment::XPackageRegistry, util::XUpdatable > t_helper;
62 
63 
64 class PackageRegistryImpl : private MutexHolder, public t_helper
65 {
66  struct ci_string_hash {
67  std::size_t operator () ( OUString const & str ) const {
68  return str.toAsciiLowerCase().hashCode();
69  }
70  };
71  struct ci_string_equals {
72  bool operator () ( OUString const & str1, OUString const & str2 ) const{
73  return str1.equalsIgnoreAsciiCase( str2 );
74  }
75  };
76  typedef std::unordered_map<
77  OUString, Reference<deployment::XPackageRegistry>,
78  ci_string_hash, ci_string_equals > t_string2registry;
79  typedef std::unordered_map<
80  OUString, OUString,
81  ci_string_hash, ci_string_equals > t_string2string;
82  typedef std::set<
83  Reference<deployment::XPackageRegistry> > t_registryset;
84 
85  t_string2registry m_mediaType2backend;
86  t_string2string m_filter2mediaType;
87  t_registryset m_ambiguousBackends;
88  t_registryset m_allBackends;
89  std::vector< Reference<deployment::XPackageTypeInfo> > m_typesInfos;
90 
91  void insertBackend(
92  Reference<deployment::XPackageRegistry> const & xBackend );
93 
94 protected:
95  void check();
96  virtual void SAL_CALL disposing() override;
97 
98  virtual ~PackageRegistryImpl() override;
99  PackageRegistryImpl() : t_helper( getMutex() ) {}
100 
101 
102 public:
103  static Reference<deployment::XPackageRegistry> create(
104  OUString const & context,
105  OUString const & cachePath,
106  Reference<XComponentContext> const & xComponentContext );
107 
108  // XUpdatable
109  virtual void SAL_CALL update() override;
110 
111  // XPackageRegistry
112  virtual Reference<deployment::XPackage> SAL_CALL bindPackage(
113  OUString const & url, OUString const & mediaType, sal_Bool bRemoved,
114  OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv ) override;
115  virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
116  getSupportedPackageTypes() override;
117  virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
118 
119 };
120 
121 
122 void PackageRegistryImpl::check()
123 {
124  ::osl::MutexGuard guard( getMutex() );
125  if (rBHelper.bInDispose || rBHelper.bDisposed) {
126  throw lang::DisposedException(
127  "PackageRegistry instance has already been disposed!",
128  static_cast<OWeakObject *>(this) );
129  }
130 }
131 
132 
133 void PackageRegistryImpl::disposing()
134 {
135  // dispose all backends:
136  for (auto const& backend : m_allBackends)
137  {
138  try_dispose(backend);
139  }
140  m_mediaType2backend = t_string2registry();
141  m_ambiguousBackends = t_registryset();
142  m_allBackends = t_registryset();
143 
144  t_helper::disposing();
145 }
146 
147 
148 PackageRegistryImpl::~PackageRegistryImpl()
149 {
150 }
151 
152 
153 OUString normalizeMediaType( OUString const & mediaType )
154 {
155  OUStringBuffer buf;
156  sal_Int32 index = 0;
157  for (;;) {
158  buf.append( mediaType.getToken( 0, '/', index ).trim() );
159  if (index < 0)
160  break;
161  buf.append( '/' );
162  }
163  return buf.makeStringAndClear();
164 }
165 
166 
167 void PackageRegistryImpl::packageRemoved(
168  OUString const & url, OUString const & mediaType)
169 {
170  const t_string2registry::const_iterator i =
171  m_mediaType2backend.find(mediaType);
172 
173  if (i != m_mediaType2backend.end())
174  {
175  i->second->packageRemoved(url, mediaType);
176  }
177 }
178 
179 void PackageRegistryImpl::insertBackend(
180  Reference<deployment::XPackageRegistry> const & xBackend )
181 {
182  m_allBackends.insert( xBackend );
183  std::unordered_set<OUString> ambiguousFilters;
184 
185  const Sequence< Reference<deployment::XPackageTypeInfo> > packageTypes(
186  xBackend->getSupportedPackageTypes() );
187  for ( Reference<deployment::XPackageTypeInfo> const & xPackageType : packageTypes )
188  {
189  m_typesInfos.push_back( xPackageType );
190 
191  const OUString mediaType( normalizeMediaType(
192  xPackageType->getMediaType() ) );
193  std::pair<t_string2registry::iterator, bool> a_insertion(
194  m_mediaType2backend.emplace( mediaType, xBackend ) );
195  if (a_insertion.second)
196  {
197  // add parameterless media-type, too:
198  sal_Int32 semi = mediaType.indexOf( ';' );
199  if (semi >= 0) {
200  m_mediaType2backend.emplace( mediaType.copy( 0, semi ), xBackend );
201  }
202  const OUString fileFilter( xPackageType->getFileFilter() );
203  //The package backend shall also be called to determine the mediatype
204  //(XPackageRegistry.bindPackage) when the URL points to a directory.
205  const bool bExtension = (mediaType == "application/vnd.sun.star.package-bundle");
206  if (fileFilter.isEmpty() || fileFilter == "*.*" || fileFilter == "*" || bExtension)
207  {
208  m_ambiguousBackends.insert( xBackend );
209  }
210  else
211  {
212  sal_Int32 nIndex = 0;
213  do {
214  OUString token( fileFilter.getToken( 0, ';', nIndex ) );
215  if (token.match( "*." ))
216  token = token.copy( 1 );
217  if (token.isEmpty())
218  continue;
219  // mark any further wildcards ambig:
220  bool ambig = (token.indexOf('*') >= 0 ||
221  token.indexOf('?') >= 0);
222  if (! ambig) {
223  std::pair<t_string2string::iterator, bool> ins(
224  m_filter2mediaType.emplace(
225  token, mediaType ) );
226  ambig = !ins.second;
227  if (ambig) {
228  // filter has already been in: add previously
229  // added backend to ambig set
230  const t_string2registry::const_iterator iFind(
231  m_mediaType2backend.find(
232  /* media-type of pr. added backend */
233  ins.first->second ) );
234  OSL_ASSERT(
235  iFind != m_mediaType2backend.end() );
236  if (iFind != m_mediaType2backend.end())
237  m_ambiguousBackends.insert( iFind->second );
238  }
239  }
240  if (ambig) {
241  m_ambiguousBackends.insert( xBackend );
242  // mark filter to be removed later from filters map:
243  ambiguousFilters.insert( token );
244  }
245  }
246  while (nIndex >= 0);
247  }
248  }
249 #if OSL_DEBUG_LEVEL > 0
250  else
251  {
252  SAL_WARN( "desktop", "more than one PackageRegistryBackend for media-type=\""
253  << mediaType
254  << "\" => "
255  << Reference<lang::XServiceInfo>(
256  xBackend, UNO_QUERY_THROW )->getImplementationName()
257  << "\"!" );
258  }
259 #endif
260  }
261 
262  // cut out ambiguous filters:
263  for (auto const& ambiguousFilter : ambiguousFilters)
264  {
265  m_filter2mediaType.erase(ambiguousFilter);
266  }
267 }
268 
269 
270 Reference<deployment::XPackageRegistry> PackageRegistryImpl::create(
271  OUString const & context,
272  OUString const & cachePath,
273  Reference<XComponentContext> const & xComponentContext )
274 {
275  PackageRegistryImpl * that = new PackageRegistryImpl;
276  Reference<deployment::XPackageRegistry> xRet(that);
277 
278  // auto-detect all registered package registries:
279  Reference<container::XEnumeration> xEnum(
280  Reference<container::XContentEnumerationAccess>(
281  xComponentContext->getServiceManager(),
282  UNO_QUERY_THROW )->createContentEnumeration(
283  "com.sun.star.deployment.PackageRegistryBackend" ) );
284  if (xEnum.is())
285  {
286  while (xEnum->hasMoreElements())
287  {
288  Any element( xEnum->nextElement() );
289  Sequence<Any> registryArgs(cachePath.isEmpty() ? 1 : 3 );
290  registryArgs[ 0 ] <<= context;
291  if (!cachePath.isEmpty())
292  {
293  Reference<lang::XServiceInfo> xServiceInfo(
294  element, UNO_QUERY_THROW );
295  OUString registryCachePath(
296  makeURL( cachePath,
297  ::rtl::Uri::encode(
298  xServiceInfo->getImplementationName(),
299  rtl_UriCharClassPchar,
300  rtl_UriEncodeIgnoreEscapes,
301  RTL_TEXTENCODING_UTF8 ) ) );
302  registryArgs[ 1 ] <<= registryCachePath;
303  registryArgs[ 2 ] <<= false; // readOnly;
304  create_folder( nullptr, registryCachePath,
306  }
307 
308  Reference<deployment::XPackageRegistry> xBackend;
309  Reference<lang::XSingleComponentFactory> xFac( element, UNO_QUERY );
310  if (xFac.is()) {
311  xBackend.set(
312  xFac->createInstanceWithArgumentsAndContext(
313  registryArgs, xComponentContext ), UNO_QUERY );
314  }
315  else {
316  Reference<lang::XSingleServiceFactory> xSingleServiceFac(
317  element, UNO_QUERY_THROW );
318  xBackend.set(
319  xSingleServiceFac->createInstanceWithArguments(
320  registryArgs ), UNO_QUERY );
321  }
322  if (! xBackend.is()) {
323  throw DeploymentException(
324  "cannot instantiate PackageRegistryBackend service: "
325  + Reference<lang::XServiceInfo>(
326  element, UNO_QUERY_THROW )->getImplementationName(),
327  static_cast<OWeakObject *>(that) );
328  }
329 
330  that->insertBackend( xBackend );
331  }
332  }
333 
334  // Insert bundle back-end.
335  // Always register as last, because we want to add extensions also as folders
336  // and as a default we accept every folder, which was not recognized by the other
337  // backends.
338  Reference<deployment::XPackageRegistry> extensionBackend =
340  that, context, cachePath, xComponentContext);
341  that->insertBackend(extensionBackend);
342 
343  Reference<lang::XServiceInfo> xServiceInfo(
344  extensionBackend, UNO_QUERY_THROW );
345 
346  OSL_ASSERT(xServiceInfo.is());
347  OUString registryCachePath(
348  makeURL( cachePath,
349  ::rtl::Uri::encode(
350  xServiceInfo->getImplementationName(),
351  rtl_UriCharClassPchar,
352  rtl_UriEncodeIgnoreEscapes,
353  RTL_TEXTENCODING_UTF8 ) ) );
354  create_folder( nullptr, registryCachePath, Reference<XCommandEnvironment>());
355 
356 
357 #if OSL_DEBUG_LEVEL > 0
358  // dump tables:
359  {
360  t_registryset allBackends;
361  dp_misc::TRACE("> [dp_registry.cxx] media-type detection:\n\n" );
362  for (auto const& elem : that->m_filter2mediaType)
363  {
364  OUStringBuffer buf;
365  buf.append( "extension \"" );
366  buf.append( elem.first );
367  buf.append( "\" maps to media-type \"" );
368  buf.append( elem.second );
369  buf.append( "\" maps to backend " );
370  const Reference<deployment::XPackageRegistry> xBackend(
371  that->m_mediaType2backend.find( elem.second )->second );
372  allBackends.insert( xBackend );
373  buf.append( Reference<lang::XServiceInfo>(
374  xBackend, UNO_QUERY_THROW )
375  ->getImplementationName() );
376  dp_misc::TRACE( buf.makeStringAndClear() + "\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 xRet;
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.copy( 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:88
bool update()
Definition: updater.cxx:284
OUString getImplementationName()
def point()
OUString makeURL(OUString const &baseURL, OUString const &relPath_)
appends a relative path to a url.
Definition: dp_misc.cxx:251
size_t pos
def check(model)
int i
std::vector< Reference< deployment::XPackageTypeInfo > > m_typesInfos
Definition: dp_registry.cxx:89
::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:86
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:488
void try_dispose(css::uno::Reference< css::uno::XInterface > const &x)
Definition: dp_misc.h:43
css::uno::Sequence< DstElementType > containerToSequence(const SrcType &i_Container)
OUString DpResId(const char *pId)
Definition: dp_shared.hxx:36
#define SAL_WARN(area, stream)
t_string2registry m_mediaType2backend
Definition: dp_registry.cxx:85
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:87
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)
::osl::Mutex & getMutex()