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