1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include "ucblockbytes.hxx"
21 
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>
57 
58 #include <comphelper/storagehelper.hxx>
59 #include <ucbhelper/content.hxx>
60 
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;
67 
68 namespace utl
69 {
70 
71 namespace {
72 
73 /**
74     Helper class for getting a XInputStream when opening a content
75  */
76 class UcbDataSink_Impl : public ::cppu::WeakImplHelper< XActiveDataControl, XActiveDataSink >
77 {
78     UcbLockBytesRef         m_xLockBytes;
79 
80 public:
UcbDataSink_Impl(UcbLockBytes * pLockBytes)81     explicit UcbDataSink_Impl( UcbLockBytes* pLockBytes )
82         : m_xLockBytes( pLockBytes )
83     {}
84 
85     // XActiveDataControl.
addListener(const Reference<XStreamListener> &)86     virtual void SAL_CALL   addListener ( const Reference<XStreamListener> &/*rxListener*/) override {}
removeListener(const Reference<XStreamListener> &)87     virtual void SAL_CALL   removeListener ( const Reference<XStreamListener> &/*rxListener*/) override {}
start()88     virtual void SAL_CALL   start() override {}
terminate()89     virtual void SAL_CALL   terminate() override
90                             { m_xLockBytes->terminate_Impl(); }
91 
92     // XActiveDataSink.
setInputStream(const Reference<XInputStream> & rxInputStream)93     virtual void SAL_CALL   setInputStream ( const Reference<XInputStream> &rxInputStream) override
94                             { m_xLockBytes->setInputStream_Impl (rxInputStream); }
getInputStream()95     virtual Reference<XInputStream> SAL_CALL getInputStream() override
96                             { return m_xLockBytes->getInputStream_Impl(); }
97 };
98 
99 /**
100     Helper class for getting a XStream when opening a content
101  */
102 class UcbStreamer_Impl : public ::cppu::WeakImplHelper< XActiveDataStreamer, XActiveDataControl >
103 {
104     Reference < XStream >   m_xStream;
105     UcbLockBytesRef         m_xLockBytes;
106 
107 public:
UcbStreamer_Impl(UcbLockBytes * pLockBytes)108     explicit UcbStreamer_Impl( UcbLockBytes* pLockBytes )
109         : m_xLockBytes( pLockBytes )
110     {}
111 
112     // XActiveDataControl.
addListener(const Reference<XStreamListener> &)113     virtual void SAL_CALL   addListener ( const Reference<XStreamListener> &/*rxListener*/) override {}
removeListener(const Reference<XStreamListener> &)114     virtual void SAL_CALL   removeListener ( const Reference<XStreamListener> &/*rxListener*/) override {}
start()115     virtual void SAL_CALL   start() override {}
terminate()116     virtual void SAL_CALL   terminate() override
117                             { m_xLockBytes->terminate_Impl(); }
118 
119     // XActiveDataStreamer
setStream(const Reference<XStream> & aStream)120     virtual void SAL_CALL   setStream( const Reference< XStream >& aStream ) override
121                             { m_xStream = aStream; m_xLockBytes->setStream_Impl( aStream ); }
getStream()122     virtual Reference< XStream > SAL_CALL getStream() override
123                             { return m_xStream; }
124 };
125 
126 /**
127     Helper class for managing interactions and progress when executing UCB commands
128  */
129 class UcbTaskEnvironment : public ::cppu::WeakImplHelper< XCommandEnvironment >
130 {
131     Reference< XInteractionHandler >                m_xInteractionHandler;
132     Reference< XProgressHandler >                   m_xProgressHandler;
133 
134 public:
UcbTaskEnvironment(const Reference<XInteractionHandler> & rxInteractionHandler,const Reference<XProgressHandler> & rxProgressHandler)135                             UcbTaskEnvironment( const Reference< XInteractionHandler>& rxInteractionHandler,
136                                                 const Reference< XProgressHandler>& rxProgressHandler )
137                                 : m_xInteractionHandler( rxInteractionHandler )
138                                 , m_xProgressHandler( rxProgressHandler )
139                             {}
140 
getInteractionHandler()141     virtual Reference<XInteractionHandler> SAL_CALL getInteractionHandler() override
142     { return m_xInteractionHandler; }
143 
getProgressHandler()144     virtual Reference<XProgressHandler> SAL_CALL    getProgressHandler() override
145     { return m_xProgressHandler; }
146 };
147 
148 /**
149     Helper class for property change notifies when executing UCB commands
150 */
151 class UcbPropertiesChangeListener_Impl : public ::cppu::WeakImplHelper< XPropertiesChangeListener >
152 {
153 public:
154     UcbLockBytesRef         m_xLockBytes;
155 
UcbPropertiesChangeListener_Impl(UcbLockBytesRef const & rRef)156     explicit UcbPropertiesChangeListener_Impl( UcbLockBytesRef const & rRef )
157         : m_xLockBytes( rRef )
158     {}
159 
disposing(const EventObject &)160     virtual void SAL_CALL   disposing ( const EventObject &/*rEvent*/) override {}
161     virtual void SAL_CALL   propertiesChange ( const Sequence<PropertyChangeEvent> &rEvent) override;
162 };
163 
164 }
165 
propertiesChange(const Sequence<PropertyChangeEvent> & rEvent)166 void SAL_CALL UcbPropertiesChangeListener_Impl::propertiesChange ( const Sequence<PropertyChangeEvent> &rEvent)
167 {
168     for (const auto& rPropChangeEvent : rEvent)
169     {
170         if (rPropChangeEvent.PropertyName == "DocumentHeader")
171         {
172             m_xLockBytes->SetStreamValid_Impl();
173         }
174     }
175 }
176 
177 namespace {
178 
179 class Moderator
180     : public osl::Thread
181 {
182     // usage restriction:
183     // It might be possible, that the call to the interactionhandler and/or
184     // progresshandler is done asynchronously, while the 'execute' simply
185     // returns. This would imply that these class must be refcounted!!!
186 
187 public:
188     /// @throws ContentCreationException
189     /// @throws RuntimeException
190     Moderator(
191         Reference < XContent > const & xContent,
192         Reference < XInteractionHandler > const & xInteract,
193         const Command& rArg
194     );
195 
196     enum class ResultType {
197         NORESULT,
198 
199         INTERACTIONREQUEST,    // reply expected
200 
201         INPUTSTREAM,
202         STREAM,
203 
204         RESULT,
205         TIMEDOUT,
206         COMMANDABORTED,
207         COMMANDFAILED,
208         INTERACTIVEIO,
209         UNSUPPORTED,
210         GENERAL
211     };
212 
213     class ConditionRes
214         : public salhelper::Condition
215     {
216     public:
ConditionRes(osl::Mutex & aMutex,Moderator & aModerator)217         ConditionRes(osl::Mutex& aMutex,Moderator& aModerator)
218             : salhelper::Condition(aMutex),
219               m_aModerator(aModerator)
220         {
221         }
222 
223     protected:
applies() const224         bool applies() const override {
225             return m_aModerator.m_aResultType != ResultType::NORESULT;
226         }
227 
228     private:
229         Moderator& m_aModerator;
230     };
231 
232     struct Result {
233         ResultType        type;
234         Any               result;
235         IOErrorCode       ioErrorCode;
236     };
237 
238     Result getResult(const sal_uInt32 milliSec);
239 
240     enum ReplyType {
241         NOREPLY,
242         EXIT,
243         REQUESTHANDLED
244     };
245 
246     class ConditionRep
247         : public salhelper::Condition
248     {
249     public:
ConditionRep(osl::Mutex & aMutex,Moderator & aModerator)250         ConditionRep(osl::Mutex& aMutex,Moderator& aModerator)
251             : salhelper::Condition(aMutex),
252               m_aModerator(aModerator)
253         {
254         }
255 
256     protected:
applies() const257         bool applies() const override {
258             return m_aModerator.m_aReplyType != NOREPLY;
259         }
260 
261     private:
262         Moderator& m_aModerator;
263     };
264 
265     void setReply(ReplyType);
266 
267     void handle( const Reference<XInteractionRequest >& Request );
268 
269     void setStream(const Reference< XStream >& aStream);
270     void setInputStream(const Reference<XInputStream> &rxInputStream);
271 
272 protected:
273     virtual void SAL_CALL run() override;
274     virtual void SAL_CALL onTerminated() override;
275 
276 private:
277     osl::Mutex        m_aMutex;
278 
279     friend class ConditionRes;
280 
281     ConditionRes      m_aRes;
282     ResultType        m_aResultType;
283     IOErrorCode       m_nIOErrorCode;
284     Any               m_aResult;
285 
286     friend class ConditionRep;
287 
288     ConditionRep      m_aRep;
289     ReplyType         m_aReplyType;
290 
291     Command                           m_aArg;
292     ::ucbhelper::Content              m_aContent;
293 };
294 
295 class ModeratorsActiveDataStreamer
296     : public ::cppu::WeakImplHelper<XActiveDataStreamer>
297 {
298 public:
299 
300     explicit ModeratorsActiveDataStreamer(Moderator &theModerator);
301 
302     // XActiveDataStreamer
303     virtual void SAL_CALL
304     setStream(
305         const Reference< XStream >& aStream
306     ) override;
307 
getStream()308     virtual Reference<XStream> SAL_CALL getStream () override
309     {
310         osl::MutexGuard aGuard(m_aMutex);
311         return m_xStream;
312     }
313 
314 private:
315     Moderator& m_aModerator;
316 
317     osl::Mutex m_aMutex;
318     Reference<XStream> m_xStream;
319 };
320 
321 class ModeratorsActiveDataSink
322     : public ::cppu::WeakImplHelper<XActiveDataSink>
323 {
324 public:
325 
326     explicit ModeratorsActiveDataSink(Moderator &theModerator);
327 
328     // XActiveDataSink.
329     virtual void SAL_CALL
330     setInputStream (
331         const Reference<XInputStream> &rxInputStream
332     ) override;
333 
getInputStream()334     virtual Reference<XInputStream> SAL_CALL getInputStream() override
335     {
336         osl::MutexGuard aGuard(m_aMutex);
337         return m_xStream;
338     }
339 
340 private:
341     Moderator& m_aModerator;
342     osl::Mutex m_aMutex;
343     Reference<XInputStream> m_xStream;
344 };
345 
346 }
347 
ModeratorsActiveDataSink(Moderator & theModerator)348 ModeratorsActiveDataSink::ModeratorsActiveDataSink(Moderator &theModerator)
349     : m_aModerator(theModerator)
350 {
351 }
352 
353 // XActiveDataSink.
354 void SAL_CALL
setInputStream(const Reference<XInputStream> & rxInputStream)355 ModeratorsActiveDataSink::setInputStream (
356     const Reference<XInputStream> &rxInputStream
357 )
358 {
359     m_aModerator.setInputStream(rxInputStream);
360     osl::MutexGuard aGuard(m_aMutex);
361     m_xStream = rxInputStream;
362 }
363 
ModeratorsActiveDataStreamer(Moderator & theModerator)364 ModeratorsActiveDataStreamer::ModeratorsActiveDataStreamer(
365     Moderator &theModerator
366 )
367     : m_aModerator(theModerator)
368 {
369 }
370 
371 // XActiveDataStreamer.
372 void SAL_CALL
setStream(const Reference<XStream> & rxStream)373 ModeratorsActiveDataStreamer::setStream (
374     const Reference<XStream> &rxStream
375 )
376 {
377     m_aModerator.setStream(rxStream);
378     osl::MutexGuard aGuard(m_aMutex);
379     m_xStream = rxStream;
380 }
381 
382 namespace {
383 
384 class ModeratorsInteractionHandler
385     : public ::cppu::WeakImplHelper<XInteractionHandler>
386 {
387 public:
388 
389     explicit ModeratorsInteractionHandler(Moderator &theModerator);
390 
391     virtual void SAL_CALL
392     handle( const Reference<XInteractionRequest >& Request ) override;
393 
394 private:
395 
396     Moderator& m_aModerator;
397 };
398 
399 }
400 
ModeratorsInteractionHandler(Moderator & aModerator)401 ModeratorsInteractionHandler::ModeratorsInteractionHandler(
402     Moderator &aModerator)
403     : m_aModerator(aModerator)
404 {
405 }
406 
407 void SAL_CALL
handle(const Reference<XInteractionRequest> & Request)408 ModeratorsInteractionHandler::handle(
409     const Reference<XInteractionRequest >& Request
410 )
411 {
412     // wakes up the mainthread
413     m_aModerator.handle(Request);
414 }
415 
Moderator(Reference<XContent> const & xContent,Reference<XInteractionHandler> const & xInteract,const Command & rArg)416 Moderator::Moderator(
417     Reference < XContent > const & xContent,
418     Reference < XInteractionHandler > const & xInteract,
419     const Command& rArg
420 )
421     : m_aMutex(),
422 
423       m_aRes(m_aMutex,*this),
424       m_aResultType(ResultType::NORESULT),
425       m_nIOErrorCode(IOErrorCode_ABORT),
426       m_aResult(),
427 
428       m_aRep(m_aMutex,*this),
429       m_aReplyType(NOREPLY),
430 
431       m_aArg(rArg),
432       m_aContent(
433           xContent,
434           new UcbTaskEnvironment(
435               xInteract.is() ? new ModeratorsInteractionHandler(*this) : nullptr,
436               nullptr),
437           comphelper::getProcessComponentContext())
438 {
439     // now exchange the whole data sink stuff
440     // with a thread safe version
441 
442     Reference<XInterface> *pxSink = nullptr;
443 
444     PostCommandArgument2 aPostArg;
445     OpenCommandArgument2 aOpenArg;
446 
447     int dec(2);
448     if(m_aArg.Argument >>= aPostArg) {
449         pxSink = &aPostArg.Sink;
450         dec = 0;
451     }
452     else if(m_aArg.Argument >>= aOpenArg) {
453         pxSink = &aOpenArg.Sink;
454         dec = 1;
455     }
456 
457     if(dec ==2)
458         throw ContentCreationException();
459 
460     Reference < XActiveDataSink > xActiveSink(*pxSink,UNO_QUERY);
461     if(xActiveSink.is())
462         pxSink->set( static_cast<cppu::OWeakObject*>(new ModeratorsActiveDataSink(*this)));
463 
464     Reference<XActiveDataStreamer> xStreamer( *pxSink, UNO_QUERY );
465     if ( xStreamer.is() )
466         pxSink->set( static_cast<cppu::OWeakObject*>(new ModeratorsActiveDataStreamer(*this)));
467 
468     if(dec == 0)
469         m_aArg.Argument <<= aPostArg;
470     else if(dec == 1)
471         m_aArg.Argument <<= aOpenArg;
472 }
473 
getResult(const sal_uInt32 milliSec)474 Moderator::Result Moderator::getResult(const sal_uInt32 milliSec)
475 {
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;
482 
483         // reset
484         m_aResultType = ResultType::NORESULT;
485     }
486     catch (const salhelper::ConditionWaiter::timedout&)
487     {
488         ret.type = ResultType::TIMEDOUT;
489     }
490 
491     return ret;
492 }
493 
setReply(ReplyType aReplyType)494 void Moderator::setReply(ReplyType aReplyType )
495 {
496     salhelper::ConditionModifier aMod(m_aRep);
497     m_aReplyType = aReplyType;
498 }
499 
handle(const Reference<XInteractionRequest> & Request)500 void Moderator::handle( const Reference<XInteractionRequest >& Request )
501 {
502     ReplyType aReplyType;
503 
504     do {
505         {
506             salhelper::ConditionModifier aMod(m_aRes);
507             m_aResultType = ResultType::INTERACTIONREQUEST;
508             m_aResult <<= Request;
509         }
510 
511         {
512             salhelper::ConditionWaiter aWait(m_aRep);
513             aReplyType = m_aReplyType;
514 
515             // reset
516             m_aReplyType = NOREPLY;
517         }
518 
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();
526                 }
527             }
528 
529             // resignal the exit condition
530             setReply(EXIT);
531             break;
532         }
533     } while(aReplyType != REQUESTHANDLED);
534 }
535 
setStream(const Reference<XStream> & aStream)536 void Moderator::setStream(const Reference< XStream >& aStream)
537 {
538     {
539         salhelper::ConditionModifier aMod(m_aRes);
540         m_aResultType = ResultType::STREAM;
541         m_aResult <<= aStream;
542     }
543     ReplyType aReplyType;
544     {
545         salhelper::ConditionWaiter aWait(m_aRep);
546         aReplyType = m_aReplyType;
547         m_aReplyType = NOREPLY;
548     }
549     if(aReplyType == EXIT)
550         setReply(EXIT);
551 }
552 
setInputStream(const Reference<XInputStream> & rxInputStream)553 void Moderator::setInputStream(const Reference<XInputStream> &rxInputStream)
554 {
555     {
556         salhelper::ConditionModifier aMod(m_aRes);
557         m_aResultType = ResultType::INPUTSTREAM;
558         m_aResult <<= rxInputStream;
559     }
560     ReplyType aReplyType;
561     {
562         salhelper::ConditionWaiter aWait(m_aRep);
563         aReplyType = m_aReplyType;
564         m_aReplyType = NOREPLY;
565     }
566     if(aReplyType == EXIT)
567         setReply(EXIT);
568 }
569 
run()570 void SAL_CALL Moderator::run()
571 {
572     osl_setThreadName("utl::Moderator");
573 
574     ResultType  aResultType;
575     Any         aResult;
576     IOErrorCode nIOErrorCode = IOErrorCode_ABORT;
577 
578     try
579     {
580         aResult = m_aContent.executeCommand(m_aArg.Name,m_aArg.Argument);
581         aResultType = ResultType::RESULT;
582     }
583     catch (const CommandAbortedException&)
584     {
585         aResultType = ResultType::COMMANDABORTED;
586     }
587     catch (const CommandFailedException&)
588     {
589         aResultType = ResultType::COMMANDFAILED;
590     }
591     catch (const InteractiveIOException& r)
592     {
593         nIOErrorCode = r.Code;
594         aResultType = ResultType::INTERACTIVEIO;
595     }
596     catch (const UnsupportedDataSinkException &)
597     {
598         aResultType = ResultType::UNSUPPORTED;
599     }
600     catch (const Exception&)
601     {
602         aResultType = ResultType::GENERAL;
603     }
604 
605     {
606         salhelper::ConditionModifier aMod(m_aRes);
607         m_aResultType = aResultType;
608         m_aResult = aResult;
609         m_nIOErrorCode = nIOErrorCode;
610     }
611 }
612 
onTerminated()613 void SAL_CALL Moderator::onTerminated()
614 {
615     {
616         salhelper::ConditionWaiter aWaiter(m_aRep);
617     }
618     delete this;
619 }
620 
621 /**
622    Function for opening UCB contents synchronously,
623    but with handled timeout;
624 */
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 );
631 
UCBOpenContentSync(const UcbLockBytesRef & xLockBytes,Reference<XContent> const & xContent,const Command & rArg,const Reference<XInterface> & xSink,Reference<XInteractionHandler> const & 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 )
638 {
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
643 
644     Reference<XContentIdentifier> xContId(
645         xContent.is() ? xContent->getIdentifier() : nullptr );
646 
647     OUString aScheme;
648     if(xContId.is())
649         aScheme = xContId->getContentProviderScheme();
650 
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);
659 
660     if ( !aScheme.equalsIgnoreAsciiCase( "http" ) &&
661          !aScheme.equalsIgnoreAsciiCase( "https" ) )
662         xLockBytes->SetStreamValid_Impl();
663 
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);
672     }
673 
674     bool bException(false);
675     bool bAborted(false);
676     bool bResultAchieved(false);
677 
678     Moderator* pMod = nullptr;
679     try
680     {
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
686     }
687     catch (const ContentCreationException&)
688     {
689         bResultAchieved = bException = true;
690         xLockBytes->SetError( ERRCODE_IO_GENERAL );
691     }
692 
693     sal_uInt32 nTimeout(5000); // initially 5000 milliSec
694     while(!bResultAchieved) {
695 
696         // try to get the result for with timeout
697         Moderator::Result res = pMod->getResult(nTimeout);
698 
699         switch(res.type) {
700         case Moderator::ResultType::STREAM:
701             {
702                 Reference<XStream> result;
703                 if(res.result >>= result) {
704                     Reference < XActiveDataStreamer > xStreamer(
705                         xSink, UNO_QUERY
706                     );
707 
708                     if(xStreamer.is())
709                         xStreamer->setStream(result);
710                 }
711                 pMod->setReply(Moderator::REQUESTHANDLED);
712                 break;
713             }
714         case Moderator::ResultType::INPUTSTREAM:
715             {
716                 Reference<XInputStream> result;
717                 res.result >>= result;
718                 Reference < XActiveDataSink > xActiveSink(
719                     xSink, UNO_QUERY
720                 );
721 
722                 if(xActiveSink.is())
723                     xActiveSink->setInputStream(result);
724                 pMod->setReply(Moderator::REQUESTHANDLED);
725                 break;
726             }
727         case Moderator::ResultType::TIMEDOUT:
728             {
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 };
748 
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);
756                     }
757                 }
758 
759                 if(!xRet.is()) {
760                     bAborted = true;
761                     xLockBytes->SetError(ERRCODE_ABORT);
762                 }
763 
764                 break;
765             }
766         case Moderator::ResultType::INTERACTIONREQUEST:
767             {
768                 Reference<XInteractionRequest> Request;
769                 res.result >>= Request;
770                 xInteract->handle(Request);
771                 pMod->setReply(Moderator::REQUESTHANDLED);
772                 break;
773             }
774         case Moderator::ResultType::RESULT:
775             {
776                 bResultAchieved = true;
777                 break;
778             }
779         case Moderator::ResultType::COMMANDABORTED:
780             {
781                 bAborted = true;
782                 xLockBytes->SetError( ERRCODE_ABORT );
783                 break;
784             }
785         case Moderator::ResultType::COMMANDFAILED:
786             {
787                 bAborted = true;
788                 xLockBytes->SetError( ERRCODE_ABORT );
789                 break;
790             }
791         case Moderator::ResultType::INTERACTIVEIO:
792             {
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;
804             }
805         case Moderator::ResultType::UNSUPPORTED:
806             {
807                 bException = true;
808                 xLockBytes->SetError( ERRCODE_IO_NOTSUPPORTED );
809                 break;
810             }
811         default:
812             {
813                 bException = true;
814                 xLockBytes->SetError( ERRCODE_IO_GENERAL );
815                 break;
816             }
817         }
818 
819         bResultAchieved |= bException;
820         bResultAchieved |= bAborted;
821         if(nTimeout == 5000) nTimeout *= 2;
822     }
823 
824     if(pMod) pMod->setReply(Moderator::EXIT);
825 
826     if ( bAborted || bException )
827     {
828         Reference < XActiveDataSink > xActiveSink( xSink, UNO_QUERY );
829         if ( xActiveSink.is() )
830             xActiveSink->setInputStream( Reference < XInputStream >() );
831 
832         Reference < XActiveDataStreamer > xStreamer( xSink, UNO_QUERY );
833         if ( xStreamer.is() )
834             xStreamer->setStream( Reference < XStream >() );
835     }
836 
837     Reference < XActiveDataControl > xControl( xSink, UNO_QUERY );
838     if ( xControl.is() )
839         xControl->terminate();
840 
841     if ( xProps.is() )
842         xProps->removePropertiesChangeListener(
843             Sequence< OUString >(),
844             xListener );
845 
846     return ( bAborted || bException );
847 }
848 
849 /**
850     Function for opening UCB contents synchronously
851  */
UCBOpenContentSync_(const UcbLockBytesRef & xLockBytes,const Reference<XContent> & xContent,const Command & rArg,const Reference<XInterface> & xSink,const Reference<XInteractionHandler> & xInteract)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 )
858 {
859     ::ucbhelper::Content aContent(
860         xContent, new UcbTaskEnvironment( xInteract, nullptr ),
861         comphelper::getProcessComponentContext() );
862     Reference < XContentIdentifier > xIdent = xContent->getIdentifier();
863     OUString aScheme = xIdent->getContentProviderScheme();
864 
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_Impl();
869 
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 );
874 
875     bool bException = false;
876     bool bAborted = false;
877 
878     try
879     {
880         aContent.executeCommand( rArg.Name, rArg.Argument );
881     }
882     catch (const CommandAbortedException&)
883     {
884         bAborted = true;
885         xLockBytes->SetError( ERRCODE_ABORT );
886     }
887     catch (const CommandFailedException&)
888     {
889         bAborted = true;
890         xLockBytes->SetError( ERRCODE_ABORT );
891     }
892     catch (const InteractiveIOException& r)
893     {
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 );
903     }
904     catch (const UnsupportedDataSinkException&)
905     {
906         bException = true;
907         xLockBytes->SetError( ERRCODE_IO_NOTSUPPORTED );
908     }
909     catch (const Exception&)
910     {
911         bException = true;
912         xLockBytes->SetError( ERRCODE_IO_GENERAL );
913     }
914 
915     if ( bAborted || bException )
916     {
917         Reference < XActiveDataSink > xActiveSink( xSink, UNO_QUERY );
918         if ( xActiveSink.is() )
919             xActiveSink->setInputStream( Reference < XInputStream >() );
920 
921         Reference < XActiveDataStreamer > xStreamer( xSink, UNO_QUERY );
922         if ( xStreamer.is() )
923             xStreamer->setStream( Reference < XStream >() );
924     }
925 
926     Reference < XActiveDataControl > xControl( xSink, UNO_QUERY );
927     if ( xControl.is() )
928         xControl->terminate();
929 
930     if ( xProps.is() )
931         xProps->removePropertiesChangeListener( Sequence< OUString >(), xListener );
932 
933     return ( bAborted || bException );
934 }
935 
UcbLockBytes()936 UcbLockBytes::UcbLockBytes()
937     : m_nError( ERRCODE_NONE )
938     , m_bTerminated  (false)
939     , m_bDontClose( false )
940     , m_bStreamValid  (false)
941 {
942     SetSynchronMode();
943 }
944 
~UcbLockBytes()945 UcbLockBytes::~UcbLockBytes()
946 {
947     if ( !m_bDontClose )
948     {
949         if ( m_xInputStream.is() )
950         {
951             try
952             {
953                 m_xInputStream->closeInput();
954             }
955             catch (const RuntimeException&)
956             {
957             }
958             catch (const IOException&)
959             {
960             }
961         }
962     }
963 
964     if ( m_xInputStream.is() || !m_xOutputStream.is() )
965         return;
966 
967     try
968     {
969         m_xOutputStream->closeOutput();
970     }
971     catch (const RuntimeException&)
972     {
973     }
974     catch (const IOException&)
975     {
976     }
977 }
978 
getInputStream()979 Reference < XInputStream > UcbLockBytes::getInputStream()
980 {
981     osl::MutexGuard aGuard( m_aMutex );
982     m_bDontClose = true;
983     return m_xInputStream;
984 }
985 
setStream_Impl(const Reference<XStream> & aStream)986 void UcbLockBytes::setStream_Impl( const Reference<XStream>& aStream )
987 {
988     osl::MutexGuard aGuard( m_aMutex );
989     if ( aStream.is() )
990     {
991         m_xOutputStream = aStream->getOutputStream();
992         setInputStream_Impl( aStream->getInputStream(), false );
993         m_xSeekable.set( aStream, UNO_QUERY );
994     }
995     else
996     {
997         m_xOutputStream.clear();
998         setInputStream_Impl( Reference < XInputStream >() );
999     }
1000 }
1001 
setInputStream_Impl(const Reference<XInputStream> & rxInputStream,bool bSetXSeekable)1002 bool UcbLockBytes::setInputStream_Impl( const Reference<XInputStream> &rxInputStream, bool bSetXSeekable )
1003 {
1004     bool bRet = false;
1005 
1006     try
1007     {
1008         osl::MutexGuard aGuard( m_aMutex );
1009 
1010         if ( !m_bDontClose && m_xInputStream.is() )
1011             m_xInputStream->closeInput();
1012 
1013         m_xInputStream = rxInputStream;
1014 
1015         if( bSetXSeekable )
1016         {
1017             m_xSeekable.set( rxInputStream, UNO_QUERY );
1018             if( !m_xSeekable.is() && rxInputStream.is() )
1019             {
1020                 Reference < XComponentContext > xContext = ::comphelper::getProcessComponentContext();
1021                 Reference< XOutputStream > rxTempOut( css::io::TempFile::create(xContext), UNO_QUERY_THROW );
1022 
1023                 ::comphelper::OStorageHelper::CopyInputToOutput( rxInputStream, rxTempOut );
1024                 m_xInputStream.set( rxTempOut, UNO_QUERY );
1025                 m_xSeekable.set( rxTempOut, UNO_QUERY );
1026             }
1027         }
1028 
1029         bRet = m_xInputStream.is();
1030     }
1031     catch (const Exception&)
1032     {
1033     }
1034 
1035     if ( m_bStreamValid && m_xInputStream.is() )
1036         m_aInitialized.set();
1037 
1038     return bRet;
1039 }
1040 
SetStreamValid_Impl()1041 void UcbLockBytes::SetStreamValid_Impl()
1042 {
1043     m_bStreamValid = true;
1044     if ( m_xInputStream.is() )
1045         m_aInitialized.set();
1046 }
1047 
terminate_Impl()1048 void UcbLockBytes::terminate_Impl()
1049 {
1050     m_bTerminated = true;
1051     m_aInitialized.set();
1052     m_aTerminated.set();
1053 
1054     if ( GetError() == ERRCODE_NONE && !m_xInputStream.is() )
1055     {
1056         OSL_FAIL("No InputStream, but no error set!" );
1057         SetError( ERRCODE_IO_NOTEXISTS );
1058     }
1059 }
1060 
ReadAt(sal_uInt64 const nPos,void * pBuffer,std::size_t nCount,std::size_t * pRead) const1061 ErrCode UcbLockBytes::ReadAt(sal_uInt64 const nPos,
1062         void *pBuffer, std::size_t nCount, std::size_t *pRead) const
1063 {
1064     if ( IsSynchronMode() )
1065     {
1066         UcbLockBytes* pThis = const_cast < UcbLockBytes* >( this );
1067         pThis->m_aInitialized.wait();
1068     }
1069 
1070     Reference <XInputStream> xStream = getInputStream_Impl();
1071     if ( !xStream.is() )
1072     {
1073         if ( m_bTerminated )
1074             return ERRCODE_IO_CANTREAD;
1075         else
1076             return ERRCODE_IO_PENDING;
1077     }
1078 
1079     if ( pRead )
1080         *pRead = 0;
1081 
1082     Reference <XSeekable> xSeekable = getSeekable_Impl();
1083     if ( !xSeekable.is() )
1084         return ERRCODE_IO_CANTREAD;
1085 
1086     try
1087     {
1088         xSeekable->seek( nPos );
1089     }
1090     catch (const IOException&)
1091     {
1092         return ERRCODE_IO_CANTSEEK;
1093     }
1094     catch (const css::lang::IllegalArgumentException&)
1095     {
1096         return ERRCODE_IO_CANTSEEK;
1097     }
1098 
1099     Sequence<sal_Int8> aData;
1100     sal_Int32          nSize;
1101 
1102     if(nCount > 0x7FFFFFFF)
1103     {
1104         nCount = 0x7FFFFFFF;
1105     }
1106     try
1107     {
1108         if ( !m_bTerminated && !IsSynchronMode() )
1109         {
1110             sal_uInt64 nLen = xSeekable->getLength();
1111             if ( nPos + nCount > nLen )
1112                 return ERRCODE_IO_PENDING;
1113         }
1114 
1115         nSize = xStream->readBytes( aData, sal_Int32(nCount) );
1116     }
1117     catch (const IOException&)
1118     {
1119         return ERRCODE_IO_CANTREAD;
1120     }
1121 
1122     memcpy (pBuffer, aData.getConstArray(), nSize);
1123     if (pRead)
1124         *pRead = static_cast<std::size_t>(nSize);
1125 
1126     return ERRCODE_NONE;
1127 }
1128 
WriteAt(sal_uInt64 const nPos,const void * pBuffer,std::size_t nCount,std::size_t * pWritten)1129 ErrCode UcbLockBytes::WriteAt(sal_uInt64 const nPos, const void *pBuffer,
1130         std::size_t nCount, std::size_t *pWritten)
1131 {
1132     if ( pWritten )
1133         *pWritten = 0;
1134 
1135     DBG_ASSERT( IsSynchronMode(), "Writing is only possible in SynchronMode!" );
1136     DBG_ASSERT( m_aInitialized.check(), "Writing bevor stream is ready!" );
1137 
1138     Reference <XSeekable> xSeekable = getSeekable_Impl();
1139     Reference <XOutputStream> xOutputStream = getOutputStream_Impl();
1140     if ( !xOutputStream.is() || !xSeekable.is() )
1141         return ERRCODE_IO_CANTWRITE;
1142 
1143     try
1144     {
1145         xSeekable->seek( nPos );
1146     }
1147     catch (const IOException&)
1148     {
1149         return ERRCODE_IO_CANTSEEK;
1150     }
1151 
1152     sal_Int8 const * pData = static_cast<sal_Int8 const *>(pBuffer);
1153     Sequence<sal_Int8> aData( pData, nCount );
1154     try
1155     {
1156         xOutputStream->writeBytes( aData );
1157         if ( pWritten )
1158             *pWritten = nCount;
1159     }
1160     catch (const Exception&)
1161     {
1162         return ERRCODE_IO_CANTWRITE;
1163     }
1164 
1165     return ERRCODE_NONE;
1166 }
1167 
Flush() const1168 ErrCode UcbLockBytes::Flush() const
1169 {
1170     Reference <XOutputStream > xOutputStream = getOutputStream_Impl();
1171     if ( !xOutputStream.is() )
1172         return ERRCODE_IO_CANTWRITE;
1173 
1174     try
1175     {
1176         xOutputStream->flush();
1177     }
1178     catch (const Exception&)
1179     {
1180         return ERRCODE_IO_CANTWRITE;
1181     }
1182 
1183     return ERRCODE_NONE;
1184 }
1185 
SetSize(sal_uInt64 const nNewSize)1186 ErrCode UcbLockBytes::SetSize (sal_uInt64 const nNewSize)
1187 {
1188     SvLockBytesStat aStat;
1189     Stat( &aStat );
1190     std::size_t nSize = aStat.nSize;
1191 
1192     if ( nSize > nNewSize )
1193     {
1194         Reference < XTruncate > xTrunc( getOutputStream_Impl(), UNO_QUERY );
1195         if ( xTrunc.is() )
1196         {
1197             xTrunc->truncate();
1198             nSize = 0;
1199         }
1200         else {
1201             SAL_INFO("unotools.ucbhelper", "Not truncable!");
1202         }
1203     }
1204 
1205     if ( nSize < nNewSize )
1206     {
1207         std::size_t nDiff = nNewSize-nSize, nCount=0;
1208         std::unique_ptr<sal_uInt8[]> pBuffer(new sal_uInt8[ nDiff ]);
1209         memset(pBuffer.get(), 0, nDiff); // initialize for enhanced security
1210         WriteAt( nSize, pBuffer.get(), nDiff, &nCount );
1211         if ( nCount != nDiff )
1212             return ERRCODE_IO_CANTWRITE;
1213     }
1214 
1215     return ERRCODE_NONE;
1216 }
1217 
Stat(SvLockBytesStat * pStat) const1218 ErrCode UcbLockBytes::Stat( SvLockBytesStat *pStat ) const
1219 {
1220     if ( IsSynchronMode() )
1221     {
1222         UcbLockBytes* pThis = const_cast < UcbLockBytes* >( this );
1223         pThis->m_aInitialized.wait();
1224     }
1225 
1226     if (!pStat)
1227         return ERRCODE_IO_INVALIDPARAMETER;
1228 
1229     Reference <XInputStream> xStream = getInputStream_Impl();
1230     Reference <XSeekable> xSeekable = getSeekable_Impl();
1231 
1232     if ( !xStream.is() )
1233     {
1234         if ( m_bTerminated )
1235             return ERRCODE_IO_INVALIDACCESS;
1236         else
1237             return ERRCODE_IO_PENDING;
1238     }
1239     else if( !xSeekable.is() )
1240         return ERRCODE_IO_CANTTELL;
1241 
1242     try
1243     {
1244         pStat->nSize = sal_uLong(xSeekable->getLength());
1245     }
1246     catch (const IOException&)
1247     {
1248         return ERRCODE_IO_CANTTELL;
1249     }
1250 
1251     return ERRCODE_NONE;
1252 }
1253 
CreateInputLockBytes(const Reference<XInputStream> & xInputStream)1254 UcbLockBytesRef UcbLockBytes::CreateInputLockBytes( const Reference< XInputStream >& xInputStream )
1255 {
1256     if( !xInputStream.is() )
1257         return nullptr;
1258 
1259     UcbLockBytesRef xLockBytes = new UcbLockBytes;
1260     xLockBytes->setDontClose_Impl();
1261     xLockBytes->setInputStream_Impl( xInputStream );
1262     xLockBytes->terminate_Impl();
1263     return xLockBytes;
1264 }
1265 
CreateLockBytes(const Reference<XStream> & xStream)1266 UcbLockBytesRef UcbLockBytes::CreateLockBytes( const Reference< XStream >& xStream )
1267 {
1268     if( !xStream.is() )
1269         return nullptr;
1270 
1271     UcbLockBytesRef xLockBytes = new UcbLockBytes;
1272     xLockBytes->setDontClose_Impl();
1273     xLockBytes->setStream_Impl( xStream );
1274     xLockBytes->terminate_Impl();
1275     return xLockBytes;
1276 }
1277 
CreateLockBytes(const Reference<XContent> & xContent,const Sequence<PropertyValue> & rProps,StreamMode eOpenMode,const Reference<XInteractionHandler> & xInteractionHandler)1278 UcbLockBytesRef UcbLockBytes::CreateLockBytes( const Reference < XContent >& xContent, const Sequence < PropertyValue >& rProps,
1279         StreamMode eOpenMode, const Reference < XInteractionHandler >& xInteractionHandler )
1280 {
1281     if( !xContent.is() )
1282         return nullptr;
1283 
1284     UcbLockBytesRef xLockBytes = new UcbLockBytes;
1285     xLockBytes->SetSynchronMode();
1286     Reference< XActiveDataControl > xSink;
1287     if ( eOpenMode & StreamMode::WRITE )
1288         xSink = new UcbStreamer_Impl(xLockBytes.get());
1289     else
1290         xSink = new UcbDataSink_Impl(xLockBytes.get());
1291 
1292     if ( rProps.hasElements() )
1293     {
1294         Reference < XCommandProcessor > xProcessor( xContent, UNO_QUERY );
1295         Command aCommand;
1296         aCommand.Name     = "setPropertyValues";
1297         aCommand.Handle   = -1; /* unknown */
1298         aCommand.Argument <<= rProps;
1299         xProcessor->execute( aCommand, 0, Reference < XCommandEnvironment >() );
1300     }
1301 
1302     OpenCommandArgument2 aArgument;
1303     aArgument.Sink = xSink;
1304     aArgument.Mode = OpenMode::DOCUMENT;
1305 
1306     Command aCommand;
1307     aCommand.Name = "open";
1308     aCommand.Argument <<= aArgument;
1309 
1310     bool bError = UCBOpenContentSync( xLockBytes,
1311                                       xContent,
1312                                       aCommand,
1313                                       xSink,
1314                                       xInteractionHandler );
1315 
1316     if ( xLockBytes->GetError() == ERRCODE_NONE && ( bError || !xLockBytes->getInputStream().is() ) )
1317     {
1318         OSL_FAIL("No InputStream, but no error set!" );
1319         xLockBytes->SetError( ERRCODE_IO_GENERAL );
1320     }
1321 
1322     return xLockBytes;
1323 }
1324 
1325 }
1326 
1327 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1328