    /* -*- 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 <sal/macros.h>
    #include <sal/log.hxx>
    #include <comphelper/processfactory.hxx>
    #include <salhelper/condition.hxx>
    #include <osl/thread.hxx>
    #include <osl/diagnose.h>
    #include <tools/urlobj.hxx>
    #include <ucbhelper/interactionrequest.hxx>
    #include <com/sun/star/task/XInteractionAbort.hpp>
    #include <com/sun/star/ucb/InteractiveNetworkConnectException.hpp>
    #include <com/sun/star/ucb/CommandFailedException.hpp>
    #include <com/sun/star/ucb/ContentCreationException.hpp>
    #include <com/sun/star/ucb/CommandAbortedException.hpp>
    #include <com/sun/star/ucb/UnsupportedDataSinkException.hpp>
    #include <com/sun/star/ucb/InteractiveIOException.hpp>
    #include <com/sun/star/io/IOException.hpp>
    #include <com/sun/star/io/XActiveDataStreamer.hpp>
    #include <com/sun/star/io/TempFile.hpp>
    #include <com/sun/star/ucb/DocumentHeaderField.hpp>
    #include <com/sun/star/ucb/XCommandInfo.hpp>
    #include <com/sun/star/ucb/XCommandProcessor.hpp>
    #include <com/sun/star/task/XInteractionHandler.hpp>
    #include <com/sun/star/ucb/OpenCommandArgument2.hpp>
    #include <com/sun/star/ucb/PostCommandArgument2.hpp>
    #include <com/sun/star/ucb/OpenMode.hpp>
    #include <com/sun/star/beans/Property.hpp>
    #include <com/sun/star/beans/PropertyValue.hpp>
    #include <com/sun/star/beans/XPropertiesChangeNotifier.hpp>
    #include <com/sun/star/beans/XPropertiesChangeListener.hpp>
    #include <com/sun/star/sdbc/XRow.hpp>
    #include <com/sun/star/io/XActiveDataSink.hpp>
    #include <com/sun/star/io/XActiveDataControl.hpp>
    #include <com/sun/star/io/XSeekable.hpp>
    #include <cppuhelper/implbase.hxx>
    #include <tools/inetmsg.hxx>
    #include <com/sun/star/io/XTruncate.hpp>
    #include <com/sun/star/lang/IllegalArgumentException.hpp>

    #include <comphelper/storagehelper.hxx>
    #include <ucbhelper/content.hxx>

    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
    {

    /**
        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<XStreamListener> &/*rxListener*/) override {}
        virtual void SAL_CALL   removeListener ( const Reference<XStreamListener> &/*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<XInputStream> &rxInputStream) override
                                { m_xLockBytes->setInputStream_Impl (rxInputStream); }
        virtual Reference<XInputStream> 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<XStreamListener> &/*rxListener*/) override {}
        virtual void SAL_CALL   removeListener ( const Reference<XStreamListener> &/*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<XInteractionHandler> SAL_CALL getInteractionHandler() override
        { return m_xInteractionHandler; }

        virtual Reference<XProgressHandler> 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<PropertyChangeEvent> &rEvent) override;
    };

    void SAL_CALL UcbPropertiesChangeListener_Impl::propertiesChange ( const Sequence<PropertyChangeEvent> &rEvent)
    {
        sal_Int32 i, n = rEvent.getLength();
        for (i = 0; i < n; i++)
        {
            PropertyChangeEvent evt (rEvent[i]);
            if (evt.PropertyName == "DocumentHeader")
            {
                Sequence<DocumentHeaderField> aHead;
                if (evt.NewValue >>= aHead)
                {
                    sal_Int32 k, m = aHead.getLength();
                    for (k = 0; k < m; k++)
                    {
                        OUString aName( aHead[k].Name );
                        OUString aValue( aHead[k].Value );

                        if (aName.compareToIgnoreAsciiCaseAscii("Expires") == 0)
                        {
                            DateTime aExpires( DateTime::EMPTY );
                            if (INetMIMEMessage::ParseDateField (aValue, aExpires))
                            {
                                aExpires.ConvertToLocalTime();
                                m_xLockBytes->SetExpireDate_Impl( aExpires );
                            }
                        }
                    }
                }

                m_xLockBytes->SetStreamValid_Impl();
            }
            else if (evt.PropertyName == "PresentationURL")
            {
                OUString aUrl;
                if (evt.NewValue >>= aUrl)
                {
                    if (!aUrl.startsWith("private:"))
                    {
                        // URL changed (Redirection).
                        m_xLockBytes->SetRealURL_Impl( aUrl );
                    }
                }
            }
            else if (evt.PropertyName == "MediaType")
            {
                OUString aContentType;
                if (evt.NewValue >>= aContentType)
                    m_xLockBytes->SetContentType_Impl( aContentType );
            }
        }
    }

    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<XInteractionRequest >& Request );

        void setStream(const Reference< XStream >& aStream);
        void setInputStream(const Reference<XInputStream> &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<XActiveDataStreamer>
    {
    public:

        explicit ModeratorsActiveDataStreamer(Moderator &theModerator);

        // XActiveDataStreamer
        virtual void SAL_CALL
        setStream(
            const Reference< XStream >& aStream
        ) override;

        virtual Reference<XStream> SAL_CALL
        getStream (
            void
        ) override
        {
            osl::MutexGuard aGuard(m_aMutex);
            return m_xStream;
        }

    private:
        Moderator& m_aModerator;

        osl::Mutex m_aMutex;
        Reference<XStream> m_xStream;
    };

    class ModeratorsActiveDataSink
        : public ::cppu::WeakImplHelper<XActiveDataSink>
    {
    public:

        explicit ModeratorsActiveDataSink(Moderator &theModerator);

        // XActiveDataSink.
        virtual void SAL_CALL
        setInputStream (
            const Reference<XInputStream> &rxInputStream
        ) override;

        virtual Reference<XInputStream> SAL_CALL
        getInputStream (
            void
        ) override
        {
            osl::MutexGuard aGuard(m_aMutex);
            return m_xStream;
        }

    private:
        Moderator& m_aModerator;
        osl::Mutex m_aMutex;
        Reference<XInputStream> m_xStream;
    };

    ModeratorsActiveDataSink::ModeratorsActiveDataSink(Moderator &theModerator)
        : m_aModerator(theModerator)
    {
    }

    // XActiveDataSink.
    void SAL_CALL
    ModeratorsActiveDataSink::setInputStream (
        const Reference<XInputStream> &rxInputStream
    )
    {
        m_aModerator.setInputStream(rxInputStream);
        osl::MutexGuard aGuard(m_aMutex);
        m_xStream = rxInputStream;
    }

    ModeratorsActiveDataStreamer::ModeratorsActiveDataStreamer(
        Moderator &theModerator
    )
        : m_aModerator(theModerator)
    {
    }

    // XActiveDataStreamer.
    void SAL_CALL
    ModeratorsActiveDataStreamer::setStream (
        const Reference<XStream> &rxStream
    )
    {
        m_aModerator.setStream(rxStream);
        osl::MutexGuard aGuard(m_aMutex);
        m_xStream = rxStream;
    }

    class ModeratorsInteractionHandler
        : public ::cppu::WeakImplHelper<XInteractionHandler>
    {
    public:

        explicit ModeratorsInteractionHandler(Moderator &theModerator);

        virtual void SAL_CALL
        handle( const Reference<XInteractionRequest >& Request ) override;

    private:

        Moderator& m_aModerator;
    };

    ModeratorsInteractionHandler::ModeratorsInteractionHandler(
        Moderator &aModerator)
        : m_aModerator(aModerator)
    {
    }

    void SAL_CALL
    ModeratorsInteractionHandler::handle(
        const Reference<XInteractionRequest >& 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<XInterface> *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<cppu::OWeakObject*>(new ModeratorsActiveDataSink(*this)));

        Reference<XActiveDataStreamer> xStreamer( *pxSink, UNO_QUERY );
        if ( xStreamer.is() )
            pxSink->set( static_cast<cppu::OWeakObject*>(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<XInteractionRequest >& 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) {
                Sequence<Reference<XInteractionContinuation> > aSeq(
                    Request->getContinuations());
                for(sal_Int32 i = 0; i < aSeq.getLength(); ++i) {
                    Reference<XInteractionAbort> aRef(aSeq[i],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<XInputStream> &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<XContentIdentifier> 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();
        }
        catch (const ContentCreationException&)
        {
            bResultAchieved = bException = true;
            xLockBytes->SetError( ERRCODE_IO_GENERAL );
        }

        sal_uInt32 nTimeout(5000); // initially 5000 milliSec
        while(!bResultAchieved) {

            Moderator::Result res;
            // try to get the result for with timeout
            res = pMod->getResult(nTimeout);

            switch(res.type) {
            case Moderator::ResultType::STREAM:
                {
                    Reference<XStream> 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<XInputStream> 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<XInteractionRetry> 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;
                        ucbhelper::InteractionRequest *ir =
                            new ucbhelper::InteractionRequest(request);
                        Reference<XInteractionRequest> xIR(ir);
                        Sequence<Reference<XInteractionContinuation> > aSeq(2);
                        ucbhelper::InteractionRetry *retryP =
                            new ucbhelper::InteractionRetry(ir);
                        aSeq[0] = retryP;
                        ucbhelper::InteractionAbort *abortP =
                            new ucbhelper::InteractionAbort(ir);
                        aSeq[1] = abortP;

                        ir->setContinuations(aSeq);
                        xInteract->handle(xIR);
                        rtl::Reference< ucbhelper::InteractionContinuation > ref
                            = ir->getSelection();
                        if(ref.is()) {
                            Reference<XInterface> xInt(ref.get());
                            xRet.set(xInt,UNO_QUERY);
                        }
                    }

                    if(!xRet.is()) {
                        bAborted = true;
                        xLockBytes->SetError(ERRCODE_ABORT);
                    }

                    break;
                }
            case Moderator::ResultType::INTERACTIONREQUEST:
                {
                    Reference<XInteractionRequest> 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_aExpireDate( DateTime::EMPTY )
        , 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() )
        {
            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<XStream>& 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<XInputStream> &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 <XInputStream> xStream = getInputStream_Impl();
        if ( !xStream.is() )
        {
            if ( m_bTerminated )
                return ERRCODE_IO_CANTREAD;
            else
                return ERRCODE_IO_PENDING;
        }

        if ( pRead )
            *pRead = 0;

        Reference <XSeekable> 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<sal_Int8> 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<std::size_t>(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> xSeekable = getSeekable_Impl();
        Reference <XOutputStream> 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<sal_Int8 const *>(pBuffer);
        Sequence<sal_Int8> 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 > 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, SvLockBytesStatFlag(0) );
        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;
            sal_uInt8* pBuffer = new sal_uInt8[ nDiff ];
            memset(pBuffer, 0, nDiff); // initialize for enhanced security
            WriteAt( nSize, pBuffer, nDiff, &nCount );
            delete[] pBuffer;
            if ( nCount != nDiff )
                return ERRCODE_IO_CANTWRITE;
        }

        return ERRCODE_NONE;
    }

    ErrCode UcbLockBytes::Stat( SvLockBytesStat *pStat, SvLockBytesStatFlag) const
    {
        if ( IsSynchronMode() )
        {
            UcbLockBytes* pThis = const_cast < UcbLockBytes* >( this );
            pThis->m_aInitialized.wait();
        }

        if (!pStat)
            return ERRCODE_IO_INVALIDPARAMETER;

        Reference <XInputStream> xStream = getInputStream_Impl();
        Reference <XSeekable> 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 = static_cast<XActiveDataControl*>(new UcbStreamer_Impl( xLockBytes.get() ));
        else
            xSink = static_cast<XActiveDataControl*>(new UcbDataSink_Impl( xLockBytes.get() ));

        if ( rProps.getLength() )
        {
            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: */

↑ V572 It is odd that the object which was created using 'new' operator is immediately cast to another type.

↑ V572 It is odd that the object which was created using 'new' operator is immediately cast to another type.