2863 lines
97 KiB
C++
2863 lines
97 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
#include <com/sun/star/io/NotConnectedException.hpp>
|
|
#include <com/sun/star/io/BufferSizeExceededException.hpp>
|
|
#include <com/sun/star/uno/RuntimeException.hpp>
|
|
#include <ucbhelper/content.hxx>
|
|
#include <com/sun/star/uno/Reference.h>
|
|
#include <com/sun/star/ucb/NameClash.hpp>
|
|
#include <unotools/tempfile.hxx>
|
|
#include <unotools/ucbstreamhelper.hxx>
|
|
#include <com/sun/star/io/XInputStream.hpp>
|
|
#include <com/sun/star/ucb/InsertCommandArgument.hpp>
|
|
#include <com/sun/star/ucb/ResultSetException.hpp>
|
|
#include <com/sun/star/uno/Sequence.h>
|
|
#include <com/sun/star/sdbc/XResultSet.hpp>
|
|
#include <com/sun/star/sdbc/XRow.hpp>
|
|
#include <com/sun/star/ucb/CommandAbortedException.hpp>
|
|
#include <com/sun/star/datatransfer/DataFlavor.hpp>
|
|
#include <com/sun/star/ucb/ContentInfo.hpp>
|
|
#include <com/sun/star/ucb/ContentInfoAttribute.hpp>
|
|
#include <com/sun/star/beans/Property.hpp>
|
|
#include <com/sun/star/packages/manifest/ManifestWriter.hpp>
|
|
#include <com/sun/star/packages/manifest/ManifestReader.hpp>
|
|
#include <com/sun/star/ucb/InteractiveIOException.hpp>
|
|
#include <com/sun/star/ucb/ContentCreationException.hpp>
|
|
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <o3tl/safeint.hxx>
|
|
#include <osl/diagnose.h>
|
|
#include <osl/file.hxx>
|
|
#include <sal/log.hxx>
|
|
#include <comphelper/diagnose_ex.hxx>
|
|
#include <tools/ref.hxx>
|
|
#include <tools/debug.hxx>
|
|
#include <unotools/streamwrap.hxx>
|
|
#include <unotools/ucbhelper.hxx>
|
|
#include <tools/urlobj.hxx>
|
|
#include <comphelper/processfactory.hxx>
|
|
#include <comphelper/propertyvalue.hxx>
|
|
#include <cppuhelper/implbase.hxx>
|
|
#include <ucbhelper/commandenvironment.hxx>
|
|
|
|
#include <sot/stg.hxx>
|
|
#include <sot/storinfo.hxx>
|
|
#include <sot/exchange.hxx>
|
|
#include <sot/formats.hxx>
|
|
#include <comphelper/classids.hxx>
|
|
|
|
#include <mutex>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace com::sun::star::ucb { class XCommandEnvironment; }
|
|
|
|
using namespace ::com::sun::star::lang;
|
|
using namespace ::com::sun::star::beans;
|
|
using namespace ::com::sun::star::uno;
|
|
using namespace ::com::sun::star::ucb;
|
|
using namespace ::com::sun::star::io;
|
|
using namespace ::com::sun::star::sdbc;
|
|
using namespace ::ucbhelper;
|
|
|
|
#if OSL_DEBUG_LEVEL > 0
|
|
static int nOpenFiles=0;
|
|
static int nOpenStreams=0;
|
|
#endif
|
|
|
|
typedef ::cppu::WeakImplHelper < XInputStream, XSeekable > FileInputStreamWrapper_Base;
|
|
|
|
namespace {
|
|
|
|
class FileStreamWrapper_Impl : public FileInputStreamWrapper_Base, public comphelper::ByteReader
|
|
{
|
|
protected:
|
|
std::mutex m_aMutex;
|
|
OUString m_aURL;
|
|
std::unique_ptr<SvStream> m_pSvStream;
|
|
|
|
public:
|
|
explicit FileStreamWrapper_Impl(OUString aName);
|
|
virtual ~FileStreamWrapper_Impl() override;
|
|
|
|
virtual void SAL_CALL seek( sal_Int64 _nLocation ) override;
|
|
virtual sal_Int64 SAL_CALL getPosition( ) override;
|
|
virtual sal_Int64 SAL_CALL getLength( ) override;
|
|
virtual sal_Int32 SAL_CALL readBytes( Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead) override;
|
|
virtual sal_Int32 SAL_CALL readSomeBytes( 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;
|
|
|
|
virtual sal_Int32 readSomeBytes(sal_Int8* aData, sal_Int32 nBytesToRead) override;
|
|
|
|
protected:
|
|
void checkConnected();
|
|
void checkError();
|
|
};
|
|
|
|
}
|
|
|
|
FileStreamWrapper_Impl::FileStreamWrapper_Impl( OUString aName )
|
|
: m_aURL(std::move( aName ))
|
|
{
|
|
// if no URL is provided the stream is empty
|
|
}
|
|
|
|
|
|
FileStreamWrapper_Impl::~FileStreamWrapper_Impl()
|
|
{
|
|
if ( m_pSvStream )
|
|
{
|
|
m_pSvStream.reset();
|
|
#if OSL_DEBUG_LEVEL > 0
|
|
--nOpenFiles;
|
|
#endif
|
|
}
|
|
|
|
if (!m_aURL.isEmpty())
|
|
osl::File::remove(m_aURL);
|
|
}
|
|
|
|
|
|
sal_Int32 SAL_CALL FileStreamWrapper_Impl::readBytes(Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead)
|
|
{
|
|
if ( m_aURL.isEmpty() )
|
|
{
|
|
aData.realloc( 0 );
|
|
return 0;
|
|
}
|
|
|
|
checkConnected();
|
|
|
|
if (nBytesToRead < 0)
|
|
throw BufferSizeExceededException(OUString(), getXWeak());
|
|
|
|
std::scoped_lock aGuard( m_aMutex );
|
|
|
|
if (aData.getLength() < nBytesToRead)
|
|
aData.realloc(nBytesToRead);
|
|
|
|
sal_uInt32 nRead = m_pSvStream->ReadBytes(static_cast<void*>(aData.getArray()), nBytesToRead);
|
|
checkError();
|
|
|
|
// if read characters < MaxLength, adjust sequence
|
|
if (nRead < o3tl::make_unsigned(aData.getLength()))
|
|
aData.realloc( nRead );
|
|
|
|
return nRead;
|
|
}
|
|
|
|
sal_Int32 FileStreamWrapper_Impl::readSomeBytes(sal_Int8* aData, sal_Int32 nBytesToRead)
|
|
{
|
|
if ( m_aURL.isEmpty() )
|
|
return 0;
|
|
|
|
checkConnected();
|
|
|
|
if (nBytesToRead < 0)
|
|
throw BufferSizeExceededException(OUString(), getXWeak());
|
|
|
|
std::scoped_lock aGuard( m_aMutex );
|
|
|
|
sal_uInt32 nRead = m_pSvStream->ReadBytes(static_cast<void*>(aData), nBytesToRead);
|
|
checkError();
|
|
|
|
return nRead;
|
|
}
|
|
|
|
sal_Int32 SAL_CALL FileStreamWrapper_Impl::readSomeBytes(Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead)
|
|
{
|
|
if ( m_aURL.isEmpty() )
|
|
{
|
|
aData.realloc( 0 );
|
|
return 0;
|
|
}
|
|
|
|
checkError();
|
|
|
|
if (nMaxBytesToRead < 0)
|
|
throw BufferSizeExceededException(OUString(), getXWeak());
|
|
|
|
if (m_pSvStream->eof())
|
|
{
|
|
aData.realloc(0);
|
|
return 0;
|
|
}
|
|
else
|
|
return readBytes(aData, nMaxBytesToRead);
|
|
}
|
|
|
|
|
|
void SAL_CALL FileStreamWrapper_Impl::skipBytes(sal_Int32 nBytesToSkip)
|
|
{
|
|
if ( m_aURL.isEmpty() )
|
|
return;
|
|
|
|
std::scoped_lock aGuard( m_aMutex );
|
|
checkError();
|
|
|
|
m_pSvStream->SeekRel(nBytesToSkip);
|
|
checkError();
|
|
}
|
|
|
|
|
|
sal_Int32 SAL_CALL FileStreamWrapper_Impl::available()
|
|
{
|
|
if ( m_aURL.isEmpty() )
|
|
return 0;
|
|
|
|
std::scoped_lock aGuard( m_aMutex );
|
|
checkConnected();
|
|
|
|
sal_Int64 nAvailable = m_pSvStream->remainingSize();
|
|
checkError();
|
|
|
|
return std::min<sal_Int64>(SAL_MAX_INT32, nAvailable);
|
|
}
|
|
|
|
|
|
void SAL_CALL FileStreamWrapper_Impl::closeInput()
|
|
{
|
|
if ( m_aURL.isEmpty() )
|
|
return;
|
|
|
|
std::scoped_lock aGuard( m_aMutex );
|
|
checkConnected();
|
|
m_pSvStream.reset();
|
|
#if OSL_DEBUG_LEVEL > 0
|
|
--nOpenFiles;
|
|
#endif
|
|
osl::File::remove(m_aURL);
|
|
m_aURL.clear();
|
|
}
|
|
|
|
|
|
void SAL_CALL FileStreamWrapper_Impl::seek( sal_Int64 _nLocation )
|
|
{
|
|
if ( m_aURL.isEmpty() )
|
|
return;
|
|
|
|
std::scoped_lock aGuard( m_aMutex );
|
|
checkConnected();
|
|
|
|
m_pSvStream->Seek(static_cast<sal_uInt32>(_nLocation));
|
|
checkError();
|
|
}
|
|
|
|
|
|
sal_Int64 SAL_CALL FileStreamWrapper_Impl::getPosition( )
|
|
{
|
|
if ( m_aURL.isEmpty() )
|
|
return 0;
|
|
|
|
std::scoped_lock aGuard( m_aMutex );
|
|
checkConnected();
|
|
|
|
sal_uInt64 nPos = m_pSvStream->Tell();
|
|
checkError();
|
|
return nPos;
|
|
}
|
|
|
|
|
|
sal_Int64 SAL_CALL FileStreamWrapper_Impl::getLength( )
|
|
{
|
|
if ( m_aURL.isEmpty() )
|
|
return 0;
|
|
|
|
std::scoped_lock aGuard( m_aMutex );
|
|
checkConnected();
|
|
|
|
checkError();
|
|
|
|
sal_Int64 nEndPos = m_pSvStream->TellEnd();
|
|
|
|
return nEndPos;
|
|
}
|
|
|
|
|
|
void FileStreamWrapper_Impl::checkConnected()
|
|
{
|
|
if ( m_aURL.isEmpty() )
|
|
throw NotConnectedException(OUString(), getXWeak());
|
|
if ( !m_pSvStream )
|
|
{
|
|
m_pSvStream = ::utl::UcbStreamHelper::CreateStream( m_aURL, StreamMode::STD_READ );
|
|
#if OSL_DEBUG_LEVEL > 0
|
|
++nOpenFiles;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
void FileStreamWrapper_Impl::checkError()
|
|
{
|
|
checkConnected();
|
|
|
|
if (m_pSvStream->SvStream::GetError() != ERRCODE_NONE)
|
|
// TODO: really evaluate the error
|
|
throw NotConnectedException(OUString(), getXWeak());
|
|
}
|
|
|
|
|
|
#define COMMIT_RESULT_FAILURE 0
|
|
#define COMMIT_RESULT_NOTHING_TO_DO 1
|
|
#define COMMIT_RESULT_SUCCESS 2
|
|
|
|
static SotClipboardFormatId GetFormatId_Impl( const SvGlobalName& aName )
|
|
{
|
|
if ( aName == SvGlobalName( SO3_SW_CLASSID_60 ) )
|
|
return SotClipboardFormatId::STARWRITER_60;
|
|
if ( aName == SvGlobalName( SO3_SWWEB_CLASSID_60 ) )
|
|
return SotClipboardFormatId::STARWRITERWEB_60;
|
|
if ( aName == SvGlobalName( SO3_SWGLOB_CLASSID_60 ) )
|
|
return SotClipboardFormatId::STARWRITERGLOB_60;
|
|
if ( aName == SvGlobalName( SO3_SDRAW_CLASSID_60 ) )
|
|
return SotClipboardFormatId::STARDRAW_60;
|
|
if ( aName == SvGlobalName( SO3_SIMPRESS_CLASSID_60 ) )
|
|
return SotClipboardFormatId::STARIMPRESS_60;
|
|
if ( aName == SvGlobalName( SO3_SC_CLASSID_60 ) )
|
|
return SotClipboardFormatId::STARCALC_60;
|
|
if ( aName == SvGlobalName( SO3_SCH_CLASSID_60 ) )
|
|
return SotClipboardFormatId::STARCHART_60;
|
|
if ( aName == SvGlobalName( SO3_SM_CLASSID_60 ) )
|
|
return SotClipboardFormatId::STARMATH_60;
|
|
if ( aName == SvGlobalName( SO3_OUT_CLASSID ) ||
|
|
aName == SvGlobalName( SO3_APPLET_CLASSID ) ||
|
|
aName == SvGlobalName( SO3_PLUGIN_CLASSID ) ||
|
|
aName == SvGlobalName( SO3_IFRAME_CLASSID ) )
|
|
// allowed, but not supported
|
|
return SotClipboardFormatId::NONE;
|
|
else
|
|
{
|
|
OSL_FAIL( "Unknown UCB storage format!" );
|
|
return SotClipboardFormatId::NONE;
|
|
}
|
|
}
|
|
|
|
|
|
static SvGlobalName GetClassId_Impl( SotClipboardFormatId nFormat )
|
|
{
|
|
switch ( nFormat )
|
|
{
|
|
case SotClipboardFormatId::STARWRITER_8 :
|
|
case SotClipboardFormatId::STARWRITER_8_TEMPLATE :
|
|
return SvGlobalName( SO3_SW_CLASSID_60 );
|
|
case SotClipboardFormatId::STARWRITERWEB_8 :
|
|
return SvGlobalName( SO3_SWWEB_CLASSID_60 );
|
|
case SotClipboardFormatId::STARWRITERGLOB_8 :
|
|
case SotClipboardFormatId::STARWRITERGLOB_8_TEMPLATE :
|
|
return SvGlobalName( SO3_SWGLOB_CLASSID_60 );
|
|
case SotClipboardFormatId::STARDRAW_8 :
|
|
case SotClipboardFormatId::STARDRAW_8_TEMPLATE :
|
|
return SvGlobalName( SO3_SDRAW_CLASSID_60 );
|
|
case SotClipboardFormatId::STARIMPRESS_8 :
|
|
case SotClipboardFormatId::STARIMPRESS_8_TEMPLATE :
|
|
return SvGlobalName( SO3_SIMPRESS_CLASSID_60 );
|
|
case SotClipboardFormatId::STARCALC_8 :
|
|
case SotClipboardFormatId::STARCALC_8_TEMPLATE :
|
|
return SvGlobalName( SO3_SC_CLASSID_60 );
|
|
case SotClipboardFormatId::STARCHART_8 :
|
|
case SotClipboardFormatId::STARCHART_8_TEMPLATE :
|
|
return SvGlobalName( SO3_SCH_CLASSID_60 );
|
|
case SotClipboardFormatId::STARMATH_8 :
|
|
case SotClipboardFormatId::STARMATH_8_TEMPLATE :
|
|
return SvGlobalName( SO3_SM_CLASSID_60 );
|
|
case SotClipboardFormatId::STARWRITER_60 :
|
|
return SvGlobalName( SO3_SW_CLASSID_60 );
|
|
case SotClipboardFormatId::STARWRITERWEB_60 :
|
|
return SvGlobalName( SO3_SWWEB_CLASSID_60 );
|
|
case SotClipboardFormatId::STARWRITERGLOB_60 :
|
|
return SvGlobalName( SO3_SWGLOB_CLASSID_60 );
|
|
case SotClipboardFormatId::STARDRAW_60 :
|
|
return SvGlobalName( SO3_SDRAW_CLASSID_60 );
|
|
case SotClipboardFormatId::STARIMPRESS_60 :
|
|
return SvGlobalName( SO3_SIMPRESS_CLASSID_60 );
|
|
case SotClipboardFormatId::STARCALC_60 :
|
|
return SvGlobalName( SO3_SC_CLASSID_60 );
|
|
case SotClipboardFormatId::STARCHART_60 :
|
|
return SvGlobalName( SO3_SCH_CLASSID_60 );
|
|
case SotClipboardFormatId::STARMATH_60 :
|
|
return SvGlobalName( SO3_SM_CLASSID_60 );
|
|
default :
|
|
return SvGlobalName();
|
|
}
|
|
}
|
|
|
|
// All storage and streams are refcounted internally; outside of this classes they are only accessible through a handle
|
|
// class, that uses the refcounted object as impl-class.
|
|
|
|
class UCBStorageStream_Impl : public SvRefBase, public SvStream
|
|
{
|
|
virtual ~UCBStorageStream_Impl() override;
|
|
public:
|
|
|
|
virtual std::size_t GetData(void* pData, std::size_t nSize) override;
|
|
virtual std::size_t PutData(const void* pData, std::size_t nSize) override;
|
|
virtual sal_uInt64 SeekPos( sal_uInt64 nPos ) override;
|
|
virtual void SetSize( sal_uInt64 nSize ) override;
|
|
virtual void FlushData() override;
|
|
virtual void ResetError() override;
|
|
|
|
UCBStorageStream* m_pAntiImpl; // only valid if an external reference exists
|
|
|
|
OUString m_aOriginalName;// the original name before accessing the stream
|
|
OUString m_aName; // the actual name ( changed with a Rename command at the parent )
|
|
OUString m_aURL; // the full path name to create the content
|
|
OUString m_aContentType;
|
|
OUString m_aOriginalContentType;
|
|
OString m_aKey;
|
|
::ucbhelper::Content* m_pContent; // the content that provides the data
|
|
Reference<XInputStream> m_rSource; // the stream covering the original data of the content
|
|
std::unique_ptr<SvStream> m_pStream; // the stream worked on; for readonly streams it is the original stream of the content
|
|
// for read/write streams it's a copy into a temporary file
|
|
OUString m_aTempURL; // URL of this temporary stream
|
|
ErrCode m_nError;
|
|
StreamMode m_nMode; // open mode ( read/write/trunc/nocreate/sharing )
|
|
bool m_bSourceRead; // Source still contains useful information
|
|
bool m_bModified; // only modified streams will be sent to the original content
|
|
bool m_bCommited; // sending the streams is coordinated by the root storage of the package
|
|
bool m_bDirect; // the storage and its streams are opened in direct mode; for UCBStorages
|
|
// this means that the root storage does an autocommit when its external
|
|
// reference is destroyed
|
|
bool m_bIsOLEStorage;// an OLEStorage on a UCBStorageStream makes this an Autocommit-stream
|
|
|
|
UCBStorageStream_Impl( const OUString&, StreamMode, UCBStorageStream*, bool,
|
|
bool bRepair, Reference< XProgressHandler > const & xProgress );
|
|
|
|
void Free();
|
|
bool Init();
|
|
bool Clear();
|
|
sal_Int16 Commit(); // if modified and committed: transfer an XInputStream to the content
|
|
void Revert(); // discard all changes
|
|
BaseStorage* CreateStorage();// create an OLE Storage on the UCBStorageStream
|
|
sal_uInt64 GetSize();
|
|
|
|
sal_uInt64 ReadSourceWriteTemporary( sal_uInt64 aLength ); // read aLength from source and copy to temporary,
|
|
// no seeking is produced
|
|
void ReadSourceWriteTemporary(); // read source till the end and copy to temporary,
|
|
|
|
void CopySourceToTemporary(); // same as ReadSourceWriteToTemporary()
|
|
// but the writing is done at the end of temporary
|
|
// pointer position is not changed
|
|
using SvStream::SetError;
|
|
void SetError( ErrCode nError );
|
|
void PrepareCachedForReopen( StreamMode nMode );
|
|
};
|
|
|
|
typedef tools::SvRef<UCBStorageStream_Impl> UCBStorageStream_ImplRef;
|
|
|
|
struct UCBStorageElement_Impl;
|
|
typedef std::vector<std::unique_ptr<UCBStorageElement_Impl>> UCBStorageElementList_Impl;
|
|
|
|
class UCBStorage_Impl : public SvRefBase
|
|
{
|
|
virtual ~UCBStorage_Impl() override;
|
|
public:
|
|
UCBStorage* m_pAntiImpl; // only valid if external references exists
|
|
|
|
OUString m_aName; // the actual name ( changed with a Rename command at the parent )
|
|
OUString m_aURL; // the full path name to create the content
|
|
OUString m_aContentType;
|
|
OUString m_aOriginalContentType;
|
|
std::optional<::ucbhelper::Content> m_oContent; // the content that provides the storage elements
|
|
std::unique_ptr<::utl::TempFileNamed> m_pTempFile; // temporary file, only for storages on stream
|
|
SvStream* m_pSource; // original stream, only for storages on a stream
|
|
ErrCode m_nError;
|
|
StreamMode m_nMode; // open mode ( read/write/trunc/nocreate/sharing )
|
|
bool m_bCommited; // sending the streams is coordinated by the root storage of the package
|
|
bool m_bDirect; // the storage and its streams are opened in direct mode; for UCBStorages
|
|
// this means that the root storage does an autocommit when its external
|
|
// reference is destroyed
|
|
bool m_bIsRoot; // marks this storage as root storages that manages all commits and reverts
|
|
bool m_bIsLinked;
|
|
bool m_bListCreated;
|
|
SotClipboardFormatId m_nFormat;
|
|
OUString m_aUserTypeName;
|
|
SvGlobalName m_aClassId;
|
|
|
|
UCBStorageElementList_Impl m_aChildrenList;
|
|
|
|
bool m_bRepairPackage;
|
|
Reference< XProgressHandler > m_xProgressHandler;
|
|
|
|
UCBStorage_Impl( const ::ucbhelper::Content&, const OUString&, StreamMode, UCBStorage*, bool,
|
|
bool, bool = false, Reference< XProgressHandler > const & = Reference< XProgressHandler >() );
|
|
UCBStorage_Impl( const OUString&, StreamMode, UCBStorage*, bool, bool,
|
|
bool, Reference< XProgressHandler > const & );
|
|
UCBStorage_Impl( SvStream&, UCBStorage*, bool );
|
|
void Init();
|
|
sal_Int16 Commit();
|
|
void Revert();
|
|
bool Insert( ::ucbhelper::Content *pContent );
|
|
UCBStorage_Impl* OpenStorage( UCBStorageElement_Impl* pElement, StreamMode nMode, bool bDirect );
|
|
void OpenStream( UCBStorageElement_Impl*, StreamMode, bool );
|
|
void SetProps( const Sequence < Sequence < PropertyValue > >& rSequence, const OUString& );
|
|
void GetProps( sal_Int32&, Sequence < Sequence < PropertyValue > >& rSequence, const OUString& );
|
|
sal_Int32 GetObjectCount();
|
|
void ReadContent();
|
|
void CreateContent();
|
|
::ucbhelper::Content* GetContent()
|
|
{
|
|
if ( !m_oContent )
|
|
CreateContent();
|
|
return m_oContent ? &*m_oContent : nullptr;
|
|
}
|
|
UCBStorageElementList_Impl& GetChildrenList()
|
|
{
|
|
const ErrCode nError = m_nError;
|
|
ReadContent();
|
|
if ( m_nMode & StreamMode::WRITE )
|
|
{
|
|
m_nError = nError;
|
|
if ( m_pAntiImpl )
|
|
{
|
|
m_pAntiImpl->ResetError();
|
|
m_pAntiImpl->SetError( nError );
|
|
}
|
|
}
|
|
return m_aChildrenList;
|
|
}
|
|
|
|
void SetError( ErrCode nError );
|
|
};
|
|
|
|
typedef tools::SvRef<UCBStorage_Impl> UCBStorage_ImplRef;
|
|
|
|
// this struct contains all necessary information on an element inside a UCBStorage
|
|
struct UCBStorageElement_Impl
|
|
{
|
|
OUString m_aName; // the actual URL relative to the root "folder"
|
|
OUString m_aOriginalName;// the original name in the content
|
|
sal_uInt64 m_nSize;
|
|
bool m_bIsFolder; // Only true when it is a UCBStorage !
|
|
bool m_bIsStorage; // Also true when it is an OLEStorage !
|
|
bool m_bIsRemoved; // element will be removed on commit
|
|
bool m_bIsInserted; // element will be removed on revert
|
|
UCBStorage_ImplRef m_xStorage; // reference to the "real" storage
|
|
UCBStorageStream_ImplRef m_xStream; // reference to the "real" stream
|
|
|
|
UCBStorageElement_Impl( const OUString& rName,
|
|
bool bIsFolder = false, sal_uInt64 nSize = 0 )
|
|
: m_aName( rName )
|
|
, m_aOriginalName( rName )
|
|
, m_nSize( nSize )
|
|
, m_bIsFolder( bIsFolder )
|
|
, m_bIsStorage( bIsFolder )
|
|
, m_bIsRemoved( false )
|
|
, m_bIsInserted( false )
|
|
{
|
|
}
|
|
|
|
::ucbhelper::Content* GetContent();
|
|
bool IsModified() const;
|
|
const OUString & GetContentType() const;
|
|
void SetContentType( const OUString& );
|
|
const OUString & GetOriginalContentType() const;
|
|
bool IsLoaded() const
|
|
{ return m_xStream.is() || m_xStorage.is(); }
|
|
};
|
|
|
|
::ucbhelper::Content* UCBStorageElement_Impl::GetContent()
|
|
{
|
|
if ( m_xStream.is() )
|
|
return m_xStream->m_pContent;
|
|
else if ( m_xStorage.is() )
|
|
return m_xStorage->GetContent();
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
const OUString & UCBStorageElement_Impl::GetContentType() const
|
|
{
|
|
if ( m_xStream.is() )
|
|
return m_xStream->m_aContentType;
|
|
else if ( m_xStorage.is() )
|
|
return m_xStorage->m_aContentType;
|
|
else
|
|
{
|
|
OSL_FAIL("Element not loaded!");
|
|
return EMPTY_OUSTRING;
|
|
}
|
|
}
|
|
|
|
void UCBStorageElement_Impl::SetContentType( const OUString& rType )
|
|
{
|
|
if ( m_xStream.is() ) {
|
|
m_xStream->m_aContentType = m_xStream->m_aOriginalContentType = rType;
|
|
}
|
|
else if ( m_xStorage.is() ) {
|
|
m_xStorage->m_aContentType = m_xStorage->m_aOriginalContentType = rType;
|
|
}
|
|
else {
|
|
OSL_FAIL("Element not loaded!");
|
|
}
|
|
}
|
|
|
|
const OUString & UCBStorageElement_Impl::GetOriginalContentType() const
|
|
{
|
|
if ( m_xStream.is() )
|
|
return m_xStream->m_aOriginalContentType;
|
|
else if ( m_xStorage.is() )
|
|
return m_xStorage->m_aOriginalContentType;
|
|
else
|
|
return EMPTY_OUSTRING;
|
|
}
|
|
|
|
bool UCBStorageElement_Impl::IsModified() const
|
|
{
|
|
bool bModified = m_bIsRemoved || m_bIsInserted || m_aName != m_aOriginalName;
|
|
if ( bModified )
|
|
{
|
|
if ( m_xStream.is() )
|
|
bModified = m_xStream->m_aContentType != m_xStream->m_aOriginalContentType;
|
|
else if ( m_xStorage.is() )
|
|
bModified = m_xStorage->m_aContentType != m_xStorage->m_aOriginalContentType;
|
|
}
|
|
|
|
return bModified;
|
|
}
|
|
|
|
UCBStorageStream_Impl::UCBStorageStream_Impl( const OUString& rName, StreamMode nMode, UCBStorageStream* pStream, bool bDirect, bool bRepair, Reference< XProgressHandler > const & xProgress )
|
|
: m_pAntiImpl( pStream )
|
|
, m_aURL( rName )
|
|
, m_pContent( nullptr )
|
|
, m_nError( ERRCODE_NONE )
|
|
, m_nMode( nMode )
|
|
, m_bSourceRead( !( nMode & StreamMode::TRUNC ) )
|
|
, m_bModified( false )
|
|
, m_bCommited( false )
|
|
, m_bDirect( bDirect )
|
|
, m_bIsOLEStorage( false )
|
|
{
|
|
// name is last segment in URL
|
|
INetURLObject aObj( rName );
|
|
m_aName = m_aOriginalName = aObj.GetLastName();
|
|
try
|
|
{
|
|
// create the content
|
|
Reference< css::ucb::XCommandEnvironment > xComEnv;
|
|
|
|
OUString aTemp( rName );
|
|
|
|
if ( bRepair )
|
|
{
|
|
xComEnv = new ::ucbhelper::CommandEnvironment( Reference< css::task::XInteractionHandler >(), xProgress );
|
|
aTemp += "?repairpackage";
|
|
}
|
|
|
|
m_pContent = new ::ucbhelper::Content( aTemp, xComEnv, comphelper::getProcessComponentContext() );
|
|
}
|
|
catch (const ContentCreationException&)
|
|
{
|
|
// content could not be created
|
|
SetError( SVSTREAM_CANNOT_MAKE );
|
|
}
|
|
catch (const RuntimeException&)
|
|
{
|
|
// any other error - not specified
|
|
SetError( ERRCODE_IO_GENERAL );
|
|
}
|
|
}
|
|
|
|
UCBStorageStream_Impl::~UCBStorageStream_Impl()
|
|
{
|
|
if( m_rSource.is() )
|
|
m_rSource.clear();
|
|
|
|
m_pStream.reset();
|
|
|
|
if (!m_aTempURL.isEmpty())
|
|
osl::File::remove(m_aTempURL);
|
|
|
|
delete m_pContent;
|
|
}
|
|
|
|
|
|
bool UCBStorageStream_Impl::Init()
|
|
{
|
|
if( !m_pStream )
|
|
{
|
|
// no temporary stream was created
|
|
// create one
|
|
|
|
if ( m_aTempURL.isEmpty() )
|
|
m_aTempURL = ::utl::CreateTempURL();
|
|
|
|
m_pStream = ::utl::UcbStreamHelper::CreateStream( m_aTempURL, StreamMode::STD_READWRITE, true /* bFileExists */ );
|
|
#if OSL_DEBUG_LEVEL > 0
|
|
++nOpenFiles;
|
|
#endif
|
|
|
|
if( !m_pStream )
|
|
{
|
|
OSL_FAIL( "Suspicious temporary stream creation!" );
|
|
SetError( SVSTREAM_CANNOT_MAKE );
|
|
return false;
|
|
}
|
|
|
|
SetError( m_pStream->GetError() );
|
|
}
|
|
|
|
if( m_bSourceRead && !m_rSource.is() )
|
|
{
|
|
// source file contain useful information and is not opened
|
|
// open it from the point of noncopied data
|
|
|
|
try
|
|
{
|
|
m_rSource = m_pContent->openStream();
|
|
}
|
|
catch (const Exception&)
|
|
{
|
|
// usually means that stream could not be opened
|
|
}
|
|
|
|
if( m_rSource.is() )
|
|
{
|
|
m_pStream->Seek( STREAM_SEEK_TO_END );
|
|
|
|
try
|
|
{
|
|
m_rSource->skipBytes( m_pStream->Tell() );
|
|
}
|
|
catch (const BufferSizeExceededException&)
|
|
{
|
|
// the temporary stream already contain all the data
|
|
m_bSourceRead = false;
|
|
}
|
|
catch (const Exception&)
|
|
{
|
|
// something is really wrong
|
|
m_bSourceRead = false;
|
|
OSL_FAIL( "Can not operate original stream!" );
|
|
SetError( SVSTREAM_CANNOT_MAKE );
|
|
}
|
|
|
|
m_pStream->Seek( 0 );
|
|
}
|
|
else
|
|
{
|
|
// if the new file is edited then no source exist
|
|
m_bSourceRead = false;
|
|
//SetError( SVSTREAM_CANNOT_MAKE );
|
|
}
|
|
}
|
|
|
|
DBG_ASSERT( m_rSource.is() || !m_bSourceRead, "Unreadable source stream!" );
|
|
|
|
return true;
|
|
}
|
|
|
|
void UCBStorageStream_Impl::ReadSourceWriteTemporary()
|
|
{
|
|
// read source stream till the end and copy all the data to
|
|
// the current position of the temporary stream
|
|
|
|
if( m_bSourceRead )
|
|
{
|
|
Sequence<sal_Int8> aData(32000);
|
|
|
|
try
|
|
{
|
|
sal_Int32 aReaded;
|
|
do
|
|
{
|
|
aReaded = m_rSource->readBytes( aData, 32000 );
|
|
m_pStream->WriteBytes(aData.getConstArray(), aReaded);
|
|
} while( aReaded == 32000 );
|
|
}
|
|
catch (const Exception &)
|
|
{
|
|
TOOLS_WARN_EXCEPTION("sot", "");
|
|
}
|
|
}
|
|
|
|
m_bSourceRead = false;
|
|
}
|
|
|
|
sal_uInt64 UCBStorageStream_Impl::ReadSourceWriteTemporary(sal_uInt64 aLength)
|
|
{
|
|
// read aLength byte from the source stream and copy them to the current
|
|
// position of the temporary stream
|
|
|
|
sal_uInt64 aResult = 0;
|
|
|
|
if( m_bSourceRead )
|
|
{
|
|
Sequence<sal_Int8> aData(32000);
|
|
|
|
try
|
|
{
|
|
|
|
sal_Int32 aReaded = 32000;
|
|
|
|
for (sal_uInt64 nInd = 0; nInd < aLength && aReaded == 32000 ; nInd += 32000)
|
|
{
|
|
sal_Int32 aToCopy = std::min<sal_Int32>( aLength - nInd, 32000 );
|
|
aReaded = m_rSource->readBytes( aData, aToCopy );
|
|
aResult += m_pStream->WriteBytes(aData.getConstArray(), aReaded);
|
|
}
|
|
|
|
if( aResult < aLength )
|
|
m_bSourceRead = false;
|
|
}
|
|
catch( const Exception & )
|
|
{
|
|
TOOLS_WARN_EXCEPTION("sot", "");
|
|
}
|
|
}
|
|
|
|
return aResult;
|
|
}
|
|
|
|
void UCBStorageStream_Impl::CopySourceToTemporary()
|
|
{
|
|
// current position of the temporary stream is not changed
|
|
if( m_bSourceRead )
|
|
{
|
|
sal_uInt64 aPos = m_pStream->Tell();
|
|
m_pStream->Seek( STREAM_SEEK_TO_END );
|
|
ReadSourceWriteTemporary();
|
|
m_pStream->Seek( aPos );
|
|
}
|
|
}
|
|
|
|
// UCBStorageStream_Impl must have a SvStream interface, because it then can be used as underlying stream
|
|
// of an OLEStorage; so every write access caused by storage operations marks the UCBStorageStream as modified
|
|
std::size_t UCBStorageStream_Impl::GetData(void* pData, std::size_t const nSize)
|
|
{
|
|
std::size_t aResult = 0;
|
|
|
|
if( !Init() )
|
|
return 0;
|
|
|
|
|
|
// read data that is in temporary stream
|
|
aResult = m_pStream->ReadBytes( pData, nSize );
|
|
if( m_bSourceRead && aResult < nSize )
|
|
{
|
|
// read the tail of the data from original stream
|
|
// copy this tail to the temporary stream
|
|
|
|
std::size_t aToRead = nSize - aResult;
|
|
pData = static_cast<void*>( static_cast<char*>(pData) + aResult );
|
|
|
|
try
|
|
{
|
|
Sequence<sal_Int8> aData( aToRead );
|
|
std::size_t aReaded = m_rSource->readBytes( aData, aToRead );
|
|
aResult += m_pStream->WriteBytes(static_cast<const void*>(aData.getConstArray()), aReaded);
|
|
memcpy( pData, aData.getArray(), aReaded );
|
|
}
|
|
catch (const Exception &)
|
|
{
|
|
TOOLS_WARN_EXCEPTION("sot", "");
|
|
}
|
|
|
|
if( aResult < nSize )
|
|
m_bSourceRead = false;
|
|
}
|
|
|
|
return aResult;
|
|
}
|
|
|
|
std::size_t UCBStorageStream_Impl::PutData(const void* pData, std::size_t const nSize)
|
|
{
|
|
if ( !(m_nMode & StreamMode::WRITE) )
|
|
{
|
|
SetError( ERRCODE_IO_ACCESSDENIED );
|
|
return 0; // ?mav?
|
|
}
|
|
|
|
if( !nSize || !Init() )
|
|
return 0;
|
|
|
|
std::size_t aResult = m_pStream->WriteBytes( pData, nSize );
|
|
|
|
m_bModified = aResult > 0;
|
|
|
|
return aResult;
|
|
|
|
}
|
|
|
|
sal_uInt64 UCBStorageStream_Impl::SeekPos(sal_uInt64 const nPos)
|
|
{
|
|
// check if a truncated STREAM_SEEK_TO_END was passed
|
|
assert(nPos != SAL_MAX_UINT32);
|
|
|
|
if( !Init() )
|
|
return 0;
|
|
|
|
sal_uInt64 aResult;
|
|
|
|
if( nPos == STREAM_SEEK_TO_END )
|
|
{
|
|
m_pStream->Seek( STREAM_SEEK_TO_END );
|
|
ReadSourceWriteTemporary();
|
|
aResult = m_pStream->Tell();
|
|
}
|
|
else
|
|
{
|
|
// the problem is that even if nPos is larger the length
|
|
// of the stream, the stream pointer will be moved to this position
|
|
// so we have to check if temporary stream does not contain required position
|
|
|
|
if( m_pStream->Tell() > nPos
|
|
|| m_pStream->Seek( STREAM_SEEK_TO_END ) > nPos )
|
|
{
|
|
// no copying is required
|
|
aResult = m_pStream->Seek( nPos );
|
|
}
|
|
else
|
|
{
|
|
// the temp stream pointer points to the end now
|
|
aResult = m_pStream->Tell();
|
|
|
|
if( aResult < nPos )
|
|
{
|
|
if( m_bSourceRead )
|
|
{
|
|
aResult += ReadSourceWriteTemporary( nPos - aResult );
|
|
if( aResult < nPos )
|
|
m_bSourceRead = false;
|
|
|
|
DBG_ASSERT( aResult == m_pStream->Tell(), "Error in stream arithmetic!\n" );
|
|
}
|
|
|
|
if( (m_nMode & StreamMode::WRITE) && !m_bSourceRead && aResult < nPos )
|
|
{
|
|
// it means that all the Source stream was copied already
|
|
// but the required position still was not reached
|
|
// for writable streams it should be done
|
|
m_pStream->SetStreamSize( nPos );
|
|
aResult = m_pStream->Seek( STREAM_SEEK_TO_END );
|
|
DBG_ASSERT( aResult == nPos, "Error in stream arithmetic!\n" );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return aResult;
|
|
}
|
|
|
|
void UCBStorageStream_Impl::SetSize(sal_uInt64 const nSize)
|
|
{
|
|
if ( !(m_nMode & StreamMode::WRITE) )
|
|
{
|
|
SetError( ERRCODE_IO_ACCESSDENIED );
|
|
return;
|
|
}
|
|
|
|
if( !Init() )
|
|
return;
|
|
|
|
m_bModified = true;
|
|
|
|
if( m_bSourceRead )
|
|
{
|
|
sal_uInt64 const aPos = m_pStream->Tell();
|
|
m_pStream->Seek( STREAM_SEEK_TO_END );
|
|
if( m_pStream->Tell() < nSize )
|
|
ReadSourceWriteTemporary( nSize - m_pStream->Tell() );
|
|
m_pStream->Seek( aPos );
|
|
}
|
|
|
|
m_pStream->SetStreamSize( nSize );
|
|
m_bSourceRead = false;
|
|
}
|
|
|
|
void UCBStorageStream_Impl::FlushData()
|
|
{
|
|
if( m_pStream )
|
|
{
|
|
CopySourceToTemporary();
|
|
m_pStream->Flush();
|
|
}
|
|
|
|
m_bCommited = true;
|
|
}
|
|
|
|
void UCBStorageStream_Impl::SetError( ErrCode nErr )
|
|
{
|
|
if ( !m_nError )
|
|
{
|
|
m_nError = nErr;
|
|
SvStream::SetError( nErr );
|
|
if ( m_pAntiImpl ) m_pAntiImpl->SetError( nErr );
|
|
}
|
|
}
|
|
|
|
void UCBStorageStream_Impl::ResetError()
|
|
{
|
|
m_nError = ERRCODE_NONE;
|
|
SvStream::ResetError();
|
|
if ( m_pAntiImpl )
|
|
m_pAntiImpl->ResetError();
|
|
}
|
|
|
|
sal_uInt64 UCBStorageStream_Impl::GetSize()
|
|
{
|
|
if( !Init() )
|
|
return 0;
|
|
|
|
sal_uInt64 nPos = m_pStream->Tell();
|
|
m_pStream->Seek( STREAM_SEEK_TO_END );
|
|
ReadSourceWriteTemporary();
|
|
sal_uInt64 nRet = m_pStream->Tell();
|
|
m_pStream->Seek( nPos );
|
|
|
|
return nRet;
|
|
}
|
|
|
|
BaseStorage* UCBStorageStream_Impl::CreateStorage()
|
|
{
|
|
// create an OLEStorage on a SvStream ( = this )
|
|
// it gets the root attribute because otherwise it would probably not write before my root is committed
|
|
UCBStorageStream* pNewStorageStream = new UCBStorageStream( this );
|
|
Storage *pStorage = new Storage( *pNewStorageStream, m_bDirect );
|
|
|
|
// GetError() call clears error code for OLE storages, must be changed in future
|
|
const ErrCode nTmpErr = pStorage->GetError();
|
|
pStorage->SetError( nTmpErr );
|
|
|
|
m_bIsOLEStorage = !nTmpErr;
|
|
return static_cast< BaseStorage* > ( pStorage );
|
|
}
|
|
|
|
sal_Int16 UCBStorageStream_Impl::Commit()
|
|
{
|
|
// send stream to the original content
|
|
// the parent storage is responsible for the correct handling of deleted contents
|
|
if ( m_bCommited || m_bIsOLEStorage || m_bDirect )
|
|
{
|
|
// modified streams with OLEStorages on it have autocommit; it is assumed that the OLEStorage
|
|
// was committed as well ( if not opened in direct mode )
|
|
|
|
if ( m_bModified )
|
|
{
|
|
try
|
|
{
|
|
CopySourceToTemporary();
|
|
|
|
// release all stream handles
|
|
Free();
|
|
|
|
// the temporary file does not exist only for truncated streams
|
|
DBG_ASSERT( !m_aTempURL.isEmpty() || ( m_nMode & StreamMode::TRUNC ), "No temporary file to read from!");
|
|
if ( m_aTempURL.isEmpty() && !( m_nMode & StreamMode::TRUNC ) )
|
|
throw RuntimeException();
|
|
|
|
InsertCommandArgument aArg;
|
|
// create wrapper to stream that is only used while reading inside package component
|
|
aArg.Data.set(new FileStreamWrapper_Impl(m_aTempURL));
|
|
aArg.ReplaceExisting = true;
|
|
m_pContent->executeCommand( u"insert"_ustr, Any(aArg) );
|
|
|
|
// wrapper now controls lifetime of temporary file
|
|
m_aTempURL.clear();
|
|
|
|
INetURLObject aObj( m_aURL );
|
|
aObj.setName( m_aName );
|
|
m_aURL = aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
|
|
m_bModified = false;
|
|
m_bSourceRead = true;
|
|
}
|
|
catch (const CommandAbortedException&)
|
|
{
|
|
// any command wasn't executed successfully - not specified
|
|
SetError( ERRCODE_IO_GENERAL );
|
|
return COMMIT_RESULT_FAILURE;
|
|
}
|
|
catch (const RuntimeException&)
|
|
{
|
|
// any other error - not specified
|
|
SetError( ERRCODE_IO_GENERAL );
|
|
return COMMIT_RESULT_FAILURE;
|
|
}
|
|
catch (const Exception&)
|
|
{
|
|
// any other error - not specified
|
|
SetError( ERRCODE_IO_GENERAL );
|
|
return COMMIT_RESULT_FAILURE;
|
|
}
|
|
|
|
m_bCommited = false;
|
|
return COMMIT_RESULT_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return COMMIT_RESULT_NOTHING_TO_DO;
|
|
}
|
|
|
|
void UCBStorageStream_Impl::Revert()
|
|
{
|
|
// if an OLEStorage is created on this stream, no "revert" is necessary because OLEStorages do nothing on "Revert" !
|
|
if ( m_bCommited )
|
|
{
|
|
OSL_FAIL("Revert while commit is in progress!" );
|
|
return; // ???
|
|
}
|
|
|
|
Free();
|
|
if ( !m_aTempURL.isEmpty() )
|
|
{
|
|
osl::File::remove(m_aTempURL);
|
|
m_aTempURL.clear();
|
|
}
|
|
|
|
m_bSourceRead = false;
|
|
try
|
|
{
|
|
m_rSource = m_pContent->openStream();
|
|
if( m_rSource.is() )
|
|
{
|
|
if ( m_pAntiImpl && ( m_nMode & StreamMode::TRUNC ) )
|
|
// stream is in use and should be truncated
|
|
m_bSourceRead = false;
|
|
else
|
|
{
|
|
m_nMode &= ~StreamMode::TRUNC;
|
|
m_bSourceRead = true;
|
|
}
|
|
}
|
|
else
|
|
SetError( SVSTREAM_CANNOT_MAKE );
|
|
}
|
|
catch (const ContentCreationException&)
|
|
{
|
|
SetError( ERRCODE_IO_GENERAL );
|
|
}
|
|
catch (const RuntimeException&)
|
|
{
|
|
SetError( ERRCODE_IO_GENERAL );
|
|
}
|
|
catch (const Exception&)
|
|
{
|
|
}
|
|
|
|
m_bModified = false;
|
|
m_aName = m_aOriginalName;
|
|
m_aContentType = m_aOriginalContentType;
|
|
}
|
|
|
|
bool UCBStorageStream_Impl::Clear()
|
|
{
|
|
bool bRet = ( m_pAntiImpl == nullptr );
|
|
DBG_ASSERT( bRet, "Removing used stream!" );
|
|
if( bRet )
|
|
{
|
|
Free();
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
void UCBStorageStream_Impl::Free()
|
|
{
|
|
#if OSL_DEBUG_LEVEL > 0
|
|
if ( m_pStream )
|
|
{
|
|
if ( !m_aTempURL.isEmpty() )
|
|
--nOpenFiles;
|
|
else
|
|
--nOpenStreams;
|
|
}
|
|
#endif
|
|
|
|
m_rSource.clear();
|
|
m_pStream.reset();
|
|
}
|
|
|
|
void UCBStorageStream_Impl::PrepareCachedForReopen( StreamMode nMode )
|
|
{
|
|
bool isWritable = bool( m_nMode & StreamMode::WRITE );
|
|
if ( isWritable )
|
|
{
|
|
// once stream was writable, never reset to readonly
|
|
nMode |= StreamMode::WRITE;
|
|
}
|
|
|
|
m_nMode = nMode;
|
|
Free();
|
|
|
|
if ( nMode & StreamMode::TRUNC )
|
|
{
|
|
m_bSourceRead = false; // usually it should be 0 already but just in case...
|
|
|
|
if ( !m_aTempURL.isEmpty() )
|
|
{
|
|
osl::File::remove(m_aTempURL);
|
|
m_aTempURL.clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
UCBStorageStream::UCBStorageStream( const OUString& rName, StreamMode nMode, bool bDirect, bool bRepair, Reference< XProgressHandler > const & xProgress )
|
|
{
|
|
// pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized
|
|
// to class UCBStorageStream !
|
|
pImp = new UCBStorageStream_Impl( rName, nMode, this, bDirect, bRepair, xProgress );
|
|
pImp->AddFirstRef(); // use direct refcounting because in header file only a pointer should be used
|
|
StorageBase::m_nMode = pImp->m_nMode;
|
|
}
|
|
|
|
UCBStorageStream::UCBStorageStream( UCBStorageStream_Impl *pImpl )
|
|
: pImp( pImpl )
|
|
{
|
|
pImp->AddFirstRef(); // use direct refcounting because in header file only a pointer should be used
|
|
pImp->m_pAntiImpl = this;
|
|
SetError( pImp->m_nError );
|
|
StorageBase::m_nMode = pImp->m_nMode;
|
|
}
|
|
|
|
UCBStorageStream::~UCBStorageStream()
|
|
{
|
|
if ( pImp->m_nMode & StreamMode::WRITE )
|
|
pImp->Flush();
|
|
pImp->m_pAntiImpl = nullptr;
|
|
pImp->Free();
|
|
pImp->ReleaseRef();
|
|
}
|
|
|
|
sal_Int32 UCBStorageStream::Read( void * pData, sal_Int32 nSize )
|
|
{
|
|
//return pImp->m_pStream->Read( pData, nSize );
|
|
return pImp->GetData( pData, nSize );
|
|
}
|
|
|
|
sal_Int32 UCBStorageStream::Write( const void* pData, sal_Int32 nSize )
|
|
{
|
|
return pImp->PutData( pData, nSize );
|
|
}
|
|
|
|
sal_uInt64 UCBStorageStream::Seek( sal_uInt64 nPos )
|
|
{
|
|
//return pImp->m_pStream->Seek( nPos );
|
|
return pImp->Seek( nPos );
|
|
}
|
|
|
|
sal_uInt64 UCBStorageStream::Tell()
|
|
{
|
|
if( !pImp->Init() )
|
|
return 0;
|
|
return pImp->m_pStream->Tell();
|
|
}
|
|
|
|
void UCBStorageStream::Flush()
|
|
{
|
|
// streams are never really transacted, so flush also means commit !
|
|
Commit();
|
|
}
|
|
|
|
bool UCBStorageStream::SetSize( sal_uInt64 nNewSize )
|
|
{
|
|
pImp->SetSize( nNewSize );
|
|
return !pImp->GetError();
|
|
}
|
|
|
|
bool UCBStorageStream::Validate( bool bWrite ) const
|
|
{
|
|
return ( !bWrite || ( pImp->m_nMode & StreamMode::WRITE ) );
|
|
}
|
|
|
|
bool UCBStorageStream::ValidateMode( StreamMode m ) const
|
|
{
|
|
// ???
|
|
if( m == ( StreamMode::READ | StreamMode::TRUNC ) ) // from stg.cxx
|
|
return true;
|
|
if( ( m & StreamMode::READWRITE) == StreamMode::READ )
|
|
{
|
|
// only SHARE_DENYWRITE or SHARE_DENYALL allowed
|
|
if( ( m & StreamMode::SHARE_DENYWRITE )
|
|
|| ( m & StreamMode::SHARE_DENYALL ) )
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// only SHARE_DENYALL allowed
|
|
// storages open in r/o mode are OK, since only
|
|
// the commit may fail
|
|
if( m & StreamMode::SHARE_DENYALL )
|
|
return true;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
SvStream* UCBStorageStream::GetModifySvStream()
|
|
{
|
|
return static_cast<SvStream*>(pImp);
|
|
}
|
|
|
|
bool UCBStorageStream::Equals( const BaseStorageStream& rStream ) const
|
|
{
|
|
// ???
|
|
return static_cast<BaseStorageStream const *>(this) == &rStream;
|
|
}
|
|
|
|
bool UCBStorageStream::Commit()
|
|
{
|
|
// mark this stream for sending it on root commit
|
|
pImp->FlushData();
|
|
return true;
|
|
}
|
|
|
|
void UCBStorageStream::CopyTo( BaseStorageStream* pDestStm )
|
|
{
|
|
if( !pImp->Init() )
|
|
return;
|
|
|
|
UCBStorageStream* pStg = dynamic_cast<UCBStorageStream*>( pDestStm );
|
|
if ( pStg )
|
|
pStg->pImp->m_aContentType = pImp->m_aContentType;
|
|
|
|
pDestStm->SetSize( 0 );
|
|
Seek( STREAM_SEEK_TO_END );
|
|
sal_Int32 n = Tell();
|
|
if( n < 0 )
|
|
return;
|
|
|
|
if( !pDestStm->SetSize( n ) || !n )
|
|
return;
|
|
|
|
std::unique_ptr<sal_uInt8[]> p(new sal_uInt8[ 4096 ]);
|
|
Seek( 0 );
|
|
pDestStm->Seek( 0 );
|
|
while( n )
|
|
{
|
|
sal_Int32 nn = n;
|
|
if( nn > 4096 )
|
|
nn = 4096;
|
|
if( Read( p.get(), nn ) != nn )
|
|
break;
|
|
if( pDestStm->Write( p.get(), nn ) != nn )
|
|
break;
|
|
n -= nn;
|
|
}
|
|
}
|
|
|
|
bool UCBStorageStream::SetProperty( const OUString& rName, const css::uno::Any& rValue )
|
|
{
|
|
if ( rName == "Title")
|
|
return false;
|
|
|
|
if ( rName == "MediaType")
|
|
{
|
|
OUString aTmp;
|
|
rValue >>= aTmp;
|
|
pImp->m_aContentType = aTmp;
|
|
}
|
|
|
|
try
|
|
{
|
|
if ( pImp->m_pContent )
|
|
{
|
|
pImp->m_pContent->setPropertyValue( rName, rValue );
|
|
return true;
|
|
}
|
|
}
|
|
catch (const Exception&)
|
|
{
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
sal_uInt64 UCBStorageStream::GetSize() const
|
|
{
|
|
return pImp->GetSize();
|
|
}
|
|
|
|
UCBStorage::UCBStorage( SvStream& rStrm, bool bDirect )
|
|
{
|
|
// pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized
|
|
// to class UCBStorage !
|
|
pImp = new UCBStorage_Impl( rStrm, this, bDirect );
|
|
|
|
pImp->AddFirstRef();
|
|
pImp->Init();
|
|
StorageBase::m_nMode = pImp->m_nMode;
|
|
}
|
|
|
|
UCBStorage::UCBStorage( const ::ucbhelper::Content& rContent, const OUString& rName, StreamMode nMode, bool bDirect, bool bIsRoot )
|
|
{
|
|
// pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized
|
|
// to class UCBStorage !
|
|
pImp = new UCBStorage_Impl( rContent, rName, nMode, this, bDirect, bIsRoot );
|
|
pImp->AddFirstRef();
|
|
pImp->Init();
|
|
StorageBase::m_nMode = pImp->m_nMode;
|
|
}
|
|
|
|
UCBStorage::UCBStorage( const OUString& rName, StreamMode nMode, bool bDirect, bool bIsRoot, bool bIsRepair, Reference< XProgressHandler > const & xProgressHandler )
|
|
{
|
|
// pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized
|
|
// to class UCBStorage !
|
|
pImp = new UCBStorage_Impl( rName, nMode, this, bDirect, bIsRoot, bIsRepair, xProgressHandler );
|
|
pImp->AddFirstRef();
|
|
pImp->Init();
|
|
StorageBase::m_nMode = pImp->m_nMode;
|
|
}
|
|
|
|
UCBStorage::UCBStorage( const OUString& rName, StreamMode nMode, bool bDirect, bool bIsRoot )
|
|
{
|
|
// pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized
|
|
// to class UCBStorage !
|
|
pImp = new UCBStorage_Impl( rName, nMode, this, bDirect, bIsRoot, false, Reference< XProgressHandler >() );
|
|
pImp->AddFirstRef();
|
|
pImp->Init();
|
|
StorageBase::m_nMode = pImp->m_nMode;
|
|
}
|
|
|
|
UCBStorage::UCBStorage( UCBStorage_Impl *pImpl )
|
|
: pImp( pImpl )
|
|
{
|
|
pImp->m_pAntiImpl = this;
|
|
SetError( pImp->m_nError );
|
|
pImp->AddFirstRef(); // use direct refcounting because in header file only a pointer should be used
|
|
StorageBase::m_nMode = pImp->m_nMode;
|
|
}
|
|
|
|
UCBStorage::~UCBStorage()
|
|
{
|
|
if ( pImp->m_bIsRoot && pImp->m_bDirect && ( !pImp->m_pTempFile || pImp->m_pSource ) )
|
|
// DirectMode is simulated with an AutoCommit
|
|
Commit();
|
|
|
|
pImp->m_pAntiImpl = nullptr;
|
|
pImp->ReleaseRef();
|
|
}
|
|
|
|
UCBStorage_Impl::UCBStorage_Impl( const ::ucbhelper::Content& rContent, const OUString& rName, StreamMode nMode, UCBStorage* pStorage, bool bDirect, bool bIsRoot, bool bIsRepair, Reference< XProgressHandler > const & xProgressHandler )
|
|
: m_pAntiImpl( pStorage )
|
|
, m_oContent( rContent )
|
|
, m_pSource( nullptr )
|
|
//, m_pStream( NULL )
|
|
, m_nError( ERRCODE_NONE )
|
|
, m_nMode( nMode )
|
|
, m_bCommited( false )
|
|
, m_bDirect( bDirect )
|
|
, m_bIsRoot( bIsRoot )
|
|
, m_bIsLinked( true )
|
|
, m_bListCreated( false )
|
|
, m_nFormat( SotClipboardFormatId::NONE )
|
|
, m_bRepairPackage( bIsRepair )
|
|
, m_xProgressHandler( xProgressHandler )
|
|
{
|
|
OUString aName( rName );
|
|
if( aName.isEmpty() )
|
|
{
|
|
// no name given = use temporary name!
|
|
DBG_ASSERT( m_bIsRoot, "SubStorage must have a name!" );
|
|
m_pTempFile.reset(new ::utl::TempFileNamed);
|
|
m_pTempFile->EnableKillingFile();
|
|
m_aName = aName = m_pTempFile->GetURL();
|
|
}
|
|
|
|
m_aURL = rName;
|
|
}
|
|
|
|
UCBStorage_Impl::UCBStorage_Impl( const OUString& rName, StreamMode nMode, UCBStorage* pStorage, bool bDirect, bool bIsRoot, bool bIsRepair, Reference< XProgressHandler > const & xProgressHandler )
|
|
: m_pAntiImpl( pStorage )
|
|
, m_pSource( nullptr )
|
|
//, m_pStream( NULL )
|
|
, m_nError( ERRCODE_NONE )
|
|
, m_nMode( nMode )
|
|
, m_bCommited( false )
|
|
, m_bDirect( bDirect )
|
|
, m_bIsRoot( bIsRoot )
|
|
, m_bIsLinked( false )
|
|
, m_bListCreated( false )
|
|
, m_nFormat( SotClipboardFormatId::NONE )
|
|
, m_bRepairPackage( bIsRepair )
|
|
, m_xProgressHandler( xProgressHandler )
|
|
{
|
|
OUString aName( rName );
|
|
if( aName.isEmpty() )
|
|
{
|
|
// no name given = use temporary name!
|
|
DBG_ASSERT( m_bIsRoot, "SubStorage must have a name!" );
|
|
m_pTempFile.reset(new ::utl::TempFileNamed);
|
|
m_pTempFile->EnableKillingFile();
|
|
m_aName = aName = m_pTempFile->GetURL();
|
|
}
|
|
|
|
if ( m_bIsRoot )
|
|
{
|
|
// create the special package URL for the package content
|
|
m_aURL = "vnd.sun.star.pkg://" +
|
|
INetURLObject::encode( aName, INetURLObject::PART_AUTHORITY, INetURLObject::EncodeMechanism::All );
|
|
|
|
if ( m_nMode & StreamMode::WRITE )
|
|
{
|
|
// the root storage opens the package, so make sure that there is any
|
|
::utl::UcbStreamHelper::CreateStream( aName, StreamMode::STD_READWRITE, m_pTempFile != nullptr /* bFileExists */ );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// substorages are opened like streams: the URL is a "child URL" of the root package URL
|
|
m_aURL = rName;
|
|
if ( !m_aURL.startsWith( "vnd.sun.star.pkg://") )
|
|
m_bIsLinked = true;
|
|
}
|
|
}
|
|
|
|
UCBStorage_Impl::UCBStorage_Impl( SvStream& rStream, UCBStorage* pStorage, bool bDirect )
|
|
: m_pAntiImpl( pStorage )
|
|
, m_pTempFile( new ::utl::TempFileNamed )
|
|
, m_pSource( &rStream )
|
|
, m_nError( ERRCODE_NONE )
|
|
, m_bCommited( false )
|
|
, m_bDirect( bDirect )
|
|
, m_bIsRoot( true )
|
|
, m_bIsLinked( false )
|
|
, m_bListCreated( false )
|
|
, m_nFormat( SotClipboardFormatId::NONE )
|
|
, m_bRepairPackage( false )
|
|
{
|
|
// opening in direct mode is too fuzzy because the data is transferred to the stream in the Commit() call,
|
|
// which will be called in the storages' dtor
|
|
m_pTempFile->EnableKillingFile();
|
|
DBG_ASSERT( !bDirect, "Storage on a stream must not be opened in direct mode!" );
|
|
|
|
// UCBStorages work on a content, so a temporary file for a content must be created, even if the stream is only
|
|
// accessed readonly
|
|
// the root storage opens the package; create the special package URL for the package content
|
|
m_aURL = "vnd.sun.star.pkg://" +
|
|
INetURLObject::encode( m_pTempFile->GetURL(), INetURLObject::PART_AUTHORITY, INetURLObject::EncodeMechanism::All );
|
|
|
|
// copy data into the temporary file
|
|
std::unique_ptr<SvStream> pStream(::utl::UcbStreamHelper::CreateStream( m_pTempFile->GetURL(), StreamMode::STD_READWRITE, true /* bFileExists */ ));
|
|
if ( pStream )
|
|
{
|
|
rStream.Seek(0);
|
|
rStream.ReadStream( *pStream );
|
|
pStream->Flush();
|
|
pStream.reset();
|
|
}
|
|
|
|
// close stream and let content access the file
|
|
m_pSource->Seek(0);
|
|
|
|
// check opening mode
|
|
m_nMode = StreamMode::READ;
|
|
if( rStream.IsWritable() )
|
|
m_nMode = StreamMode::READ | StreamMode::WRITE;
|
|
}
|
|
|
|
void UCBStorage_Impl::Init()
|
|
{
|
|
// name is last segment in URL
|
|
INetURLObject aObj( m_aURL );
|
|
if ( m_aName.isEmpty() )
|
|
// if the name was not already set to a temp name
|
|
m_aName = aObj.GetLastName();
|
|
|
|
if ( !m_oContent )
|
|
CreateContent();
|
|
|
|
if ( m_oContent )
|
|
{
|
|
if ( m_bIsLinked )
|
|
{
|
|
if( m_bIsRoot )
|
|
{
|
|
ReadContent();
|
|
if ( m_nError == ERRCODE_NONE )
|
|
{
|
|
// read the manifest.xml file
|
|
aObj.Append( u"META-INF" );
|
|
aObj.Append( u"manifest.xml" );
|
|
|
|
// create input stream
|
|
std::unique_ptr<SvStream> pStream(::utl::UcbStreamHelper::CreateStream( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::STD_READ ));
|
|
// no stream means no manifest.xml
|
|
if ( pStream )
|
|
{
|
|
if ( !pStream->GetError() )
|
|
{
|
|
rtl::Reference<::utl::OInputStreamWrapper> pHelper = new ::utl::OInputStreamWrapper( *pStream );
|
|
|
|
// create a manifest reader object that will read in the manifest from the stream
|
|
Reference < css::packages::manifest::XManifestReader > xReader =
|
|
css::packages::manifest::ManifestReader::create(
|
|
::comphelper::getProcessComponentContext() ) ;
|
|
Sequence < Sequence < PropertyValue > > aProps = xReader->readManifestSequence( pHelper );
|
|
|
|
// cleanup
|
|
xReader = nullptr;
|
|
pHelper = nullptr;
|
|
SetProps( aProps, OUString() );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
ReadContent();
|
|
}
|
|
else
|
|
{
|
|
// get the manifest information from the package
|
|
try {
|
|
Any aAny = m_oContent->getPropertyValue(u"MediaType"_ustr);
|
|
OUString aTmp;
|
|
if ( ( aAny >>= aTmp ) && !aTmp.isEmpty() )
|
|
m_aContentType = m_aOriginalContentType = aTmp;
|
|
}
|
|
catch (const Exception&)
|
|
{
|
|
SAL_WARN( "sot",
|
|
"getPropertyValue has thrown an exception! Please let developers know the scenario!" );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( m_aContentType.isEmpty() )
|
|
return;
|
|
|
|
// get the clipboard format using the content type
|
|
css::datatransfer::DataFlavor aDataFlavor;
|
|
aDataFlavor.MimeType = m_aContentType;
|
|
m_nFormat = SotExchange::GetFormat( aDataFlavor );
|
|
|
|
// get the ClassId using the clipboard format ( internal table )
|
|
m_aClassId = GetClassId_Impl( m_nFormat );
|
|
|
|
// get human presentable name using the clipboard format
|
|
SotExchange::GetFormatDataFlavor( m_nFormat, aDataFlavor );
|
|
m_aUserTypeName = aDataFlavor.HumanPresentableName;
|
|
|
|
if( m_oContent && !m_bIsLinked && m_aClassId != SvGlobalName() )
|
|
ReadContent();
|
|
}
|
|
|
|
void UCBStorage_Impl::CreateContent()
|
|
{
|
|
try
|
|
{
|
|
// create content; where to put StreamMode ?! ( already done when opening the file of the package ? )
|
|
Reference< css::ucb::XCommandEnvironment > xComEnv;
|
|
|
|
OUString aTemp( m_aURL );
|
|
|
|
if ( m_bRepairPackage )
|
|
{
|
|
xComEnv = new ::ucbhelper::CommandEnvironment( Reference< css::task::XInteractionHandler >(),
|
|
m_xProgressHandler );
|
|
aTemp += "?repairpackage";
|
|
}
|
|
|
|
m_oContent.emplace( aTemp, xComEnv, comphelper::getProcessComponentContext() );
|
|
}
|
|
catch (const ContentCreationException&)
|
|
{
|
|
// content could not be created
|
|
SetError( SVSTREAM_CANNOT_MAKE );
|
|
}
|
|
catch (const RuntimeException&)
|
|
{
|
|
// any other error - not specified
|
|
SetError( SVSTREAM_CANNOT_MAKE );
|
|
}
|
|
}
|
|
|
|
void UCBStorage_Impl::ReadContent()
|
|
{
|
|
if ( m_bListCreated )
|
|
return;
|
|
|
|
m_bListCreated = true;
|
|
|
|
try
|
|
{
|
|
GetContent();
|
|
if ( !m_oContent )
|
|
return;
|
|
|
|
// create cursor for access to children
|
|
Reference< XResultSet > xResultSet = m_oContent->createCursor( { u"Title"_ustr, u"IsFolder"_ustr, u"MediaType"_ustr, u"Size"_ustr }, ::ucbhelper::INCLUDE_FOLDERS_AND_DOCUMENTS );
|
|
Reference< XRow > xRow( xResultSet, UNO_QUERY );
|
|
if ( xResultSet.is() )
|
|
{
|
|
while ( xResultSet->next() )
|
|
{
|
|
// insert all into the children list
|
|
OUString aTitle( xRow->getString(1) );
|
|
if ( m_bIsLinked )
|
|
{
|
|
// unpacked storages have to deal with the meta-inf folder by themselves
|
|
if ( aTitle == "META-INF" )
|
|
continue;
|
|
}
|
|
|
|
bool bIsFolder( xRow->getBoolean(2) );
|
|
sal_Int64 nSize = xRow->getLong(4);
|
|
UCBStorageElement_Impl* pElement = new UCBStorageElement_Impl( aTitle, bIsFolder, nSize );
|
|
m_aChildrenList.emplace_back( pElement );
|
|
|
|
bool bIsOfficeDocument = m_bIsLinked || ( m_aClassId != SvGlobalName() );
|
|
if ( bIsFolder )
|
|
{
|
|
if ( m_bIsLinked )
|
|
OpenStorage( pElement, m_nMode, m_bDirect );
|
|
if ( pElement->m_xStorage.is() )
|
|
pElement->m_xStorage->Init();
|
|
}
|
|
else if ( bIsOfficeDocument )
|
|
{
|
|
// streams can be external OLE objects, so they are now folders, but storages!
|
|
OUString aName( m_aURL + "/" + xRow->getString(1));
|
|
|
|
Reference< css::ucb::XCommandEnvironment > xComEnv;
|
|
if ( m_bRepairPackage )
|
|
{
|
|
xComEnv = new ::ucbhelper::CommandEnvironment( Reference< css::task::XInteractionHandler >(),
|
|
m_xProgressHandler );
|
|
aName += "?repairpackage";
|
|
}
|
|
|
|
::ucbhelper::Content aContent( aName, xComEnv, comphelper::getProcessComponentContext() );
|
|
|
|
OUString aMediaType;
|
|
Any aAny = aContent.getPropertyValue(u"MediaType"_ustr);
|
|
if ( ( aAny >>= aMediaType ) && ( aMediaType == "application/vnd.sun.star.oleobject" ) )
|
|
pElement->m_bIsStorage = true;
|
|
else if ( aMediaType.isEmpty() )
|
|
{
|
|
// older files didn't have that special content type, so they must be detected
|
|
OpenStream( pElement, StreamMode::STD_READ, m_bDirect );
|
|
if ( Storage::IsStorageFile( pElement->m_xStream.get() ) )
|
|
pElement->m_bIsStorage = true;
|
|
else
|
|
pElement->m_xStream->Free();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (const InteractiveIOException& r)
|
|
{
|
|
if ( r.Code != IOErrorCode_NOT_EXISTING )
|
|
SetError( ERRCODE_IO_GENERAL );
|
|
}
|
|
catch (const CommandAbortedException&)
|
|
{
|
|
// any command wasn't executed successfully - not specified
|
|
if ( !( m_nMode & StreamMode::WRITE ) )
|
|
// if the folder was just inserted and not already committed, this is not an error!
|
|
SetError( ERRCODE_IO_GENERAL );
|
|
}
|
|
catch (const RuntimeException&)
|
|
{
|
|
// any other error - not specified
|
|
SetError( ERRCODE_IO_GENERAL );
|
|
}
|
|
catch (const ResultSetException&)
|
|
{
|
|
// means that the package file is broken
|
|
SetError( ERRCODE_IO_BROKENPACKAGE );
|
|
}
|
|
catch (const SQLException&)
|
|
{
|
|
// means that the file can be broken
|
|
SetError( ERRCODE_IO_WRONGFORMAT );
|
|
}
|
|
catch (const Exception&)
|
|
{
|
|
// any other error - not specified
|
|
SetError( ERRCODE_IO_GENERAL );
|
|
}
|
|
}
|
|
|
|
void UCBStorage_Impl::SetError( ErrCode nError )
|
|
{
|
|
if ( !m_nError )
|
|
{
|
|
m_nError = nError;
|
|
if ( m_pAntiImpl ) m_pAntiImpl->SetError( nError );
|
|
}
|
|
}
|
|
|
|
sal_Int32 UCBStorage_Impl::GetObjectCount()
|
|
{
|
|
sal_Int32 nCount = m_aChildrenList.size();
|
|
for (auto& pElement : m_aChildrenList)
|
|
{
|
|
DBG_ASSERT( !pElement->m_bIsFolder || pElement->m_xStorage.is(), "Storage should be open!" );
|
|
if ( pElement->m_bIsFolder && pElement->m_xStorage.is() )
|
|
nCount += pElement->m_xStorage->GetObjectCount();
|
|
}
|
|
|
|
return nCount;
|
|
}
|
|
|
|
static OUString Find_Impl( const Sequence < Sequence < PropertyValue > >& rSequence, std::u16string_view rPath )
|
|
{
|
|
bool bFound = false;
|
|
for ( const Sequence < PropertyValue >& rMyProps : rSequence )
|
|
{
|
|
OUString aType;
|
|
|
|
for ( const PropertyValue& rAny : rMyProps )
|
|
{
|
|
if ( rAny.Name == "FullPath" )
|
|
{
|
|
OUString aTmp;
|
|
if ( ( rAny.Value >>= aTmp ) && aTmp == rPath )
|
|
bFound = true;
|
|
if ( !aType.isEmpty() )
|
|
break;
|
|
}
|
|
else if ( rAny.Name == "MediaType" )
|
|
{
|
|
if ( ( rAny.Value >>= aType ) && !aType.isEmpty() && bFound )
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( bFound )
|
|
return aType;
|
|
}
|
|
|
|
return OUString();
|
|
}
|
|
|
|
void UCBStorage_Impl::SetProps( const Sequence < Sequence < PropertyValue > >& rSequence, const OUString& rPath )
|
|
{
|
|
OUString aPath( rPath );
|
|
if ( !m_bIsRoot )
|
|
aPath += m_aName;
|
|
aPath += "/";
|
|
|
|
m_aContentType = m_aOriginalContentType = Find_Impl( rSequence, aPath );
|
|
|
|
if ( m_bIsRoot )
|
|
// the "FullPath" of a child always starts without '/'
|
|
aPath.clear();
|
|
|
|
for (auto& pElement : m_aChildrenList)
|
|
{
|
|
DBG_ASSERT( !pElement->m_bIsFolder || pElement->m_xStorage.is(), "Storage should be open!" );
|
|
if ( pElement->m_bIsFolder && pElement->m_xStorage.is() )
|
|
pElement->m_xStorage->SetProps( rSequence, aPath );
|
|
else
|
|
{
|
|
OUString aElementPath = aPath + pElement->m_aName;
|
|
pElement->SetContentType( Find_Impl( rSequence, aElementPath ) );
|
|
}
|
|
}
|
|
|
|
if ( m_aContentType.isEmpty() )
|
|
return;
|
|
|
|
// get the clipboard format using the content type
|
|
css::datatransfer::DataFlavor aDataFlavor;
|
|
aDataFlavor.MimeType = m_aContentType;
|
|
m_nFormat = SotExchange::GetFormat( aDataFlavor );
|
|
|
|
// get the ClassId using the clipboard format ( internal table )
|
|
m_aClassId = GetClassId_Impl( m_nFormat );
|
|
|
|
// get human presentable name using the clipboard format
|
|
SotExchange::GetFormatDataFlavor( m_nFormat, aDataFlavor );
|
|
m_aUserTypeName = aDataFlavor.HumanPresentableName;
|
|
}
|
|
|
|
void UCBStorage_Impl::GetProps( sal_Int32& nProps, Sequence < Sequence < PropertyValue > >& rSequence, const OUString& rPath )
|
|
{
|
|
auto pSequence = rSequence.getArray();
|
|
|
|
// first my own properties
|
|
// first property is the "FullPath" name
|
|
// it's '/' for the root storage and m_aName for each element, followed by a '/' if it's a folder
|
|
OUString aPath( rPath );
|
|
if ( !m_bIsRoot )
|
|
aPath += m_aName;
|
|
aPath += "/";
|
|
Sequence < PropertyValue > aProps{ comphelper::makePropertyValue(u"MediaType"_ustr, m_aContentType),
|
|
comphelper::makePropertyValue(u"FullPath"_ustr, aPath) };
|
|
pSequence[nProps++] = aProps;
|
|
|
|
if ( m_bIsRoot )
|
|
// the "FullPath" of a child always starts without '/'
|
|
aPath.clear();
|
|
|
|
// now the properties of my elements
|
|
for (auto& pElement : m_aChildrenList)
|
|
{
|
|
DBG_ASSERT( !pElement->m_bIsFolder || pElement->m_xStorage.is(), "Storage should be open!" );
|
|
if ( pElement->m_bIsFolder && pElement->m_xStorage.is() )
|
|
// storages add there properties by themselves ( see above )
|
|
pElement->m_xStorage->GetProps( nProps, rSequence, aPath );
|
|
else
|
|
{
|
|
// properties of streams
|
|
OUString aElementPath = aPath + pElement->m_aName;
|
|
aProps = { comphelper::makePropertyValue(u"MediaType"_ustr, pElement->GetContentType()),
|
|
comphelper::makePropertyValue(u"FullPath"_ustr, aElementPath) };
|
|
pSequence[ nProps++ ] = aProps;
|
|
}
|
|
}
|
|
}
|
|
|
|
UCBStorage_Impl::~UCBStorage_Impl()
|
|
{
|
|
m_aChildrenList.clear();
|
|
|
|
m_oContent.reset();
|
|
m_pTempFile.reset();
|
|
}
|
|
|
|
bool UCBStorage_Impl::Insert( ::ucbhelper::Content *pContent )
|
|
{
|
|
// a new substorage is inserted into a UCBStorage ( given by the parameter pContent )
|
|
// it must be inserted with a title and a type
|
|
bool bRet = false;
|
|
|
|
try
|
|
{
|
|
const Sequence< ContentInfo > aInfo = pContent->queryCreatableContentsInfo();
|
|
if ( !aInfo.hasElements() )
|
|
return false;
|
|
|
|
for ( const ContentInfo & rCurr : aInfo )
|
|
{
|
|
// Simply look for the first KIND_FOLDER...
|
|
if ( rCurr.Attributes & ContentInfoAttribute::KIND_FOLDER )
|
|
{
|
|
// Make sure the only required bootstrap property is "Title",
|
|
const Sequence< Property > & rProps = rCurr.Properties;
|
|
if ( rProps.getLength() != 1 )
|
|
continue;
|
|
|
|
if ( rProps[ 0 ].Name != "Title" )
|
|
continue;
|
|
|
|
Content aNewFolder;
|
|
if ( !pContent->insertNewContent( rCurr.Type, { u"Title"_ustr }, { Any(m_aName) }, aNewFolder ) )
|
|
continue;
|
|
|
|
// remove old content, create an "empty" new one and initialize it with the new inserted
|
|
m_oContent.emplace( aNewFolder );
|
|
bRet = true;
|
|
}
|
|
}
|
|
}
|
|
catch (const CommandAbortedException&)
|
|
{
|
|
// any command wasn't executed successfully - not specified
|
|
SetError( ERRCODE_IO_GENERAL );
|
|
}
|
|
catch (const RuntimeException&)
|
|
{
|
|
// any other error - not specified
|
|
SetError( ERRCODE_IO_GENERAL );
|
|
}
|
|
catch (const Exception&)
|
|
{
|
|
// any other error - not specified
|
|
SetError( ERRCODE_IO_GENERAL );
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
sal_Int16 UCBStorage_Impl::Commit()
|
|
{
|
|
// send all changes to the package
|
|
sal_Int16 nRet = COMMIT_RESULT_NOTHING_TO_DO;
|
|
|
|
// there is nothing to do if the storage has been opened readonly or if it was opened in transacted mode and no
|
|
// commit command has been sent
|
|
if ( ( m_nMode & StreamMode::WRITE ) && ( m_bCommited || m_bDirect ) )
|
|
{
|
|
try
|
|
{
|
|
// all errors will be caught in the "catch" statement outside the loop
|
|
for ( size_t i = 0; i < m_aChildrenList.size() && nRet; ++i )
|
|
{
|
|
auto& pElement = m_aChildrenList[ i ];
|
|
::ucbhelper::Content* pContent = pElement->GetContent();
|
|
std::unique_ptr< ::ucbhelper::Content > xDeleteContent;
|
|
if ( !pContent && pElement->IsModified() )
|
|
{
|
|
// if the element has never been opened, no content has been created until now
|
|
OUString aName = m_aURL + "/" + pElement->m_aOriginalName;
|
|
pContent = new ::ucbhelper::Content( aName, Reference< css::ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() );
|
|
xDeleteContent.reset(pContent); // delete it later on exit scope
|
|
}
|
|
|
|
if ( pElement->m_bIsRemoved )
|
|
{
|
|
// was it inserted, then removed (so there would be nothing to do!)
|
|
if ( !pElement->m_bIsInserted )
|
|
{
|
|
// first remove all open stream handles
|
|
if (pContent && (!pElement->m_xStream.is() || pElement->m_xStream->Clear()))
|
|
{
|
|
pContent->executeCommand( u"delete"_ustr, Any( true ) );
|
|
nRet = COMMIT_RESULT_SUCCESS;
|
|
}
|
|
else
|
|
// couldn't release stream because there are external references to it
|
|
nRet = COMMIT_RESULT_FAILURE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sal_Int16 nLocalRet = COMMIT_RESULT_NOTHING_TO_DO;
|
|
if ( pElement->m_xStorage.is() )
|
|
{
|
|
// element is a storage
|
|
// do a commit in the following cases:
|
|
// - if storage is already inserted, and changed
|
|
// - storage is not in a package
|
|
// - it's a new storage, try to insert and commit if successful inserted
|
|
if ( !pElement->m_bIsInserted || m_bIsLinked
|
|
|| pElement->m_xStorage->Insert( m_oContent ? &*m_oContent : nullptr ) )
|
|
{
|
|
nLocalRet = pElement->m_xStorage->Commit();
|
|
pContent = pElement->GetContent();
|
|
}
|
|
}
|
|
else if ( pElement->m_xStream.is() )
|
|
{
|
|
// element is a stream
|
|
nLocalRet = pElement->m_xStream->Commit();
|
|
if ( pElement->m_xStream->m_bIsOLEStorage )
|
|
{
|
|
// OLE storage should be stored encrypted, if the storage uses encryption
|
|
pElement->m_xStream->m_aContentType = "application/vnd.sun.star.oleobject";
|
|
Any aValue;
|
|
aValue <<= true;
|
|
pElement->m_xStream->m_pContent->setPropertyValue(u"Encrypted"_ustr, aValue );
|
|
}
|
|
|
|
pContent = pElement->GetContent();
|
|
}
|
|
|
|
if (pContent && pElement->m_aName != pElement->m_aOriginalName)
|
|
{
|
|
// name ( title ) of the element was changed
|
|
nLocalRet = COMMIT_RESULT_SUCCESS;
|
|
pContent->setPropertyValue(u"Title"_ustr, Any(pElement->m_aName) );
|
|
}
|
|
|
|
if (pContent && pElement->IsLoaded() && pElement->GetContentType() != pElement->GetOriginalContentType())
|
|
{
|
|
// mediatype of the element was changed
|
|
nLocalRet = COMMIT_RESULT_SUCCESS;
|
|
pContent->setPropertyValue(u"MediaType"_ustr, Any(pElement->GetContentType()) );
|
|
}
|
|
|
|
if ( nLocalRet != COMMIT_RESULT_NOTHING_TO_DO )
|
|
nRet = nLocalRet;
|
|
}
|
|
|
|
if ( nRet == COMMIT_RESULT_FAILURE )
|
|
break;
|
|
}
|
|
}
|
|
catch (const ContentCreationException&)
|
|
{
|
|
// content could not be created
|
|
SetError( ERRCODE_IO_NOTEXISTS );
|
|
return COMMIT_RESULT_FAILURE;
|
|
}
|
|
catch (const CommandAbortedException&)
|
|
{
|
|
// any command wasn't executed successfully - not specified
|
|
SetError( ERRCODE_IO_GENERAL );
|
|
return COMMIT_RESULT_FAILURE;
|
|
}
|
|
catch (const RuntimeException&)
|
|
{
|
|
// any other error - not specified
|
|
SetError( ERRCODE_IO_GENERAL );
|
|
return COMMIT_RESULT_FAILURE;
|
|
}
|
|
catch (const Exception&)
|
|
{
|
|
// any other error - not specified
|
|
SetError( ERRCODE_IO_GENERAL );
|
|
return COMMIT_RESULT_FAILURE;
|
|
}
|
|
|
|
if ( m_bIsRoot && m_oContent )
|
|
{
|
|
// the root storage must flush the root package content
|
|
if ( nRet == COMMIT_RESULT_SUCCESS )
|
|
{
|
|
try
|
|
{
|
|
// commit the media type to the JAR file
|
|
// clipboard format and ClassId will be retrieved from the media type when the file is loaded again
|
|
Any aType;
|
|
aType <<= m_aContentType;
|
|
m_oContent->setPropertyValue(u"MediaType"_ustr, aType );
|
|
|
|
if ( m_bIsLinked )
|
|
{
|
|
// write a manifest file
|
|
// first create a subfolder "META-inf"
|
|
Content aNewSubFolder;
|
|
bool bRet = ::utl::UCBContentHelper::MakeFolder( *m_oContent, u"META-INF"_ustr, aNewSubFolder );
|
|
if ( bRet )
|
|
{
|
|
// create a stream to write the manifest file - use a temp file
|
|
OUString aURL( aNewSubFolder.getURL() );
|
|
std::optional< ::utl::TempFileNamed > pTempFile(&aURL );
|
|
|
|
// get the stream from the temp file and create an output stream wrapper
|
|
SvStream* pStream = pTempFile->GetStream( StreamMode::STD_READWRITE );
|
|
rtl::Reference<::utl::OOutputStreamWrapper> xOutputStream = new ::utl::OOutputStreamWrapper( *pStream );
|
|
|
|
// create a manifest writer object that will fill the stream
|
|
Reference < css::packages::manifest::XManifestWriter > xWriter =
|
|
css::packages::manifest::ManifestWriter::create(
|
|
::comphelper::getProcessComponentContext() );
|
|
sal_Int32 nCount = GetObjectCount() + 1;
|
|
Sequence < Sequence < PropertyValue > > aProps( nCount );
|
|
sal_Int32 nProps = 0;
|
|
GetProps( nProps, aProps, OUString() );
|
|
xWriter->writeManifestSequence( xOutputStream, aProps );
|
|
|
|
// move the stream to its desired location
|
|
Content aSource( pTempFile->GetURL(), Reference < XCommandEnvironment >(), comphelper::getProcessComponentContext() );
|
|
xWriter = nullptr;
|
|
xOutputStream = nullptr;
|
|
pTempFile.reset();
|
|
aNewSubFolder.transferContent( aSource, InsertOperation::Move, u"manifest.xml"_ustr, NameClash::OVERWRITE );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if OSL_DEBUG_LEVEL > 0
|
|
SAL_INFO("sot", "Files: " << nOpenFiles);
|
|
SAL_INFO("sot", "Streams: " << nOpenStreams);
|
|
#endif
|
|
// force writing
|
|
Any aAny;
|
|
m_oContent->executeCommand( u"flush"_ustr, aAny );
|
|
if ( m_pSource != nullptr )
|
|
{
|
|
std::unique_ptr<SvStream> pStream(::utl::UcbStreamHelper::CreateStream( m_pTempFile->GetURL(), StreamMode::STD_READ ));
|
|
m_pSource->SetStreamSize(0);
|
|
// m_pSource->Seek(0);
|
|
pStream->ReadStream( *m_pSource );
|
|
pStream.reset();
|
|
m_pSource->Seek(0);
|
|
}
|
|
}
|
|
}
|
|
catch (const CommandAbortedException&)
|
|
{
|
|
// how to tell the content : forget all changes ?!
|
|
// or should we assume that the content does it by itself because he threw an exception ?!
|
|
// any command wasn't executed successfully - not specified
|
|
SetError( ERRCODE_IO_GENERAL );
|
|
return COMMIT_RESULT_FAILURE;
|
|
}
|
|
catch (const RuntimeException&)
|
|
{
|
|
// how to tell the content : forget all changes ?!
|
|
// or should we assume that the content does it by itself because he threw an exception ?!
|
|
// any other error - not specified
|
|
SetError( ERRCODE_IO_GENERAL );
|
|
return COMMIT_RESULT_FAILURE;
|
|
}
|
|
catch (const InteractiveIOException& r)
|
|
{
|
|
if ( r.Code == IOErrorCode_ACCESS_DENIED || r.Code == IOErrorCode_LOCKING_VIOLATION )
|
|
SetError( ERRCODE_IO_ACCESSDENIED );
|
|
else if ( r.Code == IOErrorCode_NOT_EXISTING )
|
|
SetError( ERRCODE_IO_NOTEXISTS );
|
|
else if ( r.Code == IOErrorCode_CANT_READ )
|
|
SetError( ERRCODE_IO_CANTREAD );
|
|
else if ( r.Code == IOErrorCode_CANT_WRITE )
|
|
SetError( ERRCODE_IO_CANTWRITE );
|
|
else
|
|
SetError( ERRCODE_IO_GENERAL );
|
|
|
|
return COMMIT_RESULT_FAILURE;
|
|
}
|
|
catch (const Exception&)
|
|
{
|
|
// how to tell the content : forget all changes ?!
|
|
// or should we assume that the content does it by itself because he threw an exception ?!
|
|
// any other error - not specified
|
|
SetError( ERRCODE_IO_GENERAL );
|
|
return COMMIT_RESULT_FAILURE;
|
|
}
|
|
}
|
|
else if ( nRet != COMMIT_RESULT_NOTHING_TO_DO )
|
|
{
|
|
// how to tell the content : forget all changes ?! Should we ?!
|
|
SetError( ERRCODE_IO_GENERAL );
|
|
return nRet;
|
|
}
|
|
|
|
// after successful root commit all elements names and types are adjusted and all removed elements
|
|
// are also removed from the lists
|
|
for ( size_t i = 0; i < m_aChildrenList.size(); )
|
|
{
|
|
auto& pInnerElement = m_aChildrenList[ i ];
|
|
if ( pInnerElement->m_bIsRemoved )
|
|
m_aChildrenList.erase( m_aChildrenList.begin() + i );
|
|
else
|
|
{
|
|
pInnerElement->m_aOriginalName = pInnerElement->m_aName;
|
|
pInnerElement->m_bIsInserted = false;
|
|
++i;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_bCommited = false;
|
|
}
|
|
|
|
return nRet;
|
|
}
|
|
|
|
void UCBStorage_Impl::Revert()
|
|
{
|
|
for ( size_t i = 0; i < m_aChildrenList.size(); )
|
|
{
|
|
auto& pElement = m_aChildrenList[ i ];
|
|
pElement->m_bIsRemoved = false;
|
|
if ( pElement->m_bIsInserted )
|
|
m_aChildrenList.erase( m_aChildrenList.begin() + i );
|
|
else
|
|
{
|
|
if ( pElement->m_xStream.is() )
|
|
{
|
|
pElement->m_xStream->m_bCommited = false;
|
|
pElement->m_xStream->Revert();
|
|
}
|
|
else if ( pElement->m_xStorage.is() )
|
|
{
|
|
pElement->m_xStorage->m_bCommited = false;
|
|
pElement->m_xStorage->Revert();
|
|
}
|
|
|
|
pElement->m_aName = pElement->m_aOriginalName;
|
|
pElement->m_bIsRemoved = false;
|
|
++i;
|
|
}
|
|
}
|
|
}
|
|
|
|
const OUString& UCBStorage::GetName() const
|
|
{
|
|
return pImp->m_aName; // pImp->m_aURL ?!
|
|
}
|
|
|
|
bool UCBStorage::IsRoot() const
|
|
{
|
|
return pImp->m_bIsRoot;
|
|
}
|
|
|
|
void UCBStorage::SetDirty()
|
|
{
|
|
}
|
|
|
|
void UCBStorage::SetClass( const SvGlobalName & rClass, SotClipboardFormatId nOriginalClipFormat, const OUString & rUserTypeName )
|
|
{
|
|
pImp->m_aClassId = rClass;
|
|
pImp->m_nFormat = nOriginalClipFormat;
|
|
pImp->m_aUserTypeName = rUserTypeName;
|
|
|
|
// in UCB storages only the content type will be stored, all other information can be reconstructed
|
|
// ( see the UCBStorage_Impl::Init() method )
|
|
css::datatransfer::DataFlavor aDataFlavor;
|
|
SotExchange::GetFormatDataFlavor( pImp->m_nFormat, aDataFlavor );
|
|
pImp->m_aContentType = aDataFlavor.MimeType;
|
|
}
|
|
|
|
void UCBStorage::SetClassId( const ClsId& rClsId )
|
|
{
|
|
pImp->m_aClassId = SvGlobalName( rClsId );
|
|
if ( pImp->m_aClassId == SvGlobalName() )
|
|
return;
|
|
|
|
// in OLE storages the clipboard format and the user name will be transferred when a storage is copied because both are
|
|
// stored in one the substreams
|
|
// UCB storages store the content type information as content type in the manifest file and so this information must be
|
|
// kept up to date, and also the other type information that is hold only at runtime because it can be reconstructed from
|
|
// the content type
|
|
pImp->m_nFormat = GetFormatId_Impl( pImp->m_aClassId );
|
|
if ( pImp->m_nFormat != SotClipboardFormatId::NONE )
|
|
{
|
|
css::datatransfer::DataFlavor aDataFlavor;
|
|
SotExchange::GetFormatDataFlavor( pImp->m_nFormat, aDataFlavor );
|
|
pImp->m_aUserTypeName = aDataFlavor.HumanPresentableName;
|
|
pImp->m_aContentType = aDataFlavor.MimeType;
|
|
}
|
|
}
|
|
|
|
const ClsId& UCBStorage::GetClassId() const
|
|
{
|
|
return pImp->m_aClassId.GetCLSID();
|
|
}
|
|
|
|
SvGlobalName UCBStorage::GetClassName()
|
|
{
|
|
return pImp->m_aClassId;
|
|
}
|
|
|
|
SotClipboardFormatId UCBStorage::GetFormat()
|
|
{
|
|
return pImp->m_nFormat;
|
|
}
|
|
|
|
OUString UCBStorage::GetUserName()
|
|
{
|
|
OSL_FAIL("UserName is not implemented in UCB storages!" );
|
|
return pImp->m_aUserTypeName;
|
|
}
|
|
|
|
void UCBStorage::FillInfoList( SvStorageInfoList* pList ) const
|
|
{
|
|
// put information in childrenlist into StorageInfoList
|
|
for (auto& pElement : pImp->GetChildrenList())
|
|
{
|
|
if ( !pElement->m_bIsRemoved )
|
|
{
|
|
// problem: what about the size of a substorage ?!
|
|
sal_uInt64 nSize = pElement->m_nSize;
|
|
if ( pElement->m_xStream.is() )
|
|
nSize = pElement->m_xStream->GetSize();
|
|
SvStorageInfo aInfo( pElement->m_aName, nSize, pElement->m_bIsStorage );
|
|
pList->push_back( aInfo );
|
|
}
|
|
}
|
|
}
|
|
|
|
bool UCBStorage::CopyStorageElement_Impl( UCBStorageElement_Impl const & rElement, BaseStorage* pDest, const OUString& rNew ) const
|
|
{
|
|
// insert stream or storage into the list or stream of the destination storage
|
|
// not into the content, this will be done on commit !
|
|
// be aware of name changes !
|
|
if ( !rElement.m_bIsStorage )
|
|
{
|
|
// copy the streams data
|
|
// the destination stream must not be open
|
|
tools::SvRef<BaseStorageStream> pOtherStream(pDest->OpenStream( rNew, StreamMode::WRITE | StreamMode::SHARE_DENYALL, pImp->m_bDirect ));
|
|
BaseStorageStream* pStream = nullptr;
|
|
bool bDeleteStream = false;
|
|
|
|
// if stream is already open, it is allowed to copy it, so be aware of this
|
|
if ( rElement.m_xStream.is() )
|
|
pStream = rElement.m_xStream->m_pAntiImpl;
|
|
if ( !pStream )
|
|
{
|
|
pStream = const_cast< UCBStorage* >(this)->OpenStream( rElement.m_aName, StreamMode::STD_READ, pImp->m_bDirect );
|
|
bDeleteStream = true;
|
|
}
|
|
|
|
pStream->CopyTo( pOtherStream.get() );
|
|
SetError( pStream->GetError() );
|
|
if( pOtherStream->GetError() )
|
|
pDest->SetError( pOtherStream->GetError() );
|
|
else
|
|
pOtherStream->Commit();
|
|
|
|
if ( bDeleteStream )
|
|
delete pStream;
|
|
}
|
|
else
|
|
{
|
|
// copy the storage content
|
|
// the destination storage must not be open
|
|
BaseStorage* pStorage = nullptr;
|
|
|
|
// if stream is already open, it is allowed to copy it, so be aware of this
|
|
bool bDeleteStorage = false;
|
|
if ( rElement.m_xStorage.is() )
|
|
pStorage = rElement.m_xStorage->m_pAntiImpl;
|
|
if ( !pStorage )
|
|
{
|
|
pStorage = const_cast<UCBStorage*>(this)->OpenStorage( rElement.m_aName, pImp->m_nMode, pImp->m_bDirect );
|
|
bDeleteStorage = true;
|
|
}
|
|
|
|
UCBStorage* pUCBDest = dynamic_cast<UCBStorage*>( pDest );
|
|
UCBStorage* pUCBCopy = dynamic_cast<UCBStorage*>( pStorage );
|
|
|
|
bool bOpenUCBStorage = pUCBDest && pUCBCopy;
|
|
tools::SvRef<BaseStorage> pOtherStorage(bOpenUCBStorage ?
|
|
pDest->OpenUCBStorage( rNew, StreamMode::WRITE | StreamMode::SHARE_DENYALL, pImp->m_bDirect ) :
|
|
pDest->OpenOLEStorage( rNew, StreamMode::WRITE | StreamMode::SHARE_DENYALL, pImp->m_bDirect ));
|
|
|
|
// For UCB storages, the class id and the format id may differ,
|
|
// do passing the class id is not sufficient.
|
|
if( bOpenUCBStorage )
|
|
pOtherStorage->SetClass( pStorage->GetClassName(),
|
|
pStorage->GetFormat(),
|
|
pUCBCopy->pImp->m_aUserTypeName );
|
|
else
|
|
pOtherStorage->SetClassId( pStorage->GetClassId() );
|
|
pStorage->CopyTo( *pOtherStorage );
|
|
SetError( pStorage->GetError() );
|
|
if( pOtherStorage->GetError() )
|
|
pDest->SetError( pOtherStorage->GetError() );
|
|
else
|
|
pOtherStorage->Commit();
|
|
|
|
if ( bDeleteStorage )
|
|
delete pStorage;
|
|
}
|
|
|
|
return Good() && pDest->Good();
|
|
}
|
|
|
|
UCBStorageElement_Impl* UCBStorage::FindElement_Impl( std::u16string_view rName ) const
|
|
{
|
|
DBG_ASSERT( !rName.empty(), "Name is empty!" );
|
|
for (const auto& pElement : pImp->GetChildrenList())
|
|
{
|
|
if ( pElement->m_aName == rName && !pElement->m_bIsRemoved )
|
|
return pElement.get();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool UCBStorage::CopyTo( BaseStorage& rDestStg ) const
|
|
{
|
|
DBG_ASSERT( &rDestStg != static_cast<BaseStorage const *>(this), "Self-Copying is not possible!" );
|
|
if ( &rDestStg == static_cast<BaseStorage const *>(this) )
|
|
return false;
|
|
|
|
// perhaps it's also a problem if one storage is a parent of the other ?!
|
|
// or if not: could be optimized ?!
|
|
|
|
// For UCB storages, the class id and the format id may differ,
|
|
// do passing the class id is not sufficient.
|
|
if( dynamic_cast<const UCBStorage *>(&rDestStg) != nullptr )
|
|
rDestStg.SetClass( pImp->m_aClassId, pImp->m_nFormat,
|
|
pImp->m_aUserTypeName );
|
|
else
|
|
rDestStg.SetClassId( GetClassId() );
|
|
rDestStg.SetDirty();
|
|
|
|
bool bRet = true;
|
|
for ( size_t i = 0; i < pImp->GetChildrenList().size() && bRet; ++i )
|
|
{
|
|
auto& pElement = pImp->GetChildrenList()[ i ];
|
|
if ( !pElement->m_bIsRemoved )
|
|
bRet = CopyStorageElement_Impl( *pElement, &rDestStg, pElement->m_aName );
|
|
}
|
|
|
|
if( !bRet )
|
|
SetError( rDestStg.GetError() );
|
|
return Good() && rDestStg.Good();
|
|
}
|
|
|
|
bool UCBStorage::CopyTo( const OUString& rElemName, BaseStorage* pDest, const OUString& rNew )
|
|
{
|
|
if( rElemName.isEmpty() )
|
|
return false;
|
|
|
|
if ( pDest == static_cast<BaseStorage*>(this) )
|
|
{
|
|
// can't double an element
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
// for copying no optimization is useful, because in every case the stream data must be copied
|
|
UCBStorageElement_Impl* pElement = FindElement_Impl( rElemName );
|
|
if ( pElement )
|
|
return CopyStorageElement_Impl( *pElement, pDest, rNew );
|
|
else
|
|
{
|
|
SetError( SVSTREAM_FILE_NOT_FOUND );
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool UCBStorage::Commit()
|
|
{
|
|
// mark this storage for sending it on root commit
|
|
pImp->m_bCommited = true;
|
|
if ( pImp->m_bIsRoot )
|
|
// the root storage coordinates committing by sending a Commit command to its content
|
|
return ( pImp->Commit() != COMMIT_RESULT_FAILURE );
|
|
else
|
|
return true;
|
|
}
|
|
|
|
bool UCBStorage::Revert()
|
|
{
|
|
pImp->Revert();
|
|
return true;
|
|
}
|
|
|
|
BaseStorageStream* UCBStorage::OpenStream( const OUString& rEleName, StreamMode nMode, bool bDirect )
|
|
{
|
|
if( rEleName.isEmpty() )
|
|
return nullptr;
|
|
|
|
// try to find the storage element
|
|
UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName );
|
|
if ( !pElement )
|
|
{
|
|
// element does not exist, check if creation is allowed
|
|
if( nMode & StreamMode::NOCREATE )
|
|
{
|
|
SetError( ( nMode & StreamMode::WRITE ) ? SVSTREAM_CANNOT_MAKE : SVSTREAM_FILE_NOT_FOUND );
|
|
OUString aName = pImp->m_aURL + "/" + rEleName;
|
|
UCBStorageStream* pStream = new UCBStorageStream( aName, nMode, bDirect, pImp->m_bRepairPackage, pImp->m_xProgressHandler );
|
|
pStream->SetError( GetError() );
|
|
pStream->pImp->m_aName = rEleName;
|
|
return pStream;
|
|
}
|
|
else
|
|
{
|
|
// create a new UCBStorageElement and insert it into the list
|
|
pElement = new UCBStorageElement_Impl( rEleName );
|
|
pElement->m_bIsInserted = true;
|
|
pImp->m_aChildrenList.emplace_back( pElement );
|
|
}
|
|
}
|
|
|
|
if ( !pElement->m_bIsFolder )
|
|
{
|
|
// check if stream is already created
|
|
if ( pElement->m_xStream.is() )
|
|
{
|
|
// stream has already been created; if it has no external reference, it may be opened another time
|
|
if ( pElement->m_xStream->m_pAntiImpl )
|
|
{
|
|
OSL_FAIL("Stream is already open!" );
|
|
SetError( SVSTREAM_ACCESS_DENIED ); // ???
|
|
return nullptr;
|
|
}
|
|
else
|
|
{
|
|
// check if stream is opened with the same keyword as before
|
|
// if not, generate a new stream because it could be encrypted vs. decrypted!
|
|
if ( pElement->m_xStream->m_aKey.isEmpty() )
|
|
{
|
|
pElement->m_xStream->PrepareCachedForReopen( nMode );
|
|
|
|
return new UCBStorageStream( pElement->m_xStream.get() );
|
|
}
|
|
}
|
|
}
|
|
|
|
// stream is opened the first time
|
|
pImp->OpenStream( pElement, nMode, bDirect );
|
|
|
|
// if name has been changed before creating the stream: set name!
|
|
pElement->m_xStream->m_aName = rEleName;
|
|
return new UCBStorageStream( pElement->m_xStream.get() );
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void UCBStorage_Impl::OpenStream( UCBStorageElement_Impl* pElement, StreamMode nMode, bool bDirect )
|
|
{
|
|
OUString aName = m_aURL + "/" +pElement->m_aOriginalName;
|
|
pElement->m_xStream = new UCBStorageStream_Impl( aName, nMode, nullptr, bDirect, m_bRepairPackage, m_xProgressHandler );
|
|
}
|
|
|
|
BaseStorage* UCBStorage::OpenUCBStorage( const OUString& rEleName, StreamMode nMode, bool bDirect )
|
|
{
|
|
if( rEleName.isEmpty() )
|
|
return nullptr;
|
|
|
|
return OpenStorage_Impl( rEleName, nMode, bDirect, true );
|
|
}
|
|
|
|
BaseStorage* UCBStorage::OpenOLEStorage( const OUString& rEleName, StreamMode nMode, bool bDirect )
|
|
{
|
|
if( rEleName.isEmpty() )
|
|
return nullptr;
|
|
|
|
return OpenStorage_Impl( rEleName, nMode, bDirect, false );
|
|
}
|
|
|
|
BaseStorage* UCBStorage::OpenStorage( const OUString& rEleName, StreamMode nMode, bool bDirect )
|
|
{
|
|
if( rEleName.isEmpty() )
|
|
return nullptr;
|
|
|
|
return OpenStorage_Impl( rEleName, nMode, bDirect, true );
|
|
}
|
|
|
|
BaseStorage* UCBStorage::OpenStorage_Impl( const OUString& rEleName, StreamMode nMode, bool bDirect, bool bForceUCBStorage )
|
|
{
|
|
// try to find the storage element
|
|
UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName );
|
|
if ( !pElement )
|
|
{
|
|
// element does not exist, check if creation is allowed
|
|
if( nMode & StreamMode::NOCREATE )
|
|
{
|
|
SetError( ( nMode & StreamMode::WRITE ) ? SVSTREAM_CANNOT_MAKE : SVSTREAM_FILE_NOT_FOUND );
|
|
OUString aName = pImp->m_aURL + "/" + rEleName; // ???
|
|
UCBStorage *pStorage = new UCBStorage( aName, nMode, bDirect, false, pImp->m_bRepairPackage, pImp->m_xProgressHandler );
|
|
pStorage->pImp->m_bIsRoot = false;
|
|
pStorage->pImp->m_bListCreated = true; // the storage is pretty new, nothing to read
|
|
pStorage->SetError( GetError() );
|
|
return pStorage;
|
|
}
|
|
|
|
// create a new UCBStorageElement and insert it into the list
|
|
// problem: perhaps an OLEStorage should be created ?!
|
|
// Because nothing is known about the element that should be created, an external parameter is needed !
|
|
pElement = new UCBStorageElement_Impl( rEleName );
|
|
pElement->m_bIsInserted = true;
|
|
pImp->m_aChildrenList.emplace_back( pElement );
|
|
}
|
|
|
|
if ( !pElement->m_bIsFolder && ( pElement->m_bIsStorage || !bForceUCBStorage ) )
|
|
{
|
|
// create OLE storages on a stream ( see ctor of SotStorage )
|
|
// Such a storage will be created on a UCBStorageStream; it will write into the stream
|
|
// if it is opened in direct mode or when it is committed. In this case the stream will be
|
|
// modified and then it MUST be treated as committed.
|
|
if ( !pElement->m_xStream.is() )
|
|
{
|
|
BaseStorageStream* pStr = OpenStream( rEleName, nMode, bDirect );
|
|
UCBStorageStream* pStream = dynamic_cast<UCBStorageStream*>( pStr );
|
|
if ( !pStream )
|
|
{
|
|
SetError( ( nMode & StreamMode::WRITE ) ? SVSTREAM_CANNOT_MAKE : SVSTREAM_FILE_NOT_FOUND );
|
|
return nullptr;
|
|
}
|
|
|
|
pElement->m_xStream = pStream->pImp;
|
|
delete pStream;
|
|
}
|
|
|
|
pElement->m_xStream->PrepareCachedForReopen( nMode );
|
|
bool bInited = pElement->m_xStream->Init();
|
|
if (!bInited)
|
|
{
|
|
SetError( ( nMode & StreamMode::WRITE ) ? SVSTREAM_CANNOT_MAKE : SVSTREAM_FILE_NOT_FOUND );
|
|
return nullptr;
|
|
}
|
|
|
|
pElement->m_bIsStorage = true;
|
|
return pElement->m_xStream->CreateStorage(); // can only be created in transacted mode
|
|
}
|
|
else if ( pElement->m_xStorage.is() )
|
|
{
|
|
// storage has already been opened; if it has no external reference, it may be opened another time
|
|
if ( pElement->m_xStorage->m_pAntiImpl )
|
|
{
|
|
OSL_FAIL("Storage is already open!" );
|
|
SetError( SVSTREAM_ACCESS_DENIED ); // ???
|
|
}
|
|
else
|
|
{
|
|
bool bIsWritable = bool( pElement->m_xStorage->m_nMode & StreamMode::WRITE );
|
|
if ( !bIsWritable && ( nMode & StreamMode::WRITE ) )
|
|
{
|
|
OUString aName = pImp->m_aURL + "/" + pElement->m_aOriginalName;
|
|
UCBStorage* pStorage = new UCBStorage( aName, nMode, bDirect, false, pImp->m_bRepairPackage, pImp->m_xProgressHandler );
|
|
pElement->m_xStorage = pStorage->pImp;
|
|
return pStorage;
|
|
}
|
|
else
|
|
{
|
|
return new UCBStorage( pElement->m_xStorage.get() );
|
|
}
|
|
}
|
|
}
|
|
else if ( !pElement->m_xStream.is() )
|
|
{
|
|
// storage is opened the first time
|
|
bool bIsWritable = bool(pImp->m_nMode & StreamMode::WRITE);
|
|
if ( pImp->m_bIsLinked && pImp->m_bIsRoot && bIsWritable )
|
|
{
|
|
// make sure that the root storage object has been created before substorages will be created
|
|
INetURLObject aFolderObj( pImp->m_aURL );
|
|
aFolderObj.removeSegment();
|
|
|
|
Content aFolder( aFolderObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), Reference < XCommandEnvironment >(), comphelper::getProcessComponentContext() );
|
|
pImp->m_oContent.emplace();
|
|
bool bRet = ::utl::UCBContentHelper::MakeFolder( aFolder, pImp->m_aName, *pImp->m_oContent );
|
|
if ( !bRet )
|
|
{
|
|
SetError( SVSTREAM_CANNOT_MAKE );
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
UCBStorage_Impl* pStor = pImp->OpenStorage( pElement, nMode, bDirect );
|
|
if ( pStor )
|
|
{
|
|
if ( pElement->m_bIsInserted )
|
|
pStor->m_bListCreated = true; // the storage is pretty new, nothing to read
|
|
|
|
return new UCBStorage( pStor );
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
UCBStorage_Impl* UCBStorage_Impl::OpenStorage( UCBStorageElement_Impl* pElement, StreamMode nMode, bool bDirect )
|
|
{
|
|
UCBStorage_Impl* pRet = nullptr;
|
|
OUString aName = m_aURL + "/" + pElement->m_aOriginalName; // ???
|
|
|
|
pElement->m_bIsStorage = pElement->m_bIsFolder = true;
|
|
|
|
if ( m_bIsLinked && !::utl::UCBContentHelper::Exists( aName ) )
|
|
{
|
|
Content aNewFolder;
|
|
bool bRet = ::utl::UCBContentHelper::MakeFolder( *m_oContent, pElement->m_aOriginalName, aNewFolder );
|
|
if ( bRet )
|
|
pRet = new UCBStorage_Impl( aNewFolder, aName, nMode, nullptr, bDirect, false, m_bRepairPackage, m_xProgressHandler );
|
|
}
|
|
else
|
|
{
|
|
pRet = new UCBStorage_Impl( aName, nMode, nullptr, bDirect, false, m_bRepairPackage, m_xProgressHandler );
|
|
}
|
|
|
|
if ( pRet )
|
|
{
|
|
pRet->m_bIsLinked = m_bIsLinked;
|
|
pRet->m_bIsRoot = false;
|
|
|
|
// if name has been changed before creating the stream: set name!
|
|
pRet->m_aName = pElement->m_aOriginalName;
|
|
pElement->m_xStorage = pRet;
|
|
}
|
|
|
|
if ( pRet )
|
|
pRet->Init();
|
|
|
|
return pRet;
|
|
}
|
|
|
|
bool UCBStorage::IsStorage( const OUString& rEleName ) const
|
|
{
|
|
if( rEleName.isEmpty() )
|
|
return false;
|
|
|
|
const UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName );
|
|
return ( pElement && pElement->m_bIsStorage );
|
|
}
|
|
|
|
bool UCBStorage::IsStream( const OUString& rEleName ) const
|
|
{
|
|
if( rEleName.isEmpty() )
|
|
return false;
|
|
|
|
const UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName );
|
|
return ( pElement && !pElement->m_bIsStorage );
|
|
}
|
|
|
|
bool UCBStorage::IsContained( const OUString & rEleName ) const
|
|
{
|
|
if( rEleName.isEmpty() )
|
|
return false;
|
|
const UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName );
|
|
return ( pElement != nullptr );
|
|
}
|
|
|
|
void UCBStorage::Remove( const OUString& rEleName )
|
|
{
|
|
if( rEleName.isEmpty() )
|
|
return;
|
|
|
|
UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName );
|
|
if ( pElement )
|
|
{
|
|
pElement->m_bIsRemoved = true;
|
|
}
|
|
else
|
|
SetError( SVSTREAM_FILE_NOT_FOUND );
|
|
}
|
|
|
|
bool UCBStorage::ValidateFAT()
|
|
{
|
|
// ???
|
|
return true;
|
|
}
|
|
|
|
bool UCBStorage::Validate( bool bWrite ) const
|
|
{
|
|
// ???
|
|
return ( !bWrite || ( pImp->m_nMode & StreamMode::WRITE ) );
|
|
}
|
|
|
|
bool UCBStorage::ValidateMode( StreamMode m ) const
|
|
{
|
|
// ???
|
|
if( m == ( StreamMode::READ | StreamMode::TRUNC ) ) // from stg.cxx
|
|
return true;
|
|
// only SHARE_DENYALL allowed
|
|
// storages open in r/o mode are OK, since only
|
|
// the commit may fail
|
|
if( m & StreamMode::SHARE_DENYALL )
|
|
return true;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool UCBStorage::Equals( const BaseStorage& rStorage ) const
|
|
{
|
|
// ???
|
|
return static_cast<BaseStorage const *>(this) == &rStorage;
|
|
}
|
|
|
|
bool UCBStorage::IsStorageFile( SvStream* pFile )
|
|
{
|
|
if ( !pFile )
|
|
return false;
|
|
|
|
sal_uInt64 nPos = pFile->Tell();
|
|
if ( pFile->TellEnd() < 4 )
|
|
return false;
|
|
|
|
pFile->Seek(0);
|
|
sal_uInt32 nBytes(0);
|
|
pFile->ReadUInt32( nBytes );
|
|
|
|
// search for the magic bytes
|
|
bool bRet = ( nBytes == 0x04034b50 );
|
|
if ( !bRet )
|
|
{
|
|
// disk spanned file have an additional header in front of the usual one
|
|
bRet = ( nBytes == 0x08074b50 );
|
|
if ( bRet )
|
|
{
|
|
nBytes = 0;
|
|
pFile->ReadUInt32( nBytes );
|
|
bRet = ( nBytes == 0x04034b50 );
|
|
}
|
|
}
|
|
|
|
pFile->Seek( nPos );
|
|
return bRet;
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|