diff options
Diffstat (limited to '')
-rw-r--r-- | ucb/source/core/ucbcmds.cxx | 1923 |
1 files changed, 1923 insertions, 0 deletions
diff --git a/ucb/source/core/ucbcmds.cxx b/ucb/source/core/ucbcmds.cxx new file mode 100644 index 000000000..f0e45b7cb --- /dev/null +++ b/ucb/source/core/ucbcmds.cxx @@ -0,0 +1,1923 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <osl/diagnose.h> +#include <comphelper/propertysequence.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <rtl/ustring.hxx> +#include <com/sun/star/uno/XInterface.hpp> +#include <com/sun/star/beans/PropertyState.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/io/Pipe.hpp> +#include <com/sun/star/io/XActiveDataSink.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/task/XInteractionHandler.hpp> +#include <com/sun/star/ucb/CommandEnvironment.hpp> +#include <com/sun/star/ucb/CommandFailedException.hpp> +#include <com/sun/star/ucb/ContentInfoAttribute.hpp> +#include <com/sun/star/ucb/GlobalTransferCommandArgument2.hpp> +#include <com/sun/star/ucb/IllegalIdentifierException.hpp> +#include <com/sun/star/ucb/InsertCommandArgument2.hpp> +#include <com/sun/star/ucb/InteractiveBadTransferURLException.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/TransferInfo2.hpp> +#include <com/sun/star/ucb/UnsupportedCommandException.hpp> +#include <com/sun/star/ucb/UnsupportedNameClashException.hpp> +#include <com/sun/star/ucb/XCommandInfo.hpp> +#include <com/sun/star/ucb/XContentAccess.hpp> +#include <com/sun/star/ucb/XContentCreator.hpp> +#include <com/sun/star/ucb/XDynamicResultSet.hpp> +#include <com/sun/star/ucb/XInteractionSupplyName.hpp> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <ucbhelper/cancelcommandexecution.hxx> +#include <ucbhelper/simplenameclashresolverequest.hxx> +#include "ucbcmds.hxx" +#include "ucb.hxx" + +using namespace com::sun::star; + +namespace +{ +// Helper to provide defaults for type and attributes (save some typing) +beans::Property makeProperty(const OUString& n, sal_Int32 h, uno::Type t = {}, sal_Int16 a = {}) +{ + return { n, h, t, a }; +} + +// struct TransferCommandContext. + + +struct TransferCommandContext +{ + uno::Reference< uno::XComponentContext > m_xContext; + uno::Reference< ucb::XCommandProcessor > xProcessor; + uno::Reference< ucb::XCommandEnvironment > xEnv; + uno::Reference< ucb::XCommandEnvironment > xOrigEnv; + ucb::GlobalTransferCommandArgument2 aArg; + + TransferCommandContext( + const uno::Reference< uno::XComponentContext > & xContext, + const uno::Reference< ucb::XCommandProcessor > & rxProcessor, + const uno::Reference< ucb::XCommandEnvironment > & rxEnv, + const uno::Reference< ucb::XCommandEnvironment > & rxOrigEnv, + const ucb::GlobalTransferCommandArgument2 & rArg ) + : m_xContext( xContext ), xProcessor( rxProcessor ), xEnv( rxEnv ), + xOrigEnv( rxOrigEnv ), aArg( rArg ) {} +}; + + + + +class InteractionHandlerProxy : + public cppu::WeakImplHelper< task::XInteractionHandler > +{ + uno::Reference< task::XInteractionHandler > m_xOrig; + +public: + explicit InteractionHandlerProxy( + const uno::Reference< task::XInteractionHandler > & xOrig ) + : m_xOrig( xOrig ) {} + + // XInteractionHandler methods. + virtual void SAL_CALL handle( + const uno::Reference< task::XInteractionRequest >& Request ) override; +}; + + +// virtual +void SAL_CALL InteractionHandlerProxy::handle( + const uno::Reference< task::XInteractionRequest >& Request ) +{ + if ( !m_xOrig.is() ) + return; + + // Filter unwanted requests by just not handling them. + uno::Any aRequest = Request->getRequest(); + + // "transfer" + ucb::InteractiveBadTransferURLException aBadTransferURLEx; + if ( aRequest >>= aBadTransferURLEx ) + { + return; + } + else + { + // "transfer" + ucb::UnsupportedNameClashException aUnsupportedNameClashEx; + if ( aRequest >>= aUnsupportedNameClashEx ) + { + if ( aUnsupportedNameClashEx.NameClash + != ucb::NameClash::ERROR ) + return; + } + else + { + // "insert" + ucb::NameClashException aNameClashEx; + if ( aRequest >>= aNameClashEx ) + { + return; + } + else + { + // "transfer" + ucb::UnsupportedCommandException aUnsupportedCommandEx; + if ( aRequest >>= aUnsupportedCommandEx ) + { + return; + } + } + } + } + + // not filtered; let the original handler do the work. + m_xOrig->handle( Request ); +} + + + + +class ActiveDataSink : public cppu::WeakImplHelper< io::XActiveDataSink > +{ + uno::Reference< io::XInputStream > m_xStream; + +public: + // XActiveDataSink methods. + virtual void SAL_CALL setInputStream( + const uno::Reference< io::XInputStream >& aStream ) override; + virtual uno::Reference< io::XInputStream > SAL_CALL getInputStream() override; +}; + + +// virtual +void SAL_CALL ActiveDataSink::setInputStream( + const uno::Reference< io::XInputStream >& aStream ) +{ + m_xStream = aStream; +} + + +// virtual +uno::Reference< io::XInputStream > SAL_CALL ActiveDataSink::getInputStream() +{ + return m_xStream; +} + + + + +class CommandProcessorInfo : + public cppu::WeakImplHelper< ucb::XCommandInfo > +{ + uno::Sequence< ucb::CommandInfo > m_xInfo; + +public: + CommandProcessorInfo(); + + // XCommandInfo methods + virtual uno::Sequence< ucb::CommandInfo > SAL_CALL getCommands() override; + virtual ucb::CommandInfo SAL_CALL + getCommandInfoByName( const OUString& Name ) override; + virtual ucb::CommandInfo SAL_CALL + getCommandInfoByHandle( sal_Int32 Handle ) override; + virtual sal_Bool SAL_CALL hasCommandByName( const OUString& Name ) override; + virtual sal_Bool SAL_CALL hasCommandByHandle( sal_Int32 Handle ) override; +}; + + +CommandProcessorInfo::CommandProcessorInfo() + : m_xInfo{ + ucb::CommandInfo( + GETCOMMANDINFO_NAME, // Name + GETCOMMANDINFO_HANDLE, // Handle + cppu::UnoType<void>::get() ), // ArgType + ucb::CommandInfo( + GLOBALTRANSFER_NAME, // Name + GLOBALTRANSFER_HANDLE, // Handle + cppu::UnoType<ucb::GlobalTransferCommandArgument>::get() ), // ArgType + ucb::CommandInfo( + CHECKIN_NAME, // Name + CHECKIN_HANDLE, // Handle + cppu::UnoType<ucb::CheckinArgument>::get() ) } // ArgType +{ +} + + +// virtual +uno::Sequence< ucb::CommandInfo > SAL_CALL +CommandProcessorInfo::getCommands() +{ + return m_xInfo; +} + + +// virtual +ucb::CommandInfo SAL_CALL +CommandProcessorInfo::getCommandInfoByName( const OUString& Name ) +{ + auto pInfo = std::find_if(std::cbegin(m_xInfo), std::cend(m_xInfo), + [&Name](const ucb::CommandInfo& rInfo) { return rInfo.Name == Name; }); + if (pInfo != std::cend(m_xInfo)) + return *pInfo; + + throw ucb::UnsupportedCommandException(); +} + + +// virtual +ucb::CommandInfo SAL_CALL +CommandProcessorInfo::getCommandInfoByHandle( sal_Int32 Handle ) +{ + auto pInfo = std::find_if(std::cbegin(m_xInfo), std::cend(m_xInfo), + [&Handle](const ucb::CommandInfo& rInfo) { return rInfo.Handle == Handle; }); + if (pInfo != std::cend(m_xInfo)) + return *pInfo; + + throw ucb::UnsupportedCommandException(); +} + + +// virtual +sal_Bool SAL_CALL CommandProcessorInfo::hasCommandByName( + const OUString& Name ) +{ + return std::any_of(std::cbegin(m_xInfo), std::cend(m_xInfo), + [&Name](const ucb::CommandInfo& rInfo) { return rInfo.Name == Name; }); +} + + +// virtual +sal_Bool SAL_CALL CommandProcessorInfo::hasCommandByHandle( sal_Int32 Handle ) +{ + return std::any_of(std::cbegin(m_xInfo), std::cend(m_xInfo), + [&Handle](const ucb::CommandInfo& rInfo) { return rInfo.Handle == Handle; }); +} + + +OUString createDesiredName( + const OUString & rSourceURL, const OUString & rNewTitle ) +{ + OUString aName( rNewTitle ); + if ( aName.isEmpty() ) + { + // calculate name using source URL + + // @@@ It's not guaranteed that slashes contained in the URL are + // actually path separators. This depends on the fact whether the + // URL is hierarchical. Only then the slashes are path separators. + // Therefore this algorithm is not guaranteed to work! But, ATM + // I don't know a better solution. It would have been better to + // have a member for the clashing name in + // UnsupportedNameClashException... + + sal_Int32 nLastSlash = rSourceURL.lastIndexOf( '/' ); + bool bTrailingSlash = false; + if ( nLastSlash == rSourceURL.getLength() - 1 ) + { + nLastSlash = rSourceURL.lastIndexOf( '/', nLastSlash ); + bTrailingSlash = true; + } + + if ( nLastSlash != -1 ) + { + if ( bTrailingSlash ) + aName = rSourceURL.copy( + nLastSlash + 1, + rSourceURL.getLength() - nLastSlash - 2 ); + else + aName = rSourceURL.copy( nLastSlash + 1 ); + } + else + { + aName = rSourceURL; + } + + // query, fragment present? + sal_Int32 nPos = aName.indexOf( '?' ); + if ( nPos == -1 ) + nPos = aName.indexOf( '#' ); + + if ( nPos != -1 ) + aName = aName.copy( 0, nPos ); + } + return aName; +} + +OUString createDesiredName( + const ucb::GlobalTransferCommandArgument & rArg ) +{ + return createDesiredName( rArg.SourceURL, rArg.NewTitle ); +} + +OUString createDesiredName( + const ucb::TransferInfo & rArg ) +{ + return createDesiredName( rArg.SourceURL, rArg.NewTitle ); +} + + +enum NameClashContinuation { NOT_HANDLED, ABORT, OVERWRITE, NEW_NAME, UNKNOWN }; + +NameClashContinuation interactiveNameClashResolve( + const uno::Reference< ucb::XCommandEnvironment > & xEnv, + const OUString & rTargetURL, + const OUString & rClashingName, + /* [out] */ uno::Any & rException, + /* [out] */ OUString & rNewName ) +{ + rtl::Reference< ucbhelper::SimpleNameClashResolveRequest > xRequest( + new ucbhelper::SimpleNameClashResolveRequest( + rTargetURL, // target folder URL + rClashingName + ) ); + + rException = xRequest->getRequest(); + if ( xEnv.is() ) + { + uno::Reference< task::XInteractionHandler > xIH + = xEnv->getInteractionHandler(); + if ( xIH.is() ) + { + + xIH->handle( xRequest ); + + rtl::Reference< ucbhelper::InteractionContinuation > + xSelection( xRequest->getSelection() ); + + if ( xSelection.is() ) + { + // Handler handled the request. + uno::Reference< task::XInteractionAbort > xAbort( + xSelection.get(), uno::UNO_QUERY ); + if ( xAbort.is() ) + { + // Abort. + return ABORT; + } + else + { + uno::Reference< + ucb::XInteractionReplaceExistingData > + xReplace( + xSelection.get(), uno::UNO_QUERY ); + if ( xReplace.is() ) + { + // Try again: Replace existing data. + return OVERWRITE; + } + else + { + uno::Reference< + ucb::XInteractionSupplyName > + xSupplyName( + xSelection.get(), uno::UNO_QUERY ); + if ( xSupplyName.is() ) + { + // Try again: Use new name. + rNewName = xRequest->getNewName(); + return NEW_NAME; + } + else + { + OSL_FAIL( "Unknown interaction continuation!" ); + return UNKNOWN; + } + } + } + } + } + } + return NOT_HANDLED; +} + +/// @throws uno::RuntimeException +bool setTitle( + const uno::Reference< ucb::XCommandProcessor > & xCommandProcessor, + const uno::Reference< ucb::XCommandEnvironment > & xEnv, + const OUString & rNewTitle ) +{ + try + { + uno::Sequence< beans::PropertyValue > aPropValues{ { /* Name */ "Title", + /* Handle */ -1, + /* Value */ uno::Any(rNewTitle), + /* State */ {} } }; + + ucb::Command aSetPropsCommand( + "setPropertyValues", + -1, + uno::Any( aPropValues ) ); + + uno::Any aResult + = xCommandProcessor->execute( aSetPropsCommand, 0, xEnv ); + + uno::Sequence< uno::Any > aErrors; + aResult >>= aErrors; + + OSL_ENSURE( aErrors.getLength() == 1, + "getPropertyValues return value invalid!" ); + + if ( aErrors[ 0 ].hasValue() ) + { + // error occurred. + OSL_FAIL( "error setting Title property!" ); + return false; + } + } + catch ( uno::RuntimeException const & ) + { + throw; + } + catch ( uno::Exception const & ) + { + return false; + } + + return true; +} + +/// @throws uno::Exception +uno::Reference< ucb::XContent > createNew( + const TransferCommandContext & rContext, + const uno::Reference< ucb::XContent > & xTarget, + bool bSourceIsFolder, + bool bSourceIsDocument, + bool bSourceIsLink ) +{ + + + // (1) Obtain creatable types from target. + + + // First, try it using "CreatabeleContentsInfo" property and + // "createNewContent" command -> the "new" way. + + uno::Reference< ucb::XCommandProcessor > xCommandProcessorT( + xTarget, uno::UNO_QUERY ); + if ( !xCommandProcessorT.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Folder", uno::Any(rContext.aArg.TargetURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_CREATE, + aArgs, + rContext.xOrigEnv, + "Target is no XCommandProcessor!", + rContext.xProcessor ); + // Unreachable + } + + uno::Sequence< beans::Property > aPropsToObtain{ makeProperty("CreatableContentsInfo", -1) }; + + ucb::Command aGetPropsCommand( + "getPropertyValues", + -1, + uno::Any( aPropsToObtain ) ); + + uno::Reference< sdbc::XRow > xRow; + xCommandProcessorT->execute( aGetPropsCommand, 0, rContext.xEnv ) >>= xRow; + + uno::Sequence< ucb::ContentInfo > aTypesInfo; + bool bGotTypesInfo = false; + + if ( xRow.is() ) + { + uno::Any aValue = xRow->getObject( + 1, uno::Reference< container::XNameAccess >() ); + if ( aValue.hasValue() && ( aValue >>= aTypesInfo ) ) + { + bGotTypesInfo = true; + } + } + + uno::Reference< ucb::XContentCreator > xCreator; + + if ( !bGotTypesInfo ) + { + // Second, try it using XContentCreator interface -> the "old" way (not + // providing the chance to supply an XCommandEnvironment. + + xCreator.set( xTarget, uno::UNO_QUERY ); + + if ( !xCreator.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Folder", uno::Any(rContext.aArg.TargetURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_CREATE, + aArgs, + rContext.xOrigEnv, + "Target is no XContentCreator!", + rContext.xProcessor ); + // Unreachable + } + + aTypesInfo = xCreator->queryCreatableContentsInfo(); + } + + if ( !aTypesInfo.hasElements() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Folder", uno::Any(rContext.aArg.TargetURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_CREATE, + aArgs, + rContext.xOrigEnv, + "No types creatable!", + rContext.xProcessor ); + // Unreachable + } + + // (2) Try to find a matching target type for the source object. + + std::function<bool(const sal_Int32)> lCompare; + + if ( rContext.aArg.Operation == ucb::TransferCommandOperation_LINK ) + { + // Create link + lCompare = [](const sal_Int32 nAttribs) { return !!( nAttribs & ucb::ContentInfoAttribute::KIND_LINK ); }; + } + else if ( ( rContext.aArg.Operation == ucb::TransferCommandOperation_COPY ) || + ( rContext.aArg.Operation == ucb::TransferCommandOperation_MOVE ) ) + { + // Copy / Move + // Is source a link? Create link in target folder then. + if ( bSourceIsLink ) + { + lCompare = [](const sal_Int32 nAttribs) { return !!( nAttribs & ucb::ContentInfoAttribute::KIND_LINK ); }; + } + else + { + // (not a and not b) or (a and b) + // not( a or b) or (a and b) + lCompare = [bSourceIsFolder, bSourceIsDocument](const sal_Int32 nAttribs) { + return ( bSourceIsFolder == !!( nAttribs & ucb::ContentInfoAttribute::KIND_FOLDER ) ) + && ( bSourceIsDocument == !!( nAttribs & ucb::ContentInfoAttribute::KIND_DOCUMENT ) ) ; + }; + } + } + else + { + ucbhelper::cancelCommandExecution( + uno::Any( lang::IllegalArgumentException( + "Unknown transfer operation!", + rContext.xProcessor, + -1 ) ), + rContext.xOrigEnv ); + // Unreachable + } + + uno::Reference< ucb::XContent > xNew; + auto pTypeInfo = std::find_if(std::cbegin(aTypesInfo), std::cend(aTypesInfo), + [&lCompare](const ucb::ContentInfo& rTypeInfo) { return lCompare(rTypeInfo.Attributes); }); + if (pTypeInfo != std::cend(aTypesInfo)) + { + // (3) Create a new, empty object of matched type. + + if ( !xCreator.is() ) + { + // First, try it using "CreatabeleContentsInfo" property and + // "createNewContent" command -> the "new" way. + ucb::Command aCreateNewCommand( + "createNewContent", + -1, + uno::Any( *pTypeInfo ) ); + + xCommandProcessorT->execute( aCreateNewCommand, 0, rContext.xEnv ) + >>= xNew; + } + else + { + // Second, try it using XContentCreator interface -> the "old" + // way (not providing the chance to supply an XCommandEnvironment. + + xNew = xCreator->createNewContent( *pTypeInfo ); + } + + if ( !xNew.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Folder", uno::Any(rContext.aArg.TargetURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_CREATE, + aArgs, + rContext.xOrigEnv, + "createNewContent failed!", + rContext.xProcessor ); + // Unreachable + } + } + + return xNew; +} + +/// @throws uno::Exception +void transferProperties( + const TransferCommandContext & rContext, + const uno::Reference< ucb::XCommandProcessor > & xCommandProcessorS, + const uno::Reference< ucb::XCommandProcessor > & xCommandProcessorN ) +{ + ucb::Command aGetPropertySetInfoCommand( + "getPropertySetInfo", + -1, + uno::Any() ); + + uno::Reference< beans::XPropertySetInfo > xInfo; + xCommandProcessorS->execute( aGetPropertySetInfoCommand, 0, rContext.xEnv ) + >>= xInfo; + + if ( !xInfo.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(rContext.aArg.SourceURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + aArgs, + rContext.xOrigEnv, + "Unable to get propertyset info from source object!", + rContext.xProcessor ); + // Unreachable + } + + uno::Sequence< beans::Property > aAllProps = xInfo->getProperties(); + + ucb::Command aGetPropsCommand1( + "getPropertyValues", + -1, + uno::Any( aAllProps ) ); + + uno::Reference< sdbc::XRow > xRow1; + xCommandProcessorS->execute( + aGetPropsCommand1, 0, rContext.xEnv ) >>= xRow1; + + if ( !xRow1.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(rContext.aArg.SourceURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + aArgs, + rContext.xOrigEnv, + "Unable to get properties from source object!", + rContext.xProcessor ); + // Unreachable + } + + // Assemble data structure for setPropertyValues command. + + // Note: Make room for additional Title and TargetURL too. -> + 2 + uno::Sequence< beans::PropertyValue > aPropValues( + aAllProps.getLength() + 2 ); + auto pPropValues = aPropValues.getArray(); + + bool bHasTitle = rContext.aArg.NewTitle.isEmpty(); + bool bHasTargetURL = ( rContext.aArg.Operation + != ucb::TransferCommandOperation_LINK ); + + sal_Int32 nWritePos = 0; + for ( sal_Int32 m = 0; m < aAllProps.getLength(); ++m ) + { + const beans::Property & rCurrProp = aAllProps[ m ]; + beans::PropertyValue & rCurrValue = pPropValues[ nWritePos ]; + + uno::Any aValue; + + if ( rCurrProp.Name == "Title" ) + { + // Supply new title, if given. + if ( !bHasTitle ) + { + bHasTitle = true; + aValue <<= rContext.aArg.NewTitle; + } + } + else if ( rCurrProp.Name == "TargetURL" ) + { + // Supply source URL as link target for the new link to create. + if ( !bHasTargetURL ) + { + bHasTargetURL = true; + aValue <<= rContext.aArg.SourceURL; + } + } + + if ( !aValue.hasValue() ) + { + try + { + aValue = xRow1->getObject( + m + 1, uno::Reference< container::XNameAccess >() ); + } + catch ( sdbc::SQLException const & ) + { + // Argh! But try to bring things to an end. Perhaps the + // mad property is not really important... + } + } + + if ( aValue.hasValue() ) + { + rCurrValue.Name = rCurrProp.Name; + rCurrValue.Handle = rCurrProp.Handle; + rCurrValue.Value = aValue; +// rCurrValue.State = + + nWritePos++; + } + } + + // Title needed, but not set yet? + if ( !bHasTitle && !rContext.aArg.NewTitle.isEmpty() ) + { + pPropValues[ nWritePos ].Name = "Title"; + pPropValues[ nWritePos ].Handle = -1; + pPropValues[ nWritePos ].Value <<= rContext.aArg.NewTitle; + + nWritePos++; + } + + // TargetURL needed, but not set yet? + if ( !bHasTargetURL && ( rContext.aArg.Operation + == ucb::TransferCommandOperation_LINK ) ) + { + pPropValues[ nWritePos ].Name = "TargetURL"; + pPropValues[ nWritePos ].Handle = -1; + pPropValues[ nWritePos ].Value <<= rContext.aArg.SourceURL; + + nWritePos++; + } + + aPropValues.realloc( nWritePos ); + + // Set properties at new object. + + ucb::Command aSetPropsCommand( + "setPropertyValues", + -1, + uno::Any( aPropValues ) ); + + xCommandProcessorN->execute( aSetPropsCommand, 0, rContext.xEnv ); + + // @@@ What to do with source props that are not supported by the + // new object? addProperty ??? +} + +/// @throws uno::Exception +uno::Reference< io::XInputStream > getInputStream( + const TransferCommandContext & rContext, + const uno::Reference< ucb::XCommandProcessor > & xCommandProcessorS ) +{ + uno::Reference< io::XInputStream > xInputStream; + + + // (1) Try to get data as XInputStream via XActiveDataSink. + + + try + { + uno::Reference< io::XActiveDataSink > xSink = new ActiveDataSink; + + ucb::OpenCommandArgument2 aArg; + aArg.Mode = ucb::OpenMode::DOCUMENT; + aArg.Priority = 0; // unused + aArg.Sink = xSink; + aArg.Properties = uno::Sequence< beans::Property >( 0 ); // unused + + ucb::Command aOpenCommand( + "open", + -1, + uno::Any( aArg ) ); + + xCommandProcessorS->execute( aOpenCommand, 0, rContext.xEnv ); + xInputStream = xSink->getInputStream(); + } + catch ( uno::RuntimeException const & ) + { + throw; + } + catch ( uno::Exception const & ) + { + // will be handled below. + } + + if ( !xInputStream.is() ) + { + + + // (2) Try to get data via XOutputStream. + + + try + { + uno::Reference< io::XOutputStream > xOutputStream( io::Pipe::create(rContext.m_xContext), uno::UNO_QUERY_THROW ); + + ucb::OpenCommandArgument2 aArg; + aArg.Mode = ucb::OpenMode::DOCUMENT; + aArg.Priority = 0; // unused + aArg.Sink = xOutputStream; + aArg.Properties = uno::Sequence< beans::Property >( 0 ); + + ucb::Command aOpenCommand( + "open", + -1, + uno::Any( aArg ) ); + + xCommandProcessorS->execute( aOpenCommand, 0, rContext.xEnv ); + + xInputStream.set( xOutputStream, uno::UNO_QUERY ); + } + catch ( uno::RuntimeException const & ) + { + throw; + } + catch ( uno::Exception const & ) + { + OSL_FAIL( "unable to get input stream from document!" ); + } + } + + return xInputStream; +} + +/// @throws uno::Exception +uno::Reference< sdbc::XResultSet > getResultSet( + const TransferCommandContext & rContext, + const uno::Reference< ucb::XCommandProcessor > & xCommandProcessorS ) +{ + uno::Reference< sdbc::XResultSet > xResultSet; + + uno::Sequence< beans::Property > aProps{ makeProperty("IsFolder", -1 /* unknown */), + makeProperty("IsDocument", -1 /* unknown */), + makeProperty("TargetURL", -1 /* unknown */) }; + + ucb::OpenCommandArgument2 aArg; + aArg.Mode = ucb::OpenMode::ALL; + aArg.Priority = 0; // unused + aArg.Sink = nullptr; + aArg.Properties = aProps; + + ucb::Command aOpenCommand( "open", + -1, + uno::Any( aArg ) ); + try + { + uno::Reference< ucb::XDynamicResultSet > xSet; + xCommandProcessorS->execute( aOpenCommand, 0, rContext.xEnv ) >>= xSet; + + if ( xSet.is() ) + xResultSet = xSet->getStaticResultSet(); + } + catch ( uno::RuntimeException const & ) + { + throw; + } + catch ( uno::Exception const & ) + { + OSL_FAIL( "unable to get result set from folder!" ); + } + + return xResultSet; +} + +/// @throws uno::Exception +void handleNameClashRename( + const TransferCommandContext & rContext, + const uno::Reference< ucb::XContent > & xNew, + const uno::Reference< + ucb::XCommandProcessor > & xCommandProcessorN, + const uno::Reference< + ucb::XCommandProcessor > & xCommandProcessorS, + /* [inout] */ uno::Reference< io::XInputStream > & xInputStream ) +{ + sal_Int32 nTry = 0; + + // Obtain old title. + uno::Sequence< beans::Property > aProps{ makeProperty("Title", -1) }; + + ucb::Command aGetPropsCommand( + "getPropertyValues", + -1, + uno::Any( aProps ) ); + + uno::Reference< sdbc::XRow > xRow; + xCommandProcessorN->execute( aGetPropsCommand, 0, rContext.xEnv ) >>= xRow; + + if ( !xRow.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(xNew->getIdentifier()->getContentIdentifier())} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + aArgs, + rContext.xOrigEnv, + "Unable to get properties from new object!", + rContext.xProcessor ); + // Unreachable + } + + OUString aOldTitle = xRow->getString( 1 ); + if ( aOldTitle.isEmpty() ) + { + ucbhelper::cancelCommandExecution( + uno::Any( beans::UnknownPropertyException( + "Unable to get property 'Title' from new object!", + rContext.xProcessor ) ), + rContext.xOrigEnv ); + // Unreachable + } + + // Some pseudo-intelligence for not destroying file extensions. + OUString aOldTitlePre; + OUString aOldTitlePost; + sal_Int32 nPos = aOldTitle.lastIndexOf( '.' ); + if ( nPos != -1 ) + { + aOldTitlePre = aOldTitle.copy( 0, nPos ); + aOldTitlePost = aOldTitle.copy( nPos ); + } + else + aOldTitlePre = aOldTitle; + + if ( nPos > 0 ) + aOldTitlePre += "_"; + + bool bContinue = true; + do + { + nTry++; + + OUString aNewTitle = aOldTitlePre + OUString::number( nTry ) + + aOldTitlePost; + + // Set new title + setTitle( xCommandProcessorN, rContext.xEnv, aNewTitle ); + + // Retry inserting the content. + try + { + // Previous try may have read from stream. Seek to begin (if + // optional interface XSeekable is supported) or get a new stream. + if ( xInputStream.is() ) + { + uno::Reference< io::XSeekable > xSeekable( + xInputStream, uno::UNO_QUERY ); + if ( xSeekable.is() ) + { + try + { + xSeekable->seek( 0 ); + } + catch ( lang::IllegalArgumentException const & ) + { + xInputStream.clear(); + } + catch ( io::IOException const & ) + { + xInputStream.clear(); + } + } + else + xInputStream.clear(); + + if ( !xInputStream.is() ) + { + xInputStream + = getInputStream( rContext, xCommandProcessorS ); + if ( !xInputStream.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(xNew->getIdentifier()->getContentIdentifier())} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + aArgs, + rContext.xOrigEnv, + "Got no data stream from source!", + rContext.xProcessor ); + // Unreachable + } + } + } + + ucb::InsertCommandArgument2 aArg; + aArg.Data = xInputStream; + aArg.ReplaceExisting = false; + + ucb::Command aInsertCommand( + "insert", + -1, + uno::Any( aArg ) ); + + xCommandProcessorN->execute( aInsertCommand, 0, rContext.xEnv ); + + // Success! + bContinue = false; + } + catch ( uno::RuntimeException const & ) + { + throw; + } + catch ( uno::Exception const & ) + { + } + } + while ( bContinue && ( nTry < 50 ) ); + + if ( nTry == 50 ) + { + ucbhelper::cancelCommandExecution( + uno::Any( + ucb::UnsupportedNameClashException( + "Unable to resolve name clash!", + rContext.xProcessor, + ucb::NameClash::RENAME ) ), + rContext.xOrigEnv ); + // Unreachable + } +} + +/// @throws uno::Exception +void globalTransfer_( + const TransferCommandContext & rContext, + const uno::Reference< ucb::XContent > & xSource, + const uno::Reference< ucb::XContent > & xTarget, + const uno::Reference< sdbc::XRow > & xSourceProps ) +{ + // IsFolder: property is required. + bool bSourceIsFolder = xSourceProps->getBoolean( 1 ); + if ( !bSourceIsFolder && xSourceProps->wasNull() ) + { + ucbhelper::cancelCommandExecution( + uno::Any( beans::UnknownPropertyException( + "Unable to get property 'IsFolder' from source object!", + rContext.xProcessor ) ), + rContext.xOrigEnv ); + // Unreachable + } + + // IsDocument: property is required. + bool bSourceIsDocument = xSourceProps->getBoolean( 2 ); + if ( !bSourceIsDocument && xSourceProps->wasNull() ) + { + ucbhelper::cancelCommandExecution( + uno::Any( beans::UnknownPropertyException( + "Unable to get property 'IsDocument' from source object!", + rContext.xProcessor ) ), + rContext.xOrigEnv ); + // Unreachable + } + + // TargetURL: property is optional. + bool bSourceIsLink = !xSourceProps->getString( 3 ).isEmpty(); + + + // (1) Try to find a matching target type for the source object and + // create a new, empty object of that type. + + + uno::Reference< ucb::XContent > xNew = createNew( rContext, + xTarget, + bSourceIsFolder, + bSourceIsDocument, + bSourceIsLink ); + if ( !xNew.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Folder", uno::Any(rContext.aArg.TargetURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_CREATE, + aArgs, + rContext.xOrigEnv, + "No matching content type at target!", + rContext.xProcessor ); + // Unreachable + } + + + // (2) Transfer property values from source to new object. + + + uno::Reference< ucb::XCommandProcessor > xCommandProcessorN( + xNew, uno::UNO_QUERY ); + if ( !xCommandProcessorN.is() ) + { + uno::Any aProps(beans::PropertyValue( + "Uri", + -1, + uno::Any( + xNew->getIdentifier()-> + getContentIdentifier()), + beans::PropertyState_DIRECT_VALUE)); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_WRITE, + uno::Sequence< uno::Any >(&aProps, 1), + rContext.xOrigEnv, + "New content is not a XCommandProcessor!", + rContext.xProcessor ); + // Unreachable + } + + // Obtain all properties from source. + + uno::Reference< ucb::XCommandProcessor > xCommandProcessorS( + xSource, uno::UNO_QUERY ); + if ( !xCommandProcessorS.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(rContext.aArg.SourceURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + aArgs, + rContext.xOrigEnv, + "Source content is not a XCommandProcessor!", + rContext.xProcessor ); + // Unreachable + } + + transferProperties( rContext, xCommandProcessorS, xCommandProcessorN ); + + + // (3) Try to obtain a data stream from source. + + + uno::Reference< io::XInputStream > xInputStream; + + if ( bSourceIsDocument && ( rContext.aArg.Operation + != ucb::TransferCommandOperation_LINK ) ) + xInputStream = getInputStream( rContext, xCommandProcessorS ); + + + // (4) Try to obtain a resultset (children) from source. + + + uno::Reference< sdbc::XResultSet > xResultSet; + + if ( bSourceIsFolder && ( rContext.aArg.Operation + != ucb::TransferCommandOperation_LINK ) ) + xResultSet = getResultSet( rContext, xCommandProcessorS ); + + + // (5) Insert (store) new content. + + + ucb::InsertCommandArgument2 aArg; + aArg.Data = xInputStream; + aArg.MimeType = rContext.aArg.MimeType; + aArg.DocumentId = rContext.aArg.DocumentId; + + switch ( rContext.aArg.NameClash ) + { + case ucb::NameClash::OVERWRITE: + aArg.ReplaceExisting = true; + break; + + case ucb::NameClash::ERROR: + case ucb::NameClash::RENAME: + case ucb::NameClash::KEEP: // deprecated + case ucb::NameClash::ASK: + aArg.ReplaceExisting = false; + break; + + default: + aArg.ReplaceExisting = false; + OSL_FAIL( "Unknown nameclash directive!" ); + break; + } + + OUString aDesiredName = createDesiredName( rContext.aArg ); + + bool bRetry; + do + { + bRetry = false; + + try + { + ucb::Command aInsertCommand( + "insert", + -1, + uno::Any( aArg ) ); + + xCommandProcessorN->execute( aInsertCommand, 0, rContext.xEnv ); + } + catch ( ucb::UnsupportedNameClashException const & exc ) + { + OSL_ENSURE( !aArg.ReplaceExisting, + "BUG: UnsupportedNameClashException not allowed here!" ); + + if (exc.NameClash != ucb::NameClash::ERROR) { + OSL_FAIL( "BUG: NameClash::ERROR expected!" ); + } + + // No chance to solve name clashes, because I'm not able to detect + // whether there is one. + throw ucb::UnsupportedNameClashException( + "Unable to resolve name clashes, no chance to detect " + "that there is one!", + rContext.xProcessor, + rContext.aArg.NameClash ); + } + catch ( ucb::NameClashException const & ) + { + // The 'insert' command throws a NameClashException if the parameter + // ReplaceExisting of the command's argument was set to false and + // there exists a resource with a clashing name in the target folder + // of the operation. + + // 'insert' command has no direct support for name clashes other + // than ERROR ( ReplaceExisting == false ) and OVERWRITE + // ( ReplaceExisting == true ). So we have to implement the + // other name clash handling directives on top of the content. + + // @@@ 'insert' command should be extended that it accepts a + // name clash handling directive, exactly like 'transfer' command. + + switch ( rContext.aArg.NameClash ) + { + case ucb::NameClash::OVERWRITE: + { + ucbhelper::cancelCommandExecution( + uno::Any( + ucb::UnsupportedNameClashException( + "BUG: insert + replace == true MUST NOT " + "throw NameClashException.", + rContext.xProcessor, + rContext.aArg.NameClash ) ), + rContext.xOrigEnv ); + [[fallthrough]]; // Unreachable + } + + case ucb::NameClash::ERROR: + throw; + + case ucb::NameClash::RENAME: + { + // "invent" a new valid title. + handleNameClashRename( rContext, + xNew, + xCommandProcessorN, + xCommandProcessorS, + xInputStream ); + break; + } + + case ucb::NameClash::ASK: + { + uno::Any aExc; + OUString aNewTitle; + NameClashContinuation eCont + = interactiveNameClashResolve( + rContext.xOrigEnv, // always use original environment! + rContext.aArg.TargetURL, // target folder URL + aDesiredName, + aExc, + aNewTitle ); + + switch ( eCont ) + { + case NOT_HANDLED: + // Not handled. + cppu::throwException( aExc ); + [[fallthrough]]; // break; + + case UNKNOWN: + // Handled, but not clear, how... + // fall through intended. + + case ABORT: + throw ucb::CommandFailedException( + "abort requested via interaction " + "handler", + uno::Reference< uno::XInterface >(), + aExc ); + // break; + + case OVERWRITE: + OSL_ENSURE( !aArg.ReplaceExisting, + "Hu? ReplaceExisting already true?" + ); + aArg.ReplaceExisting = true; + bRetry = true; + break; + + case NEW_NAME: + { + // set new name -> set "Title" property... + if ( setTitle( xCommandProcessorN, + rContext.xEnv, + aNewTitle ) ) + { + // remember suggested title... + aDesiredName = aNewTitle; + + // ... and try again. + bRetry = true; + } + else + { + // error setting title. Abort. + throw ucb::CommandFailedException( + "error setting Title property!", + uno::Reference< uno::XInterface >(), + aExc ); + } + break; + } + } + + OSL_ENSURE( bRetry, "bRetry must be true here!!!" ); + } + break; + + case ucb::NameClash::KEEP: // deprecated + default: + { + ucbhelper::cancelCommandExecution( + uno::Any( + ucb::UnsupportedNameClashException( + "default action, don't know how to " + "handle name clash", + rContext.xProcessor, + rContext.aArg.NameClash ) ), + rContext.xOrigEnv ); + // Unreachable + } + } + } + } + while ( bRetry ); + + + // (6) Process children of source. + + + if ( xResultSet.is() ) + { + try + { + // Iterate over children... + + uno::Reference< sdbc::XRow > xChildRow( + xResultSet, uno::UNO_QUERY ); + + if ( !xChildRow.is() ) + { + uno::Any aProps( + beans::PropertyValue( + "Uri", + -1, + uno::Any(rContext.aArg.SourceURL), + beans::PropertyState_DIRECT_VALUE)); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + uno::Sequence< uno::Any >(&aProps, 1), + rContext.xOrigEnv, + "Unable to get properties from children of source!", + rContext.xProcessor ); + // Unreachable + } + + uno::Reference< ucb::XContentAccess > xChildAccess( + xResultSet, uno::UNO_QUERY ); + + if ( !xChildAccess.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(rContext.aArg.SourceURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + aArgs, + rContext.xOrigEnv, + "Unable to get children of source!", + rContext.xProcessor ); + // Unreachable + } + + if ( xResultSet->first() ) + { + ucb::GlobalTransferCommandArgument2 aTransArg( + rContext.aArg.Operation, + OUString(), // SourceURL; filled later + xNew->getIdentifier() + ->getContentIdentifier(), // TargetURL + OUString(), // NewTitle; + rContext.aArg.NameClash, + rContext.aArg.MimeType, + rContext.aArg.DocumentId); + + TransferCommandContext aSubCtx( + rContext.m_xContext, + rContext.xProcessor, + rContext.xEnv, + rContext.xOrigEnv, + aTransArg ); + do + { + uno::Reference< ucb::XContent > xChild + = xChildAccess->queryContent(); + if ( xChild.is() ) + { + // Recursion! + + aSubCtx.aArg.SourceURL + = xChild->getIdentifier()->getContentIdentifier(); + + globalTransfer_( aSubCtx, + xChild, + xNew, + xChildRow ); + } + } + while ( xResultSet->next() ); + } + } + catch ( sdbc::SQLException const & ) + { + } + } + + try { + uno::Reference< ucb::XCommandProcessor > xcp( + xTarget, uno::UNO_QUERY ); + + uno::Any aAny; + uno::Reference< ucb::XCommandInfo > xci; + if(xcp.is()) + aAny = + xcp->execute( + ucb::Command( + "getCommandInfo", + -1, + uno::Any()), + 0, + rContext.xEnv ); + + static const OUStringLiteral cmdName(u"flush"); + if((aAny >>= xci) && xci->hasCommandByName(cmdName)) + xcp->execute( + ucb::Command( + cmdName, + -1, + uno::Any()) , + 0, + rContext.xEnv ); + } + catch( uno::Exception const & ) + { + } +} + +} /* namespace */ + + +// UniversalContentBroker implementation ( XCommandProcessor commands ). + + +uno::Reference< ucb::XCommandInfo > +UniversalContentBroker::getCommandInfo() +{ + return uno::Reference< ucb::XCommandInfo >( new CommandProcessorInfo() ); +} + + +void UniversalContentBroker::globalTransfer( + const ucb::GlobalTransferCommandArgument2 & rArg, + const uno::Reference< ucb::XCommandEnvironment > & xEnv ) +{ + // Use own command environment with own interaction handler intercepting + // some interaction requests that shall not be handled by the user-supplied + // interaction handler. + uno::Reference< ucb::XCommandEnvironment > xLocalEnv; + if (xEnv.is()) + { + xLocalEnv.set( ucb::CommandEnvironment::create( + m_xContext, + new InteractionHandlerProxy( xEnv->getInteractionHandler() ), + xEnv->getProgressHandler() ) ); + } + + + // (1) Try to transfer the content using 'transfer' command. + + + uno::Reference< ucb::XContent > xTarget; + uno::Reference< ucb::XContentIdentifier > xId + = createContentIdentifier( rArg.TargetURL ); + if ( xId.is() ) + { + try + { + xTarget = queryContent( xId ); + } + catch ( ucb::IllegalIdentifierException const & ) + { + } + } + + if ( !xTarget.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(rArg.TargetURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + aArgs, + xEnv, + "Can't instantiate target object!", + this ); + // Unreachable + } + + if ( ( rArg.Operation == ucb::TransferCommandOperation_COPY ) || + ( rArg.Operation == ucb::TransferCommandOperation_MOVE ) ) + { + uno::Reference< ucb::XCommandProcessor > xCommandProcessor( + xTarget, uno::UNO_QUERY ); + if ( !xCommandProcessor.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(rArg.TargetURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + aArgs, + xEnv, + "Target content is not a XCommandProcessor!", + this ); + // Unreachable + } + + ucb::TransferInfo2 aTransferArg( + ( rArg.Operation + == ucb::TransferCommandOperation_MOVE ), // MoveData + rArg.SourceURL, + rArg.NewTitle, + rArg.NameClash, + rArg.MimeType ); + + bool bRetry; + do + { + bRetry = false; + + try + { + ucb::Command aCommand( + "transfer", // Name + -1, // Handle + uno::Any( aTransferArg ) ); // Argument + + xCommandProcessor->execute( aCommand, 0, xLocalEnv ); + + // Command succeeded. We're done. + return; + } + catch ( ucb::InteractiveBadTransferURLException const & ) + { + // Source URL is not supported by target. Try to transfer + // the content "manually". + } + catch ( ucb::UnsupportedCommandException const & ) + { + // 'transfer' command is not supported by commandprocessor. + // Try to transfer manually. + } + catch ( ucb::UnsupportedNameClashException const & exc ) + { + OSL_ENSURE( aTransferArg.NameClash == exc.NameClash, + "nameclash mismatch!" ); + if ( exc.NameClash == ucb::NameClash::ASK ) + { + // Try to detect a name clash by invoking "transfer" with + // NameClash::ERROR. + try + { + ucb::TransferInfo2 aTransferArg1( + aTransferArg.MoveData, + aTransferArg.SourceURL, + aTransferArg.NewTitle, + ucb::NameClash::ERROR, + aTransferArg.MimeType ); + + ucb::Command aCommand1( + "transfer", + -1, + uno::Any( aTransferArg1 ) ); + + xCommandProcessor->execute( aCommand1, 0, xLocalEnv ); + + // Command succeeded. We're done. + return; + } + catch ( ucb::UnsupportedNameClashException const & ) + { + // No chance to solve name clashes, because I'm not + // able to detect whether there is one. + throw exc; // Not just 'throw;'! + } + catch ( ucb::NameClashException const & ) + { + // There's a clash. Use interaction handler to solve it. + + uno::Any aExc; + OUString aNewTitle; + NameClashContinuation eCont + = interactiveNameClashResolve( + xEnv, // always use original environment! + rArg.TargetURL, // target folder URL + createDesiredName( + aTransferArg ), // clashing name + aExc, + aNewTitle ); + + switch ( eCont ) + { + case NOT_HANDLED: + // Not handled. + cppu::throwException( aExc ); + [[fallthrough]]; // break; + + case UNKNOWN: + // Handled, but not clear, how... + // fall through intended. + + case ABORT: + throw ucb::CommandFailedException( + "abort requested via interaction " + "handler", + uno::Reference< uno::XInterface >(), + aExc ); +// break; + + case OVERWRITE: + aTransferArg.NameClash + = ucb::NameClash::OVERWRITE; + bRetry = true; + break; + + case NEW_NAME: + aTransferArg.NewTitle = aNewTitle; + bRetry = true; + break; + } + + OSL_ENSURE( bRetry, "bRetry must be true here!!!" ); + } + } + else + { + throw; + } + } + } + while ( bRetry ); + } + + + // (2) Try to transfer the content "manually". + + + uno::Reference< ucb::XContent > xSource; + try + { + uno::Reference< ucb::XContentIdentifier > xId2 + = createContentIdentifier( rArg.SourceURL ); + if ( xId2.is() ) + xSource = queryContent( xId2 ); + } + catch ( ucb::IllegalIdentifierException const & ) + { + // Error handling via "if ( !xSource.is() )" below. + } + + if ( !xSource.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(rArg.SourceURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + aArgs, + xEnv, + "Can't instantiate source object!", + this ); + // Unreachable + } + + uno::Reference< ucb::XCommandProcessor > xCommandProcessor( + xSource, uno::UNO_QUERY ); + if ( !xCommandProcessor.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(rArg.SourceURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + aArgs, + xEnv, + "Source content is not a XCommandProcessor!", + this ); + // Unreachable + } + + // Obtain interesting property values from source... + + uno::Sequence< beans::Property > aProps{ makeProperty("IsFolder", -1 /* unknown */), + makeProperty("IsDocument", -1 /* unknown */), + makeProperty("TargetURL", -1 /* unknown */), + makeProperty("BaseURI", -1 /* unknown */) }; + + ucb::Command aGetPropsCommand( + "getPropertyValues", + -1, + uno::Any( aProps ) ); + + uno::Reference< sdbc::XRow > xRow; + xCommandProcessor->execute( aGetPropsCommand, 0, xLocalEnv ) >>= xRow; + + if ( !xRow.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(rArg.SourceURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + aArgs, + xEnv, + "Unable to get properties from source object!", + this ); + // Unreachable + } + + TransferCommandContext aTransferCtx( + m_xContext, this, xLocalEnv, xEnv, rArg ); + + if ( rArg.NewTitle.isEmpty() ) + { + // BaseURI: property is optional. + OUString aBaseURI( xRow->getString( 4 ) ); + if ( !aBaseURI.isEmpty() ) + { + aTransferCtx.aArg.NewTitle + = createDesiredName( aBaseURI, OUString() ); + } + } + + // Do it! + globalTransfer_( aTransferCtx, xSource, xTarget, xRow ); + + + // (3) Delete source, if operation is MOVE. + + + if ( rArg.Operation != ucb::TransferCommandOperation_MOVE ) + return; + + try + { + ucb::Command aCommand( + "delete", // Name + -1, // Handle + uno::Any( true ) ); // Argument + + xCommandProcessor->execute( aCommand, 0, xLocalEnv ); + } + catch ( uno::Exception const & ) + { + OSL_FAIL( "Cannot delete source object!" ); + throw; + } +} + +uno::Any UniversalContentBroker::checkIn( const ucb::CheckinArgument& rArg, + const uno::Reference< ucb::XCommandEnvironment >& xEnv ) +{ + uno::Any aRet; + // Use own command environment with own interaction handler intercepting + // some interaction requests that shall not be handled by the user-supplied + // interaction handler. + uno::Reference< ucb::XCommandEnvironment > xLocalEnv; + if (xEnv.is()) + { + xLocalEnv.set( ucb::CommandEnvironment::create( + m_xContext, + new InteractionHandlerProxy( xEnv->getInteractionHandler() ), + xEnv->getProgressHandler() ) ); + } + + uno::Reference< ucb::XContent > xTarget; + uno::Reference< ucb::XContentIdentifier > xId + = createContentIdentifier( rArg.TargetURL ); + if ( xId.is() ) + { + try + { + xTarget = queryContent( xId ); + } + catch ( ucb::IllegalIdentifierException const & ) + { + } + } + + if ( !xTarget.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(rArg.TargetURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + aArgs, + xEnv, + "Can't instantiate target object!", + this ); + // Unreachable + } + + uno::Reference< ucb::XCommandProcessor > xCommandProcessor( + xTarget, uno::UNO_QUERY ); + if ( !xCommandProcessor.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(rArg.TargetURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + aArgs, + xEnv, + "Target content is not a XCommandProcessor!", + this ); + // Unreachable + } + + try + { + ucb::Command aCommand( + "checkin", -1, + uno::Any( rArg ) ); + + aRet = xCommandProcessor->execute( aCommand, 0, xLocalEnv ); + } + catch ( ucb::UnsupportedCommandException const & ) + { + // 'checkin' command is not supported by commandprocessor: + // ignore. + } + return aRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |