From 267c6f2ac71f92999e969232431ba04678e7437e Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 15 Apr 2024 07:54:39 +0200 Subject: Adding upstream version 4:24.2.0. Signed-off-by: Daniel Baumann --- ucb/source/ucp/package/pkgcontent.cxx | 2689 ++++++++++++++++++++++++++++ ucb/source/ucp/package/pkgcontent.hxx | 276 +++ ucb/source/ucp/package/pkgcontentcaps.cxx | 504 ++++++ ucb/source/ucp/package/pkgdatasupplier.cxx | 425 +++++ ucb/source/ucp/package/pkgdatasupplier.hxx | 87 + ucb/source/ucp/package/pkgprovider.cxx | 272 +++ ucb/source/ucp/package/pkgprovider.hxx | 86 + ucb/source/ucp/package/pkgresultset.cxx | 79 + ucb/source/ucp/package/pkgresultset.hxx | 47 + ucb/source/ucp/package/pkguri.cxx | 234 +++ ucb/source/ucp/package/pkguri.hxx | 89 + ucb/source/ucp/package/ucppkg1.component | 26 + 12 files changed, 4814 insertions(+) create mode 100644 ucb/source/ucp/package/pkgcontent.cxx create mode 100644 ucb/source/ucp/package/pkgcontent.hxx create mode 100644 ucb/source/ucp/package/pkgcontentcaps.cxx create mode 100644 ucb/source/ucp/package/pkgdatasupplier.cxx create mode 100644 ucb/source/ucp/package/pkgdatasupplier.hxx create mode 100644 ucb/source/ucp/package/pkgprovider.cxx create mode 100644 ucb/source/ucp/package/pkgprovider.hxx create mode 100644 ucb/source/ucp/package/pkgresultset.cxx create mode 100644 ucb/source/ucp/package/pkgresultset.hxx create mode 100644 ucb/source/ucp/package/pkguri.cxx create mode 100644 ucb/source/ucp/package/pkguri.hxx create mode 100644 ucb/source/ucp/package/ucppkg1.component (limited to 'ucb/source/ucp/package') diff --git a/ucb/source/ucp/package/pkgcontent.cxx b/ucb/source/ucp/package/pkgcontent.cxx new file mode 100644 index 0000000000..de7b9c16ed --- /dev/null +++ b/ucb/source/ucp/package/pkgcontent.cxx @@ -0,0 +1,2689 @@ +/* -*- 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 . + */ + + +/************************************************************************** + TODO + ************************************************************************** + *************************************************************************/ +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pkgcontent.hxx" +#include "pkgprovider.hxx" +#include "pkgresultset.hxx" + +#include "../inc/urihelper.hxx" + +using namespace com::sun::star; +using namespace package_ucp; + +#define NONE_MODIFIED sal_uInt32( 0x00 ) +#define MEDIATYPE_MODIFIED sal_uInt32( 0x01 ) +#define COMPRESSED_MODIFIED sal_uInt32( 0x02 ) +#define ENCRYPTED_MODIFIED sal_uInt32( 0x04 ) +#define ENCRYPTIONKEY_MODIFIED sal_uInt32( 0x08 ) + + +// ContentProperties Implementation. + + +ContentProperties::ContentProperties( const OUString& rContentType ) +: aContentType( rContentType ), + nSize( 0 ), + bCompressed( true ), + bEncrypted( false ), + bHasEncryptedEntries( false ) +{ + bIsFolder = rContentType == PACKAGE_FOLDER_CONTENT_TYPE || rContentType == PACKAGE_ZIP_FOLDER_CONTENT_TYPE; + bIsDocument = !bIsFolder; + + OSL_ENSURE( bIsFolder || rContentType == PACKAGE_STREAM_CONTENT_TYPE || rContentType == PACKAGE_ZIP_STREAM_CONTENT_TYPE, + "ContentProperties::ContentProperties - Unknown type!" ); +} + + +uno::Sequence< ucb::ContentInfo > +ContentProperties::getCreatableContentsInfo( PackageUri const & rUri ) const +{ + if ( bIsFolder ) + { + uno::Sequence< beans::Property > aProps( 1 ); + aProps.getArray()[ 0 ] = beans::Property( + "Title", + -1, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND ); + + uno::Sequence< ucb::ContentInfo > aSeq( 2 ); + + // Folder. + aSeq.getArray()[ 0 ].Type + = Content::getContentType( rUri.getScheme(), true ); + aSeq.getArray()[ 0 ].Attributes + = ucb::ContentInfoAttribute::KIND_FOLDER; + aSeq.getArray()[ 0 ].Properties = aProps; + + // Stream. + aSeq.getArray()[ 1 ].Type + = Content::getContentType( rUri.getScheme(), false ); + aSeq.getArray()[ 1 ].Attributes + = ucb::ContentInfoAttribute::INSERT_WITH_INPUTSTREAM + | ucb::ContentInfoAttribute::KIND_DOCUMENT; + aSeq.getArray()[ 1 ].Properties = aProps; + + return aSeq; + } + else + { + return uno::Sequence< ucb::ContentInfo >( 0 ); + } +} + + +// Content Implementation. + + +// static ( "virtual" ctor ) +rtl::Reference Content::create( + const uno::Reference< uno::XComponentContext >& rxContext, + ContentProvider* pProvider, + const uno::Reference< ucb::XContentIdentifier >& Identifier ) +{ + OUString aURL = Identifier->getContentIdentifier(); + PackageUri aURI( aURL ); + ContentProperties aProps; + uno::Reference< container::XHierarchicalNameAccess > xPackage; + + if ( loadData( pProvider, aURI, aProps, xPackage ) ) + { + // resource exists + + sal_Int32 nLastSlash = aURL.lastIndexOf( '/' ); + if ( ( nLastSlash + 1 ) == aURL.getLength() ) + { + // Client explicitly requested a folder! + if ( !aProps.bIsFolder ) + return nullptr; + } + + uno::Reference< ucb::XContentIdentifier > xId + = new ::ucbhelper::ContentIdentifier( aURI.getUri() ); + return new Content( rxContext, pProvider, xId, xPackage, aURI, std::move(aProps) ); + } + else + { + // resource doesn't exist + + bool bFolder = false; + + // Guess type according to URI. + sal_Int32 nLastSlash = aURL.lastIndexOf( '/' ); + if ( ( nLastSlash + 1 ) == aURL.getLength() ) + bFolder = true; + + uno::Reference< ucb::XContentIdentifier > xId + = new ::ucbhelper::ContentIdentifier( aURI.getUri() ); + + ucb::ContentInfo aInfo; + if ( bFolder || aURI.isRootFolder() ) + aInfo.Type = getContentType( aURI.getScheme(), true ); + else + aInfo.Type = getContentType( aURI.getScheme(), false ); + + return new Content( rxContext, pProvider, xId, xPackage, aURI, aInfo ); + } +} + + +// static ( "virtual" ctor ) +rtl::Reference Content::create( + const uno::Reference< uno::XComponentContext >& rxContext, + ContentProvider* pProvider, + const uno::Reference< ucb::XContentIdentifier >& Identifier, + const ucb::ContentInfo& Info ) +{ + if ( Info.Type.isEmpty() ) + return nullptr; + + PackageUri aURI( Identifier->getContentIdentifier() ); + + if ( !Info.Type.equalsIgnoreAsciiCase( + getContentType( aURI.getScheme(), true ) ) && + !Info.Type.equalsIgnoreAsciiCase( + getContentType( aURI.getScheme(), false ) ) ) + return nullptr; + + uno::Reference< container::XHierarchicalNameAccess > xPackage = pProvider->createPackage( aURI ); + + uno::Reference< ucb::XContentIdentifier > xId + = new ::ucbhelper::ContentIdentifier( aURI.getUri() ); + return new Content( rxContext, pProvider, xId, xPackage, std::move(aURI), Info ); +} + + +// static +OUString Content::getContentType( + std::u16string_view aScheme, bool bFolder ) +{ + return ( OUString::Concat("application/") + + aScheme + + ( bFolder + ? std::u16string_view(u"-folder") + : std::u16string_view(u"-stream") ) ); +} + + +Content::Content( + const uno::Reference< uno::XComponentContext >& rxContext, + ContentProvider* pProvider, + const uno::Reference< ucb::XContentIdentifier >& Identifier, + uno::Reference< container::XHierarchicalNameAccess > Package, + PackageUri aUri, + ContentProperties aProps ) +: ContentImplHelper( rxContext, pProvider, Identifier ), + m_aUri(std::move( aUri )), + m_aProps(std::move( aProps )), + m_eState( PERSISTENT ), + m_xPackage(std::move( Package )), + m_pProvider( pProvider ), + m_nModifiedProps( NONE_MODIFIED ) +{ +} + + +Content::Content( + const uno::Reference< uno::XComponentContext >& rxContext, + ContentProvider* pProvider, + const uno::Reference< ucb::XContentIdentifier >& Identifier, + uno::Reference< container::XHierarchicalNameAccess > Package, + PackageUri aUri, + const ucb::ContentInfo& Info ) + : ContentImplHelper( rxContext, pProvider, Identifier ), + m_aUri(std::move( aUri )), + m_aProps( Info.Type ), + m_eState( TRANSIENT ), + m_xPackage(std::move( Package )), + m_pProvider( pProvider ), + m_nModifiedProps( NONE_MODIFIED ) +{ +} + + +// virtual +Content::~Content() +{ +} + + +// XInterface methods. + + +// virtual +void SAL_CALL Content::acquire() + noexcept +{ + ContentImplHelper::acquire(); +} + + +// virtual +void SAL_CALL Content::release() + noexcept +{ + ContentImplHelper::release(); +} + + +// virtual +uno::Any SAL_CALL Content::queryInterface( const uno::Type & rType ) +{ + uno::Any aRet; + + if ( isFolder() ) + aRet = cppu::queryInterface( + rType, static_cast< ucb::XContentCreator * >( this ) ); + + return aRet.hasValue() ? aRet : ContentImplHelper::queryInterface( rType ); +} + + +// XTypeProvider methods. + + +XTYPEPROVIDER_COMMON_IMPL( Content ); + + +// virtual +uno::Sequence< uno::Type > SAL_CALL Content::getTypes() +{ + if ( isFolder() ) + { + static cppu::OTypeCollection s_aFolderTypes( + 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 ), + CPPU_TYPE_REF( ucb::XContentCreator ) ); + + return s_aFolderTypes.getTypes(); + + } + else + { + static cppu::OTypeCollection s_aDocumentTypes( + 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_aDocumentTypes.getTypes(); + } +} + + +// XServiceInfo methods. + + +// virtual +OUString SAL_CALL Content::getImplementationName() +{ + return "com.sun.star.comp.ucb.PackageContent"; +} + + +// virtual +uno::Sequence< OUString > SAL_CALL Content::getSupportedServiceNames() +{ + return { isFolder()? OUString("com.sun.star.ucb.PackageFolderContent"):OUString("com.sun.star.ucb.PackageStreamContent") } ; +} + + +// XContent methods. + + +// virtual +OUString SAL_CALL Content::getContentType() +{ + return m_aProps.aContentType; +} + + +// XCommandProcessor methods. + + +// virtual +uno::Any SAL_CALL Content::execute( + const ucb::Command& aCommand, + sal_Int32 /*CommandId*/, + const uno::Reference< ucb::XCommandEnvironment >& Environment ) +{ + uno::Any aRet; + + if ( aCommand.Name == "getPropertyValues" ) + { + + // getPropertyValues + + + uno::Sequence< beans::Property > Properties; + if ( !( aCommand.Argument >>= Properties ) ) + { + ucbhelper::cancelCommandExecution( + uno::Any( lang::IllegalArgumentException( + "Wrong argument type!", + getXWeak(), + -1 ) ), + Environment ); + // Unreachable + } + + aRet <<= getPropertyValues( Properties ); + } + else if ( aCommand.Name == "setPropertyValues" ) + { + + // setPropertyValues + + + uno::Sequence< beans::PropertyValue > aProperties; + if ( !( aCommand.Argument >>= aProperties ) ) + { + ucbhelper::cancelCommandExecution( + uno::Any( lang::IllegalArgumentException( + "Wrong argument type!", + getXWeak(), + -1 ) ), + Environment ); + // Unreachable + } + + if ( !aProperties.hasElements() ) + { + ucbhelper::cancelCommandExecution( + uno::Any( lang::IllegalArgumentException( + "No properties!", + getXWeak(), + -1 ) ), + Environment ); + // Unreachable + } + + aRet <<= setPropertyValues( aProperties, Environment ); + } + else if ( aCommand.Name == "getPropertySetInfo" ) + { + + // getPropertySetInfo + + + // Note: Implemented by base class. + aRet <<= getPropertySetInfo( Environment ); + } + else if ( aCommand.Name == "getCommandInfo" ) + { + + // getCommandInfo + + + // Note: Implemented by base class. + aRet <<= getCommandInfo( Environment ); + } + else if ( aCommand.Name == "open" ) + { + + // open + + + ucb::OpenCommandArgument2 aOpenCommand; + if ( !( aCommand.Argument >>= aOpenCommand ) ) + { + ucbhelper::cancelCommandExecution( + uno::Any( lang::IllegalArgumentException( + "Wrong argument type!", + getXWeak(), + -1 ) ), + Environment ); + // Unreachable + } + + aRet = open( aOpenCommand, Environment ); + } + else if ( !m_aUri.isRootFolder() && aCommand.Name == "insert" ) + { + + // insert + + + ucb::InsertCommandArgument aArg; + if ( !( aCommand.Argument >>= aArg ) ) + { + ucbhelper::cancelCommandExecution( + uno::Any( lang::IllegalArgumentException( + "Wrong argument type!", + getXWeak(), + -1 ) ), + Environment ); + // Unreachable + } + + sal_Int32 nNameClash = aArg.ReplaceExisting + ? ucb::NameClash::OVERWRITE + : ucb::NameClash::ERROR; + insert( aArg.Data, nNameClash, Environment ); + } + else if ( !m_aUri.isRootFolder() && aCommand.Name == "delete" ) + { + + // delete + + + bool bDeletePhysical = false; + aCommand.Argument >>= bDeletePhysical; + destroy( bDeletePhysical, Environment ); + + // Remove own and all children's persistent data. + if ( !removeData() ) + { + uno::Sequence aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_WRITE, + aArgs, + Environment, + "Cannot remove persistent data!", + this ); + // Unreachable + } + + // Remove own and all children's Additional Core Properties. + removeAdditionalPropertySet(); + } + else if ( aCommand.Name == "transfer" ) + { + + // transfer + // ( Not available at stream objects ) + + + ucb::TransferInfo aInfo; + if ( !( aCommand.Argument >>= aInfo ) ) + { + ucbhelper::cancelCommandExecution( + uno::Any( lang::IllegalArgumentException( + "Wrong argument type!", + getXWeak(), + -1 ) ), + Environment ); + // Unreachable + } + + transfer( aInfo, Environment ); + } + else if ( aCommand.Name == "createNewContent" && isFolder() ) + { + + // createNewContent + // ( Not available at stream objects ) + + + ucb::ContentInfo aInfo; + if ( !( aCommand.Argument >>= aInfo ) ) + { + OSL_FAIL( "Wrong argument type!" ); + ucbhelper::cancelCommandExecution( + uno::Any( lang::IllegalArgumentException( + "Wrong argument type!", + getXWeak(), + -1 ) ), + Environment ); + // Unreachable + } + + aRet <<= createNewContent( aInfo ); + } + else if ( aCommand.Name == "flush" ) + { + + // flush + // ( Not available at stream objects ) + + + if( !flushData() ) + { + uno::Sequence aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_WRITE, + aArgs, + Environment, + "Cannot write file to disk!", + this ); + // Unreachable + } + } + else + { + + // Unsupported command + + + ucbhelper::cancelCommandExecution( + uno::Any( ucb::UnsupportedCommandException( + OUString(), + getXWeak() ) ), + Environment ); + // Unreachable + } + + return aRet; +} + + +// virtual +void SAL_CALL Content::abort( sal_Int32 /*CommandId*/ ) +{ + // @@@ Implement logic to abort running commands, if this makes + // sense for your content. +} + + +// XContentCreator methods. + + +// virtual +uno::Sequence< ucb::ContentInfo > SAL_CALL +Content::queryCreatableContentsInfo() +{ + return m_aProps.getCreatableContentsInfo( m_aUri ); +} + + +// virtual +uno::Reference< ucb::XContent > SAL_CALL +Content::createNewContent( const ucb::ContentInfo& Info ) +{ + if ( isFolder() ) + { + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + + if ( Info.Type.isEmpty() ) + return uno::Reference< ucb::XContent >(); + + if ( !Info.Type.equalsIgnoreAsciiCase( + getContentType( m_aUri.getScheme(), true ) ) && + !Info.Type.equalsIgnoreAsciiCase( + getContentType( m_aUri.getScheme(), false ) ) ) + return uno::Reference< ucb::XContent >(); + + OUString aURL = m_aUri.getUri() + "/"; + + if ( Info.Type.equalsIgnoreAsciiCase( + getContentType( m_aUri.getScheme(), true ) ) ) + aURL += "New_Folder"; + else + aURL += "New_Stream"; + + uno::Reference< ucb::XContentIdentifier > xId( + new ::ucbhelper::ContentIdentifier( aURL ) ); + + return create( m_xContext, m_pProvider, xId, Info ); + } + else + { + OSL_FAIL( "createNewContent called on non-folder object!" ); + return uno::Reference< ucb::XContent >(); + } +} + + +// Non-interface methods. + + +// virtual +OUString Content::getParentURL() +{ + return m_aUri.getParentUri(); +} + + +// static +uno::Reference< sdbc::XRow > Content::getPropertyValues( + const uno::Reference< uno::XComponentContext >& rxContext, + const uno::Sequence< beans::Property >& rProperties, + ContentProvider* pProvider, + const OUString& rContentId ) +{ + ContentProperties aData; + uno::Reference< container::XHierarchicalNameAccess > xPackage; + if ( loadData( pProvider, PackageUri( rContentId ), aData, xPackage ) ) + { + return getPropertyValues( rxContext, + rProperties, + aData, + rtl::Reference< + ::ucbhelper::ContentProviderImplHelper >( + pProvider ), + rContentId ); + } + else + { + rtl::Reference< ::ucbhelper::PropertyValueSet > xRow + = new ::ucbhelper::PropertyValueSet( rxContext ); + + for ( const beans::Property& rProp : rProperties ) + xRow->appendVoid( rProp ); + + return xRow; + } +} + + +// static +uno::Reference< sdbc::XRow > Content::getPropertyValues( + const uno::Reference< uno::XComponentContext >& rxContext, + const uno::Sequence< beans::Property >& rProperties, + const ContentProperties& rData, + const rtl::Reference< ::ucbhelper::ContentProviderImplHelper >& + rProvider, + const OUString& rContentId ) +{ + // Note: Empty sequence means "get values of all supported properties". + + rtl::Reference< ::ucbhelper::PropertyValueSet > xRow + = new ::ucbhelper::PropertyValueSet( rxContext ); + + if ( rProperties.hasElements() ) + { + uno::Reference< beans::XPropertySet > xAdditionalPropSet; + bool bTriedToGetAdditionalPropSet = false; + + for ( const beans::Property& rProp : rProperties ) + { + // Process Core properties. + + if ( rProp.Name == "ContentType" ) + { + xRow->appendString ( rProp, rData.aContentType ); + } + else if ( rProp.Name == "Title" ) + { + xRow->appendString ( rProp, rData.aTitle ); + } + else if ( rProp.Name == "IsDocument" ) + { + xRow->appendBoolean( rProp, rData.bIsDocument ); + } + else if ( rProp.Name == "IsFolder" ) + { + xRow->appendBoolean( rProp, rData.bIsFolder ); + } + else if ( rProp.Name == "CreatableContentsInfo" ) + { + xRow->appendObject( + rProp, uno::Any( + rData.getCreatableContentsInfo( + PackageUri( rContentId ) ) ) ); + } + else if ( rProp.Name == "MediaType" ) + { + xRow->appendString ( rProp, rData.aMediaType ); + } + else if ( rProp.Name == "Size" ) + { + // Property only available for streams. + if ( rData.bIsDocument ) + xRow->appendLong( rProp, rData.nSize ); + else + xRow->appendVoid( rProp ); + } + else if ( rProp.Name == "Compressed" ) + { + // Property only available for streams. + if ( rData.bIsDocument ) + xRow->appendBoolean( rProp, rData.bCompressed ); + else + xRow->appendVoid( rProp ); + } + else if ( rProp.Name == "Encrypted" ) + { + // Property only available for streams. + if ( rData.bIsDocument ) + xRow->appendBoolean( rProp, rData.bEncrypted ); + else + xRow->appendVoid( rProp ); + } + else if ( rProp.Name == "HasEncryptedEntries" ) + { + // Property only available for root folder. + PackageUri aURI( rContentId ); + if ( aURI.isRootFolder() ) + xRow->appendBoolean( rProp, rData.bHasEncryptedEntries ); + else + xRow->appendVoid( rProp ); + } + else + { + // Not a Core Property! Maybe it's an Additional Core Property?! + + if ( !bTriedToGetAdditionalPropSet && !xAdditionalPropSet.is() ) + { + xAdditionalPropSet = + rProvider->getAdditionalPropertySet( rContentId, + false ); + bTriedToGetAdditionalPropSet = true; + } + + if ( xAdditionalPropSet.is() ) + { + if ( !xRow->appendPropertySetValue( + xAdditionalPropSet, + rProp ) ) + { + // Append empty entry. + xRow->appendVoid( rProp ); + } + } + else + { + // Append empty entry. + xRow->appendVoid( rProp ); + } + } + } + } + else + { + // Append all Core Properties. + xRow->appendString ( + beans::Property( + "ContentType", + -1, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND + | beans::PropertyAttribute::READONLY ), + rData.aContentType ); + xRow->appendString( + beans::Property( + "Title", + -1, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND ), + rData.aTitle ); + xRow->appendBoolean( + beans::Property( + "IsDocument", + -1, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND + | beans::PropertyAttribute::READONLY ), + rData.bIsDocument ); + xRow->appendBoolean( + beans::Property( + "IsFolder", + -1, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND + | beans::PropertyAttribute::READONLY ), + rData.bIsFolder ); + xRow->appendObject( + beans::Property( + "CreatableContentsInfo", + -1, + cppu::UnoType>::get(), + beans::PropertyAttribute::BOUND + | beans::PropertyAttribute::READONLY ), + uno::Any( + rData.getCreatableContentsInfo( PackageUri( rContentId ) ) ) ); + xRow->appendString( + beans::Property( + "MediaType", + -1, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND ), + rData.aMediaType ); + + // Properties only available for streams. + if ( rData.bIsDocument ) + { + xRow->appendLong( + beans::Property( + "Size", + -1, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND + | beans::PropertyAttribute::READONLY ), + rData.nSize ); + + xRow->appendBoolean( + beans::Property( + "Compressed", + -1, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND ), + rData.bCompressed ); + + xRow->appendBoolean( + beans::Property( + "Encrypted", + -1, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND ), + rData.bEncrypted ); + } + + // Properties only available for root folder. + PackageUri aURI( rContentId ); + if ( aURI.isRootFolder() ) + { + xRow->appendBoolean( + beans::Property( + "HasEncryptedEntries", + -1, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND + | beans::PropertyAttribute::READONLY ), + rData.bHasEncryptedEntries ); + } + + // Append all Additional Core Properties. + + uno::Reference< beans::XPropertySet > xSet = + rProvider->getAdditionalPropertySet( rContentId, false ); + xRow->appendPropertySet( xSet ); + } + + return xRow; +} + + +uno::Reference< sdbc::XRow > Content::getPropertyValues( + const uno::Sequence< beans::Property >& rProperties ) +{ + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + return getPropertyValues( m_xContext, + rProperties, + m_aProps, + m_xProvider, + m_xIdentifier->getContentIdentifier() ); +} + + +uno::Sequence< uno::Any > Content::setPropertyValues( + const uno::Sequence< beans::PropertyValue >& rValues, + const uno::Reference< ucb::XCommandEnvironment > & xEnv ) +{ + osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex ); + + uno::Sequence< uno::Any > aRet( rValues.getLength() ); + auto aRetRange = asNonConstRange(aRet); + uno::Sequence< beans::PropertyChangeEvent > aChanges( rValues.getLength() ); + sal_Int32 nChanged = 0; + + beans::PropertyChangeEvent aEvent; + aEvent.Source = getXWeak(); + aEvent.Further = false; +// aEvent.PropertyName = + aEvent.PropertyHandle = -1; +// aEvent.OldValue = +// aEvent.NewValue = + + const beans::PropertyValue* pValues = rValues.getConstArray(); + sal_Int32 nCount = rValues.getLength(); + + uno::Reference< ucb::XPersistentPropertySet > xAdditionalPropSet; + bool bTriedToGetAdditionalPropSet = false; + bool bExchange = false; + bool bStore = false; + OUString aNewTitle; + sal_Int32 nTitlePos = -1; + + for ( sal_Int32 n = 0; n < nCount; ++n ) + { + const beans::PropertyValue& rValue = pValues[ n ]; + + if ( rValue.Name == "ContentType" ) + { + // Read-only property! + aRetRange[ n ] <<= lang::IllegalAccessException( + "Property is read-only!", + getXWeak() ); + } + else if ( rValue.Name == "IsDocument" ) + { + // Read-only property! + aRetRange[ n ] <<= lang::IllegalAccessException( + "Property is read-only!", + getXWeak() ); + } + else if ( rValue.Name == "IsFolder" ) + { + // Read-only property! + aRetRange[ n ] <<= lang::IllegalAccessException( + "Property is read-only!", + getXWeak() ); + } + else if ( rValue.Name == "CreatableContentsInfo" ) + { + // Read-only property! + aRetRange[ n ] <<= lang::IllegalAccessException( + "Property is read-only!", + getXWeak() ); + } + else if ( rValue.Name == "Title" ) + { + if ( m_aUri.isRootFolder() ) + { + // Read-only property! + aRetRange[ n ] <<= lang::IllegalAccessException( + "Property is read-only!", + getXWeak() ); + } + else + { + OUString aNewValue; + if ( rValue.Value >>= aNewValue ) + { + // No empty titles! + if ( !aNewValue.isEmpty() ) + { + if ( aNewValue != m_aProps.aTitle ) + { + // modified title -> modified URL -> exchange ! + if ( m_eState == PERSISTENT ) + bExchange = true; + + // new value will be set later... + aNewTitle = aNewValue; + + // remember position within sequence of values + // (for error handling). + nTitlePos = n; + } + } + else + { + aRetRange[ n ] <<= + lang::IllegalArgumentException( + "Empty title not allowed!", + getXWeak(), + -1 ); + } + } + else + { + aRetRange[ n ] <<= + beans::IllegalTypeException( + "Property value has wrong type!", + getXWeak() ); + } + } + } + else if ( rValue.Name == "MediaType" ) + { + OUString aNewValue; + if ( rValue.Value >>= aNewValue ) + { + if ( aNewValue != m_aProps.aMediaType ) + { + aEvent.PropertyName = rValue.Name; + aEvent.OldValue <<= m_aProps.aMediaType; + aEvent.NewValue <<= aNewValue; + + m_aProps.aMediaType = aNewValue; + nChanged++; + bStore = true; + m_nModifiedProps |= MEDIATYPE_MODIFIED; + } + } + else + { + aRetRange[ n ] <<= beans::IllegalTypeException( + "Property value has wrong type!", + getXWeak() ); + } + } + else if ( rValue.Name == "Size" ) + { + // Read-only property! + aRetRange[ n ] <<= lang::IllegalAccessException( + "Property is read-only!", + getXWeak() ); + } + else if ( rValue.Name == "Compressed" ) + { + // Property only available for streams. + if ( m_aProps.bIsDocument ) + { + bool bNewValue; + if ( rValue.Value >>= bNewValue ) + { + if ( bNewValue != m_aProps.bCompressed ) + { + aEvent.PropertyName = rValue.Name; + aEvent.OldValue <<= m_aProps.bCompressed; + aEvent.NewValue <<= bNewValue; + + m_aProps.bCompressed = bNewValue; + nChanged++; + bStore = true; + m_nModifiedProps |= COMPRESSED_MODIFIED; + } + } + else + { + aRetRange[ n ] <<= beans::IllegalTypeException( + "Property value has wrong type!", + getXWeak() ); + } + } + else + { + aRetRange[ n ] <<= beans::UnknownPropertyException( + "Compressed only supported by streams!", + getXWeak() ); + } + } + else if ( rValue.Name == "Encrypted" ) + { + // Property only available for streams. + if ( m_aProps.bIsDocument ) + { + bool bNewValue; + if ( rValue.Value >>= bNewValue ) + { + if ( bNewValue != m_aProps.bEncrypted ) + { + aEvent.PropertyName = rValue.Name; + aEvent.OldValue <<= m_aProps.bEncrypted; + aEvent.NewValue <<= bNewValue; + + m_aProps.bEncrypted = bNewValue; + nChanged++; + bStore = true; + m_nModifiedProps |= ENCRYPTED_MODIFIED; + } + } + else + { + aRetRange[ n ] <<= beans::IllegalTypeException( + "Property value has wrong type!", + getXWeak() ); + } + } + else + { + aRetRange[ n ] <<= beans::UnknownPropertyException( + "Encrypted only supported by streams!", + getXWeak() ); + } + } + else if ( rValue.Name == "HasEncryptedEntries" ) + { + // Read-only property! + aRetRange[ n ] <<= lang::IllegalAccessException( + "Property is read-only!", + getXWeak() ); + } + else if ( rValue.Name == "EncryptionKey" ) + { + // @@@ This is a temporary solution. In the future submitting + // the key should be done using an interaction handler! + + // Write-Only property. Only supported by root folder and streams + // (all non-root folders of a package have the same encryption key). + if ( m_aUri.isRootFolder() || m_aProps.bIsDocument ) + { + uno::Sequence < sal_Int8 > aNewValue; + if ( rValue.Value >>= aNewValue ) + { + if ( aNewValue != m_aProps.aEncryptionKey ) + { + aEvent.PropertyName = rValue.Name; + aEvent.OldValue <<= m_aProps.aEncryptionKey; + aEvent.NewValue <<= aNewValue; + + m_aProps.aEncryptionKey = aNewValue; + nChanged++; + bStore = true; + m_nModifiedProps |= ENCRYPTIONKEY_MODIFIED; + } + } + else + { + aRetRange[ n ] <<= beans::IllegalTypeException( + "Property value has wrong type!", + getXWeak() ); + } + } + else + { + aRetRange[ n ] <<= beans::UnknownPropertyException( + "EncryptionKey not supported by non-root folder!", + getXWeak() ); + } + } + else + { + // Not a Core Property! Maybe it's an Additional Core Property?! + + if ( !bTriedToGetAdditionalPropSet && !xAdditionalPropSet.is() ) + { + xAdditionalPropSet = getAdditionalPropertySet( false ); + bTriedToGetAdditionalPropSet = true; + } + + if ( xAdditionalPropSet.is() ) + { + try + { + uno::Any aOldValue + = xAdditionalPropSet->getPropertyValue( rValue.Name ); + if ( aOldValue != rValue.Value ) + { + xAdditionalPropSet->setPropertyValue( + rValue.Name, rValue.Value ); + + aEvent.PropertyName = rValue.Name; + aEvent.OldValue = aOldValue; + aEvent.NewValue = rValue.Value; + + aChanges.getArray()[ nChanged ] = aEvent; + nChanged++; + } + } + catch ( beans::UnknownPropertyException const & e ) + { + aRetRange[ n ] <<= e; + } + catch ( lang::WrappedTargetException const & e ) + { + aRetRange[ n ] <<= e; + } + catch ( beans::PropertyVetoException const & e ) + { + aRetRange[ n ] <<= e; + } + catch ( lang::IllegalArgumentException const & e ) + { + aRetRange[ n ] <<= e; + } + } + else + { + aRetRange[ n ] <<= uno::Exception( + "No property set for storing the value!", + getXWeak() ); + } + } + } + + if ( bExchange ) + { + uno::Reference< ucb::XContentIdentifier > xOldId = m_xIdentifier; + + // Assemble new content identifier... + OUString aNewURL = m_aUri.getParentUri() + "/" + + ::ucb_impl::urihelper::encodeSegment( aNewTitle ); + uno::Reference< ucb::XContentIdentifier > xNewId + = new ::ucbhelper::ContentIdentifier( aNewURL ); + + aGuard.clear(); + if ( exchangeIdentity( xNewId ) ) + { + // Adapt persistent data. + renameData( xOldId, xNewId ); + + // Adapt Additional Core Properties. + renameAdditionalPropertySet( xOldId->getContentIdentifier(), + xNewId->getContentIdentifier() ); + } + else + { + // Do not set new title! + aNewTitle.clear(); + + // Set error . + aRetRange[ nTitlePos ] <<= uno::Exception( + "Exchange failed!", + getXWeak() ); + } + } + + if ( !aNewTitle.isEmpty() ) + { + aEvent.PropertyName = "Title"; + aEvent.OldValue <<= m_aProps.aTitle; + aEvent.NewValue <<= aNewTitle; + + m_aProps.aTitle = aNewTitle; + + aChanges.getArray()[ nChanged ] = aEvent; + nChanged++; + } + + if ( nChanged > 0 ) + { + // Save changes, if content was already made persistent. + if ( ( m_nModifiedProps & ENCRYPTIONKEY_MODIFIED ) || + ( bStore && ( m_eState == PERSISTENT ) ) ) + { + if ( !storeData( uno::Reference< io::XInputStream >() ) ) + { + uno::Sequence aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_WRITE, + aArgs, + xEnv, + "Cannot store persistent data!", + this ); + // Unreachable + } + } + + aGuard.clear(); + aChanges.realloc( nChanged ); + notifyPropertiesChange( aChanges ); + } + + return aRet; +} + + +uno::Any Content::open( + const ucb::OpenCommandArgument2& rArg, + const uno::Reference< ucb::XCommandEnvironment >& xEnv ) +{ + if ( rArg.Mode == ucb::OpenMode::ALL || + rArg.Mode == ucb::OpenMode::FOLDERS || + rArg.Mode == ucb::OpenMode::DOCUMENTS ) + { + + // open command for a folder content + + + uno::Reference< ucb::XDynamicResultSet > xSet + = new DynamicResultSet( m_xContext, this, rArg, xEnv ); + return uno::Any( xSet ); + } + else + { + + // open command for a document content + + + if ( ( rArg.Mode == ucb::OpenMode::DOCUMENT_SHARE_DENY_NONE ) || + ( rArg.Mode == ucb::OpenMode::DOCUMENT_SHARE_DENY_WRITE ) ) + { + // Currently(?) unsupported. + ucbhelper::cancelCommandExecution( + uno::Any( ucb::UnsupportedOpenModeException( + OUString(), + getXWeak(), + sal_Int16( rArg.Mode ) ) ), + xEnv ); + // Unreachable + } + + uno::Reference< io::XOutputStream > xOut( rArg.Sink, uno::UNO_QUERY ); + if ( xOut.is() ) + { + // PUSH: write data into xOut + + uno::Reference< io::XInputStream > xIn = getInputStream(); + if ( !xIn.is() ) + { + // No interaction if we are not persistent! + uno::Sequence aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + aArgs, + m_eState == PERSISTENT + ? xEnv + : uno::Reference< ucb::XCommandEnvironment >(), + "Got no data stream!", + this ); + // Unreachable + } + + try + { + uno::Sequence< sal_Int8 > aBuffer; + while (true) + { + sal_Int32 nRead = xIn->readSomeBytes( aBuffer, 65536 ); + if (!nRead) + break; + aBuffer.realloc( nRead ); + xOut->writeBytes( aBuffer ); + } + + xOut->closeOutput(); + } + catch ( io::NotConnectedException const & ) + { + // closeOutput, readSomeBytes, writeBytes + } + catch ( io::BufferSizeExceededException const & ) + { + // closeOutput, readSomeBytes, writeBytes + } + catch ( io::IOException const & ) + { + // closeOutput, readSomeBytes, writeBytes + } + } + else + { + uno::Reference< io::XActiveDataSink > xDataSink( + rArg.Sink, uno::UNO_QUERY ); + if ( xDataSink.is() ) + { + // PULL: wait for client read + + uno::Reference< io::XInputStream > xIn = getInputStream(); + if ( !xIn.is() ) + { + // No interaction if we are not persistent! + uno::Sequence aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + aArgs, + m_eState == PERSISTENT + ? xEnv + : uno::Reference< + ucb::XCommandEnvironment >(), + "Got no data stream!", + this ); + // Unreachable + } + + // Done. + xDataSink->setInputStream( xIn ); + } + else + { + // Note: aOpenCommand.Sink may contain an XStream + // implementation. Support for this type of + // sink is optional... + ucbhelper::cancelCommandExecution( + uno::Any( + ucb::UnsupportedDataSinkException( + OUString(), + getXWeak(), + rArg.Sink ) ), + xEnv ); + // Unreachable + } + } + } + + return uno::Any(); +} + + +void Content::insert( + const uno::Reference< io::XInputStream >& xStream, + sal_Int32 nNameClashResolve, + const uno::Reference< ucb::XCommandEnvironment >& xEnv ) +{ + osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex ); + + // Check, if all required properties were set. + if ( isFolder() ) + { + // Required: Title + + if ( m_aProps.aTitle.isEmpty() ) + m_aProps.aTitle = m_aUri.getName(); + } + else + { + // Required: rArg.Data + + if ( !xStream.is() ) + { + ucbhelper::cancelCommandExecution( + uno::Any( ucb::MissingInputStreamException( + OUString(), + getXWeak() ) ), + xEnv ); + // Unreachable + } + + // Required: Title + + if ( m_aProps.aTitle.isEmpty() ) + m_aProps.aTitle = m_aUri.getName(); + } + + OUString aNewURL = m_aUri.getParentUri(); + if (1 + aNewURL.lastIndexOf('/') != aNewURL.getLength()) + aNewURL += "/"; + aNewURL += ::ucb_impl::urihelper::encodeSegment( m_aProps.aTitle ); + PackageUri aNewUri( aNewURL ); + + // Handle possible name clash... + switch ( nNameClashResolve ) + { + // fail. + case ucb::NameClash::ERROR: + if ( hasData( aNewUri ) ) + { + ucbhelper::cancelCommandExecution( + uno::Any( ucb::NameClashException( + OUString(), + getXWeak(), + task::InteractionClassification_ERROR, + m_aProps.aTitle ) ), + xEnv ); + // Unreachable + } + break; + + // replace (possibly) existing object. + case ucb::NameClash::OVERWRITE: + break; + + // "invent" a new valid title. + case ucb::NameClash::RENAME: + if ( hasData( aNewUri ) ) + { + sal_Int32 nTry = 0; + + do + { + OUString aNew = aNewUri.getUri() + "_" + OUString::number( ++nTry ); + aNewUri.setUri( aNew ); + } + while ( hasData( aNewUri ) && ( nTry < 1000 ) ); + + if ( nTry == 1000 ) + { + ucbhelper::cancelCommandExecution( + uno::Any( + ucb::UnsupportedNameClashException( + "Unable to resolve name clash!", + getXWeak(), + nNameClashResolve ) ), + xEnv ); + // Unreachable + } + else + { + m_aProps.aTitle += "_"; + m_aProps.aTitle += OUString::number( nTry ); + } + } + break; + + case ucb::NameClash::KEEP: // deprecated + case ucb::NameClash::ASK: + default: + if ( hasData( aNewUri ) ) + { + ucbhelper::cancelCommandExecution( + uno::Any( + ucb::UnsupportedNameClashException( + OUString(), + getXWeak(), + nNameClashResolve ) ), + xEnv ); + // Unreachable + } + break; + } + + // Identifier changed? + bool bNewId = ( m_aUri.getUri() != aNewUri.getUri() ); + + if ( bNewId ) + { + m_xIdentifier = new ::ucbhelper::ContentIdentifier( aNewURL ); + m_aUri = aNewUri; + } + + if ( !storeData( xStream ) ) + { + uno::Sequence aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_WRITE, + aArgs, + xEnv, + "Cannot store persistent data!", + this ); + // Unreachable + } + + m_eState = PERSISTENT; + + if ( bNewId ) + { + // Take over correct default values from underlying packager... + uno::Reference< container::XHierarchicalNameAccess > xXHierarchicalNameAccess; + loadData( m_pProvider, + m_aUri, + m_aProps, + xXHierarchicalNameAccess ); + + aGuard.clear(); + inserted(); + } +} + + +void Content::destroy( + bool bDeletePhysical, + const uno::Reference< ucb::XCommandEnvironment >& xEnv ) +{ + // @@@ take care about bDeletePhysical -> trashcan support + + osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex ); + + uno::Reference< ucb::XContent > xThis = this; + + // Persistent? + if ( m_eState != PERSISTENT ) + { + ucbhelper::cancelCommandExecution( + uno::Any( ucb::UnsupportedCommandException( + "Not persistent!", + getXWeak() ) ), + xEnv ); + // Unreachable + } + + m_eState = DEAD; + + aGuard.clear(); + deleted(); + + if ( isFolder() ) + { + // Process instantiated children... + + ContentRefList aChildren; + queryChildren( aChildren ); + + for ( auto& rChild : aChildren ) + { + rChild->destroy( bDeletePhysical, xEnv ); + } + } +} + + +void Content::transfer( + const ucb::TransferInfo& rInfo, + const uno::Reference< ucb::XCommandEnvironment > & xEnv ) +{ + osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex ); + + // Persistent? + if ( m_eState != PERSISTENT ) + { + ucbhelper::cancelCommandExecution( + uno::Any( ucb::UnsupportedCommandException( + "Not persistent!", + getXWeak() ) ), + xEnv ); + // Unreachable + } + + // Is source a package content? + if ( ( rInfo.SourceURL.isEmpty() ) || + ( rInfo.SourceURL.compareTo( + m_aUri.getUri(), PACKAGE_URL_SCHEME_LENGTH + 3 ) != 0 ) ) + { + ucbhelper::cancelCommandExecution( + uno::Any( ucb::InteractiveBadTransferURLException( + OUString(), + getXWeak() ) ), + xEnv ); + // Unreachable + } + + // Is source not a parent of me / not me? + OUString aId = m_aUri.getParentUri() + "/"; + + if ( rInfo.SourceURL.getLength() <= aId.getLength() ) + { + if ( aId.startsWith( rInfo.SourceURL ) ) + { + uno::Sequence aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(rInfo.SourceURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_RECURSIVE, + aArgs, + xEnv, + "Target is equal to or is a child of source!", + this ); + // Unreachable + } + } + + + // 0) Obtain content object for source. + + + uno::Reference< ucb::XContentIdentifier > xId + = new ::ucbhelper::ContentIdentifier( rInfo.SourceURL ); + + // Note: The static cast is okay here, because its sure that + // m_xProvider is always the PackageContentProvider. + rtl::Reference< Content > xSource; + + try + { + xSource = static_cast< Content * >( + m_xProvider->queryContent( xId ).get() ); + } + catch ( ucb::IllegalIdentifierException const & ) + { + // queryContent + } + + if ( !xSource.is() ) + { + uno::Sequence aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(xId->getContentIdentifier())} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + aArgs, + xEnv, + "Cannot instantiate source object!", + this ); + // Unreachable + } + + + // 1) Create new child content. + + + OUString aType = xSource->isFolder() + ? getContentType( m_aUri.getScheme(), true ) + : getContentType( m_aUri.getScheme(), false ); + ucb::ContentInfo aContentInfo; + aContentInfo.Type = aType; + aContentInfo.Attributes = 0; + + // Note: The static cast is okay here, because its sure that + // createNewContent always creates a Content. + rtl::Reference< Content > xTarget + = static_cast< Content * >( createNewContent( aContentInfo ).get() ); + if ( !xTarget.is() ) + { + uno::Sequence aArgs(comphelper::InitAnyPropertySequence( + { + {"Folder", uno::Any(aId)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_CREATE, + aArgs, + xEnv, + "XContentCreator::createNewContent failed!", + this ); + // Unreachable + } + + + // 2) Copy data from source content to child content. + + + uno::Sequence< beans::Property > aSourceProps + = xSource->getPropertySetInfo( xEnv )->getProperties(); + sal_Int32 nCount = aSourceProps.getLength(); + + if ( nCount ) + { + bool bHadTitle = rInfo.NewTitle.isEmpty(); + + // Get all source values. + uno::Reference< sdbc::XRow > xRow + = xSource->getPropertyValues( aSourceProps ); + + uno::Sequence< beans::PropertyValue > aValues( nCount ); + beans::PropertyValue* pValues = aValues.getArray(); + + const beans::Property* pProps = aSourceProps.getConstArray(); + for ( sal_Int32 n = 0; n < nCount; ++n ) + { + const beans::Property& rProp = pProps[ n ]; + beans::PropertyValue& rValue = pValues[ n ]; + + rValue.Name = rProp.Name; + rValue.Handle = rProp.Handle; + + if ( !bHadTitle && rProp.Name == "Title" ) + { + // Set new title instead of original. + bHadTitle = true; + rValue.Value <<= rInfo.NewTitle; + } + else + rValue.Value + = xRow->getObject( n + 1, + uno::Reference< + container::XNameAccess >() ); + + rValue.State = beans::PropertyState_DIRECT_VALUE; + + if ( rProp.Attributes & beans::PropertyAttribute::REMOVABLE ) + { + // Add Additional Core Property. + try + { + xTarget->addProperty( rProp.Name, + rProp.Attributes, + rValue.Value ); + } + catch ( beans::PropertyExistException const & ) + { + } + catch ( beans::IllegalTypeException const & ) + { + } + catch ( lang::IllegalArgumentException const & ) + { + } + } + } + + // Set target values. + xTarget->setPropertyValues( aValues, xEnv ); + } + + + // 3) Commit (insert) child. + + + xTarget->insert( xSource->getInputStream(), rInfo.NameClash, xEnv ); + + + // 4) Transfer (copy) children of source. + + + if ( xSource->isFolder() ) + { + uno::Reference< container::XEnumeration > xIter + = xSource->getIterator(); + if ( xIter.is() ) + { + while ( xIter->hasMoreElements() ) + { + try + { + uno::Reference< container::XNamed > xNamed; + xIter->nextElement() >>= xNamed; + + if ( !xNamed.is() ) + { + OSL_FAIL( "Content::transfer - Got no XNamed!" ); + break; + } + + OUString aName = xNamed->getName(); + + if ( aName.isEmpty() ) + { + OSL_FAIL( "Content::transfer - Empty name!" ); + break; + } + + OUString aChildId = xId->getContentIdentifier(); + if ( ( aChildId.lastIndexOf( '/' ) + 1 ) + != aChildId.getLength() ) + aChildId += "/"; + + aChildId += ::ucb_impl::urihelper::encodeSegment( aName ); + + ucb::TransferInfo aInfo; + aInfo.MoveData = false; + aInfo.NewTitle.clear(); + aInfo.SourceURL = aChildId; + aInfo.NameClash = rInfo.NameClash; + + // Transfer child to target. + xTarget->transfer( aInfo, xEnv ); + } + catch ( container::NoSuchElementException const & ) + { + } + catch ( lang::WrappedTargetException const & ) + { + } + } + } + } + + + // 5) Destroy source ( when moving only ) . + + + if ( !rInfo.MoveData ) + return; + + xSource->destroy( true, xEnv ); + + // Remove all persistent data of source and its children. + if ( !xSource->removeData() ) + { + uno::Sequence aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(xSource->m_xIdentifier->getContentIdentifier())} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_WRITE, + aArgs, + xEnv, + "Cannot remove persistent data of source object!", + this ); + // Unreachable + } + + // Remove own and all children's Additional Core Properties. + xSource->removeAdditionalPropertySet(); +} + + +bool Content::exchangeIdentity( + const uno::Reference< ucb::XContentIdentifier >& xNewId ) +{ + if ( !xNewId.is() ) + return false; + + osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex ); + + uno::Reference< ucb::XContent > xThis = this; + + // Already persistent? + if ( m_eState != PERSISTENT ) + { + OSL_FAIL( "Content::exchangeIdentity - Not persistent!" ); + return false; + } + + // Exchange own identity. + + // Fail, if a content with given id already exists. + PackageUri aNewUri( xNewId->getContentIdentifier() ); + if ( !hasData( aNewUri ) ) + { + OUString aOldURL = m_xIdentifier->getContentIdentifier(); + + aGuard.clear(); + if ( exchange( xNewId ) ) + { + m_aUri = aNewUri; + if ( isFolder() ) + { + // Process instantiated children... + + ContentRefList aChildren; + queryChildren( aChildren ); + + for ( const auto& rChild : aChildren ) + { + ContentRef xChild = rChild; + + // Create new content identifier for the child... + uno::Reference< ucb::XContentIdentifier > xOldChildId + = xChild->getIdentifier(); + OUString aOldChildURL + = xOldChildId->getContentIdentifier(); + OUString aNewChildURL + = aOldChildURL.replaceAt( + 0, + aOldURL.getLength(), + xNewId->getContentIdentifier() ); + uno::Reference< ucb::XContentIdentifier > xNewChildId + = new ::ucbhelper::ContentIdentifier( aNewChildURL ); + + if ( !xChild->exchangeIdentity( xNewChildId ) ) + return false; + } + } + return true; + } + } + + OSL_FAIL( "Content::exchangeIdentity - Panic! Cannot exchange identity!" ); + return false; +} + + +void Content::queryChildren( ContentRefList& rChildren ) +{ + // Obtain a list with a snapshot of all currently instantiated contents + // from provider and extract the contents which are direct children + // of this content. + + ::ucbhelper::ContentRefList aAllContents; + m_xProvider->queryExistingContents( aAllContents ); + + OUString aURL = m_xIdentifier->getContentIdentifier(); + + OSL_ENSURE( aURL.lastIndexOf( '/' ) != ( aURL.getLength() - 1 ), + "Content::queryChildren - Invalid URL!" ); + + aURL += "/"; + + sal_Int32 nLen = aURL.getLength(); + + for ( const auto& rContent : aAllContents ) + { + ::ucbhelper::ContentImplHelperRef xChild = rContent; + OUString aChildURL + = xChild->getIdentifier()->getContentIdentifier(); + + // Is aURL a prefix of aChildURL? + if ( ( aChildURL.getLength() > nLen ) && + ( aChildURL.startsWith( aURL ) ) ) + { + if ( aChildURL.indexOf( '/', nLen ) == -1 ) + { + // No further slashes. It's a child! + rChildren.emplace_back( + static_cast< Content * >( xChild.get() ) ); + } + } + } +} + + +uno::Reference< container::XHierarchicalNameAccess > Content::getPackage( + const PackageUri& rURI ) +{ + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + + if ( rURI.getPackage() == m_aUri.getPackage() ) + { + if ( !m_xPackage.is() ) + m_xPackage = m_pProvider->createPackage( m_aUri ); + + return m_xPackage; + } + + return m_pProvider->createPackage( rURI ); +} + + +uno::Reference< container::XHierarchicalNameAccess > Content::getPackage() +{ + return getPackage( m_aUri ); +} + + +// static +bool Content::hasData( + ContentProvider* pProvider, + const PackageUri& rURI, + uno::Reference< container::XHierarchicalNameAccess > & rxPackage ) +{ + rxPackage = pProvider->createPackage( rURI ); + return rxPackage->hasByHierarchicalName( rURI.getPath() ); +} + + +bool Content::hasData( const PackageUri& rURI ) +{ + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + + uno::Reference< container::XHierarchicalNameAccess > xPackage; + if ( rURI.getPackage() == m_aUri.getPackage() ) + { + xPackage = getPackage(); + return xPackage->hasByHierarchicalName( rURI.getPath() ); + } + + return hasData( m_pProvider, rURI, xPackage ); +} + + +//static +bool Content::loadData( + ContentProvider* pProvider, + const PackageUri& rURI, + ContentProperties& rProps, + uno::Reference< container::XHierarchicalNameAccess > & rxPackage ) +{ + rxPackage = pProvider->createPackage( rURI ); + + if ( rURI.isRootFolder() ) + { + // Properties available only from package + uno::Reference< beans::XPropertySet > xPackagePropSet( + rxPackage, uno::UNO_QUERY ); + + OSL_ENSURE( xPackagePropSet.is(), + "Content::loadData - " + "Got no XPropertySet interface from package!" ); + + if ( xPackagePropSet.is() ) + { + // HasEncryptedEntries (only available at root folder) + try + { + uno::Any aHasEncryptedEntries + = xPackagePropSet->getPropertyValue( "HasEncryptedEntries" ); + if ( !( aHasEncryptedEntries >>= rProps.bHasEncryptedEntries ) ) + { + OSL_FAIL( "Content::loadData - " + "Got no HasEncryptedEntries value!" ); + return false; + } + } + catch ( beans::UnknownPropertyException const & ) + { + OSL_FAIL( "Content::loadData - " + "Got no HasEncryptedEntries value!" ); + return false; + } + catch ( lang::WrappedTargetException const & ) + { + OSL_FAIL( "Content::loadData - " + "Got no HasEncryptedEntries value!" ); + return false; + } + } + } + + if ( !rxPackage->hasByHierarchicalName( rURI.getPath() ) ) + return false; + + try + { + uno::Any aEntry = rxPackage->getByHierarchicalName( rURI.getPath() ); + if ( aEntry.hasValue() ) + { + uno::Reference< beans::XPropertySet > xPropSet; + aEntry >>= xPropSet; + + if ( !xPropSet.is() ) + { + OSL_FAIL( "Content::loadData - Got no XPropertySet interface!" ); + return false; + } + + // Title + rProps.aTitle = rURI.getName(); + + // MediaType + try + { + uno::Any aMediaType = xPropSet->getPropertyValue("MediaType"); + if ( !( aMediaType >>= rProps.aMediaType ) ) + { + OSL_FAIL( "Content::loadData - Got no MediaType value!" ); + return false; + } + } + catch ( beans::UnknownPropertyException const & ) + { + OSL_FAIL( "Content::loadData - Got no MediaType value!" ); + return false; + } + catch ( lang::WrappedTargetException const & ) + { + OSL_FAIL( "Content::loadData - Got no MediaType value!" ); + return false; + } + + uno::Reference< container::XEnumerationAccess > xEnumAccess; + aEntry >>= xEnumAccess; + + // ContentType / IsFolder / IsDocument + if ( xEnumAccess.is() ) + { + // folder + rProps.aContentType = getContentType( rURI.getScheme(), true ); + rProps.bIsDocument = false; + rProps.bIsFolder = true; + } + else + { + // stream + rProps.aContentType = getContentType( rURI.getScheme(), false ); + rProps.bIsDocument = true; + rProps.bIsFolder = false; + } + + if ( rProps.bIsDocument ) + { + // Size ( only available for streams ) + try + { + uno::Any aSize = xPropSet->getPropertyValue("Size"); + if ( !( aSize >>= rProps.nSize ) ) + { + OSL_FAIL( "Content::loadData - Got no Size value!" ); + return false; + } + } + catch ( beans::UnknownPropertyException const & ) + { + OSL_FAIL( "Content::loadData - Got no Size value!" ); + return false; + } + catch ( lang::WrappedTargetException const & ) + { + OSL_FAIL( "Content::loadData - Got no Size value!" ); + return false; + } + + // Compressed ( only available for streams ) + try + { + uno::Any aCompressed = xPropSet->getPropertyValue("Compressed"); + if ( !( aCompressed >>= rProps.bCompressed ) ) + { + OSL_FAIL( "Content::loadData - Got no Compressed value!" ); + return false; + } + } + catch ( beans::UnknownPropertyException const & ) + { + OSL_FAIL( "Content::loadData - Got no Compressed value!" ); + return false; + } + catch ( lang::WrappedTargetException const & ) + { + OSL_FAIL( "Content::loadData - Got no Compressed value!" ); + return false; + } + + // Encrypted ( only available for streams ) + try + { + uno::Any aEncrypted = xPropSet->getPropertyValue("Encrypted"); + if ( !( aEncrypted >>= rProps.bEncrypted ) ) + { + OSL_FAIL( "Content::loadData - Got no Encrypted value!" ); + return false; + } + } + catch ( beans::UnknownPropertyException const & ) + { + OSL_FAIL( "Content::loadData - Got no Encrypted value!" ); + return false; + } + catch ( lang::WrappedTargetException const & ) + { + OSL_FAIL( "Content::loadData - Got no Encrypted value!" ); + return false; + } + } + return true; + } + } + catch ( container::NoSuchElementException const & ) + { + // getByHierarchicalName + } + + return false; +} + + +void Content::renameData( + const uno::Reference< ucb::XContentIdentifier >& xOldId, + const uno::Reference< ucb::XContentIdentifier >& xNewId ) +{ + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + + PackageUri aURI( xOldId->getContentIdentifier() ); + uno::Reference< container::XHierarchicalNameAccess > xNA = getPackage( + aURI ); + + if ( !xNA->hasByHierarchicalName( aURI.getPath() ) ) + return; + + try + { + uno::Any aEntry = xNA->getByHierarchicalName( aURI.getPath() ); + uno::Reference< container::XNamed > xNamed; + aEntry >>= xNamed; + + if ( !xNamed.is() ) + { + OSL_FAIL( "Content::renameData - Got no XNamed interface!" ); + return; + } + + PackageUri aNewURI( xNewId->getContentIdentifier() ); + + // No success indicator!? No return value / exceptions specified. + xNamed->setName( aNewURI.getName() ); + } + catch ( container::NoSuchElementException const & ) + { + // getByHierarchicalName + } +} + + +bool Content::storeData( const uno::Reference< io::XInputStream >& xStream ) +{ + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + + uno::Reference< container::XHierarchicalNameAccess > xNA = getPackage(); + + uno::Reference< beans::XPropertySet > xPackagePropSet( + xNA, uno::UNO_QUERY ); + OSL_ENSURE( xPackagePropSet.is(), + "Content::storeData - " + "Got no XPropertySet interface from package!" ); + + if ( !xPackagePropSet.is() ) + return false; + + if ( m_nModifiedProps & ENCRYPTIONKEY_MODIFIED ) + { + if ( m_aUri.isRootFolder() ) + { + // Property available only from package and from streams (see below) + try + { + xPackagePropSet->setPropertyValue( + "EncryptionKey", + uno::Any( m_aProps.aEncryptionKey ) ); + m_nModifiedProps &= ~ENCRYPTIONKEY_MODIFIED; + } + catch ( beans::UnknownPropertyException const & ) + { + // setPropertyValue + } + catch ( beans::PropertyVetoException const & ) + { + // setPropertyValue + } + catch ( lang::IllegalArgumentException const & ) + { + // setPropertyValue + } + catch ( lang::WrappedTargetException const & ) + { + // setPropertyValue + } + } + } + + if ( !xNA->hasByHierarchicalName( m_aUri.getPath() ) ) + { +// if ( !bCreate ) +// return sal_True; + + try + { + // Create new resource... + uno::Reference< lang::XSingleServiceFactory > xFac( + xNA, uno::UNO_QUERY ); + if ( !xFac.is() ) + { + OSL_FAIL( "Content::storeData - " + "Got no XSingleServiceFactory interface!" ); + return false; + } + + uno::Sequence< uno::Any > aArgs{ uno::Any(isFolder()) }; + + uno::Reference< uno::XInterface > xNew + = xFac->createInstanceWithArguments( aArgs ); + + if ( !xNew.is() ) + { + OSL_FAIL( "Content::storeData - createInstance failed!" ); + return false; + } + + PackageUri aParentUri( getParentURL() ); + uno::Any aEntry + = xNA->getByHierarchicalName( aParentUri.getPath() ); + uno::Reference< container::XNameContainer > xParentContainer; + aEntry >>= xParentContainer; + + if ( !xParentContainer.is() ) + { + OSL_FAIL( "Content::storeData - " + "Got no XNameContainer interface!" ); + return false; + } + + xParentContainer->insertByName( m_aProps.aTitle, + uno::Any( xNew ) ); + } + catch ( lang::IllegalArgumentException const & ) + { + // insertByName + OSL_FAIL( "Content::storeData - insertByName failed!" ); + return false; + } + catch ( uno::RuntimeException const & ) + { + throw; + } + catch ( container::ElementExistException const & ) + { + // insertByName + OSL_FAIL( "Content::storeData - insertByName failed!" ); + return false; + } + catch ( lang::WrappedTargetException const & ) + { + // insertByName + OSL_FAIL( "Content::storeData - insertByName failed!" ); + return false; + } + catch ( container::NoSuchElementException const & ) + { + // getByHierarchicalName + OSL_FAIL( "Content::storeData - getByHierarchicalName failed!" ); + return false; + } + catch ( uno::Exception const & ) + { + // createInstanceWithArguments + OSL_FAIL( "Content::storeData - Error!" ); + return false; + } + } + + if ( !xNA->hasByHierarchicalName( m_aUri.getPath() ) ) + return false; + + try + { + uno::Reference< beans::XPropertySet > xPropSet; + xNA->getByHierarchicalName( m_aUri.getPath() ) >>= xPropSet; + + if ( !xPropSet.is() ) + { + OSL_FAIL( "Content::storeData - Got no XPropertySet interface!" ); + return false; + } + + + // Store property values... + + + if ( m_nModifiedProps & MEDIATYPE_MODIFIED ) + { + xPropSet->setPropertyValue( + "MediaType", + uno::Any( m_aProps.aMediaType ) ); + m_nModifiedProps &= ~MEDIATYPE_MODIFIED; + } + + if ( m_nModifiedProps & COMPRESSED_MODIFIED ) + { + if ( !isFolder() ) + xPropSet->setPropertyValue( + "Compressed", + uno::Any( m_aProps.bCompressed ) ); + + m_nModifiedProps &= ~COMPRESSED_MODIFIED; + } + + if ( m_nModifiedProps & ENCRYPTED_MODIFIED ) + { + if ( !isFolder() ) + xPropSet->setPropertyValue( + "Encrypted", + uno::Any( m_aProps.bEncrypted ) ); + + m_nModifiedProps &= ~ENCRYPTED_MODIFIED; + } + + if ( m_nModifiedProps & ENCRYPTIONKEY_MODIFIED ) + { + if ( !isFolder() ) + xPropSet->setPropertyValue( + "EncryptionKey", + uno::Any( m_aProps.aEncryptionKey ) ); + + m_nModifiedProps &= ~ENCRYPTIONKEY_MODIFIED; + } + + + // Store data stream... + + + if ( xStream.is() && !isFolder() ) + { + uno::Reference< io::XActiveDataSink > xSink( + xPropSet, uno::UNO_QUERY ); + + if ( !xSink.is() ) + { + OSL_FAIL( "Content::storeData - " + "Got no XActiveDataSink interface!" ); + return false; + } + + xSink->setInputStream( xStream ); + } + + return true; + } + catch ( container::NoSuchElementException const & ) + { + // getByHierarchicalName + } + catch ( beans::UnknownPropertyException const & ) + { + // setPropertyValue + } + catch ( beans::PropertyVetoException const & ) + { + // setPropertyValue + } + catch ( lang::IllegalArgumentException const & ) + { + // setPropertyValue + } + catch ( lang::WrappedTargetException const & ) + { + // setPropertyValue + } + + OSL_FAIL( "Content::storeData - Error!" ); + return false; +} + + +bool Content::removeData() +{ + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + + uno::Reference< container::XHierarchicalNameAccess > xNA = getPackage(); + + PackageUri aParentUri( getParentURL() ); + if ( !xNA->hasByHierarchicalName( aParentUri.getPath() ) ) + return false; + + try + { + uno::Any aEntry = xNA->getByHierarchicalName( aParentUri.getPath() ); + uno::Reference< container::XNameContainer > xContainer; + aEntry >>= xContainer; + + if ( !xContainer.is() ) + { + OSL_FAIL( "Content::removeData - " + "Got no XNameContainer interface!" ); + return false; + } + + xContainer->removeByName( m_aUri.getName() ); + return true; + } + catch ( container::NoSuchElementException const & ) + { + // getByHierarchicalName, removeByName + } + catch ( lang::WrappedTargetException const & ) + { + // removeByName + } + + OSL_FAIL( "Content::removeData - Error!" ); + return false; +} + + +bool Content::flushData() +{ + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + + // Note: XChangesBatch is only implemented by the package itself, not + // by the single entries. Maybe this has to change... + + uno::Reference< container::XHierarchicalNameAccess > xNA = getPackage(); + + uno::Reference< util::XChangesBatch > xBatch( xNA, uno::UNO_QUERY ); + if ( !xBatch.is() ) + { + OSL_FAIL( "Content::flushData - Got no XChangesBatch interface!" ); + return false; + } + + try + { + xBatch->commitChanges(); + return true; + } + catch ( lang::WrappedTargetException const & ) + { + } + + OSL_FAIL( "Content::flushData - Error!" ); + return false; +} + + +uno::Reference< io::XInputStream > Content::getInputStream() +{ + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + + uno::Reference< io::XInputStream > xStream; + uno::Reference< container::XHierarchicalNameAccess > xNA = getPackage(); + + if ( !xNA->hasByHierarchicalName( m_aUri.getPath() ) ) + return xStream; + + try + { + uno::Any aEntry = xNA->getByHierarchicalName( m_aUri.getPath() ); + uno::Reference< io::XActiveDataSink > xSink; + aEntry >>= xSink; + + if ( !xSink.is() ) + { + OSL_FAIL( "Content::getInputStream - " + "Got no XActiveDataSink interface!" ); + return xStream; + } + + xStream = xSink->getInputStream(); + + OSL_ENSURE( xStream.is(), + "Content::getInputStream - Got no stream!" ); + } + catch ( container::NoSuchElementException const & ) + { + // getByHierarchicalName + } + + return xStream; +} + + +uno::Reference< container::XEnumeration > Content::getIterator() +{ + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + + uno::Reference< container::XEnumeration > xIter; + uno::Reference< container::XHierarchicalNameAccess > xNA = getPackage(); + + if ( !xNA->hasByHierarchicalName( m_aUri.getPath() ) ) + return xIter; + + try + { + uno::Any aEntry = xNA->getByHierarchicalName( m_aUri.getPath() ); + uno::Reference< container::XEnumerationAccess > xIterFac; + aEntry >>= xIterFac; + + if ( !xIterFac.is() ) + { + OSL_FAIL( "Content::getIterator - " + "Got no XEnumerationAccess interface!" ); + return xIter; + } + + xIter = xIterFac->createEnumeration(); + + OSL_ENSURE( xIter.is(), + "Content::getIterator - Got no iterator!" ); + } + catch ( container::NoSuchElementException const & ) + { + // getByHierarchicalName + } + + return xIter; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/ucp/package/pkgcontent.hxx b/ucb/source/ucp/package/pkgcontent.hxx new file mode 100644 index 0000000000..e1eebad50f --- /dev/null +++ b/ucb/source/ucp/package/pkgcontent.hxx @@ -0,0 +1,276 @@ +/* -*- 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 . + */ + +#pragma once + +#include + +#include +#include +#include + +#include +#include +#include "pkguri.hxx" + +namespace com::sun::star { + namespace beans + { + struct Property; + struct PropertyValue; + } + namespace container + { + class XHierarchicalNameAccess; + class XEnumeration; + } + namespace io + { + class XInputStream; + } + namespace sdbc + { + class XRow; + } + namespace ucb + { + struct OpenCommandArgument2; + struct TransferInfo; + } +} + +namespace package_ucp +{ + + +struct ContentProperties +{ + OUString aTitle; // Title + OUString aContentType; // ContentType + bool bIsDocument; // IsDocument + bool bIsFolder; // IsFolder + OUString aMediaType; // MediaType + css::uno::Sequence < sal_Int8 > aEncryptionKey; // EncryptionKey + sal_Int64 nSize; // Size + bool bCompressed; // Compressed + bool bEncrypted; // Encrypted + bool bHasEncryptedEntries; // HasEncryptedEntries + + ContentProperties() + : bIsDocument( true ), bIsFolder( false ), nSize( 0 ), + bCompressed( true ), bEncrypted( false ), + bHasEncryptedEntries( false ) {} + + explicit ContentProperties( const OUString& rContentType ); + + css::uno::Sequence< css::ucb::ContentInfo > + getCreatableContentsInfo( PackageUri const & rUri ) const; +}; + + +class ContentProvider; + +class Content : public ::ucbhelper::ContentImplHelper, + public css::ucb::XContentCreator +{ + enum ContentState { TRANSIENT, // created via CreateNewContent, + // but did not process "insert" yet + PERSISTENT, // processed "insert" + DEAD // processed "delete" + }; + + PackageUri m_aUri; + ContentProperties m_aProps; + ContentState m_eState; + css::uno::Reference< + css::container::XHierarchicalNameAccess > m_xPackage; + ContentProvider* m_pProvider; + sal_uInt32 m_nModifiedProps; + +private: + Content( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + ContentProvider* pProvider, + const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier, + css::uno::Reference< css::container::XHierarchicalNameAccess > Package, + PackageUri aUri, + ContentProperties aProps ); + Content( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + ContentProvider* pProvider, + const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier, + css::uno::Reference< css::container::XHierarchicalNameAccess > Package, + PackageUri aUri, + const css::ucb::ContentInfo& Info ); + + virtual css::uno::Sequence< css::beans::Property > + getProperties( const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv ) override; + virtual css::uno::Sequence< css::ucb::CommandInfo > + getCommands( const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv ) override; + virtual OUString getParentURL() override; + + static css::uno::Reference< css::sdbc::XRow > + getPropertyValues( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Sequence< css::beans::Property >& rProperties, + const ContentProperties& rData, + const rtl::Reference< ::ucbhelper::ContentProviderImplHelper >& rProvider, + const OUString& rContentId ); + + css::uno::Reference< css::sdbc::XRow > + getPropertyValues( const css::uno::Sequence< css::beans::Property >& rProperties ); + /// @throws css::uno::Exception + css::uno::Sequence< css::uno::Any > + setPropertyValues( const css::uno::Sequence< css::beans::PropertyValue >& rValues, + const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv ); + + css::uno::Reference< css::container::XHierarchicalNameAccess > + getPackage( const PackageUri& rURI ); + css::uno::Reference< css::container::XHierarchicalNameAccess > + getPackage(); + + static bool + loadData( ContentProvider* pProvider, + const PackageUri& rURI, + ContentProperties& rProps, + css::uno::Reference< css::container::XHierarchicalNameAccess > & rxPackage ); + static bool + hasData( ContentProvider* pProvider, + const PackageUri& rURI, + css::uno::Reference< css::container::XHierarchicalNameAccess > & rxPackage ); + + bool + hasData( const PackageUri& rURI ); + void + renameData( const css::uno::Reference< css::ucb::XContentIdentifier >& xOldId, + const css::uno::Reference< css::ucb::XContentIdentifier >& xNewId ); + bool + storeData( const css::uno::Reference< css::io::XInputStream >& xStream ); + bool + removeData(); + + bool + flushData(); + + typedef rtl::Reference< Content > ContentRef; + typedef std::vector< ContentRef > ContentRefList; + void queryChildren( ContentRefList& rChildren ); + + bool + exchangeIdentity( const css::uno::Reference< + css::ucb::XContentIdentifier >& xNewId ); + + /// @throws css::uno::Exception + css::uno::Any + open( const css::ucb::OpenCommandArgument2& rArg, + const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv ); + + /// @throws css::uno::Exception + void insert( const css::uno::Reference< css::io::XInputStream >& xStream, + sal_Int32 nNameClashResolve, + const css::uno::Reference< + css::ucb::XCommandEnvironment > & xEnv ); + + /// @throws css::uno::Exception + void destroy( bool bDeletePhysical, + const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv ); + + /// @throws css::uno::Exception + void transfer( const css::ucb::TransferInfo& rInfo, + const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv ); + + css::uno::Reference< css::io::XInputStream > + getInputStream(); + + bool isFolder() const { return m_aProps.bIsFolder; } + +public: + // Create existing content. Fail, if not already exists. + static rtl::Reference create( + const css::uno::Reference< css::uno::XComponentContext >& rxContext, + ContentProvider* pProvider, + const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier ); + + // Create new content. Fail, if already exists. + static rtl::Reference create( + const css::uno::Reference< css::uno::XComponentContext >& rxContext, + ContentProvider* pProvider, + const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier, + const css::ucb::ContentInfo& Info ); + + virtual ~Content() override; + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() + noexcept override; + virtual void SAL_CALL release() + noexcept override; + + // XTypeProvider + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + + // XServiceInfo + virtual OUString SAL_CALL + getImplementationName() override; + virtual css::uno::Sequence< OUString > SAL_CALL + getSupportedServiceNames() override; + + // XContent + virtual OUString SAL_CALL + getContentType() override; + + // XCommandProcessor + virtual css::uno::Any SAL_CALL + execute( const css::ucb::Command& aCommand, + sal_Int32 CommandId, + const css::uno::Reference< css::ucb::XCommandEnvironment >& Environment ) override; + virtual void SAL_CALL + abort( sal_Int32 CommandId ) override; + + + // Additional interfaces + + + // XContentCreator + virtual css::uno::Sequence< css::ucb::ContentInfo > SAL_CALL + queryCreatableContentsInfo() override; + virtual css::uno::Reference< css::ucb::XContent > SAL_CALL + createNewContent( const css::ucb::ContentInfo& Info ) override; + + + // Non-interface methods. + + + // Called from resultset data supplier. + static css::uno::Reference< css::sdbc::XRow > + getPropertyValues( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Sequence< css::beans::Property >& rProperties, + ContentProvider* pProvider, + const OUString& rContentId ); + + // Called from resultset data supplier. + css::uno::Reference< css::container::XEnumeration > + getIterator(); + + static OUString + getContentType( std::u16string_view aScheme, bool bFolder ); +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/ucp/package/pkgcontentcaps.cxx b/ucb/source/ucp/package/pkgcontentcaps.cxx new file mode 100644 index 0000000000..c01201d10a --- /dev/null +++ b/ucb/source/ucp/package/pkgcontentcaps.cxx @@ -0,0 +1,504 @@ +/* -*- 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 . + */ + + +/************************************************************************** + TODO + ************************************************************************** + + Props/Commands: + + rootfolder folder stream + --------------------------------------------- + ContentType r r r + IsDocument r r r + IsFolder r r r + MediaType (w) (w) w + Title r w w + Size - - r + CreatableContentsInfo r r r + Compressed - - w + Encrypted - - w + HasEncryptedEntries r - - + + getCommandInfo x x x + getPropertySetInfo x x x + getPropertyValues x x x + setPropertyValues x x x + insert - x x + delete - x x + open x x x + transfer x x - + flush x x - + createNewContent x x - + + *************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include "pkgcontent.hxx" + +using namespace com::sun::star; +using namespace package_ucp; + + +// Content implementation. + + +#define MAKEPROPSEQUENCE( a ) \ + uno::Sequence< beans::Property >( a, SAL_N_ELEMENTS( a ) ) + +#define MAKECMDSEQUENCE( a ) \ + uno::Sequence< ucb::CommandInfo >( a, SAL_N_ELEMENTS( a ) ) + + +// IMPORTANT: If any property data ( name / type / ... ) are changed, then +// Content::getPropertyValues(...) must be adapted too! + + +// virtual +uno::Sequence< beans::Property > Content::getProperties( + const uno::Reference< ucb::XCommandEnvironment > & /*xEnv*/ ) +{ + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + + if ( isFolder() ) + { + if ( m_aUri.isRootFolder() ) + { + + + // Root Folder: Supported properties + + + static const beans::Property aRootFolderPropertyInfoTable[] = + { + + // Required properties + + beans::Property( + "ContentType", + -1, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND + | beans::PropertyAttribute::READONLY + ), + beans::Property( + "IsDocument", + -1, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND + | beans::PropertyAttribute::READONLY + ), + beans::Property( + "IsFolder", + -1, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND + | beans::PropertyAttribute::READONLY + ), + beans::Property( + "Title", + -1, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND + | beans::PropertyAttribute::READONLY + ), + + // Optional standard properties + + beans::Property( + "MediaType", + -1, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND + ), + beans::Property( + "CreatableContentsInfo", + -1, + cppu::UnoType>::get(), + beans::PropertyAttribute::BOUND + | beans::PropertyAttribute::READONLY + ), + + // New properties + + beans::Property( + "HasEncryptedEntries", + -1, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND + | beans::PropertyAttribute::READONLY + ) + }; + return MAKEPROPSEQUENCE( aRootFolderPropertyInfoTable ); + } + else + { + + + // Folder: Supported properties + + + static const beans::Property aFolderPropertyInfoTable[] = + { + + // Required properties + + beans::Property( + "ContentType", + -1, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND + | beans::PropertyAttribute::READONLY + ), + beans::Property( + "IsDocument", + -1, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND + | beans::PropertyAttribute::READONLY + ), + beans::Property( + "IsFolder", + -1, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND + | beans::PropertyAttribute::READONLY + ), + beans::Property( + "Title", + -1, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND + ), + + // Optional standard properties + + beans::Property( + "MediaType", + -1, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND + ), + beans::Property( + "CreatableContentsInfo", + -1, + cppu::UnoType>::get(), + beans::PropertyAttribute::BOUND + | beans::PropertyAttribute::READONLY + ) + + // New properties + + }; + return MAKEPROPSEQUENCE( aFolderPropertyInfoTable ); + } + } + else + { + + + // Stream: Supported properties + + + static const beans::Property aStreamPropertyInfoTable[] = + { + + // Required properties + + beans::Property( + "ContentType", + -1, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND + | beans::PropertyAttribute::READONLY + ), + beans::Property( + "IsDocument", + -1, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND + | beans::PropertyAttribute::READONLY + ), + beans::Property( + "IsFolder", + -1, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND + | beans::PropertyAttribute::READONLY + ), + beans::Property( + "Title", + -1, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND + ), + + // Optional standard properties + + beans::Property( + "MediaType", + -1, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND + ), + beans::Property( + "Size", + -1, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND + | beans::PropertyAttribute::READONLY + ), + beans::Property( + "CreatableContentsInfo", + -1, + cppu::UnoType>::get(), + beans::PropertyAttribute::BOUND + | beans::PropertyAttribute::READONLY + ), + + // New properties + + beans::Property( + "Compressed", + -1, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND + ), + beans::Property( + "Encrypted", + -1, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND + ) + }; + return MAKEPROPSEQUENCE( aStreamPropertyInfoTable ); + } +} + + +// virtual +uno::Sequence< ucb::CommandInfo > Content::getCommands( + const uno::Reference< ucb::XCommandEnvironment > & /*xEnv*/ ) +{ + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + + if ( isFolder() ) + { + if ( m_aUri.isRootFolder() ) + { + + + // Root Folder: Supported commands + + + static const ucb::CommandInfo aRootFolderCommandInfoTable[] = + { + + // Required commands + + ucb::CommandInfo( + "getCommandInfo", + -1, + cppu::UnoType::get() + ), + ucb::CommandInfo( + "getPropertySetInfo", + -1, + cppu::UnoType::get() + ), + ucb::CommandInfo( + "getPropertyValues", + -1, + cppu::UnoType>::get() + ), + ucb::CommandInfo( + "setPropertyValues", + -1, + cppu::UnoType>::get() + ), + + // Optional standard commands + + ucb::CommandInfo( + "open", + -1, + cppu::UnoType::get() + ), + ucb::CommandInfo( + "transfer", + -1, + cppu::UnoType::get() + ), + ucb::CommandInfo( + "createNewContent", + -1, + cppu::UnoType::get() + ), + + // New commands + + ucb::CommandInfo( + "flush", + -1, + cppu::UnoType::get() + ) + }; + + return MAKECMDSEQUENCE( aRootFolderCommandInfoTable ); + } + else + { + + + // Folder: Supported commands + + + static const ucb::CommandInfo aFolderCommandInfoTable[] = + { + + // Required commands + + ucb::CommandInfo( + "getCommandInfo", + -1, + cppu::UnoType::get() + ), + ucb::CommandInfo( + "getPropertySetInfo", + -1, + cppu::UnoType::get() + ), + ucb::CommandInfo( + "getPropertyValues", + -1, + cppu::UnoType>::get() + ), + ucb::CommandInfo( + "setPropertyValues", + -1, + cppu::UnoType>::get() + ), + + // Optional standard commands + + ucb::CommandInfo( + "delete", + -1, + cppu::UnoType::get() + ), + ucb::CommandInfo( + "insert", + -1, + cppu::UnoType::get() + ), + ucb::CommandInfo( + "open", + -1, + cppu::UnoType::get() + ), + ucb::CommandInfo( + "transfer", + -1, + cppu::UnoType::get() + ), + ucb::CommandInfo( + "createNewContent", + -1, + cppu::UnoType::get() + ), + + // New commands + + ucb::CommandInfo( + "flush", + -1, + cppu::UnoType::get() + ) + }; + + return MAKECMDSEQUENCE( aFolderCommandInfoTable ); + } + } + else + { + + + // Stream: Supported commands + + + static const ucb::CommandInfo aStreamCommandInfoTable[] = + { + + // Required commands + + ucb::CommandInfo( + "getCommandInfo", + -1, + cppu::UnoType::get() + ), + ucb::CommandInfo( + "getPropertySetInfo", + -1, + cppu::UnoType::get() + ), + ucb::CommandInfo( + "getPropertyValues", + -1, + cppu::UnoType>::get() + ), + ucb::CommandInfo( + "setPropertyValues", + -1, + cppu::UnoType>::get() + ), + + // Optional standard commands + + ucb::CommandInfo( + "delete", + -1, + cppu::UnoType::get() + ), + ucb::CommandInfo( + "insert", + -1, + cppu::UnoType::get() + ), + ucb::CommandInfo( + "open", + -1, + cppu::UnoType::get() + ) + + // New commands + + }; + + return MAKECMDSEQUENCE( aStreamCommandInfoTable ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/ucp/package/pkgdatasupplier.cxx b/ucb/source/ucp/package/pkgdatasupplier.cxx new file mode 100644 index 0000000000..436f7023d3 --- /dev/null +++ b/ucb/source/ucp/package/pkgdatasupplier.cxx @@ -0,0 +1,425 @@ +/* -*- 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 . + */ + + +/************************************************************************** + TODO + ************************************************************************** + + *************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include "pkgdatasupplier.hxx" +#include "pkgcontent.hxx" +#include "pkgprovider.hxx" + +#include "../inc/urihelper.hxx" + +using namespace com::sun::star; +using namespace package_ucp; + +// DataSupplier Implementation. + + +DataSupplier::DataSupplier( + uno::Reference< uno::XComponentContext > xContext, + const rtl::Reference< Content >& rContent ) +: m_xContent( rContent ), m_xContext(std::move( xContext )), + m_xFolderEnum( rContent->getIterator() ), + m_bCountFinal( !m_xFolderEnum.is() ), m_bThrowException( m_bCountFinal ) +{ +} + + +// virtual +DataSupplier::~DataSupplier() +{ +} + + +// virtual +OUString DataSupplier::queryContentIdentifierString( sal_uInt32 nIndex ) +{ + std::unique_lock aGuard( m_aMutex ); + return queryContentIdentifierStringImpl(aGuard, nIndex); +} + +OUString DataSupplier::queryContentIdentifierStringImpl( std::unique_lock& rGuard, sal_uInt32 nIndex ) +{ + if ( nIndex < m_aResults.size() ) + { + OUString aId = m_aResults[ nIndex ].aURL; + if ( !aId.isEmpty() ) + { + // Already cached. + return aId; + } + } + + if ( getResultImpl( rGuard, nIndex ) ) + { + // Note: getResult fills m_aResults[ nIndex ].aURL. + return m_aResults[ nIndex ].aURL; + } + return OUString(); +} + + +// virtual +uno::Reference< ucb::XContentIdentifier > +DataSupplier::queryContentIdentifier( sal_uInt32 nIndex ) +{ + std::unique_lock aGuard( m_aMutex ); + return queryContentIdentifierImpl(aGuard, nIndex); +} + +uno::Reference< ucb::XContentIdentifier > +DataSupplier::queryContentIdentifierImpl( std::unique_lock& rGuard, sal_uInt32 nIndex ) +{ + if ( nIndex < m_aResults.size() ) + { + uno::Reference< ucb::XContentIdentifier >& xId + = m_aResults[ nIndex ].xId; + if ( xId.is() ) + { + // Already cached. + return xId; + } + } + + OUString aId = queryContentIdentifierStringImpl( rGuard, nIndex ); + if ( !aId.isEmpty() ) + { + uno::Reference< ucb::XContentIdentifier > xId + = new ::ucbhelper::ContentIdentifier( aId ); + m_aResults[ nIndex ].xId = xId; + return xId; + } + return uno::Reference< ucb::XContentIdentifier >(); +} + + +// virtual +uno::Reference< ucb::XContent > DataSupplier::queryContent( + sal_uInt32 nIndex ) +{ + std::unique_lock aGuard( m_aMutex ); + + if ( nIndex < m_aResults.size() ) + { + uno::Reference< ucb::XContent >& xContent + = m_aResults[ nIndex ].xContent; + if ( xContent.is() ) + { + // Already cached. + return xContent; + } + } + + uno::Reference< ucb::XContentIdentifier > xId + = queryContentIdentifierImpl( aGuard, nIndex ); + if ( xId.is() ) + { + try + { + uno::Reference< ucb::XContent > xContent + = m_xContent->getProvider()->queryContent( xId ); + m_aResults[ nIndex ].xContent = xContent; + return xContent; + + } + catch ( ucb::IllegalIdentifierException const & ) + { + } + } + return uno::Reference< ucb::XContent >(); +} + + +// virtual +bool DataSupplier::getResult( sal_uInt32 nIndex ) +{ + std::unique_lock aGuard( m_aMutex ); + return getResultImpl(aGuard, nIndex); +} + +bool DataSupplier::getResultImpl( std::unique_lock& rGuard, sal_uInt32 nIndex ) +{ + if ( m_aResults.size() > nIndex ) + { + // Result already present. + return true; + } + + // Result not (yet) present. + + if ( m_bCountFinal ) + return false; + + // Try to obtain result... + + sal_uInt32 nOldCount = m_aResults.size(); + bool bFound = false; + sal_uInt32 nPos = nOldCount; + + while ( m_xFolderEnum->hasMoreElements() ) + { + try + { + uno::Reference< container::XNamed > xNamed; + m_xFolderEnum->nextElement() >>= xNamed; + + if ( !xNamed.is() ) + { + OSL_FAIL( "DataSupplier::getResult - Got no XNamed!" ); + break; + } + + OUString aName = xNamed->getName(); + + if ( aName.isEmpty() ) + { + OSL_FAIL( "DataSupplier::getResult - Empty name!" ); + break; + } + + // Assemble URL for child. + OUString aURL = assembleChildURL( aName ); + + m_aResults.push_back( ResultListEntry( aURL ) ); + + if ( nPos == nIndex ) + { + // Result obtained. + bFound = true; + break; + } + + nPos++; + } + catch ( container::NoSuchElementException const & ) + { + m_bThrowException = true; + break; + } + catch ( lang::WrappedTargetException const & ) + { + m_bThrowException = true; + break; + } + } + + if ( !bFound ) + m_bCountFinal = true; + + rtl::Reference< ::ucbhelper::ResultSet > xResultSet = getResultSet(); + if ( xResultSet.is() ) + { + // Callbacks follow! + rGuard.unlock(); + + if ( nOldCount < m_aResults.size() ) + xResultSet->rowCountChanged( + nOldCount, m_aResults.size() ); + + if ( m_bCountFinal ) + xResultSet->rowCountFinal(); + + rGuard.lock(); + } + + return bFound; +} + + +// virtual +sal_uInt32 DataSupplier::totalCount() +{ + std::unique_lock aGuard( m_aMutex ); + + if ( m_bCountFinal ) + return m_aResults.size(); + + sal_uInt32 nOldCount = m_aResults.size(); + + while ( m_xFolderEnum->hasMoreElements() ) + { + try + { + uno::Reference< container::XNamed > xNamed; + m_xFolderEnum->nextElement() >>= xNamed; + + if ( !xNamed.is() ) + { + OSL_FAIL( "DataSupplier::getResult - Got no XNamed!" ); + break; + } + + OUString aName = xNamed->getName(); + + if ( aName.isEmpty() ) + { + OSL_FAIL( "DataSupplier::getResult - Empty name!" ); + break; + } + + // Assemble URL for child. + OUString aURL = assembleChildURL( aName ); + + m_aResults.push_back( ResultListEntry( aURL ) ); + } + catch ( container::NoSuchElementException const & ) + { + m_bThrowException = true; + break; + } + catch ( lang::WrappedTargetException const & ) + { + m_bThrowException = true; + break; + } + } + + m_bCountFinal = true; + + rtl::Reference< ::ucbhelper::ResultSet > xResultSet = getResultSet(); + if ( xResultSet.is() ) + { + // Callbacks follow! + aGuard.unlock(); + + if ( nOldCount < m_aResults.size() ) + xResultSet->rowCountChanged( + nOldCount, m_aResults.size() ); + + xResultSet->rowCountFinal(); + } + + return m_aResults.size(); +} + + +// virtual +sal_uInt32 DataSupplier::currentCount() +{ + return m_aResults.size(); +} + + +// virtual +bool DataSupplier::isCountFinal() +{ + return m_bCountFinal; +} + + +// virtual +uno::Reference< sdbc::XRow > DataSupplier::queryPropertyValues( + sal_uInt32 nIndex ) +{ + std::unique_lock aGuard( m_aMutex ); + + if ( nIndex < m_aResults.size() ) + { + uno::Reference< sdbc::XRow >& xRow = m_aResults[ nIndex ].xRow; + if ( xRow.is() ) + { + // Already cached. + return xRow; + } + } + + if ( getResultImpl( aGuard, nIndex ) ) + { + uno::Reference< sdbc::XRow > xRow = Content::getPropertyValues( + m_xContext, + getResultSet()->getProperties(), + static_cast< ContentProvider * >( + m_xContent->getProvider().get() ), + queryContentIdentifierStringImpl( aGuard, nIndex ) ); + m_aResults[ nIndex ].xRow = xRow; + return xRow; + } + + return uno::Reference< sdbc::XRow >(); +} + + +// virtual +void DataSupplier::releasePropertyValues( sal_uInt32 nIndex ) +{ + std::unique_lock aGuard( m_aMutex ); + + if ( nIndex < m_aResults.size() ) + m_aResults[ nIndex ].xRow.clear(); +} + + +// virtual +void DataSupplier::close() +{ +} + + +// virtual +void DataSupplier::validate() +{ + if ( m_bThrowException ) + throw ucb::ResultSetException(); +} + + +OUString DataSupplier::assembleChildURL( const OUString& aName ) +{ + OUString aURL; + OUString aContURL + = m_xContent->getIdentifier()->getContentIdentifier(); + sal_Int32 nParam = aContURL.indexOf( '?' ); + if ( nParam >= 0 ) + { + aURL = aContURL.copy( 0, nParam ); + + sal_Int32 nPackageUrlEnd = aURL.lastIndexOf( '/' ); + if ( nPackageUrlEnd != aURL.getLength() - 1 ) + aURL += "/"; + + aURL += ::ucb_impl::urihelper::encodeSegment( aName ) + + aContURL.subView( nParam ); + } + else + { + aURL = aContURL; + + sal_Int32 nPackageUrlEnd = aURL.lastIndexOf( '/' ); + if ( nPackageUrlEnd != aURL.getLength() - 1 ) + aURL += "/"; + + aURL += ::ucb_impl::urihelper::encodeSegment( aName ); + } + return aURL; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/ucp/package/pkgdatasupplier.hxx b/ucb/source/ucp/package/pkgdatasupplier.hxx new file mode 100644 index 0000000000..1872ef26e4 --- /dev/null +++ b/ucb/source/ucp/package/pkgdatasupplier.hxx @@ -0,0 +1,87 @@ +/* -*- 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 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace package_ucp { + +class Content; + +class DataSupplier : public ::ucbhelper::ResultSetDataSupplier +{ +public: + DataSupplier( css::uno::Reference< css::uno::XComponentContext > xContext, + const rtl::Reference< Content >& rContent ); + virtual ~DataSupplier() override; + + virtual OUString queryContentIdentifierString( sal_uInt32 nIndex ) override; + virtual css::uno::Reference< css::ucb::XContentIdentifier > + queryContentIdentifier( sal_uInt32 nIndex ) override; + virtual css::uno::Reference< css::ucb::XContent > + queryContent( sal_uInt32 nIndex ) override; + + virtual bool getResult( sal_uInt32 nIndex ) override; + + virtual sal_uInt32 totalCount() override; + virtual sal_uInt32 currentCount() override; + virtual bool isCountFinal() override; + + virtual css::uno::Reference< css::sdbc::XRow > + queryPropertyValues( sal_uInt32 nIndex ) override; + virtual void releasePropertyValues( sal_uInt32 nIndex ) override; + + virtual void close() override; + + virtual void validate() override; + + OUString assembleChildURL( const OUString& aName ); + +private: + bool getResultImpl( std::unique_lock&, sal_uInt32 nIndex ); + OUString queryContentIdentifierStringImpl( std::unique_lock&, sal_uInt32 nIndex ); + css::uno::Reference< css::ucb::XContentIdentifier > queryContentIdentifierImpl( std::unique_lock&, sal_uInt32 nIndex ); + + struct ResultListEntry + { + OUString aURL; + css::uno::Reference< css::ucb::XContentIdentifier > xId; + css::uno::Reference< css::ucb::XContent > xContent; + css::uno::Reference< css::sdbc::XRow > xRow; + + explicit ResultListEntry(OUString _aURL ) : aURL(std::move( _aURL )) {} + }; + std::mutex m_aMutex; + std::vector< ResultListEntry > m_aResults; + rtl::Reference< Content > m_xContent; + css::uno::Reference< css::uno::XComponentContext > m_xContext; + css::uno::Reference< css::container::XEnumeration > m_xFolderEnum; + bool m_bCountFinal; + bool m_bThrowException; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/ucp/package/pkgprovider.cxx b/ucb/source/ucp/package/pkgprovider.cxx new file mode 100644 index 0000000000..636ecb3333 --- /dev/null +++ b/ucb/source/ucp/package/pkgprovider.cxx @@ -0,0 +1,272 @@ +/* -*- 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 . + */ + + +/************************************************************************** + TODO + ************************************************************************** + + *************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pkgprovider.hxx" +#include "pkgcontent.hxx" +#include "pkguri.hxx" +#include +#include + +using namespace com::sun::star; + +namespace package_ucp +{ + + + +namespace { + +class Package : public cppu::OWeakObject, + public container::XHierarchicalNameAccess +{ + friend ContentProvider; + + OUString m_aName; + uno::Reference< container::XHierarchicalNameAccess > m_xNA; + ContentProvider* m_pOwner; + +public: + Package( OUString aName, + uno::Reference< container::XHierarchicalNameAccess > xNA, + ContentProvider* pOwner ) + : m_aName(std::move( aName )), m_xNA(std::move( xNA )), m_pOwner( pOwner ) {} + virtual ~Package() override { m_pOwner->removePackage( m_aName ); } + + // XInterface + virtual uno::Any SAL_CALL + queryInterface( const uno::Type& aType ) override + { return m_xNA->queryInterface( aType ); } + virtual void SAL_CALL + acquire() noexcept override + { OWeakObject::acquire(); } + virtual void SAL_CALL + release() noexcept override + { OWeakObject::release(); } + + // XHierarchicalNameAccess + virtual uno::Any SAL_CALL + getByHierarchicalName( const OUString& aName ) override + { return m_xNA->getByHierarchicalName( aName ); } + virtual sal_Bool SAL_CALL + hasByHierarchicalName( const OUString& aName ) override + { return m_xNA->hasByHierarchicalName( aName ); } +}; + +} + +class Packages : public std::unordered_map {}; + +} + +using namespace package_ucp; + + +// ContentProvider Implementation. +ContentProvider::ContentProvider( + const uno::Reference< uno::XComponentContext >& rxContext ) +: ::ucbhelper::ContentProviderImplHelper( rxContext ) +{ +} + + +// virtual +ContentProvider::~ContentProvider() +{ +} + +// XInterface methods. +void SAL_CALL ContentProvider::acquire() + noexcept +{ + OWeakObject::acquire(); +} + +void SAL_CALL ContentProvider::release() + noexcept +{ + OWeakObject::release(); +} + +css::uno::Any SAL_CALL ContentProvider::queryInterface( const css::uno::Type & rType ) +{ + css::uno::Any aRet = cppu::queryInterface( rType, + static_cast< lang::XTypeProvider* >(this), + static_cast< lang::XServiceInfo* >(this), + static_cast< ucb::XContentProvider* >(this) + ); + return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType ); +} + +// XTypeProvider methods. + + +XTYPEPROVIDER_IMPL_3( ContentProvider, + lang::XTypeProvider, + lang::XServiceInfo, + ucb::XContentProvider ); + + +// XServiceInfo methods. + +OUString +ContentProvider::getImplementationName() +{ + return "com.sun.star.comp.ucb.PackageContentProvider"; +} + +sal_Bool +ContentProvider::supportsService(const OUString& s) +{ + return cppu::supportsService(this, s); +} + +css::uno::Sequence< OUString > +ContentProvider::getSupportedServiceNames() +{ + return { "com.sun.star.ucb.PackageContentProvider" }; +} + + +// XContentProvider methods. + + +// virtual +uno::Reference< ucb::XContent > SAL_CALL ContentProvider::queryContent( + const uno::Reference< ucb::XContentIdentifier >& Identifier ) +{ + if ( !Identifier.is() ) + return uno::Reference< ucb::XContent >(); + + PackageUri aUri( Identifier->getContentIdentifier() ); + if ( !aUri.isValid() ) + throw ucb::IllegalIdentifierException(); + + // Create a new identifier for the normalized URL returned by + // PackageUri::getUri(). + uno::Reference< ucb::XContentIdentifier > xId = new ::ucbhelper::ContentIdentifier( aUri.getUri() ); + + osl::MutexGuard aGuard( m_aMutex ); + + // Check, if a content with given id already exists... + uno::Reference< ucb::XContent > xContent + = queryExistingContent( xId ); + if ( xContent.is() ) + return xContent; + + // Create a new content. + + xContent = Content::create( m_xContext, this, Identifier ); // not xId!!! + registerNewContent( xContent ); + + if ( xContent.is() && !xContent->getIdentifier().is() ) + throw ucb::IllegalIdentifierException(); + + return xContent; +} + + +// Other methods. + + +uno::Reference< container::XHierarchicalNameAccess > +ContentProvider::createPackage( const PackageUri & rURI ) +{ + osl::MutexGuard aGuard( m_aMutex ); + + OUString rURL = rURI.getPackage() + rURI.getParam(); + + if ( m_pPackages ) + { + Packages::const_iterator it = m_pPackages->find( rURL ); + if ( it != m_pPackages->end() ) + { + // Already instantiated. Return package. + return (*it).second->m_xNA; + } + } + else + m_pPackages.reset( new Packages ); + + // Create new package... + uno::Sequence< uno::Any > aArguments{ uno::Any(rURL) }; + uno::Reference< container::XHierarchicalNameAccess > xNameAccess; + try + { + xNameAccess.set( + m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.packages.comp.ZipPackage", + aArguments, m_xContext ), + css::uno::UNO_QUERY_THROW ); + } + catch ( uno::RuntimeException const & ) + { + throw; + } + catch ( uno::Exception const & e ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + e.Message, e.Context, anyEx); + } + + rtl::Reference< Package> xPackage = new Package( rURL, xNameAccess, this ); + (*m_pPackages)[ rURL ] = xPackage.get(); + return xPackage; +} + + +void ContentProvider::removePackage( const OUString & rName ) +{ + osl::MutexGuard aGuard( m_aMutex ); + + if ( m_pPackages ) + { + Packages::iterator it = m_pPackages->find( rName ); + if ( it != m_pPackages->end() ) + { + m_pPackages->erase( it ); + return; + } + } +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +ucb_package_ContentProvider_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence const&) +{ + return cppu::acquire(new ContentProvider(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/ucp/package/pkgprovider.hxx b/ucb/source/ucp/package/pkgprovider.hxx new file mode 100644 index 0000000000..37584e9734 --- /dev/null +++ b/ucb/source/ucp/package/pkgprovider.hxx @@ -0,0 +1,86 @@ +/* -*- 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 . + */ + +#pragma once + +#include +#include +#include "pkguri.hxx" + +namespace com::sun::star::container { + class XHierarchicalNameAccess; +} + +namespace package_ucp { + + +// UCB Content Type. +#define PACKAGE_FOLDER_CONTENT_TYPE \ + "application/" PACKAGE_URL_SCHEME "-folder" +#define PACKAGE_STREAM_CONTENT_TYPE \ + "application/" PACKAGE_URL_SCHEME "-stream" +#define PACKAGE_ZIP_FOLDER_CONTENT_TYPE \ + "application/" PACKAGE_ZIP_URL_SCHEME "-folder" +#define PACKAGE_ZIP_STREAM_CONTENT_TYPE \ + "application/" PACKAGE_ZIP_URL_SCHEME "-stream" + + +class Packages; + +class ContentProvider : public ::ucbhelper::ContentProviderImplHelper +{ + std::unique_ptr m_pPackages; + +public: + explicit ContentProvider( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + virtual ~ContentProvider() override; + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() + noexcept override; + virtual void SAL_CALL release() + noexcept override; + + // XTypeProvider + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XContentProvider + virtual css::uno::Reference< css::ucb::XContent > SAL_CALL + queryContent( const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier ) override; + + + // Non-interface methods. + + + css::uno::Reference< css::container::XHierarchicalNameAccess > + createPackage( const PackageUri & rParam ); + void + removePackage( const OUString & rName ); +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/ucp/package/pkgresultset.cxx b/ucb/source/ucp/package/pkgresultset.cxx new file mode 100644 index 0000000000..967625e9d3 --- /dev/null +++ b/ucb/source/ucp/package/pkgresultset.cxx @@ -0,0 +1,79 @@ +/* -*- 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 . + */ + + +/************************************************************************** + TODO + ************************************************************************** + + - This implementation is not a dynamic result set!!! It only implements + the necessary interfaces, but never recognizes/notifies changes!!! + + *************************************************************************/ +#include + +#include "pkgdatasupplier.hxx" +#include "pkgresultset.hxx" + +using namespace com::sun::star; + +using namespace package_ucp; + + +// DynamicResultSet Implementation. + + +DynamicResultSet::DynamicResultSet( + const uno::Reference< uno::XComponentContext >& rxContext, + rtl::Reference< Content > xContent, + const ucb::OpenCommandArgument2& rCommand, + uno::Reference< ucb::XCommandEnvironment > xEnv ) +: ResultSetImplHelper(rxContext, rCommand ), + m_xContent(std::move( xContent )), + m_xEnv(std::move( xEnv )) +{ +} + + +// Non-interface methods. + + +void DynamicResultSet::initStatic() +{ + m_xResultSet1 + = new ::ucbhelper::ResultSet( m_xContext, + m_aCommand.Properties, + new DataSupplier( m_xContext, + m_xContent ), + m_xEnv ); +} + + +void DynamicResultSet::initDynamic() +{ + m_xResultSet1 + = new ::ucbhelper::ResultSet( m_xContext, + m_aCommand.Properties, + new DataSupplier( m_xContext, + m_xContent ), + m_xEnv ); + m_xResultSet2 = m_xResultSet1; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/ucp/package/pkgresultset.hxx b/ucb/source/ucp/package/pkgresultset.hxx new file mode 100644 index 0000000000..c87a074233 --- /dev/null +++ b/ucb/source/ucp/package/pkgresultset.hxx @@ -0,0 +1,47 @@ +/* -*- 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 . + */ + +#pragma once + +#include +#include +#include "pkgcontent.hxx" + +namespace package_ucp { + +class DynamicResultSet : public ::ucbhelper::ResultSetImplHelper +{ + rtl::Reference< Content > m_xContent; + css::uno::Reference< css::ucb::XCommandEnvironment > m_xEnv; + +private: + virtual void initStatic() override; + virtual void initDynamic() override; + +public: + DynamicResultSet( + const css::uno::Reference< css::uno::XComponentContext >& rxContext, + rtl::Reference< Content > xContent, + const css::ucb::OpenCommandArgument2& rCommand, + css::uno::Reference< css::ucb::XCommandEnvironment > xEnv ); +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/ucp/package/pkguri.cxx b/ucb/source/ucp/package/pkguri.cxx new file mode 100644 index 0000000000..b1de54aa47 --- /dev/null +++ b/ucb/source/ucp/package/pkguri.cxx @@ -0,0 +1,234 @@ +/* -*- 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 . + */ + + +/************************************************************************** + TODO + ************************************************************************** + + *************************************************************************/ + +#include + +#include + +#include + +#include "../inc/urihelper.hxx" + +#include "pkguri.hxx" + +using namespace package_ucp; + + +// PackageUri Implementation. + + +static void normalize( OUString& rURL ) +{ + sal_Int32 nPos = 0; + do + { + nPos = rURL.indexOf( '%', nPos ); + if ( nPos != -1 ) + { + if ( nPos < ( rURL.getLength() - 2 ) ) + { + OUString aTmp = rURL.copy( nPos + 1, 2 ); + rURL = rURL.replaceAt( nPos + 1, 2, aTmp.toAsciiUpperCase() ); + nPos++; + } + } + } + while ( nPos != -1 ); +} + + +void PackageUri::init() const +{ + // Already inited? + if ( m_aUri.isEmpty() || !m_aPath.isEmpty() ) + return; + + // Note: Maybe it's a re-init, setUri only resets m_aPath! + m_aPackage.clear(); + m_aParentUri.clear(); + m_aName.clear(); + m_aParam.clear(); + m_aScheme.clear(); + + // URI must match at least: :// + if ( m_aUri.getLength() < PACKAGE_URL_SCHEME_LENGTH + 4 ) + { + // error, but remember that we did an init(). + m_aPath = "/"; + return; + } + + // Scheme must be followed by '://' + if ( ( m_aUri[ PACKAGE_URL_SCHEME_LENGTH ] != ':' ) + || + ( m_aUri[ PACKAGE_URL_SCHEME_LENGTH + 1 ] != '/' ) + || + ( m_aUri[ PACKAGE_URL_SCHEME_LENGTH + 2 ] != '/' ) ) + { + // error, but remember that we did an init(). + m_aPath = "/"; + return; + } + + OUString aPureUri; + sal_Int32 nParam = m_aUri.indexOf( '?' ); + if( nParam >= 0 ) + { + m_aParam = m_aUri.copy( nParam ); + aPureUri = m_aUri.copy( 0, nParam ); + } + else + aPureUri = m_aUri; + + // Scheme is case insensitive. + m_aScheme = aPureUri.copy( + 0, PACKAGE_URL_SCHEME_LENGTH ).toAsciiLowerCase(); + + if ( m_aScheme == PACKAGE_URL_SCHEME || m_aScheme == PACKAGE_ZIP_URL_SCHEME ) + { + if ( m_aScheme == PACKAGE_ZIP_URL_SCHEME ) + { + m_aParam += + ( !m_aParam.isEmpty() + ? std::u16string_view( u"&purezip" ) + : std::u16string_view( u"?purezip" ) ); + } + + aPureUri = aPureUri.replaceAt( 0, + m_aScheme.getLength(), + m_aScheme ); + + sal_Int32 nStart = PACKAGE_URL_SCHEME_LENGTH + 3; + sal_Int32 nEnd = aPureUri.lastIndexOf( '/' ); + if ( nEnd == PACKAGE_URL_SCHEME_LENGTH + 3 ) + { + // Only :/// - Empty authority + + // error, but remember that we did an init(). + m_aPath = "/"; + return; + } + else if ( nEnd == ( aPureUri.getLength() - 1 ) ) + { + if ( aPureUri[ aPureUri.getLength() - 2 ] == '/' ) + { + // Only ://// or :// + + // error, but remember that we did an init(). + m_aPath = "/"; + return; + } + + // Remove trailing slash. + aPureUri = aPureUri.copy( 0, nEnd ); + } + + + nEnd = aPureUri.indexOf( '/', nStart ); + if ( nEnd == -1 ) + { + // root folder. + + OUString aNormPackage = aPureUri.copy( nStart ); + normalize( aNormPackage ); + + aPureUri = aPureUri.replaceAt( + nStart, aPureUri.getLength() - nStart, aNormPackage ); + m_aPackage + = ::ucb_impl::urihelper::decodeSegment( aNormPackage ); + m_aPath = "/"; + m_aUri = m_aUri.replaceAt( 0, + ( nParam >= 0 ) + ? nParam + : m_aUri.getLength(), aPureUri ); + + sal_Int32 nLastSlash = m_aPackage.lastIndexOf( '/' ); + if ( nLastSlash != -1 ) + m_aName = ::ucb_impl::urihelper::decodeSegment( + m_aPackage.copy( nLastSlash + 1 ) ); + else + m_aName + = ::ucb_impl::urihelper::decodeSegment( m_aPackage ); + } + else + { + m_aPath = aPureUri.copy( nEnd + 1 ); + + // Unexpected sequences of characters: + // - empty path segments + // - encoded slashes + // - parent folder segments ".." + // - current folder segments "." + if ( m_aPath.indexOf( "//" ) != -1 + || m_aPath.indexOf( "%2F" ) != -1 + || m_aPath.indexOf( "%2f" ) != -1 + || ::comphelper::OStorageHelper::PathHasSegment( m_aPath, u".." ) + || ::comphelper::OStorageHelper::PathHasSegment( m_aPath, u"." ) ) + { + // error, but remember that we did an init(). + m_aPath = "/"; + return; + } + + OUString aNormPackage = aPureUri.copy( nStart, nEnd - nStart ); + normalize( aNormPackage ); + + aPureUri = aPureUri.replaceAt( + nStart, nEnd - nStart, aNormPackage ); + aPureUri = aPureUri.replaceAt( + nEnd + 1, + aPureUri.getLength() - nEnd - 1, + ::ucb_impl::urihelper::encodeURI( m_aPath ) ); + + m_aPackage + = ::ucb_impl::urihelper::decodeSegment( aNormPackage ); + m_aPath = ::ucb_impl::urihelper::decodeSegment( m_aPath ); + m_aUri = m_aUri.replaceAt( 0, + ( nParam >= 0 ) + ? nParam + : m_aUri.getLength(), aPureUri ); + + sal_Int32 nLastSlash = aPureUri.lastIndexOf( '/' ); + if ( nLastSlash != -1 ) + { + m_aParentUri = aPureUri.copy( 0, nLastSlash ); + m_aName = ::ucb_impl::urihelper::decodeSegment( + aPureUri.copy( nLastSlash + 1 ) ); + } + } + + // success + m_bValid = true; + } + else + { + // error, but remember that we did an init(). + m_aPath = "/"; + } + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/ucp/package/pkguri.hxx b/ucb/source/ucp/package/pkguri.hxx new file mode 100644 index 0000000000..4583a1afc8 --- /dev/null +++ b/ucb/source/ucp/package/pkguri.hxx @@ -0,0 +1,89 @@ +/* -*- 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 . + */ + +#pragma once + +#include +#include + +namespace package_ucp { + + +#define PACKAGE_URL_SCHEME "vnd.sun.star.pkg" +#define PACKAGE_ZIP_URL_SCHEME "vnd.sun.star.zip" +#define PACKAGE_URL_SCHEME_LENGTH 16 + + +class PackageUri +{ + mutable OUString m_aUri; + mutable OUString m_aParentUri; + mutable OUString m_aPackage; + mutable OUString m_aPath; + mutable OUString m_aName; + mutable OUString m_aParam; + mutable OUString m_aScheme; + mutable bool m_bValid; + +private: + void init() const; + +public: + explicit PackageUri( OUString aPackageUri ) + : m_aUri(std::move( aPackageUri )), m_bValid( false ) {} + + bool isValid() const + { init(); return m_bValid; } + + const OUString & getUri() const + { init(); return m_aUri; } + + void setUri( const OUString & rPackageUri ) + { m_aPath.clear(); m_aUri = rPackageUri; m_bValid = false; } + + const OUString & getParentUri() const + { init(); return m_aParentUri; } + + const OUString & getPackage() const + { init(); return m_aPackage; } + + const OUString & getPath() const + { init(); return m_aPath; } + + const OUString & getName() const + { init(); return m_aName; } + + const OUString & getParam() const + { init(); return m_aParam; } + + const OUString & getScheme() const + { init(); return m_aScheme; } + + inline bool isRootFolder() const; +}; + +inline bool PackageUri::isRootFolder() const +{ + init(); + return m_aPath == "/"; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/ucp/package/ucppkg1.component b/ucb/source/ucp/package/ucppkg1.component new file mode 100644 index 0000000000..5bbf387a15 --- /dev/null +++ b/ucb/source/ucp/package/ucppkg1.component @@ -0,0 +1,26 @@ + + + + + + + + -- cgit v1.2.3