/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ /************************************************************************** TODO ************************************************************************** *************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pkgcontent.hxx" #include "pkgprovider.hxx" #include "pkgresultset.hxx" #include "../inc/urihelper.hxx" using namespace com::sun::star; using namespace package_ucp; #define NONE_MODIFIED sal_uInt32( 0x00 ) #define MEDIATYPE_MODIFIED sal_uInt32( 0x01 ) #define COMPRESSED_MODIFIED sal_uInt32( 0x02 ) #define ENCRYPTED_MODIFIED sal_uInt32( 0x04 ) #define ENCRYPTIONKEY_MODIFIED sal_uInt32( 0x08 ) // ContentProperties Implementation. ContentProperties::ContentProperties( const OUString& rContentType ) : aContentType( rContentType ), nSize( 0 ), bCompressed( true ), bEncrypted( false ), bHasEncryptedEntries( false ) { bIsFolder = rContentType == PACKAGE_FOLDER_CONTENT_TYPE || rContentType == PACKAGE_ZIP_FOLDER_CONTENT_TYPE; bIsDocument = !bIsFolder; OSL_ENSURE( bIsFolder || rContentType == PACKAGE_STREAM_CONTENT_TYPE || rContentType == PACKAGE_ZIP_STREAM_CONTENT_TYPE, "ContentProperties::ContentProperties - Unknown type!" ); } uno::Sequence< ucb::ContentInfo > ContentProperties::getCreatableContentsInfo( PackageUri const & rUri ) const { if ( bIsFolder ) { uno::Sequence< beans::Property > aProps( 1 ); aProps.getArray()[ 0 ] = beans::Property( u"Title"_ustr, -1, cppu::UnoType::get(), beans::PropertyAttribute::BOUND ); uno::Sequence< ucb::ContentInfo > aSeq( 2 ); // Folder. aSeq.getArray()[ 0 ].Type = Content::getContentType( rUri.getScheme(), true ); aSeq.getArray()[ 0 ].Attributes = ucb::ContentInfoAttribute::KIND_FOLDER; aSeq.getArray()[ 0 ].Properties = aProps; // Stream. aSeq.getArray()[ 1 ].Type = Content::getContentType( rUri.getScheme(), false ); aSeq.getArray()[ 1 ].Attributes = ucb::ContentInfoAttribute::INSERT_WITH_INPUTSTREAM | ucb::ContentInfoAttribute::KIND_DOCUMENT; aSeq.getArray()[ 1 ].Properties = aProps; return aSeq; } else { return uno::Sequence< ucb::ContentInfo >( 0 ); } } // Content Implementation. // static ( "virtual" ctor ) rtl::Reference Content::create( const uno::Reference< uno::XComponentContext >& rxContext, ContentProvider* pProvider, const uno::Reference< ucb::XContentIdentifier >& Identifier ) { OUString aURL = Identifier->getContentIdentifier(); PackageUri aURI( aURL ); ContentProperties aProps; uno::Reference< container::XHierarchicalNameAccess > xPackage; if ( loadData( pProvider, aURI, aProps, xPackage ) ) { // resource exists sal_Int32 nLastSlash = aURL.lastIndexOf( '/' ); if ( ( nLastSlash + 1 ) == aURL.getLength() ) { // Client explicitly requested a folder! if ( !aProps.bIsFolder ) return nullptr; } uno::Reference< ucb::XContentIdentifier > xId = new ::ucbhelper::ContentIdentifier( aURI.getUri() ); return new Content( rxContext, pProvider, xId, xPackage, std::move(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, std::move(aURI), std::move(aInfo) ); } } // static ( "virtual" ctor ) rtl::Reference Content::create( const uno::Reference< uno::XComponentContext >& rxContext, ContentProvider* pProvider, const uno::Reference< ucb::XContentIdentifier >& Identifier, const ucb::ContentInfo& Info ) { if ( Info.Type.isEmpty() ) return nullptr; PackageUri aURI( Identifier->getContentIdentifier() ); if ( !Info.Type.equalsIgnoreAsciiCase( getContentType( aURI.getScheme(), true ) ) && !Info.Type.equalsIgnoreAsciiCase( getContentType( aURI.getScheme(), false ) ) ) return nullptr; uno::Reference< container::XHierarchicalNameAccess > xPackage = pProvider->createPackage( aURI ); uno::Reference< ucb::XContentIdentifier > xId = new ::ucbhelper::ContentIdentifier( aURI.getUri() ); return new Content( rxContext, pProvider, xId, xPackage, std::move(aURI), Info ); } // static OUString Content::getContentType( std::u16string_view aScheme, bool bFolder ) { return ( OUString::Concat("application/") + aScheme + ( bFolder ? std::u16string_view(u"-folder") : std::u16string_view(u"-stream") ) ); } Content::Content( const uno::Reference< uno::XComponentContext >& rxContext, ContentProvider* pProvider, const uno::Reference< ucb::XContentIdentifier >& Identifier, uno::Reference< container::XHierarchicalNameAccess > Package, PackageUri aUri, ContentProperties aProps ) : ContentImplHelper( rxContext, pProvider, Identifier ), m_aUri(std::move( aUri )), m_aProps(std::move( aProps )), m_eState( PERSISTENT ), m_xPackage(std::move( Package )), m_pProvider( pProvider ), m_nModifiedProps( NONE_MODIFIED ) { } Content::Content( const uno::Reference< uno::XComponentContext >& rxContext, ContentProvider* pProvider, const uno::Reference< ucb::XContentIdentifier >& Identifier, uno::Reference< container::XHierarchicalNameAccess > Package, PackageUri aUri, const ucb::ContentInfo& Info ) : ContentImplHelper( rxContext, pProvider, Identifier ), m_aUri(std::move( aUri )), m_aProps( Info.Type ), m_eState( TRANSIENT ), m_xPackage(std::move( Package )), m_pProvider( pProvider ), m_nModifiedProps( NONE_MODIFIED ) { } // virtual Content::~Content() { } // XInterface methods. // virtual void SAL_CALL Content::acquire() noexcept { ContentImplHelper::acquire(); } // virtual void SAL_CALL Content::release() noexcept { ContentImplHelper::release(); } // virtual uno::Any SAL_CALL Content::queryInterface( const uno::Type & rType ) { uno::Any aRet; if ( isFolder() ) aRet = cppu::queryInterface( rType, static_cast< ucb::XContentCreator * >( this ) ); return aRet.hasValue() ? aRet : ContentImplHelper::queryInterface( rType ); } // XTypeProvider methods. XTYPEPROVIDER_COMMON_IMPL( Content ); // virtual uno::Sequence< uno::Type > SAL_CALL Content::getTypes() { if ( isFolder() ) { static cppu::OTypeCollection s_aFolderTypes( CPPU_TYPE_REF( lang::XTypeProvider ), CPPU_TYPE_REF( lang::XServiceInfo ), CPPU_TYPE_REF( lang::XComponent ), CPPU_TYPE_REF( ucb::XContent ), CPPU_TYPE_REF( ucb::XCommandProcessor ), CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ), CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ), CPPU_TYPE_REF( beans::XPropertyContainer ), CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ), CPPU_TYPE_REF( container::XChild ), CPPU_TYPE_REF( ucb::XContentCreator ) ); return s_aFolderTypes.getTypes(); } else { static cppu::OTypeCollection s_aDocumentTypes( CPPU_TYPE_REF( lang::XTypeProvider ), CPPU_TYPE_REF( lang::XServiceInfo ), CPPU_TYPE_REF( lang::XComponent ), CPPU_TYPE_REF( ucb::XContent ), CPPU_TYPE_REF( ucb::XCommandProcessor ), CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ), CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ), CPPU_TYPE_REF( beans::XPropertyContainer ), CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ), CPPU_TYPE_REF( container::XChild ) ); return s_aDocumentTypes.getTypes(); } } // XServiceInfo methods. // virtual OUString SAL_CALL Content::getImplementationName() { return u"com.sun.star.comp.ucb.PackageContent"_ustr; } // virtual uno::Sequence< OUString > SAL_CALL Content::getSupportedServiceNames() { return { isFolder()? u"com.sun.star.ucb.PackageFolderContent"_ustr:u"com.sun.star.ucb.PackageStreamContent"_ustr } ; } // 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( u"Wrong argument type!"_ustr, 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( u"Wrong argument type!"_ustr, getXWeak(), -1 ) ), Environment ); // Unreachable } if ( !aProperties.hasElements() ) { ucbhelper::cancelCommandExecution( uno::Any( lang::IllegalArgumentException( u"No properties!"_ustr, 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( u"Wrong argument type!"_ustr, 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( u"Wrong argument type!"_ustr, getXWeak(), -1 ) ), Environment ); // Unreachable } sal_Int32 nNameClash = aArg.ReplaceExisting ? ucb::NameClash::OVERWRITE : ucb::NameClash::ERROR; insert( aArg.Data, nNameClash, Environment ); } else if ( !m_aUri.isRootFolder() && aCommand.Name == "delete" ) { // delete bool bDeletePhysical = false; aCommand.Argument >>= bDeletePhysical; destroy( bDeletePhysical, Environment ); // Remove own and all children's persistent data. if ( !removeData() ) { uno::Sequence aArgs(comphelper::InitAnyPropertySequence( { {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())} })); ucbhelper::cancelCommandExecution( ucb::IOErrorCode_CANT_WRITE, aArgs, Environment, u"Cannot remove persistent data!"_ustr, 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( u"Wrong argument type!"_ustr, 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( u"Wrong argument type!"_ustr, getXWeak(), -1 ) ), Environment ); // Unreachable } aRet <<= createNewContent( aInfo ); } else if ( aCommand.Name == "flush" ) { // flush // ( Not available at stream objects ) if( !flushData() ) { uno::Sequence aArgs(comphelper::InitAnyPropertySequence( { {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())} })); ucbhelper::cancelCommandExecution( ucb::IOErrorCode_CANT_WRITE, aArgs, Environment, u"Cannot write file to disk!"_ustr, 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( u"ContentType"_ustr, -1, cppu::UnoType::get(), beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ), rData.aContentType ); xRow->appendString( beans::Property( u"Title"_ustr, -1, cppu::UnoType::get(), beans::PropertyAttribute::BOUND ), rData.aTitle ); xRow->appendBoolean( beans::Property( u"IsDocument"_ustr, -1, cppu::UnoType::get(), beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ), rData.bIsDocument ); xRow->appendBoolean( beans::Property( u"IsFolder"_ustr, -1, cppu::UnoType::get(), beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ), rData.bIsFolder ); xRow->appendObject( beans::Property( u"CreatableContentsInfo"_ustr, -1, cppu::UnoType>::get(), beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ), uno::Any( rData.getCreatableContentsInfo( PackageUri( rContentId ) ) ) ); xRow->appendString( beans::Property( u"MediaType"_ustr, -1, cppu::UnoType::get(), beans::PropertyAttribute::BOUND ), rData.aMediaType ); // Properties only available for streams. if ( rData.bIsDocument ) { xRow->appendLong( beans::Property( u"Size"_ustr, -1, cppu::UnoType::get(), beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ), rData.nSize ); xRow->appendBoolean( beans::Property( u"Compressed"_ustr, -1, cppu::UnoType::get(), beans::PropertyAttribute::BOUND ), rData.bCompressed ); xRow->appendBoolean( beans::Property( u"Encrypted"_ustr, -1, cppu::UnoType::get(), beans::PropertyAttribute::BOUND ), rData.bEncrypted ); } // Properties only available for root folder. PackageUri aURI( rContentId ); if ( aURI.isRootFolder() ) { xRow->appendBoolean( beans::Property( u"HasEncryptedEntries"_ustr, -1, cppu::UnoType::get(), beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ), rData.bHasEncryptedEntries ); } // Append all Additional Core Properties. uno::Reference< beans::XPropertySet > xSet = rProvider->getAdditionalPropertySet( rContentId, false ); xRow->appendPropertySet( xSet ); } return xRow; } uno::Reference< sdbc::XRow > Content::getPropertyValues( const uno::Sequence< beans::Property >& rProperties ) { osl::Guard< osl::Mutex > aGuard( m_aMutex ); return getPropertyValues( m_xContext, rProperties, m_aProps, m_xProvider, m_xIdentifier->getContentIdentifier() ); } uno::Sequence< uno::Any > Content::setPropertyValues( const uno::Sequence< beans::PropertyValue >& rValues, const uno::Reference< ucb::XCommandEnvironment > & xEnv ) { osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex ); uno::Sequence< uno::Any > aRet( rValues.getLength() ); auto aRetRange = asNonConstRange(aRet); uno::Sequence< beans::PropertyChangeEvent > aChanges( rValues.getLength() ); sal_Int32 nChanged = 0; beans::PropertyChangeEvent aEvent; aEvent.Source = getXWeak(); aEvent.Further = false; // aEvent.PropertyName = aEvent.PropertyHandle = -1; // aEvent.OldValue = // aEvent.NewValue = const beans::PropertyValue* pValues = rValues.getConstArray(); sal_Int32 nCount = rValues.getLength(); uno::Reference< ucb::XPersistentPropertySet > xAdditionalPropSet; bool bTriedToGetAdditionalPropSet = false; bool bExchange = false; bool bStore = false; OUString aNewTitle; sal_Int32 nTitlePos = -1; for ( sal_Int32 n = 0; n < nCount; ++n ) { const beans::PropertyValue& rValue = pValues[ n ]; if ( rValue.Name == "ContentType" ) { // Read-only property! aRetRange[ n ] <<= lang::IllegalAccessException( u"Property is read-only!"_ustr, getXWeak() ); } else if ( rValue.Name == "IsDocument" ) { // Read-only property! aRetRange[ n ] <<= lang::IllegalAccessException( u"Property is read-only!"_ustr, getXWeak() ); } else if ( rValue.Name == "IsFolder" ) { // Read-only property! aRetRange[ n ] <<= lang::IllegalAccessException( u"Property is read-only!"_ustr, getXWeak() ); } else if ( rValue.Name == "CreatableContentsInfo" ) { // Read-only property! aRetRange[ n ] <<= lang::IllegalAccessException( u"Property is read-only!"_ustr, getXWeak() ); } else if ( rValue.Name == "Title" ) { if ( m_aUri.isRootFolder() ) { // Read-only property! aRetRange[ n ] <<= lang::IllegalAccessException( u"Property is read-only!"_ustr, 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( u"Empty title not allowed!"_ustr, getXWeak(), -1 ); } } else { aRetRange[ n ] <<= beans::IllegalTypeException( u"Property value has wrong type!"_ustr, 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( u"Property value has wrong type!"_ustr, getXWeak() ); } } else if ( rValue.Name == "Size" ) { // Read-only property! aRetRange[ n ] <<= lang::IllegalAccessException( u"Property is read-only!"_ustr, 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( u"Property value has wrong type!"_ustr, getXWeak() ); } } else { aRetRange[ n ] <<= beans::UnknownPropertyException( u"Compressed only supported by streams!"_ustr, 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( u"Property value has wrong type!"_ustr, getXWeak() ); } } else { aRetRange[ n ] <<= beans::UnknownPropertyException( u"Encrypted only supported by streams!"_ustr, getXWeak() ); } } else if ( rValue.Name == "HasEncryptedEntries" ) { // Read-only property! aRetRange[ n ] <<= lang::IllegalAccessException( u"Property is read-only!"_ustr, 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( u"Property value has wrong type!"_ustr, getXWeak() ); } } else { aRetRange[ n ] <<= beans::UnknownPropertyException( u"EncryptionKey not supported by non-root folder!"_ustr, 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( u"No property set for storing the value!"_ustr, 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( u"Exchange failed!"_ustr, getXWeak() ); } } if ( !aNewTitle.isEmpty() ) { aEvent.PropertyName = "Title"; aEvent.OldValue <<= m_aProps.aTitle; aEvent.NewValue <<= aNewTitle; m_aProps.aTitle = aNewTitle; aChanges.getArray()[ nChanged ] = aEvent; nChanged++; } if ( nChanged > 0 ) { // Save changes, if content was already made persistent. if ( ( m_nModifiedProps & ENCRYPTIONKEY_MODIFIED ) || ( bStore && ( m_eState == PERSISTENT ) ) ) { if ( !storeData( uno::Reference< io::XInputStream >() ) ) { uno::Sequence aArgs(comphelper::InitAnyPropertySequence( { {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())} })); ucbhelper::cancelCommandExecution( ucb::IOErrorCode_CANT_WRITE, aArgs, xEnv, u"Cannot store persistent data!"_ustr, this ); // Unreachable } } aGuard.clear(); aChanges.realloc( nChanged ); notifyPropertiesChange( aChanges ); } return aRet; } uno::Any Content::open( const ucb::OpenCommandArgument2& rArg, const uno::Reference< ucb::XCommandEnvironment >& xEnv ) { if ( rArg.Mode == ucb::OpenMode::ALL || rArg.Mode == ucb::OpenMode::FOLDERS || rArg.Mode == ucb::OpenMode::DOCUMENTS ) { // open command for a folder content uno::Reference< ucb::XDynamicResultSet > xSet = new DynamicResultSet( m_xContext, this, rArg, xEnv ); return uno::Any( xSet ); } else { // open command for a document content if ( ( rArg.Mode == ucb::OpenMode::DOCUMENT_SHARE_DENY_NONE ) || ( rArg.Mode == ucb::OpenMode::DOCUMENT_SHARE_DENY_WRITE ) ) { // Currently(?) unsupported. ucbhelper::cancelCommandExecution( uno::Any( ucb::UnsupportedOpenModeException( OUString(), getXWeak(), sal_Int16( rArg.Mode ) ) ), xEnv ); // Unreachable } uno::Reference< io::XOutputStream > xOut( rArg.Sink, uno::UNO_QUERY ); if ( xOut.is() ) { // PUSH: write data into xOut uno::Reference< io::XInputStream > xIn = getInputStream(); if ( !xIn.is() ) { // No interaction if we are not persistent! uno::Sequence aArgs(comphelper::InitAnyPropertySequence( { {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())} })); ucbhelper::cancelCommandExecution( ucb::IOErrorCode_CANT_READ, aArgs, m_eState == PERSISTENT ? xEnv : uno::Reference< ucb::XCommandEnvironment >(), u"Got no data stream!"_ustr, this ); // Unreachable } try { uno::Sequence< sal_Int8 > aBuffer; while (true) { sal_Int32 nRead = xIn->readSomeBytes( aBuffer, 65536 ); if (!nRead) break; aBuffer.realloc( nRead ); xOut->writeBytes( aBuffer ); } xOut->closeOutput(); } catch ( io::NotConnectedException const & ) { // closeOutput, readSomeBytes, writeBytes } catch ( io::BufferSizeExceededException const & ) { // closeOutput, readSomeBytes, writeBytes } catch ( io::IOException const & ) { // closeOutput, readSomeBytes, writeBytes } } else { uno::Reference< io::XActiveDataSink > xDataSink( rArg.Sink, uno::UNO_QUERY ); if ( xDataSink.is() ) { // PULL: wait for client read uno::Reference< io::XInputStream > xIn = getInputStream(); if ( !xIn.is() ) { // No interaction if we are not persistent! uno::Sequence aArgs(comphelper::InitAnyPropertySequence( { {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())} })); ucbhelper::cancelCommandExecution( ucb::IOErrorCode_CANT_READ, aArgs, m_eState == PERSISTENT ? xEnv : uno::Reference< ucb::XCommandEnvironment >(), u"Got no data stream!"_ustr, 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( u"Unable to resolve name clash!"_ustr, getXWeak(), nNameClashResolve ) ), xEnv ); // Unreachable } else { m_aProps.aTitle += "_"; m_aProps.aTitle += OUString::number( nTry ); } } break; case ucb::NameClash::KEEP: // deprecated case ucb::NameClash::ASK: default: if ( hasData( aNewUri ) ) { ucbhelper::cancelCommandExecution( uno::Any( ucb::UnsupportedNameClashException( OUString(), getXWeak(), nNameClashResolve ) ), xEnv ); // Unreachable } break; } // Identifier changed? bool bNewId = ( m_aUri.getUri() != aNewUri.getUri() ); if ( bNewId ) { m_xIdentifier = new ::ucbhelper::ContentIdentifier( aNewURL ); m_aUri = aNewUri; } if ( !storeData( xStream ) ) { uno::Sequence aArgs(comphelper::InitAnyPropertySequence( { {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())} })); ucbhelper::cancelCommandExecution( ucb::IOErrorCode_CANT_WRITE, aArgs, xEnv, u"Cannot store persistent data!"_ustr, 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( u"Not persistent!"_ustr, 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( u"Not persistent!"_ustr, getXWeak() ) ), xEnv ); // Unreachable } // Is source a package content? if ( ( rInfo.SourceURL.isEmpty() ) || ( rInfo.SourceURL.compareTo( m_aUri.getUri(), PACKAGE_URL_SCHEME_LENGTH + 3 ) != 0 ) ) { ucbhelper::cancelCommandExecution( uno::Any( ucb::InteractiveBadTransferURLException( OUString(), getXWeak() ) ), xEnv ); // Unreachable } // Is source not a parent of me / not me? OUString aId = m_aUri.getParentUri() + "/"; if ( rInfo.SourceURL.getLength() <= aId.getLength() ) { if ( aId.startsWith( rInfo.SourceURL ) ) { uno::Sequence aArgs(comphelper::InitAnyPropertySequence( { {"Uri", uno::Any(rInfo.SourceURL)} })); ucbhelper::cancelCommandExecution( ucb::IOErrorCode_RECURSIVE, aArgs, xEnv, u"Target is equal to or is a child of source!"_ustr, this ); // Unreachable } } // 0) Obtain content object for source. uno::Reference< ucb::XContentIdentifier > xId = new ::ucbhelper::ContentIdentifier( rInfo.SourceURL ); // Note: The static cast is okay here, because its sure that // m_xProvider is always the PackageContentProvider. rtl::Reference< Content > xSource; try { xSource = static_cast< Content * >( m_xProvider->queryContent( xId ).get() ); } catch ( ucb::IllegalIdentifierException const & ) { // queryContent } if ( !xSource.is() ) { uno::Sequence aArgs(comphelper::InitAnyPropertySequence( { {"Uri", uno::Any(xId->getContentIdentifier())} })); ucbhelper::cancelCommandExecution( ucb::IOErrorCode_CANT_READ, aArgs, xEnv, u"Cannot instantiate source object!"_ustr, this ); // Unreachable } // 1) Create new child content. ucb::ContentInfo aContentInfo; aContentInfo.Type = xSource->isFolder() ? getContentType( m_aUri.getScheme(), true ) : getContentType( m_aUri.getScheme(), false ); aContentInfo.Attributes = 0; // Note: The static cast is okay here, because its sure that // createNewContent always creates a Content. rtl::Reference< Content > xTarget = static_cast< Content * >( createNewContent( aContentInfo ).get() ); if ( !xTarget.is() ) { uno::Sequence aArgs(comphelper::InitAnyPropertySequence( { {"Folder", uno::Any(aId)} })); ucbhelper::cancelCommandExecution( ucb::IOErrorCode_CANT_CREATE, aArgs, xEnv, u"XContentCreator::createNewContent failed!"_ustr, this ); // Unreachable } // 2) Copy data from source content to child content. uno::Sequence< beans::Property > aSourceProps = xSource->getPropertySetInfo( xEnv )->getProperties(); sal_Int32 nCount = aSourceProps.getLength(); if ( nCount ) { bool bHadTitle = rInfo.NewTitle.isEmpty(); // Get all source values. uno::Reference< sdbc::XRow > xRow = xSource->getPropertyValues( aSourceProps ); uno::Sequence< beans::PropertyValue > aValues( nCount ); beans::PropertyValue* pValues = aValues.getArray(); const beans::Property* pProps = aSourceProps.getConstArray(); for ( sal_Int32 n = 0; n < nCount; ++n ) { const beans::Property& rProp = pProps[ n ]; beans::PropertyValue& rValue = pValues[ n ]; rValue.Name = rProp.Name; rValue.Handle = rProp.Handle; if ( !bHadTitle && rProp.Name == "Title" ) { // Set new title instead of original. bHadTitle = true; rValue.Value <<= rInfo.NewTitle; } else rValue.Value = xRow->getObject( n + 1, uno::Reference< container::XNameAccess >() ); rValue.State = beans::PropertyState_DIRECT_VALUE; if ( rProp.Attributes & beans::PropertyAttribute::REMOVABLE ) { // Add Additional Core Property. try { xTarget->addProperty( rProp.Name, rProp.Attributes, rValue.Value ); } catch ( beans::PropertyExistException const & ) { } catch ( beans::IllegalTypeException const & ) { } catch ( lang::IllegalArgumentException const & ) { } } } // Set target values. xTarget->setPropertyValues( aValues, xEnv ); } // 3) Commit (insert) child. xTarget->insert( xSource->getInputStream(), rInfo.NameClash, xEnv ); // 4) Transfer (copy) children of source. if ( xSource->isFolder() ) { uno::Reference< container::XEnumeration > xIter = xSource->getIterator(); if ( xIter.is() ) { while ( xIter->hasMoreElements() ) { try { uno::Reference< container::XNamed > xNamed; xIter->nextElement() >>= xNamed; if ( !xNamed.is() ) { OSL_FAIL( "Content::transfer - Got no XNamed!" ); break; } OUString aName = xNamed->getName(); if ( aName.isEmpty() ) { OSL_FAIL( "Content::transfer - Empty name!" ); break; } OUString aChildId = xId->getContentIdentifier(); if ( ( aChildId.lastIndexOf( '/' ) + 1 ) != aChildId.getLength() ) aChildId += "/"; aChildId += ::ucb_impl::urihelper::encodeSegment( aName ); ucb::TransferInfo aInfo; aInfo.MoveData = false; aInfo.NewTitle.clear(); aInfo.SourceURL = aChildId; aInfo.NameClash = rInfo.NameClash; // Transfer child to target. xTarget->transfer( aInfo, xEnv ); } catch ( container::NoSuchElementException const & ) { } catch ( lang::WrappedTargetException const & ) { } } } } // 5) Destroy source ( when moving only ) . if ( !rInfo.MoveData ) return; xSource->destroy( true, xEnv ); // Remove all persistent data of source and its children. if ( !xSource->removeData() ) { uno::Sequence aArgs(comphelper::InitAnyPropertySequence( { {"Uri", uno::Any(xSource->m_xIdentifier->getContentIdentifier())} })); ucbhelper::cancelCommandExecution( ucb::IOErrorCode_CANT_WRITE, aArgs, xEnv, u"Cannot remove persistent data of source object!"_ustr, 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( u"HasEncryptedEntries"_ustr ); 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(u"MediaType"_ustr); 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(u"Size"_ustr); 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(u"Compressed"_ustr); 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(u"Encrypted"_ustr); 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( u"EncryptionKey"_ustr, 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( u"MediaType"_ustr, uno::Any( m_aProps.aMediaType ) ); m_nModifiedProps &= ~MEDIATYPE_MODIFIED; } if ( m_nModifiedProps & COMPRESSED_MODIFIED ) { if ( !isFolder() ) xPropSet->setPropertyValue( u"Compressed"_ustr, uno::Any( m_aProps.bCompressed ) ); m_nModifiedProps &= ~COMPRESSED_MODIFIED; } if ( m_nModifiedProps & ENCRYPTED_MODIFIED ) { if ( !isFolder() ) xPropSet->setPropertyValue( u"Encrypted"_ustr, uno::Any( m_aProps.bEncrypted ) ); m_nModifiedProps &= ~ENCRYPTED_MODIFIED; } if ( m_nModifiedProps & ENCRYPTIONKEY_MODIFIED ) { if ( !isFolder() ) xPropSet->setPropertyValue( u"EncryptionKey"_ustr, 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: */