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>
21 #include <sal/log.hxx>
23 #include <dp_shared.hxx>
24 #include <dp_package.hxx>
25 #include <strings.hrc>
26 #include <dp_registry.hxx>
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>
49 #include <string_view>
50 #include <unordered_map>
51 #include <unordered_set>
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
;
59 namespace dp_registry
{
63 typedef ::cppu::WeakComponentImplHelper
<
64 deployment::XPackageRegistry
, util::XUpdatable
> t_helper
;
67 class PackageRegistryImpl
: private cppu::BaseMutex
, public t_helper
69 struct ci_string_hash
{
70 std::size_t operator () ( OUString
const & str
) const {
71 return str
.toAsciiLowerCase().hashCode();
74 struct ci_string_equals
{
75 bool operator () ( std::u16string_view str1
, std::u16string_view str2
) const{
76 return o3tl::equalsIgnoreAsciiCase( str1
, str2
);
79 typedef std::unordered_map
<
80 OUString
, Reference
<deployment::XPackageRegistry
>,
81 ci_string_hash
, ci_string_equals
> t_string2registry
;
82 typedef std::unordered_map
<
84 ci_string_hash
, ci_string_equals
> t_string2string
;
86 Reference
<deployment::XPackageRegistry
> > t_registryset
;
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
;
95 Reference
<deployment::XPackageRegistry
> const & xBackend
);
99 virtual void SAL_CALL
disposing() override
;
101 virtual ~PackageRegistryImpl() override
;
102 PackageRegistryImpl() : t_helper( m_aMutex
) {}
106 static Reference
<deployment::XPackageRegistry
> create(
107 OUString
const & context
,
108 OUString
const & cachePath
,
109 Reference
<XComponentContext
> const & xComponentContext
);
112 virtual void SAL_CALL
update() override
;
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
;
125 void PackageRegistryImpl::check()
127 ::osl::MutexGuard
guard( m_aMutex
);
128 if (rBHelper
.bInDispose
|| rBHelper
.bDisposed
) {
129 throw lang::DisposedException(
130 u
"PackageRegistry instance has already been disposed!"_ustr
,
131 static_cast<OWeakObject
*>(this) );
136 void PackageRegistryImpl::disposing()
138 // dispose all backends:
139 for (auto const& backend
: m_allBackends
)
141 try_dispose(backend
);
143 m_mediaType2backend
= t_string2registry();
144 m_ambiguousBackends
= t_registryset();
145 m_allBackends
= t_registryset();
147 t_helper::disposing();
151 PackageRegistryImpl::~PackageRegistryImpl()
156 OUString
normalizeMediaType( std::u16string_view mediaType
)
161 buf
.append( o3tl::trim(o3tl::getToken(mediaType
, 0, '/', index
)) );
166 return buf
.makeStringAndClear();
170 void PackageRegistryImpl::packageRemoved(
171 OUString
const & url
, OUString
const & mediaType
)
173 const t_string2registry::const_iterator i
=
174 m_mediaType2backend
.find(mediaType
);
176 if (i
!= m_mediaType2backend
.end())
178 i
->second
->packageRemoved(url
, mediaType
);
182 void PackageRegistryImpl::insertBackend(
183 Reference
<deployment::XPackageRegistry
> const & xBackend
)
185 m_allBackends
.insert( xBackend
);
186 std::unordered_set
<OUString
> ambiguousFilters
;
188 const Sequence
< Reference
<deployment::XPackageTypeInfo
> > packageTypes(
189 xBackend
->getSupportedPackageTypes() );
190 for ( Reference
<deployment::XPackageTypeInfo
> const & xPackageType
: packageTypes
)
192 m_typesInfos
.push_back( xPackageType
);
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
)
200 // add parameterless media-type, too:
201 sal_Int32 semi
= mediaType
.indexOf( ';' );
203 m_mediaType2backend
.emplace( mediaType
.copy( 0, semi
), xBackend
);
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
)
211 m_ambiguousBackends
.insert( xBackend
);
215 sal_Int32 nIndex
= 0;
217 OUString
token( fileFilter
.getToken( 0, ';', nIndex
) );
218 if (token
.match( "*." ))
219 token
= token
.copy( 1 );
222 // mark any further wildcards ambig:
223 bool ambig
= (token
.indexOf('*') >= 0 ||
224 token
.indexOf('?') >= 0);
226 std::pair
<t_string2string::iterator
, bool> ins(
227 m_filter2mediaType
.emplace(
228 token
, mediaType
) );
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
) );
238 iFind
!= m_mediaType2backend
.end() );
239 if (iFind
!= m_mediaType2backend
.end())
240 m_ambiguousBackends
.insert( iFind
->second
);
244 m_ambiguousBackends
.insert( xBackend
);
245 // mark filter to be removed later from filters map:
246 ambiguousFilters
.insert( token
);
252 #if OSL_DEBUG_LEVEL > 0
255 SAL_WARN( "desktop", "more than one PackageRegistryBackend for media-type=\""
258 << Reference
<lang::XServiceInfo
>(
259 xBackend
, UNO_QUERY_THROW
)->getImplementationName()
265 // cut out ambiguous filters:
266 for (auto const& ambiguousFilter
: ambiguousFilters
)
268 m_filter2mediaType
.erase(ambiguousFilter
);
273 Reference
<deployment::XPackageRegistry
> PackageRegistryImpl::create(
274 OUString
const & context
,
275 OUString
const & cachePath
,
276 Reference
<XComponentContext
> const & xComponentContext
)
278 rtl::Reference
<PackageRegistryImpl
> that
= new PackageRegistryImpl
;
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 u
"com.sun.star.deployment.PackageRegistryBackend"_ustr
) );
288 while (xEnum
->hasMoreElements())
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())
296 Reference
<lang::XServiceInfo
> xServiceInfo(
297 element
, UNO_QUERY_THROW
);
298 OUString
registryCachePath(
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
,
308 Reference
<XCommandEnvironment
>() );
311 Reference
<deployment::XPackageRegistry
> xBackend
;
312 Reference
<lang::XSingleComponentFactory
> xFac( element
, UNO_QUERY
);
315 xFac
->createInstanceWithArgumentsAndContext(
316 registryArgs
, xComponentContext
), UNO_QUERY
);
319 Reference
<lang::XSingleServiceFactory
> xSingleServiceFac(
320 element
, UNO_QUERY_THROW
);
322 xSingleServiceFac
->createInstanceWithArguments(
323 registryArgs
), UNO_QUERY
);
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()) );
333 that
->insertBackend( xBackend
);
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
341 Reference
<deployment::XPackageRegistry
> extensionBackend
=
342 ::dp_registry::backend::bundle::create(
343 that
, context
, cachePath
, xComponentContext
);
344 that
->insertBackend(extensionBackend
);
346 Reference
<lang::XServiceInfo
> xServiceInfo(
347 extensionBackend
, UNO_QUERY_THROW
);
349 OSL_ASSERT(xServiceInfo
.is());
350 OUString
registryCachePath(
353 xServiceInfo
->getImplementationName(),
354 rtl_UriCharClassPchar
,
355 rtl_UriEncodeIgnoreEscapes
,
356 RTL_TEXTENCODING_UTF8
) ) );
357 create_folder( nullptr, registryCachePath
, Reference
<XCommandEnvironment
>());
360 #if OSL_DEBUG_LEVEL > 0
363 t_registryset allBackends
;
364 dp_misc::TRACE(u
"> [dp_registry.cxx] media-type detection:\n\n"_ustr
);
365 for (auto const& elem
: that
->m_filter2mediaType
)
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()
378 dp_misc::TRACE( u
"> [dp_registry.cxx] ambiguous backends:\n\n"_ustr
);
379 for (auto const& ambiguousBackend
: that
->m_ambiguousBackends
)
383 Reference
<lang::XServiceInfo
>(
384 ambiguousBackend
, UNO_QUERY_THROW
)->getImplementationName()
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
=
391 buf
.append( xInfo
->getMediaType() );
392 const OUString
filter( xInfo
->getFileFilter() );
393 if (!filter
.isEmpty()) {
394 buf
.append( " (" + filter
+ ")" );
396 if (pos
< (types
.getLength() - 1))
399 dp_misc::TRACE(buf
+ "\n\n");
401 allBackends
.insert( that
->m_ambiguousBackends
.begin(),
402 that
->m_ambiguousBackends
.end() );
403 OSL_ASSERT( allBackends
== that
->m_allBackends
);
410 // XUpdatable: broadcast to backends
412 void PackageRegistryImpl::update()
415 for (auto const& backend
: m_allBackends
)
417 const Reference
<util::XUpdatable
> xUpdatable(backend
, UNO_QUERY
);
419 xUpdatable
->update();
425 Reference
<deployment::XPackage
> PackageRegistryImpl::bindPackage(
426 OUString
const & url
, OUString
const & mediaType_
, sal_Bool bRemoved
,
427 OUString
const & identifier
, Reference
<XCommandEnvironment
> const & xCmdEnv
)
430 OUString
mediaType(mediaType_
);
431 if (mediaType
.isEmpty())
433 ::ucbhelper::Content ucbContent
;
438 bOk
= create_ucb_content(
439 &ucbContent
, url
, xCmdEnv
, false /* no throw */ )
440 && !ucbContent
.isFolder();
442 catch (const css::ucb::ContentCreationException
&)
449 OUString
title( StrTitle::getTitle( ucbContent
) );
452 const t_string2string::const_iterator
iFind(
453 m_filter2mediaType
.find(title
) );
454 if (iFind
!= m_filter2mediaType
.end()) {
455 mediaType
= iFind
->second
;
458 sal_Int32 point
= title
.indexOf( '.', 1 /* consume . */ );
461 title
= title
.copy(point
);
465 if (mediaType
.isEmpty())
467 // try ambiguous backends:
468 for (auto const& ambiguousBackend
: m_ambiguousBackends
)
471 return ambiguousBackend
->bindPackage( url
, mediaType
, bRemoved
,
472 identifier
, xCmdEnv
);
474 catch (const lang::IllegalArgumentException
&) {
477 throw lang::IllegalArgumentException(
478 DpResId(RID_STR_CANNOT_DETECT_MEDIA_TYPE
) + url
,
479 static_cast<OWeakObject
*>(this), static_cast<sal_Int16
>(-1) );
483 // get backend by media-type:
484 t_string2registry::const_iterator
iFind(
485 m_mediaType2backend
.find( normalizeMediaType(mediaType
) ) );
486 if (iFind
== m_mediaType2backend
.end()) {
487 // xxx todo: more sophisticated media-type argument parsing...
488 sal_Int32 q
= mediaType
.indexOf( ';' );
490 iFind
= m_mediaType2backend
.find(
493 mediaType
.subView( 0, q
) ) );
496 if (iFind
== m_mediaType2backend
.end()) {
497 throw lang::IllegalArgumentException(
498 DpResId(RID_STR_UNSUPPORTED_MEDIA_TYPE
) + mediaType
,
499 static_cast<OWeakObject
*>(this), static_cast<sal_Int16
>(-1) );
501 return iFind
->second
->bindPackage( url
, mediaType
, bRemoved
,
502 identifier
, xCmdEnv
);
507 Sequence
< Reference
<deployment::XPackageTypeInfo
> >
508 PackageRegistryImpl::getSupportedPackageTypes()
510 return comphelper::containerToSequence(m_typesInfos
);
515 Reference
<deployment::XPackageRegistry
> create(
516 OUString
const & context
,
517 OUString
const & cachePath
,
518 Reference
<XComponentContext
> const & xComponentContext
)
520 return PackageRegistryImpl::create(
521 context
, cachePath
, xComponentContext
);
524 } // namespace dp_registry
526 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */