From 267c6f2ac71f92999e969232431ba04678e7437e Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 15 Apr 2024 07:54:39 +0200 Subject: Adding upstream version 4:24.2.0. Signed-off-by: Daniel Baumann --- unotools/source/ucbhelper/XTempFile.hxx | 119 ++ unotools/source/ucbhelper/localfilehelper.cxx | 92 ++ unotools/source/ucbhelper/progresshandlerwrap.cxx | 88 ++ unotools/source/ucbhelper/tempfile.cxx | 808 +++++++++++++ unotools/source/ucbhelper/ucbhelper.cxx | 413 +++++++ unotools/source/ucbhelper/ucblockbytes.cxx | 1338 +++++++++++++++++++++ unotools/source/ucbhelper/ucblockbytes.hxx | 144 +++ unotools/source/ucbhelper/ucbstreamhelper.cxx | 237 ++++ unotools/source/ucbhelper/xtempfile.cxx | 435 +++++++ 9 files changed, 3674 insertions(+) create mode 100644 unotools/source/ucbhelper/XTempFile.hxx create mode 100644 unotools/source/ucbhelper/localfilehelper.cxx create mode 100644 unotools/source/ucbhelper/progresshandlerwrap.cxx create mode 100644 unotools/source/ucbhelper/tempfile.cxx create mode 100644 unotools/source/ucbhelper/ucbhelper.cxx create mode 100644 unotools/source/ucbhelper/ucblockbytes.cxx create mode 100644 unotools/source/ucbhelper/ucblockbytes.hxx create mode 100644 unotools/source/ucbhelper/ucbstreamhelper.cxx create mode 100644 unotools/source/ucbhelper/xtempfile.cxx (limited to 'unotools/source/ucbhelper') diff --git a/unotools/source/ucbhelper/XTempFile.hxx b/unotools/source/ucbhelper/XTempFile.hxx new file mode 100644 index 0000000000..2b0ec33cb4 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 +{ + std::optional mpTempFile; + std::mutex maMutex; + SvStream* mpStream; + bool mbRemoveFile; + bool mbInClosed; + bool mbOutClosed; + +protected: + 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 0000000000..bdabd5f0ac --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 0000000000..29c1067ec7 --- /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 +#include +#include + +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 0000000000..992ff8814d --- /dev/null +++ b/unotools/source/ucbhelper/tempfile.cxx @@ -0,0 +1,808 @@ +/* -*- 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 + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef UNX +#include +#elif defined( _WIN32 ) +#include +#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, std::u16string_view 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.empty() ) + 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, u"", pParent, bDir, bKeep, + false, false); +} + +static OUString CreateTempNameFast() +{ + 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 + + OUString aName = ConstructTempDir_Impl( /*pParent*/nullptr, /*bCreateParentDirs*/false ) + aEyeCatcher; + + tools::Guid aGuid(tools::Guid::Generate); + + return aName + aGuid.getOUString() + ".tmp" ; +} + +OUString CreateTempName() +{ + OUString aName(CreateTempName_Impl( nullptr, false )); + + // convert to file URL + OUString aTmp; + if ( !aName.isEmpty() ) + FileBase::getSystemPathFromFileURL( aName, aTmp ); + return aTmp; +} + +TempFileFast::TempFileFast( ) +{ +} + +TempFileFast::TempFileFast(TempFileFast && other) noexcept : + mxStream(std::move(other.mxStream)) +{ +} + +TempFileFast::~TempFileFast() +{ + CloseStream(); +} + +SvStream* TempFileFast::GetStream( StreamMode eMode ) +{ + if (!mxStream) + { + OUString aName = CreateTempNameFast(); +#ifdef _WIN32 + mxStream.reset(new SvFileStream(aName, eMode | StreamMode::TEMPORARY | StreamMode::DELETE_ON_CLOSE)); +#else + mxStream.reset(new SvFileStream(aName, eMode | StreamMode::TEMPORARY)); +#endif + } + return mxStream.get(); +} + +void TempFileFast::CloseStream() +{ + if (mxStream) + { +#if !defined _WIN32 + OUString aName = mxStream->GetFileName(); +#endif + mxStream.reset(); +#ifdef _WIN32 + // On Windows, the file is opened with FILE_FLAG_DELETE_ON_CLOSE, so it will delete as soon as the handle closes. + // On other platforms, we need to explicitly delete it. +#else + if (!aName.isEmpty() && (osl::FileBase::getFileURLFromSystemPath(aName, aName) == osl::FileBase::E_None)) + File::remove(aName); +#endif + } +} + +OUString CreateTempURL( const OUString* pParent, bool bDirectory ) +{ + return CreateTempName_Impl( pParent, true, bDirectory ); +} + +OUString CreateTempURL( std::u16string_view rLeadingChars, bool _bStartWithZero, + std::u16string_view pExtension, const OUString* pParent, + bool bCreateParentDirs ) +{ + SequentialTokens t(_bStartWithZero); + return lcl_createName( rLeadingChars, t, pExtension, pParent, false, + true, true, bCreateParentDirs ); +} + +TempFileNamed::TempFileNamed( const OUString* pParent, bool bDirectory ) + : bIsDirectory( bDirectory ) + , bKillingFileEnabled( false ) +{ + aName = CreateTempName_Impl( pParent, true, bDirectory ); +} + +TempFileNamed::TempFileNamed( std::u16string_view rLeadingChars, bool _bStartWithZero, + std::u16string_view pExtension, const OUString* pParent, + bool bCreateParentDirs ) + : bIsDirectory( false ) + , bKillingFileEnabled( false ) +{ + SequentialTokens t(_bStartWithZero); + aName = lcl_createName( rLeadingChars, t, pExtension, pParent, false, + true, true, bCreateParentDirs ); +} + +TempFileNamed::TempFileNamed(TempFileNamed && other) noexcept : + aName(std::move(other.aName)), pStream(std::move(other.pStream)), bIsDirectory(other.bIsDirectory), + bKillingFileEnabled(other.bKillingFileEnabled) +{ + other.bKillingFileEnabled = false; +} + +TempFileNamed::~TempFileNamed() +{ + if ( !bKillingFileEnabled ) + return; + + pStream.reset(); + if ( bIsDirectory ) + { + comphelper::DirectoryHelper::deleteDirRecursively(aName); + } + else + { + File::remove( aName ); + } +} + +bool TempFileNamed::IsValid() const +{ + return !aName.isEmpty(); +} + +OUString TempFileNamed::GetFileName() const +{ + OUString aTmp; + FileBase::getSystemPathFromFileURL(aName, aTmp); + return aTmp; +} + +OUString const & TempFileNamed::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* TempFileNamed::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 TempFileNamed::CloseStream() +{ + pStream.reset(); +} + +OUString 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 + "/"; + + TempFileNamed aBase( {}, 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 GetTempNameBaseDirectory() +{ + return ConstructTempDir_Impl(nullptr, false); +} + + +TempFileFastService::TempFileFastService() +: mbInClosed( false ) +, mbOutClosed( false ) +{ + mpTempFile.emplace(); + mpStream = mpTempFile->GetStream(StreamMode::READWRITE); +} + +TempFileFastService::~TempFileFastService () +{ +} + +// XInputStream + +sal_Int32 SAL_CALL TempFileFastService::readBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead ) +{ + std::unique_lock aGuard( maMutex ); + if ( mbInClosed ) + throw css::io::NotConnectedException ( OUString(), getXWeak() ); + + checkConnected(); + if (nBytesToRead < 0) + throw css::io::BufferSizeExceededException( OUString(), getXWeak()); + + if (aData.getLength() < nBytesToRead) + aData.realloc(nBytesToRead); + + sal_uInt32 nRead = mpStream->ReadBytes(static_cast(aData.getArray()), nBytesToRead); + checkError(); + + if (nRead < o3tl::make_unsigned(aData.getLength())) + aData.realloc( nRead ); + + return nRead; +} + +sal_Int32 SAL_CALL TempFileFastService::readSomeBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead ) +{ + { + std::unique_lock aGuard( maMutex ); + if ( mbInClosed ) + throw css::io::NotConnectedException ( OUString(), getXWeak() ); + + checkConnected(); + checkError(); + + if (nMaxBytesToRead < 0) + throw css::io::BufferSizeExceededException( OUString(), getXWeak() ); + + if (mpStream->eof()) + { + aData.realloc(0); + return 0; + } + } + return readBytes(aData, nMaxBytesToRead); +} + +void SAL_CALL TempFileFastService::skipBytes( sal_Int32 nBytesToSkip ) +{ + std::unique_lock aGuard( maMutex ); + if ( mbInClosed ) + throw css::io::NotConnectedException ( OUString(), getXWeak() ); + + checkConnected(); + checkError(); + mpStream->SeekRel(nBytesToSkip); + checkError(); +} + +sal_Int32 SAL_CALL TempFileFastService::available() +{ + std::unique_lock aGuard( maMutex ); + if ( mbInClosed ) + throw css::io::NotConnectedException ( OUString(), getXWeak() ); + + checkConnected(); + + sal_Int64 nAvailable = mpStream->remainingSize(); + checkError(); + + return std::min(SAL_MAX_INT32, nAvailable); +} + +void SAL_CALL TempFileFastService::closeInput() +{ + std::unique_lock aGuard( maMutex ); + if ( mbInClosed ) + throw css::io::NotConnectedException ( OUString(), getXWeak() ); + + mbInClosed = true; + + if ( mbOutClosed ) + { + // stream will be deleted by TempFile implementation + mpStream = nullptr; + mpTempFile.reset(); + } +} + +// XOutputStream + +void SAL_CALL TempFileFastService::writeBytes( const css::uno::Sequence< sal_Int8 >& aData ) +{ + std::unique_lock aGuard( maMutex ); + if ( mbOutClosed ) + throw css::io::NotConnectedException ( OUString(), getXWeak() ); + + checkConnected(); + sal_uInt32 nWritten = mpStream->WriteBytes(aData.getConstArray(), aData.getLength()); + checkError(); + if ( nWritten != static_cast(aData.getLength())) + throw css::io::BufferSizeExceededException( OUString(), getXWeak() ); +} + +void SAL_CALL TempFileFastService::flush() +{ + std::unique_lock aGuard( maMutex ); + if ( mbOutClosed ) + throw css::io::NotConnectedException ( OUString(), getXWeak() ); + + checkConnected(); + mpStream->Flush(); + checkError(); +} + +void SAL_CALL TempFileFastService::closeOutput() +{ + std::unique_lock aGuard( maMutex ); + if ( mbOutClosed ) + throw css::io::NotConnectedException ( OUString(), getXWeak() ); + + 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 TempFileFastService::checkError() const +{ + if (!mpStream || mpStream->SvStream::GetError () != ERRCODE_NONE ) + throw css::io::NotConnectedException ( OUString(), const_cast < TempFileFastService * > (this)->getXWeak() ); +} + +void TempFileFastService::checkConnected() +{ + if (!mpStream) + throw css::io::NotConnectedException ( OUString(), getXWeak() ); +} + +// XSeekable + +void SAL_CALL TempFileFastService::seek( sal_Int64 nLocation ) +{ + std::unique_lock aGuard( maMutex ); + checkConnected(); + checkError(); + if ( nLocation < 0 ) + throw css::lang::IllegalArgumentException(); + + sal_Int64 nNewLoc = mpStream->Seek(static_cast(nLocation) ); + if ( nNewLoc != nLocation ) + throw css::lang::IllegalArgumentException(); + checkError(); +} + +sal_Int64 SAL_CALL TempFileFastService::getPosition() +{ + std::unique_lock aGuard( maMutex ); + checkConnected(); + + sal_uInt64 nPos = mpStream->Tell(); + checkError(); + return static_cast(nPos); +} + +sal_Int64 SAL_CALL TempFileFastService::getLength() +{ + std::unique_lock aGuard( maMutex ); + checkConnected(); + + checkError(); + + sal_Int64 nEndPos = mpStream->TellEnd(); + + return nEndPos; +} + +// XStream + +css::uno::Reference< css::io::XInputStream > SAL_CALL TempFileFastService::getInputStream() +{ + return this; +} + +css::uno::Reference< css::io::XOutputStream > SAL_CALL TempFileFastService::getOutputStream() +{ + return this; +} + +// XTruncate + +void SAL_CALL TempFileFastService::truncate() +{ + std::unique_lock aGuard( maMutex ); + checkConnected(); + // SetStreamSize() call does not change the position + mpStream->Seek( 0 ); + mpStream->SetStreamSize( 0 ); + checkError(); +} + + + +} + +/* 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 0000000000..aaa9d214bb --- /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 + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 getContents(OUString const & url) { + try { + std::vector cs; + ucbhelper::Content c(content(url)); + css::uno::Sequence args { "Title" }; + css::uno::Reference res( c.createCursor(args), css::uno::UNO_SET_THROW); + css::uno::Reference 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 getCasePreservingUrl(const INetURLObject& url) { + return + content(url).executeCommand( + "getCasePreservingURL", + css::uno::Any()). + get(); +} + +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 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 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()) + > convert( + content(older).getPropertyValue( + "DateModified"). + get()); + } 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 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 0000000000..182b1f674e --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +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 &/*rxListener*/) override {} + virtual void SAL_CALL removeListener ( const Reference &/*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 &rxInputStream) override + { m_xLockBytes->setInputStream(rxInputStream); } + virtual Reference 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 &/*rxListener*/) override {} + virtual void SAL_CALL removeListener ( const Reference &/*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 SAL_CALL getInteractionHandler() override + { return m_xInteractionHandler; } + + virtual Reference 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 &rEvent) override; +}; + +} + +void SAL_CALL UcbPropertiesChangeListener_Impl::propertiesChange ( const Sequence &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& Request ); + + void setStream(const Reference< XStream >& aStream); + void setInputStream(const Reference &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 +{ +public: + + explicit ModeratorsActiveDataStreamer(Moderator &theModerator); + + // XActiveDataStreamer + virtual void SAL_CALL + setStream( + const Reference< XStream >& aStream + ) override; + + virtual Reference SAL_CALL getStream () override + { + std::scoped_lock aGuard(m_aMutex); + return m_xStream; + } + +private: + Moderator& m_aModerator; + + std::mutex m_aMutex; + Reference m_xStream; +}; + +class ModeratorsActiveDataSink + : public ::cppu::WeakImplHelper +{ +public: + + explicit ModeratorsActiveDataSink(Moderator &theModerator); + + // XActiveDataSink. + virtual void SAL_CALL + setInputStream ( + const Reference &rxInputStream + ) override; + + virtual Reference SAL_CALL getInputStream() override + { + std::scoped_lock aGuard(m_aMutex); + return m_xStream; + } + +private: + Moderator& m_aModerator; + std::mutex m_aMutex; + Reference m_xStream; +}; + +} + +ModeratorsActiveDataSink::ModeratorsActiveDataSink(Moderator &theModerator) + : m_aModerator(theModerator) +{ +} + +// XActiveDataSink. +void SAL_CALL +ModeratorsActiveDataSink::setInputStream ( + const Reference &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 &rxStream +) +{ + m_aModerator.setStream(rxStream); + std::scoped_lock aGuard(m_aMutex); + m_xStream = rxStream; +} + +namespace { + +class ModeratorsInteractionHandler + : public ::cppu::WeakImplHelper +{ +public: + + explicit ModeratorsInteractionHandler(Moderator &theModerator); + + virtual void SAL_CALL + handle( const Reference& Request ) override; + +private: + + Moderator& m_aModerator; +}; + +} + +ModeratorsInteractionHandler::ModeratorsInteractionHandler( + Moderator &aModerator) + : m_aModerator(aModerator) +{ +} + +void SAL_CALL +ModeratorsInteractionHandler::handle( + const Reference& 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 *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(getXWeak(new ModeratorsActiveDataSink(*this))); + + Reference xStreamer( *pxSink, UNO_QUERY ); + if ( xStreamer.is() ) + pxSink->set(getXWeak(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& 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 > aSeq( + Request->getContinuations()); + for(const auto& rContinuation : aSeq) { + Reference 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 &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 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 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 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 xRet; + if(xInteract.is()) { + INetURLObject aURL( + xContId.is() ? + xContId->getContentIdentifier() : + OUString() ); + InteractiveNetworkConnectException aExcep( + "server not responding after five seconds", {}, + InteractionClassification_ERROR, aURL.GetHost()); + Any request; + request <<= aExcep; + rtl::Reference xIR = + new ucbhelper::InteractionRequest(request); + rtl::Reference retryP = + new ucbhelper::InteractionRetry(xIR.get()); + rtl::Reference abortP = + new ucbhelper::InteractionAbort(xIR.get()); + Sequence > aSeq { retryP, abortP }; + + xIR->setContinuations(aSeq); + xInteract->handle(xIR); + rtl::Reference< ucbhelper::InteractionContinuation > ref + = xIR->getSelection(); + if(ref.is()) { + Reference xInt(ref); + xRet.set(xInt,UNO_QUERY); + } + } + + if(!xRet.is()) { + bAborted = true; + xLockBytes->SetError(ERRCODE_ABORT); + } + + break; + } + case Moderator::ResultType::INTERACTIONREQUEST: + { + Reference 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() +{ + std::unique_lock aGuard( m_aMutex ); + m_bDontClose = true; + return m_xInputStream; +} + +void UcbLockBytes::setStream( const Reference& aStream ) +{ + std::unique_lock aGuard( m_aMutex ); + if ( aStream.is() ) + { + m_xOutputStream = aStream->getOutputStream(); + setInputStreamImpl( aGuard, aStream->getInputStream(), false ); + m_xSeekable.set( aStream, UNO_QUERY ); + } + else + { + m_xOutputStream.clear(); + setInputStreamImpl( aGuard, Reference < XInputStream >() ); + } +} + +bool UcbLockBytes::setInputStream( const Reference &rxInputStream, bool bSetXSeekable ) +{ + std::unique_lock aGuard( m_aMutex ); + return setInputStreamImpl(aGuard, rxInputStream, bSetXSeekable); +} + +bool UcbLockBytes::setInputStreamImpl( std::unique_lock& /*rGuard*/, const Reference &rxInputStream, bool bSetXSeekable ) +{ + bool bRet = false; + + try + { + 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(); + rtl::Reference< utl::TempFileFastService > rxTempOut( new utl::TempFileFastService ); + + ::comphelper::OStorageHelper::CopyInputToOutput( rxInputStream, rxTempOut ); + m_xInputStream.set( rxTempOut ); + m_xSeekable.set( rxTempOut ); + } + } + + 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 xStream = getInputStream(); + if ( !xStream.is() ) + { + if ( m_bTerminated ) + return ERRCODE_IO_CANTREAD; + else + return ERRCODE_IO_PENDING; + } + + if ( pRead ) + *pRead = 0; + + Reference 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; + } + + comphelper::ByteReader* pByteReader = dynamic_cast< comphelper::ByteReader* >(xStream.get()); + if (pByteReader) + { + nSize = pByteReader->readSomeBytes( static_cast(pBuffer), sal_Int32(nCount) ); + } + else + { + Sequence 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(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 = getSeekable(); + Reference 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(pBuffer); + Sequence 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 = 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 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 xStream = getInputStream(); + Reference 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 0000000000..d866015b25 --- /dev/null +++ b/unotools/source/ucbhelper/ucblockbytes.hxx @@ -0,0 +1,144 @@ +/* -*- 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 + +#include +#include +#include +#include + +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 UcbLockBytesRef; + +class UcbLockBytes : public SvLockBytes +{ + osl::Condition m_aInitialized; + osl::Condition m_aTerminated; + std::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 + { + std::unique_lock aGuard( const_cast< UcbLockBytes* >(this)->m_aMutex ); + return m_xInputStream; + } + + css::uno::Reference < css::io::XOutputStream > getOutputStream() const + { + std::unique_lock aGuard( const_cast< UcbLockBytes* >(this)->m_aMutex ); + return m_xOutputStream; + } + + css::uno::Reference < css::io::XSeekable > getSeekable() const + { + std::unique_lock aGuard( const_cast< UcbLockBytes* >(this)->m_aMutex ); + return m_xSeekable; + } + + void setDontClose() + { m_bDontClose = true; } + + void SetStreamValid(); + +private: + bool setInputStreamImpl( std::unique_lock& rGuard, + const css::uno::Reference < css::io::XInputStream > &rxInputStream, + bool bSetXSeekable = true ); +}; + +} + +/* 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 0000000000..e3437a864f --- /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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#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 lcl_CreateStream( const OUString& rFileName, StreamMode eOpenMode, + const Reference < XInteractionHandler >& xInteractionHandler, + bool bEnsureFileExists ) +{ + std::unique_ptr 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 UcbStreamHelper::CreateStream(const OUString& rFileName, StreamMode eOpenMode, css::uno::Reference 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 xIHScoped(new comphelper::SimpleFileAccessInteraction(xIH)); + + return lcl_CreateStream( rFileName, eOpenMode, xIHScoped, true /* bEnsureFileExists */ ); +} + +std::unique_ptr UcbStreamHelper::CreateStream(const OUString& rFileName, StreamMode eOpenMode, + bool bFileExists, css::uno::Reference 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 xIHScoped(new comphelper::SimpleFileAccessInteraction(xIH)); + return lcl_CreateStream( rFileName, eOpenMode, xIHScoped,!bFileExists ); +} + + +std::unique_ptr UcbStreamHelper::CreateStream( const Reference < XInputStream >& xStream ) +{ + std::unique_ptr 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 UcbStreamHelper::CreateStream( const Reference < XStream >& xStream ) +{ + std::unique_ptr 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 UcbStreamHelper::CreateStream( const Reference < XInputStream >& xStream, bool bCloseStream ) +{ + std::unique_ptr 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 UcbStreamHelper::CreateStream( const Reference < XStream >& xStream, bool bCloseStream ) +{ + std::unique_ptr 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 0000000000..5e0cf5cbdb --- /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 +#include +#include +#include +#include +#include +#include +#include + +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::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(), getXWeak() ); + + checkConnected(); + if (nBytesToRead < 0) + throw css::io::BufferSizeExceededException( OUString(), getXWeak()); + + if (aData.getLength() < nBytesToRead) + aData.realloc(nBytesToRead); + + sal_uInt32 nRead = mpStream->ReadBytes(static_cast(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(), getXWeak() ); + + checkConnected(); + checkError(); + + if (nMaxBytesToRead < 0) + throw css::io::BufferSizeExceededException( OUString(), getXWeak() ); + + 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(), getXWeak() ); + + checkConnected(); + checkError(); + mpStream->SeekRel(nBytesToSkip); + checkError(); +} +sal_Int32 SAL_CALL OTempFileService::available( ) +{ + std::unique_lock aGuard( maMutex ); + if ( mbInClosed ) + throw css::io::NotConnectedException ( OUString(), getXWeak() ); + + checkConnected(); + + sal_Int64 nAvailable = mpStream->remainingSize(); + checkError(); + + return std::min(SAL_MAX_INT32, nAvailable); +} +void SAL_CALL OTempFileService::closeInput( ) +{ + std::unique_lock aGuard( maMutex ); + if ( mbInClosed ) + throw css::io::NotConnectedException ( OUString(), getXWeak() ); + + 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(), getXWeak() ); + + checkConnected(); + sal_uInt32 nWritten = mpStream->WriteBytes(aData.getConstArray(), aData.getLength()); + checkError(); + if ( nWritten != static_cast(aData.getLength())) + throw css::io::BufferSizeExceededException( OUString(), getXWeak() ); +} +void SAL_CALL OTempFileService::flush( ) +{ + std::unique_lock aGuard( maMutex ); + if ( mbOutClosed ) + throw css::io::NotConnectedException ( OUString(), getXWeak() ); + + checkConnected(); + mpStream->Flush(); + checkError(); +} +void SAL_CALL OTempFileService::closeOutput( ) +{ + std::unique_lock aGuard( maMutex ); + if ( mbOutClosed ) + throw css::io::NotConnectedException ( OUString(), getXWeak() ); + + 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 < OTempFileService * > (this)->getXWeak() ); +} +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(), getXWeak() ); +} + +// 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(nLocation) ); + checkError(); +} +sal_Int64 SAL_CALL OTempFileService::getPosition( ) +{ + std::unique_lock aGuard( maMutex ); + checkConnected(); + + sal_uInt64 nPos = mpStream->Tell(); + checkError(); + return static_cast(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 this; +} + +css::uno::Reference< css::io::XOutputStream > SAL_CALL OTempFileService::getOutputStream() +{ + return this; +} + +// 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::get(), + css::beans::PropertyAttribute::READONLY ), + css::beans::Property( "RemoveFile", PROPERTY_HANDLE_REMOVE_FILE, cppu::UnoType::get(), + 0 ), + css::beans::Property( "ResourceName", PROPERTY_HANDLE_RESOURCE_NAME, cppu::UnoType::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() ); + 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() ); 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 const&) +{ + return cppu::acquire(new OTempFileService(context)); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit v1.2.3