summaryrefslogtreecommitdiffstats
path: root/ucb/source/ucp/ext/ucpext_content.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /ucb/source/ucp/ext/ucpext_content.cxx
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ucb/source/ucp/ext/ucpext_content.cxx')
-rw-r--r--ucb/source/ucp/ext/ucpext_content.cxx624
1 files changed, 624 insertions, 0 deletions
diff --git a/ucb/source/ucp/ext/ucpext_content.cxx b/ucb/source/ucp/ext/ucpext_content.cxx
new file mode 100644
index 000000000..71d6cbbae
--- /dev/null
+++ b/ucb/source/ucp/ext/ucpext_content.cxx
@@ -0,0 +1,624 @@
+/* -*- 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 "ucpext_content.hxx"
+#include "ucpext_provider.hxx"
+#include "ucpext_resultset.hxx"
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/lang/IllegalAccessException.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/ucb/XCommandInfo.hpp>
+#include <com/sun/star/ucb/OpenCommandArgument2.hpp>
+#include <com/sun/star/ucb/OpenMode.hpp>
+#include <com/sun/star/ucb/UnsupportedCommandException.hpp>
+#include <com/sun/star/ucb/XDynamicResultSet.hpp>
+#include <com/sun/star/deployment/PackageInformationProvider.hpp>
+
+#include <o3tl/string_view.hxx>
+#include <ucbhelper/propertyvalueset.hxx>
+#include <ucbhelper/cancelcommandexecution.hxx>
+#include <ucbhelper/content.hxx>
+#include <tools/diagnose_ex.h>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/uri.hxx>
+#include <sal/macros.h>
+#include <sal/log.hxx>
+
+#include <algorithm>
+#include <string_view>
+
+
+namespace ucb::ucp::ext
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::UNO_SET_THROW;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::ucb::XContentIdentifier;
+ using ::com::sun::star::ucb::XCommandEnvironment;
+ using ::com::sun::star::ucb::Command;
+ using ::com::sun::star::beans::Property;
+ using ::com::sun::star::lang::IllegalArgumentException;
+ using ::com::sun::star::beans::PropertyValue;
+ using ::com::sun::star::ucb::OpenCommandArgument2;
+ using ::com::sun::star::ucb::XDynamicResultSet;
+ using ::com::sun::star::ucb::UnsupportedCommandException;
+ using ::com::sun::star::sdbc::XRow;
+ using ::com::sun::star::beans::PropertyChangeEvent;
+ using ::com::sun::star::lang::IllegalAccessException;
+ using ::com::sun::star::ucb::CommandInfo;
+ using ::com::sun::star::deployment::PackageInformationProvider;
+ using ::com::sun::star::deployment::XPackageInformationProvider;
+
+ namespace OpenMode = ::com::sun::star::ucb::OpenMode;
+ namespace PropertyAttribute = ::com::sun::star::beans::PropertyAttribute;
+
+
+ //= helper
+
+ namespace
+ {
+
+ OUString lcl_compose( std::u16string_view i_rBaseURL, const OUString& i_rRelativeURL )
+ {
+ ENSURE_OR_RETURN( !i_rBaseURL.empty(), "illegal base URL", i_rRelativeURL );
+
+ OUStringBuffer aComposer( i_rBaseURL );
+ if ( !o3tl::ends_with(i_rBaseURL, u"/") )
+ aComposer.append( '/' );
+ aComposer.append( i_rRelativeURL );
+ return aComposer.makeStringAndClear();
+ }
+
+
+ struct SelectPropertyName
+ {
+ const OUString& operator()( const Property& i_rProperty ) const
+ {
+ return i_rProperty.Name;
+ }
+ };
+ }
+
+
+ //= Content
+
+
+ Content::Content( const Reference< XComponentContext >& rxContext, ::ucbhelper::ContentProviderImplHelper* i_pProvider,
+ const Reference< XContentIdentifier >& i_rIdentifier )
+ :Content_Base( rxContext, i_pProvider, i_rIdentifier )
+ ,m_eExtContentType( E_UNKNOWN )
+ {
+ const OUString sURL( getIdentifier()->getContentIdentifier() );
+ if ( denotesRootContent( sURL ) )
+ {
+ m_eExtContentType = E_ROOT;
+ }
+ else
+ {
+ const std::u16string_view sRelativeURL( sURL.subView( ContentProvider::getRootURL().getLength() ) );
+ const size_t nSepPos = sRelativeURL.find( '/' );
+ if ( ( nSepPos == std::u16string_view::npos ) || ( nSepPos == sRelativeURL.size() - 1 ) )
+ {
+ m_eExtContentType = E_EXTENSION_ROOT;
+ }
+ else
+ {
+ m_eExtContentType = E_EXTENSION_CONTENT;
+ }
+ }
+
+ if ( m_eExtContentType == E_ROOT )
+ return;
+
+ const OUString sRootURL = ContentProvider::getRootURL();
+ m_sExtensionId = sURL.copy( sRootURL.getLength() );
+
+ const sal_Int32 nNextSep = m_sExtensionId.indexOf( '/' );
+ if ( nNextSep > -1 )
+ {
+ m_sPathIntoExtension = m_sExtensionId.copy( nNextSep + 1 );
+ m_sExtensionId = m_sExtensionId.copy( 0, nNextSep );
+ }
+ m_sExtensionId = Content::decodeIdentifier( m_sExtensionId );
+ }
+
+
+ Content::~Content()
+ {
+ }
+
+
+ OUString SAL_CALL Content::getImplementationName()
+ {
+ return "org.openoffice.comp.ucp.ext.Content";
+ }
+
+
+ Sequence< OUString > SAL_CALL Content::getSupportedServiceNames()
+ {
+ return { "com.sun.star.ucb.Content", "com.sun.star.ucb.ExtensionContent" };
+ }
+
+
+ OUString SAL_CALL Content::getContentType()
+ {
+ impl_determineContentType();
+ return *m_aContentType;
+ }
+
+
+ Any SAL_CALL Content::execute( const Command& aCommand, sal_Int32 /* CommandId */, const Reference< XCommandEnvironment >& i_rEnvironment )
+ {
+ Any aRet;
+
+ if ( aCommand.Name == "getPropertyValues" )
+ {
+ Sequence< Property > Properties;
+ if ( !( aCommand.Argument >>= Properties ) )
+ {
+ ::ucbhelper::cancelCommandExecution( Any( IllegalArgumentException(
+ OUString(), *this, -1 ) ),
+ i_rEnvironment );
+ // unreachable
+ }
+
+ aRet <<= getPropertyValues( Properties, i_rEnvironment );
+ }
+ else if ( aCommand.Name == "setPropertyValues" )
+ {
+ Sequence< PropertyValue > aProperties;
+ if ( !( aCommand.Argument >>= aProperties ) )
+ {
+ ::ucbhelper::cancelCommandExecution( Any( IllegalArgumentException(
+ OUString(), *this, -1 ) ),
+ i_rEnvironment );
+ // unreachable
+ }
+
+ if ( !aProperties.hasElements() )
+ {
+ ::ucbhelper::cancelCommandExecution( Any( IllegalArgumentException(
+ OUString(), *this, -1 ) ),
+ i_rEnvironment );
+ // unreachable
+ }
+
+ aRet <<= setPropertyValues( aProperties );
+ }
+ else if ( aCommand.Name == "getPropertySetInfo" )
+ {
+ // implemented by base class.
+ aRet <<= getPropertySetInfo( i_rEnvironment );
+ }
+ else if ( aCommand.Name == "getCommandInfo" )
+ {
+ // implemented by base class.
+ aRet <<= getCommandInfo( i_rEnvironment );
+ }
+ else if ( aCommand.Name == "open" )
+ {
+ OpenCommandArgument2 aOpenCommand;
+ if ( !( aCommand.Argument >>= aOpenCommand ) )
+ {
+ ::ucbhelper::cancelCommandExecution( Any( IllegalArgumentException(
+ OUString(), *this, -1 ) ),
+ i_rEnvironment );
+ // unreachable
+ }
+
+ bool bOpenFolder =
+ ( ( aOpenCommand.Mode == OpenMode::ALL ) ||
+ ( aOpenCommand.Mode == OpenMode::FOLDERS ) ||
+ ( aOpenCommand.Mode == OpenMode::DOCUMENTS ) );
+
+
+ if ( bOpenFolder && impl_isFolder() )
+ {
+ Reference< XDynamicResultSet > xSet = new ResultSet( m_xContext, this, aOpenCommand, i_rEnvironment );
+ aRet <<= xSet;
+ }
+
+ if ( aOpenCommand.Sink.is() )
+ {
+ const OUString sPhysicalContentURL( getPhysicalURL() );
+ ::ucbhelper::Content aRequestedContent( sPhysicalContentURL, i_rEnvironment, m_xContext );
+ aRet = aRequestedContent.executeCommand( "open", Any( aOpenCommand ) );
+ }
+ }
+
+ else
+ {
+ ::ucbhelper::cancelCommandExecution( Any( UnsupportedCommandException(
+ OUString(), *this ) ),
+ i_rEnvironment );
+ // unreachable
+ }
+
+ return aRet;
+ }
+
+
+ void SAL_CALL Content::abort( sal_Int32 )
+ {
+ }
+
+
+ OUString Content::encodeIdentifier( const OUString& i_rIdentifier )
+ {
+ return ::rtl::Uri::encode( i_rIdentifier, rtl_UriCharClassRegName, rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 );
+ }
+
+
+ OUString Content::decodeIdentifier( const OUString& i_rIdentifier )
+ {
+ return ::rtl::Uri::decode( i_rIdentifier, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8 );
+ }
+
+
+ bool Content::denotesRootContent( const OUString& i_rContentIdentifier )
+ {
+ const OUString sRootURL( ContentProvider::getRootURL() );
+ if ( i_rContentIdentifier == sRootURL )
+ return true;
+
+ // the root URL contains only two trailing /, but we also recognize 3 of them as denoting the root URL
+ if ( i_rContentIdentifier.match( sRootURL )
+ && ( i_rContentIdentifier.getLength() == sRootURL.getLength() + 1 )
+ && ( i_rContentIdentifier[ i_rContentIdentifier.getLength() - 1 ] == '/' )
+ )
+ return true;
+
+ return false;
+ }
+
+
+ OUString Content::getParentURL()
+ {
+ const OUString sRootURL( ContentProvider::getRootURL() );
+
+ switch ( m_eExtContentType )
+ {
+ case E_ROOT:
+ // don't have a parent
+ return sRootURL;
+
+ case E_EXTENSION_ROOT:
+ // our parent is the root itself
+ return sRootURL;
+
+ case E_EXTENSION_CONTENT:
+ {
+ const OUString sURL = m_xIdentifier->getContentIdentifier();
+
+ // cut the root URL
+ if ( !sURL.match( sRootURL ) )
+ {
+ SAL_INFO( "ucb.ucp.ext", "illegal URL structure - no root" );
+ break;
+ }
+
+ OUString sRelativeURL( sURL.copy( sRootURL.getLength() ) );
+
+ // cut the extension ID
+ const OUString sSeparatedExtensionId( encodeIdentifier( m_sExtensionId ) + "/" );
+ if ( !sRelativeURL.match( sSeparatedExtensionId ) )
+ {
+ SAL_INFO( "ucb.ucp.ext", "illegal URL structure - no extension ID" );
+ break;
+ }
+
+ sRelativeURL = sRelativeURL.copy( sSeparatedExtensionId.getLength() );
+
+ // cut the final slash (if any)
+ if ( sRelativeURL.isEmpty() )
+ {
+ SAL_INFO( "ucb.ucp.ext", "illegal URL structure - ExtensionContent should have a level below the extension ID" );
+ break;
+ }
+
+ if ( sRelativeURL.endsWith("/") )
+ sRelativeURL = sRelativeURL.copy( 0, sRelativeURL.getLength() - 1 );
+
+ // remove the last segment
+ const sal_Int32 nLastSep = sRelativeURL.lastIndexOf( '/' );
+ sRelativeURL = sRelativeURL.copy( 0, nLastSep != -1 ? nLastSep : 0 );
+
+ return sRootURL + sSeparatedExtensionId + sRelativeURL;
+ }
+
+ default:
+ OSL_FAIL( "Content::getParentURL: unhandled case!" );
+ break;
+ }
+ return OUString();
+ }
+
+
+ Reference< XRow > Content::getArtificialNodePropertyValues( const Reference< XComponentContext >& rxContext,
+ const Sequence< Property >& i_rProperties, const OUString& i_rTitle )
+ {
+ // note: empty sequence means "get values of all supported properties".
+ ::rtl::Reference< ::ucbhelper::PropertyValueSet > xRow = new ::ucbhelper::PropertyValueSet( rxContext );
+
+ if ( i_rProperties.hasElements() )
+ {
+ for ( const Property& rProp : i_rProperties )
+ {
+ // Process Core properties.
+ if ( rProp.Name == "ContentType" )
+ {
+ xRow->appendString ( rProp, ContentProvider::getArtificialNodeContentType() );
+ }
+ else if ( rProp.Name == "Title" )
+ {
+ xRow->appendString ( rProp, i_rTitle );
+ }
+ else if ( rProp.Name == "IsDocument" )
+ {
+ xRow->appendBoolean( rProp, false );
+ }
+ else if ( rProp.Name == "IsFolder" )
+ {
+ xRow->appendBoolean( rProp, true );
+ }
+ else
+ {
+ // append empty entry.
+ xRow->appendVoid( rProp );
+ }
+ }
+ }
+ else
+ {
+ // Append all Core Properties.
+ xRow->appendString ( Property( "ContentType",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ PropertyAttribute::BOUND | PropertyAttribute::READONLY ),
+ ContentProvider::getArtificialNodeContentType() );
+ xRow->appendString ( Property( "Title",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ PropertyAttribute::BOUND | PropertyAttribute::READONLY ),
+ i_rTitle );
+ xRow->appendBoolean( Property( "IsDocument",
+ -1,
+ cppu::UnoType<bool>::get(),
+ PropertyAttribute::BOUND | PropertyAttribute::READONLY ),
+ false );
+ xRow->appendBoolean( Property( "IsFolder",
+ -1,
+ cppu::UnoType<bool>::get(),
+ PropertyAttribute::BOUND | PropertyAttribute::READONLY ),
+ true );
+ }
+
+ return xRow;
+ }
+
+
+ OUString Content::getPhysicalURL() const
+ {
+ ENSURE_OR_RETURN( m_eExtContentType != E_ROOT, "illegal call", OUString() );
+
+ // create a ucb::XContent for the physical file within the deployed extension
+ const Reference< XPackageInformationProvider > xPackageInfo = PackageInformationProvider::get(m_xContext);
+ const OUString sPackageLocation( xPackageInfo->getPackageLocation( m_sExtensionId ) );
+
+ if ( m_sPathIntoExtension.isEmpty() )
+ return sPackageLocation;
+ return lcl_compose( sPackageLocation, m_sPathIntoExtension );
+ }
+
+
+ Reference< XRow > Content::getPropertyValues( const Sequence< Property >& i_rProperties, const Reference< XCommandEnvironment >& i_rEnv )
+ {
+ ::osl::Guard< ::osl::Mutex > aGuard( m_aMutex );
+
+ switch ( m_eExtContentType )
+ {
+ case E_ROOT:
+ return getArtificialNodePropertyValues( m_xContext, i_rProperties, ContentProvider::getRootURL() );
+ case E_EXTENSION_ROOT:
+ return getArtificialNodePropertyValues( m_xContext, i_rProperties, m_sExtensionId );
+ case E_EXTENSION_CONTENT:
+ {
+ const OUString sPhysicalContentURL( getPhysicalURL() );
+ ::ucbhelper::Content aRequestedContent( sPhysicalContentURL, i_rEnv, m_xContext );
+
+ // translate the property request
+ Sequence< OUString > aPropertyNames( i_rProperties.getLength() );
+ ::std::transform(
+ i_rProperties.begin(),
+ i_rProperties.end(),
+ aPropertyNames.getArray(),
+ SelectPropertyName()
+ );
+ const Sequence< Any > aPropertyValues = aRequestedContent.getPropertyValues( aPropertyNames );
+ const ::rtl::Reference< ::ucbhelper::PropertyValueSet > xValueRow = new ::ucbhelper::PropertyValueSet( m_xContext );
+ sal_Int32 i=0;
+ for ( const Any* value = aPropertyValues.getConstArray();
+ value != aPropertyValues.getConstArray() + aPropertyValues.getLength();
+ ++value, ++i
+ )
+ {
+ xValueRow->appendObject( aPropertyNames[i], *value );
+ }
+ return xValueRow;
+ }
+
+ default:
+ OSL_FAIL( "Content::getPropertyValues: unhandled case!" );
+ break;
+ }
+
+ OSL_FAIL( "Content::getPropertyValues: unreachable!" );
+ return nullptr;
+ }
+
+
+ Sequence< Any > Content::setPropertyValues( const Sequence< PropertyValue >& i_rValues)
+ {
+ ::osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
+
+ Sequence< Any > aRet( i_rValues.getLength() );
+
+ PropertyChangeEvent aEvent;
+ aEvent.Source = static_cast< cppu::OWeakObject * >( this );
+ aEvent.Further = false;
+ aEvent.PropertyHandle = -1;
+
+ for ( auto& rRet : asNonConstRange(aRet) )
+ {
+ // all our properties are read-only ...
+ rRet <<= IllegalAccessException("property is read-only.", *this );
+ }
+
+ return aRet;
+ }
+
+
+ Sequence< CommandInfo > Content::getCommands( const Reference< XCommandEnvironment > & /*xEnv*/ )
+ {
+ static const CommandInfo aCommandInfoTable[] =
+ {
+ // Mandatory commands
+
+ CommandInfo(
+ "getCommandInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ CommandInfo(
+ "getPropertySetInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ CommandInfo(
+ "getPropertyValues",
+ -1,
+ cppu::UnoType<Sequence< Property >>::get()
+ ),
+ CommandInfo(
+ "setPropertyValues",
+ -1,
+ cppu::UnoType<Sequence< PropertyValue >>::get()
+ )
+
+ // Optional standard commands
+
+ , CommandInfo(
+ "open",
+ -1,
+ cppu::UnoType<OpenCommandArgument2>::get()
+ )
+ };
+
+ return Sequence< CommandInfo >( aCommandInfoTable, SAL_N_ELEMENTS(aCommandInfoTable) );
+ }
+
+
+ Sequence< Property > Content::getProperties( const Reference< XCommandEnvironment > & /*xEnv*/ )
+ {
+ static const Property aProperties[] =
+ {
+ Property(
+ "ContentType",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ PropertyAttribute::BOUND | PropertyAttribute::READONLY
+ ),
+ Property(
+ "IsDocument",
+ -1,
+ cppu::UnoType<bool>::get(),
+ PropertyAttribute::BOUND | PropertyAttribute::READONLY
+ ),
+ Property(
+ "IsFolder",
+ -1,
+ cppu::UnoType<bool>::get(),
+ PropertyAttribute::BOUND | PropertyAttribute::READONLY
+ ),
+ Property(
+ "Title",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ PropertyAttribute::BOUND | PropertyAttribute::READONLY
+ )
+ };
+ return Sequence< Property >( aProperties, SAL_N_ELEMENTS( aProperties ) );
+ }
+
+
+ bool Content::impl_isFolder()
+ {
+ if ( !!m_aIsFolder )
+ return *m_aIsFolder;
+
+ bool bIsFolder = false;
+ try
+ {
+ Sequence< Property > aProps{ { /*Name*/ "IsFolder", {}, {}, {} } };
+ Reference< XRow > xRow( getPropertyValues( aProps, nullptr ), UNO_SET_THROW );
+ bIsFolder = xRow->getBoolean(1);
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("ucb.ucp.ext");
+ }
+ m_aIsFolder = bIsFolder;
+ return *m_aIsFolder;
+ }
+
+
+ void Content::impl_determineContentType()
+ {
+ if ( !!m_aContentType )
+ return;
+
+ m_aContentType = ContentProvider::getArtificialNodeContentType();
+ if ( m_eExtContentType != E_EXTENSION_CONTENT )
+ return;
+
+ try
+ {
+ Sequence< Property > aProps{ { /*Name*/ "ContentType", {}, {}, {} } };
+ Reference< XRow > xRow( getPropertyValues( aProps, nullptr ), UNO_SET_THROW );
+ m_aContentType = xRow->getString(1);
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("ucb.ucp.ext");
+ }
+ }
+
+
+} // namespace ucp::ext
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */