Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / unotools / source / ucbhelper / ucblockbytes.cxx
blob0a46323b57ebf4215f89899aeb5e1daddc5ab893
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
63 #include <mutex>
64 #include <utility>
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;
73 namespace utl
76 namespace {
78 /**
79 Helper class for getting a XInputStream when opening a content
81 class UcbDataSink_Impl : public ::cppu::WeakImplHelper< XActiveDataControl, XActiveDataSink >
83 UcbLockBytesRef m_xLockBytes;
85 public:
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(); }
97 // XActiveDataSink.
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;
112 public:
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;
139 public:
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 >
158 public:
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();
182 namespace {
184 class Moderator
185 : public osl::Thread
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!!!
192 public:
193 /// @throws ContentCreationException
194 /// @throws RuntimeException
195 Moderator(
196 Reference < XContent > const & xContent,
197 Reference < XInteractionHandler > const & xInteract,
198 Command aArg
201 enum class ResultType {
202 NORESULT,
204 INTERACTIONREQUEST, // reply expected
206 INPUTSTREAM,
207 STREAM,
209 RESULT,
210 TIMEDOUT,
211 COMMANDABORTED,
212 COMMANDFAILED,
213 INTERACTIVEIO,
214 UNSUPPORTED,
215 GENERAL
218 class ConditionRes
219 : public salhelper::Condition
221 public:
222 ConditionRes(osl::Mutex& aMutex,Moderator& aModerator)
223 : salhelper::Condition(aMutex),
224 m_aModerator(aModerator)
228 protected:
229 bool applies() const override {
230 return m_aModerator.m_aResultType != ResultType::NORESULT;
233 private:
234 Moderator& m_aModerator;
237 struct Result {
238 ResultType type;
239 Any result;
240 IOErrorCode ioErrorCode;
243 Result getResult(const sal_uInt32 milliSec);
245 enum ReplyType {
246 NOREPLY,
247 EXIT,
248 REQUESTHANDLED
251 class ConditionRep
252 : public salhelper::Condition
254 public:
255 ConditionRep(osl::Mutex& aMutex,Moderator& aModerator)
256 : salhelper::Condition(aMutex),
257 m_aModerator(aModerator)
261 protected:
262 bool applies() const override {
263 return m_aModerator.m_aReplyType != NOREPLY;
266 private:
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);
277 protected:
278 virtual void SAL_CALL run() override;
279 virtual void SAL_CALL onTerminated() override;
281 private:
282 osl::Mutex m_aMutex;
284 friend class ConditionRes;
286 ConditionRes m_aRes;
287 ResultType m_aResultType;
288 IOErrorCode m_nIOErrorCode;
289 Any m_aResult;
291 friend class ConditionRep;
293 ConditionRep m_aRep;
294 ReplyType m_aReplyType;
296 Command m_aArg;
297 ::ucbhelper::Content m_aContent;
300 class ModeratorsActiveDataStreamer
301 : public ::cppu::WeakImplHelper<XActiveDataStreamer>
303 public:
305 explicit ModeratorsActiveDataStreamer(Moderator &theModerator);
307 // XActiveDataStreamer
308 virtual void SAL_CALL
309 setStream(
310 const Reference< XStream >& aStream
311 ) override;
313 virtual Reference<XStream> SAL_CALL getStream () override
315 std::scoped_lock aGuard(m_aMutex);
316 return m_xStream;
319 private:
320 Moderator& m_aModerator;
322 std::mutex m_aMutex;
323 Reference<XStream> m_xStream;
326 class ModeratorsActiveDataSink
327 : public ::cppu::WeakImplHelper<XActiveDataSink>
329 public:
331 explicit ModeratorsActiveDataSink(Moderator &theModerator);
333 // XActiveDataSink.
334 virtual void SAL_CALL
335 setInputStream (
336 const Reference<XInputStream> &rxInputStream
337 ) override;
339 virtual Reference<XInputStream> SAL_CALL getInputStream() override
341 std::scoped_lock aGuard(m_aMutex);
342 return m_xStream;
345 private:
346 Moderator& m_aModerator;
347 std::mutex m_aMutex;
348 Reference<XInputStream> m_xStream;
353 ModeratorsActiveDataSink::ModeratorsActiveDataSink(Moderator &theModerator)
354 : m_aModerator(theModerator)
358 // XActiveDataSink.
359 void SAL_CALL
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.
377 void SAL_CALL
378 ModeratorsActiveDataStreamer::setStream (
379 const Reference<XStream> &rxStream
382 m_aModerator.setStream(rxStream);
383 std::scoped_lock aGuard(m_aMutex);
384 m_xStream = rxStream;
387 namespace {
389 class ModeratorsInteractionHandler
390 : public ::cppu::WeakImplHelper<XInteractionHandler>
392 public:
394 explicit ModeratorsInteractionHandler(Moderator &theModerator);
396 virtual void SAL_CALL
397 handle( const Reference<XInteractionRequest >& Request ) override;
399 private:
401 Moderator& m_aModerator;
406 ModeratorsInteractionHandler::ModeratorsInteractionHandler(
407 Moderator &aModerator)
408 : m_aModerator(aModerator)
412 void SAL_CALL
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,
424 Command aArg
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)),
432 m_aContent(
433 xContent,
434 new UcbTaskEnvironment(
435 xInteract.is() ? new ModeratorsInteractionHandler(*this) : nullptr,
436 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;
447 int dec(2);
448 if(m_aArg.Argument >>= aPostArg) {
449 pxSink = &aPostArg.Sink;
450 dec = 0;
452 else if(m_aArg.Argument >>= aOpenArg) {
453 pxSink = &aOpenArg.Sink;
454 dec = 1;
457 if(dec ==2)
458 throw ContentCreationException();
460 Reference < XActiveDataSink > xActiveSink(*pxSink,UNO_QUERY);
461 if(xActiveSink.is())
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)));
468 if(dec == 0)
469 m_aArg.Argument <<= aPostArg;
470 else if(dec == 1)
471 m_aArg.Argument <<= aOpenArg;
474 Moderator::Result Moderator::getResult(const sal_uInt32 milliSec)
476 Result ret;
477 try {
478 salhelper::ConditionWaiter aWaiter(m_aRes,milliSec);
479 ret.type = m_aResultType;
480 ret.result = m_aResult;
481 ret.ioErrorCode = m_nIOErrorCode;
483 // reset
484 m_aResultType = ResultType::NORESULT;
486 catch (const salhelper::ConditionWaiter::timedout&)
488 ret.type = ResultType::TIMEDOUT;
491 return ret;
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;
504 do {
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;
515 // reset
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);
524 if(aRef.is()) {
525 aRef->select();
529 // resignal the exit condition
530 setReply(EXIT);
531 break;
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)
550 setReply(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)
567 setReply(EXIT);
570 void SAL_CALL Moderator::run()
572 osl_setThreadName("utl::Moderator");
574 ResultType aResultType;
575 Any aResult;
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;
608 m_aResult = aResult;
609 m_nIOErrorCode = nIOErrorCode;
613 void SAL_CALL Moderator::onTerminated()
616 salhelper::ConditionWaiter aWaiter(m_aRep);
618 delete this;
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,
628 const Command& rArg,
629 const Reference < XInterface >& xSink,
630 const Reference < XInteractionHandler >& xInteract );
632 static bool UCBOpenContentSync(
633 const UcbLockBytesRef& xLockBytes,
634 Reference < XContent > const & xContent,
635 const Command& rArg,
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
642 // headers is valid
644 Reference<XContentIdentifier> xContId(
645 xContent.is() ? xContent->getIdentifier() : nullptr );
647 OUString aScheme;
648 if(xContId.is())
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);
666 if(xProps.is()) {
667 xListener =
668 new UcbPropertiesChangeListener_Impl(xLockBytes);
669 xProps->addPropertiesChangeListener(
670 Sequence< OUString >(),
671 xListener);
674 bool bException(false);
675 bool bAborted(false);
676 bool bResultAchieved(false);
678 Moderator* pMod = nullptr;
681 pMod = new Moderator(xContent,xInteract,rArg);
682 pMod->create();
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);
699 switch(res.type) {
700 case Moderator::ResultType::STREAM:
702 Reference<XStream> result;
703 if(res.result >>= result) {
704 Reference < XActiveDataStreamer > xStreamer(
705 xSink, UNO_QUERY
708 if(xStreamer.is())
709 xStreamer->setStream(result);
711 pMod->setReply(Moderator::REQUESTHANDLED);
712 break;
714 case Moderator::ResultType::INPUTSTREAM:
716 Reference<XInputStream> result;
717 res.result >>= result;
718 Reference < XActiveDataSink > xActiveSink(
719 xSink, UNO_QUERY
722 if(xActiveSink.is())
723 xActiveSink->setInputStream(result);
724 pMod->setReply(Moderator::REQUESTHANDLED);
725 break;
727 case Moderator::ResultType::TIMEDOUT:
729 Reference<XInteractionRetry> xRet;
730 if(xInteract.is()) {
731 InteractiveNetworkConnectException aExcep;
732 INetURLObject aURL(
733 xContId.is() ?
734 xContId->getContentIdentifier() :
735 OUString() );
736 aExcep.Server = aURL.GetHost();
737 aExcep.Classification = InteractionClassification_ERROR;
738 aExcep.Message = "server not responding after five seconds";
739 Any request;
740 request <<= aExcep;
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();
753 if(ref.is()) {
754 Reference<XInterface> xInt(ref);
755 xRet.set(xInt,UNO_QUERY);
759 if(!xRet.is()) {
760 bAborted = true;
761 xLockBytes->SetError(ERRCODE_ABORT);
764 break;
766 case Moderator::ResultType::INTERACTIONREQUEST:
768 Reference<XInteractionRequest> Request;
769 res.result >>= Request;
770 xInteract->handle(Request);
771 pMod->setReply(Moderator::REQUESTHANDLED);
772 break;
774 case Moderator::ResultType::RESULT:
776 bResultAchieved = true;
777 break;
779 case Moderator::ResultType::COMMANDABORTED:
781 bAborted = true;
782 xLockBytes->SetError( ERRCODE_ABORT );
783 break;
785 case Moderator::ResultType::COMMANDFAILED:
787 bAborted = true;
788 xLockBytes->SetError( ERRCODE_ABORT );
789 break;
791 case Moderator::ResultType::INTERACTIVEIO:
793 bException = true;
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 );
801 else
802 xLockBytes->SetError( ERRCODE_IO_GENERAL );
803 break;
805 case Moderator::ResultType::UNSUPPORTED:
807 bException = true;
808 xLockBytes->SetError( ERRCODE_IO_NOTSUPPORTED );
809 break;
811 default:
813 bException = true;
814 xLockBytes->SetError( ERRCODE_IO_GENERAL );
815 break;
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 );
838 if ( xControl.is() )
839 xControl->terminate();
841 if ( xProps.is() )
842 xProps->removePropertiesChangeListener(
843 Sequence< OUString >(),
844 xListener );
846 return ( bAborted || bException );
850 Function for opening UCB contents synchronously
852 static bool UCBOpenContentSync_(
853 const UcbLockBytesRef& xLockBytes,
854 const Reference < XContent >& xContent,
855 const Command& rArg,
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 );
872 if ( xProps.is() )
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&)
884 bAborted = true;
885 xLockBytes->SetError( ERRCODE_ABORT );
887 catch (const CommandFailedException&)
889 bAborted = true;
890 xLockBytes->SetError( ERRCODE_ABORT );
892 catch (const InteractiveIOException& r)
894 bException = true;
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 );
901 else
902 xLockBytes->SetError( ERRCODE_IO_GENERAL );
904 catch (const UnsupportedDataSinkException&)
906 bException = true;
907 xLockBytes->SetError( ERRCODE_IO_NOTSUPPORTED );
909 catch (const Exception&)
911 bException = true;
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 );
927 if ( xControl.is() )
928 xControl->terminate();
930 if ( xProps.is() )
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)
942 SetSynchronMode();
945 UcbLockBytes::~UcbLockBytes()
947 if ( !m_bDontClose )
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() )
965 return;
969 m_xOutputStream->closeOutput();
971 catch (const RuntimeException&)
974 catch (const IOException&)
979 Reference < XInputStream > UcbLockBytes::getInputStream()
981 std::unique_lock aGuard( m_aMutex );
982 m_bDontClose = true;
983 return m_xInputStream;
986 void UcbLockBytes::setStream( const Reference<XStream>& aStream )
988 std::unique_lock aGuard( m_aMutex );
989 if ( aStream.is() )
991 m_xOutputStream = aStream->getOutputStream();
992 setInputStreamImpl( aGuard, aStream->getInputStream(), false );
993 m_xSeekable.set( aStream, UNO_QUERY );
995 else
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 )
1010 bool bRet = false;
1014 if ( !m_bDontClose && m_xInputStream.is() )
1015 m_xInputStream->closeInput();
1017 m_xInputStream = rxInputStream;
1019 if( bSetXSeekable )
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();
1042 return bRet;
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;
1079 else
1080 return ERRCODE_IO_PENDING;
1083 if ( pRead )
1084 *pRead = 0;
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;
1103 sal_Int32 nSize;
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());
1119 if (pByteReader)
1121 nSize = pByteReader->readSomeBytes( static_cast<sal_Int8*>(pBuffer), sal_Int32(nCount) );
1123 else
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;
1135 if (pRead)
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)
1144 if ( pWritten )
1145 *pWritten = 0;
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 );
1169 if ( pWritten )
1170 *pWritten = nCount;
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;
1201 Stat( &aStat );
1202 std::size_t nSize = aStat.nSize;
1204 if ( nSize > nNewSize )
1206 Reference < XTruncate > xTrunc( getOutputStream(), UNO_QUERY );
1207 if ( xTrunc.is() )
1209 xTrunc->truncate();
1210 nSize = 0;
1212 else {
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();
1238 if (!pStat)
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;
1248 else
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() )
1269 return nullptr;
1271 UcbLockBytesRef xLockBytes = new UcbLockBytes;
1272 xLockBytes->setDontClose();
1273 xLockBytes->setInputStream( xInputStream );
1274 xLockBytes->terminate();
1275 return xLockBytes;
1278 UcbLockBytesRef UcbLockBytes::CreateLockBytes( const Reference< XStream >& xStream )
1280 if( !xStream.is() )
1281 return nullptr;
1283 UcbLockBytesRef xLockBytes = new UcbLockBytes;
1284 xLockBytes->setDontClose();
1285 xLockBytes->setStream( xStream );
1286 xLockBytes->terminate();
1287 return xLockBytes;
1290 UcbLockBytesRef UcbLockBytes::CreateLockBytes( const Reference < XContent >& xContent, const Sequence < PropertyValue >& rProps,
1291 StreamMode eOpenMode, const Reference < XInteractionHandler >& xInteractionHandler )
1293 if( !xContent.is() )
1294 return nullptr;
1296 UcbLockBytesRef xLockBytes = new UcbLockBytes;
1297 xLockBytes->SetSynchronMode();
1298 Reference< XActiveDataControl > xSink;
1299 if ( eOpenMode & StreamMode::WRITE )
1300 xSink = new UcbStreamer_Impl(xLockBytes.get());
1301 else
1302 xSink = new UcbDataSink_Impl(xLockBytes.get());
1304 if ( rProps.hasElements() )
1306 Reference < XCommandProcessor > xProcessor( xContent, UNO_QUERY );
1307 Command aCommand;
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;
1318 Command aCommand;
1319 aCommand.Name = "open";
1320 aCommand.Argument <<= aArgument;
1322 bool bError = UCBOpenContentSync( xLockBytes,
1323 xContent,
1324 aCommand,
1325 xSink,
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 );
1334 return xLockBytes;
1339 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */