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_resource.h>
29 #include <dp_interact.h>
30 #include <dp_ucb.h>
31 #include <osl/diagnose.h>
32 #include <rtl/ustrbuf.hxx>
33 #include <rtl/uri.hxx>
34 #include <cppuhelper/compbase.hxx>
35 #include <cppuhelper/exc_hlp.hxx>
36 #include <comphelper/sequence.hxx>
37 #include <ucbhelper/content.hxx>
38 #include <com/sun/star/ucb/ContentCreationException.hpp>
39 #include <com/sun/star/uno/DeploymentException.hpp>
40 #include <com/sun/star/lang/DisposedException.hpp>
41 #include <com/sun/star/lang/IllegalArgumentException.hpp>
42 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
43 #include <com/sun/star/lang/XServiceInfo.hpp>
44 #include <com/sun/star/lang/XSingleComponentFactory.hpp>
45 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
46 #include <com/sun/star/util/XUpdatable.hpp>
47 #include <com/sun/star/container/XContentEnumerationAccess.hpp>
48 #include <com/sun/star/deployment/PackageRegistryBackend.hpp>
49 #include <set>
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 MutexHolder, 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 () ( OUString const & str1, OUString const & str2 ) const{
76  return str1.equalsIgnoreAsciiCase( 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( getMutex() ) {}
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( getMutex() );
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( OUString const & mediaType )
157 {
158  OUStringBuffer buf;
159  sal_Int32 index = 0;
160  for (;;) {
161  buf.append( mediaType.getToken( 0, '/', index ).trim() );
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 ( sal_Int32 pos = 0; pos < packageTypes.getLength(); ++pos )
191  {
192  Reference<deployment::XPackageTypeInfo> const & xPackageType =
193  packageTypes[ pos ];
194  m_typesInfos.push_back( xPackageType );
195 
196  const OUString mediaType( normalizeMediaType(
197  xPackageType->getMediaType() ) );
198  std::pair<t_string2registry::iterator, bool> a_insertion(
199  m_mediaType2backend.emplace( mediaType, xBackend ) );
200  if (a_insertion.second)
201  {
202  // add parameterless media-type, too:
203  sal_Int32 semi = mediaType.indexOf( ';' );
204  if (semi >= 0) {
205  m_mediaType2backend.emplace( mediaType.copy( 0, semi ), xBackend );
206  }
207  const OUString fileFilter( xPackageType->getFileFilter() );
208  //The package backend shall also be called to determine the mediatype
209  //(XPackageRegistry.bindPackage) when the URL points to a directory.
210  const bool bExtension = (mediaType == "application/vnd.sun.star.package-bundle");
211  if (fileFilter.isEmpty() || fileFilter == "*.*" || fileFilter == "*" || bExtension)
212  {
213  m_ambiguousBackends.insert( xBackend );
214  }
215  else
216  {
217  sal_Int32 nIndex = 0;
218  do {
219  OUString token( fileFilter.getToken( 0, ';', nIndex ) );
220  if (token.match( "*." ))
221  token = token.copy( 1 );
222  if (token.isEmpty())
223  continue;
224  // mark any further wildcards ambig:
225  bool ambig = (token.indexOf('*') >= 0 ||
226  token.indexOf('?') >= 0);
227  if (! ambig) {
228  std::pair<t_string2string::iterator, bool> ins(
229  m_filter2mediaType.emplace(
230  token, mediaType ) );
231  ambig = !ins.second;
232  if (ambig) {
233  // filter has already been in: add previously
234  // added backend to ambig set
235  const t_string2registry::const_iterator iFind(
236  m_mediaType2backend.find(
237  /* media-type of pr. added backend */
238  ins.first->second ) );
239  OSL_ASSERT(
240  iFind != m_mediaType2backend.end() );
241  if (iFind != m_mediaType2backend.end())
242  m_ambiguousBackends.insert( iFind->second );
243  }
244  }
245  if (ambig) {
246  m_ambiguousBackends.insert( xBackend );
247  // mark filter to be removed later from filters map:
248  ambiguousFilters.insert( token );
249  }
250  }
251  while (nIndex >= 0);
252  }
253  }
254 #if OSL_DEBUG_LEVEL > 0
255  else
256  {
257  SAL_WARN( "desktop", "more than one PackageRegistryBackend for media-type=\""
258  << mediaType
259  << "\" => "
260  << Reference<lang::XServiceInfo>(
261  xBackend, UNO_QUERY_THROW )->getImplementationName()
262  << "\"!" );
263  }
264 #endif
265  }
266 
267  // cut out ambiguous filters:
268  for (auto const& ambiguousFilter : ambiguousFilters)
269  {
270  m_filter2mediaType.erase(ambiguousFilter);
271  }
272 }
273 
274 
275 Reference<deployment::XPackageRegistry> PackageRegistryImpl::create(
276  OUString const & context,
277  OUString const & cachePath,
278  Reference<XComponentContext> const & xComponentContext )
279 {
280  PackageRegistryImpl * that = new PackageRegistryImpl;
281  Reference<deployment::XPackageRegistry> xRet(that);
282 
283  // auto-detect all registered package registries:
285  Reference<container::XContentEnumerationAccess>(
286  xComponentContext->getServiceManager(),
287  UNO_QUERY_THROW )->createContentEnumeration(
288  "com.sun.star.deployment.PackageRegistryBackend" ) );
289  if (xEnum.is())
290  {
291  while (xEnum->hasMoreElements())
292  {
293  Any element( xEnum->nextElement() );
294  Sequence<Any> registryArgs(cachePath.isEmpty() ? 1 : 3 );
295  registryArgs[ 0 ] <<= context;
296  if (!cachePath.isEmpty())
297  {
298  Reference<lang::XServiceInfo> xServiceInfo(
299  element, UNO_QUERY_THROW );
300  OUString registryCachePath(
301  makeURL( cachePath,
302  ::rtl::Uri::encode(
303  xServiceInfo->getImplementationName(),
304  rtl_UriCharClassPchar,
305  rtl_UriEncodeIgnoreEscapes,
306  RTL_TEXTENCODING_UTF8 ) ) );
307  registryArgs[ 1 ] <<= registryCachePath;
308  registryArgs[ 2 ] <<= false; // readOnly;
309  create_folder( nullptr, registryCachePath,
311  }
312 
313  Reference<deployment::XPackageRegistry> xBackend;
314  Reference<lang::XSingleComponentFactory> xFac( element, UNO_QUERY );
315  if (xFac.is()) {
316  xBackend.set(
317  xFac->createInstanceWithArgumentsAndContext(
318  registryArgs, xComponentContext ), UNO_QUERY );
319  }
320  else {
321  Reference<lang::XSingleServiceFactory> xSingleServiceFac(
322  element, UNO_QUERY_THROW );
323  xBackend.set(
324  xSingleServiceFac->createInstanceWithArguments(
325  registryArgs ), UNO_QUERY );
326  }
327  if (! xBackend.is()) {
328  throw DeploymentException(
329  "cannot instantiate PackageRegistryBackend service: "
330  + Reference<lang::XServiceInfo>(
331  element, UNO_QUERY_THROW )->getImplementationName(),
332  static_cast<OWeakObject *>(that) );
333  }
334 
335  that->insertBackend( xBackend );
336  }
337  }
338 
339  // Insert bundle back-end.
340  // Always register as last, because we want to add extensions also as folders
341  // and as a default we accept every folder, which was not recognized by the other
342  // backends.
343  Reference<deployment::XPackageRegistry> extensionBackend =
345  that, context, cachePath, xComponentContext);
346  that->insertBackend(extensionBackend);
347 
348  Reference<lang::XServiceInfo> xServiceInfo(
349  extensionBackend, UNO_QUERY_THROW );
350 
351  OSL_ASSERT(xServiceInfo.is());
352  OUString registryCachePath(
353  makeURL( cachePath,
354  ::rtl::Uri::encode(
355  xServiceInfo->getImplementationName(),
356  rtl_UriCharClassPchar,
357  rtl_UriEncodeIgnoreEscapes,
358  RTL_TEXTENCODING_UTF8 ) ) );
359  create_folder( nullptr, registryCachePath, Reference<XCommandEnvironment>());
360 
361 
362 #if OSL_DEBUG_LEVEL > 0
363  // dump tables:
364  {
365  t_registryset allBackends;
366  dp_misc::TRACE("> [dp_registry.cxx] media-type detection:\n\n" );
367  for (auto const& elem : that->m_filter2mediaType)
368  {
369  OUStringBuffer buf;
370  buf.append( "extension \"" );
371  buf.append( elem.first );
372  buf.append( "\" maps to media-type \"" );
373  buf.append( elem.second );
374  buf.append( "\" maps to backend " );
375  const Reference<deployment::XPackageRegistry> xBackend(
376  that->m_mediaType2backend.find( elem.second )->second );
377  allBackends.insert( xBackend );
378  buf.append( Reference<lang::XServiceInfo>(
379  xBackend, UNO_QUERY_THROW )
380  ->getImplementationName() );
381  dp_misc::TRACE( buf.makeStringAndClear() + "\n");
382  }
383  dp_misc::TRACE( "> [dp_registry.cxx] ambiguous backends:\n\n" );
384  for (auto const& ambiguousBackend : that->m_ambiguousBackends)
385  {
386  OUStringBuffer buf;
387  buf.append(
388  Reference<lang::XServiceInfo>(
389  ambiguousBackend, UNO_QUERY_THROW )->getImplementationName() );
390  buf.append( ": " );
391  const Sequence< Reference<deployment::XPackageTypeInfo> > types(
392  ambiguousBackend->getSupportedPackageTypes() );
393  for ( sal_Int32 pos = 0; pos < types.getLength(); ++pos ) {
394  Reference<deployment::XPackageTypeInfo> const & xInfo =
395  types[ pos ];
396  buf.append( xInfo->getMediaType() );
397  const OUString filter( xInfo->getFileFilter() );
398  if (!filter.isEmpty()) {
399  buf.append( " (" );
400  buf.append( filter );
401  buf.append( ")" );
402  }
403  if (pos < (types.getLength() - 1))
404  buf.append( ", " );
405  }
406  dp_misc::TRACE(buf.makeStringAndClear() + "\n\n");
407  }
408  allBackends.insert( that->m_ambiguousBackends.begin(),
409  that->m_ambiguousBackends.end() );
410  OSL_ASSERT( allBackends == that->m_allBackends );
411  }
412 #endif
413 
414  return xRet;
415 }
416 
417 // XUpdatable: broadcast to backends
418 
420 {
421  check();
422  for (auto const& backend : m_allBackends)
423  {
424  const Reference<util::XUpdatable> xUpdatable(backend, UNO_QUERY);
425  if (xUpdatable.is())
426  xUpdatable->update();
427  }
428 }
429 
430 // XPackageRegistry
431 
432 Reference<deployment::XPackage> PackageRegistryImpl::bindPackage(
433  OUString const & url, OUString const & mediaType_, sal_Bool bRemoved,
434  OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv )
435 {
436  check();
437  OUString mediaType(mediaType_);
438  if (mediaType.isEmpty())
439  {
440  ::ucbhelper::Content ucbContent;
441  bool bOk=true;
442 
443  try
444  {
445  bOk = create_ucb_content(
446  &ucbContent, url, xCmdEnv, false /* no throw */ )
447  && !ucbContent.isFolder();
448  }
449  catch (const css::ucb::ContentCreationException&)
450  {
451  bOk = false;
452  }
453 
454  if (bOk)
455  {
456  OUString title( StrTitle::getTitle( ucbContent ) );
457  for (;;)
458  {
459  const t_string2string::const_iterator iFind(
460  m_filter2mediaType.find(title) );
461  if (iFind != m_filter2mediaType.end()) {
462  mediaType = iFind->second;
463  break;
464  }
465  sal_Int32 point = title.indexOf( '.', 1 /* consume . */ );
466  if (point < 0)
467  break;
468  title = title.copy(point);
469  }
470  }
471  }
472  if (mediaType.isEmpty())
473  {
474  // try ambiguous backends:
475  for (auto const& ambiguousBackend : m_ambiguousBackends)
476  {
477  try {
478  return ambiguousBackend->bindPackage( url, mediaType, bRemoved,
479  identifier, xCmdEnv );
480  }
481  catch (const lang::IllegalArgumentException &) {
482  }
483  }
484  throw lang::IllegalArgumentException(
485  DpResId(RID_STR_CANNOT_DETECT_MEDIA_TYPE) + url,
486  static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
487  }
488  else
489  {
490  // get backend by media-type:
491  t_string2registry::const_iterator iFind(
492  m_mediaType2backend.find( normalizeMediaType(mediaType) ) );
493  if (iFind == m_mediaType2backend.end()) {
494  // xxx todo: more sophisticated media-type argument parsing...
495  sal_Int32 q = mediaType.indexOf( ';' );
496  if (q >= 0) {
497  iFind = m_mediaType2backend.find(
498  normalizeMediaType(
499  // cut parameters:
500  mediaType.copy( 0, q ) ) );
501  }
502  }
503  if (iFind == m_mediaType2backend.end()) {
504  throw lang::IllegalArgumentException(
505  DpResId(RID_STR_UNSUPPORTED_MEDIA_TYPE) + mediaType,
506  static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
507  }
508  return iFind->second->bindPackage( url, mediaType, bRemoved,
509  identifier, xCmdEnv );
510  }
511 }
512 
513 
514 Sequence< Reference<deployment::XPackageTypeInfo> >
515 PackageRegistryImpl::getSupportedPackageTypes()
516 {
518 }
519 } // anon namespace
520 
521 
522 Reference<deployment::XPackageRegistry> create(
523  OUString const & context,
524  OUString const & cachePath,
525  Reference<XComponentContext> const & xComponentContext )
526 {
528  context, cachePath, xComponentContext );
529 }
530 
531 } // namespace dp_registry
532 
533 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
t_registryset m_allBackends
Definition: dp_registry.cxx:91
bool update()
Definition: updater.cxx:284
OUString getImplementationName()
Definition: splash.cxx:640
def point()
OUString makeURL(OUString const &baseURL, OUString const &relPath_)
appends a relative path to a url.
Definition: dp_misc.cxx:266
def check(model)
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
int i
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:527
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)
OUString DpResId(const char *pId)
Definition: dp_shared.hxx:38
#define SAL_WARN(area, stream)
t_string2registry m_mediaType2backend
Definition: dp_registry.cxx:88
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)
::osl::Mutex & getMutex()