/* -*- 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 "ucblockbytes.hxx" #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 using namespace ::com::sun::star::uno; using namespace ::com::sun::star::io; using namespace ::com::sun::star::ucb; using namespace ::com::sun::star::task; using namespace ::com::sun::star::lang; using namespace ::com::sun::star::beans; namespace utl { namespace { /** Helper class for getting a XInputStream when opening a content */ class UcbDataSink_Impl : public ::cppu::WeakImplHelper< XActiveDataControl, XActiveDataSink > { UcbLockBytesRef m_xLockBytes; public: explicit UcbDataSink_Impl( UcbLockBytes* pLockBytes ) : m_xLockBytes( pLockBytes ) {} // XActiveDataControl. virtual void SAL_CALL addListener ( const Reference &/*rxListener*/) override {} virtual void SAL_CALL removeListener ( const Reference &/*rxListener*/) override {} virtual void SAL_CALL start() override {} virtual void SAL_CALL terminate() override { m_xLockBytes->terminate_Impl(); } // XActiveDataSink. virtual void SAL_CALL setInputStream ( const Reference &rxInputStream) override { m_xLockBytes->setInputStream_Impl (rxInputStream); } virtual Reference SAL_CALL getInputStream() override { return m_xLockBytes->getInputStream_Impl(); } }; /** Helper class for getting a XStream when opening a content */ class UcbStreamer_Impl : public ::cppu::WeakImplHelper< XActiveDataStreamer, XActiveDataControl > { Reference < XStream > m_xStream; UcbLockBytesRef m_xLockBytes; public: explicit UcbStreamer_Impl( UcbLockBytes* pLockBytes ) : m_xLockBytes( pLockBytes ) {} // XActiveDataControl. virtual void SAL_CALL addListener ( const Reference &/*rxListener*/) override {} virtual void SAL_CALL removeListener ( const Reference &/*rxListener*/) override {} virtual void SAL_CALL start() override {} virtual void SAL_CALL terminate() override { m_xLockBytes->terminate_Impl(); } // XActiveDataStreamer virtual void SAL_CALL setStream( const Reference< XStream >& aStream ) override { m_xStream = aStream; m_xLockBytes->setStream_Impl( aStream ); } virtual Reference< XStream > SAL_CALL getStream() override { return m_xStream; } }; /** Helper class for managing interactions and progress when executing UCB commands */ class UcbTaskEnvironment : public ::cppu::WeakImplHelper< XCommandEnvironment > { Reference< XInteractionHandler > m_xInteractionHandler; Reference< XProgressHandler > m_xProgressHandler; public: UcbTaskEnvironment( const Reference< XInteractionHandler>& rxInteractionHandler, const Reference< XProgressHandler>& rxProgressHandler ) : m_xInteractionHandler( rxInteractionHandler ) , m_xProgressHandler( rxProgressHandler ) {} virtual Reference SAL_CALL getInteractionHandler() override { return m_xInteractionHandler; } virtual Reference SAL_CALL getProgressHandler() override { return m_xProgressHandler; } }; /** Helper class for property change notifies when executing UCB commands */ class UcbPropertiesChangeListener_Impl : public ::cppu::WeakImplHelper< XPropertiesChangeListener > { public: UcbLockBytesRef m_xLockBytes; explicit UcbPropertiesChangeListener_Impl( UcbLockBytesRef const & rRef ) : m_xLockBytes( rRef ) {} virtual void SAL_CALL disposing ( const EventObject &/*rEvent*/) override {} virtual void SAL_CALL propertiesChange ( const Sequence &rEvent) override; }; } void SAL_CALL UcbPropertiesChangeListener_Impl::propertiesChange ( const Sequence &rEvent) { for (const auto& rPropChangeEvent : rEvent) { if (rPropChangeEvent.PropertyName == "DocumentHeader") { m_xLockBytes->SetStreamValid_Impl(); } } } namespace { class Moderator : public osl::Thread { // usage restriction: // It might be possible, that the call to the interactionhandler and/or // progresshandler is done asynchronously, while the 'execute' simply // returns. This would imply that these class must be refcounted!!! public: /// @throws ContentCreationException /// @throws RuntimeException Moderator( Reference < XContent > const & xContent, Reference < XInteractionHandler > const & xInteract, const Command& rArg ); enum class ResultType { NORESULT, INTERACTIONREQUEST, // reply expected INPUTSTREAM, STREAM, RESULT, TIMEDOUT, COMMANDABORTED, COMMANDFAILED, INTERACTIVEIO, UNSUPPORTED, GENERAL }; class ConditionRes : public salhelper::Condition { public: ConditionRes(osl::Mutex& aMutex,Moderator& aModerator) : salhelper::Condition(aMutex), m_aModerator(aModerator) { } protected: bool applies() const override { return m_aModerator.m_aResultType != ResultType::NORESULT; } private: Moderator& m_aModerator; }; struct Result { ResultType type; Any result; IOErrorCode ioErrorCode; }; Result getResult(const sal_uInt32 milliSec); enum ReplyType { NOREPLY, EXIT, REQUESTHANDLED }; class ConditionRep : public salhelper::Condition { public: ConditionRep(osl::Mutex& aMutex,Moderator& aModerator) : salhelper::Condition(aMutex), m_aModerator(aModerator) { } protected: bool applies() const override { return m_aModerator.m_aReplyType != NOREPLY; } private: Moderator& m_aModerator; }; void setReply(ReplyType); void handle( const Reference& Request ); void setStream(const Reference< XStream >& aStream); void setInputStream(const Reference &rxInputStream); protected: virtual void SAL_CALL run() override; virtual void SAL_CALL onTerminated() override; private: osl::Mutex m_aMutex; friend class ConditionRes; ConditionRes m_aRes; ResultType m_aResultType; IOErrorCode m_nIOErrorCode; Any m_aResult; friend class ConditionRep; ConditionRep m_aRep; ReplyType m_aReplyType; Command m_aArg; ::ucbhelper::Content m_aContent; }; class ModeratorsActiveDataStreamer : public ::cppu::WeakImplHelper { public: explicit ModeratorsActiveDataStreamer(Moderator &theModerator); // XActiveDataStreamer virtual void SAL_CALL setStream( const Reference< XStream >& aStream ) override; virtual Reference SAL_CALL getStream () override { osl::MutexGuard aGuard(m_aMutex); return m_xStream; } private: Moderator& m_aModerator; osl::Mutex m_aMutex; Reference m_xStream; }; class ModeratorsActiveDataSink : public ::cppu::WeakImplHelper { public: explicit ModeratorsActiveDataSink(Moderator &theModerator); // XActiveDataSink. virtual void SAL_CALL setInputStream ( const Reference &rxInputStream ) override; virtual Reference SAL_CALL getInputStream() override { std::lock_guard aGuard(m_aMutex); return m_xStream; } private: Moderator& m_aModerator; std::mutex m_aMutex; Reference m_xStream; }; } ModeratorsActiveDataSink::ModeratorsActiveDataSink(Moderator &theModerator) : m_aModerator(theModerator) { } // XActiveDataSink. void SAL_CALL ModeratorsActiveDataSink::setInputStream ( const Reference &rxInputStream ) { m_aModerator.setInputStream(rxInputStream); std::lock_guard aGuard(m_aMutex); m_xStream = rxInputStream; } ModeratorsActiveDataStreamer::ModeratorsActiveDataStreamer( Moderator &theModerator ) : m_aModerator(theModerator) { } // XActiveDataStreamer. void SAL_CALL ModeratorsActiveDataStreamer::setStream ( const Reference &rxStream ) { m_aModerator.setStream(rxStream); osl::MutexGuard aGuard(m_aMutex); m_xStream = rxStream; } namespace { class ModeratorsInteractionHandler : public ::cppu::WeakImplHelper { public: explicit ModeratorsInteractionHandler(Moderator &theModerator); virtual void SAL_CALL handle( const Reference& Request ) override; private: Moderator& m_aModerator; }; } ModeratorsInteractionHandler::ModeratorsInteractionHandler( Moderator &aModerator) : m_aModerator(aModerator) { } void SAL_CALL ModeratorsInteractionHandler::handle( const Reference& Request ) { // wakes up the mainthread m_aModerator.handle(Request); } Moderator::Moderator( Reference < XContent > const & xContent, Reference < XInteractionHandler > const & xInteract, const Command& rArg ) : m_aMutex(), m_aRes(m_aMutex,*this), m_aResultType(ResultType::NORESULT), m_nIOErrorCode(IOErrorCode_ABORT), m_aResult(), m_aRep(m_aMutex,*this), m_aReplyType(NOREPLY), m_aArg(rArg), m_aContent( xContent, new UcbTaskEnvironment( xInteract.is() ? new ModeratorsInteractionHandler(*this) : nullptr, nullptr), comphelper::getProcessComponentContext()) { // now exchange the whole data sink stuff // with a thread safe version Reference *pxSink = nullptr; PostCommandArgument2 aPostArg; OpenCommandArgument2 aOpenArg; int dec(2); if(m_aArg.Argument >>= aPostArg) { pxSink = &aPostArg.Sink; dec = 0; } else if(m_aArg.Argument >>= aOpenArg) { pxSink = &aOpenArg.Sink; dec = 1; } if(dec ==2) throw ContentCreationException(); Reference < XActiveDataSink > xActiveSink(*pxSink,UNO_QUERY); if(xActiveSink.is()) pxSink->set( static_cast(new ModeratorsActiveDataSink(*this))); Reference xStreamer( *pxSink, UNO_QUERY ); if ( xStreamer.is() ) pxSink->set( static_cast(new ModeratorsActiveDataStreamer(*this))); if(dec == 0) m_aArg.Argument <<= aPostArg; else if(dec == 1) m_aArg.Argument <<= aOpenArg; } Moderator::Result Moderator::getResult(const sal_uInt32 milliSec) { Result ret; try { salhelper::ConditionWaiter aWaiter(m_aRes,milliSec); ret.type = m_aResultType; ret.result = m_aResult; ret.ioErrorCode = m_nIOErrorCode; // reset m_aResultType = ResultType::NORESULT; } catch (const salhelper::ConditionWaiter::timedout&) { ret.type = ResultType::TIMEDOUT; } return ret; } void Moderator::setReply(ReplyType aReplyType ) { salhelper::ConditionModifier aMod(m_aRep); m_aReplyType = aReplyType; } void Moderator::handle( const Reference& Request ) { ReplyType aReplyType; do { { salhelper::ConditionModifier aMod(m_aRes); m_aResultType = ResultType::INTERACTIONREQUEST; m_aResult <<= Request; } { salhelper::ConditionWaiter aWait(m_aRep); aReplyType = m_aReplyType; // reset m_aReplyType = NOREPLY; } if(aReplyType == EXIT) { const Sequence > aSeq( Request->getContinuations()); for(const auto& rContinuation : aSeq) { Reference aRef(rContinuation,UNO_QUERY); if(aRef.is()) { aRef->select(); } } // resignal the exit condition setReply(EXIT); break; } } while(aReplyType != REQUESTHANDLED); } void Moderator::setStream(const Reference< XStream >& aStream) { { salhelper::ConditionModifier aMod(m_aRes); m_aResultType = ResultType::STREAM; m_aResult <<= aStream; } ReplyType aReplyType; { salhelper::ConditionWaiter aWait(m_aRep); aReplyType = m_aReplyType; m_aReplyType = NOREPLY; } if(aReplyType == EXIT) setReply(EXIT); } void Moderator::setInputStream(const Reference &rxInputStream) { { salhelper::ConditionModifier aMod(m_aRes); m_aResultType = ResultType::INPUTSTREAM; m_aResult <<= rxInputStream; } ReplyType aReplyType; { salhelper::ConditionWaiter aWait(m_aRep); aReplyType = m_aReplyType; m_aReplyType = NOREPLY; } if(aReplyType == EXIT) setReply(EXIT); } void SAL_CALL Moderator::run() { osl_setThreadName("utl::Moderator"); ResultType aResultType; Any aResult; IOErrorCode nIOErrorCode = IOErrorCode_ABORT; try { aResult = m_aContent.executeCommand(m_aArg.Name,m_aArg.Argument); aResultType = ResultType::RESULT; } catch (const CommandAbortedException&) { aResultType = ResultType::COMMANDABORTED; } catch (const CommandFailedException&) { aResultType = ResultType::COMMANDFAILED; } catch (const InteractiveIOException& r) { nIOErrorCode = r.Code; aResultType = ResultType::INTERACTIVEIO; } catch (const UnsupportedDataSinkException &) { aResultType = ResultType::UNSUPPORTED; } catch (const Exception&) { aResultType = ResultType::GENERAL; } { salhelper::ConditionModifier aMod(m_aRes); m_aResultType = aResultType; m_aResult = aResult; m_nIOErrorCode = nIOErrorCode; } } void SAL_CALL Moderator::onTerminated() { { salhelper::ConditionWaiter aWaiter(m_aRep); } delete this; } /** Function for opening UCB contents synchronously, but with handled timeout; */ static bool UCBOpenContentSync_( const UcbLockBytesRef& xLockBytes, const Reference < XContent >& xContent, const Command& rArg, const Reference < XInterface >& xSink, const Reference < XInteractionHandler >& xInteract ); static bool UCBOpenContentSync( const UcbLockBytesRef& xLockBytes, Reference < XContent > const & xContent, const Command& rArg, const Reference < XInterface >& xSink, Reference < XInteractionHandler > const & xInteract ) { // http protocol must be handled in a special way: // during the opening process the input stream may change // only the last inputstream after notifying the document // headers is valid Reference xContId( xContent.is() ? xContent->getIdentifier() : nullptr ); OUString aScheme; if(xContId.is()) aScheme = xContId->getContentProviderScheme(); // now determine whether we use a timeout or not; if( ! aScheme.equalsIgnoreAsciiCase("http") && ! aScheme.equalsIgnoreAsciiCase("https") && ! aScheme.equalsIgnoreAsciiCase("vnd.sun.star.webdav") && ! aScheme.equalsIgnoreAsciiCase("vnd.sun.star.webdavs") && ! aScheme.equalsIgnoreAsciiCase("ftp")) return UCBOpenContentSync_( xLockBytes,xContent,rArg,xSink,xInteract); if ( !aScheme.equalsIgnoreAsciiCase( "http" ) && !aScheme.equalsIgnoreAsciiCase( "https" ) ) xLockBytes->SetStreamValid_Impl(); Reference< XPropertiesChangeListener > xListener; Reference< XPropertiesChangeNotifier > xProps(xContent,UNO_QUERY); if(xProps.is()) { xListener = new UcbPropertiesChangeListener_Impl(xLockBytes); xProps->addPropertiesChangeListener( Sequence< OUString >(), xListener); } bool bException(false); bool bAborted(false); bool bResultAchieved(false); Moderator* pMod = nullptr; try { pMod = new Moderator(xContent,xInteract,rArg); pMod->create(); //TODO: a protocol is missing how to join with the launched thread before exit(3), to // ensure the thread is no longer relying on any infrastructure while that // infrastructure is being shut down in atexit handlers } catch (const ContentCreationException&) { bResultAchieved = bException = true; xLockBytes->SetError( ERRCODE_IO_GENERAL ); } sal_uInt32 nTimeout(5000); // initially 5000 milliSec while(!bResultAchieved) { // try to get the result for with timeout Moderator::Result res = pMod->getResult(nTimeout); switch(res.type) { case Moderator::ResultType::STREAM: { Reference result; if(res.result >>= result) { Reference < XActiveDataStreamer > xStreamer( xSink, UNO_QUERY ); if(xStreamer.is()) xStreamer->setStream(result); } pMod->setReply(Moderator::REQUESTHANDLED); break; } case Moderator::ResultType::INPUTSTREAM: { Reference result; res.result >>= result; Reference < XActiveDataSink > xActiveSink( xSink, UNO_QUERY ); if(xActiveSink.is()) xActiveSink->setInputStream(result); pMod->setReply(Moderator::REQUESTHANDLED); break; } case Moderator::ResultType::TIMEDOUT: { Reference xRet; if(xInteract.is()) { InteractiveNetworkConnectException aExcep; INetURLObject aURL( xContId.is() ? xContId->getContentIdentifier() : OUString() ); aExcep.Server = aURL.GetHost(); aExcep.Classification = InteractionClassification_ERROR; aExcep.Message = "server not responding after five seconds"; Any request; request <<= aExcep; rtl::Reference xIR = new ucbhelper::InteractionRequest(request); rtl::Reference retryP = new ucbhelper::InteractionRetry(xIR.get()); rtl::Reference abortP = new ucbhelper::InteractionAbort(xIR.get()); Sequence > aSeq { retryP, abortP }; xIR->setContinuations(aSeq); xInteract->handle(xIR); rtl::Reference< ucbhelper::InteractionContinuation > ref = xIR->getSelection(); if(ref.is()) { Reference xInt(ref); xRet.set(xInt,UNO_QUERY); } } if(!xRet.is()) { bAborted = true; xLockBytes->SetError(ERRCODE_ABORT); } break; } case Moderator::ResultType::INTERACTIONREQUEST: { Reference Request; res.result >>= Request; xInteract->handle(Request); pMod->setReply(Moderator::REQUESTHANDLED); break; } case Moderator::ResultType::RESULT: { bResultAchieved = true; break; } case Moderator::ResultType::COMMANDABORTED: { bAborted = true; xLockBytes->SetError( ERRCODE_ABORT ); break; } case Moderator::ResultType::COMMANDFAILED: { bAborted = true; xLockBytes->SetError( ERRCODE_ABORT ); break; } case Moderator::ResultType::INTERACTIVEIO: { bException = true; if ( res.ioErrorCode == IOErrorCode_ACCESS_DENIED || res.ioErrorCode == IOErrorCode_LOCKING_VIOLATION ) xLockBytes->SetError( ERRCODE_IO_ACCESSDENIED ); else if ( res.ioErrorCode == IOErrorCode_NOT_EXISTING ) xLockBytes->SetError( ERRCODE_IO_NOTEXISTS ); else if ( res.ioErrorCode == IOErrorCode_CANT_READ ) xLockBytes->SetError( ERRCODE_IO_CANTREAD ); else xLockBytes->SetError( ERRCODE_IO_GENERAL ); break; } case Moderator::ResultType::UNSUPPORTED: { bException = true; xLockBytes->SetError( ERRCODE_IO_NOTSUPPORTED ); break; } default: { bException = true; xLockBytes->SetError( ERRCODE_IO_GENERAL ); break; } } bResultAchieved |= bException; bResultAchieved |= bAborted; if(nTimeout == 5000) nTimeout *= 2; } if(pMod) pMod->setReply(Moderator::EXIT); if ( bAborted || bException ) { Reference < XActiveDataSink > xActiveSink( xSink, UNO_QUERY ); if ( xActiveSink.is() ) xActiveSink->setInputStream( Reference < XInputStream >() ); Reference < XActiveDataStreamer > xStreamer( xSink, UNO_QUERY ); if ( xStreamer.is() ) xStreamer->setStream( Reference < XStream >() ); } Reference < XActiveDataControl > xControl( xSink, UNO_QUERY ); if ( xControl.is() ) xControl->terminate(); if ( xProps.is() ) xProps->removePropertiesChangeListener( Sequence< OUString >(), xListener ); return ( bAborted || bException ); } /** Function for opening UCB contents synchronously */ static bool UCBOpenContentSync_( const UcbLockBytesRef& xLockBytes, const Reference < XContent >& xContent, const Command& rArg, const Reference < XInterface >& xSink, const Reference < XInteractionHandler >& xInteract ) { ::ucbhelper::Content aContent( xContent, new UcbTaskEnvironment( xInteract, nullptr ), comphelper::getProcessComponentContext() ); Reference < XContentIdentifier > xIdent = xContent->getIdentifier(); OUString aScheme = xIdent->getContentProviderScheme(); // http protocol must be handled in a special way: during the opening process the input stream may change // only the last inputstream after notifying the document headers is valid if ( !aScheme.equalsIgnoreAsciiCase("http") ) xLockBytes->SetStreamValid_Impl(); Reference< XPropertiesChangeListener > xListener = new UcbPropertiesChangeListener_Impl( xLockBytes ); Reference< XPropertiesChangeNotifier > xProps ( xContent, UNO_QUERY ); if ( xProps.is() ) xProps->addPropertiesChangeListener( Sequence< OUString >(), xListener ); bool bException = false; bool bAborted = false; try { aContent.executeCommand( rArg.Name, rArg.Argument ); } catch (const CommandAbortedException&) { bAborted = true; xLockBytes->SetError( ERRCODE_ABORT ); } catch (const CommandFailedException&) { bAborted = true; xLockBytes->SetError( ERRCODE_ABORT ); } catch (const InteractiveIOException& r) { bException = true; if ( r.Code == IOErrorCode_ACCESS_DENIED || r.Code == IOErrorCode_LOCKING_VIOLATION ) xLockBytes->SetError( ERRCODE_IO_ACCESSDENIED ); else if ( r.Code == IOErrorCode_NOT_EXISTING ) xLockBytes->SetError( ERRCODE_IO_NOTEXISTS ); else if ( r.Code == IOErrorCode_CANT_READ ) xLockBytes->SetError( ERRCODE_IO_CANTREAD ); else xLockBytes->SetError( ERRCODE_IO_GENERAL ); } catch (const UnsupportedDataSinkException&) { bException = true; xLockBytes->SetError( ERRCODE_IO_NOTSUPPORTED ); } catch (const Exception&) { bException = true; xLockBytes->SetError( ERRCODE_IO_GENERAL ); } if ( bAborted || bException ) { Reference < XActiveDataSink > xActiveSink( xSink, UNO_QUERY ); if ( xActiveSink.is() ) xActiveSink->setInputStream( Reference < XInputStream >() ); Reference < XActiveDataStreamer > xStreamer( xSink, UNO_QUERY ); if ( xStreamer.is() ) xStreamer->setStream( Reference < XStream >() ); } Reference < XActiveDataControl > xControl( xSink, UNO_QUERY ); if ( xControl.is() ) xControl->terminate(); if ( xProps.is() ) xProps->removePropertiesChangeListener( Sequence< OUString >(), xListener ); return ( bAborted || bException ); } UcbLockBytes::UcbLockBytes() : m_nError( ERRCODE_NONE ) , m_bTerminated (false) , m_bDontClose( false ) , m_bStreamValid (false) { SetSynchronMode(); } UcbLockBytes::~UcbLockBytes() { if ( !m_bDontClose ) { if ( m_xInputStream.is() ) { try { m_xInputStream->closeInput(); } catch (const RuntimeException&) { } catch (const IOException&) { } } } if ( m_xInputStream.is() || !m_xOutputStream.is() ) return; try { m_xOutputStream->closeOutput(); } catch (const RuntimeException&) { } catch (const IOException&) { } } Reference < XInputStream > UcbLockBytes::getInputStream() { osl::MutexGuard aGuard( m_aMutex ); m_bDontClose = true; return m_xInputStream; } void UcbLockBytes::setStream_Impl( const Reference& aStream ) { osl::MutexGuard aGuard( m_aMutex ); if ( aStream.is() ) { m_xOutputStream = aStream->getOutputStream(); setInputStream_Impl( aStream->getInputStream(), false ); m_xSeekable.set( aStream, UNO_QUERY ); } else { m_xOutputStream.clear(); setInputStream_Impl( Reference < XInputStream >() ); } } bool UcbLockBytes::setInputStream_Impl( const Reference &rxInputStream, bool bSetXSeekable ) { bool bRet = false; try { osl::MutexGuard aGuard( m_aMutex ); if ( !m_bDontClose && m_xInputStream.is() ) m_xInputStream->closeInput(); m_xInputStream = rxInputStream; if( bSetXSeekable ) { m_xSeekable.set( rxInputStream, UNO_QUERY ); if( !m_xSeekable.is() && rxInputStream.is() ) { Reference < XComponentContext > xContext = ::comphelper::getProcessComponentContext(); Reference< XOutputStream > rxTempOut( css::io::TempFile::create(xContext), UNO_QUERY_THROW ); ::comphelper::OStorageHelper::CopyInputToOutput( rxInputStream, rxTempOut ); m_xInputStream.set( rxTempOut, UNO_QUERY ); m_xSeekable.set( rxTempOut, UNO_QUERY ); } } bRet = m_xInputStream.is(); } catch (const Exception&) { } if ( m_bStreamValid && m_xInputStream.is() ) m_aInitialized.set(); return bRet; } void UcbLockBytes::SetStreamValid_Impl() { m_bStreamValid = true; if ( m_xInputStream.is() ) m_aInitialized.set(); } void UcbLockBytes::terminate_Impl() { m_bTerminated = true; m_aInitialized.set(); m_aTerminated.set(); if ( GetError() == ERRCODE_NONE && !m_xInputStream.is() ) { OSL_FAIL("No InputStream, but no error set!" ); SetError( ERRCODE_IO_NOTEXISTS ); } } ErrCode UcbLockBytes::ReadAt(sal_uInt64 const nPos, void *pBuffer, std::size_t nCount, std::size_t *pRead) const { if ( IsSynchronMode() ) { UcbLockBytes* pThis = const_cast < UcbLockBytes* >( this ); pThis->m_aInitialized.wait(); } Reference xStream = getInputStream_Impl(); if ( !xStream.is() ) { if ( m_bTerminated ) return ERRCODE_IO_CANTREAD; else return ERRCODE_IO_PENDING; } if ( pRead ) *pRead = 0; Reference xSeekable = getSeekable_Impl(); if ( !xSeekable.is() ) return ERRCODE_IO_CANTREAD; try { xSeekable->seek( nPos ); } catch (const IOException&) { return ERRCODE_IO_CANTSEEK; } catch (const css::lang::IllegalArgumentException&) { return ERRCODE_IO_CANTSEEK; } Sequence aData; sal_Int32 nSize; if(nCount > 0x7FFFFFFF) { nCount = 0x7FFFFFFF; } try { if ( !m_bTerminated && !IsSynchronMode() ) { sal_uInt64 nLen = xSeekable->getLength(); if ( nPos + nCount > nLen ) return ERRCODE_IO_PENDING; } nSize = xStream->readBytes( aData, sal_Int32(nCount) ); } catch (const IOException&) { return ERRCODE_IO_CANTREAD; } memcpy (pBuffer, aData.getConstArray(), nSize); if (pRead) *pRead = static_cast(nSize); return ERRCODE_NONE; } ErrCode UcbLockBytes::WriteAt(sal_uInt64 const nPos, const void *pBuffer, std::size_t nCount, std::size_t *pWritten) { if ( pWritten ) *pWritten = 0; DBG_ASSERT( IsSynchronMode(), "Writing is only possible in SynchronMode!" ); DBG_ASSERT( m_aInitialized.check(), "Writing bevor stream is ready!" ); Reference xSeekable = getSeekable_Impl(); Reference xOutputStream = getOutputStream_Impl(); if ( !xOutputStream.is() || !xSeekable.is() ) return ERRCODE_IO_CANTWRITE; try { xSeekable->seek( nPos ); } catch (const IOException&) { return ERRCODE_IO_CANTSEEK; } sal_Int8 const * pData = static_cast(pBuffer); Sequence aData( pData, nCount ); try { xOutputStream->writeBytes( aData ); if ( pWritten ) *pWritten = nCount; } catch (const Exception&) { return ERRCODE_IO_CANTWRITE; } return ERRCODE_NONE; } ErrCode UcbLockBytes::Flush() const { Reference xOutputStream = getOutputStream_Impl(); if ( !xOutputStream.is() ) return ERRCODE_IO_CANTWRITE; try { xOutputStream->flush(); } catch (const Exception&) { return ERRCODE_IO_CANTWRITE; } return ERRCODE_NONE; } ErrCode UcbLockBytes::SetSize (sal_uInt64 const nNewSize) { SvLockBytesStat aStat; Stat( &aStat ); std::size_t nSize = aStat.nSize; if ( nSize > nNewSize ) { Reference < XTruncate > xTrunc( getOutputStream_Impl(), UNO_QUERY ); if ( xTrunc.is() ) { xTrunc->truncate(); nSize = 0; } else { SAL_INFO("unotools.ucbhelper", "Not truncable!"); } } if ( nSize < nNewSize ) { std::size_t nDiff = nNewSize-nSize, nCount=0; std::unique_ptr pBuffer(new sal_uInt8[ nDiff ]); memset(pBuffer.get(), 0, nDiff); // initialize for enhanced security WriteAt( nSize, pBuffer.get(), nDiff, &nCount ); if ( nCount != nDiff ) return ERRCODE_IO_CANTWRITE; } return ERRCODE_NONE; } ErrCode UcbLockBytes::Stat( SvLockBytesStat *pStat ) const { if ( IsSynchronMode() ) { UcbLockBytes* pThis = const_cast < UcbLockBytes* >( this ); pThis->m_aInitialized.wait(); } if (!pStat) return ERRCODE_IO_INVALIDPARAMETER; Reference xStream = getInputStream_Impl(); Reference xSeekable = getSeekable_Impl(); if ( !xStream.is() ) { if ( m_bTerminated ) return ERRCODE_IO_INVALIDACCESS; else return ERRCODE_IO_PENDING; } else if( !xSeekable.is() ) return ERRCODE_IO_CANTTELL; try { pStat->nSize = sal_uLong(xSeekable->getLength()); } catch (const IOException&) { return ERRCODE_IO_CANTTELL; } return ERRCODE_NONE; } UcbLockBytesRef UcbLockBytes::CreateInputLockBytes( const Reference< XInputStream >& xInputStream ) { if( !xInputStream.is() ) return nullptr; UcbLockBytesRef xLockBytes = new UcbLockBytes; xLockBytes->setDontClose_Impl(); xLockBytes->setInputStream_Impl( xInputStream ); xLockBytes->terminate_Impl(); return xLockBytes; } UcbLockBytesRef UcbLockBytes::CreateLockBytes( const Reference< XStream >& xStream ) { if( !xStream.is() ) return nullptr; UcbLockBytesRef xLockBytes = new UcbLockBytes; xLockBytes->setDontClose_Impl(); xLockBytes->setStream_Impl( xStream ); xLockBytes->terminate_Impl(); return xLockBytes; } UcbLockBytesRef UcbLockBytes::CreateLockBytes( const Reference < XContent >& xContent, const Sequence < PropertyValue >& rProps, StreamMode eOpenMode, const Reference < XInteractionHandler >& xInteractionHandler ) { if( !xContent.is() ) return nullptr; UcbLockBytesRef xLockBytes = new UcbLockBytes; xLockBytes->SetSynchronMode(); Reference< XActiveDataControl > xSink; if ( eOpenMode & StreamMode::WRITE ) xSink = new UcbStreamer_Impl(xLockBytes.get()); else xSink = new UcbDataSink_Impl(xLockBytes.get()); if ( rProps.hasElements() ) { Reference < XCommandProcessor > xProcessor( xContent, UNO_QUERY ); Command aCommand; aCommand.Name = "setPropertyValues"; aCommand.Handle = -1; /* unknown */ aCommand.Argument <<= rProps; xProcessor->execute( aCommand, 0, Reference < XCommandEnvironment >() ); } OpenCommandArgument2 aArgument; aArgument.Sink = xSink; aArgument.Mode = OpenMode::DOCUMENT; Command aCommand; aCommand.Name = "open"; aCommand.Argument <<= aArgument; bool bError = UCBOpenContentSync( xLockBytes, xContent, aCommand, xSink, xInteractionHandler ); if ( xLockBytes->GetError() == ERRCODE_NONE && ( bError || !xLockBytes->getInputStream().is() ) ) { OSL_FAIL("No InputStream, but no error set!" ); xLockBytes->SetError( ERRCODE_IO_GENERAL ); } return xLockBytes; } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */