Bump version to 6.0-36
[LibreOffice.git] / desktop / source / deployment / registry / dp_registry.cxx
bloba14b0fe6bbf2ec1b5819d76612601486459ece71
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
22 #include <dp_shared.hxx>
23 #include <dp_package.hxx>
24 #include <strings.hrc>
25 #include <dp_registry.hxx>
26 #include <dp_misc.h>
27 #include <dp_resource.h>
28 #include <dp_interact.h>
29 #include <dp_ucb.h>
30 #include <osl/diagnose.h>
31 #include <rtl/ustrbuf.hxx>
32 #include <rtl/uri.hxx>
33 #include <cppuhelper/compbase.hxx>
34 #include <cppuhelper/exc_hlp.hxx>
35 #include <comphelper/sequence.hxx>
36 #include <ucbhelper/content.hxx>
37 #include <com/sun/star/uno/DeploymentException.hpp>
38 #include <com/sun/star/lang/DisposedException.hpp>
39 #include <com/sun/star/lang/IllegalArgumentException.hpp>
40 #include <com/sun/star/lang/WrappedTargetRuntimeException.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/PackageRegistryBackend.hpp>
47 #include <set>
48 #include <unordered_map>
49 #include <unordered_set>
51 using namespace ::dp_misc;
52 using namespace ::com::sun::star;
53 using namespace ::com::sun::star::uno;
54 using namespace ::com::sun::star::ucb;
57 namespace dp_registry {
59 namespace {
61 typedef ::cppu::WeakComponentImplHelper<
62 deployment::XPackageRegistry, util::XUpdatable > t_helper;
65 class PackageRegistryImpl : private MutexHolder, public t_helper
67 struct ci_string_hash {
68 std::size_t operator () ( OUString const & str ) const {
69 return str.toAsciiLowerCase().hashCode();
72 struct ci_string_equals {
73 bool operator () ( OUString const & str1, OUString const & str2 ) const{
74 return str1.equalsIgnoreAsciiCase( str2 );
77 typedef std::unordered_map<
78 OUString, Reference<deployment::XPackageRegistry>,
79 ci_string_hash, ci_string_equals > t_string2registry;
80 typedef std::unordered_map<
81 OUString, OUString,
82 ci_string_hash, ci_string_equals > t_string2string;
83 typedef std::set<
84 Reference<deployment::XPackageRegistry> > t_registryset;
86 t_string2registry m_mediaType2backend;
87 t_string2string m_filter2mediaType;
88 t_registryset m_ambiguousBackends;
89 t_registryset m_allBackends;
90 std::vector< Reference<deployment::XPackageTypeInfo> > m_typesInfos;
92 void insertBackend(
93 Reference<deployment::XPackageRegistry> const & xBackend );
95 protected:
96 inline void check();
97 virtual void SAL_CALL disposing() override;
99 virtual ~PackageRegistryImpl() override;
100 PackageRegistryImpl() : t_helper( getMutex() ) {}
103 public:
104 static Reference<deployment::XPackageRegistry> create(
105 OUString const & context,
106 OUString const & cachePath, bool readOnly,
107 Reference<XComponentContext> const & xComponentContext );
109 // XUpdatable
110 virtual void SAL_CALL update() override;
112 // XPackageRegistry
113 virtual Reference<deployment::XPackage> SAL_CALL bindPackage(
114 OUString const & url, OUString const & mediaType, sal_Bool bRemoved,
115 OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv ) override;
116 virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
117 getSupportedPackageTypes() override;
118 virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
123 inline void PackageRegistryImpl::check()
125 ::osl::MutexGuard guard( getMutex() );
126 if (rBHelper.bInDispose || rBHelper.bDisposed) {
127 throw lang::DisposedException(
128 "PackageRegistry instance has already been disposed!",
129 static_cast<OWeakObject *>(this) );
134 void PackageRegistryImpl::disposing()
136 // dispose all backends:
137 t_registryset::const_iterator iPos( m_allBackends.begin() );
138 t_registryset::const_iterator const iEnd( m_allBackends.end() );
139 for ( ; iPos != iEnd; ++iPos ) {
140 try_dispose( *iPos );
142 m_mediaType2backend = t_string2registry();
143 m_ambiguousBackends = t_registryset();
144 m_allBackends = t_registryset();
146 t_helper::disposing();
150 PackageRegistryImpl::~PackageRegistryImpl()
155 OUString normalizeMediaType( OUString const & mediaType )
157 OUStringBuffer buf;
158 sal_Int32 index = 0;
159 for (;;) {
160 buf.append( mediaType.getToken( 0, '/', index ).trim() );
161 if (index < 0)
162 break;
163 buf.append( '/' );
165 return buf.makeStringAndClear();
169 void PackageRegistryImpl::packageRemoved(
170 OUString const & url, OUString const & mediaType)
172 const t_string2registry::const_iterator i =
173 m_mediaType2backend.find(mediaType);
175 if (i != m_mediaType2backend.end())
177 i->second->packageRemoved(url, mediaType);
181 void PackageRegistryImpl::insertBackend(
182 Reference<deployment::XPackageRegistry> const & xBackend )
184 m_allBackends.insert( xBackend );
185 typedef std::unordered_set<OUString> t_stringset;
186 t_stringset ambiguousFilters;
188 const Sequence< Reference<deployment::XPackageTypeInfo> > packageTypes(
189 xBackend->getSupportedPackageTypes() );
190 for ( sal_Int32 pos = 0; pos < packageTypes.getLength(); ++pos )
192 Reference<deployment::XPackageTypeInfo> const & xPackageType =
193 packageTypes[ pos ];
194 m_typesInfos.push_back( xPackageType );
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)
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 );
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)
213 m_ambiguousBackends.insert( xBackend );
215 else
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 );
245 if (ambig) {
246 m_ambiguousBackends.insert( xBackend );
247 // mark filter to be removed later from filters map:
248 ambiguousFilters.insert( token );
251 while (nIndex >= 0);
254 #if OSL_DEBUG_LEVEL > 0
255 else
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 << "\"!" );
264 #endif
267 // cut out ambiguous filters:
268 t_stringset::const_iterator iPos( ambiguousFilters.begin() );
269 const t_stringset::const_iterator iEnd( ambiguousFilters.end() );
270 for ( ; iPos != iEnd; ++iPos ) {
271 m_filter2mediaType.erase( *iPos );
276 Reference<deployment::XPackageRegistry> PackageRegistryImpl::create(
277 OUString const & context,
278 OUString const & cachePath, bool readOnly,
279 Reference<XComponentContext> const & xComponentContext )
281 PackageRegistryImpl * that = new PackageRegistryImpl;
282 Reference<deployment::XPackageRegistry> xRet(that);
284 // auto-detect all registered package registries:
285 Reference<container::XEnumeration> xEnum(
286 Reference<container::XContentEnumerationAccess>(
287 xComponentContext->getServiceManager(),
288 UNO_QUERY_THROW )->createContentEnumeration(
289 "com.sun.star.deployment.PackageRegistryBackend" ) );
290 if (xEnum.is())
292 while (xEnum->hasMoreElements())
294 Any element( xEnum->nextElement() );
295 Sequence<Any> registryArgs(cachePath.isEmpty() ? 1 : 3 );
296 registryArgs[ 0 ] <<= context;
297 if (!cachePath.isEmpty())
299 Reference<lang::XServiceInfo> xServiceInfo(
300 element, UNO_QUERY_THROW );
301 OUString registryCachePath(
302 makeURL( cachePath,
303 ::rtl::Uri::encode(
304 xServiceInfo->getImplementationName(),
305 rtl_UriCharClassPchar,
306 rtl_UriEncodeIgnoreEscapes,
307 RTL_TEXTENCODING_UTF8 ) ) );
308 registryArgs[ 1 ] <<= registryCachePath;
309 registryArgs[ 2 ] <<= readOnly;
310 if (! readOnly)
311 create_folder( nullptr, registryCachePath,
312 Reference<XCommandEnvironment>() );
315 Reference<deployment::XPackageRegistry> xBackend;
316 Reference<lang::XSingleComponentFactory> xFac( element, UNO_QUERY );
317 if (xFac.is()) {
318 xBackend.set(
319 xFac->createInstanceWithArgumentsAndContext(
320 registryArgs, xComponentContext ), UNO_QUERY );
322 else {
323 Reference<lang::XSingleServiceFactory> xSingleServiceFac(
324 element, UNO_QUERY_THROW );
325 xBackend.set(
326 xSingleServiceFac->createInstanceWithArguments(
327 registryArgs ), UNO_QUERY );
329 if (! xBackend.is()) {
330 throw DeploymentException(
331 "cannot instantiate PackageRegistryBackend service: "
332 + Reference<lang::XServiceInfo>(
333 element, UNO_QUERY_THROW )->getImplementationName(),
334 static_cast<OWeakObject *>(that) );
337 that->insertBackend( xBackend );
341 // Insert bundle back-end.
342 // Always register as last, because we want to add extensions also as folders
343 // and as a default we accept every folder, which was not recognized by the other
344 // backends.
345 Reference<deployment::XPackageRegistry> extensionBackend =
346 ::dp_registry::backend::bundle::create(
347 that, context, cachePath, readOnly, xComponentContext);
348 that->insertBackend(extensionBackend);
350 Reference<lang::XServiceInfo> xServiceInfo(
351 extensionBackend, UNO_QUERY_THROW );
353 OSL_ASSERT(xServiceInfo.is());
354 OUString registryCachePath(
355 makeURL( cachePath,
356 ::rtl::Uri::encode(
357 xServiceInfo->getImplementationName(),
358 rtl_UriCharClassPchar,
359 rtl_UriEncodeIgnoreEscapes,
360 RTL_TEXTENCODING_UTF8 ) ) );
361 create_folder( nullptr, registryCachePath, Reference<XCommandEnvironment>());
364 #if OSL_DEBUG_LEVEL > 0
365 // dump tables:
367 t_registryset allBackends;
368 dp_misc::TRACE("> [dp_registry.cxx] media-type detection:\n\n" );
369 for ( t_string2string::const_iterator iPos(
370 that->m_filter2mediaType.begin() );
371 iPos != that->m_filter2mediaType.end(); ++iPos )
373 OUStringBuffer buf;
374 buf.append( "extension \"" );
375 buf.append( iPos->first );
376 buf.append( "\" maps to media-type \"" );
377 buf.append( iPos->second );
378 buf.append( "\" maps to backend " );
379 const Reference<deployment::XPackageRegistry> xBackend(
380 that->m_mediaType2backend.find( iPos->second )->second );
381 allBackends.insert( xBackend );
382 buf.append( Reference<lang::XServiceInfo>(
383 xBackend, UNO_QUERY_THROW )
384 ->getImplementationName() );
385 dp_misc::TRACE( buf.makeStringAndClear() + "\n");
387 dp_misc::TRACE( "> [dp_registry.cxx] ambiguous backends:\n\n" );
388 for ( t_registryset::const_iterator iPos(
389 that->m_ambiguousBackends.begin() );
390 iPos != that->m_ambiguousBackends.end(); ++iPos )
392 OUStringBuffer buf;
393 buf.append(
394 Reference<lang::XServiceInfo>(
395 *iPos, UNO_QUERY_THROW )->getImplementationName() );
396 buf.append( ": " );
397 const Sequence< Reference<deployment::XPackageTypeInfo> > types(
398 (*iPos)->getSupportedPackageTypes() );
399 for ( sal_Int32 pos = 0; pos < types.getLength(); ++pos ) {
400 Reference<deployment::XPackageTypeInfo> const & xInfo =
401 types[ pos ];
402 buf.append( xInfo->getMediaType() );
403 const OUString filter( xInfo->getFileFilter() );
404 if (!filter.isEmpty()) {
405 buf.append( " (" );
406 buf.append( filter );
407 buf.append( ")" );
409 if (pos < (types.getLength() - 1))
410 buf.append( ", " );
412 dp_misc::TRACE(buf.makeStringAndClear() + "\n\n");
414 allBackends.insert( that->m_ambiguousBackends.begin(),
415 that->m_ambiguousBackends.end() );
416 OSL_ASSERT( allBackends == that->m_allBackends );
418 #endif
420 return xRet;
423 // XUpdatable: broadcast to backends
425 void PackageRegistryImpl::update()
427 check();
428 t_registryset::const_iterator iPos( m_allBackends.begin() );
429 const t_registryset::const_iterator iEnd( m_allBackends.end() );
430 for ( ; iPos != iEnd; ++iPos ) {
431 const Reference<util::XUpdatable> xUpdatable( *iPos, UNO_QUERY );
432 if (xUpdatable.is())
433 xUpdatable->update();
437 // XPackageRegistry
439 Reference<deployment::XPackage> PackageRegistryImpl::bindPackage(
440 OUString const & url, OUString const & mediaType_, sal_Bool bRemoved,
441 OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv )
443 check();
444 OUString mediaType(mediaType_);
445 if (mediaType.isEmpty())
447 ::ucbhelper::Content ucbContent;
448 bool bOk=true;
452 bOk = create_ucb_content(
453 &ucbContent, url, xCmdEnv, false /* no throw */ )
454 && !ucbContent.isFolder();
456 catch (const css::ucb::ContentCreationException&)
458 bOk = false;
461 if (bOk)
463 OUString title( StrTitle::getTitle( ucbContent ) );
464 for (;;)
466 const t_string2string::const_iterator iFind(
467 m_filter2mediaType.find(title) );
468 if (iFind != m_filter2mediaType.end()) {
469 mediaType = iFind->second;
470 break;
472 sal_Int32 point = title.indexOf( '.', 1 /* consume . */ );
473 if (point < 0)
474 break;
475 title = title.copy(point);
479 if (mediaType.isEmpty())
481 // try ambiguous backends:
482 t_registryset::const_iterator iPos( m_ambiguousBackends.begin() );
483 const t_registryset::const_iterator iEnd( m_ambiguousBackends.end() );
484 for ( ; iPos != iEnd; ++iPos )
486 try {
487 return (*iPos)->bindPackage( url, mediaType, bRemoved,
488 identifier, xCmdEnv );
490 catch (const lang::IllegalArgumentException &) {
493 throw lang::IllegalArgumentException(
494 DpResId(RID_STR_CANNOT_DETECT_MEDIA_TYPE) + url,
495 static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
497 else
499 // get backend by media-type:
500 t_string2registry::const_iterator iFind(
501 m_mediaType2backend.find( normalizeMediaType(mediaType) ) );
502 if (iFind == m_mediaType2backend.end()) {
503 // xxx todo: more sophisticated media-type argument parsing...
504 sal_Int32 q = mediaType.indexOf( ';' );
505 if (q >= 0) {
506 iFind = m_mediaType2backend.find(
507 normalizeMediaType(
508 // cut parameters:
509 mediaType.copy( 0, q ) ) );
512 if (iFind == m_mediaType2backend.end()) {
513 throw lang::IllegalArgumentException(
514 DpResId(RID_STR_UNSUPPORTED_MEDIA_TYPE) + mediaType,
515 static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
517 return iFind->second->bindPackage( url, mediaType, bRemoved,
518 identifier, xCmdEnv );
523 Sequence< Reference<deployment::XPackageTypeInfo> >
524 PackageRegistryImpl::getSupportedPackageTypes()
526 return comphelper::containerToSequence(m_typesInfos);
528 } // anon namespace
531 Reference<deployment::XPackageRegistry> SAL_CALL create(
532 OUString const & context,
533 OUString const & cachePath,
534 Reference<XComponentContext> const & xComponentContext )
536 return PackageRegistryImpl::create(
537 context, cachePath, false/*readOnly*/, xComponentContext );
540 } // namespace dp_registry
542 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */