Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / unotools / source / ucbhelper / ucblockbytes.cxx
blob0aa836bfd7fbf5ef8b8ad49433fbc168ddbde8a8
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/task/XInteractionAbort.hpp>
31 #include <com/sun/star/ucb/InteractiveNetworkConnectException.hpp>
32 #include <com/sun/star/ucb/CommandFailedException.hpp>
33 #include <com/sun/star/ucb/ContentCreationException.hpp>
34 #include <com/sun/star/ucb/CommandAbortedException.hpp>
35 #include <com/sun/star/ucb/UnsupportedDataSinkException.hpp>
36 #include <com/sun/star/ucb/InteractiveIOException.hpp>
37 #include <com/sun/star/ucb/XContentIdentifier.hpp>
38 #include <com/sun/star/ucb/XContent.hpp>
39 #include <com/sun/star/io/IOException.hpp>
40 #include <com/sun/star/io/XActiveDataStreamer.hpp>
41 #include <com/sun/star/io/TempFile.hpp>
42 #include <com/sun/star/ucb/XCommandProcessor.hpp>
43 #include <com/sun/star/task/XInteractionHandler.hpp>
44 #include <com/sun/star/ucb/OpenCommandArgument2.hpp>
45 #include <com/sun/star/ucb/PostCommandArgument2.hpp>
46 #include <com/sun/star/ucb/OpenMode.hpp>
47 #include <com/sun/star/beans/PropertyValue.hpp>
48 #include <com/sun/star/beans/XPropertiesChangeNotifier.hpp>
49 #include <com/sun/star/beans/XPropertiesChangeListener.hpp>
50 #include <com/sun/star/io/XActiveDataSink.hpp>
51 #include <com/sun/star/io/XActiveDataControl.hpp>
52 #include <com/sun/star/io/XSeekable.hpp>
53 #include <cppuhelper/implbase.hxx>
54 #include <tools/debug.hxx>
55 #include <com/sun/star/io/XTruncate.hpp>
56 #include <com/sun/star/lang/IllegalArgumentException.hpp>
58 #include <comphelper/storagehelper.hxx>
59 #include <ucbhelper/content.hxx>
61 using namespace ::com::sun::star::uno;
62 using namespace ::com::sun::star::io;
63 using namespace ::com::sun::star::ucb;
64 using namespace ::com::sun::star::task;
65 using namespace ::com::sun::star::lang;
66 using namespace ::com::sun::star::beans;
68 namespace utl
71 /**
72 Helper class for getting a XInputStream when opening a content
74 class UcbDataSink_Impl : public ::cppu::WeakImplHelper< XActiveDataControl, XActiveDataSink >
76 UcbLockBytesRef m_xLockBytes;
78 public:
79 explicit UcbDataSink_Impl( UcbLockBytes* pLockBytes )
80 : m_xLockBytes( pLockBytes )
83 // XActiveDataControl.
84 virtual void SAL_CALL addListener ( const Reference<XStreamListener> &/*rxListener*/) override {}
85 virtual void SAL_CALL removeListener ( const Reference<XStreamListener> &/*rxListener*/) override {}
86 virtual void SAL_CALL start() override {}
87 virtual void SAL_CALL terminate() override
88 { m_xLockBytes->terminate_Impl(); }
90 // XActiveDataSink.
91 virtual void SAL_CALL setInputStream ( const Reference<XInputStream> &rxInputStream) override
92 { m_xLockBytes->setInputStream_Impl (rxInputStream); }
93 virtual Reference<XInputStream> SAL_CALL getInputStream() override
94 { return m_xLockBytes->getInputStream_Impl(); }
97 /**
98 Helper class for getting a XStream when opening a content
100 class UcbStreamer_Impl : public ::cppu::WeakImplHelper< XActiveDataStreamer, XActiveDataControl >
102 Reference < XStream > m_xStream;
103 UcbLockBytesRef m_xLockBytes;
105 public:
106 explicit UcbStreamer_Impl( UcbLockBytes* pLockBytes )
107 : m_xLockBytes( pLockBytes )
110 // XActiveDataControl.
111 virtual void SAL_CALL addListener ( const Reference<XStreamListener> &/*rxListener*/) override {}
112 virtual void SAL_CALL removeListener ( const Reference<XStreamListener> &/*rxListener*/) override {}
113 virtual void SAL_CALL start() override {}
114 virtual void SAL_CALL terminate() override
115 { m_xLockBytes->terminate_Impl(); }
117 // XActiveDataStreamer
118 virtual void SAL_CALL setStream( const Reference< XStream >& aStream ) override
119 { m_xStream = aStream; m_xLockBytes->setStream_Impl( aStream ); }
120 virtual Reference< XStream > SAL_CALL getStream() override
121 { return m_xStream; }
125 Helper class for managing interactions and progress when executing UCB commands
127 class UcbTaskEnvironment : public ::cppu::WeakImplHelper< XCommandEnvironment >
129 Reference< XInteractionHandler > m_xInteractionHandler;
130 Reference< XProgressHandler > m_xProgressHandler;
132 public:
133 UcbTaskEnvironment( const Reference< XInteractionHandler>& rxInteractionHandler,
134 const Reference< XProgressHandler>& rxProgressHandler )
135 : m_xInteractionHandler( rxInteractionHandler )
136 , m_xProgressHandler( rxProgressHandler )
139 virtual Reference<XInteractionHandler> SAL_CALL getInteractionHandler() override
140 { return m_xInteractionHandler; }
142 virtual Reference<XProgressHandler> SAL_CALL getProgressHandler() override
143 { return m_xProgressHandler; }
147 Helper class for property change notifies when executing UCB commands
149 class UcbPropertiesChangeListener_Impl : public ::cppu::WeakImplHelper< XPropertiesChangeListener >
151 public:
152 UcbLockBytesRef m_xLockBytes;
154 explicit UcbPropertiesChangeListener_Impl( UcbLockBytesRef const & rRef )
155 : m_xLockBytes( rRef )
158 virtual void SAL_CALL disposing ( const EventObject &/*rEvent*/) override {}
159 virtual void SAL_CALL propertiesChange ( const Sequence<PropertyChangeEvent> &rEvent) override;
162 void SAL_CALL UcbPropertiesChangeListener_Impl::propertiesChange ( const Sequence<PropertyChangeEvent> &rEvent)
164 for (const auto& rPropChangeEvent : rEvent)
166 if (rPropChangeEvent.PropertyName == "DocumentHeader")
168 m_xLockBytes->SetStreamValid_Impl();
173 class Moderator
174 : public osl::Thread
176 // usage restriction:
177 // It might be possible, that the call to the interactionhandler and/or
178 // progresshandler is done asynchronously, while the 'execute' simply
179 // returns. This would imply that these class must be refcounted!!!
181 public:
182 /// @throws ContentCreationException
183 /// @throws RuntimeException
184 Moderator(
185 Reference < XContent > const & xContent,
186 Reference < XInteractionHandler > const & xInteract,
187 const Command& rArg
190 enum class ResultType {
191 NORESULT,
193 INTERACTIONREQUEST, // reply expected
195 INPUTSTREAM,
196 STREAM,
198 RESULT,
199 TIMEDOUT,
200 COMMANDABORTED,
201 COMMANDFAILED,
202 INTERACTIVEIO,
203 UNSUPPORTED,
204 GENERAL
207 class ConditionRes
208 : public salhelper::Condition
210 public:
211 ConditionRes(osl::Mutex& aMutex,Moderator& aModerator)
212 : salhelper::Condition(aMutex),
213 m_aModerator(aModerator)
217 protected:
218 bool applies() const override {
219 return m_aModerator.m_aResultType != ResultType::NORESULT;
222 private:
223 Moderator& m_aModerator;
226 struct Result {
227 ResultType type;
228 Any result;
229 IOErrorCode ioErrorCode;
232 Result getResult(const sal_uInt32 milliSec);
234 enum ReplyType {
235 NOREPLY,
236 EXIT,
237 REQUESTHANDLED
240 class ConditionRep
241 : public salhelper::Condition
243 public:
244 ConditionRep(osl::Mutex& aMutex,Moderator& aModerator)
245 : salhelper::Condition(aMutex),
246 m_aModerator(aModerator)
250 protected:
251 bool applies() const override {
252 return m_aModerator.m_aReplyType != NOREPLY;
255 private:
256 Moderator& m_aModerator;
259 void setReply(ReplyType);
261 void handle( const Reference<XInteractionRequest >& Request );
263 void setStream(const Reference< XStream >& aStream);
264 void setInputStream(const Reference<XInputStream> &rxInputStream);
266 protected:
267 virtual void SAL_CALL run() override;
268 virtual void SAL_CALL onTerminated() override;
270 private:
271 osl::Mutex m_aMutex;
273 friend class ConditionRes;
275 ConditionRes m_aRes;
276 ResultType m_aResultType;
277 IOErrorCode m_nIOErrorCode;
278 Any m_aResult;
280 friend class ConditionRep;
282 ConditionRep m_aRep;
283 ReplyType m_aReplyType;
285 Command m_aArg;
286 ::ucbhelper::Content m_aContent;
289 class ModeratorsActiveDataStreamer
290 : public ::cppu::WeakImplHelper<XActiveDataStreamer>
292 public:
294 explicit ModeratorsActiveDataStreamer(Moderator &theModerator);
296 // XActiveDataStreamer
297 virtual void SAL_CALL
298 setStream(
299 const Reference< XStream >& aStream
300 ) override;
302 virtual Reference<XStream> SAL_CALL getStream () override
304 osl::MutexGuard aGuard(m_aMutex);
305 return m_xStream;
308 private:
309 Moderator& m_aModerator;
311 osl::Mutex m_aMutex;
312 Reference<XStream> m_xStream;
315 class ModeratorsActiveDataSink
316 : public ::cppu::WeakImplHelper<XActiveDataSink>
318 public:
320 explicit ModeratorsActiveDataSink(Moderator &theModerator);
322 // XActiveDataSink.
323 virtual void SAL_CALL
324 setInputStream (
325 const Reference<XInputStream> &rxInputStream
326 ) override;
328 virtual Reference<XInputStream> SAL_CALL getInputStream() override
330 osl::MutexGuard aGuard(m_aMutex);
331 return m_xStream;
334 private:
335 Moderator& m_aModerator;
336 osl::Mutex m_aMutex;
337 Reference<XInputStream> m_xStream;
340 ModeratorsActiveDataSink::ModeratorsActiveDataSink(Moderator &theModerator)
341 : m_aModerator(theModerator)
345 // XActiveDataSink.
346 void SAL_CALL
347 ModeratorsActiveDataSink::setInputStream (
348 const Reference<XInputStream> &rxInputStream
351 m_aModerator.setInputStream(rxInputStream);
352 osl::MutexGuard aGuard(m_aMutex);
353 m_xStream = rxInputStream;
356 ModeratorsActiveDataStreamer::ModeratorsActiveDataStreamer(
357 Moderator &theModerator
359 : m_aModerator(theModerator)
363 // XActiveDataStreamer.
364 void SAL_CALL
365 ModeratorsActiveDataStreamer::setStream (
366 const Reference<XStream> &rxStream
369 m_aModerator.setStream(rxStream);
370 osl::MutexGuard aGuard(m_aMutex);
371 m_xStream = rxStream;
374 class ModeratorsInteractionHandler
375 : public ::cppu::WeakImplHelper<XInteractionHandler>
377 public:
379 explicit ModeratorsInteractionHandler(Moderator &theModerator);
381 virtual void SAL_CALL
382 handle( const Reference<XInteractionRequest >& Request ) override;
384 private:
386 Moderator& m_aModerator;
389 ModeratorsInteractionHandler::ModeratorsInteractionHandler(
390 Moderator &aModerator)
391 : m_aModerator(aModerator)
395 void SAL_CALL
396 ModeratorsInteractionHandler::handle(
397 const Reference<XInteractionRequest >& Request
400 // wakes up the mainthread
401 m_aModerator.handle(Request);
404 Moderator::Moderator(
405 Reference < XContent > const & xContent,
406 Reference < XInteractionHandler > const & xInteract,
407 const Command& rArg
409 : m_aMutex(),
411 m_aRes(m_aMutex,*this),
412 m_aResultType(ResultType::NORESULT),
413 m_nIOErrorCode(IOErrorCode_ABORT),
414 m_aResult(),
416 m_aRep(m_aMutex,*this),
417 m_aReplyType(NOREPLY),
419 m_aArg(rArg),
420 m_aContent(
421 xContent,
422 new UcbTaskEnvironment(
423 xInteract.is() ? new ModeratorsInteractionHandler(*this) : nullptr,
424 nullptr),
425 comphelper::getProcessComponentContext())
427 // now exchange the whole data sink stuff
428 // with a thread safe version
430 Reference<XInterface> *pxSink = nullptr;
432 PostCommandArgument2 aPostArg;
433 OpenCommandArgument2 aOpenArg;
435 int dec(2);
436 if(m_aArg.Argument >>= aPostArg) {
437 pxSink = &aPostArg.Sink;
438 dec = 0;
440 else if(m_aArg.Argument >>= aOpenArg) {
441 pxSink = &aOpenArg.Sink;
442 dec = 1;
445 if(dec ==2)
446 throw ContentCreationException();
448 Reference < XActiveDataSink > xActiveSink(*pxSink,UNO_QUERY);
449 if(xActiveSink.is())
450 pxSink->set( static_cast<cppu::OWeakObject*>(new ModeratorsActiveDataSink(*this)));
452 Reference<XActiveDataStreamer> xStreamer( *pxSink, UNO_QUERY );
453 if ( xStreamer.is() )
454 pxSink->set( static_cast<cppu::OWeakObject*>(new ModeratorsActiveDataStreamer(*this)));
456 if(dec == 0)
457 m_aArg.Argument <<= aPostArg;
458 else if(dec == 1)
459 m_aArg.Argument <<= aOpenArg;
462 Moderator::Result Moderator::getResult(const sal_uInt32 milliSec)
464 Result ret;
465 try {
466 salhelper::ConditionWaiter aWaiter(m_aRes,milliSec);
467 ret.type = m_aResultType;
468 ret.result = m_aResult;
469 ret.ioErrorCode = m_nIOErrorCode;
471 // reset
472 m_aResultType = ResultType::NORESULT;
474 catch (const salhelper::ConditionWaiter::timedout&)
476 ret.type = ResultType::TIMEDOUT;
479 return ret;
482 void Moderator::setReply(ReplyType aReplyType )
484 salhelper::ConditionModifier aMod(m_aRep);
485 m_aReplyType = aReplyType;
488 void Moderator::handle( const Reference<XInteractionRequest >& Request )
490 ReplyType aReplyType;
492 do {
494 salhelper::ConditionModifier aMod(m_aRes);
495 m_aResultType = ResultType::INTERACTIONREQUEST;
496 m_aResult <<= Request;
500 salhelper::ConditionWaiter aWait(m_aRep);
501 aReplyType = m_aReplyType;
503 // reset
504 m_aReplyType = NOREPLY;
507 if(aReplyType == EXIT) {
508 const Sequence<Reference<XInteractionContinuation> > aSeq(
509 Request->getContinuations());
510 for(const auto& rContinuation : aSeq) {
511 Reference<XInteractionAbort> aRef(rContinuation,UNO_QUERY);
512 if(aRef.is()) {
513 aRef->select();
517 // resignal the exit condition
518 setReply(EXIT);
519 break;
521 } while(aReplyType != REQUESTHANDLED);
524 void Moderator::setStream(const Reference< XStream >& aStream)
527 salhelper::ConditionModifier aMod(m_aRes);
528 m_aResultType = ResultType::STREAM;
529 m_aResult <<= aStream;
531 ReplyType aReplyType;
533 salhelper::ConditionWaiter aWait(m_aRep);
534 aReplyType = m_aReplyType;
535 m_aReplyType = NOREPLY;
537 if(aReplyType == EXIT)
538 setReply(EXIT);
541 void Moderator::setInputStream(const Reference<XInputStream> &rxInputStream)
544 salhelper::ConditionModifier aMod(m_aRes);
545 m_aResultType = ResultType::INPUTSTREAM;
546 m_aResult <<= rxInputStream;
548 ReplyType aReplyType;
550 salhelper::ConditionWaiter aWait(m_aRep);
551 aReplyType = m_aReplyType;
552 m_aReplyType = NOREPLY;
554 if(aReplyType == EXIT)
555 setReply(EXIT);
558 void SAL_CALL Moderator::run()
560 osl_setThreadName("utl::Moderator");
562 ResultType aResultType;
563 Any aResult;
564 IOErrorCode nIOErrorCode = IOErrorCode_ABORT;
568 aResult = m_aContent.executeCommand(m_aArg.Name,m_aArg.Argument);
569 aResultType = ResultType::RESULT;
571 catch (const CommandAbortedException&)
573 aResultType = ResultType::COMMANDABORTED;
575 catch (const CommandFailedException&)
577 aResultType = ResultType::COMMANDFAILED;
579 catch (const InteractiveIOException& r)
581 nIOErrorCode = r.Code;
582 aResultType = ResultType::INTERACTIVEIO;
584 catch (const UnsupportedDataSinkException &)
586 aResultType = ResultType::UNSUPPORTED;
588 catch (const Exception&)
590 aResultType = ResultType::GENERAL;
594 salhelper::ConditionModifier aMod(m_aRes);
595 m_aResultType = aResultType;
596 m_aResult = aResult;
597 m_nIOErrorCode = nIOErrorCode;
601 void SAL_CALL Moderator::onTerminated()
604 salhelper::ConditionWaiter aWaiter(m_aRep);
606 delete this;
610 Function for opening UCB contents synchronously,
611 but with handled timeout;
613 static bool UCBOpenContentSync_(
614 const UcbLockBytesRef& xLockBytes,
615 const Reference < XContent >& xContent,
616 const Command& rArg,
617 const Reference < XInterface >& xSink,
618 const Reference < XInteractionHandler >& xInteract );
620 static bool UCBOpenContentSync(
621 const UcbLockBytesRef& xLockBytes,
622 Reference < XContent > const & xContent,
623 const Command& rArg,
624 const Reference < XInterface >& xSink,
625 Reference < XInteractionHandler > const & xInteract )
627 // http protocol must be handled in a special way:
628 // during the opening process the input stream may change
629 // only the last inputstream after notifying the document
630 // headers is valid
632 Reference<XContentIdentifier> xContId(
633 xContent.is() ? xContent->getIdentifier() : nullptr );
635 OUString aScheme;
636 if(xContId.is())
637 aScheme = xContId->getContentProviderScheme();
639 // now determine whether we use a timeout or not;
640 if( ! aScheme.equalsIgnoreAsciiCase("http") &&
641 ! aScheme.equalsIgnoreAsciiCase("https") &&
642 ! aScheme.equalsIgnoreAsciiCase("vnd.sun.star.webdav") &&
643 ! aScheme.equalsIgnoreAsciiCase("vnd.sun.star.webdavs") &&
644 ! aScheme.equalsIgnoreAsciiCase("ftp"))
645 return UCBOpenContentSync_(
646 xLockBytes,xContent,rArg,xSink,xInteract);
648 if ( !aScheme.equalsIgnoreAsciiCase( "http" ) &&
649 !aScheme.equalsIgnoreAsciiCase( "https" ) )
650 xLockBytes->SetStreamValid_Impl();
652 Reference< XPropertiesChangeListener > xListener;
653 Reference< XPropertiesChangeNotifier > xProps(xContent,UNO_QUERY);
654 if(xProps.is()) {
655 xListener =
656 new UcbPropertiesChangeListener_Impl(xLockBytes);
657 xProps->addPropertiesChangeListener(
658 Sequence< OUString >(),
659 xListener);
662 bool bException(false);
663 bool bAborted(false);
664 bool bResultAchieved(false);
666 Moderator* pMod = nullptr;
669 pMod = new Moderator(xContent,xInteract,rArg);
670 pMod->create();
672 catch (const ContentCreationException&)
674 bResultAchieved = bException = true;
675 xLockBytes->SetError( ERRCODE_IO_GENERAL );
678 sal_uInt32 nTimeout(5000); // initially 5000 milliSec
679 while(!bResultAchieved) {
681 // try to get the result for with timeout
682 Moderator::Result res = pMod->getResult(nTimeout);
684 switch(res.type) {
685 case Moderator::ResultType::STREAM:
687 Reference<XStream> result;
688 if(res.result >>= result) {
689 Reference < XActiveDataStreamer > xStreamer(
690 xSink, UNO_QUERY
693 if(xStreamer.is())
694 xStreamer->setStream(result);
696 pMod->setReply(Moderator::REQUESTHANDLED);
697 break;
699 case Moderator::ResultType::INPUTSTREAM:
701 Reference<XInputStream> result;
702 res.result >>= result;
703 Reference < XActiveDataSink > xActiveSink(
704 xSink, UNO_QUERY
707 if(xActiveSink.is())
708 xActiveSink->setInputStream(result);
709 pMod->setReply(Moderator::REQUESTHANDLED);
710 break;
712 case Moderator::ResultType::TIMEDOUT:
714 Reference<XInteractionRetry> xRet;
715 if(xInteract.is()) {
716 InteractiveNetworkConnectException aExcep;
717 INetURLObject aURL(
718 xContId.is() ?
719 xContId->getContentIdentifier() :
720 OUString() );
721 aExcep.Server = aURL.GetHost();
722 aExcep.Classification = InteractionClassification_ERROR;
723 aExcep.Message = "server not responding after five seconds";
724 Any request;
725 request <<= aExcep;
726 ucbhelper::InteractionRequest *ir =
727 new ucbhelper::InteractionRequest(request);
728 Reference<XInteractionRequest> xIR(ir);
729 Sequence<Reference<XInteractionContinuation> > aSeq(2);
730 ucbhelper::InteractionRetry *retryP =
731 new ucbhelper::InteractionRetry(ir);
732 aSeq[0] = retryP;
733 ucbhelper::InteractionAbort *abortP =
734 new ucbhelper::InteractionAbort(ir);
735 aSeq[1] = abortP;
737 ir->setContinuations(aSeq);
738 xInteract->handle(xIR);
739 rtl::Reference< ucbhelper::InteractionContinuation > ref
740 = ir->getSelection();
741 if(ref.is()) {
742 Reference<XInterface> xInt(ref.get());
743 xRet.set(xInt,UNO_QUERY);
747 if(!xRet.is()) {
748 bAborted = true;
749 xLockBytes->SetError(ERRCODE_ABORT);
752 break;
754 case Moderator::ResultType::INTERACTIONREQUEST:
756 Reference<XInteractionRequest> Request;
757 res.result >>= Request;
758 xInteract->handle(Request);
759 pMod->setReply(Moderator::REQUESTHANDLED);
760 break;
762 case Moderator::ResultType::RESULT:
764 bResultAchieved = true;
765 break;
767 case Moderator::ResultType::COMMANDABORTED:
769 bAborted = true;
770 xLockBytes->SetError( ERRCODE_ABORT );
771 break;
773 case Moderator::ResultType::COMMANDFAILED:
775 bAborted = true;
776 xLockBytes->SetError( ERRCODE_ABORT );
777 break;
779 case Moderator::ResultType::INTERACTIVEIO:
781 bException = true;
782 if ( res.ioErrorCode == IOErrorCode_ACCESS_DENIED ||
783 res.ioErrorCode == IOErrorCode_LOCKING_VIOLATION )
784 xLockBytes->SetError( ERRCODE_IO_ACCESSDENIED );
785 else if ( res.ioErrorCode == IOErrorCode_NOT_EXISTING )
786 xLockBytes->SetError( ERRCODE_IO_NOTEXISTS );
787 else if ( res.ioErrorCode == IOErrorCode_CANT_READ )
788 xLockBytes->SetError( ERRCODE_IO_CANTREAD );
789 else
790 xLockBytes->SetError( ERRCODE_IO_GENERAL );
791 break;
793 case Moderator::ResultType::UNSUPPORTED:
795 bException = true;
796 xLockBytes->SetError( ERRCODE_IO_NOTSUPPORTED );
797 break;
799 default:
801 bException = true;
802 xLockBytes->SetError( ERRCODE_IO_GENERAL );
803 break;
807 bResultAchieved |= bException;
808 bResultAchieved |= bAborted;
809 if(nTimeout == 5000) nTimeout *= 2;
812 if(pMod) pMod->setReply(Moderator::EXIT);
814 if ( bAborted || bException )
816 Reference < XActiveDataSink > xActiveSink( xSink, UNO_QUERY );
817 if ( xActiveSink.is() )
818 xActiveSink->setInputStream( Reference < XInputStream >() );
820 Reference < XActiveDataStreamer > xStreamer( xSink, UNO_QUERY );
821 if ( xStreamer.is() )
822 xStreamer->setStream( Reference < XStream >() );
825 Reference < XActiveDataControl > xControl( xSink, UNO_QUERY );
826 if ( xControl.is() )
827 xControl->terminate();
829 if ( xProps.is() )
830 xProps->removePropertiesChangeListener(
831 Sequence< OUString >(),
832 xListener );
834 return ( bAborted || bException );
838 Function for opening UCB contents synchronously
840 static bool UCBOpenContentSync_(
841 const UcbLockBytesRef& xLockBytes,
842 const Reference < XContent >& xContent,
843 const Command& rArg,
844 const Reference < XInterface >& xSink,
845 const Reference < XInteractionHandler >& xInteract )
847 ::ucbhelper::Content aContent(
848 xContent, new UcbTaskEnvironment( xInteract, nullptr ),
849 comphelper::getProcessComponentContext() );
850 Reference < XContentIdentifier > xIdent = xContent->getIdentifier();
851 OUString aScheme = xIdent->getContentProviderScheme();
853 // http protocol must be handled in a special way: during the opening process the input stream may change
854 // only the last inputstream after notifying the document headers is valid
855 if ( !aScheme.equalsIgnoreAsciiCase("http") )
856 xLockBytes->SetStreamValid_Impl();
858 Reference< XPropertiesChangeListener > xListener = new UcbPropertiesChangeListener_Impl( xLockBytes );
859 Reference< XPropertiesChangeNotifier > xProps ( xContent, UNO_QUERY );
860 if ( xProps.is() )
861 xProps->addPropertiesChangeListener( Sequence< OUString >(), xListener );
863 bool bException = false;
864 bool bAborted = false;
868 aContent.executeCommand( rArg.Name, rArg.Argument );
870 catch (const CommandAbortedException&)
872 bAborted = true;
873 xLockBytes->SetError( ERRCODE_ABORT );
875 catch (const CommandFailedException&)
877 bAborted = true;
878 xLockBytes->SetError( ERRCODE_ABORT );
880 catch (const InteractiveIOException& r)
882 bException = true;
883 if ( r.Code == IOErrorCode_ACCESS_DENIED || r.Code == IOErrorCode_LOCKING_VIOLATION )
884 xLockBytes->SetError( ERRCODE_IO_ACCESSDENIED );
885 else if ( r.Code == IOErrorCode_NOT_EXISTING )
886 xLockBytes->SetError( ERRCODE_IO_NOTEXISTS );
887 else if ( r.Code == IOErrorCode_CANT_READ )
888 xLockBytes->SetError( ERRCODE_IO_CANTREAD );
889 else
890 xLockBytes->SetError( ERRCODE_IO_GENERAL );
892 catch (const UnsupportedDataSinkException&)
894 bException = true;
895 xLockBytes->SetError( ERRCODE_IO_NOTSUPPORTED );
897 catch (const Exception&)
899 bException = true;
900 xLockBytes->SetError( ERRCODE_IO_GENERAL );
903 if ( bAborted || bException )
905 Reference < XActiveDataSink > xActiveSink( xSink, UNO_QUERY );
906 if ( xActiveSink.is() )
907 xActiveSink->setInputStream( Reference < XInputStream >() );
909 Reference < XActiveDataStreamer > xStreamer( xSink, UNO_QUERY );
910 if ( xStreamer.is() )
911 xStreamer->setStream( Reference < XStream >() );
914 Reference < XActiveDataControl > xControl( xSink, UNO_QUERY );
915 if ( xControl.is() )
916 xControl->terminate();
918 if ( xProps.is() )
919 xProps->removePropertiesChangeListener( Sequence< OUString >(), xListener );
921 return ( bAborted || bException );
924 UcbLockBytes::UcbLockBytes()
925 : m_nError( ERRCODE_NONE )
926 , m_bTerminated (false)
927 , m_bDontClose( false )
928 , m_bStreamValid (false)
930 SetSynchronMode();
933 UcbLockBytes::~UcbLockBytes()
935 if ( !m_bDontClose )
937 if ( m_xInputStream.is() )
941 m_xInputStream->closeInput();
943 catch (const RuntimeException&)
946 catch (const IOException&)
952 if ( !m_xInputStream.is() && m_xOutputStream.is() )
956 m_xOutputStream->closeOutput();
958 catch (const RuntimeException&)
961 catch (const IOException&)
967 Reference < XInputStream > UcbLockBytes::getInputStream()
969 osl::MutexGuard aGuard( m_aMutex );
970 m_bDontClose = true;
971 return m_xInputStream;
974 void UcbLockBytes::setStream_Impl( const Reference<XStream>& aStream )
976 osl::MutexGuard aGuard( m_aMutex );
977 if ( aStream.is() )
979 m_xOutputStream = aStream->getOutputStream();
980 setInputStream_Impl( aStream->getInputStream(), false );
981 m_xSeekable.set( aStream, UNO_QUERY );
983 else
985 m_xOutputStream.clear();
986 setInputStream_Impl( Reference < XInputStream >() );
990 bool UcbLockBytes::setInputStream_Impl( const Reference<XInputStream> &rxInputStream, bool bSetXSeekable )
992 bool bRet = false;
996 osl::MutexGuard aGuard( m_aMutex );
998 if ( !m_bDontClose && m_xInputStream.is() )
999 m_xInputStream->closeInput();
1001 m_xInputStream = rxInputStream;
1003 if( bSetXSeekable )
1005 m_xSeekable.set( rxInputStream, UNO_QUERY );
1006 if( !m_xSeekable.is() && rxInputStream.is() )
1008 Reference < XComponentContext > xContext = ::comphelper::getProcessComponentContext();
1009 Reference< XOutputStream > rxTempOut( css::io::TempFile::create(xContext), UNO_QUERY_THROW );
1011 ::comphelper::OStorageHelper::CopyInputToOutput( rxInputStream, rxTempOut );
1012 m_xInputStream.set( rxTempOut, UNO_QUERY );
1013 m_xSeekable.set( rxTempOut, UNO_QUERY );
1017 bRet = m_xInputStream.is();
1019 catch (const Exception&)
1023 if ( m_bStreamValid && m_xInputStream.is() )
1024 m_aInitialized.set();
1026 return bRet;
1029 void UcbLockBytes::SetStreamValid_Impl()
1031 m_bStreamValid = true;
1032 if ( m_xInputStream.is() )
1033 m_aInitialized.set();
1036 void UcbLockBytes::terminate_Impl()
1038 m_bTerminated = true;
1039 m_aInitialized.set();
1040 m_aTerminated.set();
1042 if ( GetError() == ERRCODE_NONE && !m_xInputStream.is() )
1044 OSL_FAIL("No InputStream, but no error set!" );
1045 SetError( ERRCODE_IO_NOTEXISTS );
1049 ErrCode UcbLockBytes::ReadAt(sal_uInt64 const nPos,
1050 void *pBuffer, std::size_t nCount, std::size_t *pRead) const
1052 if ( IsSynchronMode() )
1054 UcbLockBytes* pThis = const_cast < UcbLockBytes* >( this );
1055 pThis->m_aInitialized.wait();
1058 Reference <XInputStream> xStream = getInputStream_Impl();
1059 if ( !xStream.is() )
1061 if ( m_bTerminated )
1062 return ERRCODE_IO_CANTREAD;
1063 else
1064 return ERRCODE_IO_PENDING;
1067 if ( pRead )
1068 *pRead = 0;
1070 Reference <XSeekable> xSeekable = getSeekable_Impl();
1071 if ( !xSeekable.is() )
1072 return ERRCODE_IO_CANTREAD;
1076 xSeekable->seek( nPos );
1078 catch (const IOException&)
1080 return ERRCODE_IO_CANTSEEK;
1082 catch (const css::lang::IllegalArgumentException&)
1084 return ERRCODE_IO_CANTSEEK;
1087 Sequence<sal_Int8> aData;
1088 sal_Int32 nSize;
1090 if(nCount > 0x7FFFFFFF)
1092 nCount = 0x7FFFFFFF;
1096 if ( !m_bTerminated && !IsSynchronMode() )
1098 sal_uInt64 nLen = xSeekable->getLength();
1099 if ( nPos + nCount > nLen )
1100 return ERRCODE_IO_PENDING;
1103 nSize = xStream->readBytes( aData, sal_Int32(nCount) );
1105 catch (const IOException&)
1107 return ERRCODE_IO_CANTREAD;
1110 memcpy (pBuffer, aData.getConstArray(), nSize);
1111 if (pRead)
1112 *pRead = static_cast<std::size_t>(nSize);
1114 return ERRCODE_NONE;
1117 ErrCode UcbLockBytes::WriteAt(sal_uInt64 const nPos, const void *pBuffer,
1118 std::size_t nCount, std::size_t *pWritten)
1120 if ( pWritten )
1121 *pWritten = 0;
1123 DBG_ASSERT( IsSynchronMode(), "Writing is only possible in SynchronMode!" );
1124 DBG_ASSERT( m_aInitialized.check(), "Writing bevor stream is ready!" );
1126 Reference <XSeekable> xSeekable = getSeekable_Impl();
1127 Reference <XOutputStream> xOutputStream = getOutputStream_Impl();
1128 if ( !xOutputStream.is() || !xSeekable.is() )
1129 return ERRCODE_IO_CANTWRITE;
1133 xSeekable->seek( nPos );
1135 catch (const IOException&)
1137 return ERRCODE_IO_CANTSEEK;
1140 sal_Int8 const * pData = static_cast<sal_Int8 const *>(pBuffer);
1141 Sequence<sal_Int8> aData( pData, nCount );
1144 xOutputStream->writeBytes( aData );
1145 if ( pWritten )
1146 *pWritten = nCount;
1148 catch (const Exception&)
1150 return ERRCODE_IO_CANTWRITE;
1153 return ERRCODE_NONE;
1156 ErrCode UcbLockBytes::Flush() const
1158 Reference <XOutputStream > xOutputStream = getOutputStream_Impl();
1159 if ( !xOutputStream.is() )
1160 return ERRCODE_IO_CANTWRITE;
1164 xOutputStream->flush();
1166 catch (const Exception&)
1168 return ERRCODE_IO_CANTWRITE;
1171 return ERRCODE_NONE;
1174 ErrCode UcbLockBytes::SetSize (sal_uInt64 const nNewSize)
1176 SvLockBytesStat aStat;
1177 Stat( &aStat );
1178 std::size_t nSize = aStat.nSize;
1180 if ( nSize > nNewSize )
1182 Reference < XTruncate > xTrunc( getOutputStream_Impl(), UNO_QUERY );
1183 if ( xTrunc.is() )
1185 xTrunc->truncate();
1186 nSize = 0;
1188 else {
1189 SAL_INFO("unotools.ucbhelper", "Not truncable!");
1193 if ( nSize < nNewSize )
1195 std::size_t nDiff = nNewSize-nSize, nCount=0;
1196 std::unique_ptr<sal_uInt8[]> pBuffer(new sal_uInt8[ nDiff ]);
1197 memset(pBuffer.get(), 0, nDiff); // initialize for enhanced security
1198 WriteAt( nSize, pBuffer.get(), nDiff, &nCount );
1199 if ( nCount != nDiff )
1200 return ERRCODE_IO_CANTWRITE;
1203 return ERRCODE_NONE;
1206 ErrCode UcbLockBytes::Stat( SvLockBytesStat *pStat ) const
1208 if ( IsSynchronMode() )
1210 UcbLockBytes* pThis = const_cast < UcbLockBytes* >( this );
1211 pThis->m_aInitialized.wait();
1214 if (!pStat)
1215 return ERRCODE_IO_INVALIDPARAMETER;
1217 Reference <XInputStream> xStream = getInputStream_Impl();
1218 Reference <XSeekable> xSeekable = getSeekable_Impl();
1220 if ( !xStream.is() )
1222 if ( m_bTerminated )
1223 return ERRCODE_IO_INVALIDACCESS;
1224 else
1225 return ERRCODE_IO_PENDING;
1227 else if( !xSeekable.is() )
1228 return ERRCODE_IO_CANTTELL;
1232 pStat->nSize = sal_uLong(xSeekable->getLength());
1234 catch (const IOException&)
1236 return ERRCODE_IO_CANTTELL;
1239 return ERRCODE_NONE;
1242 UcbLockBytesRef UcbLockBytes::CreateInputLockBytes( const Reference< XInputStream >& xInputStream )
1244 if( !xInputStream.is() )
1245 return nullptr;
1247 UcbLockBytesRef xLockBytes = new UcbLockBytes;
1248 xLockBytes->setDontClose_Impl();
1249 xLockBytes->setInputStream_Impl( xInputStream );
1250 xLockBytes->terminate_Impl();
1251 return xLockBytes;
1254 UcbLockBytesRef UcbLockBytes::CreateLockBytes( const Reference< XStream >& xStream )
1256 if( !xStream.is() )
1257 return nullptr;
1259 UcbLockBytesRef xLockBytes = new UcbLockBytes;
1260 xLockBytes->setDontClose_Impl();
1261 xLockBytes->setStream_Impl( xStream );
1262 xLockBytes->terminate_Impl();
1263 return xLockBytes;
1266 UcbLockBytesRef UcbLockBytes::CreateLockBytes( const Reference < XContent >& xContent, const Sequence < PropertyValue >& rProps,
1267 StreamMode eOpenMode, const Reference < XInteractionHandler >& xInteractionHandler )
1269 if( !xContent.is() )
1270 return nullptr;
1272 UcbLockBytesRef xLockBytes = new UcbLockBytes;
1273 xLockBytes->SetSynchronMode();
1274 Reference< XActiveDataControl > xSink;
1275 if ( eOpenMode & StreamMode::WRITE )
1276 xSink = new UcbStreamer_Impl(xLockBytes.get());
1277 else
1278 xSink = new UcbDataSink_Impl(xLockBytes.get());
1280 if ( rProps.hasElements() )
1282 Reference < XCommandProcessor > xProcessor( xContent, UNO_QUERY );
1283 Command aCommand;
1284 aCommand.Name = "setPropertyValues";
1285 aCommand.Handle = -1; /* unknown */
1286 aCommand.Argument <<= rProps;
1287 xProcessor->execute( aCommand, 0, Reference < XCommandEnvironment >() );
1290 OpenCommandArgument2 aArgument;
1291 aArgument.Sink = xSink;
1292 aArgument.Mode = OpenMode::DOCUMENT;
1294 Command aCommand;
1295 aCommand.Name = "open";
1296 aCommand.Argument <<= aArgument;
1298 bool bError = UCBOpenContentSync( xLockBytes,
1299 xContent,
1300 aCommand,
1301 xSink,
1302 xInteractionHandler );
1304 if ( xLockBytes->GetError() == ERRCODE_NONE && ( bError || !xLockBytes->getInputStream().is() ) )
1306 OSL_FAIL("No InputStream, but no error set!" );
1307 xLockBytes->SetError( ERRCODE_IO_GENERAL );
1310 return xLockBytes;
1315 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */