blob: d26a293c5f9c3f5d85f13e32ce0b626b07644ae8 [file] [log] [blame]
/**************************************************************
*
* 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 <hash_map>
#include <set>
#include <hash_set>
#include <memory>
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<deployment::XPackageRegistry> create(
Reference<deployment::XPackageRegistry> const & xRootRegistry,
OUString const & context, OUString const & cachePath, bool readOnly,
Reference<XComponentContext> 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<deployment::XPackageRegistry>,
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<deployment::XPackageRegistry> > t_registryset;
t_string2registry m_mediaType2backend;
t_string2string m_filter2mediaType;
t_registryset m_ambiguousBackends;
t_registryset m_allBackends;
::std::vector< Reference<deployment::XPackageTypeInfo> > m_typesInfos;
void insertBackend(
Reference<deployment::XPackageRegistry> const & xBackend );
protected:
inline void check();
virtual void SAL_CALL disposing();
virtual ~PackageRegistryImpl();
PackageRegistryImpl() : t_helper( getMutex() ) {}
public:
static Reference<deployment::XPackageRegistry> create(
OUString const & context,
OUString const & cachePath, bool readOnly,
Reference<XComponentContext> const & xComponentContext );
// XUpdatable
virtual void SAL_CALL update() throw (RuntimeException);
// XPackageRegistry
virtual Reference<deployment::XPackage> SAL_CALL bindPackage(
OUString const & url, OUString const & mediaType, sal_Bool bRemoved,
OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv )
throw (deployment::DeploymentException,
deployment::InvalidRemovedParameterException,
CommandFailedException,
lang::IllegalArgumentException, RuntimeException);
virtual Sequence< Reference<deployment::XPackageTypeInfo> > 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<OWeakObject *>(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<deployment::XPackageRegistry> const & xBackend )
{
m_allBackends.insert( xBackend );
typedef ::std::hash_set<OUString, ::rtl::OUStringHash> t_stringset;
t_stringset ambiguousFilters;
const Sequence< Reference<deployment::XPackageTypeInfo> > packageTypes(
xBackend->getSupportedPackageTypes() );
for ( sal_Int32 pos = 0; pos < packageTypes.getLength(); ++pos )
{
Reference<deployment::XPackageTypeInfo> const & xPackageType =
packageTypes[ pos ];
m_typesInfos.push_back( xPackageType );
const OUString mediaType( normalizeMediaType(
xPackageType->getMediaType() ) );
::std::pair<t_string2registry::iterator, bool> 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<t_string2string::iterator, bool> 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<lang::XServiceInfo>(
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<deployment::XPackageRegistry> PackageRegistryImpl::create(
OUString const & context,
OUString const & cachePath, bool readOnly,
Reference<XComponentContext> const & xComponentContext )
{
PackageRegistryImpl * that = new PackageRegistryImpl;
Reference<deployment::XPackageRegistry> xRet(that);
// auto-detect all registered package registries:
Reference<container::XEnumeration> xEnum(
Reference<container::XContentEnumerationAccess>(
xComponentContext->getServiceManager(),
UNO_QUERY_THROW )->createContentEnumeration(
OUSTR("com.sun.star.deployment.PackageRegistryBackend") ) );
if (xEnum.is())
{
while (xEnum->hasMoreElements())
{
Any element( xEnum->nextElement() );
Sequence<Any> registryArgs(
cachePath.getLength() == 0 ? 1 : 3 );
registryArgs[ 0 ] <<= context;
if (cachePath.getLength() > 0)
{
Reference<lang::XServiceInfo> 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<XCommandEnvironment>() );
}
Reference<deployment::XPackageRegistry> xBackend;
Reference<lang::XSingleComponentFactory> xFac( element, UNO_QUERY );
if (xFac.is()) {
xBackend.set(
xFac->createInstanceWithArgumentsAndContext(
registryArgs, xComponentContext ), UNO_QUERY );
}
else {
Reference<lang::XSingleServiceFactory> xSingleServiceFac(
element, UNO_QUERY_THROW );
xBackend.set(
xSingleServiceFac->createInstanceWithArguments(
registryArgs ), UNO_QUERY );
}
if (! xBackend.is()) {
throw DeploymentException(
OUSTR("cannot instantiate PackageRegistryBackend service: ")
+ Reference<lang::XServiceInfo>(
element, UNO_QUERY_THROW )->getImplementationName(),
static_cast<OWeakObject *>(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<deployment::XPackageRegistry> extensionBackend =
::dp_registry::backend::bundle::create(
that, context, cachePath, readOnly, xComponentContext);
that->insertBackend(extensionBackend);
Reference<lang::XServiceInfo> 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<XCommandEnvironment>());
#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<deployment::XPackageRegistry> xBackend(
that->m_mediaType2backend.find( iPos->second )->second );
allBackends.insert( xBackend );
buf.append( Reference<lang::XServiceInfo>(
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<lang::XServiceInfo>(
*iPos, UNO_QUERY_THROW )->getImplementationName() );
buf.appendAscii( RTL_CONSTASCII_STRINGPARAM(": ") );
const Sequence< Reference<deployment::XPackageTypeInfo> > types(
(*iPos)->getSupportedPackageTypes() );
for ( sal_Int32 pos = 0; pos < types.getLength(); ++pos ) {
Reference<deployment::XPackageTypeInfo> 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<util::XUpdatable> xUpdatable( *iPos, UNO_QUERY );
if (xUpdatable.is())
xUpdatable->update();
}
}
// XPackageRegistry
//______________________________________________________________________________
Reference<deployment::XPackage> PackageRegistryImpl::bindPackage(
OUString const & url, OUString const & mediaType_, sal_Bool bRemoved,
OUString const & identifier, Reference<XCommandEnvironment> 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<OUString>() );
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<OWeakObject *>(this), static_cast<sal_Int16>(-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<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
}
return iFind->second->bindPackage( url, mediaType, bRemoved,
identifier, xCmdEnv );
}
}
//______________________________________________________________________________
Sequence< Reference<deployment::XPackageTypeInfo> >
PackageRegistryImpl::getSupportedPackageTypes() throw (RuntimeException)
{
return comphelper::containerToSequence(m_typesInfos);
}
} // anon namespace
//==============================================================================
Reference<deployment::XPackageRegistry> SAL_CALL create(
OUString const & context,
OUString const & cachePath, bool readOnly,
Reference<XComponentContext> const & xComponentContext )
{
return PackageRegistryImpl::create(
context, cachePath, readOnly, xComponentContext );
}
} // namespace dp_registry