summaryrefslogtreecommitdiffstats
path: root/ucb/source/ucp/package
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /ucb/source/ucp/package
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ucb/source/ucp/package')
-rw-r--r--ucb/source/ucp/package/pkgcontent.cxx2689
-rw-r--r--ucb/source/ucp/package/pkgcontent.hxx276
-rw-r--r--ucb/source/ucp/package/pkgcontentcaps.cxx504
-rw-r--r--ucb/source/ucp/package/pkgdatasupplier.cxx425
-rw-r--r--ucb/source/ucp/package/pkgdatasupplier.hxx87
-rw-r--r--ucb/source/ucp/package/pkgprovider.cxx272
-rw-r--r--ucb/source/ucp/package/pkgprovider.hxx86
-rw-r--r--ucb/source/ucp/package/pkgresultset.cxx79
-rw-r--r--ucb/source/ucp/package/pkgresultset.hxx47
-rw-r--r--ucb/source/ucp/package/pkguri.cxx234
-rw-r--r--ucb/source/ucp/package/pkguri.hxx89
-rw-r--r--ucb/source/ucp/package/ucppkg1.component26
12 files changed, 4814 insertions, 0 deletions
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 <sal/config.h>
+
+#include <string_view>
+
+#include <osl/diagnose.h>
+
+#include <rtl/ustring.hxx>
+#include <com/sun/star/beans/IllegalTypeException.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/PropertyExistException.hpp>
+#include <com/sun/star/beans/PropertyState.hpp>
+#include <com/sun/star/container/XEnumerationAccess.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/io/BufferSizeExceededException.hpp>
+#include <com/sun/star/io/NotConnectedException.hpp>
+#include <com/sun/star/io/XActiveDataSink.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/lang/IllegalAccessException.hpp>
+#include <com/sun/star/ucb/ContentInfoAttribute.hpp>
+#include <com/sun/star/ucb/IllegalIdentifierException.hpp>
+#include <com/sun/star/ucb/InsertCommandArgument.hpp>
+#include <com/sun/star/ucb/InteractiveBadTransferURLException.hpp>
+#include <com/sun/star/ucb/MissingInputStreamException.hpp>
+#include <com/sun/star/ucb/NameClash.hpp>
+#include <com/sun/star/ucb/NameClashException.hpp>
+#include <com/sun/star/ucb/OpenCommandArgument2.hpp>
+#include <com/sun/star/ucb/OpenMode.hpp>
+#include <com/sun/star/ucb/TransferInfo.hpp>
+#include <com/sun/star/ucb/UnsupportedCommandException.hpp>
+#include <com/sun/star/ucb/UnsupportedDataSinkException.hpp>
+#include <com/sun/star/ucb/UnsupportedNameClashException.hpp>
+#include <com/sun/star/ucb/UnsupportedOpenModeException.hpp>
+#include <com/sun/star/ucb/XCommandInfo.hpp>
+#include <com/sun/star/ucb/XPersistentPropertySet.hpp>
+#include <com/sun/star/util/XChangesBatch.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <ucbhelper/contentidentifier.hxx>
+#include <ucbhelper/propertyvalueset.hxx>
+#include <ucbhelper/cancelcommandexecution.hxx>
+#include <ucbhelper/macros.hxx>
+#include <utility>
+#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<OUString>::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> 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> 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<uno::Any> 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<uno::Any> 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<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ),
+ rData.aContentType );
+ xRow->appendString(
+ beans::Property(
+ "Title",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND ),
+ rData.aTitle );
+ xRow->appendBoolean(
+ beans::Property(
+ "IsDocument",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ),
+ rData.bIsDocument );
+ xRow->appendBoolean(
+ beans::Property(
+ "IsFolder",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ),
+ rData.bIsFolder );
+ xRow->appendObject(
+ beans::Property(
+ "CreatableContentsInfo",
+ -1,
+ cppu::UnoType<uno::Sequence< ucb::ContentInfo >>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ),
+ uno::Any(
+ rData.getCreatableContentsInfo( PackageUri( rContentId ) ) ) );
+ xRow->appendString(
+ beans::Property(
+ "MediaType",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND ),
+ rData.aMediaType );
+
+ // Properties only available for streams.
+ if ( rData.bIsDocument )
+ {
+ xRow->appendLong(
+ beans::Property(
+ "Size",
+ -1,
+ cppu::UnoType<sal_Int64>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ),
+ rData.nSize );
+
+ xRow->appendBoolean(
+ beans::Property(
+ "Compressed",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND ),
+ rData.bCompressed );
+
+ xRow->appendBoolean(
+ beans::Property(
+ "Encrypted",
+ -1,
+ cppu::UnoType<bool>::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<bool>::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<uno::Any> 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<uno::Any> 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<uno::Any> 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<uno::Any> 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<uno::Any> 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<uno::Any> 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<uno::Any> 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<uno::Any> 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 <sal/config.h>
+
+#include <string_view>
+#include <vector>
+#include <rtl/ref.hxx>
+
+#include <com/sun/star/ucb/XContentCreator.hpp>
+#include <ucbhelper/contenthelper.hxx>
+#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<Content> 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<Content> 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 <com/sun/star/beans/Property.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/ucb/CommandInfo.hpp>
+#include <com/sun/star/ucb/OpenCommandArgument2.hpp>
+#include <com/sun/star/ucb/TransferInfo.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <sal/macros.h>
+#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<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "IsDocument",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "IsFolder",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "Title",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+
+ // Optional standard properties
+
+ beans::Property(
+ "MediaType",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ ),
+ beans::Property(
+ "CreatableContentsInfo",
+ -1,
+ cppu::UnoType<uno::Sequence< ucb::ContentInfo >>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+
+ // New properties
+
+ beans::Property(
+ "HasEncryptedEntries",
+ -1,
+ cppu::UnoType<bool>::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<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "IsDocument",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "IsFolder",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "Title",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ ),
+
+ // Optional standard properties
+
+ beans::Property(
+ "MediaType",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ ),
+ beans::Property(
+ "CreatableContentsInfo",
+ -1,
+ cppu::UnoType<uno::Sequence< ucb::ContentInfo >>::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<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "IsDocument",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "IsFolder",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "Title",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ ),
+
+ // Optional standard properties
+
+ beans::Property(
+ "MediaType",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ ),
+ beans::Property(
+ "Size",
+ -1,
+ cppu::UnoType<sal_Int64>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "CreatableContentsInfo",
+ -1,
+ cppu::UnoType<uno::Sequence< ucb::ContentInfo >>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+
+ // New properties
+
+ beans::Property(
+ "Compressed",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ ),
+ beans::Property(
+ "Encrypted",
+ -1,
+ cppu::UnoType<bool>::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<void>::get()
+ ),
+ ucb::CommandInfo(
+ "getPropertySetInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "getPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::Property >>::get()
+ ),
+ ucb::CommandInfo(
+ "setPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get()
+ ),
+
+ // Optional standard commands
+
+ ucb::CommandInfo(
+ "open",
+ -1,
+ cppu::UnoType<ucb::OpenCommandArgument2>::get()
+ ),
+ ucb::CommandInfo(
+ "transfer",
+ -1,
+ cppu::UnoType<ucb::TransferInfo>::get()
+ ),
+ ucb::CommandInfo(
+ "createNewContent",
+ -1,
+ cppu::UnoType<ucb::ContentInfo>::get()
+ ),
+
+ // New commands
+
+ ucb::CommandInfo(
+ "flush",
+ -1,
+ cppu::UnoType<void>::get()
+ )
+ };
+
+ return MAKECMDSEQUENCE( aRootFolderCommandInfoTable );
+ }
+ else
+ {
+
+
+ // Folder: Supported commands
+
+
+ static const ucb::CommandInfo aFolderCommandInfoTable[] =
+ {
+
+ // Required commands
+
+ ucb::CommandInfo(
+ "getCommandInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "getPropertySetInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "getPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::Property >>::get()
+ ),
+ ucb::CommandInfo(
+ "setPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get()
+ ),
+
+ // Optional standard commands
+
+ ucb::CommandInfo(
+ "delete",
+ -1,
+ cppu::UnoType<bool>::get()
+ ),
+ ucb::CommandInfo(
+ "insert",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "open",
+ -1,
+ cppu::UnoType<ucb::OpenCommandArgument2>::get()
+ ),
+ ucb::CommandInfo(
+ "transfer",
+ -1,
+ cppu::UnoType<ucb::TransferInfo>::get()
+ ),
+ ucb::CommandInfo(
+ "createNewContent",
+ -1,
+ cppu::UnoType<ucb::ContentInfo>::get()
+ ),
+
+ // New commands
+
+ ucb::CommandInfo(
+ "flush",
+ -1,
+ cppu::UnoType<void>::get()
+ )
+ };
+
+ return MAKECMDSEQUENCE( aFolderCommandInfoTable );
+ }
+ }
+ else
+ {
+
+
+ // Stream: Supported commands
+
+
+ static const ucb::CommandInfo aStreamCommandInfoTable[] =
+ {
+
+ // Required commands
+
+ ucb::CommandInfo(
+ "getCommandInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "getPropertySetInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "getPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::Property >>::get()
+ ),
+ ucb::CommandInfo(
+ "setPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get()
+ ),
+
+ // Optional standard commands
+
+ ucb::CommandInfo(
+ "delete",
+ -1,
+ cppu::UnoType<bool>::get()
+ ),
+ ucb::CommandInfo(
+ "insert",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "open",
+ -1,
+ cppu::UnoType<ucb::OpenCommandArgument2>::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 <osl/diagnose.h>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/ucb/IllegalIdentifierException.hpp>
+#include <com/sun/star/ucb/ResultSetException.hpp>
+#include <ucbhelper/contentidentifier.hxx>
+#include <ucbhelper/providerhelper.hxx>
+#include <utility>
+#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<std::mutex>& 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<std::mutex>& 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<std::mutex>& 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 <rtl/ref.hxx>
+#include <ucbhelper/resultset.hxx>
+#include <com/sun/star/container/XEnumeration.hpp>
+#include <mutex>
+#include <utility>
+#include <vector>
+
+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<std::mutex>&, sal_uInt32 nIndex );
+ OUString queryContentIdentifierStringImpl( std::unique_lock<std::mutex>&, sal_uInt32 nIndex );
+ css::uno::Reference< css::ucb::XContentIdentifier > queryContentIdentifierImpl( std::unique_lock<std::mutex>&, 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 <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/weak.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <ucbhelper/contentidentifier.hxx>
+#include <ucbhelper/macros.hxx>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/ucb/IllegalIdentifierException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include "pkgprovider.hxx"
+#include "pkgcontent.hxx"
+#include "pkguri.hxx"
+#include <unordered_map>
+#include <utility>
+
+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<OUString, Package*> {};
+
+}
+
+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<css::uno::Any> 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 <memory>
+#include <ucbhelper/providerhelper.hxx>
+#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<Packages> 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 <utility>
+
+#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 <rtl/ref.hxx>
+#include <ucbhelper/resultsethelper.hxx>
+#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 <sal/config.h>
+
+#include <string_view>
+
+#include <comphelper/storagehelper.hxx>
+
+#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: <scheme>://<non_empty_url_to_file>
+ 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 <scheme>:/// - 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 <scheme>://// or <scheme>://<something>
+
+ // 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 <rtl/ustring.hxx>
+#include <utility>
+
+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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * 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 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.ucb.PackageContentProvider"
+ constructor="ucb_package_ContentProvider_get_implementation" single-instance="true">
+ <service name="com.sun.star.ucb.PackageContentProvider"/>
+ </implementation>
+</component>