LanguageTool: don't crash if REST protocol isn't set
[LibreOffice.git] / sfx2 / source / doc / docfile.cxx
blob588e349a2e5bc0f8e1f6a6956931f8ed10b6ec3c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2 /*
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>
22 #ifdef UNX
23 #include <sys/stat.h>
24 #endif
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/container/XChild.hpp>
34 #include <com/sun/star/document/XDocumentRevisionListPersistence.hpp>
35 #include <com/sun/star/document/LockedDocumentRequest.hpp>
36 #include <com/sun/star/document/LockedOnSavingRequest.hpp>
37 #include <com/sun/star/document/OwnLockOnDocumentRequest.hpp>
38 #include <com/sun/star/document/LockFileIgnoreRequest.hpp>
39 #include <com/sun/star/document/LockFileCorruptRequest.hpp>
40 #include <com/sun/star/document/ChangedByOthersRequest.hpp>
41 #include <com/sun/star/document/ReloadEditableRequest.hpp>
42 #include <com/sun/star/embed/XTransactedObject.hpp>
43 #include <com/sun/star/embed/ElementModes.hpp>
44 #include <com/sun/star/embed/UseBackupException.hpp>
45 #include <com/sun/star/embed/XOptimizedStorage.hpp>
46 #include <com/sun/star/frame/Desktop.hpp>
47 #include <com/sun/star/frame/XModel.hpp>
48 #include <com/sun/star/frame/XTerminateListener.hpp>
49 #include <com/sun/star/graphic/XGraphic.hpp>
50 #include <com/sun/star/ucb/ContentCreationException.hpp>
51 #include <com/sun/star/ucb/InteractiveIOException.hpp>
52 #include <com/sun/star/ucb/CommandFailedException.hpp>
53 #include <com/sun/star/ucb/CommandAbortedException.hpp>
54 #include <com/sun/star/ucb/InteractiveLockingLockedException.hpp>
55 #include <com/sun/star/ucb/InteractiveNetworkWriteException.hpp>
56 #include <com/sun/star/ucb/Lock.hpp>
57 #include <com/sun/star/ucb/NameClashException.hpp>
58 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
59 #include <com/sun/star/ucb/XProgressHandler.hpp>
60 #include <com/sun/star/io/XOutputStream.hpp>
61 #include <com/sun/star/io/XInputStream.hpp>
62 #include <com/sun/star/io/XTruncate.hpp>
63 #include <com/sun/star/io/XSeekable.hpp>
64 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
65 #include <com/sun/star/ucb/InsertCommandArgument.hpp>
66 #include <com/sun/star/ucb/NameClash.hpp>
67 #include <com/sun/star/beans/NamedValue.hpp>
68 #include <com/sun/star/beans/PropertyValue.hpp>
69 #include <com/sun/star/security/DocumentDigitalSignatures.hpp>
70 #include <com/sun/star/security/XCertificate.hpp>
71 #include <tools/urlobj.hxx>
72 #include <tools/fileutil.hxx>
73 #include <unotools/configmgr.hxx>
74 #include <unotools/tempfile.hxx>
75 #include <comphelper/fileurl.hxx>
76 #include <comphelper/processfactory.hxx>
77 #include <comphelper/propertyvalue.hxx>
78 #include <comphelper/interaction.hxx>
79 #include <comphelper/sequence.hxx>
80 #include <comphelper/simplefileaccessinteraction.hxx>
81 #include <framework/interaction.hxx>
82 #include <utility>
83 #include <svl/stritem.hxx>
84 #include <svl/eitem.hxx>
85 #include <svtools/sfxecode.hxx>
86 #include <svl/itemset.hxx>
87 #include <svl/intitem.hxx>
88 #include <svtools/svparser.hxx>
89 #include <sal/log.hxx>
91 #include <unotools/streamwrap.hxx>
93 #include <osl/file.hxx>
95 #include <comphelper/storagehelper.hxx>
96 #include <unotools/mediadescriptor.hxx>
97 #include <comphelper/docpasswordhelper.hxx>
98 #include <tools/datetime.hxx>
99 #include <unotools/pathoptions.hxx>
100 #include <svtools/asynclink.hxx>
101 #include <ucbhelper/commandenvironment.hxx>
102 #include <unotools/ucbstreamhelper.hxx>
103 #include <unotools/ucbhelper.hxx>
104 #include <unotools/progresshandlerwrap.hxx>
105 #include <ucbhelper/content.hxx>
106 #include <ucbhelper/interactionrequest.hxx>
107 #include <sot/storage.hxx>
108 #include <unotools/saveopt.hxx>
109 #include <svl/documentlockfile.hxx>
110 #include <svl/msodocumentlockfile.hxx>
111 #include <com/sun/star/document/DocumentRevisionListPersistence.hpp>
113 #include <sfx2/app.hxx>
114 #include <sfx2/frame.hxx>
115 #include <sfx2/dispatch.hxx>
116 #include <sfx2/fcontnr.hxx>
117 #include <sfx2/docfilt.hxx>
118 #include <sfx2/sfxsids.hrc>
119 #include <sfx2/sfxuno.hxx>
120 #include <openflag.hxx>
121 #include <officecfg/Office/Common.hxx>
122 #include <comphelper/propertysequence.hxx>
123 #include <vcl/weld.hxx>
124 #include <vcl/svapp.hxx>
125 #include <tools/diagnose_ex.h>
126 #include <unotools/fltrcfg.hxx>
127 #include <sfx2/digitalsignatures.hxx>
128 #include <sfx2/viewfrm.hxx>
129 #include <comphelper/threadpool.hxx>
130 #include <condition_variable>
131 #include <comphelper/scopeguard.hxx>
133 #include <com/sun/star/io/WrongFormatException.hpp>
135 #include <memory>
137 using namespace ::com::sun::star;
138 using namespace ::com::sun::star::graphic;
139 using namespace ::com::sun::star::uno;
140 using namespace ::com::sun::star::ucb;
141 using namespace ::com::sun::star::beans;
142 using namespace ::com::sun::star::io;
143 using namespace ::com::sun::star::security;
145 namespace
148 struct ReadOnlyMediumEntry
150 ReadOnlyMediumEntry(std::shared_ptr<std::recursive_mutex> pMutex,
151 std::shared_ptr<bool> pIsDestructed)
152 : _pMutex(pMutex)
153 , _pIsDestructed(pIsDestructed)
156 std::shared_ptr<std::recursive_mutex> _pMutex;
157 std::shared_ptr<bool> _pIsDestructed;
162 static std::mutex g_chkReadOnlyGlobalMutex;
163 static bool g_bChkReadOnlyTaskRunning = false;
164 static std::unordered_map<SfxMedium*, std::shared_ptr<ReadOnlyMediumEntry>> g_newReadOnlyDocs;
165 static std::unordered_map<SfxMedium*, std::shared_ptr<ReadOnlyMediumEntry>> g_existingReadOnlyDocs;
167 namespace {
169 #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
171 bool IsSystemFileLockingUsed()
173 #if HAVE_FEATURE_MACOSX_SANDBOX
174 return true;
175 #else
176 return officecfg::Office::Common::Misc::UseDocumentSystemFileLocking::get();
177 #endif
181 bool IsOOoLockFileUsed()
183 #if HAVE_FEATURE_MACOSX_SANDBOX
184 return false;
185 #else
186 return officecfg::Office::Common::Misc::UseDocumentOOoLockFile::get();
187 #endif
190 bool IsLockingUsed()
192 return officecfg::Office::Common::Misc::UseLocking::get();
195 #endif
197 #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
198 bool IsWebDAVLockingUsed()
200 return officecfg::Office::Common::Misc::UseWebDAVFileLocking::get();
202 #endif
204 /// Gets default attributes of a file:// URL.
205 sal_uInt64 GetDefaultFileAttributes(const OUString& rURL)
207 sal_uInt64 nRet = 0;
209 if (!comphelper::isFileUrl(rURL))
210 return nRet;
212 // Make sure the file exists (and create it if not).
213 osl::File aFile(rURL);
214 osl::File::RC nRes = aFile.open(osl_File_OpenFlag_Create);
215 if (nRes != osl::File::E_None && nRes != osl::File::E_EXIST)
216 return nRet;
218 aFile.close();
220 osl::DirectoryItem aItem;
221 if (osl::DirectoryItem::get(rURL, aItem) != osl::DirectoryItem::E_None)
222 return nRet;
224 osl::FileStatus aStatus(osl_FileStatus_Mask_Attributes);
225 if (aItem.getFileStatus(aStatus) != osl::DirectoryItem::E_None)
226 return nRet;
228 nRet = aStatus.getAttributes();
229 return nRet;
232 /// Determines if rURL is safe to move or not.
233 bool IsFileMovable(const INetURLObject& rURL)
235 #ifdef MACOSX
236 (void)rURL;
237 // Hide extension macOS-specific file property would be lost.
238 return false;
239 #else
241 if (rURL.GetProtocol() != INetProtocol::File)
242 // Not a file:// URL.
243 return false;
245 #ifdef UNX
246 OUString sPath = rURL.getFSysPath(FSysStyle::Unix);
247 if (sPath.isEmpty())
248 return false;
250 struct stat buf;
251 if (lstat(sPath.toUtf8().getStr(), &buf) != 0)
252 return false;
254 // Hardlink or symlink: osl::File::move() doesn't play with these nicely.
255 if (buf.st_nlink > 1 || S_ISLNK(buf.st_mode))
256 return false;
257 #elif defined _WIN32
258 if (tools::IsMappedWebDAVPath(rURL.GetMainURL(INetURLObject::DecodeMechanism::NONE)))
259 return false;
260 #endif
262 return true;
263 #endif
266 class CheckReadOnlyTaskTerminateListener
267 : public ::cppu::WeakImplHelper<css::frame::XTerminateListener>
269 public:
270 // XEventListener
271 void SAL_CALL disposing(const css::lang::EventObject& Source) override;
273 // XTerminateListener
274 void SAL_CALL queryTermination(const css::lang::EventObject& aEvent) override;
275 void SAL_CALL notifyTermination(const css::lang::EventObject& aEvent) override;
277 bool bIsTerminated = false;
278 std::condition_variable mCond;
279 std::mutex mMutex;
282 class CheckReadOnlyTask : public comphelper::ThreadTask
284 public:
285 CheckReadOnlyTask(const std::shared_ptr<comphelper::ThreadTaskTag>& pTag);
286 ~CheckReadOnlyTask();
288 virtual void doWork() override;
290 private:
291 rtl::Reference<CheckReadOnlyTaskTerminateListener> m_xListener;
294 } // anonymous namespace
296 CheckReadOnlyTask::CheckReadOnlyTask(const std::shared_ptr<comphelper::ThreadTaskTag>& pTag)
297 : ThreadTask(pTag)
298 , m_xListener(new CheckReadOnlyTaskTerminateListener)
300 Reference<css::frame::XDesktop> xDesktop
301 = css::frame::Desktop::create(comphelper::getProcessComponentContext());
302 if (xDesktop.is() && m_xListener != nullptr)
304 xDesktop->addTerminateListener(m_xListener);
308 CheckReadOnlyTask::~CheckReadOnlyTask()
310 Reference<css::frame::XDesktop> xDesktop
311 = css::frame::Desktop::create(comphelper::getProcessComponentContext());
312 if (xDesktop.is() && m_xListener != nullptr)
314 std::unique_lock<std::mutex> lock(m_xListener->mMutex);
315 if (!m_xListener->bIsTerminated)
317 lock.unlock();
318 xDesktop->removeTerminateListener(m_xListener);
323 namespace
325 void SAL_CALL
326 CheckReadOnlyTaskTerminateListener::disposing(const css::lang::EventObject& /*Source*/)
330 void SAL_CALL
331 CheckReadOnlyTaskTerminateListener::queryTermination(const css::lang::EventObject& /*aEvent*/)
335 void SAL_CALL
336 CheckReadOnlyTaskTerminateListener::notifyTermination(const css::lang::EventObject& /*aEvent*/)
338 std::unique_lock<std::mutex> lock(mMutex);
339 bIsTerminated = true;
340 lock.unlock();
341 mCond.notify_one();
345 class SfxMedium_Impl
347 public:
348 StreamMode m_nStorOpenMode;
349 ErrCode m_eError;
351 ::ucbhelper::Content aContent;
352 bool bUpdatePickList:1;
353 bool bIsTemp:1;
354 bool bDownloadDone:1;
355 bool bIsStorage:1;
356 bool bUseInteractionHandler:1;
357 bool bAllowDefaultIntHdl:1;
358 bool bDisposeStorage:1;
359 bool bStorageBasedOnInStream:1;
360 bool m_bSalvageMode:1;
361 bool m_bVersionsAlreadyLoaded:1;
362 bool m_bLocked:1;
363 bool m_bMSOLockFileCreated : 1;
364 bool m_bDisableUnlockWebDAV:1;
365 bool m_bGotDateTime:1;
366 bool m_bRemoveBackup:1;
367 bool m_bOriginallyReadOnly:1;
368 bool m_bOriginallyLoadedReadOnly:1;
369 bool m_bTriedStorage:1;
370 bool m_bRemote:1;
371 bool m_bInputStreamIsReadOnly:1;
372 bool m_bInCheckIn:1;
373 bool m_bDisableFileSync = false;
374 bool m_bNotifyWhenEditable = false;
376 OUString m_aName;
377 OUString m_aLogicName;
378 OUString m_aLongName;
380 mutable std::shared_ptr<SfxItemSet> m_pSet;
381 mutable std::unique_ptr<INetURLObject> m_pURLObj;
383 std::shared_ptr<const SfxFilter> m_pFilter;
384 std::shared_ptr<const SfxFilter> m_pCustomFilter;
386 std::shared_ptr<std::recursive_mutex> m_pCheckEditableWorkerMutex;
387 std::shared_ptr<bool> m_pIsDestructed;
388 ImplSVEvent* m_pReloadEvent;
390 std::unique_ptr<SvStream> m_pInStream;
391 std::unique_ptr<SvStream> m_pOutStream;
393 OUString aOrigURL;
394 DateTime aExpireTime;
395 SfxFrameWeakRef wLoadTargetFrame;
396 SvKeyValueIteratorRef xAttributes;
398 svtools::AsynchronLink aDoneLink;
400 uno::Sequence < util::RevisionTag > aVersions;
402 std::unique_ptr<::utl::TempFile> pTempFile;
404 uno::Reference<embed::XStorage> xStorage;
405 uno::Reference<embed::XStorage> m_xZipStorage;
406 uno::Reference<io::XInputStream> m_xInputStreamToLoadFrom;
407 uno::Reference<io::XInputStream> xInputStream;
408 uno::Reference<io::XStream> xStream;
409 uno::Reference<io::XStream> m_xLockingStream;
410 uno::Reference<task::XInteractionHandler> xInteraction;
412 ErrCode nLastStorageError;
414 OUString m_aBackupURL;
416 // the following member is changed and makes sense only during saving
417 // TODO/LATER: in future the signature state should be controlled by the medium not by the document
418 // in this case the member will hold this information
419 SignatureState m_nSignatureState;
421 bool m_bHasEmbeddedObjects = false;
423 util::DateTime m_aDateTime;
425 uno::Sequence<beans::PropertyValue> m_aArgs;
427 explicit SfxMedium_Impl();
428 ~SfxMedium_Impl();
429 SfxMedium_Impl(const SfxMedium_Impl&) = delete;
430 SfxMedium_Impl& operator=(const SfxMedium_Impl&) = delete;
432 OUString getFilterMimeType() const
433 { return !m_pFilter ? OUString() : m_pFilter->GetMimeType(); }
436 SfxMedium_Impl::SfxMedium_Impl() :
437 m_nStorOpenMode(SFX_STREAM_READWRITE),
438 m_eError(ERRCODE_NONE),
439 bUpdatePickList(true),
440 bIsTemp( false ),
441 bDownloadDone( true ),
442 bIsStorage( false ),
443 bUseInteractionHandler( true ),
444 bAllowDefaultIntHdl( false ),
445 bDisposeStorage( false ),
446 bStorageBasedOnInStream( false ),
447 m_bSalvageMode( false ),
448 m_bVersionsAlreadyLoaded( false ),
449 m_bLocked( false ),
450 m_bMSOLockFileCreated( false ),
451 m_bDisableUnlockWebDAV( false ),
452 m_bGotDateTime( false ),
453 m_bRemoveBackup( false ),
454 m_bOriginallyReadOnly(false),
455 m_bOriginallyLoadedReadOnly(false),
456 m_bTriedStorage(false),
457 m_bRemote(false),
458 m_bInputStreamIsReadOnly(false),
459 m_bInCheckIn(false),
460 m_pReloadEvent(nullptr),
461 aExpireTime( DateTime( DateTime::SYSTEM ) + static_cast<sal_Int32>(10) ),
462 nLastStorageError( ERRCODE_NONE ),
463 m_nSignatureState( SignatureState::NOSIGNATURES )
468 SfxMedium_Impl::~SfxMedium_Impl()
470 aDoneLink.ClearPendingCall();
472 pTempFile.reset();
473 m_pSet.reset();
474 std::unique_lock<std::recursive_mutex> chkEditLock;
475 if (m_pCheckEditableWorkerMutex != nullptr)
476 chkEditLock = std::unique_lock<std::recursive_mutex>(*m_pCheckEditableWorkerMutex);
477 m_pURLObj.reset();
480 void SfxMedium::ResetError()
482 pImpl->m_eError = ERRCODE_NONE;
483 if( pImpl->m_pInStream )
484 pImpl->m_pInStream->ResetError();
485 if( pImpl->m_pOutStream )
486 pImpl->m_pOutStream->ResetError();
489 ErrCode const & SfxMedium::GetLastStorageCreationState() const
491 return pImpl->nLastStorageError;
494 void SfxMedium::SetError(ErrCode nError)
496 pImpl->m_eError = nError;
499 ErrCode SfxMedium::GetErrorCode() const
501 ErrCode lError = pImpl->m_eError;
502 if(!lError && pImpl->m_pInStream)
503 lError = pImpl->m_pInStream->GetErrorCode();
504 if(!lError && pImpl->m_pOutStream)
505 lError = pImpl->m_pOutStream->GetErrorCode();
506 return lError;
509 void SfxMedium::CheckFileDate( const util::DateTime& aInitDate )
511 GetInitFileDate( true );
512 if ( pImpl->m_aDateTime.Seconds == aInitDate.Seconds
513 && pImpl->m_aDateTime.Minutes == aInitDate.Minutes
514 && pImpl->m_aDateTime.Hours == aInitDate.Hours
515 && pImpl->m_aDateTime.Day == aInitDate.Day
516 && pImpl->m_aDateTime.Month == aInitDate.Month
517 && pImpl->m_aDateTime.Year == aInitDate.Year )
518 return;
520 uno::Reference< task::XInteractionHandler > xHandler = GetInteractionHandler();
522 if ( !xHandler.is() )
523 return;
527 ::rtl::Reference< ::ucbhelper::InteractionRequest > xInteractionRequestImpl = new ::ucbhelper::InteractionRequest( uno::makeAny(
528 document::ChangedByOthersRequest() ) );
529 uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations{
530 new ::ucbhelper::InteractionAbort( xInteractionRequestImpl.get() ),
531 new ::ucbhelper::InteractionApprove( xInteractionRequestImpl.get() )
533 xInteractionRequestImpl->setContinuations( aContinuations );
535 xHandler->handle( xInteractionRequestImpl );
537 ::rtl::Reference< ::ucbhelper::InteractionContinuation > xSelected = xInteractionRequestImpl->getSelection();
538 if ( uno::Reference< task::XInteractionAbort >( xSelected.get(), uno::UNO_QUERY ).is() )
540 SetError(ERRCODE_ABORT);
543 catch ( const uno::Exception& )
547 bool SfxMedium::DocNeedsFileDateCheck() const
549 return ( !IsReadOnly() && ( GetURLObject().GetProtocol() == INetProtocol::File ||
550 GetURLObject().isAnyKnownWebDAVScheme() ) );
553 util::DateTime const & SfxMedium::GetInitFileDate( bool bIgnoreOldValue )
555 if ( ( bIgnoreOldValue || !pImpl->m_bGotDateTime ) && !pImpl->m_aLogicName.isEmpty() )
559 // add a default css::ucb::XCommandEnvironment
560 // in order to have the WebDAV UCP provider manage http/https authentication correctly
561 ::ucbhelper::Content aContent( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ),
562 utl::UCBContentHelper::getDefaultCommandEnvironment(),
563 comphelper::getProcessComponentContext() );
565 aContent.getPropertyValue("DateModified") >>= pImpl->m_aDateTime;
566 pImpl->m_bGotDateTime = true;
568 catch ( const css::uno::Exception& )
573 return pImpl->m_aDateTime;
577 Reference < XContent > SfxMedium::GetContent() const
579 if ( !pImpl->aContent.get().is() )
581 Reference < css::ucb::XContent > xContent;
583 // tdf#95144 add a default css::ucb::XCommandEnvironment
584 // in order to have the WebDAV UCP provider manage https protocol certificates correctly
585 css:: uno::Reference< task::XInteractionHandler > xIH(
586 css::task::InteractionHandler::createWithParent( comphelper::getProcessComponentContext(), nullptr ) );
588 css::uno::Reference< css::ucb::XProgressHandler > xProgress;
589 rtl::Reference<::ucbhelper::CommandEnvironment> pCommandEnv = new ::ucbhelper::CommandEnvironment( new comphelper::SimpleFileAccessInteraction( xIH ), xProgress );
591 const SfxUnoAnyItem* pItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_CONTENT, false);
592 if ( pItem )
593 pItem->GetValue() >>= xContent;
595 if ( xContent.is() )
599 pImpl->aContent = ::ucbhelper::Content( xContent, pCommandEnv, comphelper::getProcessComponentContext() );
601 catch ( const Exception& )
605 else
607 // TODO: SAL_WARN( "sfx.doc", "SfxMedium::GetContent()\nCreate Content? This code exists as fallback only. Please clarify, why it's used.");
608 OUString aURL;
609 if ( !pImpl->m_aName.isEmpty() )
610 osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aURL );
611 else if ( !pImpl->m_aLogicName.isEmpty() )
612 aURL = GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE );
613 if (!aURL.isEmpty() )
614 (void)::ucbhelper::Content::create( aURL, pCommandEnv, comphelper::getProcessComponentContext(), pImpl->aContent );
618 return pImpl->aContent.get();
621 OUString SfxMedium::GetBaseURL( bool bForSaving )
623 OUString aBaseURL;
624 const SfxStringItem* pBaseURLItem = GetItemSet()->GetItem<SfxStringItem>(SID_DOC_BASEURL);
625 if ( pBaseURLItem )
626 aBaseURL = pBaseURLItem->GetValue();
627 else if (!utl::ConfigManager::IsFuzzing() && GetContent().is())
631 Any aAny = pImpl->aContent.getPropertyValue("BaseURI");
632 aAny >>= aBaseURL;
634 catch ( const css::uno::Exception& )
638 if ( aBaseURL.isEmpty() )
639 aBaseURL = GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE );
642 if ( bForSaving )
644 bool bIsRemote = IsRemote();
645 if( (bIsRemote && !officecfg::Office::Common::Save::URL::Internet::get())
646 || (!pImpl->m_bRemote && !officecfg::Office::Common::Save::URL::FileSystem::get()) )
647 return OUString();
650 return aBaseURL;
653 bool SfxMedium::IsSkipImages() const
655 const SfxStringItem* pSkipImagesItem = GetItemSet()->GetItem<SfxStringItem>(SID_FILE_FILTEROPTIONS);
656 return pSkipImagesItem && pSkipImagesItem->GetValue() == "SkipImages";
659 SvStream* SfxMedium::GetInStream()
661 if ( pImpl->m_pInStream )
662 return pImpl->m_pInStream.get();
664 if ( pImpl->pTempFile )
666 pImpl->m_pInStream.reset( new SvFileStream(pImpl->m_aName, pImpl->m_nStorOpenMode) );
668 pImpl->m_eError = pImpl->m_pInStream->GetError();
670 if (!pImpl->m_eError && (pImpl->m_nStorOpenMode & StreamMode::WRITE)
671 && ! pImpl->m_pInStream->IsWritable() )
673 pImpl->m_eError = ERRCODE_IO_ACCESSDENIED;
674 pImpl->m_pInStream.reset();
676 else
677 return pImpl->m_pInStream.get();
680 GetMedium_Impl();
682 if ( GetError() )
683 return nullptr;
685 return pImpl->m_pInStream.get();
689 void SfxMedium::CloseInStream()
691 CloseInStream_Impl();
694 void SfxMedium::CloseInStream_Impl(bool bInDestruction)
696 // if there is a storage based on the InStream, we have to
697 // close the storage, too, because otherwise the storage
698 // would use an invalid ( deleted ) stream.
699 if ( pImpl->m_pInStream && pImpl->xStorage.is() )
701 if ( pImpl->bStorageBasedOnInStream )
702 CloseStorage();
705 if ( pImpl->m_pInStream && !GetContent().is() && !bInDestruction )
707 CreateTempFile();
708 return;
711 pImpl->m_pInStream.reset();
712 if ( pImpl->m_pSet )
713 pImpl->m_pSet->ClearItem( SID_INPUTSTREAM );
715 CloseZipStorage_Impl();
716 pImpl->xInputStream.clear();
718 if ( !pImpl->m_pOutStream )
720 // output part of the stream is not used so the whole stream can be closed
721 // TODO/LATER: is it correct?
722 pImpl->xStream.clear();
723 if ( pImpl->m_pSet )
724 pImpl->m_pSet->ClearItem( SID_STREAM );
729 SvStream* SfxMedium::GetOutStream()
731 if ( !pImpl->m_pOutStream )
733 // Create a temp. file if there is none because we always
734 // need one.
735 CreateTempFile( false );
737 if ( pImpl->pTempFile )
739 // On windows we try to re-use XOutStream from xStream if that exists;
740 // because opening new SvFileStream in this situation may fail with ERROR_SHARING_VIOLATION
741 // TODO: this is a horrible hack that should probably be removed,
742 // somebody needs to investigate this more thoroughly...
743 if (getenv("SFX_MEDIUM_REUSE_STREAM") && pImpl->xStream.is())
745 assert(pImpl->xStream->getOutputStream().is()); // need that...
746 pImpl->m_pOutStream = utl::UcbStreamHelper::CreateStream(
747 pImpl->xStream, false);
749 else
751 // On Unix don't try to re-use XOutStream from xStream if that exists;
752 // it causes fdo#59022 (fails opening files via SMB on Linux)
753 pImpl->m_pOutStream.reset( new SvFileStream(
754 pImpl->m_aName, StreamMode::STD_READWRITE) );
756 CloseStorage();
760 return pImpl->m_pOutStream.get();
764 void SfxMedium::CloseOutStream()
766 CloseOutStream_Impl();
769 void SfxMedium::CloseOutStream_Impl()
771 if ( pImpl->m_pOutStream )
773 // if there is a storage based on the OutStream, we have to
774 // close the storage, too, because otherwise the storage
775 // would use an invalid ( deleted ) stream.
776 //TODO/MBA: how to deal with this?!
777 //maybe we need a new flag when the storage was created from the outstream
778 if ( pImpl->xStorage.is() )
780 CloseStorage();
783 pImpl->m_pOutStream.reset();
786 if ( !pImpl->m_pInStream )
788 // input part of the stream is not used so the whole stream can be closed
789 // TODO/LATER: is it correct?
790 pImpl->xStream.clear();
791 if ( pImpl->m_pSet )
792 pImpl->m_pSet->ClearItem( SID_STREAM );
797 const OUString& SfxMedium::GetPhysicalName() const
799 if ( pImpl->m_aName.isEmpty() && !pImpl->m_aLogicName.isEmpty() )
800 const_cast<SfxMedium*>(this)->CreateFileStream();
802 // return the name then
803 return pImpl->m_aName;
807 void SfxMedium::CreateFileStream()
809 // force synchron
810 if( pImpl->m_pInStream )
812 SvLockBytes* pBytes = pImpl->m_pInStream->GetLockBytes();
813 if( pBytes )
814 pBytes->SetSynchronMode();
817 GetInStream();
818 if( pImpl->m_pInStream )
820 CreateTempFile( false );
821 pImpl->bIsTemp = true;
822 CloseInStream_Impl();
827 bool SfxMedium::Commit()
829 if( pImpl->xStorage.is() )
830 StorageCommit_Impl();
831 else if( pImpl->m_pOutStream )
832 pImpl->m_pOutStream->Flush();
833 else if( pImpl->m_pInStream )
834 pImpl->m_pInStream->Flush();
836 if ( GetError() == ERRCODE_NONE )
838 // does something only in case there is a temporary file ( means aName points to different location than aLogicName )
839 Transfer_Impl();
842 bool bResult = ( GetError() == ERRCODE_NONE );
844 if ( bResult && DocNeedsFileDateCheck() )
845 GetInitFileDate( true );
847 // remove truncation mode from the flags
848 pImpl->m_nStorOpenMode &= ~StreamMode::TRUNC;
849 return bResult;
853 bool SfxMedium::IsStorage()
855 if ( pImpl->xStorage.is() )
856 return true;
858 if ( pImpl->m_bTriedStorage )
859 return pImpl->bIsStorage;
861 if ( pImpl->pTempFile )
863 OUString aURL;
864 if ( osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aURL )
865 != osl::FileBase::E_None )
867 SAL_WARN( "sfx.doc", "Physical name '" << pImpl->m_aName << "' not convertible to file URL");
869 pImpl->bIsStorage = SotStorage::IsStorageFile( aURL ) && !SotStorage::IsOLEStorage( aURL);
870 if ( !pImpl->bIsStorage )
871 pImpl->m_bTriedStorage = true;
873 else if ( GetInStream() )
875 pImpl->bIsStorage = SotStorage::IsStorageFile( pImpl->m_pInStream.get() ) && !SotStorage::IsOLEStorage( pImpl->m_pInStream.get() );
876 if ( !pImpl->m_pInStream->GetError() && !pImpl->bIsStorage )
877 pImpl->m_bTriedStorage = true;
880 return pImpl->bIsStorage;
884 bool SfxMedium::IsPreview_Impl() const
886 bool bPreview = false;
887 const SfxBoolItem* pPreview = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_PREVIEW, false);
888 if ( pPreview )
889 bPreview = pPreview->GetValue();
890 else
892 const SfxStringItem* pFlags = SfxItemSet::GetItem<SfxStringItem>(GetItemSet(), SID_OPTIONS, false);
893 if ( pFlags )
895 OUString aFileFlags = pFlags->GetValue();
896 aFileFlags = aFileFlags.toAsciiUpperCase();
897 if ( -1 != aFileFlags.indexOf( 'B' ) )
898 bPreview = true;
902 return bPreview;
906 void SfxMedium::StorageBackup_Impl()
908 ::ucbhelper::Content aOriginalContent;
909 Reference< css::ucb::XCommandEnvironment > xDummyEnv;
911 bool bBasedOnOriginalFile =
912 !pImpl->pTempFile
913 && ( pImpl->m_aLogicName.isEmpty() || !pImpl->m_bSalvageMode )
914 && !GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ).isEmpty()
915 && GetURLObject().GetProtocol() == INetProtocol::File
916 && ::utl::UCBContentHelper::IsDocument( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
918 if ( bBasedOnOriginalFile && pImpl->m_aBackupURL.isEmpty()
919 && ::ucbhelper::Content::create( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), xDummyEnv, comphelper::getProcessComponentContext(), aOriginalContent ) )
921 DoInternalBackup_Impl( aOriginalContent );
922 if( pImpl->m_aBackupURL.isEmpty() )
923 SetError(ERRCODE_SFX_CANTCREATEBACKUP);
928 OUString const & SfxMedium::GetBackup_Impl()
930 if ( pImpl->m_aBackupURL.isEmpty() )
931 StorageBackup_Impl();
933 return pImpl->m_aBackupURL;
937 uno::Reference < embed::XStorage > SfxMedium::GetOutputStorage()
939 if ( GetError() )
940 return uno::Reference< embed::XStorage >();
942 // if the medium was constructed with a Storage: use this one, not a temp. storage
943 // if a temporary storage already exists: use it
944 if ( pImpl->xStorage.is() && ( pImpl->m_aLogicName.isEmpty() || pImpl->pTempFile ) )
945 return pImpl->xStorage;
947 // if necessary close stream that was used for reading
948 if ( pImpl->m_pInStream && !pImpl->m_pInStream->IsWritable() )
949 CloseInStream();
951 DBG_ASSERT( !pImpl->m_pOutStream, "OutStream in a readonly Medium?!" );
953 // TODO/LATER: The current solution is to store the document temporary and then copy it to the target location;
954 // in future it should be stored directly and then copied to the temporary location, since in this case no
955 // file attributes have to be preserved and system copying mechanics could be used instead of streaming.
956 CreateTempFileNoCopy();
958 return GetStorage();
962 void SfxMedium::SetEncryptionDataToStorage_Impl()
964 // in case media-descriptor contains password it should be used on opening
965 if ( !pImpl->xStorage.is() || !pImpl->m_pSet )
966 return;
968 uno::Sequence< beans::NamedValue > aEncryptionData;
969 if ( !GetEncryptionData_Impl( pImpl->m_pSet.get(), aEncryptionData ) )
970 return;
972 // replace the password with encryption data
973 pImpl->m_pSet->ClearItem( SID_PASSWORD );
974 pImpl->m_pSet->Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::makeAny( aEncryptionData ) ) );
978 ::comphelper::OStorageHelper::SetCommonStorageEncryptionData( pImpl->xStorage, aEncryptionData );
980 catch( const uno::Exception& )
982 SAL_WARN( "sfx.doc", "It must be possible to set a common password for the storage" );
983 // TODO/LATER: set the error code in case of problem
984 // SetError(ERRCODE_IO_GENERAL);
988 #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
990 // FIXME: Hmm actually lock files should be used for sftp: documents
991 // even if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT. Only the use of lock
992 // files for *local* documents is unnecessary in that case. But
993 // actually, the checks for sftp: here are just wishful thinking; I
994 // don't this there is any support for actually editing documents
995 // behind sftp: URLs anyway.
997 // Sure, there could perhaps be a 3rd-party extension that brings UCB
998 // the potential to handle files behind sftp:. But there could also be
999 // an extension that handles some arbitrary foobar: scheme *and* it
1000 // could be that lock files would be the correct thing to use for
1001 // foobar: documents, too. But the hardcoded test below won't know
1002 // that. Clearly the knowledge whether lock files should be used or
1003 // not for some URL scheme belongs in UCB, not here.
1005 namespace
1008 OUString tryMSOwnerFiles(const OUString& sDocURL)
1010 svt::MSODocumentLockFile aMSOLockFile(sDocURL);
1011 LockFileEntry aData;
1014 aData = aMSOLockFile.GetLockData();
1016 catch( const uno::Exception& )
1018 return OUString();
1021 OUString sUserData = aData[LockFileComponent::OOOUSERNAME];
1023 if (!sUserData.isEmpty())
1024 sUserData += " (MS Office)"; // Mention the used office suite
1026 return sUserData;
1029 OUString tryForeignLockfiles(const OUString& sDocURL)
1031 OUString sUserData = tryMSOwnerFiles(sDocURL);
1032 // here we can test for empty result, and add other known applications' lockfile testing
1033 return sUserData.trim();
1037 SfxMedium::ShowLockResult SfxMedium::ShowLockedDocumentDialog(const LockFileEntry& aData,
1038 bool bIsLoading, bool bOwnLock,
1039 bool bHandleSysLocked)
1041 ShowLockResult nResult = ShowLockResult::NoLock;
1043 // tdf#92817: Simple check for empty lock file that needs to be deleted, when system locking is enabled
1044 if( aData[LockFileComponent::OOOUSERNAME].isEmpty() && aData[LockFileComponent::SYSUSERNAME].isEmpty() && !bHandleSysLocked )
1045 bOwnLock=true;
1047 // show the interaction regarding the document opening
1048 uno::Reference< task::XInteractionHandler > xHandler = GetInteractionHandler();
1050 if ( xHandler.is() && ( bIsLoading || !bHandleSysLocked || bOwnLock ) )
1052 OUString aDocumentURL
1053 = GetURLObject().GetLastName(INetURLObject::DecodeMechanism::WithCharset);
1054 OUString aInfo;
1055 ::rtl::Reference< ::ucbhelper::InteractionRequest > xInteractionRequestImpl;
1057 sal_Int32 nContinuations = 3;
1059 if ( bOwnLock )
1061 aInfo = aData[LockFileComponent::EDITTIME];
1063 xInteractionRequestImpl = new ::ucbhelper::InteractionRequest( uno::makeAny(
1064 document::OwnLockOnDocumentRequest( OUString(), uno::Reference< uno::XInterface >(), aDocumentURL, aInfo, !bIsLoading ) ) );
1066 else
1068 // Use a fourth continuation in case there's no filesystem lock:
1069 // "Ignore lock file and open/replace the document"
1070 if (!bHandleSysLocked)
1071 nContinuations = 4;
1073 if ( !aData[LockFileComponent::OOOUSERNAME].isEmpty() )
1074 aInfo = aData[LockFileComponent::OOOUSERNAME];
1075 else
1076 aInfo = aData[LockFileComponent::SYSUSERNAME];
1078 if (aInfo.isEmpty() && !GetURLObject().isAnyKnownWebDAVScheme())
1079 // Try to get name of user who has locked the file using other applications
1080 aInfo = tryForeignLockfiles(
1081 GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::NONE));
1083 if ( !aInfo.isEmpty() && !aData[LockFileComponent::EDITTIME].isEmpty() )
1084 aInfo += " ( " + aData[LockFileComponent::EDITTIME] + " )";
1086 if (!bIsLoading) // so, !bHandleSysLocked
1088 xInteractionRequestImpl = new ::ucbhelper::InteractionRequest(uno::makeAny(
1089 document::LockedOnSavingRequest(OUString(), uno::Reference< uno::XInterface >(), aDocumentURL, aInfo)));
1090 // Currently, only the last "Retry" continuation (meaning ignore the lock and try overwriting) can be returned.
1092 else /*logically therefore bIsLoading is set */
1094 xInteractionRequestImpl = new ::ucbhelper::InteractionRequest( uno::makeAny(
1095 document::LockedDocumentRequest( OUString(), uno::Reference< uno::XInterface >(), aDocumentURL, aInfo ) ) );
1099 uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations(nContinuations);
1100 auto pContinuations = aContinuations.getArray();
1101 pContinuations[0] = new ::ucbhelper::InteractionAbort( xInteractionRequestImpl.get() );
1102 pContinuations[1] = new ::ucbhelper::InteractionApprove( xInteractionRequestImpl.get() );
1103 pContinuations[2] = new ::ucbhelper::InteractionDisapprove( xInteractionRequestImpl.get() );
1104 if (nContinuations > 3)
1106 // We use InteractionRetry to reflect that user wants to
1107 // ignore the (stale?) alien lock file and open/overwrite the document
1108 pContinuations[3] = new ::ucbhelper::InteractionRetry(xInteractionRequestImpl.get());
1110 xInteractionRequestImpl->setContinuations( aContinuations );
1112 xHandler->handle( xInteractionRequestImpl );
1114 bool bOpenReadOnly = false;
1115 ::rtl::Reference< ::ucbhelper::InteractionContinuation > xSelected = xInteractionRequestImpl->getSelection();
1116 if ( uno::Reference< task::XInteractionAbort >( xSelected.get(), uno::UNO_QUERY ).is() )
1118 SetError(ERRCODE_ABORT);
1120 else if ( uno::Reference< task::XInteractionDisapprove >( xSelected.get(), uno::UNO_QUERY ).is() )
1122 // own lock on loading, user has selected to ignore the lock
1123 // own lock on saving, user has selected to ignore the lock
1124 // alien lock on loading, user has selected to edit a copy of document
1125 // TODO/LATER: alien lock on saving, user has selected to do SaveAs to different location
1126 if ( !bOwnLock ) // bIsLoading implied from outermost condition
1128 // means that a copy of the document should be opened
1129 GetItemSet()->Put( SfxBoolItem( SID_TEMPLATE, true ) );
1131 else
1132 nResult = ShowLockResult::Succeeded;
1134 else if (uno::Reference< task::XInteractionRetry >(xSelected.get(), uno::UNO_QUERY).is())
1136 // User decided to ignore the alien (stale?) lock file without filesystem lock
1137 nResult = ShowLockResult::Succeeded;
1139 else if (uno::Reference< task::XInteractionApprove >( xSelected.get(), uno::UNO_QUERY ).is())
1141 bOpenReadOnly = true;
1143 else // user selected "Notify"
1145 pImpl->m_bNotifyWhenEditable = true;
1146 AddToCheckEditableWorkerList();
1147 bOpenReadOnly = true;
1150 if (bOpenReadOnly)
1152 // own lock on loading, user has selected to open readonly
1153 // own lock on saving, user has selected to open readonly
1154 // alien lock on loading, user has selected to retry saving
1155 // TODO/LATER: alien lock on saving, user has selected to retry saving
1157 if (bIsLoading)
1158 GetItemSet()->Put(SfxBoolItem(SID_DOC_READONLY, true));
1159 else
1160 nResult = ShowLockResult::Try;
1163 else
1165 if ( bIsLoading )
1167 // if no interaction handler is provided the default answer is open readonly
1168 // that usually happens in case the document is loaded per API
1169 // so the document must be opened readonly for backward compatibility
1170 GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
1172 else
1173 SetError(ERRCODE_IO_ACCESSDENIED);
1177 return nResult;
1180 bool SfxMedium::ShowLockFileProblemDialog(MessageDlg nWhichDlg)
1182 // system file locking is not active, ask user whether he wants to open the document without any locking
1183 uno::Reference< task::XInteractionHandler > xHandler = GetInteractionHandler();
1185 if (xHandler.is())
1187 ::rtl::Reference< ::ucbhelper::InteractionRequest > xIgnoreRequestImpl;
1189 switch (nWhichDlg)
1191 case MessageDlg::LockFileIgnore:
1192 xIgnoreRequestImpl = new ::ucbhelper::InteractionRequest(uno::makeAny( document::LockFileIgnoreRequest() ));
1193 break;
1194 case MessageDlg::LockFileCorrupt:
1195 xIgnoreRequestImpl = new ::ucbhelper::InteractionRequest(uno::makeAny( document::LockFileCorruptRequest() ));
1196 break;
1199 uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations{
1200 new ::ucbhelper::InteractionAbort(xIgnoreRequestImpl.get()),
1201 new ::ucbhelper::InteractionApprove(xIgnoreRequestImpl.get())
1203 xIgnoreRequestImpl->setContinuations(aContinuations);
1205 xHandler->handle(xIgnoreRequestImpl);
1207 ::rtl::Reference< ::ucbhelper::InteractionContinuation > xSelected = xIgnoreRequestImpl->getSelection();
1208 bool bReadOnly = true;
1210 if (uno::Reference<task::XInteractionAbort>(xSelected.get(), uno::UNO_QUERY).is())
1212 SetError(ERRCODE_ABORT);
1213 bReadOnly = false;
1215 else if (!uno::Reference<task::XInteractionApprove>(xSelected.get(), uno::UNO_QUERY).is())
1217 // user selected "Notify"
1218 pImpl->m_bNotifyWhenEditable = true;
1219 AddToCheckEditableWorkerList();
1222 if (bReadOnly)
1223 GetItemSet()->Put(SfxBoolItem(SID_DOC_READONLY, true));
1225 return bReadOnly;
1228 return false;
1231 namespace
1233 bool isSuitableProtocolForLocking(const OUString & rLogicName)
1235 INetURLObject aUrl( rLogicName );
1236 INetProtocol eProt = aUrl.GetProtocol();
1237 #if !HAVE_FEATURE_MACOSX_SANDBOX
1238 if (eProt == INetProtocol::File) {
1239 return true;
1241 #endif
1242 return eProt == INetProtocol::Smb || eProt == INetProtocol::Sftp;
1246 #endif // HAVE_FEATURE_MULTIUSER_ENVIRONMENT
1248 // sets SID_DOC_READONLY if the document cannot be opened for editing
1249 // if user cancel the loading the ERROR_ABORT is set
1250 SfxMedium::LockFileResult SfxMedium::LockOrigFileOnDemand(bool bLoading, bool bNoUI,
1251 bool bTryIgnoreLockFile,
1252 LockFileEntry* pLockData)
1254 #if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT
1255 (void) bLoading;
1256 (void) bNoUI;
1257 (void) bTryIgnoreLockFile;
1258 (void) pLockData;
1259 return LockFileResult::Succeeded;
1260 #else
1261 LockFileResult eResult = LockFileResult::Failed;
1263 // check if path scheme is http:// or https://
1264 // may be this is better if used always, in Android and iOS as well?
1265 // if this code should be always there, remember to move the relevant code in UnlockFile method as well !
1267 if ( GetURLObject().isAnyKnownWebDAVScheme() )
1269 // do nothing if WebDAV locking is disabled
1270 if (!IsWebDAVLockingUsed())
1271 return LockFileResult::Succeeded;
1275 bool bResult = pImpl->m_bLocked;
1276 bool bIsTemplate = false;
1277 // so, this is webdav stuff...
1278 if ( !bResult )
1280 // no read-write access is necessary on loading if the document is explicitly opened as copy
1281 const SfxBoolItem* pTemplateItem = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_TEMPLATE, false);
1282 bIsTemplate = ( bLoading && pTemplateItem && pTemplateItem->GetValue() );
1285 if ( !bIsTemplate && !bResult && !IsReadOnly() )
1287 ShowLockResult bUIStatus = ShowLockResult::NoLock;
1290 if( !bResult )
1292 uno::Reference< task::XInteractionHandler > xCHandler = GetInteractionHandler( true );
1293 Reference< css::ucb::XCommandEnvironment > xComEnv = new ::ucbhelper::CommandEnvironment(
1294 xCHandler, Reference< css::ucb::XProgressHandler >() );
1296 ucbhelper::Content aContentToLock(
1297 GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ),
1298 xComEnv, comphelper::getProcessComponentContext() );
1302 aContentToLock.lock();
1303 bResult = true;
1305 catch ( ucb::InteractiveLockingLockedException& )
1307 // received when the resource is already locked
1308 if (!bNoUI || pLockData)
1310 // get the lock owner, using a special ucb.webdav property
1311 // the owner property retrieved here is what the other principal send the server
1312 // when activating the lock.
1313 // See http://tools.ietf.org/html/rfc4918#section-14.17 for details
1314 LockFileEntry aLockData;
1315 aLockData[LockFileComponent::OOOUSERNAME] = "Unknown user";
1316 // This solution works right when the LO user name and the WebDAV user
1317 // name are the same.
1318 // A better thing to do would be to obtain the 'real' WebDAV user name,
1319 // but that's not possible from a WebDAV UCP provider client.
1320 LockFileEntry aOwnData = svt::LockFileCommon::GenerateOwnEntry();
1321 // use the current LO user name as the system name
1322 aLockData[LockFileComponent::SYSUSERNAME]
1323 = aOwnData[LockFileComponent::SYSUSERNAME];
1325 uno::Sequence<css::ucb::Lock> aLocks;
1326 // getting the property, send a PROPFIND to the server over the net
1327 if ((aContentToLock.getPropertyValue("DAV:lockdiscovery") >>= aLocks) && aLocks.hasElements())
1329 // got at least a lock, show the owner of the first lock returned
1330 css::ucb::Lock aLock = aLocks[0];
1331 OUString aOwner;
1332 if (aLock.Owner >>= aOwner)
1334 // we need to display the WebDAV user name owning the lock, not the local one
1335 aLockData[LockFileComponent::OOOUSERNAME] = aOwner;
1339 if (!bNoUI)
1341 bUIStatus = ShowLockedDocumentDialog(aLockData, bLoading, false,
1342 true);
1345 if (pLockData)
1347 std::copy(aLockData.begin(), aLockData.end(), pLockData->begin());
1351 catch( ucb::InteractiveNetworkWriteException& )
1353 // This catch it's not really needed, here just for the sake of documentation on the behaviour.
1354 // This is the most likely reason:
1355 // - the remote site is a WebDAV with special configuration: read/only for read operations
1356 // and read/write for write operations, the user is not allowed to lock/write and
1357 // she cancelled the credentials request.
1358 // this is not actually an error, but the exception is sent directly from ucb, avoiding the automatic
1359 // management that takes part in cancelCommandExecution()
1360 // Unfortunately there is no InteractiveNetwork*Exception available to signal this more correctly
1361 // since it mostly happens on read/only part of webdav, this can be the most correct
1362 // exception available
1364 catch( uno::Exception& )
1367 } while( !bResult && bUIStatus == ShowLockResult::Try );
1370 pImpl->m_bLocked = bResult;
1372 if ( !bResult && GetError() == ERRCODE_NONE )
1374 // the error should be set in case it is storing process
1375 // or the document has been opened for editing explicitly
1376 const SfxBoolItem* pReadOnlyItem = SfxItemSet::GetItem<SfxBoolItem>(pImpl->m_pSet.get(), SID_DOC_READONLY, false);
1378 if ( !bLoading || (pReadOnlyItem && !pReadOnlyItem->GetValue()) )
1379 SetError(ERRCODE_IO_ACCESSDENIED);
1380 else
1381 GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
1384 // when the file is locked, get the current file date
1385 if ( bResult && DocNeedsFileDateCheck() )
1386 GetInitFileDate( true );
1388 if ( bResult )
1389 eResult = LockFileResult::Succeeded;
1391 catch ( const uno::Exception& )
1393 TOOLS_WARN_EXCEPTION( "sfx.doc", "Locking exception: WebDAV while trying to lock the file" );
1395 return eResult;
1398 if (!IsLockingUsed())
1399 return LockFileResult::Succeeded;
1400 if (GetURLObject().HasError())
1401 return eResult;
1405 if ( pImpl->m_bLocked && bLoading
1406 && GetURLObject().GetProtocol() == INetProtocol::File )
1408 // if the document is already locked the system locking might be temporarily off after storing
1409 // check whether the system file locking should be taken again
1410 GetLockingStream_Impl();
1413 bool bResult = pImpl->m_bLocked;
1415 if ( !bResult )
1417 // no read-write access is necessary on loading if the document is explicitly opened as copy
1418 const SfxBoolItem* pTemplateItem = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_TEMPLATE, false);
1419 bResult = ( bLoading && pTemplateItem && pTemplateItem->GetValue() );
1422 if ( !bResult && !IsReadOnly() )
1424 bool bContentReadonly = false;
1425 if ( bLoading && GetURLObject().GetProtocol() == INetProtocol::File )
1427 // let the original document be opened to check the possibility to open it for editing
1428 // and to let the writable stream stay open to hold the lock on the document
1429 GetLockingStream_Impl();
1432 // "IsReadOnly" property does not allow to detect whether the file is readonly always
1433 // so we try always to open the file for editing
1434 // the file is readonly only in case the read-write stream can not be opened
1435 if ( bLoading && !pImpl->m_xLockingStream.is() )
1439 // MediaDescriptor does this check also, the duplication should be avoided in future
1440 Reference< css::ucb::XCommandEnvironment > xDummyEnv;
1441 ::ucbhelper::Content aContent( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), xDummyEnv, comphelper::getProcessComponentContext() );
1442 aContent.getPropertyValue("IsReadOnly") >>= bContentReadonly;
1444 catch( const uno::Exception& ) {}
1447 // do further checks only if the file not readonly in fs
1448 if ( !bContentReadonly )
1450 // the special file locking should be used only for suitable URLs
1451 if ( isSuitableProtocolForLocking( pImpl->m_aLogicName ) )
1454 // in case of storing the document should request the output before locking
1455 if ( bLoading )
1457 // let the stream be opened to check the system file locking
1458 GetMedium_Impl();
1459 if (GetError() != ERRCODE_NONE) {
1460 return eResult;
1464 ShowLockResult bUIStatus = ShowLockResult::NoLock;
1466 // check whether system file locking has been used, the default value is false
1467 bool bUseSystemLock = comphelper::isFileUrl( pImpl->m_aLogicName ) && IsSystemFileLockingUsed();
1469 // TODO/LATER: This implementation does not allow to detect the system lock on saving here, actually this is no big problem
1470 // if system lock is used the writeable stream should be available
1471 bool bHandleSysLocked = ( bLoading && bUseSystemLock && !pImpl->xStream.is() && !pImpl->m_pOutStream );
1473 // The file is attempted to get locked for the duration of lockfile creation on save
1474 std::unique_ptr<osl::File> pFileLock;
1475 if (!bLoading && bUseSystemLock && pImpl->pTempFile)
1477 INetURLObject aDest(GetURLObject());
1478 OUString aDestURL(aDest.GetMainURL(INetURLObject::DecodeMechanism::NONE));
1480 if (comphelper::isFileUrl(aDestURL) || !aDest.removeSegment())
1482 pFileLock = std::make_unique<osl::File>(aDestURL);
1483 auto rc = pFileLock->open(osl_File_OpenFlag_Write);
1484 if (rc == osl::FileBase::E_ACCES)
1485 bHandleSysLocked = true;
1493 ::svt::DocumentLockFile aLockFile( pImpl->m_aLogicName );
1495 std::unique_ptr<svt::MSODocumentLockFile> pMSOLockFile;
1496 const SvtFilterOptions& rOpt = SvtFilterOptions::Get();
1497 if (rOpt.IsMSOLockFileCreationIsEnabled() && svt::MSODocumentLockFile::IsMSOSupportedFileFormat(pImpl->m_aLogicName))
1499 pMSOLockFile.reset(new svt::MSODocumentLockFile(pImpl->m_aLogicName));
1500 pImpl->m_bMSOLockFileCreated = true;
1503 bool bIoErr = false;
1505 if (!bHandleSysLocked)
1509 bResult = aLockFile.CreateOwnLockFile();
1510 if(pMSOLockFile)
1511 bResult &= pMSOLockFile->CreateOwnLockFile();
1513 catch (const uno::Exception&)
1515 if (tools::IsMappedWebDAVPath(GetURLObject().GetMainURL(
1516 INetURLObject::DecodeMechanism::NONE)))
1518 // This is a path that redirects to a WebDAV resource;
1519 // so failure creating lockfile is not an error here.
1520 bResult = true;
1522 else if (bLoading && !bNoUI)
1524 bIoErr = true;
1525 ShowLockFileProblemDialog(MessageDlg::LockFileIgnore);
1526 bResult = true; // always delete the defect lock-file
1530 // in case OOo locking is turned off the lock file is still written if possible
1531 // but it is ignored while deciding whether the document should be opened for editing or not
1532 if (!bResult && !IsOOoLockFileUsed() && !bIoErr)
1534 bResult = true;
1535 // take the ownership over the lock file
1536 aLockFile.OverwriteOwnLockFile();
1538 if(pMSOLockFile)
1539 pMSOLockFile->OverwriteOwnLockFile();
1543 if ( !bResult )
1545 LockFileEntry aData;
1548 aData = aLockFile.GetLockData();
1550 catch (const io::WrongFormatException&)
1552 // we get empty or corrupt data
1553 // info to the user
1554 if (!bIoErr && bLoading && !bNoUI )
1555 bResult = ShowLockFileProblemDialog(MessageDlg::LockFileCorrupt);
1557 // not show the Lock Document Dialog
1558 bIoErr = true;
1560 catch( const uno::Exception& )
1562 // show the Lock Document Dialog, when locked from other app
1563 bIoErr = !bHandleSysLocked;
1566 bool bOwnLock = false;
1568 if (!bHandleSysLocked)
1570 LockFileEntry aOwnData = svt::LockFileCommon::GenerateOwnEntry();
1571 bOwnLock = aOwnData[LockFileComponent::SYSUSERNAME] == aData[LockFileComponent::SYSUSERNAME];
1573 if (bOwnLock
1574 && aOwnData[LockFileComponent::LOCALHOST] == aData[LockFileComponent::LOCALHOST]
1575 && aOwnData[LockFileComponent::USERURL] == aData[LockFileComponent::USERURL])
1577 // this is own lock from the same installation, it could remain because of crash
1578 bResult = true;
1582 if ( !bResult && !bIoErr)
1584 if (!bNoUI)
1585 bUIStatus = ShowLockedDocumentDialog(
1586 aData, bLoading, bOwnLock, bHandleSysLocked);
1587 else if (bLoading && bTryIgnoreLockFile && !bHandleSysLocked)
1588 bUIStatus = ShowLockResult::Succeeded;
1590 if ( bUIStatus == ShowLockResult::Succeeded )
1592 // take the ownership over the lock file
1593 bResult = aLockFile.OverwriteOwnLockFile();
1595 if(pMSOLockFile)
1596 pMSOLockFile->OverwriteOwnLockFile();
1598 else if (bLoading && !bHandleSysLocked)
1599 eResult = LockFileResult::FailedLockFile;
1601 if (!bResult && pLockData)
1603 std::copy(aData.begin(), aData.end(), pLockData->begin());
1608 catch( const uno::Exception& )
1611 } while( !bResult && bUIStatus == ShowLockResult::Try );
1613 pImpl->m_bLocked = bResult;
1615 else
1617 // this is no file URL, check whether the file is readonly
1618 bResult = !bContentReadonly;
1621 else // read-only
1623 AddToCheckEditableWorkerList();
1627 if ( !bResult && GetError() == ERRCODE_NONE )
1629 // the error should be set in case it is storing process
1630 // or the document has been opened for editing explicitly
1631 const SfxBoolItem* pReadOnlyItem = SfxItemSet::GetItem<SfxBoolItem>(pImpl->m_pSet.get(), SID_DOC_READONLY, false);
1633 if ( !bLoading || (pReadOnlyItem && !pReadOnlyItem->GetValue()) )
1634 SetError(ERRCODE_IO_ACCESSDENIED);
1635 else
1636 GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
1639 // when the file is locked, get the current file date
1640 if ( bResult && DocNeedsFileDateCheck() )
1641 GetInitFileDate( true );
1643 if ( bResult )
1644 eResult = LockFileResult::Succeeded;
1646 catch( const uno::Exception& )
1648 TOOLS_WARN_EXCEPTION( "sfx.doc", "Locking exception: high probability, that the content has not been created" );
1651 return eResult;
1652 #endif
1656 uno::Reference < embed::XStorage > SfxMedium::GetStorage( bool bCreateTempFile )
1658 if ( pImpl->xStorage.is() || pImpl->m_bTriedStorage )
1659 return pImpl->xStorage;
1661 uno::Sequence< uno::Any > aArgs( 2 );
1662 auto pArgs = aArgs.getArray();
1664 // the medium should be retrieved before temporary file creation
1665 // to let the MediaDescriptor be filled with the streams
1666 GetMedium_Impl();
1668 if ( bCreateTempFile )
1669 CreateTempFile( false );
1671 GetMedium_Impl();
1673 if ( GetError() )
1674 return pImpl->xStorage;
1676 const SfxBoolItem* pRepairItem = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_REPAIRPACKAGE, false);
1677 if ( pRepairItem && pRepairItem->GetValue() )
1679 // the storage should be created for repairing mode
1680 CreateTempFile( false );
1681 GetMedium_Impl();
1683 Reference< css::ucb::XProgressHandler > xProgressHandler;
1684 Reference< css::task::XStatusIndicator > xStatusIndicator;
1686 const SfxUnoAnyItem* pxProgressItem = SfxItemSet::GetItem<SfxUnoAnyItem>(GetItemSet(), SID_PROGRESS_STATUSBAR_CONTROL, false);
1687 if( pxProgressItem && ( pxProgressItem->GetValue() >>= xStatusIndicator ) )
1688 xProgressHandler.set( new utl::ProgressHandlerWrap( xStatusIndicator ) );
1690 uno::Sequence< beans::PropertyValue > aAddProps{
1691 comphelper::makePropertyValue("RepairPackage", true),
1692 comphelper::makePropertyValue("StatusIndicator", xProgressHandler)
1695 // the first arguments will be filled later
1696 aArgs.realloc( 3 );
1697 pArgs = aArgs.getArray();
1698 pArgs[2] <<= aAddProps;
1701 if ( pImpl->xStream.is() )
1703 // since the storage is based on temporary stream we open it always read-write
1704 pArgs[0] <<= pImpl->xStream;
1705 pArgs[1] <<= embed::ElementModes::READWRITE;
1706 pImpl->bStorageBasedOnInStream = true;
1707 if (pImpl->m_bDisableFileSync)
1709 // Forward NoFileSync to the storage factory.
1710 aArgs.realloc(3); // ??? this may re-write the data added above for pRepairItem
1711 pArgs = aArgs.getArray();
1712 uno::Sequence<beans::PropertyValue> aProperties(
1713 comphelper::InitPropertySequence({ { "NoFileSync", uno::makeAny(true) } }));
1714 pArgs[2] <<= aProperties;
1717 else if ( pImpl->xInputStream.is() )
1719 // since the storage is based on temporary stream we open it always read-write
1720 pArgs[0] <<= pImpl->xInputStream;
1721 pArgs[1] <<= embed::ElementModes::READ;
1722 pImpl->bStorageBasedOnInStream = true;
1724 else
1726 CloseStreams_Impl();
1727 pArgs[0] <<= pImpl->m_aName;
1728 pArgs[1] <<= embed::ElementModes::READ;
1729 pImpl->bStorageBasedOnInStream = false;
1734 pImpl->xStorage.set( ::comphelper::OStorageHelper::GetStorageFactory()->createInstanceWithArguments( aArgs ),
1735 uno::UNO_QUERY );
1737 catch( const uno::Exception& )
1739 // impossibility to create the storage is no error
1742 if( ( pImpl->nLastStorageError = GetError() ) != ERRCODE_NONE )
1744 pImpl->xStorage = nullptr;
1745 if ( pImpl->m_pInStream )
1746 pImpl->m_pInStream->Seek(0);
1747 return uno::Reference< embed::XStorage >();
1750 pImpl->m_bTriedStorage = true;
1752 // TODO/LATER: Get versionlist on demand
1753 if ( pImpl->xStorage.is() )
1755 SetEncryptionDataToStorage_Impl();
1756 GetVersionList();
1759 const SfxInt16Item* pVersion = SfxItemSet::GetItem<SfxInt16Item>(pImpl->m_pSet.get(), SID_VERSION, false);
1761 bool bResetStorage = false;
1762 if ( pVersion && pVersion->GetValue() )
1764 // Read all available versions
1765 if ( pImpl->aVersions.hasElements() )
1767 // Search for the version fits the comment
1768 // The versions are numbered starting with 1, versions with
1769 // negative versions numbers are counted backwards from the
1770 // current version
1771 short nVersion = pVersion->GetValue();
1772 if ( nVersion<0 )
1773 nVersion = static_cast<short>(pImpl->aVersions.getLength()) + nVersion;
1774 else // nVersion > 0; pVersion->GetValue() != 0 was the condition to this block
1775 nVersion--;
1777 const util::RevisionTag& rTag = pImpl->aVersions[nVersion];
1779 // Open SubStorage for all versions
1780 uno::Reference < embed::XStorage > xSub = pImpl->xStorage->openStorageElement( "Versions",
1781 embed::ElementModes::READ );
1783 DBG_ASSERT( xSub.is(), "Version list, but no Versions!" );
1785 // There the version is stored as packed Stream
1786 uno::Reference < io::XStream > xStr = xSub->openStreamElement( rTag.Identifier, embed::ElementModes::READ );
1787 std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream( xStr ));
1788 if ( pStream && pStream->GetError() == ERRCODE_NONE )
1790 // Unpack Stream in TempDir
1791 ::utl::TempFile aTempFile;
1792 const OUString& aTmpName = aTempFile.GetURL();
1793 SvFileStream aTmpStream( aTmpName, SFX_STREAM_READWRITE );
1795 pStream->ReadStream( aTmpStream );
1796 pStream.reset();
1797 aTmpStream.Close();
1799 // Open data as Storage
1800 pImpl->m_nStorOpenMode = SFX_STREAM_READONLY;
1801 pImpl->xStorage = comphelper::OStorageHelper::GetStorageFromURL( aTmpName, embed::ElementModes::READ );
1802 pImpl->bStorageBasedOnInStream = false;
1803 OUString aTemp;
1804 osl::FileBase::getSystemPathFromFileURL( aTmpName, aTemp );
1805 SetPhysicalName_Impl( aTemp );
1807 pImpl->bIsTemp = true;
1808 GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
1809 // TODO/MBA
1810 pImpl->aVersions.realloc(0);
1812 else
1813 bResetStorage = true;
1816 else
1817 bResetStorage = true;
1820 if ( bResetStorage )
1822 pImpl->xStorage.clear();
1823 if ( pImpl->m_pInStream )
1824 pImpl->m_pInStream->Seek( 0 );
1827 pImpl->bIsStorage = pImpl->xStorage.is();
1828 return pImpl->xStorage;
1832 uno::Reference< embed::XStorage > const & SfxMedium::GetZipStorageToSign_Impl( bool bReadOnly )
1834 if ( !GetError() && !pImpl->m_xZipStorage.is() )
1836 GetMedium_Impl();
1840 // we can not sign document if there is no stream
1841 // should it be possible at all?
1842 if ( !bReadOnly && pImpl->xStream.is() )
1844 pImpl->m_xZipStorage = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream( ZIP_STORAGE_FORMAT_STRING, pImpl->xStream );
1846 else if ( pImpl->xInputStream.is() )
1848 pImpl->m_xZipStorage = ::comphelper::OStorageHelper::GetStorageOfFormatFromInputStream( ZIP_STORAGE_FORMAT_STRING, pImpl->xInputStream );
1851 catch( const uno::Exception& )
1853 SAL_WARN( "sfx.doc", "No possibility to get readonly version of storage from medium!" );
1856 if ( GetError() ) // do not remove warnings
1857 ResetError();
1860 return pImpl->m_xZipStorage;
1864 void SfxMedium::CloseZipStorage_Impl()
1866 if ( pImpl->m_xZipStorage.is() )
1868 try {
1869 pImpl->m_xZipStorage->dispose();
1870 } catch( const uno::Exception& )
1873 pImpl->m_xZipStorage.clear();
1877 void SfxMedium::CloseStorage()
1879 if ( pImpl->xStorage.is() )
1881 uno::Reference < lang::XComponent > xComp = pImpl->xStorage;
1882 // in the salvage mode the medium does not own the storage
1883 if ( pImpl->bDisposeStorage && !pImpl->m_bSalvageMode )
1885 try {
1886 xComp->dispose();
1887 } catch( const uno::Exception& )
1889 SAL_WARN( "sfx.doc", "Medium's storage is already disposed!" );
1893 pImpl->xStorage.clear();
1894 pImpl->bStorageBasedOnInStream = false;
1897 pImpl->m_bTriedStorage = false;
1898 pImpl->bIsStorage = false;
1901 void SfxMedium::CanDisposeStorage_Impl( bool bDisposeStorage )
1903 pImpl->bDisposeStorage = bDisposeStorage;
1906 bool SfxMedium::WillDisposeStorageOnClose_Impl()
1908 return pImpl->bDisposeStorage;
1911 StreamMode SfxMedium::GetOpenMode() const
1913 return pImpl->m_nStorOpenMode;
1916 void SfxMedium::SetOpenMode( StreamMode nStorOpen,
1917 bool bDontClose )
1919 if ( pImpl->m_nStorOpenMode != nStorOpen )
1921 pImpl->m_nStorOpenMode = nStorOpen;
1923 if( !bDontClose )
1925 if ( pImpl->xStorage.is() )
1926 CloseStorage();
1928 CloseStreams_Impl();
1934 bool SfxMedium::UseBackupToRestore_Impl( ::ucbhelper::Content& aOriginalContent,
1935 const Reference< css::ucb::XCommandEnvironment >& xComEnv )
1939 ::ucbhelper::Content aTransactCont( pImpl->m_aBackupURL, xComEnv, comphelper::getProcessComponentContext() );
1941 Reference< XInputStream > aOrigInput = aTransactCont.openStream();
1942 aOriginalContent.writeStream( aOrigInput, true );
1943 return true;
1945 catch( const Exception& )
1947 // in case of failure here the backup file should not be removed
1948 // TODO/LATER: a message should be used to let user know about the backup
1949 pImpl->m_bRemoveBackup = false;
1950 // TODO/LATER: needs a specific error code
1951 pImpl->m_eError = ERRCODE_IO_GENERAL;
1954 return false;
1958 bool SfxMedium::StorageCommit_Impl()
1960 bool bResult = false;
1961 Reference< css::ucb::XCommandEnvironment > xDummyEnv;
1962 ::ucbhelper::Content aOriginalContent;
1964 if ( pImpl->xStorage.is() )
1966 if ( !GetError() )
1968 uno::Reference < embed::XTransactedObject > xTrans( pImpl->xStorage, uno::UNO_QUERY );
1969 if ( xTrans.is() )
1973 xTrans->commit();
1974 CloseZipStorage_Impl();
1975 bResult = true;
1977 catch ( const embed::UseBackupException& aBackupExc )
1979 // since the temporary file is created always now, the scenario is close to be impossible
1980 if ( !pImpl->pTempFile )
1982 OSL_ENSURE( !pImpl->m_aBackupURL.isEmpty(), "No backup on storage commit!" );
1983 if ( !pImpl->m_aBackupURL.isEmpty()
1984 && ::ucbhelper::Content::create( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ),
1985 xDummyEnv, comphelper::getProcessComponentContext(),
1986 aOriginalContent ) )
1988 // use backup to restore the file
1989 // the storage has already disconnected from original location
1990 CloseAndReleaseStreams_Impl();
1991 if ( !UseBackupToRestore_Impl( aOriginalContent, xDummyEnv ) )
1993 // connect the medium to the temporary file of the storage
1994 pImpl->aContent = ::ucbhelper::Content();
1995 pImpl->m_aName = aBackupExc.TemporaryFileURL;
1996 OSL_ENSURE( !pImpl->m_aName.isEmpty(), "The exception _must_ contain the temporary URL!" );
2001 if (!GetError())
2002 SetError(ERRCODE_IO_GENERAL);
2004 catch ( const uno::Exception& )
2006 //TODO/LATER: improve error handling
2007 SetError(ERRCODE_IO_GENERAL);
2013 return bResult;
2017 void SfxMedium::TransactedTransferForFS_Impl( const INetURLObject& aSource,
2018 const INetURLObject& aDest,
2019 const Reference< css::ucb::XCommandEnvironment >& xComEnv )
2021 Reference< css::ucb::XCommandEnvironment > xDummyEnv;
2022 ::ucbhelper::Content aOriginalContent;
2026 aOriginalContent = ::ucbhelper::Content( aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() );
2028 catch ( const css::ucb::CommandAbortedException& )
2030 pImpl->m_eError = ERRCODE_ABORT;
2032 catch ( const css::ucb::CommandFailedException& )
2034 pImpl->m_eError = ERRCODE_ABORT;
2036 catch (const css::ucb::ContentCreationException& ex)
2038 pImpl->m_eError = ERRCODE_IO_GENERAL;
2039 if (
2040 (ex.eError == css::ucb::ContentCreationError_NO_CONTENT_PROVIDER ) ||
2041 (ex.eError == css::ucb::ContentCreationError_CONTENT_CREATION_FAILED)
2044 pImpl->m_eError = ERRCODE_IO_NOTEXISTSPATH;
2047 catch (const css::uno::Exception&)
2049 pImpl->m_eError = ERRCODE_IO_GENERAL;
2052 if( pImpl->m_eError && !pImpl->m_eError.IsWarning() )
2053 return;
2055 if ( pImpl->xStorage.is() )
2056 CloseStorage();
2058 CloseStreams_Impl();
2060 ::ucbhelper::Content aTempCont;
2061 if( ::ucbhelper::Content::create( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xDummyEnv, comphelper::getProcessComponentContext(), aTempCont ) )
2063 bool bTransactStarted = false;
2064 const SfxBoolItem* pOverWrite = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_OVERWRITE, false);
2065 bool bOverWrite = !pOverWrite || pOverWrite->GetValue();
2066 bool bResult = false;
2070 // tdf#60237 - if the OverWrite property of the MediaDescriptor is set to false,
2071 // try to write the file before trying to rename or copy it
2072 if (!(bOverWrite
2073 && ::utl::UCBContentHelper::IsDocument(
2074 aDest.GetMainURL(INetURLObject::DecodeMechanism::NONE))))
2076 Reference< XInputStream > aTempInput = aTempCont.openStream();
2077 aOriginalContent.writeStream( aTempInput, bOverWrite );
2078 bResult = true;
2079 } else {
2080 OUString aSourceMainURL = aSource.GetMainURL(INetURLObject::DecodeMechanism::NONE);
2081 OUString aDestMainURL = aDest.GetMainURL(INetURLObject::DecodeMechanism::NONE);
2083 sal_uInt64 nAttributes = GetDefaultFileAttributes(aDestMainURL);
2084 if (IsFileMovable(aDest)
2085 && osl::File::replace(aSourceMainURL, aDestMainURL) == osl::FileBase::E_None)
2087 if (nAttributes)
2088 // Adjust attributes, source might be created with
2089 // the osl_File_OpenFlag_Private flag.
2090 osl::File::setAttributes(aDestMainURL, nAttributes);
2091 bResult = true;
2093 else
2095 if( pImpl->m_aBackupURL.isEmpty() )
2096 DoInternalBackup_Impl( aOriginalContent );
2098 if( !pImpl->m_aBackupURL.isEmpty() )
2100 Reference< XInputStream > aTempInput = aTempCont.openStream();
2101 bTransactStarted = true;
2102 aOriginalContent.setPropertyValue( "Size", uno::makeAny( sal_Int64(0) ) );
2103 aOriginalContent.writeStream( aTempInput, bOverWrite );
2104 bResult = true;
2106 else
2108 pImpl->m_eError = ERRCODE_SFX_CANTCREATEBACKUP;
2113 catch ( const css::ucb::CommandAbortedException& )
2115 pImpl->m_eError = ERRCODE_ABORT;
2117 catch ( const css::ucb::CommandFailedException& )
2119 pImpl->m_eError = ERRCODE_ABORT;
2121 catch ( const css::ucb::InteractiveIOException& r )
2123 if ( r.Code == IOErrorCode_ACCESS_DENIED )
2124 pImpl->m_eError = ERRCODE_IO_ACCESSDENIED;
2125 else if ( r.Code == IOErrorCode_NOT_EXISTING )
2126 pImpl->m_eError = ERRCODE_IO_NOTEXISTS;
2127 else if ( r.Code == IOErrorCode_CANT_READ )
2128 pImpl->m_eError = ERRCODE_IO_CANTREAD;
2129 else
2130 pImpl->m_eError = ERRCODE_IO_GENERAL;
2132 // tdf#60237 - if the file is already present, raise the appropriate error
2133 catch (const css::ucb::NameClashException& )
2135 pImpl->m_eError = ERRCODE_IO_ALREADYEXISTS;
2137 catch ( const css::uno::Exception& )
2139 pImpl->m_eError = ERRCODE_IO_GENERAL;
2142 if ( bResult )
2144 if ( pImpl->pTempFile )
2146 pImpl->pTempFile->EnableKillingFile();
2147 pImpl->pTempFile.reset();
2150 else if ( bTransactStarted )
2152 UseBackupToRestore_Impl( aOriginalContent, xDummyEnv );
2155 else
2156 pImpl->m_eError = ERRCODE_IO_CANTREAD;
2160 bool SfxMedium::TryDirectTransfer( const OUString& aURL, SfxItemSet const & aTargetSet )
2162 if ( GetError() )
2163 return false;
2165 // if the document had no password it should be stored without password
2166 // if the document had password it should be stored with the same password
2167 // otherwise the stream copying can not be done
2168 const SfxStringItem* pNewPassItem = aTargetSet.GetItem<SfxStringItem>(SID_PASSWORD, false);
2169 const SfxStringItem* pOldPassItem = SfxItemSet::GetItem<SfxStringItem>(GetItemSet(), SID_PASSWORD, false);
2170 if ( ( !pNewPassItem && !pOldPassItem )
2171 || ( pNewPassItem && pOldPassItem && pNewPassItem->GetValue() == pOldPassItem->GetValue() ) )
2173 // the filter must be the same
2174 const SfxStringItem* pNewFilterItem = aTargetSet.GetItem<SfxStringItem>(SID_FILTER_NAME, false);
2175 const SfxStringItem* pOldFilterItem = SfxItemSet::GetItem<SfxStringItem>(GetItemSet(), SID_FILTER_NAME, false);
2176 if ( pNewFilterItem && pOldFilterItem && pNewFilterItem->GetValue() == pOldFilterItem->GetValue() )
2178 // get the input stream and copy it
2179 // in case of success return true
2180 uno::Reference< io::XInputStream > xInStream = GetInputStream();
2182 ResetError();
2183 if ( xInStream.is() )
2187 uno::Reference< io::XSeekable > xSeek( xInStream, uno::UNO_QUERY );
2188 sal_Int64 nPos = 0;
2189 if ( xSeek.is() )
2191 nPos = xSeek->getPosition();
2192 xSeek->seek( 0 );
2195 uno::Reference < css::ucb::XCommandEnvironment > xEnv;
2196 ::ucbhelper::Content aTargetContent( aURL, xEnv, comphelper::getProcessComponentContext() );
2198 InsertCommandArgument aInsertArg;
2199 aInsertArg.Data = xInStream;
2200 const SfxBoolItem* pOverWrite = aTargetSet.GetItem<SfxBoolItem>(SID_OVERWRITE, false);
2201 if ( pOverWrite && !pOverWrite->GetValue() ) // argument says: never overwrite
2202 aInsertArg.ReplaceExisting = false;
2203 else
2204 aInsertArg.ReplaceExisting = true; // default is overwrite existing files
2206 Any aCmdArg;
2207 aCmdArg <<= aInsertArg;
2208 aTargetContent.executeCommand( "insert",
2209 aCmdArg );
2211 if ( xSeek.is() )
2212 xSeek->seek( nPos );
2214 return true;
2216 catch( const uno::Exception& )
2222 return false;
2226 void SfxMedium::Transfer_Impl()
2228 // The transfer is required only in two cases: either if there is a temporary file or if there is a salvage item
2229 OUString aNameURL;
2230 if ( pImpl->pTempFile )
2231 aNameURL = pImpl->pTempFile->GetURL();
2232 else if ( !pImpl->m_aLogicName.isEmpty() && pImpl->m_bSalvageMode )
2234 // makes sense only in case logic name is set
2235 if ( osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aNameURL )
2236 != osl::FileBase::E_None )
2237 SAL_WARN( "sfx.doc", "The medium name is not convertible!" );
2240 if ( aNameURL.isEmpty() || ( pImpl->m_eError && !pImpl->m_eError.IsWarning() ) )
2241 return;
2243 SAL_INFO( "sfx.doc", "SfxMedium::Transfer_Impl, copying to target" );
2245 Reference < css::ucb::XCommandEnvironment > xEnv;
2246 Reference< XOutputStream > rOutStream;
2248 // in case an output stream is provided from outside and the URL is correct
2249 // commit to the stream
2250 if (pImpl->m_aLogicName.startsWith("private:stream"))
2252 // TODO/LATER: support storing to SID_STREAM
2253 const SfxUnoAnyItem* pOutStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_OUTPUTSTREAM, false);
2254 if( pOutStreamItem && ( pOutStreamItem->GetValue() >>= rOutStream ) )
2256 if ( pImpl->xStorage.is() )
2257 CloseStorage();
2259 CloseStreams_Impl();
2261 INetURLObject aSource( aNameURL );
2262 ::ucbhelper::Content aTempCont;
2263 if( ::ucbhelper::Content::create( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xEnv, comphelper::getProcessComponentContext(), aTempCont ) )
2267 sal_Int32 nRead;
2268 sal_Int32 nBufferSize = 32767;
2269 Sequence < sal_Int8 > aSequence ( nBufferSize );
2270 Reference< XInputStream > aTempInput = aTempCont.openStream();
2274 nRead = aTempInput->readBytes ( aSequence, nBufferSize );
2275 if ( nRead < nBufferSize )
2277 Sequence < sal_Int8 > aTempBuf ( aSequence.getConstArray(), nRead );
2278 rOutStream->writeBytes ( aTempBuf );
2280 else
2281 rOutStream->writeBytes ( aSequence );
2283 while ( nRead == nBufferSize );
2285 // remove temporary file
2286 if ( pImpl->pTempFile )
2288 pImpl->pTempFile->EnableKillingFile();
2289 pImpl->pTempFile.reset();
2292 catch( const Exception& )
2296 else
2298 SAL_WARN( "sfx.doc", "Illegal Output stream parameter!" );
2299 SetError(ERRCODE_IO_GENERAL);
2302 // free the reference
2303 if ( pImpl->m_pSet )
2304 pImpl->m_pSet->ClearItem( SID_OUTPUTSTREAM );
2306 return;
2309 GetContent();
2310 if ( !pImpl->aContent.get().is() )
2312 pImpl->m_eError = ERRCODE_IO_NOTEXISTS;
2313 return;
2316 INetURLObject aDest( GetURLObject() );
2318 // source is the temp file written so far
2319 INetURLObject aSource( aNameURL );
2321 // a special case, an interaction handler should be used for
2322 // authentication in case it is available
2323 Reference< css::ucb::XCommandEnvironment > xComEnv;
2324 Reference< css::task::XInteractionHandler > xInteractionHandler = GetInteractionHandler();
2325 if (xInteractionHandler.is())
2326 xComEnv = new ::ucbhelper::CommandEnvironment( xInteractionHandler,
2327 Reference< css::ucb::XProgressHandler >() );
2329 OUString aDestURL( aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
2331 if ( comphelper::isFileUrl( aDestURL ) || !aDest.removeSegment() )
2333 TransactedTransferForFS_Impl( aSource, aDest, xComEnv );
2335 if (!pImpl->m_bDisableFileSync)
2337 // Hideous - no clean way to do this, so we re-open the file just to fsync it
2338 osl::File aFile( aDestURL );
2339 if ( aFile.open( osl_File_OpenFlag_Write ) == osl::FileBase::E_None )
2341 aFile.sync();
2342 SAL_INFO( "sfx.doc", "fsync'd saved file '" << aDestURL << "'" );
2343 aFile.close();
2347 else
2349 // create content for the parent folder and call transfer on that content with the source content
2350 // and the destination file name as parameters
2351 ::ucbhelper::Content aSourceContent;
2352 ::ucbhelper::Content aTransferContent;
2354 ::ucbhelper::Content aDestContent;
2355 (void)::ucbhelper::Content::create( aDestURL, xComEnv, comphelper::getProcessComponentContext(), aDestContent );
2356 // For checkin, we need the object URL, not the parent folder:
2357 if ( !IsInCheckIn( ) )
2359 // Get the parent URL from the XChild if possible: why would the URL necessarily have
2360 // a hierarchical path? It's not always the case for CMIS.
2361 Reference< css::container::XChild> xChild( aDestContent.get(), uno::UNO_QUERY );
2362 OUString sParentUrl;
2363 if ( xChild.is( ) )
2365 Reference< css::ucb::XContent > xParent( xChild->getParent( ), uno::UNO_QUERY );
2366 if ( xParent.is( ) )
2368 sParentUrl = xParent->getIdentifier( )->getContentIdentifier();
2372 if ( sParentUrl.isEmpty() )
2373 aDestURL = aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE );
2374 // adjust to above aDest.removeSegment()
2375 else
2376 aDestURL = sParentUrl;
2379 // LongName wasn't defined anywhere, only used here... get the Title instead
2380 // as it's less probably empty
2381 OUString aFileName;
2382 Any aAny = aDestContent.getPropertyValue("Title");
2383 aAny >>= aFileName;
2384 aAny = aDestContent.getPropertyValue( "ObjectId" );
2385 OUString sObjectId;
2386 aAny >>= sObjectId;
2387 if ( aFileName.isEmpty() )
2388 aFileName = GetURLObject().getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
2392 aTransferContent = ::ucbhelper::Content( aDestURL, xComEnv, comphelper::getProcessComponentContext() );
2394 catch (const css::ucb::ContentCreationException& ex)
2396 pImpl->m_eError = ERRCODE_IO_GENERAL;
2397 if (
2398 (ex.eError == css::ucb::ContentCreationError_NO_CONTENT_PROVIDER ) ||
2399 (ex.eError == css::ucb::ContentCreationError_CONTENT_CREATION_FAILED)
2402 pImpl->m_eError = ERRCODE_IO_NOTEXISTSPATH;
2405 catch (const css::uno::Exception&)
2407 pImpl->m_eError = ERRCODE_IO_GENERAL;
2410 if ( !pImpl->m_eError || pImpl->m_eError.IsWarning() )
2412 // free resources, otherwise the transfer may fail
2413 if ( pImpl->xStorage.is() )
2414 CloseStorage();
2416 CloseStreams_Impl();
2418 (void)::ucbhelper::Content::create( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xEnv, comphelper::getProcessComponentContext(), aSourceContent );
2420 // check for external parameters that may customize the handling of NameClash situations
2421 const SfxBoolItem* pOverWrite = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_OVERWRITE, false);
2422 sal_Int32 nNameClash;
2423 if ( pOverWrite && !pOverWrite->GetValue() )
2424 // argument says: never overwrite
2425 nNameClash = NameClash::ERROR;
2426 else
2427 // default is overwrite existing files
2428 nNameClash = NameClash::OVERWRITE;
2432 OUString aMimeType = pImpl->getFilterMimeType();
2433 ::ucbhelper::InsertOperation eOperation = ::ucbhelper::InsertOperation::Copy;
2434 bool bMajor = false;
2435 OUString sComment;
2436 if ( IsInCheckIn( ) )
2438 eOperation = ::ucbhelper::InsertOperation::Checkin;
2439 const SfxBoolItem* pMajor = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_DOCINFO_MAJOR, false);
2440 bMajor = pMajor && pMajor->GetValue( );
2441 const SfxStringItem* pComments = SfxItemSet::GetItem<SfxStringItem>(GetItemSet(), SID_DOCINFO_COMMENTS, false);
2442 if ( pComments )
2443 sComment = pComments->GetValue( );
2445 OUString sResultURL;
2446 aTransferContent.transferContent(
2447 aSourceContent, eOperation,
2448 aFileName, nNameClash, aMimeType, bMajor, sComment,
2449 &sResultURL, sObjectId );
2451 if ( !sResultURL.isEmpty( ) ) // Likely to happen only for checkin
2452 SwitchDocumentToFile( sResultURL );
2455 if ( GetURLObject().isAnyKnownWebDAVScheme() &&
2456 eOperation == ::ucbhelper::InsertOperation::Copy )
2458 // tdf#95272 try to re-issue a lock command when a new file is created.
2459 // This may be needed because some WebDAV servers fail to implement the
2460 // 'LOCK on unallocated reference', see issue comment:
2461 // <https://bugs.documentfoundation.org/show_bug.cgi?id=95792#c8>
2462 // and specification at:
2463 // <http://tools.ietf.org/html/rfc4918#section-7.3>
2464 // If the WebDAV resource is already locked by this LO instance, nothing will
2465 // happen, e.g. the LOCK method will not be sent to the server.
2466 ::ucbhelper::Content aLockContent( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() );
2467 aLockContent.lock();
2470 catch ( css::uno::Exception & )
2472 TOOLS_WARN_EXCEPTION( "sfx.doc", "LOCK not working while re-issuing it" );
2475 catch ( const css::ucb::CommandAbortedException& )
2477 pImpl->m_eError = ERRCODE_ABORT;
2479 catch ( const css::ucb::CommandFailedException& )
2481 pImpl->m_eError = ERRCODE_ABORT;
2483 catch ( const css::ucb::InteractiveIOException& r )
2485 if ( r.Code == IOErrorCode_ACCESS_DENIED )
2486 pImpl->m_eError = ERRCODE_IO_ACCESSDENIED;
2487 else if ( r.Code == IOErrorCode_NOT_EXISTING )
2488 pImpl->m_eError = ERRCODE_IO_NOTEXISTS;
2489 else if ( r.Code == IOErrorCode_CANT_READ )
2490 pImpl->m_eError = ERRCODE_IO_CANTREAD;
2491 else
2492 pImpl->m_eError = ERRCODE_IO_GENERAL;
2494 catch ( const css::uno::Exception& )
2496 pImpl->m_eError = ERRCODE_IO_GENERAL;
2499 // do not switch from temporary file in case of nonfile protocol
2503 if ( ( !pImpl->m_eError || pImpl->m_eError.IsWarning() ) && !pImpl->pTempFile )
2505 // without a TempFile the physical and logical name should be the same after successful transfer
2506 if (osl::FileBase::getSystemPathFromFileURL(
2507 GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), pImpl->m_aName )
2508 != osl::FileBase::E_None)
2510 pImpl->m_aName.clear();
2512 pImpl->m_bSalvageMode = false;
2517 void SfxMedium::DoInternalBackup_Impl( const ::ucbhelper::Content& aOriginalContent,
2518 const OUString& aPrefix,
2519 const OUString& aExtension,
2520 const OUString& aDestDir )
2522 if ( !pImpl->m_aBackupURL.isEmpty() )
2523 return; // the backup was done already
2525 ::utl::TempFile aTransactTemp( aPrefix, true, &aExtension, &aDestDir );
2527 INetURLObject aBackObj( aTransactTemp.GetURL() );
2528 OUString aBackupName = aBackObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
2530 Reference < css::ucb::XCommandEnvironment > xDummyEnv;
2531 ::ucbhelper::Content aBackupCont;
2532 if( ::ucbhelper::Content::create( aDestDir, xDummyEnv, comphelper::getProcessComponentContext(), aBackupCont ) )
2536 OUString sMimeType = pImpl->getFilterMimeType();
2537 aBackupCont.transferContent( aOriginalContent,
2538 ::ucbhelper::InsertOperation::Copy,
2539 aBackupName,
2540 NameClash::OVERWRITE,
2541 sMimeType );
2542 pImpl->m_aBackupURL = aBackObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
2543 pImpl->m_bRemoveBackup = true;
2545 catch( const Exception& )
2549 if ( pImpl->m_aBackupURL.isEmpty() )
2550 aTransactTemp.EnableKillingFile();
2554 void SfxMedium::DoInternalBackup_Impl( const ::ucbhelper::Content& aOriginalContent )
2556 if ( !pImpl->m_aBackupURL.isEmpty() )
2557 return; // the backup was done already
2559 OUString aFileName = GetURLObject().getName( INetURLObject::LAST_SEGMENT,
2560 true,
2561 INetURLObject::DecodeMechanism::NONE );
2563 sal_Int32 nPrefixLen = aFileName.lastIndexOf( '.' );
2564 OUString aPrefix = ( nPrefixLen == -1 ) ? aFileName : aFileName.copy( 0, nPrefixLen );
2565 OUString aExtension = ( nPrefixLen == -1 ) ? OUString() : aFileName.copy( nPrefixLen );
2566 OUString aBakDir = SvtPathOptions().GetBackupPath();
2568 // create content for the parent folder ( = backup folder )
2569 ::ucbhelper::Content aContent;
2570 Reference < css::ucb::XCommandEnvironment > xEnv;
2571 if( ::utl::UCBContentHelper::ensureFolder(comphelper::getProcessComponentContext(), xEnv, aBakDir, aContent) )
2572 DoInternalBackup_Impl( aOriginalContent, aPrefix, aExtension, aBakDir );
2574 if ( !pImpl->m_aBackupURL.isEmpty() )
2575 return;
2577 // the copying to the backup catalog failed ( for example because
2578 // of using an encrypted partition as target catalog )
2579 // since the user did not specify to make backup explicitly
2580 // office should try to make backup in another place,
2581 // target catalog does not look bad for this case ( and looks
2582 // to be the only way for encrypted partitions )
2584 INetURLObject aDest = GetURLObject();
2585 if ( aDest.removeSegment() )
2586 DoInternalBackup_Impl( aOriginalContent, aPrefix, aExtension, aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
2590 void SfxMedium::DoBackup_Impl()
2592 // source file name is the logical name of this medium
2593 INetURLObject aSource( GetURLObject() );
2595 // there is nothing to backup in case source file does not exist
2596 if ( !::utl::UCBContentHelper::IsDocument( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) )
2597 return;
2599 bool bSuccess = false;
2601 // get path for backups
2602 OUString aBakDir = SvtPathOptions().GetBackupPath();
2603 if( !aBakDir.isEmpty() )
2605 // create content for the parent folder ( = backup folder )
2606 ::ucbhelper::Content aContent;
2607 Reference < css::ucb::XCommandEnvironment > xEnv;
2608 if( ::utl::UCBContentHelper::ensureFolder(comphelper::getProcessComponentContext(), xEnv, aBakDir, aContent) )
2610 // save as ".bak" file
2611 INetURLObject aDest( aBakDir );
2612 aDest.insertName( aSource.getName() );
2613 aDest.setExtension( u"bak" );
2614 OUString aFileName = aDest.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
2616 // create a content for the source file
2617 ::ucbhelper::Content aSourceContent;
2618 if ( ::ucbhelper::Content::create( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xEnv, comphelper::getProcessComponentContext(), aSourceContent ) )
2622 // do the transfer ( copy source file to backup dir )
2623 OUString sMimeType = pImpl->getFilterMimeType();
2624 aContent.transferContent( aSourceContent,
2625 ::ucbhelper::InsertOperation::Copy,
2626 aFileName,
2627 NameClash::OVERWRITE,
2628 sMimeType );
2629 pImpl->m_aBackupURL = aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE );
2630 pImpl->m_bRemoveBackup = false;
2631 bSuccess = true;
2633 catch ( const css::uno::Exception& )
2640 if ( !bSuccess )
2642 pImpl->m_eError = ERRCODE_SFX_CANTCREATEBACKUP;
2647 void SfxMedium::ClearBackup_Impl()
2649 if( pImpl->m_bRemoveBackup )
2651 // currently a document is always stored in a new medium,
2652 // thus if a backup can not be removed the backup URL should not be cleaned
2653 if ( !pImpl->m_aBackupURL.isEmpty() )
2655 if ( ::utl::UCBContentHelper::Kill( pImpl->m_aBackupURL ) )
2657 pImpl->m_bRemoveBackup = false;
2658 pImpl->m_aBackupURL.clear();
2660 else
2663 SAL_WARN( "sfx.doc", "Couldn't remove backup file!");
2667 else
2668 pImpl->m_aBackupURL.clear();
2672 void SfxMedium::GetLockingStream_Impl()
2674 if ( GetURLObject().GetProtocol() != INetProtocol::File
2675 || pImpl->m_xLockingStream.is() )
2676 return;
2678 const SfxUnoAnyItem* pWriteStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_STREAM, false);
2679 if ( pWriteStreamItem )
2680 pWriteStreamItem->GetValue() >>= pImpl->m_xLockingStream;
2682 if ( pImpl->m_xLockingStream.is() )
2683 return;
2685 // open the original document
2686 uno::Sequence< beans::PropertyValue > xProps;
2687 TransformItems( SID_OPENDOC, *GetItemSet(), xProps );
2688 utl::MediaDescriptor aMedium( xProps );
2690 aMedium.addInputStreamOwnLock();
2692 uno::Reference< io::XInputStream > xInputStream;
2693 aMedium[utl::MediaDescriptor::PROP_STREAM] >>= pImpl->m_xLockingStream;
2694 aMedium[utl::MediaDescriptor::PROP_INPUTSTREAM] >>= xInputStream;
2696 if ( !pImpl->pTempFile && pImpl->m_aName.isEmpty() )
2698 // the medium is still based on the original file, it makes sense to initialize the streams
2699 if ( pImpl->m_xLockingStream.is() )
2700 pImpl->xStream = pImpl->m_xLockingStream;
2702 if ( xInputStream.is() )
2703 pImpl->xInputStream = xInputStream;
2705 if ( !pImpl->xInputStream.is() && pImpl->xStream.is() )
2706 pImpl->xInputStream = pImpl->xStream->getInputStream();
2711 void SfxMedium::GetMedium_Impl()
2713 if ( pImpl->m_pInStream
2714 && (!pImpl->bIsTemp || pImpl->xInputStream.is() || pImpl->m_xInputStreamToLoadFrom.is() || pImpl->xStream.is() || pImpl->m_xLockingStream.is() ) )
2715 return;
2717 pImpl->bDownloadDone = false;
2718 Reference< css::task::XInteractionHandler > xInteractionHandler = GetInteractionHandler();
2720 //TODO/MBA: need support for SID_STREAM
2721 const SfxUnoAnyItem* pWriteStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_STREAM, false);
2722 const SfxUnoAnyItem* pInStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_INPUTSTREAM, false);
2723 if ( pWriteStreamItem )
2725 pWriteStreamItem->GetValue() >>= pImpl->xStream;
2727 if ( pInStreamItem )
2728 pInStreamItem->GetValue() >>= pImpl->xInputStream;
2730 if ( !pImpl->xInputStream.is() && pImpl->xStream.is() )
2731 pImpl->xInputStream = pImpl->xStream->getInputStream();
2733 else if ( pInStreamItem )
2735 pInStreamItem->GetValue() >>= pImpl->xInputStream;
2737 else
2739 uno::Sequence < beans::PropertyValue > xProps;
2740 OUString aFileName;
2741 if (!pImpl->m_aName.isEmpty())
2743 if ( osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aFileName )
2744 != osl::FileBase::E_None )
2746 SAL_WARN( "sfx.doc", "Physical name not convertible!");
2749 else
2750 aFileName = GetName();
2752 // in case the temporary file exists the streams should be initialized from it,
2753 // but the original MediaDescriptor should not be changed
2754 bool bFromTempFile = ( pImpl->pTempFile != nullptr );
2756 if ( !bFromTempFile )
2758 GetItemSet()->Put( SfxStringItem( SID_FILE_NAME, aFileName ) );
2759 if( !(pImpl->m_nStorOpenMode & StreamMode::WRITE) )
2760 GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
2761 if (xInteractionHandler.is())
2762 GetItemSet()->Put( SfxUnoAnyItem( SID_INTERACTIONHANDLER, makeAny(xInteractionHandler) ) );
2765 if ( pImpl->m_xInputStreamToLoadFrom.is() )
2767 pImpl->xInputStream = pImpl->m_xInputStreamToLoadFrom;
2768 if (pImpl->m_bInputStreamIsReadOnly)
2769 GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
2771 else
2773 TransformItems( SID_OPENDOC, *GetItemSet(), xProps );
2774 utl::MediaDescriptor aMedium( xProps );
2776 if ( pImpl->m_xLockingStream.is() && !bFromTempFile )
2778 // the medium is not based on the temporary file, so the original stream can be used
2779 pImpl->xStream = pImpl->m_xLockingStream;
2781 else
2783 if ( bFromTempFile )
2785 aMedium[utl::MediaDescriptor::PROP_URL] <<= aFileName;
2786 aMedium.erase( utl::MediaDescriptor::PROP_READONLY );
2787 aMedium.addInputStream();
2789 else if ( GetURLObject().GetProtocol() == INetProtocol::File )
2791 // use the special locking approach only for file URLs
2792 aMedium.addInputStreamOwnLock();
2794 else
2796 // add a check for protocol, if it's http or https or provide webdav then add
2797 // the interaction handler to be used by the authentication dialog
2798 if ( GetURLObject().isAnyKnownWebDAVScheme() )
2800 aMedium[utl::MediaDescriptor::PROP_AUTHENTICATIONHANDLER] <<= GetInteractionHandler( true );
2802 aMedium.addInputStream();
2804 // the ReadOnly property set in aMedium is ignored
2805 // the check is done in LockOrigFileOnDemand() for file and non-file URLs
2807 //TODO/MBA: what happens if property is not there?!
2808 aMedium[utl::MediaDescriptor::PROP_STREAM] >>= pImpl->xStream;
2809 aMedium[utl::MediaDescriptor::PROP_INPUTSTREAM] >>= pImpl->xInputStream;
2812 GetContent();
2813 if ( !pImpl->xInputStream.is() && pImpl->xStream.is() )
2814 pImpl->xInputStream = pImpl->xStream->getInputStream();
2817 if ( !bFromTempFile )
2819 //TODO/MBA: need support for SID_STREAM
2820 if ( pImpl->xStream.is() )
2821 GetItemSet()->Put( SfxUnoAnyItem( SID_STREAM, makeAny( pImpl->xStream ) ) );
2823 GetItemSet()->Put( SfxUnoAnyItem( SID_INPUTSTREAM, makeAny( pImpl->xInputStream ) ) );
2827 //TODO/MBA: ErrorHandling - how to transport error from MediaDescriptor
2828 if ( !GetError() && !pImpl->xStream.is() && !pImpl->xInputStream.is() )
2829 SetError(ERRCODE_IO_ACCESSDENIED);
2831 if ( !GetError() && !pImpl->m_pInStream )
2833 if ( pImpl->xStream.is() )
2834 pImpl->m_pInStream = utl::UcbStreamHelper::CreateStream( pImpl->xStream );
2835 else if ( pImpl->xInputStream.is() )
2836 pImpl->m_pInStream = utl::UcbStreamHelper::CreateStream( pImpl->xInputStream );
2839 pImpl->bDownloadDone = true;
2840 pImpl->aDoneLink.ClearPendingCall();
2841 ErrCode nError = GetError();
2842 pImpl->aDoneLink.Call( reinterpret_cast<void*>(sal_uInt32(nError)) );
2845 bool SfxMedium::IsRemote() const
2847 return pImpl->m_bRemote;
2850 void SfxMedium::SetUpdatePickList(bool bVal)
2852 pImpl->bUpdatePickList = bVal;
2855 bool SfxMedium::IsUpdatePickList() const
2857 return pImpl->bUpdatePickList;
2860 void SfxMedium::SetLongName(const OUString &rName)
2862 pImpl->m_aLongName = rName;
2865 const OUString& SfxMedium::GetLongName() const
2867 return pImpl->m_aLongName;
2870 void SfxMedium::SetDoneLink( const Link<void*,void>& rLink )
2872 pImpl->aDoneLink = rLink;
2875 void SfxMedium::Download( const Link<void*,void>& aLink )
2877 SetDoneLink( aLink );
2878 GetInStream();
2879 if ( pImpl->m_pInStream && !aLink.IsSet() )
2881 while( !pImpl->bDownloadDone && !Application::IsQuit())
2882 Application::Yield();
2887 void SfxMedium::Init_Impl()
2888 /* [Description]
2889 Includes a valid:: sun:: com:: star:: util:: URL (If a file name was
2890 previously in there) in the logical name and if available sets the
2891 physical name as the file name.
2895 Reference< XOutputStream > rOutStream;
2897 // TODO/LATER: handle lifetime of storages
2898 pImpl->bDisposeStorage = false;
2900 const SfxStringItem* pSalvageItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_DOC_SALVAGE, false);
2901 if ( pSalvageItem && pSalvageItem->GetValue().isEmpty() )
2903 pSalvageItem = nullptr;
2904 pImpl->m_pSet->ClearItem( SID_DOC_SALVAGE );
2907 if (!pImpl->m_aLogicName.isEmpty())
2909 INetURLObject aUrl( pImpl->m_aLogicName );
2910 INetProtocol eProt = aUrl.GetProtocol();
2911 if ( eProt == INetProtocol::NotValid )
2913 SAL_WARN( "sfx.doc", "URL <" << pImpl->m_aLogicName << "> with unknown protocol" );
2915 else
2917 if ( aUrl.HasMark() )
2919 std::unique_lock<std::recursive_mutex> chkEditLock;
2920 if (pImpl->m_pCheckEditableWorkerMutex != nullptr)
2921 chkEditLock = std::unique_lock<std::recursive_mutex>(
2922 *(pImpl->m_pCheckEditableWorkerMutex));
2923 pImpl->m_aLogicName = aUrl.GetURLNoMark( INetURLObject::DecodeMechanism::NONE );
2924 if (chkEditLock.owns_lock())
2925 chkEditLock.unlock();
2926 GetItemSet()->Put( SfxStringItem( SID_JUMPMARK, aUrl.GetMark() ) );
2929 // try to convert the URL into a physical name - but never change a physical name
2930 // physical name may be set if the logical name is changed after construction
2931 if ( pImpl->m_aName.isEmpty() )
2932 osl::FileBase::getSystemPathFromFileURL( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), pImpl->m_aName );
2933 else
2935 DBG_ASSERT( pSalvageItem, "Suspicious change of logical name!" );
2940 if ( pSalvageItem && !pSalvageItem->GetValue().isEmpty() )
2942 std::unique_lock<std::recursive_mutex> chkEditLock;
2943 if (pImpl->m_pCheckEditableWorkerMutex != nullptr)
2944 chkEditLock
2945 = std::unique_lock<std::recursive_mutex>(*(pImpl->m_pCheckEditableWorkerMutex));
2946 pImpl->m_aLogicName = pSalvageItem->GetValue();
2947 pImpl->m_pURLObj.reset();
2948 if (chkEditLock.owns_lock())
2949 chkEditLock.unlock();
2950 pImpl->m_bSalvageMode = true;
2953 // in case output stream is by mistake here
2954 // clear the reference
2955 const SfxUnoAnyItem* pOutStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_OUTPUTSTREAM, false);
2956 if( pOutStreamItem
2957 && ( !( pOutStreamItem->GetValue() >>= rOutStream )
2958 || !pImpl->m_aLogicName.startsWith("private:stream")) )
2960 pImpl->m_pSet->ClearItem( SID_OUTPUTSTREAM );
2961 SAL_WARN( "sfx.doc", "Unexpected Output stream parameter!" );
2964 if (!pImpl->m_aLogicName.isEmpty())
2966 // if the logic name is set it should be set in MediaDescriptor as well
2967 const SfxStringItem* pFileNameItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_FILE_NAME, false);
2968 if ( !pFileNameItem )
2970 // let the ItemSet be created if necessary
2971 GetItemSet()->Put(
2972 SfxStringItem(
2973 SID_FILE_NAME, INetURLObject( pImpl->m_aLogicName ).GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) );
2977 SetIsRemote_Impl();
2979 osl::DirectoryItem item;
2980 if (osl::DirectoryItem::get(GetName(), item) == osl::FileBase::E_None) {
2981 osl::FileStatus stat(osl_FileStatus_Mask_Attributes);
2982 if (item.getFileStatus(stat) == osl::FileBase::E_None
2983 && stat.isValid(osl_FileStatus_Mask_Attributes))
2985 if ((stat.getAttributes() & osl_File_Attribute_ReadOnly) != 0)
2987 pImpl->m_bOriginallyReadOnly = true;
2994 SfxMedium::SfxMedium() : pImpl(new SfxMedium_Impl)
2996 Init_Impl();
3000 void SfxMedium::UseInteractionHandler( bool bUse )
3002 pImpl->bAllowDefaultIntHdl = bUse;
3006 css::uno::Reference< css::task::XInteractionHandler >
3007 SfxMedium::GetInteractionHandler( bool bGetAlways )
3009 // if interaction isn't allowed explicitly ... return empty reference!
3010 if ( !bGetAlways && !pImpl->bUseInteractionHandler )
3011 return css::uno::Reference< css::task::XInteractionHandler >();
3013 // search a possible existing handler inside cached item set
3014 if ( pImpl->m_pSet )
3016 css::uno::Reference< css::task::XInteractionHandler > xHandler;
3017 const SfxUnoAnyItem* pHandler = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_INTERACTIONHANDLER, false);
3018 if ( pHandler && (pHandler->GetValue() >>= xHandler) && xHandler.is() )
3019 return xHandler;
3022 // if default interaction isn't allowed explicitly ... return empty reference!
3023 if ( !bGetAlways && !pImpl->bAllowDefaultIntHdl )
3024 return css::uno::Reference< css::task::XInteractionHandler >();
3026 // otherwise return cached default handler ... if it exist.
3027 if ( pImpl->xInteraction.is() )
3028 return pImpl->xInteraction;
3030 // create default handler and cache it!
3031 Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
3032 pImpl->xInteraction.set(
3033 task::InteractionHandler::createWithParent(xContext, nullptr), UNO_QUERY_THROW );
3034 return pImpl->xInteraction;
3037 void SfxMedium::SetFilter( const std::shared_ptr<const SfxFilter>& pFilter )
3039 pImpl->m_pFilter = pFilter;
3042 const std::shared_ptr<const SfxFilter>& SfxMedium::GetFilter() const
3044 return pImpl->m_pFilter;
3047 sal_uInt32 SfxMedium::CreatePasswordToModifyHash( const OUString& aPasswd, bool bWriter )
3049 sal_uInt32 nHash = 0;
3051 if ( !aPasswd.isEmpty() )
3053 if ( bWriter )
3055 nHash = ::comphelper::DocPasswordHelper::GetWordHashAsUINT32( aPasswd );
3057 else
3059 rtl_TextEncoding nEncoding = osl_getThreadTextEncoding();
3060 nHash = ::comphelper::DocPasswordHelper::GetXLHashAsUINT16( aPasswd, nEncoding );
3064 return nHash;
3068 void SfxMedium::Close(bool bInDestruction)
3070 if ( pImpl->xStorage.is() )
3072 CloseStorage();
3075 CloseStreams_Impl(bInDestruction);
3077 UnlockFile( false );
3080 void SfxMedium::CloseAndRelease()
3082 if ( pImpl->xStorage.is() )
3084 CloseStorage();
3087 CloseAndReleaseStreams_Impl();
3089 UnlockFile( true );
3092 void SfxMedium::DisableUnlockWebDAV( bool bDisableUnlockWebDAV )
3094 pImpl->m_bDisableUnlockWebDAV = bDisableUnlockWebDAV;
3097 void SfxMedium::DisableFileSync(bool bDisableFileSync)
3099 pImpl->m_bDisableFileSync = bDisableFileSync;
3102 void SfxMedium::UnlockFile( bool bReleaseLockStream )
3104 #if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT
3105 (void) bReleaseLockStream;
3106 #else
3107 // check if webdav
3108 if ( GetURLObject().isAnyKnownWebDAVScheme() )
3110 // do nothing if WebDAV locking if disabled
3111 // (shouldn't happen because we already skipped locking,
3112 // see LockOrigFileOnDemand, but just in case ...)
3113 if (!IsWebDAVLockingUsed())
3114 return;
3116 if ( pImpl->m_bLocked )
3118 // an interaction handler should be used for authentication, if needed
3119 try {
3120 uno::Reference< css::task::XInteractionHandler > xHandler = GetInteractionHandler( true );
3121 uno::Reference< css::ucb::XCommandEnvironment > xComEnv = new ::ucbhelper::CommandEnvironment( xHandler,
3122 Reference< css::ucb::XProgressHandler >() );
3123 ucbhelper::Content aContentToUnlock( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext());
3124 pImpl->m_bLocked = false;
3125 //check if WebDAV unlock was explicitly disabled
3126 if ( !pImpl->m_bDisableUnlockWebDAV )
3127 aContentToUnlock.unlock();
3129 catch ( uno::Exception& )
3131 TOOLS_WARN_EXCEPTION( "sfx.doc", "Locking exception: WebDAV while trying to lock the file" );
3134 return;
3137 if ( pImpl->m_xLockingStream.is() )
3139 if ( bReleaseLockStream )
3143 uno::Reference< io::XInputStream > xInStream = pImpl->m_xLockingStream->getInputStream();
3144 uno::Reference< io::XOutputStream > xOutStream = pImpl->m_xLockingStream->getOutputStream();
3145 if ( xInStream.is() )
3146 xInStream->closeInput();
3147 if ( xOutStream.is() )
3148 xOutStream->closeOutput();
3150 catch( const uno::Exception& )
3154 pImpl->m_xLockingStream.clear();
3157 if ( !pImpl->m_bLocked )
3158 return;
3160 ::svt::DocumentLockFile aLockFile( pImpl->m_aLogicName );
3164 pImpl->m_bLocked = false;
3165 // TODO/LATER: A warning could be shown in case the file is not the own one
3166 aLockFile.RemoveFile();
3168 catch( const io::WrongFormatException& )
3172 // erase the empty or corrupt file
3173 aLockFile.RemoveFileDirectly();
3175 catch( const uno::Exception& )
3178 catch( const uno::Exception& )
3181 if(!pImpl->m_bMSOLockFileCreated)
3182 return;
3184 ::svt::MSODocumentLockFile aMSOLockFile( pImpl->m_aLogicName );
3188 pImpl->m_bLocked = false;
3189 // TODO/LATER: A warning could be shown in case the file is not the own one
3190 aMSOLockFile.RemoveFile();
3192 catch( const io::WrongFormatException& )
3196 // erase the empty or corrupt file
3197 aMSOLockFile.RemoveFileDirectly();
3199 catch( const uno::Exception& )
3202 catch( const uno::Exception& )
3204 pImpl->m_bMSOLockFileCreated = false;
3205 #endif
3208 void SfxMedium::CloseAndReleaseStreams_Impl()
3210 CloseZipStorage_Impl();
3212 uno::Reference< io::XInputStream > xInToClose = pImpl->xInputStream;
3213 uno::Reference< io::XOutputStream > xOutToClose;
3214 if ( pImpl->xStream.is() )
3216 xOutToClose = pImpl->xStream->getOutputStream();
3218 // if the locking stream is closed here the related member should be cleaned
3219 if ( pImpl->xStream == pImpl->m_xLockingStream )
3220 pImpl->m_xLockingStream.clear();
3223 // The probably existing SvStream wrappers should be closed first
3224 CloseStreams_Impl();
3226 // in case of salvage mode the storage is based on the streams
3227 if ( pImpl->m_bSalvageMode )
3228 return;
3232 if ( xInToClose.is() )
3233 xInToClose->closeInput();
3234 if ( xOutToClose.is() )
3235 xOutToClose->closeOutput();
3237 catch ( const uno::Exception& )
3243 void SfxMedium::CloseStreams_Impl(bool bInDestruction)
3245 CloseInStream_Impl(bInDestruction);
3246 CloseOutStream_Impl();
3248 if ( pImpl->m_pSet )
3249 pImpl->m_pSet->ClearItem( SID_CONTENT );
3251 pImpl->aContent = ::ucbhelper::Content();
3255 void SfxMedium::SetIsRemote_Impl()
3257 INetURLObject aObj( GetName() );
3258 switch( aObj.GetProtocol() )
3260 case INetProtocol::Ftp:
3261 case INetProtocol::Http:
3262 case INetProtocol::Https:
3263 pImpl->m_bRemote = true;
3264 break;
3265 default:
3266 pImpl->m_bRemote = GetName().startsWith("private:msgid");
3267 break;
3270 // As files that are written to the remote transmission must also be able
3271 // to be read.
3272 if (pImpl->m_bRemote)
3273 pImpl->m_nStorOpenMode |= StreamMode::READ;
3277 void SfxMedium::SetName( const OUString& aNameP, bool bSetOrigURL )
3279 if (pImpl->aOrigURL.isEmpty())
3280 pImpl->aOrigURL = pImpl->m_aLogicName;
3281 if( bSetOrigURL )
3282 pImpl->aOrigURL = aNameP;
3283 std::unique_lock<std::recursive_mutex> chkEditLock;
3284 if (pImpl->m_pCheckEditableWorkerMutex != nullptr)
3285 chkEditLock = std::unique_lock<std::recursive_mutex>(*(pImpl->m_pCheckEditableWorkerMutex));
3286 pImpl->m_aLogicName = aNameP;
3287 pImpl->m_pURLObj.reset();
3288 if (chkEditLock.owns_lock())
3289 chkEditLock.unlock();
3290 pImpl->aContent = ::ucbhelper::Content();
3291 Init_Impl();
3295 const OUString& SfxMedium::GetOrigURL() const
3297 return pImpl->aOrigURL.isEmpty() ? pImpl->m_aLogicName : pImpl->aOrigURL;
3301 void SfxMedium::SetPhysicalName_Impl( const OUString& rNameP )
3303 if ( rNameP != pImpl->m_aName )
3305 pImpl->pTempFile.reset();
3307 if ( !pImpl->m_aName.isEmpty() || !rNameP.isEmpty() )
3308 pImpl->aContent = ::ucbhelper::Content();
3310 pImpl->m_aName = rNameP;
3311 pImpl->m_bTriedStorage = false;
3312 pImpl->bIsStorage = false;
3316 void SfxMedium::ReOpen()
3318 bool bUseInteractionHandler = pImpl->bUseInteractionHandler;
3319 pImpl->bUseInteractionHandler = false;
3320 GetMedium_Impl();
3321 pImpl->bUseInteractionHandler = bUseInteractionHandler;
3324 void SfxMedium::CompleteReOpen()
3326 // do not use temporary file for reopen and in case of success throw the temporary file away
3327 bool bUseInteractionHandler = pImpl->bUseInteractionHandler;
3328 pImpl->bUseInteractionHandler = false;
3330 std::unique_ptr<::utl::TempFile> pTmpFile;
3331 if ( pImpl->pTempFile )
3333 pTmpFile = std::move(pImpl->pTempFile);
3334 pImpl->m_aName.clear();
3337 GetMedium_Impl();
3339 if ( GetError() )
3341 if ( pImpl->pTempFile )
3343 pImpl->pTempFile->EnableKillingFile();
3344 pImpl->pTempFile.reset();
3346 pImpl->pTempFile = std::move( pTmpFile );
3347 if ( pImpl->pTempFile )
3348 pImpl->m_aName = pImpl->pTempFile->GetFileName();
3350 else if (pTmpFile)
3352 pTmpFile->EnableKillingFile();
3353 pTmpFile.reset();
3356 pImpl->bUseInteractionHandler = bUseInteractionHandler;
3359 SfxMedium::SfxMedium(const OUString &rName, StreamMode nOpenMode, std::shared_ptr<const SfxFilter> pFilter, const std::shared_ptr<SfxItemSet>& pInSet) :
3360 pImpl(new SfxMedium_Impl)
3362 pImpl->m_pSet = pInSet;
3363 pImpl->m_pFilter = std::move(pFilter);
3364 pImpl->m_aLogicName = rName;
3365 pImpl->m_nStorOpenMode = nOpenMode;
3366 Init_Impl();
3369 SfxMedium::SfxMedium(const OUString &rName, const OUString &rReferer, StreamMode nOpenMode, std::shared_ptr<const SfxFilter> pFilter, const std::shared_ptr<SfxItemSet>& pInSet) :
3370 pImpl(new SfxMedium_Impl)
3372 pImpl->m_pSet = pInSet;
3373 SfxItemSet * s = GetItemSet();
3374 if (s->GetItem(SID_REFERER) == nullptr) {
3375 s->Put(SfxStringItem(SID_REFERER, rReferer));
3377 pImpl->m_pFilter = std::move(pFilter);
3378 pImpl->m_aLogicName = rName;
3379 pImpl->m_nStorOpenMode = nOpenMode;
3380 Init_Impl();
3383 SfxMedium::SfxMedium( const uno::Sequence<beans::PropertyValue>& aArgs ) :
3384 pImpl(new SfxMedium_Impl)
3386 SfxAllItemSet *pParams = new SfxAllItemSet( SfxGetpApp()->GetPool() );
3387 pImpl->m_pSet.reset( pParams );
3388 TransformParameters( SID_OPENDOC, aArgs, *pParams );
3389 SetArgs(aArgs);
3391 OUString aFilterProvider, aFilterName;
3393 const SfxPoolItem* pItem = nullptr;
3394 if (pImpl->m_pSet->HasItem(SID_FILTER_PROVIDER, &pItem))
3395 aFilterProvider = static_cast<const SfxStringItem*>(pItem)->GetValue();
3397 if (pImpl->m_pSet->HasItem(SID_FILTER_NAME, &pItem))
3398 aFilterName = static_cast<const SfxStringItem*>(pItem)->GetValue();
3401 if (aFilterProvider.isEmpty())
3403 // This is a conventional filter type.
3404 pImpl->m_pFilter = SfxGetpApp()->GetFilterMatcher().GetFilter4FilterName( aFilterName );
3406 else
3408 // This filter is from an external provider such as orcus.
3409 pImpl->m_pCustomFilter = std::make_shared<SfxFilter>(aFilterProvider, aFilterName);
3410 pImpl->m_pFilter = pImpl->m_pCustomFilter;
3413 const SfxStringItem* pSalvageItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_DOC_SALVAGE, false);
3414 if( pSalvageItem )
3416 // QUESTION: there is some treatment of Salvage in Init_Impl; align!
3417 if ( !pSalvageItem->GetValue().isEmpty() )
3419 // if a URL is provided in SalvageItem that means that the FileName refers to a temporary file
3420 // that must be copied here
3422 const SfxStringItem* pFileNameItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_FILE_NAME, false);
3423 if (!pFileNameItem) throw uno::RuntimeException();
3424 OUString aNewTempFileURL = SfxMedium::CreateTempCopyWithExt( pFileNameItem->GetValue() );
3425 if ( !aNewTempFileURL.isEmpty() )
3427 pImpl->m_pSet->Put( SfxStringItem( SID_FILE_NAME, aNewTempFileURL ) );
3428 pImpl->m_pSet->ClearItem( SID_INPUTSTREAM );
3429 pImpl->m_pSet->ClearItem( SID_STREAM );
3430 pImpl->m_pSet->ClearItem( SID_CONTENT );
3432 else
3434 SAL_WARN( "sfx.doc", "Can not create a new temporary file for crash recovery!" );
3439 const SfxBoolItem* pReadOnlyItem = SfxItemSet::GetItem<SfxBoolItem>(pImpl->m_pSet.get(), SID_DOC_READONLY, false);
3440 if ( pReadOnlyItem && pReadOnlyItem->GetValue() )
3441 pImpl->m_bOriginallyLoadedReadOnly = true;
3443 const SfxStringItem* pFileNameItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_FILE_NAME, false);
3444 if (!pFileNameItem) throw uno::RuntimeException();
3445 pImpl->m_aLogicName = pFileNameItem->GetValue();
3446 pImpl->m_nStorOpenMode = pImpl->m_bOriginallyLoadedReadOnly
3447 ? SFX_STREAM_READONLY : SFX_STREAM_READWRITE;
3448 Init_Impl();
3451 void SfxMedium::SetArgs(const uno::Sequence<beans::PropertyValue>& rArgs)
3453 comphelper::SequenceAsHashMap aArgsMap(rArgs);
3454 aArgsMap.erase("Stream");
3455 aArgsMap.erase("InputStream");
3456 pImpl->m_aArgs = aArgsMap.getAsConstPropertyValueList();
3459 const uno::Sequence<beans::PropertyValue> & SfxMedium::GetArgs() const { return pImpl->m_aArgs; }
3461 SfxMedium::SfxMedium( const uno::Reference < embed::XStorage >& rStor, const OUString& rBaseURL, const std::shared_ptr<SfxItemSet>& p ) :
3462 pImpl(new SfxMedium_Impl)
3464 OUString aType = SfxFilter::GetTypeFromStorage(rStor);
3465 pImpl->m_pFilter = SfxGetpApp()->GetFilterMatcher().GetFilter4EA( aType );
3466 DBG_ASSERT( pImpl->m_pFilter, "No Filter for storage found!" );
3468 Init_Impl();
3469 pImpl->xStorage = rStor;
3470 pImpl->bDisposeStorage = false;
3472 // always take BaseURL first, could be overwritten by ItemSet
3473 GetItemSet()->Put( SfxStringItem( SID_DOC_BASEURL, rBaseURL ) );
3474 if ( p )
3475 GetItemSet()->Put( *p );
3479 SfxMedium::SfxMedium( const uno::Reference < embed::XStorage >& rStor, const OUString& rBaseURL, const OUString &rTypeName, const std::shared_ptr<SfxItemSet>& p ) :
3480 pImpl(new SfxMedium_Impl)
3482 pImpl->m_pFilter = SfxGetpApp()->GetFilterMatcher().GetFilter4EA( rTypeName );
3483 DBG_ASSERT( pImpl->m_pFilter, "No Filter for storage found!" );
3485 Init_Impl();
3486 pImpl->xStorage = rStor;
3487 pImpl->bDisposeStorage = false;
3489 // always take BaseURL first, could be overwritten by ItemSet
3490 GetItemSet()->Put( SfxStringItem( SID_DOC_BASEURL, rBaseURL ) );
3491 if ( p )
3492 GetItemSet()->Put( *p );
3495 // NOTE: should only be called on main thread
3496 SfxMedium::~SfxMedium()
3498 CancelCheckEditableEntry();
3500 // if there is a requirement to clean the backup this is the last possibility to do it
3501 ClearBackup_Impl();
3503 Close(/*bInDestruction*/true);
3505 if( !pImpl->bIsTemp || pImpl->m_aName.isEmpty() )
3506 return;
3508 OUString aTemp;
3509 if ( osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aTemp )
3510 != osl::FileBase::E_None )
3512 SAL_WARN( "sfx.doc", "Physical name not convertible!");
3515 if ( !::utl::UCBContentHelper::Kill( aTemp ) )
3517 SAL_WARN( "sfx.doc", "Couldn't remove temporary file!");
3521 const OUString& SfxMedium::GetName() const
3523 return pImpl->m_aLogicName;
3526 const INetURLObject& SfxMedium::GetURLObject() const
3528 std::unique_lock<std::recursive_mutex> chkEditLock;
3529 if (pImpl->m_pCheckEditableWorkerMutex != nullptr)
3530 chkEditLock = std::unique_lock<std::recursive_mutex>(*(pImpl->m_pCheckEditableWorkerMutex));
3532 if (!pImpl->m_pURLObj)
3534 pImpl->m_pURLObj.reset( new INetURLObject( pImpl->m_aLogicName ) );
3535 pImpl->m_pURLObj->SetMark(u"");
3538 return *pImpl->m_pURLObj;
3541 void SfxMedium::SetExpired_Impl( const DateTime& rDateTime )
3543 pImpl->aExpireTime = rDateTime;
3547 bool SfxMedium::IsExpired() const
3549 return pImpl->aExpireTime.IsValidAndGregorian() && pImpl->aExpireTime < DateTime( DateTime::SYSTEM );
3553 SfxFrame* SfxMedium::GetLoadTargetFrame() const
3555 return pImpl->wLoadTargetFrame;
3558 void SfxMedium::setStreamToLoadFrom(const css::uno::Reference<css::io::XInputStream>& xInputStream, bool bIsReadOnly )
3560 pImpl->m_xInputStreamToLoadFrom = xInputStream;
3561 pImpl->m_bInputStreamIsReadOnly = bIsReadOnly;
3564 void SfxMedium::SetLoadTargetFrame(SfxFrame* pFrame )
3566 pImpl->wLoadTargetFrame = pFrame;
3570 void SfxMedium::SetStorage_Impl( const uno::Reference < embed::XStorage >& rStor )
3572 pImpl->xStorage = rStor;
3576 SfxItemSet* SfxMedium::GetItemSet() const
3578 // this method *must* return an ItemSet, returning NULL can cause crashes
3579 if (!pImpl->m_pSet)
3580 pImpl->m_pSet = std::make_shared<SfxAllItemSet>( SfxGetpApp()->GetPool() );
3581 return pImpl->m_pSet.get();
3585 SvKeyValueIterator* SfxMedium::GetHeaderAttributes_Impl()
3587 if( !pImpl->xAttributes.is() )
3589 pImpl->xAttributes = SvKeyValueIteratorRef( new SvKeyValueIterator );
3591 if ( GetContent().is() )
3595 Any aAny = pImpl->aContent.getPropertyValue("MediaType");
3596 OUString aContentType;
3597 aAny >>= aContentType;
3599 pImpl->xAttributes->Append( SvKeyValue( "content-type", aContentType ) );
3601 catch ( const css::uno::Exception& )
3607 return pImpl->xAttributes.get();
3610 css::uno::Reference< css::io::XInputStream > const & SfxMedium::GetInputStream()
3612 if ( !pImpl->xInputStream.is() )
3613 GetMedium_Impl();
3614 return pImpl->xInputStream;
3617 const uno::Sequence < util::RevisionTag >& SfxMedium::GetVersionList( bool _bNoReload )
3619 // if the medium has no name, then this medium should represent a new document and can have no version info
3620 if ( ( !_bNoReload || !pImpl->m_bVersionsAlreadyLoaded ) && !pImpl->aVersions.hasElements() &&
3621 ( !pImpl->m_aName.isEmpty() || !pImpl->m_aLogicName.isEmpty() ) && GetStorage().is() )
3623 uno::Reference < document::XDocumentRevisionListPersistence > xReader =
3624 document::DocumentRevisionListPersistence::create( comphelper::getProcessComponentContext() );
3627 pImpl->aVersions = xReader->load( GetStorage() );
3629 catch ( const uno::Exception& )
3634 if ( !pImpl->m_bVersionsAlreadyLoaded )
3635 pImpl->m_bVersionsAlreadyLoaded = true;
3637 return pImpl->aVersions;
3640 uno::Sequence < util::RevisionTag > SfxMedium::GetVersionList( const uno::Reference < embed::XStorage >& xStorage )
3642 uno::Reference < document::XDocumentRevisionListPersistence > xReader =
3643 document::DocumentRevisionListPersistence::create( comphelper::getProcessComponentContext() );
3646 return xReader->load( xStorage );
3648 catch ( const uno::Exception& )
3652 return uno::Sequence < util::RevisionTag >();
3655 void SfxMedium::AddVersion_Impl( util::RevisionTag& rRevision )
3657 if ( !GetStorage().is() )
3658 return;
3660 // To determine a unique name for the stream
3661 std::vector<sal_uInt32> aLongs;
3662 sal_Int32 nLength = pImpl->aVersions.getLength();
3663 for ( const auto& rVersion : std::as_const(pImpl->aVersions) )
3665 sal_uInt32 nVer = static_cast<sal_uInt32>( rVersion.Identifier.copy(7).toInt32());
3666 size_t n;
3667 for ( n=0; n<aLongs.size(); ++n )
3668 if ( nVer<aLongs[n] )
3669 break;
3671 aLongs.insert( aLongs.begin()+n, nVer );
3674 std::vector<sal_uInt32>::size_type nKey;
3675 for ( nKey=0; nKey<aLongs.size(); ++nKey )
3676 if ( aLongs[nKey] > nKey+1 )
3677 break;
3679 OUString aRevName = "Version" + OUString::number( nKey + 1 );
3680 pImpl->aVersions.realloc( nLength+1 );
3681 rRevision.Identifier = aRevName;
3682 pImpl->aVersions.getArray()[nLength] = rRevision;
3685 void SfxMedium::RemoveVersion_Impl( const OUString& rName )
3687 if ( !pImpl->aVersions.hasElements() )
3688 return;
3690 auto pVersion = std::find_if(std::cbegin(pImpl->aVersions), std::cend(pImpl->aVersions),
3691 [&rName](const auto& rVersion) { return rVersion.Identifier == rName; });
3692 if (pVersion != std::cend(pImpl->aVersions))
3694 auto nIndex = static_cast<sal_Int32>(std::distance(std::cbegin(pImpl->aVersions), pVersion));
3695 comphelper::removeElementAt(pImpl->aVersions, nIndex);
3699 bool SfxMedium::TransferVersionList_Impl( SfxMedium const & rMedium )
3701 if ( rMedium.pImpl->aVersions.hasElements() )
3703 pImpl->aVersions = rMedium.pImpl->aVersions;
3704 return true;
3707 return false;
3710 void SfxMedium::SaveVersionList_Impl()
3712 if ( !GetStorage().is() )
3713 return;
3715 if ( !pImpl->aVersions.hasElements() )
3716 return;
3718 uno::Reference < document::XDocumentRevisionListPersistence > xWriter =
3719 document::DocumentRevisionListPersistence::create( comphelper::getProcessComponentContext() );
3722 xWriter->store( GetStorage(), pImpl->aVersions );
3724 catch ( const uno::Exception& )
3729 bool SfxMedium::IsReadOnly() const
3731 // a) ReadOnly filter can't produce read/write contents!
3732 bool bReadOnly = pImpl->m_pFilter && (pImpl->m_pFilter->GetFilterFlags() & SfxFilterFlags::OPENREADONLY);
3734 // b) if filter allow read/write contents .. check open mode of the storage
3735 if (!bReadOnly)
3736 bReadOnly = !( GetOpenMode() & StreamMode::WRITE );
3738 // c) the API can force the readonly state!
3739 if (!bReadOnly)
3741 const SfxBoolItem* pItem = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_DOC_READONLY, false);
3742 if (pItem)
3743 bReadOnly = pItem->GetValue();
3746 return bReadOnly;
3749 bool SfxMedium::IsOriginallyReadOnly() const
3751 return pImpl->m_bOriginallyReadOnly;
3754 void SfxMedium::SetOriginallyReadOnly(bool val)
3756 pImpl->m_bOriginallyReadOnly = val;
3759 bool SfxMedium::IsOriginallyLoadedReadOnly() const
3761 return pImpl->m_bOriginallyLoadedReadOnly;
3764 bool SfxMedium::SetWritableForUserOnly( const OUString& aURL )
3766 // UCB does not allow to allow write access only for the user,
3767 // use osl API
3768 bool bResult = false;
3770 ::osl::DirectoryItem aDirItem;
3771 if ( ::osl::DirectoryItem::get( aURL, aDirItem ) == ::osl::FileBase::E_None )
3773 ::osl::FileStatus aFileStatus( osl_FileStatus_Mask_Attributes );
3774 if ( aDirItem.getFileStatus( aFileStatus ) == osl::FileBase::E_None
3775 && aFileStatus.isValid( osl_FileStatus_Mask_Attributes ) )
3777 sal_uInt64 nAttributes = aFileStatus.getAttributes();
3779 nAttributes &= ~(osl_File_Attribute_OwnWrite |
3780 osl_File_Attribute_GrpWrite |
3781 osl_File_Attribute_OthWrite |
3782 osl_File_Attribute_ReadOnly);
3783 nAttributes |= (osl_File_Attribute_OwnWrite |
3784 osl_File_Attribute_OwnRead);
3786 bResult = ( osl::File::setAttributes( aURL, nAttributes ) == ::osl::FileBase::E_None );
3790 return bResult;
3793 namespace
3795 /// Get the parent directory of a temporary file for output purposes.
3796 OUString GetLogicBase(const INetURLObject& rURL, std::unique_ptr<SfxMedium_Impl> const & pImpl)
3798 OUString aLogicBase;
3800 #if HAVE_FEATURE_MACOSX_SANDBOX
3801 // In a sandboxed environment we don't want to attempt to create temporary files in the same
3802 // directory where the user has selected an output file to be stored. The sandboxed process has
3803 // permission only to create the specifically named output file in that directory.
3804 (void) rURL;
3805 (void) pImpl;
3806 #else
3808 if (!pImpl->m_bHasEmbeddedObjects // Embedded objects would mean a special base, ignore that.
3809 && rURL.GetProtocol() == INetProtocol::File && !pImpl->m_pInStream)
3811 // Try to create the temp file in the same directory when storing.
3812 INetURLObject aURL(rURL);
3813 aURL.removeSegment();
3814 aLogicBase = aURL.GetMainURL(INetURLObject::DecodeMechanism::WithCharset);
3817 #endif // !HAVE_FEATURE_MACOSX_SANDBOX
3819 return aLogicBase;
3823 void SfxMedium::CreateTempFile( bool bReplace )
3825 if ( pImpl->pTempFile )
3827 if ( !bReplace )
3828 return;
3830 pImpl->pTempFile.reset();
3831 pImpl->m_aName.clear();
3834 OUString aLogicBase = GetLogicBase(GetURLObject(), pImpl);
3835 pImpl->pTempFile.reset(new ::utl::TempFile(&aLogicBase));
3836 pImpl->pTempFile->EnableKillingFile();
3837 pImpl->m_aName = pImpl->pTempFile->GetFileName();
3838 OUString aTmpURL = pImpl->pTempFile->GetURL();
3839 if ( pImpl->m_aName.isEmpty() || aTmpURL.isEmpty() )
3841 SetError(ERRCODE_IO_CANTWRITE);
3842 return;
3845 if ( !(pImpl->m_nStorOpenMode & StreamMode::TRUNC) )
3847 bool bTransferSuccess = false;
3849 if ( GetContent().is()
3850 && GetURLObject().GetProtocol() == INetProtocol::File
3851 && ::utl::UCBContentHelper::IsDocument( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) )
3853 // if there is already such a document, we should copy it
3854 // if it is a file system use OS copy process
3857 uno::Reference< css::ucb::XCommandEnvironment > xComEnv;
3858 INetURLObject aTmpURLObj( aTmpURL );
3859 OUString aFileName = aTmpURLObj.getName( INetURLObject::LAST_SEGMENT,
3860 true,
3861 INetURLObject::DecodeMechanism::WithCharset );
3862 if ( !aFileName.isEmpty() && aTmpURLObj.removeSegment() )
3864 ::ucbhelper::Content aTargetContent( aTmpURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() );
3865 OUString sMimeType = pImpl->getFilterMimeType();
3866 aTargetContent.transferContent( pImpl->aContent, ::ucbhelper::InsertOperation::Copy, aFileName, NameClash::OVERWRITE, sMimeType );
3867 SetWritableForUserOnly( aTmpURL );
3868 bTransferSuccess = true;
3871 catch( const uno::Exception& )
3874 if ( bTransferSuccess )
3876 CloseOutStream();
3877 CloseInStream();
3881 if ( !bTransferSuccess && pImpl->m_pInStream )
3883 // the case when there is no URL-access available or this is a remote protocol
3884 // but there is an input stream
3885 GetOutStream();
3886 if ( pImpl->m_pOutStream )
3888 std::unique_ptr<char[]> pBuf(new char [8192]);
3889 ErrCode nErr = ERRCODE_NONE;
3891 pImpl->m_pInStream->Seek(0);
3892 pImpl->m_pOutStream->Seek(0);
3894 while( !pImpl->m_pInStream->eof() && nErr == ERRCODE_NONE )
3896 sal_uInt32 nRead = pImpl->m_pInStream->ReadBytes(pBuf.get(), 8192);
3897 nErr = pImpl->m_pInStream->GetError();
3898 pImpl->m_pOutStream->WriteBytes( pBuf.get(), nRead );
3901 bTransferSuccess = true;
3902 CloseInStream();
3904 CloseOutStream_Impl();
3906 else
3908 // Quite strange design, but currently it is expected that in this case no transfer happens
3909 // TODO/LATER: get rid of this inconsistent part of the call design
3910 bTransferSuccess = true;
3911 CloseInStream();
3914 if ( !bTransferSuccess )
3916 SetError(ERRCODE_IO_CANTWRITE);
3917 return;
3921 CloseStorage();
3925 void SfxMedium::CreateTempFileNoCopy()
3927 // this call always replaces the existing temporary file
3928 pImpl->pTempFile.reset();
3930 OUString aLogicBase = GetLogicBase(GetURLObject(), pImpl);
3931 pImpl->pTempFile.reset(new ::utl::TempFile(&aLogicBase));
3932 pImpl->pTempFile->EnableKillingFile();
3933 pImpl->m_aName = pImpl->pTempFile->GetFileName();
3934 if ( pImpl->m_aName.isEmpty() )
3936 SetError(ERRCODE_IO_CANTWRITE);
3937 return;
3940 CloseOutStream_Impl();
3941 CloseStorage();
3944 bool SfxMedium::SignDocumentContentUsingCertificate(
3945 const css::uno::Reference<css::frame::XModel>& xModel, bool bHasValidDocumentSignature,
3946 const Reference<XCertificate>& xCertificate)
3948 bool bChanges = false;
3950 if (IsOpen() || GetError())
3952 SAL_WARN("sfx.doc", "The medium must be closed by the signer!");
3953 return bChanges;
3956 // The component should know if there was a valid document signature, since
3957 // it should show a warning in this case
3958 OUString aODFVersion(comphelper::OStorageHelper::GetODFVersionFromStorage(GetStorage()));
3959 uno::Reference< security::XDocumentDigitalSignatures > xSigner(
3960 security::DocumentDigitalSignatures::createWithVersionAndValidSignature(
3961 comphelper::getProcessComponentContext(), aODFVersion, bHasValidDocumentSignature ) );
3962 auto xModelSigner = dynamic_cast<sfx2::DigitalSignatures*>(xSigner.get());
3963 if (!xModelSigner)
3965 return bChanges;
3968 uno::Reference< embed::XStorage > xWriteableZipStor;
3970 // we can reuse the temporary file if there is one already
3971 CreateTempFile( false );
3972 GetMedium_Impl();
3976 if ( !pImpl->xStream.is() )
3977 throw uno::RuntimeException();
3979 bool bODF = GetFilter()->IsOwnFormat();
3982 xWriteableZipStor = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream( ZIP_STORAGE_FORMAT_STRING, pImpl->xStream );
3984 catch (const io::IOException&)
3986 if (bODF)
3988 TOOLS_WARN_EXCEPTION("sfx.doc", "ODF stream is not a zip storage");
3992 if ( !xWriteableZipStor.is() && bODF )
3993 throw uno::RuntimeException();
3995 uno::Reference< embed::XStorage > xMetaInf;
3996 if (xWriteableZipStor.is() && xWriteableZipStor->hasByName("META-INF"))
3998 xMetaInf = xWriteableZipStor->openStorageElement(
3999 "META-INF",
4000 embed::ElementModes::READWRITE );
4001 if ( !xMetaInf.is() )
4002 throw uno::RuntimeException();
4006 if (xMetaInf.is())
4008 // ODF.
4009 uno::Reference< io::XStream > xStream;
4010 if (GetFilter() && GetFilter()->IsOwnFormat())
4011 xStream.set(xMetaInf->openStreamElement(xSigner->getDocumentContentSignatureDefaultStreamName(), embed::ElementModes::READWRITE), uno::UNO_SET_THROW);
4013 bool bSuccess = xModelSigner->SignModelWithCertificate(
4014 xModel, xCertificate, GetZipStorageToSign_Impl(), xStream);
4016 if (bSuccess)
4018 uno::Reference< embed::XTransactedObject > xTransact( xMetaInf, uno::UNO_QUERY_THROW );
4019 xTransact->commit();
4020 xTransact.set( xWriteableZipStor, uno::UNO_QUERY_THROW );
4021 xTransact->commit();
4023 // the temporary file has been written, commit it to the original file
4024 Commit();
4025 bChanges = true;
4028 else if (xWriteableZipStor.is())
4030 // OOXML.
4031 uno::Reference<io::XStream> xStream;
4033 // We need read-write to be able to add the signature relation.
4034 bool bSuccess = xModelSigner->SignModelWithCertificate(
4035 xModel, xCertificate, GetZipStorageToSign_Impl(/*bReadOnly=*/false), xStream);
4037 if (bSuccess)
4039 uno::Reference<embed::XTransactedObject> xTransact(xWriteableZipStor, uno::UNO_QUERY_THROW);
4040 xTransact->commit();
4042 // the temporary file has been written, commit it to the original file
4043 Commit();
4044 bChanges = true;
4047 else
4049 // Something not ZIP based: e.g. PDF.
4050 std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(GetName(), StreamMode::READ | StreamMode::WRITE));
4051 uno::Reference<io::XStream> xStream(new utl::OStreamWrapper(*pStream));
4052 if (xModelSigner->SignModelWithCertificate(
4053 xModel, xCertificate, uno::Reference<embed::XStorage>(), xStream))
4054 bChanges = true;
4058 catch ( const uno::Exception& )
4060 SAL_WARN( "sfx.doc", "Couldn't use signing functionality!" );
4063 CloseAndRelease();
4065 ResetError();
4067 return bChanges;
4070 bool SfxMedium::SignContents_Impl(weld::Window* pDialogParent,
4071 bool bSignScriptingContent,
4072 bool bHasValidDocumentSignature,
4073 const OUString& aSignatureLineId,
4074 const Reference<XCertificate>& xCert,
4075 const Reference<XGraphic>& xValidGraphic,
4076 const Reference<XGraphic>& xInvalidGraphic,
4077 const OUString& aComment)
4079 bool bChanges = false;
4081 if (IsOpen() || GetError())
4083 SAL_WARN("sfx.doc", "The medium must be closed by the signer!");
4084 return bChanges;
4087 // The component should know if there was a valid document signature, since
4088 // it should show a warning in this case
4089 OUString aODFVersion(comphelper::OStorageHelper::GetODFVersionFromStorage(GetStorage()));
4090 uno::Reference< security::XDocumentDigitalSignatures > xSigner(
4091 security::DocumentDigitalSignatures::createWithVersionAndValidSignature(
4092 comphelper::getProcessComponentContext(), aODFVersion, bHasValidDocumentSignature ) );
4093 if (pDialogParent)
4094 xSigner->setParentWindow(pDialogParent->GetXWindow());
4096 uno::Reference< embed::XStorage > xWriteableZipStor;
4098 // we can reuse the temporary file if there is one already
4099 CreateTempFile( false );
4100 GetMedium_Impl();
4104 if ( !pImpl->xStream.is() )
4105 throw uno::RuntimeException();
4107 bool bODF = GetFilter()->IsOwnFormat();
4110 xWriteableZipStor = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream( ZIP_STORAGE_FORMAT_STRING, pImpl->xStream );
4112 catch (const io::IOException&)
4114 if (bODF)
4116 TOOLS_WARN_EXCEPTION("sfx.doc", "ODF stream is not a zip storage");
4120 if ( !xWriteableZipStor.is() && bODF )
4121 throw uno::RuntimeException();
4123 uno::Reference< embed::XStorage > xMetaInf;
4124 if (xWriteableZipStor.is() && xWriteableZipStor->hasByName("META-INF"))
4126 xMetaInf = xWriteableZipStor->openStorageElement(
4127 "META-INF",
4128 embed::ElementModes::READWRITE );
4129 if ( !xMetaInf.is() )
4130 throw uno::RuntimeException();
4133 if ( bSignScriptingContent )
4135 // If the signature has already the document signature it will be removed
4136 // after the scripting signature is inserted.
4137 uno::Reference< io::XStream > xStream(
4138 xMetaInf->openStreamElement( xSigner->getScriptingContentSignatureDefaultStreamName(),
4139 embed::ElementModes::READWRITE ),
4140 uno::UNO_SET_THROW );
4142 if ( xSigner->signScriptingContent( GetZipStorageToSign_Impl(), xStream ) )
4144 // remove the document signature if any
4145 OUString aDocSigName = xSigner->getDocumentContentSignatureDefaultStreamName();
4146 if ( !aDocSigName.isEmpty() && xMetaInf->hasByName( aDocSigName ) )
4147 xMetaInf->removeElement( aDocSigName );
4149 uno::Reference< embed::XTransactedObject > xTransact( xMetaInf, uno::UNO_QUERY_THROW );
4150 xTransact->commit();
4151 xTransact.set( xWriteableZipStor, uno::UNO_QUERY_THROW );
4152 xTransact->commit();
4154 // the temporary file has been written, commit it to the original file
4155 Commit();
4156 bChanges = true;
4159 else
4161 if (xMetaInf.is())
4163 // ODF.
4164 uno::Reference< io::XStream > xStream;
4165 if (GetFilter() && GetFilter()->IsOwnFormat())
4166 xStream.set(xMetaInf->openStreamElement(xSigner->getDocumentContentSignatureDefaultStreamName(), embed::ElementModes::READWRITE), uno::UNO_SET_THROW);
4168 bool bSuccess = false;
4169 if (xCert.is())
4170 bSuccess = xSigner->signSignatureLine(
4171 GetZipStorageToSign_Impl(), xStream, aSignatureLineId, xCert,
4172 xValidGraphic, xInvalidGraphic, aComment);
4173 else
4174 bSuccess = xSigner->signDocumentContent(GetZipStorageToSign_Impl(),
4175 xStream);
4177 if (bSuccess)
4179 uno::Reference< embed::XTransactedObject > xTransact( xMetaInf, uno::UNO_QUERY_THROW );
4180 xTransact->commit();
4181 xTransact.set( xWriteableZipStor, uno::UNO_QUERY_THROW );
4182 xTransact->commit();
4184 // the temporary file has been written, commit it to the original file
4185 Commit();
4186 bChanges = true;
4189 else if (xWriteableZipStor.is())
4191 // OOXML.
4192 uno::Reference<io::XStream> xStream;
4194 bool bSuccess = false;
4195 if (xCert.is())
4197 bSuccess = xSigner->signSignatureLine(
4198 GetZipStorageToSign_Impl(/*bReadOnly=*/false), xStream, aSignatureLineId,
4199 xCert, xValidGraphic, xInvalidGraphic, aComment);
4201 else
4203 // We need read-write to be able to add the signature relation.
4204 bSuccess =xSigner->signDocumentContent(
4205 GetZipStorageToSign_Impl(/*bReadOnly=*/false), xStream);
4208 if (bSuccess)
4210 uno::Reference<embed::XTransactedObject> xTransact(xWriteableZipStor, uno::UNO_QUERY_THROW);
4211 xTransact->commit();
4213 // the temporary file has been written, commit it to the original file
4214 Commit();
4215 bChanges = true;
4218 else
4220 // Something not ZIP based: e.g. PDF.
4221 std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(GetName(), StreamMode::READ | StreamMode::WRITE));
4222 uno::Reference<io::XStream> xStream(new utl::OStreamWrapper(*pStream));
4223 if (xSigner->signDocumentContent(uno::Reference<embed::XStorage>(), xStream))
4224 bChanges = true;
4228 catch ( const uno::Exception& )
4230 SAL_WARN( "sfx.doc", "Couldn't use signing functionality!" );
4233 CloseAndRelease();
4235 ResetError();
4237 return bChanges;
4241 SignatureState SfxMedium::GetCachedSignatureState_Impl() const
4243 return pImpl->m_nSignatureState;
4247 void SfxMedium::SetCachedSignatureState_Impl( SignatureState nState )
4249 pImpl->m_nSignatureState = nState;
4252 void SfxMedium::SetHasEmbeddedObjects(bool bHasEmbeddedObjects)
4254 pImpl->m_bHasEmbeddedObjects = bHasEmbeddedObjects;
4257 bool SfxMedium::HasStorage_Impl() const
4259 return pImpl->xStorage.is();
4262 bool SfxMedium::IsOpen() const
4264 return pImpl->m_pInStream || pImpl->m_pOutStream || pImpl->xStorage.is();
4267 OUString SfxMedium::CreateTempCopyWithExt( const OUString& aURL )
4269 OUString aResult;
4271 if ( !aURL.isEmpty() )
4273 sal_Int32 nPrefixLen = aURL.lastIndexOf( '.' );
4274 OUString aExt = ( nPrefixLen == -1 ) ? OUString() : aURL.copy( nPrefixLen );
4276 OUString aNewTempFileURL = ::utl::TempFile( OUString(), true, &aExt ).GetURL();
4277 if ( !aNewTempFileURL.isEmpty() )
4279 INetURLObject aSource( aURL );
4280 INetURLObject aDest( aNewTempFileURL );
4281 OUString aFileName = aDest.getName( INetURLObject::LAST_SEGMENT,
4282 true,
4283 INetURLObject::DecodeMechanism::WithCharset );
4284 if ( !aFileName.isEmpty() && aDest.removeSegment() )
4288 uno::Reference< css::ucb::XCommandEnvironment > xComEnv;
4289 ::ucbhelper::Content aTargetContent( aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() );
4290 ::ucbhelper::Content aSourceContent( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() );
4291 aTargetContent.transferContent( aSourceContent,
4292 ::ucbhelper::InsertOperation::Copy,
4293 aFileName,
4294 NameClash::OVERWRITE );
4295 aResult = aNewTempFileURL;
4297 catch( const uno::Exception& )
4303 return aResult;
4306 bool SfxMedium::CallApproveHandler(const uno::Reference< task::XInteractionHandler >& xHandler, const uno::Any& rRequest, bool bAllowAbort)
4308 bool bResult = false;
4310 if ( xHandler.is() )
4314 uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations( bAllowAbort ? 2 : 1 );
4315 auto pContinuations = aContinuations.getArray();
4317 ::rtl::Reference< ::comphelper::OInteractionApprove > pApprove( new ::comphelper::OInteractionApprove );
4318 pContinuations[ 0 ] = pApprove.get();
4320 if ( bAllowAbort )
4322 ::rtl::Reference< ::comphelper::OInteractionAbort > pAbort( new ::comphelper::OInteractionAbort );
4323 pContinuations[ 1 ] = pAbort.get();
4326 xHandler->handle(::framework::InteractionRequest::CreateRequest(rRequest, aContinuations));
4327 bResult = pApprove->wasSelected();
4329 catch( const Exception& )
4334 return bResult;
4337 OUString SfxMedium::SwitchDocumentToTempFile()
4339 // the method returns empty string in case of failure
4340 OUString aResult;
4341 OUString aOrigURL = pImpl->m_aLogicName;
4343 if ( !aOrigURL.isEmpty() )
4345 sal_Int32 nPrefixLen = aOrigURL.lastIndexOf( '.' );
4346 OUString const aExt = (nPrefixLen == -1)
4347 ? OUString()
4348 : aOrigURL.copy(nPrefixLen);
4349 OUString aNewURL = ::utl::TempFile( OUString(), true, &aExt ).GetURL();
4351 // TODO/LATER: In future the aLogicName should be set to shared folder URL
4352 // and a temporary file should be created. Transport_Impl should be impossible then.
4353 if ( !aNewURL.isEmpty() )
4355 uno::Reference< embed::XStorage > xStorage = GetStorage();
4356 uno::Reference< embed::XOptimizedStorage > xOptStorage( xStorage, uno::UNO_QUERY );
4358 if ( xOptStorage.is() )
4360 // TODO/LATER: reuse the pImpl->pTempFile if it already exists
4361 CanDisposeStorage_Impl( false );
4362 Close();
4363 SetPhysicalName_Impl( OUString() );
4364 SetName( aNewURL );
4366 // remove the readonly state
4367 bool bWasReadonly = false;
4368 pImpl->m_nStorOpenMode = SFX_STREAM_READWRITE;
4369 const SfxBoolItem* pReadOnlyItem = SfxItemSet::GetItem<SfxBoolItem>(pImpl->m_pSet.get(), SID_DOC_READONLY, false);
4370 if ( pReadOnlyItem && pReadOnlyItem->GetValue() )
4371 bWasReadonly = true;
4372 GetItemSet()->ClearItem( SID_DOC_READONLY );
4374 GetMedium_Impl();
4375 LockOrigFileOnDemand( false, false );
4376 CreateTempFile();
4377 GetMedium_Impl();
4379 if ( pImpl->xStream.is() )
4383 xOptStorage->writeAndAttachToStream( pImpl->xStream );
4384 pImpl->xStorage = xStorage;
4385 aResult = aNewURL;
4387 catch( const uno::Exception& )
4391 if ( aResult.isEmpty() )
4393 Close();
4394 SetPhysicalName_Impl( OUString() );
4395 SetName( aOrigURL );
4396 if ( bWasReadonly )
4398 // set the readonly state back
4399 pImpl->m_nStorOpenMode = SFX_STREAM_READONLY;
4400 GetItemSet()->Put( SfxBoolItem(SID_DOC_READONLY, true));
4402 GetMedium_Impl();
4403 pImpl->xStorage = xStorage;
4409 return aResult;
4412 bool SfxMedium::SwitchDocumentToFile( const OUString& aURL )
4414 // the method is only for storage based documents
4415 bool bResult = false;
4416 OUString aOrigURL = pImpl->m_aLogicName;
4418 if ( !aURL.isEmpty() && !aOrigURL.isEmpty() )
4420 uno::Reference< embed::XStorage > xStorage = GetStorage();
4421 uno::Reference< embed::XOptimizedStorage > xOptStorage( xStorage, uno::UNO_QUERY );
4423 // TODO/LATER: reuse the pImpl->pTempFile if it already exists
4424 CanDisposeStorage_Impl( false );
4425 Close();
4426 SetPhysicalName_Impl( OUString() );
4427 SetName( aURL );
4429 // open the temporary file based document
4430 GetMedium_Impl();
4431 LockOrigFileOnDemand( false, false );
4432 CreateTempFile();
4433 GetMedium_Impl();
4435 if ( pImpl->xStream.is() )
4439 uno::Reference< io::XTruncate > xTruncate( pImpl->xStream, uno::UNO_QUERY_THROW );
4440 xTruncate->truncate();
4441 if ( xOptStorage.is() )
4442 xOptStorage->writeAndAttachToStream( pImpl->xStream );
4443 pImpl->xStorage = xStorage;
4444 bResult = true;
4446 catch( const uno::Exception& )
4450 if ( !bResult )
4452 Close();
4453 SetPhysicalName_Impl( OUString() );
4454 SetName( aOrigURL );
4455 GetMedium_Impl();
4456 pImpl->xStorage = xStorage;
4460 return bResult;
4463 void SfxMedium::SetInCheckIn( bool bInCheckIn )
4465 pImpl->m_bInCheckIn = bInCheckIn;
4468 bool SfxMedium::IsInCheckIn( ) const
4470 return pImpl->m_bInCheckIn;
4473 // should only be called on main thread
4474 const std::shared_ptr<std::recursive_mutex>& SfxMedium::GetCheckEditableMutex() const
4476 return pImpl->m_pCheckEditableWorkerMutex;
4479 // should only be called while holding pImpl->m_pCheckEditableWorkerMutex
4480 void SfxMedium::SetWorkerReloadEvent(ImplSVEvent* pEvent)
4482 pImpl->m_pReloadEvent = pEvent;
4485 // should only be called while holding pImpl->m_pCheckEditableWorkerMutex
4486 ImplSVEvent* SfxMedium::GetWorkerReloadEvent() const
4488 return pImpl->m_pReloadEvent;
4491 // should only be called on main thread
4492 void SfxMedium::AddToCheckEditableWorkerList()
4494 if (!pImpl->m_bNotifyWhenEditable)
4495 return;
4497 CancelCheckEditableEntry();
4499 if (pImpl->m_pCheckEditableWorkerMutex == nullptr)
4501 pImpl->m_pCheckEditableWorkerMutex = std::make_shared<std::recursive_mutex>();
4502 if (pImpl->m_pCheckEditableWorkerMutex == nullptr)
4503 return;
4506 pImpl->m_pIsDestructed = std::make_shared<bool>(false);
4507 if (pImpl->m_pIsDestructed == nullptr)
4508 return;
4510 std::unique_lock<std::mutex> globalLock(g_chkReadOnlyGlobalMutex);
4511 if (g_newReadOnlyDocs.find(this) == g_newReadOnlyDocs.end())
4513 bool bAddNewEntry = false;
4514 if (!g_bChkReadOnlyTaskRunning)
4516 std::shared_ptr<comphelper::ThreadTaskTag> pTag
4517 = comphelper::ThreadPool::createThreadTaskTag();
4518 if (pTag != nullptr)
4520 g_bChkReadOnlyTaskRunning = true;
4521 bAddNewEntry = true;
4522 comphelper::ThreadPool::getSharedOptimalPool().pushTask(
4523 std::make_unique<CheckReadOnlyTask>(pTag));
4526 else
4527 bAddNewEntry = true;
4529 if (bAddNewEntry)
4531 std::shared_ptr<ReadOnlyMediumEntry> newEntry = std::make_shared<ReadOnlyMediumEntry>(
4532 pImpl->m_pCheckEditableWorkerMutex, pImpl->m_pIsDestructed);
4534 if (newEntry != nullptr)
4536 g_newReadOnlyDocs[this] = newEntry;
4542 // should only be called on main thread
4543 void SfxMedium::CancelCheckEditableEntry(bool bRemoveEvent)
4545 if (pImpl->m_pCheckEditableWorkerMutex != nullptr)
4547 std::unique_lock<std::recursive_mutex> lock(*(pImpl->m_pCheckEditableWorkerMutex));
4549 if (pImpl->m_pReloadEvent != nullptr)
4551 if (bRemoveEvent)
4552 Application::RemoveUserEvent(pImpl->m_pReloadEvent);
4553 // make sure destructor doesn't use a freed reference
4554 // and reset the event so we can check again
4555 pImpl->m_pReloadEvent = nullptr;
4558 if (pImpl->m_pIsDestructed != nullptr)
4560 *(pImpl->m_pIsDestructed) = true;
4561 pImpl->m_pIsDestructed = nullptr;
4566 /** callback function, which is triggered by worker thread after successfully checking if the file
4567 is editable. Sent from <Application::PostUserEvent(..)>
4568 Note: This method has to be run in the main thread.
4570 IMPL_STATIC_LINK(SfxMedium, ShowReloadEditableDialog, void*, p, void)
4572 SfxMedium* pMed = static_cast<SfxMedium*>(p);
4573 if (pMed == nullptr)
4574 return;
4576 pMed->CancelCheckEditableEntry(false);
4578 uno::Reference<task::XInteractionHandler> xHandler = pMed->GetInteractionHandler();
4579 if (xHandler.is())
4581 OUString aDocumentURL
4582 = pMed->GetURLObject().GetLastName(INetURLObject::DecodeMechanism::WithCharset);
4583 ::rtl::Reference<::ucbhelper::InteractionRequest> xInteractionRequestImpl
4584 = new ::ucbhelper::InteractionRequest(uno::makeAny(document::ReloadEditableRequest(
4585 OUString(), uno::Reference<uno::XInterface>(), aDocumentURL)));
4586 if (xInteractionRequestImpl != nullptr)
4588 uno::Sequence<uno::Reference<task::XInteractionContinuation>> aContinuations{
4589 new ::ucbhelper::InteractionAbort(xInteractionRequestImpl.get()),
4590 new ::ucbhelper::InteractionApprove(xInteractionRequestImpl.get())
4592 xInteractionRequestImpl->setContinuations(aContinuations);
4593 xHandler->handle(xInteractionRequestImpl);
4594 ::rtl::Reference<::ucbhelper::InteractionContinuation> xSelected
4595 = xInteractionRequestImpl->getSelection();
4596 if (uno::Reference<task::XInteractionApprove>(xSelected.get(), uno::UNO_QUERY).is())
4598 for (SfxViewFrame* pFrame = SfxViewFrame::GetFirst(); pFrame;
4599 pFrame = SfxViewFrame::GetNext(*pFrame))
4601 if (pFrame->GetObjectShell()->GetMedium() == pMed)
4603 // special case to ensure view isn't set to read-only in
4604 // SfxViewFrame::ExecReload_Impl after reloading
4605 pMed->SetOriginallyReadOnly(false);
4606 pFrame->GetDispatcher()->Execute(SID_RELOAD);
4607 break;
4615 bool SfxMedium::CheckCanGetLockfile() const
4617 #if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT
4618 bool bCanReload = true;
4619 #else
4620 bool bCanReload = false;
4621 ::svt::DocumentLockFile aLockFile(GetName());
4622 LockFileEntry aData;
4623 osl::DirectoryItem rItem;
4624 auto nError1 = osl::DirectoryItem::get(aLockFile.GetURL(), rItem);
4625 if (nError1 == osl::FileBase::E_None)
4629 aData = aLockFile.GetLockData();
4631 catch (const io::WrongFormatException&)
4633 // we get empty or corrupt data
4634 return false;
4636 catch (const uno::Exception&)
4638 // locked from other app
4639 return false;
4641 LockFileEntry aOwnData = svt::LockFileCommon::GenerateOwnEntry();
4642 bool bOwnLock
4643 = aOwnData[LockFileComponent::SYSUSERNAME] == aData[LockFileComponent::SYSUSERNAME];
4644 if (bOwnLock
4645 && aOwnData[LockFileComponent::LOCALHOST] == aData[LockFileComponent::LOCALHOST]
4646 && aOwnData[LockFileComponent::USERURL] == aData[LockFileComponent::USERURL])
4648 // this is own lock from the same installation, it could remain because of crash
4649 bCanReload = true;
4652 else if (nError1 == osl::FileBase::E_NOENT) // file doesn't exist
4656 aLockFile.CreateOwnLockFile();
4659 // TODO/LATER: A warning could be shown in case the file is not the own one
4660 aLockFile.RemoveFile();
4662 catch (const io::WrongFormatException&)
4666 // erase the empty or corrupt file
4667 aLockFile.RemoveFileDirectly();
4669 catch (const uno::Exception&)
4673 bCanReload = true;
4675 catch (const uno::Exception&)
4679 #endif
4680 return bCanReload;
4683 // worker thread method, should only be one thread globally
4684 void CheckReadOnlyTask::doWork()
4686 if (m_xListener == nullptr)
4687 return;
4689 while (true)
4691 std::unique_lock<std::mutex> termLock(m_xListener->mMutex);
4692 if (m_xListener->mCond.wait_for(termLock, std::chrono::seconds(60),
4693 [this] { return m_xListener->bIsTerminated; }))
4694 // signalled, spurious wakeups should not be possible
4695 return;
4697 // must have timed-out
4698 termLock.unlock();
4699 std::unique_lock<std::mutex> globalLock(g_chkReadOnlyGlobalMutex);
4700 for (auto it = g_newReadOnlyDocs.begin(); it != g_newReadOnlyDocs.end(); )
4702 auto [pMed, roEntry] = *it;
4703 g_existingReadOnlyDocs[pMed] = roEntry;
4704 it = g_newReadOnlyDocs.erase(it);
4706 if (g_existingReadOnlyDocs.size() == 0)
4708 g_bChkReadOnlyTaskRunning = false;
4709 return;
4711 globalLock.unlock();
4713 auto checkForErase = [](SfxMedium* pMed, const std::shared_ptr<ReadOnlyMediumEntry>& roEntry) -> bool
4715 if (pMed == nullptr || roEntry == nullptr || roEntry->_pMutex == nullptr
4716 || roEntry->_pIsDestructed == nullptr)
4717 return true;
4719 std::unique_lock<std::recursive_mutex> medLock(*(roEntry->_pMutex));
4720 if (*(roEntry->_pIsDestructed) || pMed->GetWorkerReloadEvent() != nullptr)
4721 return true;
4723 osl::File aFile(
4724 pMed->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::WithCharset));
4725 if (aFile.open(osl_File_OpenFlag_Write) != osl::FileBase::E_None)
4726 return false;
4728 if (!pMed->CheckCanGetLockfile())
4729 return false;
4731 if (aFile.close() != osl::FileBase::E_None)
4732 return true;
4734 // we can load, ask user
4735 ImplSVEvent* pEvent = Application::PostUserEvent(
4736 LINK(nullptr, SfxMedium, ShowReloadEditableDialog), pMed);
4737 pMed->SetWorkerReloadEvent(pEvent);
4738 return true;
4741 for (auto it = g_existingReadOnlyDocs.begin(); it != g_existingReadOnlyDocs.end(); )
4743 if (checkForErase(it->first, it->second))
4744 it = g_existingReadOnlyDocs.erase(it);
4745 else
4746 ++it;
4751 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */