summaryrefslogtreecommitdiffstats
path: root/unotools/source/ucbhelper
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /unotools/source/ucbhelper
parentInitial commit. (diff)
downloadlibreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz
libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'unotools/source/ucbhelper')
-rw-r--r--unotools/source/ucbhelper/XTempFile.hxx119
-rw-r--r--unotools/source/ucbhelper/localfilehelper.cxx92
-rw-r--r--unotools/source/ucbhelper/progresshandlerwrap.cxx88
-rw-r--r--unotools/source/ucbhelper/tempfile.cxx490
-rw-r--r--unotools/source/ucbhelper/ucbhelper.cxx413
-rw-r--r--unotools/source/ucbhelper/ucblockbytes.cxx1338
-rw-r--r--unotools/source/ucbhelper/ucblockbytes.hxx139
-rw-r--r--unotools/source/ucbhelper/ucbstreamhelper.cxx237
-rw-r--r--unotools/source/ucbhelper/xtempfile.cxx435
9 files changed, 3351 insertions, 0 deletions
diff --git a/unotools/source/ucbhelper/XTempFile.hxx b/unotools/source/ucbhelper/XTempFile.hxx
new file mode 100644
index 000000000..7fcaefb7b
--- /dev/null
+++ b/unotools/source/ucbhelper/XTempFile.hxx
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <optional>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/io/XTempFile.hpp>
+#include <com/sun/star/io/XTruncate.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertyAccess.hpp>
+#include <com/sun/star/beans/XFastPropertySet.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <mutex>
+#include <unotools/tempfile.hxx>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+class SvStream;
+namespace utl { class TempFile; }
+
+
+typedef ::cppu::WeakImplHelper< css::io::XTempFile
+ , css::io::XInputStream
+ , css::io::XOutputStream
+ , css::io::XTruncate
+ , css::beans::XPropertySet
+ , css::beans::XFastPropertySet
+ , css::beans::XPropertyAccess
+ , css::lang::XServiceInfo> OTempFileBase;
+
+class OTempFileService : public OTempFileBase
+{
+protected:
+ std::optional<utl::TempFile> mpTempFile;
+ std::mutex maMutex;
+ SvStream* mpStream;
+ bool mbRemoveFile;
+ bool mbInClosed;
+ bool mbOutClosed;
+
+ void checkError () const;
+ void checkConnected ();
+
+public:
+ explicit OTempFileService (css::uno::Reference< css::uno::XComponentContext > const & context);
+
+ //Methods
+ // XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override;
+
+ // XServiceInfo
+ virtual sal_Bool SAL_CALL supportsService(const OUString& sServiceName) override;
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XTempFile
+ virtual sal_Bool SAL_CALL getRemoveFile() override;
+ virtual void SAL_CALL setRemoveFile( sal_Bool _removefile ) override;
+ virtual OUString SAL_CALL getUri() override;
+ virtual OUString SAL_CALL getResourceName() override;
+
+ // XInputStream
+ virtual ::sal_Int32 SAL_CALL readBytes( css::uno::Sequence< ::sal_Int8 >& aData, ::sal_Int32 nBytesToRead ) override;
+ virtual ::sal_Int32 SAL_CALL readSomeBytes( css::uno::Sequence< ::sal_Int8 >& aData, ::sal_Int32 nMaxBytesToRead ) override;
+ virtual void SAL_CALL skipBytes( ::sal_Int32 nBytesToSkip ) override;
+ virtual ::sal_Int32 SAL_CALL available( ) override;
+ virtual void SAL_CALL closeInput( ) override;
+ // XOutputStream
+ virtual void SAL_CALL writeBytes( const css::uno::Sequence< ::sal_Int8 >& aData ) override;
+ virtual void SAL_CALL flush( ) override;
+ virtual void SAL_CALL closeOutput( ) override;
+ // XSeekable
+ virtual void SAL_CALL seek( sal_Int64 location ) override;
+ virtual sal_Int64 SAL_CALL getPosition( ) override;
+ virtual sal_Int64 SAL_CALL getLength( ) override;
+ // XStream
+ virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getInputStream( ) override;
+ virtual css::uno::Reference< css::io::XOutputStream > SAL_CALL getOutputStream( ) override;
+ // XTruncate
+ virtual void SAL_CALL truncate() override;
+
+ // XPropertySet
+ virtual ::css::uno::Reference< ::css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override;
+ virtual void SAL_CALL setPropertyValue( const ::rtl::OUString& aPropertyName, const ::css::uno::Any& aValue ) override;
+ virtual ::css::uno::Any SAL_CALL getPropertyValue( const ::rtl::OUString& PropertyName ) override;
+ virtual void SAL_CALL addPropertyChangeListener( const ::rtl::OUString& aPropertyName, const ::css::uno::Reference< ::css::beans::XPropertyChangeListener >& xListener ) override;
+ virtual void SAL_CALL removePropertyChangeListener( const ::rtl::OUString& aPropertyName, const ::css::uno::Reference< ::css::beans::XPropertyChangeListener >& aListener ) override;
+ virtual void SAL_CALL addVetoableChangeListener( const ::rtl::OUString& PropertyName, const ::css::uno::Reference< ::css::beans::XVetoableChangeListener >& aListener ) override;
+ virtual void SAL_CALL removeVetoableChangeListener( const ::rtl::OUString& PropertyName, const ::css::uno::Reference< ::css::beans::XVetoableChangeListener >& aListener ) override;
+ // XFastPropertySet
+ virtual void SAL_CALL setFastPropertyValue( ::sal_Int32 nHandle, const ::css::uno::Any& aValue ) override;
+ virtual ::css::uno::Any SAL_CALL getFastPropertyValue( ::sal_Int32 nHandle ) override;
+ // XPropertyAccess
+ virtual ::css::uno::Sequence< ::css::beans::PropertyValue > SAL_CALL getPropertyValues() override;
+ virtual void SAL_CALL setPropertyValues( const ::css::uno::Sequence< ::css::beans::PropertyValue >& aProps ) override;
+
+
+ virtual ~OTempFileService () override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/ucbhelper/localfilehelper.cxx b/unotools/source/ucbhelper/localfilehelper.cxx
new file mode 100644
index 000000000..bdabd5f0a
--- /dev/null
+++ b/unotools/source/ucbhelper/localfilehelper.cxx
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/ucb/XContentAccess.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <comphelper/DirectoryHelper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/sequence.hxx>
+#include <sal/log.hxx>
+#include <unotools/localfilehelper.hxx>
+#include <rtl/ustring.hxx>
+#include <ucbhelper/content.hxx>
+#include <vector>
+
+using namespace ::osl;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+
+namespace utl
+{
+
+css::uno::Sequence < OUString > LocalFileHelper::GetFolderContents( const OUString& rFolder, bool bFolder )
+{
+ std::vector< OUString > vFiles;
+ try
+ {
+ ::ucbhelper::Content aCnt(
+ rFolder, Reference< XCommandEnvironment >(),
+ comphelper::getProcessComponentContext() );
+ Reference< css::sdbc::XResultSet > xResultSet;
+ css::uno::Sequence< OUString > aProps { "Url" };
+
+ try
+ {
+ ::ucbhelper::ResultSetInclude eInclude = bFolder ? ::ucbhelper::INCLUDE_FOLDERS_AND_DOCUMENTS : ::ucbhelper::INCLUDE_DOCUMENTS_ONLY;
+ xResultSet = aCnt.createCursor( aProps, eInclude );
+ }
+ catch( css::ucb::CommandAbortedException& )
+ {
+ }
+ catch( Exception& )
+ {
+ }
+
+ if ( xResultSet.is() )
+ {
+ Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY );
+ try
+ {
+ while ( xResultSet->next() )
+ vFiles.push_back( xContentAccess->queryContentIdentifierString() );
+ }
+ catch( css::ucb::CommandAbortedException& )
+ {
+ }
+ catch( Exception& )
+ {
+ }
+ }
+ }
+ catch( Exception& )
+ {
+ }
+
+ return comphelper::containerToSequence(vFiles);
+}
+
+void removeTree(OUString const & url) {
+ const bool bError = comphelper::DirectoryHelper::deleteDirRecursively(url);
+ SAL_WARN_IF(bError, "desktop.app", "error removing directory " << url);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/ucbhelper/progresshandlerwrap.cxx b/unotools/source/ucbhelper/progresshandlerwrap.cxx
new file mode 100644
index 000000000..29c1067ec
--- /dev/null
+++ b/unotools/source/ucbhelper/progresshandlerwrap.cxx
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <unotools/progresshandlerwrap.hxx>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <utility>
+
+namespace utl
+{
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::task;
+using namespace ::com::sun::star::ucb;
+
+ProgressHandlerWrap::ProgressHandlerWrap( css::uno::Reference< css::task::XStatusIndicator > xSI )
+: m_xStatusIndicator(std::move( xSI ))
+{
+}
+
+static bool getStatusFromAny_Impl( const Any& aAny, OUString& aText, sal_Int32& nNum )
+{
+ bool bNumIsSet = false;
+
+ Sequence< Any > aSetList;
+ if( aAny >>= aSetList )
+ for( const auto& rSet : std::as_const(aSetList) )
+ {
+ if( !bNumIsSet && ( rSet >>= nNum ) )
+ bNumIsSet = true;
+ else
+ aText.isEmpty() && ( rSet >>= aText );
+ }
+
+ return bNumIsSet;
+}
+
+void SAL_CALL ProgressHandlerWrap::push( const Any& Status )
+{
+ if( !m_xStatusIndicator.is() )
+ return;
+
+ OUString aText;
+ sal_Int32 nRange;
+
+ if( getStatusFromAny_Impl( Status, aText, nRange ) )
+ m_xStatusIndicator->start( aText, nRange );
+}
+
+void SAL_CALL ProgressHandlerWrap::update( const Any& Status )
+{
+ if( !m_xStatusIndicator.is() )
+ return;
+
+ OUString aText;
+ sal_Int32 nValue;
+
+ if( getStatusFromAny_Impl( Status, aText, nValue ) )
+ {
+ if( !aText.isEmpty() ) m_xStatusIndicator->setText( aText );
+ m_xStatusIndicator->setValue( nValue );
+ }
+}
+
+void SAL_CALL ProgressHandlerWrap::pop()
+{
+ if( m_xStatusIndicator.is() )
+ m_xStatusIndicator->end();
+}
+
+} // namespace utl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/ucbhelper/tempfile.cxx b/unotools/source/ucbhelper/tempfile.cxx
new file mode 100644
index 000000000..faca685d2
--- /dev/null
+++ b/unotools/source/ucbhelper/tempfile.cxx
@@ -0,0 +1,490 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cassert>
+#include <utility>
+
+#include <unotools/tempfile.hxx>
+#include <rtl/ustring.hxx>
+#include <osl/mutex.hxx>
+#include <osl/detail/file.h>
+#include <osl/file.hxx>
+#include <tools/time.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/DirectoryHelper.hxx>
+
+#ifdef UNX
+#include <unistd.h>
+#elif defined( _WIN32 )
+#include <process.h>
+#endif
+
+using namespace osl;
+
+namespace
+{
+ OUString gTempNameBase_Impl;
+}
+
+namespace utl
+{
+
+static OUString getParentName( std::u16string_view aFileName )
+{
+ size_t lastIndex = aFileName.rfind( '/' );
+ OUString aParent;
+
+ if (lastIndex != std::u16string_view::npos)
+ {
+ aParent = aFileName.substr(0, lastIndex);
+
+ if (aParent.endsWith(":") && aParent.getLength() == 6)
+ aParent += "/";
+
+ if (aParent.equalsIgnoreAsciiCase("file://"))
+ aParent = "file:///";
+ }
+
+ return aParent;
+}
+
+static bool ensuredir( const OUString& rUnqPath )
+{
+ OUString aPath;
+ if ( rUnqPath.isEmpty() )
+ return false;
+
+ // remove trailing slash
+ if ( rUnqPath.endsWith("/") )
+ aPath = rUnqPath.copy( 0, rUnqPath.getLength() - 1 );
+ else
+ aPath = rUnqPath;
+
+ // HACK: create directory on a mount point with nobrowse option
+ // returns ENOSYS in any case !!
+ osl::Directory aDirectory( aPath );
+ osl::FileBase::RC nError = aDirectory.open();
+ aDirectory.close();
+ if( nError == osl::File::E_None )
+ return true;
+
+ // try to create the directory
+ nError = osl::Directory::create( aPath );
+ bool bSuccess = ( nError == osl::File::E_None || nError == osl::FileBase::E_EXIST );
+ if( !bSuccess )
+ {
+ // perhaps parent(s) don't exist
+ OUString aParentDir = getParentName( aPath );
+ if ( aParentDir != aPath )
+ {
+ bSuccess = ensuredir( getParentName( aPath ) );
+
+ // After parent directory structure exists try it one's more
+ if ( bSuccess )
+ {
+ // Parent directory exists, retry creation of directory
+ nError = osl::Directory::create( aPath );
+ bSuccess =( nError == osl::File::E_None || nError == osl::FileBase::E_EXIST );
+ }
+ }
+ }
+
+ return bSuccess;
+}
+
+static OUString ConstructTempDir_Impl( const OUString* pParent, bool bCreateParentDirs )
+{
+ OUString aName;
+
+ // Ignore pParent on iOS. We don't want to create any temp files
+ // in the same directory where the document being edited is.
+#ifndef IOS
+ if ( pParent && !pParent->isEmpty() )
+ {
+ // test for valid filename
+ OUString aRet;
+ if ((osl::FileBase::getSystemPathFromFileURL(*pParent, aRet)
+ == osl::FileBase::E_None)
+ && (osl::FileBase::getFileURLFromSystemPath(aRet, aRet)
+ == osl::FileBase::E_None))
+ {
+ ::osl::DirectoryItem aItem;
+ sal_Int32 i = aRet.getLength();
+ if ( aRet[i-1] == '/' )
+ i--;
+
+ if ( DirectoryItem::get( aRet.copy(0, i), aItem ) == FileBase::E_None || bCreateParentDirs )
+ aName = aRet;
+ }
+ }
+#else
+ (void) pParent;
+ (void) bCreateParentDirs;
+#endif
+
+ if ( aName.isEmpty() )
+ {
+ if (gTempNameBase_Impl.isEmpty())
+ {
+ OUString ustrTempDirURL;
+ ::osl::FileBase::RC rc = ::osl::File::getTempDirURL(
+ ustrTempDirURL );
+ if (rc == ::osl::FileBase::E_None)
+ gTempNameBase_Impl = ustrTempDirURL;
+ ensuredir( aName );
+ }
+ // if no parent or invalid parent : use default directory
+ DBG_ASSERT( !gTempNameBase_Impl.isEmpty(), "No TempDir!" );
+ aName = gTempNameBase_Impl;
+ }
+
+ // Make sure that directory ends with a separator
+ if( !aName.isEmpty() && !aName.endsWith("/") )
+ aName += "/";
+
+ return aName;
+}
+
+namespace {
+
+class Tokens {
+public:
+ virtual bool next(OUString *) = 0;
+
+protected:
+ virtual ~Tokens() {} // avoid warnings
+};
+
+class SequentialTokens: public Tokens {
+public:
+ explicit SequentialTokens(bool showZero): m_value(0), m_show(showZero) {}
+
+ bool next(OUString * token) override {
+ assert(token != nullptr);
+ if (m_value == SAL_MAX_UINT32) {
+ return false;
+ }
+ *token = m_show ? OUString::number(m_value) : OUString();
+ ++m_value;
+ m_show = true;
+ return true;
+ }
+
+private:
+ sal_uInt32 m_value;
+ bool m_show;
+};
+
+class UniqueTokens: public Tokens {
+public:
+ UniqueTokens(): m_count(0) {}
+
+ bool next(OUString * token) override {
+ assert(token != nullptr);
+ // Because of the shared globalValue, no single instance of UniqueTokens
+ // is guaranteed to exhaustively test all 36^6 possible values, but stop
+ // after that many attempts anyway:
+ sal_uInt32 radix = 36;
+ sal_uInt32 max = radix * radix * radix * radix * radix * radix;
+ // 36^6 == 2'176'782'336 < SAL_MAX_UINT32 == 4'294'967'295
+ if (m_count == max) {
+ return false;
+ }
+ sal_uInt32 v;
+ {
+ osl::MutexGuard g(osl::Mutex::getGlobalMutex());
+ globalValue
+ = ((globalValue == SAL_MAX_UINT32
+ ? tools::Time::GetSystemTicks() : globalValue + 1)
+ % max);
+ v = globalValue;
+ }
+ *token = OUString::number(v, radix);
+ ++m_count;
+ return true;
+ }
+
+private:
+ static sal_uInt32 globalValue;
+
+ sal_uInt32 m_count;
+};
+
+}
+
+sal_uInt32 UniqueTokens::globalValue = SAL_MAX_UINT32;
+
+namespace
+{
+ class TempDirCreatedObserver : public DirectoryCreationObserver
+ {
+ public:
+ virtual void DirectoryCreated(const OUString& aDirectoryUrl) override
+ {
+ File::setAttributes( aDirectoryUrl, osl_File_Attribute_OwnRead |
+ osl_File_Attribute_OwnWrite | osl_File_Attribute_OwnExe );
+ };
+ };
+};
+
+static OUString lcl_createName(
+ std::u16string_view rLeadingChars, Tokens & tokens, const OUString* pExtension,
+ const OUString* pParent, bool bDirectory, bool bKeep, bool bLock,
+ bool bCreateParentDirs )
+{
+ OUString aName = ConstructTempDir_Impl( pParent, bCreateParentDirs );
+ if ( bCreateParentDirs )
+ {
+ size_t nOffset = rLeadingChars.rfind(u"/");
+ OUString aDirName;
+ if (std::u16string_view::npos != nOffset)
+ aDirName = aName + rLeadingChars.substr( 0, nOffset );
+ else
+ aDirName = aName;
+ TempDirCreatedObserver observer;
+ FileBase::RC err = Directory::createPath( aDirName, &observer );
+ if ( err != FileBase::E_None && err != FileBase::E_EXIST )
+ return OUString();
+ }
+ aName += rLeadingChars;
+
+ OUString token;
+ while (tokens.next(&token))
+ {
+ OUString aTmp( aName + token );
+ if ( pExtension )
+ aTmp += *pExtension;
+ else
+ aTmp += ".tmp";
+ if ( bDirectory )
+ {
+ FileBase::RC err = Directory::create(
+ aTmp,
+ (osl_File_OpenFlag_Read | osl_File_OpenFlag_Write
+ | osl_File_OpenFlag_Private));
+ if ( err == FileBase::E_None )
+ {
+ // !bKeep: only for creating a name, not a file or directory
+ if ( bKeep || Directory::remove( aTmp ) == FileBase::E_None )
+ return aTmp;
+ else
+ return OUString();
+ }
+ else if ( err != FileBase::E_EXIST )
+ // if f.e. name contains invalid chars stop trying to create dirs
+ return OUString();
+ }
+ else
+ {
+ DBG_ASSERT( bKeep, "Too expensive, use directory for creating name!" );
+ File aFile( aTmp );
+ FileBase::RC err = aFile.open(
+ osl_File_OpenFlag_Create | osl_File_OpenFlag_Private
+ | (bLock ? 0 : osl_File_OpenFlag_NoLock));
+ if ( err == FileBase::E_None || (bLock && err == FileBase::E_NOLCK) )
+ {
+ aFile.close();
+ return aTmp;
+ }
+ else if ( err != FileBase::E_EXIST )
+ {
+ // if f.e. name contains invalid chars stop trying to create dirs
+ // but if there is a folder with such name proceed further
+
+ DirectoryItem aTmpItem;
+ FileStatus aTmpStatus( osl_FileStatus_Mask_Type );
+ if ( DirectoryItem::get( aTmp, aTmpItem ) != FileBase::E_None
+ || aTmpItem.getFileStatus( aTmpStatus ) != FileBase::E_None
+ || aTmpStatus.getFileType() != FileStatus::Directory )
+ return OUString();
+ }
+ }
+ }
+ return OUString();
+}
+
+static OUString CreateTempName_Impl( const OUString* pParent, bool bKeep, bool bDir = true )
+{
+ OUString aEyeCatcher = "lu";
+#ifdef UNX
+#ifdef DBG_UTIL
+ const char* eye = getenv("LO_TESTNAME");
+ if(eye)
+ {
+ aEyeCatcher = OUString(eye, strlen(eye), RTL_TEXTENCODING_ASCII_US);
+ }
+#else
+ static const pid_t pid = getpid();
+ static const OUString aPidString = OUString::number(pid);
+ aEyeCatcher += aPidString;
+#endif
+#elif defined(_WIN32)
+ static const int pid = _getpid();
+ static const OUString aPidString = OUString::number(pid);
+ aEyeCatcher += aPidString;
+#endif
+ UniqueTokens t;
+ return lcl_createName( aEyeCatcher, t, nullptr, pParent, bDir, bKeep,
+ false, false);
+}
+
+OUString TempFile::CreateTempName()
+{
+ OUString aName(CreateTempName_Impl( nullptr, false ));
+
+ // convert to file URL
+ OUString aTmp;
+ if ( !aName.isEmpty() )
+ FileBase::getSystemPathFromFileURL( aName, aTmp );
+ return aTmp;
+}
+
+TempFile::TempFile( const OUString* pParent, bool bDirectory )
+ : bIsDirectory( bDirectory )
+ , bKillingFileEnabled( false )
+{
+ aName = CreateTempName_Impl( pParent, true, bDirectory );
+}
+
+TempFile::TempFile( std::u16string_view rLeadingChars, bool _bStartWithZero,
+ const OUString* pExtension, const OUString* pParent,
+ bool bCreateParentDirs )
+ : bIsDirectory( false )
+ , bKillingFileEnabled( false )
+{
+ SequentialTokens t(_bStartWithZero);
+ aName = lcl_createName( rLeadingChars, t, pExtension, pParent, false,
+ true, true, bCreateParentDirs );
+}
+
+TempFile::TempFile(TempFile && other) noexcept :
+ aName(std::move(other.aName)), pStream(std::move(other.pStream)), bIsDirectory(other.bIsDirectory),
+ bKillingFileEnabled(other.bKillingFileEnabled)
+{
+ other.bKillingFileEnabled = false;
+}
+
+TempFile::~TempFile()
+{
+ if ( !bKillingFileEnabled )
+ return;
+
+ pStream.reset();
+ if ( bIsDirectory )
+ {
+ comphelper::DirectoryHelper::deleteDirRecursively(aName);
+ }
+ else
+ {
+ File::remove( aName );
+ }
+}
+
+bool TempFile::IsValid() const
+{
+ return !aName.isEmpty();
+}
+
+OUString TempFile::GetFileName() const
+{
+ OUString aTmp;
+ FileBase::getSystemPathFromFileURL(aName, aTmp);
+ return aTmp;
+}
+
+OUString const & TempFile::GetURL() const
+{
+ // if you request the URL, then you presumably want to access this via UCB,
+ // and UCB will want to open the file via a separate file handle, which means
+ // we have to make this file data actually hit disk. We do this here (and not
+ // elsewhere) to make the other (normal) paths fast. Flushing to disk
+ // really slows temp files down.
+ if (pStream)
+ pStream->Flush();
+ return aName;
+}
+
+SvStream* TempFile::GetStream( StreamMode eMode )
+{
+ if (!pStream)
+ {
+ if (!aName.isEmpty())
+ pStream.reset(new SvFileStream(aName, eMode | StreamMode::TEMPORARY));
+ else
+ pStream.reset(new SvMemoryStream);
+ }
+
+ return pStream.get();
+}
+
+void TempFile::CloseStream()
+{
+ pStream.reset();
+}
+
+OUString TempFile::SetTempNameBaseDirectory( const OUString &rBaseName )
+{
+ if( rBaseName.isEmpty() )
+ return OUString();
+
+ OUString aUnqPath( rBaseName );
+
+ // remove trailing slash
+ if ( rBaseName.endsWith("/") )
+ aUnqPath = rBaseName.copy( 0, rBaseName.getLength() - 1 );
+
+ // try to create the directory
+ bool bRet = false;
+ osl::FileBase::RC err = osl::Directory::create( aUnqPath );
+ if ( err != FileBase::E_None && err != FileBase::E_EXIST )
+ // perhaps parent(s) don't exist
+ bRet = ensuredir( aUnqPath );
+ else
+ bRet = true;
+
+ // failure to create base directory means returning an empty string
+ OUString aTmp;
+ if ( bRet )
+ {
+ // append own internal directory
+ OUString &rTempNameBase_Impl = gTempNameBase_Impl;
+ rTempNameBase_Impl = rBaseName + "/";
+
+ TempFile aBase( nullptr, true );
+ if ( aBase.IsValid() )
+ // use it in case of success
+ rTempNameBase_Impl = aBase.aName;
+
+ // return system path of used directory
+ FileBase::getSystemPathFromFileURL( rTempNameBase_Impl, aTmp );
+ }
+
+ return aTmp;
+}
+
+OUString TempFile::GetTempNameBaseDirectory()
+{
+ return ConstructTempDir_Impl(nullptr, false);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/ucbhelper/ucbhelper.cxx b/unotools/source/ucbhelper/ucbhelper.cxx
new file mode 100644
index 000000000..4878ec1ce
--- /dev/null
+++ b/unotools/source/ucbhelper/ucbhelper.cxx
@@ -0,0 +1,413 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cassert>
+#include <vector>
+
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/ContentInfo.hpp>
+#include <com/sun/star/ucb/ContentInfoAttribute.hpp>
+#include <com/sun/star/ucb/IOErrorCode.hpp>
+#include <com/sun/star/ucb/InteractiveIOException.hpp>
+#include <com/sun/star/ucb/NameClashException.hpp>
+#include <com/sun/star/ucb/UniversalContentBroker.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/ucb/XContentAccess.hpp>
+#include <com/sun/star/ucb/XUniversalContentBroker.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Exception.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/simplefileaccessinteraction.hxx>
+#include <osl/file.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <tools/datetime.hxx>
+#include <tools/urlobj.hxx>
+#include <tools/diagnose_ex.h>
+#include <ucbhelper/commandenvironment.hxx>
+#include <ucbhelper/content.hxx>
+#include <unotools/ucbhelper.hxx>
+
+namespace com::sun::star::ucb { class XProgressHandler; }
+namespace com::sun::star::uno { class XComponentContext; }
+namespace com::sun::star::util { struct DateTime; }
+
+namespace {
+
+OUString canonic(OUString const & url) {
+ INetURLObject o(url);
+ SAL_WARN_IF(o.HasError(), "unotools.ucbhelper", "Invalid URL \"" << url << '"');
+ return o.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+}
+
+ucbhelper::Content content(OUString const & url) {
+ return ucbhelper::Content(
+ canonic(url),
+ utl::UCBContentHelper::getDefaultCommandEnvironment(),
+ comphelper::getProcessComponentContext());
+}
+
+ucbhelper::Content content(INetURLObject const & url) {
+ return ucbhelper::Content(
+ url.GetMainURL(INetURLObject::DecodeMechanism::NONE),
+ utl::UCBContentHelper::getDefaultCommandEnvironment(),
+ comphelper::getProcessComponentContext());
+}
+
+std::vector<OUString> getContents(OUString const & url) {
+ try {
+ std::vector<OUString> cs;
+ ucbhelper::Content c(content(url));
+ css::uno::Sequence<OUString> args { "Title" };
+ css::uno::Reference<css::sdbc::XResultSet> res( c.createCursor(args), css::uno::UNO_SET_THROW);
+ css::uno::Reference<css::ucb::XContentAccess> acc( res, css::uno::UNO_QUERY_THROW);
+ while (res->next()) {
+ cs.push_back(acc->queryContentIdentifierString());
+ }
+ return cs;
+ } catch (css::uno::RuntimeException const &) {
+ throw;
+ } catch (css::ucb::CommandAbortedException const &) {
+ assert(false && "this cannot happen");
+ throw;
+ } catch (css::uno::Exception const &) {
+ TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "getContents(" << url << ")");
+ return std::vector<OUString>();
+ }
+}
+
+OUString getCasePreservingUrl(const INetURLObject& url) {
+ return
+ content(url).executeCommand(
+ "getCasePreservingURL",
+ css::uno::Any()).
+ get<OUString>();
+}
+
+DateTime convert(css::util::DateTime const & dt) {
+ return DateTime(dt);
+}
+
+}
+
+css::uno::Reference< css::ucb::XCommandEnvironment > utl::UCBContentHelper::getDefaultCommandEnvironment()
+{
+ css::uno::Reference< css::task::XInteractionHandler > xIH(
+ css::task::InteractionHandler::createWithParent(
+ comphelper::getProcessComponentContext(), nullptr ) );
+
+ css::uno::Reference< css::ucb::XProgressHandler > xProgress;
+ rtl::Reference<ucbhelper::CommandEnvironment> pCommandEnv =
+ new ::ucbhelper::CommandEnvironment(
+ new comphelper::SimpleFileAccessInteraction( xIH ), xProgress );
+
+ return pCommandEnv;
+}
+
+bool utl::UCBContentHelper::IsDocument(OUString const & url) {
+ try {
+ return content(url).isDocument();
+ } catch (css::uno::RuntimeException const &) {
+ throw;
+ } catch (css::ucb::CommandAbortedException const &) {
+ assert(false && "this cannot happen");
+ throw;
+ } catch (css::uno::Exception const &) {
+ TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::IsDocument(" << url << ")");
+ return false;
+ }
+}
+
+css::uno::Any utl::UCBContentHelper::GetProperty(
+ OUString const & url, OUString const & property)
+{
+ try {
+ return content(url).getPropertyValue(property);
+ } catch (css::uno::RuntimeException const &) {
+ throw;
+ } catch (css::ucb::CommandAbortedException const &) {
+ assert(false && "this cannot happen");
+ throw;
+ } catch (css::uno::Exception const &) {
+ TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::GetProperty(" << url << ", " << property << ")");
+ return css::uno::Any();
+ }
+}
+
+bool utl::UCBContentHelper::IsFolder(OUString const & url) {
+ try {
+ return content(url).isFolder();
+ } catch (css::uno::RuntimeException const &) {
+ throw;
+ } catch (css::ucb::CommandAbortedException const &) {
+ assert(false && "this cannot happen");
+ throw;
+ } catch (css::uno::Exception const &) {
+ TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::IsFolder(" << url << ")");
+ return false;
+ }
+}
+
+bool utl::UCBContentHelper::GetTitle(
+ OUString const & url, OUString * title)
+{
+ assert(title != nullptr);
+ try {
+ return content(url).getPropertyValue("Title") >>= *title;
+ } catch (css::uno::RuntimeException const &) {
+ throw;
+ } catch (css::ucb::CommandAbortedException const &) {
+ assert(false && "this cannot happen");
+ throw;
+ } catch (css::uno::Exception const &) {
+ TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::GetTitle(" << url << ")");
+ return false;
+ }
+}
+
+bool utl::UCBContentHelper::Kill(OUString const & url) {
+ try {
+ content(url).executeCommand(
+ "delete",
+ css::uno::Any(true));
+ return true;
+ } catch (css::uno::RuntimeException const &) {
+ throw;
+ } catch (css::ucb::CommandAbortedException const &) {
+ assert(false && "this cannot happen");
+ throw;
+ } catch (css::uno::Exception const &) {
+ TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::Kill(" << url << ")");
+ return false;
+ }
+}
+
+bool utl::UCBContentHelper::MakeFolder(
+ ucbhelper::Content & parent, OUString const & title,
+ ucbhelper::Content & result)
+{
+ bool exists = false;
+ try {
+ const css::uno::Sequence<css::ucb::ContentInfo> info(
+ parent.queryCreatableContentsInfo());
+ for (const auto& rInfo : info) {
+ // Simply look for the first KIND_FOLDER:
+ if ((rInfo.Attributes
+ & css::ucb::ContentInfoAttribute::KIND_FOLDER)
+ != 0)
+ {
+ // Make sure the only required bootstrap property is "Title":
+ if ( rInfo.Properties.getLength() != 1 || rInfo.Properties[0].Name != "Title" )
+ {
+ continue;
+ }
+ if (parent.insertNewContent(rInfo.Type, { "Title" }, { css::uno::Any(title) }, result))
+ {
+ return true;
+ }
+ }
+ }
+ } catch (css::ucb::InteractiveIOException const & e) {
+ if (e.Code == css::ucb::IOErrorCode_ALREADY_EXISTING) {
+ exists = true;
+ } else {
+ TOOLS_INFO_EXCEPTION(
+ "unotools.ucbhelper",
+ "UCBContentHelper::MakeFolder(" << title << ")");
+ }
+ } catch (css::ucb::NameClashException const &) {
+ exists = true;
+ } catch (css::uno::RuntimeException const &) {
+ throw;
+ } catch (css::ucb::CommandAbortedException const &) {
+ assert(false && "this cannot happen");
+ throw;
+ } catch (css::uno::Exception const &) {
+ TOOLS_INFO_EXCEPTION(
+ "unotools.ucbhelper",
+ "UCBContentHelper::MakeFolder(" << title << ") ");
+ }
+ if (exists) {
+ INetURLObject o(parent.getURL());
+ o.Append(title);
+ result = content(o);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool utl::UCBContentHelper::IsYounger(
+ OUString const & younger, OUString const & older)
+{
+ try {
+ return
+ convert(
+ content(younger).getPropertyValue(
+ "DateModified").
+ get<css::util::DateTime>())
+ > convert(
+ content(older).getPropertyValue(
+ "DateModified").
+ get<css::util::DateTime>());
+ } catch (css::uno::RuntimeException const &) {
+ throw;
+ } catch (css::ucb::CommandAbortedException const &) {
+ assert(false && "this cannot happen");
+ throw;
+ } catch (css::uno::Exception const &) {
+ TOOLS_INFO_EXCEPTION(
+ "unotools.ucbhelper",
+ "UCBContentHelper::IsYounger(" << younger << ", " << older << ")");
+ return false;
+ }
+}
+
+bool utl::UCBContentHelper::Exists(OUString const & url) {
+ OUString pathname;
+ if (osl::FileBase::getSystemPathFromFileURL(url, pathname)
+ == osl::FileBase::E_None)
+ {
+ // Try to create a directory entry for the given URL:
+ OUString url2;
+ if (osl::FileBase::getFileURLFromSystemPath(pathname, url2)
+ == osl::FileBase::E_None)
+ {
+ // #106526 osl_getDirectoryItem is an existence check, no further
+ // osl_getFileStatus call necessary:
+ osl::DirectoryItem item;
+ return osl::DirectoryItem::get(url2, item) == osl::FileBase::E_None;
+ } else {
+ return false;
+ }
+ } else {
+ // Divide URL into folder and name part:
+ INetURLObject o(url);
+ OUString name(
+ o.getName(
+ INetURLObject::LAST_SEGMENT, true,
+ INetURLObject::DecodeMechanism::WithCharset));
+ o.removeSegment();
+ o.removeFinalSlash();
+ std::vector<OUString> cs(
+ getContents(o.GetMainURL(INetURLObject::DecodeMechanism::NONE)));
+ return std::any_of(cs.begin(), cs.end(),
+ [&name](const OUString& rItem) {
+ return INetURLObject(rItem).
+ getName(INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset).
+ equalsIgnoreAsciiCase(name); });
+ }
+}
+
+bool utl::UCBContentHelper::IsSubPath(
+ OUString const & parent, OUString const & child)
+{
+ // The comparison is done in the following way:
+ // - First, compare case sensitively
+ // - If names are different, try a fallback comparing case insensitively
+ // - If the last comparison succeeded, get case preserving normalized names
+ // for the files and compare them
+ // (The second step is required because retrieving the normalized names
+ // might be very expensive in some cases.)
+ INetURLObject candidate(child);
+ INetURLObject folder(parent);
+ if (candidate.GetProtocol() != folder.GetProtocol()) {
+ return false;
+ }
+ INetURLObject candidateLower(child.toAsciiLowerCase());
+ INetURLObject folderLower(parent.toAsciiLowerCase());
+ try {
+ INetURLObject tmp;
+ do {
+ if (candidate == folder
+ || (candidate.GetProtocol() == INetProtocol::File
+ && candidateLower == folderLower
+ && (getCasePreservingUrl(candidate)
+ == getCasePreservingUrl(folder))))
+ {
+ return true;
+ }
+ tmp = candidate;
+ } while (candidate.removeSegment() && candidateLower.removeSegment()
+ && candidate != tmp);
+ // INetURLObject::removeSegment sometimes returns true without
+ // modifying the URL, e.g., in case of "file:///"
+ } catch (css::uno::RuntimeException const &) {
+ throw;
+ } catch (css::ucb::CommandAbortedException const &) {
+ assert(false && "this cannot happen");
+ throw;
+ } catch (css::uno::Exception const &) {
+ TOOLS_INFO_EXCEPTION(
+ "unotools.ucbhelper",
+ "UCBContentHelper::IsSubPath(" << parent << ", " << child << ")");
+ }
+ return false;
+}
+
+bool utl::UCBContentHelper::EqualURLs(
+ OUString const & url1, OUString const & url2)
+{
+ if (url1.isEmpty() || url2.isEmpty()) {
+ return false;
+ }
+ css::uno::Reference< css::ucb::XUniversalContentBroker > ucb(
+ css::ucb::UniversalContentBroker::create(
+ comphelper::getProcessComponentContext()));
+ return
+ ucb->compareContentIds(
+ ucb->createContentIdentifier(canonic(url1)),
+ ucb->createContentIdentifier(canonic(url2)))
+ == 0;
+}
+
+bool utl::UCBContentHelper::ensureFolder(
+ const css::uno::Reference< css::uno::XComponentContext >& xCtx,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv,
+ std::u16string_view rFolder, ucbhelper::Content & result) noexcept
+{
+ try
+ {
+ INetURLObject aURL( rFolder );
+ OUString aTitle = aURL.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
+ aURL.removeSegment();
+ ::ucbhelper::Content aParent;
+
+ if ( ::ucbhelper::Content::create( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ),
+ xEnv, xCtx, aParent ) )
+ {
+ return ::utl::UCBContentHelper::MakeFolder(aParent, aTitle, result);
+ }
+ }
+ catch (...)
+ {
+ }
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/ucbhelper/ucblockbytes.cxx b/unotools/source/ucbhelper/ucblockbytes.cxx
new file mode 100644
index 000000000..ad116eff2
--- /dev/null
+++ b/unotools/source/ucbhelper/ucblockbytes.cxx
@@ -0,0 +1,1338 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "ucblockbytes.hxx"
+
+#include <sal/log.hxx>
+#include <comphelper/processfactory.hxx>
+#include <salhelper/condition.hxx>
+#include <osl/thread.hxx>
+#include <osl/diagnose.h>
+#include <tools/urlobj.hxx>
+#include <tools/solar.h>
+#include <ucbhelper/interactionrequest.hxx>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/task/XInteractionAbort.hpp>
+#include <com/sun/star/ucb/InteractiveNetworkConnectException.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/UnsupportedDataSinkException.hpp>
+#include <com/sun/star/ucb/InteractiveIOException.hpp>
+#include <com/sun/star/ucb/XContentIdentifier.hpp>
+#include <com/sun/star/ucb/XContent.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/XActiveDataStreamer.hpp>
+#include <com/sun/star/io/TempFile.hpp>
+#include <com/sun/star/ucb/XCommandProcessor.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/ucb/OpenCommandArgument2.hpp>
+#include <com/sun/star/ucb/PostCommandArgument2.hpp>
+#include <com/sun/star/ucb/OpenMode.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/XPropertiesChangeNotifier.hpp>
+#include <com/sun/star/beans/XPropertiesChangeListener.hpp>
+#include <com/sun/star/io/XActiveDataSink.hpp>
+#include <com/sun/star/io/XActiveDataControl.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <tools/debug.hxx>
+#include <com/sun/star/io/XTruncate.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+#include <comphelper/bytereader.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <ucbhelper/content.hxx>
+#include <mutex>
+#include <utility>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::ucb;
+using namespace ::com::sun::star::task;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+
+namespace utl
+{
+
+namespace {
+
+/**
+ Helper class for getting a XInputStream when opening a content
+ */
+class UcbDataSink_Impl : public ::cppu::WeakImplHelper< XActiveDataControl, XActiveDataSink >
+{
+ UcbLockBytesRef m_xLockBytes;
+
+public:
+ explicit UcbDataSink_Impl( UcbLockBytes* pLockBytes )
+ : m_xLockBytes( pLockBytes )
+ {}
+
+ // XActiveDataControl.
+ virtual void SAL_CALL addListener ( const Reference<XStreamListener> &/*rxListener*/) override {}
+ virtual void SAL_CALL removeListener ( const Reference<XStreamListener> &/*rxListener*/) override {}
+ virtual void SAL_CALL start() override {}
+ virtual void SAL_CALL terminate() override
+ { m_xLockBytes->terminate(); }
+
+ // XActiveDataSink.
+ virtual void SAL_CALL setInputStream ( const Reference<XInputStream> &rxInputStream) override
+ { m_xLockBytes->setInputStream(rxInputStream); }
+ virtual Reference<XInputStream> SAL_CALL getInputStream() override
+ { return m_xLockBytes->getInputStream(); }
+};
+
+/**
+ Helper class for getting a XStream when opening a content
+ */
+class UcbStreamer_Impl : public ::cppu::WeakImplHelper< XActiveDataStreamer, XActiveDataControl >
+{
+ Reference < XStream > m_xStream;
+ UcbLockBytesRef m_xLockBytes;
+
+public:
+ explicit UcbStreamer_Impl( UcbLockBytes* pLockBytes )
+ : m_xLockBytes( pLockBytes )
+ {}
+
+ // XActiveDataControl.
+ virtual void SAL_CALL addListener ( const Reference<XStreamListener> &/*rxListener*/) override {}
+ virtual void SAL_CALL removeListener ( const Reference<XStreamListener> &/*rxListener*/) override {}
+ virtual void SAL_CALL start() override {}
+ virtual void SAL_CALL terminate() override
+ { m_xLockBytes->terminate(); }
+
+ // XActiveDataStreamer
+ virtual void SAL_CALL setStream( const Reference< XStream >& aStream ) override
+ { m_xStream = aStream; m_xLockBytes->setStream( aStream ); }
+ virtual Reference< XStream > SAL_CALL getStream() override
+ { return m_xStream; }
+};
+
+/**
+ Helper class for managing interactions and progress when executing UCB commands
+ */
+class UcbTaskEnvironment : public ::cppu::WeakImplHelper< XCommandEnvironment >
+{
+ Reference< XInteractionHandler > m_xInteractionHandler;
+ Reference< XProgressHandler > m_xProgressHandler;
+
+public:
+ UcbTaskEnvironment( const Reference< XInteractionHandler>& rxInteractionHandler,
+ const Reference< XProgressHandler>& rxProgressHandler )
+ : m_xInteractionHandler( rxInteractionHandler )
+ , m_xProgressHandler( rxProgressHandler )
+ {}
+
+ virtual Reference<XInteractionHandler> SAL_CALL getInteractionHandler() override
+ { return m_xInteractionHandler; }
+
+ virtual Reference<XProgressHandler> SAL_CALL getProgressHandler() override
+ { return m_xProgressHandler; }
+};
+
+/**
+ Helper class for property change notifies when executing UCB commands
+*/
+class UcbPropertiesChangeListener_Impl : public ::cppu::WeakImplHelper< XPropertiesChangeListener >
+{
+public:
+ UcbLockBytesRef m_xLockBytes;
+
+ explicit UcbPropertiesChangeListener_Impl( UcbLockBytesRef xRef )
+ : m_xLockBytes(std::move( xRef ))
+ {}
+
+ virtual void SAL_CALL disposing ( const EventObject &/*rEvent*/) override {}
+ virtual void SAL_CALL propertiesChange ( const Sequence<PropertyChangeEvent> &rEvent) override;
+};
+
+}
+
+void SAL_CALL UcbPropertiesChangeListener_Impl::propertiesChange ( const Sequence<PropertyChangeEvent> &rEvent)
+{
+ for (const auto& rPropChangeEvent : rEvent)
+ {
+ if (rPropChangeEvent.PropertyName == "DocumentHeader")
+ {
+ m_xLockBytes->SetStreamValid();
+ }
+ }
+}
+
+namespace {
+
+class Moderator
+ : public osl::Thread
+{
+ // usage restriction:
+ // It might be possible, that the call to the interactionhandler and/or
+ // progresshandler is done asynchronously, while the 'execute' simply
+ // returns. This would imply that these class must be refcounted!!!
+
+public:
+ /// @throws ContentCreationException
+ /// @throws RuntimeException
+ Moderator(
+ Reference < XContent > const & xContent,
+ Reference < XInteractionHandler > const & xInteract,
+ Command aArg
+ );
+
+ enum class ResultType {
+ NORESULT,
+
+ INTERACTIONREQUEST, // reply expected
+
+ INPUTSTREAM,
+ STREAM,
+
+ RESULT,
+ TIMEDOUT,
+ COMMANDABORTED,
+ COMMANDFAILED,
+ INTERACTIVEIO,
+ UNSUPPORTED,
+ GENERAL
+ };
+
+ class ConditionRes
+ : public salhelper::Condition
+ {
+ public:
+ ConditionRes(osl::Mutex& aMutex,Moderator& aModerator)
+ : salhelper::Condition(aMutex),
+ m_aModerator(aModerator)
+ {
+ }
+
+ protected:
+ bool applies() const override {
+ return m_aModerator.m_aResultType != ResultType::NORESULT;
+ }
+
+ private:
+ Moderator& m_aModerator;
+ };
+
+ struct Result {
+ ResultType type;
+ Any result;
+ IOErrorCode ioErrorCode;
+ };
+
+ Result getResult(const sal_uInt32 milliSec);
+
+ enum ReplyType {
+ NOREPLY,
+ EXIT,
+ REQUESTHANDLED
+ };
+
+ class ConditionRep
+ : public salhelper::Condition
+ {
+ public:
+ ConditionRep(osl::Mutex& aMutex,Moderator& aModerator)
+ : salhelper::Condition(aMutex),
+ m_aModerator(aModerator)
+ {
+ }
+
+ protected:
+ bool applies() const override {
+ return m_aModerator.m_aReplyType != NOREPLY;
+ }
+
+ private:
+ Moderator& m_aModerator;
+ };
+
+ void setReply(ReplyType);
+
+ void handle( const Reference<XInteractionRequest >& Request );
+
+ void setStream(const Reference< XStream >& aStream);
+ void setInputStream(const Reference<XInputStream> &rxInputStream);
+
+protected:
+ virtual void SAL_CALL run() override;
+ virtual void SAL_CALL onTerminated() override;
+
+private:
+ osl::Mutex m_aMutex;
+
+ friend class ConditionRes;
+
+ ConditionRes m_aRes;
+ ResultType m_aResultType;
+ IOErrorCode m_nIOErrorCode;
+ Any m_aResult;
+
+ friend class ConditionRep;
+
+ ConditionRep m_aRep;
+ ReplyType m_aReplyType;
+
+ Command m_aArg;
+ ::ucbhelper::Content m_aContent;
+};
+
+class ModeratorsActiveDataStreamer
+ : public ::cppu::WeakImplHelper<XActiveDataStreamer>
+{
+public:
+
+ explicit ModeratorsActiveDataStreamer(Moderator &theModerator);
+
+ // XActiveDataStreamer
+ virtual void SAL_CALL
+ setStream(
+ const Reference< XStream >& aStream
+ ) override;
+
+ virtual Reference<XStream> SAL_CALL getStream () override
+ {
+ std::scoped_lock aGuard(m_aMutex);
+ return m_xStream;
+ }
+
+private:
+ Moderator& m_aModerator;
+
+ std::mutex m_aMutex;
+ Reference<XStream> m_xStream;
+};
+
+class ModeratorsActiveDataSink
+ : public ::cppu::WeakImplHelper<XActiveDataSink>
+{
+public:
+
+ explicit ModeratorsActiveDataSink(Moderator &theModerator);
+
+ // XActiveDataSink.
+ virtual void SAL_CALL
+ setInputStream (
+ const Reference<XInputStream> &rxInputStream
+ ) override;
+
+ virtual Reference<XInputStream> SAL_CALL getInputStream() override
+ {
+ std::scoped_lock aGuard(m_aMutex);
+ return m_xStream;
+ }
+
+private:
+ Moderator& m_aModerator;
+ std::mutex m_aMutex;
+ Reference<XInputStream> m_xStream;
+};
+
+}
+
+ModeratorsActiveDataSink::ModeratorsActiveDataSink(Moderator &theModerator)
+ : m_aModerator(theModerator)
+{
+}
+
+// XActiveDataSink.
+void SAL_CALL
+ModeratorsActiveDataSink::setInputStream (
+ const Reference<XInputStream> &rxInputStream
+)
+{
+ m_aModerator.setInputStream(rxInputStream);
+ std::scoped_lock aGuard(m_aMutex);
+ m_xStream = rxInputStream;
+}
+
+ModeratorsActiveDataStreamer::ModeratorsActiveDataStreamer(
+ Moderator &theModerator
+)
+ : m_aModerator(theModerator)
+{
+}
+
+// XActiveDataStreamer.
+void SAL_CALL
+ModeratorsActiveDataStreamer::setStream (
+ const Reference<XStream> &rxStream
+)
+{
+ m_aModerator.setStream(rxStream);
+ std::scoped_lock aGuard(m_aMutex);
+ m_xStream = rxStream;
+}
+
+namespace {
+
+class ModeratorsInteractionHandler
+ : public ::cppu::WeakImplHelper<XInteractionHandler>
+{
+public:
+
+ explicit ModeratorsInteractionHandler(Moderator &theModerator);
+
+ virtual void SAL_CALL
+ handle( const Reference<XInteractionRequest >& Request ) override;
+
+private:
+
+ Moderator& m_aModerator;
+};
+
+}
+
+ModeratorsInteractionHandler::ModeratorsInteractionHandler(
+ Moderator &aModerator)
+ : m_aModerator(aModerator)
+{
+}
+
+void SAL_CALL
+ModeratorsInteractionHandler::handle(
+ const Reference<XInteractionRequest >& Request
+)
+{
+ // wakes up the mainthread
+ m_aModerator.handle(Request);
+}
+
+Moderator::Moderator(
+ Reference < XContent > const & xContent,
+ Reference < XInteractionHandler > const & xInteract,
+ Command aArg
+)
+ : m_aRes(m_aMutex,*this),
+ m_aResultType(ResultType::NORESULT),
+ m_nIOErrorCode(IOErrorCode_ABORT),
+ m_aRep(m_aMutex,*this),
+ m_aReplyType(NOREPLY),
+ m_aArg(std::move(aArg)),
+ m_aContent(
+ xContent,
+ new UcbTaskEnvironment(
+ xInteract.is() ? new ModeratorsInteractionHandler(*this) : nullptr,
+ nullptr),
+ comphelper::getProcessComponentContext())
+{
+ // now exchange the whole data sink stuff
+ // with a thread safe version
+
+ Reference<XInterface> *pxSink = nullptr;
+
+ PostCommandArgument2 aPostArg;
+ OpenCommandArgument2 aOpenArg;
+
+ int dec(2);
+ if(m_aArg.Argument >>= aPostArg) {
+ pxSink = &aPostArg.Sink;
+ dec = 0;
+ }
+ else if(m_aArg.Argument >>= aOpenArg) {
+ pxSink = &aOpenArg.Sink;
+ dec = 1;
+ }
+
+ if(dec ==2)
+ throw ContentCreationException();
+
+ Reference < XActiveDataSink > xActiveSink(*pxSink,UNO_QUERY);
+ if(xActiveSink.is())
+ pxSink->set( static_cast<cppu::OWeakObject*>(new ModeratorsActiveDataSink(*this)));
+
+ Reference<XActiveDataStreamer> xStreamer( *pxSink, UNO_QUERY );
+ if ( xStreamer.is() )
+ pxSink->set( static_cast<cppu::OWeakObject*>(new ModeratorsActiveDataStreamer(*this)));
+
+ if(dec == 0)
+ m_aArg.Argument <<= aPostArg;
+ else if(dec == 1)
+ m_aArg.Argument <<= aOpenArg;
+}
+
+Moderator::Result Moderator::getResult(const sal_uInt32 milliSec)
+{
+ Result ret;
+ try {
+ salhelper::ConditionWaiter aWaiter(m_aRes,milliSec);
+ ret.type = m_aResultType;
+ ret.result = m_aResult;
+ ret.ioErrorCode = m_nIOErrorCode;
+
+ // reset
+ m_aResultType = ResultType::NORESULT;
+ }
+ catch (const salhelper::ConditionWaiter::timedout&)
+ {
+ ret.type = ResultType::TIMEDOUT;
+ }
+
+ return ret;
+}
+
+void Moderator::setReply(ReplyType aReplyType )
+{
+ salhelper::ConditionModifier aMod(m_aRep);
+ m_aReplyType = aReplyType;
+}
+
+void Moderator::handle( const Reference<XInteractionRequest >& Request )
+{
+ ReplyType aReplyType;
+
+ do {
+ {
+ salhelper::ConditionModifier aMod(m_aRes);
+ m_aResultType = ResultType::INTERACTIONREQUEST;
+ m_aResult <<= Request;
+ }
+
+ {
+ salhelper::ConditionWaiter aWait(m_aRep);
+ aReplyType = m_aReplyType;
+
+ // reset
+ m_aReplyType = NOREPLY;
+ }
+
+ if(aReplyType == EXIT) {
+ const Sequence<Reference<XInteractionContinuation> > aSeq(
+ Request->getContinuations());
+ for(const auto& rContinuation : aSeq) {
+ Reference<XInteractionAbort> aRef(rContinuation,UNO_QUERY);
+ if(aRef.is()) {
+ aRef->select();
+ }
+ }
+
+ // resignal the exit condition
+ setReply(EXIT);
+ break;
+ }
+ } while(aReplyType != REQUESTHANDLED);
+}
+
+void Moderator::setStream(const Reference< XStream >& aStream)
+{
+ {
+ salhelper::ConditionModifier aMod(m_aRes);
+ m_aResultType = ResultType::STREAM;
+ m_aResult <<= aStream;
+ }
+ ReplyType aReplyType;
+ {
+ salhelper::ConditionWaiter aWait(m_aRep);
+ aReplyType = m_aReplyType;
+ m_aReplyType = NOREPLY;
+ }
+ if(aReplyType == EXIT)
+ setReply(EXIT);
+}
+
+void Moderator::setInputStream(const Reference<XInputStream> &rxInputStream)
+{
+ {
+ salhelper::ConditionModifier aMod(m_aRes);
+ m_aResultType = ResultType::INPUTSTREAM;
+ m_aResult <<= rxInputStream;
+ }
+ ReplyType aReplyType;
+ {
+ salhelper::ConditionWaiter aWait(m_aRep);
+ aReplyType = m_aReplyType;
+ m_aReplyType = NOREPLY;
+ }
+ if(aReplyType == EXIT)
+ setReply(EXIT);
+}
+
+void SAL_CALL Moderator::run()
+{
+ osl_setThreadName("utl::Moderator");
+
+ ResultType aResultType;
+ Any aResult;
+ IOErrorCode nIOErrorCode = IOErrorCode_ABORT;
+
+ try
+ {
+ aResult = m_aContent.executeCommand(m_aArg.Name,m_aArg.Argument);
+ aResultType = ResultType::RESULT;
+ }
+ catch (const CommandAbortedException&)
+ {
+ aResultType = ResultType::COMMANDABORTED;
+ }
+ catch (const CommandFailedException&)
+ {
+ aResultType = ResultType::COMMANDFAILED;
+ }
+ catch (const InteractiveIOException& r)
+ {
+ nIOErrorCode = r.Code;
+ aResultType = ResultType::INTERACTIVEIO;
+ }
+ catch (const UnsupportedDataSinkException &)
+ {
+ aResultType = ResultType::UNSUPPORTED;
+ }
+ catch (const Exception&)
+ {
+ aResultType = ResultType::GENERAL;
+ }
+
+ {
+ salhelper::ConditionModifier aMod(m_aRes);
+ m_aResultType = aResultType;
+ m_aResult = aResult;
+ m_nIOErrorCode = nIOErrorCode;
+ }
+}
+
+void SAL_CALL Moderator::onTerminated()
+{
+ {
+ salhelper::ConditionWaiter aWaiter(m_aRep);
+ }
+ delete this;
+}
+
+/**
+ Function for opening UCB contents synchronously,
+ but with handled timeout;
+*/
+static bool UCBOpenContentSync_(
+ const UcbLockBytesRef& xLockBytes,
+ const Reference < XContent >& xContent,
+ const Command& rArg,
+ const Reference < XInterface >& xSink,
+ const Reference < XInteractionHandler >& xInteract );
+
+static bool UCBOpenContentSync(
+ const UcbLockBytesRef& xLockBytes,
+ Reference < XContent > const & xContent,
+ const Command& rArg,
+ const Reference < XInterface >& xSink,
+ Reference < XInteractionHandler > const & xInteract )
+{
+ // http protocol must be handled in a special way:
+ // during the opening process the input stream may change
+ // only the last inputstream after notifying the document
+ // headers is valid
+
+ Reference<XContentIdentifier> xContId(
+ xContent.is() ? xContent->getIdentifier() : nullptr );
+
+ OUString aScheme;
+ if(xContId.is())
+ aScheme = xContId->getContentProviderScheme();
+
+ // now determine whether we use a timeout or not;
+ if( ! aScheme.equalsIgnoreAsciiCase("http") &&
+ ! aScheme.equalsIgnoreAsciiCase("https") &&
+ ! aScheme.equalsIgnoreAsciiCase("vnd.sun.star.webdav") &&
+ ! aScheme.equalsIgnoreAsciiCase("vnd.sun.star.webdavs") &&
+ ! aScheme.equalsIgnoreAsciiCase("ftp"))
+ return UCBOpenContentSync_(
+ xLockBytes,xContent,rArg,xSink,xInteract);
+
+ if ( !aScheme.equalsIgnoreAsciiCase( "http" ) &&
+ !aScheme.equalsIgnoreAsciiCase( "https" ) )
+ xLockBytes->SetStreamValid();
+
+ Reference< XPropertiesChangeListener > xListener;
+ Reference< XPropertiesChangeNotifier > xProps(xContent,UNO_QUERY);
+ if(xProps.is()) {
+ xListener =
+ new UcbPropertiesChangeListener_Impl(xLockBytes);
+ xProps->addPropertiesChangeListener(
+ Sequence< OUString >(),
+ xListener);
+ }
+
+ bool bException(false);
+ bool bAborted(false);
+ bool bResultAchieved(false);
+
+ Moderator* pMod = nullptr;
+ try
+ {
+ pMod = new Moderator(xContent,xInteract,rArg);
+ pMod->create();
+ //TODO: a protocol is missing how to join with the launched thread before exit(3), to
+ // ensure the thread is no longer relying on any infrastructure while that
+ // infrastructure is being shut down in atexit handlers
+ }
+ catch (const ContentCreationException&)
+ {
+ bResultAchieved = bException = true;
+ xLockBytes->SetError( ERRCODE_IO_GENERAL );
+ }
+
+ sal_uInt32 nTimeout(5000); // initially 5000 milliSec
+ while(!bResultAchieved) {
+
+ // try to get the result for with timeout
+ Moderator::Result res = pMod->getResult(nTimeout);
+
+ switch(res.type) {
+ case Moderator::ResultType::STREAM:
+ {
+ Reference<XStream> result;
+ if(res.result >>= result) {
+ Reference < XActiveDataStreamer > xStreamer(
+ xSink, UNO_QUERY
+ );
+
+ if(xStreamer.is())
+ xStreamer->setStream(result);
+ }
+ pMod->setReply(Moderator::REQUESTHANDLED);
+ break;
+ }
+ case Moderator::ResultType::INPUTSTREAM:
+ {
+ Reference<XInputStream> result;
+ res.result >>= result;
+ Reference < XActiveDataSink > xActiveSink(
+ xSink, UNO_QUERY
+ );
+
+ if(xActiveSink.is())
+ xActiveSink->setInputStream(result);
+ pMod->setReply(Moderator::REQUESTHANDLED);
+ break;
+ }
+ case Moderator::ResultType::TIMEDOUT:
+ {
+ Reference<XInteractionRetry> xRet;
+ if(xInteract.is()) {
+ InteractiveNetworkConnectException aExcep;
+ INetURLObject aURL(
+ xContId.is() ?
+ xContId->getContentIdentifier() :
+ OUString() );
+ aExcep.Server = aURL.GetHost();
+ aExcep.Classification = InteractionClassification_ERROR;
+ aExcep.Message = "server not responding after five seconds";
+ Any request;
+ request <<= aExcep;
+ rtl::Reference<ucbhelper::InteractionRequest> xIR =
+ new ucbhelper::InteractionRequest(request);
+ rtl::Reference<ucbhelper::InteractionRetry> retryP =
+ new ucbhelper::InteractionRetry(xIR.get());
+ rtl::Reference<ucbhelper::InteractionAbort> abortP =
+ new ucbhelper::InteractionAbort(xIR.get());
+ Sequence<Reference<XInteractionContinuation> > aSeq { retryP, abortP };
+
+ xIR->setContinuations(aSeq);
+ xInteract->handle(xIR);
+ rtl::Reference< ucbhelper::InteractionContinuation > ref
+ = xIR->getSelection();
+ if(ref.is()) {
+ Reference<XInterface> xInt(ref);
+ xRet.set(xInt,UNO_QUERY);
+ }
+ }
+
+ if(!xRet.is()) {
+ bAborted = true;
+ xLockBytes->SetError(ERRCODE_ABORT);
+ }
+
+ break;
+ }
+ case Moderator::ResultType::INTERACTIONREQUEST:
+ {
+ Reference<XInteractionRequest> Request;
+ res.result >>= Request;
+ xInteract->handle(Request);
+ pMod->setReply(Moderator::REQUESTHANDLED);
+ break;
+ }
+ case Moderator::ResultType::RESULT:
+ {
+ bResultAchieved = true;
+ break;
+ }
+ case Moderator::ResultType::COMMANDABORTED:
+ {
+ bAborted = true;
+ xLockBytes->SetError( ERRCODE_ABORT );
+ break;
+ }
+ case Moderator::ResultType::COMMANDFAILED:
+ {
+ bAborted = true;
+ xLockBytes->SetError( ERRCODE_ABORT );
+ break;
+ }
+ case Moderator::ResultType::INTERACTIVEIO:
+ {
+ bException = true;
+ if ( res.ioErrorCode == IOErrorCode_ACCESS_DENIED ||
+ res.ioErrorCode == IOErrorCode_LOCKING_VIOLATION )
+ xLockBytes->SetError( ERRCODE_IO_ACCESSDENIED );
+ else if ( res.ioErrorCode == IOErrorCode_NOT_EXISTING )
+ xLockBytes->SetError( ERRCODE_IO_NOTEXISTS );
+ else if ( res.ioErrorCode == IOErrorCode_CANT_READ )
+ xLockBytes->SetError( ERRCODE_IO_CANTREAD );
+ else
+ xLockBytes->SetError( ERRCODE_IO_GENERAL );
+ break;
+ }
+ case Moderator::ResultType::UNSUPPORTED:
+ {
+ bException = true;
+ xLockBytes->SetError( ERRCODE_IO_NOTSUPPORTED );
+ break;
+ }
+ default:
+ {
+ bException = true;
+ xLockBytes->SetError( ERRCODE_IO_GENERAL );
+ break;
+ }
+ }
+
+ bResultAchieved |= bException;
+ bResultAchieved |= bAborted;
+ if(nTimeout == 5000) nTimeout *= 2;
+ }
+
+ if(pMod) pMod->setReply(Moderator::EXIT);
+
+ if ( bAborted || bException )
+ {
+ Reference < XActiveDataSink > xActiveSink( xSink, UNO_QUERY );
+ if ( xActiveSink.is() )
+ xActiveSink->setInputStream( Reference < XInputStream >() );
+
+ Reference < XActiveDataStreamer > xStreamer( xSink, UNO_QUERY );
+ if ( xStreamer.is() )
+ xStreamer->setStream( Reference < XStream >() );
+ }
+
+ Reference < XActiveDataControl > xControl( xSink, UNO_QUERY );
+ if ( xControl.is() )
+ xControl->terminate();
+
+ if ( xProps.is() )
+ xProps->removePropertiesChangeListener(
+ Sequence< OUString >(),
+ xListener );
+
+ return ( bAborted || bException );
+}
+
+/**
+ Function for opening UCB contents synchronously
+ */
+static bool UCBOpenContentSync_(
+ const UcbLockBytesRef& xLockBytes,
+ const Reference < XContent >& xContent,
+ const Command& rArg,
+ const Reference < XInterface >& xSink,
+ const Reference < XInteractionHandler >& xInteract )
+{
+ ::ucbhelper::Content aContent(
+ xContent, new UcbTaskEnvironment( xInteract, nullptr ),
+ comphelper::getProcessComponentContext() );
+ Reference < XContentIdentifier > xIdent = xContent->getIdentifier();
+ OUString aScheme = xIdent->getContentProviderScheme();
+
+ // http protocol must be handled in a special way: during the opening process the input stream may change
+ // only the last inputstream after notifying the document headers is valid
+ if ( !aScheme.equalsIgnoreAsciiCase("http") )
+ xLockBytes->SetStreamValid();
+
+ Reference< XPropertiesChangeListener > xListener = new UcbPropertiesChangeListener_Impl( xLockBytes );
+ Reference< XPropertiesChangeNotifier > xProps ( xContent, UNO_QUERY );
+ if ( xProps.is() )
+ xProps->addPropertiesChangeListener( Sequence< OUString >(), xListener );
+
+ bool bException = false;
+ bool bAborted = false;
+
+ try
+ {
+ aContent.executeCommand( rArg.Name, rArg.Argument );
+ }
+ catch (const CommandAbortedException&)
+ {
+ bAborted = true;
+ xLockBytes->SetError( ERRCODE_ABORT );
+ }
+ catch (const CommandFailedException&)
+ {
+ bAborted = true;
+ xLockBytes->SetError( ERRCODE_ABORT );
+ }
+ catch (const InteractiveIOException& r)
+ {
+ bException = true;
+ if ( r.Code == IOErrorCode_ACCESS_DENIED || r.Code == IOErrorCode_LOCKING_VIOLATION )
+ xLockBytes->SetError( ERRCODE_IO_ACCESSDENIED );
+ else if ( r.Code == IOErrorCode_NOT_EXISTING )
+ xLockBytes->SetError( ERRCODE_IO_NOTEXISTS );
+ else if ( r.Code == IOErrorCode_CANT_READ )
+ xLockBytes->SetError( ERRCODE_IO_CANTREAD );
+ else
+ xLockBytes->SetError( ERRCODE_IO_GENERAL );
+ }
+ catch (const UnsupportedDataSinkException&)
+ {
+ bException = true;
+ xLockBytes->SetError( ERRCODE_IO_NOTSUPPORTED );
+ }
+ catch (const Exception&)
+ {
+ bException = true;
+ xLockBytes->SetError( ERRCODE_IO_GENERAL );
+ }
+
+ if ( bAborted || bException )
+ {
+ Reference < XActiveDataSink > xActiveSink( xSink, UNO_QUERY );
+ if ( xActiveSink.is() )
+ xActiveSink->setInputStream( Reference < XInputStream >() );
+
+ Reference < XActiveDataStreamer > xStreamer( xSink, UNO_QUERY );
+ if ( xStreamer.is() )
+ xStreamer->setStream( Reference < XStream >() );
+ }
+
+ Reference < XActiveDataControl > xControl( xSink, UNO_QUERY );
+ if ( xControl.is() )
+ xControl->terminate();
+
+ if ( xProps.is() )
+ xProps->removePropertiesChangeListener( Sequence< OUString >(), xListener );
+
+ return ( bAborted || bException );
+}
+
+UcbLockBytes::UcbLockBytes()
+ : m_nError( ERRCODE_NONE )
+ , m_bTerminated (false)
+ , m_bDontClose( false )
+ , m_bStreamValid (false)
+{
+ SetSynchronMode();
+}
+
+UcbLockBytes::~UcbLockBytes()
+{
+ if ( !m_bDontClose )
+ {
+ if ( m_xInputStream.is() )
+ {
+ try
+ {
+ m_xInputStream->closeInput();
+ }
+ catch (const RuntimeException&)
+ {
+ }
+ catch (const IOException&)
+ {
+ }
+ }
+ }
+
+ if ( m_xInputStream.is() || !m_xOutputStream.is() )
+ return;
+
+ try
+ {
+ m_xOutputStream->closeOutput();
+ }
+ catch (const RuntimeException&)
+ {
+ }
+ catch (const IOException&)
+ {
+ }
+}
+
+Reference < XInputStream > UcbLockBytes::getInputStream()
+{
+ osl::MutexGuard aGuard( m_aMutex );
+ m_bDontClose = true;
+ return m_xInputStream;
+}
+
+void UcbLockBytes::setStream( const Reference<XStream>& aStream )
+{
+ osl::MutexGuard aGuard( m_aMutex );
+ if ( aStream.is() )
+ {
+ m_xOutputStream = aStream->getOutputStream();
+ setInputStream( aStream->getInputStream(), false );
+ m_xSeekable.set( aStream, UNO_QUERY );
+ }
+ else
+ {
+ m_xOutputStream.clear();
+ setInputStream( Reference < XInputStream >() );
+ }
+}
+
+bool UcbLockBytes::setInputStream( const Reference<XInputStream> &rxInputStream, bool bSetXSeekable )
+{
+ bool bRet = false;
+
+ try
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+
+ if ( !m_bDontClose && m_xInputStream.is() )
+ m_xInputStream->closeInput();
+
+ m_xInputStream = rxInputStream;
+
+ if( bSetXSeekable )
+ {
+ m_xSeekable.set( rxInputStream, UNO_QUERY );
+ if( !m_xSeekable.is() && rxInputStream.is() )
+ {
+ Reference < XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ Reference< XOutputStream > rxTempOut( css::io::TempFile::create(xContext), UNO_QUERY_THROW );
+
+ ::comphelper::OStorageHelper::CopyInputToOutput( rxInputStream, rxTempOut );
+ m_xInputStream.set( rxTempOut, UNO_QUERY );
+ m_xSeekable.set( rxTempOut, UNO_QUERY );
+ }
+ }
+
+ bRet = m_xInputStream.is();
+ }
+ catch (const Exception&)
+ {
+ }
+
+ if ( m_bStreamValid && m_xInputStream.is() )
+ m_aInitialized.set();
+
+ return bRet;
+}
+
+void UcbLockBytes::SetStreamValid()
+{
+ m_bStreamValid = true;
+ if ( m_xInputStream.is() )
+ m_aInitialized.set();
+}
+
+void UcbLockBytes::terminate()
+{
+ m_bTerminated = true;
+ m_aInitialized.set();
+ m_aTerminated.set();
+
+ if ( GetError() == ERRCODE_NONE && !m_xInputStream.is() )
+ {
+ OSL_FAIL("No InputStream, but no error set!" );
+ SetError( ERRCODE_IO_NOTEXISTS );
+ }
+}
+
+ErrCode UcbLockBytes::ReadAt(sal_uInt64 const nPos,
+ void *pBuffer, std::size_t nCount, std::size_t *pRead) const
+{
+ if ( IsSynchronMode() )
+ {
+ UcbLockBytes* pThis = const_cast < UcbLockBytes* >( this );
+ pThis->m_aInitialized.wait();
+ }
+
+ Reference <XInputStream> xStream = getInputStream();
+ if ( !xStream.is() )
+ {
+ if ( m_bTerminated )
+ return ERRCODE_IO_CANTREAD;
+ else
+ return ERRCODE_IO_PENDING;
+ }
+
+ if ( pRead )
+ *pRead = 0;
+
+ Reference <XSeekable> xSeekable = getSeekable();
+ if ( !xSeekable.is() )
+ return ERRCODE_IO_CANTREAD;
+
+ try
+ {
+ xSeekable->seek( nPos );
+ }
+ catch (const IOException&)
+ {
+ return ERRCODE_IO_CANTSEEK;
+ }
+ catch (const css::lang::IllegalArgumentException&)
+ {
+ return ERRCODE_IO_CANTSEEK;
+ }
+
+ sal_Int32 nSize;
+
+ if(nCount > 0x7FFFFFFF)
+ {
+ nCount = 0x7FFFFFFF;
+ }
+ try
+ {
+ if ( !m_bTerminated && !IsSynchronMode() )
+ {
+ sal_uInt64 nLen = xSeekable->getLength();
+ if ( nPos + nCount > nLen )
+ return ERRCODE_IO_PENDING;
+ }
+
+ Reference< css::lang::XUnoTunnel > xTunnel( xStream, UNO_QUERY );
+ comphelper::ByteReader* pByteReader = nullptr;
+ if (xTunnel)
+ pByteReader = reinterpret_cast< comphelper::ByteReader* >( xTunnel->getSomething( comphelper::ByteReader::getUnoTunnelId() ) );
+
+ if (pByteReader)
+ {
+ nSize = pByteReader->readSomeBytes( static_cast<sal_Int8*>(pBuffer), sal_Int32(nCount) );
+ }
+ else
+ {
+ Sequence<sal_Int8> aData;
+ nSize = xStream->readBytes( aData, sal_Int32(nCount) );
+ memcpy (pBuffer, aData.getConstArray(), nSize);
+ }
+ }
+ catch (const IOException&)
+ {
+ return ERRCODE_IO_CANTREAD;
+ }
+
+ if (pRead)
+ *pRead = static_cast<std::size_t>(nSize);
+
+ return ERRCODE_NONE;
+}
+
+ErrCode UcbLockBytes::WriteAt(sal_uInt64 const nPos, const void *pBuffer,
+ std::size_t nCount, std::size_t *pWritten)
+{
+ if ( pWritten )
+ *pWritten = 0;
+
+ DBG_ASSERT( IsSynchronMode(), "Writing is only possible in SynchronMode!" );
+ DBG_ASSERT( m_aInitialized.check(), "Writing bevor stream is ready!" );
+
+ Reference <XSeekable> xSeekable = getSeekable();
+ Reference <XOutputStream> xOutputStream = getOutputStream();
+ if ( !xOutputStream.is() || !xSeekable.is() )
+ return ERRCODE_IO_CANTWRITE;
+
+ try
+ {
+ xSeekable->seek( nPos );
+ }
+ catch (const IOException&)
+ {
+ return ERRCODE_IO_CANTSEEK;
+ }
+
+ sal_Int8 const * pData = static_cast<sal_Int8 const *>(pBuffer);
+ Sequence<sal_Int8> aData( pData, nCount );
+ try
+ {
+ xOutputStream->writeBytes( aData );
+ if ( pWritten )
+ *pWritten = nCount;
+ }
+ catch (const Exception&)
+ {
+ return ERRCODE_IO_CANTWRITE;
+ }
+
+ return ERRCODE_NONE;
+}
+
+ErrCode UcbLockBytes::Flush() const
+{
+ Reference <XOutputStream > xOutputStream = getOutputStream();
+ if ( !xOutputStream.is() )
+ return ERRCODE_IO_CANTWRITE;
+
+ try
+ {
+ xOutputStream->flush();
+ }
+ catch (const Exception&)
+ {
+ return ERRCODE_IO_CANTWRITE;
+ }
+
+ return ERRCODE_NONE;
+}
+
+ErrCode UcbLockBytes::SetSize (sal_uInt64 const nNewSize)
+{
+ SvLockBytesStat aStat;
+ Stat( &aStat );
+ std::size_t nSize = aStat.nSize;
+
+ if ( nSize > nNewSize )
+ {
+ Reference < XTruncate > xTrunc( getOutputStream(), UNO_QUERY );
+ if ( xTrunc.is() )
+ {
+ xTrunc->truncate();
+ nSize = 0;
+ }
+ else {
+ SAL_INFO("unotools.ucbhelper", "Not truncable!");
+ }
+ }
+
+ if ( nSize < nNewSize )
+ {
+ std::size_t nDiff = nNewSize-nSize, nCount=0;
+ std::unique_ptr<sal_uInt8[]> pBuffer(new sal_uInt8[ nDiff ]);
+ memset(pBuffer.get(), 0, nDiff); // initialize for enhanced security
+ WriteAt( nSize, pBuffer.get(), nDiff, &nCount );
+ if ( nCount != nDiff )
+ return ERRCODE_IO_CANTWRITE;
+ }
+
+ return ERRCODE_NONE;
+}
+
+ErrCode UcbLockBytes::Stat( SvLockBytesStat *pStat ) const
+{
+ if ( IsSynchronMode() )
+ {
+ UcbLockBytes* pThis = const_cast < UcbLockBytes* >( this );
+ pThis->m_aInitialized.wait();
+ }
+
+ if (!pStat)
+ return ERRCODE_IO_INVALIDPARAMETER;
+
+ Reference <XInputStream> xStream = getInputStream();
+ Reference <XSeekable> xSeekable = getSeekable();
+
+ if ( !xStream.is() )
+ {
+ if ( m_bTerminated )
+ return ERRCODE_IO_INVALIDACCESS;
+ else
+ return ERRCODE_IO_PENDING;
+ }
+ else if( !xSeekable.is() )
+ return ERRCODE_IO_CANTTELL;
+
+ try
+ {
+ pStat->nSize = sal_uLong(xSeekable->getLength());
+ }
+ catch (const IOException&)
+ {
+ return ERRCODE_IO_CANTTELL;
+ }
+
+ return ERRCODE_NONE;
+}
+
+UcbLockBytesRef UcbLockBytes::CreateInputLockBytes( const Reference< XInputStream >& xInputStream )
+{
+ if( !xInputStream.is() )
+ return nullptr;
+
+ UcbLockBytesRef xLockBytes = new UcbLockBytes;
+ xLockBytes->setDontClose();
+ xLockBytes->setInputStream( xInputStream );
+ xLockBytes->terminate();
+ return xLockBytes;
+}
+
+UcbLockBytesRef UcbLockBytes::CreateLockBytes( const Reference< XStream >& xStream )
+{
+ if( !xStream.is() )
+ return nullptr;
+
+ UcbLockBytesRef xLockBytes = new UcbLockBytes;
+ xLockBytes->setDontClose();
+ xLockBytes->setStream( xStream );
+ xLockBytes->terminate();
+ return xLockBytes;
+}
+
+UcbLockBytesRef UcbLockBytes::CreateLockBytes( const Reference < XContent >& xContent, const Sequence < PropertyValue >& rProps,
+ StreamMode eOpenMode, const Reference < XInteractionHandler >& xInteractionHandler )
+{
+ if( !xContent.is() )
+ return nullptr;
+
+ UcbLockBytesRef xLockBytes = new UcbLockBytes;
+ xLockBytes->SetSynchronMode();
+ Reference< XActiveDataControl > xSink;
+ if ( eOpenMode & StreamMode::WRITE )
+ xSink = new UcbStreamer_Impl(xLockBytes.get());
+ else
+ xSink = new UcbDataSink_Impl(xLockBytes.get());
+
+ if ( rProps.hasElements() )
+ {
+ Reference < XCommandProcessor > xProcessor( xContent, UNO_QUERY );
+ Command aCommand;
+ aCommand.Name = "setPropertyValues";
+ aCommand.Handle = -1; /* unknown */
+ aCommand.Argument <<= rProps;
+ xProcessor->execute( aCommand, 0, Reference < XCommandEnvironment >() );
+ }
+
+ OpenCommandArgument2 aArgument;
+ aArgument.Sink = xSink;
+ aArgument.Mode = OpenMode::DOCUMENT;
+
+ Command aCommand;
+ aCommand.Name = "open";
+ aCommand.Argument <<= aArgument;
+
+ bool bError = UCBOpenContentSync( xLockBytes,
+ xContent,
+ aCommand,
+ xSink,
+ xInteractionHandler );
+
+ if ( xLockBytes->GetError() == ERRCODE_NONE && ( bError || !xLockBytes->getInputStream().is() ) )
+ {
+ OSL_FAIL("No InputStream, but no error set!" );
+ xLockBytes->SetError( ERRCODE_IO_GENERAL );
+ }
+
+ return xLockBytes;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/ucbhelper/ucblockbytes.hxx b/unotools/source/ucbhelper/ucblockbytes.hxx
new file mode 100644
index 000000000..ca4eac2ab
--- /dev/null
+++ b/unotools/source/ucbhelper/ucblockbytes.hxx
@@ -0,0 +1,139 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <com/sun/star/uno/Reference.hxx>
+
+#include <osl/conditn.hxx>
+#include <osl/mutex.hxx>
+#include <tools/stream.hxx>
+#include <vcl/errcode.hxx>
+
+namespace com
+{
+ namespace sun
+ {
+ namespace star
+ {
+ namespace task
+ {
+ class XInteractionHandler;
+ }
+ namespace io
+ {
+ class XStream;
+ class XInputStream;
+ class XOutputStream;
+ class XSeekable;
+ }
+ namespace ucb
+ {
+ class XContent;
+ }
+ namespace beans
+ {
+ struct PropertyValue;
+ }
+ }
+ }
+}
+
+namespace utl
+{
+class UcbLockBytes;
+typedef tools::SvRef<UcbLockBytes> UcbLockBytesRef;
+
+class UcbLockBytes : public SvLockBytes
+{
+ osl::Condition m_aInitialized;
+ osl::Condition m_aTerminated;
+ osl::Mutex m_aMutex;
+
+ css::uno::Reference < css::io::XInputStream > m_xInputStream;
+ css::uno::Reference < css::io::XOutputStream > m_xOutputStream;
+ css::uno::Reference < css::io::XSeekable > m_xSeekable;
+
+ ErrCode m_nError;
+
+ bool m_bTerminated;
+ bool m_bDontClose;
+ bool m_bStreamValid;
+
+ UcbLockBytes();
+protected:
+ virtual ~UcbLockBytes() override;
+
+public:
+ // properties: Referer, PostMimeType
+ static UcbLockBytesRef CreateLockBytes( const css::uno::Reference < css::ucb::XContent >& xContent,
+ const css::uno::Sequence < css::beans::PropertyValue >& rProps,
+ StreamMode eMode,
+ const css::uno::Reference < css::task::XInteractionHandler >& xInter );
+
+ static UcbLockBytesRef CreateInputLockBytes( const css::uno::Reference < css::io::XInputStream >& xContent );
+ static UcbLockBytesRef CreateLockBytes( const css::uno::Reference < css::io::XStream >& xContent );
+
+ // SvLockBytes
+ virtual ErrCode ReadAt(sal_uInt64 nPos, void *pBuffer, std::size_t nCount, std::size_t *pRead) const override;
+ virtual ErrCode WriteAt(sal_uInt64, const void*, std::size_t, std::size_t *pWritten) override;
+ virtual ErrCode Flush() const override;
+ virtual ErrCode SetSize(sal_uInt64) override;
+ virtual ErrCode Stat ( SvLockBytesStat *pStat ) const override;
+
+ void SetError( ErrCode nError )
+ { m_nError = nError; }
+
+ ErrCode const & GetError() const
+ { return m_nError; }
+
+ // calling this method delegates the responsibility to call closeinput to the caller!
+ css::uno::Reference < css::io::XInputStream > getInputStream();
+
+ bool setInputStream( const css::uno::Reference < css::io::XInputStream > &rxInputStream,
+ bool bSetXSeekable = true );
+ void setStream( const css::uno::Reference < css::io::XStream > &rxStream );
+ void terminate();
+
+ css::uno::Reference < css::io::XInputStream > getInputStream() const
+ {
+ osl::MutexGuard aGuard( const_cast< UcbLockBytes* >(this)->m_aMutex );
+ return m_xInputStream;
+ }
+
+ css::uno::Reference < css::io::XOutputStream > getOutputStream() const
+ {
+ osl::MutexGuard aGuard( const_cast< UcbLockBytes* >(this)->m_aMutex );
+ return m_xOutputStream;
+ }
+
+ css::uno::Reference < css::io::XSeekable > getSeekable() const
+ {
+ osl::MutexGuard aGuard( const_cast< UcbLockBytes* >(this)->m_aMutex );
+ return m_xSeekable;
+ }
+
+ void setDontClose()
+ { m_bDontClose = true; }
+
+ void SetStreamValid();
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/ucbhelper/ucbstreamhelper.cxx b/unotools/source/ucbhelper/ucbstreamhelper.cxx
new file mode 100644
index 000000000..e3437a864
--- /dev/null
+++ b/unotools/source/ucbhelper/ucbstreamhelper.cxx
@@ -0,0 +1,237 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <rtl/ustring.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/InsertCommandArgument.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+#include <comphelper/simplefileaccessinteraction.hxx>
+#include <ucbhelper/content.hxx>
+#include <unotools/streamwrap.hxx>
+#include "ucblockbytes.hxx"
+
+namespace com::sun::star::ucb { class XCommandEnvironment; }
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::ucb;
+using namespace ::com::sun::star::task;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+
+namespace utl
+{
+
+static std::unique_ptr<SvStream> lcl_CreateStream( const OUString& rFileName, StreamMode eOpenMode,
+ const Reference < XInteractionHandler >& xInteractionHandler,
+ bool bEnsureFileExists )
+{
+ std::unique_ptr<SvStream> pStream;
+ UcbLockBytesRef xLockBytes;
+ if ( eOpenMode & StreamMode::WRITE )
+ {
+ bool bTruncate = bool( eOpenMode & StreamMode::TRUNC );
+ if ( bTruncate )
+ {
+ try
+ {
+ // truncate is implemented with deleting the original file
+ ::ucbhelper::Content aCnt(
+ rFileName, Reference < XCommandEnvironment >(),
+ comphelper::getProcessComponentContext() );
+ aCnt.executeCommand( "delete", css::uno::Any( true ) );
+ }
+
+ catch ( const CommandAbortedException& )
+ {
+ // couldn't truncate/delete
+ }
+ catch ( const ContentCreationException& )
+ {
+ }
+ catch ( const Exception& )
+ {
+ }
+ }
+
+ if ( bEnsureFileExists || bTruncate )
+ {
+ try
+ {
+ // make sure that the desired file exists before trying to open
+ SvMemoryStream aStream(0,0);
+ rtl::Reference<::utl::OInputStreamWrapper> xInput = new ::utl::OInputStreamWrapper( aStream );
+
+ ::ucbhelper::Content aContent(
+ rFileName, Reference < XCommandEnvironment >(),
+ comphelper::getProcessComponentContext() );
+ InsertCommandArgument aInsertArg;
+ aInsertArg.Data = xInput;
+
+ aInsertArg.ReplaceExisting = false;
+ Any aCmdArg;
+ aCmdArg <<= aInsertArg;
+ aContent.executeCommand( "insert", aCmdArg );
+ }
+
+ // it is NOT an error when the stream already exists and no truncation was desired
+ catch ( const CommandAbortedException& )
+ {
+ // currently never an error is detected !
+ }
+ catch ( const ContentCreationException& )
+ {
+ }
+ catch ( const Exception& )
+ {
+ }
+ }
+ }
+
+ try
+ {
+ // create LockBytes using UCB
+ ::ucbhelper::Content aContent(
+ rFileName, Reference < XCommandEnvironment >(),
+ comphelper::getProcessComponentContext() );
+ xLockBytes = UcbLockBytes::CreateLockBytes( aContent.get(), Sequence < PropertyValue >(),
+ eOpenMode, xInteractionHandler );
+ if ( xLockBytes.is() )
+ {
+ pStream.reset( new SvStream( xLockBytes.get() ) );
+ pStream->SetBufferSize( 4096 );
+ pStream->SetError( xLockBytes->GetError() );
+ }
+ }
+ catch ( const CommandAbortedException& )
+ {
+ }
+ catch ( const ContentCreationException& )
+ {
+ }
+ catch ( const Exception& )
+ {
+ }
+
+ return pStream;
+}
+
+std::unique_ptr<SvStream> UcbStreamHelper::CreateStream(const OUString& rFileName, StreamMode eOpenMode, css::uno::Reference<css::awt::XWindow> xParentWin)
+{
+ // related tdf#99312
+ // create a specialized interaction handler to manages Web certificates and Web credentials when needed
+ Reference< XInteractionHandler > xIH(
+ css::task::InteractionHandler::createWithParent(comphelper::getProcessComponentContext(), xParentWin));
+ Reference<XInteractionHandler> xIHScoped(new comphelper::SimpleFileAccessInteraction(xIH));
+
+ return lcl_CreateStream( rFileName, eOpenMode, xIHScoped, true /* bEnsureFileExists */ );
+}
+
+std::unique_ptr<SvStream> UcbStreamHelper::CreateStream(const OUString& rFileName, StreamMode eOpenMode,
+ bool bFileExists, css::uno::Reference<css::awt::XWindow> xParentWin)
+{
+ // related tdf#99312
+ // create a specialized interaction handler to manages Web certificates and Web credentials when needed
+ Reference< XInteractionHandler > xIH(
+ css::task::InteractionHandler::createWithParent(comphelper::getProcessComponentContext(), xParentWin));
+ Reference<XInteractionHandler> xIHScoped(new comphelper::SimpleFileAccessInteraction(xIH));
+ return lcl_CreateStream( rFileName, eOpenMode, xIHScoped,!bFileExists );
+}
+
+
+std::unique_ptr<SvStream> UcbStreamHelper::CreateStream( const Reference < XInputStream >& xStream )
+{
+ std::unique_ptr<SvStream> pStream;
+ UcbLockBytesRef xLockBytes = UcbLockBytes::CreateInputLockBytes( xStream );
+ if ( xLockBytes.is() )
+ {
+ pStream.reset( new SvStream( xLockBytes.get() ) );
+ pStream->SetBufferSize( 4096 );
+ pStream->SetError( xLockBytes->GetError() );
+ }
+
+ return pStream;
+}
+
+std::unique_ptr<SvStream> UcbStreamHelper::CreateStream( const Reference < XStream >& xStream )
+{
+ std::unique_ptr<SvStream> pStream;
+ if ( xStream->getOutputStream().is() )
+ {
+ UcbLockBytesRef xLockBytes = UcbLockBytes::CreateLockBytes( xStream );
+ if ( xLockBytes.is() )
+ {
+ pStream.reset( new SvStream( xLockBytes.get() ) );
+ pStream->SetBufferSize( 4096 );
+ pStream->SetError( xLockBytes->GetError() );
+ }
+ }
+ else
+ return CreateStream( xStream->getInputStream() );
+
+ return pStream;
+}
+
+std::unique_ptr<SvStream> UcbStreamHelper::CreateStream( const Reference < XInputStream >& xStream, bool bCloseStream )
+{
+ std::unique_ptr<SvStream> pStream;
+ UcbLockBytesRef xLockBytes = UcbLockBytes::CreateInputLockBytes( xStream );
+ if ( xLockBytes.is() )
+ {
+ if ( !bCloseStream )
+ xLockBytes->setDontClose();
+
+ pStream.reset( new SvStream( xLockBytes.get() ) );
+ pStream->SetBufferSize( 4096 );
+ pStream->SetError( xLockBytes->GetError() );
+ }
+
+ return pStream;
+};
+
+std::unique_ptr<SvStream> UcbStreamHelper::CreateStream( const Reference < XStream >& xStream, bool bCloseStream )
+{
+ std::unique_ptr<SvStream> pStream;
+ if ( xStream->getOutputStream().is() )
+ {
+ UcbLockBytesRef xLockBytes = UcbLockBytes::CreateLockBytes( xStream );
+ if ( xLockBytes.is() )
+ {
+ if ( !bCloseStream )
+ xLockBytes->setDontClose();
+
+ pStream.reset( new SvStream( xLockBytes.get() ) );
+ pStream->SetBufferSize( 4096 );
+ pStream->SetError( xLockBytes->GetError() );
+ }
+ }
+ else
+ return CreateStream( xStream->getInputStream(), bCloseStream );
+
+ return pStream;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/ucbhelper/xtempfile.cxx b/unotools/source/ucbhelper/xtempfile.cxx
new file mode 100644
index 000000000..a5772d8ba
--- /dev/null
+++ b/unotools/source/ucbhelper/xtempfile.cxx
@@ -0,0 +1,435 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "XTempFile.hxx"
+#include <com/sun/star/io/BufferSizeExceededException.hpp>
+#include <com/sun/star/io/NotConnectedException.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <cppuhelper/typeprovider.hxx>
+#include <o3tl/safeint.hxx>
+#include <unotools/tempfile.hxx>
+#include <cppuhelper/propshlp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+OTempFileService::OTempFileService(css::uno::Reference< css::uno::XComponentContext > const &)
+: mpStream( nullptr )
+, mbRemoveFile( true )
+, mbInClosed( false )
+, mbOutClosed( false )
+{
+ mpTempFile.emplace();
+ mpTempFile->EnableKillingFile();
+}
+
+OTempFileService::~OTempFileService ()
+{
+}
+
+// XTypeProvider
+
+css::uno::Sequence< css::uno::Type > SAL_CALL OTempFileService::getTypes( )
+{
+ static ::cppu::OTypeCollection ourTypeCollection(
+ cppu::UnoType<css::beans::XPropertySet>::get()
+ ,OTempFileBase::getTypes() );
+
+ return ourTypeCollection.getTypes();
+};
+
+// XTempFile
+
+sal_Bool SAL_CALL OTempFileService::getRemoveFile()
+{
+ std::unique_lock aGuard( maMutex );
+
+ if ( !mpTempFile )
+ {
+ // the stream is already disconnected
+ throw css::uno::RuntimeException("Not connected to a file.");
+ }
+
+ return mbRemoveFile;
+};
+void SAL_CALL OTempFileService::setRemoveFile( sal_Bool _removefile )
+{
+ std::unique_lock aGuard( maMutex );
+
+ if ( !mpTempFile )
+ {
+ // the stream is already disconnected
+ throw css::uno::RuntimeException("Not connected to a file.");
+ }
+
+ mbRemoveFile = _removefile;
+ mpTempFile->EnableKillingFile( mbRemoveFile );
+};
+OUString SAL_CALL OTempFileService::getUri()
+{
+ std::unique_lock aGuard( maMutex );
+
+ if ( !mpTempFile )
+ {
+ throw css::uno::RuntimeException("Not connected to a file.");
+ }
+
+ return mpTempFile->GetURL();
+
+};
+OUString SAL_CALL OTempFileService::getResourceName()
+{
+ std::unique_lock aGuard( maMutex );
+
+ if ( !mpTempFile )
+ {
+ throw css::uno::RuntimeException("Not connected to a file.");
+ }
+
+ return mpTempFile->GetFileName();
+};
+
+// XInputStream
+
+sal_Int32 SAL_CALL OTempFileService::readBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead )
+{
+ std::unique_lock aGuard( maMutex );
+ if ( mbInClosed )
+ throw css::io::NotConnectedException ( OUString(), static_cast < css::uno::XWeak * > (this ) );
+
+ checkConnected();
+ if (nBytesToRead < 0)
+ throw css::io::BufferSizeExceededException( OUString(), static_cast< css::uno::XWeak * >(this));
+
+ if (aData.getLength() < nBytesToRead)
+ aData.realloc(nBytesToRead);
+
+ sal_uInt32 nRead = mpStream->ReadBytes(static_cast<void*>(aData.getArray()), nBytesToRead);
+ checkError();
+
+ if (nRead < o3tl::make_unsigned(aData.getLength()))
+ aData.realloc( nRead );
+
+ return nRead;
+}
+sal_Int32 SAL_CALL OTempFileService::readSomeBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead )
+{
+ {
+ std::unique_lock aGuard( maMutex );
+ if ( mbInClosed )
+ throw css::io::NotConnectedException ( OUString(), static_cast < css::uno::XWeak * > (this ) );
+
+ checkConnected();
+ checkError();
+
+ if (nMaxBytesToRead < 0)
+ throw css::io::BufferSizeExceededException( OUString(), static_cast < css::uno::XWeak * >( this ) );
+
+ if (mpStream->eof())
+ {
+ aData.realloc(0);
+ return 0;
+ }
+ }
+ return readBytes(aData, nMaxBytesToRead);
+}
+void SAL_CALL OTempFileService::skipBytes( sal_Int32 nBytesToSkip )
+{
+ std::unique_lock aGuard( maMutex );
+ if ( mbInClosed )
+ throw css::io::NotConnectedException ( OUString(), static_cast < css::uno::XWeak * > (this ) );
+
+ checkConnected();
+ checkError();
+ mpStream->SeekRel(nBytesToSkip);
+ checkError();
+}
+sal_Int32 SAL_CALL OTempFileService::available( )
+{
+ std::unique_lock aGuard( maMutex );
+ if ( mbInClosed )
+ throw css::io::NotConnectedException ( OUString(), static_cast < css::uno::XWeak * > (this ) );
+
+ checkConnected();
+
+ sal_Int64 nAvailable = mpStream->remainingSize();
+ checkError();
+
+ return std::min<sal_Int64>(SAL_MAX_INT32, nAvailable);
+}
+void SAL_CALL OTempFileService::closeInput( )
+{
+ std::unique_lock aGuard( maMutex );
+ if ( mbInClosed )
+ throw css::io::NotConnectedException ( OUString(), static_cast < css::uno::XWeak * > (this ) );
+
+ mbInClosed = true;
+
+ if ( mbOutClosed )
+ {
+ // stream will be deleted by TempFile implementation
+ mpStream = nullptr;
+ mpTempFile.reset();
+ }
+}
+
+// XOutputStream
+
+void SAL_CALL OTempFileService::writeBytes( const css::uno::Sequence< sal_Int8 >& aData )
+{
+ std::unique_lock aGuard( maMutex );
+ if ( mbOutClosed )
+ throw css::io::NotConnectedException ( OUString(), static_cast < css::uno::XWeak * > (this ) );
+
+ checkConnected();
+ sal_uInt32 nWritten = mpStream->WriteBytes(aData.getConstArray(), aData.getLength());
+ checkError();
+ if ( nWritten != static_cast<sal_uInt32>(aData.getLength()))
+ throw css::io::BufferSizeExceededException( OUString(),static_cast < css::uno::XWeak * > ( this ) );
+}
+void SAL_CALL OTempFileService::flush( )
+{
+ std::unique_lock aGuard( maMutex );
+ if ( mbOutClosed )
+ throw css::io::NotConnectedException ( OUString(), static_cast < css::uno::XWeak * > (this ) );
+
+ checkConnected();
+ mpStream->Flush();
+ checkError();
+}
+void SAL_CALL OTempFileService::closeOutput( )
+{
+ std::unique_lock aGuard( maMutex );
+ if ( mbOutClosed )
+ throw css::io::NotConnectedException ( OUString(), static_cast < css::uno::XWeak * > (this ) );
+
+ mbOutClosed = true;
+ if (mpStream)
+ {
+ // so that if you then open the InputStream, you can read the content
+ mpStream->FlushBuffer();
+ mpStream->Seek(0);
+ }
+
+ if ( mbInClosed )
+ {
+ // stream will be deleted by TempFile implementation
+ mpStream = nullptr;
+ mpTempFile.reset();
+ }
+}
+
+void OTempFileService::checkError () const
+{
+ if (!mpStream || mpStream->SvStream::GetError () != ERRCODE_NONE )
+ throw css::io::NotConnectedException ( OUString(), const_cast < css::uno::XWeak * > ( static_cast < const css::uno::XWeak * > (this ) ) );
+}
+void OTempFileService::checkConnected ()
+{
+ if (!mpStream && mpTempFile)
+ {
+ // Ideally we should open this SHARE_DENYALL, but the JunitTest_unotools_complex test wants to open
+ // this file directly and read from it.
+ mpStream = mpTempFile->GetStream(StreamMode::READ | StreamMode::WRITE
+ | StreamMode::SHARE_DENYWRITE);
+ }
+
+ if (!mpStream)
+ throw css::io::NotConnectedException ( OUString(), static_cast < css::uno::XWeak * > (this ) );
+}
+
+// XSeekable
+
+void SAL_CALL OTempFileService::seek( sal_Int64 nLocation )
+{
+ std::unique_lock aGuard( maMutex );
+ checkConnected();
+ checkError();
+ sal_Int64 nEndPos = mpStream->TellEnd();
+ if ( nLocation < 0 || nLocation > nEndPos )
+ throw css::lang::IllegalArgumentException();
+
+ mpStream->Seek(static_cast<sal_uInt32>(nLocation) );
+ checkError();
+}
+sal_Int64 SAL_CALL OTempFileService::getPosition( )
+{
+ std::unique_lock aGuard( maMutex );
+ checkConnected();
+
+ sal_uInt32 nPos = mpStream->Tell();
+ checkError();
+ return static_cast<sal_Int64>(nPos);
+}
+sal_Int64 SAL_CALL OTempFileService::getLength( )
+{
+ std::unique_lock aGuard( maMutex );
+ checkConnected();
+
+ checkError();
+
+ sal_Int64 nEndPos = mpStream->TellEnd();
+
+ return nEndPos;
+}
+
+// XStream
+
+css::uno::Reference< css::io::XInputStream > SAL_CALL OTempFileService::getInputStream()
+ {
+ return css::uno::Reference< css::io::XInputStream >( *this, css::uno::UNO_QUERY );
+}
+
+css::uno::Reference< css::io::XOutputStream > SAL_CALL OTempFileService::getOutputStream()
+ {
+ return css::uno::Reference< css::io::XOutputStream >( *this, css::uno::UNO_QUERY );
+ }
+
+// XTruncate
+
+void SAL_CALL OTempFileService::truncate()
+{
+ std::unique_lock aGuard( maMutex );
+ checkConnected();
+ // SetStreamSize() call does not change the position
+ mpStream->Seek( 0 );
+ mpStream->SetStreamSize( 0 );
+ checkError();
+}
+
+#define PROPERTY_HANDLE_URI 1
+#define PROPERTY_HANDLE_REMOVE_FILE 2
+#define PROPERTY_HANDLE_RESOURCE_NAME 3
+
+// XPropertySet
+::css::uno::Reference< ::css::beans::XPropertySetInfo > OTempFileService::getPropertySetInfo()
+{
+ // Create a table that map names to index values.
+ // attention: properties need to be sorted by name!
+ static cppu::OPropertyArrayHelper ourPropertyInfo(
+ {
+ css::beans::Property( "Uri", PROPERTY_HANDLE_URI, cppu::UnoType<OUString>::get(),
+ css::beans::PropertyAttribute::READONLY ),
+ css::beans::Property( "RemoveFile", PROPERTY_HANDLE_REMOVE_FILE, cppu::UnoType<bool>::get(),
+ 0 ),
+ css::beans::Property( "ResourceName", PROPERTY_HANDLE_RESOURCE_NAME, cppu::UnoType<OUString>::get(),
+ css::beans::PropertyAttribute::READONLY )
+ },
+ true );
+ static css::uno::Reference< css::beans::XPropertySetInfo > xInfo(
+ ::cppu::OPropertySetHelper::createPropertySetInfo( ourPropertyInfo ) );
+ return xInfo;
+}
+void OTempFileService::setPropertyValue( const ::rtl::OUString& aPropertyName, const ::css::uno::Any& aValue )
+{
+ if ( aPropertyName == "RemoveFile" )
+ setRemoveFile( aValue.get<bool>() );
+ else
+ {
+ assert(false);
+ throw css::beans::UnknownPropertyException(aPropertyName);
+ }
+}
+::css::uno::Any OTempFileService::getPropertyValue( const ::rtl::OUString& aPropertyName )
+{
+ if ( aPropertyName == "RemoveFile" )
+ return css::uno::Any(getRemoveFile());
+ else if ( aPropertyName == "ResourceName" )
+ return css::uno::Any(getResourceName());
+ else if ( aPropertyName == "Uri" )
+ return css::uno::Any(getUri());
+ else
+ {
+ assert(false);
+ throw css::beans::UnknownPropertyException(aPropertyName);
+ }
+}
+void OTempFileService::addPropertyChangeListener( const ::rtl::OUString& /*aPropertyName*/, const ::css::uno::Reference< ::css::beans::XPropertyChangeListener >& /*xListener*/ )
+{
+ assert(false);
+}
+void OTempFileService::removePropertyChangeListener( const ::rtl::OUString& /*aPropertyName*/, const ::css::uno::Reference< ::css::beans::XPropertyChangeListener >& /*xListener*/ )
+{
+ assert(false);
+}
+void OTempFileService::addVetoableChangeListener( const ::rtl::OUString& /*aPropertyName*/, const ::css::uno::Reference< ::css::beans::XVetoableChangeListener >& /*xListener*/ )
+{
+ assert(false);
+}
+void OTempFileService::removeVetoableChangeListener( const ::rtl::OUString& /*aPropertyName*/, const ::css::uno::Reference< ::css::beans::XVetoableChangeListener >& /*xListener*/ )
+{
+ assert(false);
+}
+// XFastPropertySet
+void OTempFileService::setFastPropertyValue( ::sal_Int32 nHandle, const ::css::uno::Any& aValue )
+{
+ switch (nHandle)
+ {
+ case PROPERTY_HANDLE_REMOVE_FILE: setRemoveFile( aValue.get<bool>() ); return;
+ }
+ assert(false);
+ throw css::beans::UnknownPropertyException(OUString::number(nHandle));
+}
+::css::uno::Any OTempFileService::getFastPropertyValue( ::sal_Int32 nHandle )
+{
+ switch (nHandle)
+ {
+ case PROPERTY_HANDLE_REMOVE_FILE: return css::uno::Any(getRemoveFile());
+ case PROPERTY_HANDLE_RESOURCE_NAME: return css::uno::Any(getResourceName());
+ case PROPERTY_HANDLE_URI: return css::uno::Any(getUri());
+ }
+ assert(false);
+ throw css::beans::UnknownPropertyException(OUString::number(nHandle));
+}
+// XPropertyAccess
+::css::uno::Sequence< ::css::beans::PropertyValue > OTempFileService::getPropertyValues()
+{
+ return {
+ css::beans::PropertyValue("Uri", PROPERTY_HANDLE_URI, css::uno::Any(getUri()), css::beans::PropertyState_DEFAULT_VALUE),
+ css::beans::PropertyValue("RemoveFile", PROPERTY_HANDLE_REMOVE_FILE, css::uno::Any(getRemoveFile()), css::beans::PropertyState_DEFAULT_VALUE),
+ css::beans::PropertyValue("ResourceName", PROPERTY_HANDLE_RESOURCE_NAME, css::uno::Any(getResourceName()), css::beans::PropertyState_DEFAULT_VALUE)
+ };
+}
+void OTempFileService::setPropertyValues( const ::css::uno::Sequence< ::css::beans::PropertyValue >& aProps )
+{
+ for ( auto const & rPropVal : aProps )
+ setPropertyValue( rPropVal.Name, rPropVal.Value );
+}
+
+// XServiceInfo
+sal_Bool OTempFileService::supportsService(const OUString& sServiceName)
+{
+ return cppu::supportsService(this, sServiceName);
+}
+OUString OTempFileService::getImplementationName()
+{
+ return "com.sun.star.io.comp.TempFile";
+}
+css::uno::Sequence< OUString > OTempFileService::getSupportedServiceNames()
+{
+ return { "com.sun.star.io.TempFile" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+unotools_OTempFileService_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new OTempFileService(context));
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */