summaryrefslogtreecommitdiffstats
path: root/ucb/source/ucp/cmis/cmis_repo_content.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
commit940b4d1848e8c70ab7642901a68594e8016caffc (patch)
treeeb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /ucb/source/ucp/cmis/cmis_repo_content.cxx
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.zip
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ucb/source/ucp/cmis/cmis_repo_content.cxx')
-rw-r--r--ucb/source/ucp/cmis/cmis_repo_content.cxx424
1 files changed, 424 insertions, 0 deletions
diff --git a/ucb/source/ucp/cmis/cmis_repo_content.cxx b/ucb/source/ucp/cmis/cmis_repo_content.cxx
new file mode 100644
index 000000000..ead15fb1e
--- /dev/null
+++ b/ucb/source/ucp/cmis/cmis_repo_content.cxx
@@ -0,0 +1,424 @@
+/* -*- 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/.
+ */
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/ucb/XCommandInfo.hpp>
+#include <com/sun/star/ucb/XDynamicResultSet.hpp>
+#ifndef SYSTEM_CURL
+#include <com/sun/star/xml/crypto/XDigestContext.hpp>
+#include <com/sun/star/xml/crypto/DigestID.hpp>
+#include <com/sun/star/xml/crypto/NSSInitializer.hpp>
+#endif
+
+#include <config_oauth2.h>
+#include <rtl/uri.hxx>
+#include <sal/log.hxx>
+#include <tools/urlobj.hxx>
+#include <ucbhelper/cancelcommandexecution.hxx>
+#include <ucbhelper/contentidentifier.hxx>
+#include <ucbhelper/propertyvalueset.hxx>
+#include <ucbhelper/proxydecider.hxx>
+#include <ucbhelper/macros.hxx>
+
+#include "auth_provider.hxx"
+#include "certvalidation_handler.hxx"
+#include "cmis_content.hxx"
+#include "cmis_provider.hxx"
+#include "cmis_repo_content.hxx"
+#include "cmis_resultset.hxx"
+#include <memory>
+
+#define OUSTR_TO_STDSTR(s) std::string( OUStringToOString( s, RTL_TEXTENCODING_UTF8 ).getStr() )
+#define STD_TO_OUSTR( str ) OUString( str.c_str(), str.length( ), RTL_TEXTENCODING_UTF8 )
+
+using namespace com::sun::star;
+
+namespace cmis
+{
+ RepoContent::RepoContent( const uno::Reference< uno::XComponentContext >& rxContext,
+ ContentProvider *pProvider, const uno::Reference< ucb::XContentIdentifier >& Identifier,
+ std::vector< libcmis::RepositoryPtr > const & aRepos )
+ : ContentImplHelper( rxContext, pProvider, Identifier ),
+ m_pProvider( pProvider ),
+ m_aURL( Identifier->getContentIdentifier( ) ),
+ m_sRepositoryId( ),
+ m_aRepositories( aRepos )
+ {
+ // Split the URL into bits
+ OUString sURL = m_xIdentifier->getContentIdentifier( );
+ SAL_INFO( "ucb.ucp.cmis", "RepoContent::RepoContent() " << sURL );
+
+ m_sRepositoryId = m_aURL.getObjectPath();
+ if (!m_sRepositoryId.isEmpty() && m_sRepositoryId[0] == '/')
+ m_sRepositoryId = m_sRepositoryId.copy(1);
+ }
+
+ RepoContent::~RepoContent()
+ {
+ }
+
+ uno::Any RepoContent::getBadArgExcept()
+ {
+ return uno::makeAny( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ), -1) );
+ }
+
+ uno::Reference< sdbc::XRow > RepoContent::getPropertyValues(
+ const uno::Sequence< beans::Property >& rProperties,
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv )
+ {
+ rtl::Reference< ::ucbhelper::PropertyValueSet > xRow = new ::ucbhelper::PropertyValueSet( m_xContext );
+
+ for( const beans::Property& rProp : rProperties )
+ {
+ try
+ {
+ if ( rProp.Name == "IsDocument" )
+ {
+ xRow->appendBoolean( rProp, false );
+ }
+ else if ( rProp.Name == "IsFolder" )
+ {
+ xRow->appendBoolean( rProp, true );
+ }
+ else if ( rProp.Name == "Title" )
+ {
+ xRow->appendString( rProp, STD_TO_OUSTR( getRepository( xEnv )->getName( ) ) );
+ }
+ else if ( rProp.Name == "IsReadOnly" )
+ {
+ xRow->appendBoolean( rProp, true );
+ }
+ else
+ {
+ xRow->appendVoid( rProp );
+ SAL_INFO( "ucb.ucp.cmis", "Looking for unsupported property " << rProp.Name );
+ }
+ }
+ catch (const libcmis::Exception&)
+ {
+ xRow->appendVoid( rProp );
+ }
+ }
+
+ return uno::Reference< sdbc::XRow >( xRow.get() );
+ }
+
+ void RepoContent::getRepositories( const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+ {
+#ifndef SYSTEM_CURL
+ // Initialize NSS library to make sure libcmis (and curl) can access CACERTs using NSS
+ // when using internal libcurl.
+ uno::Reference< css::xml::crypto::XNSSInitializer >
+ xNSSInitializer = css::xml::crypto::NSSInitializer::create( m_xContext );
+
+ uno::Reference< css::xml::crypto::XDigestContext > xDigestContext(
+ xNSSInitializer->getDigestContext( css::xml::crypto::DigestID::SHA256,
+ uno::Sequence< beans::NamedValue >() ),
+ uno::UNO_SET_THROW );
+#endif
+
+ // Set the proxy if needed. We are doing that all times as the proxy data shouldn't be cached.
+ ucbhelper::InternetProxyDecider aProxyDecider( m_xContext );
+ INetURLObject aBindingUrl( m_aURL.getBindingUrl( ) );
+ const ucbhelper::InternetProxyServer& rProxy = aProxyDecider.getProxy(
+ INetURLObject::GetScheme( aBindingUrl.GetProtocol( ) ), aBindingUrl.GetHost(), aBindingUrl.GetPort() );
+ OUString sProxy = rProxy.aName;
+ if ( rProxy.nPort > 0 )
+ sProxy += ":" + OUString::number( rProxy.nPort );
+ libcmis::SessionFactory::setProxySettings( OUSTR_TO_STDSTR( sProxy ), std::string(), std::string(), std::string() );
+
+ if ( m_aRepositories.empty() )
+ {
+ // Set the SSL Validation handler
+ libcmis::CertValidationHandlerPtr certHandler(
+ new CertValidationHandler( xEnv, m_xContext, aBindingUrl.GetHost( ) ) );
+ libcmis::SessionFactory::setCertificateValidationHandler( certHandler );
+
+ // Get the auth credentials
+ AuthProvider authProvider( xEnv, m_xIdentifier->getContentIdentifier( ), m_aURL.getBindingUrl( ) );
+ AuthProvider::setXEnv( xEnv );
+
+ std::string rUsername = OUSTR_TO_STDSTR( m_aURL.getUsername( ) );
+ std::string rPassword = OUSTR_TO_STDSTR( m_aURL.getPassword( ) );
+
+ bool bIsDone = false;
+
+ while( !bIsDone )
+ {
+ if ( authProvider.authenticationQuery( rUsername, rPassword ) )
+ {
+ try
+ {
+ // Create a session to get repositories
+ libcmis::OAuth2DataPtr oauth2Data;
+ if ( m_aURL.getBindingUrl( ) == GDRIVE_BASE_URL )
+ {
+ libcmis::SessionFactory::setOAuth2AuthCodeProvider( AuthProvider::gdriveAuthCodeFallback );
+ oauth2Data.reset( new libcmis::OAuth2Data(
+ GDRIVE_AUTH_URL, GDRIVE_TOKEN_URL,
+ GDRIVE_SCOPE, GDRIVE_REDIRECT_URI,
+ GDRIVE_CLIENT_ID, GDRIVE_CLIENT_SECRET ) );
+ }
+ if ( m_aURL.getBindingUrl().startsWith( ALFRESCO_CLOUD_BASE_URL ) )
+ oauth2Data.reset( new libcmis::OAuth2Data(
+ ALFRESCO_CLOUD_AUTH_URL, ALFRESCO_CLOUD_TOKEN_URL,
+ ALFRESCO_CLOUD_SCOPE, ALFRESCO_CLOUD_REDIRECT_URI,
+ ALFRESCO_CLOUD_CLIENT_ID, ALFRESCO_CLOUD_CLIENT_SECRET ) );
+ if ( m_aURL.getBindingUrl( ) == ONEDRIVE_BASE_URL )
+ {
+ libcmis::SessionFactory::setOAuth2AuthCodeProvider( AuthProvider::onedriveAuthCodeFallback );
+ oauth2Data.reset( new libcmis::OAuth2Data(
+ ONEDRIVE_AUTH_URL, ONEDRIVE_TOKEN_URL,
+ ONEDRIVE_SCOPE, ONEDRIVE_REDIRECT_URI,
+ ONEDRIVE_CLIENT_ID, ONEDRIVE_CLIENT_SECRET ) );
+ }
+
+ std::unique_ptr<libcmis::Session> session(libcmis::SessionFactory::createSession(
+ OUSTR_TO_STDSTR( m_aURL.getBindingUrl( ) ),
+ rUsername, rPassword, "", false, oauth2Data ));
+ if (!session)
+ ucbhelper::cancelCommandExecution(
+ ucb::IOErrorCode_INVALID_DEVICE,
+ uno::Sequence< uno::Any >( 0 ),
+ xEnv );
+ m_aRepositories = session->getRepositories( );
+
+ bIsDone = true;
+ }
+ catch ( const libcmis::Exception& e )
+ {
+ SAL_INFO( "ucb.ucp.cmis", "Error getting repositories: " << e.what() );
+
+ if ( e.getType() != "permissionDenied" )
+ {
+ ucbhelper::cancelCommandExecution(
+ ucb::IOErrorCode_INVALID_DEVICE,
+ uno::Sequence< uno::Any >( 0 ),
+ xEnv );
+ }
+ }
+ }
+ else
+ {
+ // Throw user cancelled exception
+ ucbhelper::cancelCommandExecution(
+ ucb::IOErrorCode_ABORT,
+ uno::Sequence< uno::Any >( 0 ),
+ xEnv,
+ "Authentication cancelled" );
+ }
+ }
+ }
+ }
+
+ libcmis::RepositoryPtr RepoContent::getRepository( const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+ {
+ // Ensure we have the repositories extracted
+ getRepositories( xEnv );
+
+ libcmis::RepositoryPtr repo;
+
+ if ( !m_sRepositoryId.isEmpty() )
+ {
+ auto it = std::find_if(m_aRepositories.begin(), m_aRepositories.end(),
+ [&](const libcmis::RepositoryPtr& rRepo) { return STD_TO_OUSTR(rRepo->getId()) == m_sRepositoryId; });
+ if (it != m_aRepositories.end())
+ repo = *it;
+ }
+ else
+ repo = m_aRepositories.front( );
+ return repo;
+ }
+
+ uno::Sequence< beans::Property > RepoContent::getProperties(
+ const uno::Reference< ucb::XCommandEnvironment > & /*xEnv*/ )
+ {
+ static const beans::Property aGenericProperties[] =
+ {
+ beans::Property( "IsDocument",
+ -1, cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
+ beans::Property( "IsFolder",
+ -1, cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
+ beans::Property( "Title",
+ -1, cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND ),
+ beans::Property( "IsReadOnly",
+ -1, cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
+ };
+
+ const int nProps = SAL_N_ELEMENTS(aGenericProperties);
+ return uno::Sequence< beans::Property > ( aGenericProperties, nProps );
+ }
+
+ uno::Sequence< ucb::CommandInfo > RepoContent::getCommands(
+ const uno::Reference< ucb::XCommandEnvironment > & /*xEnv*/ )
+ {
+ static const ucb::CommandInfo aCommandInfoTable[] =
+ {
+ // Required commands
+ ucb::CommandInfo
+ ( "getCommandInfo",
+ -1, cppu::UnoType<void>::get() ),
+ ucb::CommandInfo
+ ( "getPropertySetInfo",
+ -1, cppu::UnoType<void>::get() ),
+ ucb::CommandInfo
+ ( "getPropertyValues",
+ -1, cppu::UnoType<uno::Sequence< beans::Property >>::get() ),
+ ucb::CommandInfo
+ ( "setPropertyValues",
+ -1, cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get() ),
+
+ // Optional standard commands
+ ucb::CommandInfo
+ ( "open",
+ -1, cppu::UnoType<ucb::OpenCommandArgument2>::get() ),
+ };
+
+ const int nProps = SAL_N_ELEMENTS(aCommandInfoTable);
+ return uno::Sequence< ucb::CommandInfo >(aCommandInfoTable, nProps );
+ }
+
+ OUString RepoContent::getParentURL( )
+ {
+ SAL_INFO( "ucb.ucp.cmis", "RepoContent::getParentURL()" );
+
+ // TODO Implement me
+
+ return OUString();
+ }
+
+ XTYPEPROVIDER_COMMON_IMPL( RepoContent );
+
+ OUString SAL_CALL RepoContent::getImplementationName()
+ {
+ return "com.sun.star.comp.CmisRepoContent";
+ }
+
+ uno::Sequence< OUString > SAL_CALL RepoContent::getSupportedServiceNames()
+ {
+ return { "com.sun.star.ucb.Content" };
+ }
+
+ OUString SAL_CALL RepoContent::getContentType()
+ {
+ return CMIS_REPO_TYPE;
+ }
+
+ uno::Any SAL_CALL RepoContent::execute(
+ const ucb::Command& aCommand,
+ sal_Int32 /*CommandId*/,
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv )
+ {
+ SAL_INFO( "ucb.ucp.cmis", "RepoContent::execute( ) - " << aCommand.Name );
+
+ uno::Any aRet;
+
+ if ( aCommand.Name == "getPropertyValues" )
+ {
+ uno::Sequence< beans::Property > Properties;
+ if ( !( aCommand.Argument >>= Properties ) )
+ ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv );
+ aRet <<= getPropertyValues( Properties, xEnv );
+ }
+ else if ( aCommand.Name == "getPropertySetInfo" )
+ aRet <<= getPropertySetInfo( xEnv, false );
+ else if ( aCommand.Name == "getCommandInfo" )
+ aRet <<= getCommandInfo( xEnv, false );
+ else if ( aCommand.Name == "open" )
+ {
+ ucb::OpenCommandArgument2 aOpenCommand;
+ if ( !( aCommand.Argument >>= aOpenCommand ) )
+ ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv );
+ const ucb::OpenCommandArgument2& rOpenCommand = aOpenCommand;
+
+ getRepositories( xEnv );
+ uno::Reference< ucb::XDynamicResultSet > xSet
+ = new DynamicResultSet(m_xContext, this, rOpenCommand, xEnv );
+ aRet <<= xSet;
+ }
+ else
+ {
+ SAL_INFO( "ucb.ucp.cmis", "Command not allowed" );
+ }
+
+ return aRet;
+ }
+
+ void SAL_CALL RepoContent::abort( sal_Int32 /*CommandId*/ )
+ {
+ SAL_INFO( "ucb.ucp.cmis", "TODO - RepoContent::abort()" );
+ // TODO Implement me
+ }
+
+ uno::Sequence< uno::Type > SAL_CALL RepoContent::getTypes()
+ {
+ static cppu::OTypeCollection s_aFolderCollection
+ (CPPU_TYPE_REF( lang::XTypeProvider ),
+ CPPU_TYPE_REF( lang::XServiceInfo ),
+ CPPU_TYPE_REF( lang::XComponent ),
+ CPPU_TYPE_REF( ucb::XContent ),
+ CPPU_TYPE_REF( ucb::XCommandProcessor ),
+ CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ),
+ CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ),
+ CPPU_TYPE_REF( beans::XPropertyContainer ),
+ CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ),
+ CPPU_TYPE_REF( container::XChild ) );
+ return s_aFolderCollection.getTypes();
+ }
+
+ std::vector< uno::Reference< ucb::XContent > > RepoContent::getChildren( )
+ {
+ std::vector< uno::Reference< ucb::XContent > > result;
+
+ // TODO Cache the results somehow
+ SAL_INFO( "ucb.ucp.cmis", "RepoContent::getChildren" );
+
+ if ( m_sRepositoryId.isEmpty( ) )
+ {
+ for ( const auto& rRepo : m_aRepositories )
+ {
+ URL aUrl( m_aURL );
+ aUrl.setObjectPath( STD_TO_OUSTR( rRepo->getId( ) ) );
+
+ uno::Reference< ucb::XContentIdentifier > xId = new ucbhelper::ContentIdentifier( aUrl.asString( ) );
+ uno::Reference< ucb::XContent > xContent = new RepoContent( m_xContext, m_pProvider, xId, m_aRepositories );
+
+ result.push_back( xContent );
+ }
+ }
+ else
+ {
+ // Return the repository root as child
+ OUString sUrl;
+ OUString sEncodedBinding = rtl::Uri::encode(
+ m_aURL.getBindingUrl( ) + "#" + m_sRepositoryId,
+ rtl_UriCharClassRelSegment,
+ rtl_UriEncodeKeepEscapes,
+ RTL_TEXTENCODING_UTF8 );
+ sUrl = "vnd.libreoffice.cmis://" + sEncodedBinding;
+
+ uno::Reference< ucb::XContentIdentifier > xId = new ucbhelper::ContentIdentifier( sUrl );
+ uno::Reference< ucb::XContent > xContent = new Content( m_xContext, m_pProvider, xId );
+
+ result.push_back( xContent );
+ }
+ return result;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */