1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "ucblockbytes.hxx"
22 #include <sal/log.hxx>
23 #include <comphelper/processfactory.hxx>
24 #include <salhelper/condition.hxx>
25 #include <osl/thread.hxx>
26 #include <osl/diagnose.h>
27 #include <tools/urlobj.hxx>
28 #include <tools/solar.h>
29 #include <ucbhelper/interactionrequest.hxx>
30 #include <com/sun/star/lang/XUnoTunnel.hpp>
31 #include <com/sun/star/task/XInteractionAbort.hpp>
32 #include <com/sun/star/ucb/InteractiveNetworkConnectException.hpp>
33 #include <com/sun/star/ucb/CommandFailedException.hpp>
34 #include <com/sun/star/ucb/ContentCreationException.hpp>
35 #include <com/sun/star/ucb/CommandAbortedException.hpp>
36 #include <com/sun/star/ucb/UnsupportedDataSinkException.hpp>
37 #include <com/sun/star/ucb/InteractiveIOException.hpp>
38 #include <com/sun/star/ucb/XContentIdentifier.hpp>
39 #include <com/sun/star/ucb/XContent.hpp>
40 #include <com/sun/star/io/IOException.hpp>
41 #include <com/sun/star/io/XActiveDataStreamer.hpp>
42 #include <com/sun/star/io/TempFile.hpp>
43 #include <com/sun/star/ucb/XCommandProcessor.hpp>
44 #include <com/sun/star/task/XInteractionHandler.hpp>
45 #include <com/sun/star/ucb/OpenCommandArgument2.hpp>
46 #include <com/sun/star/ucb/PostCommandArgument2.hpp>
47 #include <com/sun/star/ucb/OpenMode.hpp>
48 #include <com/sun/star/beans/PropertyValue.hpp>
49 #include <com/sun/star/beans/XPropertiesChangeNotifier.hpp>
50 #include <com/sun/star/beans/XPropertiesChangeListener.hpp>
51 #include <com/sun/star/io/XActiveDataSink.hpp>
52 #include <com/sun/star/io/XActiveDataControl.hpp>
53 #include <com/sun/star/io/XSeekable.hpp>
54 #include <cppuhelper/implbase.hxx>
55 #include <tools/debug.hxx>
56 #include <com/sun/star/io/XTruncate.hpp>
57 #include <com/sun/star/lang/IllegalArgumentException.hpp>
59 #include <comphelper/bytereader.hxx>
60 #include <comphelper/storagehelper.hxx>
61 #include <ucbhelper/content.hxx>
62 #include <unotools/tempfile.hxx>
66 using namespace ::com::sun::star::uno
;
67 using namespace ::com::sun::star::io
;
68 using namespace ::com::sun::star::ucb
;
69 using namespace ::com::sun::star::task
;
70 using namespace ::com::sun::star::lang
;
71 using namespace ::com::sun::star::beans
;
79 Helper class for getting a XInputStream when opening a content
81 class UcbDataSink_Impl
: public ::cppu::WeakImplHelper
< XActiveDataControl
, XActiveDataSink
>
83 UcbLockBytesRef m_xLockBytes
;
86 explicit UcbDataSink_Impl( UcbLockBytes
* pLockBytes
)
87 : m_xLockBytes( pLockBytes
)
90 // XActiveDataControl.
91 virtual void SAL_CALL
addListener ( const Reference
<XStreamListener
> &/*rxListener*/) override
{}
92 virtual void SAL_CALL
removeListener ( const Reference
<XStreamListener
> &/*rxListener*/) override
{}
93 virtual void SAL_CALL
start() override
{}
94 virtual void SAL_CALL
terminate() override
95 { m_xLockBytes
->terminate(); }
98 virtual void SAL_CALL
setInputStream ( const Reference
<XInputStream
> &rxInputStream
) override
99 { m_xLockBytes
->setInputStream(rxInputStream
); }
100 virtual Reference
<XInputStream
> SAL_CALL
getInputStream() override
101 { return m_xLockBytes
->getInputStream(); }
105 Helper class for getting a XStream when opening a content
107 class UcbStreamer_Impl
: public ::cppu::WeakImplHelper
< XActiveDataStreamer
, XActiveDataControl
>
109 Reference
< XStream
> m_xStream
;
110 UcbLockBytesRef m_xLockBytes
;
113 explicit UcbStreamer_Impl( UcbLockBytes
* pLockBytes
)
114 : m_xLockBytes( pLockBytes
)
117 // XActiveDataControl.
118 virtual void SAL_CALL
addListener ( const Reference
<XStreamListener
> &/*rxListener*/) override
{}
119 virtual void SAL_CALL
removeListener ( const Reference
<XStreamListener
> &/*rxListener*/) override
{}
120 virtual void SAL_CALL
start() override
{}
121 virtual void SAL_CALL
terminate() override
122 { m_xLockBytes
->terminate(); }
124 // XActiveDataStreamer
125 virtual void SAL_CALL
setStream( const Reference
< XStream
>& aStream
) override
126 { m_xStream
= aStream
; m_xLockBytes
->setStream( aStream
); }
127 virtual Reference
< XStream
> SAL_CALL
getStream() override
128 { return m_xStream
; }
132 Helper class for managing interactions and progress when executing UCB commands
134 class UcbTaskEnvironment
: public ::cppu::WeakImplHelper
< XCommandEnvironment
>
136 Reference
< XInteractionHandler
> m_xInteractionHandler
;
137 Reference
< XProgressHandler
> m_xProgressHandler
;
140 UcbTaskEnvironment( const Reference
< XInteractionHandler
>& rxInteractionHandler
,
141 const Reference
< XProgressHandler
>& rxProgressHandler
)
142 : m_xInteractionHandler( rxInteractionHandler
)
143 , m_xProgressHandler( rxProgressHandler
)
146 virtual Reference
<XInteractionHandler
> SAL_CALL
getInteractionHandler() override
147 { return m_xInteractionHandler
; }
149 virtual Reference
<XProgressHandler
> SAL_CALL
getProgressHandler() override
150 { return m_xProgressHandler
; }
154 Helper class for property change notifies when executing UCB commands
156 class UcbPropertiesChangeListener_Impl
: public ::cppu::WeakImplHelper
< XPropertiesChangeListener
>
159 UcbLockBytesRef m_xLockBytes
;
161 explicit UcbPropertiesChangeListener_Impl( UcbLockBytesRef xRef
)
162 : m_xLockBytes(std::move( xRef
))
165 virtual void SAL_CALL
disposing ( const EventObject
&/*rEvent*/) override
{}
166 virtual void SAL_CALL
propertiesChange ( const Sequence
<PropertyChangeEvent
> &rEvent
) override
;
171 void SAL_CALL
UcbPropertiesChangeListener_Impl::propertiesChange ( const Sequence
<PropertyChangeEvent
> &rEvent
)
173 for (const auto& rPropChangeEvent
: rEvent
)
175 if (rPropChangeEvent
.PropertyName
== "DocumentHeader")
177 m_xLockBytes
->SetStreamValid();
187 // usage restriction:
188 // It might be possible, that the call to the interactionhandler and/or
189 // progresshandler is done asynchronously, while the 'execute' simply
190 // returns. This would imply that these class must be refcounted!!!
193 /// @throws ContentCreationException
194 /// @throws RuntimeException
196 Reference
< XContent
> const & xContent
,
197 Reference
< XInteractionHandler
> const & xInteract
,
201 enum class ResultType
{
204 INTERACTIONREQUEST
, // reply expected
219 : public salhelper::Condition
222 ConditionRes(osl::Mutex
& aMutex
,Moderator
& aModerator
)
223 : salhelper::Condition(aMutex
),
224 m_aModerator(aModerator
)
229 bool applies() const override
{
230 return m_aModerator
.m_aResultType
!= ResultType::NORESULT
;
234 Moderator
& m_aModerator
;
240 IOErrorCode ioErrorCode
;
243 Result
getResult(const sal_uInt32 milliSec
);
252 : public salhelper::Condition
255 ConditionRep(osl::Mutex
& aMutex
,Moderator
& aModerator
)
256 : salhelper::Condition(aMutex
),
257 m_aModerator(aModerator
)
262 bool applies() const override
{
263 return m_aModerator
.m_aReplyType
!= NOREPLY
;
267 Moderator
& m_aModerator
;
270 void setReply(ReplyType
);
272 void handle( const Reference
<XInteractionRequest
>& Request
);
274 void setStream(const Reference
< XStream
>& aStream
);
275 void setInputStream(const Reference
<XInputStream
> &rxInputStream
);
278 virtual void SAL_CALL
run() override
;
279 virtual void SAL_CALL
onTerminated() override
;
284 friend class ConditionRes
;
287 ResultType m_aResultType
;
288 IOErrorCode m_nIOErrorCode
;
291 friend class ConditionRep
;
294 ReplyType m_aReplyType
;
297 ::ucbhelper::Content m_aContent
;
300 class ModeratorsActiveDataStreamer
301 : public ::cppu::WeakImplHelper
<XActiveDataStreamer
>
305 explicit ModeratorsActiveDataStreamer(Moderator
&theModerator
);
307 // XActiveDataStreamer
308 virtual void SAL_CALL
310 const Reference
< XStream
>& aStream
313 virtual Reference
<XStream
> SAL_CALL
getStream () override
315 std::scoped_lock
aGuard(m_aMutex
);
320 Moderator
& m_aModerator
;
323 Reference
<XStream
> m_xStream
;
326 class ModeratorsActiveDataSink
327 : public ::cppu::WeakImplHelper
<XActiveDataSink
>
331 explicit ModeratorsActiveDataSink(Moderator
&theModerator
);
334 virtual void SAL_CALL
336 const Reference
<XInputStream
> &rxInputStream
339 virtual Reference
<XInputStream
> SAL_CALL
getInputStream() override
341 std::scoped_lock
aGuard(m_aMutex
);
346 Moderator
& m_aModerator
;
348 Reference
<XInputStream
> m_xStream
;
353 ModeratorsActiveDataSink::ModeratorsActiveDataSink(Moderator
&theModerator
)
354 : m_aModerator(theModerator
)
360 ModeratorsActiveDataSink::setInputStream (
361 const Reference
<XInputStream
> &rxInputStream
364 m_aModerator
.setInputStream(rxInputStream
);
365 std::scoped_lock
aGuard(m_aMutex
);
366 m_xStream
= rxInputStream
;
369 ModeratorsActiveDataStreamer::ModeratorsActiveDataStreamer(
370 Moderator
&theModerator
372 : m_aModerator(theModerator
)
376 // XActiveDataStreamer.
378 ModeratorsActiveDataStreamer::setStream (
379 const Reference
<XStream
> &rxStream
382 m_aModerator
.setStream(rxStream
);
383 std::scoped_lock
aGuard(m_aMutex
);
384 m_xStream
= rxStream
;
389 class ModeratorsInteractionHandler
390 : public ::cppu::WeakImplHelper
<XInteractionHandler
>
394 explicit ModeratorsInteractionHandler(Moderator
&theModerator
);
396 virtual void SAL_CALL
397 handle( const Reference
<XInteractionRequest
>& Request
) override
;
401 Moderator
& m_aModerator
;
406 ModeratorsInteractionHandler::ModeratorsInteractionHandler(
407 Moderator
&aModerator
)
408 : m_aModerator(aModerator
)
413 ModeratorsInteractionHandler::handle(
414 const Reference
<XInteractionRequest
>& Request
417 // wakes up the mainthread
418 m_aModerator
.handle(Request
);
421 Moderator::Moderator(
422 Reference
< XContent
> const & xContent
,
423 Reference
< XInteractionHandler
> const & xInteract
,
426 : m_aRes(m_aMutex
,*this),
427 m_aResultType(ResultType::NORESULT
),
428 m_nIOErrorCode(IOErrorCode_ABORT
),
429 m_aRep(m_aMutex
,*this),
430 m_aReplyType(NOREPLY
),
431 m_aArg(std::move(aArg
)),
434 new UcbTaskEnvironment(
435 xInteract
.is() ? new ModeratorsInteractionHandler(*this) : nullptr,
437 comphelper::getProcessComponentContext())
439 // now exchange the whole data sink stuff
440 // with a thread safe version
442 Reference
<XInterface
> *pxSink
= nullptr;
444 PostCommandArgument2 aPostArg
;
445 OpenCommandArgument2 aOpenArg
;
448 if(m_aArg
.Argument
>>= aPostArg
) {
449 pxSink
= &aPostArg
.Sink
;
452 else if(m_aArg
.Argument
>>= aOpenArg
) {
453 pxSink
= &aOpenArg
.Sink
;
458 throw ContentCreationException();
460 Reference
< XActiveDataSink
> xActiveSink(*pxSink
,UNO_QUERY
);
462 pxSink
->set(getXWeak(new ModeratorsActiveDataSink(*this)));
464 Reference
<XActiveDataStreamer
> xStreamer( *pxSink
, UNO_QUERY
);
465 if ( xStreamer
.is() )
466 pxSink
->set(getXWeak(new ModeratorsActiveDataStreamer(*this)));
469 m_aArg
.Argument
<<= aPostArg
;
471 m_aArg
.Argument
<<= aOpenArg
;
474 Moderator::Result
Moderator::getResult(const sal_uInt32 milliSec
)
478 salhelper::ConditionWaiter
aWaiter(m_aRes
,milliSec
);
479 ret
.type
= m_aResultType
;
480 ret
.result
= m_aResult
;
481 ret
.ioErrorCode
= m_nIOErrorCode
;
484 m_aResultType
= ResultType::NORESULT
;
486 catch (const salhelper::ConditionWaiter::timedout
&)
488 ret
.type
= ResultType::TIMEDOUT
;
494 void Moderator::setReply(ReplyType aReplyType
)
496 salhelper::ConditionModifier
aMod(m_aRep
);
497 m_aReplyType
= aReplyType
;
500 void Moderator::handle( const Reference
<XInteractionRequest
>& Request
)
502 ReplyType aReplyType
;
506 salhelper::ConditionModifier
aMod(m_aRes
);
507 m_aResultType
= ResultType::INTERACTIONREQUEST
;
508 m_aResult
<<= Request
;
512 salhelper::ConditionWaiter
aWait(m_aRep
);
513 aReplyType
= m_aReplyType
;
516 m_aReplyType
= NOREPLY
;
519 if(aReplyType
== EXIT
) {
520 const Sequence
<Reference
<XInteractionContinuation
> > aSeq(
521 Request
->getContinuations());
522 for(const auto& rContinuation
: aSeq
) {
523 Reference
<XInteractionAbort
> aRef(rContinuation
,UNO_QUERY
);
529 // resignal the exit condition
533 } while(aReplyType
!= REQUESTHANDLED
);
536 void Moderator::setStream(const Reference
< XStream
>& aStream
)
539 salhelper::ConditionModifier
aMod(m_aRes
);
540 m_aResultType
= ResultType::STREAM
;
541 m_aResult
<<= aStream
;
543 ReplyType aReplyType
;
545 salhelper::ConditionWaiter
aWait(m_aRep
);
546 aReplyType
= m_aReplyType
;
547 m_aReplyType
= NOREPLY
;
549 if(aReplyType
== EXIT
)
553 void Moderator::setInputStream(const Reference
<XInputStream
> &rxInputStream
)
556 salhelper::ConditionModifier
aMod(m_aRes
);
557 m_aResultType
= ResultType::INPUTSTREAM
;
558 m_aResult
<<= rxInputStream
;
560 ReplyType aReplyType
;
562 salhelper::ConditionWaiter
aWait(m_aRep
);
563 aReplyType
= m_aReplyType
;
564 m_aReplyType
= NOREPLY
;
566 if(aReplyType
== EXIT
)
570 void SAL_CALL
Moderator::run()
572 osl_setThreadName("utl::Moderator");
574 ResultType aResultType
;
576 IOErrorCode nIOErrorCode
= IOErrorCode_ABORT
;
580 aResult
= m_aContent
.executeCommand(m_aArg
.Name
,m_aArg
.Argument
);
581 aResultType
= ResultType::RESULT
;
583 catch (const CommandAbortedException
&)
585 aResultType
= ResultType::COMMANDABORTED
;
587 catch (const CommandFailedException
&)
589 aResultType
= ResultType::COMMANDFAILED
;
591 catch (const InteractiveIOException
& r
)
593 nIOErrorCode
= r
.Code
;
594 aResultType
= ResultType::INTERACTIVEIO
;
596 catch (const UnsupportedDataSinkException
&)
598 aResultType
= ResultType::UNSUPPORTED
;
600 catch (const Exception
&)
602 aResultType
= ResultType::GENERAL
;
606 salhelper::ConditionModifier
aMod(m_aRes
);
607 m_aResultType
= aResultType
;
609 m_nIOErrorCode
= nIOErrorCode
;
613 void SAL_CALL
Moderator::onTerminated()
616 salhelper::ConditionWaiter
aWaiter(m_aRep
);
622 Function for opening UCB contents synchronously,
623 but with handled timeout;
625 static bool UCBOpenContentSync_(
626 const UcbLockBytesRef
& xLockBytes
,
627 const Reference
< XContent
>& xContent
,
629 const Reference
< XInterface
>& xSink
,
630 const Reference
< XInteractionHandler
>& xInteract
);
632 static bool UCBOpenContentSync(
633 const UcbLockBytesRef
& xLockBytes
,
634 Reference
< XContent
> const & xContent
,
636 const Reference
< XInterface
>& xSink
,
637 Reference
< XInteractionHandler
> const & xInteract
)
639 // http protocol must be handled in a special way:
640 // during the opening process the input stream may change
641 // only the last inputstream after notifying the document
644 Reference
<XContentIdentifier
> xContId(
645 xContent
.is() ? xContent
->getIdentifier() : nullptr );
649 aScheme
= xContId
->getContentProviderScheme();
651 // now determine whether we use a timeout or not;
652 if( ! aScheme
.equalsIgnoreAsciiCase("http") &&
653 ! aScheme
.equalsIgnoreAsciiCase("https") &&
654 ! aScheme
.equalsIgnoreAsciiCase("vnd.sun.star.webdav") &&
655 ! aScheme
.equalsIgnoreAsciiCase("vnd.sun.star.webdavs") &&
656 ! aScheme
.equalsIgnoreAsciiCase("ftp"))
657 return UCBOpenContentSync_(
658 xLockBytes
,xContent
,rArg
,xSink
,xInteract
);
660 if ( !aScheme
.equalsIgnoreAsciiCase( "http" ) &&
661 !aScheme
.equalsIgnoreAsciiCase( "https" ) )
662 xLockBytes
->SetStreamValid();
664 Reference
< XPropertiesChangeListener
> xListener
;
665 Reference
< XPropertiesChangeNotifier
> xProps(xContent
,UNO_QUERY
);
668 new UcbPropertiesChangeListener_Impl(xLockBytes
);
669 xProps
->addPropertiesChangeListener(
670 Sequence
< OUString
>(),
674 bool bException(false);
675 bool bAborted(false);
676 bool bResultAchieved(false);
678 Moderator
* pMod
= nullptr;
681 pMod
= new Moderator(xContent
,xInteract
,rArg
);
683 //TODO: a protocol is missing how to join with the launched thread before exit(3), to
684 // ensure the thread is no longer relying on any infrastructure while that
685 // infrastructure is being shut down in atexit handlers
687 catch (const ContentCreationException
&)
689 bResultAchieved
= bException
= true;
690 xLockBytes
->SetError( ERRCODE_IO_GENERAL
);
693 sal_uInt32
nTimeout(5000); // initially 5000 milliSec
694 while(!bResultAchieved
) {
696 // try to get the result for with timeout
697 Moderator::Result res
= pMod
->getResult(nTimeout
);
700 case Moderator::ResultType::STREAM
:
702 Reference
<XStream
> result
;
703 if(res
.result
>>= result
) {
704 Reference
< XActiveDataStreamer
> xStreamer(
709 xStreamer
->setStream(result
);
711 pMod
->setReply(Moderator::REQUESTHANDLED
);
714 case Moderator::ResultType::INPUTSTREAM
:
716 Reference
<XInputStream
> result
;
717 res
.result
>>= result
;
718 Reference
< XActiveDataSink
> xActiveSink(
723 xActiveSink
->setInputStream(result
);
724 pMod
->setReply(Moderator::REQUESTHANDLED
);
727 case Moderator::ResultType::TIMEDOUT
:
729 Reference
<XInteractionRetry
> xRet
;
731 InteractiveNetworkConnectException aExcep
;
734 xContId
->getContentIdentifier() :
736 aExcep
.Server
= aURL
.GetHost();
737 aExcep
.Classification
= InteractionClassification_ERROR
;
738 aExcep
.Message
= "server not responding after five seconds";
741 rtl::Reference
<ucbhelper::InteractionRequest
> xIR
=
742 new ucbhelper::InteractionRequest(request
);
743 rtl::Reference
<ucbhelper::InteractionRetry
> retryP
=
744 new ucbhelper::InteractionRetry(xIR
.get());
745 rtl::Reference
<ucbhelper::InteractionAbort
> abortP
=
746 new ucbhelper::InteractionAbort(xIR
.get());
747 Sequence
<Reference
<XInteractionContinuation
> > aSeq
{ retryP
, abortP
};
749 xIR
->setContinuations(aSeq
);
750 xInteract
->handle(xIR
);
751 rtl::Reference
< ucbhelper::InteractionContinuation
> ref
752 = xIR
->getSelection();
754 Reference
<XInterface
> xInt(ref
);
755 xRet
.set(xInt
,UNO_QUERY
);
761 xLockBytes
->SetError(ERRCODE_ABORT
);
766 case Moderator::ResultType::INTERACTIONREQUEST
:
768 Reference
<XInteractionRequest
> Request
;
769 res
.result
>>= Request
;
770 xInteract
->handle(Request
);
771 pMod
->setReply(Moderator::REQUESTHANDLED
);
774 case Moderator::ResultType::RESULT
:
776 bResultAchieved
= true;
779 case Moderator::ResultType::COMMANDABORTED
:
782 xLockBytes
->SetError( ERRCODE_ABORT
);
785 case Moderator::ResultType::COMMANDFAILED
:
788 xLockBytes
->SetError( ERRCODE_ABORT
);
791 case Moderator::ResultType::INTERACTIVEIO
:
794 if ( res
.ioErrorCode
== IOErrorCode_ACCESS_DENIED
||
795 res
.ioErrorCode
== IOErrorCode_LOCKING_VIOLATION
)
796 xLockBytes
->SetError( ERRCODE_IO_ACCESSDENIED
);
797 else if ( res
.ioErrorCode
== IOErrorCode_NOT_EXISTING
)
798 xLockBytes
->SetError( ERRCODE_IO_NOTEXISTS
);
799 else if ( res
.ioErrorCode
== IOErrorCode_CANT_READ
)
800 xLockBytes
->SetError( ERRCODE_IO_CANTREAD
);
802 xLockBytes
->SetError( ERRCODE_IO_GENERAL
);
805 case Moderator::ResultType::UNSUPPORTED
:
808 xLockBytes
->SetError( ERRCODE_IO_NOTSUPPORTED
);
814 xLockBytes
->SetError( ERRCODE_IO_GENERAL
);
819 bResultAchieved
|= bException
;
820 bResultAchieved
|= bAborted
;
821 if(nTimeout
== 5000) nTimeout
*= 2;
824 if(pMod
) pMod
->setReply(Moderator::EXIT
);
826 if ( bAborted
|| bException
)
828 Reference
< XActiveDataSink
> xActiveSink( xSink
, UNO_QUERY
);
829 if ( xActiveSink
.is() )
830 xActiveSink
->setInputStream( Reference
< XInputStream
>() );
832 Reference
< XActiveDataStreamer
> xStreamer( xSink
, UNO_QUERY
);
833 if ( xStreamer
.is() )
834 xStreamer
->setStream( Reference
< XStream
>() );
837 Reference
< XActiveDataControl
> xControl( xSink
, UNO_QUERY
);
839 xControl
->terminate();
842 xProps
->removePropertiesChangeListener(
843 Sequence
< OUString
>(),
846 return ( bAborted
|| bException
);
850 Function for opening UCB contents synchronously
852 static bool UCBOpenContentSync_(
853 const UcbLockBytesRef
& xLockBytes
,
854 const Reference
< XContent
>& xContent
,
856 const Reference
< XInterface
>& xSink
,
857 const Reference
< XInteractionHandler
>& xInteract
)
859 ::ucbhelper::Content
aContent(
860 xContent
, new UcbTaskEnvironment( xInteract
, nullptr ),
861 comphelper::getProcessComponentContext() );
862 Reference
< XContentIdentifier
> xIdent
= xContent
->getIdentifier();
863 OUString aScheme
= xIdent
->getContentProviderScheme();
865 // http protocol must be handled in a special way: during the opening process the input stream may change
866 // only the last inputstream after notifying the document headers is valid
867 if ( !aScheme
.equalsIgnoreAsciiCase("http") )
868 xLockBytes
->SetStreamValid();
870 Reference
< XPropertiesChangeListener
> xListener
= new UcbPropertiesChangeListener_Impl( xLockBytes
);
871 Reference
< XPropertiesChangeNotifier
> xProps ( xContent
, UNO_QUERY
);
873 xProps
->addPropertiesChangeListener( Sequence
< OUString
>(), xListener
);
875 bool bException
= false;
876 bool bAborted
= false;
880 aContent
.executeCommand( rArg
.Name
, rArg
.Argument
);
882 catch (const CommandAbortedException
&)
885 xLockBytes
->SetError( ERRCODE_ABORT
);
887 catch (const CommandFailedException
&)
890 xLockBytes
->SetError( ERRCODE_ABORT
);
892 catch (const InteractiveIOException
& r
)
895 if ( r
.Code
== IOErrorCode_ACCESS_DENIED
|| r
.Code
== IOErrorCode_LOCKING_VIOLATION
)
896 xLockBytes
->SetError( ERRCODE_IO_ACCESSDENIED
);
897 else if ( r
.Code
== IOErrorCode_NOT_EXISTING
)
898 xLockBytes
->SetError( ERRCODE_IO_NOTEXISTS
);
899 else if ( r
.Code
== IOErrorCode_CANT_READ
)
900 xLockBytes
->SetError( ERRCODE_IO_CANTREAD
);
902 xLockBytes
->SetError( ERRCODE_IO_GENERAL
);
904 catch (const UnsupportedDataSinkException
&)
907 xLockBytes
->SetError( ERRCODE_IO_NOTSUPPORTED
);
909 catch (const Exception
&)
912 xLockBytes
->SetError( ERRCODE_IO_GENERAL
);
915 if ( bAborted
|| bException
)
917 Reference
< XActiveDataSink
> xActiveSink( xSink
, UNO_QUERY
);
918 if ( xActiveSink
.is() )
919 xActiveSink
->setInputStream( Reference
< XInputStream
>() );
921 Reference
< XActiveDataStreamer
> xStreamer( xSink
, UNO_QUERY
);
922 if ( xStreamer
.is() )
923 xStreamer
->setStream( Reference
< XStream
>() );
926 Reference
< XActiveDataControl
> xControl( xSink
, UNO_QUERY
);
928 xControl
->terminate();
931 xProps
->removePropertiesChangeListener( Sequence
< OUString
>(), xListener
);
933 return ( bAborted
|| bException
);
936 UcbLockBytes::UcbLockBytes()
937 : m_nError( ERRCODE_NONE
)
938 , m_bTerminated (false)
939 , m_bDontClose( false )
940 , m_bStreamValid (false)
945 UcbLockBytes::~UcbLockBytes()
949 if ( m_xInputStream
.is() )
953 m_xInputStream
->closeInput();
955 catch (const RuntimeException
&)
958 catch (const IOException
&)
964 if ( m_xInputStream
.is() || !m_xOutputStream
.is() )
969 m_xOutputStream
->closeOutput();
971 catch (const RuntimeException
&)
974 catch (const IOException
&)
979 Reference
< XInputStream
> UcbLockBytes::getInputStream()
981 std::unique_lock
aGuard( m_aMutex
);
983 return m_xInputStream
;
986 void UcbLockBytes::setStream( const Reference
<XStream
>& aStream
)
988 std::unique_lock
aGuard( m_aMutex
);
991 m_xOutputStream
= aStream
->getOutputStream();
992 setInputStreamImpl( aGuard
, aStream
->getInputStream(), false );
993 m_xSeekable
.set( aStream
, UNO_QUERY
);
997 m_xOutputStream
.clear();
998 setInputStreamImpl( aGuard
, Reference
< XInputStream
>() );
1002 bool UcbLockBytes::setInputStream( const Reference
<XInputStream
> &rxInputStream
, bool bSetXSeekable
)
1004 std::unique_lock
aGuard( m_aMutex
);
1005 return setInputStreamImpl(aGuard
, rxInputStream
, bSetXSeekable
);
1008 bool UcbLockBytes::setInputStreamImpl( std::unique_lock
<std::mutex
>& /*rGuard*/, const Reference
<XInputStream
> &rxInputStream
, bool bSetXSeekable
)
1014 if ( !m_bDontClose
&& m_xInputStream
.is() )
1015 m_xInputStream
->closeInput();
1017 m_xInputStream
= rxInputStream
;
1021 m_xSeekable
.set( rxInputStream
, UNO_QUERY
);
1022 if( !m_xSeekable
.is() && rxInputStream
.is() )
1024 Reference
< XComponentContext
> xContext
= ::comphelper::getProcessComponentContext();
1025 rtl::Reference
< utl::TempFileFastService
> rxTempOut( new utl::TempFileFastService
);
1027 ::comphelper::OStorageHelper::CopyInputToOutput( rxInputStream
, rxTempOut
);
1028 m_xInputStream
.set( rxTempOut
);
1029 m_xSeekable
.set( rxTempOut
);
1033 bRet
= m_xInputStream
.is();
1035 catch (const Exception
&)
1039 if ( m_bStreamValid
&& m_xInputStream
.is() )
1040 m_aInitialized
.set();
1045 void UcbLockBytes::SetStreamValid()
1047 m_bStreamValid
= true;
1048 if ( m_xInputStream
.is() )
1049 m_aInitialized
.set();
1052 void UcbLockBytes::terminate()
1054 m_bTerminated
= true;
1055 m_aInitialized
.set();
1056 m_aTerminated
.set();
1058 if ( GetError() == ERRCODE_NONE
&& !m_xInputStream
.is() )
1060 OSL_FAIL("No InputStream, but no error set!" );
1061 SetError( ERRCODE_IO_NOTEXISTS
);
1065 ErrCode
UcbLockBytes::ReadAt(sal_uInt64
const nPos
,
1066 void *pBuffer
, std::size_t nCount
, std::size_t *pRead
) const
1068 if ( IsSynchronMode() )
1070 UcbLockBytes
* pThis
= const_cast < UcbLockBytes
* >( this );
1071 pThis
->m_aInitialized
.wait();
1074 Reference
<XInputStream
> xStream
= getInputStream();
1075 if ( !xStream
.is() )
1077 if ( m_bTerminated
)
1078 return ERRCODE_IO_CANTREAD
;
1080 return ERRCODE_IO_PENDING
;
1086 Reference
<XSeekable
> xSeekable
= getSeekable();
1087 if ( !xSeekable
.is() )
1088 return ERRCODE_IO_CANTREAD
;
1092 xSeekable
->seek( nPos
);
1094 catch (const IOException
&)
1096 return ERRCODE_IO_CANTSEEK
;
1098 catch (const css::lang::IllegalArgumentException
&)
1100 return ERRCODE_IO_CANTSEEK
;
1105 if(nCount
> 0x7FFFFFFF)
1107 nCount
= 0x7FFFFFFF;
1111 if ( !m_bTerminated
&& !IsSynchronMode() )
1113 sal_uInt64 nLen
= xSeekable
->getLength();
1114 if ( nPos
+ nCount
> nLen
)
1115 return ERRCODE_IO_PENDING
;
1118 comphelper::ByteReader
* pByteReader
= dynamic_cast< comphelper::ByteReader
* >(xStream
.get());
1121 nSize
= pByteReader
->readSomeBytes( static_cast<sal_Int8
*>(pBuffer
), sal_Int32(nCount
) );
1125 Sequence
<sal_Int8
> aData
;
1126 nSize
= xStream
->readBytes( aData
, sal_Int32(nCount
) );
1127 memcpy (pBuffer
, aData
.getConstArray(), nSize
);
1130 catch (const IOException
&)
1132 return ERRCODE_IO_CANTREAD
;
1136 *pRead
= static_cast<std::size_t>(nSize
);
1138 return ERRCODE_NONE
;
1141 ErrCode
UcbLockBytes::WriteAt(sal_uInt64
const nPos
, const void *pBuffer
,
1142 std::size_t nCount
, std::size_t *pWritten
)
1147 DBG_ASSERT( IsSynchronMode(), "Writing is only possible in SynchronMode!" );
1148 DBG_ASSERT( m_aInitialized
.check(), "Writing bevor stream is ready!" );
1150 Reference
<XSeekable
> xSeekable
= getSeekable();
1151 Reference
<XOutputStream
> xOutputStream
= getOutputStream();
1152 if ( !xOutputStream
.is() || !xSeekable
.is() )
1153 return ERRCODE_IO_CANTWRITE
;
1157 xSeekable
->seek( nPos
);
1159 catch (const IOException
&)
1161 return ERRCODE_IO_CANTSEEK
;
1164 sal_Int8
const * pData
= static_cast<sal_Int8
const *>(pBuffer
);
1165 Sequence
<sal_Int8
> aData( pData
, nCount
);
1168 xOutputStream
->writeBytes( aData
);
1172 catch (const Exception
&)
1174 return ERRCODE_IO_CANTWRITE
;
1177 return ERRCODE_NONE
;
1180 ErrCode
UcbLockBytes::Flush() const
1182 Reference
<XOutputStream
> xOutputStream
= getOutputStream();
1183 if ( !xOutputStream
.is() )
1184 return ERRCODE_IO_CANTWRITE
;
1188 xOutputStream
->flush();
1190 catch (const Exception
&)
1192 return ERRCODE_IO_CANTWRITE
;
1195 return ERRCODE_NONE
;
1198 ErrCode
UcbLockBytes::SetSize (sal_uInt64
const nNewSize
)
1200 SvLockBytesStat aStat
;
1202 std::size_t nSize
= aStat
.nSize
;
1204 if ( nSize
> nNewSize
)
1206 Reference
< XTruncate
> xTrunc( getOutputStream(), UNO_QUERY
);
1213 SAL_INFO("unotools.ucbhelper", "Not truncable!");
1217 if ( nSize
< nNewSize
)
1219 std::size_t nDiff
= nNewSize
-nSize
, nCount
=0;
1220 std::unique_ptr
<sal_uInt8
[]> pBuffer(new sal_uInt8
[ nDiff
]);
1221 memset(pBuffer
.get(), 0, nDiff
); // initialize for enhanced security
1222 WriteAt( nSize
, pBuffer
.get(), nDiff
, &nCount
);
1223 if ( nCount
!= nDiff
)
1224 return ERRCODE_IO_CANTWRITE
;
1227 return ERRCODE_NONE
;
1230 ErrCode
UcbLockBytes::Stat( SvLockBytesStat
*pStat
) const
1232 if ( IsSynchronMode() )
1234 UcbLockBytes
* pThis
= const_cast < UcbLockBytes
* >( this );
1235 pThis
->m_aInitialized
.wait();
1239 return ERRCODE_IO_INVALIDPARAMETER
;
1241 Reference
<XInputStream
> xStream
= getInputStream();
1242 Reference
<XSeekable
> xSeekable
= getSeekable();
1244 if ( !xStream
.is() )
1246 if ( m_bTerminated
)
1247 return ERRCODE_IO_INVALIDACCESS
;
1249 return ERRCODE_IO_PENDING
;
1251 else if( !xSeekable
.is() )
1252 return ERRCODE_IO_CANTTELL
;
1256 pStat
->nSize
= sal_uLong(xSeekable
->getLength());
1258 catch (const IOException
&)
1260 return ERRCODE_IO_CANTTELL
;
1263 return ERRCODE_NONE
;
1266 UcbLockBytesRef
UcbLockBytes::CreateInputLockBytes( const Reference
< XInputStream
>& xInputStream
)
1268 if( !xInputStream
.is() )
1271 UcbLockBytesRef xLockBytes
= new UcbLockBytes
;
1272 xLockBytes
->setDontClose();
1273 xLockBytes
->setInputStream( xInputStream
);
1274 xLockBytes
->terminate();
1278 UcbLockBytesRef
UcbLockBytes::CreateLockBytes( const Reference
< XStream
>& xStream
)
1283 UcbLockBytesRef xLockBytes
= new UcbLockBytes
;
1284 xLockBytes
->setDontClose();
1285 xLockBytes
->setStream( xStream
);
1286 xLockBytes
->terminate();
1290 UcbLockBytesRef
UcbLockBytes::CreateLockBytes( const Reference
< XContent
>& xContent
, const Sequence
< PropertyValue
>& rProps
,
1291 StreamMode eOpenMode
, const Reference
< XInteractionHandler
>& xInteractionHandler
)
1293 if( !xContent
.is() )
1296 UcbLockBytesRef xLockBytes
= new UcbLockBytes
;
1297 xLockBytes
->SetSynchronMode();
1298 Reference
< XActiveDataControl
> xSink
;
1299 if ( eOpenMode
& StreamMode::WRITE
)
1300 xSink
= new UcbStreamer_Impl(xLockBytes
.get());
1302 xSink
= new UcbDataSink_Impl(xLockBytes
.get());
1304 if ( rProps
.hasElements() )
1306 Reference
< XCommandProcessor
> xProcessor( xContent
, UNO_QUERY
);
1308 aCommand
.Name
= "setPropertyValues";
1309 aCommand
.Handle
= -1; /* unknown */
1310 aCommand
.Argument
<<= rProps
;
1311 xProcessor
->execute( aCommand
, 0, Reference
< XCommandEnvironment
>() );
1314 OpenCommandArgument2 aArgument
;
1315 aArgument
.Sink
= xSink
;
1316 aArgument
.Mode
= OpenMode::DOCUMENT
;
1319 aCommand
.Name
= "open";
1320 aCommand
.Argument
<<= aArgument
;
1322 bool bError
= UCBOpenContentSync( xLockBytes
,
1326 xInteractionHandler
);
1328 if ( xLockBytes
->GetError() == ERRCODE_NONE
&& ( bError
|| !xLockBytes
->getInputStream().is() ) )
1330 OSL_FAIL("No InputStream, but no error set!" );
1331 xLockBytes
->SetError( ERRCODE_IO_GENERAL
);
1339 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */