1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
27 #include <dp_resource.h>
28 #include <dp_interact.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>
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
{
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
<
82 ci_string_hash
, ci_string_equals
> t_string2string
;
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
;
93 Reference
<deployment::XPackageRegistry
> const & xBackend
);
97 virtual void SAL_CALL
disposing() override
;
99 virtual ~PackageRegistryImpl() override
;
100 PackageRegistryImpl() : t_helper( getMutex() ) {}
104 static Reference
<deployment::XPackageRegistry
> create(
105 OUString
const & context
,
106 OUString
const & cachePath
, bool readOnly
,
107 Reference
<XComponentContext
> const & xComponentContext
);
110 virtual void SAL_CALL
update() override
;
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
)
160 buf
.append( mediaType
.getToken( 0, '/', index
).trim() );
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
=
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( ';' );
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
);
217 sal_Int32 nIndex
= 0;
219 OUString
token( fileFilter
.getToken( 0, ';', nIndex
) );
220 if (token
.match( "*." ))
221 token
= token
.copy( 1 );
224 // mark any further wildcards ambig:
225 bool ambig
= (token
.indexOf('*') >= 0 ||
226 token
.indexOf('?') >= 0);
228 std::pair
<t_string2string::iterator
, bool> ins(
229 m_filter2mediaType
.emplace(
230 token
, mediaType
) );
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
) );
240 iFind
!= m_mediaType2backend
.end() );
241 if (iFind
!= m_mediaType2backend
.end())
242 m_ambiguousBackends
.insert( iFind
->second
);
246 m_ambiguousBackends
.insert( xBackend
);
247 // mark filter to be removed later from filters map:
248 ambiguousFilters
.insert( token
);
254 #if OSL_DEBUG_LEVEL > 0
257 SAL_WARN( "desktop", "more than one PackageRegistryBackend for media-type=\""
260 << Reference
<lang::XServiceInfo
>(
261 xBackend
, UNO_QUERY_THROW
)->getImplementationName()
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" ) );
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(
304 xServiceInfo
->getImplementationName(),
305 rtl_UriCharClassPchar
,
306 rtl_UriEncodeIgnoreEscapes
,
307 RTL_TEXTENCODING_UTF8
) ) );
308 registryArgs
[ 1 ] <<= registryCachePath
;
309 registryArgs
[ 2 ] <<= readOnly
;
311 create_folder( nullptr, registryCachePath
,
312 Reference
<XCommandEnvironment
>() );
315 Reference
<deployment::XPackageRegistry
> xBackend
;
316 Reference
<lang::XSingleComponentFactory
> xFac( element
, UNO_QUERY
);
319 xFac
->createInstanceWithArgumentsAndContext(
320 registryArgs
, xComponentContext
), UNO_QUERY
);
323 Reference
<lang::XSingleServiceFactory
> xSingleServiceFac(
324 element
, UNO_QUERY_THROW
);
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
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(
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
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
)
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
)
394 Reference
<lang::XServiceInfo
>(
395 *iPos
, UNO_QUERY_THROW
)->getImplementationName() );
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
=
402 buf
.append( xInfo
->getMediaType() );
403 const OUString
filter( xInfo
->getFileFilter() );
404 if (!filter
.isEmpty()) {
406 buf
.append( filter
);
409 if (pos
< (types
.getLength() - 1))
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
);
423 // XUpdatable: broadcast to backends
425 void PackageRegistryImpl::update()
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
);
433 xUpdatable
->update();
439 Reference
<deployment::XPackage
> PackageRegistryImpl::bindPackage(
440 OUString
const & url
, OUString
const & mediaType_
, sal_Bool bRemoved
,
441 OUString
const & identifier
, Reference
<XCommandEnvironment
> const & xCmdEnv
)
444 OUString
mediaType(mediaType_
);
445 if (mediaType
.isEmpty())
447 ::ucbhelper::Content ucbContent
;
452 bOk
= create_ucb_content(
453 &ucbContent
, url
, xCmdEnv
, false /* no throw */ )
454 && !ucbContent
.isFolder();
456 catch (const css::ucb::ContentCreationException
&)
463 OUString
title( StrTitle::getTitle( ucbContent
) );
466 const t_string2string::const_iterator
iFind(
467 m_filter2mediaType
.find(title
) );
468 if (iFind
!= m_filter2mediaType
.end()) {
469 mediaType
= iFind
->second
;
472 sal_Int32 point
= title
.indexOf( '.', 1 /* consume . */ );
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
)
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) );
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( ';' );
506 iFind
= m_mediaType2backend
.find(
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
);
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: */