Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / desktop / source / deployment / registry / dp_registry.cxx
blob2bd542379a59087ff1e1c8b801673db89dd70086
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>
21 #include <sal/log.hxx>
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>
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 {
61 namespace {
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<
83 OUString, OUString,
84 ci_string_hash, ci_string_equals > t_string2string;
85 typedef std::set<
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;
94 void insertBackend(
95 Reference<deployment::XPackageRegistry> const & xBackend );
97 protected:
98 void check();
99 virtual void SAL_CALL disposing() override;
101 virtual ~PackageRegistryImpl() override;
102 PackageRegistryImpl() : t_helper( getMutex() ) {}
105 public:
106 static Reference<deployment::XPackageRegistry> create(
107 OUString const & context,
108 OUString const & cachePath,
109 Reference<XComponentContext> const & xComponentContext );
111 // XUpdatable
112 virtual void SAL_CALL update() override;
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;
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 )
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( '/' );
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 =
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 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" ) );
289 if (xEnum.is())
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(
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,
310 Reference<XCommandEnvironment>() );
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 );
320 else {
321 Reference<lang::XSingleServiceFactory> xSingleServiceFac(
322 element, UNO_QUERY_THROW );
323 xBackend.set(
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
342 // backends.
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(
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>());
362 #if OSL_DEBUG_LEVEL > 0
363 // dump tables:
365 t_registryset allBackends;
366 dp_misc::TRACE("> [dp_registry.cxx] media-type detection:\n\n" );
367 for (auto const& elem : that->m_filter2mediaType)
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");
383 dp_misc::TRACE( "> [dp_registry.cxx] ambiguous backends:\n\n" );
384 for (auto const& ambiguousBackend : that->m_ambiguousBackends)
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( ")" );
403 if (pos < (types.getLength() - 1))
404 buf.append( ", " );
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 );
412 #endif
414 return xRet;
417 // XUpdatable: broadcast to backends
419 void PackageRegistryImpl::update()
421 check();
422 for (auto const& backend : m_allBackends)
424 const Reference<util::XUpdatable> xUpdatable(backend, UNO_QUERY);
425 if (xUpdatable.is())
426 xUpdatable->update();
430 // XPackageRegistry
432 Reference<deployment::XPackage> PackageRegistryImpl::bindPackage(
433 OUString const & url, OUString const & mediaType_, sal_Bool bRemoved,
434 OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv )
436 check();
437 OUString mediaType(mediaType_);
438 if (mediaType.isEmpty())
440 ::ucbhelper::Content ucbContent;
441 bool bOk=true;
445 bOk = create_ucb_content(
446 &ucbContent, url, xCmdEnv, false /* no throw */ )
447 && !ucbContent.isFolder();
449 catch (const css::ucb::ContentCreationException&)
451 bOk = false;
454 if (bOk)
456 OUString title( StrTitle::getTitle( ucbContent ) );
457 for (;;)
459 const t_string2string::const_iterator iFind(
460 m_filter2mediaType.find(title) );
461 if (iFind != m_filter2mediaType.end()) {
462 mediaType = iFind->second;
463 break;
465 sal_Int32 point = title.indexOf( '.', 1 /* consume . */ );
466 if (point < 0)
467 break;
468 title = title.copy(point);
472 if (mediaType.isEmpty())
474 // try ambiguous backends:
475 for (auto const& ambiguousBackend : m_ambiguousBackends)
477 try {
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) );
488 else
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 ) ) );
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);
519 } // anon namespace
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: */