update credits
[LibreOffice.git] / desktop / source / deployment / registry / dp_registry.cxx
blobfdb3fba23a2a4c25f398e1e7401583cef0523fd0
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 .
21 #include "dp_registry.hrc"
22 #include "dp_misc.h"
23 #include "dp_resource.h"
24 #include "dp_interact.h"
25 #include "dp_ucb.h"
26 #include "osl/diagnose.h"
27 #include "rtl/ustrbuf.hxx"
28 #include "rtl/uri.hxx"
29 #include "cppuhelper/compbase2.hxx"
30 #include "cppuhelper/exc_hlp.hxx"
31 #include "comphelper/sequence.hxx"
32 #include "ucbhelper/content.hxx"
33 #include "com/sun/star/uno/DeploymentException.hpp"
34 #include "com/sun/star/lang/DisposedException.hpp"
35 #include "com/sun/star/lang/WrappedTargetRuntimeException.hpp"
36 #include "com/sun/star/lang/XServiceInfo.hpp"
37 #include "com/sun/star/lang/XSingleComponentFactory.hpp"
38 #include "com/sun/star/lang/XSingleServiceFactory.hpp"
39 #include "com/sun/star/util/XUpdatable.hpp"
40 #include "com/sun/star/container/XContentEnumerationAccess.hpp"
41 #include "com/sun/star/deployment/PackageRegistryBackend.hpp"
42 #include <boost/unordered_map.hpp>
43 #include <set>
44 #include <boost/unordered_set.hpp>
45 #include <memory>
47 using namespace ::dp_misc;
48 using namespace ::com::sun::star;
49 using namespace ::com::sun::star::uno;
50 using namespace ::com::sun::star::ucb;
53 namespace dp_registry {
55 namespace backend {
56 namespace bundle {
57 Reference<deployment::XPackageRegistry> create(
58 Reference<deployment::XPackageRegistry> const & xRootRegistry,
59 OUString const & context, OUString const & cachePath, bool readOnly,
60 Reference<XComponentContext> const & xComponentContext );
64 namespace {
66 typedef ::cppu::WeakComponentImplHelper2<
67 deployment::XPackageRegistry, util::XUpdatable > t_helper;
69 //==============================================================================
70 class PackageRegistryImpl : private MutexHolder, public t_helper
72 struct ci_string_hash {
73 ::std::size_t operator () ( OUString const & str ) const {
74 return str.toAsciiLowerCase().hashCode();
77 struct ci_string_equals {
78 bool operator () ( OUString const & str1, OUString const & str2 ) const{
79 return str1.equalsIgnoreAsciiCase( str2 );
82 typedef ::boost::unordered_map<
83 OUString, Reference<deployment::XPackageRegistry>,
84 ci_string_hash, ci_string_equals > t_string2registry;
85 typedef ::boost::unordered_map<
86 OUString, OUString,
87 ci_string_hash, ci_string_equals > t_string2string;
88 typedef ::std::set<
89 Reference<deployment::XPackageRegistry> > t_registryset;
91 t_string2registry m_mediaType2backend;
92 t_string2string m_filter2mediaType;
93 t_registryset m_ambiguousBackends;
94 t_registryset m_allBackends;
95 ::std::vector< Reference<deployment::XPackageTypeInfo> > m_typesInfos;
97 void insertBackend(
98 Reference<deployment::XPackageRegistry> const & xBackend );
100 protected:
101 inline void check();
102 virtual void SAL_CALL disposing();
104 virtual ~PackageRegistryImpl();
105 PackageRegistryImpl() : t_helper( getMutex() ) {}
108 public:
109 static Reference<deployment::XPackageRegistry> create(
110 OUString const & context,
111 OUString const & cachePath, bool readOnly,
112 Reference<XComponentContext> const & xComponentContext );
114 // XUpdatable
115 virtual void SAL_CALL update() throw (RuntimeException);
117 // XPackageRegistry
118 virtual Reference<deployment::XPackage> SAL_CALL bindPackage(
119 OUString const & url, OUString const & mediaType, sal_Bool bRemoved,
120 OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv )
121 throw (deployment::DeploymentException,
122 deployment::InvalidRemovedParameterException,
123 CommandFailedException,
124 lang::IllegalArgumentException, RuntimeException);
125 virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
126 getSupportedPackageTypes() throw (RuntimeException);
127 virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType)
128 throw (deployment::DeploymentException,
129 RuntimeException);
133 //______________________________________________________________________________
134 inline void PackageRegistryImpl::check()
136 ::osl::MutexGuard guard( getMutex() );
137 if (rBHelper.bInDispose || rBHelper.bDisposed) {
138 throw lang::DisposedException(
139 "PackageRegistry instance has already been disposed!",
140 static_cast<OWeakObject *>(this) );
144 //______________________________________________________________________________
145 void PackageRegistryImpl::disposing()
147 // dispose all backends:
148 t_registryset::const_iterator iPos( m_allBackends.begin() );
149 t_registryset::const_iterator const iEnd( m_allBackends.end() );
150 for ( ; iPos != iEnd; ++iPos ) {
151 try_dispose( *iPos );
153 m_mediaType2backend = t_string2registry();
154 m_ambiguousBackends = t_registryset();
155 m_allBackends = t_registryset();
157 t_helper::disposing();
160 //______________________________________________________________________________
161 PackageRegistryImpl::~PackageRegistryImpl()
165 //______________________________________________________________________________
166 OUString normalizeMediaType( OUString const & mediaType )
168 OUStringBuffer buf;
169 sal_Int32 index = 0;
170 for (;;) {
171 buf.append( mediaType.getToken( 0, '/', index ).trim() );
172 if (index < 0)
173 break;
174 buf.append( static_cast< sal_Unicode >('/') );
176 return buf.makeStringAndClear();
179 //______________________________________________________________________________
181 void PackageRegistryImpl::packageRemoved(
182 OUString const & url, OUString const & mediaType)
183 throw (css::deployment::DeploymentException,
184 css::uno::RuntimeException)
186 const t_string2registry::const_iterator i =
187 m_mediaType2backend.find(mediaType);
189 if (i != m_mediaType2backend.end())
191 i->second->packageRemoved(url, mediaType);
195 void PackageRegistryImpl::insertBackend(
196 Reference<deployment::XPackageRegistry> const & xBackend )
198 m_allBackends.insert( xBackend );
199 typedef ::boost::unordered_set<OUString, OUStringHash> t_stringset;
200 t_stringset ambiguousFilters;
202 const Sequence< Reference<deployment::XPackageTypeInfo> > packageTypes(
203 xBackend->getSupportedPackageTypes() );
204 for ( sal_Int32 pos = 0; pos < packageTypes.getLength(); ++pos )
206 Reference<deployment::XPackageTypeInfo> const & xPackageType =
207 packageTypes[ pos ];
208 m_typesInfos.push_back( xPackageType );
210 const OUString mediaType( normalizeMediaType(
211 xPackageType->getMediaType() ) );
212 ::std::pair<t_string2registry::iterator, bool> mb_insertion(
213 m_mediaType2backend.insert( t_string2registry::value_type(
214 mediaType, xBackend ) ) );
215 if (mb_insertion.second)
217 // add parameterless media-type, too:
218 sal_Int32 semi = mediaType.indexOf( ';' );
219 if (semi >= 0) {
220 m_mediaType2backend.insert(
221 t_string2registry::value_type(
222 mediaType.copy( 0, semi ), xBackend ) );
224 const OUString fileFilter( xPackageType->getFileFilter() );
225 //The package backend shall also be called to determine the mediatype
226 //(XPackageRegistry.bindPackage) when the URL points to a directory.
227 const bool bExtension = (mediaType == "application/vnd.sun.star.package-bundle");
228 if (fileFilter.isEmpty() || fileFilter == "*.*" || fileFilter == "*" || bExtension)
230 m_ambiguousBackends.insert( xBackend );
232 else
234 sal_Int32 nIndex = 0;
235 do {
236 OUString token( fileFilter.getToken( 0, ';', nIndex ) );
237 if (token.match( "*." ))
238 token = token.copy( 1 );
239 if (token.isEmpty())
240 continue;
241 // mark any further wildcards ambig:
242 bool ambig = (token.indexOf('*') >= 0 ||
243 token.indexOf('?') >= 0);
244 if (! ambig) {
245 ::std::pair<t_string2string::iterator, bool> ins(
246 m_filter2mediaType.insert(
247 t_string2string::value_type(
248 token, mediaType ) ) );
249 ambig = !ins.second;
250 if (ambig) {
251 // filter has already been in: add previously
252 // added backend to ambig set
253 const t_string2registry::const_iterator iFind(
254 m_mediaType2backend.find(
255 /* media-type of pr. added backend */
256 ins.first->second ) );
257 OSL_ASSERT(
258 iFind != m_mediaType2backend.end() );
259 if (iFind != m_mediaType2backend.end())
260 m_ambiguousBackends.insert( iFind->second );
263 if (ambig) {
264 m_ambiguousBackends.insert( xBackend );
265 // mark filter to be removed later from filters map:
266 ambiguousFilters.insert( token );
269 while (nIndex >= 0);
272 #if OSL_DEBUG_LEVEL > 0
273 else
275 OUStringBuffer buf;
276 buf.appendAscii( "more than one PackageRegistryBackend for media-type=\"" );
277 buf.append( mediaType );
278 buf.appendAscii( "\" => " );
279 buf.append( Reference<lang::XServiceInfo>(
280 xBackend, UNO_QUERY_THROW )->
281 getImplementationName() );
282 buf.appendAscii( "\"!" );
283 OSL_FAIL( OUStringToOString(
284 buf.makeStringAndClear(),
285 RTL_TEXTENCODING_UTF8).getStr() );
287 #endif
290 // cut out ambiguous filters:
291 t_stringset::const_iterator iPos( ambiguousFilters.begin() );
292 const t_stringset::const_iterator iEnd( ambiguousFilters.end() );
293 for ( ; iPos != iEnd; ++iPos ) {
294 m_filter2mediaType.erase( *iPos );
298 //______________________________________________________________________________
299 Reference<deployment::XPackageRegistry> PackageRegistryImpl::create(
300 OUString const & context,
301 OUString const & cachePath, bool readOnly,
302 Reference<XComponentContext> const & xComponentContext )
304 PackageRegistryImpl * that = new PackageRegistryImpl;
305 Reference<deployment::XPackageRegistry> xRet(that);
307 // auto-detect all registered package registries:
308 Reference<container::XEnumeration> xEnum(
309 Reference<container::XContentEnumerationAccess>(
310 xComponentContext->getServiceManager(),
311 UNO_QUERY_THROW )->createContentEnumeration(
312 "com.sun.star.deployment.PackageRegistryBackend" ) );
313 if (xEnum.is())
315 while (xEnum->hasMoreElements())
317 Any element( xEnum->nextElement() );
318 Sequence<Any> registryArgs(cachePath.isEmpty() ? 1 : 3 );
319 registryArgs[ 0 ] <<= context;
320 if (!cachePath.isEmpty())
322 Reference<lang::XServiceInfo> xServiceInfo(
323 element, UNO_QUERY_THROW );
324 OUString registryCachePath(
325 makeURL( cachePath,
326 ::rtl::Uri::encode(
327 xServiceInfo->getImplementationName(),
328 rtl_UriCharClassPchar,
329 rtl_UriEncodeIgnoreEscapes,
330 RTL_TEXTENCODING_UTF8 ) ) );
331 registryArgs[ 1 ] <<= registryCachePath;
332 registryArgs[ 2 ] <<= readOnly;
333 if (! readOnly)
334 create_folder( 0, registryCachePath,
335 Reference<XCommandEnvironment>() );
338 Reference<deployment::XPackageRegistry> xBackend;
339 Reference<lang::XSingleComponentFactory> xFac( element, UNO_QUERY );
340 if (xFac.is()) {
341 xBackend.set(
342 xFac->createInstanceWithArgumentsAndContext(
343 registryArgs, xComponentContext ), UNO_QUERY );
345 else {
346 Reference<lang::XSingleServiceFactory> xSingleServiceFac(
347 element, UNO_QUERY_THROW );
348 xBackend.set(
349 xSingleServiceFac->createInstanceWithArguments(
350 registryArgs ), UNO_QUERY );
352 if (! xBackend.is()) {
353 throw DeploymentException(
354 "cannot instantiate PackageRegistryBackend service: "
355 + Reference<lang::XServiceInfo>(
356 element, UNO_QUERY_THROW )->getImplementationName(),
357 static_cast<OWeakObject *>(that) );
360 that->insertBackend( xBackend );
364 // Insert bundle back-end.
365 // Always register as last, because we want to add extensions also as folders
366 // and as a default we accept every folder, which was not recognized by the other
367 // backends.
368 Reference<deployment::XPackageRegistry> extensionBackend =
369 ::dp_registry::backend::bundle::create(
370 that, context, cachePath, readOnly, xComponentContext);
371 that->insertBackend(extensionBackend);
373 Reference<lang::XServiceInfo> xServiceInfo(
374 extensionBackend, UNO_QUERY_THROW );
376 OSL_ASSERT(xServiceInfo.is());
377 OUString registryCachePath(
378 makeURL( cachePath,
379 ::rtl::Uri::encode(
380 xServiceInfo->getImplementationName(),
381 rtl_UriCharClassPchar,
382 rtl_UriEncodeIgnoreEscapes,
383 RTL_TEXTENCODING_UTF8 ) ) );
384 create_folder( 0, registryCachePath, Reference<XCommandEnvironment>());
387 #if OSL_DEBUG_LEVEL > 1
388 // dump tables:
390 t_registryset allBackends;
391 dp_misc::TRACE("> [dp_registry.cxx] media-type detection:\n\n" );
392 for ( t_string2string::const_iterator iPos(
393 that->m_filter2mediaType.begin() );
394 iPos != that->m_filter2mediaType.end(); ++iPos )
396 OUStringBuffer buf;
397 buf.appendAscii( "extension \"" );
398 buf.append( iPos->first );
399 buf.appendAscii( "\" maps to media-type \"" );
400 buf.append( iPos->second );
401 buf.appendAscii( "\" maps to backend " );
402 const Reference<deployment::XPackageRegistry> xBackend(
403 that->m_mediaType2backend.find( iPos->second )->second );
404 allBackends.insert( xBackend );
405 buf.append( Reference<lang::XServiceInfo>(
406 xBackend, UNO_QUERY_THROW )
407 ->getImplementationName() );
408 dp_misc::writeConsole( buf.makeStringAndClear() + "\n");
410 dp_misc::TRACE( "> [dp_registry.cxx] ambiguous backends:\n\n" );
411 for ( t_registryset::const_iterator iPos(
412 that->m_ambiguousBackends.begin() );
413 iPos != that->m_ambiguousBackends.end(); ++iPos )
415 OUStringBuffer buf;
416 buf.append(
417 Reference<lang::XServiceInfo>(
418 *iPos, UNO_QUERY_THROW )->getImplementationName() );
419 buf.appendAscii( ": " );
420 const Sequence< Reference<deployment::XPackageTypeInfo> > types(
421 (*iPos)->getSupportedPackageTypes() );
422 for ( sal_Int32 pos = 0; pos < types.getLength(); ++pos ) {
423 Reference<deployment::XPackageTypeInfo> const & xInfo =
424 types[ pos ];
425 buf.append( xInfo->getMediaType() );
426 const OUString filter( xInfo->getFileFilter() );
427 if (!filter.isEmpty()) {
428 buf.appendAscii( " (" );
429 buf.append( filter );
430 buf.appendAscii( ")" );
432 if (pos < (types.getLength() - 1))
433 buf.appendAscii( ", " );
435 dp_misc::TRACE(buf.makeStringAndClear() + "\n\n");
437 allBackends.insert( that->m_ambiguousBackends.begin(),
438 that->m_ambiguousBackends.end() );
439 OSL_ASSERT( allBackends == that->m_allBackends );
441 #endif
443 return xRet;
446 // XUpdatable: broadcast to backends
447 //______________________________________________________________________________
448 void PackageRegistryImpl::update() throw (RuntimeException)
450 check();
451 t_registryset::const_iterator iPos( m_allBackends.begin() );
452 const t_registryset::const_iterator iEnd( m_allBackends.end() );
453 for ( ; iPos != iEnd; ++iPos ) {
454 const Reference<util::XUpdatable> xUpdatable( *iPos, UNO_QUERY );
455 if (xUpdatable.is())
456 xUpdatable->update();
460 // XPackageRegistry
461 //______________________________________________________________________________
462 Reference<deployment::XPackage> PackageRegistryImpl::bindPackage(
463 OUString const & url, OUString const & mediaType_, sal_Bool bRemoved,
464 OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv )
465 throw (deployment::DeploymentException, deployment::InvalidRemovedParameterException,
466 CommandFailedException,
467 lang::IllegalArgumentException, RuntimeException)
469 check();
470 OUString mediaType(mediaType_);
471 if (mediaType.isEmpty())
473 ::ucbhelper::Content ucbContent;
474 if (create_ucb_content(
475 &ucbContent, url, xCmdEnv, false /* no throw */ )
476 && !ucbContent.isFolder())
478 OUString title( StrTitle::getTitle( ucbContent ) );
479 for (;;)
481 const t_string2string::const_iterator iFind(
482 m_filter2mediaType.find(title) );
483 if (iFind != m_filter2mediaType.end()) {
484 mediaType = iFind->second;
485 break;
487 sal_Int32 point = title.indexOf( '.', 1 /* consume . */ );
488 if (point < 0)
489 break;
490 title = title.copy(point);
494 if (mediaType.isEmpty())
496 // try ambiguous backends:
497 t_registryset::const_iterator iPos( m_ambiguousBackends.begin() );
498 const t_registryset::const_iterator iEnd( m_ambiguousBackends.end() );
499 for ( ; iPos != iEnd; ++iPos )
501 try {
502 return (*iPos)->bindPackage( url, mediaType, bRemoved,
503 identifier, xCmdEnv );
505 catch (const lang::IllegalArgumentException &) {
508 throw lang::IllegalArgumentException(
509 getResourceString(RID_STR_CANNOT_DETECT_MEDIA_TYPE) + url,
510 static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
512 else
514 // get backend by media-type:
515 t_string2registry::const_iterator iFind(
516 m_mediaType2backend.find( normalizeMediaType(mediaType) ) );
517 if (iFind == m_mediaType2backend.end()) {
518 // xxx todo: more sophisticated media-type argument parsing...
519 sal_Int32 q = mediaType.indexOf( ';' );
520 if (q >= 0) {
521 iFind = m_mediaType2backend.find(
522 normalizeMediaType(
523 // cut parameters:
524 mediaType.copy( 0, q ) ) );
527 if (iFind == m_mediaType2backend.end()) {
528 throw lang::IllegalArgumentException(
529 getResourceString(RID_STR_UNSUPPORTED_MEDIA_TYPE) + mediaType,
530 static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
532 return iFind->second->bindPackage( url, mediaType, bRemoved,
533 identifier, xCmdEnv );
537 //______________________________________________________________________________
538 Sequence< Reference<deployment::XPackageTypeInfo> >
539 PackageRegistryImpl::getSupportedPackageTypes() throw (RuntimeException)
541 return comphelper::containerToSequence(m_typesInfos);
543 } // anon namespace
545 //==============================================================================
546 Reference<deployment::XPackageRegistry> SAL_CALL create(
547 OUString const & context,
548 OUString const & cachePath, bool readOnly,
549 Reference<XComponentContext> const & xComponentContext )
551 return PackageRegistryImpl::create(
552 context, cachePath, readOnly, xComponentContext );
555 } // namespace dp_registry
557 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */