1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_features.h>
26 #include <sfx2/docfile.hxx>
27 #include <sfx2/signaturestate.hxx>
29 #include <com/sun/star/task/InteractionHandler.hpp>
30 #include <com/sun/star/task/XStatusIndicator.hpp>
31 #include <com/sun/star/uno/Reference.h>
32 #include <com/sun/star/ucb/XContent.hpp>
33 #include <com/sun/star/beans/XPropertySet.hpp>
34 #include <com/sun/star/container/XChild.hpp>
35 #include <com/sun/star/document/XDocumentRevisionListPersistence.hpp>
36 #include <com/sun/star/document/LockedDocumentRequest.hpp>
37 #include <com/sun/star/document/LockedOnSavingRequest.hpp>
38 #include <com/sun/star/document/OwnLockOnDocumentRequest.hpp>
39 #include <com/sun/star/document/LockFileIgnoreRequest.hpp>
40 #include <com/sun/star/document/LockFileCorruptRequest.hpp>
41 #include <com/sun/star/document/ChangedByOthersRequest.hpp>
42 #include <com/sun/star/document/ReloadEditableRequest.hpp>
43 #include <com/sun/star/embed/XTransactedObject.hpp>
44 #include <com/sun/star/embed/ElementModes.hpp>
45 #include <com/sun/star/embed/UseBackupException.hpp>
46 #include <com/sun/star/embed/XOptimizedStorage.hpp>
47 #include <com/sun/star/frame/Desktop.hpp>
48 #include <com/sun/star/frame/XModel.hpp>
49 #include <com/sun/star/frame/XTerminateListener.hpp>
50 #include <com/sun/star/graphic/XGraphic.hpp>
51 #include <com/sun/star/ucb/ContentCreationException.hpp>
52 #include <com/sun/star/ucb/InteractiveIOException.hpp>
53 #include <com/sun/star/ucb/CommandFailedException.hpp>
54 #include <com/sun/star/ucb/CommandAbortedException.hpp>
55 #include <com/sun/star/ucb/InteractiveLockingLockedException.hpp>
56 #include <com/sun/star/ucb/InteractiveNetworkReadException.hpp>
57 #include <com/sun/star/ucb/InteractiveNetworkWriteException.hpp>
58 #include <com/sun/star/ucb/Lock.hpp>
59 #include <com/sun/star/ucb/NameClashException.hpp>
60 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
61 #include <com/sun/star/ucb/XProgressHandler.hpp>
62 #include <com/sun/star/io/XOutputStream.hpp>
63 #include <com/sun/star/io/XInputStream.hpp>
64 #include <com/sun/star/io/XTruncate.hpp>
65 #include <com/sun/star/io/XSeekable.hpp>
66 #include <com/sun/star/io/TempFile.hpp>
67 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
68 #include <com/sun/star/ucb/InsertCommandArgument.hpp>
69 #include <com/sun/star/ucb/NameClash.hpp>
70 #include <com/sun/star/util/XModifiable.hpp>
71 #include <com/sun/star/beans/NamedValue.hpp>
72 #include <com/sun/star/beans/PropertyValue.hpp>
73 #include <com/sun/star/security/DocumentDigitalSignatures.hpp>
74 #include <com/sun/star/security/XCertificate.hpp>
75 #include <tools/urlobj.hxx>
76 #include <tools/fileutil.hxx>
77 #include <unotools/configmgr.hxx>
78 #include <unotools/tempfile.hxx>
79 #include <comphelper/lok.hxx>
80 #include <comphelper/fileurl.hxx>
81 #include <comphelper/processfactory.hxx>
82 #include <comphelper/propertyvalue.hxx>
83 #include <comphelper/interaction.hxx>
84 #include <comphelper/sequence.hxx>
85 #include <comphelper/simplefileaccessinteraction.hxx>
86 #include <comphelper/string.hxx>
87 #include <framework/interaction.hxx>
89 #include <svl/stritem.hxx>
90 #include <svl/eitem.hxx>
91 #include <svtools/sfxecode.hxx>
92 #include <svl/itemset.hxx>
93 #include <svl/intitem.hxx>
94 #include <svtools/svparser.hxx>
95 #include <sal/log.hxx>
97 #include <unotools/streamwrap.hxx>
99 #include <osl/file.hxx>
101 #include <comphelper/storagehelper.hxx>
102 #include <unotools/mediadescriptor.hxx>
103 #include <comphelper/docpasswordhelper.hxx>
104 #include <tools/datetime.hxx>
105 #include <unotools/pathoptions.hxx>
106 #include <svtools/asynclink.hxx>
107 #include <ucbhelper/commandenvironment.hxx>
108 #include <unotools/ucbstreamhelper.hxx>
109 #include <unotools/ucbhelper.hxx>
110 #include <unotools/progresshandlerwrap.hxx>
111 #include <ucbhelper/content.hxx>
112 #include <ucbhelper/interactionrequest.hxx>
113 #include <sot/storage.hxx>
114 #include <svl/documentlockfile.hxx>
115 #include <svl/msodocumentlockfile.hxx>
116 #include <com/sun/star/document/DocumentRevisionListPersistence.hpp>
118 #include <sfx2/app.hxx>
119 #include <sfx2/frame.hxx>
120 #include <sfx2/dispatch.hxx>
121 #include <sfx2/fcontnr.hxx>
122 #include <sfx2/docfilt.hxx>
123 #include <sfx2/sfxsids.hrc>
124 #include <sfx2/sfxuno.hxx>
125 #include <openflag.hxx>
126 #include <officecfg/Office/Common.hxx>
127 #include <comphelper/propertysequence.hxx>
128 #include <vcl/weld.hxx>
129 #include <vcl/svapp.hxx>
130 #include <comphelper/diagnose_ex.hxx>
131 #include <unotools/fltrcfg.hxx>
132 #include <sfx2/digitalsignatures.hxx>
133 #include <sfx2/viewfrm.hxx>
134 #include <comphelper/threadpool.hxx>
135 #include <o3tl/string_view.hxx>
136 #include <condition_variable>
138 #include <com/sun/star/io/WrongFormatException.hpp>
142 using namespace ::com::sun::star
;
143 using namespace ::com::sun::star::graphic
;
144 using namespace ::com::sun::star::uno
;
145 using namespace ::com::sun::star::ucb
;
146 using namespace ::com::sun::star::beans
;
147 using namespace ::com::sun::star::io
;
148 using namespace ::com::sun::star::security
;
153 struct ReadOnlyMediumEntry
155 ReadOnlyMediumEntry(std::shared_ptr
<std::recursive_mutex
> pMutex
,
156 std::shared_ptr
<bool> pIsDestructed
)
157 : _pMutex(std::move(pMutex
))
158 , _pIsDestructed(std::move(pIsDestructed
))
161 std::shared_ptr
<std::recursive_mutex
> _pMutex
;
162 std::shared_ptr
<bool> _pIsDestructed
;
167 static std::mutex g_chkReadOnlyGlobalMutex
;
168 static bool g_bChkReadOnlyTaskRunning
= false;
169 static std::unordered_map
<SfxMedium
*, std::shared_ptr
<ReadOnlyMediumEntry
>> g_newReadOnlyDocs
;
170 static std::unordered_map
<SfxMedium
*, std::shared_ptr
<ReadOnlyMediumEntry
>> g_existingReadOnlyDocs
;
174 #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
176 bool IsSystemFileLockingUsed()
178 #if HAVE_FEATURE_MACOSX_SANDBOX
181 return officecfg::Office::Common::Misc::UseDocumentSystemFileLocking::get();
186 bool IsOOoLockFileUsed()
188 #if HAVE_FEATURE_MACOSX_SANDBOX
191 return officecfg::Office::Common::Misc::UseDocumentOOoLockFile::get();
197 return officecfg::Office::Common::Misc::UseLocking::get();
202 #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
203 bool IsWebDAVLockingUsed()
205 return officecfg::Office::Common::Misc::UseWebDAVFileLocking::get();
209 /// Gets default attributes of a file:// URL.
210 sal_uInt64
GetDefaultFileAttributes(const OUString
& rURL
)
214 if (!comphelper::isFileUrl(rURL
))
217 // Make sure the file exists (and create it if not).
218 osl::File
aFile(rURL
);
219 osl::File::RC nRes
= aFile
.open(osl_File_OpenFlag_Create
);
220 if (nRes
!= osl::File::E_None
&& nRes
!= osl::File::E_EXIST
)
225 osl::DirectoryItem aItem
;
226 if (osl::DirectoryItem::get(rURL
, aItem
) != osl::DirectoryItem::E_None
)
229 osl::FileStatus
aStatus(osl_FileStatus_Mask_Attributes
);
230 if (aItem
.getFileStatus(aStatus
) != osl::DirectoryItem::E_None
)
233 nRet
= aStatus
.getAttributes();
237 /// Determines if rURL is safe to move or not.
238 bool IsFileMovable(const INetURLObject
& rURL
)
242 // Hide extension macOS-specific file property would be lost.
246 if (rURL
.GetProtocol() != INetProtocol::File
)
247 // Not a file:// URL.
251 OUString sPath
= rURL
.getFSysPath(FSysStyle::Unix
);
256 if (lstat(sPath
.toUtf8().getStr(), &buf
) != 0)
259 // Hardlink or symlink: osl::File::move() doesn't play with these nicely.
260 if (buf
.st_nlink
> 1 || S_ISLNK(buf
.st_mode
))
263 if (tools::IsMappedWebDAVPath(rURL
.GetMainURL(INetURLObject::DecodeMechanism::NONE
)))
271 class CheckReadOnlyTaskTerminateListener
272 : public ::cppu::WeakImplHelper
<css::frame::XTerminateListener
>
276 void SAL_CALL
disposing(const css::lang::EventObject
& Source
) override
;
278 // XTerminateListener
279 void SAL_CALL
queryTermination(const css::lang::EventObject
& aEvent
) override
;
280 void SAL_CALL
notifyTermination(const css::lang::EventObject
& aEvent
) override
;
282 bool bIsTerminated
= false;
283 std::condition_variable mCond
;
287 class CheckReadOnlyTask
: public comphelper::ThreadTask
290 CheckReadOnlyTask(const std::shared_ptr
<comphelper::ThreadTaskTag
>& pTag
);
291 ~CheckReadOnlyTask();
293 virtual void doWork() override
;
296 rtl::Reference
<CheckReadOnlyTaskTerminateListener
> m_xListener
;
299 } // anonymous namespace
301 CheckReadOnlyTask::CheckReadOnlyTask(const std::shared_ptr
<comphelper::ThreadTaskTag
>& pTag
)
303 , m_xListener(new CheckReadOnlyTaskTerminateListener
)
305 Reference
<css::frame::XDesktop
> xDesktop
306 = css::frame::Desktop::create(comphelper::getProcessComponentContext());
307 if (xDesktop
.is() && m_xListener
!= nullptr)
309 xDesktop
->addTerminateListener(m_xListener
);
313 CheckReadOnlyTask::~CheckReadOnlyTask()
315 Reference
<css::frame::XDesktop
> xDesktop
316 = css::frame::Desktop::create(comphelper::getProcessComponentContext());
317 if (xDesktop
.is() && m_xListener
!= nullptr)
319 std::unique_lock
<std::mutex
> lock(m_xListener
->mMutex
);
320 if (!m_xListener
->bIsTerminated
)
323 xDesktop
->removeTerminateListener(m_xListener
);
331 CheckReadOnlyTaskTerminateListener::disposing(const css::lang::EventObject
& /*Source*/)
336 CheckReadOnlyTaskTerminateListener::queryTermination(const css::lang::EventObject
& /*aEvent*/)
341 CheckReadOnlyTaskTerminateListener::notifyTermination(const css::lang::EventObject
& /*aEvent*/)
343 std::unique_lock
<std::mutex
> lock(mMutex
);
344 bIsTerminated
= true;
349 /// Temporary file wrapper to handle tmp file lifecyle
350 /// for lok fork a background saving worker issues.
351 class MediumTempFile
: public ::utl::TempFileNamed
355 MediumTempFile(const OUString
*pParent
)
356 : ::utl::TempFileNamed(pParent
)
357 , m_bWasChild(comphelper::LibreOfficeKit::isForkedChild())
361 MediumTempFile(const MediumTempFile
&rFrom
) = delete;
365 bool isForked
= comphelper::LibreOfficeKit::isForkedChild();
367 // avoid deletion of files created by the parent
368 if (isForked
&& ! m_bWasChild
)
370 EnableKillingFile(false);
379 StreamMode m_nStorOpenMode
;
381 ErrCodeMsg m_eWarningError
;
383 ::ucbhelper::Content aContent
;
384 bool bUpdatePickList
:1;
386 bool bDownloadDone
:1;
388 bool bUseInteractionHandler
:1;
389 bool bAllowDefaultIntHdl
:1;
390 bool bDisposeStorage
:1;
391 bool bStorageBasedOnInStream
:1;
392 bool m_bSalvageMode
:1;
393 bool m_bVersionsAlreadyLoaded
:1;
395 bool m_bMSOLockFileCreated
: 1;
396 bool m_bDisableUnlockWebDAV
:1;
397 bool m_bGotDateTime
:1;
398 bool m_bRemoveBackup
:1;
399 bool m_bOriginallyReadOnly
:1;
400 bool m_bOriginallyLoadedReadOnly
:1;
401 bool m_bTriedStorage
:1;
403 bool m_bInputStreamIsReadOnly
:1;
405 bool m_bDisableFileSync
= false;
406 bool m_bNotifyWhenEditable
= false;
407 /// if true, xStorage is an inner package and not directly from xStream
408 bool m_bODFWholesomeEncryption
= false;
411 OUString m_aLogicName
;
412 OUString m_aLongName
;
414 mutable std::shared_ptr
<SfxItemSet
> m_pSet
;
415 mutable std::unique_ptr
<INetURLObject
> m_pURLObj
;
417 std::shared_ptr
<const SfxFilter
> m_pFilter
;
418 std::shared_ptr
<const SfxFilter
> m_pCustomFilter
;
420 std::shared_ptr
<std::recursive_mutex
> m_pCheckEditableWorkerMutex
;
421 std::shared_ptr
<bool> m_pIsDestructed
;
422 ImplSVEvent
* m_pReloadEvent
;
424 std::unique_ptr
<SvStream
> m_pInStream
;
425 std::unique_ptr
<SvStream
> m_pOutStream
;
428 DateTime aExpireTime
;
429 SfxFrameWeakRef wLoadTargetFrame
;
430 SvKeyValueIteratorRef xAttributes
;
432 svtools::AsynchronLink aDoneLink
;
434 uno::Sequence
< util::RevisionTag
> aVersions
;
436 std::unique_ptr
<MediumTempFile
> pTempFile
;
438 uno::Reference
<embed::XStorage
> xStorage
;
439 uno::Reference
<embed::XStorage
> m_xZipStorage
;
440 uno::Reference
<io::XInputStream
> m_xInputStreamToLoadFrom
;
441 uno::Reference
<io::XInputStream
> xInputStream
;
442 uno::Reference
<io::XStream
> xStream
;
443 uno::Reference
<io::XStream
> m_xLockingStream
;
444 uno::Reference
<task::XInteractionHandler
> xInteraction
;
445 uno::Reference
<io::XStream
> m_xODFDecryptedInnerPackageStream
;
446 uno::Reference
<embed::XStorage
> m_xODFEncryptedOuterStorage
;
447 uno::Reference
<embed::XStorage
> m_xODFDecryptedInnerZipStorage
;
449 ErrCodeMsg nLastStorageError
;
451 OUString m_aBackupURL
;
453 // the following member is changed and makes sense only during saving
454 // TODO/LATER: in future the signature state should be controlled by the medium not by the document
455 // in this case the member will hold this information
456 SignatureState m_nSignatureState
;
458 bool m_bHasEmbeddedObjects
= false;
460 util::DateTime m_aDateTime
;
462 uno::Sequence
<beans::PropertyValue
> m_aArgs
;
464 explicit SfxMedium_Impl();
466 SfxMedium_Impl(const SfxMedium_Impl
&) = delete;
467 SfxMedium_Impl
& operator=(const SfxMedium_Impl
&) = delete;
469 OUString
getFilterMimeType() const
470 { return !m_pFilter
? OUString() : m_pFilter
->GetMimeType(); }
473 SfxMedium_Impl::SfxMedium_Impl() :
474 m_nStorOpenMode(SFX_STREAM_READWRITE
),
475 m_eError(ERRCODE_NONE
),
476 m_eWarningError(ERRCODE_NONE
),
477 bUpdatePickList(true),
479 bDownloadDone( true ),
481 bUseInteractionHandler( true ),
482 bAllowDefaultIntHdl( false ),
483 bDisposeStorage( false ),
484 bStorageBasedOnInStream( false ),
485 m_bSalvageMode( false ),
486 m_bVersionsAlreadyLoaded( false ),
488 m_bMSOLockFileCreated( false ),
489 m_bDisableUnlockWebDAV( false ),
490 m_bGotDateTime( false ),
491 m_bRemoveBackup( false ),
492 m_bOriginallyReadOnly(false),
493 m_bOriginallyLoadedReadOnly(false),
494 m_bTriedStorage(false),
496 m_bInputStreamIsReadOnly(false),
498 m_pReloadEvent(nullptr),
499 aExpireTime( DateTime( DateTime::SYSTEM
) + static_cast<sal_Int32
>(10) ),
500 nLastStorageError( ERRCODE_NONE
),
501 m_nSignatureState( SignatureState::NOSIGNATURES
)
506 SfxMedium_Impl::~SfxMedium_Impl()
508 aDoneLink
.ClearPendingCall();
512 std::unique_lock
<std::recursive_mutex
> chkEditLock
;
513 if (m_pCheckEditableWorkerMutex
!= nullptr)
514 chkEditLock
= std::unique_lock
<std::recursive_mutex
>(*m_pCheckEditableWorkerMutex
);
518 void SfxMedium::ResetError()
520 pImpl
->m_eError
= ERRCODE_NONE
;
521 if( pImpl
->m_pInStream
)
522 pImpl
->m_pInStream
->ResetError();
523 if( pImpl
->m_pOutStream
)
524 pImpl
->m_pOutStream
->ResetError();
527 ErrCodeMsg
const & SfxMedium::GetWarningError() const
529 return pImpl
->m_eWarningError
;
532 ErrCodeMsg
const & SfxMedium::GetLastStorageCreationState() const
534 return pImpl
->nLastStorageError
;
537 void SfxMedium::SetError(ErrCodeMsg nError
)
539 pImpl
->m_eError
= nError
;
542 void SfxMedium::SetWarningError(const ErrCodeMsg
& nWarningError
)
544 pImpl
->m_eWarningError
= nWarningError
;
547 ErrCodeMsg
SfxMedium::GetErrorCode() const
549 ErrCodeMsg lError
= pImpl
->m_eError
;
550 if(!lError
&& pImpl
->m_pInStream
)
551 lError
= pImpl
->m_pInStream
->GetErrorCode();
552 if(!lError
&& pImpl
->m_pOutStream
)
553 lError
= pImpl
->m_pOutStream
->GetErrorCode();
557 void SfxMedium::CheckFileDate( const util::DateTime
& aInitDate
)
559 GetInitFileDate( true );
560 if ( pImpl
->m_aDateTime
.Seconds
== aInitDate
.Seconds
561 && pImpl
->m_aDateTime
.Minutes
== aInitDate
.Minutes
562 && pImpl
->m_aDateTime
.Hours
== aInitDate
.Hours
563 && pImpl
->m_aDateTime
.Day
== aInitDate
.Day
564 && pImpl
->m_aDateTime
.Month
== aInitDate
.Month
565 && pImpl
->m_aDateTime
.Year
== aInitDate
.Year
)
568 uno::Reference
< task::XInteractionHandler
> xHandler
= GetInteractionHandler();
570 if ( !xHandler
.is() )
575 ::rtl::Reference
< ::ucbhelper::InteractionRequest
> xInteractionRequestImpl
= new ::ucbhelper::InteractionRequest( uno::Any(
576 document::ChangedByOthersRequest() ) );
577 uno::Sequence
< uno::Reference
< task::XInteractionContinuation
> > aContinuations
{
578 new ::ucbhelper::InteractionAbort( xInteractionRequestImpl
.get() ),
579 new ::ucbhelper::InteractionApprove( xInteractionRequestImpl
.get() )
581 xInteractionRequestImpl
->setContinuations( aContinuations
);
583 xHandler
->handle( xInteractionRequestImpl
);
585 ::rtl::Reference
< ::ucbhelper::InteractionContinuation
> xSelected
= xInteractionRequestImpl
->getSelection();
586 if ( uno::Reference
< task::XInteractionAbort
>( xSelected
.get(), uno::UNO_QUERY
).is() )
588 SetError(ERRCODE_ABORT
);
591 catch ( const uno::Exception
& )
595 bool SfxMedium::DocNeedsFileDateCheck() const
597 return ( !IsReadOnly() && ( GetURLObject().GetProtocol() == INetProtocol::File
||
598 GetURLObject().isAnyKnownWebDAVScheme() ) );
601 util::DateTime
const & SfxMedium::GetInitFileDate( bool bIgnoreOldValue
)
603 if ( ( bIgnoreOldValue
|| !pImpl
->m_bGotDateTime
) && !pImpl
->m_aLogicName
.isEmpty() )
607 // add a default css::ucb::XCommandEnvironment
608 // in order to have the WebDAV UCP provider manage http/https authentication correctly
609 ::ucbhelper::Content
aContent( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE
),
610 utl::UCBContentHelper::getDefaultCommandEnvironment(),
611 comphelper::getProcessComponentContext() );
613 aContent
.getPropertyValue("DateModified") >>= pImpl
->m_aDateTime
;
614 pImpl
->m_bGotDateTime
= true;
616 catch ( const css::uno::Exception
& )
621 return pImpl
->m_aDateTime
;
625 Reference
< XContent
> SfxMedium::GetContent() const
627 if ( !pImpl
->aContent
.get().is() )
629 Reference
< css::ucb::XContent
> xContent
;
631 // tdf#95144 add a default css::ucb::XCommandEnvironment
632 // in order to have the WebDAV UCP provider manage https protocol certificates correctly
633 css:: uno::Reference
< task::XInteractionHandler
> xIH(
634 css::task::InteractionHandler::createWithParent( comphelper::getProcessComponentContext(), nullptr ) );
636 css::uno::Reference
< css::ucb::XProgressHandler
> xProgress
;
637 rtl::Reference
<::ucbhelper::CommandEnvironment
> pCommandEnv
= new ::ucbhelper::CommandEnvironment( new comphelper::SimpleFileAccessInteraction( xIH
), xProgress
);
639 const SfxUnoAnyItem
* pItem
= SfxItemSet::GetItem
<SfxUnoAnyItem
>(pImpl
->m_pSet
.get(), SID_CONTENT
, false);
641 pItem
->GetValue() >>= xContent
;
647 pImpl
->aContent
= ::ucbhelper::Content( xContent
, pCommandEnv
, comphelper::getProcessComponentContext() );
649 catch ( const Exception
& )
655 // TODO: SAL_WARN( "sfx.doc", "SfxMedium::GetContent()\nCreate Content? This code exists as fallback only. Please clarify, why it's used.");
657 if ( !pImpl
->m_aName
.isEmpty() )
658 osl::FileBase::getFileURLFromSystemPath( pImpl
->m_aName
, aURL
);
659 else if ( !pImpl
->m_aLogicName
.isEmpty() )
660 aURL
= GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE
);
661 if (!aURL
.isEmpty() )
662 (void)::ucbhelper::Content::create( aURL
, pCommandEnv
, comphelper::getProcessComponentContext(), pImpl
->aContent
);
666 return pImpl
->aContent
.get();
669 OUString
SfxMedium::GetBaseURL( bool bForSaving
)
673 bool bIsRemote
= IsRemote();
674 if ((bIsRemote
&& !officecfg::Office::Common::Save::URL::Internet::get())
675 || (!bIsRemote
&& !officecfg::Office::Common::Save::URL::FileSystem::get()))
679 if (const SfxStringItem
* pBaseURLItem
= GetItemSet().GetItem
<SfxStringItem
>(SID_DOC_BASEURL
))
680 return pBaseURLItem
->GetValue();
683 if (!utl::ConfigManager::IsFuzzing() && GetContent().is())
687 Any aAny
= pImpl
->aContent
.getPropertyValue("BaseURI");
690 catch ( const css::uno::Exception
& )
694 if ( aBaseURL
.isEmpty() )
695 aBaseURL
= GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE
);
700 bool SfxMedium::IsSkipImages() const
702 const SfxStringItem
* pSkipImagesItem
= GetItemSet().GetItem
<SfxStringItem
>(SID_FILE_FILTEROPTIONS
);
703 return pSkipImagesItem
&& pSkipImagesItem
->GetValue() == "SkipImages";
706 SvStream
* SfxMedium::GetInStream()
708 if ( pImpl
->m_pInStream
)
709 return pImpl
->m_pInStream
.get();
711 if ( pImpl
->pTempFile
)
713 pImpl
->m_pInStream
.reset( new SvFileStream(pImpl
->m_aName
, pImpl
->m_nStorOpenMode
) );
715 pImpl
->m_eError
= pImpl
->m_pInStream
->GetError();
717 if (!pImpl
->m_eError
&& (pImpl
->m_nStorOpenMode
& StreamMode::WRITE
)
718 && ! pImpl
->m_pInStream
->IsWritable() )
720 pImpl
->m_eError
= ERRCODE_IO_ACCESSDENIED
;
721 pImpl
->m_pInStream
.reset();
724 return pImpl
->m_pInStream
.get();
729 if ( GetErrorIgnoreWarning() )
732 return pImpl
->m_pInStream
.get();
736 void SfxMedium::CloseInStream()
738 CloseInStream_Impl();
741 void SfxMedium::CloseInStream_Impl(bool bInDestruction
)
743 // if there is a storage based on the InStream, we have to
744 // close the storage, too, because otherwise the storage
745 // would use an invalid ( deleted ) stream.
746 if ( pImpl
->m_pInStream
&& pImpl
->xStorage
.is() )
748 if ( pImpl
->bStorageBasedOnInStream
)
752 if ( pImpl
->m_pInStream
&& !GetContent().is() && !bInDestruction
)
758 pImpl
->m_pInStream
.reset();
760 pImpl
->m_pSet
->ClearItem( SID_INPUTSTREAM
);
762 CloseZipStorage_Impl();
763 pImpl
->xInputStream
.clear();
765 if ( !pImpl
->m_pOutStream
)
767 // output part of the stream is not used so the whole stream can be closed
768 // TODO/LATER: is it correct?
769 pImpl
->xStream
.clear();
771 pImpl
->m_pSet
->ClearItem( SID_STREAM
);
776 SvStream
* SfxMedium::GetOutStream()
778 if ( !pImpl
->m_pOutStream
)
780 // Create a temp. file if there is none because we always
782 CreateTempFile( false );
784 if ( pImpl
->pTempFile
)
786 // On windows we try to re-use XOutStream from xStream if that exists;
787 // because opening new SvFileStream in this situation may fail with ERROR_SHARING_VIOLATION
788 // TODO: this is a horrible hack that should probably be removed,
789 // somebody needs to investigate this more thoroughly...
790 if (getenv("SFX_MEDIUM_REUSE_STREAM") && pImpl
->xStream
.is())
792 assert(pImpl
->xStream
->getOutputStream().is()); // need that...
793 pImpl
->m_pOutStream
= utl::UcbStreamHelper::CreateStream(
794 pImpl
->xStream
, false);
798 // On Unix don't try to re-use XOutStream from xStream if that exists;
799 // it causes fdo#59022 (fails opening files via SMB on Linux)
800 pImpl
->m_pOutStream
.reset( new SvFileStream(
801 pImpl
->m_aName
, StreamMode::STD_READWRITE
) );
807 return pImpl
->m_pOutStream
.get();
811 void SfxMedium::CloseOutStream()
813 CloseOutStream_Impl();
816 void SfxMedium::CloseOutStream_Impl()
818 if ( pImpl
->m_pOutStream
)
820 // if there is a storage based on the OutStream, we have to
821 // close the storage, too, because otherwise the storage
822 // would use an invalid ( deleted ) stream.
823 //TODO/MBA: how to deal with this?!
824 //maybe we need a new flag when the storage was created from the outstream
825 if ( pImpl
->xStorage
.is() )
830 pImpl
->m_pOutStream
.reset();
833 if ( !pImpl
->m_pInStream
)
835 // input part of the stream is not used so the whole stream can be closed
836 // TODO/LATER: is it correct?
837 pImpl
->xStream
.clear();
839 pImpl
->m_pSet
->ClearItem( SID_STREAM
);
844 const OUString
& SfxMedium::GetPhysicalName() const
846 if ( pImpl
->m_aName
.isEmpty() && !pImpl
->m_aLogicName
.isEmpty() )
847 const_cast<SfxMedium
*>(this)->CreateFileStream();
849 // return the name then
850 return pImpl
->m_aName
;
854 void SfxMedium::CreateFileStream()
857 if( pImpl
->m_pInStream
)
859 SvLockBytes
* pBytes
= pImpl
->m_pInStream
->GetLockBytes();
861 pBytes
->SetSynchronMode();
865 if( pImpl
->m_pInStream
)
867 CreateTempFile( false );
868 pImpl
->bIsTemp
= true;
869 CloseInStream_Impl();
874 bool SfxMedium::Commit()
876 if( pImpl
->xStorage
.is() )
877 StorageCommit_Impl();
878 else if( pImpl
->m_pOutStream
)
879 pImpl
->m_pOutStream
->FlushBuffer();
880 else if( pImpl
->m_pInStream
)
881 pImpl
->m_pInStream
->FlushBuffer();
883 if ( GetErrorIgnoreWarning() == ERRCODE_NONE
)
885 // does something only in case there is a temporary file ( means aName points to different location than aLogicName )
889 bool bResult
= ( GetErrorIgnoreWarning() == ERRCODE_NONE
);
891 if ( bResult
&& DocNeedsFileDateCheck() )
892 GetInitFileDate( true );
894 // remove truncation mode from the flags
895 pImpl
->m_nStorOpenMode
&= ~StreamMode::TRUNC
;
900 bool SfxMedium::IsStorage()
902 if ( pImpl
->xStorage
.is() )
905 if ( pImpl
->m_bTriedStorage
)
906 return pImpl
->bIsStorage
;
908 if ( pImpl
->pTempFile
)
911 if ( osl::FileBase::getFileURLFromSystemPath( pImpl
->m_aName
, aURL
)
912 != osl::FileBase::E_None
)
914 SAL_WARN( "sfx.doc", "Physical name '" << pImpl
->m_aName
<< "' not convertible to file URL");
916 pImpl
->bIsStorage
= SotStorage::IsStorageFile( aURL
) && !SotStorage::IsOLEStorage( aURL
);
917 if ( !pImpl
->bIsStorage
)
918 pImpl
->m_bTriedStorage
= true;
920 else if ( GetInStream() )
922 pImpl
->bIsStorage
= SotStorage::IsStorageFile( pImpl
->m_pInStream
.get() ) && !SotStorage::IsOLEStorage( pImpl
->m_pInStream
.get() );
923 if ( !pImpl
->m_pInStream
->GetError() && !pImpl
->bIsStorage
)
924 pImpl
->m_bTriedStorage
= true;
927 return pImpl
->bIsStorage
;
931 bool SfxMedium::IsPreview_Impl() const
933 bool bPreview
= false;
934 const SfxBoolItem
* pPreview
= GetItemSet().GetItem(SID_PREVIEW
, false);
936 bPreview
= pPreview
->GetValue();
939 const SfxStringItem
* pFlags
= GetItemSet().GetItem(SID_OPTIONS
, false);
942 OUString aFileFlags
= pFlags
->GetValue();
943 aFileFlags
= aFileFlags
.toAsciiUpperCase();
944 if ( -1 != aFileFlags
.indexOf( 'B' ) )
953 void SfxMedium::StorageBackup_Impl()
955 ::ucbhelper::Content aOriginalContent
;
956 Reference
< css::ucb::XCommandEnvironment
> xDummyEnv
;
958 bool bBasedOnOriginalFile
=
960 && ( pImpl
->m_aLogicName
.isEmpty() || !pImpl
->m_bSalvageMode
)
961 && !GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE
).isEmpty()
962 && GetURLObject().GetProtocol() == INetProtocol::File
963 && ::utl::UCBContentHelper::IsDocument( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE
) );
965 if ( bBasedOnOriginalFile
&& pImpl
->m_aBackupURL
.isEmpty()
966 && ::ucbhelper::Content::create( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE
), xDummyEnv
, comphelper::getProcessComponentContext(), aOriginalContent
) )
968 DoInternalBackup_Impl( aOriginalContent
);
969 if( pImpl
->m_aBackupURL
.isEmpty() )
970 SetError(ERRCODE_SFX_CANTCREATEBACKUP
);
975 OUString
const & SfxMedium::GetBackup_Impl()
977 if ( pImpl
->m_aBackupURL
.isEmpty() )
978 StorageBackup_Impl();
980 return pImpl
->m_aBackupURL
;
984 uno::Reference
< embed::XStorage
> SfxMedium::GetOutputStorage()
986 if ( GetErrorIgnoreWarning() )
987 return uno::Reference
< embed::XStorage
>();
989 // if the medium was constructed with a Storage: use this one, not a temp. storage
990 // if a temporary storage already exists: use it
991 if (pImpl
->xStorage
.is()
992 && (pImpl
->m_bODFWholesomeEncryption
|| pImpl
->m_aLogicName
.isEmpty() || pImpl
->pTempFile
))
994 return pImpl
->xStorage
;
997 // if necessary close stream that was used for reading
998 if ( pImpl
->m_pInStream
&& !pImpl
->m_pInStream
->IsWritable() )
1001 DBG_ASSERT( !pImpl
->m_pOutStream
, "OutStream in a readonly Medium?!" );
1003 // TODO/LATER: The current solution is to store the document temporary and then copy it to the target location;
1004 // in future it should be stored directly and then copied to the temporary location, since in this case no
1005 // file attributes have to be preserved and system copying mechanics could be used instead of streaming.
1006 CreateTempFileNoCopy();
1008 return GetStorage();
1012 bool SfxMedium::SetEncryptionDataToStorage_Impl()
1014 // in case media-descriptor contains password it should be used on opening
1015 if ( !pImpl
->xStorage
.is() || !pImpl
->m_pSet
)
1018 uno::Sequence
< beans::NamedValue
> aEncryptionData
;
1019 if ( !GetEncryptionData_Impl( pImpl
->m_pSet
.get(), aEncryptionData
) )
1022 // replace the password with encryption data
1023 pImpl
->m_pSet
->ClearItem( SID_PASSWORD
);
1024 pImpl
->m_pSet
->Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA
, uno::Any( aEncryptionData
) ) );
1028 ::comphelper::OStorageHelper::SetCommonStorageEncryptionData( pImpl
->xStorage
, aEncryptionData
);
1030 catch( const uno::Exception
& )
1032 SAL_WARN( "sfx.doc", "It must be possible to set a common password for the storage" );
1033 SetError(ERRCODE_IO_GENERAL
);
1039 #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
1041 // FIXME: Hmm actually lock files should be used for sftp: documents
1042 // even if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT. Only the use of lock
1043 // files for *local* documents is unnecessary in that case. But
1044 // actually, the checks for sftp: here are just wishful thinking; I
1045 // don't this there is any support for actually editing documents
1046 // behind sftp: URLs anyway.
1048 // Sure, there could perhaps be a 3rd-party extension that brings UCB
1049 // the potential to handle files behind sftp:. But there could also be
1050 // an extension that handles some arbitrary foobar: scheme *and* it
1051 // could be that lock files would be the correct thing to use for
1052 // foobar: documents, too. But the hardcoded test below won't know
1053 // that. Clearly the knowledge whether lock files should be used or
1054 // not for some URL scheme belongs in UCB, not here.
1059 OUString
tryMSOwnerFiles(std::u16string_view sDocURL
)
1061 svt::MSODocumentLockFile
aMSOLockFile(sDocURL
);
1062 LockFileEntry aData
;
1065 aData
= aMSOLockFile
.GetLockData();
1067 catch( const uno::Exception
& )
1072 OUString sUserData
= aData
[LockFileComponent::OOOUSERNAME
];
1074 if (!sUserData
.isEmpty())
1075 sUserData
+= " (MS Office)"; // Mention the used office suite
1080 OUString
tryForeignLockfiles(std::u16string_view sDocURL
)
1082 OUString sUserData
= tryMSOwnerFiles(sDocURL
);
1083 // here we can test for empty result, and add other known applications' lockfile testing
1084 return sUserData
.trim();
1088 SfxMedium::ShowLockResult
SfxMedium::ShowLockedDocumentDialog(const LockFileEntry
& aData
,
1089 bool bIsLoading
, bool bOwnLock
,
1090 bool bHandleSysLocked
)
1092 ShowLockResult nResult
= ShowLockResult::NoLock
;
1094 // tdf#92817: Simple check for empty lock file that needs to be deleted, when system locking is enabled
1095 if( aData
[LockFileComponent::OOOUSERNAME
].isEmpty() && aData
[LockFileComponent::SYSUSERNAME
].isEmpty() && !bHandleSysLocked
)
1098 // show the interaction regarding the document opening
1099 uno::Reference
< task::XInteractionHandler
> xHandler
= GetInteractionHandler();
1101 if ( xHandler
.is() && ( bIsLoading
|| !bHandleSysLocked
|| bOwnLock
) )
1103 OUString aDocumentURL
1104 = GetURLObject().GetLastName(INetURLObject::DecodeMechanism::WithCharset
);
1106 ::rtl::Reference
< ::ucbhelper::InteractionRequest
> xInteractionRequestImpl
;
1108 sal_Int32 nContinuations
= 3;
1112 aInfo
= aData
[LockFileComponent::EDITTIME
];
1114 xInteractionRequestImpl
= new ::ucbhelper::InteractionRequest( uno::Any(
1115 document::OwnLockOnDocumentRequest( OUString(), uno::Reference
< uno::XInterface
>(), aDocumentURL
, aInfo
, !bIsLoading
) ) );
1119 // Use a fourth continuation in case there's no filesystem lock:
1120 // "Ignore lock file and open/replace the document"
1121 if (!bHandleSysLocked
)
1124 if ( !aData
[LockFileComponent::OOOUSERNAME
].isEmpty() )
1125 aInfo
= aData
[LockFileComponent::OOOUSERNAME
];
1127 aInfo
= aData
[LockFileComponent::SYSUSERNAME
];
1129 if (aInfo
.isEmpty() && !GetURLObject().isAnyKnownWebDAVScheme())
1130 // Try to get name of user who has locked the file using other applications
1131 aInfo
= tryForeignLockfiles(
1132 GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::NONE
));
1134 if ( !aInfo
.isEmpty() && !aData
[LockFileComponent::EDITTIME
].isEmpty() )
1135 aInfo
+= " ( " + aData
[LockFileComponent::EDITTIME
] + " )";
1137 if (!bIsLoading
) // so, !bHandleSysLocked
1139 xInteractionRequestImpl
= new ::ucbhelper::InteractionRequest(uno::Any(
1140 document::LockedOnSavingRequest(OUString(), uno::Reference
< uno::XInterface
>(), aDocumentURL
, aInfo
)));
1141 // Currently, only the last "Retry" continuation (meaning ignore the lock and try overwriting) can be returned.
1143 else /*logically therefore bIsLoading is set */
1145 xInteractionRequestImpl
= new ::ucbhelper::InteractionRequest( uno::Any(
1146 document::LockedDocumentRequest( OUString(), uno::Reference
< uno::XInterface
>(), aDocumentURL
, aInfo
) ) );
1150 uno::Sequence
< uno::Reference
< task::XInteractionContinuation
> > aContinuations(nContinuations
);
1151 auto pContinuations
= aContinuations
.getArray();
1152 pContinuations
[0] = new ::ucbhelper::InteractionAbort( xInteractionRequestImpl
.get() );
1153 pContinuations
[1] = new ::ucbhelper::InteractionApprove( xInteractionRequestImpl
.get() );
1154 pContinuations
[2] = new ::ucbhelper::InteractionDisapprove( xInteractionRequestImpl
.get() );
1155 if (nContinuations
> 3)
1157 // We use InteractionRetry to reflect that user wants to
1158 // ignore the (stale?) alien lock file and open/overwrite the document
1159 pContinuations
[3] = new ::ucbhelper::InteractionRetry(xInteractionRequestImpl
.get());
1161 xInteractionRequestImpl
->setContinuations( aContinuations
);
1163 xHandler
->handle( xInteractionRequestImpl
);
1165 bool bOpenReadOnly
= false;
1166 ::rtl::Reference
< ::ucbhelper::InteractionContinuation
> xSelected
= xInteractionRequestImpl
->getSelection();
1167 if ( uno::Reference
< task::XInteractionAbort
>( xSelected
.get(), uno::UNO_QUERY
).is() )
1169 SetError(ERRCODE_ABORT
);
1171 else if ( uno::Reference
< task::XInteractionDisapprove
>( xSelected
.get(), uno::UNO_QUERY
).is() )
1173 // own lock on loading, user has selected to ignore the lock
1174 // own lock on saving, user has selected to ignore the lock
1175 // alien lock on loading, user has selected to edit a copy of document
1176 // TODO/LATER: alien lock on saving, user has selected to do SaveAs to different location
1177 if ( !bOwnLock
) // bIsLoading implied from outermost condition
1179 // means that a copy of the document should be opened
1180 GetItemSet().Put( SfxBoolItem( SID_TEMPLATE
, true ) );
1183 nResult
= ShowLockResult::Succeeded
;
1185 else if (uno::Reference
< task::XInteractionRetry
>(xSelected
.get(), uno::UNO_QUERY
).is())
1187 // User decided to ignore the alien (stale?) lock file without filesystem lock
1188 nResult
= ShowLockResult::Succeeded
;
1190 else if (uno::Reference
< task::XInteractionApprove
>( xSelected
.get(), uno::UNO_QUERY
).is())
1192 bOpenReadOnly
= true;
1194 else // user selected "Notify"
1196 pImpl
->m_bNotifyWhenEditable
= true;
1197 AddToCheckEditableWorkerList();
1198 bOpenReadOnly
= true;
1203 // own lock on loading, user has selected to open readonly
1204 // own lock on saving, user has selected to open readonly
1205 // alien lock on loading, user has selected to retry saving
1206 // TODO/LATER: alien lock on saving, user has selected to retry saving
1209 GetItemSet().Put(SfxBoolItem(SID_DOC_READONLY
, true));
1211 nResult
= ShowLockResult::Try
;
1218 // if no interaction handler is provided the default answer is open readonly
1219 // that usually happens in case the document is loaded per API
1220 // so the document must be opened readonly for backward compatibility
1221 GetItemSet().Put( SfxBoolItem( SID_DOC_READONLY
, true ) );
1224 SetError(ERRCODE_IO_ACCESSDENIED
);
1231 bool SfxMedium::ShowLockFileProblemDialog(MessageDlg nWhichDlg
)
1233 // system file locking is not active, ask user whether he wants to open the document without any locking
1234 uno::Reference
< task::XInteractionHandler
> xHandler
= GetInteractionHandler();
1238 ::rtl::Reference
< ::ucbhelper::InteractionRequest
> xIgnoreRequestImpl
;
1242 case MessageDlg::LockFileIgnore
:
1243 xIgnoreRequestImpl
= new ::ucbhelper::InteractionRequest(uno::Any( document::LockFileIgnoreRequest() ));
1245 case MessageDlg::LockFileCorrupt
:
1246 xIgnoreRequestImpl
= new ::ucbhelper::InteractionRequest(uno::Any( document::LockFileCorruptRequest() ));
1250 uno::Sequence
< uno::Reference
< task::XInteractionContinuation
> > aContinuations
{
1251 new ::ucbhelper::InteractionAbort(xIgnoreRequestImpl
.get()),
1252 new ::ucbhelper::InteractionApprove(xIgnoreRequestImpl
.get())
1254 xIgnoreRequestImpl
->setContinuations(aContinuations
);
1256 xHandler
->handle(xIgnoreRequestImpl
);
1258 ::rtl::Reference
< ::ucbhelper::InteractionContinuation
> xSelected
= xIgnoreRequestImpl
->getSelection();
1259 bool bReadOnly
= true;
1261 if (uno::Reference
<task::XInteractionAbort
>(xSelected
.get(), uno::UNO_QUERY
).is())
1263 SetError(ERRCODE_ABORT
);
1266 else if (!uno::Reference
<task::XInteractionApprove
>(xSelected
.get(), uno::UNO_QUERY
).is())
1268 // user selected "Notify"
1269 pImpl
->m_bNotifyWhenEditable
= true;
1270 AddToCheckEditableWorkerList();
1274 GetItemSet().Put(SfxBoolItem(SID_DOC_READONLY
, true));
1284 bool isSuitableProtocolForLocking(const OUString
& rLogicName
)
1286 INetURLObject
aUrl( rLogicName
);
1287 INetProtocol eProt
= aUrl
.GetProtocol();
1288 #if !HAVE_FEATURE_MACOSX_SANDBOX
1289 if (eProt
== INetProtocol::File
) {
1293 return eProt
== INetProtocol::Smb
|| eProt
== INetProtocol::Sftp
;
1300 // for LOCK request, suppress dialog on 403, typically indicates read-only
1301 // document and there's a 2nd dialog prompting to open a copy anyway
1302 class LockInteractionHandler
: public ::cppu::WeakImplHelper
<task::XInteractionHandler
>
1305 uno::Reference
<task::XInteractionHandler
> m_xHandler
;
1308 explicit LockInteractionHandler(uno::Reference
<task::XInteractionHandler
> const& xHandler
)
1309 : m_xHandler(xHandler
)
1313 virtual void SAL_CALL
handle(uno::Reference
<task::XInteractionRequest
> const& xRequest
) override
1315 ucb::InteractiveNetworkWriteException readException
;
1316 ucb::InteractiveNetworkReadException writeException
;
1317 if ((xRequest
->getRequest() >>= readException
)
1318 || (xRequest
->getRequest() >>= writeException
))
1320 return; // 403 gets reported as one of these; ignore to avoid dialog
1322 m_xHandler
->handle(xRequest
);
1328 #endif // HAVE_FEATURE_MULTIUSER_ENVIRONMENT
1330 // sets SID_DOC_READONLY if the document cannot be opened for editing
1331 // if user cancel the loading the ERROR_ABORT is set
1332 SfxMedium::LockFileResult
SfxMedium::LockOrigFileOnDemand(bool bLoading
, bool bNoUI
,
1333 bool bTryIgnoreLockFile
,
1334 LockFileEntry
* pLockData
)
1336 #if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT
1339 (void) bTryIgnoreLockFile
;
1341 return LockFileResult::Succeeded
;
1343 LockFileResult eResult
= LockFileResult::Failed
;
1345 // check if path scheme is http:// or https://
1346 // may be this is better if used always, in Android and iOS as well?
1347 // if this code should be always there, remember to move the relevant code in UnlockFile method as well !
1349 if ( GetURLObject().isAnyKnownWebDAVScheme() )
1351 // do nothing if WebDAV locking is disabled
1352 if (!IsWebDAVLockingUsed())
1353 return LockFileResult::Succeeded
;
1356 bool bResult
= pImpl
->m_bLocked
;
1357 bool bIsTemplate
= false;
1358 // so, this is webdav stuff...
1361 // no read-write access is necessary on loading if the document is explicitly opened as copy
1362 const SfxBoolItem
* pTemplateItem
= GetItemSet().GetItem(SID_TEMPLATE
, false);
1363 bIsTemplate
= ( bLoading
&& pTemplateItem
&& pTemplateItem
->GetValue() );
1366 if ( !bIsTemplate
&& !bResult
&& !IsReadOnly() )
1368 ShowLockResult bUIStatus
= ShowLockResult::NoLock
;
1373 uno::Reference
< task::XInteractionHandler
> xCHandler
= GetInteractionHandler( true );
1374 // Dialog with error is superfluous:
1375 // on loading, will result in read-only with infobar.
1376 // bNoUI case for Reload failing, will open dialog later.
1377 if (bLoading
|| bNoUI
)
1379 xCHandler
= new LockInteractionHandler(xCHandler
);
1381 Reference
< css::ucb::XCommandEnvironment
> xComEnv
= new ::ucbhelper::CommandEnvironment(
1382 xCHandler
, Reference
< css::ucb::XProgressHandler
>() );
1384 ucbhelper::Content
aContentToLock(
1385 GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE
),
1386 xComEnv
, comphelper::getProcessComponentContext() );
1390 aContentToLock
.lock();
1393 catch ( ucb::InteractiveLockingLockedException
& )
1395 // received when the resource is already locked
1396 if (!bNoUI
|| pLockData
)
1398 // get the lock owner, using a special ucb.webdav property
1399 // the owner property retrieved here is what the other principal send the server
1400 // when activating the lock.
1401 // See http://tools.ietf.org/html/rfc4918#section-14.17 for details
1402 LockFileEntry aLockData
;
1403 aLockData
[LockFileComponent::OOOUSERNAME
] = "Unknown user";
1404 // This solution works right when the LO user name and the WebDAV user
1405 // name are the same.
1406 // A better thing to do would be to obtain the 'real' WebDAV user name,
1407 // but that's not possible from a WebDAV UCP provider client.
1408 LockFileEntry aOwnData
= svt::LockFileCommon::GenerateOwnEntry();
1409 // use the current LO user name as the system name
1410 aLockData
[LockFileComponent::SYSUSERNAME
]
1411 = aOwnData
[LockFileComponent::SYSUSERNAME
];
1413 uno::Sequence
<css::ucb::Lock
> aLocks
;
1414 // getting the property, send a PROPFIND to the server over the net
1415 if ((aContentToLock
.getPropertyValue("DAV:lockdiscovery") >>= aLocks
) && aLocks
.hasElements())
1417 // got at least a lock, show the owner of the first lock returned
1418 css::ucb::Lock aLock
= aLocks
[0];
1420 if (aLock
.Owner
>>= aOwner
)
1422 // we need to display the WebDAV user name owning the lock, not the local one
1423 aLockData
[LockFileComponent::OOOUSERNAME
] = aOwner
;
1429 bUIStatus
= ShowLockedDocumentDialog(aLockData
, bLoading
, false,
1435 std::copy(aLockData
.begin(), aLockData
.end(), pLockData
->begin());
1439 catch( ucb::InteractiveNetworkWriteException
& )
1441 // This catch it's not really needed, here just for the sake of documentation on the behaviour.
1442 // This is the most likely reason:
1443 // - the remote site is a WebDAV with special configuration: read/only for read operations
1444 // and read/write for write operations, the user is not allowed to lock/write and
1445 // she cancelled the credentials request.
1446 // this is not actually an error, but the exception is sent directly from ucb, avoiding the automatic
1447 // management that takes part in cancelCommandExecution()
1448 // Unfortunately there is no InteractiveNetwork*Exception available to signal this more correctly
1449 // since it mostly happens on read/only part of webdav, this can be the most correct
1450 // exception available
1452 catch( uno::Exception
& )
1454 TOOLS_WARN_EXCEPTION( "sfx.doc", "Locking exception: WebDAV while trying to lock the file" );
1457 } while( !bResult
&& bUIStatus
== ShowLockResult::Try
);
1460 pImpl
->m_bLocked
= bResult
;
1462 if ( !bResult
&& GetErrorIgnoreWarning() == ERRCODE_NONE
)
1464 // the error should be set in case it is storing process
1465 // or the document has been opened for editing explicitly
1466 const SfxBoolItem
* pReadOnlyItem
= SfxItemSet::GetItem
<SfxBoolItem
>(pImpl
->m_pSet
.get(), SID_DOC_READONLY
, false);
1468 if ( !bLoading
|| (pReadOnlyItem
&& !pReadOnlyItem
->GetValue()) )
1469 SetError(ERRCODE_IO_ACCESSDENIED
);
1471 GetItemSet().Put( SfxBoolItem( SID_DOC_READONLY
, true ) );
1474 // when the file is locked, get the current file date
1475 if ( bResult
&& DocNeedsFileDateCheck() )
1476 GetInitFileDate( true );
1479 eResult
= LockFileResult::Succeeded
;
1484 if (!IsLockingUsed())
1485 return LockFileResult::Succeeded
;
1486 if (GetURLObject().HasError())
1491 if ( pImpl
->m_bLocked
&& bLoading
1492 && GetURLObject().GetProtocol() == INetProtocol::File
)
1494 // if the document is already locked the system locking might be temporarily off after storing
1495 // check whether the system file locking should be taken again
1496 GetLockingStream_Impl();
1499 bool bResult
= pImpl
->m_bLocked
;
1503 // no read-write access is necessary on loading if the document is explicitly opened as copy
1504 const SfxBoolItem
* pTemplateItem
= GetItemSet().GetItem(SID_TEMPLATE
, false);
1505 bResult
= ( bLoading
&& pTemplateItem
&& pTemplateItem
->GetValue() );
1508 if ( !bResult
&& !IsReadOnly() )
1510 bool bContentReadonly
= false;
1511 if ( bLoading
&& GetURLObject().GetProtocol() == INetProtocol::File
)
1513 // let the original document be opened to check the possibility to open it for editing
1514 // and to let the writable stream stay open to hold the lock on the document
1515 GetLockingStream_Impl();
1518 // "IsReadOnly" property does not allow to detect whether the file is readonly always
1519 // so we try always to open the file for editing
1520 // the file is readonly only in case the read-write stream can not be opened
1521 if ( bLoading
&& !pImpl
->m_xLockingStream
.is() )
1525 // MediaDescriptor does this check also, the duplication should be avoided in future
1526 Reference
< css::ucb::XCommandEnvironment
> xDummyEnv
;
1527 ::ucbhelper::Content
aContent( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE
), xDummyEnv
, comphelper::getProcessComponentContext() );
1528 aContent
.getPropertyValue("IsReadOnly") >>= bContentReadonly
;
1530 catch( const uno::Exception
& ) {}
1533 // do further checks only if the file not readonly in fs
1534 if ( !bContentReadonly
)
1536 // the special file locking should be used only for suitable URLs
1537 if ( isSuitableProtocolForLocking( pImpl
->m_aLogicName
) )
1540 // in case of storing the document should request the output before locking
1543 // let the stream be opened to check the system file locking
1545 if (GetErrorIgnoreWarning() != ERRCODE_NONE
) {
1550 ShowLockResult bUIStatus
= ShowLockResult::NoLock
;
1552 // check whether system file locking has been used, the default value is false
1553 bool bUseSystemLock
= comphelper::isFileUrl( pImpl
->m_aLogicName
) && IsSystemFileLockingUsed();
1555 // TODO/LATER: This implementation does not allow to detect the system lock on saving here, actually this is no big problem
1556 // if system lock is used the writeable stream should be available
1557 bool bHandleSysLocked
= ( bLoading
&& bUseSystemLock
&& !pImpl
->xStream
.is() && !pImpl
->m_pOutStream
);
1559 // The file is attempted to get locked for the duration of lockfile creation on save
1560 std::unique_ptr
<osl::File
> pFileLock
;
1561 if (!bLoading
&& bUseSystemLock
&& pImpl
->pTempFile
)
1563 INetURLObject
aDest(GetURLObject());
1564 OUString
aDestURL(aDest
.GetMainURL(INetURLObject::DecodeMechanism::NONE
));
1566 if (comphelper::isFileUrl(aDestURL
) || !aDest
.removeSegment())
1568 pFileLock
= std::make_unique
<osl::File
>(aDestURL
);
1569 auto rc
= pFileLock
->open(osl_File_OpenFlag_Write
);
1570 if (rc
== osl::FileBase::E_ACCES
)
1571 bHandleSysLocked
= true;
1579 ::svt::DocumentLockFile
aLockFile( pImpl
->m_aLogicName
);
1581 std::unique_ptr
<svt::MSODocumentLockFile
> pMSOLockFile
;
1582 const SvtFilterOptions
& rOpt
= SvtFilterOptions::Get();
1583 if (rOpt
.IsMSOLockFileCreationIsEnabled() && svt::MSODocumentLockFile::IsMSOSupportedFileFormat(pImpl
->m_aLogicName
))
1585 pMSOLockFile
.reset(new svt::MSODocumentLockFile(pImpl
->m_aLogicName
));
1586 pImpl
->m_bMSOLockFileCreated
= true;
1589 bool bIoErr
= false;
1591 if (!bHandleSysLocked
)
1595 bResult
= aLockFile
.CreateOwnLockFile();
1597 bResult
&= pMSOLockFile
->CreateOwnLockFile();
1599 catch (const uno::Exception
&)
1601 if (tools::IsMappedWebDAVPath(GetURLObject().GetMainURL(
1602 INetURLObject::DecodeMechanism::NONE
)))
1604 // This is a path that redirects to a WebDAV resource;
1605 // so failure creating lockfile is not an error here.
1608 else if (bLoading
&& !bNoUI
)
1611 ShowLockFileProblemDialog(MessageDlg::LockFileIgnore
);
1612 bResult
= true; // always delete the defect lock-file
1616 // in case OOo locking is turned off the lock file is still written if possible
1617 // but it is ignored while deciding whether the document should be opened for editing or not
1618 if (!bResult
&& !IsOOoLockFileUsed() && !bIoErr
)
1621 // take the ownership over the lock file
1622 aLockFile
.OverwriteOwnLockFile();
1625 pMSOLockFile
->OverwriteOwnLockFile();
1631 LockFileEntry aData
;
1634 aData
= aLockFile
.GetLockData();
1636 catch (const io::WrongFormatException
&)
1638 // we get empty or corrupt data
1640 if (!bIoErr
&& bLoading
&& !bNoUI
)
1641 bResult
= ShowLockFileProblemDialog(MessageDlg::LockFileCorrupt
);
1643 // not show the Lock Document Dialog
1646 catch( const uno::Exception
& )
1648 // show the Lock Document Dialog, when locked from other app
1649 bIoErr
= !bHandleSysLocked
;
1652 bool bOwnLock
= false;
1654 if (!bHandleSysLocked
)
1656 LockFileEntry aOwnData
= svt::LockFileCommon::GenerateOwnEntry();
1657 bOwnLock
= aOwnData
[LockFileComponent::SYSUSERNAME
] == aData
[LockFileComponent::SYSUSERNAME
];
1660 && aOwnData
[LockFileComponent::LOCALHOST
] == aData
[LockFileComponent::LOCALHOST
]
1661 && aOwnData
[LockFileComponent::USERURL
] == aData
[LockFileComponent::USERURL
])
1663 // this is own lock from the same installation, it could remain because of crash
1668 if ( !bResult
&& !bIoErr
)
1671 bUIStatus
= ShowLockedDocumentDialog(
1672 aData
, bLoading
, bOwnLock
, bHandleSysLocked
);
1673 else if (bLoading
&& bTryIgnoreLockFile
&& !bHandleSysLocked
)
1674 bUIStatus
= ShowLockResult::Succeeded
;
1676 if ( bUIStatus
== ShowLockResult::Succeeded
)
1678 // take the ownership over the lock file
1679 bResult
= aLockFile
.OverwriteOwnLockFile();
1682 pMSOLockFile
->OverwriteOwnLockFile();
1684 else if (bLoading
&& !bHandleSysLocked
)
1685 eResult
= LockFileResult::FailedLockFile
;
1687 if (!bResult
&& pLockData
)
1689 std::copy(aData
.begin(), aData
.end(), pLockData
->begin());
1694 catch( const uno::Exception
& )
1697 } while( !bResult
&& bUIStatus
== ShowLockResult::Try
);
1699 pImpl
->m_bLocked
= bResult
;
1703 // this is no file URL, check whether the file is readonly
1704 bResult
= !bContentReadonly
;
1709 AddToCheckEditableWorkerList();
1713 if ( !bResult
&& GetErrorIgnoreWarning() == ERRCODE_NONE
)
1715 // the error should be set in case it is storing process
1716 // or the document has been opened for editing explicitly
1717 const SfxBoolItem
* pReadOnlyItem
= SfxItemSet::GetItem
<SfxBoolItem
>(pImpl
->m_pSet
.get(), SID_DOC_READONLY
, false);
1719 if ( !bLoading
|| (pReadOnlyItem
&& !pReadOnlyItem
->GetValue()) )
1720 SetError(ERRCODE_IO_ACCESSDENIED
);
1722 GetItemSet().Put( SfxBoolItem( SID_DOC_READONLY
, true ) );
1725 // when the file is locked, get the current file date
1726 if ( bResult
&& DocNeedsFileDateCheck() )
1727 GetInitFileDate( true );
1730 eResult
= LockFileResult::Succeeded
;
1732 catch( const uno::Exception
& )
1734 TOOLS_WARN_EXCEPTION( "sfx.doc", "Locking exception: high probability, that the content has not been created" );
1741 // this either returns non-null or throws exception
1742 uno::Reference
<embed::XStorage
>
1743 SfxMedium::TryEncryptedInnerPackage(uno::Reference
<embed::XStorage
> const xStorage
)
1745 uno::Reference
<embed::XStorage
> xRet
;
1746 if (xStorage
->hasByName("encrypted-package"))
1748 uno::Reference
<io::XStream
> const
1749 xDecryptedInnerPackage
= xStorage
->openStreamElement(
1750 "encrypted-package",
1751 embed::ElementModes::READ
| embed::ElementModes::NOCREATE
);
1752 // either this throws due to wrong password or IO error, or returns stream
1753 assert(xDecryptedInnerPackage
.is());
1754 // need a seekable stream => copy
1755 Reference
<uno::XComponentContext
> const xContext(::comphelper::getProcessComponentContext());
1756 uno::Reference
<io::XStream
> const xDecryptedInnerPackageStream(
1757 xContext
->getServiceManager()->createInstanceWithContext(
1758 "com.sun.star.comp.MemoryStream", xContext
),
1760 comphelper::OStorageHelper::CopyInputToOutput(xDecryptedInnerPackage
->getInputStream(), xDecryptedInnerPackageStream
->getOutputStream());
1761 xDecryptedInnerPackageStream
->getOutputStream()->closeOutput();
1763 // debug: dump to temp file
1764 uno::Reference
<io::XTempFile
> const xTempFile(io::TempFile::create(xContext
), uno::UNO_SET_THROW
);
1765 xTempFile
->setRemoveFile(false);
1766 comphelper::OStorageHelper::CopyInputToOutput(xDecryptedInnerPackageStream
->getInputStream(), xTempFile
->getOutputStream());
1767 xTempFile
->getOutputStream()->closeOutput();
1768 SAL_DE
BUG("AAA tempfile " << xTempFile
->getResourceName());
1769 uno::Reference
<io::XSeekable
>(xDecryptedInnerPackageStream
, uno::UNO_QUERY_THROW
)->seek(0);
1771 // create inner storage; opening the stream should have already verified
1772 // the password so any failure here is probably due to a bug
1773 xRet
= ::comphelper::OStorageHelper::GetStorageOfFormatFromStream(
1774 PACKAGE_STORAGE_FORMAT_STRING
, xDecryptedInnerPackageStream
,
1775 embed::ElementModes::READWRITE
, xContext
, false);
1777 // consistency check: outer and inner package must have same mimetype
1778 OUString
const outerMediaType(uno::Reference
<beans::XPropertySet
>(pImpl
->xStorage
,
1779 uno::UNO_QUERY_THROW
)->getPropertyValue("MediaType").get
<OUString
>());
1780 OUString
const innerMediaType(uno::Reference
<beans::XPropertySet
>(xRet
,
1781 uno::UNO_QUERY_THROW
)->getPropertyValue("MediaType").get
<OUString
>());
1782 if (outerMediaType
.isEmpty() || outerMediaType
!= innerMediaType
)
1784 throw io::WrongFormatException("MediaType inconsistent in encrypted ODF package");
1787 pImpl
->m_bODFWholesomeEncryption
= true;
1788 pImpl
->m_xODFDecryptedInnerPackageStream
= xDecryptedInnerPackageStream
;
1789 pImpl
->m_xODFEncryptedOuterStorage
= xStorage
;
1790 pImpl
->xStorage
= xRet
;
1795 uno::Reference
< embed::XStorage
> SfxMedium::GetStorage( bool bCreateTempFile
)
1797 if ( pImpl
->xStorage
.is() || pImpl
->m_bTriedStorage
)
1798 return pImpl
->xStorage
;
1800 uno::Sequence
< uno::Any
> aArgs( 2 );
1801 auto pArgs
= aArgs
.getArray();
1803 // the medium should be retrieved before temporary file creation
1804 // to let the MediaDescriptor be filled with the streams
1807 if ( bCreateTempFile
)
1808 CreateTempFile( false );
1812 if ( GetErrorIgnoreWarning() )
1813 return pImpl
->xStorage
;
1815 const SfxBoolItem
* pRepairItem
= GetItemSet().GetItem(SID_REPAIRPACKAGE
, false);
1816 if ( pRepairItem
&& pRepairItem
->GetValue() )
1818 // the storage should be created for repairing mode
1819 CreateTempFile( false );
1822 Reference
< css::ucb::XProgressHandler
> xProgressHandler
;
1823 Reference
< css::task::XStatusIndicator
> xStatusIndicator
;
1825 const SfxUnoAnyItem
* pxProgressItem
= GetItemSet().GetItem(SID_PROGRESS_STATUSBAR_CONTROL
, false);
1826 if( pxProgressItem
&& ( pxProgressItem
->GetValue() >>= xStatusIndicator
) )
1827 xProgressHandler
.set( new utl::ProgressHandlerWrap( xStatusIndicator
) );
1829 uno::Sequence
< beans::PropertyValue
> aAddProps
{
1830 comphelper::makePropertyValue("RepairPackage", true),
1831 comphelper::makePropertyValue("StatusIndicator", xProgressHandler
)
1834 // the first arguments will be filled later
1836 pArgs
= aArgs
.getArray();
1837 pArgs
[2] <<= aAddProps
;
1840 if ( pImpl
->xStream
.is() )
1842 // since the storage is based on temporary stream we open it always read-write
1843 pArgs
[0] <<= pImpl
->xStream
;
1844 pArgs
[1] <<= embed::ElementModes::READWRITE
;
1845 pImpl
->bStorageBasedOnInStream
= true;
1846 if (pImpl
->m_bDisableFileSync
)
1848 // Forward NoFileSync to the storage factory.
1849 aArgs
.realloc(3); // ??? this may re-write the data added above for pRepairItem
1850 pArgs
= aArgs
.getArray();
1851 uno::Sequence
<beans::PropertyValue
> aProperties(
1852 comphelper::InitPropertySequence({ { "NoFileSync", uno::Any(true) } }));
1853 pArgs
[2] <<= aProperties
;
1856 else if ( pImpl
->xInputStream
.is() )
1858 // since the storage is based on temporary stream we open it always read-write
1859 pArgs
[0] <<= pImpl
->xInputStream
;
1860 pArgs
[1] <<= embed::ElementModes::READ
;
1861 pImpl
->bStorageBasedOnInStream
= true;
1865 CloseStreams_Impl();
1866 pArgs
[0] <<= pImpl
->m_aName
;
1867 pArgs
[1] <<= embed::ElementModes::READ
;
1868 pImpl
->bStorageBasedOnInStream
= false;
1873 pImpl
->xStorage
.set( ::comphelper::OStorageHelper::GetStorageFactory()->createInstanceWithArguments( aArgs
),
1876 catch( const uno::Exception
& )
1878 // impossibility to create the storage is no error
1881 pImpl
->nLastStorageError
= GetErrorIgnoreWarning();
1882 if( pImpl
->nLastStorageError
!= ERRCODE_NONE
)
1884 pImpl
->xStorage
= nullptr;
1885 if ( pImpl
->m_pInStream
)
1886 pImpl
->m_pInStream
->Seek(0);
1887 return uno::Reference
< embed::XStorage
>();
1890 pImpl
->m_bTriedStorage
= true;
1892 if (pImpl
->xStorage
.is())
1894 pImpl
->m_bODFWholesomeEncryption
= false;
1895 if (SetEncryptionDataToStorage_Impl())
1899 TryEncryptedInnerPackage(pImpl
->xStorage
);
1901 catch (Exception
const&)
1903 TOOLS_WARN_EXCEPTION("sfx.doc", "exception from TryEncryptedInnerPackage: ");
1904 SetError(ERRCODE_IO_GENERAL
);
1909 if (GetErrorCode()) // decryption failed?
1911 pImpl
->xStorage
.clear();
1914 // TODO/LATER: Get versionlist on demand
1915 if ( pImpl
->xStorage
.is() )
1920 const SfxInt16Item
* pVersion
= SfxItemSet::GetItem
<SfxInt16Item
>(pImpl
->m_pSet
.get(), SID_VERSION
, false);
1922 bool bResetStorage
= false;
1923 if ( pVersion
&& pVersion
->GetValue() )
1925 // Read all available versions
1926 if ( pImpl
->aVersions
.hasElements() )
1928 // Search for the version fits the comment
1929 // The versions are numbered starting with 1, versions with
1930 // negative versions numbers are counted backwards from the
1932 short nVersion
= pVersion
->GetValue();
1934 nVersion
= static_cast<short>(pImpl
->aVersions
.getLength()) + nVersion
;
1935 else // nVersion > 0; pVersion->GetValue() != 0 was the condition to this block
1938 const util::RevisionTag
& rTag
= pImpl
->aVersions
[nVersion
];
1940 // Open SubStorage for all versions
1941 uno::Reference
< embed::XStorage
> xSub
= pImpl
->xStorage
->openStorageElement( "Versions",
1942 embed::ElementModes::READ
);
1944 DBG_ASSERT( xSub
.is(), "Version list, but no Versions!" );
1946 // There the version is stored as packed Stream
1947 uno::Reference
< io::XStream
> xStr
= xSub
->openStreamElement( rTag
.Identifier
, embed::ElementModes::READ
);
1948 std::unique_ptr
<SvStream
> pStream(utl::UcbStreamHelper::CreateStream( xStr
));
1949 if ( pStream
&& pStream
->GetError() == ERRCODE_NONE
)
1951 // Unpack Stream in TempDir
1952 const OUString aTmpName
= ::utl::CreateTempURL();
1953 SvFileStream
aTmpStream( aTmpName
, SFX_STREAM_READWRITE
);
1955 pStream
->ReadStream( aTmpStream
);
1959 // Open data as Storage
1960 pImpl
->m_nStorOpenMode
= SFX_STREAM_READONLY
;
1961 pImpl
->xStorage
= comphelper::OStorageHelper::GetStorageFromURL( aTmpName
, embed::ElementModes::READ
);
1962 pImpl
->bStorageBasedOnInStream
= false;
1964 osl::FileBase::getSystemPathFromFileURL( aTmpName
, aTemp
);
1965 SetPhysicalName_Impl( aTemp
);
1967 pImpl
->bIsTemp
= true;
1968 GetItemSet().Put( SfxBoolItem( SID_DOC_READONLY
, true ) );
1970 pImpl
->aVersions
.realloc(0);
1973 bResetStorage
= true;
1977 bResetStorage
= true;
1980 if ( bResetStorage
)
1982 pImpl
->xStorage
.clear();
1983 pImpl
->m_xODFDecryptedInnerPackageStream
.clear();
1984 pImpl
->m_xODFEncryptedOuterStorage
.clear();
1985 if ( pImpl
->m_pInStream
)
1986 pImpl
->m_pInStream
->Seek( 0 );
1989 pImpl
->bIsStorage
= pImpl
->xStorage
.is();
1990 return pImpl
->xStorage
;
1993 uno::Reference
<embed::XStorage
> SfxMedium::GetScriptingStorageToSign_Impl()
1995 // this was set when it was initially loaded
1996 if (pImpl
->m_bODFWholesomeEncryption
)
1998 // (partial) scripting signature can only be in inner storage!
1999 // Note: a "PackageFormat" storage like pImpl->xStorage doesn't work
2000 // (even if it's not encrypted) because it hides the "META-INF" dir.
2001 // This "ZipFormat" storage is used only read-only; a writable one is
2002 // created manually in SignContents_Impl().
2003 if (!pImpl
->m_xODFDecryptedInnerZipStorage
.is())
2006 // don't care about xStorage here because Zip is readonly
2007 SAL_WARN_IF(!pImpl
->m_xODFDecryptedInnerPackageStream
.is(), "sfx.doc", "no inner package stream?");
2008 if (pImpl
->m_xODFDecryptedInnerPackageStream
.is())
2010 const SfxBoolItem
* pRepairItem
= GetItemSet().GetItem(SID_REPAIRPACKAGE
, false);
2011 const bool bRepairPackage
= pRepairItem
&& pRepairItem
->GetValue();
2012 pImpl
->m_xODFDecryptedInnerZipStorage
=
2013 ::comphelper::OStorageHelper::GetStorageOfFormatFromInputStream(
2014 ZIP_STORAGE_FORMAT_STRING
,
2015 pImpl
->m_xODFDecryptedInnerPackageStream
->getInputStream(), {},
2019 return pImpl
->m_xODFDecryptedInnerZipStorage
;
2023 return GetZipStorageToSign_Impl(true);
2027 // note: currently nobody who calls this with "false" writes into an ODF
2028 // storage that is returned here, that is only for OOXML
2029 uno::Reference
< embed::XStorage
> const & SfxMedium::GetZipStorageToSign_Impl( bool bReadOnly
)
2031 if ( !GetErrorIgnoreWarning() && !pImpl
->m_xZipStorage
.is() )
2037 const SfxBoolItem
* pRepairItem
= GetItemSet().GetItem(SID_REPAIRPACKAGE
, false);
2038 const bool bRepairPackage
= pRepairItem
&& pRepairItem
->GetValue();
2039 // we can not sign document if there is no stream
2040 // should it be possible at all?
2041 if ( !bReadOnly
&& pImpl
->xStream
.is() )
2043 pImpl
->m_xZipStorage
= ::comphelper::OStorageHelper::GetStorageOfFormatFromStream(
2044 ZIP_STORAGE_FORMAT_STRING
, pImpl
->xStream
, css::embed::ElementModes::READWRITE
,
2045 {}, bRepairPackage
);
2047 else if ( pImpl
->xInputStream
.is() )
2049 pImpl
->m_xZipStorage
2050 = ::comphelper::OStorageHelper::GetStorageOfFormatFromInputStream(
2051 ZIP_STORAGE_FORMAT_STRING
, pImpl
->xInputStream
, {}, bRepairPackage
);
2054 catch( const uno::Exception
& )
2056 SAL_WARN( "sfx.doc", "No possibility to get readonly version of storage from medium!" );
2059 if ( GetErrorIgnoreWarning() ) // do not remove warnings
2063 return pImpl
->m_xZipStorage
;
2067 void SfxMedium::CloseZipStorage_Impl()
2069 if ( pImpl
->m_xZipStorage
.is() )
2072 pImpl
->m_xZipStorage
->dispose();
2073 } catch( const uno::Exception
& )
2076 pImpl
->m_xZipStorage
.clear();
2078 pImpl
->m_xODFDecryptedInnerZipStorage
.clear();
2081 void SfxMedium::CloseStorage()
2083 if ( pImpl
->xStorage
.is() )
2085 uno::Reference
< lang::XComponent
> xComp
= pImpl
->xStorage
;
2086 // in the salvage mode the medium does not own the storage
2087 if ( pImpl
->bDisposeStorage
&& !pImpl
->m_bSalvageMode
)
2091 } catch( const uno::Exception
& )
2093 SAL_WARN( "sfx.doc", "Medium's storage is already disposed!" );
2097 pImpl
->xStorage
.clear();
2098 pImpl
->m_xODFDecryptedInnerPackageStream
.clear();
2099 // pImpl->m_xODFDecryptedInnerZipStorage.clear();
2100 pImpl
->m_xODFEncryptedOuterStorage
.clear();
2101 pImpl
->bStorageBasedOnInStream
= false;
2104 pImpl
->m_bTriedStorage
= false;
2105 pImpl
->bIsStorage
= false;
2108 void SfxMedium::CanDisposeStorage_Impl( bool bDisposeStorage
)
2110 pImpl
->bDisposeStorage
= bDisposeStorage
;
2113 bool SfxMedium::WillDisposeStorageOnClose_Impl()
2115 return pImpl
->bDisposeStorage
;
2118 StreamMode
SfxMedium::GetOpenMode() const
2120 return pImpl
->m_nStorOpenMode
;
2123 void SfxMedium::SetOpenMode( StreamMode nStorOpen
,
2126 if ( pImpl
->m_nStorOpenMode
!= nStorOpen
)
2128 pImpl
->m_nStorOpenMode
= nStorOpen
;
2132 if ( pImpl
->xStorage
.is() )
2135 CloseStreams_Impl();
2141 bool SfxMedium::UseBackupToRestore_Impl( ::ucbhelper::Content
& aOriginalContent
,
2142 const Reference
< css::ucb::XCommandEnvironment
>& xComEnv
)
2146 ::ucbhelper::Content
aTransactCont( pImpl
->m_aBackupURL
, xComEnv
, comphelper::getProcessComponentContext() );
2148 Reference
< XInputStream
> aOrigInput
= aTransactCont
.openStream();
2149 aOriginalContent
.writeStream( aOrigInput
, true );
2152 catch( const Exception
& )
2154 // in case of failure here the backup file should not be removed
2155 // TODO/LATER: a message should be used to let user know about the backup
2156 pImpl
->m_bRemoveBackup
= false;
2157 // TODO/LATER: needs a specific error code
2158 pImpl
->m_eError
= ERRCODE_IO_GENERAL
;
2165 bool SfxMedium::StorageCommit_Impl()
2167 bool bResult
= false;
2168 Reference
< css::ucb::XCommandEnvironment
> xDummyEnv
;
2169 ::ucbhelper::Content aOriginalContent
;
2171 if ( pImpl
->xStorage
.is() )
2173 if ( !GetErrorIgnoreWarning() )
2175 uno::Reference
< embed::XTransactedObject
> xTrans( pImpl
->xStorage
, uno::UNO_QUERY
);
2181 CloseZipStorage_Impl();
2184 catch ( const embed::UseBackupException
& aBackupExc
)
2186 // since the temporary file is created always now, the scenario is close to be impossible
2187 if ( !pImpl
->pTempFile
)
2189 OSL_ENSURE( !pImpl
->m_aBackupURL
.isEmpty(), "No backup on storage commit!" );
2190 if ( !pImpl
->m_aBackupURL
.isEmpty()
2191 && ::ucbhelper::Content::create( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE
),
2192 xDummyEnv
, comphelper::getProcessComponentContext(),
2193 aOriginalContent
) )
2195 // use backup to restore the file
2196 // the storage has already disconnected from original location
2197 CloseAndReleaseStreams_Impl();
2198 if ( !UseBackupToRestore_Impl( aOriginalContent
, xDummyEnv
) )
2200 // connect the medium to the temporary file of the storage
2201 pImpl
->aContent
= ::ucbhelper::Content();
2202 pImpl
->m_aName
= aBackupExc
.TemporaryFileURL
;
2203 OSL_ENSURE( !pImpl
->m_aName
.isEmpty(), "The exception _must_ contain the temporary URL!" );
2208 if (!GetErrorIgnoreWarning())
2209 SetError(ERRCODE_IO_GENERAL
);
2211 catch ( const uno::Exception
& )
2213 //TODO/LATER: improve error handling
2214 SetError(ERRCODE_IO_GENERAL
);
2224 void SfxMedium::TransactedTransferForFS_Impl( const INetURLObject
& aSource
,
2225 const INetURLObject
& aDest
,
2226 const Reference
< css::ucb::XCommandEnvironment
>& xComEnv
)
2228 Reference
< css::ucb::XCommandEnvironment
> xDummyEnv
;
2229 ::ucbhelper::Content aOriginalContent
;
2233 aOriginalContent
= ::ucbhelper::Content( aDest
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), xComEnv
, comphelper::getProcessComponentContext() );
2235 catch ( const css::ucb::CommandAbortedException
& )
2237 pImpl
->m_eError
= ERRCODE_ABORT
;
2239 catch ( const css::ucb::CommandFailedException
& )
2241 pImpl
->m_eError
= ERRCODE_ABORT
;
2243 catch (const css::ucb::ContentCreationException
& ex
)
2245 pImpl
->m_eError
= ERRCODE_IO_GENERAL
;
2247 (ex
.eError
== css::ucb::ContentCreationError_NO_CONTENT_PROVIDER
) ||
2248 (ex
.eError
== css::ucb::ContentCreationError_CONTENT_CREATION_FAILED
)
2251 pImpl
->m_eError
= ERRCODE_IO_NOTEXISTSPATH
;
2254 catch (const css::uno::Exception
&)
2256 pImpl
->m_eError
= ERRCODE_IO_GENERAL
;
2259 if( pImpl
->m_eError
&& !pImpl
->m_eError
.IsWarning() )
2262 if ( pImpl
->xStorage
.is() )
2265 CloseStreams_Impl();
2267 ::ucbhelper::Content aTempCont
;
2268 if( ::ucbhelper::Content::create( aSource
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), xDummyEnv
, comphelper::getProcessComponentContext(), aTempCont
) )
2270 bool bTransactStarted
= false;
2271 const SfxBoolItem
* pOverWrite
= GetItemSet().GetItem
<SfxBoolItem
>(SID_OVERWRITE
, false);
2272 bool bOverWrite
= !pOverWrite
|| pOverWrite
->GetValue();
2273 bool bResult
= false;
2277 // tdf#60237 - if the OverWrite property of the MediaDescriptor is set to false,
2278 // try to write the file before trying to rename or copy it
2280 && ::utl::UCBContentHelper::IsDocument(
2281 aDest
.GetMainURL(INetURLObject::DecodeMechanism::NONE
))))
2283 Reference
< XInputStream
> aTempInput
= aTempCont
.openStream();
2284 aOriginalContent
.writeStream( aTempInput
, bOverWrite
);
2287 OUString aSourceMainURL
= aSource
.GetMainURL(INetURLObject::DecodeMechanism::NONE
);
2288 OUString aDestMainURL
= aDest
.GetMainURL(INetURLObject::DecodeMechanism::NONE
);
2290 sal_uInt64 nAttributes
= GetDefaultFileAttributes(aDestMainURL
);
2291 if (IsFileMovable(aDest
)
2292 && osl::File::replace(aSourceMainURL
, aDestMainURL
) == osl::FileBase::E_None
)
2295 // Adjust attributes, source might be created with
2296 // the osl_File_OpenFlag_Private flag.
2297 osl::File::setAttributes(aDestMainURL
, nAttributes
);
2302 if( pImpl
->m_aBackupURL
.isEmpty() )
2303 DoInternalBackup_Impl( aOriginalContent
);
2305 if( !pImpl
->m_aBackupURL
.isEmpty() )
2307 Reference
< XInputStream
> aTempInput
= aTempCont
.openStream();
2308 bTransactStarted
= true;
2309 aOriginalContent
.setPropertyValue( "Size", uno::Any( sal_Int64(0) ) );
2310 aOriginalContent
.writeStream( aTempInput
, bOverWrite
);
2315 pImpl
->m_eError
= ERRCODE_SFX_CANTCREATEBACKUP
;
2320 catch ( const css::ucb::CommandAbortedException
& )
2322 pImpl
->m_eError
= ERRCODE_ABORT
;
2324 catch ( const css::ucb::CommandFailedException
& )
2326 pImpl
->m_eError
= ERRCODE_ABORT
;
2328 catch ( const css::ucb::InteractiveIOException
& r
)
2330 if ( r
.Code
== IOErrorCode_ACCESS_DENIED
)
2331 pImpl
->m_eError
= ERRCODE_IO_ACCESSDENIED
;
2332 else if ( r
.Code
== IOErrorCode_NOT_EXISTING
)
2333 pImpl
->m_eError
= ERRCODE_IO_NOTEXISTS
;
2334 else if ( r
.Code
== IOErrorCode_CANT_READ
)
2335 pImpl
->m_eError
= ERRCODE_IO_CANTREAD
;
2337 pImpl
->m_eError
= ERRCODE_IO_GENERAL
;
2339 // tdf#60237 - if the file is already present, raise the appropriate error
2340 catch (const css::ucb::NameClashException
& )
2342 pImpl
->m_eError
= ERRCODE_IO_ALREADYEXISTS
;
2344 catch ( const css::uno::Exception
& )
2346 pImpl
->m_eError
= ERRCODE_IO_GENERAL
;
2351 if ( pImpl
->pTempFile
)
2353 pImpl
->pTempFile
->EnableKillingFile();
2354 pImpl
->pTempFile
.reset();
2357 else if ( bTransactStarted
&& pImpl
->m_eError
!= ERRCODE_ABORT
)
2359 UseBackupToRestore_Impl( aOriginalContent
, xDummyEnv
);
2363 pImpl
->m_eError
= ERRCODE_IO_CANTREAD
;
2367 bool SfxMedium::TryDirectTransfer( const OUString
& aURL
, SfxItemSet
const & aTargetSet
)
2369 if ( GetErrorIgnoreWarning() )
2372 // if the document had no password it should be stored without password
2373 // if the document had password it should be stored with the same password
2374 // otherwise the stream copying can not be done
2375 const SfxStringItem
* pNewPassItem
= aTargetSet
.GetItem(SID_PASSWORD
, false);
2376 const SfxStringItem
* pOldPassItem
= GetItemSet().GetItem(SID_PASSWORD
, false);
2377 if ( ( !pNewPassItem
&& !pOldPassItem
)
2378 || ( pNewPassItem
&& pOldPassItem
&& pNewPassItem
->GetValue() == pOldPassItem
->GetValue() ) )
2380 // the filter must be the same
2381 const SfxStringItem
* pNewFilterItem
= aTargetSet
.GetItem(SID_FILTER_NAME
, false);
2382 const SfxStringItem
* pOldFilterItem
= GetItemSet().GetItem(SID_FILTER_NAME
, false);
2383 if ( pNewFilterItem
&& pOldFilterItem
&& pNewFilterItem
->GetValue() == pOldFilterItem
->GetValue() )
2385 // get the input stream and copy it
2386 // in case of success return true
2387 uno::Reference
< io::XInputStream
> xInStream
= GetInputStream();
2390 if ( xInStream
.is() )
2394 uno::Reference
< io::XSeekable
> xSeek( xInStream
, uno::UNO_QUERY
);
2398 nPos
= xSeek
->getPosition();
2402 uno::Reference
< css::ucb::XCommandEnvironment
> xEnv
;
2403 ::ucbhelper::Content
aTargetContent( aURL
, xEnv
, comphelper::getProcessComponentContext() );
2405 InsertCommandArgument aInsertArg
;
2406 aInsertArg
.Data
= xInStream
;
2407 const SfxBoolItem
* pOverWrite
= aTargetSet
.GetItem
<SfxBoolItem
>(SID_OVERWRITE
, false);
2408 if ( pOverWrite
&& !pOverWrite
->GetValue() ) // argument says: never overwrite
2409 aInsertArg
.ReplaceExisting
= false;
2411 aInsertArg
.ReplaceExisting
= true; // default is overwrite existing files
2414 aCmdArg
<<= aInsertArg
;
2415 aTargetContent
.executeCommand( "insert",
2419 xSeek
->seek( nPos
);
2423 catch( const uno::Exception
& )
2433 void SfxMedium::Transfer_Impl()
2435 // The transfer is required only in two cases: either if there is a temporary file or if there is a salvage item
2437 if ( pImpl
->pTempFile
)
2438 aNameURL
= pImpl
->pTempFile
->GetURL();
2439 else if ( !pImpl
->m_aLogicName
.isEmpty() && pImpl
->m_bSalvageMode
)
2441 // makes sense only in case logic name is set
2442 if ( osl::FileBase::getFileURLFromSystemPath( pImpl
->m_aName
, aNameURL
)
2443 != osl::FileBase::E_None
)
2444 SAL_WARN( "sfx.doc", "The medium name is not convertible!" );
2447 if ( aNameURL
.isEmpty() || ( pImpl
->m_eError
&& !pImpl
->m_eError
.IsWarning() ) )
2450 SAL_INFO( "sfx.doc", "SfxMedium::Transfer_Impl, copying to target" );
2452 Reference
< css::ucb::XCommandEnvironment
> xEnv
;
2453 Reference
< XOutputStream
> rOutStream
;
2455 // in case an output stream is provided from outside and the URL is correct
2456 // commit to the stream
2457 if (pImpl
->m_aLogicName
.startsWith("private:stream"))
2459 // TODO/LATER: support storing to SID_STREAM
2460 const SfxUnoAnyItem
* pOutStreamItem
= SfxItemSet::GetItem
<SfxUnoAnyItem
>(pImpl
->m_pSet
.get(), SID_OUTPUTSTREAM
, false);
2461 if( pOutStreamItem
&& ( pOutStreamItem
->GetValue() >>= rOutStream
) )
2463 if ( pImpl
->xStorage
.is() )
2466 CloseStreams_Impl();
2468 INetURLObject
aSource( aNameURL
);
2469 ::ucbhelper::Content aTempCont
;
2470 if( ::ucbhelper::Content::create( aSource
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), xEnv
, comphelper::getProcessComponentContext(), aTempCont
) )
2475 sal_Int32 nBufferSize
= 32767;
2476 Sequence
< sal_Int8
> aSequence ( nBufferSize
);
2477 Reference
< XInputStream
> aTempInput
= aTempCont
.openStream();
2481 nRead
= aTempInput
->readBytes ( aSequence
, nBufferSize
);
2482 if ( nRead
< nBufferSize
)
2484 Sequence
< sal_Int8
> aTempBuf ( aSequence
.getConstArray(), nRead
);
2485 rOutStream
->writeBytes ( aTempBuf
);
2488 rOutStream
->writeBytes ( aSequence
);
2490 while ( nRead
== nBufferSize
);
2492 // remove temporary file
2493 if ( pImpl
->pTempFile
)
2495 pImpl
->pTempFile
->EnableKillingFile();
2496 pImpl
->pTempFile
.reset();
2499 catch( const Exception
& )
2505 SAL_WARN( "sfx.doc", "Illegal Output stream parameter!" );
2506 SetError(ERRCODE_IO_GENERAL
);
2509 // free the reference
2510 if ( pImpl
->m_pSet
)
2511 pImpl
->m_pSet
->ClearItem( SID_OUTPUTSTREAM
);
2517 if ( !pImpl
->aContent
.get().is() )
2519 pImpl
->m_eError
= ERRCODE_IO_NOTEXISTS
;
2523 INetURLObject
aDest( GetURLObject() );
2525 // source is the temp file written so far
2526 INetURLObject
aSource( aNameURL
);
2528 // a special case, an interaction handler should be used for
2529 // authentication in case it is available
2530 Reference
< css::ucb::XCommandEnvironment
> xComEnv
;
2531 bool bForceInteractionHandler
= GetURLObject().isAnyKnownWebDAVScheme();
2532 Reference
< css::task::XInteractionHandler
> xInteractionHandler
= GetInteractionHandler(bForceInteractionHandler
);
2533 if (xInteractionHandler
.is())
2534 xComEnv
= new ::ucbhelper::CommandEnvironment( xInteractionHandler
,
2535 Reference
< css::ucb::XProgressHandler
>() );
2537 OUString
aDestURL( aDest
.GetMainURL( INetURLObject::DecodeMechanism::NONE
) );
2539 if ( comphelper::isFileUrl( aDestURL
) || !aDest
.removeSegment() )
2541 TransactedTransferForFS_Impl( aSource
, aDest
, xComEnv
);
2543 if (!pImpl
->m_bDisableFileSync
)
2545 // Hideous - no clean way to do this, so we re-open the file just to fsync it
2546 osl::File
aFile( aDestURL
);
2547 if ( aFile
.open( osl_File_OpenFlag_Write
) == osl::FileBase::E_None
)
2550 SAL_INFO( "sfx.doc", "fsync'd saved file '" << aDestURL
<< "'" );
2557 // create content for the parent folder and call transfer on that content with the source content
2558 // and the destination file name as parameters
2559 ::ucbhelper::Content aSourceContent
;
2560 ::ucbhelper::Content aTransferContent
;
2562 ::ucbhelper::Content aDestContent
;
2563 (void)::ucbhelper::Content::create( aDestURL
, xComEnv
, comphelper::getProcessComponentContext(), aDestContent
);
2564 // For checkin, we need the object URL, not the parent folder:
2565 if ( !IsInCheckIn( ) )
2567 // Get the parent URL from the XChild if possible: why would the URL necessarily have
2568 // a hierarchical path? It's not always the case for CMIS.
2569 Reference
< css::container::XChild
> xChild( aDestContent
.get(), uno::UNO_QUERY
);
2570 OUString sParentUrl
;
2573 Reference
< css::ucb::XContent
> xParent( xChild
->getParent( ), uno::UNO_QUERY
);
2574 if ( xParent
.is( ) )
2576 sParentUrl
= xParent
->getIdentifier( )->getContentIdentifier();
2580 if ( sParentUrl
.isEmpty() )
2581 aDestURL
= aDest
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
2582 // adjust to above aDest.removeSegment()
2584 aDestURL
= sParentUrl
;
2587 // LongName wasn't defined anywhere, only used here... get the Title instead
2588 // as it's less probably empty
2593 Any aAny
= aDestContent
.getPropertyValue("Title");
2595 aAny
= aDestContent
.getPropertyValue("ObjectId");
2598 catch (uno::Exception
const&)
2600 SAL_INFO("sfx.doc", "exception while getting Title or ObjectId");
2602 if ( aFileName
.isEmpty() )
2603 aFileName
= GetURLObject().getName( INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::WithCharset
);
2607 aTransferContent
= ::ucbhelper::Content( aDestURL
, xComEnv
, comphelper::getProcessComponentContext() );
2609 catch (const css::ucb::ContentCreationException
& ex
)
2611 pImpl
->m_eError
= ERRCODE_IO_GENERAL
;
2613 (ex
.eError
== css::ucb::ContentCreationError_NO_CONTENT_PROVIDER
) ||
2614 (ex
.eError
== css::ucb::ContentCreationError_CONTENT_CREATION_FAILED
)
2617 pImpl
->m_eError
= ERRCODE_IO_NOTEXISTSPATH
;
2620 catch (const css::uno::Exception
&)
2622 pImpl
->m_eError
= ERRCODE_IO_GENERAL
;
2625 if ( !pImpl
->m_eError
|| pImpl
->m_eError
.IsWarning() )
2627 // free resources, otherwise the transfer may fail
2628 if ( pImpl
->xStorage
.is() )
2631 CloseStreams_Impl();
2633 (void)::ucbhelper::Content::create( aSource
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), xEnv
, comphelper::getProcessComponentContext(), aSourceContent
);
2635 // check for external parameters that may customize the handling of NameClash situations
2636 const SfxBoolItem
* pOverWrite
= GetItemSet().GetItem
<SfxBoolItem
>(SID_OVERWRITE
, false);
2637 sal_Int32 nNameClash
;
2638 if ( pOverWrite
&& !pOverWrite
->GetValue() )
2639 // argument says: never overwrite
2640 nNameClash
= NameClash::ERROR
;
2642 // default is overwrite existing files
2643 nNameClash
= NameClash::OVERWRITE
;
2647 OUString aMimeType
= pImpl
->getFilterMimeType();
2648 ::ucbhelper::InsertOperation eOperation
= ::ucbhelper::InsertOperation::Copy
;
2649 bool bMajor
= false;
2651 if ( IsInCheckIn( ) )
2653 eOperation
= ::ucbhelper::InsertOperation::Checkin
;
2654 const SfxBoolItem
* pMajor
= GetItemSet().GetItem
<SfxBoolItem
>(SID_DOCINFO_MAJOR
, false);
2655 bMajor
= pMajor
&& pMajor
->GetValue( );
2656 const SfxStringItem
* pComments
= GetItemSet().GetItem(SID_DOCINFO_COMMENTS
, false);
2658 sComment
= pComments
->GetValue( );
2660 OUString sResultURL
;
2661 aTransferContent
.transferContent(
2662 aSourceContent
, eOperation
,
2663 aFileName
, nNameClash
, aMimeType
, bMajor
, sComment
,
2664 &sResultURL
, sObjectId
);
2666 if ( !sResultURL
.isEmpty( ) ) // Likely to happen only for checkin
2667 SwitchDocumentToFile( sResultURL
);
2670 if ( GetURLObject().isAnyKnownWebDAVScheme() &&
2671 eOperation
== ::ucbhelper::InsertOperation::Copy
)
2673 // tdf#95272 try to re-issue a lock command when a new file is created.
2674 // This may be needed because some WebDAV servers fail to implement the
2675 // 'LOCK on unallocated reference', see issue comment:
2676 // <https://bugs.documentfoundation.org/show_bug.cgi?id=95792#c8>
2677 // and specification at:
2678 // <http://tools.ietf.org/html/rfc4918#section-7.3>
2679 // If the WebDAV resource is already locked by this LO instance, nothing will
2680 // happen, e.g. the LOCK method will not be sent to the server.
2681 ::ucbhelper::Content
aLockContent( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE
), xComEnv
, comphelper::getProcessComponentContext() );
2682 aLockContent
.lock();
2685 catch ( css::uno::Exception
& )
2687 TOOLS_WARN_EXCEPTION( "sfx.doc", "LOCK not working while re-issuing it" );
2690 catch ( const css::ucb::CommandAbortedException
& )
2692 pImpl
->m_eError
= ERRCODE_ABORT
;
2694 catch ( const css::ucb::CommandFailedException
& )
2696 pImpl
->m_eError
= ERRCODE_ABORT
;
2698 catch ( const css::ucb::InteractiveIOException
& r
)
2700 if ( r
.Code
== IOErrorCode_ACCESS_DENIED
)
2701 pImpl
->m_eError
= ERRCODE_IO_ACCESSDENIED
;
2702 else if ( r
.Code
== IOErrorCode_NOT_EXISTING
)
2703 pImpl
->m_eError
= ERRCODE_IO_NOTEXISTS
;
2704 else if ( r
.Code
== IOErrorCode_CANT_READ
)
2705 pImpl
->m_eError
= ERRCODE_IO_CANTREAD
;
2707 pImpl
->m_eError
= ERRCODE_IO_GENERAL
;
2709 catch ( const css::uno::Exception
& )
2711 pImpl
->m_eError
= ERRCODE_IO_GENERAL
;
2714 // do not switch from temporary file in case of nonfile protocol
2718 if ( ( !pImpl
->m_eError
|| pImpl
->m_eError
.IsWarning() ) && !pImpl
->pTempFile
)
2720 // without a TempFile the physical and logical name should be the same after successful transfer
2721 if (osl::FileBase::getSystemPathFromFileURL(
2722 GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE
), pImpl
->m_aName
)
2723 != osl::FileBase::E_None
)
2725 pImpl
->m_aName
.clear();
2727 pImpl
->m_bSalvageMode
= false;
2732 void SfxMedium::DoInternalBackup_Impl( const ::ucbhelper::Content
& aOriginalContent
,
2733 std::u16string_view aPrefix
,
2734 std::u16string_view aExtension
,
2735 const OUString
& aDestDir
)
2737 if ( !pImpl
->m_aBackupURL
.isEmpty() )
2738 return; // the backup was done already
2740 ::utl::TempFileNamed
aTransactTemp( aPrefix
, true, aExtension
, &aDestDir
);
2742 INetURLObject
aBackObj( aTransactTemp
.GetURL() );
2743 OUString aBackupName
= aBackObj
.getName( INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::WithCharset
);
2745 Reference
< css::ucb::XCommandEnvironment
> xDummyEnv
;
2746 ::ucbhelper::Content aBackupCont
;
2747 if( ::ucbhelper::Content::create( aDestDir
, xDummyEnv
, comphelper::getProcessComponentContext(), aBackupCont
) )
2751 OUString sMimeType
= pImpl
->getFilterMimeType();
2752 aBackupCont
.transferContent( aOriginalContent
,
2753 ::ucbhelper::InsertOperation::Copy
,
2755 NameClash::OVERWRITE
,
2757 pImpl
->m_aBackupURL
= aBackObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
2758 pImpl
->m_bRemoveBackup
= true;
2760 catch( const Exception
& )
2764 if ( pImpl
->m_aBackupURL
.isEmpty() )
2765 aTransactTemp
.EnableKillingFile();
2769 void SfxMedium::DoInternalBackup_Impl( const ::ucbhelper::Content
& aOriginalContent
)
2771 if ( !pImpl
->m_aBackupURL
.isEmpty() )
2772 return; // the backup was done already
2774 OUString aFileName
= GetURLObject().getName( INetURLObject::LAST_SEGMENT
,
2776 INetURLObject::DecodeMechanism::NONE
);
2778 sal_Int32 nPrefixLen
= aFileName
.lastIndexOf( '.' );
2779 OUString aPrefix
= ( nPrefixLen
== -1 ) ? aFileName
: aFileName
.copy( 0, nPrefixLen
);
2780 OUString aExtension
= ( nPrefixLen
== -1 ) ? OUString() : aFileName
.copy( nPrefixLen
);
2781 OUString aBakDir
= SvtPathOptions().GetBackupPath();
2783 // create content for the parent folder ( = backup folder )
2784 ::ucbhelper::Content aContent
;
2785 Reference
< css::ucb::XCommandEnvironment
> xEnv
;
2786 if( ::utl::UCBContentHelper::ensureFolder(comphelper::getProcessComponentContext(), xEnv
, aBakDir
, aContent
) )
2787 DoInternalBackup_Impl( aOriginalContent
, aPrefix
, aExtension
, aBakDir
);
2789 if ( !pImpl
->m_aBackupURL
.isEmpty() )
2792 // the copying to the backup catalog failed ( for example because
2793 // of using an encrypted partition as target catalog )
2794 // since the user did not specify to make backup explicitly
2795 // office should try to make backup in another place,
2796 // target catalog does not look bad for this case ( and looks
2797 // to be the only way for encrypted partitions )
2799 INetURLObject aDest
= GetURLObject();
2800 if ( aDest
.removeSegment() )
2801 DoInternalBackup_Impl( aOriginalContent
, aPrefix
, aExtension
, aDest
.GetMainURL( INetURLObject::DecodeMechanism::NONE
) );
2805 void SfxMedium::DoBackup_Impl(bool bForceUsingBackupPath
)
2807 // source file name is the logical name of this medium
2808 INetURLObject
aSource( GetURLObject() );
2810 // there is nothing to backup in case source file does not exist
2811 if ( !::utl::UCBContentHelper::IsDocument( aSource
.GetMainURL( INetURLObject::DecodeMechanism::NONE
) ) )
2814 bool bSuccess
= false;
2815 bool bOnErrorRetryUsingBackupPath
= false;
2817 // get path for backups
2819 if (!bForceUsingBackupPath
2820 && officecfg::Office::Common::Save::Document::BackupIntoDocumentFolder::get())
2822 aBakDir
= aSource
.GetPartBeforeLastName();
2823 bOnErrorRetryUsingBackupPath
= true;
2826 aBakDir
= SvtPathOptions().GetBackupPath();
2827 if( !aBakDir
.isEmpty() )
2829 // create content for the parent folder ( = backup folder )
2830 ::ucbhelper::Content aContent
;
2831 Reference
< css::ucb::XCommandEnvironment
> xEnv
;
2832 if( ::utl::UCBContentHelper::ensureFolder(comphelper::getProcessComponentContext(), xEnv
, aBakDir
, aContent
) )
2834 // save as ".bak" file
2835 INetURLObject
aDest( aBakDir
);
2836 aDest
.insertName( aSource
.getName() );
2838 = aSource
.hasExtension() ? aSource
.getExtension() + ".bak" : OUString("bak");
2839 aDest
.setExtension(sExt
);
2840 OUString aFileName
= aDest
.getName( INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::WithCharset
);
2842 // create a content for the source file
2843 ::ucbhelper::Content aSourceContent
;
2844 if ( ::ucbhelper::Content::create( aSource
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), xEnv
, comphelper::getProcessComponentContext(), aSourceContent
) )
2848 // do the transfer ( copy source file to backup dir )
2849 OUString sMimeType
= pImpl
->getFilterMimeType();
2850 aContent
.transferContent( aSourceContent
,
2851 ::ucbhelper::InsertOperation::Copy
,
2853 NameClash::OVERWRITE
,
2855 pImpl
->m_aBackupURL
= aDest
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
2856 pImpl
->m_bRemoveBackup
= false;
2859 catch ( const css::uno::Exception
& )
2868 // in case a webdav server prevents file creation, or a partition is full, or whatever...
2869 if (bOnErrorRetryUsingBackupPath
)
2870 return DoBackup_Impl(/*bForceUsingBackupPath=*/true);
2872 pImpl
->m_eError
= ERRCODE_SFX_CANTCREATEBACKUP
;
2877 void SfxMedium::ClearBackup_Impl()
2879 if( pImpl
->m_bRemoveBackup
)
2881 // currently a document is always stored in a new medium,
2882 // thus if a backup can not be removed the backup URL should not be cleaned
2883 if ( !pImpl
->m_aBackupURL
.isEmpty() )
2885 if ( ::utl::UCBContentHelper::Kill( pImpl
->m_aBackupURL
) )
2887 pImpl
->m_bRemoveBackup
= false;
2888 pImpl
->m_aBackupURL
.clear();
2893 SAL_WARN( "sfx.doc", "Couldn't remove backup file!");
2898 pImpl
->m_aBackupURL
.clear();
2902 void SfxMedium::GetLockingStream_Impl()
2904 if ( GetURLObject().GetProtocol() != INetProtocol::File
2905 || pImpl
->m_xLockingStream
.is() )
2908 const SfxUnoAnyItem
* pWriteStreamItem
= SfxItemSet::GetItem
<SfxUnoAnyItem
>(pImpl
->m_pSet
.get(), SID_STREAM
, false);
2909 if ( pWriteStreamItem
)
2910 pWriteStreamItem
->GetValue() >>= pImpl
->m_xLockingStream
;
2912 if ( pImpl
->m_xLockingStream
.is() )
2915 // open the original document
2916 uno::Sequence
< beans::PropertyValue
> xProps
;
2917 TransformItems( SID_OPENDOC
, GetItemSet(), xProps
);
2918 utl::MediaDescriptor
aMedium( xProps
);
2920 aMedium
.addInputStreamOwnLock();
2922 uno::Reference
< io::XInputStream
> xInputStream
;
2923 aMedium
[utl::MediaDescriptor::PROP_STREAM
] >>= pImpl
->m_xLockingStream
;
2924 aMedium
[utl::MediaDescriptor::PROP_INPUTSTREAM
] >>= xInputStream
;
2926 if ( !pImpl
->pTempFile
&& pImpl
->m_aName
.isEmpty() )
2928 // the medium is still based on the original file, it makes sense to initialize the streams
2929 if ( pImpl
->m_xLockingStream
.is() )
2930 pImpl
->xStream
= pImpl
->m_xLockingStream
;
2932 if ( xInputStream
.is() )
2933 pImpl
->xInputStream
= xInputStream
;
2935 if ( !pImpl
->xInputStream
.is() && pImpl
->xStream
.is() )
2936 pImpl
->xInputStream
= pImpl
->xStream
->getInputStream();
2941 void SfxMedium::GetMedium_Impl()
2943 if ( pImpl
->m_pInStream
2944 && (!pImpl
->bIsTemp
|| pImpl
->xInputStream
.is() || pImpl
->m_xInputStreamToLoadFrom
.is() || pImpl
->xStream
.is() || pImpl
->m_xLockingStream
.is() ) )
2947 pImpl
->bDownloadDone
= false;
2948 Reference
< css::task::XInteractionHandler
> xInteractionHandler
= GetInteractionHandler();
2950 //TODO/MBA: need support for SID_STREAM
2951 const SfxUnoAnyItem
* pWriteStreamItem
= SfxItemSet::GetItem
<SfxUnoAnyItem
>(pImpl
->m_pSet
.get(), SID_STREAM
, false);
2952 const SfxUnoAnyItem
* pInStreamItem
= SfxItemSet::GetItem
<SfxUnoAnyItem
>(pImpl
->m_pSet
.get(), SID_INPUTSTREAM
, false);
2953 if ( pWriteStreamItem
)
2955 pWriteStreamItem
->GetValue() >>= pImpl
->xStream
;
2957 if ( pInStreamItem
)
2958 pInStreamItem
->GetValue() >>= pImpl
->xInputStream
;
2960 if ( !pImpl
->xInputStream
.is() && pImpl
->xStream
.is() )
2961 pImpl
->xInputStream
= pImpl
->xStream
->getInputStream();
2963 else if ( pInStreamItem
)
2965 pInStreamItem
->GetValue() >>= pImpl
->xInputStream
;
2969 uno::Sequence
< beans::PropertyValue
> xProps
;
2971 if (!pImpl
->m_aName
.isEmpty())
2973 if ( osl::FileBase::getFileURLFromSystemPath( pImpl
->m_aName
, aFileName
)
2974 != osl::FileBase::E_None
)
2976 SAL_WARN( "sfx.doc", "Physical name not convertible!");
2980 aFileName
= GetName();
2982 // in case the temporary file exists the streams should be initialized from it,
2983 // but the original MediaDescriptor should not be changed
2984 bool bFromTempFile
= ( pImpl
->pTempFile
!= nullptr );
2986 if ( !bFromTempFile
)
2988 GetItemSet().Put( SfxStringItem( SID_FILE_NAME
, aFileName
) );
2989 if( !(pImpl
->m_nStorOpenMode
& StreamMode::WRITE
) )
2990 GetItemSet().Put( SfxBoolItem( SID_DOC_READONLY
, true ) );
2991 if (xInteractionHandler
.is())
2992 GetItemSet().Put( SfxUnoAnyItem( SID_INTERACTIONHANDLER
, Any(xInteractionHandler
) ) );
2995 if ( pImpl
->m_xInputStreamToLoadFrom
.is() )
2997 pImpl
->xInputStream
= pImpl
->m_xInputStreamToLoadFrom
;
2998 if (pImpl
->m_bInputStreamIsReadOnly
)
2999 GetItemSet().Put( SfxBoolItem( SID_DOC_READONLY
, true ) );
3003 TransformItems( SID_OPENDOC
, GetItemSet(), xProps
);
3004 utl::MediaDescriptor
aMedium( xProps
);
3006 if ( pImpl
->m_xLockingStream
.is() && !bFromTempFile
)
3008 // the medium is not based on the temporary file, so the original stream can be used
3009 pImpl
->xStream
= pImpl
->m_xLockingStream
;
3013 if ( bFromTempFile
)
3015 aMedium
[utl::MediaDescriptor::PROP_URL
] <<= aFileName
;
3016 aMedium
.erase( utl::MediaDescriptor::PROP_READONLY
);
3017 aMedium
.addInputStream();
3019 else if ( GetURLObject().GetProtocol() == INetProtocol::File
)
3021 // use the special locking approach only for file URLs
3022 aMedium
.addInputStreamOwnLock();
3026 // add a check for protocol, if it's http or https or provide webdav then add
3027 // the interaction handler to be used by the authentication dialog
3028 if ( GetURLObject().isAnyKnownWebDAVScheme() )
3030 aMedium
[utl::MediaDescriptor::PROP_AUTHENTICATIONHANDLER
] <<= GetInteractionHandler( true );
3032 aMedium
.addInputStream();
3034 // the ReadOnly property set in aMedium is ignored
3035 // the check is done in LockOrigFileOnDemand() for file and non-file URLs
3037 //TODO/MBA: what happens if property is not there?!
3038 aMedium
[utl::MediaDescriptor::PROP_STREAM
] >>= pImpl
->xStream
;
3039 aMedium
[utl::MediaDescriptor::PROP_INPUTSTREAM
] >>= pImpl
->xInputStream
;
3043 if ( !pImpl
->xInputStream
.is() && pImpl
->xStream
.is() )
3044 pImpl
->xInputStream
= pImpl
->xStream
->getInputStream();
3047 if ( !bFromTempFile
)
3049 //TODO/MBA: need support for SID_STREAM
3050 if ( pImpl
->xStream
.is() )
3051 GetItemSet().Put( SfxUnoAnyItem( SID_STREAM
, Any( pImpl
->xStream
) ) );
3053 GetItemSet().Put( SfxUnoAnyItem( SID_INPUTSTREAM
, Any( pImpl
->xInputStream
) ) );
3057 //TODO/MBA: ErrorHandling - how to transport error from MediaDescriptor
3058 if ( !GetErrorIgnoreWarning() && !pImpl
->xStream
.is() && !pImpl
->xInputStream
.is() )
3059 SetError(ERRCODE_IO_ACCESSDENIED
);
3061 if ( !GetErrorIgnoreWarning() && !pImpl
->m_pInStream
)
3063 if ( pImpl
->xStream
.is() )
3064 pImpl
->m_pInStream
= utl::UcbStreamHelper::CreateStream( pImpl
->xStream
);
3065 else if ( pImpl
->xInputStream
.is() )
3066 pImpl
->m_pInStream
= utl::UcbStreamHelper::CreateStream( pImpl
->xInputStream
);
3069 pImpl
->bDownloadDone
= true;
3070 pImpl
->aDoneLink
.ClearPendingCall();
3071 ErrCodeMsg nError
= GetErrorIgnoreWarning();
3072 pImpl
->aDoneLink
.Call( reinterpret_cast<void*>(sal_uInt32(nError
.GetCode())) );
3075 bool SfxMedium::IsRemote() const
3077 return pImpl
->m_bRemote
;
3080 void SfxMedium::SetUpdatePickList(bool bVal
)
3082 pImpl
->bUpdatePickList
= bVal
;
3085 bool SfxMedium::IsUpdatePickList() const
3087 return pImpl
->bUpdatePickList
;
3090 void SfxMedium::SetLongName(const OUString
&rName
)
3092 pImpl
->m_aLongName
= rName
;
3095 const OUString
& SfxMedium::GetLongName() const
3097 return pImpl
->m_aLongName
;
3100 void SfxMedium::SetDoneLink( const Link
<void*,void>& rLink
)
3102 pImpl
->aDoneLink
= rLink
;
3105 void SfxMedium::Download( const Link
<void*,void>& aLink
)
3107 SetDoneLink( aLink
);
3109 if ( pImpl
->m_pInStream
&& !aLink
.IsSet() )
3111 while( !pImpl
->bDownloadDone
&& !Application::IsQuit())
3112 Application::Yield();
3118 Sets m_aLogicName to a valid URL and if available sets
3119 the physical name m_aName to the file name.
3121 void SfxMedium::Init_Impl()
3123 Reference
< XOutputStream
> rOutStream
;
3125 // TODO/LATER: handle lifetime of storages
3126 pImpl
->bDisposeStorage
= false;
3128 const SfxStringItem
* pSalvageItem
= SfxItemSet::GetItem
<SfxStringItem
>(pImpl
->m_pSet
.get(), SID_DOC_SALVAGE
, false);
3129 if ( pSalvageItem
&& pSalvageItem
->GetValue().isEmpty() )
3131 pSalvageItem
= nullptr;
3132 pImpl
->m_pSet
->ClearItem( SID_DOC_SALVAGE
);
3135 if (!pImpl
->m_aLogicName
.isEmpty())
3137 INetURLObject
aUrl( pImpl
->m_aLogicName
);
3138 INetProtocol eProt
= aUrl
.GetProtocol();
3139 if ( eProt
== INetProtocol::NotValid
)
3141 SAL_WARN( "sfx.doc", "URL <" << pImpl
->m_aLogicName
<< "> with unknown protocol" );
3145 if ( aUrl
.HasMark() )
3147 std::unique_lock
<std::recursive_mutex
> chkEditLock
;
3148 if (pImpl
->m_pCheckEditableWorkerMutex
!= nullptr)
3149 chkEditLock
= std::unique_lock
<std::recursive_mutex
>(
3150 *(pImpl
->m_pCheckEditableWorkerMutex
));
3151 pImpl
->m_aLogicName
= aUrl
.GetURLNoMark( INetURLObject::DecodeMechanism::NONE
);
3152 if (chkEditLock
.owns_lock())
3153 chkEditLock
.unlock();
3154 GetItemSet().Put( SfxStringItem( SID_JUMPMARK
, aUrl
.GetMark() ) );
3157 // try to convert the URL into a physical name - but never change a physical name
3158 // physical name may be set if the logical name is changed after construction
3159 if ( pImpl
->m_aName
.isEmpty() )
3160 osl::FileBase::getSystemPathFromFileURL( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE
), pImpl
->m_aName
);
3163 DBG_ASSERT( pSalvageItem
, "Suspicious change of logical name!" );
3170 std::unique_lock
<std::recursive_mutex
> chkEditLock
;
3171 if (pImpl
->m_pCheckEditableWorkerMutex
!= nullptr)
3173 = std::unique_lock
<std::recursive_mutex
>(*(pImpl
->m_pCheckEditableWorkerMutex
));
3174 pImpl
->m_aLogicName
= pSalvageItem
->GetValue();
3175 pImpl
->m_pURLObj
.reset();
3176 if (chkEditLock
.owns_lock())
3177 chkEditLock
.unlock();
3178 pImpl
->m_bSalvageMode
= true;
3181 // in case output stream is by mistake here
3182 // clear the reference
3183 const SfxUnoAnyItem
* pOutStreamItem
= SfxItemSet::GetItem
<SfxUnoAnyItem
>(pImpl
->m_pSet
.get(), SID_OUTPUTSTREAM
, false);
3185 && ( !( pOutStreamItem
->GetValue() >>= rOutStream
)
3186 || !pImpl
->m_aLogicName
.startsWith("private:stream")) )
3188 pImpl
->m_pSet
->ClearItem( SID_OUTPUTSTREAM
);
3189 SAL_WARN( "sfx.doc", "Unexpected Output stream parameter!" );
3192 if (!pImpl
->m_aLogicName
.isEmpty())
3194 // if the logic name is set it should be set in MediaDescriptor as well
3195 const SfxStringItem
* pFileNameItem
= SfxItemSet::GetItem
<SfxStringItem
>(pImpl
->m_pSet
.get(), SID_FILE_NAME
, false);
3196 if ( !pFileNameItem
)
3198 // let the ItemSet be created if necessary
3201 SID_FILE_NAME
, INetURLObject( pImpl
->m_aLogicName
).GetMainURL( INetURLObject::DecodeMechanism::NONE
) ) );
3207 osl::DirectoryItem item
;
3208 if (osl::DirectoryItem::get(GetName(), item
) == osl::FileBase::E_None
) {
3209 osl::FileStatus
stat(osl_FileStatus_Mask_Attributes
);
3210 if (item
.getFileStatus(stat
) == osl::FileBase::E_None
3211 && stat
.isValid(osl_FileStatus_Mask_Attributes
))
3213 if ((stat
.getAttributes() & osl_File_Attribute_ReadOnly
) != 0)
3215 pImpl
->m_bOriginallyReadOnly
= true;
3222 SfxMedium::SfxMedium() : pImpl(new SfxMedium_Impl
)
3228 void SfxMedium::UseInteractionHandler( bool bUse
)
3230 pImpl
->bAllowDefaultIntHdl
= bUse
;
3234 css::uno::Reference
< css::task::XInteractionHandler
>
3235 SfxMedium::GetInteractionHandler( bool bGetAlways
)
3237 // if interaction isn't allowed explicitly ... return empty reference!
3238 if ( !bGetAlways
&& !pImpl
->bUseInteractionHandler
)
3239 return css::uno::Reference
< css::task::XInteractionHandler
>();
3241 // search a possible existing handler inside cached item set
3242 if ( pImpl
->m_pSet
)
3244 css::uno::Reference
< css::task::XInteractionHandler
> xHandler
;
3245 const SfxUnoAnyItem
* pHandler
= SfxItemSet::GetItem
<SfxUnoAnyItem
>(pImpl
->m_pSet
.get(), SID_INTERACTIONHANDLER
, false);
3246 if ( pHandler
&& (pHandler
->GetValue() >>= xHandler
) && xHandler
.is() )
3250 // if default interaction isn't allowed explicitly ... return empty reference!
3251 if ( !bGetAlways
&& !pImpl
->bAllowDefaultIntHdl
)
3252 return css::uno::Reference
< css::task::XInteractionHandler
>();
3254 // otherwise return cached default handler ... if it exist.
3255 if ( pImpl
->xInteraction
.is() )
3256 return pImpl
->xInteraction
;
3258 // create default handler and cache it!
3259 Reference
< uno::XComponentContext
> xContext
= ::comphelper::getProcessComponentContext();
3260 pImpl
->xInteraction
.set(
3261 task::InteractionHandler::createWithParent(xContext
, nullptr), UNO_QUERY_THROW
);
3262 return pImpl
->xInteraction
;
3265 void SfxMedium::SetFilter( const std::shared_ptr
<const SfxFilter
>& pFilter
)
3267 pImpl
->m_pFilter
= pFilter
;
3270 const std::shared_ptr
<const SfxFilter
>& SfxMedium::GetFilter() const
3272 return pImpl
->m_pFilter
;
3275 sal_uInt32
SfxMedium::CreatePasswordToModifyHash( std::u16string_view aPasswd
, bool bWriter
)
3277 sal_uInt32 nHash
= 0;
3279 if ( !aPasswd
.empty() )
3283 nHash
= ::comphelper::DocPasswordHelper::GetWordHashAsUINT32( aPasswd
);
3287 rtl_TextEncoding nEncoding
= osl_getThreadTextEncoding();
3288 nHash
= ::comphelper::DocPasswordHelper::GetXLHashAsUINT16( aPasswd
, nEncoding
);
3296 void SfxMedium::Close(bool bInDestruction
)
3298 if ( pImpl
->xStorage
.is() )
3303 CloseStreams_Impl(bInDestruction
);
3305 UnlockFile( false );
3308 void SfxMedium::CloseAndRelease()
3310 if ( pImpl
->xStorage
.is() )
3315 CloseAndReleaseStreams_Impl();
3320 void SfxMedium::DisableUnlockWebDAV( bool bDisableUnlockWebDAV
)
3322 pImpl
->m_bDisableUnlockWebDAV
= bDisableUnlockWebDAV
;
3325 void SfxMedium::DisableFileSync(bool bDisableFileSync
)
3327 pImpl
->m_bDisableFileSync
= bDisableFileSync
;
3330 void SfxMedium::UnlockFile( bool bReleaseLockStream
)
3332 #if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT
3333 (void) bReleaseLockStream
;
3336 if ( GetURLObject().isAnyKnownWebDAVScheme() )
3338 // do nothing if WebDAV locking if disabled
3339 // (shouldn't happen because we already skipped locking,
3340 // see LockOrigFileOnDemand, but just in case ...)
3341 if (!IsWebDAVLockingUsed())
3344 if ( pImpl
->m_bLocked
)
3346 // an interaction handler should be used for authentication, if needed
3348 uno::Reference
< css::task::XInteractionHandler
> xHandler
= GetInteractionHandler( true );
3349 uno::Reference
< css::ucb::XCommandEnvironment
> xComEnv
= new ::ucbhelper::CommandEnvironment( xHandler
,
3350 Reference
< css::ucb::XProgressHandler
>() );
3351 ucbhelper::Content
aContentToUnlock( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE
), xComEnv
, comphelper::getProcessComponentContext());
3352 pImpl
->m_bLocked
= false;
3353 //check if WebDAV unlock was explicitly disabled
3354 if ( !pImpl
->m_bDisableUnlockWebDAV
)
3355 aContentToUnlock
.unlock();
3357 catch ( uno::Exception
& )
3359 TOOLS_WARN_EXCEPTION( "sfx.doc", "Locking exception: WebDAV while trying to lock the file" );
3365 if ( pImpl
->m_xLockingStream
.is() )
3367 if ( bReleaseLockStream
)
3371 uno::Reference
< io::XInputStream
> xInStream
= pImpl
->m_xLockingStream
->getInputStream();
3372 uno::Reference
< io::XOutputStream
> xOutStream
= pImpl
->m_xLockingStream
->getOutputStream();
3373 if ( xInStream
.is() )
3374 xInStream
->closeInput();
3375 if ( xOutStream
.is() )
3376 xOutStream
->closeOutput();
3378 catch( const uno::Exception
& )
3382 pImpl
->m_xLockingStream
.clear();
3385 if ( !pImpl
->m_bLocked
)
3390 ::svt::DocumentLockFile
aLockFile(pImpl
->m_aLogicName
);
3394 pImpl
->m_bLocked
= false;
3395 // TODO/LATER: A warning could be shown in case the file is not the own one
3396 aLockFile
.RemoveFile();
3398 catch (const io::WrongFormatException
&)
3400 // erase the empty or corrupt file
3401 aLockFile
.RemoveFileDirectly();
3404 catch( const uno::Exception
& )
3407 if(!pImpl
->m_bMSOLockFileCreated
)
3412 ::svt::MSODocumentLockFile
aMSOLockFile(pImpl
->m_aLogicName
);
3416 pImpl
->m_bLocked
= false;
3417 // TODO/LATER: A warning could be shown in case the file is not the own one
3418 aMSOLockFile
.RemoveFile();
3420 catch (const io::WrongFormatException
&)
3422 // erase the empty or corrupt file
3423 aMSOLockFile
.RemoveFileDirectly();
3426 catch( const uno::Exception
& )
3428 pImpl
->m_bMSOLockFileCreated
= false;
3432 void SfxMedium::CloseAndReleaseStreams_Impl()
3434 CloseZipStorage_Impl();
3436 uno::Reference
< io::XInputStream
> xInToClose
= pImpl
->xInputStream
;
3437 uno::Reference
< io::XOutputStream
> xOutToClose
;
3438 if ( pImpl
->xStream
.is() )
3440 xOutToClose
= pImpl
->xStream
->getOutputStream();
3442 // if the locking stream is closed here the related member should be cleaned
3443 if ( pImpl
->xStream
== pImpl
->m_xLockingStream
)
3444 pImpl
->m_xLockingStream
.clear();
3447 // The probably existing SvStream wrappers should be closed first
3448 CloseStreams_Impl();
3450 // in case of salvage mode the storage is based on the streams
3451 if ( pImpl
->m_bSalvageMode
)
3456 if ( xInToClose
.is() )
3457 xInToClose
->closeInput();
3458 if ( xOutToClose
.is() )
3459 xOutToClose
->closeOutput();
3461 catch ( const uno::Exception
& )
3467 void SfxMedium::CloseStreams_Impl(bool bInDestruction
)
3469 CloseInStream_Impl(bInDestruction
);
3470 CloseOutStream_Impl();
3472 if ( pImpl
->m_pSet
)
3473 pImpl
->m_pSet
->ClearItem( SID_CONTENT
);
3475 pImpl
->aContent
= ::ucbhelper::Content();
3479 void SfxMedium::SetIsRemote_Impl()
3481 INetURLObject
aObj( GetName() );
3482 switch( aObj
.GetProtocol() )
3484 case INetProtocol::Ftp
:
3485 case INetProtocol::Http
:
3486 case INetProtocol::Https
:
3487 pImpl
->m_bRemote
= true;
3490 pImpl
->m_bRemote
= GetName().startsWith("private:msgid");
3494 // As files that are written to the remote transmission must also be able
3496 if (pImpl
->m_bRemote
)
3497 pImpl
->m_nStorOpenMode
|= StreamMode::READ
;
3501 void SfxMedium::SetName( const OUString
& aNameP
, bool bSetOrigURL
)
3503 if (pImpl
->aOrigURL
.isEmpty())
3504 pImpl
->aOrigURL
= pImpl
->m_aLogicName
;
3506 pImpl
->aOrigURL
= aNameP
;
3507 std::unique_lock
<std::recursive_mutex
> chkEditLock
;
3508 if (pImpl
->m_pCheckEditableWorkerMutex
!= nullptr)
3509 chkEditLock
= std::unique_lock
<std::recursive_mutex
>(*(pImpl
->m_pCheckEditableWorkerMutex
));
3510 pImpl
->m_aLogicName
= aNameP
;
3511 pImpl
->m_pURLObj
.reset();
3512 if (chkEditLock
.owns_lock())
3513 chkEditLock
.unlock();
3514 pImpl
->aContent
= ::ucbhelper::Content();
3519 const OUString
& SfxMedium::GetOrigURL() const
3521 return pImpl
->aOrigURL
.isEmpty() ? pImpl
->m_aLogicName
: pImpl
->aOrigURL
;
3525 void SfxMedium::SetPhysicalName_Impl( const OUString
& rNameP
)
3527 if ( rNameP
!= pImpl
->m_aName
)
3529 pImpl
->pTempFile
.reset();
3531 if ( !pImpl
->m_aName
.isEmpty() || !rNameP
.isEmpty() )
3532 pImpl
->aContent
= ::ucbhelper::Content();
3534 pImpl
->m_aName
= rNameP
;
3535 pImpl
->m_bTriedStorage
= false;
3536 pImpl
->bIsStorage
= false;
3540 void SfxMedium::ReOpen()
3542 bool bUseInteractionHandler
= pImpl
->bUseInteractionHandler
;
3543 pImpl
->bUseInteractionHandler
= false;
3545 pImpl
->bUseInteractionHandler
= bUseInteractionHandler
;
3548 void SfxMedium::CompleteReOpen()
3550 // do not use temporary file for reopen and in case of success throw the temporary file away
3551 bool bUseInteractionHandler
= pImpl
->bUseInteractionHandler
;
3552 pImpl
->bUseInteractionHandler
= false;
3554 std::unique_ptr
<MediumTempFile
> pTmpFile
;
3555 if ( pImpl
->pTempFile
)
3557 pTmpFile
= std::move(pImpl
->pTempFile
);
3558 pImpl
->m_aName
.clear();
3563 if ( GetErrorIgnoreWarning() )
3565 if ( pImpl
->pTempFile
)
3567 pImpl
->pTempFile
->EnableKillingFile();
3568 pImpl
->pTempFile
.reset();
3570 pImpl
->pTempFile
= std::move( pTmpFile
);
3571 if ( pImpl
->pTempFile
)
3572 pImpl
->m_aName
= pImpl
->pTempFile
->GetFileName();
3576 pTmpFile
->EnableKillingFile();
3580 pImpl
->bUseInteractionHandler
= bUseInteractionHandler
;
3583 SfxMedium::SfxMedium(const OUString
&rName
, StreamMode nOpenMode
, std::shared_ptr
<const SfxFilter
> pFilter
, const std::shared_ptr
<SfxItemSet
>& pInSet
) :
3584 pImpl(new SfxMedium_Impl
)
3586 pImpl
->m_pSet
= pInSet
;
3587 pImpl
->m_pFilter
= std::move(pFilter
);
3588 pImpl
->m_aLogicName
= rName
;
3589 pImpl
->m_nStorOpenMode
= nOpenMode
;
3593 SfxMedium::SfxMedium(const OUString
&rName
, const OUString
&rReferer
, StreamMode nOpenMode
, std::shared_ptr
<const SfxFilter
> pFilter
, const std::shared_ptr
<SfxItemSet
>& pInSet
) :
3594 pImpl(new SfxMedium_Impl
)
3596 pImpl
->m_pSet
= pInSet
;
3597 SfxItemSet
& s
= GetItemSet();
3598 if (s
.GetItem(SID_REFERER
) == nullptr) {
3599 s
.Put(SfxStringItem(SID_REFERER
, rReferer
));
3601 pImpl
->m_pFilter
= std::move(pFilter
);
3602 pImpl
->m_aLogicName
= rName
;
3603 pImpl
->m_nStorOpenMode
= nOpenMode
;
3607 SfxMedium::SfxMedium( const uno::Sequence
<beans::PropertyValue
>& aArgs
) :
3608 pImpl(new SfxMedium_Impl
)
3610 SfxAllItemSet
*pParams
= new SfxAllItemSet( SfxGetpApp()->GetPool() );
3611 pImpl
->m_pSet
.reset( pParams
);
3612 TransformParameters( SID_OPENDOC
, aArgs
, *pParams
);
3615 OUString aFilterProvider
, aFilterName
;
3617 const SfxStringItem
* pItem
= nullptr;
3618 if ((pItem
= pImpl
->m_pSet
->GetItemIfSet(SID_FILTER_PROVIDER
)))
3619 aFilterProvider
= pItem
->GetValue();
3621 if ((pItem
= pImpl
->m_pSet
->GetItemIfSet(SID_FILTER_NAME
)))
3622 aFilterName
= pItem
->GetValue();
3625 if (aFilterProvider
.isEmpty())
3627 // This is a conventional filter type.
3628 pImpl
->m_pFilter
= SfxGetpApp()->GetFilterMatcher().GetFilter4FilterName( aFilterName
);
3632 // This filter is from an external provider such as orcus.
3633 pImpl
->m_pCustomFilter
= std::make_shared
<SfxFilter
>(aFilterProvider
, aFilterName
);
3634 pImpl
->m_pFilter
= pImpl
->m_pCustomFilter
;
3637 const SfxStringItem
* pSalvageItem
= SfxItemSet::GetItem
<SfxStringItem
>(pImpl
->m_pSet
.get(), SID_DOC_SALVAGE
, false);
3640 // QUESTION: there is some treatment of Salvage in Init_Impl; align!
3641 if ( !pSalvageItem
->GetValue().isEmpty() )
3643 // if a URL is provided in SalvageItem that means that the FileName refers to a temporary file
3644 // that must be copied here
3646 const SfxStringItem
* pFileNameItem
= SfxItemSet::GetItem
<SfxStringItem
>(pImpl
->m_pSet
.get(), SID_FILE_NAME
, false);
3647 if (!pFileNameItem
) throw uno::RuntimeException();
3648 OUString aNewTempFileURL
= SfxMedium::CreateTempCopyWithExt( pFileNameItem
->GetValue() );
3649 if ( !aNewTempFileURL
.isEmpty() )
3651 pImpl
->m_pSet
->Put( SfxStringItem( SID_FILE_NAME
, aNewTempFileURL
) );
3652 pImpl
->m_pSet
->ClearItem( SID_INPUTSTREAM
);
3653 pImpl
->m_pSet
->ClearItem( SID_STREAM
);
3654 pImpl
->m_pSet
->ClearItem( SID_CONTENT
);
3658 SAL_WARN( "sfx.doc", "Can not create a new temporary file for crash recovery!" );
3663 const SfxBoolItem
* pReadOnlyItem
= SfxItemSet::GetItem
<SfxBoolItem
>(pImpl
->m_pSet
.get(), SID_DOC_READONLY
, false);
3664 if ( pReadOnlyItem
&& pReadOnlyItem
->GetValue() )
3665 pImpl
->m_bOriginallyLoadedReadOnly
= true;
3667 const SfxStringItem
* pFileNameItem
= SfxItemSet::GetItem
<SfxStringItem
>(pImpl
->m_pSet
.get(), SID_FILE_NAME
, false);
3668 if (!pFileNameItem
) throw uno::RuntimeException();
3669 pImpl
->m_aLogicName
= pFileNameItem
->GetValue();
3670 pImpl
->m_nStorOpenMode
= pImpl
->m_bOriginallyLoadedReadOnly
3671 ? SFX_STREAM_READONLY
: SFX_STREAM_READWRITE
;
3675 void SfxMedium::SetArgs(const uno::Sequence
<beans::PropertyValue
>& rArgs
)
3677 static constexpr OUStringLiteral
sStream(u
"Stream");
3678 static constexpr OUStringLiteral
sInputStream(u
"InputStream");
3679 comphelper::SequenceAsHashMap
aArgsMap(rArgs
);
3680 aArgsMap
.erase(sStream
);
3681 aArgsMap
.erase(sInputStream
);
3682 pImpl
->m_aArgs
= aArgsMap
.getAsConstPropertyValueList();
3685 const uno::Sequence
<beans::PropertyValue
> & SfxMedium::GetArgs() const { return pImpl
->m_aArgs
; }
3687 SfxMedium::SfxMedium( const uno::Reference
< embed::XStorage
>& rStor
, const OUString
& rBaseURL
, const std::shared_ptr
<SfxItemSet
>& p
) :
3688 pImpl(new SfxMedium_Impl
)
3690 OUString aType
= SfxFilter::GetTypeFromStorage(rStor
);
3691 pImpl
->m_pFilter
= SfxGetpApp()->GetFilterMatcher().GetFilter4EA( aType
);
3692 DBG_ASSERT( pImpl
->m_pFilter
, "No Filter for storage found!" );
3695 pImpl
->xStorage
= rStor
;
3696 pImpl
->bDisposeStorage
= false;
3698 // always take BaseURL first, could be overwritten by ItemSet
3699 GetItemSet().Put( SfxStringItem( SID_DOC_BASEURL
, rBaseURL
) );
3701 GetItemSet().Put( *p
);
3705 SfxMedium::SfxMedium( const uno::Reference
< embed::XStorage
>& rStor
, const OUString
& rBaseURL
, const OUString
&rTypeName
, const std::shared_ptr
<SfxItemSet
>& p
) :
3706 pImpl(new SfxMedium_Impl
)
3708 pImpl
->m_pFilter
= SfxGetpApp()->GetFilterMatcher().GetFilter4EA( rTypeName
);
3709 DBG_ASSERT( pImpl
->m_pFilter
, "No Filter for storage found!" );
3712 pImpl
->xStorage
= rStor
;
3713 pImpl
->bDisposeStorage
= false;
3715 // always take BaseURL first, could be overwritten by ItemSet
3716 GetItemSet().Put( SfxStringItem( SID_DOC_BASEURL
, rBaseURL
) );
3718 GetItemSet().Put( *p
);
3721 // NOTE: should only be called on main thread
3722 SfxMedium::~SfxMedium()
3724 CancelCheckEditableEntry();
3726 // if there is a requirement to clean the backup this is the last possibility to do it
3729 Close(/*bInDestruction*/true);
3731 if( !pImpl
->bIsTemp
|| pImpl
->m_aName
.isEmpty() )
3735 if ( osl::FileBase::getFileURLFromSystemPath( pImpl
->m_aName
, aTemp
)
3736 != osl::FileBase::E_None
)
3738 SAL_WARN( "sfx.doc", "Physical name not convertible!");
3741 if ( !::utl::UCBContentHelper::Kill( aTemp
) )
3743 SAL_WARN( "sfx.doc", "Couldn't remove temporary file!");
3747 const OUString
& SfxMedium::GetName() const
3749 return pImpl
->m_aLogicName
;
3752 const INetURLObject
& SfxMedium::GetURLObject() const
3754 std::unique_lock
<std::recursive_mutex
> chkEditLock
;
3755 if (pImpl
->m_pCheckEditableWorkerMutex
!= nullptr)
3756 chkEditLock
= std::unique_lock
<std::recursive_mutex
>(*(pImpl
->m_pCheckEditableWorkerMutex
));
3758 if (!pImpl
->m_pURLObj
)
3760 pImpl
->m_pURLObj
.reset( new INetURLObject( pImpl
->m_aLogicName
) );
3761 pImpl
->m_pURLObj
->SetMark(u
"");
3764 return *pImpl
->m_pURLObj
;
3767 void SfxMedium::SetExpired_Impl( const DateTime
& rDateTime
)
3769 pImpl
->aExpireTime
= rDateTime
;
3773 bool SfxMedium::IsExpired() const
3775 return pImpl
->aExpireTime
.IsValidAndGregorian() && pImpl
->aExpireTime
< DateTime( DateTime::SYSTEM
);
3779 SfxFrame
* SfxMedium::GetLoadTargetFrame() const
3781 return pImpl
->wLoadTargetFrame
;
3784 void SfxMedium::setStreamToLoadFrom(const css::uno::Reference
<css::io::XInputStream
>& xInputStream
, bool bIsReadOnly
)
3786 pImpl
->m_xInputStreamToLoadFrom
= xInputStream
;
3787 pImpl
->m_bInputStreamIsReadOnly
= bIsReadOnly
;
3790 void SfxMedium::SetLoadTargetFrame(SfxFrame
* pFrame
)
3792 pImpl
->wLoadTargetFrame
= pFrame
;
3795 void SfxMedium::SetStorage_Impl(const uno::Reference
<embed::XStorage
>& xStorage
)
3797 pImpl
->xStorage
= xStorage
;
3798 pImpl
->m_bODFWholesomeEncryption
= false;
3801 void SfxMedium::SetInnerStorage_Impl(const uno::Reference
<embed::XStorage
>& xStorage
)
3803 pImpl
->xStorage
= xStorage
;
3804 pImpl
->m_bODFWholesomeEncryption
= true;
3807 SfxItemSet
& SfxMedium::GetItemSet() const
3810 pImpl
->m_pSet
= std::make_shared
<SfxAllItemSet
>( SfxGetpApp()->GetPool() );
3811 return *pImpl
->m_pSet
;
3815 SvKeyValueIterator
* SfxMedium::GetHeaderAttributes_Impl()
3817 if( !pImpl
->xAttributes
.is() )
3819 pImpl
->xAttributes
= SvKeyValueIteratorRef( new SvKeyValueIterator
);
3821 if ( GetContent().is() )
3825 Any aAny
= pImpl
->aContent
.getPropertyValue("MediaType");
3826 OUString aContentType
;
3827 aAny
>>= aContentType
;
3829 pImpl
->xAttributes
->Append( SvKeyValue( "content-type", aContentType
) );
3831 catch ( const css::uno::Exception
& )
3837 return pImpl
->xAttributes
.get();
3840 css::uno::Reference
< css::io::XInputStream
> const & SfxMedium::GetInputStream()
3842 if ( !pImpl
->xInputStream
.is() )
3844 return pImpl
->xInputStream
;
3847 const uno::Sequence
< util::RevisionTag
>& SfxMedium::GetVersionList( bool _bNoReload
)
3849 // if the medium has no name, then this medium should represent a new document and can have no version info
3850 if ( ( !_bNoReload
|| !pImpl
->m_bVersionsAlreadyLoaded
) && !pImpl
->aVersions
.hasElements() &&
3851 ( !pImpl
->m_aName
.isEmpty() || !pImpl
->m_aLogicName
.isEmpty() ) && GetStorage().is() )
3853 uno::Reference
< document::XDocumentRevisionListPersistence
> xReader
=
3854 document::DocumentRevisionListPersistence::create( comphelper::getProcessComponentContext() );
3857 pImpl
->aVersions
= xReader
->load( GetStorage() );
3859 catch ( const uno::Exception
& )
3864 if ( !pImpl
->m_bVersionsAlreadyLoaded
)
3865 pImpl
->m_bVersionsAlreadyLoaded
= true;
3867 return pImpl
->aVersions
;
3870 uno::Sequence
< util::RevisionTag
> SfxMedium::GetVersionList( const uno::Reference
< embed::XStorage
>& xStorage
)
3872 uno::Reference
< document::XDocumentRevisionListPersistence
> xReader
=
3873 document::DocumentRevisionListPersistence::create( comphelper::getProcessComponentContext() );
3876 return xReader
->load( xStorage
);
3878 catch ( const uno::Exception
& )
3882 return uno::Sequence
< util::RevisionTag
>();
3885 void SfxMedium::AddVersion_Impl( util::RevisionTag
& rRevision
)
3887 if ( !GetStorage().is() )
3890 // To determine a unique name for the stream
3891 std::vector
<sal_uInt32
> aLongs
;
3892 sal_Int32 nLength
= pImpl
->aVersions
.getLength();
3893 for ( const auto& rVersion
: std::as_const(pImpl
->aVersions
) )
3895 sal_uInt32 nVer
= static_cast<sal_uInt32
>( o3tl::toInt32(rVersion
.Identifier
.subView(7)));
3897 for ( n
=0; n
<aLongs
.size(); ++n
)
3898 if ( nVer
<aLongs
[n
] )
3901 aLongs
.insert( aLongs
.begin()+n
, nVer
);
3904 std::vector
<sal_uInt32
>::size_type nKey
;
3905 for ( nKey
=0; nKey
<aLongs
.size(); ++nKey
)
3906 if ( aLongs
[nKey
] > nKey
+1 )
3909 OUString aRevName
= "Version" + OUString::number( nKey
+ 1 );
3910 pImpl
->aVersions
.realloc( nLength
+1 );
3911 rRevision
.Identifier
= aRevName
;
3912 pImpl
->aVersions
.getArray()[nLength
] = rRevision
;
3915 void SfxMedium::RemoveVersion_Impl( const OUString
& rName
)
3917 if ( !pImpl
->aVersions
.hasElements() )
3920 auto pVersion
= std::find_if(std::cbegin(pImpl
->aVersions
), std::cend(pImpl
->aVersions
),
3921 [&rName
](const auto& rVersion
) { return rVersion
.Identifier
== rName
; });
3922 if (pVersion
!= std::cend(pImpl
->aVersions
))
3924 auto nIndex
= static_cast<sal_Int32
>(std::distance(std::cbegin(pImpl
->aVersions
), pVersion
));
3925 comphelper::removeElementAt(pImpl
->aVersions
, nIndex
);
3929 bool SfxMedium::TransferVersionList_Impl( SfxMedium
const & rMedium
)
3931 if ( rMedium
.pImpl
->aVersions
.hasElements() )
3933 pImpl
->aVersions
= rMedium
.pImpl
->aVersions
;
3940 void SfxMedium::SaveVersionList_Impl()
3942 if ( !GetStorage().is() )
3945 if ( !pImpl
->aVersions
.hasElements() )
3948 uno::Reference
< document::XDocumentRevisionListPersistence
> xWriter
=
3949 document::DocumentRevisionListPersistence::create( comphelper::getProcessComponentContext() );
3952 xWriter
->store( GetStorage(), pImpl
->aVersions
);
3954 catch ( const uno::Exception
& )
3959 bool SfxMedium::IsReadOnly() const
3961 // a) ReadOnly filter can't produce read/write contents!
3962 bool bReadOnly
= pImpl
->m_pFilter
&& (pImpl
->m_pFilter
->GetFilterFlags() & SfxFilterFlags::OPENREADONLY
);
3964 // b) if filter allow read/write contents .. check open mode of the storage
3966 bReadOnly
= !( GetOpenMode() & StreamMode::WRITE
);
3968 // c) the API can force the readonly state!
3971 const SfxBoolItem
* pItem
= GetItemSet().GetItem(SID_DOC_READONLY
, false);
3973 bReadOnly
= pItem
->GetValue();
3979 bool SfxMedium::IsOriginallyReadOnly() const
3981 return pImpl
->m_bOriginallyReadOnly
;
3984 void SfxMedium::SetOriginallyReadOnly(bool val
)
3986 pImpl
->m_bOriginallyReadOnly
= val
;
3989 bool SfxMedium::IsOriginallyLoadedReadOnly() const
3991 return pImpl
->m_bOriginallyLoadedReadOnly
;
3994 bool SfxMedium::SetWritableForUserOnly( const OUString
& aURL
)
3996 // UCB does not allow to allow write access only for the user,
3998 bool bResult
= false;
4000 ::osl::DirectoryItem aDirItem
;
4001 if ( ::osl::DirectoryItem::get( aURL
, aDirItem
) == ::osl::FileBase::E_None
)
4003 ::osl::FileStatus
aFileStatus( osl_FileStatus_Mask_Attributes
);
4004 if ( aDirItem
.getFileStatus( aFileStatus
) == osl::FileBase::E_None
4005 && aFileStatus
.isValid( osl_FileStatus_Mask_Attributes
) )
4007 sal_uInt64 nAttributes
= aFileStatus
.getAttributes();
4009 nAttributes
&= ~(osl_File_Attribute_OwnWrite
|
4010 osl_File_Attribute_GrpWrite
|
4011 osl_File_Attribute_OthWrite
|
4012 osl_File_Attribute_ReadOnly
);
4013 nAttributes
|= (osl_File_Attribute_OwnWrite
|
4014 osl_File_Attribute_OwnRead
);
4016 bResult
= ( osl::File::setAttributes( aURL
, nAttributes
) == ::osl::FileBase::E_None
);
4025 /// Get the parent directory of a temporary file for output purposes.
4026 OUString
GetLogicBase(const INetURLObject
& rURL
, std::unique_ptr
<SfxMedium_Impl
> const & pImpl
)
4028 OUString aLogicBase
;
4030 #if HAVE_FEATURE_MACOSX_SANDBOX
4031 // In a sandboxed environment we don't want to attempt to create temporary files in the same
4032 // directory where the user has selected an output file to be stored. The sandboxed process has
4033 // permission only to create the specifically named output file in that directory.
4038 if (!pImpl
->m_bHasEmbeddedObjects
// Embedded objects would mean a special base, ignore that.
4039 && rURL
.GetProtocol() == INetProtocol::File
&& !pImpl
->m_pInStream
)
4041 // Try to create the temp file in the same directory when storing.
4042 INetURLObject
aURL(rURL
);
4043 aURL
.removeSegment();
4044 aLogicBase
= aURL
.GetMainURL(INetURLObject::DecodeMechanism::WithCharset
);
4047 #endif // !HAVE_FEATURE_MACOSX_SANDBOX
4053 void SfxMedium::CreateTempFile( bool bReplace
)
4055 if ( pImpl
->pTempFile
)
4060 pImpl
->pTempFile
.reset();
4061 pImpl
->m_aName
.clear();
4064 OUString aLogicBase
= GetLogicBase(GetURLObject(), pImpl
);
4065 pImpl
->pTempFile
.reset(new MediumTempFile(&aLogicBase
));
4066 pImpl
->pTempFile
->EnableKillingFile();
4067 pImpl
->m_aName
= pImpl
->pTempFile
->GetFileName();
4068 OUString aTmpURL
= pImpl
->pTempFile
->GetURL();
4069 if ( pImpl
->m_aName
.isEmpty() || aTmpURL
.isEmpty() )
4071 SetError(ERRCODE_IO_CANTWRITE
);
4075 if ( !(pImpl
->m_nStorOpenMode
& StreamMode::TRUNC
) )
4077 bool bTransferSuccess
= false;
4079 if ( GetContent().is()
4080 && GetURLObject().GetProtocol() == INetProtocol::File
4081 && ::utl::UCBContentHelper::IsDocument( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE
) ) )
4083 // if there is already such a document, we should copy it
4084 // if it is a file system use OS copy process
4087 uno::Reference
< css::ucb::XCommandEnvironment
> xComEnv
;
4088 INetURLObject
aTmpURLObj( aTmpURL
);
4089 OUString aFileName
= aTmpURLObj
.getName( INetURLObject::LAST_SEGMENT
,
4091 INetURLObject::DecodeMechanism::WithCharset
);
4092 if ( !aFileName
.isEmpty() && aTmpURLObj
.removeSegment() )
4094 ::ucbhelper::Content
aTargetContent( aTmpURLObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), xComEnv
, comphelper::getProcessComponentContext() );
4095 OUString sMimeType
= pImpl
->getFilterMimeType();
4096 aTargetContent
.transferContent( pImpl
->aContent
, ::ucbhelper::InsertOperation::Copy
, aFileName
, NameClash::OVERWRITE
, sMimeType
);
4097 SetWritableForUserOnly( aTmpURL
);
4098 bTransferSuccess
= true;
4101 catch( const uno::Exception
& )
4104 if ( bTransferSuccess
)
4111 if ( !bTransferSuccess
&& pImpl
->m_pInStream
)
4113 // the case when there is no URL-access available or this is a remote protocol
4114 // but there is an input stream
4116 if ( pImpl
->m_pOutStream
)
4118 std::unique_ptr
<char[]> pBuf(new char [8192]);
4119 ErrCode nErr
= ERRCODE_NONE
;
4121 pImpl
->m_pInStream
->Seek(0);
4122 pImpl
->m_pOutStream
->Seek(0);
4124 while( !pImpl
->m_pInStream
->eof() && nErr
== ERRCODE_NONE
)
4126 sal_uInt32 nRead
= pImpl
->m_pInStream
->ReadBytes(pBuf
.get(), 8192);
4127 nErr
= pImpl
->m_pInStream
->GetError();
4128 pImpl
->m_pOutStream
->WriteBytes( pBuf
.get(), nRead
);
4131 bTransferSuccess
= true;
4134 CloseOutStream_Impl();
4138 // Quite strange design, but currently it is expected that in this case no transfer happens
4139 // TODO/LATER: get rid of this inconsistent part of the call design
4140 bTransferSuccess
= true;
4144 if ( !bTransferSuccess
)
4146 SetError(ERRCODE_IO_CANTWRITE
);
4155 void SfxMedium::CreateTempFileNoCopy()
4157 // this call always replaces the existing temporary file
4158 pImpl
->pTempFile
.reset();
4160 OUString aLogicBase
= GetLogicBase(GetURLObject(), pImpl
);
4161 pImpl
->pTempFile
.reset(new MediumTempFile(&aLogicBase
));
4162 pImpl
->pTempFile
->EnableKillingFile();
4163 pImpl
->m_aName
= pImpl
->pTempFile
->GetFileName();
4164 if ( pImpl
->m_aName
.isEmpty() )
4166 SetError(ERRCODE_IO_CANTWRITE
);
4170 CloseOutStream_Impl();
4174 bool SfxMedium::SignDocumentContentUsingCertificate(
4175 const css::uno::Reference
<css::frame::XModel
>& xModel
, bool bHasValidDocumentSignature
,
4176 const Reference
<XCertificate
>& xCertificate
)
4178 bool bChanges
= false;
4180 if (IsOpen() || GetErrorIgnoreWarning())
4182 SAL_WARN("sfx.doc", "The medium must be closed by the signer!");
4186 // The component should know if there was a valid document signature, since
4187 // it should show a warning in this case
4188 OUString
aODFVersion(comphelper::OStorageHelper::GetODFVersionFromStorage(GetStorage()));
4189 uno::Reference
< security::XDocumentDigitalSignatures
> xSigner(
4190 security::DocumentDigitalSignatures::createWithVersionAndValidSignature(
4191 comphelper::getProcessComponentContext(), aODFVersion
, bHasValidDocumentSignature
) );
4192 auto xModelSigner
= dynamic_cast<sfx2::DigitalSignatures
*>(xSigner
.get());
4198 uno::Reference
< embed::XStorage
> xWriteableZipStor
;
4200 // we can reuse the temporary file if there is one already
4201 CreateTempFile( false );
4206 if ( !pImpl
->xStream
.is() )
4207 throw uno::RuntimeException();
4209 bool bODF
= GetFilter()->IsOwnFormat();
4212 xWriteableZipStor
= ::comphelper::OStorageHelper::GetStorageOfFormatFromStream( ZIP_STORAGE_FORMAT_STRING
, pImpl
->xStream
);
4214 catch (const io::IOException
&)
4218 TOOLS_WARN_EXCEPTION("sfx.doc", "ODF stream is not a zip storage");
4222 if ( !xWriteableZipStor
.is() && bODF
)
4223 throw uno::RuntimeException();
4225 uno::Reference
< embed::XStorage
> xMetaInf
;
4226 if (xWriteableZipStor
.is() && xWriteableZipStor
->hasByName("META-INF"))
4228 xMetaInf
= xWriteableZipStor
->openStorageElement(
4230 embed::ElementModes::READWRITE
);
4231 if ( !xMetaInf
.is() )
4232 throw uno::RuntimeException();
4239 uno::Reference
< io::XStream
> xStream
;
4240 if (GetFilter() && GetFilter()->IsOwnFormat())
4241 xStream
.set(xMetaInf
->openStreamElement(xSigner
->getDocumentContentSignatureDefaultStreamName(), embed::ElementModes::READWRITE
), uno::UNO_SET_THROW
);
4243 bool bSuccess
= xModelSigner
->SignModelWithCertificate(
4244 xModel
, xCertificate
, GetZipStorageToSign_Impl(), xStream
);
4248 uno::Reference
< embed::XTransactedObject
> xTransact( xMetaInf
, uno::UNO_QUERY_THROW
);
4249 xTransact
->commit();
4250 xTransact
.set( xWriteableZipStor
, uno::UNO_QUERY_THROW
);
4251 xTransact
->commit();
4253 // the temporary file has been written, commit it to the original file
4258 else if (xWriteableZipStor
.is())
4261 uno::Reference
<io::XStream
> xStream
;
4263 // We need read-write to be able to add the signature relation.
4264 bool bSuccess
= xModelSigner
->SignModelWithCertificate(
4265 xModel
, xCertificate
, GetZipStorageToSign_Impl(/*bReadOnly=*/false), xStream
);
4269 uno::Reference
<embed::XTransactedObject
> xTransact(xWriteableZipStor
, uno::UNO_QUERY_THROW
);
4270 xTransact
->commit();
4272 // the temporary file has been written, commit it to the original file
4279 // Something not ZIP based: e.g. PDF.
4280 std::unique_ptr
<SvStream
> pStream(utl::UcbStreamHelper::CreateStream(GetName(), StreamMode::READ
| StreamMode::WRITE
));
4281 uno::Reference
<io::XStream
> xStream(new utl::OStreamWrapper(*pStream
));
4282 if (xModelSigner
->SignModelWithCertificate(
4283 xModel
, xCertificate
, uno::Reference
<embed::XStorage
>(), xStream
))
4288 catch ( const uno::Exception
& )
4290 TOOLS_WARN_EXCEPTION("sfx.doc", "Couldn't use signing functionality!");
4300 // note: this is the only function creating scripting signature
4301 bool SfxMedium::SignContents_Impl(weld::Window
* pDialogParent
,
4302 bool bSignScriptingContent
,
4303 bool bHasValidDocumentSignature
,
4304 const OUString
& aSignatureLineId
,
4305 const Reference
<XCertificate
>& xCert
,
4306 const Reference
<XGraphic
>& xValidGraphic
,
4307 const Reference
<XGraphic
>& xInvalidGraphic
,
4308 const OUString
& aComment
)
4310 bool bChanges
= false;
4312 if (IsOpen() || GetErrorIgnoreWarning())
4314 SAL_WARN("sfx.doc", "The medium must be closed by the signer!");
4318 // The component should know if there was a valid document signature, since
4319 // it should show a warning in this case
4320 OUString
aODFVersion(comphelper::OStorageHelper::GetODFVersionFromStorage(GetStorage()));
4321 uno::Reference
< security::XDocumentDigitalSignatures
> xSigner(
4322 security::DocumentDigitalSignatures::createWithVersionAndValidSignature(
4323 comphelper::getProcessComponentContext(), aODFVersion
, bHasValidDocumentSignature
) );
4325 xSigner
->setParentWindow(pDialogParent
->GetXWindow());
4327 uno::Reference
< embed::XStorage
> xWriteableZipStor
;
4329 // we can reuse the temporary file if there is one already
4330 CreateTempFile( false );
4335 if ( !pImpl
->xStream
.is() )
4336 throw uno::RuntimeException();
4338 bool bODF
= GetFilter()->IsOwnFormat();
4341 if (pImpl
->m_bODFWholesomeEncryption
&& bSignScriptingContent
)
4343 assert(pImpl
->xStorage
); // GetStorage was called above
4344 assert(pImpl
->m_xODFDecryptedInnerPackageStream
);
4345 xWriteableZipStor
= ::comphelper::OStorageHelper::GetStorageOfFormatFromStream(
4346 ZIP_STORAGE_FORMAT_STRING
, pImpl
->m_xODFDecryptedInnerPackageStream
);
4350 xWriteableZipStor
= ::comphelper::OStorageHelper::GetStorageOfFormatFromStream(
4351 ZIP_STORAGE_FORMAT_STRING
, pImpl
->xStream
);
4354 catch (const io::IOException
&)
4358 TOOLS_WARN_EXCEPTION("sfx.doc", "ODF stream is not a zip storage");
4362 if ( !xWriteableZipStor
.is() && bODF
)
4363 throw uno::RuntimeException();
4365 uno::Reference
< embed::XStorage
> xMetaInf
;
4366 if (xWriteableZipStor
.is() && xWriteableZipStor
->hasByName("META-INF"))
4368 xMetaInf
= xWriteableZipStor
->openStorageElement(
4370 embed::ElementModes::READWRITE
);
4371 if ( !xMetaInf
.is() )
4372 throw uno::RuntimeException();
4375 if ( bSignScriptingContent
)
4377 // If the signature has already the document signature it will be removed
4378 // after the scripting signature is inserted.
4379 uno::Reference
< io::XStream
> xStream(
4380 xMetaInf
->openStreamElement( xSigner
->getScriptingContentSignatureDefaultStreamName(),
4381 embed::ElementModes::READWRITE
),
4382 uno::UNO_SET_THROW
);
4384 // note: the storage passed here must be independent from the
4385 // xWriteableZipStor because a writable storage can't have 2
4386 // instances of sub-storage for the same directory open, but with
4387 // independent storages it somehow works
4388 if (xSigner
->signScriptingContent(GetScriptingStorageToSign_Impl(), xStream
))
4390 // remove the document signature if any
4391 OUString aDocSigName
= xSigner
->getDocumentContentSignatureDefaultStreamName();
4392 if ( !aDocSigName
.isEmpty() && xMetaInf
->hasByName( aDocSigName
) )
4393 xMetaInf
->removeElement( aDocSigName
);
4395 uno::Reference
< embed::XTransactedObject
> xTransact( xMetaInf
, uno::UNO_QUERY_THROW
);
4396 xTransact
->commit();
4397 xTransact
.set( xWriteableZipStor
, uno::UNO_QUERY_THROW
);
4398 xTransact
->commit();
4400 if (pImpl
->m_bODFWholesomeEncryption
)
4401 { // manually copy the inner package to the outer one
4402 uno::Reference
<io::XSeekable
>(pImpl
->m_xODFDecryptedInnerPackageStream
, uno::UNO_QUERY_THROW
)->seek(0);
4403 uno::Reference
<io::XStream
> const xEncryptedPackage
=
4404 pImpl
->m_xODFEncryptedOuterStorage
->openStreamElement(
4405 "encrypted-package",
4406 embed::ElementModes::WRITE
|embed::ElementModes::TRUNCATE
);
4407 comphelper::OStorageHelper::CopyInputToOutput(pImpl
->m_xODFDecryptedInnerPackageStream
->getInputStream(), xEncryptedPackage
->getOutputStream());
4408 xTransact
.set(pImpl
->m_xODFEncryptedOuterStorage
, uno::UNO_QUERY_THROW
);
4409 xTransact
->commit(); // Commit() below won't do this
4412 assert(!pImpl
->xStorage
.is() // ensure this doesn't overwrite
4413 || !uno::Reference
<util::XModifiable
>(pImpl
->xStorage
, uno::UNO_QUERY_THROW
)->isModified());
4414 // the temporary file has been written, commit it to the original file
4424 uno::Reference
< io::XStream
> xStream
;
4425 if (GetFilter() && GetFilter()->IsOwnFormat())
4426 xStream
.set(xMetaInf
->openStreamElement(xSigner
->getDocumentContentSignatureDefaultStreamName(), embed::ElementModes::READWRITE
), uno::UNO_SET_THROW
);
4428 bool bSuccess
= false;
4430 bSuccess
= xSigner
->signSignatureLine(
4431 GetZipStorageToSign_Impl(), xStream
, aSignatureLineId
, xCert
,
4432 xValidGraphic
, xInvalidGraphic
, aComment
);
4434 bSuccess
= xSigner
->signDocumentContent(GetZipStorageToSign_Impl(),
4439 uno::Reference
< embed::XTransactedObject
> xTransact( xMetaInf
, uno::UNO_QUERY_THROW
);
4440 xTransact
->commit();
4441 xTransact
.set( xWriteableZipStor
, uno::UNO_QUERY_THROW
);
4442 xTransact
->commit();
4444 // the temporary file has been written, commit it to the original file
4449 else if (xWriteableZipStor
.is())
4452 uno::Reference
<io::XStream
> xStream
;
4454 bool bSuccess
= false;
4457 bSuccess
= xSigner
->signSignatureLine(
4458 GetZipStorageToSign_Impl(/*bReadOnly=*/false), xStream
, aSignatureLineId
,
4459 xCert
, xValidGraphic
, xInvalidGraphic
, aComment
);
4463 // We need read-write to be able to add the signature relation.
4464 bSuccess
=xSigner
->signDocumentContent(
4465 GetZipStorageToSign_Impl(/*bReadOnly=*/false), xStream
);
4470 uno::Reference
<embed::XTransactedObject
> xTransact(xWriteableZipStor
, uno::UNO_QUERY_THROW
);
4471 xTransact
->commit();
4473 // the temporary file has been written, commit it to the original file
4480 // Something not ZIP based: e.g. PDF.
4481 std::unique_ptr
<SvStream
> pStream(utl::UcbStreamHelper::CreateStream(GetName(), StreamMode::READ
| StreamMode::WRITE
));
4482 uno::Reference
<io::XStream
> xStream(new utl::OStreamWrapper(*pStream
));
4483 if (xSigner
->signDocumentContent(uno::Reference
<embed::XStorage
>(), xStream
))
4488 catch ( const uno::Exception
& )
4490 TOOLS_WARN_EXCEPTION("sfx.doc", "Couldn't use signing functionality!");
4501 SignatureState
SfxMedium::GetCachedSignatureState_Impl() const
4503 return pImpl
->m_nSignatureState
;
4507 void SfxMedium::SetCachedSignatureState_Impl( SignatureState nState
)
4509 pImpl
->m_nSignatureState
= nState
;
4512 void SfxMedium::SetHasEmbeddedObjects(bool bHasEmbeddedObjects
)
4514 pImpl
->m_bHasEmbeddedObjects
= bHasEmbeddedObjects
;
4517 bool SfxMedium::HasStorage_Impl() const
4519 return pImpl
->xStorage
.is();
4522 bool SfxMedium::IsOpen() const
4524 return pImpl
->m_pInStream
|| pImpl
->m_pOutStream
|| pImpl
->xStorage
.is();
4527 OUString
SfxMedium::CreateTempCopyWithExt( std::u16string_view aURL
)
4531 if ( !aURL
.empty() )
4533 size_t nPrefixLen
= aURL
.rfind( '.' );
4534 std::u16string_view aExt
= ( nPrefixLen
== std::u16string_view::npos
) ? std::u16string_view() : aURL
.substr( nPrefixLen
);
4536 OUString aNewTempFileURL
= ::utl::CreateTempURL( u
"", true, aExt
);
4537 if ( !aNewTempFileURL
.isEmpty() )
4539 INetURLObject
aSource( aURL
);
4540 INetURLObject
aDest( aNewTempFileURL
);
4541 OUString aFileName
= aDest
.getName( INetURLObject::LAST_SEGMENT
,
4543 INetURLObject::DecodeMechanism::WithCharset
);
4544 if ( !aFileName
.isEmpty() && aDest
.removeSegment() )
4548 uno::Reference
< css::ucb::XCommandEnvironment
> xComEnv
;
4549 ::ucbhelper::Content
aTargetContent( aDest
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), xComEnv
, comphelper::getProcessComponentContext() );
4550 ::ucbhelper::Content
aSourceContent( aSource
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), xComEnv
, comphelper::getProcessComponentContext() );
4551 aTargetContent
.transferContent( aSourceContent
,
4552 ::ucbhelper::InsertOperation::Copy
,
4554 NameClash::OVERWRITE
);
4555 aResult
= aNewTempFileURL
;
4557 catch( const uno::Exception
& )
4566 bool SfxMedium::CallApproveHandler(const uno::Reference
< task::XInteractionHandler
>& xHandler
, const uno::Any
& rRequest
, bool bAllowAbort
)
4568 bool bResult
= false;
4570 if ( xHandler
.is() )
4574 uno::Sequence
< uno::Reference
< task::XInteractionContinuation
> > aContinuations( bAllowAbort
? 2 : 1 );
4575 auto pContinuations
= aContinuations
.getArray();
4577 ::rtl::Reference
< ::comphelper::OInteractionApprove
> pApprove( new ::comphelper::OInteractionApprove
);
4578 pContinuations
[ 0 ] = pApprove
.get();
4582 ::rtl::Reference
< ::comphelper::OInteractionAbort
> pAbort( new ::comphelper::OInteractionAbort
);
4583 pContinuations
[ 1 ] = pAbort
.get();
4586 xHandler
->handle(::framework::InteractionRequest::CreateRequest(rRequest
, aContinuations
));
4587 bResult
= pApprove
->wasSelected();
4589 catch( const Exception
& )
4597 OUString
SfxMedium::SwitchDocumentToTempFile()
4599 // the method returns empty string in case of failure
4601 OUString aOrigURL
= pImpl
->m_aLogicName
;
4603 if ( !aOrigURL
.isEmpty() )
4605 sal_Int32 nPrefixLen
= aOrigURL
.lastIndexOf( '.' );
4606 std::u16string_view aExt
= (nPrefixLen
== -1)
4607 ? std::u16string_view()
4608 : aOrigURL
.subView(nPrefixLen
);
4609 OUString aNewURL
= ::utl::CreateTempURL( u
"", true, aExt
);
4611 // TODO/LATER: In future the aLogicName should be set to shared folder URL
4612 // and a temporary file should be created. Transport_Impl should be impossible then.
4613 if ( !aNewURL
.isEmpty() )
4615 uno::Reference
< embed::XStorage
> xStorage
= GetStorage();
4616 uno::Reference
< embed::XOptimizedStorage
> xOptStorage( xStorage
, uno::UNO_QUERY
);
4618 if ( xOptStorage
.is() )
4620 // TODO/LATER: reuse the pImpl->pTempFile if it already exists
4621 CanDisposeStorage_Impl( false );
4623 SetPhysicalName_Impl( OUString() );
4626 // remove the readonly state
4627 bool bWasReadonly
= false;
4628 pImpl
->m_nStorOpenMode
= SFX_STREAM_READWRITE
;
4629 const SfxBoolItem
* pReadOnlyItem
= SfxItemSet::GetItem
<SfxBoolItem
>(pImpl
->m_pSet
.get(), SID_DOC_READONLY
, false);
4630 if ( pReadOnlyItem
&& pReadOnlyItem
->GetValue() )
4631 bWasReadonly
= true;
4632 GetItemSet().ClearItem( SID_DOC_READONLY
);
4635 LockOrigFileOnDemand( false, false );
4639 if ( pImpl
->xStream
.is() )
4643 xOptStorage
->writeAndAttachToStream( pImpl
->xStream
);
4644 pImpl
->xStorage
= xStorage
;
4647 catch( const uno::Exception
& )
4653 // set the readonly state back
4654 pImpl
->m_nStorOpenMode
= SFX_STREAM_READONLY
;
4655 GetItemSet().Put(SfxBoolItem(SID_DOC_READONLY
, true));
4658 if ( aResult
.isEmpty() )
4661 SetPhysicalName_Impl( OUString() );
4662 SetName( aOrigURL
);
4664 pImpl
->xStorage
= xStorage
;
4673 bool SfxMedium::SwitchDocumentToFile( const OUString
& aURL
)
4675 // the method is only for storage based documents
4676 bool bResult
= false;
4677 OUString aOrigURL
= pImpl
->m_aLogicName
;
4679 if ( !aURL
.isEmpty() && !aOrigURL
.isEmpty() )
4681 uno::Reference
< embed::XStorage
> xStorage
= GetStorage();
4682 uno::Reference
< embed::XOptimizedStorage
> xOptStorage( xStorage
, uno::UNO_QUERY
);
4684 // TODO/LATER: reuse the pImpl->pTempFile if it already exists
4685 CanDisposeStorage_Impl( false );
4687 SetPhysicalName_Impl( OUString() );
4690 // open the temporary file based document
4692 LockOrigFileOnDemand( false, false );
4696 if ( pImpl
->xStream
.is() )
4700 uno::Reference
< io::XTruncate
> xTruncate( pImpl
->xStream
, uno::UNO_QUERY_THROW
);
4701 xTruncate
->truncate();
4702 if ( xOptStorage
.is() )
4703 xOptStorage
->writeAndAttachToStream( pImpl
->xStream
);
4704 pImpl
->xStorage
= xStorage
;
4707 catch( const uno::Exception
& )
4714 SetPhysicalName_Impl( OUString() );
4715 SetName( aOrigURL
);
4717 pImpl
->xStorage
= xStorage
;
4724 void SfxMedium::SetInCheckIn( bool bInCheckIn
)
4726 pImpl
->m_bInCheckIn
= bInCheckIn
;
4729 bool SfxMedium::IsInCheckIn( ) const
4731 return pImpl
->m_bInCheckIn
;
4734 // should only be called on main thread
4735 const std::shared_ptr
<std::recursive_mutex
>& SfxMedium::GetCheckEditableMutex() const
4737 return pImpl
->m_pCheckEditableWorkerMutex
;
4740 // should only be called while holding pImpl->m_pCheckEditableWorkerMutex
4741 void SfxMedium::SetWorkerReloadEvent(ImplSVEvent
* pEvent
)
4743 pImpl
->m_pReloadEvent
= pEvent
;
4746 // should only be called while holding pImpl->m_pCheckEditableWorkerMutex
4747 ImplSVEvent
* SfxMedium::GetWorkerReloadEvent() const
4749 return pImpl
->m_pReloadEvent
;
4752 // should only be called on main thread
4753 void SfxMedium::AddToCheckEditableWorkerList()
4755 if (!pImpl
->m_bNotifyWhenEditable
)
4758 CancelCheckEditableEntry();
4760 if (pImpl
->m_pCheckEditableWorkerMutex
== nullptr)
4762 pImpl
->m_pCheckEditableWorkerMutex
= std::make_shared
<std::recursive_mutex
>();
4763 if (pImpl
->m_pCheckEditableWorkerMutex
== nullptr)
4767 pImpl
->m_pIsDestructed
= std::make_shared
<bool>(false);
4768 if (pImpl
->m_pIsDestructed
== nullptr)
4771 std::unique_lock
<std::mutex
> globalLock(g_chkReadOnlyGlobalMutex
);
4772 if (g_newReadOnlyDocs
.find(this) == g_newReadOnlyDocs
.end())
4774 bool bAddNewEntry
= false;
4775 if (!g_bChkReadOnlyTaskRunning
)
4777 std::shared_ptr
<comphelper::ThreadTaskTag
> pTag
4778 = comphelper::ThreadPool::createThreadTaskTag();
4779 if (pTag
!= nullptr)
4781 g_bChkReadOnlyTaskRunning
= true;
4782 bAddNewEntry
= true;
4783 comphelper::ThreadPool::getSharedOptimalPool().pushTask(
4784 std::make_unique
<CheckReadOnlyTask
>(pTag
));
4788 bAddNewEntry
= true;
4792 std::shared_ptr
<ReadOnlyMediumEntry
> newEntry
= std::make_shared
<ReadOnlyMediumEntry
>(
4793 pImpl
->m_pCheckEditableWorkerMutex
, pImpl
->m_pIsDestructed
);
4795 if (newEntry
!= nullptr)
4797 g_newReadOnlyDocs
[this] = newEntry
;
4803 // should only be called on main thread
4804 void SfxMedium::CancelCheckEditableEntry(bool bRemoveEvent
)
4806 if (pImpl
->m_pCheckEditableWorkerMutex
!= nullptr)
4808 std::unique_lock
<std::recursive_mutex
> lock(*(pImpl
->m_pCheckEditableWorkerMutex
));
4810 if (pImpl
->m_pReloadEvent
!= nullptr)
4813 Application::RemoveUserEvent(pImpl
->m_pReloadEvent
);
4814 // make sure destructor doesn't use a freed reference
4815 // and reset the event so we can check again
4816 pImpl
->m_pReloadEvent
= nullptr;
4819 if (pImpl
->m_pIsDestructed
!= nullptr)
4821 *(pImpl
->m_pIsDestructed
) = true;
4822 pImpl
->m_pIsDestructed
= nullptr;
4827 /** callback function, which is triggered by worker thread after successfully checking if the file
4828 is editable. Sent from <Application::PostUserEvent(..)>
4829 Note: This method has to be run in the main thread.
4831 IMPL_STATIC_LINK(SfxMedium
, ShowReloadEditableDialog
, void*, p
, void)
4833 SfxMedium
* pMed
= static_cast<SfxMedium
*>(p
);
4834 if (pMed
== nullptr)
4837 pMed
->CancelCheckEditableEntry(false);
4839 uno::Reference
<task::XInteractionHandler
> xHandler
= pMed
->GetInteractionHandler();
4842 OUString aDocumentURL
4843 = pMed
->GetURLObject().GetLastName(INetURLObject::DecodeMechanism::WithCharset
);
4844 ::rtl::Reference
<::ucbhelper::InteractionRequest
> xInteractionRequestImpl
4845 = new ::ucbhelper::InteractionRequest(uno::Any(document::ReloadEditableRequest(
4846 OUString(), uno::Reference
<uno::XInterface
>(), aDocumentURL
)));
4847 if (xInteractionRequestImpl
!= nullptr)
4849 uno::Sequence
<uno::Reference
<task::XInteractionContinuation
>> aContinuations
{
4850 new ::ucbhelper::InteractionAbort(xInteractionRequestImpl
.get()),
4851 new ::ucbhelper::InteractionApprove(xInteractionRequestImpl
.get())
4853 xInteractionRequestImpl
->setContinuations(aContinuations
);
4854 xHandler
->handle(xInteractionRequestImpl
);
4855 ::rtl::Reference
<::ucbhelper::InteractionContinuation
> xSelected
4856 = xInteractionRequestImpl
->getSelection();
4857 if (uno::Reference
<task::XInteractionApprove
>(xSelected
.get(), uno::UNO_QUERY
).is())
4859 for (SfxViewFrame
* pFrame
= SfxViewFrame::GetFirst(); pFrame
;
4860 pFrame
= SfxViewFrame::GetNext(*pFrame
))
4862 if (pFrame
->GetObjectShell()->GetMedium() == pMed
)
4864 // special case to ensure view isn't set to read-only in
4865 // SfxViewFrame::ExecReload_Impl after reloading
4866 pMed
->SetOriginallyReadOnly(false);
4867 pFrame
->GetDispatcher()->Execute(SID_RELOAD
);
4876 bool SfxMedium::CheckCanGetLockfile() const
4878 #if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT
4879 bool bCanReload
= true;
4881 bool bCanReload
= false;
4882 ::svt::DocumentLockFile
aLockFile(GetName());
4883 LockFileEntry aData
;
4884 osl::DirectoryItem rItem
;
4885 auto nError1
= osl::DirectoryItem::get(aLockFile
.GetURL(), rItem
);
4886 if (nError1
== osl::FileBase::E_None
)
4890 aData
= aLockFile
.GetLockData();
4892 catch (const io::WrongFormatException
&)
4894 // we get empty or corrupt data
4897 catch (const uno::Exception
&)
4899 // locked from other app
4902 LockFileEntry aOwnData
= svt::LockFileCommon::GenerateOwnEntry();
4904 = aOwnData
[LockFileComponent::SYSUSERNAME
] == aData
[LockFileComponent::SYSUSERNAME
];
4906 && aOwnData
[LockFileComponent::LOCALHOST
] == aData
[LockFileComponent::LOCALHOST
]
4907 && aOwnData
[LockFileComponent::USERURL
] == aData
[LockFileComponent::USERURL
])
4909 // this is own lock from the same installation, it could remain because of crash
4913 else if (nError1
== osl::FileBase::E_NOENT
) // file doesn't exist
4917 aLockFile
.CreateOwnLockFile();
4920 // TODO/LATER: A warning could be shown in case the file is not the own one
4921 aLockFile
.RemoveFile();
4923 catch (const io::WrongFormatException
&)
4927 // erase the empty or corrupt file
4928 aLockFile
.RemoveFileDirectly();
4930 catch (const uno::Exception
&)
4936 catch (const uno::Exception
&)
4944 // worker thread method, should only be one thread globally
4945 void CheckReadOnlyTask::doWork()
4947 if (m_xListener
== nullptr)
4952 std::unique_lock
<std::mutex
> termLock(m_xListener
->mMutex
);
4953 if (m_xListener
->mCond
.wait_for(termLock
, std::chrono::seconds(60),
4954 [this] { return m_xListener
->bIsTerminated
; }))
4955 // signalled, spurious wakeups should not be possible
4958 // must have timed-out
4960 std::unique_lock
<std::mutex
> globalLock(g_chkReadOnlyGlobalMutex
);
4961 for (auto it
= g_newReadOnlyDocs
.begin(); it
!= g_newReadOnlyDocs
.end(); )
4963 auto [pMed
, roEntry
] = *it
;
4964 g_existingReadOnlyDocs
[pMed
] = roEntry
;
4965 it
= g_newReadOnlyDocs
.erase(it
);
4967 if (g_existingReadOnlyDocs
.size() == 0)
4969 g_bChkReadOnlyTaskRunning
= false;
4972 globalLock
.unlock();
4974 auto checkForErase
= [](SfxMedium
* pMed
, const std::shared_ptr
<ReadOnlyMediumEntry
>& roEntry
) -> bool
4976 if (pMed
== nullptr || roEntry
== nullptr || roEntry
->_pMutex
== nullptr
4977 || roEntry
->_pIsDestructed
== nullptr)
4980 std::unique_lock
<std::recursive_mutex
> medLock(*(roEntry
->_pMutex
));
4981 if (*(roEntry
->_pIsDestructed
) || pMed
->GetWorkerReloadEvent() != nullptr)
4985 pMed
->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::WithCharset
));
4986 if (aFile
.open(osl_File_OpenFlag_Write
) != osl::FileBase::E_None
)
4989 if (!pMed
->CheckCanGetLockfile())
4992 if (aFile
.close() != osl::FileBase::E_None
)
4995 // we can load, ask user
4996 ImplSVEvent
* pEvent
= Application::PostUserEvent(
4997 LINK(nullptr, SfxMedium
, ShowReloadEditableDialog
), pMed
);
4998 pMed
->SetWorkerReloadEvent(pEvent
);
5002 for (auto it
= g_existingReadOnlyDocs
.begin(); it
!= g_existingReadOnlyDocs
.end(); )
5004 if (checkForErase(it
->first
, it
->second
))
5005 it
= g_existingReadOnlyDocs
.erase(it
);
5012 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */