/************************************************************** * * 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 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_desktop.hxx" #include "dp_registry.hrc" #include "dp_misc.h" #include "dp_resource.h" #include "dp_interact.h" #include "dp_ucb.h" #include "osl/diagnose.h" #include "rtl/ustrbuf.hxx" #include "rtl/uri.hxx" #include "cppuhelper/compbase2.hxx" #include "cppuhelper/exc_hlp.hxx" #include "comphelper/sequence.hxx" #include "ucbhelper/content.hxx" #include "com/sun/star/uno/DeploymentException.hpp" #include "com/sun/star/lang/DisposedException.hpp" #include "com/sun/star/lang/WrappedTargetRuntimeException.hpp" #include "com/sun/star/lang/XServiceInfo.hpp" #include "com/sun/star/lang/XSingleComponentFactory.hpp" #include "com/sun/star/lang/XSingleServiceFactory.hpp" #include "com/sun/star/util/XUpdatable.hpp" #include "com/sun/star/container/XContentEnumerationAccess.hpp" #include "com/sun/star/deployment/PackageRegistryBackend.hpp" #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; using ::rtl::OUString; namespace dp_registry { namespace backend { namespace bundle { Reference create( Reference const & xRootRegistry, OUString const & context, OUString const & cachePath, bool readOnly, Reference const & xComponentContext ); } } namespace { typedef ::cppu::WeakComponentImplHelper2< 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::hash_map< OUString, Reference, ci_string_hash, ci_string_equals > t_string2registry; typedef ::std::hash_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: inline void check(); virtual void SAL_CALL disposing(); virtual ~PackageRegistryImpl(); PackageRegistryImpl() : t_helper( getMutex() ) {} public: static Reference create( OUString const & context, OUString const & cachePath, bool readOnly, Reference const & xComponentContext ); // XUpdatable virtual void SAL_CALL update() throw (RuntimeException); // XPackageRegistry virtual Reference SAL_CALL bindPackage( OUString const & url, OUString const & mediaType, sal_Bool bRemoved, OUString const & identifier, Reference const & xCmdEnv ) throw (deployment::DeploymentException, deployment::InvalidRemovedParameterException, CommandFailedException, lang::IllegalArgumentException, RuntimeException); virtual Sequence< Reference > SAL_CALL getSupportedPackageTypes() throw (RuntimeException); virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) throw (deployment::DeploymentException, RuntimeException); }; //______________________________________________________________________________ inline void PackageRegistryImpl::check() { ::osl::MutexGuard guard( getMutex() ); if (rBHelper.bInDispose || rBHelper.bDisposed) { throw lang::DisposedException( OUSTR("PackageRegistry instance has already been disposed!"), static_cast(this) ); } } //______________________________________________________________________________ void PackageRegistryImpl::disposing() { // dispose all backends: t_registryset::const_iterator iPos( m_allBackends.begin() ); t_registryset::const_iterator const iEnd( m_allBackends.end() ); for ( ; iPos != iEnd; ++iPos ) { try_dispose( *iPos ); } m_mediaType2backend = t_string2registry(); m_ambiguousBackends = t_registryset(); m_allBackends = t_registryset(); t_helper::disposing(); } //______________________________________________________________________________ PackageRegistryImpl::~PackageRegistryImpl() { } //______________________________________________________________________________ OUString normalizeMediaType( OUString const & mediaType ) { ::rtl::OUStringBuffer buf; sal_Int32 index = 0; for (;;) { buf.append( mediaType.getToken( 0, '/', index ).trim() ); if (index < 0) break; buf.append( static_cast< sal_Unicode >('/') ); } return buf.makeStringAndClear(); } //______________________________________________________________________________ void PackageRegistryImpl::packageRemoved( ::rtl::OUString const & url, ::rtl::OUString const & mediaType) throw (css::deployment::DeploymentException, css::uno::RuntimeException) { 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 ); typedef ::std::hash_set t_stringset; t_stringset ambiguousFilters; const Sequence< Reference > packageTypes( xBackend->getSupportedPackageTypes() ); for ( sal_Int32 pos = 0; pos < packageTypes.getLength(); ++pos ) { Reference const & xPackageType = packageTypes[ pos ]; m_typesInfos.push_back( xPackageType ); const OUString mediaType( normalizeMediaType( xPackageType->getMediaType() ) ); ::std::pair mb_insertion( m_mediaType2backend.insert( t_string2registry::value_type( mediaType, xBackend ) ) ); if (mb_insertion.second) { // add parameterless media-type, too: sal_Int32 semi = mediaType.indexOf( ';' ); if (semi >= 0) { m_mediaType2backend.insert( t_string2registry::value_type( 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.equals(OUSTR("application/vnd.sun.star.package-bundle")); if (fileFilter.getLength() == 0 || fileFilter.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("*.*") ) || fileFilter.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("*") ) || bExtension) { m_ambiguousBackends.insert( xBackend ); } else { sal_Int32 nIndex = 0; do { OUString token( fileFilter.getToken( 0, ';', nIndex ) ); if (token.matchAsciiL( RTL_CONSTASCII_STRINGPARAM("*.") )) token = token.copy( 1 ); if (token.getLength() == 0) continue; // mark any further wildcards ambig: bool ambig = (token.indexOf('*') >= 0 || token.indexOf('?') >= 0); if (! ambig) { ::std::pair ins( m_filter2mediaType.insert( t_string2string::value_type( 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 { ::rtl::OUStringBuffer buf; buf.appendAscii( RTL_CONSTASCII_STRINGPARAM( "more than one PackageRegistryBackend for " "media-type=\"") ); buf.append( mediaType ); buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("\" => ") ); buf.append( Reference( xBackend, UNO_QUERY_THROW )-> getImplementationName() ); buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("\"!") ); OSL_ENSURE( 0, ::rtl::OUStringToOString( buf.makeStringAndClear(), RTL_TEXTENCODING_UTF8 ) ); } #endif } // cut out ambiguous filters: t_stringset::const_iterator iPos( ambiguousFilters.begin() ); const t_stringset::const_iterator iEnd( ambiguousFilters.end() ); for ( ; iPos != iEnd; ++iPos ) { m_filter2mediaType.erase( *iPos ); } } //______________________________________________________________________________ Reference PackageRegistryImpl::create( OUString const & context, OUString const & cachePath, bool readOnly, 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( OUSTR("com.sun.star.deployment.PackageRegistryBackend") ) ); if (xEnum.is()) { while (xEnum->hasMoreElements()) { Any element( xEnum->nextElement() ); Sequence registryArgs( cachePath.getLength() == 0 ? 1 : 3 ); registryArgs[ 0 ] <<= context; if (cachePath.getLength() > 0) { 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 ] <<= readOnly; if (! readOnly) create_folder( 0, 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( OUSTR("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, readOnly, 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( 0, registryCachePath, Reference()); #if OSL_DEBUG_LEVEL > 1 // dump tables: { t_registryset allBackends; dp_misc::TRACE("> [dp_registry.cxx] media-type detection:\n\n" ); for ( t_string2string::const_iterator iPos( that->m_filter2mediaType.begin() ); iPos != that->m_filter2mediaType.end(); ++iPos ) { ::rtl::OUStringBuffer buf; buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("extension \"") ); buf.append( iPos->first ); buf.appendAscii( RTL_CONSTASCII_STRINGPARAM( "\" maps to media-type \"") ); buf.append( iPos->second ); buf.appendAscii( RTL_CONSTASCII_STRINGPARAM( "\" maps to backend ") ); const Reference xBackend( that->m_mediaType2backend.find( iPos->second )->second ); allBackends.insert( xBackend ); buf.append( Reference( xBackend, UNO_QUERY_THROW ) ->getImplementationName() ); dp_misc::writeConsole( buf.makeStringAndClear() + OUSTR("\n")); } dp_misc::TRACE( "> [dp_registry.cxx] ambiguous backends:\n\n" ); for ( t_registryset::const_iterator iPos( that->m_ambiguousBackends.begin() ); iPos != that->m_ambiguousBackends.end(); ++iPos ) { ::rtl::OUStringBuffer buf; buf.append( Reference( *iPos, UNO_QUERY_THROW )->getImplementationName() ); buf.appendAscii( RTL_CONSTASCII_STRINGPARAM(": ") ); const Sequence< Reference > types( (*iPos)->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.getLength() > 0) { buf.appendAscii( RTL_CONSTASCII_STRINGPARAM(" (") ); buf.append( filter ); buf.appendAscii( RTL_CONSTASCII_STRINGPARAM(")") ); } if (pos < (types.getLength() - 1)) buf.appendAscii( RTL_CONSTASCII_STRINGPARAM(", ") ); } dp_misc::TRACE(buf.makeStringAndClear() + OUSTR("\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() throw (RuntimeException) { check(); t_registryset::const_iterator iPos( m_allBackends.begin() ); const t_registryset::const_iterator iEnd( m_allBackends.end() ); for ( ; iPos != iEnd; ++iPos ) { const Reference xUpdatable( *iPos, 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 ) throw (deployment::DeploymentException, deployment::InvalidRemovedParameterException, CommandFailedException, lang::IllegalArgumentException, RuntimeException) { check(); OUString mediaType(mediaType_); if (mediaType.getLength() == 0) { ::ucbhelper::Content ucbContent; if (create_ucb_content( &ucbContent, url, xCmdEnv, false /* no throw */ ) && !ucbContent.isFolder()) { OUString title( ucbContent.getPropertyValue( StrTitle::get() ).get() ); 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.getLength() == 0) { // try ambiguous backends: t_registryset::const_iterator iPos( m_ambiguousBackends.begin() ); const t_registryset::const_iterator iEnd( m_ambiguousBackends.end() ); for ( ; iPos != iEnd; ++iPos ) { try { return (*iPos)->bindPackage( url, mediaType, bRemoved, identifier, xCmdEnv ); } catch (lang::IllegalArgumentException &) { } } throw lang::IllegalArgumentException( getResourceString(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( getResourceString(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() throw (RuntimeException) { return comphelper::containerToSequence(m_typesInfos); } } // anon namespace //============================================================================== Reference SAL_CALL create( OUString const & context, OUString const & cachePath, bool readOnly, Reference const & xComponentContext ) { return PackageRegistryImpl::create( context, cachePath, readOnly, xComponentContext ); } } // namespace dp_registry