/* -*- 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 <config_features.h>
 
#ifdef UNX
#include <sys/stat.h>
#endif
 
#include <sfx2/docfile.hxx>
#include <sfx2/signaturestate.hxx>
 
#include <uno/mapping.hxx>
#include <com/sun/star/task/InteractionHandler.hpp>
#include <com/sun/star/uno/Reference.h>
#include <com/sun/star/ucb/XContent.hpp>
#include <com/sun/star/container/XChild.hpp>
#include <com/sun/star/document/XDocumentRevisionListPersistence.hpp>
#include <com/sun/star/document/LockedDocumentRequest.hpp>
#include <com/sun/star/document/LockedOnSavingRequest.hpp>
#include <com/sun/star/document/OwnLockOnDocumentRequest.hpp>
#include <com/sun/star/document/LockFileIgnoreRequest.hpp>
#include <com/sun/star/document/LockFileCorruptRequest.hpp>
#include <com/sun/star/document/ChangedByOthersRequest.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/embed/XTransactedObject.hpp>
#include <com/sun/star/embed/ElementModes.hpp>
#include <com/sun/star/embed/UseBackupException.hpp>
#include <com/sun/star/embed/XOptimizedStorage.hpp>
#include <com/sun/star/graphic/XGraphic.hpp>
#include <com/sun/star/ucb/ContentCreationException.hpp>
#include <com/sun/star/ucb/InteractiveIOException.hpp>
#include <com/sun/star/ucb/UnsupportedDataSinkException.hpp>
#include <com/sun/star/ucb/CommandFailedException.hpp>
#include <com/sun/star/ucb/CommandAbortedException.hpp>
#include <com/sun/star/ucb/InteractiveLockingLockedException.hpp>
#include <com/sun/star/ucb/InteractiveNetworkWriteException.hpp>
#include <com/sun/star/ucb/Lock.hpp>
#include <com/sun/star/ucb/XCommandEnvironment.hpp>
#include <com/sun/star/ucb/XContentIdentifierFactory.hpp>
#include <com/sun/star/ucb/XContentProvider.hpp>
#include <com/sun/star/ucb/XProgressHandler.hpp>
#include <com/sun/star/ucb/XCommandInfo.hpp>
#include <com/sun/star/io/XOutputStream.hpp>
#include <com/sun/star/io/XInputStream.hpp>
#include <com/sun/star/io/XTruncate.hpp>
#include <com/sun/star/io/XStreamListener.hpp>
#include <com/sun/star/io/XSeekable.hpp>
#include <com/sun/star/ucb/XSimpleFileAccess.hpp>
#include <com/sun/star/lang/XInitialization.hpp>
#include <com/sun/star/ucb/InsertCommandArgument.hpp>
#include <com/sun/star/ucb/NameClash.hpp>
#include <com/sun/star/ucb/TransferInfo.hpp>
#include <com/sun/star/ucb/OpenCommandArgument2.hpp>
#include <com/sun/star/ucb/OpenMode.hpp>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/security/DocumentSignatureInformation.hpp>
#include <com/sun/star/security/DocumentDigitalSignatures.hpp>
#include <com/sun/star/security/XCertificate.hpp>
#include <o3tl/make_unique.hxx>
#include <tools/urlobj.hxx>
#include <tools/fileutil.hxx>
#include <unotools/configmgr.hxx>
#include <unotools/tempfile.hxx>
#include <comphelper/fileurl.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/interaction.hxx>
#include <comphelper/simplefileaccessinteraction.hxx>
#include <framework/interaction.hxx>
#include <unotools/streamhelper.hxx>
#include <unotools/localedatawrapper.hxx>
#include <utility>
#include <svl/stritem.hxx>
#include <svl/eitem.hxx>
#include <svl/lckbitem.hxx>
#include <svtools/sfxecode.hxx>
#include <svl/itemset.hxx>
#include <svl/intitem.hxx>
#include <svtools/svparser.hxx>
#include <cppuhelper/weakref.hxx>
#include <sal/log.hxx>
 
#include <unotools/streamwrap.hxx>
 
#include <osl/file.hxx>
 
#include <comphelper/storagehelper.hxx>
#include <unotools/mediadescriptor.hxx>
#include <comphelper/docpasswordhelper.hxx>
#include <tools/datetime.hxx>
#include <unotools/pathoptions.hxx>
#include <svtools/asynclink.hxx>
#include <ucbhelper/commandenvironment.hxx>
#include <unotools/ucbstreamhelper.hxx>
#include <unotools/ucbhelper.hxx>
#include <unotools/progresshandlerwrap.hxx>
#include <ucbhelper/content.hxx>
#include <ucbhelper/interactionrequest.hxx>
#include <sot/stg.hxx>
#include <sot/storage.hxx>
#include <unotools/saveopt.hxx>
#include <svl/documentlockfile.hxx>
#include <com/sun/star/document/DocumentRevisionListPersistence.hpp>
 
#include <helper.hxx>
#include <sfx2/request.hxx>
#include <sfx2/app.hxx>
#include <sfx2/frame.hxx>
#include <sfx2/fcontnr.hxx>
#include <sfx2/docfilt.hxx>
#include <sfx2/objsh.hxx>
#include <sfx2/docfac.hxx>
#include <sfx2/sfxsids.hrc>
#include <openflag.hxx>
#include <officecfg/Office/Common.hxx>
#include <comphelper/propertysequence.hxx>
 
#include <com/sun/star/io/WrongFormatException.hpp>
 
#include <memory>
 
using namespace ::com::sun::star;
using namespace ::com::sun::star::graphic;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::ucb;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::io;
using namespace ::com::sun::star::security;
 
namespace {
 
#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
 
bool IsSystemFileLockingUsed()
{
#if HAVE_FEATURE_MACOSX_SANDBOX
    return true;
#else
    return officecfg::Office::Common::Misc::UseDocumentSystemFileLocking::get();
#endif
}
 
 
bool IsOOoLockFileUsed()
{
#if HAVE_FEATURE_MACOSX_SANDBOX
    return false;
#else
    return officecfg::Office::Common::Misc::UseDocumentOOoLockFile::get();
#endif
}
 
bool IsLockingUsed()
{
    return officecfg::Office::Common::Misc::UseLocking::get();
}
 
#endif
 
/// Gets default attributes of a file:// URL.
sal_uInt64 GetDefaultFileAttributes(const OUString& rURL)
{
    sal_uInt64 nRet = 0;
 
    if (!comphelper::isFileUrl(rURL))
        return nRet;
 
    // Make sure the file exists (and create it if not).
    osl::File aFile(rURL);
    osl::File::RC nRes = aFile.open(osl_File_OpenFlag_Create);
    if (nRes != osl::File::E_None && nRes != osl::File::E_EXIST)
        return nRet;
 
    aFile.close();
 
    osl::DirectoryItem aItem;
    if (osl::DirectoryItem::get(rURL, aItem) != osl::DirectoryItem::E_None)
        return nRet;
 
    osl::FileStatus aStatus(osl_FileStatus_Mask_Attributes);
    if (aItem.getFileStatus(aStatus) != osl::DirectoryItem::E_None)
        return nRet;
 
    nRet = aStatus.getAttributes();
    return nRet;
}
 
/// Determines if rURL is safe to move or not.
bool IsFileMovable(const OUString& rURL)
{
#ifdef MACOSX
    (void)rURL;
    // Hide extension macOS-specific file property would be lost.
    return false;
#else
 
    if (!comphelper::isFileUrl(rURL))
        // Not a file:// URL.
        return false;
 
#ifdef UNX
    OUString rPath;
    if (osl::FileBase::getSystemPathFromFileURL(rURL, rPath) != osl::FileBase::E_None)
        return false;
 
    struct stat buf;
    if (lstat(rPath.toUtf8().getStr(), &buf) != 0)
        return false;
 
    // Hardlink or symlink: osl::File::move() doesn't play with these nicely.
    if (buf.st_nlink > 1 || S_ISLNK(buf.st_mode))
        return false;
#endif
 
    return true;
#endif
}
 
} // anonymous namespace
 
class SfxMedium_Impl
{
public:
    StreamMode m_nStorOpenMode;
    ErrCode    m_eError;
 
    ::ucbhelper::Content aContent;
    bool bUpdatePickList:1;
    bool bIsTemp:1;
    bool bDownloadDone:1;
    bool bIsStorage:1;
    bool bUseInteractionHandler:1;
    bool bAllowDefaultIntHdl:1;
    bool bDisposeStorage:1;
    bool bStorageBasedOnInStream:1;
    bool m_bSalvageMode:1;
    bool m_bVersionsAlreadyLoaded:1;
    bool m_bLocked:1;
    bool m_bDisableUnlockWebDAV:1;
    bool m_bGotDateTime:1;
    bool m_bRemoveBackup:1;
    bool m_bOriginallyReadOnly:1;
    bool m_bOriginallyLoadedReadOnly:1;
    bool m_bTriedStorage:1;
    bool m_bRemote:1;
    bool m_bInputStreamIsReadOnly:1;
    bool m_bInCheckIn:1;
    bool m_bDisableFileSync = false;
 
    OUString m_aName;
    OUString m_aLogicName;
    OUString m_aLongName;
 
    mutable std::unique_ptr<SfxItemSet> m_pSet;
    mutable std::unique_ptr<INetURLObject> m_pURLObj;
 
    std::shared_ptr<const SfxFilter> m_pFilter;
    std::shared_ptr<const SfxFilter> m_pCustomFilter;
 
    std::unique_ptr<SvStream> m_pInStream;
    std::unique_ptr<SvStream> m_pOutStream;
 
    OUString    aOrigURL;
    DateTime         aExpireTime;
    SfxFrameWeakRef  wLoadTargetFrame;
    SvKeyValueIteratorRef xAttributes;
 
    svtools::AsynchronLink  aDoneLink;
 
    uno::Sequence < util::RevisionTag > aVersions;
 
    std::unique_ptr<::utl::TempFile> pTempFile;
 
    uno::Reference<embed::XStorage> xStorage;
    uno::Reference<embed::XStorage> m_xZipStorage;
    uno::Reference<io::XInputStream> m_xInputStreamToLoadFrom;
    uno::Reference<io::XInputStream> xInputStream;
    uno::Reference<io::XStream> xStream;
    uno::Reference<io::XStream> m_xLockingStream;
    uno::Reference<task::XInteractionHandler> xInteraction;
 
    ErrCode  nLastStorageError;
 
    OUString m_aBackupURL;
 
    // the following member is changed and makes sense only during saving
    // TODO/LATER: in future the signature state should be controlled by the medium not by the document
    //             in this case the member will hold this information
    SignatureState             m_nSignatureState;
 
    bool m_bHasEmbeddedObjects = false;
 
    util::DateTime m_aDateTime;
 
    explicit SfxMedium_Impl();
    ~SfxMedium_Impl();
    SfxMedium_Impl(const SfxMedium_Impl&) = delete;
    SfxMedium_Impl& operator=(const SfxMedium_Impl&) = delete;
 
    OUString getFilterMimeType()
        { return !m_pFilter ? OUString() : m_pFilter->GetMimeType(); }
};
 
 
SfxMedium_Impl::SfxMedium_Impl() :
    m_nStorOpenMode(SFX_STREAM_READWRITE),
    m_eError(ERRCODE_NONE),
    bUpdatePickList(true),
    bIsTemp( false ),
    bDownloadDone( true ),
    bIsStorage( false ),
    bUseInteractionHandler( true ),
    bAllowDefaultIntHdl( false ),
    bDisposeStorage( false ),
    bStorageBasedOnInStream( false ),
    m_bSalvageMode( false ),
    m_bVersionsAlreadyLoaded( false ),
    m_bLocked( false ),
    m_bDisableUnlockWebDAV( false ),
    m_bGotDateTime( false ),
    m_bRemoveBackup( false ),
    m_bOriginallyReadOnly(false),
    m_bOriginallyLoadedReadOnly(false),
    m_bTriedStorage(false),
    m_bRemote(false),
    m_bInputStreamIsReadOnly(false),
    m_bInCheckIn(false),
    aExpireTime( DateTime( DateTime::SYSTEM ) + static_cast<sal_Int32>(10) ),
    nLastStorageError( ERRCODE_NONE ),
    m_nSignatureState( SignatureState::NOSIGNATURES )
{
    aDoneLink.CreateMutex();
}
 
 
SfxMedium_Impl::~SfxMedium_Impl()
{
    aDoneLink.ClearPendingCall();
 
    pTempFile.reset();
    m_pSet.reset();
    m_pURLObj.reset();
}
 
void SfxMedium::ResetError()
{
    pImpl->m_eError = ERRCODE_NONE;
    if( pImpl->m_pInStream )
        pImpl->m_pInStream->ResetError();
    if( pImpl->m_pOutStream )
        pImpl->m_pOutStream->ResetError();
}
 
ErrCode const & SfxMedium::GetLastStorageCreationState()
{
    return pImpl->nLastStorageError;
}
 
void SfxMedium::SetError(ErrCode nError)
{
    pImpl->m_eError = nError;
}
 
ErrCode SfxMedium::GetErrorCode() const
{
    ErrCode lError = pImpl->m_eError;
    if(!lError && pImpl->m_pInStream)
        lError = pImpl->m_pInStream->GetErrorCode();
    if(!lError && pImpl->m_pOutStream)
        lError = pImpl->m_pOutStream->GetErrorCode();
    return lError;
}
 
void SfxMedium::CheckFileDate( const util::DateTime& aInitDate )
{
    GetInitFileDate( true );
    if ( pImpl->m_aDateTime.Seconds != aInitDate.Seconds
      || pImpl->m_aDateTime.Minutes != aInitDate.Minutes
      || pImpl->m_aDateTime.Hours != aInitDate.Hours
      || pImpl->m_aDateTime.Day != aInitDate.Day
      || pImpl->m_aDateTime.Month != aInitDate.Month
      || pImpl->m_aDateTime.Year != aInitDate.Year )
    {
        uno::Reference< task::XInteractionHandler > xHandler = GetInteractionHandler();
 
        if ( xHandler.is() )
        {
            try
            {
                ::rtl::Reference< ::ucbhelper::InteractionRequest > xInteractionRequestImpl = new ::ucbhelper::InteractionRequest( uno::makeAny(
                    document::ChangedByOthersRequest() ) );
                uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations( 3 );
                aContinuations[0] = new ::ucbhelper::InteractionAbort( xInteractionRequestImpl.get() );
                aContinuations[1] = new ::ucbhelper::InteractionApprove( xInteractionRequestImpl.get() );
                xInteractionRequestImpl->setContinuations( aContinuations );
 
                xHandler->handle( xInteractionRequestImpl.get() );
 
                ::rtl::Reference< ::ucbhelper::InteractionContinuation > xSelected = xInteractionRequestImpl->getSelection();
                if ( uno::Reference< task::XInteractionAbort >( xSelected.get(), uno::UNO_QUERY ).is() )
                {
                    SetError(ERRCODE_ABORT);
                }
            }
            catch ( const uno::Exception& )
            {}
        }
    }
}
 
bool SfxMedium::DocNeedsFileDateCheck() const
{
    return ( !IsReadOnly() && ( GetURLObject().GetProtocol() == INetProtocol::File ||
                                GetURLObject().isAnyKnownWebDAVScheme() ) );
}
 
util::DateTime const & SfxMedium::GetInitFileDate( bool bIgnoreOldValue )
{
    if ( ( bIgnoreOldValue || !pImpl->m_bGotDateTime ) && !pImpl->m_aLogicName.isEmpty() )
    {
        try
        {
            // add a default css::ucb::XCommandEnvironment
            // in order to have the WebDAV UCP provider manage http/https authentication correctly
            ::ucbhelper::Content aContent( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ),
                                           utl::UCBContentHelper::getDefaultCommandEnvironment(),
                                           comphelper::getProcessComponentContext() );
 
            aContent.getPropertyValue("DateModified") >>= pImpl->m_aDateTime;
            pImpl->m_bGotDateTime = true;
        }
        catch ( const css::uno::Exception& )
        {
        }
    }
 
    return pImpl->m_aDateTime;
}
 
 
Reference < XContent > SfxMedium::GetContent() const
{
    if ( !pImpl->aContent.get().is() )
    {
        Reference < css::ucb::XContent > xContent;
 
        // tdf#95144 add a default css::ucb::XCommandEnvironment
        // in order to have the WebDAV UCP provider manage https protocol certificates correctly
        css:: uno::Reference< task::XInteractionHandler > xIH(
                css::task::InteractionHandler::createWithParent( comphelper::getProcessComponentContext(), nullptr ) );
 
        css::uno::Reference< css::ucb::XProgressHandler > xProgress;
        ::ucbhelper::CommandEnvironment* pCommandEnv = new ::ucbhelper::CommandEnvironment( new comphelper::SimpleFileAccessInteraction( xIH ), xProgress );
 
        Reference < css::ucb::XCommandEnvironment > xEnv( static_cast< css::ucb::XCommandEnvironment* >(pCommandEnv), css::uno::UNO_QUERY );
 
        const SfxUnoAnyItem* pItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_CONTENT, false);
        if ( pItem )
            pItem->GetValue() >>= xContent;
 
        if ( xContent.is() )
        {
            try
            {
                pImpl->aContent = ::ucbhelper::Content( xContent, xEnv, comphelper::getProcessComponentContext() );
            }
            catch ( const Exception& )
            {
            }
        }
        else
        {
            // TODO: SAL_WARN( "sfx.doc", "SfxMedium::GetContent()\nCreate Content? This code exists as fallback only. Please clarify, why its used.");
            OUString aURL;
            if ( !pImpl->m_aName.isEmpty() )
                osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aURL );
            else if ( !pImpl->m_aLogicName.isEmpty() )
                aURL = GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE );
            if (!aURL.isEmpty() )
                (void)::ucbhelper::Content::create( aURL, xEnv, comphelper::getProcessComponentContext(), pImpl->aContent );
        }
    }
 
    return pImpl->aContent.get();
}
 
OUString SfxMedium::GetBaseURL( bool bForSaving )
{
    OUString aBaseURL;
    const SfxStringItem* pBaseURLItem = GetItemSet()->GetItem<SfxStringItem>(SID_DOC_BASEURL);
    if ( pBaseURLItem )
        aBaseURL = pBaseURLItem->GetValue();
    else if (!utl::ConfigManager::IsFuzzing() && GetContent().is())
    {
        try
        {
            Any aAny = pImpl->aContent.getPropertyValue("BaseURI");
            aAny >>= aBaseURL;
        }
        catch ( const css::uno::Exception& )
        {
        }
 
        if ( aBaseURL.isEmpty() )
            aBaseURL = GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE );
    }
 
    if ( bForSaving )
    {
        SvtSaveOptions aOpt;
        bool bIsRemote = IsRemote();
        if( (bIsRemote && !aOpt.IsSaveRelINet()) || (!pImpl->m_bRemote && !aOpt.IsSaveRelFSys()) )
            return OUString();
    }
 
    return aBaseURL;
}
 
bool SfxMedium::IsSkipImages()
{
    const SfxStringItem* pSkipImagesItem = GetItemSet()->GetItem<SfxStringItem>(SID_FILE_FILTEROPTIONS);
    return pSkipImagesItem && pSkipImagesItem->GetValue() == "SkipImages";
}
 
SvStream* SfxMedium::GetInStream()
{
    if ( pImpl->m_pInStream )
        return pImpl->m_pInStream.get();
 
    if ( pImpl->pTempFile )
    {
        pImpl->m_pInStream.reset( new SvFileStream(pImpl->m_aName, pImpl->m_nStorOpenMode) );
 
        pImpl->m_eError = pImpl->m_pInStream->GetError();
 
        if (!pImpl->m_eError && (pImpl->m_nStorOpenMode & StreamMode::WRITE)
                    && ! pImpl->m_pInStream->IsWritable() )
        {
            pImpl->m_eError = ERRCODE_IO_ACCESSDENIED;
            pImpl->m_pInStream.reset();
        }
        else
            return pImpl->m_pInStream.get();
    }
 
    GetMedium_Impl();
 
    if ( GetError() )
        return nullptr;
 
    return pImpl->m_pInStream.get();
}
 
 
void SfxMedium::CloseInStream()
{
    CloseInStream_Impl();
}
 
void SfxMedium::CloseInStream_Impl()
{
    // if there is a storage based on the InStream, we have to
    // close the storage, too, because otherwise the storage
    // would use an invalid ( deleted ) stream.
    if ( pImpl->m_pInStream && pImpl->xStorage.is() )
    {
        if ( pImpl->bStorageBasedOnInStream )
            CloseStorage();
    }
 
    if ( pImpl->m_pInStream && !GetContent().is() )
    {
        CreateTempFile();
        return;
    }
 
    pImpl->m_pInStream.reset();
    if ( pImpl->m_pSet )
        pImpl->m_pSet->ClearItem( SID_INPUTSTREAM );
 
    CloseZipStorage_Impl();
    pImpl->xInputStream.clear();
 
    if ( !pImpl->m_pOutStream )
    {
        // output part of the stream is not used so the whole stream can be closed
        // TODO/LATER: is it correct?
        pImpl->xStream.clear();
        if ( pImpl->m_pSet )
            pImpl->m_pSet->ClearItem( SID_STREAM );
    }
}
 
 
SvStream* SfxMedium::GetOutStream()
{
    if ( !pImpl->m_pOutStream )
    {
        // Create a temp. file if there is none because we always
        // need one.
        CreateTempFile( false );
 
        if ( pImpl->pTempFile )
        {
            // On windows we try to re-use XOutStream from xStream if that exists;
            // because opening new SvFileStream in this situation may fail with ERROR_SHARING_VIOLATION
            // TODO: this is a horrible hack that should probably be removed,
            // somebody needs to investigate this more thoroughly...
            if (getenv("SFX_MEDIUM_REUSE_STREAM") && pImpl->xStream.is())
            {
                assert(pImpl->xStream->getOutputStream().is()); // need that...
                pImpl->m_pOutStream = utl::UcbStreamHelper::CreateStream(
                        pImpl->xStream, false);
            }
            else
            {
            // On Unix don't try to re-use XOutStream from xStream if that exists;
            // it causes fdo#59022 (fails opening files via SMB on Linux)
                pImpl->m_pOutStream.reset( new SvFileStream(
                            pImpl->m_aName, StreamMode::STD_READWRITE) );
            }
            CloseStorage();
        }
    }
 
    return pImpl->m_pOutStream.get();
}
 
 
void SfxMedium::CloseOutStream()
{
    CloseOutStream_Impl();
}
 
void SfxMedium::CloseOutStream_Impl()
{
    if ( pImpl->m_pOutStream )
    {
        // if there is a storage based on the OutStream, we have to
        // close the storage, too, because otherwise the storage
        // would use an invalid ( deleted ) stream.
        //TODO/MBA: how to deal with this?!
        //maybe we need a new flag when the storage was created from the outstream
        if ( pImpl->xStorage.is() )
        {
                CloseStorage();
        }
 
        pImpl->m_pOutStream.reset();
    }
 
    if ( !pImpl->m_pInStream )
    {
        // input part of the stream is not used so the whole stream can be closed
        // TODO/LATER: is it correct?
        pImpl->xStream.clear();
        if ( pImpl->m_pSet )
            pImpl->m_pSet->ClearItem( SID_STREAM );
    }
}
 
 
const OUString& SfxMedium::GetPhysicalName() const
{
    if ( pImpl->m_aName.isEmpty() && !pImpl->m_aLogicName.isEmpty() )
        const_cast<SfxMedium*>(this)->CreateFileStream();
 
    // return the name then
    return pImpl->m_aName;
}
 
 
void SfxMedium::CreateFileStream()
{
    // force synchron
    if( pImpl->m_pInStream )
    {
        SvLockBytes* pBytes = pImpl->m_pInStream->GetLockBytes();
        if( pBytes )
            pBytes->SetSynchronMode();
    }
 
    GetInStream();
    if( pImpl->m_pInStream )
    {
        CreateTempFile( false );
        pImpl->bIsTemp = true;
        CloseInStream_Impl();
    }
}
 
 
bool SfxMedium::Commit()
{
    if( pImpl->xStorage.is() )
        StorageCommit_Impl();
    else if( pImpl->m_pOutStream  )
        pImpl->m_pOutStream->Flush();
    else if( pImpl->m_pInStream  )
        pImpl->m_pInStream->Flush();
 
    if ( GetError() == ERRCODE_NONE )
    {
        // does something only in case there is a temporary file ( means aName points to different location than aLogicName )
        Transfer_Impl();
    }
 
    bool bResult = ( GetError() == ERRCODE_NONE );
 
    if ( bResult && DocNeedsFileDateCheck() )
        GetInitFileDate( true );
 
    // remove truncation mode from the flags
    pImpl->m_nStorOpenMode &= (~StreamMode::TRUNC);
    return bResult;
}
 
 
bool SfxMedium::IsStorage()
{
    if ( pImpl->xStorage.is() )
        return true;
 
    if ( pImpl->m_bTriedStorage )
        return pImpl->bIsStorage;
 
    if ( pImpl->pTempFile )
    {
        OUString aURL;
        if ( osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aURL )
             == osl::FileBase::E_None )
        {
            SAL_WARN( "sfx.doc", "Physical name not convertible!");
        }
        pImpl->bIsStorage = SotStorage::IsStorageFile( aURL ) && !SotStorage::IsOLEStorage( aURL);
        if ( !pImpl->bIsStorage )
            pImpl->m_bTriedStorage = true;
    }
    else if ( GetInStream() )
    {
        pImpl->bIsStorage = SotStorage::IsStorageFile( pImpl->m_pInStream.get() ) && !SotStorage::IsOLEStorage( pImpl->m_pInStream.get() );
        if ( !pImpl->m_pInStream->GetError() && !pImpl->bIsStorage )
            pImpl->m_bTriedStorage = true;
    }
 
    return pImpl->bIsStorage;
}
 
 
bool SfxMedium::IsPreview_Impl()
{
    bool bPreview = false;
    const SfxBoolItem* pPreview = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_PREVIEW, false);
    if ( pPreview )
        bPreview = pPreview->GetValue();
    else
    {
        const SfxStringItem* pFlags = SfxItemSet::GetItem<SfxStringItem>(GetItemSet(), SID_OPTIONS, false);
        if ( pFlags )
        {
            OUString aFileFlags = pFlags->GetValue();
            aFileFlags = aFileFlags.toAsciiUpperCase();
            if ( -1 != aFileFlags.indexOf( 'B' ) )
                bPreview = true;
        }
    }
 
    return bPreview;
}
 
 
void SfxMedium::StorageBackup_Impl()
{
    ::ucbhelper::Content aOriginalContent;
    Reference< css::ucb::XCommandEnvironment > xDummyEnv;
 
    bool bBasedOnOriginalFile = ( !pImpl->pTempFile && !( !pImpl->m_aLogicName.isEmpty() && pImpl->m_bSalvageMode )
        && !GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ).isEmpty()
        && GetURLObject().GetProtocol() == INetProtocol::File
        && ::utl::UCBContentHelper::IsDocument( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) );
 
    if ( bBasedOnOriginalFile && pImpl->m_aBackupURL.isEmpty()
      && ::ucbhelper::Content::create( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), xDummyEnv, comphelper::getProcessComponentContext(), aOriginalContent ) )
    {
        DoInternalBackup_Impl( aOriginalContent );
        if( pImpl->m_aBackupURL.isEmpty() )
            SetError(ERRCODE_SFX_CANTCREATEBACKUP);
    }
}
 
 
OUString const & SfxMedium::GetBackup_Impl()
{
    if ( pImpl->m_aBackupURL.isEmpty() )
        StorageBackup_Impl();
 
    return pImpl->m_aBackupURL;
}
 
 
uno::Reference < embed::XStorage > SfxMedium::GetOutputStorage()
{
    if ( GetError() )
        return uno::Reference< embed::XStorage >();
 
    // if the medium was constructed with a Storage: use this one, not a temp. storage
    // if a temporary storage already exists: use it
    if ( pImpl->xStorage.is() && ( pImpl->m_aLogicName.isEmpty() || pImpl->pTempFile ) )
        return pImpl->xStorage;
 
    // if necessary close stream that was used for reading
    if ( pImpl->m_pInStream && !pImpl->m_pInStream->IsWritable() )
        CloseInStream();
 
    DBG_ASSERT( !pImpl->m_pOutStream, "OutStream in a readonly Medium?!" );
 
    // TODO/LATER: The current solution is to store the document temporary and then copy it to the target location;
    // in future it should be stored directly and then copied to the temporary location, since in this case no
    // file attributes have to be preserved and system copying mechanics could be used instead of streaming.
    CreateTempFileNoCopy();
 
    return GetStorage();
}
 
 
void SfxMedium::SetEncryptionDataToStorage_Impl()
{
    // in case media-descriptor contains password it should be used on opening
    if ( pImpl->xStorage.is() && pImpl->m_pSet )
    {
        uno::Sequence< beans::NamedValue > aEncryptionData;
        if ( GetEncryptionData_Impl( pImpl->m_pSet.get(), aEncryptionData ) )
        {
            // replace the password with encryption data
            pImpl->m_pSet->ClearItem( SID_PASSWORD );
            pImpl->m_pSet->Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::makeAny( aEncryptionData ) ) );
 
            try
            {
                ::comphelper::OStorageHelper::SetCommonStorageEncryptionData( pImpl->xStorage, aEncryptionData );
            }
            catch( const uno::Exception& )
            {
                SAL_WARN( "sfx.doc", "It must be possible to set a common password for the storage" );
                // TODO/LATER: set the error code in case of problem
                // SetError(ERRCODE_IO_GENERAL);
            }
        }
    }
}
 
#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
 
// FIXME: Hmm actually lock files should be used for sftp: documents
// even if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT. Only the use of lock
// files for *local* documents is unnecessary in that case. But
// actually, the checks for sftp: here are just wishful thinking; I
// don't this there is any support for actually editing documents
// behind sftp: URLs anyway.
 
// Sure, there could perhaps be a 3rd-party extension that brings UCB
// the potential to handle files behind sftp:. But there could also be
// an extension that handles some arbitrary foobar: scheme *and* it
// could be that lock files would be the correct thing to use for
// foobar: documents, too. But the hardcoded test below won't know
// that. Clearly the knowledge whether lock files should be used or
// not for some URL scheme belongs in UCB, not here.
 
 
SfxMedium::ShowLockResult SfxMedium::ShowLockedDocumentDialog( const LockFileEntry& aData, bool bIsLoading, bool bOwnLock, bool bHandleSysLocked )
{
    ShowLockResult nResult = ShowLockResult::NoLock;
 
    // tdf#92817: Simple check for empty lock file that needs to be deleted, when system locking is enabled
    if( aData[LockFileComponent::OOOUSERNAME].isEmpty() && aData[LockFileComponent::SYSUSERNAME].isEmpty() && !bHandleSysLocked )
        bOwnLock=true;
 
    // show the interaction regarding the document opening
    uno::Reference< task::XInteractionHandler > xHandler = GetInteractionHandler();
 
    if ( ::svt::DocumentLockFile::IsInteractionAllowed() && xHandler.is() && ( bIsLoading || !bHandleSysLocked || bOwnLock ) )
    {
        OUString aDocumentURL = GetURLObject().GetLastName();
        OUString aInfo;
        ::rtl::Reference< ::ucbhelper::InteractionRequest > xInteractionRequestImpl;
 
        sal_Int32 nContinuations = 3;
 
        if ( bOwnLock )
        {
            aInfo = aData[LockFileComponent::EDITTIME];
 
            xInteractionRequestImpl = new ::ucbhelper::InteractionRequest( uno::makeAny(
                document::OwnLockOnDocumentRequest( OUString(), uno::Reference< uno::XInterface >(), aDocumentURL, aInfo, !bIsLoading ) ) );
        }
        else
        {
            // Use a fourth continuation in case there's no filesystem lock:
            // "Ignore lock file and open/replace the document"
            if (!bHandleSysLocked)
                nContinuations = 4;
 
            if ( !aData[LockFileComponent::OOOUSERNAME].isEmpty() )
                aInfo = aData[LockFileComponent::OOOUSERNAME];
            else
                aInfo = aData[LockFileComponent::SYSUSERNAME];
 
            if ( !aInfo.isEmpty() && !aData[LockFileComponent::EDITTIME].isEmpty() )
                aInfo += " ( " + aData[LockFileComponent::EDITTIME] + " )";
 
            if (!bIsLoading) // so, !bHandleSysLocked
            {
                xInteractionRequestImpl = new ::ucbhelper::InteractionRequest(uno::makeAny(
                    document::LockedOnSavingRequest(OUString(), uno::Reference< uno::XInterface >(), aDocumentURL, aInfo)));
                // Currently, only the last "Retry" continuation (meaning ignore the lock and try overwriting) can be returned.
            }
            else /*logically therefore bIsLoading is set */
            {
                xInteractionRequestImpl = new ::ucbhelper::InteractionRequest( uno::makeAny(
                    document::LockedDocumentRequest( OUString(), uno::Reference< uno::XInterface >(), aDocumentURL, aInfo ) ) );
            }
        }
 
        uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations(nContinuations);
        aContinuations[0] = new ::ucbhelper::InteractionAbort( xInteractionRequestImpl.get() );
        aContinuations[1] = new ::ucbhelper::InteractionApprove( xInteractionRequestImpl.get() );
        aContinuations[2] = new ::ucbhelper::InteractionDisapprove( xInteractionRequestImpl.get() );
        if (nContinuations > 3)
        {
            // We use InteractionRetry to reflect that user wants to
            // ignore the (stale?) alien lock file and open/overwrite the document
            aContinuations[3] = new ::ucbhelper::InteractionRetry(xInteractionRequestImpl.get());
        }
        xInteractionRequestImpl->setContinuations( aContinuations );
 
        xHandler->handle( xInteractionRequestImpl.get() );
 
        ::rtl::Reference< ::ucbhelper::InteractionContinuation > xSelected = xInteractionRequestImpl->getSelection();
        if ( uno::Reference< task::XInteractionAbort >( xSelected.get(), uno::UNO_QUERY ).is() )
        {
            SetError(ERRCODE_ABORT);
        }
        else if ( uno::Reference< task::XInteractionDisapprove >( xSelected.get(), uno::UNO_QUERY ).is() )
        {
            // own lock on loading, user has selected to ignore the lock
            // own lock on saving, user has selected to ignore the lock
            // alien lock on loading, user has selected to edit a copy of document
            // TODO/LATER: alien lock on saving, user has selected to do SaveAs to different location
            if ( !bOwnLock ) // bIsLoading implied from outermost condition
            {
                // means that a copy of the document should be opened
                GetItemSet()->Put( SfxBoolItem( SID_TEMPLATE, true ) );
            }
            else
                nResult = ShowLockResult::Succeeded;
        }
        else if (uno::Reference< task::XInteractionRetry >(xSelected.get(), uno::UNO_QUERY).is())
        {
            // User decided to ignore the alien (stale?) lock file without filesystem lock
            nResult = ShowLockResult::Succeeded;
        }
        else // if ( XSelected == aContinuations[1] )
        {
            // own lock on loading, user has selected to open readonly
            // own lock on saving, user has selected to open readonly
            // alien lock on loading, user has selected to retry saving
            // TODO/LATER: alien lock on saving, user has selected to retry saving
 
            if ( bIsLoading )
                GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
            else
                nResult = ShowLockResult::Try;
        }
    }
    else
    {
        if ( bIsLoading )
        {
            // if no interaction handler is provided the default answer is open readonly
            // that usually happens in case the document is loaded per API
            // so the document must be opened readonly for backward compatibility
            GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
        }
        else
            SetError(ERRCODE_IO_ACCESSDENIED);
 
    }
 
    return nResult;
}
 
 
bool SfxMedium::ShowLockFileProblemDialog(MessageDlg nWhichDlg)
{
    // system file locking is not active, ask user whether he wants to open the document without any locking
    uno::Reference< task::XInteractionHandler > xHandler = GetInteractionHandler();
 
    if (xHandler.is())
    {
        ::rtl::Reference< ::ucbhelper::InteractionRequest > xIgnoreRequestImpl;
 
        switch (nWhichDlg)
        {
            case MessageDlg::LockFileIgnore:
                xIgnoreRequestImpl = new ::ucbhelper::InteractionRequest(uno::makeAny( document::LockFileIgnoreRequest() ));
                break;
            case MessageDlg::LockFileCorrupt:
                xIgnoreRequestImpl = new ::ucbhelper::InteractionRequest(uno::makeAny( document::LockFileCorruptRequest() ));
                break;
        }
 
        uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations(2);
        aContinuations[0] = new ::ucbhelper::InteractionAbort(xIgnoreRequestImpl.get());
        aContinuations[1] = new ::ucbhelper::InteractionApprove(xIgnoreRequestImpl.get());
        xIgnoreRequestImpl->setContinuations(aContinuations);
 
        xHandler->handle(xIgnoreRequestImpl.get());
 
        ::rtl::Reference< ::ucbhelper::InteractionContinuation > xSelected = xIgnoreRequestImpl->getSelection();
        bool bReadOnly = uno::Reference< task::XInteractionApprove >(xSelected.get(), uno::UNO_QUERY).is();
 
        if (bReadOnly)
        {
            GetItemSet()->Put(SfxBoolItem(SID_DOC_READONLY, true));
        }
        else
        {
            SetError(ERRCODE_ABORT);
        }
 
        return bReadOnly;
    }
 
    return false;
}
 
namespace
{
    bool isSuitableProtocolForLocking(const OUString & rLogicName)
    {
        INetURLObject aUrl( rLogicName );
        INetProtocol eProt = aUrl.GetProtocol();
#if !HAVE_FEATURE_MACOSX_SANDBOX
        if (eProt == INetProtocol::File) {
            return true;
        }
#endif
        return eProt == INetProtocol::Smb || eProt == INetProtocol::Sftp;
    }
}
 
#endif // HAVE_FEATURE_MULTIUSER_ENVIRONMENT
 
// sets SID_DOC_READONLY if the document cannot be opened for editing
// if user cancel the loading the ERROR_ABORT is set
SfxMedium::LockFileResult SfxMedium::LockOrigFileOnDemand( bool bLoading, bool bNoUI, bool bTryIgnoreLockFile )
{
#if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT
    (void) bLoading;
    (void) bNoUI;
    (void) bTryIgnoreLockFile;
    return LockFileResult::Succeeded;
#else
    LockFileResult eResult = LockFileResult::Failed;
 
    // check if path scheme is http:// or https://
    // may be this is better if used always, in Android and iOS as well?
    // if this code should be always there, remember to move the relevant code in UnlockFile method as well !
 
    if ( GetURLObject().isAnyKnownWebDAVScheme() )
    {
        try
        {
            bool bResult = pImpl->m_bLocked;
            bool bIsTemplate = false;
            // so, this is webdav stuff...
            if ( !bResult )
            {
                // no read-write access is necessary on loading if the document is explicitly opened as copy
                const SfxBoolItem* pTemplateItem = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_TEMPLATE, false);
                bIsTemplate = ( bLoading && pTemplateItem && pTemplateItem->GetValue() );
            }
 
            if ( !bIsTemplate && !bResult && !IsReadOnly() )
            {
                ShowLockResult bUIStatus = ShowLockResult::NoLock;
                do
                {
                    if( !bResult )
                    {
                        uno::Reference< task::XInteractionHandler > xCHandler = GetInteractionHandler( true );
                        Reference< css::ucb::XCommandEnvironment > xComEnv = new ::ucbhelper::CommandEnvironment(
                            xCHandler, Reference< css::ucb::XProgressHandler >() );
 
                        ucbhelper::Content aContentToLock(
                            GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ),
                            xComEnv, comphelper::getProcessComponentContext() );
 
                        try
                        {
                            aContentToLock.lock();
                            bResult = true;
                        }
                        catch ( ucb::InteractiveLockingLockedException& )
                        {
                            // received when the resource is already locked
                            // get the lock owner, using a special ucb.webdav property
                            // the owner property retrieved here is  what the other principal send the server
                            // when activating the lock.
                            // See http://tools.ietf.org/html/rfc4918#section-14.17 for details
                            LockFileEntry aLockData;
                            aLockData[LockFileComponent::OOOUSERNAME] = "Unknown user";
                            // This solution works right when the LO user name and the WebDAV user
                            // name are the same.
                            // A better thing to do would be to obtain the 'real' WebDAV user name,
                            // but that's not possible from a WebDAV UCP provider client.
                            LockFileEntry aOwnData = svt::LockFileCommon::GenerateOwnEntry();
                            // use the current LO user name as the system name
                            aLockData[LockFileComponent::SYSUSERNAME] = aOwnData[LockFileComponent::SYSUSERNAME];
 
                            uno::Sequence< css::ucb::Lock >  aLocks;
                            // getting the property, send a PROPFIND to the server over the net
                            if( aContentToLock.getPropertyValue( "DAV:lockdiscovery" )  >>= aLocks )
                            {
                                // got at least a lock, show the owner of the first lock returned
                                css::ucb::Lock aLock = aLocks[0];
                                OUString aOwner;
                                if(aLock.Owner >>= aOwner)
                                {
                                    // we need to display the WebDAV user name owning the lock, not the local one
                                    aLockData[LockFileComponent::OOOUSERNAME] = aOwner;
                                }
                            }
 
                            if ( !bResult && !bNoUI )
                            {
                                bUIStatus = ShowLockedDocumentDialog( aLockData, bLoading, false , true );
                            }
                        }
                        catch( ucb::InteractiveNetworkWriteException& )
                        {
                            // This catch it's not really needed, here just for the sake of documentation on the behaviour.
                            // This is the most likely reason:
                            // - the remote site is a WebDAV with special configuration: read/only for read operations
                            //   and read/write for write operations, the user is not allowed to lock/write and
                            //   she cancelled the credentials request.
                            //   this is not actually an error, but the exception is sent directly from ucb, avoiding the automatic
                            //   management that takes part in cancelCommandExecution()
                            // Unfortunately there is no InteractiveNetwork*Exception available to signal this more correctly
                            // since it mostly happens on read/only part of webdav, this can be the most correct
                            // exception available
                        }
                        catch( uno::Exception& )
                        {}
                    }
                } while( !bResult && bUIStatus == ShowLockResult::Try );
            }
 
            pImpl->m_bLocked = bResult;
 
            if ( !bResult && GetError() == ERRCODE_NONE )
            {
                // the error should be set in case it is storing process
                // or the document has been opened for editing explicitly
                const SfxBoolItem* pReadOnlyItem = SfxItemSet::GetItem<SfxBoolItem>(pImpl->m_pSet.get(), SID_DOC_READONLY, false);
 
                if ( !bLoading || (pReadOnlyItem && !pReadOnlyItem->GetValue()) )
                    SetError(ERRCODE_IO_ACCESSDENIED);
                else
                    GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
            }
 
            // when the file is locked, get the current file date
            if ( bResult && DocNeedsFileDateCheck() )
                GetInitFileDate( true );
 
            if ( bResult )
                eResult = LockFileResult::Succeeded;
        }
        catch ( const uno::Exception& )
        {
            SAL_WARN( "sfx.doc", "Locking exception: WebDAV while trying to lock the file" );
        }
        return eResult;
    }
 
    if (!IsLockingUsed())
        return LockFileResult::Succeeded;
    if (GetURLObject().HasError())
        return eResult;
 
    try
    {
        if ( pImpl->m_bLocked && bLoading
             && GetURLObject().GetProtocol() == INetProtocol::File )
        {
            // if the document is already locked the system locking might be temporarily off after storing
            // check whether the system file locking should be taken again
            GetLockingStream_Impl();
        }
 
        bool bResult = pImpl->m_bLocked;
 
        if ( !bResult )
        {
            // no read-write access is necessary on loading if the document is explicitly opened as copy
            const SfxBoolItem* pTemplateItem = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_TEMPLATE, false);
            bResult = ( bLoading && pTemplateItem && pTemplateItem->GetValue() );
        }
 
        if ( !bResult && !IsReadOnly() )
        {
            bool bContentReadonly = false;
            if ( bLoading && GetURLObject().GetProtocol() == INetProtocol::File )
            {
                // let the original document be opened to check the possibility to open it for editing
                // and to let the writable stream stay open to hold the lock on the document
                GetLockingStream_Impl();
            }
 
            // "IsReadOnly" property does not allow to detect whether the file is readonly always
            // so we try always to open the file for editing
            // the file is readonly only in case the read-write stream can not be opened
            if ( bLoading && !pImpl->m_xLockingStream.is() )
            {
                try
                {
                    // MediaDescriptor does this check also, the duplication should be avoided in future
                    Reference< css::ucb::XCommandEnvironment > xDummyEnv;
                    ::ucbhelper::Content aContent( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), xDummyEnv, comphelper::getProcessComponentContext() );
                    aContent.getPropertyValue("IsReadOnly") >>= bContentReadonly;
                }
                catch( const uno::Exception& ) {}
            }
 
            // do further checks only if the file not readonly in fs
            if ( !bContentReadonly )
            {
                // the special file locking should be used only for suitable URLs
                if ( isSuitableProtocolForLocking( pImpl->m_aLogicName ) )
                {
 
                    // in case of storing the document should request the output before locking
                    if ( bLoading )
                    {
                        // let the stream be opened to check the system file locking
                        GetMedium_Impl();
                        if (GetError() != ERRCODE_NONE) {
                            return eResult;
                        }
                    }
 
                    ShowLockResult bUIStatus = ShowLockResult::NoLock;
 
                    // check whether system file locking has been used, the default value is false
                    bool bUseSystemLock = comphelper::isFileUrl( pImpl->m_aLogicName ) && IsSystemFileLockingUsed();
 
                    // TODO/LATER: This implementation does not allow to detect the system lock on saving here, actually this is no big problem
                    // if system lock is used the writeable stream should be available
                    bool bHandleSysLocked = ( bLoading && bUseSystemLock && !pImpl->xStream.is() && !pImpl->m_pOutStream );
 
                    // The file is attempted to get locked for the duration of lockfile creation on save
                    std::unique_ptr<osl::File> pFileLock;
                    if (!bLoading && bUseSystemLock && pImpl->pTempFile)
                    {
                        INetURLObject aDest(GetURLObject());
                        OUString aDestURL(aDest.GetMainURL(INetURLObject::DecodeMechanism::NONE));
 
                        if (comphelper::isFileUrl(aDestURL) || !aDest.removeSegment())
                        {
                            pFileLock = o3tl::make_unique<osl::File>(aDestURL);
                            auto rc = pFileLock->open(osl_File_OpenFlag_Write);
                            if (rc == osl::FileBase::E_ACCES)
                                bHandleSysLocked = true;
                        }
                    }
 
                    do
                    {
                        try
                        {
                            ::svt::DocumentLockFile aLockFile( pImpl->m_aLogicName );
                            bool  bIoErr = false;
 
                            if (!bHandleSysLocked)
                            {
                                try
                                {
                                    bResult = aLockFile.CreateOwnLockFile();
                                }
                                catch (const uno::Exception&)
                                {
                                    if (tools::IsMappedWebDAVPath(GetURLObject()))
                                    {
                                        // This is a path that redirects to a WebDAV resource;
                                        // so failure creating lockfile is not an error here.
                                        bResult = true;
                                    }
                                    else if (bLoading && !bNoUI)
                                    {
                                        bIoErr = true;
                                        ShowLockFileProblemDialog(MessageDlg::LockFileIgnore);
                                        bResult = true;   // always delete the defect lock-file
                                    }
                                }
 
                                // in case OOo locking is turned off the lock file is still written if possible
                                // but it is ignored while deciding whether the document should be opened for editing or not
                                if (!bResult && !IsOOoLockFileUsed() && !bIoErr)
                                {
                                    bResult = true;
                                    // take the ownership over the lock file
                                    aLockFile.OverwriteOwnLockFile();
                                }
                            }
 
                            if ( !bResult )
                            {
                                LockFileEntry aData;
                                try
                                {
                                    aData = aLockFile.GetLockData();
                                }
                                catch (const io::WrongFormatException&)
                                {
                                    // we get empty or corrupt data
                                    // info to the user
                                    if (!bIoErr && bLoading && !bNoUI )
                                        bResult = ShowLockFileProblemDialog(MessageDlg::LockFileCorrupt);
 
                                    // not show the Lock Document Dialog
                                    bIoErr = true;
                                }
                                catch( const uno::Exception& )
                                {
                                    // show the Lock Document Dialog, when locked from other app
                                    bIoErr = !bHandleSysLocked;
                                }
 
                                bool bOwnLock = false;
 
                                if (!bHandleSysLocked)
                                {
                                    LockFileEntry aOwnData = svt::LockFileCommon::GenerateOwnEntry();
                                    bOwnLock = aOwnData[LockFileComponent::SYSUSERNAME] == aData[LockFileComponent::SYSUSERNAME];
 
                                    if (bOwnLock
                                        && aOwnData[LockFileComponent::LOCALHOST] == aData[LockFileComponent::LOCALHOST]
                                        && aOwnData[LockFileComponent::USERURL] == aData[LockFileComponent::USERURL])
                                    {
                                        // this is own lock from the same installation, it could remain because of crash
                                        bResult = true;
                                    }
                                }
 
                                if ( !bResult && !bIoErr)
                                {
                                    if (!bNoUI)
                                        bUIStatus = ShowLockedDocumentDialog(aData, bLoading, bOwnLock, bHandleSysLocked);
                                    else if (bLoading && bTryIgnoreLockFile && !bHandleSysLocked)
                                        bUIStatus = ShowLockResult::Succeeded;
 
                                    if ( bUIStatus == ShowLockResult::Succeeded )
                                    {
                                        // take the ownership over the lock file
                                        bResult = aLockFile.OverwriteOwnLockFile();
                                    }
                                    else if (bLoading && !bHandleSysLocked)
                                        eResult = LockFileResult::FailedLockFile;
                                }
                            }
                        }
                        catch( const uno::Exception& )
                        {
                        }
                    } while( !bResult && bUIStatus == ShowLockResult::Try );
 
                    pImpl->m_bLocked = bResult;
                }
                else
                {
                    // this is no file URL, check whether the file is readonly
                    bResult = !bContentReadonly;
                }
            }
        }
 
        if ( !bResult && GetError() == ERRCODE_NONE )
        {
            // the error should be set in case it is storing process
            // or the document has been opened for editing explicitly
            const SfxBoolItem* pReadOnlyItem = SfxItemSet::GetItem<SfxBoolItem>(pImpl->m_pSet.get(), SID_DOC_READONLY, false);
 
            if ( !bLoading || (pReadOnlyItem && !pReadOnlyItem->GetValue()) )
                SetError(ERRCODE_IO_ACCESSDENIED);
            else
                GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
        }
 
        // when the file is locked, get the current file date
        if ( bResult && DocNeedsFileDateCheck() )
            GetInitFileDate( true );
 
        if ( bResult )
            eResult = LockFileResult::Succeeded;
    }
    catch( const uno::Exception& )
    {
        SAL_WARN( "sfx.doc", "Locking exception: high probability, that the content has not been created" );
    }
 
    return eResult;
#endif
}
 
 
uno::Reference < embed::XStorage > SfxMedium::GetStorage( bool bCreateTempIfNo )
{
    if ( pImpl->xStorage.is() || pImpl->m_bTriedStorage )
        return pImpl->xStorage;
 
    uno::Sequence< uno::Any > aArgs( 2 );
 
    // the medium should be retrieved before temporary file creation
    // to let the MediaDescriptor be filled with the streams
    GetMedium_Impl();
 
    if ( bCreateTempIfNo )
        CreateTempFile( false );
 
    GetMedium_Impl();
 
    if ( GetError() )
        return pImpl->xStorage;
 
    const SfxBoolItem* pRepairItem = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_REPAIRPACKAGE, false);
    if ( pRepairItem && pRepairItem->GetValue() )
    {
        // the storage should be created for repairing mode
        CreateTempFile( false );
        GetMedium_Impl();
 
        Reference< css::ucb::XProgressHandler > xProgressHandler;
        Reference< css::task::XStatusIndicator > xStatusIndicator;
 
        const SfxUnoAnyItem* pxProgressItem = SfxItemSet::GetItem<SfxUnoAnyItem>(GetItemSet(), SID_PROGRESS_STATUSBAR_CONTROL, false);
        if( pxProgressItem && ( pxProgressItem->GetValue() >>= xStatusIndicator ) )
            xProgressHandler.set( new utl::ProgressHandlerWrap( xStatusIndicator ) );
 
        uno::Sequence< beans::PropertyValue > aAddProps( 2 );
        aAddProps[0].Name = "RepairPackage";
        aAddProps[0].Value <<= true;
        aAddProps[1].Name = "StatusIndicator";
        aAddProps[1].Value <<= xProgressHandler;
 
        // the first arguments will be filled later
        aArgs.realloc( 3 );
        aArgs[2] <<= aAddProps;
    }
 
    if ( pImpl->xStream.is() )
    {
        // since the storage is based on temporary stream we open it always read-write
        aArgs[0] <<= pImpl->xStream;
        aArgs[1] <<= embed::ElementModes::READWRITE;
        pImpl->bStorageBasedOnInStream = true;
        if (pImpl->m_bDisableFileSync)
        {
            // Forward NoFileSync to the storage factory.
            aArgs.realloc(3);
            uno::Sequence<beans::PropertyValue> aProperties(
                comphelper::InitPropertySequence({ { "NoFileSync", uno::makeAny(true) } }));
            aArgs[2] <<= aProperties;
        }
    }
    else if ( pImpl->xInputStream.is() )
    {
        // since the storage is based on temporary stream we open it always read-write
        aArgs[0] <<= pImpl->xInputStream;
        aArgs[1] <<= embed::ElementModes::READ;
        pImpl->bStorageBasedOnInStream = true;
    }
    else
    {
        CloseStreams_Impl();
        aArgs[0] <<= pImpl->m_aName;
        aArgs[1] <<= embed::ElementModes::READ;
        pImpl->bStorageBasedOnInStream = false;
    }
 
    try
    {
        pImpl->xStorage.set( ::comphelper::OStorageHelper::GetStorageFactory()->createInstanceWithArguments( aArgs ),
                            uno::UNO_QUERY );
    }
    catch( const uno::Exception& )
    {
        // impossibility to create the storage is no error
    }
 
    if( ( pImpl->nLastStorageError = GetError() ) != ERRCODE_NONE )
    {
        pImpl->xStorage = nullptr;
        if ( pImpl->m_pInStream )
            pImpl->m_pInStream->Seek(0);
        return uno::Reference< embed::XStorage >();
    }
 
    pImpl->m_bTriedStorage = true;
 
    // TODO/LATER: Get versionlist on demand
    if ( pImpl->xStorage.is() )
    {
        SetEncryptionDataToStorage_Impl();
        GetVersionList();
    }
 
    const SfxInt16Item* pVersion = SfxItemSet::GetItem<SfxInt16Item>(pImpl->m_pSet.get(), SID_VERSION, false);
 
    bool bResetStorage = false;
    if ( pVersion && pVersion->GetValue() )
    {
        // Read all available versions
        if ( pImpl->aVersions.getLength() )
        {
            // Search for the version fits the comment
            // The versions are numbered starting with 1, versions with
            // negative versions numbers are counted backwards from the
            // current version
            short nVersion = pVersion ? pVersion->GetValue() : 0;
            if ( nVersion<0 )
                nVersion = static_cast<short>(pImpl->aVersions.getLength()) + nVersion;
            else if ( nVersion )
                nVersion--;
 
            util::RevisionTag& rTag = pImpl->aVersions[nVersion];
            {
                // Open SubStorage for all versions
                uno::Reference < embed::XStorage > xSub = pImpl->xStorage->openStorageElement( "Versions",
                        embed::ElementModes::READ );
 
                DBG_ASSERT( xSub.is(), "Version list, but no Versions!" );
 
                // There the version is stored as packed Stream
                uno::Reference < io::XStream > xStr = xSub->openStreamElement( rTag.Identifier, embed::ElementModes::READ );
                std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream( xStr ));
                if ( pStream && pStream->GetError() == ERRCODE_NONE )
                {
                    // Unpack Stream  in TempDir
                    ::utl::TempFile aTempFile;
                    OUString          aTmpName = aTempFile.GetURL();
                    SvFileStream    aTmpStream( aTmpName, SFX_STREAM_READWRITE );
 
                    pStream->ReadStream( aTmpStream );
                    pStream.reset();
                    aTmpStream.Close();
 
                    // Open data as Storage
                    pImpl->m_nStorOpenMode = SFX_STREAM_READONLY;
                    pImpl->xStorage = comphelper::OStorageHelper::GetStorageFromURL( aTmpName, embed::ElementModes::READ );
                    pImpl->bStorageBasedOnInStream = false;
                    OUString aTemp;
                    osl::FileBase::getSystemPathFromFileURL( aTmpName, aTemp );
                    SetPhysicalName_Impl( aTemp );
 
                    pImpl->bIsTemp = true;
                    GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
                    // TODO/MBA
                    pImpl->aVersions.realloc(0);
                }
                else
                    bResetStorage = true;
            }
        }
        else
            bResetStorage = true;
    }
 
    if ( bResetStorage )
    {
        pImpl->xStorage.clear();
        if ( pImpl->m_pInStream )
            pImpl->m_pInStream->Seek( 0 );
    }
 
    pImpl->bIsStorage = pImpl->xStorage.is();
    return pImpl->xStorage;
}
 
 
uno::Reference< embed::XStorage > const & SfxMedium::GetZipStorageToSign_Impl( bool bReadOnly )
{
    if ( !GetError() && !pImpl->m_xZipStorage.is() )
    {
        GetMedium_Impl();
 
        try
        {
            // we can not sign document if there is no stream
            // should it be possible at all?
            if ( !bReadOnly && pImpl->xStream.is() )
            {
                pImpl->m_xZipStorage = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream( ZIP_STORAGE_FORMAT_STRING, pImpl->xStream );
            }
            else if ( pImpl->xInputStream.is() )
            {
                pImpl->m_xZipStorage = ::comphelper::OStorageHelper::GetStorageOfFormatFromInputStream( ZIP_STORAGE_FORMAT_STRING, pImpl->xInputStream );
            }
        }
        catch( const uno::Exception& )
        {
            SAL_WARN( "sfx.doc", "No possibility to get readonly version of storage from medium!" );
        }
 
        if ( GetError() ) // do not remove warnings
            ResetError();
    }
 
    return pImpl->m_xZipStorage;
}
 
 
void SfxMedium::CloseZipStorage_Impl()
{
    if ( pImpl->m_xZipStorage.is() )
    {
        try {
            pImpl->m_xZipStorage->dispose();
        } catch( const uno::Exception& )
        {}
 
        pImpl->m_xZipStorage.clear();
    }
}
 
void SfxMedium::CloseStorage()
{
    if ( pImpl->xStorage.is() )
    {
        uno::Reference < lang::XComponent > xComp( pImpl->xStorage, uno::UNO_QUERY );
        // in the salvage mode the medium does not own the storage
        if ( pImpl->bDisposeStorage && !pImpl->m_bSalvageMode )
        {
            try {
                xComp->dispose();
            } catch( const uno::Exception& )
            {
                SAL_WARN( "sfx.doc", "Medium's storage is already disposed!" );
            }
        }
 
        pImpl->xStorage.clear();
        pImpl->bStorageBasedOnInStream = false;
    }
 
    pImpl->m_bTriedStorage = false;
    pImpl->bIsStorage = false;
}
 
void SfxMedium::CanDisposeStorage_Impl( bool bDisposeStorage )
{
    pImpl->bDisposeStorage = bDisposeStorage;
}
 
bool SfxMedium::WillDisposeStorageOnClose_Impl()
{
    return pImpl->bDisposeStorage;
}
 
StreamMode SfxMedium::GetOpenMode() const
{
    return pImpl->m_nStorOpenMode;
}
 
void SfxMedium::SetOpenMode( StreamMode nStorOpen,
                             bool bDontClose )
{
    if ( pImpl->m_nStorOpenMode != nStorOpen )
    {
        pImpl->m_nStorOpenMode = nStorOpen;
 
        if( !bDontClose )
        {
            if ( pImpl->xStorage.is() )
                CloseStorage();
 
            CloseStreams_Impl();
        }
    }
}
 
 
bool SfxMedium::UseBackupToRestore_Impl( ::ucbhelper::Content& aOriginalContent,
                                            const Reference< css::ucb::XCommandEnvironment >& xComEnv )
{
    try
    {
        ::ucbhelper::Content aTransactCont( pImpl->m_aBackupURL, xComEnv, comphelper::getProcessComponentContext() );
 
        Reference< XInputStream > aOrigInput = aTransactCont.openStream();
        aOriginalContent.writeStream( aOrigInput, true );
        return true;
    }
    catch( const Exception& )
    {
        // in case of failure here the backup file should not be removed
        // TODO/LATER: a message should be used to let user know about the backup
        pImpl->m_bRemoveBackup = false;
        // TODO/LATER: needs a specific error code
        pImpl->m_eError = ERRCODE_IO_GENERAL;
    }
 
    return false;
}
 
 
bool SfxMedium::StorageCommit_Impl()
{
    bool bResult = false;
    Reference< css::ucb::XCommandEnvironment > xDummyEnv;
    ::ucbhelper::Content aOriginalContent;
 
    if ( pImpl->xStorage.is() )
    {
        if ( !GetError() )
        {
            uno::Reference < embed::XTransactedObject > xTrans( pImpl->xStorage, uno::UNO_QUERY );
            if ( xTrans.is() )
            {
                try
                {
                    xTrans->commit();
                    CloseZipStorage_Impl();
                    bResult = true;
                }
                catch ( const embed::UseBackupException& aBackupExc )
                {
                    // since the temporary file is created always now, the scenario is close to be impossible
                    if ( !pImpl->pTempFile )
                    {
                        OSL_ENSURE( !pImpl->m_aBackupURL.isEmpty(), "No backup on storage commit!" );
                        if ( !pImpl->m_aBackupURL.isEmpty()
                            && ::ucbhelper::Content::create( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ),
                                                        xDummyEnv, comphelper::getProcessComponentContext(),
                                                        aOriginalContent ) )
                        {
                            // use backup to restore the file
                            // the storage has already disconnected from original location
                            CloseAndReleaseStreams_Impl();
                            if ( !UseBackupToRestore_Impl( aOriginalContent, xDummyEnv ) )
                            {
                                // connect the medium to the temporary file of the storage
                                pImpl->aContent = ::ucbhelper::Content();
                                pImpl->m_aName = aBackupExc.TemporaryFileURL;
                                OSL_ENSURE( !pImpl->m_aName.isEmpty(), "The exception _must_ contain the temporary URL!" );
                            }
                        }
                    }
 
                    if (!GetError())
                        SetError(ERRCODE_IO_GENERAL);
                }
                catch ( const uno::Exception& )
                {
                    //TODO/LATER: improve error handling
                    SetError(ERRCODE_IO_GENERAL);
                }
            }
        }
    }
 
    return bResult;
}
 
 
void SfxMedium::TransactedTransferForFS_Impl( const INetURLObject& aSource,
                                                 const INetURLObject& aDest,
                                                 const Reference< css::ucb::XCommandEnvironment >& xComEnv )
{
    bool bResult = false;
    Reference< css::ucb::XCommandEnvironment > xDummyEnv;
    ::ucbhelper::Content aOriginalContent;
 
    try
    {
        aOriginalContent = ::ucbhelper::Content( aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() );
    }
    catch ( const css::ucb::CommandAbortedException& )
    {
        pImpl->m_eError = ERRCODE_ABORT;
    }
    catch ( const css::ucb::CommandFailedException& )
    {
        pImpl->m_eError = ERRCODE_ABORT;
    }
    catch (const css::ucb::ContentCreationException& ex)
    {
        pImpl->m_eError = ERRCODE_IO_GENERAL;
        if (
            (ex.eError == css::ucb::ContentCreationError_NO_CONTENT_PROVIDER    ) ||
            (ex.eError == css::ucb::ContentCreationError_CONTENT_CREATION_FAILED)
           )
        {
            pImpl->m_eError = ERRCODE_IO_NOTEXISTSPATH;
        }
    }
    catch (const css::uno::Exception&)
    {
       pImpl->m_eError = ERRCODE_IO_GENERAL;
    }
 
    if( !pImpl->m_eError || pImpl->m_eError.IsWarning() )
    {
        if ( pImpl->xStorage.is() )
            CloseStorage();
 
        CloseStreams_Impl();
 
        ::ucbhelper::Content aTempCont;
        if( ::ucbhelper::Content::create( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xDummyEnv, comphelper::getProcessComponentContext(), aTempCont ) )
        {
            bool bTransactStarted = false;
            const SfxBoolItem* pOverWrite = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_OVERWRITE, false);
            bool bOverWrite = !pOverWrite || pOverWrite->GetValue();
 
            try
            {
                OUString aSourceMainURL = aSource.GetMainURL(INetURLObject::DecodeMechanism::NONE);
                OUString aDestMainURL = aDest.GetMainURL(INetURLObject::DecodeMechanism::NONE);
 
                sal_uInt64 nAttributes = GetDefaultFileAttributes(aDestMainURL);
                if (IsFileMovable(aDestMainURL) && osl::File::move(aSourceMainURL, aDestMainURL) == osl::FileBase::E_None)
                {
                    if (nAttributes)
                        // Adjust attributes, source might be created with
                        // the osl_File_OpenFlag_Private flag.
                        osl::File::setAttributes(aDestMainURL, nAttributes);
                    bResult = true;
                }
                else
                {
                    if (bOverWrite && ::utl::UCBContentHelper::IsDocument(aDestMainURL))
                    {
                        if( pImpl->m_aBackupURL.isEmpty() )
                            DoInternalBackup_Impl( aOriginalContent );
 
                        if( !pImpl->m_aBackupURL.isEmpty() )
                        {
                            Reference< XInputStream > aTempInput = aTempCont.openStream();
                            bTransactStarted = true;
                            aOriginalContent.setPropertyValue( "Size", uno::makeAny( sal_Int64(0) ) );
                            aOriginalContent.writeStream( aTempInput, bOverWrite );
                            bResult = true;
                        }
                        else
                        {
                            pImpl->m_eError = ERRCODE_SFX_CANTCREATEBACKUP;
                        }
                    }
                    else
                    {
                        Reference< XInputStream > aTempInput = aTempCont.openStream();
                        aOriginalContent.writeStream( aTempInput, bOverWrite );
                        bResult = true;
                    }
                }
            }
            catch ( const css::ucb::CommandAbortedException& )
            {
                pImpl->m_eError = ERRCODE_ABORT;
            }
            catch ( const css::ucb::CommandFailedException& )
            {
                pImpl->m_eError = ERRCODE_ABORT;
            }
            catch ( const css::ucb::InteractiveIOException& r )
            {
                if ( r.Code == IOErrorCode_ACCESS_DENIED )
                    pImpl->m_eError = ERRCODE_IO_ACCESSDENIED;
                else if ( r.Code == IOErrorCode_NOT_EXISTING )
                    pImpl->m_eError = ERRCODE_IO_NOTEXISTS;
                else if ( r.Code == IOErrorCode_CANT_READ )
                    pImpl->m_eError = ERRCODE_IO_CANTREAD;
                else
                    pImpl->m_eError = ERRCODE_IO_GENERAL;
            }
            catch ( const css::uno::Exception& )
            {
                pImpl->m_eError = ERRCODE_IO_GENERAL;
            }
 
               if ( bResult )
               {
                if ( pImpl->pTempFile )
                {
                    pImpl->pTempFile->EnableKillingFile();
                    pImpl->pTempFile.reset();
                }
               }
            else if ( bTransactStarted )
            {
                UseBackupToRestore_Impl( aOriginalContent, xDummyEnv );
            }
        }
        else
            pImpl->m_eError = ERRCODE_IO_CANTREAD;
    }
}
 
 
bool SfxMedium::TryDirectTransfer( const OUString& aURL, SfxItemSet const & aTargetSet )
{
    if ( GetError() )
        return false;
 
    // if the document had no password it should be stored without password
    // if the document had password it should be stored with the same password
    // otherwise the stream copying can not be done
    const SfxStringItem* pNewPassItem = aTargetSet.GetItem<SfxStringItem>(SID_PASSWORD, false);
    const SfxStringItem* pOldPassItem = SfxItemSet::GetItem<SfxStringItem>(GetItemSet(), SID_PASSWORD, false);
    if ( ( !pNewPassItem && !pOldPassItem )
      || ( pNewPassItem && pOldPassItem && pNewPassItem->GetValue() == pOldPassItem->GetValue() ) )
    {
        // the filter must be the same
        const SfxStringItem* pNewFilterItem = aTargetSet.GetItem<SfxStringItem>(SID_FILTER_NAME, false);
        const SfxStringItem* pOldFilterItem = SfxItemSet::GetItem<SfxStringItem>(GetItemSet(), SID_FILTER_NAME, false);
        if ( pNewFilterItem && pOldFilterItem && pNewFilterItem->GetValue() == pOldFilterItem->GetValue() )
        {
            // get the input stream and copy it
            // in case of success return true
            uno::Reference< io::XInputStream > xInStream = GetInputStream();
 
            ResetError();
            if ( xInStream.is() )
            {
                try
                {
                    uno::Reference< io::XSeekable > xSeek( xInStream, uno::UNO_QUERY );
                    sal_Int64 nPos = 0;
                    if ( xSeek.is() )
                    {
                        nPos = xSeek->getPosition();
                        xSeek->seek( 0 );
                    }
 
                    uno::Reference < css::ucb::XCommandEnvironment > xEnv;
                    ::ucbhelper::Content aTargetContent( aURL, xEnv, comphelper::getProcessComponentContext() );
 
                    InsertCommandArgument aInsertArg;
                    aInsertArg.Data = xInStream;
                    const SfxBoolItem* pOverWrite = aTargetSet.GetItem<SfxBoolItem>(SID_OVERWRITE, false);
                    if ( pOverWrite && !pOverWrite->GetValue() ) // argument says: never overwrite
                        aInsertArg.ReplaceExisting = false;
                    else
                        aInsertArg.ReplaceExisting = true; // default is overwrite existing files
 
                    Any aCmdArg;
                    aCmdArg <<= aInsertArg;
                    aTargetContent.executeCommand( "insert",
                                                    aCmdArg );
 
                    if ( xSeek.is() )
                        xSeek->seek( nPos );
 
                    return true;
                }
                catch( const uno::Exception& )
                {}
            }
        }
    }
 
    return false;
}
 
 
void SfxMedium::Transfer_Impl()
{
    // The transfer is required only in two cases: either if there is a temporary file or if there is a salvage item
    OUString aNameURL;
    if ( pImpl->pTempFile )
        aNameURL = pImpl->pTempFile->GetURL();
    else if ( !pImpl->m_aLogicName.isEmpty() && pImpl->m_bSalvageMode )
    {
        // makes sense only in case logic name is set
        if ( osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aNameURL )
             != osl::FileBase::E_None )
            SAL_WARN( "sfx.doc", "The medium name is not convertible!" );
    }
 
    if ( !aNameURL.isEmpty() && ( !pImpl->m_eError || pImpl->m_eError.IsWarning() ) )
    {
        SAL_INFO( "sfx.doc", "SfxMedium::Transfer_Impl, copying to target" );
 
        Reference < css::ucb::XCommandEnvironment > xEnv;
        Reference< XOutputStream > rOutStream;
 
        // in case an output stream is provided from outside and the URL is correct
        // commit to the stream
        if (pImpl->m_aLogicName.startsWith("private:stream"))
        {
            // TODO/LATER: support storing to SID_STREAM
            const SfxUnoAnyItem* pOutStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_OUTPUTSTREAM, false);
            if( pOutStreamItem && ( pOutStreamItem->GetValue() >>= rOutStream ) )
            {
                if ( pImpl->xStorage.is() )
                    CloseStorage();
 
                CloseStreams_Impl();
 
                INetURLObject aSource( aNameURL );
                ::ucbhelper::Content aTempCont;
                if( ::ucbhelper::Content::create( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xEnv, comphelper::getProcessComponentContext(), aTempCont ) )
                {
                    try
                    {
                        sal_Int32 nRead;
                        sal_Int32 nBufferSize = 32767;
                        Sequence < sal_Int8 > aSequence ( nBufferSize );
                        Reference< XInputStream > aTempInput = aTempCont.openStream();
 
                        do
                        {
                            nRead = aTempInput->readBytes ( aSequence, nBufferSize );
                            if ( nRead < nBufferSize )
                            {
                                Sequence < sal_Int8 > aTempBuf ( aSequence.getConstArray(), nRead );
                                rOutStream->writeBytes ( aTempBuf );
                            }
                            else
                                rOutStream->writeBytes ( aSequence );
                        }
                        while ( nRead == nBufferSize );
 
                        // remove temporary file
                        if ( pImpl->pTempFile )
                        {
                            pImpl->pTempFile->EnableKillingFile();
                            pImpl->pTempFile.reset();
                        }
                    }
                    catch( const Exception& )
                    {}
                }
            }
            else
            {
                SAL_WARN( "sfx.doc", "Illegal Output stream parameter!" );
                SetError(ERRCODE_IO_GENERAL);
            }
 
            // free the reference
            if ( pImpl->m_pSet )
                pImpl->m_pSet->ClearItem( SID_OUTPUTSTREAM );
 
            return;
        }
 
        GetContent();
        if ( !pImpl->aContent.get().is() )
        {
            pImpl->m_eError = ERRCODE_IO_NOTEXISTS;
            return;
        }
 
        INetURLObject aDest( GetURLObject() );
 
        // source is the temp file written so far
        INetURLObject aSource( aNameURL );
 
        // a special case, an interaction handler should be used for
        // authentication in case it is available
        Reference< css::ucb::XCommandEnvironment > xComEnv;
        Reference< css::task::XInteractionHandler > xInteractionHandler = GetInteractionHandler();
        if (xInteractionHandler.is())
            xComEnv = new ::ucbhelper::CommandEnvironment( xInteractionHandler,
                                                      Reference< css::ucb::XProgressHandler >() );
 
        OUString aDestURL( aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
 
        if ( comphelper::isFileUrl( aDestURL ) || !aDest.removeSegment() )
        {
            TransactedTransferForFS_Impl( aSource, aDest, xComEnv );
 
            if (!pImpl->m_bDisableFileSync)
            {
                // Hideous - no clean way to do this, so we re-open the file just to fsync it
                osl::File aFile( aDestURL );
                if ( aFile.open( osl_File_OpenFlag_Write ) == osl::FileBase::E_None )
                {
                    aFile.sync();
                    SAL_INFO( "sfx.doc", "fsync'd saved file '" << aDestURL << "'" );
                    aFile.close();
                }
            }
        }
        else
        {
            // create content for the parent folder and call transfer on that content with the source content
            // and the destination file name as parameters
            ::ucbhelper::Content aSourceContent;
            ::ucbhelper::Content aTransferContent;
 
            ::ucbhelper::Content aDestContent;
            (void)::ucbhelper::Content::create( aDestURL, xComEnv, comphelper::getProcessComponentContext(), aDestContent );
            // For checkin, we need the object URL, not the parent folder:
            if ( !IsInCheckIn( ) )
            {
                // Get the parent URL from the XChild if possible: why would the URL necessarily have
                // a hierarchical path? It's not always the case for CMIS.
                Reference< css::container::XChild> xChild( aDestContent.get(), uno::UNO_QUERY );
                OUString sParentUrl;
                if ( xChild.is( ) )
                {
                    Reference< css::ucb::XContent > xParent( xChild->getParent( ), uno::UNO_QUERY );
                    if ( xParent.is( ) )
                    {
                        sParentUrl = xParent->getIdentifier( )->getContentIdentifier();
                    }
                }
 
                if ( sParentUrl.isEmpty() )
                    aDestURL = aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE );
                        // adjust to above aDest.removeSegment()
                else
                    aDestURL = sParentUrl;
            }
 
            // LongName wasn't defined anywhere, only used here... get the Title instead
            // as it's less probably empty
            OUString aFileName;
            Any aAny = aDestContent.getPropertyValue("Title");
            aAny >>= aFileName;
            aAny = aDestContent.getPropertyValue( "ObjectId" );
            OUString sObjectId;
            aAny >>= sObjectId;
            if ( aFileName.isEmpty() )
                aFileName = GetURLObject().getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
 
            try
            {
                aTransferContent = ::ucbhelper::Content( aDestURL, xComEnv, comphelper::getProcessComponentContext() );
            }
            catch (const css::ucb::ContentCreationException& ex)
            {
                pImpl->m_eError = ERRCODE_IO_GENERAL;
                if (
                    (ex.eError == css::ucb::ContentCreationError_NO_CONTENT_PROVIDER    ) ||
                    (ex.eError == css::ucb::ContentCreationError_CONTENT_CREATION_FAILED)
                   )
                {
                    pImpl->m_eError = ERRCODE_IO_NOTEXISTSPATH;
                }
            }
            catch (const css::uno::Exception&)
            {
                pImpl->m_eError = ERRCODE_IO_GENERAL;
            }
 
            if ( !pImpl->m_eError || pImpl->m_eError.IsWarning() )
            {
                // free resources, otherwise the transfer may fail
                if ( pImpl->xStorage.is() )
                    CloseStorage();
 
                CloseStreams_Impl();
 
                (void)::ucbhelper::Content::create( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xEnv, comphelper::getProcessComponentContext(), aSourceContent );
 
                // check for external parameters that may customize the handling of NameClash situations
                const SfxBoolItem* pOverWrite = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_OVERWRITE, false);
                sal_Int32 nNameClash;
                if ( pOverWrite && !pOverWrite->GetValue() )
                    // argument says: never overwrite
                    nNameClash = NameClash::ERROR;
                else
                    // default is overwrite existing files
                    nNameClash = NameClash::OVERWRITE;
 
                try
                {
                    OUString aMimeType = pImpl->getFilterMimeType();
                    ::ucbhelper::InsertOperation eOperation = ::ucbhelper::InsertOperation::Copy;
                    bool bMajor = false;
                    OUString sComment;
                    if ( IsInCheckIn( ) )
                    {
                        eOperation = ::ucbhelper::InsertOperation::Checkin;
                        const SfxBoolItem* pMajor = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_DOCINFO_MAJOR, false);
                        bMajor = pMajor && pMajor->GetValue( );
                        const SfxStringItem* pComments = SfxItemSet::GetItem<SfxStringItem>(GetItemSet(), SID_DOCINFO_COMMENTS, false);
                        if ( pComments )
                            sComment = pComments->GetValue( );
                    }
                    OUString sResultURL;
                    aTransferContent.transferContent(
                        aSourceContent, eOperation,
                        aFileName, nNameClash, aMimeType, bMajor, sComment,
                        &sResultURL, sObjectId );
 
                    if ( !sResultURL.isEmpty( ) )  // Likely to happen only for checkin
                        SwitchDocumentToFile( sResultURL );
                    try
                    {
                        if ( GetURLObject().isAnyKnownWebDAVScheme() &&
                             eOperation == ::ucbhelper::InsertOperation::Copy )
                        {
                            // tdf#95272 try to re-issue a lock command when a new file is created.
                            // This may be needed because some WebDAV servers fail to implement the
                            // 'LOCK on unallocated reference', see issue comment:
                            // <https://bugs.documentfoundation.org/show_bug.cgi?id=95792#c8>
                            // and specification at:
                            // <http://tools.ietf.org/html/rfc4918#section-7.3>
                            // If the WebDAV resource is already locked by this LO instance, nothing will
                            // happen, e.g. the LOCK method will not be sent to the server.
                            ::ucbhelper::Content aLockContent = ::ucbhelper::Content( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() );
                            aLockContent.lock();
                        }
                    }
                    catch ( css::uno::Exception & e )
                    {
                        SAL_WARN( "sfx.doc", "LOCK not working while re-issuing it. Exception message: " << e );
                    }
                }
                catch ( const css::ucb::CommandAbortedException& )
                {
                    pImpl->m_eError = ERRCODE_ABORT;
                }
                catch ( const css::ucb::CommandFailedException& )
                {
                    pImpl->m_eError = ERRCODE_ABORT;
                }
                catch ( const css::ucb::InteractiveIOException& r )
                {
                    if ( r.Code == IOErrorCode_ACCESS_DENIED )
                        pImpl->m_eError = ERRCODE_IO_ACCESSDENIED;
                    else if ( r.Code == IOErrorCode_NOT_EXISTING )
                        pImpl->m_eError = ERRCODE_IO_NOTEXISTS;
                    else if ( r.Code == IOErrorCode_CANT_READ )
                        pImpl->m_eError = ERRCODE_IO_CANTREAD;
                    else
                        pImpl->m_eError = ERRCODE_IO_GENERAL;
                }
                catch ( const css::uno::Exception& )
                {
                    pImpl->m_eError = ERRCODE_IO_GENERAL;
                }
 
                // do not switch from temporary file in case of nonfile protocol
            }
        }
 
        if ( ( !pImpl->m_eError || pImpl->m_eError.IsWarning() ) && !pImpl->pTempFile )
        {
            // without a TempFile the physical and logical name should be the same after successful transfer
            if (osl::FileBase::getSystemPathFromFileURL(
                  GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), pImpl->m_aName )
                != osl::FileBase::E_None)
            {
                pImpl->m_aName.clear();
            }
            pImpl->m_bSalvageMode = false;
        }
    }
}
 
 
void SfxMedium::DoInternalBackup_Impl( const ::ucbhelper::Content& aOriginalContent,
                                       const OUString& aPrefix,
                                       const OUString& aExtension,
                                       const OUString& aDestDir )
{
    if ( !pImpl->m_aBackupURL.isEmpty() )
        return; // the backup was done already
 
    ::utl::TempFile aTransactTemp( aPrefix, true, &aExtension, &aDestDir );
 
    INetURLObject aBackObj( aTransactTemp.GetURL() );
    OUString aBackupName = aBackObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
 
    Reference < css::ucb::XCommandEnvironment > xDummyEnv;
    ::ucbhelper::Content aBackupCont;
    if( ::ucbhelper::Content::create( aDestDir, xDummyEnv, comphelper::getProcessComponentContext(), aBackupCont ) )
    {
        try
        {
            OUString sMimeType = pImpl->getFilterMimeType();
            aBackupCont.transferContent( aOriginalContent,
                                            ::ucbhelper::InsertOperation::Copy,
                                            aBackupName,
                                            NameClash::OVERWRITE,
                                            sMimeType );
            pImpl->m_aBackupURL = aBackObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
            pImpl->m_bRemoveBackup = true;
        }
        catch( const Exception& )
        {}
    }
 
    if ( pImpl->m_aBackupURL.isEmpty() )
        aTransactTemp.EnableKillingFile();
}
 
 
void SfxMedium::DoInternalBackup_Impl( const ::ucbhelper::Content& aOriginalContent )
{
    if ( !pImpl->m_aBackupURL.isEmpty() )
        return; // the backup was done already
 
    OUString aFileName =  GetURLObject().getName( INetURLObject::LAST_SEGMENT,
                                                        true,
                                                        INetURLObject::DecodeMechanism::NONE );
 
    sal_Int32 nPrefixLen = aFileName.lastIndexOf( '.' );
    OUString aPrefix = ( nPrefixLen == -1 ) ? aFileName : aFileName.copy( 0, nPrefixLen );
    OUString aExtension = ( nPrefixLen == -1 ) ? OUString() : aFileName.copy( nPrefixLen );
    OUString aBakDir = SvtPathOptions().GetBackupPath();
 
    // create content for the parent folder ( = backup folder )
    ::ucbhelper::Content  aContent;
    Reference < css::ucb::XCommandEnvironment > xEnv;
    if( ::utl::UCBContentHelper::ensureFolder(comphelper::getProcessComponentContext(), xEnv, aBakDir, aContent) )
        DoInternalBackup_Impl( aOriginalContent, aPrefix, aExtension, aBakDir );
 
    if ( pImpl->m_aBackupURL.isEmpty() )
    {
        // the copiing to the backup catalog failed ( for example because
        // of using an encrypted partition as target catalog )
        // since the user did not specify to make backup explicitly
        // office should try to make backup in another place,
        // target catalog does not look bad for this case ( and looks
        // to be the only way for encrypted partitions )
 
        INetURLObject aDest = GetURLObject();
        if ( aDest.removeSegment() )
            DoInternalBackup_Impl( aOriginalContent, aPrefix, aExtension, aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
    }
}
 
 
void SfxMedium::DoBackup_Impl()
{
    // source file name is the logical name of this medium
    INetURLObject aSource( GetURLObject() );
 
    // there is nothing to backup in case source file does not exist
    if ( !::utl::UCBContentHelper::IsDocument( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) )
        return;
 
    bool        bSuccess = false;
 
    // get path for backups
    OUString aBakDir = SvtPathOptions().GetBackupPath();
    if( !aBakDir.isEmpty() )
    {
        // create content for the parent folder ( = backup folder )
        ::ucbhelper::Content  aContent;
        Reference < css::ucb::XCommandEnvironment > xEnv;
        if( ::utl::UCBContentHelper::ensureFolder(comphelper::getProcessComponentContext(), xEnv, aBakDir, aContent) )
        {
            // save as ".bak" file
            INetURLObject aDest( aBakDir );
            aDest.insertName( aSource.getName() );
            aDest.setExtension( "bak" );
            OUString aFileName = aDest.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
 
            // create a content for the source file
            ::ucbhelper::Content aSourceContent;
            if ( ::ucbhelper::Content::create( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xEnv, comphelper::getProcessComponentContext(), aSourceContent ) )
            {
                try
                {
                    // do the transfer ( copy source file to backup dir )
                    OUString sMimeType = pImpl->getFilterMimeType();
                    aContent.transferContent( aSourceContent,
                                                        ::ucbhelper::InsertOperation::Copy,
                                                        aFileName,
                                                        NameClash::OVERWRITE,
                                                        sMimeType );
                    pImpl->m_aBackupURL = aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE );
                    pImpl->m_bRemoveBackup = false;
                    bSuccess = true;
                }
                catch ( const css::uno::Exception& )
                {
                }
            }
        }
    }
 
    if ( !bSuccess )
    {
        pImpl->m_eError = ERRCODE_SFX_CANTCREATEBACKUP;
    }
}
 
 
void SfxMedium::ClearBackup_Impl()
{
    if( pImpl->m_bRemoveBackup )
    {
        // currently a document is always stored in a new medium,
        // thus if a backup can not be removed the backup URL should not be cleaned
        if ( !pImpl->m_aBackupURL.isEmpty() )
        {
            if ( ::utl::UCBContentHelper::Kill( pImpl->m_aBackupURL ) )
            {
                pImpl->m_bRemoveBackup = false;
                pImpl->m_aBackupURL.clear();
            }
            else
            {
 
                SAL_WARN( "sfx.doc", "Couldn't remove backup file!");
            }
        }
    }
    else
        pImpl->m_aBackupURL.clear();
}
 
 
void SfxMedium::GetLockingStream_Impl()
{
    if ( GetURLObject().GetProtocol() == INetProtocol::File
      && !pImpl->m_xLockingStream.is() )
    {
        const SfxUnoAnyItem* pWriteStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_STREAM, false);
        if ( pWriteStreamItem )
            pWriteStreamItem->GetValue() >>= pImpl->m_xLockingStream;
 
        if ( !pImpl->m_xLockingStream.is() )
        {
            // open the original document
            uno::Sequence< beans::PropertyValue > xProps;
            TransformItems( SID_OPENDOC, *GetItemSet(), xProps );
            utl::MediaDescriptor aMedium( xProps );
 
            aMedium.addInputStreamOwnLock();
 
            uno::Reference< io::XInputStream > xInputStream;
            aMedium[utl::MediaDescriptor::PROP_STREAM()] >>= pImpl->m_xLockingStream;
            aMedium[utl::MediaDescriptor::PROP_INPUTSTREAM()] >>= xInputStream;
 
            if ( !pImpl->pTempFile && pImpl->m_aName.isEmpty() )
            {
                // the medium is still based on the original file, it makes sense to initialize the streams
                if ( pImpl->m_xLockingStream.is() )
                    pImpl->xStream = pImpl->m_xLockingStream;
 
                if ( xInputStream.is() )
                    pImpl->xInputStream = xInputStream;
 
                if ( !pImpl->xInputStream.is() && pImpl->xStream.is() )
                    pImpl->xInputStream = pImpl->xStream->getInputStream();
            }
        }
    }
}
 
 
void SfxMedium::GetMedium_Impl()
{
    if ( !pImpl->m_pInStream
        || (pImpl->bIsTemp && !pImpl->xInputStream.is() && !pImpl->m_xInputStreamToLoadFrom.is() && !pImpl->xStream.is() && !pImpl->m_xLockingStream.is() ) )
    {
        pImpl->bDownloadDone = false;
        Reference< css::task::XInteractionHandler > xInteractionHandler = GetInteractionHandler();
 
        //TODO/MBA: need support for SID_STREAM
        const SfxUnoAnyItem* pWriteStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_STREAM, false);
        const SfxUnoAnyItem* pInStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_INPUTSTREAM, false);
        if ( pWriteStreamItem )
        {
            pWriteStreamItem->GetValue() >>= pImpl->xStream;
 
            if ( pInStreamItem )
                pInStreamItem->GetValue() >>= pImpl->xInputStream;
 
            if ( !pImpl->xInputStream.is() && pImpl->xStream.is() )
                pImpl->xInputStream = pImpl->xStream->getInputStream();
        }
        else if ( pInStreamItem )
        {
            pInStreamItem->GetValue() >>= pImpl->xInputStream;
        }
        else
        {
            uno::Sequence < beans::PropertyValue > xProps;
            OUString aFileName;
            if (!pImpl->m_aName.isEmpty())
            {
                if ( osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aFileName )
                     != osl::FileBase::E_None )
                {
                    SAL_WARN( "sfx.doc", "Physical name not convertible!");
                }
            }
            else
                aFileName = GetName();
 
            // in case the temporary file exists the streams should be initialized from it,
            // but the original MediaDescriptor should not be changed
            bool bFromTempFile = ( pImpl->pTempFile != nullptr );
 
            if ( !bFromTempFile )
            {
                GetItemSet()->Put( SfxStringItem( SID_FILE_NAME, aFileName ) );
                if( !(pImpl->m_nStorOpenMode & StreamMode::WRITE) )
                    GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
                if (xInteractionHandler.is())
                    GetItemSet()->Put( SfxUnoAnyItem( SID_INTERACTIONHANDLER, makeAny(xInteractionHandler) ) );
            }
 
            if ( pImpl->m_xInputStreamToLoadFrom.is() )
            {
                pImpl->xInputStream = pImpl->m_xInputStreamToLoadFrom;
                if (pImpl->m_bInputStreamIsReadOnly)
                    GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
            }
            else
            {
                TransformItems( SID_OPENDOC, *GetItemSet(), xProps );
                utl::MediaDescriptor aMedium( xProps );
 
                if ( pImpl->m_xLockingStream.is() && !bFromTempFile )
                {
                    // the medium is not based on the temporary file, so the original stream can be used
                    pImpl->xStream = pImpl->m_xLockingStream;
                }
                else
                {
                    if ( bFromTempFile )
                    {
                        aMedium[utl::MediaDescriptor::PROP_URL()] <<= aFileName;
                        aMedium.erase( utl::MediaDescriptor::PROP_READONLY() );
                        aMedium.addInputStream();
                    }
                    else if ( GetURLObject().GetProtocol() == INetProtocol::File )
                    {
                        // use the special locking approach only for file URLs
                        aMedium.addInputStreamOwnLock();
                    }
                    else
                    {
                        // add a check for protocol, if it's http or https or provide webdav then add
                        // the interaction handler to be used by the authentication dialog
                        if ( GetURLObject().isAnyKnownWebDAVScheme() )
                        {
                            aMedium[utl::MediaDescriptor::PROP_AUTHENTICATIONHANDLER()] <<= GetInteractionHandler( true );
                        }
                        aMedium.addInputStream();
                    }
                    // the ReadOnly property set in aMedium is ignored
                    // the check is done in LockOrigFileOnDemand() for file and non-file URLs
 
                    //TODO/MBA: what happens if property is not there?!
                    aMedium[utl::MediaDescriptor::PROP_STREAM()] >>= pImpl->xStream;
                    aMedium[utl::MediaDescriptor::PROP_INPUTSTREAM()] >>= pImpl->xInputStream;
                }
 
                GetContent();
                if ( !pImpl->xInputStream.is() && pImpl->xStream.is() )
                    pImpl->xInputStream = pImpl->xStream->getInputStream();
            }
 
            if ( !bFromTempFile )
            {
                //TODO/MBA: need support for SID_STREAM
                if ( pImpl->xStream.is() )
                    GetItemSet()->Put( SfxUnoAnyItem( SID_STREAM, makeAny( pImpl->xStream ) ) );
 
                GetItemSet()->Put( SfxUnoAnyItem( SID_INPUTSTREAM, makeAny( pImpl->xInputStream ) ) );
            }
        }
 
        //TODO/MBA: ErrorHandling - how to transport error from MediaDescriptor
        if ( !GetError() && !pImpl->xStream.is() && !pImpl->xInputStream.is() )
            SetError(ERRCODE_IO_ACCESSDENIED);
 
        if ( !GetError() && !pImpl->m_pInStream )
        {
            if ( pImpl->xStream.is() )
                pImpl->m_pInStream = utl::UcbStreamHelper::CreateStream( pImpl->xStream );
            else if ( pImpl->xInputStream.is() )
                pImpl->m_pInStream = utl::UcbStreamHelper::CreateStream( pImpl->xInputStream );
        }
 
        pImpl->bDownloadDone = true;
        pImpl->aDoneLink.ClearPendingCall();
        ErrCode nError = GetError();
        pImpl->aDoneLink.Call( reinterpret_cast<void*>(sal_uInt32(nError)) );
    }
}
 
bool SfxMedium::IsRemote() const
{
    return pImpl->m_bRemote;
}
 
void SfxMedium::SetUpdatePickList(bool bVal)
{
    pImpl->bUpdatePickList = bVal;
}
 
bool SfxMedium::IsUpdatePickList() const
{
    return pImpl->bUpdatePickList;
}
 
void SfxMedium::SetLongName(const OUString &rName)
{
    pImpl->m_aLongName = rName;
}
 
const OUString& SfxMedium::GetLongName() const
{
    return pImpl->m_aLongName;
}
 
void SfxMedium::SetDoneLink( const Link<void*,void>& rLink )
{
    pImpl->aDoneLink = rLink;
}
 
void SfxMedium::Download( const Link<void*,void>& aLink )
{
    SetDoneLink( aLink );
    GetInStream();
    if ( pImpl->m_pInStream && !aLink.IsSet() )
    {
        while( !pImpl->bDownloadDone )
            Application::Yield();
    }
}
 
 
void SfxMedium::Init_Impl()
/*  [Description]
    Includes a valid:: sun:: com:: star:: util:: URL (If a file name was
    previously in there) in the logical name and if available sets the
    physical name as the file name.
 */
 
{
    Reference< XOutputStream > rOutStream;
 
    // TODO/LATER: handle lifetime of storages
    pImpl->bDisposeStorage = false;
 
    const SfxStringItem* pSalvageItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_DOC_SALVAGE, false);
    if ( pSalvageItem && pSalvageItem->GetValue().isEmpty() )
    {
        pSalvageItem = nullptr;
        pImpl->m_pSet->ClearItem( SID_DOC_SALVAGE );
    }
 
    if (!pImpl->m_aLogicName.isEmpty())
    {
        INetURLObject aUrl( pImpl->m_aLogicName );
        INetProtocol eProt = aUrl.GetProtocol();
        if ( eProt == INetProtocol::NotValid )
        {
            SAL_WARN( "sfx.doc", "URL <" << pImpl->m_aLogicName << "> with unknown protocol" );
        }
        else
        {
            if ( aUrl.HasMark() )
            {
                pImpl->m_aLogicName = aUrl.GetURLNoMark( INetURLObject::DecodeMechanism::NONE );
                GetItemSet()->Put( SfxStringItem( SID_JUMPMARK, aUrl.GetMark() ) );
            }
 
            // try to convert the URL into a physical name - but never change a physical name
            // physical name may be set if the logical name is changed after construction
            if ( pImpl->m_aName.isEmpty() )
                osl::FileBase::getSystemPathFromFileURL( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), pImpl->m_aName );
            else
            {
                DBG_ASSERT( pSalvageItem, "Suspicious change of logical name!" );
            }
        }
    }
 
    if ( pSalvageItem && !pSalvageItem->GetValue().isEmpty() )
    {
        pImpl->m_aLogicName = pSalvageItem->GetValue();
        pImpl->m_pURLObj.reset();
        pImpl->m_bSalvageMode = true;
    }
 
    // in case output stream is by mistake here
    // clear the reference
    const SfxUnoAnyItem* pOutStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_OUTPUTSTREAM, false);
    if( pOutStreamItem
     && ( !( pOutStreamItem->GetValue() >>= rOutStream )
          || !pImpl->m_aLogicName.startsWith("private:stream")) )
    {
        pImpl->m_pSet->ClearItem( SID_OUTPUTSTREAM );
        SAL_WARN( "sfx.doc", "Unexpected Output stream parameter!" );
    }
 
    if (!pImpl->m_aLogicName.isEmpty())
    {
        // if the logic name is set it should be set in MediaDescriptor as well
        const SfxStringItem* pFileNameItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_FILE_NAME, false);
        if ( !pFileNameItem )
        {
            // let the ItemSet be created if necessary
            GetItemSet()->Put(
                SfxStringItem(
                    SID_FILE_NAME, INetURLObject( pImpl->m_aLogicName ).GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) );
        }
    }
 
    SetIsRemote_Impl();
 
    osl::DirectoryItem item;
    if (osl::DirectoryItem::get(GetName(), item) == osl::FileBase::E_None) {
        osl::FileStatus stat(osl_FileStatus_Mask_Attributes);
        if (item.getFileStatus(stat) == osl::FileBase::E_None
            && stat.isValid(osl_FileStatus_Mask_Attributes))
        {
            if ((stat.getAttributes() & osl_File_Attribute_ReadOnly) != 0) {
                pImpl->m_bOriginallyReadOnly = true;
            }
        }
    }
}
 
 
SfxMedium::SfxMedium() : pImpl(new SfxMedium_Impl)
{
    Init_Impl();
}
 
 
void SfxMedium::UseInteractionHandler( bool bUse )
{
    pImpl->bAllowDefaultIntHdl = bUse;
}
 
 
css::uno::Reference< css::task::XInteractionHandler >
SfxMedium::GetInteractionHandler( bool bGetAlways )
{
    // if interaction isn't allowed explicitly ... return empty reference!
    if ( !bGetAlways && !pImpl->bUseInteractionHandler )
        return css::uno::Reference< css::task::XInteractionHandler >();
 
    // search a possible existing handler inside cached item set
    if ( pImpl->m_pSet )
    {
        css::uno::Reference< css::task::XInteractionHandler > xHandler;
        const SfxUnoAnyItem* pHandler = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_INTERACTIONHANDLER, false);
        if ( pHandler && (pHandler->GetValue() >>= xHandler) && xHandler.is() )
            return xHandler;
    }
 
    // if default interaction isn't allowed explicitly ... return empty reference!
    if ( !bGetAlways && !pImpl->bAllowDefaultIntHdl )
        return css::uno::Reference< css::task::XInteractionHandler >();
 
    // otherwise return cached default handler ... if it exist.
    if ( pImpl->xInteraction.is() )
        return pImpl->xInteraction;
 
    // create default handler and cache it!
    Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
    pImpl->xInteraction.set(
        task::InteractionHandler::createWithParent(xContext, nullptr), UNO_QUERY_THROW );
    return pImpl->xInteraction;
}
 
 
void SfxMedium::SetFilter( const std::shared_ptr<const SfxFilter>& pFilter )
{
    pImpl->m_pFilter = pFilter;
}
 
const std::shared_ptr<const SfxFilter>& SfxMedium::GetFilter() const
{
    return pImpl->m_pFilter;
}
 
sal_uInt32 SfxMedium::CreatePasswordToModifyHash( const OUString& aPasswd, bool bWriter )
{
    sal_uInt32 nHash = 0;
 
    if ( !aPasswd.isEmpty() )
    {
        if ( bWriter )
        {
            nHash = ::comphelper::DocPasswordHelper::GetWordHashAsUINT32( aPasswd );
        }
        else
        {
            rtl_TextEncoding nEncoding = osl_getThreadTextEncoding();
            nHash = ::comphelper::DocPasswordHelper::GetXLHashAsUINT16( aPasswd, nEncoding );
        }
    }
 
    return nHash;
}
 
 
void SfxMedium::Close()
{
    if ( pImpl->xStorage.is() )
    {
        CloseStorage();
    }
 
    CloseStreams_Impl();
 
    UnlockFile( false );
}
 
void SfxMedium::CloseAndRelease()
{
    if ( pImpl->xStorage.is() )
    {
        CloseStorage();
    }
 
    CloseAndReleaseStreams_Impl();
 
    UnlockFile( true );
}
 
void SfxMedium::DisableUnlockWebDAV( bool bDisableUnlockWebDAV )
{
    pImpl->m_bDisableUnlockWebDAV = bDisableUnlockWebDAV;
}
 
void SfxMedium::DisableFileSync(bool bDisableFileSync)
{
    pImpl->m_bDisableFileSync = bDisableFileSync;
}
 
void SfxMedium::UnlockFile( bool bReleaseLockStream )
{
#if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT
    (void) bReleaseLockStream;
#else
    // check if webdav
    if ( GetURLObject().isAnyKnownWebDAVScheme() )
    {
        if ( pImpl->m_bLocked )
        {
            // an interaction handler should be used for authentication, if needed
            try {
                uno::Reference< css::task::XInteractionHandler > xHandler = GetInteractionHandler( true );
                uno::Reference< css::ucb::XCommandEnvironment > xComEnv = new ::ucbhelper::CommandEnvironment( xHandler,
                                                               Reference< css::ucb::XProgressHandler >() );
                ucbhelper::Content aContentToUnlock( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext());
                pImpl->m_bLocked = false;
                //check if WebDAV unlock was explicitly disabled
                if ( !pImpl->m_bDisableUnlockWebDAV )
                    aContentToUnlock.unlock();
            }
            catch ( uno::Exception& )
            {
                SAL_WARN( "sfx.doc", "Locking exception: WebDAV while trying to lock the file" );
            }
        }
        return;
    }
 
    if ( pImpl->m_xLockingStream.is() )
    {
        if ( bReleaseLockStream )
        {
            try
            {
                uno::Reference< io::XInputStream > xInStream = pImpl->m_xLockingStream->getInputStream();
                uno::Reference< io::XOutputStream > xOutStream = pImpl->m_xLockingStream->getOutputStream();
                if ( xInStream.is() )
                    xInStream->closeInput();
                if ( xOutStream.is() )
                    xOutStream->closeOutput();
            }
            catch( const uno::Exception& )
            {}
        }
 
        pImpl->m_xLockingStream.clear();
    }
 
    if ( pImpl->m_bLocked )
    {
        ::svt::DocumentLockFile aLockFile( pImpl->m_aLogicName );
 
        try
        {
            pImpl->m_bLocked = false;
            // TODO/LATER: A warning could be shown in case the file is not the own one
            aLockFile.RemoveFile();
        }
        catch( const io::WrongFormatException& )
        {
            try
            {
                // erase the empty or corrupt file
                aLockFile.RemoveFileDirectly();
            }
            catch( const uno::Exception& )
            {}
        }
        catch( const uno::Exception& )
        {}
    }
#endif
}
 
void SfxMedium::CloseAndReleaseStreams_Impl()
{
    CloseZipStorage_Impl();
 
    uno::Reference< io::XInputStream > xInToClose = pImpl->xInputStream;
    uno::Reference< io::XOutputStream > xOutToClose;
    if ( pImpl->xStream.is() )
    {
        xOutToClose = pImpl->xStream->getOutputStream();
 
        // if the locking stream is closed here the related member should be cleaned
        if ( pImpl->xStream == pImpl->m_xLockingStream )
            pImpl->m_xLockingStream.clear();
    }
 
    // The probably exsisting SvStream wrappers should be closed first
    CloseStreams_Impl();
 
    // in case of salvage mode the storage is based on the streams
    if ( !pImpl->m_bSalvageMode )
    {
        try
        {
            if ( xInToClose.is() )
                xInToClose->closeInput();
            if ( xOutToClose.is() )
                xOutToClose->closeOutput();
        }
        catch ( const uno::Exception& )
        {
        }
    }
}
 
 
void SfxMedium::CloseStreams_Impl()
{
    CloseInStream_Impl();
    CloseOutStream_Impl();
 
    if ( pImpl->m_pSet )
        pImpl->m_pSet->ClearItem( SID_CONTENT );
 
    pImpl->aContent = ::ucbhelper::Content();
}
 
 
void SfxMedium::SetIsRemote_Impl()
{
    INetURLObject aObj( GetName() );
    switch( aObj.GetProtocol() )
    {
        case INetProtocol::Ftp:
        case INetProtocol::Http:
        case INetProtocol::Https:
            pImpl->m_bRemote = true;
        break;
        default:
            pImpl->m_bRemote = GetName().startsWith("private:msgid");
            break;
    }
 
    // As files that are written to the remote transmission must also be able
    // to be read.
    if (pImpl->m_bRemote)
        pImpl->m_nStorOpenMode |= StreamMode::READ;
}
 
 
void SfxMedium::SetName( const OUString& aNameP, bool bSetOrigURL )
{
    if (pImpl->aOrigURL.isEmpty())
        pImpl->aOrigURL = pImpl->m_aLogicName;
    if( bSetOrigURL )
        pImpl->aOrigURL = aNameP;
    pImpl->m_aLogicName = aNameP;
    pImpl->m_pURLObj.reset();
    pImpl->aContent = ::ucbhelper::Content();
    Init_Impl();
}
 
 
const OUString& SfxMedium::GetOrigURL() const
{
    return pImpl->aOrigURL.isEmpty() ? pImpl->m_aLogicName : pImpl->aOrigURL;
}
 
 
void SfxMedium::SetPhysicalName_Impl( const OUString& rNameP )
{
    if ( rNameP != pImpl->m_aName )
    {
        pImpl->pTempFile.reset();
 
        if ( !pImpl->m_aName.isEmpty() || !rNameP.isEmpty() )
            pImpl->aContent = ::ucbhelper::Content();
 
        pImpl->m_aName = rNameP;
        pImpl->m_bTriedStorage = false;
        pImpl->bIsStorage = false;
    }
}
 
 
void SfxMedium::ReOpen()
{
    bool bUseInteractionHandler = pImpl->bUseInteractionHandler;
    pImpl->bUseInteractionHandler = false;
    GetMedium_Impl();
    pImpl->bUseInteractionHandler = bUseInteractionHandler;
}
 
 
void SfxMedium::CompleteReOpen()
{
    // do not use temporary file for reopen and in case of success throw the temporary file away
    bool bUseInteractionHandler = pImpl->bUseInteractionHandler;
    pImpl->bUseInteractionHandler = false;
 
    ::utl::TempFile* pTmpFile = nullptr;
    if ( pImpl->pTempFile )
    {
        pTmpFile = pImpl->pTempFile.release();
        pImpl->m_aName.clear();
    }
 
    GetMedium_Impl();
 
    if ( GetError() )
    {
        if ( pImpl->pTempFile )
        {
            pImpl->pTempFile->EnableKillingFile();
            pImpl->pTempFile.reset();
        }
        pImpl->pTempFile.reset( pTmpFile );
        if ( pImpl->pTempFile )
            pImpl->m_aName = pImpl->pTempFile->GetFileName();
    }
    else if (pTmpFile)
    {
        pTmpFile->EnableKillingFile();
        delete pTmpFile;
 
    }
 
    pImpl->bUseInteractionHandler = bUseInteractionHandler;
}
 
SfxMedium::SfxMedium(const OUString &rName, StreamMode nOpenMode, std::shared_ptr<const SfxFilter> pFilter, std::unique_ptr<SfxItemSet> pInSet) :
    pImpl(new SfxMedium_Impl)
{
    pImpl->m_pSet = std::move( pInSet );
    pImpl->m_pFilter = std::move(pFilter);
    pImpl->m_aLogicName = rName;
    pImpl->m_nStorOpenMode = nOpenMode;
    Init_Impl();
}
 
SfxMedium::SfxMedium(const OUString &rName, const OUString &rReferer, StreamMode nOpenMode, std::shared_ptr<const SfxFilter> pFilter, std::unique_ptr<SfxItemSet> pInSet) :
    pImpl(new SfxMedium_Impl)
{
    pImpl->m_pSet = std::move(pInSet);
    SfxItemSet * s = GetItemSet();
    if (s->GetItem(SID_REFERER) == nullptr) {
        s->Put(SfxStringItem(SID_REFERER, rReferer));
    }
    pImpl->m_pFilter = std::move(pFilter);
    pImpl->m_aLogicName = rName;
    pImpl->m_nStorOpenMode = nOpenMode;
    Init_Impl();
}
 
SfxMedium::SfxMedium( const uno::Sequence<beans::PropertyValue>& aArgs ) :
    pImpl(new SfxMedium_Impl)
{
    SfxAllItemSet *pParams = new SfxAllItemSet( SfxGetpApp()->GetPool() );
    pImpl->m_pSet.reset( pParams );
    TransformParameters( SID_OPENDOC, aArgs, *pParams );
 
    OUString aFilterProvider, aFilterName;
    {
        const SfxPoolItem* pItem = nullptr;
        if (pImpl->m_pSet->HasItem(SID_FILTER_PROVIDER, &pItem))
            aFilterProvider = static_cast<const SfxStringItem*>(pItem)->GetValue();
 
        if (pImpl->m_pSet->HasItem(SID_FILTER_NAME, &pItem))
            aFilterName = static_cast<const SfxStringItem*>(pItem)->GetValue();
    }
 
    if (aFilterProvider.isEmpty())
    {
        // This is a conventional filter type.
        pImpl->m_pFilter = SfxGetpApp()->GetFilterMatcher().GetFilter4FilterName( aFilterName );
    }
    else
    {
        // This filter is from an external provider such as orcus.
        pImpl->m_pCustomFilter.reset(new SfxFilter(aFilterProvider, aFilterName));
        pImpl->m_pFilter = pImpl->m_pCustomFilter;
    }
 
    const SfxStringItem* pSalvageItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_DOC_SALVAGE, false);
    if( pSalvageItem )
    {
        // QUESTION: there is some treatment of Salvage in Init_Impl; align!
        if ( !pSalvageItem->GetValue().isEmpty() )
        {
            // if an URL is provided in SalvageItem that means that the FileName refers to a temporary file
            // that must be copied here
 
            const SfxStringItem* pFileNameItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_FILE_NAME, false);
            if (!pFileNameItem) throw uno::RuntimeException();
            OUString aNewTempFileURL = SfxMedium::CreateTempCopyWithExt( pFileNameItem->GetValue() );
            if ( !aNewTempFileURL.isEmpty() )
            {
                pImpl->m_pSet->Put( SfxStringItem( SID_FILE_NAME, aNewTempFileURL ) );
                pImpl->m_pSet->ClearItem( SID_INPUTSTREAM );
                pImpl->m_pSet->ClearItem( SID_STREAM );
                pImpl->m_pSet->ClearItem( SID_CONTENT );
            }
            else
            {
                SAL_WARN( "sfx.doc", "Can not create a new temporary file for crash recovery!" );
            }
        }
    }
 
    const SfxBoolItem* pReadOnlyItem = SfxItemSet::GetItem<SfxBoolItem>(pImpl->m_pSet.get(), SID_DOC_READONLY, false);
    if ( pReadOnlyItem && pReadOnlyItem->GetValue() )
        pImpl->m_bOriginallyLoadedReadOnly = true;
 
    const SfxStringItem* pFileNameItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_FILE_NAME, false);
    if (!pFileNameItem) throw uno::RuntimeException();
    pImpl->m_aLogicName = pFileNameItem->GetValue();
    pImpl->m_nStorOpenMode = pImpl->m_bOriginallyLoadedReadOnly
        ? SFX_STREAM_READONLY : SFX_STREAM_READWRITE;
    Init_Impl();
}
 
 
SfxMedium::SfxMedium( const uno::Reference < embed::XStorage >& rStor, const OUString& rBaseURL, const SfxItemSet* p ) :
    pImpl(new SfxMedium_Impl)
{
    OUString aType = SfxFilter::GetTypeFromStorage(rStor);
    pImpl->m_pFilter = SfxGetpApp()->GetFilterMatcher().GetFilter4EA( aType );
    DBG_ASSERT( pImpl->m_pFilter, "No Filter for storage found!" );
 
    Init_Impl();
    pImpl->xStorage = rStor;
    pImpl->bDisposeStorage = false;
 
    // always take BaseURL first, could be overwritten by ItemSet
    GetItemSet()->Put( SfxStringItem( SID_DOC_BASEURL, rBaseURL ) );
    if ( p )
        GetItemSet()->Put( *p );
}
 
 
SfxMedium::SfxMedium( const uno::Reference < embed::XStorage >& rStor, const OUString& rBaseURL, const OUString &rTypeName, const SfxItemSet* p ) :
    pImpl(new SfxMedium_Impl)
{
    pImpl->m_pFilter = SfxGetpApp()->GetFilterMatcher().GetFilter4EA( rTypeName );
    DBG_ASSERT( pImpl->m_pFilter, "No Filter for storage found!" );
 
    Init_Impl();
    pImpl->xStorage = rStor;
    pImpl->bDisposeStorage = false;
 
    // always take BaseURL first, could be overwritten by ItemSet
    GetItemSet()->Put( SfxStringItem( SID_DOC_BASEURL, rBaseURL ) );
    if ( p )
        GetItemSet()->Put( *p );
}
 
 
SfxMedium::~SfxMedium()
{
    // if there is a requirement to clean the backup this is the last possibility to do it
    ClearBackup_Impl();
 
    Close();
 
    if( pImpl->bIsTemp && !pImpl->m_aName.isEmpty() )
    {
        OUString aTemp;
        if ( osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aTemp )
             != osl::FileBase::E_None )
        {
            SAL_WARN( "sfx.doc", "Physical name not convertible!");
        }
 
        if ( !::utl::UCBContentHelper::Kill( aTemp ) )
        {
            SAL_WARN( "sfx.doc", "Couldn't remove temporary file!");
        }
    }
}
 
const OUString& SfxMedium::GetName() const
{
    return pImpl->m_aLogicName;
}
 
const INetURLObject& SfxMedium::GetURLObject() const
{
    if (!pImpl->m_pURLObj)
    {
        pImpl->m_pURLObj.reset( new INetURLObject( pImpl->m_aLogicName ) );
        pImpl->m_pURLObj->SetMark("");
    }
 
    return *pImpl->m_pURLObj;
}
 
void SfxMedium::SetExpired_Impl( const DateTime& rDateTime )
{
    pImpl->aExpireTime = rDateTime;
}
 
 
bool SfxMedium::IsExpired() const
{
    return pImpl->aExpireTime.IsValidAndGregorian() && pImpl->aExpireTime < DateTime( DateTime::SYSTEM );
}
 
 
SfxFrame* SfxMedium::GetLoadTargetFrame() const
{
    return pImpl->wLoadTargetFrame;
}
 
void SfxMedium::setStreamToLoadFrom(const css::uno::Reference<css::io::XInputStream>& xInputStream, bool bIsReadOnly )
{
    pImpl->m_xInputStreamToLoadFrom = xInputStream;
    pImpl->m_bInputStreamIsReadOnly = bIsReadOnly;
}
 
void SfxMedium::SetLoadTargetFrame(SfxFrame* pFrame )
{
    pImpl->wLoadTargetFrame = pFrame;
}
 
 
void SfxMedium::SetStorage_Impl( const uno::Reference < embed::XStorage >& rStor )
{
    pImpl->xStorage = rStor;
}
 
 
SfxItemSet* SfxMedium::GetItemSet() const
{
    // this method *must* return an ItemSet, returning NULL can cause crashes
    if (!pImpl->m_pSet)
        pImpl->m_pSet.reset( new SfxAllItemSet( SfxGetpApp()->GetPool() ) );
    return pImpl->m_pSet.get();
}
 
 
SvKeyValueIterator* SfxMedium::GetHeaderAttributes_Impl()
{
    if( !pImpl->xAttributes.is() )
    {
        pImpl->xAttributes = SvKeyValueIteratorRef( new SvKeyValueIterator );
 
        if ( GetContent().is() )
        {
            try
            {
                Any aAny = pImpl->aContent.getPropertyValue("MediaType");
                OUString aContentType;
                aAny >>= aContentType;
 
                pImpl->xAttributes->Append( SvKeyValue( "content-type", aContentType ) );
            }
            catch ( const css::uno::Exception& )
            {
            }
        }
    }
 
    return pImpl->xAttributes.get();
}
 
css::uno::Reference< css::io::XInputStream > const &  SfxMedium::GetInputStream()
{
    if ( !pImpl->xInputStream.is() )
        GetMedium_Impl();
    return pImpl->xInputStream;
}
 
const uno::Sequence < util::RevisionTag >& SfxMedium::GetVersionList( bool _bNoReload )
{
    // if the medium has no name, then this medium should represent a new document and can have no version info
    if ( ( !_bNoReload || !pImpl->m_bVersionsAlreadyLoaded ) && !pImpl->aVersions.getLength() &&
         ( !pImpl->m_aName.isEmpty() || !pImpl->m_aLogicName.isEmpty() ) && GetStorage().is() )
    {
        uno::Reference < document::XDocumentRevisionListPersistence > xReader =
                document::DocumentRevisionListPersistence::create( comphelper::getProcessComponentContext() );
        try
        {
            pImpl->aVersions = xReader->load( GetStorage() );
        }
        catch ( const uno::Exception& )
        {
        }
    }
 
    if ( !pImpl->m_bVersionsAlreadyLoaded )
        pImpl->m_bVersionsAlreadyLoaded = true;
 
    return pImpl->aVersions;
}
 
uno::Sequence < util::RevisionTag > SfxMedium::GetVersionList( const uno::Reference < embed::XStorage >& xStorage )
{
    uno::Reference < document::XDocumentRevisionListPersistence > xReader =
        document::DocumentRevisionListPersistence::create( comphelper::getProcessComponentContext() );
    try
    {
        return xReader->load( xStorage );
    }
    catch ( const uno::Exception& )
    {
    }
 
    return uno::Sequence < util::RevisionTag >();
}
 
void SfxMedium::AddVersion_Impl( util::RevisionTag& rRevision )
{
    if ( GetStorage().is() )
    {
        // To determine a unique name for the stream
        std::vector<sal_uInt32> aLongs;
        sal_Int32 nLength = pImpl->aVersions.getLength();
        for ( sal_Int32 m=0; m<nLength; m++ )
        {
            sal_uInt32 nVer = static_cast<sal_uInt32>( pImpl->aVersions[m].Identifier.copy(7).toInt32());
            size_t n;
            for ( n=0; n<aLongs.size(); ++n )
                if ( nVer<aLongs[n] )
                    break;
 
            aLongs.insert( aLongs.begin()+n, nVer );
        }
 
        std::vector<sal_uInt32>::size_type nKey;
        for ( nKey=0; nKey<aLongs.size(); ++nKey )
            if ( aLongs[nKey] > nKey+1 )
                break;
 
        OUString aRevName = "Version" + OUString::number( nKey + 1 );
        pImpl->aVersions.realloc( nLength+1 );
        rRevision.Identifier = aRevName;
        pImpl->aVersions[nLength] = rRevision;
    }
}
 
void SfxMedium::RemoveVersion_Impl( const OUString& rName )
{
    if ( !pImpl->aVersions.getLength() )
        return;
 
    sal_Int32 nLength = pImpl->aVersions.getLength();
    for ( sal_Int32 n=0; n<nLength; n++ )
    {
        if ( pImpl->aVersions[n].Identifier == rName )
        {
            for ( sal_Int32 m=n; m<nLength-1; m++ )
                pImpl->aVersions[m] = pImpl->aVersions[m+1];
            pImpl->aVersions.realloc(nLength-1);
            return;
        }
    }
}
 
bool SfxMedium::TransferVersionList_Impl( SfxMedium const & rMedium )
{
    if ( rMedium.pImpl->aVersions.getLength() )
    {
        pImpl->aVersions = rMedium.pImpl->aVersions;
        return true;
    }
 
    return false;
}
 
void SfxMedium::SaveVersionList_Impl()
{
    if ( GetStorage().is() )
    {
        if ( !pImpl->aVersions.getLength() )
            return;
 
        uno::Reference < document::XDocumentRevisionListPersistence > xWriter =
                 document::DocumentRevisionListPersistence::create( comphelper::getProcessComponentContext() );
        try
        {
            xWriter->store( GetStorage(), pImpl->aVersions );
        }
        catch ( const uno::Exception& )
        {
        }
    }
}
 
bool SfxMedium::IsReadOnly() const
{
    // a) ReadOnly filter can't produce read/write contents!
    bool bReadOnly = pImpl->m_pFilter && (pImpl->m_pFilter->GetFilterFlags() & SfxFilterFlags::OPENREADONLY);
 
    // b) if filter allow read/write contents .. check open mode of the storage
    if (!bReadOnly)
        bReadOnly = !( GetOpenMode() & StreamMode::WRITE );
 
    // c) the API can force the readonly state!
    if (!bReadOnly)
    {
        const SfxBoolItem* pItem = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_DOC_READONLY, false);
        if (pItem)
            bReadOnly = pItem->GetValue();
    }
 
    return bReadOnly;
}
 
bool SfxMedium::IsOriginallyReadOnly() const
{
    return pImpl->m_bOriginallyReadOnly;
}
 
bool SfxMedium::IsOriginallyLoadedReadOnly() const
{
    return pImpl->m_bOriginallyLoadedReadOnly;
}
 
bool SfxMedium::SetWritableForUserOnly( const OUString& aURL )
{
    // UCB does not allow to allow write access only for the user,
    // use osl API
    bool bResult = false;
 
    ::osl::DirectoryItem aDirItem;
    if ( ::osl::DirectoryItem::get( aURL, aDirItem ) == ::osl::FileBase::E_None )
    {
        ::osl::FileStatus aFileStatus( osl_FileStatus_Mask_Attributes );
        if ( aDirItem.getFileStatus( aFileStatus ) == osl::FileBase::E_None
          && aFileStatus.isValid( osl_FileStatus_Mask_Attributes ) )
        {
            sal_uInt64 nAttributes = aFileStatus.getAttributes();
 
            nAttributes &= ~(osl_File_Attribute_OwnWrite |
                             osl_File_Attribute_GrpWrite |
                             osl_File_Attribute_OthWrite |
                             osl_File_Attribute_ReadOnly);
            nAttributes |=  (osl_File_Attribute_OwnWrite |
                             osl_File_Attribute_OwnRead);
 
            bResult = ( osl::File::setAttributes( aURL, nAttributes ) == ::osl::FileBase::E_None );
        }
    }
 
    return bResult;
}
 
namespace
{
/// Get the parent directory of a temporary file for output purposes.
OUString GetLogicBase(std::unique_ptr<SfxMedium_Impl> const & pImpl)
{
    OUString aLogicBase;
 
    if (comphelper::isFileUrl(pImpl->m_aLogicName) && !pImpl->m_pInStream)
    {
        // Try to create the temp file in the same directory when storing.
        sal_Int32 nOffset = pImpl->m_aLogicName.lastIndexOf("/");
        if (nOffset != -1)
            aLogicBase = pImpl->m_aLogicName.copy(0, nOffset);
        if (aLogicBase == "file://")
            // Doesn't make sense.
            aLogicBase.clear();
    }
 
    if (pImpl->m_bHasEmbeddedObjects)
        // Embedded objects would mean a special base, ignore that.
        aLogicBase.clear();
 
    return aLogicBase;
}
}
 
void SfxMedium::CreateTempFile( bool bReplace )
{
    if ( pImpl->pTempFile )
    {
        if ( !bReplace )
            return;
 
        pImpl->pTempFile.reset();
        pImpl->m_aName.clear();
    }
 
    OUString aLogicBase = GetLogicBase(pImpl);
    pImpl->pTempFile.reset( new ::utl::TempFile(aLogicBase.isEmpty() ? nullptr : &aLogicBase) );
    pImpl->pTempFile->EnableKillingFile();
    pImpl->m_aName = pImpl->pTempFile->GetFileName();
    OUString aTmpURL = pImpl->pTempFile->GetURL();
    if ( pImpl->m_aName.isEmpty() || aTmpURL.isEmpty() )
    {
        SetError(ERRCODE_IO_CANTWRITE);
        return;
    }
 
    if ( !(pImpl->m_nStorOpenMode & StreamMode::TRUNC) )
    {
        bool bTransferSuccess = false;
 
        if ( GetContent().is()
          && GetURLObject().GetProtocol() == INetProtocol::File
          && ::utl::UCBContentHelper::IsDocument( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) )
        {
            // if there is already such a document, we should copy it
            // if it is a file system use OS copy process
            try
            {
                uno::Reference< css::ucb::XCommandEnvironment > xComEnv;
                INetURLObject aTmpURLObj( aTmpURL );
                OUString aFileName = aTmpURLObj.getName( INetURLObject::LAST_SEGMENT,
                                                                true,
                                                                INetURLObject::DecodeMechanism::WithCharset );
                if ( !aFileName.isEmpty() && aTmpURLObj.removeSegment() )
                {
                    ::ucbhelper::Content aTargetContent( aTmpURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() );
                    OUString sMimeType = pImpl->getFilterMimeType();
                    aTargetContent.transferContent( pImpl->aContent, ::ucbhelper::InsertOperation::Copy, aFileName, NameClash::OVERWRITE, sMimeType );
                    SetWritableForUserOnly( aTmpURL );
                    bTransferSuccess = true;
                }
            }
            catch( const uno::Exception& )
            {}
 
            if ( bTransferSuccess )
            {
                CloseOutStream();
                CloseInStream();
            }
        }
 
        if ( !bTransferSuccess && pImpl->m_pInStream )
        {
            // the case when there is no URL-access available or this is a remote protocol
            // but there is an input stream
            GetOutStream();
            if ( pImpl->m_pOutStream )
            {
                char        *pBuf = new char [8192];
                ErrCode      nErr = ERRCODE_NONE;
 
                pImpl->m_pInStream->Seek(0);
                pImpl->m_pOutStream->Seek(0);
 
                while( !pImpl->m_pInStream->eof() && nErr == ERRCODE_NONE )
                {
                    sal_uInt32 nRead = pImpl->m_pInStream->ReadBytes(pBuf, 8192);
                    nErr = pImpl->m_pInStream->GetError();
                    pImpl->m_pOutStream->WriteBytes( pBuf, nRead );
                }
 
                bTransferSuccess = true;
                delete[] pBuf;
                CloseInStream();
            }
            CloseOutStream_Impl();
        }
        else
        {
            // Quite strange design, but currently it is expected that in this case no transfer happens
            // TODO/LATER: get rid of this inconsistent part of the call design
            bTransferSuccess = true;
            CloseInStream();
        }
 
        if ( !bTransferSuccess )
        {
            SetError(ERRCODE_IO_CANTWRITE);
            return;
        }
    }
 
    CloseStorage();
}
 
 
void SfxMedium::CreateTempFileNoCopy()
{
    // this call always replaces the existing temporary file
    pImpl->pTempFile.reset();
 
    OUString aLogicBase = GetLogicBase(pImpl);
    pImpl->pTempFile.reset( new ::utl::TempFile(aLogicBase.isEmpty() ? nullptr : &aLogicBase) );
    pImpl->pTempFile->EnableKillingFile();
    pImpl->m_aName = pImpl->pTempFile->GetFileName();
    if ( pImpl->m_aName.isEmpty() )
    {
        SetError(ERRCODE_IO_CANTWRITE);
        return;
    }
 
    CloseOutStream_Impl();
    CloseStorage();
}
 
bool SfxMedium::SignContents_Impl(bool bSignScriptingContent, bool bHasValidDocumentSignature,
                                  const OUString& aSignatureLineId,
                                  const Reference<XCertificate>& xCert,
                                  const Reference<XGraphic>& xValidGraphic,
                                  const Reference<XGraphic>& xInvalidGraphic,
                                  const OUString& aComment)
{
    bool bChanges = false;
 
    if (IsOpen() || GetError())
    {
        SAL_WARN("sfx.doc", "The medium must be closed by the signer!");
        return bChanges;
    }
 
    // The component should know if there was a valid document signature, since
    // it should show a warning in this case
    OUString aODFVersion(comphelper::OStorageHelper::GetODFVersionFromStorage(GetStorage()));
    uno::Reference< security::XDocumentDigitalSignatures > xSigner(
        security::DocumentDigitalSignatures::createWithVersionAndValidSignature(
            comphelper::getProcessComponentContext(), aODFVersion, bHasValidDocumentSignature ) );
 
    uno::Reference< embed::XStorage > xWriteableZipStor;
 
    // we can reuse the temporary file if there is one already
    CreateTempFile( false );
    GetMedium_Impl();
 
    try
    {
        if ( !pImpl->xStream.is() )
            throw uno::RuntimeException();
 
        bool bODF = GetFilter()->IsOwnFormat();
        try
        {
            xWriteableZipStor = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream( ZIP_STORAGE_FORMAT_STRING, pImpl->xStream );
        }
        catch (const io::IOException& rException)
        {
            if (bODF)
                SAL_WARN("sfx.doc", "ODF stream is not a zip storage: " << rException);
        }
 
        if ( !xWriteableZipStor.is() && bODF )
            throw uno::RuntimeException();
 
        uno::Reference< embed::XStorage > xMetaInf;
        uno::Reference<container::XNameAccess> xNameAccess(xWriteableZipStor, uno::UNO_QUERY);
        if (xNameAccess.is() && xNameAccess->hasByName("META-INF"))
        {
            xMetaInf = xWriteableZipStor->openStorageElement(
                                            "META-INF",
                                            embed::ElementModes::READWRITE );
            if ( !xMetaInf.is() )
                throw uno::RuntimeException();
        }
 
        if ( bSignScriptingContent )
        {
            // If the signature has already the document signature it will be removed
            // after the scripting signature is inserted.
            uno::Reference< io::XStream > xStream(
                xMetaInf->openStreamElement( xSigner->getScriptingContentSignatureDefaultStreamName(),
                                                embed::ElementModes::READWRITE ),
                uno::UNO_SET_THROW );
 
            if ( xSigner->signScriptingContent( GetZipStorageToSign_Impl(), xStream ) )
            {
                // remove the document signature if any
                OUString aDocSigName = xSigner->getDocumentContentSignatureDefaultStreamName();
                if ( !aDocSigName.isEmpty() && xMetaInf->hasByName( aDocSigName ) )
                    xMetaInf->removeElement( aDocSigName );
 
                uno::Reference< embed::XTransactedObject > xTransact( xMetaInf, uno::UNO_QUERY_THROW );
                xTransact->commit();
                xTransact.set( xWriteableZipStor, uno::UNO_QUERY_THROW );
                xTransact->commit();
 
                // the temporary file has been written, commit it to the original file
                Commit();
                bChanges = true;
            }
        }
        else
        {
            if (xMetaInf.is())
            {
                // ODF.
                uno::Reference< io::XStream > xStream;
                if (GetFilter() && GetFilter()->IsOwnFormat())
                    xStream.set(xMetaInf->openStreamElement(xSigner->getDocumentContentSignatureDefaultStreamName(), embed::ElementModes::READWRITE), uno::UNO_SET_THROW);
 
                bool bSuccess = false;
                if (xCert.is())
                    bSuccess = xSigner->signSignatureLine(
                        GetZipStorageToSign_Impl(), xStream, aSignatureLineId, xCert,
                        xValidGraphic, xInvalidGraphic, aComment);
                else
                    bSuccess = xSigner->signDocumentContent(GetZipStorageToSign_Impl(),
                                                            xStream);
 
                if (bSuccess)
                {
                    uno::Reference< embed::XTransactedObject > xTransact( xMetaInf, uno::UNO_QUERY_THROW );
                    xTransact->commit();
                    xTransact.set( xWriteableZipStor, uno::UNO_QUERY_THROW );
                    xTransact->commit();
 
                    // the temporary file has been written, commit it to the original file
                    Commit();
                    bChanges = true;
                }
            }
            else if (xWriteableZipStor.is())
            {
                // OOXML.
                uno::Reference<io::XStream> xStream;
 
                bool bSuccess = false;
                if (xCert.is())
                {
                    bSuccess = xSigner->signSignatureLine(
                        GetZipStorageToSign_Impl(/*bReadOnly=*/false), xStream, aSignatureLineId,
                        xCert, xValidGraphic, xInvalidGraphic, aComment);
                }
                else
                {
                    // We need read-write to be able to add the signature relation.
                    bSuccess =xSigner->signDocumentContent(
                        GetZipStorageToSign_Impl(/*bReadOnly=*/false), xStream);
                }
 
                if (bSuccess)
                {
                    uno::Reference<embed::XTransactedObject> xTransact(xWriteableZipStor, uno::UNO_QUERY_THROW);
                    xTransact->commit();
 
                    // the temporary file has been written, commit it to the original file
                    Commit();
                    bChanges = true;
                }
            }
            else
            {
                // Something not ZIP based: e.g. PDF.
                std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(GetName(), StreamMode::READ | StreamMode::WRITE));
                uno::Reference<io::XStream> xStream(new utl::OStreamWrapper(*pStream));
                if (xSigner->signDocumentContent(uno::Reference<embed::XStorage>(), xStream))
                    bChanges = true;
            }
        }
    }
    catch ( const uno::Exception& )
    {
        SAL_WARN( "sfx.doc", "Couldn't use signing functionality!" );
    }
 
    CloseAndRelease();
 
    ResetError();
 
    return bChanges;
}
 
 
SignatureState SfxMedium::GetCachedSignatureState_Impl()
{
    return pImpl->m_nSignatureState;
}
 
 
void SfxMedium::SetCachedSignatureState_Impl( SignatureState nState )
{
    pImpl->m_nSignatureState = nState;
}
 
void SfxMedium::SetHasEmbeddedObjects(bool bHasEmbeddedObjects)
{
    pImpl->m_bHasEmbeddedObjects = bHasEmbeddedObjects;
}
 
bool SfxMedium::HasStorage_Impl() const
{
    return pImpl->xStorage.is();
}
 
bool SfxMedium::IsOpen() const
{
    return pImpl->m_pInStream || pImpl->m_pOutStream || pImpl->xStorage.is();
}
 
OUString SfxMedium::CreateTempCopyWithExt( const OUString& aURL )
{
    OUString aResult;
 
    if ( !aURL.isEmpty() )
    {
        sal_Int32 nPrefixLen = aURL.lastIndexOf( '.' );
        OUString aExt = ( nPrefixLen == -1 ) ? OUString() :  aURL.copy( nPrefixLen );
 
        OUString aNewTempFileURL = ::utl::TempFile( OUString(), true, &aExt ).GetURL();
        if ( !aNewTempFileURL.isEmpty() )
        {
            INetURLObject aSource( aURL );
            INetURLObject aDest( aNewTempFileURL );
            OUString aFileName = aDest.getName( INetURLObject::LAST_SEGMENT,
                                                        true,
                                                        INetURLObject::DecodeMechanism::WithCharset );
            if ( !aFileName.isEmpty() && aDest.removeSegment() )
            {
                try
                {
                    uno::Reference< css::ucb::XCommandEnvironment > xComEnv;
                    ::ucbhelper::Content aTargetContent( aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() );
                    ::ucbhelper::Content aSourceContent( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() );
                    aTargetContent.transferContent( aSourceContent,
                                                        ::ucbhelper::InsertOperation::Copy,
                                                        aFileName,
                                                        NameClash::OVERWRITE );
                    aResult = aNewTempFileURL;
                }
                catch( const uno::Exception& )
                {}
            }
        }
    }
 
    return aResult;
}
 
bool SfxMedium::CallApproveHandler(const uno::Reference< task::XInteractionHandler >& xHandler, const uno::Any& rRequest, bool bAllowAbort)
{
    bool bResult = false;
 
    if ( xHandler.is() )
    {
        try
        {
            uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations( bAllowAbort ? 2 : 1 );
 
            ::rtl::Reference< ::comphelper::OInteractionApprove > pApprove( new ::comphelper::OInteractionApprove );
            aContinuations[ 0 ] = pApprove.get();
 
            if ( bAllowAbort )
            {
                ::rtl::Reference< ::comphelper::OInteractionAbort > pAbort( new ::comphelper::OInteractionAbort );
                aContinuations[ 1 ] = pAbort.get();
            }
 
            xHandler->handle(::framework::InteractionRequest::CreateRequest(rRequest, aContinuations));
            bResult = pApprove->wasSelected();
        }
        catch( const Exception& )
        {
        }
    }
 
    return bResult;
}
 
OUString SfxMedium::SwitchDocumentToTempFile()
{
    // the method returns empty string in case of failure
    OUString aResult;
    OUString aOrigURL = pImpl->m_aLogicName;
 
    if ( !aOrigURL.isEmpty() )
    {
        sal_Int32 nPrefixLen = aOrigURL.lastIndexOf( '.' );
        OUString const aExt = (nPrefixLen == -1)
                                ? OUString()
                                : aOrigURL.copy(nPrefixLen);
        OUString aNewURL = ::utl::TempFile( OUString(), true, &aExt ).GetURL();
 
        // TODO/LATER: In future the aLogicName should be set to shared folder URL
        //             and a temporary file should be created. Transport_Impl should be impossible then.
        if ( !aNewURL.isEmpty() )
        {
            uno::Reference< embed::XStorage > xStorage = GetStorage();
            uno::Reference< embed::XOptimizedStorage > xOptStorage( xStorage, uno::UNO_QUERY );
 
            if ( xOptStorage.is() )
            {
                // TODO/LATER: reuse the pImpl->pTempFile if it already exists
                CanDisposeStorage_Impl( false );
                Close();
                SetPhysicalName_Impl( OUString() );
                SetName( aNewURL );
 
                // remove the readonly state
                bool bWasReadonly = false;
                pImpl->m_nStorOpenMode = SFX_STREAM_READWRITE;
                const SfxBoolItem* pReadOnlyItem = SfxItemSet::GetItem<SfxBoolItem>(pImpl->m_pSet.get(), SID_DOC_READONLY, false);
                if ( pReadOnlyItem && pReadOnlyItem->GetValue() )
                    bWasReadonly = true;
                GetItemSet()->ClearItem( SID_DOC_READONLY );
 
                GetMedium_Impl();
                LockOrigFileOnDemand( false, false );
                CreateTempFile();
                GetMedium_Impl();
 
                if ( pImpl->xStream.is() )
                {
                    try
                    {
                        xOptStorage->writeAndAttachToStream( pImpl->xStream );
                        pImpl->xStorage = xStorage;
                        aResult = aNewURL;
                    }
                    catch( const uno::Exception& )
                    {}
                }
 
                if ( aResult.isEmpty() )
                {
                    Close();
                    SetPhysicalName_Impl( OUString() );
                    SetName( aOrigURL );
                    if ( bWasReadonly )
                    {
                        // set the readonly state back
                        pImpl->m_nStorOpenMode = SFX_STREAM_READONLY;
                        GetItemSet()->Put( SfxBoolItem(SID_DOC_READONLY, true));
                    }
                    GetMedium_Impl();
                    pImpl->xStorage = xStorage;
                }
            }
        }
    }
 
    return aResult;
}
 
bool SfxMedium::SwitchDocumentToFile( const OUString& aURL )
{
    // the method is only for storage based documents
    bool bResult = false;
    OUString aOrigURL = pImpl->m_aLogicName;
 
    if ( !aURL.isEmpty() && !aOrigURL.isEmpty() )
    {
        uno::Reference< embed::XStorage > xStorage = GetStorage();
        uno::Reference< embed::XOptimizedStorage > xOptStorage( xStorage, uno::UNO_QUERY );
 
        // TODO/LATER: reuse the pImpl->pTempFile if it already exists
        CanDisposeStorage_Impl( false );
        Close();
        SetPhysicalName_Impl( OUString() );
        SetName( aURL );
 
        // open the temporary file based document
        GetMedium_Impl();
        LockOrigFileOnDemand( false, false );
        CreateTempFile();
        GetMedium_Impl();
 
        if ( pImpl->xStream.is() )
        {
            try
            {
                uno::Reference< io::XTruncate > xTruncate( pImpl->xStream, uno::UNO_QUERY_THROW );
                xTruncate->truncate();
                if ( xOptStorage.is() )
                    xOptStorage->writeAndAttachToStream( pImpl->xStream );
                pImpl->xStorage = xStorage;
                bResult = true;
            }
            catch( const uno::Exception& )
            {}
        }
 
        if ( !bResult )
        {
            Close();
            SetPhysicalName_Impl( OUString() );
            SetName( aOrigURL );
            GetMedium_Impl();
            pImpl->xStorage = xStorage;
        }
    }
 
    return bResult;
}
 
void SfxMedium::SetInCheckIn( bool bInCheckIn )
{
    pImpl->m_bInCheckIn = bInCheckIn;
}
 
bool SfxMedium::IsInCheckIn( )
{
    return pImpl->m_bInCheckIn;
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V1019 Compound assignment expression is used inside condition.

V547 Expression 'pVersion' is always true.

V1019 Compound assignment expression is used inside condition.

V519 The 'aAny' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 2149, 2150.

V1019 Compound assignment expression is used inside condition.

V1019 Compound assignment expression 'pHandler->GetValue() >>= xHandler' is used inside condition.