Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/sfx2/source/doc/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 182 kB image not shown  

Quelle  docfile.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
 * 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 <com/sun/star/task/InteractionHandler.hpp>
#include <com/sun/star/task/XStatusIndicator.hpp>
#include <com/sun/star/uno/Reference.h>
#include <com/sun/star/ucb/XContent.hpp>
#include <com/sun/star/beans/XPropertySet.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/document/ReloadEditableRequest.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/frame/Desktop.hpp>
#include <com/sun/star/frame/XModel.hpp>
#include <com/sun/star/frame/XTerminateListener.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/CommandFailedException.hpp>
#include <com/sun/star/ucb/CommandAbortedException.hpp>
#include <com/sun/star/ucb/InteractiveLockingLockedException.hpp>
#include <com/sun/star/ucb/InteractiveNetworkReadException.hpp>
#include <com/sun/star/ucb/InteractiveNetworkWriteException.hpp>
#include <com/sun/star/ucb/Lock.hpp>
#include <com/sun/star/ucb/NameClashException.hpp>
#include <com/sun/star/ucb/XCommandEnvironment.hpp>
#include <com/sun/star/ucb/XProgressHandler.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/XSeekable.hpp>
#include <com/sun/star/io/TempFile.hpp>
#include <com/sun/star/lang/XSingleServiceFactory.hpp>
#include <com/sun/star/ucb/InsertCommandArgument.hpp>
#include <com/sun/star/ucb/NameClash.hpp>
#include <com/sun/star/util/XModifiable.hpp>
#include <com/sun/star/beans/NamedValue.hpp>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/security/DocumentDigitalSignatures.hpp>
#include <com/sun/star/security/XCertificate.hpp>
#include <tools/urlobj.hxx>
#include <tools/fileutil.hxx>
#include <unotools/configmgr.hxx>
#include <unotools/tempfile.hxx>
#include <comphelper/lok.hxx>
#include <comphelper/fileurl.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/propertyvalue.hxx>
#include <comphelper/interaction.hxx>
#include <comphelper/sequence.hxx>
#include <comphelper/simplefileaccessinteraction.hxx>
#include <comphelper/string.hxx>
#include <framework/interaction.hxx>
#include <utility>
#include <svl/stritem.hxx>
#include <svl/eitem.hxx>
#include <svtools/sfxecode.hxx>
#include <svl/itemset.hxx>
#include <svl/intitem.hxx>
#include <svtools/svparser.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/storage.hxx>
#include <svl/documentlockfile.hxx>
#include <svl/msodocumentlockfile.hxx>
#include <com/sun/star/document/DocumentRevisionListPersistence.hpp>

#include <sfx2/app.hxx>
#include <sfx2/frame.hxx>
#include <sfx2/dispatch.hxx>
#include <sfx2/fcontnr.hxx>
#include <sfx2/docfilt.hxx>
#include <sfx2/sfxsids.hrc>
#include <sfx2/sfxuno.hxx>
#include <openflag.hxx>
#include <officecfg/Office/Common.hxx>
#include <comphelper/propertysequence.hxx>
#include <vcl/weld.hxx>
#include <vcl/svapp.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <sfx2/digitalsignatures.hxx>
#include <sfx2/viewfrm.hxx>
#include <comphelper/threadpool.hxx>
#include <o3tl/string_view.hxx>
#include <svl/cryptosign.hxx>
#include <condition_variable>

#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
{

struct ReadOnlyMediumEntry
{
    ReadOnlyMediumEntry(std::shared_ptr<std::recursive_mutex> pMutex,
                        std::shared_ptr<bool> pIsDestructed)
        : _pMutex(std::move(pMutex))
        , _pIsDestructed(std::move(pIsDestructed))
    {
    }
    std::shared_ptr<std::recursive_mutex> _pMutex;
    std::shared_ptr<bool> _pIsDestructed;
};

}

static std::mutex g_chkReadOnlyGlobalMutex;
static bool g_bChkReadOnlyTaskRunning = false;
static std::unordered_map<SfxMedium*, std::shared_ptr<ReadOnlyMediumEntry>> g_newReadOnlyDocs;
static std::unordered_map<SfxMedium*, std::shared_ptr<ReadOnlyMediumEntry>> g_existingReadOnlyDocs;

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

#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
bool IsWebDAVLockingUsed()
{
    return officecfg::Office::Common::Misc::UseWebDAVFileLocking::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 INetURLObject& rURL)
{
#ifdef MACOSX
    (void)rURL;
    // Hide extension macOS-specific file property would be lost.
    return false;
#else

    if (rURL.GetProtocol() != INetProtocol::File)
        // Not a file:// URL.
        return false;

#ifdef UNX
    OUString sPath = rURL.getFSysPath(FSysStyle::Unix);
    if (sPath.isEmpty())
        return false;

    struct stat buf;
    if (lstat(sPath.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;

    // Read-only target path: this would be silently replaced.
    if (access(sPath.toUtf8().getStr(), W_OK) == -1)
        return false;

#elif defined _WIN32
    if (tools::IsMappedWebDAVPath(rURL.GetMainURL(INetURLObject::DecodeMechanism::NONE)))
        return false;
#endif

    return true;
#endif
}

class CheckReadOnlyTaskTerminateListener
    : public ::cppu::WeakImplHelper<css::frame::XTerminateListener>
{
public:
    // XEventListener
    void SAL_CALL disposing(const css::lang::EventObject& Source) override;

    // XTerminateListener
    void SAL_CALL queryTermination(const css::lang::EventObject& aEvent) override;
    void SAL_CALL notifyTermination(const css::lang::EventObject& aEvent) override;

    bool bIsTerminated = false;
    std::condition_variable mCond;
    std::mutex mMutex;
};

class CheckReadOnlyTask : public comphelper::ThreadTask
{
public:
    CheckReadOnlyTask(const std::shared_ptr<comphelper::ThreadTaskTag>& pTag);
    ~CheckReadOnlyTask();

    virtual void doWork() override;

private:
    rtl::Reference<CheckReadOnlyTaskTerminateListener> m_xListener;
};

// anonymous namespace

CheckReadOnlyTask::CheckReadOnlyTask(const std::shared_ptr<comphelper::ThreadTaskTag>& pTag)
    : ThreadTask(pTag)
    , m_xListener(new CheckReadOnlyTaskTerminateListener)
{
    Reference<css::frame::XDesktop> xDesktop
        = css::frame::Desktop::create(comphelper::getProcessComponentContext());
    if (xDesktop.is() && m_xListener != nullptr)
    {
        xDesktop->addTerminateListener(m_xListener);
    }
}

CheckReadOnlyTask::~CheckReadOnlyTask()
{
    Reference<css::frame::XDesktop> xDesktop
        = css::frame::Desktop::create(comphelper::getProcessComponentContext());
    if (xDesktop.is() && m_xListener != nullptr)
    {
        std::unique_lock<std::mutex> lock(m_xListener->mMutex);
        if (!m_xListener->bIsTerminated)
        {
            lock.unlock();
            xDesktop->removeTerminateListener(m_xListener);
        }
    }
}

namespace
{
void SAL_CALL
CheckReadOnlyTaskTerminateListener::disposing(const css::lang::EventObject& /*Source*/)
{
}

void SAL_CALL
CheckReadOnlyTaskTerminateListener::queryTermination(const css::lang::EventObject&&nbsp;/*aEvent*/)
{
}

void SAL_CALL
CheckReadOnlyTaskTerminateListener::notifyTermination(const css::lang::EventObject& /*aEvent*/)
{
    std::unique_lock<std::mutex> lock(mMutex);
    bIsTerminated = true;
    lock.unlock();
    mCond.notify_one();
}

/// Temporary file wrapper to handle tmp file lifecycle
/// for lok fork a background saving worker issues.
class MediumTempFile : public ::utl::TempFileNamed
{
    bool m_bWasChild;
public:
    MediumTempFile(const OUString *pParent )
        : ::utl::TempFileNamed(pParent)
        , m_bWasChild(comphelper::LibreOfficeKit::isForkedChild())
    {
    }

    MediumTempFile(const MediumTempFile &rFrom ) = delete;

    ~MediumTempFile()
    {
        bool isForked = comphelper::LibreOfficeKit::isForkedChild();

        // avoid deletion of files created by the parent
        if (isForked && ! m_bWasChild)
        {
            EnableKillingFile(false);
        }
    }
};
}

class SfxMedium_Impl
{
public:
    StreamMode m_nStorOpenMode;
    ErrCodeMsg m_eError;
    ErrCodeMsg m_eWarningError;

    ::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_bMSOLockFileCreated : 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;
    bool m_bNotifyWhenEditable = false;
    /// if true, xStorage is an inner package and not directly from xStream
    bool m_bODFWholesomeEncryption = false;

    OUString m_aName;
    OUString m_aLogicName;
    OUString m_aLongName;

    mutable std::shared_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::shared_ptr<std::recursive_mutex> m_pCheckEditableWorkerMutex;
    std::shared_ptr<bool> m_pIsDestructed;
    ImplSVEvent* m_pReloadEvent;

    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<MediumTempFile> 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;
    uno::Reference<io::XStream> m_xODFDecryptedInnerPackageStream;
    uno::Reference<embed::XStorage> m_xODFEncryptedOuterStorage;
    uno::Reference<embed::XStorage> m_xODFDecryptedInnerZipStorage;

    ErrCodeMsg  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;

    uno::Sequence<beans::PropertyValue> m_aArgs;

    explicit SfxMedium_Impl();
    ~SfxMedium_Impl();
    SfxMedium_Impl(const SfxMedium_Impl&) = delete;
    SfxMedium_Impl& operator=(const SfxMedium_Impl&) = delete;

    OUString getFilterMimeType() const
        { return !m_pFilter ? OUString() : m_pFilter->GetMimeType(); }
};

SfxMedium_Impl::SfxMedium_Impl() :
    m_nStorOpenMode(SFX_STREAM_READWRITE),
    m_eError(ERRCODE_NONE),
    m_eWarningError(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_bMSOLockFileCreated( 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),
    m_pReloadEvent(nullptr),
    aExpireTime( DateTime( DateTime::SYSTEM ) + static_cast<sal_Int32>(10) ),
    nLastStorageError( ERRCODE_NONE ),
    m_nSignatureState( SignatureState::NOSIGNATURES )
{
}


SfxMedium_Impl::~SfxMedium_Impl()
{
    aDoneLink.ClearPendingCall();

    pTempFile.reset();
    m_pSet.reset();
    std::unique_lock<std::recursive_mutex> chkEditLock;
    if (m_pCheckEditableWorkerMutex != nullptr)
        chkEditLock = std::unique_lock<std::recursive_mutex>(*m_pCheckEditableWorkerMutex);
    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();
}

ErrCodeMsg const & SfxMedium::GetWarningError() const
{
    return pImpl->m_eWarningError;
}

ErrCodeMsg const & SfxMedium::GetLastStorageCreationState() const
{
    return pImpl->nLastStorageError;
}

void SfxMedium::SetError(const ErrCodeMsg& rError)
{
    if (pImpl->m_eError == ERRCODE_NONE || (pImpl->m_eError.IsWarning() && rError.IsError()))
        pImpl->m_eError = rError;
}

void SfxMedium::SetWarningError(const ErrCodeMsg& nWarningError)
{
    pImpl->m_eWarningError = nWarningError;
}

ErrCodeMsg SfxMedium::GetErrorCode() const
{
    ErrCodeMsg 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 )
        return;

    uno::Reference< task::XInteractionHandler > xHandler = GetInteractionHandler();

    if ( !xHandler.is() )
        return;

    try
    {
        ::rtl::Reference< ::ucbhelper::InteractionRequest > xInteractionRequestImpl = new ::ucbhelper::InteractionRequest( uno::Any(
            document::ChangedByOthersRequest() ) );
        uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations{
            new ::ucbhelper::InteractionAbort( xInteractionRequestImpl.get() ),
            new ::ucbhelper::InteractionApprove( xInteractionRequestImpl.get() )
        };
        xInteractionRequestImpl->setContinuations( aContinuations );

        xHandler->handle( xInteractionRequestImpl );

        ::rtl::Reference< ::ucbhelper::InteractionContinuation > xSelected = xInteractionRequestImpl->getSelection();
        if ( uno::Reference< task::XInteractionAbort >( cppu::getXWeak(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(u"DateModified"_ustr) >>= 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;
        rtl::Reference<::ucbhelper::CommandEnvironment> pCommandEnv = new ::ucbhelper::CommandEnvironment( new comphelper::SimpleFileAccessInteraction( xIH ), xProgress );

        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, pCommandEnv, comphelper::getProcessComponentContext() );
            }
            catch ( const Exception& )
            {
            }
        }
        else
        {
            // TODO: SAL_WARN( "sfx.doc", "SfxMedium::GetContent()\nCreate Content? This code exists as fallback only. Please clarify, why it's 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, pCommandEnv, comphelper::getProcessComponentContext(), pImpl->aContent );
        }
    }

    return pImpl->aContent.get();
}

OUString SfxMedium::GetBaseURL( bool bForSaving )
{
    if (bForSaving)
    {
        bool bIsRemote = IsRemote();
        if ((bIsRemote && !officecfg::Office::Common::Save::URL::Internet::get())
            || (!bIsRemote && !officecfg::Office::Common::Save::URL::FileSystem::get()))
            return OUString();
    }

    if (const SfxStringItem* pBaseURLItem = GetItemSet().GetItem<SfxStringItem>(SID_DOC_BASEURL))
        return pBaseURLItem->GetValue();

    OUString aBaseURL;
    if (!comphelper::IsFuzzing() && GetContent().is())
    {
        try
        {
            Any aAny = pImpl->aContent.getPropertyValue(u"BaseURI"_ustr);
            aAny >>= aBaseURL;
        }
        catch ( const css::uno::Exception& )
        {
        }

        if ( aBaseURL.isEmpty() )
            aBaseURL = GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE );
    }
    return aBaseURL;
}

bool SfxMedium::IsSkipImages() const
{
    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 ( GetErrorIgnoreWarning() )
        return nullptr;

    return pImpl->m_pInStream.get();
}


void SfxMedium::CloseInStream()
{
    CloseInStream_Impl();
}

void SfxMedium::CloseInStream_Impl(bool bInDestruction)
{
    // 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() && !bInDestruction )
    {
        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->FlushBuffer();
    else if( pImpl->m_pInStream  )
        pImpl->m_pInStream->FlushBuffer();

    if ( GetErrorIgnoreWarning() == ERRCODE_NONE )
    {
        // does something only in case there is a temporary file ( means aName points to different location than aLogicName )
        Transfer_Impl();
    }

    bool bResult = ( GetErrorIgnoreWarning() == 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 '" << pImpl->m_aName << "' not convertible to file URL");
        }
        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() const
{
    bool bPreview = false;
    const SfxBoolItem* pPreview = GetItemSet().GetItem(SID_PREVIEW, false);
    if ( pPreview )
        bPreview = pPreview->GetValue();
    else
    {
        const SfxStringItem* pFlags = GetItemSet().GetItem(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 ( GetErrorIgnoreWarning() )
        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_bODFWholesomeEncryption || 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();
}


bool SfxMedium::SetEncryptionDataToStorage_Impl()
{
    // in case media-descriptor contains password it should be used on opening
    if ( !pImpl->xStorage.is() || !pImpl->m_pSet )
        return false;

    uno::Sequence< beans::NamedValue > aEncryptionData;
    if ( !GetEncryptionData_Impl( pImpl->m_pSet.get(), aEncryptionData ) )
        return false;

    // replace the password with encryption data
    pImpl->m_pSet->ClearItem( SID_PASSWORD );
    pImpl->m_pSet->Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::Any( aEncryptionData ) ) );

    try
    {
        ::comphelper::OStorageHelper::SetCommonStorageEncryptionData( pImpl->xStorage, aEncryptionData );
    }
    catchconst uno::Exception& )
    {
        SAL_WARN( "sfx.doc""It must be possible to set a common password for the storage" );
        SetError(ERRCODE_IO_GENERAL);
        return false;
    }
    return true;
}

#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.

namespace
{

OUString tryMSOwnerFiles(std::u16string_view sDocURL)
{
    svt::MSODocumentLockFile aMSOLockFile(sDocURL);
    LockFileEntry aData;
    try
    {
        aData = aMSOLockFile.GetLockData();
    }
    catchconst uno::Exception& )
    {
        return OUString();
    }

    OUString sUserData = aData[LockFileComponent::OOOUSERNAME];

    if (!sUserData.isEmpty())
        sUserData += " (MS Office)"// Mention the used office suite

    return sUserData;
}

OUString tryForeignLockfiles(std::u16string_view sDocURL)
{
    OUString sUserData = tryMSOwnerFiles(sDocURL);
    // here we can test for empty result, and add other known applications' lockfile testing
    return sUserData.trim();
}
}

SfxMedium::ShowLockResult SfxMedium::ShowLockedDocumentDialog(const LockFileEntry&&nbsp;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 ( xHandler.is() && ( bIsLoading || !bHandleSysLocked || bOwnLock ) )
    {
        OUString aDocumentURL
            = GetURLObject().GetLastName(INetURLObject::DecodeMechanism::WithCharset);
        OUString aInfo;
        ::rtl::Reference< ::ucbhelper::InteractionRequest > xInteractionRequestImpl;

        sal_Int32 nContinuations = 3;

        if ( bOwnLock )
        {
            aInfo = aData[LockFileComponent::EDITTIME];

            xInteractionRequestImpl = new ::ucbhelper::InteractionRequest( uno::Any(
                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() && !GetURLObject().isAnyKnownWebDAVScheme())
                // Try to get name of user who has locked the file using other applications
                aInfo = tryForeignLockfiles(
                    GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::NONE));

            if ( !aInfo.isEmpty() && !aData[LockFileComponent::EDITTIME].isEmpty() )
                aInfo += " ( " + aData[LockFileComponent::EDITTIME] + " )";

            if (!bIsLoading) // so, !bHandleSysLocked
            {
                xInteractionRequestImpl = new ::ucbhelper::InteractionRequest(uno::Any(
                    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::Any(
                    document::LockedDocumentRequest( OUString(), uno::Reference< uno::XInterface >(), aDocumentURL, aInfo ) ) );
            }
        }

        uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations(nContinuations);
        auto pContinuations = aContinuations.getArray();
        pContinuations[0] = new ::ucbhelper::InteractionAbort( xInteractionRequestImpl.get() );
        pContinuations[1] = new ::ucbhelper::InteractionApprove( xInteractionRequestImpl.get() );
        pContinuations[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
            pContinuations[3] = new ::ucbhelper::InteractionRetry(xInteractionRequestImpl.get());
        }
        xInteractionRequestImpl->setContinuations( aContinuations );

        xHandler->handle( xInteractionRequestImpl );

        bool bOpenReadOnly = false;
        ::rtl::Reference< ::ucbhelper::InteractionContinuation > xSelected = xInteractionRequestImpl->getSelection();
        if ( uno::Reference< task::XInteractionAbort >( cppu::getXWeak(xSelected.get()), uno::UNO_QUERY ).is() )
        {
            SetError(ERRCODE_ABORT);
        }
        else if ( uno::Reference< task::XInteractionDisapprove >( cppu::getXWeak(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 >(cppu::getXWeak(xSelected.get()), uno::UNO_QUERY).is())
        {
            // User decided to ignore the alien (stale?) lock file without filesystem lock
            nResult = ShowLockResult::Succeeded;
        }
        else if (uno::Reference< task::XInteractionApprove >( cppu::getXWeak(xSelected.get()), uno::UNO_QUERY ).is())
        {
            bOpenReadOnly = true;
        }
        else // user selected "Notify"
        {
            pImpl->m_bNotifyWhenEditable = true;
            AddToCheckEditableWorkerList();
            bOpenReadOnly = true;
        }

        if (bOpenReadOnly)
        {
            // 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::Any( document::LockFileIgnoreRequest() ));
                break;
            case MessageDlg::LockFileCorrupt:
                xIgnoreRequestImpl = new ::ucbhelper::InteractionRequest(uno::Any( document::LockFileCorruptRequest() ));
                break;
        }

        uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations{
            new ::ucbhelper::InteractionAbort(xIgnoreRequestImpl.get()),
            new ::ucbhelper::InteractionApprove(xIgnoreRequestImpl.get())
        };
        xIgnoreRequestImpl->setContinuations(aContinuations);

        xHandler->handle(xIgnoreRequestImpl);

        ::rtl::Reference< ::ucbhelper::InteractionContinuation > xSelected = xIgnoreRequestImpl->getSelection();
        bool bReadOnly = true;

        if (uno::Reference<task::XInteractionAbort>(cppu::getXWeak(xSelected.get()), uno::UNO_QUERY).is())
        {
            SetError(ERRCODE_ABORT);
            bReadOnly = false;
        }
        else if (!uno::Reference<task::XInteractionApprove>(cppu::getXWeak(xSelected.get()), uno::UNO_QUERY).is())
        {
            // user selected "Notify"
            pImpl->m_bNotifyWhenEditable = true;
            AddToCheckEditableWorkerList();
        }

        if (bReadOnly)
            GetItemSet().Put(SfxBoolItem(SID_DOC_READONLY, true));

        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;
    }
}

namespace
{

// for LOCK request, suppress dialog on 403, typically indicates read-only
// document and there's a 2nd dialog prompting to open a copy anyway
class LockInteractionHandler : public ::cppu::WeakImplHelper<task::XInteractionHandler>
{
private:
    uno::Reference<task::XInteractionHandler> m_xHandler;

public:
    explicit LockInteractionHandler(uno::Reference<task::XInteractionHandler> constxHandler)
        : m_xHandler(xHandler)
    {
    }

    virtual void SAL_CALL handle(uno::Reference<task::XInteractionRequest> const& xRequest) override
    {
        ucb::InteractiveNetworkWriteException readException;
        ucb::InteractiveNetworkReadException writeException;
        if ((xRequest->getRequest() >>= readException)
            || (xRequest->getRequest() >>= writeException))
        {
            return// 403 gets reported as one of these; ignore to avoid dialog
        }
        m_xHandler->handle(xRequest);
    }
};

// namespace

#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,
                                                          LockFileEntry* pLockData)
{
#if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT
    (void) bLoading;
    (void) bNoUI;
    (void) bTryIgnoreLockFile;
    (void) pLockData;
    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() )
    {
        // do nothing if WebDAV locking is disabled
        if (!IsWebDAVLockingUsed())
            return LockFileResult::Succeeded;

        {
            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 = GetItemSet().GetItem(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 );
                        // Dialog with error is superfluous:
                        // on loading, will result in read-only with infobar.
                        // bNoUI case for Reload failing, will open dialog later.
                        if (bLoading || bNoUI)
                        {
                            xCHandler = new LockInteractionHandler(xCHandler);
                        }
                        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
                            if (!bNoUI || pLockData)
                            {
                                // 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(u"DAV:lockdiscovery"_ustr) >>= aLocks) && aLocks.hasElements())
                                {
                                    // got at least a lock, show the owner of the first lock returned
                                    const 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 (!bNoUI)
                                {
                                    bUIStatus = ShowLockedDocumentDialog(aLockData, bLoading, false,
                                                                         true);
                                }

                                if (pLockData)
                                {
                                    std::copy(aLockData.begin(), aLockData.end(), pLockData->begin());
                                }
                            }
                        }
                        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& )
                        {
                            TOOLS_WARN_EXCEPTION( "sfx.doc""Locking exception: WebDAV while trying to lock the file" );
                        }
                    }
                } while( !bResult && bUIStatus == ShowLockResult::Try );
            }

            pImpl->m_bLocked = bResult;

            if ( !bResult && GetErrorIgnoreWarning() == 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;
        }
        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 = GetItemSet().GetItem(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(u"IsReadOnly"_ustr) >>= bContentReadonly;
                }
                catchconst 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 (GetErrorIgnoreWarning() != 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 = std::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 );

                            std::unique_ptr<svt::MSODocumentLockFile> pMSOLockFile;
                            if (officecfg::Office::Common::Filter::Microsoft::Import::CreateMSOLockFiles::get()  && svt::MSODocumentLockFile::IsMSOSupportedFileFormat(pImpl->m_aLogicName))
                            {
                                pMSOLockFile.reset(new svt::MSODocumentLockFile(pImpl->m_aLogicName));
                                pImpl->m_bMSOLockFileCreated = true;
                            }

                            bool  bIoErr = false;

                            if (!bHandleSysLocked)
                            {
                                try
                                {
                                    bResult = aLockFile.CreateOwnLockFile();
                                    if(pMSOLockFile)
                                        bResult &= pMSOLockFile->CreateOwnLockFile();
                                }
                                catch (const uno::Exception&)
                                {
                                    if (tools::IsMappedWebDAVPath(GetURLObject().GetMainURL(
                                            INetURLObject::DecodeMechanism::NONE)))
                                    {
                                        // 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(pMSOLockFile)
                                        pMSOLockFile->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;
                                }
                                catchconst 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();

                                        if(pMSOLockFile)
                                            pMSOLockFile->OverwriteOwnLockFile();
                                    }
                                    else if (bLoading && !bHandleSysLocked)
                                        eResult = LockFileResult::FailedLockFile;

                                    if (!bResult && pLockData)
                                    {
                                        std::copy(aData.begin(), aData.end(), pLockData->begin());
                                    }
                                }
                            }
                        }
                        catchconst 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;
                }
            }
            else // read-only
            {
                AddToCheckEditableWorkerList();
            }
        }

        if ( !bResult && GetErrorIgnoreWarning() == 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;
    }
    catchconst uno::Exception& )
    {
        TOOLS_WARN_EXCEPTION( "sfx.doc""Locking exception: high probability, that the content has not been created" );
    }

    return eResult;
#endif
}

// this either returns non-null or throws exception
uno::Reference<embed::XStorage>
SfxMedium::TryEncryptedInnerPackage(uno::Reference<embed::XStorage> const & xStorage)
{
    uno::Reference<embed::XStorage> xRet;
    if (xStorage->hasByName(u"encrypted-package"_ustr))
    {
        uno::Reference<io::XStream> const
            xDecryptedInnerPackage = xStorage->openStreamElement(
                u"encrypted-package"_ustr,
                embed::ElementModes::READ | embed::ElementModes::NOCREATE);
        // either this throws due to wrong password or IO error, or returns stream
        assert(xDecryptedInnerPackage.is());
        // need a seekable stream => copy
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=92 H=98 G=94

¤ Dauer der Verarbeitung: 0.20 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.