/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ::dp_misc; using namespace ::com::sun::star; using namespace ::com::sun::star::uno; using namespace ::com::sun::star::ucb; namespace dp_registry { namespace { typedef ::cppu::WeakComponentImplHelper< deployment::XPackageRegistry, util::XUpdatable > t_helper; class PackageRegistryImpl : private MutexHolder, public t_helper { struct ci_string_hash { std::size_t operator () ( OUString const & str ) const { return str.toAsciiLowerCase().hashCode(); } }; struct ci_string_equals { bool operator () ( OUString const & str1, OUString const & str2 ) const{ return str1.equalsIgnoreAsciiCase( str2 ); } }; typedef std::unordered_map< OUString, Reference, ci_string_hash, ci_string_equals > t_string2registry; typedef std::unordered_map< OUString, OUString, ci_string_hash, ci_string_equals > t_string2string; typedef std::set< Reference > t_registryset; t_string2registry m_mediaType2backend; t_string2string m_filter2mediaType; t_registryset m_ambiguousBackends; t_registryset m_allBackends; std::vector< Reference > m_typesInfos; void insertBackend( Reference const & xBackend ); protected: void check(); virtual void SAL_CALL disposing() override; virtual ~PackageRegistryImpl() override; PackageRegistryImpl() : t_helper( getMutex() ) {} public: static Reference create( OUString const & context, OUString const & cachePath, Reference const & xComponentContext ); // XUpdatable virtual void SAL_CALL update() override; // XPackageRegistry virtual Reference SAL_CALL bindPackage( OUString const & url, OUString const & mediaType, sal_Bool bRemoved, OUString const & identifier, Reference const & xCmdEnv ) override; virtual Sequence< Reference > SAL_CALL getSupportedPackageTypes() override; virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override; }; void PackageRegistryImpl::check() { ::osl::MutexGuard guard( getMutex() ); if (rBHelper.bInDispose || rBHelper.bDisposed) { throw lang::DisposedException( "PackageRegistry instance has already been disposed!", static_cast(this) ); } } void PackageRegistryImpl::disposing() { // dispose all backends: for (auto const& backend : m_allBackends) { try_dispose(backend); } m_mediaType2backend = t_string2registry(); m_ambiguousBackends = t_registryset(); m_allBackends = t_registryset(); t_helper::disposing(); } PackageRegistryImpl::~PackageRegistryImpl() { } OUString normalizeMediaType( OUString const & mediaType ) { OUStringBuffer buf; sal_Int32 index = 0; for (;;) { buf.append( mediaType.getToken( 0, '/', index ).trim() ); if (index < 0) break; buf.append( '/' ); } return buf.makeStringAndClear(); } void PackageRegistryImpl::packageRemoved( OUString const & url, OUString const & mediaType) { const t_string2registry::const_iterator i = m_mediaType2backend.find(mediaType); if (i != m_mediaType2backend.end()) { i->second->packageRemoved(url, mediaType); } } void PackageRegistryImpl::insertBackend( Reference const & xBackend ) { m_allBackends.insert( xBackend ); std::unordered_set ambiguousFilters; const Sequence< Reference > packageTypes( xBackend->getSupportedPackageTypes() ); for ( Reference const & xPackageType : packageTypes ) { m_typesInfos.push_back( xPackageType ); const OUString mediaType( normalizeMediaType( xPackageType->getMediaType() ) ); std::pair a_insertion( m_mediaType2backend.emplace( mediaType, xBackend ) ); if (a_insertion.second) { // add parameterless media-type, too: sal_Int32 semi = mediaType.indexOf( ';' ); if (semi >= 0) { m_mediaType2backend.emplace( mediaType.copy( 0, semi ), xBackend ); } const OUString fileFilter( xPackageType->getFileFilter() ); //The package backend shall also be called to determine the mediatype //(XPackageRegistry.bindPackage) when the URL points to a directory. const bool bExtension = (mediaType == "application/vnd.sun.star.package-bundle"); if (fileFilter.isEmpty() || fileFilter == "*.*" || fileFilter == "*" || bExtension) { m_ambiguousBackends.insert( xBackend ); } else { sal_Int32 nIndex = 0; do { OUString token( fileFilter.getToken( 0, ';', nIndex ) ); if (token.match( "*." )) token = token.copy( 1 ); if (token.isEmpty()) continue; // mark any further wildcards ambig: bool ambig = (token.indexOf('*') >= 0 || token.indexOf('?') >= 0); if (! ambig) { std::pair ins( m_filter2mediaType.emplace( token, mediaType ) ); ambig = !ins.second; if (ambig) { // filter has already been in: add previously // added backend to ambig set const t_string2registry::const_iterator iFind( m_mediaType2backend.find( /* media-type of pr. added backend */ ins.first->second ) ); OSL_ASSERT( iFind != m_mediaType2backend.end() ); if (iFind != m_mediaType2backend.end()) m_ambiguousBackends.insert( iFind->second ); } } if (ambig) { m_ambiguousBackends.insert( xBackend ); // mark filter to be removed later from filters map: ambiguousFilters.insert( token ); } } while (nIndex >= 0); } } #if OSL_DEBUG_LEVEL > 0 else { SAL_WARN( "desktop", "more than one PackageRegistryBackend for media-type=\"" << mediaType << "\" => " << Reference( xBackend, UNO_QUERY_THROW )->getImplementationName() << "\"!" ); } #endif } // cut out ambiguous filters: for (auto const& ambiguousFilter : ambiguousFilters) { m_filter2mediaType.erase(ambiguousFilter); } } Reference PackageRegistryImpl::create( OUString const & context, OUString const & cachePath, Reference const & xComponentContext ) { PackageRegistryImpl * that = new PackageRegistryImpl; Reference xRet(that); // auto-detect all registered package registries: Reference xEnum( Reference( xComponentContext->getServiceManager(), UNO_QUERY_THROW )->createContentEnumeration( "com.sun.star.deployment.PackageRegistryBackend" ) ); if (xEnum.is()) { while (xEnum->hasMoreElements()) { Any element( xEnum->nextElement() ); Sequence registryArgs(cachePath.isEmpty() ? 1 : 3 ); registryArgs[ 0 ] <<= context; if (!cachePath.isEmpty()) { Reference xServiceInfo( element, UNO_QUERY_THROW ); OUString registryCachePath( makeURL( cachePath, ::rtl::Uri::encode( xServiceInfo->getImplementationName(), rtl_UriCharClassPchar, rtl_UriEncodeIgnoreEscapes, RTL_TEXTENCODING_UTF8 ) ) ); registryArgs[ 1 ] <<= registryCachePath; registryArgs[ 2 ] <<= false; // readOnly; create_folder( nullptr, registryCachePath, Reference() ); } Reference xBackend; Reference xFac( element, UNO_QUERY ); if (xFac.is()) { xBackend.set( xFac->createInstanceWithArgumentsAndContext( registryArgs, xComponentContext ), UNO_QUERY ); } else { Reference xSingleServiceFac( element, UNO_QUERY_THROW ); xBackend.set( xSingleServiceFac->createInstanceWithArguments( registryArgs ), UNO_QUERY ); } if (! xBackend.is()) { throw DeploymentException( "cannot instantiate PackageRegistryBackend service: " + Reference( element, UNO_QUERY_THROW )->getImplementationName(), static_cast(that) ); } that->insertBackend( xBackend ); } } // Insert bundle back-end. // Always register as last, because we want to add extensions also as folders // and as a default we accept every folder, which was not recognized by the other // backends. Reference extensionBackend = ::dp_registry::backend::bundle::create( that, context, cachePath, xComponentContext); that->insertBackend(extensionBackend); Reference xServiceInfo( extensionBackend, UNO_QUERY_THROW ); OSL_ASSERT(xServiceInfo.is()); OUString registryCachePath( makeURL( cachePath, ::rtl::Uri::encode( xServiceInfo->getImplementationName(), rtl_UriCharClassPchar, rtl_UriEncodeIgnoreEscapes, RTL_TEXTENCODING_UTF8 ) ) ); create_folder( nullptr, registryCachePath, Reference()); #if OSL_DEBUG_LEVEL > 0 // dump tables: { t_registryset allBackends; dp_misc::TRACE("> [dp_registry.cxx] media-type detection:\n\n" ); for (auto const& elem : that->m_filter2mediaType) { OUStringBuffer buf; buf.append( "extension \"" ); buf.append( elem.first ); buf.append( "\" maps to media-type \"" ); buf.append( elem.second ); buf.append( "\" maps to backend " ); const Reference xBackend( that->m_mediaType2backend.find( elem.second )->second ); allBackends.insert( xBackend ); buf.append( Reference( xBackend, UNO_QUERY_THROW ) ->getImplementationName() ); dp_misc::TRACE( buf.makeStringAndClear() + "\n"); } dp_misc::TRACE( "> [dp_registry.cxx] ambiguous backends:\n\n" ); for (auto const& ambiguousBackend : that->m_ambiguousBackends) { OUStringBuffer buf; buf.append( Reference( ambiguousBackend, UNO_QUERY_THROW )->getImplementationName() ); buf.append( ": " ); const Sequence< Reference > types( ambiguousBackend->getSupportedPackageTypes() ); for ( sal_Int32 pos = 0; pos < types.getLength(); ++pos ) { Reference const & xInfo = types[ pos ]; buf.append( xInfo->getMediaType() ); const OUString filter( xInfo->getFileFilter() ); if (!filter.isEmpty()) { buf.append( " (" ); buf.append( filter ); buf.append( ")" ); } if (pos < (types.getLength() - 1)) buf.append( ", " ); } dp_misc::TRACE(buf.makeStringAndClear() + "\n\n"); } allBackends.insert( that->m_ambiguousBackends.begin(), that->m_ambiguousBackends.end() ); OSL_ASSERT( allBackends == that->m_allBackends ); } #endif return xRet; } // XUpdatable: broadcast to backends void PackageRegistryImpl::update() { check(); for (auto const& backend : m_allBackends) { const Reference xUpdatable(backend, UNO_QUERY); if (xUpdatable.is()) xUpdatable->update(); } } // XPackageRegistry Reference PackageRegistryImpl::bindPackage( OUString const & url, OUString const & mediaType_, sal_Bool bRemoved, OUString const & identifier, Reference const & xCmdEnv ) { check(); OUString mediaType(mediaType_); if (mediaType.isEmpty()) { ::ucbhelper::Content ucbContent; bool bOk=true; try { bOk = create_ucb_content( &ucbContent, url, xCmdEnv, false /* no throw */ ) && !ucbContent.isFolder(); } catch (const css::ucb::ContentCreationException&) { bOk = false; } if (bOk) { OUString title( StrTitle::getTitle( ucbContent ) ); for (;;) { const t_string2string::const_iterator iFind( m_filter2mediaType.find(title) ); if (iFind != m_filter2mediaType.end()) { mediaType = iFind->second; break; } sal_Int32 point = title.indexOf( '.', 1 /* consume . */ ); if (point < 0) break; title = title.copy(point); } } } if (mediaType.isEmpty()) { // try ambiguous backends: for (auto const& ambiguousBackend : m_ambiguousBackends) { try { return ambiguousBackend->bindPackage( url, mediaType, bRemoved, identifier, xCmdEnv ); } catch (const lang::IllegalArgumentException &) { } } throw lang::IllegalArgumentException( DpResId(RID_STR_CANNOT_DETECT_MEDIA_TYPE) + url, static_cast(this), static_cast(-1) ); } else { // get backend by media-type: t_string2registry::const_iterator iFind( m_mediaType2backend.find( normalizeMediaType(mediaType) ) ); if (iFind == m_mediaType2backend.end()) { // xxx todo: more sophisticated media-type argument parsing... sal_Int32 q = mediaType.indexOf( ';' ); if (q >= 0) { iFind = m_mediaType2backend.find( normalizeMediaType( // cut parameters: mediaType.copy( 0, q ) ) ); } } if (iFind == m_mediaType2backend.end()) { throw lang::IllegalArgumentException( DpResId(RID_STR_UNSUPPORTED_MEDIA_TYPE) + mediaType, static_cast(this), static_cast(-1) ); } return iFind->second->bindPackage( url, mediaType, bRemoved, identifier, xCmdEnv ); } } Sequence< Reference > PackageRegistryImpl::getSupportedPackageTypes() { return comphelper::containerToSequence(m_typesInfos); } } // anon namespace Reference create( OUString const & context, OUString const & cachePath, Reference const & xComponentContext ) { return PackageRegistryImpl::create( context, cachePath, xComponentContext ); } } // namespace dp_registry /* vim:set shiftwidth=4 softtabstop=4 expandtab: */