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>
28 #include <dp_resource.h>
29 #include <dp_interact.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>
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 MutexHolder
, 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 () ( OUString
const & str1
, OUString
const & str2
) const{
76 return str1
.equalsIgnoreAsciiCase( 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( getMutex() ) {}
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( getMutex() );
128 if (rBHelper
.bInDispose
|| rBHelper
.bDisposed
) {
129 throw lang::DisposedException(
130 "PackageRegistry instance has already been disposed!",
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( OUString
const & mediaType
)
161 buf
.append( mediaType
.getToken( 0, '/', index
).trim() );
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 ( 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 for (auto const& ambiguousFilter
: ambiguousFilters
)
270 m_filter2mediaType
.erase(ambiguousFilter
);
275 Reference
<deployment::XPackageRegistry
> PackageRegistryImpl::create(
276 OUString
const & context
,
277 OUString
const & cachePath
,
278 Reference
<XComponentContext
> const & xComponentContext
)
280 PackageRegistryImpl
* that
= new PackageRegistryImpl
;
281 Reference
<deployment::XPackageRegistry
> xRet(that
);
283 // auto-detect all registered package registries:
284 Reference
<container::XEnumeration
> xEnum(
285 Reference
<container::XContentEnumerationAccess
>(
286 xComponentContext
->getServiceManager(),
287 UNO_QUERY_THROW
)->createContentEnumeration(
288 "com.sun.star.deployment.PackageRegistryBackend" ) );
291 while (xEnum
->hasMoreElements())
293 Any
element( xEnum
->nextElement() );
294 Sequence
<Any
> registryArgs(cachePath
.isEmpty() ? 1 : 3 );
295 registryArgs
[ 0 ] <<= context
;
296 if (!cachePath
.isEmpty())
298 Reference
<lang::XServiceInfo
> xServiceInfo(
299 element
, UNO_QUERY_THROW
);
300 OUString
registryCachePath(
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
,
310 Reference
<XCommandEnvironment
>() );
313 Reference
<deployment::XPackageRegistry
> xBackend
;
314 Reference
<lang::XSingleComponentFactory
> xFac( element
, UNO_QUERY
);
317 xFac
->createInstanceWithArgumentsAndContext(
318 registryArgs
, xComponentContext
), UNO_QUERY
);
321 Reference
<lang::XSingleServiceFactory
> xSingleServiceFac(
322 element
, UNO_QUERY_THROW
);
324 xSingleServiceFac
->createInstanceWithArguments(
325 registryArgs
), UNO_QUERY
);
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
) );
335 that
->insertBackend( xBackend
);
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
343 Reference
<deployment::XPackageRegistry
> extensionBackend
=
344 ::dp_registry::backend::bundle::create(
345 that
, context
, cachePath
, xComponentContext
);
346 that
->insertBackend(extensionBackend
);
348 Reference
<lang::XServiceInfo
> xServiceInfo(
349 extensionBackend
, UNO_QUERY_THROW
);
351 OSL_ASSERT(xServiceInfo
.is());
352 OUString
registryCachePath(
355 xServiceInfo
->getImplementationName(),
356 rtl_UriCharClassPchar
,
357 rtl_UriEncodeIgnoreEscapes
,
358 RTL_TEXTENCODING_UTF8
) ) );
359 create_folder( nullptr, registryCachePath
, Reference
<XCommandEnvironment
>());
362 #if OSL_DEBUG_LEVEL > 0
365 t_registryset allBackends
;
366 dp_misc::TRACE("> [dp_registry.cxx] media-type detection:\n\n" );
367 for (auto const& elem
: that
->m_filter2mediaType
)
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");
383 dp_misc::TRACE( "> [dp_registry.cxx] ambiguous backends:\n\n" );
384 for (auto const& ambiguousBackend
: that
->m_ambiguousBackends
)
388 Reference
<lang::XServiceInfo
>(
389 ambiguousBackend
, UNO_QUERY_THROW
)->getImplementationName() );
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
=
396 buf
.append( xInfo
->getMediaType() );
397 const OUString
filter( xInfo
->getFileFilter() );
398 if (!filter
.isEmpty()) {
400 buf
.append( filter
);
403 if (pos
< (types
.getLength() - 1))
406 dp_misc::TRACE(buf
.makeStringAndClear() + "\n\n");
408 allBackends
.insert( that
->m_ambiguousBackends
.begin(),
409 that
->m_ambiguousBackends
.end() );
410 OSL_ASSERT( allBackends
== that
->m_allBackends
);
417 // XUpdatable: broadcast to backends
419 void PackageRegistryImpl::update()
422 for (auto const& backend
: m_allBackends
)
424 const Reference
<util::XUpdatable
> xUpdatable(backend
, UNO_QUERY
);
426 xUpdatable
->update();
432 Reference
<deployment::XPackage
> PackageRegistryImpl::bindPackage(
433 OUString
const & url
, OUString
const & mediaType_
, sal_Bool bRemoved
,
434 OUString
const & identifier
, Reference
<XCommandEnvironment
> const & xCmdEnv
)
437 OUString
mediaType(mediaType_
);
438 if (mediaType
.isEmpty())
440 ::ucbhelper::Content ucbContent
;
445 bOk
= create_ucb_content(
446 &ucbContent
, url
, xCmdEnv
, false /* no throw */ )
447 && !ucbContent
.isFolder();
449 catch (const css::ucb::ContentCreationException
&)
456 OUString
title( StrTitle::getTitle( ucbContent
) );
459 const t_string2string::const_iterator
iFind(
460 m_filter2mediaType
.find(title
) );
461 if (iFind
!= m_filter2mediaType
.end()) {
462 mediaType
= iFind
->second
;
465 sal_Int32 point
= title
.indexOf( '.', 1 /* consume . */ );
468 title
= title
.copy(point
);
472 if (mediaType
.isEmpty())
474 // try ambiguous backends:
475 for (auto const& ambiguousBackend
: m_ambiguousBackends
)
478 return ambiguousBackend
->bindPackage( url
, mediaType
, bRemoved
,
479 identifier
, xCmdEnv
);
481 catch (const lang::IllegalArgumentException
&) {
484 throw lang::IllegalArgumentException(
485 DpResId(RID_STR_CANNOT_DETECT_MEDIA_TYPE
) + url
,
486 static_cast<OWeakObject
*>(this), static_cast<sal_Int16
>(-1) );
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( ';' );
497 iFind
= m_mediaType2backend
.find(
500 mediaType
.copy( 0, q
) ) );
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) );
508 return iFind
->second
->bindPackage( url
, mediaType
, bRemoved
,
509 identifier
, xCmdEnv
);
514 Sequence
< Reference
<deployment::XPackageTypeInfo
> >
515 PackageRegistryImpl::getSupportedPackageTypes()
517 return comphelper::containerToSequence(m_typesInfos
);
522 Reference
<deployment::XPackageRegistry
> create(
523 OUString
const & context
,
524 OUString
const & cachePath
,
525 Reference
<XComponentContext
> const & xComponentContext
)
527 return PackageRegistryImpl::create(
528 context
, cachePath
, xComponentContext
);
531 } // namespace dp_registry
533 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */